1 /**
2 * Copyright (c) 2021-2025 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 "compiler/optimizer/code_generator/codegen.h"
17
18 namespace ark::compiler {
19
EtsGetNativeMethod(IntrinsicInst * inst,Reg dst,SRCREGS & src)20 void Codegen::EtsGetNativeMethod(IntrinsicInst *inst, Reg dst, [[maybe_unused]] SRCREGS &src)
21 {
22 auto *encoder = GetEncoder();
23 ScopedTmpReg methodReg(encoder);
24
25 if (GetGraph()->IsJitOrOsrMode()) {
26 auto *method = inst->GetCallMethod();
27 encoder->EncodeMov(methodReg, Imm(bit_cast<uintptr_t>(method)));
28 } else {
29 ScopedTmpReg addrReg(encoder);
30 auto *aotData = GetGraph()->GetAotData();
31 auto methodId = inst->GetCallMethodId();
32 intptr_t offset = aotData->GetCommonSlotOffset(encoder->GetCursorOffset(), methodId);
33 encoder->MakeLoadAotTableAddr(offset, addrReg, methodReg);
34
35 auto skipLabel = encoder->CreateLabel();
36 encoder->EncodeJump(skipLabel, methodReg, Condition::NE);
37 LoadMethod(methodReg);
38 CallRuntime(inst, EntrypointId::GET_CALLEE_METHOD, methodReg, RegMask::GetZeroMask(), methodReg,
39 TypedImm(methodId));
40 encoder->EncodeStr(methodReg, MemRef(addrReg));
41 encoder->BindLabel(skipLabel);
42 }
43
44 auto successLabel = encoder->CreateLabel();
45 auto failLabel = encoder->CreateLabel();
46
47 // If native method is not registered, deoptimize
48 ScopedTmpReg tmpReg(encoder);
49 encoder->EncodeLdr(tmpReg, false, MemRef(methodReg, GetRuntime()->GetNativePointerOffset(GetArch())));
50 encoder->EncodeJump(failLabel, tmpReg, Condition::EQ);
51
52 // Don't support deprecated Native API and currently Function native mode in new API
53 // NOTE: add Function mode support
54 if (GetRuntime()->CanNativeMethodUseObjects(inst->GetCallMethod())) {
55 tmpReg.ChangeType(INT32_TYPE);
56 encoder->EncodeLdr(tmpReg, false, MemRef(methodReg, GetRuntime()->GetAccessFlagsOffset(GetArch())));
57 auto unsupportedMask = GetRuntime()->GetDeprecatedNativeApiMask();
58 encoder->EncodeAnd(tmpReg, tmpReg, Imm(unsupportedMask));
59 encoder->EncodeJump(failLabel, tmpReg, Condition::NE);
60 }
61 encoder->EncodeMov(dst, methodReg);
62 encoder->EncodeJump(successLabel);
63
64 encoder->BindLabel(failLabel);
65 encoder->EncodeMov(dst, Imm(0));
66
67 encoder->BindLabel(successLabel);
68 }
69
EtsGetNativeMethodManagedClass(IntrinsicInst * inst,Reg dst,SRCREGS & src)70 void Codegen::EtsGetNativeMethodManagedClass(IntrinsicInst *inst, Reg dst, SRCREGS &src)
71 {
72 if (GetGraph()->IsJitOrOsrMode()) {
73 auto *method = inst->GetCallMethod();
74 auto runtimeClass = bit_cast<uintptr_t>(GetRuntime()->GetClass(method));
75 auto managedClass = runtimeClass - GetRuntime()->GetRuntimeClassOffset(GetArch());
76 GetEncoder()->EncodeMov(dst, Imm(managedClass));
77 } else {
78 auto methodReg = src[0U];
79 GetEncoder()->EncodeLdr(dst, false, MemRef(methodReg, GetRuntime()->GetClassOffset(GetArch())));
80 GetEncoder()->EncodeSub(dst, dst, Imm(GetRuntime()->GetRuntimeClassOffset(GetArch())));
81 }
82 }
83
EtsGetMethodNativePointer(IntrinsicInst * inst,Reg dst,SRCREGS & src)84 void Codegen::EtsGetMethodNativePointer(IntrinsicInst *inst, Reg dst, SRCREGS &src)
85 {
86 auto *method = inst->GetCallMethod();
87 auto *nativePointer = GetRuntime()->GetMethodNativePointer(method);
88 if (GetGraph()->IsJitOrOsrMode() && nativePointer != nullptr) {
89 GetEncoder()->EncodeMov(dst, Imm(bit_cast<uintptr_t>(nativePointer)));
90 } else {
91 auto methodReg = src[0U];
92 GetEncoder()->EncodeLdr(dst, false, MemRef(methodReg, GetRuntime()->GetNativePointerOffset(GetArch())));
93 }
94 }
95
EtsGetNativeApiEnv(IntrinsicInst * inst,Reg dst,SRCREGS & src)96 void Codegen::EtsGetNativeApiEnv([[maybe_unused]] IntrinsicInst *inst, Reg dst, [[maybe_unused]] SRCREGS &src)
97 {
98 GetEncoder()->EncodeLdr(dst, false, MemRef(ThreadReg(), GetRuntime()->GetTlsNativeApiOffset(GetArch())));
99 }
100
EtsBeginNativeMethod(IntrinsicInst * inst,Reg dst,SRCREGS & src)101 void Codegen::EtsBeginNativeMethod(IntrinsicInst *inst, [[maybe_unused]] Reg dst, [[maybe_unused]] SRCREGS &src)
102 {
103 ASSERT(!HasLiveCallerSavedRegs(inst));
104 ASSERT(inst->GetSaveState()->GetRootsRegsMask().none());
105
106 {
107 SCOPED_DISASM_STR(this, "Prepare for begin native");
108 GetEncoder()->EncodeStr(FpReg(), MemRef(ThreadReg(), GetRuntime()->GetTlsFrameOffset(GetArch())));
109
110 CreateStackMap(inst);
111 ScopedTmpReg pc(GetEncoder());
112 GetEncoder()->EncodeGetCurrentPc(pc);
113 GetEncoder()->EncodeStr(pc, MemRef(ThreadReg(), GetRuntime()->GetTlsNativePcOffset(GetArch())));
114 }
115
116 {
117 SCOPED_DISASM_STR(this, "Call begin native intrinsic");
118 bool switchesToNative = GetRuntime()->IsNecessarySwitchThreadState(inst->GetCallMethod());
119 auto id =
120 switchesToNative ? EntrypointId::BEGIN_GENERAL_NATIVE_METHOD : EntrypointId::BEGIN_QUICK_NATIVE_METHOD;
121 MemRef entry(ThreadReg(), GetRuntime()->GetEntrypointTlsOffset(GetArch(), id));
122 GetEncoder()->MakeCall(entry);
123 }
124 }
125
EtsEndNativeMethodPrim(IntrinsicInst * inst,Reg dst,SRCREGS & src)126 void Codegen::EtsEndNativeMethodPrim(IntrinsicInst *inst, Reg dst, SRCREGS &src)
127 {
128 EtsEndNativeMethod(inst, dst, src, false);
129 }
130
EtsEndNativeMethodObj(IntrinsicInst * inst,Reg dst,SRCREGS & src)131 void Codegen::EtsEndNativeMethodObj(IntrinsicInst *inst, Reg dst, SRCREGS &src)
132 {
133 EtsEndNativeMethod(inst, dst, src, true);
134 }
135
EtsEndNativeMethod(IntrinsicInst * inst,Reg dst,SRCREGS & src,bool isObject)136 void Codegen::EtsEndNativeMethod(IntrinsicInst *inst, [[maybe_unused]] Reg dst, [[maybe_unused]] SRCREGS &src,
137 bool isObject)
138 {
139 ASSERT(!HasLiveCallerSavedRegs(inst));
140 ASSERT(inst->GetSaveState()->GetRootsRegsMask().none());
141
142 {
143 SCOPED_DISASM_STR(this, "Call end native intrinsic");
144 bool switchesToNative = GetRuntime()->IsNecessarySwitchThreadState(inst->GetCallMethod());
145 EntrypointId id = EntrypointId::INVALID;
146 if (switchesToNative) {
147 id = isObject ? EntrypointId::END_GENERAL_NATIVE_METHOD_OBJ : EntrypointId::END_GENERAL_NATIVE_METHOD_PRIM;
148 } else {
149 id = isObject ? EntrypointId::END_QUICK_NATIVE_METHOD_OBJ : EntrypointId::END_QUICK_NATIVE_METHOD_PRIM;
150 }
151 MemRef entry(ThreadReg(), GetRuntime()->GetEntrypointTlsOffset(GetArch(), id));
152 GetEncoder()->MakeCall(entry);
153 }
154 }
155
EtsCheckNativeException(IntrinsicInst * inst,Reg dst,SRCREGS & src)156 void Codegen::EtsCheckNativeException(IntrinsicInst *inst, [[maybe_unused]] Reg dst, [[maybe_unused]] SRCREGS &src)
157 {
158 ASSERT(inst->CanThrow());
159 auto throwLabel = CreateSlowPath<SlowPathEntrypoint>(inst, EntrypointId::THROW_NATIVE_EXCEPTION)->GetLabel();
160
161 ScopedTmpReg tmpReg(GetEncoder());
162 GetEncoder()->EncodeLdr(tmpReg, false, MemRef(ThreadReg(), GetRuntime()->GetExceptionOffset(GetArch())));
163 GetEncoder()->EncodeJump(throwLabel, tmpReg, Condition::NE);
164 }
165
EtsWrapObjectNative(WrapObjectNativeInst * wrapObject)166 void Codegen::EtsWrapObjectNative(WrapObjectNativeInst *wrapObject)
167 {
168 Inst *obj = wrapObject->GetDataFlowInput(0);
169 ASSERT(obj != nullptr);
170 CallInst *callNative = wrapObject->GetUsers().Front().GetInst()->CastToCallNative();
171 ASSERT(callNative != nullptr);
172
173 ASSERT(GetGraph()->IsAnalysisValid<LivenessAnalyzer>());
174 auto &la = GetGraph()->GetAnalysis<LivenessAnalyzer>();
175 auto targetLifeNumber = la.GetInstLifeIntervals(callNative)->GetBegin();
176
177 auto *interval = la.GetInstLifeIntervals(obj)->FindSiblingAt(targetLifeNumber);
178 ASSERT(interval != nullptr);
179 auto location = interval->GetLocation();
180 ASSERT(location.GetKind() == LocationType::STACK_PARAMETER || location.GetKind() == LocationType::STACK);
181
182 auto dstReg = ConvertRegister(wrapObject->GetDstReg(), wrapObject->GetType());
183 GetEncoder()->EncodeAdd(dstReg, SpReg(), Imm(GetStackOffset(location)));
184 // don't apply stack ref mask, since it is empty
185 ASSERT(GetRuntime()->GetStackReferenceMask() == 0U);
186 }
187
ResolveCallByNameCodegen(ResolveVirtualInst * resolver)188 bool Codegen::ResolveCallByNameCodegen(ResolveVirtualInst *resolver)
189 {
190 SCOPED_DISASM_STR(this, "Create runtime call to resolve a call by name");
191 ASSERT(resolver->GetOpcode() == Opcode::ResolveByName);
192 ASSERT(resolver->GetCallMethod() != nullptr);
193
194 auto methodReg = ConvertRegister(resolver->GetDstReg(), resolver->GetType());
195 auto objectReg = ConvertRegister(resolver->GetSrcReg(0), DataType::REFERENCE);
196 ScopedTmpReg tmpMethodReg(GetEncoder());
197 LoadMethod(tmpMethodReg);
198
199 CallRuntime(resolver, EntrypointId::RESOLVE_CALL_BY_NAME, tmpMethodReg, {}, tmpMethodReg, objectReg,
200 TypedImm(resolver->GetCallMethodId()));
201 GetEncoder()->EncodeMov(methodReg, tmpMethodReg);
202 return true;
203 }
204
205 } // namespace ark::compiler
206