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 "inst.h"
17 #include "graph.h"
18 #include "graph_visitor.h"
19 #include "optimizer/optimizations/vn.h"
20 
21 namespace panda::compiler {
22 
ReserveInputs(size_t capacity)23 void Inst::ReserveInputs(size_t capacity)
24 {
25     ASSERT(IsOperandsDynamic());
26     GetDynamicOperands()->Reallocate(capacity);
27 }
28 
GetInst()29 Inst *User::GetInst()
30 {
31     if (UNLIKELY(IsDynamic())) {
32         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
33         return *reinterpret_cast<Inst **>(this + GetIndex() + 1);
34     }
35     auto p = reinterpret_cast<uintptr_t>(this);
36     p += (GetIndex() + 1) * sizeof(User);
37 
38     auto inputs_count {SizeField::Decode(properties_)};
39     p += (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count)) * sizeof(Input);
40     return reinterpret_cast<Inst *>(p);
41 }
42 
InsertBefore(Inst *inst)43 void Inst::InsertBefore(Inst *inst)
44 {
45     ASSERT(bb_ != nullptr);
46     bb_->InsertBefore(inst, this);
47 }
48 
InsertAfter(Inst *inst)49 void Inst::InsertAfter(Inst *inst)
50 {
51     ASSERT(bb_ != nullptr);
52     bb_->InsertAfter(inst, this);
53 }
54 
Reallocate([[maybe_unused]] size_t new_capacity )55 void DynamicOperands::Reallocate([[maybe_unused]] size_t new_capacity /* =0 */)
56 {
57     if (new_capacity == 0) {
58         constexpr auto IMM_2 = 2;
59         new_capacity = (((capacity_ != 0U) ? capacity_ : 1U) << 1U) + IMM_2;
60     } else if (new_capacity <= capacity_) {
61         return;
62     }
63     auto size = new_capacity * (sizeof(User) + sizeof(Inst *)) + sizeof(Inst *);
64     auto new_stor = reinterpret_cast<uintptr_t>(allocator_->Alloc(size));
65     CHECK(new_stor != 0);
66 
67     auto owner_inst {GetOwnerInst()};
68     // Set pointer to owned instruction into new storage NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
69     *reinterpret_cast<Inst **>(reinterpret_cast<User *>(new_stor) + new_capacity) = owner_inst;
70 
71     if (users_ == nullptr) {
72         users_ = reinterpret_cast<User *>(new_stor);
73         capacity_ = new_capacity;
74         return;
75     }
76     Input *old_inputs = Inputs();
77     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
78     auto *new_inputs = reinterpret_cast<Input *>(new_stor + sizeof(User) * new_capacity) + 1;
79 
80     for (size_t i = 0; i < size_; i++) {
81         Inst *old_input = old_inputs[i].GetInst();  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
82         ASSERT(old_input);
83         // Initialize new User in container. Since users are placed from end of array, i.e. zero index element
84         // will be at the end of array, we need to add capacity and substitute index.
85         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
86         User *new_user = new (reinterpret_cast<User *>(new_stor) + new_capacity - i - 1) User(false, i, new_capacity);
87         auto old_user {GetUser(i)};
88         if (owner_inst->IsSaveState()) {
89             new_user->SetVirtualRegister(old_user->GetVirtualRegister());
90         } else if (owner_inst->IsPhi()) {
91             new_user->SetBbNum(old_user->GetBbNum());
92         }
93         old_input->RemoveUser(old_user);
94         old_input->AddUser(new_user);
95         new_inputs[i] = Input(old_input);  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
96     }
97     capacity_ = new_capacity;
98     users_ = reinterpret_cast<User *>(new_stor);
99 }
100 
Append(Inst *inst)101 unsigned DynamicOperands::Append(Inst *inst)
102 {
103     ASSERT(capacity_ >= size_);
104     if (capacity_ == size_) {
105         Reallocate();
106     }
107     SetInput(size_, Input(inst));
108     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
109     new (users_ + capacity_ - size_ - 1) User(false, size_, capacity_);
110     auto user {GetUser(size_)};
111     if (GetOwnerInst()->IsPhi()) {
112         user->SetBbNum(size_);
113     }
114     inst->AddUser(user);
115     return size_++;
116 }
117 
Remove(unsigned index)118 void DynamicOperands::Remove(unsigned index)
119 {
120     size_--;
121     auto *curr_input = GetInput(index)->GetInst();
122     if (curr_input->GetBasicBlock() != nullptr && curr_input->HasUsers()) {
123         curr_input->RemoveUser(GetUser(index));
124     }
125 
126     auto bb_num {GetUser(index)->GetBbNum()};
127     auto owner_inst {GetOwnerInst()};
128 
129     if (index != size_) {
130         auto *last_input = GetInput(size_)->GetInst();
131         if (last_input->HasUsers()) {
132             last_input->RemoveUser(GetUser(size_));
133             last_input->AddUser(GetUser(index));
134         }
135         SetInput(index, *GetInput(size_));
136         if (owner_inst->IsSaveState()) {
137             GetUser(index)->SetVirtualRegister(GetUser(size_)->GetVirtualRegister());
138         } else if (owner_inst->IsPhi()) {
139             GetUser(index)->SetBbNum(GetUser(size_)->GetBbNum());
140         }
141     }
142 
143     if (owner_inst->IsPhi()) {
144         for (size_t i {0}; i < size_; ++i) {
145             if (GetUser(i)->GetBbNum() == size_) {
146                 GetUser(i)->SetBbNum(bb_num);
147                 break;
148             }
149         }
150     }
151 }
152 
SetVnObject(VnObject *vn_obj)153 void CompareInst::SetVnObject(VnObject *vn_obj)
154 {
155     vn_obj->Add(static_cast<uint32_t>(GetCc()));
156 }
157 
SetVnObject(VnObject *vn_obj)158 void IfInst::SetVnObject(VnObject *vn_obj)
159 {
160     vn_obj->Add(static_cast<uint32_t>(GetCc()));
161 }
162 
SetVnObject(VnObject *vn_obj)163 void IfImmInst::SetVnObject(VnObject *vn_obj)
164 {
165     vn_obj->Add(static_cast<uint32_t>(GetCc()));
166 }
167 
SetVnObject(VnObject *vn_obj)168 void CmpInst::SetVnObject(VnObject *vn_obj)
169 {
170     if (DataType::IsFloatType(GetOperandsType())) {
171         vn_obj->Add(static_cast<uint32_t>(IsFcmpg()));
172     }
173     vn_obj->Add(static_cast<uint32_t>(GetInputType(0)));
174 }
175 
GetPhiInputBb(unsigned index)176 BasicBlock *PhiInst::GetPhiInputBb(unsigned index)
177 {
178     ASSERT(index < GetInputsCount());
179 
180     auto bb_num {GetPhiInputBbNum(index)};
181     ASSERT(bb_num < GetBasicBlock()->GetPredsBlocks().size());
182     return GetBasicBlock()->GetPredsBlocks()[bb_num];
183 }
184 
GetPhiInput(BasicBlock *bb)185 Inst *PhiInst::GetPhiInput(BasicBlock *bb)
186 {
187     auto index = GetPredBlockIndex(bb);
188     ASSERT(index < GetInputs().size());
189     return GetInput(index).GetInst();
190 }
191 
192 Inst *PhiInst::GetPhiDataflowInput(BasicBlock *bb)
193 {
194     auto index = GetPredBlockIndex(bb);
195     ASSERT(index < GetInputs().size());
196     return GetDataFlowInput(index);
197 }
198 
199 size_t PhiInst::GetPredBlockIndex(const BasicBlock *block) const
200 {
201     for (size_t i {0}; i < GetInputsCount(); ++i) {
202         if (GetPhiInputBb(i) == block) {
203             return i;
204         }
205     }
206     UNREACHABLE();
207 }
208 
209 template <Opcode opc, size_t input_idx>
210 Inst *SkipInstructions(Inst *input_inst)
211 {
212     // NOLINTNEXTLINE(readability-magic-numbers)
213     for (Opcode opcode = input_inst->GetOpcode(); opcode == opc; opcode = input_inst->GetOpcode()) {
214         input_inst = input_inst->GetInput(input_idx).GetInst();
215     }
216     return input_inst;
217 }
218 /*
219  * For instructions LoadArray, StoreArray, LoadArrayPair, StoreArrayPair, LoadArrayI, StoreArrayI, LoadArrayPairI,
220  * StoreArrayPairI, LenArray, LoadObject, StoreObject, CallVirtual, Monitor with NullCheck input the dataflow user
221  * is object, which is the first input of NullCheck instruction.
222  * For instructions LoadArray, StoreArray, LoadArrayPair, StoreArrayPair with BoundsCheck input the dataflow user is
223  * array index, which is the second input of BoundsCheck instruction
224  * For instructions Div and Mod with ZeroCheck input the dataflow user is the first input of ZeroCheck
225  */
GetDataFlowInput(Inst *input_inst) const226 Inst *Inst::GetDataFlowInput(Inst *input_inst) const
227 {
228     return input_inst;
229 }
230 
IsPrecedingInSameBlock(const Inst *other) const231 bool Inst::IsPrecedingInSameBlock(const Inst *other) const
232 {
233     ASSERT(other != nullptr && GetBasicBlock() == other->GetBasicBlock());
234     if (this == other) {
235         return true;
236     }
237     auto next = GetNext();
238     while (next != nullptr) {
239         if (next == other) {
240             return true;
241         }
242         next = next->GetNext();
243     }
244     return false;
245 }
246 
IsDominate(const Inst *other) const247 bool Inst::IsDominate(const Inst *other) const
248 {
249     ASSERT(other != nullptr);
250     if (this == other) {
251         return true;
252     }
253     auto this_bb = GetBasicBlock();
254     auto other_bb = other->GetBasicBlock();
255     return this_bb == other_bb ? IsPrecedingInSameBlock(other) : this_bb->IsDominate(other_bb);
256 }
257 
InSameBlockOrDominate(const Inst *other) const258 bool Inst::InSameBlockOrDominate(const Inst *other) const
259 {
260     return GetBasicBlock() == other->GetBasicBlock() || IsDominate(other);
261 }
262 
Clone(const Graph *targetGraph) const263 Inst *Inst::Clone(const Graph *targetGraph) const
264 {
265     ASSERT(targetGraph != nullptr);
266     auto clone = targetGraph->CreateInst(GetOpcode());
267     CHECK_NOT_NULL(clone);
268     clone->bit_fields_ = GetAllFields();
269     clone->pc_ = GetPc();
270 #ifndef NDEBUG
271     clone->SetDstReg(GetDstReg());
272 #endif
273     if (IsOperandsDynamic()) {
274         clone->ReserveInputs(GetInputsCount());
275     }
276     return clone;
277 }
278 
279 #if PANDA_TARGET_MACOS
280 template class FixedInputsInst<0>;
281 template class FixedInputsInst<1>;
282 template class FixedInputsInst<2U>;
283 #endif
284 
Clone(const Graph *targetGraph) const285 Inst *SpillFillInst::Clone(const Graph *targetGraph) const
286 {
287     auto clone = FixedInputsInst::Clone(targetGraph)->CastToSpillFill();
288     for (auto spill_fill : spill_fills_) {
289         clone->AddSpillFill(spill_fill);
290     }
291     return clone;
292 }
293 
Clone(const Graph *targetGraph) const294 Inst *CompareInst::Clone(const Graph *targetGraph) const
295 {
296     auto clone = FixedInputsInst::Clone(targetGraph);
297     clone->CastToCompare()->SetCc(GetCc());
298     clone->CastToCompare()->SetOperandsType(GetOperandsType());
299     return clone;
300 }
301 
Clone(const Graph *targetGraph) const302 Inst *CmpInst::Clone(const Graph *targetGraph) const
303 {
304     auto clone = FixedInputsInst::Clone(targetGraph);
305     clone->CastToCmp()->SetOperandsType(GetOperandsType());
306     return clone;
307 }
308 
Clone(const Graph *targetGraph) const309 Inst *IfInst::Clone(const Graph *targetGraph) const
310 {
311     auto clone = FixedInputsInst::Clone(targetGraph);
312     static_cast<IfInst *>(clone)->SetCc(GetCc());
313     static_cast<IfInst *>(clone)->SetOperandsType(GetOperandsType());
314     static_cast<IfInst *>(clone)->SetMethod(GetMethod());
315     return clone;
316 }
317 
Clone(const Graph *targetGraph) const318 Inst *IntrinsicInst::Clone(const Graph *targetGraph) const
319 {
320     ASSERT(targetGraph != nullptr);
321     auto intrinsicClone = Inst::Clone(targetGraph)->CastToIntrinsic();
322     intrinsicClone->SetIntrinsicId(GetIntrinsicId());
323     CloneTypes(targetGraph->GetAllocator(), intrinsicClone);
324     if (HasImms()) {
325         for (auto imm : GetImms()) {
326             intrinsicClone->AddImm(targetGraph->GetAllocator(), imm);
327         }
328     }
329     return intrinsicClone;
330 }
331 
Clone(const Graph *targetGraph) const332 Inst *ConstantInst::Clone(const Graph *targetGraph) const
333 {
334     Inst *new_cnst = nullptr;
335     bool is_support_int32 = GetBasicBlock()->GetGraph()->IsBytecodeOptimizer();
336     switch (GetType()) {
337         case DataType::INT32:
338             new_cnst = targetGraph->CreateInstConstant(static_cast<int32_t>(GetIntValue()), is_support_int32);
339             break;
340         case DataType::INT64:
341             new_cnst = targetGraph->CreateInstConstant(GetIntValue(), is_support_int32);
342             break;
343         case DataType::FLOAT32:
344             new_cnst = targetGraph->CreateInstConstant(GetFloatValue(), is_support_int32);
345             break;
346         case DataType::FLOAT64:
347             new_cnst = targetGraph->CreateInstConstant(GetDoubleValue(), is_support_int32);
348             break;
349         case DataType::ANY:
350             new_cnst = targetGraph->CreateInstConstant(GetRawValue(), is_support_int32);
351             new_cnst->SetType(DataType::ANY);
352             break;
353         default:
354             UNREACHABLE();
355     }
356 #ifndef NDEBUG
357     new_cnst->SetDstReg(GetDstReg());
358 #endif
359     return new_cnst;
360 }
361 
Clone(const Graph *targetGraph) const362 Inst *ParameterInst::Clone(const Graph *targetGraph) const
363 {
364     auto clone = Inst::Clone(targetGraph)->CastToParameter();
365     clone->SetArgNumber(GetArgNumber());
366     clone->SetLocationData(GetLocationData());
367     return clone;
368 }
369 
Clone(const Graph *targetGraph) const370 Inst *SaveStateInst::Clone(const Graph *targetGraph) const
371 {
372     auto clone = static_cast<SaveStateInst *>(Inst::Clone(targetGraph));
373     if (GetImmediatesCount() > 0) {
374         clone->AllocateImmediates(targetGraph->GetAllocator(), GetImmediatesCount());
375         std::copy(immediates_->begin(), immediates_->end(), clone->immediates_->begin());
376     }
377     clone->method_ = method_;
378     return clone;
379 }
380 
AppendImmediate(uint64_t imm, uint16_t vreg, DataType::Type type, bool is_acc)381 void SaveStateInst::AppendImmediate(uint64_t imm, uint16_t vreg, DataType::Type type, bool is_acc)
382 {
383     if (immediates_ == nullptr) {
384         ASSERT(GetBasicBlock() != nullptr);
385         AllocateImmediates(GetBasicBlock()->GetGraph()->GetAllocator(), 0);
386     }
387     immediates_->emplace_back(SaveStateImm {imm, vreg, type, is_acc});
388 }
389 
AllocateImmediates(ArenaAllocator *allocator, size_t size)390 void SaveStateInst::AllocateImmediates(ArenaAllocator *allocator, size_t size)
391 {
392     immediates_ = allocator->New<ArenaVector<SaveStateImm>>(allocator->Adapter());
393     CHECK_NOT_NULL(immediates_);
394     immediates_->resize(size);
395 }
396 
AppendCatchTypeId(uint32_t id, uint32_t catch_edge_index)397 void TryInst::AppendCatchTypeId(uint32_t id, uint32_t catch_edge_index)
398 {
399     if (catch_type_ids_ == nullptr) {
400         ASSERT(catch_edge_indexes_ == nullptr);
401         ASSERT(GetBasicBlock() != nullptr);
402         auto allocator = GetBasicBlock()->GetGraph()->GetAllocator();
403         catch_type_ids_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
404         catch_edge_indexes_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
405         CHECK_NOT_NULL(catch_type_ids_);
406         CHECK_NOT_NULL(catch_edge_indexes_);
407     }
408     catch_type_ids_->push_back(id);
409     catch_edge_indexes_->push_back(catch_edge_index);
410 }
411 
AppendThrowableInst(const Inst *inst)412 void CatchPhiInst::AppendThrowableInst(const Inst *inst)
413 {
414     if (throw_insts_ == nullptr) {
415         ASSERT(GetBasicBlock() != nullptr);
416         auto allocator = GetBasicBlock()->GetGraph()->GetAllocator();
417         throw_insts_ = allocator->New<ArenaVector<const Inst *>>(allocator->Adapter());
418         CHECK_NOT_NULL(throw_insts_);
419     }
420     throw_insts_->push_back(inst);
421 }
422 
ReplaceThrowableInst(const Inst *old_inst, const Inst *new_inst)423 void CatchPhiInst::ReplaceThrowableInst(const Inst *old_inst, const Inst *new_inst)
424 {
425     auto index = GetThrowableInstIndex(old_inst);
426     throw_insts_->at(index) = new_inst;
427 }
428 
RemoveInput(unsigned index)429 void CatchPhiInst::RemoveInput(unsigned index)
430 {
431     Inst::RemoveInput(index);
432     if (throw_insts_ != nullptr) {
433         throw_insts_->at(index) = throw_insts_->back();
434         throw_insts_->pop_back();
435     }
436 }
437 
Clone(const Graph *targetGraph) const438 Inst *TryInst::Clone(const Graph *targetGraph) const
439 {
440     auto clone = FixedInputsInst::Clone(targetGraph)->CastToTry();
441     if (auto ids_count = this->GetCatchTypeIdsCount(); ids_count > 0) {
442         if (clone->catch_type_ids_ == nullptr) {
443             auto allocator = targetGraph->GetAllocator();
444             clone->catch_type_ids_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
445             clone->catch_edge_indexes_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
446             CHECK_NOT_NULL(clone->catch_edge_indexes_);
447         }
448         clone->catch_type_ids_->resize(ids_count);
449         clone->catch_edge_indexes_->resize(ids_count);
450         std::copy(this->catch_type_ids_->begin(), this->catch_type_ids_->end(), clone->catch_type_ids_->begin());
451         std::copy(this->catch_edge_indexes_->begin(), this->catch_edge_indexes_->end(),
452                   clone->catch_edge_indexes_->begin());
453     }
454     return clone;
455 }
456 
GetEdgeIfInputTrue()457 BasicBlock *IfImmInst::GetEdgeIfInputTrue()
458 {
459     return GetBasicBlock()->GetSuccessor(GetTrueInputEdgeIdx());
460 }
461 
GetEdgeIfInputFalse()462 BasicBlock *IfImmInst::GetEdgeIfInputFalse()
463 {
464     return GetBasicBlock()->GetSuccessor(1 - GetTrueInputEdgeIdx());
465 }
466 
467 /**
468  * NB! Can be called before Lowering pass only
469  * Return if_imm's block successor index when input is true
470  */
GetTrueInputEdgeIdx()471 size_t IfImmInst::GetTrueInputEdgeIdx()
472 {
473     ASSERT(GetBasicBlock() != nullptr);
474     ASSERT(GetBasicBlock()->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
475     ASSERT(GetCc() == ConditionCode::CC_NE || GetCc() == ConditionCode::CC_EQ);
476     ASSERT(GetImm() == 0);
477     return GetCc() == CC_NE ? 0 : 1;
478 }
479 
IsPropagateLiveness() const480 bool Inst::IsPropagateLiveness() const
481 {
482     return (CanThrow() && GetBasicBlock()->IsTry()) || CanDeoptimize();
483 }
484 
RequireRegMap() const485 bool Inst::RequireRegMap() const
486 {
487     return CanThrow() && GetBasicBlock()->IsTry();
488 }
489 
IsZeroRegInst() const490 bool Inst::IsZeroRegInst() const
491 {
492     ASSERT(GetBasicBlock() != nullptr);
493     ASSERT(GetBasicBlock()->GetGraph() != nullptr);
494     return false;
495 }
496 
IsAccRead() const497 bool Inst::IsAccRead() const
498 {
499     return GetFlag(inst_flags::ACC_READ);
500 }
501 
IsAccWrite() const502 bool Inst::IsAccWrite() const
503 {
504     if (IsConst()) {
505         return true;
506     }
507     return GetFlag(inst_flags::ACC_WRITE);
508 }
509 
GetTryBeginInst(const BasicBlock *try_begin_bb)510 TryInst *GetTryBeginInst(const BasicBlock *try_begin_bb)
511 {
512     ASSERT(try_begin_bb != nullptr && try_begin_bb->IsTryBegin());
513     for (auto inst : try_begin_bb->AllInsts()) {
514         if (inst->GetOpcode() == Opcode::Try) {
515             return inst->CastToTry();
516         }
517     }
518     UNREACHABLE();
519     return nullptr;
520 }
521 
522 /**
523  * Regalloc's helper to checks if intrinsic's arguments should be located on the registers according to
524  * calling-convention
525  */
IsNativeCall() const526 bool IntrinsicInst::IsNativeCall() const
527 {
528     return false;
529 }
530 
531 }  // namespace panda::compiler
532