1/* 2 * Copyright (c) 2023-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 "ecmascript/compiler/trampoline/x64/common_call.h" 17 18#include "ecmascript/compiler/assembler/assembler.h" 19#include "ecmascript/compiler/rt_call_signature.h" 20#include "ecmascript/compiler/argument_accessor.h" 21#include "ecmascript/deoptimizer/deoptimizer.h" 22#include "ecmascript/ecma_runtime_call_info.h" 23#include "ecmascript/frames.h" 24#include "ecmascript/js_function.h" 25#include "ecmascript/js_thread.h" 26#include "ecmascript/message_string.h" 27#include "ecmascript/method.h" 28#include "ecmascript/runtime_call_id.h" 29 30namespace panda::ecmascript::x64 { 31#define __ assembler-> 32 33// * uint64_t OptimizedFastCallEntry(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[], 34// uintptr_t prevFp) 35// * Arguments: 36// %rdi - glue 37// %rsi - actualNumArgs 38// %rdx - argV 39// %rcx - prevFp 40 41void OptimizedFastCall::OptimizedFastCallEntry(ExtendedAssembler *assembler) 42{ 43 __ BindAssemblerStub(RTSTUB_ID(OptimizedFastCallEntry)); 44 Register glueReg = rdi; 45 Register argv = rdx; 46 Register prevFpReg = rcx; 47 48 OptimizedCall::PushJSFunctionEntryFrame(assembler, prevFpReg); 49 __ Movq(argv, r8); 50 __ Movq(rsi, rcx); 51 __ Movq(Operand(r8, 0), rsi); // func 52 __ Movq(Operand(r8, FRAME_SLOT_SIZE), rdx); // thisobj 53 __ Addq(DOUBLE_SLOT_SIZE, r8); 54 __ CallAssemblerStub(RTSTUB_ID(JSFastCallWithArgV), false); 55 56 __ Popq(prevFpReg); 57 __ Addq(FRAME_SLOT_SIZE, rsp); // 8: frame type 58 __ Popq(rbp); 59 __ Popq(glueReg); // caller restore 60 __ PopCppCalleeSaveRegisters(); // callee restore 61 __ Movq(prevFpReg, Operand(glueReg, JSThread::GlueData::GetLeaveFrameOffset(false))); 62 __ Ret(); 63} 64 65 66// * uint64_t OptimizedFastCallAndPushArgv(uintptr_t glue, uint32_t expectedNumArgs, uint32_t actualNumArgs, 67// uintptr_t codeAddr, uintptr_t argv) 68// * Arguments wil CC calling convention: 69// %rdi - glue 70// %rsi - actualNumArgs 71// %rdx - actualArgv 72// %rcx - func 73// %r8 - new target 74// %r9 - this 75// * The OptimizedJSFunctionArgsConfig Frame's structure is illustrated as the following: 76// +--------------------------+ 77// | arg[N-1] | 78// +--------------------------+ 79// | . . . . | 80// +--------------------------+ 81// | arg[0] | 82// +--------------------------+ 83// | argC | 84// sp ---> +--------------------------+ ----------------- 85// | | ^ 86// | prevFP | | 87// |--------------------------| OptimizedJSFunctionArgsConfigFrame 88// | frameType | | 89// | | V 90// +--------------------------+ ----------------- 91void OptimizedFastCall::OptimizedFastCallAndPushArgv(ExtendedAssembler *assembler) 92{ 93 __ BindAssemblerStub(RTSTUB_ID(OptimizedFastCallAndPushArgv)); 94 Register actualNumArgsReg = rsi; 95 Register jsFuncReg = rcx; 96 Register thisObj = r9; 97 Label lCopyExtraAument1; 98 Label lCopyExtraUndefineToSp; 99 Label lCopyLoop1; 100 Label lCopyLoop2; 101 Label pushUndefined; 102 Label call; 103 Label arg4; 104 Label argc; 105 Label checkExpectedArgs; 106 JsFunctionArgsConfigFrameScope scope(assembler); // push frametype and callee save 107 __ Movq(actualNumArgsReg, r13); 108 actualNumArgsReg = r13; 109 __ Movq(rcx, rsi); // func move to argc 110 jsFuncReg = rsi; 111 __ Movq(thisObj, rdx); // this move to argv 112 Register method = r14; 113 Register methodCallField = rbx; 114 Register codeAddrReg = rax; 115 Register argvReg = r12; 116 __ Leaq(Operand(rsp, 8 * FRAME_SLOT_SIZE), argvReg); // 8: skip 8 frames to get argv 117 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 118 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), codeAddrReg); // get codeAddress 119 __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field 120 __ Shr(MethodLiteral::NumArgsBits::START_BIT, methodCallField); 121 __ Andl(((1LU << MethodLiteral::NumArgsBits::SIZE) - 1), methodCallField); 122 __ Addl(NUM_MANDATORY_JSFUNC_ARGS, methodCallField); // add mandatory argumentr 123 Register expectedNumArgsReg = rbx; 124 125 Label arg5; 126 Label arg6; 127 __ Cmp(Immediate(3), actualNumArgsReg); // 3: func new this 128 __ Jne(&arg4); 129 __ Movq(JSTaggedValue::VALUE_UNDEFINED, rcx); 130 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8); 131 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 132 __ Subq(3, expectedNumArgsReg); // 3: skip 3 register 133 __ Jmp(&checkExpectedArgs); 134 135 __ Bind(&arg4); 136 { 137 __ Movq(Operand(argvReg, 0), rcx); 138 __ Addq(FRAME_SLOT_SIZE, argvReg); 139 __ Cmp(Immediate(4), actualNumArgsReg); // 4: func new this arg0 140 __ Jne(&arg5); 141 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8); 142 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 143 __ Subq(3, expectedNumArgsReg); // 3: skip 3 register 144 __ Jmp(&checkExpectedArgs); 145 } 146 147 __ Bind(&arg5); 148 { 149 __ Movq(Operand(argvReg, 0), r8); 150 __ Addq(FRAME_SLOT_SIZE, argvReg); 151 __ Cmp(Immediate(5), actualNumArgsReg); // 5: 5 args 152 __ Jne(&arg6); 153 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 154 __ Subq(3, expectedNumArgsReg); // 3: skip 3 register 155 __ Jmp(&checkExpectedArgs); 156 } 157 158 __ Bind(&arg6); 159 { 160 __ Movq(Operand(argvReg, 0), r9); 161 __ Addq(FRAME_SLOT_SIZE, argvReg); 162 __ Cmp(Immediate(6), actualNumArgsReg); // 6: 6 args 163 __ Jne(&argc); 164 __ Subq(3, expectedNumArgsReg); // 3: skip above 3 args 165 __ Jmp(&checkExpectedArgs); 166 } 167 168 __ Bind(&argc); // actualNumArgsReg >=7 169 { 170 __ Cmpq(expectedNumArgsReg, actualNumArgsReg); 171 __ Jb(&pushUndefined); 172 // 16 bytes align check 173 __ Subq(6, actualNumArgsReg); // 6: skip above 6 args 174 __ Subq(6, expectedNumArgsReg); // 6: skip above 6 args 175 __ Testb(1, actualNumArgsReg); 176 __ Je(&lCopyLoop2); 177 __ Pushq(0); 178 __ Bind(&lCopyLoop2); 179 __ Movq(Operand(argvReg, actualNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r14); // -8: stack index 180 __ Pushq(r14); 181 __ Subq(1, actualNumArgsReg); 182 __ Jne(&lCopyLoop2); 183 __ Jmp(&call); 184 185 __ Bind(&pushUndefined); 186 // 16 bytes align check 187 __ Subq(6, actualNumArgsReg); // 6: skip above 6 args 188 __ Subq(6, expectedNumArgsReg); // 6: skip above 6 args 189 __ Testb(1, expectedNumArgsReg); 190 __ Je(&lCopyExtraAument1); 191 __ Pushq(0); 192 __ Bind(&lCopyExtraAument1); // copy undefined value to stack 193 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 194 __ Subq(1, expectedNumArgsReg); 195 __ Cmpq(actualNumArgsReg, expectedNumArgsReg); 196 __ Ja(&lCopyExtraAument1); 197 __ Bind(&lCopyLoop1); 198 __ Movq(Operand(argvReg, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r14); // -8: stack index 199 __ Pushq(r14); 200 __ Subq(1, expectedNumArgsReg); 201 __ Jne(&lCopyLoop1); 202 __ Jmp(&call); 203 } 204 205 __ Bind(&checkExpectedArgs); // actualNumArgsReg < 7 206 { 207 __ Cmp(Immediate(3), expectedNumArgsReg); // 3: expectedNumArgsReg <= 3 jump 208 __ Jbe(&call); 209 // expectedNumArgsReg > 6, expectedNumArgsReg > actual;NumArgsReg 210 __ Subq(3, expectedNumArgsReg); // 3 : skpi func new this 211 __ Testb(1, expectedNumArgsReg); 212 __ Je(&lCopyExtraUndefineToSp); 213 __ Pushq(0); // expectedNumArgsReg is odd need align 214 __ Bind(&lCopyExtraUndefineToSp); // copy undefined value to stack 215 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 216 __ Subq(1, expectedNumArgsReg); 217 __ Cmp(0, expectedNumArgsReg); 218 __ Ja(&lCopyExtraUndefineToSp); 219 __ Jmp(&call); 220 } 221 __ Bind(&call); 222 __ Callq(codeAddrReg); // then call jsFunction 223} 224 225// * uint64_t JSFastCallWithArgV(uintptr_t glue, uint32_t actualNumArgs, const JSTaggedType argV[], uintptr_t prevFp, 226// size_t callType) 227// cc callconv 228// * Arguments: 229// %rdi - glue 230// %rsi - func 231// %rdx - this 232// %rcx - actualNumArgs 233// %r8 - argv 234 235void OptimizedFastCall::JSFastCallWithArgV(ExtendedAssembler *assembler) 236{ 237 __ BindAssemblerStub(RTSTUB_ID(JSFastCallWithArgV)); 238 Register sp(rsp); 239 Register callsiteSp = __ AvailableRegister2(); 240 Label align16Bytes; 241 Label call; 242 243 __ Movq(sp, callsiteSp); 244 __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp); // 8 : 8 means skip pc to get last callsitesp 245 OptimizedUnfoldArgVFrameFrameScope scope(assembler); // push frametype and callee save 246 __ Movq(rcx, r12); 247 __ Movq(r8, rbx); 248 Register actualNumArgs(r12); 249 Register argV(rbx); 250 251 __ Cmp(0, actualNumArgs); 252 __ Jz(&call); 253 __ Movq(Operand(argV, 0), rcx); // first arg 254 __ Addq(FRAME_SLOT_SIZE, argV); 255 __ Addq(-1, actualNumArgs); 256 257 __ Cmp(0, actualNumArgs); 258 __ Jz(&call); 259 __ Movq(Operand(argV, 0), r8); // second arg 260 __ Addq(FRAME_SLOT_SIZE, argV); 261 __ Addq(-1, actualNumArgs); 262 263 __ Cmp(0, actualNumArgs); 264 __ Jz(&call); 265 __ Movq(Operand(argV, 0), r9); // third arg 266 __ Addq(FRAME_SLOT_SIZE, argV); 267 __ Addq(-1, actualNumArgs); 268 269 __ Cmp(0, actualNumArgs); 270 __ Jz(&call); 271 272 __ Testb(1, actualNumArgs); 273 __ Je(&align16Bytes); 274 __ PushAlignBytes(); 275 __ Bind(&align16Bytes); 276 __ Mov(actualNumArgs, rax); 277 CopyArgumentWithArgV(assembler, rax, argV); 278 279 __ Bind(&call); 280 Register method = r12; 281 Register jsFuncReg = rsi; 282 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 283 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), rbx); // get codeAddress 284 __ Callq(rbx); 285} 286 287// cc callconv 288// * Arguments: 289// %rdi - glue 290// %rsi - func 291// %rdx - this 292// %rcx - actualNumArgs 293// %r8 - argv 294// %r9 - expectedNumArgs 295 296void OptimizedFastCall::JSFastCallWithArgVAndPushArgv(ExtendedAssembler *assembler) 297{ 298 __ BindAssemblerStub(RTSTUB_ID(JSFastCallWithArgVAndPushArgv)); 299 Register sp(rsp); 300 Register callsiteSp = __ AvailableRegister2(); 301 Label call; 302 Label lCopyExtraAument1; 303 Label lCopyExtraUndefineToSp; 304 Label lCopyLoop1; 305 Label lCopyLoop2; 306 Label pushUndefined; 307 Label arg1; 308 Label arg2; 309 Label arg3; 310 Label argc; 311 Label checkExpectedArgs; 312 313 __ Movq(sp, callsiteSp); 314 __ Addq(Immediate(FRAME_SLOT_SIZE), callsiteSp); // 8 : 8 means skip pc to get last callsitesp 315 OptimizedUnfoldArgVFrameFrame1Scope scope(assembler); 316 317 __ Movq(rcx, r12); 318 __ Movq(r8, rbx); 319 __ Movq(r9, r14); 320 Register actualNumArgsReg(r12); 321 Register expectedNumArgsReg(r14); 322 Register argV(rbx); 323 324 __ Cmp(0, actualNumArgsReg); 325 __ Jne(&arg1); 326 __ Movq(JSTaggedValue::VALUE_UNDEFINED, rcx); 327 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8); 328 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 329 __ Jmp(&checkExpectedArgs); 330 331 __ Bind(&arg1); 332 { 333 __ Movq(Operand(argV, 0), rcx); // first arg 334 __ Addq(FRAME_SLOT_SIZE, argV); 335 __ Cmp(1, actualNumArgsReg); 336 __ Jne(&arg2); 337 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r8); 338 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 339 __ Jmp(&checkExpectedArgs); 340 } 341 342 __ Bind(&arg2); 343 { 344 __ Movq(Operand(argV, 0), r8); // second arg 345 __ Addq(FRAME_SLOT_SIZE, argV); 346 __ Cmp(2, actualNumArgsReg); // 2: 2 args 347 __ Jne(&arg3); 348 __ Movq(JSTaggedValue::VALUE_UNDEFINED, r9); 349 __ Jmp(&checkExpectedArgs); 350 } 351 352 __ Bind(&arg3); 353 { 354 __ Movq(Operand(argV, 0), r9); // third arg 355 __ Addq(FRAME_SLOT_SIZE, argV); 356 __ Cmp(3, actualNumArgsReg); // 3: 3 args 357 __ Jne(&argc); 358 __ Jmp(&checkExpectedArgs); 359 } 360 361 __ Bind(&argc); // actualNumArgsReg >=4 362 { 363 __ Cmpq(expectedNumArgsReg, actualNumArgsReg); 364 __ Jb(&pushUndefined); 365 __ Subq(3, actualNumArgsReg); // 3: skip above 3 args 366 __ Subq(3, expectedNumArgsReg); // 3: skip above 3 args 367 __ Testb(1, actualNumArgsReg); 368 __ Je(&lCopyLoop2); 369 __ Pushq(0); 370 __ Bind(&lCopyLoop2); 371 __ Movq(Operand(argV, actualNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r13); // -8: stack index 372 __ Pushq(r13); 373 __ Subq(1, actualNumArgsReg); 374 __ Jne(&lCopyLoop2); 375 __ Jmp(&call); 376 377 __ Bind(&pushUndefined); 378 __ Subq(3, actualNumArgsReg); // 3: skip above 3 args 379 __ Subq(3, expectedNumArgsReg); // 3: skip above 3 args 380 __ Testb(1, expectedNumArgsReg); 381 __ Je(&lCopyExtraAument1); 382 __ Pushq(0); 383 __ Bind(&lCopyExtraAument1); // copy undefined value to stack 384 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 385 __ Subq(1, expectedNumArgsReg); 386 __ Cmpq(actualNumArgsReg, expectedNumArgsReg); 387 __ Ja(&lCopyExtraAument1); 388 __ Bind(&lCopyLoop1); 389 __ Movq(Operand(argV, expectedNumArgsReg, Scale::Times8, -FRAME_SLOT_SIZE), r13); // -8: stack index 390 __ Pushq(r13); 391 __ Subq(1, expectedNumArgsReg); 392 __ Jne(&lCopyLoop1); 393 __ Jmp(&call); 394 } 395 396 __ Bind(&checkExpectedArgs); 397 { 398 __ Cmp(Immediate(3), expectedNumArgsReg); // 3:expectedNumArgsReg <= 3 jump 399 __ Jbe(&call); 400 __ Subq(3, expectedNumArgsReg); // 3 : skpi func new this 401 __ Testb(1, expectedNumArgsReg); 402 __ Je(&lCopyExtraUndefineToSp); 403 __ Pushq(0); // expectedNumArgsReg is odd need align 404 __ Bind(&lCopyExtraUndefineToSp); // copy undefined value to stack 405 __ Pushq(JSTaggedValue::VALUE_UNDEFINED); 406 __ Subq(1, expectedNumArgsReg); 407 __ Cmp(0, expectedNumArgsReg); 408 __ Ja(&lCopyExtraUndefineToSp); 409 __ Jmp(&call); 410 } 411 __ Bind(&call); 412 Register method = r12; 413 Register jsFuncReg = rsi; 414 __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method 415 __ Mov(Operand(jsFuncReg, JSFunctionBase::CODE_ENTRY_OFFSET), rbx); // get codeAddress 416 __ Callq(rbx); 417} 418#undef __ 419} // namespace panda::ecmascript::x64