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