• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 "optimizer/ir_builder/inst_builder.h"
17 #include "optimizer/ir_builder/ir_builder.h"
18 #include "optimizer/ir/inst.h"
19 #include "bytecode_instruction.h"
20 #include "bytecode_instruction-inl.h"
21 
22 namespace ark::compiler {
23 
24 template <RuntimeInterface::IntrinsicId ID, DataType::Type RET_TYPE, DataType::Type... PARAM_TYPES>
25 struct IntrinsicBuilder {
26     static constexpr size_t N = sizeof...(PARAM_TYPES);
27     template <typename... ARGS>
Buildark::compiler::IntrinsicBuilder28     static IntrinsicInst *Build(InstBuilder *ib, size_t pc, const ARGS &...inputs)
29     {
30         static_assert(sizeof...(inputs) == N + 1);
31         return ib->BuildInteropIntrinsic<N>(pc, ID, RET_TYPE, {PARAM_TYPES...}, {inputs...});
32     }
33 };
34 
35 using IntrinsicCompilerConvertJSValueToLocal =
36     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_CONVERT_JS_VALUE_TO_LOCAL, DataType::POINTER,
37                      DataType::REFERENCE>;
38 using IntrinsicCompilerResolveQualifiedJSCall =
39     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_RESOLVE_QUALIFIED_JS_CALL, DataType::POINTER,
40                      DataType::POINTER, DataType::UINT32, DataType::UINT32, DataType::UINT32>;
41 using IntrinsicCompilerLoadResolvedJSCallFunction =
42     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_LOAD_RESOLVED_JS_FUNCTION, DataType::POINTER,
43                      DataType::POINTER>;
44 using IntrinsicCompilerConvertLocalToJSValue =
45     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_CONVERT_LOCAL_TO_JS_VALUE, DataType::REFERENCE,
46                      DataType::POINTER>;
47 using IntrinsicCompilerCreateLocalScope =
48     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_CREATE_LOCAL_SCOPE, DataType::VOID>;
49 using IntrinsicCompilerDestroyLocalScope =
50     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_DESTROY_LOCAL_SCOPE, DataType::VOID>;
51 using IntrinsicCompilerJSCallCheck = IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_JS_CALL_CHECK,
52                                                       DataType::POINTER, DataType::POINTER>;
53 using IntrinsicCompilerInitJSCallClassForCtx =
54     IntrinsicBuilder<RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_INIT_JS_CALL_CLASS_FOR_CTX, DataType::VOID,
55                      DataType::REFERENCE>;
56 
57 template <size_t N>
BuildInteropIntrinsic(size_t pc,RuntimeInterface::IntrinsicId id,DataType::Type retType,const std::array<DataType::Type,N> & types,const std::array<Inst *,N+1> & inputs)58 IntrinsicInst *InstBuilder::BuildInteropIntrinsic(size_t pc, RuntimeInterface::IntrinsicId id, DataType::Type retType,
59                                                   const std::array<DataType::Type, N> &types,
60                                                   const std::array<Inst *, N + 1> &inputs)
61 {
62     auto intrinsic = GetGraph()->CreateInstIntrinsic(retType, pc, id);
63     intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), N + 1);
64     for (size_t i = 0; i < N; ++i) {
65         intrinsic->AppendInput(inputs[i], types[i]);
66     }
67     intrinsic->AppendInput(inputs[N], DataType::NO_TYPE);  // SaveState input
68     AddInstruction(intrinsic);
69     return intrinsic;
70 }
71 
BuildInitJSCallClass(RuntimeInterface::MethodPtr method,size_t pc,SaveStateInst * saveState)72 Inst *InstBuilder::BuildInitJSCallClass(RuntimeInterface::MethodPtr method, size_t pc, SaveStateInst *saveState)
73 {
74     auto klass = GetRuntime()->GetClass(method);
75     auto cpOffsetField = GetRuntime()->GetInteropConstantPoolOffsetField(klass);
76     auto *cpOffsetForClass =
77         BuildLoadStaticInst(pc, DataType::UINT32, GetRuntime()->GetFieldId(cpOffsetField), saveState);
78     AddInstruction(cpOffsetForClass);
79     auto *loadAndInitJSCallClass = cpOffsetForClass->GetInput(0).GetInst();
80 
81     // Preload JS strings forming qualified name. Rely on GVN
82     IntrinsicCompilerInitJSCallClassForCtx::Build(this, pc, loadAndInitJSCallClass, saveState);
83     return cpOffsetForClass;
84 }
85 
BuildResolveInteropCallIntrinsic(RuntimeInterface::InteropCallKind callKind,size_t pc,RuntimeInterface::MethodPtr method,Inst * arg0,Inst * arg1,Inst * arg2,Inst * cpOffsetForClass,SaveStateInst * saveState)86 std::pair<Inst *, Inst *> InstBuilder::BuildResolveInteropCallIntrinsic(RuntimeInterface::InteropCallKind callKind,
87                                                                         size_t pc, RuntimeInterface::MethodPtr method,
88                                                                         Inst *arg0, Inst *arg1, Inst *arg2,
89                                                                         Inst *cpOffsetForClass,
90                                                                         SaveStateInst *saveState)
91 {
92     IntrinsicInst *jsThis = nullptr;
93     IntrinsicInst *jsFn = nullptr;
94     if (callKind == RuntimeInterface::InteropCallKind::CALL_BY_VALUE) {
95         ASSERT(arg2 == nullptr);
96         jsFn = IntrinsicCompilerConvertJSValueToLocal::Build(this, pc, arg0, saveState);
97         jsThis = IntrinsicCompilerConvertJSValueToLocal::Build(this, pc, arg1, saveState);
98     } else {
99         ASSERT(arg2 != nullptr);
100         auto jsVal = IntrinsicCompilerConvertJSValueToLocal::Build(this, pc, arg0, saveState);
101         // `arg1` and `arg2` are int constants, but may be phi instructions in IRBuilder
102         // We replace `ResolveQualifiedJSCall` by chain of `GetNamedProperty` intrinsics in Peepholes
103 
104         jsThis =
105             IntrinsicCompilerResolveQualifiedJSCall::Build(this, pc, jsVal, arg1, arg2, cpOffsetForClass, saveState);
106         jsThis->SetMethod(method);
107         jsFn = IntrinsicCompilerLoadResolvedJSCallFunction::Build(this, pc, jsThis, saveState);
108     }
109     return {jsThis, jsFn};
110 }
111 
CreateInteropCallIntrinsic(size_t pc,RuntimeInterface::InteropCallKind callKind)112 IntrinsicInst *InstBuilder::CreateInteropCallIntrinsic(size_t pc, RuntimeInterface::InteropCallKind callKind)
113 {
114     RuntimeInterface::IntrinsicId callId = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_JS_CALL_FUNCTION;
115     if (callKind == RuntimeInterface::InteropCallKind::NEW_INSTANCE) {
116         callId = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_JS_NEW_INSTANCE;
117     }
118     return GetGraph()->CreateInstIntrinsic(DataType::POINTER, pc, callId);
119 }
120 
BuildReturnValueConvertInteropIntrinsic(RuntimeInterface::InteropCallKind callKind,size_t pc,RuntimeInterface::MethodPtr method,Inst * jsCall,SaveStateInst * saveState)121 void InstBuilder::BuildReturnValueConvertInteropIntrinsic(RuntimeInterface::InteropCallKind callKind, size_t pc,
122                                                           RuntimeInterface::MethodPtr method, Inst *jsCall,
123                                                           SaveStateInst *saveState)
124 {
125     if (callKind == RuntimeInterface::InteropCallKind::NEW_INSTANCE) {
126         auto ret = IntrinsicCompilerConvertLocalToJSValue::Build(this, pc, jsCall, saveState);
127         UpdateDefinitionAcc(ret);
128     } else {
129         auto retIntrinsicId = GetGraph()->GetRuntime()->GetInfoForInteropCallRetValueConversion(method);
130         if (retIntrinsicId.has_value()) {
131             Inst *ret = nullptr;
132             auto [id, retType] = retIntrinsicId.value();
133             if (id == RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_CONVERT_LOCAL_TO_REF_TYPE) {
134                 auto loadClass = BuildLoadClass(GetRuntime()->GetMethodReturnTypeId(method), pc, saveState);
135                 loadClass->ClearFlag(inst_flags::NO_HOIST);
136                 AddInstruction(loadClass);
137                 // LoadClass returns ref, create a new SaveState
138                 saveState = CreateSaveState(Opcode::SaveState, pc);
139                 AddInstruction(saveState);
140                 ret = BuildInteropIntrinsic<2U>(pc, id, retType, {DataType::REFERENCE, DataType::POINTER},
141                                                 {loadClass, jsCall, saveState});
142             } else {
143                 ret = BuildInteropIntrinsic<1>(pc, id, retType, {DataType::POINTER}, {jsCall, saveState});
144             }
145             UpdateDefinitionAcc(ret);
146         }
147     }
148 }
149 
BuildInteropCall(const BytecodeInstruction * bcInst,RuntimeInterface::InteropCallKind callKind,RuntimeInterface::MethodPtr method,bool isRange,bool accRead)150 void InstBuilder::BuildInteropCall(const BytecodeInstruction *bcInst, RuntimeInterface::InteropCallKind callKind,
151                                    RuntimeInterface::MethodPtr method, bool isRange, bool accRead)
152 {
153     auto pc = GetPc(bcInst->GetAddress());
154     auto saveState = CreateSaveState(Opcode::SaveState, pc);
155     AddInstruction(saveState);
156 
157     // Create LOCAL scope
158     IntrinsicCompilerCreateLocalScope::Build(this, pc, saveState);
159     // Resolve call target
160     Inst *arg2 = nullptr;
161     uint32_t skipArgs = 2;
162     Inst *cpOffsetForClass = nullptr;
163     if (callKind != RuntimeInterface::InteropCallKind::CALL_BY_VALUE) {
164         cpOffsetForClass = BuildInitJSCallClass(method, pc, saveState);
165         arg2 = GetArgDefinition(bcInst, 2U, accRead, isRange);
166         skipArgs++;
167     }
168 
169     auto [jsThis, jsFn] = BuildResolveInteropCallIntrinsic(
170         callKind, pc, method, GetArgDefinition(bcInst, 0, accRead, isRange),
171         GetArgDefinition(bcInst, 1, accRead, isRange), arg2, cpOffsetForClass, saveState);
172     // js call check
173     auto jsCallCheck = IntrinsicCompilerJSCallCheck::Build(this, pc, jsFn, saveState);
174 
175     // js call
176     auto jsCall = CreateInteropCallIntrinsic(pc, callKind);
177     ArenaVector<std::pair<RuntimeInterface::IntrinsicId, DataType::Type>> intrinsicsIds(
178         GetGraph()->GetLocalAllocator()->Adapter());
179     GetGraph()->GetRuntime()->GetInfoForInteropCallArgsConversion(method, skipArgs, &intrinsicsIds);
180     if (callKind != RuntimeInterface::InteropCallKind::NEW_INSTANCE) {
181         jsCall->AllocateInputTypes(GetGraph()->GetAllocator(), intrinsicsIds.size() + 4U);
182         jsCall->AppendInput(jsThis, DataType::POINTER);
183     } else {
184         jsCall->AllocateInputTypes(GetGraph()->GetAllocator(), intrinsicsIds.size() + 3U);
185     }
186     jsCall->AppendInputs(
187         {{jsCallCheck, DataType::POINTER}, {GetGraph()->FindOrCreateConstant(intrinsicsIds.size()), DataType::UINT32}});
188 
189     // Convert args
190     size_t argIdx = 0;
191     for (auto [intrinsicId, type] : intrinsicsIds) {
192         Inst *arg = nullptr;
193         if (type != DataType::NO_TYPE) {
194             arg = BuildInteropIntrinsic<1>(pc, intrinsicId, DataType::POINTER, {type},
195                                            {GetArgDefinition(bcInst, argIdx + skipArgs, accRead, isRange), saveState});
196         } else {
197             arg = BuildInteropIntrinsic<0>(pc, intrinsicId, DataType::POINTER, {}, {saveState});
198         }
199         jsCall->AppendInput(arg, DataType::POINTER);
200         argIdx++;
201     }
202 
203     jsCall->AppendInput(saveState, DataType::NO_TYPE);
204     AddInstruction(jsCall);
205 
206     // Convert ret value
207     BuildReturnValueConvertInteropIntrinsic(callKind, pc, method, jsCall, saveState);
208     // Create new a SaveState because instruction with ref value was built
209     saveState = CreateSaveState(Opcode::SaveState, pc);
210     AddInstruction(saveState);
211     // Destroy handle scope
212     IntrinsicCompilerDestroyLocalScope::Build(this, pc, saveState);
213 }
214 
TryBuildInteropCall(const BytecodeInstruction * bcInst,bool isRange,bool accRead)215 bool InstBuilder::TryBuildInteropCall(const BytecodeInstruction *bcInst, bool isRange, bool accRead)
216 {
217     auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), bcInst->GetId(0).AsIndex());
218     auto method = GetRuntime()->GetMethodById(GetMethod(), methodId);
219     if (g_options.IsCompilerEnableFastInterop()) {
220         auto interopCallKind = GetRuntime()->GetInteropCallKind(method);
221         if (interopCallKind != RuntimeInterface::InteropCallKind::UNKNOWN) {
222             auto arg1 = GetArgDefinition(bcInst, 1, accRead, isRange);
223             if (arg1->GetType() == DataType::REFERENCE) {
224                 // arg1 is String - this should appear only in tests
225                 return false;
226             }
227             BuildInteropCall(bcInst, interopCallKind, method, isRange, accRead);
228             return true;
229         }
230     }
231     return false;
232 }
233 }  // namespace ark::compiler
234