/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/compiler/builtins_lowering.h" #include "builtins/builtins_string_stub_builder.h" #include "ecmascript/global_env.h" namespace panda::ecmascript::kungfu { void BuiltinLowering::LowerTypedCallBuitin(GateRef gate) { Environment env(gate, circuit_, &builder_); auto valuesIn = acc_.GetNumValueIn(gate); ASSERT(valuesIn >= 1); auto idGate = acc_.GetValueIn(gate, valuesIn - 1); auto id = static_cast(acc_.GetConstantValue(idGate)); if (traceBuiltins_) { AddTraceLogs(gate, id); } switch (id) { case BUILTINS_STUB_ID(StringLocaleCompare): LowerTypedLocaleCompare(gate); break; case BUILTINS_STUB_ID(JsonStringify): LowerTypedStringify(gate); break; case BUILTINS_STUB_ID(MapProtoIterator): case BUILTINS_STUB_ID(SetProtoIterator): case BUILTINS_STUB_ID(StringProtoIterator): case BUILTINS_STUB_ID(ArrayProtoIterator): case BUILTINS_STUB_ID(TypedArrayProtoIterator): LowerBuiltinIterator(gate, id); break; case BUILTINS_STUB_ID(MapIteratorProtoNext): case BUILTINS_STUB_ID(SetIteratorProtoNext): case BUILTINS_STUB_ID(StringIteratorProtoNext): case BUILTINS_STUB_ID(ArrayIteratorProtoNext): LowerIteratorNext(gate, id); break; case BUILTINS_STUB_ID(IteratorProtoReturn): LowerIteratorReturn(gate, id); break; case BUILTINS_STUB_ID(NumberConstructor): LowerNumberConstructor(gate); break; case BUILTINS_STUB_ID(GlobalDecodeURIComponent): LowerGlobalDecodeURIComponent(gate); break; default: LowerCallBuiltinStub(gate, id); break; } } void BuiltinLowering::LowerTypedFloor(GateRef gate) { auto ret = TypedFloor(gate); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), ret); } GateRef BuiltinLowering::TypedLocaleCompare(GateRef glue, GateRef gate, GateRef thisObj, GateRef thatObj) { auto env = builder_.GetCurrentEnvironment(); Label entry(&builder_); env->SubCfgEntry(&entry); Label slowPath(&builder_); Label fastPath(&builder_); Label localeCompareGC(&builder_); Label exit(&builder_); DEFVALUE(result, (&builder_), VariableType::JS_ANY(), builder_.Undefined()); GateRef isString = builder_.BothAreString(glue, thisObj, thatObj); builder_.Branch(isString, &fastPath, &slowPath); builder_.Bind(&fastPath); { result = builder_.CallRuntime(glue, RTSTUB_ID(LocaleCompareCacheable), Gate::InvalidGateRef, { builder_.Undefined(), thisObj, thatObj }, gate); GateRef status = builder_.TaggedIsUndefined(*result); builder_.Branch(status, &localeCompareGC, &exit, BranchWeight::ONE_WEIGHT, BranchWeight::STRONG_WEIGHT, "TypedLocaleCompare"); builder_.Bind(&localeCompareGC); { result = builder_.CallRuntime(glue, RTSTUB_ID(LocaleCompareWithGc), Gate::InvalidGateRef, { builder_.Undefined(), thisObj, thatObj, builder_.Undefined() }, gate); builder_.Jump(&exit); } } builder_.Bind(&slowPath); { result = LowerCallRuntime(glue, gate, RTSTUB_ID(LocaleCompare), { thisObj, thatObj, builder_.Undefined(), builder_.Undefined()}); builder_.Jump(&exit); } builder_.Bind(&exit); auto ret = *result; env->SubCfgExit(); return ret; } GateRef BuiltinLowering::TypedFloor(GateRef gate) { auto env = builder_.GetCurrentEnvironment(); Label entry(&builder_); env->SubCfgEntry(&entry); Label numberBranch(&builder_); Label notNumberBranch(&builder_); Label exit(&builder_); GateRef para1 = acc_.GetValueIn(gate, 0); DEFVALUE(result, (&builder_), VariableType::JS_ANY(), builder_.HoleConstant()); BRANCH_CIR(builder_.TaggedIsNumber(para1), &numberBranch, ¬NumberBranch); builder_.Bind(&numberBranch); { GateRef value = builder_.GetDoubleOfTNumber(para1); Label IsNan(&builder_); Label NotNan(&builder_); GateRef condition = builder_.DoubleIsNAN(value); BRANCH_CIR(condition, &IsNan, &NotNan); builder_.Bind(&NotNan); { GateRef glue = glue_; int index = RTSTUB_ID(FloatFloor); GateRef floor = builder_.CallNGCRuntime(glue, index, Gate::InvalidGateRef, {value}, gate); result = builder_.DoubleToTaggedDoublePtr(floor); builder_.Jump(&exit); } builder_.Bind(&IsNan); { result = builder_.DoubleToTaggedDoublePtr(builder_.Double(base::NAN_VALUE)); builder_.Jump(&exit); } } builder_.Bind(¬NumberBranch); { builder_.Jump(&exit); } builder_.Bind(&exit); auto ret = *result; env->SubCfgExit(); return ret; } GateRef BuiltinLowering::IntToTaggedIntPtr(GateRef x) { GateRef val = builder_.SExtInt32ToInt64(x); return builder_.ToTaggedIntPtr(val); } GateRef BuiltinLowering::LowerCallRuntime(GateRef glue, GateRef gate, int index, const std::vector &args, bool useLabel) { const std::string name = RuntimeStubCSigns::GetRTName(index); if (useLabel) { GateRef result = builder_.CallRuntime(glue, index, Gate::InvalidGateRef, args, gate, name.c_str()); return result; } else { const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(CallRuntime)); GateRef target = builder_.IntPtr(index); GateRef result = builder_.Call(cs, glue, target, builder_.GetDepend(), args, gate, name.c_str()); return result; } } void BuiltinLowering::ReplaceHirWithValue(GateRef hirGate, GateRef value) { acc_.ReplaceHirDirectly(hirGate, builder_.GetStateDepend(), value); } void BuiltinLowering::LowerTypedLocaleCompare(GateRef gate) { GateRef glue = glue_; size_t index = 0; GateRef thisObj = acc_.GetValueIn(gate, index++); GateRef thatObj = acc_.GetValueIn(gate, index++); static constexpr size_t NUM_OF_ARGS = 4; ASSERT(acc_.GetNumValueIn(gate) > 0); size_t argsIn = acc_.GetNumValueIn(gate) - 1; GateRef result = Circuit::NullGate(); if (argsIn == 2) { // 2: string.localeCompare(string) // If the number of args is two, it must satisfy conditions for cache optimization. // The cache of icu collator if locale is undefined result = TypedLocaleCompare(glue, gate, thisObj, thatObj); } else { // willdo: Implement cache fastpath std::vector args = { thisObj, thatObj }; ASSERT(argsIn <= NUM_OF_ARGS); args.reserve(NUM_OF_ARGS); while (index < argsIn) { GateRef arg = acc_.GetValueIn(gate, index++); args.emplace_back(arg); } while (index < NUM_OF_ARGS) { args.emplace_back(builder_.Undefined()); index++; } result = LowerCallRuntime(glue, gate, RTSTUB_ID(LocaleCompare), args); } ReplaceHirWithValue(gate, result); } GateRef BuiltinLowering::LowerCallTargetCheck(Environment *env, GateRef gate) { builder_.SetEnvironment(env); GateRef idGate = acc_.GetValueIn(gate, 1); BuiltinsStubCSigns::ID id = static_cast(acc_.GetConstantValue(idGate)); switch (id) { case BuiltinsStubCSigns::ID::MapProtoIterator: case BuiltinsStubCSigns::ID::SetProtoIterator: case BuiltinsStubCSigns::ID::StringProtoIterator: case BuiltinsStubCSigns::ID::ArrayProtoIterator: case BuiltinsStubCSigns::ID::TypedArrayProtoIterator: { return LowerCallTargetCheckWithDetector(gate, id); } case BuiltinsStubCSigns::ID::DateGetTime: case BuiltinsStubCSigns::ID::MapClear: case BuiltinsStubCSigns::ID::MapDelete: case BuiltinsStubCSigns::ID::MapGet: case BuiltinsStubCSigns::ID::MapHas: case BuiltinsStubCSigns::ID::SetAdd: case BuiltinsStubCSigns::ID::SetClear: case BuiltinsStubCSigns::ID::SetDelete: case BuiltinsStubCSigns::ID::SetHas: { return LowerCallTargetCheckWithObjectType(gate, id); } case BuiltinsStubCSigns::ID::BigIntConstructor: case BuiltinsStubCSigns::ID::NumberConstructor: { return LowerCallTargetCheckWithGlobalEnv(gate, id); } default: { return LowerCallTargetCheckDefault(gate, id); } } } GateRef BuiltinLowering::LowerCallTargetCheckDefault(GateRef gate, BuiltinsStubCSigns::ID id) { GateRef globalEnvFunction = builder_.GetGlobalEnvValue(VariableType::JS_ANY(), glue_, circuit_->GetGlobalEnvCache(), static_cast(GET_TYPED_ENV_FIELD_INEDX(id))); GateRef function = acc_.GetValueIn(gate, 0); // 0: function return builder_.Equal(function, globalEnvFunction); } GateRef BuiltinLowering::LowerCallTargetCheckWithGlobalEnv(GateRef gate, BuiltinsStubCSigns::ID id) { GateRef globalEnv = circuit_->GetGlobalEnvCache(); GateRef globalFunction = builder_.GetGlobalEnvObj(globalEnv, GET_TYPED_GLOBAL_ENV_INDEX(id)); GateRef target = acc_.GetValueIn(gate, 0); // 0:target return builder_.Equal(target, globalFunction); } GateRef BuiltinLowering::LowerCallTargetCheckWithDetector(GateRef gate, BuiltinsStubCSigns::ID id) { JSType expectType = JSType::INVALID; GateRef detectorValue = Gate::InvalidGateRef; switch (id) { case BuiltinsStubCSigns::ID::MapProtoIterator: { expectType = JSType::JS_MAP; detectorValue = builder_.BoolNot(builder_.GetMapIteratorDetector(circuit_->GetGlobalEnvCache())); break; } case BuiltinsStubCSigns::ID::SetProtoIterator: { expectType = JSType::JS_SET; detectorValue = builder_.BoolNot(builder_.GetSetIteratorDetector(circuit_->GetGlobalEnvCache())); break; } case BuiltinsStubCSigns::ID::StringProtoIterator: { expectType = JSType::STRING_FIRST; detectorValue = builder_.BoolNot(builder_.GetStringIteratorDetector(circuit_->GetGlobalEnvCache())); break; } case BuiltinsStubCSigns::ID::ArrayProtoIterator: { expectType = JSType::JS_ARRAY; detectorValue = builder_.BoolNot(builder_.GetArrayIteratorDetector(circuit_->GetGlobalEnvCache())); break; } case BuiltinsStubCSigns::ID::TypedArrayProtoIterator: { expectType = JSType::JS_TYPED_ARRAY_FIRST; detectorValue = builder_.BoolNot(builder_.GetTypedArrayIteratorDetector(circuit_->GetGlobalEnvCache())); break; } default: { LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } GateRef obj = acc_.GetValueIn(gate, 2); // 2: iterator obj return LogicAndBuilder(builder_.GetCurrentEnvironment()) .And(builder_.TaggedIsHeapObjectOp(obj)) .And(builder_.IsSpecificObjectType(obj, expectType)) .And(detectorValue) .Done(); } GateRef BuiltinLowering::LowerCallTargetCheckWithObjectType(GateRef gate, BuiltinsStubCSigns::ID id) { JSType expectType = JSType::INVALID; switch (id) { case BuiltinsStubCSigns::ID::MapClear: case BuiltinsStubCSigns::ID::MapDelete: case BuiltinsStubCSigns::ID::MapGet: case BuiltinsStubCSigns::ID::MapHas: { expectType = JSType::JS_MAP; break; } case BuiltinsStubCSigns::ID::SetAdd: case BuiltinsStubCSigns::ID::SetClear: case BuiltinsStubCSigns::ID::SetDelete: case BuiltinsStubCSigns::ID::SetHas: { expectType = JSType::JS_SET; break; } case BuiltinsStubCSigns::ID::DateGetTime: { expectType = JSType::JS_DATE; break; } default: { LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } GateRef obj = acc_.GetValueIn(gate, 2); // 2: receiver obj return LogicAndBuilder(builder_.GetCurrentEnvironment()) .And(builder_.TaggedIsHeapObjectOp(obj)) .And(builder_.IsSpecificObjectType(obj, expectType)) .And(LowerCallTargetCheckDefault(gate, id)) .Done(); } GateRef BuiltinLowering::CheckPara(GateRef gate, GateRef funcCheck) { GateRef idGate = acc_.GetValueIn(gate, 1); BuiltinsStubCSigns::ID id = static_cast(acc_.GetConstantValue(idGate)); if (IS_TYPED_INLINE_BUILTINS_ID(id)) { // Don't need check param. Param was checked before return funcCheck; } if (IS_TYPED_BUILTINS_ID(id)) { // Don't need check param. Param was checked before return funcCheck; } switch (id) { case BuiltinsStubCSigns::ID::StringLocaleCompare: case BuiltinsStubCSigns::ID::ArrayConcat: case BuiltinsStubCSigns::ID::ArraySort: case BuiltinsStubCSigns::ID::JsonStringify: case BuiltinsStubCSigns::ID::MapProtoIterator: case BuiltinsStubCSigns::ID::SetProtoIterator: case BuiltinsStubCSigns::ID::StringProtoIterator: case BuiltinsStubCSigns::ID::ArrayProtoIterator: case BuiltinsStubCSigns::ID::TypedArrayProtoIterator: case BuiltinsStubCSigns::ID::MapIteratorProtoNext: case BuiltinsStubCSigns::ID::SetIteratorProtoNext: case BuiltinsStubCSigns::ID::StringIteratorProtoNext: case BuiltinsStubCSigns::ID::ArrayIteratorProtoNext: case BuiltinsStubCSigns::ID::IteratorProtoReturn: case BuiltinsStubCSigns::ID::NumberConstructor: case BuiltinsStubCSigns::ID::TypedArrayEntries: case BuiltinsStubCSigns::ID::TypedArrayKeys: case BuiltinsStubCSigns::ID::TypedArrayValues: case BuiltinsStubCSigns::ID::GlobalDecodeURIComponent: // Don't need check para return funcCheck; default: { LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } } void BuiltinLowering::LowerTypedStringify(GateRef gate) { GateRef glue = glue_; GateRef value = acc_.GetValueIn(gate, 1); std::vector args; args.emplace_back(value); GateRef result = LowerCallRuntime(glue, gate, RTSTUB_ID(FastStringify), args); ReplaceHirWithValue(gate, result); } void BuiltinLowering::LowerBuiltinIterator(GateRef gate, BuiltinsStubCSigns::ID id) { GateRef glue = glue_; GateRef obj = acc_.GetValueIn(gate, 0); GateRef result = Circuit::NullGate(); switch (id) { case BUILTINS_STUB_ID(MapProtoIterator): { result = builder_.CallStub(glue, gate, CommonStubCSigns::CreateJSMapIterator, { glue, obj }); break; } case BUILTINS_STUB_ID(SetProtoIterator): { result = builder_.CallStub(glue, gate, CommonStubCSigns::CreateJSSetIterator, { glue, obj }); break; } case BUILTINS_STUB_ID(StringProtoIterator): { result = LowerCallRuntime(glue, gate, RTSTUB_ID(CreateStringIterator), { obj }, true); break; } case BUILTINS_STUB_ID(ArrayProtoIterator): { result = LowerCallRuntime(glue, gate, RTSTUB_ID(NewJSArrayIterator), { obj }, true); break; } case BUILTINS_STUB_ID(TypedArrayProtoIterator): { result = LowerCallRuntime(glue, gate, RTSTUB_ID(NewJSTypedArrayIterator), { obj }, true); break; } default: UNREACHABLE(); } ReplaceHirWithValue(gate, result); } void BuiltinLowering::LowerIteratorNext(GateRef gate, BuiltinsStubCSigns::ID id) { GateRef glue = glue_; GateRef thisObj = acc_.GetValueIn(gate, 0); GateRef result = Circuit::NullGate(); switch (id) { case BUILTINS_STUB_ID(MapIteratorProtoNext): { result = builder_.CallStub(glue, gate, CommonStubCSigns::MapIteratorNext, {glue, thisObj, circuit_->GetGlobalEnvCache()}); break; } case BUILTINS_STUB_ID(SetIteratorProtoNext): { result = builder_.CallStub(glue, gate, CommonStubCSigns::SetIteratorNext, {glue, thisObj, circuit_->GetGlobalEnvCache()}); break; } case BUILTINS_STUB_ID(StringIteratorProtoNext): { result = builder_.CallStub(glue, gate, CommonStubCSigns::StringIteratorNext, {glue, thisObj, circuit_->GetGlobalEnvCache()}); break; } case BUILTINS_STUB_ID(ArrayIteratorProtoNext): { result = builder_.CallStub(glue, gate, CommonStubCSigns::ArrayIteratorNext, {glue, thisObj, circuit_->GetGlobalEnvCache()}); break; } default: UNREACHABLE(); } ReplaceHirWithValue(gate, result); } void BuiltinLowering::LowerIteratorReturn(GateRef gate, BuiltinsStubCSigns::ID id) { GateRef glue = glue_; GateRef thisObj = acc_.GetValueIn(gate, 0); GateRef result = Circuit::NullGate(); switch (id) { case BUILTINS_STUB_ID(IteratorProtoReturn): { result = LowerCallRuntime(glue, gate, RTSTUB_ID(IteratorReturn), { thisObj }, true); break; } default: UNREACHABLE(); } ReplaceHirWithValue(gate, result); } void BuiltinLowering::LowerNumberConstructor(GateRef gate) { auto env = builder_.GetCurrentEnvironment(); GateRef glue = glue_; DEFVALUE(result, (&builder_), VariableType::JS_ANY(), IntToTaggedIntPtr(builder_.Int32(0))); GateRef param = acc_.GetValueIn(gate, 0); Label exit(env); Label isNumber(env); Label notNumber(env); BRANCH_CIR(builder_.TaggedIsNumber(param), &isNumber, ¬Number); builder_.Bind(&isNumber); { result = param; builder_.Jump(&exit); } builder_.Bind(¬Number); { Label isLineUtf8String(env); Label notLineUtf8String(env); // only line string can be integer, because length of sliced string and tree string is at least 13 BRANCH_CIR(builder_.TaggedIsLineUtf8String(glue, param), &isLineUtf8String, ¬LineUtf8String); builder_.Bind(&isLineUtf8String); { Label nonZeroLength(env); auto length = builder_.GetLengthFromString(param); BRANCH_CIR(builder_.Equal(length, builder_.Int32(0)), &exit, &nonZeroLength); builder_.Bind(&nonZeroLength); Label isInteger(env); BuiltinsStringStubBuilder stringStub(builder_.GetCurrentEnvironment(), circuit_->GetGlobalEnvCache()); GateRef dataUtf8 = builder_.PtrAdd(param, builder_.IntPtr(LineString::DATA_OFFSET)); GateRef res = stringStub.StringDataToUint(dataUtf8, length, std::numeric_limits::max()); BRANCH_CIR(builder_.Int64NotEqual(res, builder_.Int64(-1)), &isInteger, ¬LineUtf8String); builder_.Bind(&isInteger); { result = IntToTaggedIntPtr(res); builder_.Jump(&exit); } } builder_.Bind(¬LineUtf8String); { result = LowerCallRuntime(glue, gate, RTSTUB_ID(ToNumericConvertBigInt), { param }, true); builder_.Jump(&exit); } } builder_.Bind(&exit); ReplaceHirWithValue(gate, *result); } void BuiltinLowering::LowerGlobalDecodeURIComponent(GateRef gate) { GateRef glue = glue_; GateRef param = acc_.GetValueIn(gate, 0); GateRef result = LowerCallRuntime(glue, gate, RTSTUB_ID(DecodeURIComponent), { param }, true); ReplaceHirWithValue(gate, result); } void BuiltinLowering::LowerCallBuiltinStub(GateRef gate, BuiltinsStubCSigns::ID id) { Environment env(gate, circuit_, &builder_); size_t numIn = acc_.GetNumValueIn(gate); GateRef glue = glue_; GateRef function = builder_.GetGlobalEnvValue(VariableType::JS_ANY(), glue, circuit_->GetGlobalEnvCache(), static_cast(GET_TYPED_ENV_FIELD_INEDX(id))); GateRef nativeCode = builder_.LoadWithoutBarrier(VariableType::NATIVE_POINTER(), function, builder_.IntPtr(JSFunction::CODE_ENTRY_OFFSET)); std::vector args(static_cast(BuiltinsArgs::NUM_OF_INPUTS), builder_.Undefined()); args[static_cast(BuiltinsArgs::GLUE)] = glue; args[static_cast(BuiltinsArgs::NATIVECODE)] = nativeCode; args[static_cast(BuiltinsArgs::FUNC)] = function; args[static_cast(BuiltinsArgs::NEWTARGET)] = builder_.Undefined(); args[static_cast(BuiltinsArgs::THISVALUE)] = acc_.GetValueIn(gate, 0); args[static_cast(BuiltinsArgs::NUMARGS)] = builder_.Int32(numIn - 2); // 2: skip thisValue and id for (size_t idx = 1; idx < numIn - 1; idx++) { args[static_cast(BuiltinsArgs::ARG0_OR_ARGV) + idx - 1] = acc_.GetValueIn(gate, idx); } const CallSignature *cs = BuiltinsStubCSigns::BuiltinsCSign(); size_t ptrSize = env.Is32Bit() ? sizeof(uint32_t) : sizeof(uint64_t); GateRef target = builder_.IntPtr(static_cast(id) * ptrSize); GateRef ret = builder_.Call(cs, glue, target, builder_.GetDepend(), args, gate, BuiltinsStubCSigns::GetBuiltinName(id).c_str()); ReplaceHirWithValue(gate, ret); } void BuiltinLowering::AddTraceLogs(GateRef gate, BuiltinsStubCSigns::ID id) { size_t index = RTSTUB_ID(AotCallBuiltinTrace); GateRef frameArgs = acc_.GetFrameArgs(gate); GateRef callerFunc = acc_.GetValueIn(frameArgs, static_cast(FrameArgIdx::FUNC)); std::vector args{callerFunc, builder_.Int32ToTaggedInt(builder_.Int32(id))}; GateRef trace = builder_.CallRuntime(glue_, index, Gate::InvalidGateRef, args, gate); acc_.SetDep(gate, trace); builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: profiling or STATE_SPLIT } } // namespace panda::ecmascript::kungfu