• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "dangling_pointers_checker.h"
17 #include "compiler/optimizer/ir/basicblock.h"
18 #include "compiler/optimizer/ir/graph.h"
19 #include "runtime/interpreter/frame.h"
20 #include "runtime/include/managed_thread.h"
21 
22 namespace panda::compiler {
DanglingPointersChecker(Graph * graph)23 DanglingPointersChecker::DanglingPointersChecker(Graph *graph)
24     : Analysis {graph},
25       objectsUsers_ {graph->GetLocalAllocator()->Adapter()},
26       checkedBlocks_ {graph->GetLocalAllocator()->Adapter()},
27       phiInsts_ {graph->GetLocalAllocator()->Adapter()},
28       objectInsts_ {graph->GetLocalAllocator()->Adapter()}
29 {
30 }
31 
RunImpl()32 bool DanglingPointersChecker::RunImpl()
33 {
34     return CheckAccSyncCallRuntime();
35 }
36 
MoveToPrevInst(Inst * inst,BasicBlock * bb)37 static Inst *MoveToPrevInst(Inst *inst, BasicBlock *bb)
38 {
39     if (inst == bb->GetFirstInst()) {
40         return nullptr;
41     }
42     return inst->GetPrev();
43 }
44 
IsRefType(Inst * inst)45 static bool IsRefType(Inst *inst)
46 {
47     return inst->GetType() == DataType::REFERENCE;
48 }
49 
IsPointerType(Inst * inst)50 static bool IsPointerType(Inst *inst)
51 {
52     return inst->GetType() == DataType::POINTER;
53 }
54 
IsObjectDef(Inst * inst)55 static bool IsObjectDef(Inst *inst)
56 {
57     if (!IsRefType(inst) && !IsPointerType(inst)) {
58         return false;
59     }
60     if (inst->IsPhi()) {
61         return false;
62     }
63     if (inst->GetOpcode() != Opcode::AddI) {
64         return true;
65     }
66     auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
67     return imm != static_cast<uint64_t>(cross_values::GetFrameAccOffset(inst->GetBasicBlock()->GetGraph()->GetArch()));
68 }
69 
InitLiveIns()70 void DanglingPointersChecker::InitLiveIns()
71 {
72     auto arch = GetGraph()->GetArch();
73     for (auto inst : GetGraph()->GetStartBlock()->Insts()) {
74         if (inst->GetOpcode() != Opcode::LiveIn) {
75             continue;
76         }
77         if (inst->GetDstReg() == regmap_[arch]["acc"]) {
78             accLivein_ = inst;
79         }
80         if (inst->GetDstReg() == regmap_[arch]["acc_tag"]) {
81             accTagLivein_ = inst;
82         }
83         if (inst->GetDstReg() == regmap_[arch]["frame"]) {
84             frameLivein_ = inst;
85         }
86         if (inst->GetDstReg() == regmap_[arch]["thread"]) {
87             threadLivein_ = inst;
88         }
89     }
90 }
91 
92 // Frame can be defined in three ways:
93 // 1. LiveIn(frame)
94 // 2. LoadI(LiveIn(thread)).Imm(ManagedThread::GetFrameOffset())
95 // 3. LoadI(<another_frame_def>).Imm(Frame::GetPrevFrameOffset())
96 
IsFrameDef(Inst * inst)97 bool DanglingPointersChecker::IsFrameDef(Inst *inst)
98 {
99     //    inst := LiveIn(frame)
100     if (inst == frameLivein_) {
101         return true;
102     }
103     // or
104     //    inst := LoadI(inst_input).Imm(imm)
105     if (inst->GetOpcode() == Opcode::LoadI) {
106         // where
107         //       inst_input := LiveIn(thread)
108         //       imm := ManagedThread::GetFrameOffset()
109         auto instInput = inst->GetInput(0).GetInst();
110         if (instInput == threadLivein_ &&
111             static_cast<LoadInstI *>(inst)->GetImm() == panda::ManagedThread::GetFrameOffset()) {
112             return true;
113         }
114         // or
115         //       inst_input := <frame_def>
116         //       imm := Frame::GetPrevFrameOffset()
117         if (static_cast<LoadInstI *>(inst)->GetImm() == static_cast<uint64_t>(panda::Frame::GetPrevFrameOffset()) &&
118             IsFrameDef(instInput)) {
119             return true;
120         }
121     }
122     return false;
123 }
124 
CheckSuccessors(BasicBlock * bb,bool prevRes)125 bool DanglingPointersChecker::CheckSuccessors(BasicBlock *bb, bool prevRes)
126 {
127     if (checkedBlocks_.find(bb) != checkedBlocks_.end()) {
128         return prevRes;
129     }
130     for (auto succBb : bb->GetSuccsBlocks()) {
131         if (!prevRes) {
132             return false;
133         }
134         for (auto inst : succBb->AllInsts()) {
135             auto user = std::find(objectsUsers_.begin(), objectsUsers_.end(), inst);
136             if (user == objectsUsers_.end() || (*user)->IsPhi()) {
137                 continue;
138             }
139             return false;
140         }
141         checkedBlocks_.insert(bb);
142 
143         prevRes &= CheckSuccessors(succBb, prevRes);
144     }
145 
146     return prevRes;
147 }
148 
149 // Accumulator can be defined in three ways:
150 // 1. acc_def := LiveIn(acc)
151 // 2. acc_def := LoadI(last_frame_def).Imm(frame_acc_offset)
152 // 3. acc_ptr := AddI(last_frame_def).Imm(frame_acc_offset)
153 //    acc_def := LoadI(acc_ptr).Imm(0)
154 
GetAccAndFrameDefs(Inst * inst)155 std::tuple<Inst *, Inst *> DanglingPointersChecker::GetAccAndFrameDefs(Inst *inst)
156 {
157     auto arch = GetGraph()->GetArch();
158     if (inst == accLivein_) {
159         return std::make_pair(accLivein_, nullptr);
160     }
161     if (inst->GetOpcode() != Opcode::LoadI) {
162         return std::make_pair(nullptr, nullptr);
163     }
164 
165     auto instInput = inst->GetInput(0).GetInst();
166     auto frameAccOffset = static_cast<uint64_t>(cross_values::GetFrameAccOffset(arch));
167     auto loadImm = static_cast<LoadInstI *>(inst)->GetImm();
168     if (loadImm == frameAccOffset && IsFrameDef(instInput)) {
169         return std::make_pair(inst, instInput);
170     }
171 
172     if (loadImm == 0 && IsAccPtr(instInput)) {
173         return std::make_pair(inst, instInput->GetInput(0).GetInst());
174     }
175 
176     return std::make_pair(nullptr, nullptr);
177 }
178 
179 // Accumulator tag can be defined in three ways:
180 // 1. acc_tag_def := LiveIn(acc_tag)
181 // 2. acc_ptr     := AddI(last_frame_def).Imm(frame_acc_offset)
182 //    acc_tag_def := LoadI(acc_ptr).Imm(acc_tag_offset)
183 // 3. acc_ptr     := AddI(last_frame_def).Imm(frame_acc_offset)
184 //    acc_tag_ptr := AddI(acc_ptr).Imm(acc_tag_offset)
185 //    acc_tag_def := LoadI(acc_tag_ptr).Imm(0)
186 
IsAccTagDef(Inst * inst)187 bool DanglingPointersChecker::IsAccTagDef(Inst *inst)
188 {
189     if (inst == accTagLivein_) {
190         return true;
191     }
192     if (inst->GetOpcode() != Opcode::LoadI) {
193         return false;
194     }
195 
196     auto instInput = inst->GetInput(0).GetInst();
197     auto arch = GetGraph()->GetArch();
198     auto accTagOffset = static_cast<uint64_t>(cross_values::GetFrameAccMirrorOffset(arch));
199     auto loadImm = static_cast<LoadInstI *>(inst)->GetImm();
200     if (loadImm == accTagOffset && IsAccPtr(instInput)) {
201         return true;
202     }
203 
204     if (loadImm == 0 && IsAccTagPtr(instInput)) {
205         return true;
206     }
207 
208     return false;
209 }
210 
IsAccTagPtr(Inst * inst)211 bool DanglingPointersChecker::IsAccTagPtr(Inst *inst)
212 {
213     if (inst->GetOpcode() != Opcode::AddI) {
214         return false;
215     }
216     auto arch = GetGraph()->GetArch();
217     auto instImm = static_cast<BinaryImmOperation *>(inst)->GetImm();
218     auto accTagOffset = static_cast<uint64_t>(cross_values::GetFrameAccMirrorOffset(arch));
219     if (instImm != accTagOffset) {
220         return false;
221     }
222     auto accPtrInst = inst->GetInput(0).GetInst();
223     return IsAccPtr(accPtrInst);
224 }
225 
IsAccPtr(Inst * inst)226 bool DanglingPointersChecker::IsAccPtr(Inst *inst)
227 {
228     if (inst->GetOpcode() != Opcode::AddI) {
229         return false;
230     }
231     auto arch = GetGraph()->GetArch();
232     auto instImm = static_cast<BinaryImmOperation *>(inst)->GetImm();
233     auto frameAccOffset = static_cast<uint64_t>(cross_values::GetFrameAccOffset(arch));
234     if (instImm != frameAccOffset) {
235         return false;
236     }
237     auto frameInst = inst->GetInput(0).GetInst();
238     if (!IsFrameDef(frameInst)) {
239         return false;
240     }
241     if (lastFrameDef_ == nullptr) {
242         return true;
243     }
244     return lastFrameDef_ == frameInst;
245 }
246 
UpdateLastAccAndFrameDef(Inst * inst)247 void DanglingPointersChecker::UpdateLastAccAndFrameDef(Inst *inst)
248 {
249     auto [acc_def, frame_def] = GetAccAndFrameDefs(inst);
250     if (acc_def != nullptr) {
251         // inst is acc definition
252         if (lastAccDef_ == nullptr) {
253             // don't have acc definition before
254             lastAccDef_ = acc_def;
255             lastFrameDef_ = frame_def;
256         }
257     } else {
258         // inst isn't acc definition
259         if (IsObjectDef(inst) && !IsPointerType(inst)) {
260             // objects defs should be only ref type
261             objectInsts_.insert(inst);
262         }
263     }
264 }
265 
GetLastAccDefinition(CallInst * runtimeCallInst)266 void DanglingPointersChecker::GetLastAccDefinition(CallInst *runtimeCallInst)
267 {
268     auto block = runtimeCallInst->GetBasicBlock();
269     auto prevInst = runtimeCallInst->GetPrev();
270 
271     phiInsts_.clear();
272     objectInsts_.clear();
273     while (block != GetGraph()->GetStartBlock()) {
274         while (prevInst != nullptr) {
275             UpdateLastAccAndFrameDef(prevInst);
276 
277             if (lastAccTagDef_ == nullptr && IsAccTagDef(prevInst)) {
278                 lastAccTagDef_ = prevInst;
279             }
280 
281             prevInst = MoveToPrevInst(prevInst, block);
282         }
283 
284         objectInsts_.insert(accLivein_);
285 
286         for (auto *phiInst : block->PhiInsts()) {
287             phiInsts_.push_back(phiInst);
288         }
289         block = block->GetDominator();
290         prevInst = block->GetLastInst();
291     }
292 
293     // Check that accumulator has not been overwritten in any execution branch except restored acc
294     auto [phi_def_acc, phi_def_frame] = GetPhiAccDef();
295     if (phi_def_acc != nullptr) {
296         lastAccDef_ = phi_def_acc;
297         lastFrameDef_ = phi_def_frame;
298     }
299     if (lastAccTagDef_ == nullptr) {
300         lastAccTagDef_ = GetPhiAccTagDef();
301     }
302 
303     if (lastAccDef_ == nullptr) {
304         lastAccDef_ = accLivein_;
305     }
306 
307     if (lastAccTagDef_ == nullptr) {
308         lastAccTagDef_ = accTagLivein_;
309     }
310 }
311 
GetPhiAccDef()312 std::tuple<Inst *, Inst *> DanglingPointersChecker::GetPhiAccDef()
313 {
314     // If any input isn't a definition (or there are no definitions among its inputs),
315     // then the phi is not a definition.
316     // Otherwise, if we have reached the last input and it is a definition (or there is a definition in among its
317     // inputs), then the phi is a definition.
318     for (auto *phiInst : phiInsts_) {
319         bool isAccDefPhi = true;
320         auto inputsCount = phiInst->GetInputsCount();
321         Inst *accDef {nullptr};
322         Inst *frameDef {nullptr};
323         for (uint32_t inputIdx = 0; inputIdx < inputsCount; inputIdx++) {
324             auto inputInst = phiInst->GetInput(inputIdx).GetInst();
325             std::tie(accDef, frameDef) = GetAccAndFrameDefs(inputInst);
326             if (accDef != nullptr || inputInst == nullptr) {
327                 continue;
328             }
329             if (inputInst->IsConst() ||
330                 (inputInst->GetOpcode() == Opcode::Bitcast && inputInst->GetInput(0).GetInst()->IsConst())) {
331                 accDef = inputInst;
332                 continue;
333             }
334             std::tie(accDef, frameDef) = GetAccDefFromInputs(inputInst);
335             if (accDef == nullptr) {
336                 isAccDefPhi = false;
337                 break;
338             }
339         }
340         if (!isAccDefPhi) {
341             continue;
342         }
343         if (accDef != nullptr) {
344             return std::make_pair(phiInst, frameDef);
345         }
346     }
347     return std::make_pair(nullptr, nullptr);
348 }
349 
GetAccDefFromInputs(Inst * inst)350 std::tuple<Inst *, Inst *> DanglingPointersChecker::GetAccDefFromInputs(Inst *inst)
351 {
352     auto inputsCount = inst->GetInputsCount();
353     Inst *accDef {nullptr};
354     Inst *frameDef {nullptr};
355     for (uint32_t inputIdx = 0; inputIdx < inputsCount; inputIdx++) {
356         auto inputInst = inst->GetInput(inputIdx).GetInst();
357 
358         std::tie(accDef, frameDef) = GetAccAndFrameDefs(inputInst);
359         if (accDef != nullptr || inputInst == nullptr) {
360             continue;
361         }
362         if (inputInst->IsConst() ||
363             (inputInst->GetOpcode() == Opcode::Bitcast && inputInst->GetInput(0).GetInst()->IsConst())) {
364             accDef = inputInst;
365             continue;
366         }
367         std::tie(accDef, frameDef) = GetAccDefFromInputs(inputInst);
368         if (accDef == nullptr) {
369             return std::make_pair(nullptr, nullptr);
370         }
371     }
372     return std::make_pair(accDef, frameDef);
373 }
374 
GetPhiAccTagDef()375 Inst *DanglingPointersChecker::GetPhiAccTagDef()
376 {
377     for (auto *phiInst : phiInsts_) {
378         if (IsRefType(phiInst) || IsPointerType(phiInst)) {
379             continue;
380         }
381         auto inputsCount = phiInst->GetInputsCount();
382         for (uint32_t inputIdx = 0; inputIdx < inputsCount; inputIdx++) {
383             auto inputInst = phiInst->GetInput(inputIdx).GetInst();
384             auto isAccTagDef = IsAccTagDef(inputInst);
385             if (isAccTagDef || inputInst->IsConst()) {
386                 if (inputIdx == inputsCount - 1) {
387                     return phiInst;
388                 }
389                 continue;
390             }
391 
392             if (!IsAccTagDefInInputs(inputInst)) {
393                 break;
394             }
395             return phiInst;
396         }
397     }
398     return nullptr;
399 }
400 
IsAccTagDefInInputs(Inst * inst)401 bool DanglingPointersChecker::IsAccTagDefInInputs(Inst *inst)
402 {
403     auto inputsCount = inst->GetInputsCount();
404     for (uint32_t inputIdx = 0; inputIdx < inputsCount; inputIdx++) {
405         auto inputInst = inst->GetInput(inputIdx).GetInst();
406         if (IsAccTagDef(inputInst)) {
407             return true;
408         }
409 
410         if ((inputIdx == inputsCount - 1) && inputInst->IsConst()) {
411             return true;
412         }
413 
414         if (IsAccTagDefInInputs(inputInst)) {
415             return true;
416         }
417     }
418     return false;
419 }
420 
IsSaveAcc(const Inst * inst)421 bool DanglingPointersChecker::IsSaveAcc(const Inst *inst)
422 {
423     if (inst->GetOpcode() != Opcode::StoreI) {
424         return false;
425     }
426 
427     auto arch = GetGraph()->GetArch();
428     auto frameAccOffset = static_cast<uint64_t>(cross_values::GetFrameAccOffset(arch));
429     if (static_cast<const StoreInstI *>(inst)->GetImm() != frameAccOffset) {
430         return false;
431     }
432     auto storeInput1 = inst->GetInput(1).GetInst();
433     if (storeInput1 != lastAccDef_) {
434         return false;
435     }
436     auto storeInput0 = inst->GetInput(0).GetInst();
437     if (lastFrameDef_ == nullptr) {
438         if (IsFrameDef(storeInput0)) {
439             return true;
440         }
441     } else if (storeInput0 == lastFrameDef_) {
442         return true;
443     }
444     return false;
445 }
446 
447 // Accumulator is saved using the StoreI instruction:
448 // StoreI(last_frame_def, last_acc_def).Imm(cross_values::GetFrameAccOffset(GetArch()))
449 
CheckStoreAcc(CallInst * runtimeCallInst)450 bool DanglingPointersChecker::CheckStoreAcc(CallInst *runtimeCallInst)
451 {
452     auto prevInst = runtimeCallInst->GetPrev();
453     auto block = runtimeCallInst->GetBasicBlock();
454     while (block != GetGraph()->GetStartBlock()) {
455         while (prevInst != nullptr && prevInst != lastAccDef_) {
456             if (IsSaveAcc(prevInst)) {
457                 return true;
458             }
459             prevInst = MoveToPrevInst(prevInst, block);
460         }
461         block = block->GetDominator();
462         prevInst = block->GetLastInst();
463     }
464     return false;
465 }
466 
467 // Accumulator tag is saved using the StoreI instruction:
468 // StoreI(acc_ptr, last_acc_tag_def).Imm(cross_values::GetFrameAccMirrorOffset(GetArch()))
469 
CheckStoreAccTag(CallInst * runtimeCallInst)470 bool DanglingPointersChecker::CheckStoreAccTag(CallInst *runtimeCallInst)
471 {
472     bool isSaveAccTag = false;
473     auto arch = GetGraph()->GetArch();
474     auto prevInst = runtimeCallInst->GetPrev();
475     auto block = runtimeCallInst->GetBasicBlock();
476     auto accTagOffset = static_cast<uint64_t>(cross_values::GetFrameAccMirrorOffset(arch));
477     while (block != GetGraph()->GetStartBlock()) {
478         while (prevInst != nullptr && prevInst != lastAccDef_) {
479             if (prevInst->GetOpcode() != Opcode::StoreI) {
480                 prevInst = MoveToPrevInst(prevInst, block);
481                 continue;
482             }
483             if (static_cast<StoreInstI *>(prevInst)->GetImm() != accTagOffset) {
484                 prevInst = MoveToPrevInst(prevInst, block);
485                 continue;
486             }
487             auto storeInput1 = prevInst->GetInput(1).GetInst();
488             if (lastAccTagDef_ == nullptr) {
489                 lastAccTagDef_ = storeInput1;
490             }
491             if (storeInput1 != lastAccTagDef_ && !storeInput1->IsConst()) {
492                 prevInst = MoveToPrevInst(prevInst, block);
493                 continue;
494             }
495             auto storeInput0 = prevInst->GetInput(0).GetInst();
496             if (IsAccPtr(storeInput0)) {
497                 isSaveAccTag = true;
498                 break;
499             }
500 
501             prevInst = MoveToPrevInst(prevInst, block);
502         }
503         if (isSaveAccTag) {
504             break;
505         }
506         block = block->GetDominator();
507         prevInst = block->GetLastInst();
508     }
509     return isSaveAccTag;
510 }
511 
CheckAccUsers(CallInst * runtimeCallInst)512 bool DanglingPointersChecker::CheckAccUsers(CallInst *runtimeCallInst)
513 {
514     objectsUsers_.clear();
515     for (const auto &user : lastAccDef_->GetUsers()) {
516         objectsUsers_.push_back(user.GetInst());
517     }
518 
519     return CheckUsers(runtimeCallInst);
520 }
521 
CheckObjectsUsers(CallInst * runtimeCallInst)522 bool DanglingPointersChecker::CheckObjectsUsers(CallInst *runtimeCallInst)
523 {
524     objectsUsers_.clear();
525     for (auto *objectInst : objectInsts_) {
526         for (const auto &user : objectInst->GetUsers()) {
527             objectsUsers_.push_back(user.GetInst());
528         }
529     }
530 
531     return CheckUsers(runtimeCallInst);
532 }
533 
CheckUsers(CallInst * runtimeCallInst)534 bool DanglingPointersChecker::CheckUsers(CallInst *runtimeCallInst)
535 {
536     bool checkObjectUsers = true;
537     auto runtimeCallBlock = runtimeCallInst->GetBasicBlock();
538 
539     auto nextInst = runtimeCallInst->GetNext();
540     while (nextInst != nullptr) {
541         auto user = std::find(objectsUsers_.begin(), objectsUsers_.end(), nextInst);
542         if (user == objectsUsers_.end() || (*user)->IsPhi()) {
543             nextInst = nextInst->GetNext();
544             continue;
545         }
546         return false;
547     }
548 
549     checkedBlocks_.clear();
550     return CheckSuccessors(runtimeCallBlock, checkObjectUsers);
551 }
552 
CheckAccSyncCallRuntime()553 bool DanglingPointersChecker::CheckAccSyncCallRuntime()
554 {
555     if (regmap_.find(GetGraph()->GetArch()) == regmap_.end()) {
556         return true;
557     }
558 
559     if (GetGraph()->GetRelocationHandler() == nullptr) {
560         return true;
561     }
562 
563     // collect runtime calls
564     ArenaVector<CallInst *> runtimeCalls(GetGraph()->GetLocalAllocator()->Adapter());
565     for (auto block : GetGraph()->GetBlocksRPO()) {
566         for (auto inst : block->Insts()) {
567             if (!inst->IsCall()) {
568                 continue;
569             }
570             auto callInst = static_cast<CallInst *>(inst);
571             auto callFuncName =
572                 GetGraph()->GetRuntime()->GetExternalMethodName(GetGraph()->GetMethod(), callInst->GetCallMethodId());
573             if (targetFuncs_.find(callFuncName) == targetFuncs_.end()) {
574                 continue;
575             }
576             runtimeCalls.push_back(callInst);
577         }
578     }
579     if (runtimeCalls.empty()) {
580         return true;
581     }
582 
583     // find LiveIns for acc and frame
584     InitLiveIns();
585 
586     for (auto runtimeCallInst : runtimeCalls) {
587         lastAccDef_ = nullptr;
588         lastAccTagDef_ = nullptr;
589         GetLastAccDefinition(runtimeCallInst);
590 
591         if (!IsRefType(lastAccDef_) && !IsPointerType(lastAccDef_)) {
592             continue;
593         }
594 
595         // check that acc has been stored in the frame before call
596         if (!CheckStoreAcc(runtimeCallInst)) {
597             return false;
598         }
599 
600         if (!GetGraph()->IsDynamicMethod() && !CheckStoreAccTag(runtimeCallInst)) {
601             return false;
602         }
603 
604         // check that acc isn't used after call
605         if (!CheckAccUsers(runtimeCallInst)) {
606             return false;
607         }
608 
609         // check that other objects aren't used after call
610         if (!CheckObjectsUsers(runtimeCallInst)) {
611             return false;
612         }
613     }
614     return true;
615 }
616 }  // namespace panda::compiler
617