• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "optimizer/optimizations/simplify_string_builder.h"
17 
18 #include "compiler_logger.h"
19 
20 #include "optimizer/analysis/alias_analysis.h"
21 #include "optimizer/analysis/bounds_analysis.h"
22 
23 namespace panda::compiler {
24 /**
25  * Replaces
26  *      let s = new StringBuilder(str).toString();
27  * or
28  *      let sb = new StringBuilder(str);
29  *      let s = sb.toString();
30  * with
31  *      let s = str;
32  * Skips toString calls dominated by other usages of StringBuilder instance
33  * Removes StringBuilder instance construction if it was used for toString calls only
34  */
SimplifyStringBuilder(Graph * graph)35 SimplifyStringBuilder::SimplifyStringBuilder(Graph *graph) : Optimization(graph) {}
36 
RunImpl()37 bool SimplifyStringBuilder::RunImpl()
38 {
39     isApplied_ = false;
40     for (auto block : GetGraph()->GetBlocksRPO()) {
41         if (block->IsEmpty()) {
42             continue;
43         }
44         VisitBlock(block);
45     }
46     COMPILER_LOG(DEBUG, SIMPLIFY_SB) << "Simplify StringBuilder complete";
47     return isApplied_;
48 }
49 
InvalidateAnalyses()50 void SimplifyStringBuilder::InvalidateAnalyses()
51 {
52     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
53     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
54 }
55 
IsMethodStringBuilderConstructorWithStringArg(Inst * inst)56 bool SimplifyStringBuilder::IsMethodStringBuilderConstructorWithStringArg(Inst *inst)
57 {
58     if (inst->GetOpcode() != Opcode::CallStatic) {
59         return false;
60     }
61 
62     auto call = inst->CastToCallStatic();
63     if (call->IsInlined()) {
64         return false;
65     }
66 
67     auto runtime = GetGraph()->GetRuntime();
68     return runtime->IsMethodStringBuilderConstructorWithStringArg(call->GetCallMethod());
69 }
70 
IsMethodStringBuilderToString(Inst * inst)71 bool SimplifyStringBuilder::IsMethodStringBuilderToString(Inst *inst)
72 {
73     if (inst->GetOpcode() == Opcode::CallVirtual) {
74         auto call = inst->CastToCallVirtual();
75         if (call->IsInlined()) {
76             return false;
77         }
78         return GetGraph()->GetRuntime()->IsMethodStringBuilderToString(call->GetCallMethod());
79     }
80     if (inst->IsIntrinsic()) {
81         auto intrinsic = inst->CastToIntrinsic();
82         return GetGraph()->GetRuntime()->IsIntrinsicStringBuilderToString(intrinsic->GetIntrinsicId());
83     }
84     return false;
85 }
86 
SkipToStringBuilderConstructor(InstIter begin,InstIter end)87 InstIter SimplifyStringBuilder::SkipToStringBuilderConstructor(InstIter begin, InstIter end)
88 {
89     return std::find_if(std::move(begin), std::move(end),
90                         [this](auto inst) { return IsMethodStringBuilderConstructorWithStringArg(inst); });
91 }
92 
IsDataFlowInput(Inst * inst,Inst * input)93 bool IsDataFlowInput(Inst *inst, Inst *input)
94 {
95     for (size_t i = 0; i < inst->GetInputsCount(); ++i) {
96         if (inst->GetDataFlowInput(i) == input) {
97             return true;
98         }
99     }
100     return false;
101 }
102 
IsUsedOutsideBasicBlock(Inst * inst,BasicBlock * bb)103 bool IsUsedOutsideBasicBlock(Inst *inst, BasicBlock *bb)
104 {
105     for (auto &user : inst->GetUsers()) {
106         auto userInst = user.GetInst();
107         if (userInst->IsCheck()) {
108             if (!userInst->HasSingleUser()) {
109                 // In case of multi user check-instruction we assume it is used outside current basic block without
110                 // actually testing it.
111                 return true;
112             }
113             // In case of signle user check-instruction we test its the only user.
114             userInst = userInst->GetUsers().Front().GetInst();
115         }
116         if (userInst->GetBasicBlock() != bb) {
117             return true;
118         }
119     }
120     return false;
121 }
122 
VisitBlock(BasicBlock * block)123 void SimplifyStringBuilder::VisitBlock(BasicBlock *block)
124 {
125     ASSERT(block != nullptr);
126     ASSERT(block->GetGraph() == GetGraph());
127 
128     // Walk through a basic block, find every StringBuilder instance and constructor call,
129     // and check it we can remove/replace them
130     InstIter inst = block->Insts().begin();
131     while ((inst = SkipToStringBuilderConstructor(inst, block->Insts().end())) != block->Insts().end()) {
132         ASSERT((*inst)->IsStaticCall());
133         auto ctorCall = (*inst)->CastToCallStatic();
134 
135         // void StringBuilder::<ctor> instance, arg, save_state
136         ASSERT(ctorCall->GetInputsCount() == CONSTRUCTOR_WITH_STRING_ARG_TOTAL_ARGS_NUM);
137         auto instance = ctorCall->GetInput(0).GetInst();
138         auto arg = ctorCall->GetInput(1).GetInst();
139 
140         // Look for StringBuilder usages within current basic block
141         auto nextInst = block->Insts().end();
142         bool removeCtor = true;
143         for (++inst; inst != block->Insts().end(); ++inst) {
144             // Skip SaveState instructions
145             if ((*inst)->IsSaveState()) {
146                 continue;
147             }
148 
149             // Skip check instructions, like NullCheck, RefTypeCheck, etc.
150             if ((*inst)->IsCheck()) {
151                 continue;
152             }
153 
154             // Continue (outer loop) with the next StringBuilder constructor,
155             // in case we met one in inner loop
156             if (IsMethodStringBuilderConstructorWithStringArg(*inst)) {
157                 nextInst = nextInst != block->Insts().end() ? nextInst : inst;
158             }
159 
160             if (!IsDataFlowInput(*inst, instance)) {
161                 continue;
162             }
163 
164             // Process usages of StringBuilder instance:
165             // replace toString()-calls until we met something else
166             if (IsMethodStringBuilderToString(*inst)) {
167                 (*inst)->ReplaceUsers(arg);
168                 (*inst)->ClearFlag(compiler::inst_flags::NO_DCE);
169                 COMPILER_LOG(DEBUG, SIMPLIFY_SB)
170                     << "Remove StringBuilder toString()-call (id=" << (*inst)->GetId() << ")";
171                 isApplied_ = true;
172             } else {
173                 removeCtor = false;
174                 break;
175             }
176         }
177 
178         // Remove StringBuilder constructor unless it has usages
179         if (removeCtor && !IsUsedOutsideBasicBlock(instance, instance->GetBasicBlock())) {
180             ctorCall->ClearFlag(compiler::inst_flags::NO_DCE);
181             COMPILER_LOG(DEBUG, SIMPLIFY_SB) << "Remove StringBuilder constructor (id=" << ctorCall->GetId() << ")";
182             isApplied_ = true;
183         }
184 
185         // Proceed to the next StringBuilder constructor
186         inst = nextInst != block->Insts().end() ? nextInst : inst;
187     }
188 }
189 
190 }  // namespace panda::compiler
191