1 /**
2 * Copyright (c) 2024 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 "graph_analyzer.h"
17
18 #include "compiler/optimizer/ir/basicblock.h"
19
20 #include "function.h"
21 #include "util/assert_util.h"
22
23 using namespace panda::guard;
24
25 namespace {
26 constexpr std::string_view TAG = "[Graph]";
27 constexpr std::string_view UI_COMPONENT_BASE_CLASS_VIEW_PU = "ViewPU";
28 constexpr std::string_view UI_COMPONENT_BASE_CLASS_VIEW_V2 = "ViewV2";
29
30 using IntrinsicId = panda::compiler::RuntimeInterface::IntrinsicId;
31 using InstIdFilterList = std::vector<IntrinsicId>;
32 struct InstParam {
33 public:
34 unsigned index; // Target instruction: Index in the input instruction register list
35 InstIdFilterList filterList; // Input instruction: Type list
36 };
37
38 const InstIdFilterList INST_ID_LIST_DYNAMICIMPORT = {
39 IntrinsicId::DYNAMICIMPORT,
40 };
41 const InstIdFilterList INST_ID_LIST_STLEXVAR = {IntrinsicId::STLEXVAR_IMM4_IMM4, IntrinsicId::STLEXVAR_IMM8_IMM8};
42 const InstIdFilterList INST_ID_LIST_LDOBJBYVALUE = {
43 IntrinsicId::LDOBJBYVALUE_IMM8_V8,
44 IntrinsicId::LDOBJBYVALUE_IMM16_V8,
45 };
46 const InstIdFilterList INST_ID_LIST_STOBJBYVALUE = {
47 IntrinsicId::STOBJBYVALUE_IMM8_V8_V8,
48 IntrinsicId::STOBJBYVALUE_IMM16_V8_V8,
49 };
50 const InstIdFilterList INST_ID_LIST_DEFINEGETTERSETTERBYVALUE = {
51 IntrinsicId::DEFINEGETTERSETTERBYVALUE_V8_V8_V8_V8,
52 };
53 const InstIdFilterList INST_ID_LIST_ISIN = {
54 IntrinsicId::ISIN_IMM8_V8,
55 };
56 const InstIdFilterList INST_ID_LIST_LDSUPERBYVALUE = {
57 IntrinsicId::LDSUPERBYVALUE_IMM16_V8,
58 IntrinsicId::LDSUPERBYVALUE_IMM8_V8,
59 };
60 const InstIdFilterList INST_ID_LIST_STSUPERBYVALUE = {
61 IntrinsicId::STSUPERBYVALUE_IMM16_V8_V8,
62 IntrinsicId::STSUPERBYVALUE_IMM8_V8_V8,
63 };
64 const InstIdFilterList INST_ID_LIST_STOWNBYNAME = {
65 IntrinsicId::STOWNBYNAME_IMM16_ID16_V8,
66 IntrinsicId::STOWNBYNAME_IMM8_ID16_V8,
67 };
68 const InstIdFilterList INST_ID_LIST_STOWNBYNAMEWITHNAMESET = {
69 IntrinsicId::STOWNBYNAMEWITHNAMESET_IMM16_ID16_V8,
70 IntrinsicId::STOWNBYNAMEWITHNAMESET_IMM8_ID16_V8,
71 };
72 const InstIdFilterList INST_ID_LIST_STOWNBYVALUE = {
73 IntrinsicId::STOWNBYVALUE_IMM16_V8_V8,
74 IntrinsicId::STOWNBYVALUE_IMM8_V8_V8,
75 };
76 const InstIdFilterList INST_ID_LIST_STOWNBYVALUEWITHNAMESET = {
77 IntrinsicId::STOWNBYVALUEWITHNAMESET_IMM16_V8_V8,
78 IntrinsicId::STOWNBYVALUEWITHNAMESET_IMM8_V8_V8,
79 };
80 const InstIdFilterList INST_ID_LIST_DEFINEMETHOD = {
81 IntrinsicId::DEFINEMETHOD_IMM16_ID16_IMM8,
82 IntrinsicId::DEFINEMETHOD_IMM8_ID16_IMM8,
83 };
84 const InstIdFilterList INST_ID_LIST_CREATEOBJECTWITHBUFFER = {
85 IntrinsicId::CREATEOBJECTWITHBUFFER_IMM16_ID16,
86 IntrinsicId::CREATEOBJECTWITHBUFFER_IMM8_ID16,
87 };
88 const InstIdFilterList INST_ID_LIST_DEFINECLASSWITHBUFFER = {
89 IntrinsicId::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8,
90 IntrinsicId::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8,
91 };
92 const InstIdFilterList INST_ID_LIST_LDOBJBYNAME = {
93 IntrinsicId::LDOBJBYNAME_IMM16_ID16,
94 IntrinsicId::LDOBJBYNAME_IMM8_ID16,
95 };
96 const InstIdFilterList INST_ID_LIST_CALLRUNTIME_TOPROPERTYKEY = {
97 IntrinsicId::CALLRUNTIME_TOPROPERTYKEY_PREF_NONE,
98 };
99 const InstIdFilterList INST_ID_LIST_TRYLDGLOBALBYNAME = {
100 IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16,
101 IntrinsicId ::TRYLDGLOBALBYNAME_IMM16_ID16,
102 };
103 const InstIdFilterList INST_ID_LIST_DEFINEPROPERTYBYNAME = {
104 IntrinsicId::DEFINEPROPERTYBYNAME_IMM8_ID16_V8,
105 };
106 const InstIdFilterList INST_ID_LIST_CALLRUNTIME_DEFINESENDABLECLASS = {
107 IntrinsicId::CALLRUNTIME_DEFINESENDABLECLASS_PREF_IMM16_ID16_ID16_IMM16_V8,
108 };
109
110 const std::map<panda::pandasm::Opcode, InstParam> LDA_STA_INST_PARAM_MAP = {
111 {panda::pandasm::Opcode::DYNAMICIMPORT, {0, INST_ID_LIST_DYNAMICIMPORT}},
112 {panda::pandasm::Opcode::STLEXVAR, {0, INST_ID_LIST_STLEXVAR}},
113 {panda::pandasm::Opcode::LDOBJBYVALUE, {1, INST_ID_LIST_LDOBJBYVALUE}},
114 {panda::pandasm::Opcode::STOBJBYVALUE, {1, INST_ID_LIST_STOBJBYVALUE}},
115 {panda::pandasm::Opcode::DEFINEGETTERSETTERBYVALUE, {1, INST_ID_LIST_DEFINEGETTERSETTERBYVALUE}},
116 {panda::pandasm::Opcode::ISIN, {0, INST_ID_LIST_ISIN}},
117 {panda::pandasm::Opcode::LDSUPERBYVALUE, {1, INST_ID_LIST_LDSUPERBYVALUE}},
118 {panda::pandasm::Opcode::STSUPERBYVALUE, {1, INST_ID_LIST_STSUPERBYVALUE}},
119 {panda::pandasm::Opcode::STOWNBYVALUE, {1, INST_ID_LIST_STOWNBYVALUE}},
120 {panda::pandasm::Opcode::STOWNBYVALUEWITHNAMESET, {1, INST_ID_LIST_STOWNBYVALUEWITHNAMESET}}};
121
122 const std::vector<InstIdFilterList> DEFINE_INST_PASS_THROUGH_LIST = {
123 INST_ID_LIST_DEFINEGETTERSETTERBYVALUE,
124 INST_ID_LIST_DEFINEMETHOD,
125 };
126 const std::vector<InstIdFilterList> DEFINE_INST_ID_LIST = {
127 INST_ID_LIST_DEFINECLASSWITHBUFFER,
128 INST_ID_LIST_CREATEOBJECTWITHBUFFER,
129 INST_ID_LIST_LDOBJBYNAME,
130 INST_ID_LIST_CALLRUNTIME_DEFINESENDABLECLASS,
131 };
132 const std::vector<InstIdFilterList> METHOD_NAME_INST_ID_LIST = {
133 INST_ID_LIST_DEFINEGETTERSETTERBYVALUE, INST_ID_LIST_STOWNBYNAME,
134 INST_ID_LIST_STOWNBYNAMEWITHNAMESET, INST_ID_LIST_STOWNBYVALUE,
135 INST_ID_LIST_STOWNBYVALUEWITHNAMESET,
136 };
137
138 /**
139 * Is the instruction type in the list
140 */
IsInstIdMatched(const panda::compiler::Inst * inst,const InstIdFilterList & list)141 bool IsInstIdMatched(const panda::compiler::Inst *inst, const InstIdFilterList &list)
142 {
143 if (!inst->IsIntrinsic()) {
144 return false;
145 }
146
147 IntrinsicId instId = inst->CastToIntrinsic()->GetIntrinsicId();
148 return std::any_of(list.begin(), list.end(), [&instId](IntrinsicId filter) -> bool { return instId == filter; });
149 }
150
IsInstIdMatched(const panda::compiler::Inst * inst,const std::vector<InstIdFilterList> & list)151 bool IsInstIdMatched(const panda::compiler::Inst *inst, const std::vector<InstIdFilterList> &list)
152 {
153 return std::any_of(list.begin(), list.end(),
154 [&](const InstIdFilterList &filter) -> bool { return IsInstIdMatched(inst, filter); });
155 }
156
157 /**
158 * Does the current instruction match the requirement, Consistent with InstructionInfo
159 * @param inst Graph instruction to be checked
160 * @param list The instruction type that should be matched
161 * @param targetIns The InstructionInfo that should be matched
162 */
IsInstMatched(const panda::compiler::Inst * inst,const InstructionInfo & targetIns,const InstIdFilterList & list)163 bool IsInstMatched(const panda::compiler::Inst *inst, const InstructionInfo &targetIns, const InstIdFilterList &list)
164 {
165 if (!IsInstIdMatched(inst, list)) {
166 return false;
167 }
168
169 uint32_t pcVal = inst->GetPc();
170 auto &pcInstMap = targetIns.function_->pcInstMap_;
171 if (pcInstMap.find(pcVal) == pcInstMap.end() || pcInstMap.at(pcVal) != targetIns.index_) {
172 return false;
173 }
174
175 return true;
176 }
177
178 /**
179 * Traverse the Graph to find matching instructions, Converting InstructionInfo to Graph Instruction
180 * @param inIns Instructions to be found
181 * @param list instruction type
182 * @param outInst Corresponding instructions in the graph
183 */
VisitGraph(const InstructionInfo & inIns,const InstIdFilterList & list,const panda::compiler::Inst * & outInst)184 void VisitGraph(const InstructionInfo &inIns, const InstIdFilterList &list, const panda::compiler::Inst *&outInst)
185 {
186 Function *func = inIns.function_;
187 panda::compiler::Graph *graph;
188 func->GetGraph(graph);
189
190 for (const panda::compiler::BasicBlock *bb : graph->GetBlocksLinearOrder()) {
191 for (const panda::compiler::Inst *inst : bb->AllInsts()) {
192 if (!IsInstMatched(inst, inIns, list)) {
193 continue;
194 }
195 outInst = inst;
196 return;
197 }
198 }
199
200 PANDA_GUARD_ABORT_PRINT(TAG, ErrorCode::GENERIC_ERROR,
201 "not find inst: " << inIns.index_ << " in " << inIns.function_->idx_);
202 }
203
204 /**
205 * Fill the graph instruction into InstructionInfo, Convert Graph instruction to InstructionInfo
206 * @param inIns Used to obtain background information of InstructionInfo
207 * @param target graph instruction
208 * @param outIns InstructionInfo for filling
209 */
FillInstInfo(const InstructionInfo & inIns,const panda::compiler::Inst * target,InstructionInfo & outIns)210 void FillInstInfo(const InstructionInfo &inIns, const panda::compiler::Inst *target, InstructionInfo &outIns)
211 {
212 uint32_t pcVal = target->GetPc();
213 Function *func = inIns.function_;
214 PANDA_GUARD_ASSERT_PRINT(func->pcInstMap_.find(pcVal) == func->pcInstMap_.end(), TAG, ErrorCode::GENERIC_ERROR,
215 "no valid target ins: " << inIns.function_->idx_ << ", " << inIns.index_);
216 func->FillInstInfo(func->pcInstMap_.at(pcVal), outIns);
217 }
218
219 /**
220 * Print Graph instruction content
221 */
PrintInst(const panda::compiler::Inst * ins)222 std::string PrintInst(const panda::compiler::Inst *ins)
223 {
224 std::stringstream stream;
225 ins->Dump(&stream, false);
226 return stream.str();
227 }
228
229 /**
230 * Find the lda.str instruction corresponding to the input command
231 */
FindLdaStr(const panda::compiler::Inst * prevInst,const panda::compiler::Inst * & outIns)232 void FindLdaStr(const panda::compiler::Inst *prevInst, const panda::compiler::Inst *&outIns)
233 {
234 if (IsInstIdMatched(prevInst, INST_ID_LIST_CALLRUNTIME_TOPROPERTYKEY)) {
235 prevInst = prevInst->GetInput(0).GetInst();
236 }
237
238 if (prevInst->GetOpcode() != panda::compiler::Opcode::CastValueToAnyType) {
239 return;
240 }
241 const panda::compiler::Inst *targetInst = prevInst->GetInput(0).GetInst();
242 if (targetInst->GetOpcode() != panda::compiler::Opcode::LoadString) {
243 return;
244 }
245
246 outIns = targetInst;
247 }
248
249 /**
250 * Find the object definition instruction corresponding to the input instruction
251 */
FindDefineInst(const panda::compiler::Inst * prevInst,const panda::compiler::Inst * & outIns)252 void FindDefineInst(const panda::compiler::Inst *prevInst, const panda::compiler::Inst *&outIns)
253 {
254 if (IsInstIdMatched(prevInst, {INST_ID_LIST_CREATEOBJECTWITHBUFFER, INST_ID_LIST_DEFINECLASSWITHBUFFER,
255 INST_ID_LIST_CALLRUNTIME_DEFINESENDABLECLASS})) {
256 outIns = prevInst;
257 return;
258 }
259
260 // Try to find class define in prototype instance
261 if (!IsInstIdMatched(prevInst, INST_ID_LIST_LDOBJBYNAME)) {
262 return;
263 }
264
265 const panda::compiler::Inst *targetIns = prevInst->GetInput(0).GetInst();
266 if (!IsInstIdMatched(targetIns, INST_ID_LIST_DEFINECLASSWITHBUFFER)) {
267 return;
268 }
269 outIns = targetIns;
270 }
271 } // namespace
272
GetLdaStr(const InstructionInfo & inIns,InstructionInfo & outIns,int index)273 void GraphAnalyzer::GetLdaStr(const InstructionInfo &inIns, InstructionInfo &outIns, int index /* = -1 */)
274 {
275 auto it = LDA_STA_INST_PARAM_MAP.find(inIns.ins_->opcode);
276 PANDA_GUARD_ASSERT_PRINT(it == LDA_STA_INST_PARAM_MAP.end(), TAG, ErrorCode::GENERIC_ERROR,
277 "unsupported lda.str scene: " << inIns.ins_->ToString());
278
279 const panda::compiler::Inst *inst;
280 VisitGraph(inIns, it->second.filterList, inst);
281
282 unsigned targetIndex = index >= 0 ? index : it->second.index;
283 const panda::compiler::Inst *prevInst = inst->GetInput(targetIndex).GetInst();
284 const panda::compiler::Inst *targetInst = nullptr;
285 FindLdaStr(prevInst, targetInst);
286
287 if (targetInst == nullptr) {
288 LOG(INFO, PANDAGUARD) << TAG << "GetLdaStr failed: " << inIns.function_->idx_ << ", " << inIns.index_ << ";"
289 << PrintInst(prevInst);
290 return;
291 }
292
293 FillInstInfo(inIns, targetInst, outIns);
294 }
295
HandleDefineMethod(const InstructionInfo & inIns,InstructionInfo & defineIns,InstructionInfo & nameIns)296 void GraphAnalyzer::HandleDefineMethod(const InstructionInfo &inIns, InstructionInfo &defineIns,
297 InstructionInfo &nameIns)
298 {
299 const panda::compiler::Inst *inst;
300 VisitGraph(inIns, INST_ID_LIST_DEFINEMETHOD, inst);
301
302 for (const auto &userIns : inst->GetUsers()) {
303 if (IsInstIdMatched(userIns.GetInst(), METHOD_NAME_INST_ID_LIST)) {
304 FillInstInfo(inIns, userIns.GetInst(), nameIns);
305 break;
306 }
307 }
308
309 do {
310 inst = inst->GetInput(0).GetInst();
311 } while (IsInstIdMatched(inst, DEFINE_INST_PASS_THROUGH_LIST));
312 PANDA_GUARD_ASSERT_PRINT(!IsInstIdMatched(inst, DEFINE_INST_ID_LIST), TAG, ErrorCode::GENERIC_ERROR,
313 "unexpect defineMethod scene: " << inIns.function_->idx_ << ", " << inIns.index_ << ";"
314 << PrintInst(inst));
315
316 const panda::compiler::Inst *targetInst = nullptr;
317 FindDefineInst(inst, targetInst);
318 PANDA_GUARD_ASSERT_PRINT(targetInst == nullptr, TAG, ErrorCode::GENERIC_ERROR,
319 "HandleDefineMethod failed: " << inIns.function_->idx_ << ", " << inIns.index_ << ";"
320 << PrintInst(inst));
321
322 FillInstInfo(inIns, targetInst, defineIns);
323 }
324
HandleDefineProperty(const panda::guard::InstructionInfo & inIns,panda::guard::InstructionInfo & defineIns)325 void GraphAnalyzer::HandleDefineProperty(const panda::guard::InstructionInfo &inIns,
326 panda::guard::InstructionInfo &defineIns)
327 {
328 const panda::compiler::Inst *inst;
329 VisitGraph(inIns, INST_ID_LIST_DEFINEPROPERTYBYNAME, inst);
330
331 const panda::compiler::Inst *target = nullptr;
332 FindDefineInst(inst->GetInput(0).GetInst(), target);
333 if (target == nullptr) {
334 return;
335 }
336
337 FillInstInfo(inIns, target, defineIns);
338 }
339
IsComponentClass(const panda::guard::InstructionInfo & inIns)340 bool GraphAnalyzer::IsComponentClass(const panda::guard::InstructionInfo &inIns)
341 {
342 if (inIns.ins_->opcode != pandasm::Opcode::DEFINECLASSWITHBUFFER) {
343 return false;
344 }
345
346 const panda::compiler::Inst *inst;
347 VisitGraph(inIns, INST_ID_LIST_DEFINECLASSWITHBUFFER, inst);
348
349 const panda::compiler::Inst *tryldglobalbyname = inst->GetInput(0).GetInst();
350 if (!IsInstIdMatched(tryldglobalbyname, INST_ID_LIST_TRYLDGLOBALBYNAME)) {
351 return false;
352 }
353
354 InstructionInfo out;
355 FillInstInfo(inIns, tryldglobalbyname, out);
356
357 const std::string baseClassName = out.ins_->ids[0];
358 if (baseClassName != UI_COMPONENT_BASE_CLASS_VIEW_PU && baseClassName != UI_COMPONENT_BASE_CLASS_VIEW_V2) {
359 return false;
360 }
361
362 LOG(INFO, PANDAGUARD) << TAG << "found UI component:" << inIns.ins_->ids[0];
363
364 return true;
365 }
366