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