mirror of
https://gitee.com/openharmony/arkcompiler_runtime_core
synced 2024-11-23 14:50:51 +00:00
!2768 Remove NewArray/StoreArray
Merge pull request !2768 from Mikhail Ivanov/20372-remove-newarray-storearray-after-optimizestringconcat
This commit is contained in:
commit
6f7c20c102
@ -1216,8 +1216,8 @@ InlinedGraph Inlining::BuildGraph(InlineContext *ctx, CallInst *callInst, CallIn
|
||||
auto objectTypeApplied = graphInl->RunPass<ObjectTypeCheckElimination>();
|
||||
if (peepholeApplied || objectTypeApplied) {
|
||||
graphInl->RunPass<BranchElimination>();
|
||||
graphInl->RunPass<Cleanup>();
|
||||
}
|
||||
graphInl->RunPass<Cleanup>(false);
|
||||
graphInl->RunPass<OptimizeStringConcat>();
|
||||
graphInl->RunPass<SimplifyStringBuilder>();
|
||||
|
||||
|
@ -31,7 +31,10 @@
|
||||
|
||||
namespace ark::compiler {
|
||||
|
||||
OptimizeStringConcat::OptimizeStringConcat(Graph *graph) : Optimization(graph) {}
|
||||
OptimizeStringConcat::OptimizeStringConcat(Graph *graph)
|
||||
: Optimization(graph), arrayElements_ {graph->GetAllocator()->Adapter()}
|
||||
{
|
||||
}
|
||||
|
||||
RuntimeInterface::IdType GetStringBuilderClassId(Graph *graph)
|
||||
{
|
||||
@ -80,33 +83,6 @@ void OptimizeStringConcat::InvalidateAnalyses()
|
||||
GetGraph()->InvalidateAnalysis<AliasAnalysis>();
|
||||
}
|
||||
|
||||
Inst *GetPhiConstantInput(Inst *phi)
|
||||
{
|
||||
ASSERT(phi->GetInputsCount() == 2U); // NOLINT(readability-magic-numbers)
|
||||
ASSERT(phi->GetDataFlowInput(1)->IsPhi());
|
||||
auto inputInst0 = phi->GetDataFlowInput(0);
|
||||
if (inputInst0->IsConst()) {
|
||||
return inputInst0;
|
||||
}
|
||||
if (inputInst0->IsPhi()) {
|
||||
return GetPhiConstantInput(inputInst0);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Inst *GetArrayLength(Inst *newArray)
|
||||
{
|
||||
ASSERT(newArray->GetInputsCount() > 1);
|
||||
auto inputInst1 = newArray->GetDataFlowInput(1);
|
||||
if (inputInst1->IsConst()) {
|
||||
return inputInst1;
|
||||
}
|
||||
if (inputInst1->IsPhi()) {
|
||||
return GetPhiConstantInput(inputInst1);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Inst *CreateInstructionStringBuilderInstance(Graph *graph, uint32_t pc, SaveStateInst *saveState)
|
||||
{
|
||||
auto runtime = graph->GetRuntime();
|
||||
@ -188,11 +164,8 @@ void OptimizeStringConcat::FixBrokenSaveStates(Inst *source, Inst *target)
|
||||
}
|
||||
}
|
||||
|
||||
void OptimizeStringConcat::CreateAppendArgsIntrinsics(Inst *instance, Inst *args, uint64_t arrayLengthValue,
|
||||
SaveStateInst *saveState)
|
||||
void OptimizeStringConcat::CreateAppendArgsIntrinsic(Inst *instance, Inst *arg, SaveStateInst *saveState)
|
||||
{
|
||||
for (uint64_t index = 0; index < arrayLengthValue; ++index) {
|
||||
auto arg = CreateLoadArray(GetGraph(), args, index);
|
||||
auto appendIntrinsic = CreateStringBuilderAppendStringIntrinsic(GetGraph(), instance, arg, saveState);
|
||||
InsertBeforeWithInputs(appendIntrinsic, saveState);
|
||||
|
||||
@ -202,6 +175,21 @@ void OptimizeStringConcat::CreateAppendArgsIntrinsics(Inst *instance, Inst *args
|
||||
COMPILER_LOG(DEBUG, OPTIMIZE_STRING_CONCAT)
|
||||
<< "Insert StringBuilder.append intrinsic (id=" << appendIntrinsic->GetId() << ")";
|
||||
}
|
||||
|
||||
void OptimizeStringConcat::CreateAppendArgsIntrinsics(Inst *instance, SaveStateInst *saveState)
|
||||
{
|
||||
for (auto arg : arrayElements_) {
|
||||
CreateAppendArgsIntrinsic(instance, arg, saveState);
|
||||
}
|
||||
}
|
||||
|
||||
void OptimizeStringConcat::CreateAppendArgsIntrinsics(Inst *instance, Inst *args, uint64_t arrayLengthValue,
|
||||
SaveStateInst *saveState)
|
||||
{
|
||||
for (uint64_t index = 0; index < arrayLengthValue; ++index) {
|
||||
auto arg = CreateLoadArray(GetGraph(), args, index);
|
||||
CreateAppendArgsIntrinsic(instance, arg, saveState);
|
||||
}
|
||||
}
|
||||
|
||||
Inst *CreateSafePoint(Graph *graph, uint32_t pc, SaveStateInst *saveState)
|
||||
@ -278,6 +266,24 @@ BasicBlock *OptimizeStringConcat::CreateAppendArgsLoop(Inst *instance, Inst *str
|
||||
return postExit;
|
||||
}
|
||||
|
||||
bool OptimizeStringConcat::HasStoreArrayUsersOnly(Inst *newArray, Inst *removable)
|
||||
{
|
||||
ASSERT(newArray->GetOpcode() == Opcode::NewArray);
|
||||
|
||||
MarkerHolder visited {newArray->GetBasicBlock()->GetGraph()};
|
||||
bool found = HasUserRecursively(newArray, visited.GetMarker(), [newArray, removable](auto &user) {
|
||||
auto userInst = user.GetInst();
|
||||
auto isSaveState = userInst->IsSaveState();
|
||||
auto isCheck = userInst->IsCheck();
|
||||
auto isStoreArray = userInst->GetOpcode() == Opcode::StoreArray && userInst->GetDataFlowInput(0) == newArray;
|
||||
bool isRemovable = userInst == removable;
|
||||
return !isSaveState && !isCheck && !isStoreArray && !isRemovable;
|
||||
});
|
||||
|
||||
ResetUserMarkersRecursively(newArray, visited.GetMarker());
|
||||
return !found;
|
||||
}
|
||||
|
||||
void OptimizeStringConcat::ReplaceStringConcatWithStringBuilderAppend(Inst *concatCall)
|
||||
{
|
||||
// Input:
|
||||
@ -311,16 +317,17 @@ void OptimizeStringConcat::ReplaceStringConcatWithStringBuilderAppend(Inst *conc
|
||||
|
||||
auto toStringCall = CreateStringBuilderToStringIntrinsic(GetGraph(), instance, concatCall->GetSaveState());
|
||||
|
||||
if (args->GetOpcode() == Opcode::NewArray) {
|
||||
auto arrayLength = GetArrayLength(args);
|
||||
auto arrayLength = GetArrayLengthConstant(args);
|
||||
bool collected = args->GetOpcode() == Opcode::NewArray && CollectArrayElements(args, arrayElements_);
|
||||
if (collected) {
|
||||
CreateAppendArgsIntrinsics(instance, concatCall->GetSaveState());
|
||||
InsertBeforeWithSaveState(toStringCall, concatCall->GetSaveState());
|
||||
} else if (args->GetOpcode() == Opcode::NewArray && arrayLength != nullptr) {
|
||||
CreateAppendArgsIntrinsics(instance, args, arrayLength->CastToConstant()->GetIntValue(),
|
||||
concatCall->GetSaveState());
|
||||
InsertBeforeWithSaveState(toStringCall, concatCall->GetSaveState());
|
||||
|
||||
COMPILER_LOG(DEBUG, OPTIMIZE_STRING_CONCAT) << "Replace String.concat call (id=" << concatCall->GetId()
|
||||
<< ") with StringBuilder instance (id=" << instance->GetId() << ")";
|
||||
} else {
|
||||
auto arrayLength = CreateLenArray(GetGraph(), args);
|
||||
arrayLength = CreateLenArray(GetGraph(), args);
|
||||
concatCall->GetSaveState()->InsertBefore(arrayLength);
|
||||
auto postExit = CreateAppendArgsLoop(instance, str, args, arrayLength->CastToLenArray(), concatCall);
|
||||
|
||||
@ -329,10 +336,9 @@ void OptimizeStringConcat::ReplaceStringConcatWithStringBuilderAppend(Inst *conc
|
||||
|
||||
InvalidateBlocksOrderAnalyzes(GetGraph());
|
||||
GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
|
||||
|
||||
}
|
||||
COMPILER_LOG(DEBUG, OPTIMIZE_STRING_CONCAT) << "Replace String.concat call (id=" << concatCall->GetId()
|
||||
<< ") with StringBuilder instance (id=" << instance->GetId() << ")";
|
||||
}
|
||||
|
||||
FixBrokenSaveStates(instance, toStringCall);
|
||||
|
||||
@ -342,6 +348,11 @@ void OptimizeStringConcat::ReplaceStringConcatWithStringBuilderAppend(Inst *conc
|
||||
if (concatCall->GetInput(0).GetInst()->IsCheck()) {
|
||||
concatCall->GetInput(0).GetInst()->ClearFlag(inst_flags::NO_DCE);
|
||||
}
|
||||
|
||||
if (collected && HasStoreArrayUsersOnly(args, concatCall)) {
|
||||
CleanupStoreArrayInstructions(args);
|
||||
args->ClearFlag(inst_flags::NO_DCE);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ark::compiler
|
||||
|
@ -49,14 +49,18 @@ public:
|
||||
void InvalidateAnalyses() override;
|
||||
|
||||
private:
|
||||
void CreateAppendArgsIntrinsic(Inst *instance, Inst *arg, SaveStateInst *saveState);
|
||||
void CreateAppendArgsIntrinsics(Inst *instance, SaveStateInst *saveState);
|
||||
void CreateAppendArgsIntrinsics(Inst *instance, Inst *args, uint64_t arrayLengthValue, SaveStateInst *saveState);
|
||||
BasicBlock *CreateAppendArgsLoop(Inst *instance, Inst *str, Inst *args, LengthMethodInst *arrayLength,
|
||||
Inst *concatCall);
|
||||
void FixBrokenSaveStates(Inst *source, Inst *target);
|
||||
bool HasStoreArrayUsersOnly(Inst *newArray, Inst *removable);
|
||||
void ReplaceStringConcatWithStringBuilderAppend(Inst *concatCall);
|
||||
|
||||
private:
|
||||
SaveStateBridgesBuilder ssb_ {};
|
||||
InstVector arrayElements_;
|
||||
};
|
||||
|
||||
} // namespace ark::compiler
|
||||
|
@ -223,12 +223,8 @@ IntrinsicInst *SimplifyStringBuilder::CreateConcatIntrinsic(
|
||||
return concatIntrinsic;
|
||||
}
|
||||
|
||||
bool SimplifyStringBuilder::MatchConcatenation(InstIter &begin, const InstIter &end, ConcatenationMatch &match)
|
||||
bool CheckUnsupportedCases(Inst *instance)
|
||||
{
|
||||
// Walk instruction range [begin, end) and fill the match structure with StringBuilder usage instructions found
|
||||
|
||||
auto instance = match.instance;
|
||||
|
||||
if (IsUsedOutsideBasicBlock(instance, instance->GetBasicBlock())) {
|
||||
return false; // Unsupported case: doesn't look like concatenation pattern
|
||||
}
|
||||
@ -244,10 +240,21 @@ bool SimplifyStringBuilder::MatchConcatenation(InstIter &begin, const InstIter &
|
||||
if (appendCount != appendStringCount) {
|
||||
return false; // Unsupported case: arguments must be strings
|
||||
}
|
||||
if (appendCount <= 1 || appendCount > match.appendIntrinsics.size()) {
|
||||
if (appendCount <= 1 || appendCount > ARGS_NUM_4) {
|
||||
return false; // Unsupported case: too many strings concatenated
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimplifyStringBuilder::MatchConcatenation(InstIter &begin, const InstIter &end, ConcatenationMatch &match)
|
||||
{
|
||||
auto instance = match.instance;
|
||||
if (!CheckUnsupportedCases(instance)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk instruction range [begin, end) and fill the match structure with StringBuilder usage instructions found
|
||||
for (; begin != end; ++begin) {
|
||||
if ((*begin)->IsSaveState()) {
|
||||
continue;
|
||||
|
@ -222,6 +222,33 @@ bool HasUserPhiRecursively(Inst *inst, Marker visited, const FindUserPredicate &
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasUserRecursively(Inst *inst, Marker visited, const FindUserPredicate &predicate)
|
||||
{
|
||||
// Check if instruction is used in a context defined by predicate
|
||||
// All Check-instruction users are checked recursively
|
||||
|
||||
if (HasUser(inst, predicate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
inst->SetMarker(visited);
|
||||
|
||||
for (auto &user : inst->GetUsers()) {
|
||||
auto userInst = user.GetInst();
|
||||
if (!userInst->IsCheck()) {
|
||||
continue;
|
||||
}
|
||||
if (userInst->IsMarked(visited)) {
|
||||
continue;
|
||||
}
|
||||
if (HasUserRecursively(userInst, visited, predicate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t CountUsers(Inst *inst, const FindUserPredicate &predicate)
|
||||
{
|
||||
size_t count = 0;
|
||||
@ -347,4 +374,141 @@ bool BreakStringBuilderAppendChains(BasicBlock *block)
|
||||
return isApplied;
|
||||
}
|
||||
|
||||
Inst *GetStoreArrayIndexConstant(Inst *storeArray)
|
||||
{
|
||||
ASSERT(storeArray->GetOpcode() == Opcode::StoreArray);
|
||||
ASSERT(storeArray->GetInputsCount() > 1);
|
||||
|
||||
auto inputInst1 = storeArray->GetDataFlowInput(1U);
|
||||
if (inputInst1->IsConst()) {
|
||||
return inputInst1;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FillArrayElement(Inst *inst, InstVector &arrayElements)
|
||||
{
|
||||
if (inst->GetOpcode() == Opcode::StoreArray) {
|
||||
auto indexInst = GetStoreArrayIndexConstant(inst);
|
||||
if (indexInst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT(indexInst->IsConst());
|
||||
auto indexValue = indexInst->CastToConstant()->GetIntValue();
|
||||
if (arrayElements[indexValue] != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto element = inst->GetDataFlowInput(2U);
|
||||
arrayElements[indexValue] = element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FillArrayElements(Inst *inst, InstVector &arrayElements)
|
||||
{
|
||||
for (auto &user : inst->GetUsers()) {
|
||||
auto userInst = user.GetInst();
|
||||
if (!FillArrayElement(userInst, arrayElements)) {
|
||||
return false;
|
||||
}
|
||||
if (userInst->GetOpcode() == Opcode::NullCheck) {
|
||||
if (!FillArrayElements(userInst, arrayElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Inst *GetArrayLengthConstant(Inst *newArray)
|
||||
{
|
||||
if (newArray->GetOpcode() != Opcode::NewArray) {
|
||||
return nullptr;
|
||||
}
|
||||
ASSERT(newArray->GetInputsCount() > 1);
|
||||
|
||||
auto inputInst1 = newArray->GetDataFlowInput(1U);
|
||||
if (inputInst1->IsConst()) {
|
||||
return inputInst1;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CollectArrayElements(Inst *newArray, InstVector &arrayElements)
|
||||
{
|
||||
/*
|
||||
Collect instructions stored to a given array
|
||||
|
||||
This functions used to find all the arguments of the calls like:
|
||||
str.concat(a, b, c)
|
||||
IR builder generates the following IR for it:
|
||||
|
||||
bb_start:
|
||||
v0 Constant 0x0
|
||||
v1 Constant 0x1
|
||||
v2 Constant 0x2
|
||||
v3 Constant 0x3
|
||||
bb1:
|
||||
v9 NewArray class, v3, save_state
|
||||
v10 StoreArray v9, v0, a
|
||||
v11 StoreArray v9, v1, b
|
||||
v12 StoreArray v9, v2, c
|
||||
v20 CallStatic String::concat str, v9, save_state
|
||||
|
||||
Conditions:
|
||||
- array size is constant (3 in the sample code above)
|
||||
- every StoreArray instruction stores value by constant index (0, 1 and 2 in the sample code above)
|
||||
- every element stored only once
|
||||
- array filled completely
|
||||
|
||||
If any of the above is false, this functions returns false and clears array.
|
||||
If all the above conditions true, this function returns true and fills array.
|
||||
*/
|
||||
|
||||
ASSERT(newArray->GetOpcode() == Opcode::NewArray);
|
||||
arrayElements.clear();
|
||||
|
||||
auto lengthInst = GetArrayLengthConstant(newArray);
|
||||
if (lengthInst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ASSERT(lengthInst->IsConst());
|
||||
|
||||
auto length = lengthInst->CastToConstant()->GetIntValue();
|
||||
arrayElements.resize(length);
|
||||
|
||||
if (!FillArrayElements(newArray, arrayElements)) {
|
||||
arrayElements.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if array is filled completely
|
||||
auto foundNull =
|
||||
std::find_if(arrayElements.begin(), arrayElements.end(), [](auto &element) { return element == nullptr; });
|
||||
if (foundNull != arrayElements.end()) {
|
||||
arrayElements.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CleanupStoreArrayInstructions(Inst *inst)
|
||||
{
|
||||
for (auto &user : inst->GetUsers()) {
|
||||
auto userInst = user.GetInst();
|
||||
if (userInst->GetOpcode() == Opcode::StoreArray) {
|
||||
userInst->ClearFlag(inst_flags::NO_DCE);
|
||||
}
|
||||
if (userInst->IsCheck()) {
|
||||
userInst->ClearFlag(inst_flags::NO_DCE);
|
||||
CleanupStoreArrayInstructions(userInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ark::compiler
|
||||
|
@ -41,6 +41,7 @@ void ResetInputMarkersRecursively(Inst *inst, Marker visited);
|
||||
using FindUserPredicate = std::function<bool(User &user)>;
|
||||
bool HasUser(Inst *inst, const FindUserPredicate &predicate);
|
||||
bool HasUserPhiRecursively(Inst *inst, Marker visited, const FindUserPredicate &predicate);
|
||||
bool HasUserRecursively(Inst *inst, Marker visited, const FindUserPredicate &predicate);
|
||||
size_t CountUsers(Inst *inst, const FindUserPredicate &predicate);
|
||||
void ResetUserMarkersRecursively(Inst *inst, Marker visited);
|
||||
Inst *SkipSingleUserCheckInstruction(Inst *inst);
|
||||
@ -69,6 +70,9 @@ using InputDesc = std::pair<Inst *, unsigned>;
|
||||
void RemoveFromInstructionInputs(ArenaVector<InputDesc> &inputDescriptors);
|
||||
bool BreakStringBuilderAppendChains(BasicBlock *block);
|
||||
|
||||
Inst *GetArrayLengthConstant(Inst *newArray);
|
||||
bool CollectArrayElements(Inst *newArray, InstVector &arrayElements);
|
||||
void CleanupStoreArrayInstructions(Inst *inst);
|
||||
} // namespace ark::compiler
|
||||
|
||||
#endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_STRING_BUILDER_UTILS_H
|
||||
|
@ -173,6 +173,7 @@ bool Pipeline::RunOptimizations()
|
||||
graph->SetUnrollComplete();
|
||||
}
|
||||
graph->RunPass<Peepholes>();
|
||||
graph->RunPass<Cleanup>(false);
|
||||
graph->RunPass<BranchElimination>();
|
||||
graph->RunPass<OptimizeStringConcat>();
|
||||
graph->RunPass<SimplifyStringBuilder>();
|
||||
|
@ -42,10 +42,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,2
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 1
|
||||
//! PASS_AFTER "Escape"
|
||||
@ -56,10 +58,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,3
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 1
|
||||
//! PASS_AFTER "Escape"
|
||||
@ -70,10 +74,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,4
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 1
|
||||
//! PASS_AFTER "Escape"
|
||||
@ -84,10 +90,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,5
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 2
|
||||
//! PASS_AFTER "Escape"
|
||||
@ -98,10 +106,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,6
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 2
|
||||
//! PASS_AFTER "Escape"
|
||||
@ -112,10 +122,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,4
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 1
|
||||
//! PASS_AFTER "Escape"
|
||||
@ -126,10 +138,12 @@
|
||||
//! INST /String::concat/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbAppendString/
|
||||
//! INST_NOT /Intrinsic.StdCoreSbToString/
|
||||
//! INST "StoreArray"
|
||||
//! PASS_AFTER "OptimizeStringConcat"
|
||||
//! INST_NOT /String::concat/
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbAppendString/,6
|
||||
//! INST_COUNT /Intrinsic.StdCoreSbToString/,1
|
||||
//! INST_NOT "StoreArray"
|
||||
//! PASS_BEFORE "Escape"
|
||||
//! INST_COUNT "NewArray", 2
|
||||
//! PASS_AFTER "Escape"
|
||||
|
Loading…
Reference in New Issue
Block a user