• 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 "slow_path.h"
17 #include "codegen.h"
18 
19 namespace panda::compiler {
20 
Generate(Codegen * codegen)21 void SlowPathBase::Generate(Codegen *codegen)
22 {
23     ASSERT(!generated_);
24 
25     SCOPED_DISASM_STR(codegen, std::string("SlowPath for inst ") + std::to_string(GetInst()->GetId()) + ". " +
26                                    GetInst()->GetOpcodeStr());
27     Encoder *encoder = codegen->GetEncoder();
28     ASSERT(encoder->IsValid());
29     encoder->BindLabel(GetLabel());
30 
31     GenerateImpl(codegen);
32 
33     if (encoder->IsLabelValid(label_back_)) {
34         codegen->GetEncoder()->EncodeJump(GetBackLabel());
35     }
36 #ifndef NDEBUG
37     generated_ = true;
38 #endif
39 }
40 
41 // ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION, STRING_INDEX_OUT_OF_BOUNDS_EXCEPTION
GenerateThrowOutOfBoundsException(Codegen * codegen)42 bool SlowPathEntrypoint::GenerateThrowOutOfBoundsException(Codegen *codegen)
43 {
44     auto len_reg = codegen->ConvertRegister(GetInst()->GetSrcReg(0), GetInst()->GetInputType(0));
45     if (GetInst()->GetOpcode() == Opcode::BoundsCheckI) {
46         ScopedTmpReg index_reg(codegen->GetEncoder());
47         codegen->GetEncoder()->EncodeMov(index_reg, Imm(GetInst()->CastToBoundsCheckI()->GetImm()));
48         codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {index_reg, len_reg});
49     } else {
50         ASSERT(GetInst()->GetOpcode() == Opcode::BoundsCheck);
51         auto index_reg = codegen->ConvertRegister(GetInst()->GetSrcReg(1), GetInst()->GetInputType(1));
52         codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {index_reg, len_reg});
53     }
54     return true;
55 }
56 
57 // INITIALIZE_CLASS
GenerateInitializeClass(Codegen * codegen)58 bool SlowPathEntrypoint::GenerateInitializeClass(Codegen *codegen)
59 {
60     auto inst = GetInst();
61     if (GetInst()->GetDstReg() != INVALID_REG) {
62         ASSERT(inst->GetOpcode() == Opcode::LoadAndInitClass);
63         Reg klass_reg {codegen->ConvertRegister(GetInst()->GetDstReg(), DataType::REFERENCE)};
64         RegMask preserved_regs;
65         codegen->GetEncoder()->SetRegister(&preserved_regs, nullptr, klass_reg);
66         codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {klass_reg}, preserved_regs);
67     } else {
68         ASSERT(inst->GetOpcode() == Opcode::InitClass);
69         ASSERT(!codegen->GetGraph()->IsAotMode());
70         auto klass = reinterpret_cast<uintptr_t>(inst->CastToInitClass()->GetClass());
71         codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {Imm(klass)});
72     }
73     return true;
74 }
75 
76 // IS_INSTANCE
GenerateIsInstance(Codegen * codegen)77 bool SlowPathEntrypoint::GenerateIsInstance(Codegen *codegen)
78 {
79     auto src = codegen->ConvertRegister(GetInst()->GetSrcReg(0), DataType::REFERENCE);  // obj
80     auto klass = codegen->ConvertRegister(GetInst()->GetSrcReg(1), DataType::REFERENCE);
81     auto dst = codegen->ConvertRegister(GetInst()->GetDstReg(), GetInst()->GetType());
82     codegen->CallRuntime(GetInst(), EntrypointId::IS_INSTANCE, dst, {src, klass});
83     return true;
84 }
85 
86 // CHECK_CAST
GenerateCheckCast(Codegen * codegen)87 bool SlowPathEntrypoint::GenerateCheckCast(Codegen *codegen)
88 {
89     auto src = codegen->ConvertRegister(GetInst()->GetSrcReg(0), DataType::REFERENCE);  // obj
90     auto klass = codegen->ConvertRegister(GetInst()->GetSrcReg(1), DataType::REFERENCE);
91     codegen->CallRuntime(GetInst(), EntrypointId::CHECK_CAST, INVALID_REGISTER, {src, klass});
92     return true;
93 }
94 
95 // DEOPTIMIZE
GenerateDeoptimize(Codegen * codegen)96 bool SlowPathEntrypoint::GenerateDeoptimize(Codegen *codegen)
97 {
98     DeoptimizeType type = DeoptimizeType::INVALID;
99     if (GetInst()->GetOpcode() == Opcode::Deoptimize) {
100         type = GetInst()->CastToDeoptimize()->GetDeoptimizeType();
101     } else if (GetInst()->GetOpcode() == Opcode::DeoptimizeIf) {
102         type = GetInst()->CastToDeoptimizeIf()->GetDeoptimizeType();
103     } else if (GetInst()->GetOpcode() == Opcode::DeoptimizeCompare) {
104         type = GetInst()->CastToDeoptimizeCompare()->GetDeoptimizeType();
105     } else if (GetInst()->GetOpcode() == Opcode::DeoptimizeCompareImm) {
106         type = GetInst()->CastToDeoptimizeCompareImm()->GetDeoptimizeType();
107     } else if (GetInst()->GetOpcode() == Opcode::AnyTypeCheck) {
108         type = DeoptimizeType::ANY_TYPE_CHECK;
109     } else if (GetInst()->GetOpcode() == Opcode::AddOverflowCheck) {
110         type = DeoptimizeType::DEOPT_OVERFLOW;
111     } else if (GetInst()->GetOpcode() == Opcode::SubOverflowCheck) {
112         type = DeoptimizeType::DEOPT_OVERFLOW;
113     } else {
114         UNREACHABLE();
115     }
116     codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {Imm(static_cast<uint8_t>(type))});
117     return true;
118 }
119 
120 // CREATE_OBJECT
GenerateCreateObject(Codegen * codegen)121 bool SlowPathEntrypoint::GenerateCreateObject(Codegen *codegen)
122 {
123     auto inst = GetInst();
124     auto dst = codegen->ConvertRegister(inst->GetDstReg(), inst->GetType());
125     auto src = codegen->ConvertRegister(inst->GetSrcReg(0), inst->GetInputType(0));
126 
127     codegen->CallRuntime(inst, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, {src});
128 
129     return true;
130 }
131 
GenerateByEntry(Codegen * codegen)132 bool SlowPathEntrypoint::GenerateByEntry(Codegen *codegen)
133 {
134     switch (GetEntrypoint()) {
135         case EntrypointId::THROW_EXCEPTION: {
136             auto src = codegen->ConvertRegister(GetInst()->GetSrcReg(0), DataType::Type::REFERENCE);
137             codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {src});
138             return true;
139         }
140         case EntrypointId::NULL_POINTER_EXCEPTION:
141         case EntrypointId::ARITHMETIC_EXCEPTION:
142             codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {});
143             return true;
144         case EntrypointId::ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION:
145         case EntrypointId::STRING_INDEX_OUT_OF_BOUNDS_EXCEPTION:
146             return GenerateThrowOutOfBoundsException(codegen);
147         case EntrypointId::NEGATIVE_ARRAY_SIZE_EXCEPTION: {
148             auto size = codegen->ConvertRegister(GetInst()->GetSrcReg(0), GetInst()->GetInputType(0));
149             codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {size});
150             return true;
151         }
152         case EntrypointId::INITIALIZE_CLASS:
153             return GenerateInitializeClass(codegen);
154         case EntrypointId::IS_INSTANCE:
155             return GenerateIsInstance(codegen);
156         case EntrypointId::CHECK_CAST:
157             return GenerateCheckCast(codegen);
158         case EntrypointId::CREATE_OBJECT_BY_CLASS:
159             return GenerateCreateObject(codegen);
160         case EntrypointId::SAFEPOINT:
161             codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {});
162             return true;
163         case EntrypointId::DEOPTIMIZE: {
164             return GenerateDeoptimize(codegen);
165         }
166         default:
167             return false;
168     }
169 }
170 
GenerateImpl(Codegen * codegen)171 void SlowPathEntrypoint::GenerateImpl(Codegen *codegen)
172 {
173     if (!GenerateByEntry(codegen)) {
174         switch (GetEntrypoint()) {
175             case EntrypointId::GET_UNKNOWN_CALLEE_METHOD:
176             case EntrypointId::RESOLVE_UNKNOWN_VIRTUAL_CALL:
177             case EntrypointId::GET_FIELD_OFFSET:
178             case EntrypointId::GET_UNKNOWN_STATIC_FIELD_MEMORY_ADDRESS:
179             case EntrypointId::GET_UNKNOWN_STATIC_FIELD_PTR:
180             case EntrypointId::RESOLVE_CLASS_OBJECT:
181             case EntrypointId::RESOLVE_CLASS:
182             case EntrypointId::ABSTRACT_METHOD_ERROR:
183             case EntrypointId::INITIALIZE_CLASS_BY_ID:
184             case EntrypointId::CHECK_STORE_ARRAY_REFERENCE:
185             case EntrypointId::RESOLVE_STRING_AOT:
186             case EntrypointId::CLASS_CAST_EXCEPTION:
187                 break;
188             default:
189                 LOG(FATAL, COMPILER) << "Unsupported entrypoint!";
190                 UNREACHABLE();
191                 break;
192         }
193     }
194 }
195 
GenerateImpl(Codegen * codegen)196 void SlowPathIntrinsic::GenerateImpl(Codegen *codegen)
197 {
198     codegen->CreateCallIntrinsic(GetInst()->CastToIntrinsic());
199 }
200 
GenerateImpl(Codegen * codegen)201 void SlowPathImplicitNullCheck::GenerateImpl(Codegen *codegen)
202 {
203     ASSERT(!GetInst()->CastToNullCheck()->IsImplicit());
204     SlowPathEntrypoint::GenerateImpl(codegen);
205 }
206 
GenerateImpl(Codegen * codegen)207 void SlowPathShared::GenerateImpl(Codegen *codegen)
208 {
209     ASSERT(tmp_reg_ != INVALID_REGISTER);
210     [[maybe_unused]] ScopedTmpReg tmp_reg(codegen->GetEncoder(), tmp_reg_);
211     ASSERT(tmp_reg.GetReg().GetId() == tmp_reg_.GetId());
212     auto graph = codegen->GetGraph();
213     ASSERT(graph->IsAotMode());
214     auto aot_data = graph->GetAotData();
215     aot_data->SetSharedSlowPathOffset(GetEntrypoint(), codegen->GetEncoder()->GetCursorOffset());
216     MemRef entry(codegen->ThreadReg(), graph->GetRuntime()->GetEntrypointTlsOffset(graph->GetArch(), GetEntrypoint()));
217     ScopedTmpReg tmp1_reg(codegen->GetEncoder());
218     codegen->GetEncoder()->EncodeLdr(tmp1_reg, false, entry);
219     codegen->GetEncoder()->EncodeJump(tmp1_reg);
220 }
221 
GenerateImpl(Codegen * codegen)222 void SlowPathResolveStringAot::GenerateImpl(Codegen *codegen)
223 {
224     ScopedTmpRegU64 tmp_addr_reg(codegen->GetEncoder());
225     // Slot address was loaded into temporary register before we jumped into slow path, but it is already released
226     // because temporary registers are scoped. Try to allocate a new one and check that it is the same register
227     // as was allocated in codegen. If it is a different register then copy the slot address into it.
228     if (tmp_addr_reg.GetReg() != addr_reg_) {
229         codegen->GetEncoder()->EncodeMov(tmp_addr_reg, addr_reg_);
230     }
231     codegen->CallRuntimeWithMethod(GetInst(), method_, GetEntrypoint(), dst_reg_, Imm(string_id_), tmp_addr_reg);
232 }
233 
GenerateImpl(Codegen * codegen)234 void SlowPathRefCheck::GenerateImpl(Codegen *codegen)
235 {
236     ASSERT(array_reg_ != INVALID_REGISTER);
237     ASSERT(ref_reg_ != INVALID_REGISTER);
238     codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {array_reg_, ref_reg_});
239 }
240 
GenerateImpl(Codegen * codegen)241 void SlowPathAbstract::GenerateImpl(Codegen *codegen)
242 {
243     SCOPED_DISASM_STR(codegen, std::string("SlowPath for Abstract method ") + std::to_string(GetInst()->GetId()));
244     ASSERT(method_reg_ != INVALID_REGISTER);
245     ScopedTmpReg method_reg(codegen->GetEncoder(), method_reg_);
246     ASSERT(method_reg.GetReg().GetId() == method_reg_.GetId());
247     codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {method_reg.GetReg()});
248 }
249 
GenerateImpl(Codegen * codegen)250 void SlowPathCheckCast::GenerateImpl(Codegen *codegen)
251 {
252     SCOPED_DISASM_STR(codegen, std::string("SlowPath for CheckCast exception") + std::to_string(GetInst()->GetId()));
253     auto inst = GetInst();
254     auto src = codegen->ConvertRegister(inst->GetSrcReg(0), inst->GetInputType(0));
255 
256     codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {class_reg_, src});
257 }
258 
GenerateImpl(Codegen * codegen)259 void SlowPathUnresolved::GenerateImpl(Codegen *codegen)
260 {
261     SlowPathEntrypoint::GenerateImpl(codegen);
262 
263     ASSERT(method_ != nullptr);
264     ASSERT(type_id_ != 0);
265     ASSERT(slot_addr_ != 0);
266 
267     ScopedTmpReg value_reg(codegen->GetEncoder());
268     if (GetInst()->GetOpcode() == Opcode::UnresolvedCallVirtual) {
269         codegen->CallRuntimeWithMethod(GetInst(), method_, GetEntrypoint(), value_reg, arg_reg_, Imm(type_id_),
270                                        Imm(slot_addr_));
271     } else if (GetEntrypoint() == EntrypointId::GET_UNKNOWN_CALLEE_METHOD ||
272                GetEntrypoint() == EntrypointId::GET_UNKNOWN_STATIC_FIELD_MEMORY_ADDRESS ||
273                GetEntrypoint() == EntrypointId::GET_UNKNOWN_STATIC_FIELD_PTR) {
274         codegen->CallRuntimeWithMethod(GetInst(), method_, GetEntrypoint(), value_reg, Imm(type_id_), Imm(slot_addr_));
275     } else {
276         codegen->CallRuntimeWithMethod(GetInst(), method_, GetEntrypoint(), value_reg, Imm(type_id_));
277 
278         ScopedTmpReg addr_reg(codegen->GetEncoder());
279         codegen->GetEncoder()->EncodeMov(addr_reg, Imm(slot_addr_));
280         codegen->GetEncoder()->EncodeStr(value_reg, MemRef(addr_reg));
281     }
282 
283     if (dst_reg_.IsValid()) {
284         codegen->GetEncoder()->EncodeMov(dst_reg_, value_reg);
285     }
286 }
287 
288 }  // namespace panda::compiler
289