From e85a323d24451d3665f7c9b8df24c424ce02a326 Mon Sep 17 00:00:00 2001 From: xwcai98 Date: Fri, 25 Jul 2025 10:53:14 +0800 Subject: [PATCH 1/2] ASMItpEntry call IRBuiltin Issue: Signed-off-by: xwcai98 Change-Id: Ia085c78928a317a43a3c617afff2939b5749b05c --- .../aarch64/asm_interpreter_call.cpp | 104 ++++++++++++++++- .../trampoline/aarch64/common_call.cpp | 23 ++++ .../compiler/trampoline/aarch64/common_call.h | 6 +- .../trampoline/aarch64/optimized_call.cpp | 23 ---- .../trampoline/x64/asm_interpreter_call.cpp | 110 ++++++++++++++++-- .../compiler/trampoline/x64/common_call.cpp | 13 +++ .../compiler/trampoline/x64/common_call.h | 5 +- .../trampoline/x64/optimized_call.cpp | 13 --- test/moduletest/calltype/calltype.js | 9 ++ test/moduletest/calltype/expect_output.txt | 2 + 10 files changed, 255 insertions(+), 53 deletions(-) diff --git a/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp b/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp index 0a38a55fb0..4b7012c23d 100644 --- a/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp +++ b/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp @@ -1910,6 +1910,8 @@ void AsmInterpreterCall::CallBCStub(ExtendedAssembler *assembler, Register &newS void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJSFunction) { + Label callFastBuiltin; + Label callNativeBuiltin; Register glue(X0); Register argv(X5); Register function(X1); @@ -1917,21 +1919,26 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS Register temp(X9); // get native pointer if (isJSFunction) { + Register callFieldRegister = __ CallDispatcherArgument(kungfu::CallDispatchInputs::CALL_FIELD); __ Ldr(nativeCode, MemoryOperand(function, JSFunctionBase::CODE_ENTRY_OFFSET)); + __ Tbnz(callFieldRegister, MethodLiteral::IsFastBuiltinBit::START_BIT, &callFastBuiltin); + } else { + // JSProxy or JSBoundFunction + Register method(X2); + __ Ldr(nativeCode, MemoryOperand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET)); + } - Label next; + __ Bind(&callNativeBuiltin); + if (isJSFunction) { Register lexicalEnv = temp; + + Label next; __ Ldr(lexicalEnv, MemoryOperand(function, JSFunction::LEXICAL_ENV_OFFSET)); __ Cmp(lexicalEnv, Immediate(JSTaggedValue::VALUE_UNDEFINED)); __ B(Condition::EQ, &next); __ Str(lexicalEnv, MemoryOperand(glue, JSThread::GlueData::GetCurrentEnvOffset(false))); __ Bind(&next); - } else { - // JSProxy or JSBoundFunction - Register method(X2); - __ Ldr(nativeCode, MemoryOperand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET)); } - Register sp(SP); // 2: function & align __ Stp(function, Register(Zero), MemoryOperand(sp, -2 * FRAME_SLOT_SIZE, AddrMode::PREINDEX)); @@ -1945,6 +1952,91 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS // 4: skip function __ Add(sp, sp, Immediate(4 * FRAME_SLOT_SIZE)); __ Ret(); + + __ Bind(&callFastBuiltin); + CallFastBuiltin(assembler, &callNativeBuiltin); +} + +void AsmInterpreterCall::CallFastBuiltin(ExtendedAssembler *assembler, Label *callNativeBuiltin) +{ + Label lCall1; + Label lCall2; + Label lCall3; + Label callEntry; + Register sp(SP); + Register glue(X0); + Register function(X1); + Register method(X2); + Register argc(X4); + Register argv(X5); + Register nativeCode(X7); + + Register builtinId = __ AvailableRegister1(); + Register temp = __ AvailableRegister2(); + // get builtinid + __ Ldr(builtinId, MemoryOperand(method, Method::EXTRA_LITERAL_INFO_OFFSET)); // get extra literal + __ And(builtinId.W(), builtinId.W(), LogicalImmediate::Create(0xff, RegWSize)); + __ Cmp(builtinId.W(), Immediate(BUILTINS_STUB_ID(BUILTINS_CONSTRUCTOR_STUB_FIRST))); + __ B(Condition::GE, callNativeBuiltin); + + __ Cmp(argc, Immediate(3)); // 3: number of args + __ B(Condition::HI, callNativeBuiltin); + + // get builtin func addr + __ Add(builtinId, glue, Operand(builtinId.W(), UXTW, FRAME_SLOT_SIZE_LOG2)); + __ Ldr(builtinId, MemoryOperand(builtinId, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false))); + // create frame + PushAsmBridgeFrame(assembler); + __ Mov(temp, function); + __ Mov(X1, nativeCode); + __ Mov(X2, temp); + __ Mov(temp, argv); + __ Mov(X5, argc); + __ Ldr(X3, MemoryOperand(temp, FRAME_SLOT_SIZE)); + __ Ldr(X4, MemoryOperand(temp, DOUBLE_SLOT_SIZE)); + + __ Cmp(Register(X5), Immediate(0)); + __ B(Condition::NE, &lCall1); + __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED)); + __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); + __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); + __ B(&callEntry); + + __ Bind(&lCall1); + { + __ Cmp(Register(X5), Immediate(1)); + __ B(Condition::NE, &lCall2); + __ Ldr(Register(X6), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); + __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); // reset x7 + __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); + __ B(&callEntry); + } + + __ Bind(&lCall2); + { + __ Cmp(Register(X5), Immediate(2)); // 2: number of args + __ B(Condition::NE, &lCall3); + __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); + __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); + __ Ldp(Register(X6), Register(X7), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); + __ B(&callEntry); + } + + __ Bind(&lCall3); + { + __ Ldr(Register(X7), MemoryOperand(temp, QUINTUPLE_SLOT_SIZE)); + __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); + __ Ldp(Register(X6), Register(X7), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); // get arg0 arg1 + __ B(&callEntry); + } + + __ Bind(&callEntry); + { + __ Blr(builtinId); + __ Add(sp, sp, Immediate(DOUBLE_SLOT_SIZE)); + } + PopAsmBridgeFrame(assembler); + __ Ret(); } void AsmInterpreterCall::ThrowStackOverflowExceptionAndReturn(ExtendedAssembler *assembler, Register glue, diff --git a/ecmascript/compiler/trampoline/aarch64/common_call.cpp b/ecmascript/compiler/trampoline/aarch64/common_call.cpp index 9d74269ddf..c3c8eb4b22 100644 --- a/ecmascript/compiler/trampoline/aarch64/common_call.cpp +++ b/ecmascript/compiler/trampoline/aarch64/common_call.cpp @@ -177,5 +177,28 @@ void CommonCall::StackOverflowCheck(ExtendedAssembler *assembler, Register glue, __ Cbz(op, stackOverflow); __ Bind(&skipThrow); } + +void CommonCall::PushAsmBridgeFrame(ExtendedAssembler *assembler) +{ + Register sp(SP); + TempRegister2Scope temp2Scope(assembler); + Register frameType = __ TempRegister2(); + __ PushFpAndLr(); + // construct frame + __ Mov(frameType, Immediate(static_cast(FrameType::ASM_BRIDGE_FRAME))); + // 2 : 2 means pairs. X19 means calleesave and 16bytes align + __ Stp(Register(X19), frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX)); + __ Add(Register(FP), sp, Immediate(DOUBLE_SLOT_SIZE)); +} + +void CommonCall::PopAsmBridgeFrame(ExtendedAssembler *assembler) +{ + TempRegister2Scope temp2Scope(assembler); + Register sp(SP); + Register frameType = __ TempRegister2(); + // 2 : 2 means pop call site sp and type + __ Ldp(Register(X19), frameType, MemoryOperand(sp, FRAME_SLOT_SIZE * 2, AddrMode::POSTINDEX)); + __ RestoreFpAndLr(); +} #undef __ } // panda::ecmascript::aarch64 \ No newline at end of file diff --git a/ecmascript/compiler/trampoline/aarch64/common_call.h b/ecmascript/compiler/trampoline/aarch64/common_call.h index 923fb9d838..ac739c6b33 100644 --- a/ecmascript/compiler/trampoline/aarch64/common_call.h +++ b/ecmascript/compiler/trampoline/aarch64/common_call.h @@ -73,6 +73,8 @@ public: Register op, Label *stackOverflow); static void PushLeaveFrame(ExtendedAssembler *assembler, Register glue); static void PopLeaveFrame(ExtendedAssembler *assembler); + static void PushAsmBridgeFrame(ExtendedAssembler *assembler); + static void PopAsmBridgeFrame(ExtendedAssembler *assembler); }; class OptimizedCall : public CommonCall { @@ -129,8 +131,6 @@ private: int64_t numExtraArgs = 0); static void PushOptimizedArgsConfigFrame(ExtendedAssembler *assembler); static void PopOptimizedArgsConfigFrame(ExtendedAssembler *assembler); - static void PushAsmBridgeFrame(ExtendedAssembler *assembler); - static void PopAsmBridgeFrame(ExtendedAssembler *assembler); static void JSCallInternal(ExtendedAssembler *assembler, Register jsfunc, bool isNew = false); static void CallBuiltinTrampoline(ExtendedAssembler *assembler); static void CallBuiltinConstructorStub(ExtendedAssembler *assembler, Register builtinStub, Register argv, @@ -280,6 +280,8 @@ private: static void CallNativeEntry(ExtendedAssembler *assembler, bool isJSFunction); + static void CallFastBuiltin(ExtendedAssembler *assembler, Label *callNativeBuiltin); + static void CallNativeWithArgv(ExtendedAssembler *assembler, bool callNew, bool hasNewTarget = false); static void PreserveMostCall(ExtendedAssembler* assembler); friend class OptimizedCall; diff --git a/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp b/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp index 3535e62a17..58b52fac90 100644 --- a/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp +++ b/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp @@ -1171,29 +1171,6 @@ void OptimizedCall::PopOptimizedArgsConfigFrame(ExtendedAssembler *assembler) __ RestoreFpAndLr(); } -void OptimizedCall::PushAsmBridgeFrame(ExtendedAssembler *assembler) -{ - Register sp(SP); - TempRegister2Scope temp2Scope(assembler); - Register frameType = __ TempRegister2(); - __ PushFpAndLr(); - // construct frame - __ Mov(frameType, Immediate(static_cast(FrameType::ASM_BRIDGE_FRAME))); - // 2 : 2 means pairs. X19 means calleesave and 16bytes align - __ Stp(Register(X19), frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX)); - __ Add(Register(FP), sp, Immediate(DOUBLE_SLOT_SIZE)); -} - -void OptimizedCall::PopAsmBridgeFrame(ExtendedAssembler *assembler) -{ - TempRegister2Scope temp2Scope(assembler); - Register sp(SP); - Register frameType = __ TempRegister2(); - // 2 : 2 means pop call site sp and type - __ Ldp(Register(X19), frameType, MemoryOperand(sp, FRAME_SLOT_SIZE * 2, AddrMode::POSTINDEX)); - __ RestoreFpAndLr(); -} - // * uint64_t PushOptimizedUnfoldArgVFrame(uintptr_t glue, uint32_t argc, JSTaggedType calltarget, // JSTaggedType new, JSTaggedType this, JSTaggedType argV[]) // * cc calling convention call js function() diff --git a/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp b/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp index 2df550b9d0..1434985cac 100644 --- a/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp +++ b/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp @@ -927,16 +927,30 @@ void AsmInterpreterCall::CallNativeWithArgv(ExtendedAssembler *assembler, bool c void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJSFunction) { + Label callFastBuiltin; + Label callNativeBuiltin; Register glue = rdi; Register argv = r9; Register function = rsi; Register nativeCode = r10; // get native pointer + if (isJSFunction) { + Register callFieldRegister = __ CallDispatcherArgument(kungfu::CallDispatchInputs::CALL_FIELD); + + __ Movq(Operand(function, JSFunctionBase::CODE_ENTRY_OFFSET), nativeCode); + + __ Btq(MethodLiteral::IsFastBuiltinBit::START_BIT, callFieldRegister); + __ Jb(&callFastBuiltin); + } else { + // JSProxy or JSBoundFunction + Register method = rdx; + __ Movq(Operand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET), nativeCode); + } + + __ Bind(&callNativeBuiltin); if (isJSFunction) { [[maybe_unused]] TempRegisterScope scope(assembler); Register lexicalEnv = __ TempRegister(); - - __ Movq(Operand(function, JSFunctionBase::CODE_ENTRY_OFFSET), nativeCode); Label next; __ Movq(Operand(function, JSFunction::LEXICAL_ENV_OFFSET), lexicalEnv); @@ -944,12 +958,7 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS __ Je(&next); __ Movq(lexicalEnv, Operand(glue, JSThread::GlueData::GetCurrentEnvOffset(false))); __ Bind(&next); - } else { - // JSProxy or JSBoundFunction - Register method = rdx; - __ Movq(Operand(method, Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET), nativeCode); } - __ PushAlignBytes(); __ Push(function); // 3: 24 means skip thread & argc & returnAddr @@ -966,6 +975,93 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS // 5: 40 means skip function __ Addq(5 * FRAME_SLOT_SIZE, rsp); __ Ret(); + + __ Bind(&callFastBuiltin); + CallFastBuiltin(assembler, &callNativeBuiltin); +} + +void AsmInterpreterCall::CallFastBuiltin(ExtendedAssembler *assembler, Label *callNativeBuiltin) +{ + Label arg1; + Label arg2; + Label arg3; + Label callEntry; + Register glue = rdi; + Register argc = r8; + Register argv = r9; + Register method = rdx; + Register function = rsi; + Register nativeCode = r10; + Register temp = rax; + Register temp1 = r11; + // get builtins id + __ Movq(Operand(method, Method::EXTRA_LITERAL_INFO_OFFSET), temp1); + __ Shr(MethodLiteral::BuiltinIdBits::START_BIT, temp1); + __ Andl((1LU << MethodLiteral::BuiltinIdBits::SIZE) - 1, temp1); + + __ Cmpl(static_cast(BUILTINS_STUB_ID(BUILTINS_CONSTRUCTOR_STUB_FIRST)), temp1); + __ Jge(callNativeBuiltin); + + __ Cmpq(Immediate(3), argc); // 3: number of args + __ Jg(callNativeBuiltin); + + // create frame + PushAsmBridgeFrame(assembler); + + // register args + __ Movq(function, temp); + __ Movq(nativeCode, rsi); // nativeCode is rsi + __ Movq(temp, rdx); // fun is rdx + __ Movq(argv, temp); // temp is argv + __ Movq(argc, r9); // argc is r9 + __ Movq(Operand(temp, FRAME_SLOT_SIZE), rcx); // get new target + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 2), r8); // 2: skip func & new target to get this target + + __ Cmp(Immediate(0), r9); + __ Jne(&arg1); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Jmp(&callEntry); + __ Bind(&arg1); + { + __ Cmp(Immediate(1), r9); + __ Jne(&arg2); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 3), r10); // 3: get arg0 + __ Pushq(r10); + __ Jmp(&callEntry); + } + __ Bind(&arg2); + { + __ Cmp(Immediate(2), r9); // 2: number of args + __ Jne(&arg3); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 4), r10); // 4: get arg1 + __ Pushq(r10); + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 3), r10); // 3: get arg0 + __ Pushq(r10); + __ Jmp(&callEntry); + } + __ Bind(&arg3); + { + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 5), r10); // 5: get arg2 + __ Pushq(r10); + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 4), r10); // 4: get arg1 + __ Pushq(r10); + __ Movq(Operand(temp, FRAME_SLOT_SIZE * 3), r10); // 3: get arg0 + __ Pushq(r10); + __ Jmp(&callEntry); + } + __ Bind(&callEntry); + { + __ Movq(Operand(glue, temp1, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), temp1); + __ Callq(temp1); + __ Addq(QUADRUPLE_SLOT_SIZE, rsp); + __ Pop(rbp); + __ Ret(); + } } // uint64_t PushCallArgsAndDispatchNative(uintptr_t codeAddress, uintptr_t glue, uint32_t argc, ...) diff --git a/ecmascript/compiler/trampoline/x64/common_call.cpp b/ecmascript/compiler/trampoline/x64/common_call.cpp index 87493e833f..090e90041c 100644 --- a/ecmascript/compiler/trampoline/x64/common_call.cpp +++ b/ecmascript/compiler/trampoline/x64/common_call.cpp @@ -113,5 +113,18 @@ void CommonCall::StackOverflowCheck(ExtendedAssembler *assembler, Register glue, } __ Jle(stackOverflow); } + +void CommonCall::PushAsmBridgeFrame(ExtendedAssembler *assembler) +{ + __ Pushq(rbp); + __ Pushq(static_cast(FrameType::ASM_BRIDGE_FRAME)); + __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); +} + +void CommonCall::PopAsmBridgeFrame(ExtendedAssembler *assembler) +{ + __ Addq(FRAME_SLOT_SIZE, rsp); // skip type + __ Popq(rbp); +} #undef __ } // namespace panda::ecmascript::x64 diff --git a/ecmascript/compiler/trampoline/x64/common_call.h b/ecmascript/compiler/trampoline/x64/common_call.h index 832777454a..b9b53d8c9f 100644 --- a/ecmascript/compiler/trampoline/x64/common_call.h +++ b/ecmascript/compiler/trampoline/x64/common_call.h @@ -46,6 +46,8 @@ public: Register op1, Register op2, Label *stackOverflow); static void StackOverflowCheck(ExtendedAssembler *assembler, Register glue, Register numArgs, Register op1, Register op2, Label *stackOverflow); + static void PushAsmBridgeFrame(ExtendedAssembler *assembler); + static void PopAsmBridgeFrame(ExtendedAssembler *assembler); }; class OptimizedCall : public CommonCall { @@ -99,9 +101,7 @@ private: static void PopJSFunctionEntryFrame(ExtendedAssembler *assembler, Register glue); static void PushOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler, Register callSiteSp); static void PopOptimizedUnfoldArgVFrame(ExtendedAssembler *assembler); - static void PushAsmBridgeFrame(ExtendedAssembler *assembler); static void CallBuiltinTrampoline(ExtendedAssembler *assembler, Register temp); - static void PopAsmBridgeFrame(ExtendedAssembler *assembler); static void CallBuiltinConstructorStub(ExtendedAssembler *assembler, Register builtinStub, Register argv, Register glue, Register temp); static void RemoveArgv(ExtendedAssembler *assembler, Register temp); @@ -224,6 +224,7 @@ private: Register callTargetRegister, Register methodRegister, Register accRegister = rInvalid, bool hasException = false); static void CallNativeEntry(ExtendedAssembler *assembler, bool isJSFunction); + static void CallFastBuiltin(ExtendedAssembler *assembler, Label *callNativeBuiltin); static void CallNativeWithArgv(ExtendedAssembler *assembler, bool callNew, bool hasNewTarget = false); static void CallNativeInternal(ExtendedAssembler *assembler, Register nativeCode); static bool PushBuiltinFrame(ExtendedAssembler *assembler, Register glue, FrameType type); diff --git a/ecmascript/compiler/trampoline/x64/optimized_call.cpp b/ecmascript/compiler/trampoline/x64/optimized_call.cpp index 1a4d4d9999..3ba9865698 100644 --- a/ecmascript/compiler/trampoline/x64/optimized_call.cpp +++ b/ecmascript/compiler/trampoline/x64/optimized_call.cpp @@ -815,19 +815,6 @@ void OptimizedCall::FastCallToAsmInterBridge(ExtendedAssembler *assembler) assembler, JSCallMode::CALL_FROM_AOT, FrameTransitionType::OTHER_TO_OTHER); } -void OptimizedCall::PushAsmBridgeFrame(ExtendedAssembler *assembler) -{ - __ Pushq(rbp); - __ Pushq(static_cast(FrameType::ASM_BRIDGE_FRAME)); - __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); -} - -void OptimizedCall::PopAsmBridgeFrame(ExtendedAssembler *assembler) -{ - __ Addq(FRAME_SLOT_SIZE, rsp); // skip type - __ Popq(rbp); -} - void OptimizedCall::JSCallCheck(ExtendedAssembler *assembler, Register jsFuncReg, Label *lNonCallable, Label *lNotJSFunction, Label *lJSFunctionCall) { diff --git a/test/moduletest/calltype/calltype.js b/test/moduletest/calltype/calltype.js index bb5d563fa8..d687a6fe5c 100644 --- a/test/moduletest/calltype/calltype.js +++ b/test/moduletest/calltype/calltype.js @@ -132,3 +132,12 @@ prototypeCallTest.call(0, 1); prototypeCallTest.call(0, 1, 2); prototypeCallTest.call(0, 1, 2, 3); prototypeCallTest.call(0, 1, 2, 3, 4); + +var x = []; +x[new Number(0)] = 0; +print(x[0]); + +const reg = /bar/; + +reg[Symbol.match] = false; +print('/bar/'.startsWith(reg)); diff --git a/test/moduletest/calltype/expect_output.txt b/test/moduletest/calltype/expect_output.txt index ea0326a91d..5aaa71624c 100644 --- a/test/moduletest/calltype/expect_output.txt +++ b/test/moduletest/calltype/expect_output.txt @@ -35,3 +35,5 @@ undefined 1,2,3 0 1,2,3,4 +0 +true -- Gitee From bd4b5bbc6bc912c2546d2408853d1c851f6470db Mon Sep 17 00:00:00 2001 From: xwcai98 Date: Fri, 25 Jul 2025 16:39:36 +0800 Subject: [PATCH 2/2] Opt1 Signed-off-by: xwcai98 Change-Id: If4fcea2f58c40ea8b65aa184f30529ff7704c9d4 --- .../aarch64/asm_interpreter_call.cpp | 110 ++++++++++------- .../trampoline/x64/asm_interpreter_call.cpp | 111 +++++++++++------- 2 files changed, 135 insertions(+), 86 deletions(-) diff --git a/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp b/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp index 4b7012c23d..5d929304a9 100644 --- a/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp +++ b/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp @@ -1929,9 +1929,9 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS } __ Bind(&callNativeBuiltin); + // For non-FastBuiltin native JSFunction, Call will enter C++ and GlobalEnv needs to be set on glue if (isJSFunction) { Register lexicalEnv = temp; - Label next; __ Ldr(lexicalEnv, MemoryOperand(function, JSFunction::LEXICAL_ENV_OFFSET)); __ Cmp(lexicalEnv, Immediate(JSTaggedValue::VALUE_UNDEFINED)); @@ -1957,12 +1957,29 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS CallFastBuiltin(assembler, &callNativeBuiltin); } +// InterpreterEntry attempts to call a fast builtin. Entry registers: +// Input: glue - %X0 +// callTarget - %X1 +// method - %X2 +// callField - %X3 +// argc - %X4 +// argv - %X5( are at the beginning of argv) +// nativeCode - %7 +// Fast builtin uses C calling convention: +// Input: glue - %X0 +// nativeCode - %X1 +// func - %X2 +// newTarget - %X3 +// this - %X4 +// argc - %X5 +// arg0 - %X6 +// arg1 - %X7 +// arg2 - stack void AsmInterpreterCall::CallFastBuiltin(ExtendedAssembler *assembler, Label *callNativeBuiltin) { - Label lCall1; - Label lCall2; - Label lCall3; - Label callEntry; + + Label dispatchTable[3]; // 3: call with argc = 0, 1, 2 + Label callEntryAndRet; Register sp(SP); Register glue(X0); Register function(X1); @@ -1973,70 +1990,77 @@ void AsmInterpreterCall::CallFastBuiltin(ExtendedAssembler *assembler, Label *ca Register builtinId = __ AvailableRegister1(); Register temp = __ AvailableRegister2(); - // get builtinid - __ Ldr(builtinId, MemoryOperand(method, Method::EXTRA_LITERAL_INFO_OFFSET)); // get extra literal + // Get builtinId + __ Ldr(builtinId, MemoryOperand(method, Method::EXTRA_LITERAL_INFO_OFFSET)); __ And(builtinId.W(), builtinId.W(), LogicalImmediate::Create(0xff, RegWSize)); __ Cmp(builtinId.W(), Immediate(BUILTINS_STUB_ID(BUILTINS_CONSTRUCTOR_STUB_FIRST))); __ B(Condition::GE, callNativeBuiltin); - __ Cmp(argc, Immediate(3)); // 3: number of args + __ Cmp(argc, Immediate(3)); // 3: Quick arity check: we only handle argc <= 3 here __ B(Condition::HI, callNativeBuiltin); - // get builtin func addr + // Resolve stub entry pointer: glue->builtinsStubEntries[builtinId] __ Add(builtinId, glue, Operand(builtinId.W(), UXTW, FRAME_SLOT_SIZE_LOG2)); __ Ldr(builtinId, MemoryOperand(builtinId, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false))); - // create frame + // Create AsmBridge frame PushAsmBridgeFrame(assembler); - __ Mov(temp, function); - __ Mov(X1, nativeCode); - __ Mov(X2, temp); - __ Mov(temp, argv); - __ Mov(X5, argc); - __ Ldr(X3, MemoryOperand(temp, FRAME_SLOT_SIZE)); - __ Ldr(X4, MemoryOperand(temp, DOUBLE_SLOT_SIZE)); - - __ Cmp(Register(X5), Immediate(0)); - __ B(Condition::NE, &lCall1); - __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED)); - __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); + // Shuffle registers to match C calling convention, X0(glue) already in place + __ Mov(temp, function); // Save function to temp + __ Mov(X1, nativeCode); // X1 = nativeCode + __ Mov(X2, temp); // X2 = func + __ Mov(temp, argv); // temp = argv + __ Mov(X5, argc); // X5 = argc + __ Ldr(X3, MemoryOperand(temp, FRAME_SLOT_SIZE)); // X3 = newTarget + __ Ldr(X4, MemoryOperand(temp, DOUBLE_SLOT_SIZE)); // X4 = this + + // Dispatch according to argc (0, 1, 2, or 3) + __ Cmp(X5, Immediate(0)); + __ B(Condition::EQ, &dispatchTable[0]); + __ Cmp(X5, Immediate(1)); + __ B(Condition::EQ, &dispatchTable[1]); + __ Cmp(X5, Immediate(2)); + __ B(Condition::EQ, &dispatchTable[2]); + // fallthrough to argc = 3 + + // argc = 3 + __ Ldr(Register(X7), MemoryOperand(temp, QUINTUPLE_SLOT_SIZE)); __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); - __ B(&callEntry); + __ Ldp(Register(X6), Register(X7), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); + __ B(&callEntryAndRet); - __ Bind(&lCall1); + // argc = 0 + __ Bind(&dispatchTable[0]); { - __ Cmp(Register(X5), Immediate(1)); - __ B(Condition::NE, &lCall2); - __ Ldr(Register(X6), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); - __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); // reset x7 + __ Mov(Register(X6), Immediate(JSTaggedValue::VALUE_UNDEFINED)); + __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); - __ B(&callEntry); + __ B(&callEntryAndRet); } - - __ Bind(&lCall2); + // argc = 1 + __ Bind(&dispatchTable[1]); { - __ Cmp(Register(X5), Immediate(2)); // 2: number of args - __ B(Condition::NE, &lCall3); + __ Ldr(Register(X6), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); - __ Ldp(Register(X6), Register(X7), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); - __ B(&callEntry); + __ B(&callEntryAndRet); } - - __ Bind(&lCall3); + // argc = 2 + __ Bind(&dispatchTable[2]); { - __ Ldr(Register(X7), MemoryOperand(temp, QUINTUPLE_SLOT_SIZE)); + __ Mov(Register(X7), Immediate(JSTaggedValue::VALUE_UNDEFINED)); // dummy for stack slot __ Stp(Register(X7), Register(X7), MemoryOperand(sp, -DOUBLE_SLOT_SIZE, PREINDEX)); - __ Ldp(Register(X6), Register(X7), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); // get arg0 arg1 - __ B(&callEntry); + __ Ldp(Register(X6), Register(X7), MemoryOperand(temp, TRIPLE_SLOT_SIZE)); + // fallthrough to callEntryAndRet } - __ Bind(&callEntry); + __ Bind(&callEntryAndRet); { __ Blr(builtinId); __ Add(sp, sp, Immediate(DOUBLE_SLOT_SIZE)); + // Tear down frame and return + PopAsmBridgeFrame(assembler); + __ Ret(); } - PopAsmBridgeFrame(assembler); - __ Ret(); } void AsmInterpreterCall::ThrowStackOverflowExceptionAndReturn(ExtendedAssembler *assembler, Register glue, diff --git a/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp b/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp index 1434985cac..b29288441f 100644 --- a/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp +++ b/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp @@ -949,9 +949,9 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS __ Bind(&callNativeBuiltin); if (isJSFunction) { + // For non-FastBuiltin native JSFunction, Call will enter C++ and GlobalEnv needs to be set on glue [[maybe_unused]] TempRegisterScope scope(assembler); Register lexicalEnv = __ TempRegister(); - Label next; __ Movq(Operand(function, JSFunction::LEXICAL_ENV_OFFSET), lexicalEnv); __ Cmpq(JSTaggedValue::Undefined().GetRawData(), lexicalEnv); @@ -980,12 +980,28 @@ void AsmInterpreterCall::CallNativeEntry(ExtendedAssembler *assembler, bool isJS CallFastBuiltin(assembler, &callNativeBuiltin); } +// InterpreterEntry attempts to call a fast builtin. Entry registers: +// Input: glue - %rdi +// callTarget - %rsi +// method - %rdx +// callField - %rcx +// argc - %r8 +// argv - %r9( are at the beginning of argv) +// nativeCode - %r10 +// Fast builtin uses C calling convention: +// Input: glue - %rdi +// nativeCode - %rsi +// func - %rdx +// newTarget - %rcx +// this - %r8 +// argc - %r9 +// arg0 - stack +// arg1 - stack +// arg2 - stack void AsmInterpreterCall::CallFastBuiltin(ExtendedAssembler *assembler, Label *callNativeBuiltin) { - Label arg1; - Label arg2; - Label arg3; - Label callEntry; + Label dispatchTable[3]; // 3: call with argc = 0, 1, 2 + Label callEntryAndRet; Register glue = rdi; Register argc = r8; Register argv = r9; @@ -994,69 +1010,78 @@ void AsmInterpreterCall::CallFastBuiltin(ExtendedAssembler *assembler, Label *ca Register nativeCode = r10; Register temp = rax; Register temp1 = r11; - // get builtins id + // Get builtinId __ Movq(Operand(method, Method::EXTRA_LITERAL_INFO_OFFSET), temp1); __ Shr(MethodLiteral::BuiltinIdBits::START_BIT, temp1); __ Andl((1LU << MethodLiteral::BuiltinIdBits::SIZE) - 1, temp1); - __ Cmpl(static_cast(BUILTINS_STUB_ID(BUILTINS_CONSTRUCTOR_STUB_FIRST)), temp1); __ Jge(callNativeBuiltin); - __ Cmpq(Immediate(3), argc); // 3: number of args + __ Cmpq(Immediate(3), argc); // 3: Quick arity check: we only handle argc <= 3 here __ Jg(callNativeBuiltin); - // create frame + // Resolve stub entry pointer: glue->builtinsStubEntries[builtinId] + __ Movq(Operand(glue, temp1, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), temp1); + // Create AsmBridge frame PushAsmBridgeFrame(assembler); - // register args - __ Movq(function, temp); - __ Movq(nativeCode, rsi); // nativeCode is rsi - __ Movq(temp, rdx); // fun is rdx - __ Movq(argv, temp); // temp is argv - __ Movq(argc, r9); // argc is r9 - __ Movq(Operand(temp, FRAME_SLOT_SIZE), rcx); // get new target - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 2), r8); // 2: skip func & new target to get this target + // Shuffle registers to match C calling convention, rdi(glue) already in place + __ Movq(function, temp); // Save function to temp + __ Movq(nativeCode, rsi); // rsi = nativeCode + __ Movq(temp, rdx); // rdx = func + __ Movq(argv, temp); // temp = argv + __ Movq(argc, r9); // r9 = argc + __ Movq(Operand(temp, FRAME_SLOT_SIZE), rcx); // rcx = newTarget + __ Movq(Operand(temp, DOUBLE_SLOT_SIZE), r8); // r8 = this + // Dispatch according to argc (0, 1, 2, or 3) __ Cmp(Immediate(0), r9); - __ Jne(&arg1); - __ Pushq(JSTaggedValue::VALUE_UNDEFINED); - __ Pushq(JSTaggedValue::VALUE_UNDEFINED); - __ Pushq(JSTaggedValue::VALUE_UNDEFINED); - __ Jmp(&callEntry); - __ Bind(&arg1); + __ Je(&dispatchTable[0]); + __ Cmp(Immediate(1), r9); + __ Je(&dispatchTable[1]); + __ Cmp(Immediate(2), r9); + __ Je(&dispatchTable[2]); + // fallthrough to argc = 3 + + // argc = 3 + __ Movq(Operand(temp, QUINTUPLE_SLOT_SIZE), r10); + __ Pushq(r10); + __ Movq(Operand(temp, QUADRUPLE_SLOT_SIZE), r10); + __ Pushq(r10); + __ Movq(Operand(temp, TRIPLE_SLOT_SIZE), r10); + __ Pushq(r10); + __ Jmp(&callEntryAndRet); + + // argc = 0 + __ Bind(&dispatchTable[0]); { - __ Cmp(Immediate(1), r9); - __ Jne(&arg2); __ Pushq(JSTaggedValue::VALUE_UNDEFINED); __ Pushq(JSTaggedValue::VALUE_UNDEFINED); - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 3), r10); // 3: get arg0 - __ Pushq(r10); - __ Jmp(&callEntry); + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Jmp(&callEntryAndRet); } - __ Bind(&arg2); + // argc = 1 + __ Bind(&dispatchTable[1]); { - __ Cmp(Immediate(2), r9); // 2: number of args - __ Jne(&arg3); __ Pushq(JSTaggedValue::VALUE_UNDEFINED); - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 4), r10); // 4: get arg1 - __ Pushq(r10); - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 3), r10); // 3: get arg0 + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Movq(Operand(temp, TRIPLE_SLOT_SIZE), r10); __ Pushq(r10); - __ Jmp(&callEntry); + __ Jmp(&callEntryAndRet); } - __ Bind(&arg3); + // argc = 2 + __ Bind(&dispatchTable[2]); { - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 5), r10); // 5: get arg2 - __ Pushq(r10); - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 4), r10); // 4: get arg1 + __ Pushq(JSTaggedValue::VALUE_UNDEFINED); + __ Movq(Operand(temp, QUADRUPLE_SLOT_SIZE), r10); __ Pushq(r10); - __ Movq(Operand(temp, FRAME_SLOT_SIZE * 3), r10); // 3: get arg0 + __ Movq(Operand(temp, TRIPLE_SLOT_SIZE), r10); __ Pushq(r10); - __ Jmp(&callEntry); + // fallthrough to callEntryAndRet } - __ Bind(&callEntry); + + __ Bind(&callEntryAndRet); { - __ Movq(Operand(glue, temp1, Times8, JSThread::GlueData::GetBuiltinsStubEntriesOffset(false)), temp1); __ Callq(temp1); __ Addq(QUADRUPLE_SLOT_SIZE, rsp); __ Pop(rbp); -- Gitee