mirror of
https://gitee.com/openharmony/arkcompiler_runtime_core
synced 2024-11-23 23:00:08 +00:00
Fix StringBuilder merging
Description: StringBuilder merge optimization mistakenly merges two builder when the way they used is not supported Issue: #IB1IO8 Tests: All required pre-merge test passed Results are available in the ggwatcher Signed-off-by: Mikhail Ivanov <ivanov.mikhail2@huawei.com>
This commit is contained in:
parent
a2083faee2
commit
09b368afeb
@ -25,7 +25,6 @@
|
|||||||
#include "optimizer/ir/inst.h"
|
#include "optimizer/ir/inst.h"
|
||||||
|
|
||||||
#include "optimizer/optimizations/cleanup.h"
|
#include "optimizer/optimizations/cleanup.h"
|
||||||
#include "optimizer/optimizations/string_builder_utils.h"
|
|
||||||
|
|
||||||
namespace ark::compiler {
|
namespace ark::compiler {
|
||||||
|
|
||||||
@ -125,26 +124,6 @@ bool IsDataFlowInput(Inst *inst, Inst *input)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsUsedOutsideBasicBlock(Inst *inst, BasicBlock *bb)
|
|
||||||
{
|
|
||||||
for (auto &user : inst->GetUsers()) {
|
|
||||||
auto userInst = user.GetInst();
|
|
||||||
if (userInst->IsCheck()) {
|
|
||||||
if (!userInst->HasSingleUser()) {
|
|
||||||
// In case of multi user check-instruction we assume it is used outside current basic block without
|
|
||||||
// actually testing it.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// In case of single user check-instruction we test its the only user.
|
|
||||||
userInst = userInst->GetUsers().Front().GetInst();
|
|
||||||
}
|
|
||||||
if (userInst->GetBasicBlock() != bb) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimplifyStringBuilder::OptimizeStringBuilderToString(BasicBlock *block)
|
void SimplifyStringBuilder::OptimizeStringBuilderToString(BasicBlock *block)
|
||||||
{
|
{
|
||||||
// Removes unnecessary String Builder instances
|
// Removes unnecessary String Builder instances
|
||||||
@ -302,8 +281,15 @@ bool SimplifyStringBuilder::MatchConcatenation(InstIter &begin, const InstIter &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supported case: number of toString-calls is one,
|
for (size_t index = 0; index < match.appendCount; ++index) {
|
||||||
// number of append calls is between 2 and 4
|
if (!match.appendIntrinsics[index]->IsDominate(match.toStringCall)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supported case: number of toString calls is one,
|
||||||
|
// number of append calls is between 2 and 4,
|
||||||
|
// toString call is dominated by all append calls.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,21 +523,6 @@ SaveStateInst *FindPreHeaderSaveState(Loop *loop)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveStateInst *FindFirstSaveState(BasicBlock *block)
|
|
||||||
{
|
|
||||||
if (block->IsEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto inst : block->Insts()) {
|
|
||||||
if (inst->GetOpcode() == Opcode::SaveState) {
|
|
||||||
return inst->CastToSaveState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t CountOuterLoopSuccs(BasicBlock *block)
|
size_t CountOuterLoopSuccs(BasicBlock *block)
|
||||||
{
|
{
|
||||||
return std::count_if(block->GetSuccsBlocks().begin(), block->GetSuccsBlocks().end(),
|
return std::count_if(block->GetSuccsBlocks().begin(), block->GetSuccsBlocks().end(),
|
||||||
@ -654,26 +625,6 @@ ArenaVector<Inst *> SimplifyStringBuilder::FindStringBuilderAppendInstructions(I
|
|||||||
return appendInstructions;
|
return appendInstructions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveFromInstructionInputs(ArenaVector<std::pair<Inst *, size_t>> &inputDescriptors)
|
|
||||||
{
|
|
||||||
// Inputs must be walked in reverse order for removal
|
|
||||||
std::sort(inputDescriptors.begin(), inputDescriptors.end(),
|
|
||||||
[](auto inputDescX, auto inputDescY) { return inputDescX.second > inputDescY.second; });
|
|
||||||
|
|
||||||
for (auto inputDesc : inputDescriptors) {
|
|
||||||
auto inst = inputDesc.first;
|
|
||||||
auto index = inputDesc.second;
|
|
||||||
|
|
||||||
[[maybe_unused]] auto inputInst = inst->GetInput(index).GetInst();
|
|
||||||
COMPILER_LOG(DEBUG, SIMPLIFY_SB) << "Remove input id=" << inputInst->GetId() << " ("
|
|
||||||
<< GetOpcodeString(inputInst->GetOpcode())
|
|
||||||
<< ") from instruction id=" << inst->GetId() << " ("
|
|
||||||
<< GetOpcodeString(inst->GetOpcode()) << ")";
|
|
||||||
|
|
||||||
inst->RemoveInput(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimplifyStringBuilder::RemoveFromSaveStateInputs(Inst *inst)
|
void SimplifyStringBuilder::RemoveFromSaveStateInputs(Inst *inst)
|
||||||
{
|
{
|
||||||
inputDescriptors_.clear();
|
inputDescriptors_.clear();
|
||||||
@ -1789,29 +1740,6 @@ void SimplifyStringBuilder::ReplaceWithAppendIntrinsic(const ConcatenationMatch
|
|||||||
isApplied_ = true;
|
isApplied_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BreakStringBuilderAppendChains(BasicBlock *block)
|
|
||||||
{
|
|
||||||
// StringBuilder append-call returns 'this' (instance)
|
|
||||||
// Replace all users of append-call with instance itself to support chain calls
|
|
||||||
// like: sb.append(s0).append(s1)...
|
|
||||||
bool isApplied = false;
|
|
||||||
for (auto inst : block->Insts()) {
|
|
||||||
if (!IsStringBuilderAppend(inst) && !IsStringBuilderToString(inst)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto instance = inst->GetDataFlowInput(0);
|
|
||||||
for (auto &user : instance->GetUsers()) {
|
|
||||||
auto userInst = SkipSingleUserCheckInstruction(user.GetInst());
|
|
||||||
if (IsStringBuilderAppend(userInst)) {
|
|
||||||
userInst->ReplaceUsers(instance);
|
|
||||||
isApplied = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isApplied;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasPhiOfStringBuilders(BasicBlock *block)
|
bool HasPhiOfStringBuilders(BasicBlock *block)
|
||||||
{
|
{
|
||||||
for (auto phi : block->PhiInsts()) {
|
for (auto phi : block->PhiInsts()) {
|
||||||
@ -1946,30 +1874,27 @@ void SimplifyStringBuilder::CollectStringBuilderLastCalls(BasicBlock *block)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsMethodStringBuilderDefaultConstructor(inst) || IsMethodStringBuilderConstructorWithCharArrayArg(inst) ||
|
if (!IsStringBuilderToString(inst)) {
|
||||||
IsMethodStringBuilderConstructorWithStringArg(inst)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t index = 0; index < inst->GetInputsCount(); ++index) {
|
auto inputInst = inst->GetDataFlowInput(0);
|
||||||
auto inputInst = inst->GetDataFlowInput(index);
|
if (inputInst->IsSaveState()) {
|
||||||
if (inputInst->IsSaveState()) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto instance = inputInst;
|
auto instance = inputInst;
|
||||||
if (!IsStringBuilderInstance(instance)) {
|
if (!IsStringBuilderInstance(instance)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto calls = stringBuilderFirstLastCalls_.find(instance);
|
auto calls = stringBuilderFirstLastCalls_.find(instance);
|
||||||
if (calls == stringBuilderFirstLastCalls_.end()) {
|
if (calls == stringBuilderFirstLastCalls_.end()) {
|
||||||
calls = stringBuilderFirstLastCalls_.emplace(instance, InstPair {}).first;
|
calls = stringBuilderFirstLastCalls_.emplace(instance, InstPair {}).first;
|
||||||
}
|
}
|
||||||
auto &lastCall = calls->second.second;
|
auto &lastCall = calls->second.second;
|
||||||
if (lastCall == nullptr) {
|
if (lastCall == nullptr) {
|
||||||
lastCall = inst;
|
lastCall = inst;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1998,8 +1923,8 @@ bool SimplifyStringBuilder::CanMergeStringBuilders(Inst *instance, const InstPai
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &[inputInstnceFirstAppendCall, inputInstanceLastToStringCall] = inputInstanceCalls->second;
|
auto &[inputInstanceFirstAppendCall, inputInstanceLastToStringCall] = inputInstanceCalls->second;
|
||||||
if (inputInstnceFirstAppendCall == nullptr || inputInstanceLastToStringCall == nullptr) {
|
if (inputInstanceFirstAppendCall == nullptr || inputInstanceLastToStringCall == nullptr) {
|
||||||
return false; // Unsupported case: doesn't look like concatenation pattern
|
return false; // Unsupported case: doesn't look like concatenation pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2019,6 +1944,18 @@ bool SimplifyStringBuilder::CanMergeStringBuilders(Inst *instance, const InstPai
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if 'inputInstance' toString call comes after any 'inputInstance' append call
|
||||||
|
for (auto &user : inputInstance->GetUsers()) {
|
||||||
|
auto userInst = SkipSingleUserCheckInstruction(user.GetInst());
|
||||||
|
if (!IsStringBuilderAppend(userInst)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputInstanceLastToStringCall->IsDominate(userInst)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if all 'inputInstance' calls comes before any 'instance' call
|
// Check if all 'inputInstance' calls comes before any 'instance' call
|
||||||
return inputInstanceLastToStringCall->IsDominate(instanceFirstCall);
|
return inputInstanceLastToStringCall->IsDominate(instanceFirstCall);
|
||||||
}
|
}
|
||||||
@ -2220,7 +2157,10 @@ void SimplifyStringBuilder::OptimizeStringBuilderChain()
|
|||||||
auto &calls = instanceCalls.second;
|
auto &calls = instanceCalls.second;
|
||||||
|
|
||||||
for (auto call : calls) {
|
for (auto call : calls) {
|
||||||
call->SetInput(0U, inputInstance);
|
// Switch call to new instance only if it is not marked for removal
|
||||||
|
if (call->GetFlag(inst_flags::NO_DCE)) {
|
||||||
|
call->SetInput(0U, inputInstance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FixBrokenSaveStatesForStringBuilderCalls(inputInstance);
|
FixBrokenSaveStatesForStringBuilderCalls(inputInstance);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "optimizer/ir/graph.h"
|
#include "optimizer/ir/graph.h"
|
||||||
#include "optimizer/ir/inst.h"
|
#include "optimizer/ir/inst.h"
|
||||||
#include "optimizer/pass.h"
|
#include "optimizer/pass.h"
|
||||||
|
#include "optimizer/optimizations/string_builder_utils.h"
|
||||||
|
|
||||||
namespace ark::compiler {
|
namespace ark::compiler {
|
||||||
|
|
||||||
@ -273,7 +274,7 @@ private:
|
|||||||
SaveStateBridgesBuilder ssb_ {};
|
SaveStateBridgesBuilder ssb_ {};
|
||||||
ArenaStack<Inst *> instructionsStack_;
|
ArenaStack<Inst *> instructionsStack_;
|
||||||
ArenaVector<Inst *> instructionsVector_;
|
ArenaVector<Inst *> instructionsVector_;
|
||||||
ArenaVector<std::pair<Inst *, size_t>> inputDescriptors_;
|
ArenaVector<InputDesc> inputDescriptors_;
|
||||||
ArenaVector<StringBuilderUsage> usages_;
|
ArenaVector<StringBuilderUsage> usages_;
|
||||||
ArenaVector<ConcatenationLoopMatch> matches_;
|
ArenaVector<ConcatenationLoopMatch> matches_;
|
||||||
StringBuilderCallsMap stringBuilderCalls_;
|
StringBuilderCallsMap stringBuilderCalls_;
|
||||||
|
@ -273,4 +273,78 @@ bool IsIntrinsicStringBuilderAppendString(Inst *inst)
|
|||||||
return runtime->IsIntrinsicStringBuilderAppendString(inst->CastToIntrinsic()->GetIntrinsicId());
|
return runtime->IsIntrinsicStringBuilderAppendString(inst->CastToIntrinsic()->GetIntrinsicId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsUsedOutsideBasicBlock(Inst *inst, BasicBlock *bb)
|
||||||
|
{
|
||||||
|
for (auto &user : inst->GetUsers()) {
|
||||||
|
auto userInst = user.GetInst();
|
||||||
|
if (userInst->IsCheck()) {
|
||||||
|
if (!userInst->HasUsers()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!userInst->HasSingleUser()) {
|
||||||
|
// In case of multi user check-instruction we assume it is used outside current basic block without
|
||||||
|
// actually testing it.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// In case of single user check-instruction we test its the only user.
|
||||||
|
userInst = userInst->GetUsers().Front().GetInst();
|
||||||
|
}
|
||||||
|
if (userInst->GetBasicBlock() != bb) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveStateInst *FindFirstSaveState(BasicBlock *block)
|
||||||
|
{
|
||||||
|
if (block->IsEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto inst : block->Insts()) {
|
||||||
|
if (inst->GetOpcode() == Opcode::SaveState) {
|
||||||
|
return inst->CastToSaveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveFromInstructionInputs(ArenaVector<InputDesc> &inputDescriptors)
|
||||||
|
{
|
||||||
|
// Inputs must be walked in reverse order for removal
|
||||||
|
std::sort(inputDescriptors.begin(), inputDescriptors.end(),
|
||||||
|
[](auto inputDescX, auto inputDescY) { return inputDescX.second > inputDescY.second; });
|
||||||
|
|
||||||
|
for (auto inputDesc : inputDescriptors) {
|
||||||
|
auto inst = inputDesc.first;
|
||||||
|
auto index = inputDesc.second;
|
||||||
|
inst->RemoveInput(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakStringBuilderAppendChains(BasicBlock *block)
|
||||||
|
{
|
||||||
|
// StringBuilder append-call returns 'this' (instance)
|
||||||
|
// Replace all users of append-call with instance itself to support chain calls
|
||||||
|
// like: sb.append(s0).append(s1)...
|
||||||
|
bool isApplied = false;
|
||||||
|
for (auto inst : block->Insts()) {
|
||||||
|
if (!IsStringBuilderAppend(inst) && !IsStringBuilderToString(inst)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto instance = inst->GetDataFlowInput(0);
|
||||||
|
for (auto &user : instance->GetUsers()) {
|
||||||
|
auto userInst = SkipSingleUserCheckInstruction(user.GetInst());
|
||||||
|
if (IsStringBuilderAppend(userInst)) {
|
||||||
|
userInst->ReplaceUsers(instance);
|
||||||
|
isApplied = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isApplied;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ark::compiler
|
} // namespace ark::compiler
|
||||||
|
@ -44,6 +44,8 @@ bool HasUserPhiRecursively(Inst *inst, Marker visited, const FindUserPredicate &
|
|||||||
size_t CountUsers(Inst *inst, const FindUserPredicate &predicate);
|
size_t CountUsers(Inst *inst, const FindUserPredicate &predicate);
|
||||||
void ResetUserMarkersRecursively(Inst *inst, Marker visited);
|
void ResetUserMarkersRecursively(Inst *inst, Marker visited);
|
||||||
Inst *SkipSingleUserCheckInstruction(Inst *inst);
|
Inst *SkipSingleUserCheckInstruction(Inst *inst);
|
||||||
|
bool IsUsedOutsideBasicBlock(Inst *inst, BasicBlock *bb);
|
||||||
|
SaveStateInst *FindFirstSaveState(BasicBlock *block);
|
||||||
|
|
||||||
template <bool ALLOW_INLINED = false>
|
template <bool ALLOW_INLINED = false>
|
||||||
bool IsStringBuilderAppend(Inst *inst)
|
bool IsStringBuilderAppend(Inst *inst)
|
||||||
@ -62,6 +64,11 @@ bool IsStringBuilderAppend(Inst *inst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsIntrinsicStringBuilderAppendString(Inst *inst);
|
bool IsIntrinsicStringBuilderAppendString(Inst *inst);
|
||||||
|
|
||||||
|
using InputDesc = std::pair<Inst *, unsigned>;
|
||||||
|
void RemoveFromInstructionInputs(ArenaVector<InputDesc> &inputDescriptors);
|
||||||
|
bool BreakStringBuilderAppendChains(BasicBlock *block);
|
||||||
|
|
||||||
} // namespace ark::compiler
|
} // namespace ark::compiler
|
||||||
|
|
||||||
#endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_STRING_BUILDER_UTILS_H
|
#endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_STRING_BUILDER_UTILS_H
|
||||||
|
@ -181,6 +181,26 @@
|
|||||||
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",1
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",1
|
||||||
//! PASS_AFTER "Codegen"
|
//! PASS_AFTER "Codegen"
|
||||||
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//!
|
||||||
|
//! METHOD "ETSGLOBAL::unsupported_usage_chain_concat17"
|
||||||
|
//! PASS_BEFORE "BranchElimination"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//! PASS_AFTER "SimplifyStringBuilder"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//!
|
||||||
|
//! METHOD "ETSGLOBAL::unsupported_usage_chain_concat18"
|
||||||
|
//! PASS_BEFORE "BranchElimination"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//! PASS_AFTER "SimplifyStringBuilder"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
|
||||||
//! CHECKER JIT IR Builder, check String Builders merging
|
//! CHECKER JIT IR Builder, check String Builders merging
|
||||||
//! SKIP_IF @architecture == "arm32"
|
//! SKIP_IF @architecture == "arm32"
|
||||||
@ -349,6 +369,28 @@
|
|||||||
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",1
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",1
|
||||||
//! PASS_AFTER "Codegen"
|
//! PASS_AFTER "Codegen"
|
||||||
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//!
|
||||||
|
//! METHOD "ETSGLOBAL::unsupported_usage_chain_concat17"
|
||||||
|
//! PASS_BEFORE "BranchElimination"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//! PASS_AFTER "SimplifyStringBuilder"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//!
|
||||||
|
//! METHOD "ETSGLOBAL::unsupported_usage_chain_concat18"
|
||||||
|
//! PASS_BEFORE "BranchElimination"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",1
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",1
|
||||||
|
//! INST_COUNT "String::concat",1
|
||||||
|
//! PASS_AFTER "SimplifyStringBuilder"
|
||||||
|
//! INST_COUNT "StringBuilder::<ctor>",2
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbAppendString ",3
|
||||||
|
//! INST_COUNT "Intrinsic.StdCoreSbToString",2
|
||||||
|
//! INST_NOT "String::concat"
|
||||||
|
|
||||||
// Each '+=' operation on strings in tests below is translated into StringBuilder + 2 append calls by frontend
|
// Each '+=' operation on strings in tests below is translated into StringBuilder + 2 append calls by frontend
|
||||||
|
|
||||||
@ -628,6 +670,21 @@ function partially_supported_chain_concat16(a: String, b: String, c: String, d:
|
|||||||
return abcd; // applied, 3 SBs merged into 1 SB + concat2
|
return abcd; // applied, 3 SBs merged into 1 SB + concat2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unsupported_usage_chain_concat17(a: String): String {
|
||||||
|
let sb = new StringBuilder();
|
||||||
|
sb.append(a);
|
||||||
|
let result = sb.toString();
|
||||||
|
sb.append(a);
|
||||||
|
return result; // not applied, sb must not be used after sb.toString() appended
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsupported_usage_chain_concat18(a: String): String {
|
||||||
|
let sb = new StringBuilder();
|
||||||
|
sb.append(a);
|
||||||
|
let result = sb.toString();
|
||||||
|
sb.append(a);
|
||||||
|
return result.concat(); // not applied, sb must not be used after sb.toString() appended
|
||||||
|
}
|
||||||
|
|
||||||
//! CHECKER AOT IR Builder, check String Builders merging (chain_concat19 only, with --compiler-inlining=true)
|
//! CHECKER AOT IR Builder, check String Builders merging (chain_concat19 only, with --compiler-inlining=true)
|
||||||
//! SKIP_IF @architecture == "arm32"
|
//! SKIP_IF @architecture == "arm32"
|
||||||
@ -686,6 +743,8 @@ function main() {
|
|||||||
assert partially_supported_chain_concat14("ab", "c", "d", "e") == "abcde": "Wrong result at partially_supported_chain_concat14";
|
assert partially_supported_chain_concat14("ab", "c", "d", "e") == "abcde": "Wrong result at partially_supported_chain_concat14";
|
||||||
assert supported_chain_concat15("ab", "c", "d", "e") == "abcde": "Wrong result at supported_chain_concat15";
|
assert supported_chain_concat15("ab", "c", "d", "e") == "abcde": "Wrong result at supported_chain_concat15";
|
||||||
assert partially_supported_chain_concat16("ab", "c", "d", "e") == "abcde": "Wrong result at partially_supported_chain_concat16";
|
assert partially_supported_chain_concat16("ab", "c", "d", "e") == "abcde": "Wrong result at partially_supported_chain_concat16";
|
||||||
|
assert unsupported_usage_chain_concat17("abc") == "abc": "Wrong result at unsupported_usage_chain_concat17";
|
||||||
|
assert unsupported_usage_chain_concat18("abc") == "abc": "Wrong result at unsupported_usage_chain_concat18";
|
||||||
|
|
||||||
chain_concat19();
|
chain_concat19();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user