1 /*
2 * Copyright (c) 2023 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 #include "ecmascript/compiler/native_inline_lowering.h"
16 #include "ecmascript/compiler/circuit_builder-inl.h"
17 #include "ecmascript/compiler/circuit_builder_helper.h"
18 #include "ecmascript/compiler/circuit.h"
19 #include "ecmascript/js_thread.h"
20 #include "ecmascript/message_string.h"
21
22 namespace panda::ecmascript::kungfu {
RunNativeInlineLowering()23 void NativeInlineLowering::RunNativeInlineLowering()
24 {
25 std::vector<GateRef> gateList;
26 circuit_->GetAllGates(gateList);
27 for (const auto &gate : gateList) {
28 auto op = acc_.GetOpCode(gate);
29 if (op == OpCode::JS_BYTECODE) {
30 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
31 switch (ecmaOpcode) {
32 case EcmaOpcode::CALLTHIS1_IMM8_V8_V8:
33 TryInlineNativeCallThis1(gate);
34 break;
35 default:
36 break;
37 }
38 }
39 }
40
41 if (EnableLog()) {
42 LOG_COMPILER(INFO) << " ";
43 LOG_COMPILER(INFO) << "\033[34m" << "================="
44 << " After Native Inline Lowering "
45 << "[" << GetMethodName() << "] "
46 << "=================" << "\033[0m";
47 circuit_->PrintAllGatesWithBytecode();
48 LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m";
49 }
50 }
51
TryInlineNativeCallThis1(GateRef gate)52 void NativeInlineLowering::TryInlineNativeCallThis1(GateRef gate)
53 {
54 CallThis1TypeInfoAccessor tacc(thread_, circuit_, gate);
55 BuiltinsStubCSigns::ID id = tacc.TryGetPGOBuiltinId();
56 switch (id) {
57 case BuiltinsStubCSigns::ID::StringFromCharCode: {
58 TryInlineStringFromCharCode(gate, tacc);
59 break;
60 }
61 default:
62 break;
63 }
64 }
65
TryInlineStringFromCharCode(GateRef gate,CallThis1TypeInfoAccessor & tacc)66 void NativeInlineLowering::TryInlineStringFromCharCode(GateRef gate, CallThis1TypeInfoAccessor &tacc)
67 {
68 if (acc_.GetByteCodeOpcode(gate) != EcmaOpcode::CALLTHIS1_IMM8_V8_V8) {
69 return;
70 }
71 Environment env(gate, circuit_, &builder_);
72 if (!Uncheck()) {
73 builder_.CallTargetCheck(gate, tacc.GetFunc(),
74 builder_.IntPtr(static_cast<int64_t>(BuiltinsStubCSigns::ID::StringFromCharCode)),
75 tacc.GetArg0());
76 }
77 GateRef ret = builder_.StringFromSingleCharCode(tacc.GetArg0());
78 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), ret);
79 }
80
TryInlineBuiltinsArrayFunc(GateRef gate)81 void NativeInlineLowering::TryInlineBuiltinsArrayFunc(GateRef gate)
82 {
83 GateRef func = acc_.GetValueIn(gate, acc_.GetNumValueIn(gate) - 1); // 1: last value in
84 GateType funcType = acc_.GetGateType(func);
85 if (tsManager_->IsBuiltinObjectMethod(BuiltinTypeId::ARRAY, funcType)) {
86 std::string name = tsManager_->GetFuncName(funcType);
87 if (name == "forEach") {
88 RunArrayForeachInline(gate);
89 }
90 }
91 }
92
RunArrayForeachInline(GateRef gate)93 void NativeInlineLowering::RunArrayForeachInline(GateRef gate)
94 {
95 Environment env(gate, circuit_, &builder_);
96
97 GateRef thisObj = acc_.GetValueIn(gate, 0);
98 GateRef callBack = acc_.GetValueIn(gate, 1);
99 GateRef thisArg = builder_.Undefined();
100 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
101 if (ecmaOpcode == EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8) {
102 thisArg = acc_.GetValueIn(gate, 2); // 2: this arg parameter
103 }
104 builder_.BuiltinPrototypeHClassCheck(thisObj, BuiltinTypeId::ARRAY);
105 ElementsKind kind = acc_.TryGetArrayElementsKind(thisObj);
106 if (!IsCreateArray(thisObj)) {
107 builder_.StableArrayCheck(thisObj, kind, ArrayMetaDataAccessor::Mode::LOAD_ELEMENT);
108 }
109 Label callBackIsCallable(&builder_);
110 Label callBackNotCallable(&builder_);
111 builder_.Branch(builder_.IsCallable(callBack), &callBackIsCallable, &callBackNotCallable);
112 builder_.Bind(&callBackNotCallable);
113 {
114 GateRef taggedId = builder_.Int64(GET_MESSAGE_STRING_ID(NonCallable));
115 LowerCallRuntime(glue_, gate, RTSTUB_ID(ThrowTypeError), { builder_.ToTaggedIntPtr(taggedId) }, true);
116 GateRef exception = builder_.ExceptionConstant();
117 builder_.Return(exception);
118 }
119 builder_.Bind(&callBackIsCallable);
120 Label exit(&builder_);
121 ArrayForeachCall(gate, thisObj, callBack, thisArg, &exit);
122 builder_.Bind(&exit);
123 ReplaceHirDirectly(gate, builder_.Undefined());
124 }
125
ArrayForeachCall(GateRef gate,GateRef thisObj,GateRef callBack,GateRef thisArg,Label * exit)126 void NativeInlineLowering::ArrayForeachCall(GateRef gate, GateRef thisObj, GateRef callBack, GateRef thisArg,
127 Label *exit)
128 {
129 GateRef length = builder_.LoadArrayLength(thisObj);
130 Label loopHead(&builder_);
131 Label loopEnd(&builder_);
132 Label exitloop(&builder_);
133 Label slowForeach(&builder_);
134 DEFVALUE(i, (&builder_), VariableType::INT32(), builder_.Int32(0));
135 DEFVALUE(propKey, (&builder_), VariableType::JS_ANY(), builder_.ToTaggedIntPtr(builder_.SExtInt32ToInt64(*i)));
136 DEFVALUE(value, (&builder_), VariableType::JS_ANY(), builder_.Hole());
137 builder_.Branch(builder_.Int32LessThan(*i, length), &loopHead, &exitloop);
138 builder_.LoopBegin(&loopHead);
139 ElementsKind kind = acc_.TryGetArrayElementsKind(thisObj);
140 value = LoadArrayElement(kind, thisObj, *i);
141 Label NotHole(&builder_);
142 Label merge(&builder_);
143 builder_.Branch(builder_.NotEqual(*value, builder_.Hole()), &NotHole, &exitloop);
144 builder_.Bind(&NotHole);
145 {
146 auto depend = builder_.GetDepend();
147 GateRef nativeCall = NativeCallTS(gate, depend,
148 {glue_, builder_.Int64(6), callBack, builder_.Undefined(), thisArg, *value, *propKey, thisObj}); // 6: args
149 builder_.SetDepend(nativeCall);
150 i = builder_.Int32Add(*i, builder_.Int32(1));
151 propKey = builder_.ToTaggedIntPtr(builder_.SExtInt32ToInt64(*i));
152 builder_.Branch(builder_.IsStabelArray(glue_, thisObj), &merge, &exitloop);
153 }
154 builder_.Bind(&merge);
155 builder_.Branch(builder_.Int32LessThan(*i, length), &loopEnd, &exitloop);
156 builder_.Bind(&loopEnd);
157 builder_.LoopEnd(&loopHead);
158 builder_.Bind(&exitloop);
159 builder_.Branch(builder_.Int32LessThan(*i, length), &slowForeach, exit);
160 builder_.Bind(&slowForeach);
161 {
162 GateRef taggedLength = builder_.ToTaggedIntPtr(builder_.SExtInt32ToInt64(length));
163 GateRef slowPathCall = LowerCallRuntime(glue_, gate, RTSTUB_ID(ArrayForEachContinue),
164 {thisArg, *propKey, thisObj, callBack, taggedLength}, true);
165 builder_.SetDepend(slowPathCall);
166 builder_.Jump(exit);
167 }
168 }
169
IsCreateArray(GateRef gate)170 bool NativeInlineLowering::IsCreateArray(GateRef gate)
171 {
172 if (acc_.GetOpCode(gate) != OpCode::JS_BYTECODE) {
173 return false;
174 }
175 EcmaOpcode ecmaop = acc_.GetByteCodeOpcode(gate);
176 switch (ecmaop) {
177 case EcmaOpcode::CREATEEMPTYARRAY_IMM8:
178 case EcmaOpcode::CREATEEMPTYARRAY_IMM16:
179 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
180 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16:
181 return true;
182 default:
183 return false;
184 }
185 UNREACHABLE();
186 return false;
187 }
188
LoadArrayElement(ElementsKind kind,GateRef gate,GateRef index)189 GateRef NativeInlineLowering::LoadArrayElement(ElementsKind kind, GateRef gate, GateRef index)
190 {
191 switch (kind) {
192 case ElementsKind::INT:
193 return builder_.LoadElement<TypedLoadOp::ARRAY_LOAD_INT_ELEMENT>(gate, index);
194 case ElementsKind::NUMBER:
195 return builder_.LoadElement<TypedLoadOp::ARRAY_LOAD_DOUBLE_ELEMENT>(gate, index);
196 case ElementsKind::OBJECT:
197 return builder_.LoadElement<TypedLoadOp::ARRAY_LOAD_OBJECT_ELEMENT>(gate, index);
198 default:
199 if (!Elements::IsHole(kind)) {
200 return builder_.LoadElement<TypedLoadOp::ARRAY_LOAD_TAGGED_ELEMENT>(gate, index);
201 }
202 }
203 return builder_.LoadElement<TypedLoadOp::ARRAY_LOAD_HOLE_TAGGED_ELEMENT>(gate, index);
204 }
205
ReplaceHirDirectly(GateRef gate,GateRef value)206 void NativeInlineLowering::ReplaceHirDirectly(GateRef gate, GateRef value)
207 {
208 GateRef state = builder_.GetState();
209 GateRef depend = builder_.GetDepend();
210 auto uses = acc_.Uses(gate);
211 for (auto it = uses.begin(); it != uses.end();) {
212 if (acc_.IsStateIn(it)) {
213 ASSERT(acc_.GetOpCode(*it) != OpCode::IF_SUCCESS &&
214 acc_.GetOpCode(*it) != OpCode::IF_EXCEPTION);
215 it = acc_.ReplaceIn(it, state);
216 } else if (acc_.IsDependIn(it)) {
217 it = acc_.ReplaceIn(it, depend);
218 } else {
219 ASSERT(acc_.IsValueIn(it));
220 it = acc_.ReplaceIn(it, value);
221 }
222 }
223 acc_.DeleteGate(gate);
224 }
225
LowerCallRuntime(GateRef glue,GateRef hirGate,int index,const std::vector<GateRef> & args,bool useLabel)226 GateRef NativeInlineLowering::LowerCallRuntime(GateRef glue, GateRef hirGate, int index,
227 const std::vector<GateRef> &args, bool useLabel)
228 {
229 if (useLabel) {
230 GateRef result = builder_.CallRuntime(glue, index, Gate::InvalidGateRef, args, hirGate);
231 return result;
232 } else {
233 const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(CallRuntime));
234 GateRef target = builder_.IntPtr(index);
235 GateRef result = builder_.Call(cs, glue, target, acc_.GetDependRoot(), args, hirGate);
236 return result;
237 }
238 }
239
NativeCallTS(GateRef gate,GateRef depend,const std::vector<GateRef> & args)240 GateRef NativeInlineLowering::NativeCallTS(GateRef gate, GateRef depend, const std::vector<GateRef> &args)
241 {
242 const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(JSCall));
243 GateRef target = builder_.IntPtr(RTSTUB_ID(JSCall));
244 GateRef call = builder_.Call(cs, glue_, target, depend, args, gate);
245 return call;
246 }
247 } // namespace panda::ecmascript
248