this repo has no description
at trunk 3602 lines 122 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "interpreter-gen.h" 3 4#include <cinttypes> 5#include <cstdio> 6#include <cstring> 7#include <type_traits> 8 9#include "assembler-x64.h" 10#include "bytecode.h" 11#include "event.h" 12#include "frame.h" 13#include "ic.h" 14#include "interpreter.h" 15#include "memory-region.h" 16#include "os.h" 17#include "register-state.h" 18#include "runtime.h" 19#include "thread.h" 20 21// This file generates an assembly version of our interpreter. The default 22// implementation for all opcodes calls back to the C++ version, with 23// hand-written assembly versions of perf-critical opcodes. More details are 24// inline with the relevant constants and functions. 25 26namespace py { 27 28namespace { 29 30#if DCHECK_IS_ON() 31#define COMMENT(...) __ comment(__VA_ARGS__) 32#else 33#define COMMENT(...) 34#endif 35 36using namespace x64; 37 38const word kInstructionCacheLineSize = 64; 39 40// Abbreviated x86-64 ABI: 41constexpr Register kArgRegs[] = {RDI, RSI, RDX, RCX, R8, R9}; 42constexpr Register kReturnRegs[] = {RAX, RDX}; 43 44// Currently unused in code, but kept around for reference: 45// callee-saved registers: {RSP, RBP, RBX, R12, R13, R14, R15} 46 47constexpr Register kCallerSavedRegs[] = {RAX, RCX, RDX, RDI, RSI, 48 R8, R9, R10, R11}; 49 50constexpr Register kScratchRegs[] = {RAX, RDX, R8, R9, R10, R11}; 51 52// During normal execution, the following values are live: 53 54// Current bytecode, a RawMutableBytes. 55const Register kBCReg = RCX; 56 57// Current PC, as an index into the bytecode. 58const Register kPCReg = R14; 59 60// Current opcode argument, as a uint32_t. 61const Register kOpargReg = kArgRegs[1]; 62 63// Current Frame*. 64const Register kFrameReg = RBX; 65 66// Current Thread*. 67const Register kThreadReg = R12; 68 69// Handler base address (see below for more about the handlers). 70const Register kHandlersBaseReg = R13; 71 72// Callable objects shared for function call path. 73const Register kCallableReg = RDI; 74 75// Used for signaling the return mode when jumping to entryAsm 76const Register kReturnModeReg = R15; 77 78// The native frame/stack looks like this: 79// +-------------+ 80// | return addr | 81// | saved %rbp | <- %rbp 82// | ... | 83// | ... | <- callee-saved registers 84// | ... | 85// | padding | <- native %rsp, when materialized for a C++ call 86// +-------------+ 87constexpr Register kUsedCalleeSavedRegs[] = {RBX, R12, R13, R14, R15}; 88const word kNumCalleeSavedRegs = ARRAYSIZE(kUsedCalleeSavedRegs); 89const word kFrameOffset = -kNumCalleeSavedRegs * kPointerSize; 90const word kPaddingBytes = (kFrameOffset % 16) == 0 ? 0 : kPointerSize; 91const word kNativeStackFrameSize = -kFrameOffset + kPaddingBytes; 92const word kCallStackAlignment = 16; 93static_assert(kNativeStackFrameSize % 16 == 0, 94 "native frame size must be multiple of 16"); 95 96// The interpreter code itself is a prologue followed by an array of 97// regularly-sized opcode handlers, spaced such that the address of a handler 98// can be computed with a base address and the opcode's value. A few special 99// pseudo-handlers are at negative offsets from the base address, which are used 100// to handle control flow such as exceptions and returning. 101// 102// +----------------------+ 103// | prologue, setup code | <- interpreter entry point 104// |----------------------+ 105// | UNWIND handler | <- handlers_base - 3 * kHandlerSize 106// +----------------------+ 107// | RETURN handler | <- handlers_base - 2 * kHandlerSize 108// +----------------------+ 109// | YIELD handler | <- handlers_base - 1 * kHandlerSize 110// +----------------------+ 111// | opcode 0 handler | <- handlers_base + 0 * kHandlerSize 112// +----------------------+ 113// | etc... | 114// +----------------------+ 115// | opcode 255 handler | <- handlers_base + 255 * kHandlerSize 116// +----------------------+ 117const word kHandlerSizeShift = 8; 118const word kHandlerSize = 1 << kHandlerSizeShift; 119 120const Interpreter::OpcodeHandler kCppHandlers[] = { 121#define OP(name, id, handler) Interpreter::handler, 122 FOREACH_BYTECODE(OP) 123#undef OP 124}; 125 126static const int kMaxNargs = 8; 127 128struct EmitEnv; 129 130class WARN_UNUSED ScratchReg : public VirtualRegister { 131 public: 132 ScratchReg(EmitEnv* env); 133 ScratchReg(EmitEnv* env, Register reg); 134 ~ScratchReg(); 135 136 private: 137 EmitEnv* env_; 138}; 139 140// Environment shared by all emit functions. 141struct EmitEnv { 142 Assembler as; 143 Bytecode current_op; 144 const char* current_handler; 145 Label unwind_handler; 146 147 VirtualRegister bytecode{"bytecode"}; 148 VirtualRegister pc{"pc"}; 149 VirtualRegister oparg{"oparg"}; 150 VirtualRegister frame{"frame"}; 151 VirtualRegister thread{"thread"}; 152 VirtualRegister handlers_base{"handlers_base"}; 153 VirtualRegister callable{"callable"}; 154 VirtualRegister return_value{"return_value"}; 155 VirtualRegister return_mode{"return_mode"}; 156 157 View<RegisterAssignment> handler_assignment = kNoRegisterAssignment; 158 Label call_handler; 159 Label opcode_handlers[kNumBytecodes]; 160 161 View<RegisterAssignment> function_entry_assignment = kNoRegisterAssignment; 162 Label function_entry_with_intrinsic_handler; 163 Label function_entry_with_no_intrinsic_handler; 164 Label function_entry_simple_interpreted_handler[kMaxNargs]; 165 Label function_entry_simple_builtin[kMaxNargs]; 166 167 Label call_interpreted_slow_path; 168 View<RegisterAssignment> call_interpreted_slow_path_assignment = 169 kNoRegisterAssignment; 170 171 Label call_trampoline; 172 View<RegisterAssignment> call_trampoline_assignment = kNoRegisterAssignment; 173 174 Label do_return; 175 View<RegisterAssignment> do_return_assignment = kNoRegisterAssignment; 176 177 View<RegisterAssignment> return_handler_assignment = kNoRegisterAssignment; 178 179 RegisterState register_state; 180 word handler_offset; 181 word counting_handler_offset; 182 bool count_opcodes; 183 bool in_jit = false; 184}; 185 186class JitEnv : public EmitEnv { 187 public: 188 JitEnv(HandleScope* scope, Thread* compiling_thread, const Function& function, 189 word num_opcodes) 190 : function_(scope, *function), 191 thread_(compiling_thread), 192 num_opcodes_(num_opcodes) { 193 in_jit = true; 194 opcode_handlers = new Label[num_opcodes]; 195 } 196 197 ~JitEnv() { 198 delete[] opcode_handlers; 199 opcode_handlers = nullptr; 200 } 201 202 RawObject function() { return *function_; } 203 204 Thread* compilingThread() { return thread_; } 205 206 word numOpcodes() { return num_opcodes_; } 207 208 Label* opcodeAtByteOffset(word byte_offset) { 209 word opcode_index = byte_offset / kCodeUnitSize; 210 DCHECK_INDEX(opcode_index, num_opcodes_); 211 return &opcode_handlers[opcode_index]; 212 } 213 214 word virtualPC() { return virtual_pc_; } 215 216 void setVirtualPC(word virtual_pc) { virtual_pc_ = virtual_pc; } 217 218 BytecodeOp currentOp() { return current_op_; } 219 220 void setCurrentOp(BytecodeOp op) { current_op_ = op; } 221 222 View<RegisterAssignment> jit_handler_assignment = kNoRegisterAssignment; 223 224 View<RegisterAssignment> deopt_assignment = kNoRegisterAssignment; 225 226 Label deopt_handler; 227 228 private: 229 Function function_; 230 Thread* thread_ = nullptr; 231 word num_opcodes_ = 0; 232 word virtual_pc_ = 0; 233 Label* opcode_handlers = nullptr; 234 BytecodeOp current_op_; 235}; 236 237// This macro helps instruction-emitting code stand out while staying compact. 238#define __ env->as. 239 240// RAII helper to ensure that a region of code is nop-padded to a specific size, 241// with checks that it doesn't overflow the limit. 242class HandlerSizer { 243 public: 244 HandlerSizer(EmitEnv* env, word size) 245 : env_(env), size_(size), start_cursor_(env->as.codeSize()) {} 246 247 ~HandlerSizer() { 248 word padding = start_cursor_ + size_ - env_->as.codeSize(); 249 CHECK(padding >= 0, "Handler for %s overflowed by %" PRIdPTR " bytes", 250 env_->current_handler, -padding); 251 env_->as.nops(padding); 252 } 253 254 private: 255 EmitEnv* env_; 256 word size_; 257 word start_cursor_; 258}; 259 260ScratchReg::ScratchReg(EmitEnv* env) : VirtualRegister("scratch"), env_(env) { 261 env_->register_state.allocate(this, kScratchRegs); 262} 263 264ScratchReg::ScratchReg(EmitEnv* env, Register reg) 265 : VirtualRegister("scratch"), env_(env) { 266 env_->register_state.assign(this, reg); 267} 268 269ScratchReg::~ScratchReg() { 270 if (isAssigned()) { 271 env_->register_state.free(this); 272 } 273} 274 275// Shorthand for the Immediate corresponding to a Bool value. 276Immediate boolImmediate(bool value) { 277 return Immediate(Bool::fromBool(value).raw()); 278} 279 280Immediate smallIntImmediate(word value) { 281 return Immediate(SmallInt::fromWord(value).raw()); 282} 283 284// The offset to use to access a given offset with a HeapObject, accounting for 285// the tag bias. 286int32_t heapObjectDisp(int32_t offset) { 287 return -Object::kHeapObjectTag + offset; 288} 289 290void emitCurrentCacheIndex(EmitEnv* env, Register dst) { 291 if (env->in_jit) { 292 JitEnv* jenv = static_cast<JitEnv*>(env); 293 __ movq(dst, Immediate(static_cast<word>(jenv->currentOp().cache))); 294 return; 295 } 296 __ movzwq(dst, Address(env->bytecode, env->pc, TIMES_1, 297 heapObjectDisp(-kCodeUnitSize + 2))); 298} 299 300void emitNextOpcodeImpl(EmitEnv* env) { 301 ScratchReg r_scratch(env); 302 303 __ movzbl(r_scratch, 304 Address(env->bytecode, env->pc, TIMES_1, heapObjectDisp(0))); 305 env->register_state.assign(&env->oparg, kOpargReg); 306 __ movzbl(env->oparg, 307 Address(env->bytecode, env->pc, TIMES_1, heapObjectDisp(1))); 308 __ addl(env->pc, Immediate(kCodeUnitSize)); 309 __ shll(r_scratch, Immediate(kHandlerSizeShift)); 310 __ addq(r_scratch, env->handlers_base); 311 env->register_state.check(env->handler_assignment); 312 __ jmp(r_scratch); 313 // Hint to the branch predictor that the indirect jmp never falls through to 314 // here. 315 __ ud2(); 316} 317 318// Load the next opcode, advance PC, and jump to the appropriate handler. 319void emitNextOpcodeFallthrough(EmitEnv* env) { 320 if (env->in_jit) { 321 JitEnv* jenv = static_cast<JitEnv*>(env); 322 env->register_state.check(jenv->jit_handler_assignment); 323 return; 324 } 325 emitNextOpcodeImpl(env); 326} 327 328void emitNextOpcode(EmitEnv* env) { 329 if (env->in_jit) { 330 JitEnv* jenv = static_cast<JitEnv*>(env); 331 env->register_state.check(jenv->jit_handler_assignment); 332 __ jmp(jenv->opcodeAtByteOffset(jenv->virtualPC()), Assembler::kFarJump); 333 return; 334 } 335 emitNextOpcodeImpl(env); 336} 337 338enum SaveRestoreFlags { 339 kVMStack = 1 << 0, 340 kVMFrame = 1 << 1, 341 kBytecode = 1 << 2, 342 kVMPC = 1 << 3, 343 kHandlerBase = 1 << 4, 344 kHandlerWithoutFrameChange = kVMStack | kBytecode, 345 kAllState = kVMStack | kVMFrame | kBytecode | kVMPC | kHandlerBase, 346 kGenericHandler = kAllState & ~kHandlerBase, 347}; 348 349void emitSaveInterpreterState(EmitEnv* env, word flags) { 350 if (flags & kVMFrame) { 351 __ movq(Address(env->thread, Thread::currentFrameOffset()), env->frame); 352 } 353 if (flags & kVMStack) { 354 __ movq(Address(env->thread, Thread::stackPointerOffset()), RSP); 355 __ leaq(RSP, Address(RBP, -kNativeStackFrameSize)); 356 } 357 DCHECK((flags & kBytecode) == 0, "Storing bytecode not supported"); 358 if (flags & kVMPC) { 359 __ movq(Address(env->frame, Frame::kVirtualPCOffset), env->pc); 360 } 361 DCHECK((flags & kHandlerBase) == 0, "Storing handlerbase not supported"); 362} 363 364void emitRestoreInterpreterState(EmitEnv* env, word flags) { 365 if (flags & kVMFrame) { 366 env->register_state.assign(&env->frame, kFrameReg); 367 __ movq(env->frame, Address(env->thread, Thread::currentFrameOffset())); 368 } 369 if (flags & kVMStack) { 370 __ movq(RSP, Address(env->thread, Thread::stackPointerOffset())); 371 } 372 if (flags & kBytecode) { 373 env->register_state.assign(&env->bytecode, kBCReg); 374 __ movq(env->bytecode, Address(env->frame, Frame::kBytecodeOffset)); 375 } 376 if (flags & kVMPC) { 377 env->register_state.assign(&env->pc, kPCReg); 378 __ movl(env->pc, Address(env->frame, Frame::kVirtualPCOffset)); 379 } 380 if (flags & kHandlerBase) { 381 env->register_state.assign(&env->handlers_base, kHandlersBaseReg); 382 __ movq(env->handlers_base, 383 Address(env->thread, Thread::interpreterDataOffset())); 384 } 385} 386 387SaveRestoreFlags mayChangeFramePC(Bytecode bc) { 388 // These opcodes have been manually vetted to ensure that they don't change 389 // the current frame or PC (or if they do, it's through something like 390 // Interpreter::callMethodN(), which restores the previous frame when it's 391 // finished). This lets us avoid reloading the frame after calling their C++ 392 // implementations. 393 switch (bc) { 394 case BINARY_ADD_SMALLINT: 395 case BINARY_AND_SMALLINT: 396 case BINARY_FLOORDIV_SMALLINT: 397 case BINARY_SUB_SMALLINT: 398 case BINARY_OR_SMALLINT: 399 case COMPARE_EQ_SMALLINT: 400 case COMPARE_LE_SMALLINT: 401 case COMPARE_NE_SMALLINT: 402 case COMPARE_GE_SMALLINT: 403 case COMPARE_LT_SMALLINT: 404 case COMPARE_GT_SMALLINT: 405 case INPLACE_ADD_SMALLINT: 406 case INPLACE_SUB_SMALLINT: 407 case LOAD_ATTR_INSTANCE: 408 case LOAD_ATTR_INSTANCE_TYPE_BOUND_METHOD: 409 case LOAD_ATTR_POLYMORPHIC: 410 case STORE_ATTR_INSTANCE: 411 case STORE_ATTR_INSTANCE_OVERFLOW: 412 case STORE_ATTR_POLYMORPHIC: 413 case LOAD_METHOD_INSTANCE_FUNCTION: 414 case LOAD_METHOD_POLYMORPHIC: 415 return kHandlerWithoutFrameChange; 416 case CALL_FUNCTION: 417 case CALL_FUNCTION_ANAMORPHIC: 418 return kAllState; 419 default: 420 return kGenericHandler; 421 } 422} 423 424template <typename FPtr> 425void emitCall(EmitEnv* env, FPtr function) { 426 ScratchReg r_function(env); 427 // TODO(T84334712) Use call with immediate instead of movq+call. 428 __ movq(r_function, Immediate(reinterpret_cast<int64_t>(function))); 429 __ call(r_function); 430 env->register_state.clobber(kCallerSavedRegs); 431} 432 433void emitCallReg(EmitEnv* env, Register function) { 434 __ call(function); 435 env->register_state.clobber(kCallerSavedRegs); 436} 437 438void emitJumpToDeopt(EmitEnv* env) { 439 DCHECK(env->in_jit, "deopt not supported for non-JIT assembly"); 440 JitEnv* jenv = static_cast<JitEnv*>(env); 441 // Set the PC to what would be in a normal opcode handler. We may not have 442 // set it if the JIT handler did not need the PC. 443 env->register_state.assign(&env->pc, kPCReg); 444 __ movq(env->pc, Immediate(jenv->virtualPC())); 445 env->register_state.check(jenv->deopt_assignment); 446 __ jmp(&jenv->deopt_handler, Assembler::kFarJump); 447} 448 449void emitHandleContinue(EmitEnv* env, SaveRestoreFlags flags) { 450 ScratchReg r_result(env, kReturnRegs[0]); 451 452 Label handle_flow; 453 static_assert(static_cast<int>(Interpreter::Continue::NEXT) == 0, 454 "NEXT must be 0"); 455 __ testl(r_result, r_result); 456 __ jcc(NOT_ZERO, &handle_flow, Assembler::kNearJump); 457 458 // Note that we do not restore the `kHandlerBase` for now. That saves some 459 // cycles but fail to cleanly switch interpreter handlers for stackframes that 460 // are already active at the time the handlers are switched. 461 emitRestoreInterpreterState(env, flags); 462 emitNextOpcode(env); 463 464 // TODO(T91195773): Decide if this special-case here makes sense or if it is 465 // worth duplicating the pseudo-handlers / exposing their address via some 466 // API so that we can jump directly into the DEOPT one. Would rather not have 467 // a separate pseudo-handler per function. 468 __ bind(&handle_flow); 469 if (env->in_jit) { 470 Label deopt; 471 __ cmpb(r_result, 472 Immediate(static_cast<byte>(Interpreter::Continue::DEOPT))); 473 __ jcc(EQUAL, &deopt, Assembler::kNearJump); 474 // The JIT should never get here; it should always deopt beforehand. 475 __ ud2(); 476 477 __ bind(&deopt); 478 // TODO(T91195826): See if we can get this data statically instead of off 479 // the frame object. 480 emitRestoreInterpreterState(env, kGenericHandler); 481 emitJumpToDeopt(env); 482 } else { 483 __ shll(r_result, Immediate(kHandlerSizeShift)); 484 __ leaq(r_result, Address(env->handlers_base, r_result, TIMES_1, 485 -Interpreter::kNumContinues * kHandlerSize)); 486 env->register_state.check(env->return_handler_assignment); 487 __ jmp(r_result); 488 } 489 490 env->register_state.reset(); 491} 492 493void emitHandleContinueIntoInterpreter(EmitEnv* env, SaveRestoreFlags flags) { 494 ScratchReg r_result(env, kReturnRegs[0]); 495 496 Label handle_flow; 497 static_assert(static_cast<int>(Interpreter::Continue::NEXT) == 0, 498 "NEXT must be 0"); 499 __ testl(r_result, r_result); 500 __ jcc(NOT_ZERO, &handle_flow, Assembler::kNearJump); 501 502 // Note that we do not restore the `kHandlerBase` for now. That saves some 503 // cycles but fail to cleanly switch interpreter handlers for stackframes that 504 // are already active at the time the handlers are switched. 505 emitRestoreInterpreterState(env, flags); 506 emitNextOpcodeImpl(env); 507 508 __ bind(&handle_flow); 509 __ shll(r_result, Immediate(kHandlerSizeShift)); 510 __ leaq(r_result, Address(env->handlers_base, r_result, TIMES_1, 511 -Interpreter::kNumContinues * kHandlerSize)); 512 env->register_state.check(env->return_handler_assignment); 513 __ jmp(r_result); 514 env->register_state.reset(); 515} 516 517// Emit a call to the C++ implementation of the given Bytecode, saving and 518// restoring appropriate interpreter state before and after the call. This code 519// is emitted as a series of stubs after the main set of handlers; it's used 520// from the hot path with emitJumpToGenericHandler(). 521void emitGenericHandler(EmitEnv* env, Bytecode bc) { 522 __ movq(kArgRegs[0], env->thread); 523 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 524 525 // Sync VM state to memory and restore native stack pointer. 526 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 527 528 emitCall<Interpreter::Continue (*)(Thread*, word)>(env, kCppHandlers[bc]); 529 530 emitHandleContinue(env, mayChangeFramePC(bc)); 531} 532 533Label* genericHandlerLabel(EmitEnv* env) { 534 return &env->opcode_handlers[env->current_op]; 535} 536 537// Jump to the generic handler for the Bytecode being currently emitted. 538void emitJumpToGenericHandler(EmitEnv* env) { 539 if (env->in_jit) { 540 // Just generate the jump to generic handler inline. No side table. 541 emitGenericHandler(env, env->current_op); 542 return; 543 } 544 env->register_state.check(env->handler_assignment); 545 __ jmp(genericHandlerLabel(env), Assembler::kFarJump); 546} 547 548// Fallback handler for all unimplemented opcodes: call out to C++. 549template <Bytecode bc> 550void emitHandler(EmitEnv* env) { 551 emitJumpToGenericHandler(env); 552} 553 554static void emitJumpIfSmallInt(EmitEnv* env, Register object, Label* target) { 555 static_assert(Object::kSmallIntTag == 0, "unexpected tag for SmallInt"); 556 __ testb(object, Immediate(Object::kSmallIntTagMask)); 557 __ jcc(ZERO, target, Assembler::kNearJump); 558} 559 560static void emitJumpIfNotSmallInt(EmitEnv* env, Register object, 561 Label* target) { 562 static_assert(Object::kSmallIntTag == 0, "unexpected tag for SmallInt"); 563 __ testb(object, Immediate(Object::kSmallIntTagMask)); 564 __ jcc(NOT_ZERO, target, Assembler::kNearJump); 565} 566 567static void emitJumpIfNotBothSmallInt(EmitEnv* env, Register value0, 568 Register value1, Register scratch, 569 Label* target) { 570 static_assert(Object::kSmallIntTag == 0, "unexpected tag for SmallInt"); 571 __ movq(scratch, value1); 572 __ orq(scratch, value0); 573 emitJumpIfNotSmallInt(env, scratch, target); 574} 575 576static void emitJumpIfImmediate(EmitEnv* env, Register obj, Label* target, 577 bool is_near_jump) { 578 ScratchReg r_scratch(env); 579 // Adding `(-kHeapObjectTag) & kPrimaryTagMask` will set the lowest 580 // `kPrimaryTagBits` bits to zero iff the object had a `kHeapObjectTag`. 581 __ leal(r_scratch, 582 Address(obj, (-Object::kHeapObjectTag) & Object::kPrimaryTagMask)); 583 __ testl(r_scratch, Immediate(Object::kPrimaryTagMask)); 584 __ jcc(NOT_ZERO, target, is_near_jump); 585} 586 587// Load the LayoutId of the RawObject in r_obj into r_dst as a SmallInt. 588// 589// Writes to r_dst. 590void emitGetLayoutId(EmitEnv* env, Register r_dst, Register r_obj) { 591 Label not_heap_object; 592 emitJumpIfImmediate(env, r_obj, &not_heap_object, Assembler::kNearJump); 593 594 // It is a HeapObject. 595 static_assert(RawHeader::kLayoutIdOffset + Header::kLayoutIdBits <= 32, 596 "expected layout id in lower 32 bits"); 597 __ movl(r_dst, Address(r_obj, heapObjectDisp(RawHeapObject::kHeaderOffset))); 598 __ shrl(r_dst, 599 Immediate(RawHeader::kLayoutIdOffset - Object::kSmallIntTagBits)); 600 __ andl(r_dst, Immediate(Header::kLayoutIdMask << Object::kSmallIntTagBits)); 601 Label done; 602 __ jmp(&done, Assembler::kNearJump); 603 604 __ bind(&not_heap_object); 605 static_assert(static_cast<int>(LayoutId::kSmallInt) == 0, 606 "Expected SmallInt LayoutId to be 0"); 607 __ xorl(r_dst, r_dst); 608 static_assert(Object::kSmallIntTagBits == 1 && Object::kSmallIntTag == 0, 609 "unexpected SmallInt tag"); 610 emitJumpIfSmallInt(env, r_obj, &done); 611 612 // Immediate. 613 __ movl(r_dst, r_obj); 614 __ andl(r_dst, Immediate(Object::kImmediateTagMask)); 615 static_assert(Object::kSmallIntTag == 0, "Unexpected SmallInt tag"); 616 __ shll(r_dst, Immediate(Object::kSmallIntTagBits)); 617 618 __ bind(&done); 619} 620 621// Assumes r_obj is a HeapObject. 622void emitJumpIfNotHasLayoutId(EmitEnv* env, Register r_obj, LayoutId layout_id, 623 Label* target) { 624 // It is a HeapObject. 625 ScratchReg r_scratch(env); 626 static_assert(RawHeader::kLayoutIdOffset + Header::kLayoutIdBits <= 32, 627 "expected layout id in lower 32 bits"); 628 __ movl(r_scratch, 629 Address(r_obj, heapObjectDisp(RawHeapObject::kHeaderOffset))); 630 __ andl(r_scratch, 631 Immediate(Header::kLayoutIdMask << RawHeader::kLayoutIdOffset)); 632 __ cmpl(r_scratch, Immediate(static_cast<word>(layout_id) 633 << RawHeader::kLayoutIdOffset)); 634 __ jcc(NOT_EQUAL, target, Assembler::kNearJump); 635} 636 637void emitJumpIfNotHeapObjectWithLayoutId(EmitEnv* env, Register r_obj, 638 LayoutId layout_id, Label* target) { 639 emitJumpIfImmediate(env, r_obj, target, Assembler::kNearJump); 640 641 // It is a HeapObject. 642 emitJumpIfNotHasLayoutId(env, r_obj, layout_id, target); 643} 644 645// Convert the given register from a SmallInt to an int. 646void emitConvertFromSmallInt(EmitEnv* env, Register reg) { 647 __ sarq(reg, Immediate(Object::kSmallIntTagBits)); 648} 649 650// Look up an inline cache entry, like icLookup(). If found, the result will be 651// stored in r_dst. If not found, r_dst will be unmodified and the code will 652// jump to not_found. r_layout_id should contain the output of 653// emitGetLayoutId(), r_caches should hold the RawTuple of caches for the 654// current function, r_index should contain the opcode argument for the current 655// instruction, and r_scratch is used as scratch. 656// 657// Writes to r_dst, r_layout_id (to turn it into a SmallInt), r_caches, and 658// r_scratch. 659void emitIcLookupPolymorphic(EmitEnv* env, Label* not_found, Register r_dst, 660 Register r_layout_id, Register r_caches) { 661 ScratchReg r_scratch(env); 662 // Load the cache index into r_scratch 663 emitCurrentCacheIndex(env, r_scratch); 664 // Set r_caches = r_caches + index * kPointerSize * kPointersPerEntry. 665 static_assert(kPointerSize * kIcPointersPerEntry == 1 << 4, 666 "Unexpected kIcPointersPerEntry"); 667 // Read the first value as the polymorphic cache. 668 __ shll(r_scratch, Immediate(4)); 669 __ movq(r_caches, 670 Address(r_caches, r_scratch, TIMES_1, 671 heapObjectDisp(kIcEntryValueOffset * kPointerSize))); 672 __ leaq(r_caches, Address(r_caches, heapObjectDisp(0))); 673 Label done; 674 for (int i = 0; i < kIcPointersPerPolyCache; i += kIcPointersPerEntry) { 675 bool is_last = i + kIcPointersPerEntry == kIcPointersPerPolyCache; 676 __ cmpl(Address(r_caches, (i + kIcEntryKeyOffset) * kPointerSize), 677 r_layout_id); 678 if (is_last) { 679 __ jcc(NOT_EQUAL, not_found, Assembler::kFarJump); 680 __ movq(r_dst, 681 Address(r_caches, (i + kIcEntryValueOffset) * kPointerSize)); 682 } else { 683 __ cmoveq(r_dst, 684 Address(r_caches, (i + kIcEntryValueOffset) * kPointerSize)); 685 __ jcc(EQUAL, &done, Assembler::kNearJump); 686 } 687 } 688 __ bind(&done); 689} 690 691void emitIcLookupMonomorphic(EmitEnv* env, Label* not_found, Register r_dst, 692 Register r_layout_id, Register r_caches) { 693 ScratchReg r_scratch(env); 694 // Load the cache index into r_scratch 695 emitCurrentCacheIndex(env, r_scratch); 696 // Set r_caches = r_caches + index * kPointerSize * kPointersPerEntry. 697 static_assert(kIcPointersPerEntry == 2, "Unexpected kIcPointersPerEntry"); 698 __ leaq(r_scratch, Address(r_scratch, TIMES_2, 0)); 699 __ leaq(r_caches, Address(r_caches, r_scratch, TIMES_8, heapObjectDisp(0))); 700 __ cmpl(Address(r_caches, kIcEntryKeyOffset * kPointerSize), r_layout_id); 701 __ jcc(NOT_EQUAL, not_found, Assembler::kNearJump); 702 __ movq(r_dst, Address(r_caches, kIcEntryValueOffset * kPointerSize)); 703} 704 705// Allocate and push a BoundMethod on the stack. If the heap is full and a GC 706// is needed, jump to slow_path instead. r_self and r_function will be used to 707// populate the BoundMethod. r_space and r_scratch are used as scratch 708// registers. 709// 710// Writes to r_space and r_scratch. 711void emitPushBoundMethod(EmitEnv* env, Label* slow_path, Register r_self, 712 Register r_function, Register r_space) { 713 ScratchReg r_scratch(env); 714 __ movq(r_space, Address(env->thread, Thread::runtimeOffset())); 715 __ movq(r_space, 716 Address(r_space, Runtime::heapOffset() + Heap::spaceOffset())); 717 718 __ movq(r_scratch, Address(r_space, Space::fillOffset())); 719 int num_attrs = BoundMethod::kSize / kPointerSize; 720 __ addq(r_scratch, Immediate(Instance::allocationSize(num_attrs))); 721 __ cmpq(r_scratch, Address(r_space, Space::endOffset())); 722 __ jcc(GREATER, slow_path, Assembler::kFarJump); 723 __ xchgq(r_scratch, Address(r_space, Space::fillOffset())); 724 RawHeader header = Header::from(num_attrs, 0, LayoutId::kBoundMethod, 725 ObjectFormat::kObjects); 726 __ movq(Address(r_scratch, 0), Immediate(header.raw())); 727 __ leaq(r_scratch, Address(r_scratch, -RawBoundMethod::kHeaderOffset + 728 Object::kHeapObjectTag)); 729 __ movq(Address(r_scratch, heapObjectDisp(RawBoundMethod::kSelfOffset)), 730 r_self); 731 __ movq(Address(r_scratch, heapObjectDisp(RawBoundMethod::kFunctionOffset)), 732 r_function); 733 __ pushq(r_scratch); 734} 735 736// Given a RawObject in r_obj and its LayoutId (as a SmallInt) in r_layout_id, 737// load its overflow RawTuple into r_dst. 738// 739// Writes to r_dst. 740void emitLoadOverflowTuple(EmitEnv* env, Register r_dst, Register r_layout_id, 741 Register r_obj) { 742 // Both uses of TIMES_4 in this function are a shortcut to multiply the value 743 // of a SmallInt by kPointerSize. 744 static_assert(kPointerSize >> Object::kSmallIntTagBits == 4, 745 "Unexpected values of kPointerSize and/or kSmallIntTagBits"); 746 747 // TODO(bsimmers): This sequence of loads is pretty gross. See if we can make 748 // the information more accessible. 749 750 // Load thread->runtime() 751 __ movq(r_dst, Address(env->thread, Thread::runtimeOffset())); 752 // Load runtime->layouts_ 753 __ movq(r_dst, Address(r_dst, Runtime::layoutsOffset())); 754 // Load layouts_[r_layout_id] 755 __ movq(r_dst, Address(r_dst, r_layout_id, TIMES_4, heapObjectDisp(0))); 756 // Load layout.numInObjectAttributes 757 __ movq( 758 r_dst, 759 Address(r_dst, heapObjectDisp(RawLayout::kNumInObjectAttributesOffset))); 760 __ movq(r_dst, Address(r_obj, r_dst, TIMES_4, heapObjectDisp(0))); 761} 762 763// Push/pop from/into an attribute of r_obj, given a SmallInt offset in r_offset 764// (which may be negative to signal an overflow attribute). r_layout_id should 765// contain the object's LayoutId as a SmallInt and is used to look up the 766// overflow tuple offset if needed. 767// 768// Emits the "next opcode" sequence after the in-object attribute case, binding 769// next at that location, and jumps to next at the end of the overflow attribute 770// case. 771// 772// Writes to r_offset. 773void emitAttrWithOffset(EmitEnv* env, void (Assembler::*asm_op)(Address), 774 Label* next, Register r_obj, Register r_offset, 775 Register r_layout_id) { 776 Label is_overflow; 777 emitConvertFromSmallInt(env, r_offset); 778 __ testq(r_offset, r_offset); 779 __ jcc(SIGN, &is_overflow, Assembler::kNearJump); 780 // In-object attribute. For now, asm_op is always pushq or popq. 781 (env->as.*asm_op)(Address(r_obj, r_offset, TIMES_1, heapObjectDisp(0))); 782 __ bind(next); 783 emitNextOpcode(env); 784 785 __ bind(&is_overflow); 786 ScratchReg r_scratch(env); 787 emitLoadOverflowTuple(env, r_scratch, r_layout_id, r_obj); 788 // The real tuple index is -offset - 1, which is the same as ~offset. 789 __ notq(r_offset); 790 (env->as.*asm_op)(Address(r_scratch, r_offset, TIMES_8, heapObjectDisp(0))); 791 __ jmp(next, Assembler::kNearJump); 792} 793 794template <> 795void emitHandler<BINARY_ADD_SMALLINT>(EmitEnv* env) { 796 ScratchReg r_right(env); 797 ScratchReg r_left(env); 798 ScratchReg r_result(env); 799 Label slow_path; 800 801 __ popq(r_right); 802 __ popq(r_left); 803 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 804 // Preserve argument values in case of overflow. 805 __ movq(r_result, r_left); 806 __ addq(r_result, r_right); 807 __ jcc(YES_OVERFLOW, &slow_path, Assembler::kNearJump); 808 __ pushq(r_result); 809 emitNextOpcode(env); 810 811 __ bind(&slow_path); 812 __ pushq(r_left); 813 __ pushq(r_right); 814 if (env->in_jit) { 815 emitJumpToDeopt(env); 816 return; 817 } 818 __ movq(kArgRegs[0], env->thread); 819 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 820 emitCurrentCacheIndex(env, kArgRegs[2]); 821 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 822 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 823 env, Interpreter::binaryOpUpdateCache); 824 emitHandleContinue(env, kGenericHandler); 825} 826 827template <> 828void emitHandler<BINARY_AND_SMALLINT>(EmitEnv* env) { 829 ScratchReg r_right(env); 830 ScratchReg r_left(env); 831 ScratchReg r_result(env); 832 Label slow_path; 833 834 __ popq(r_right); 835 __ popq(r_left); 836 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 837 __ movq(r_result, r_left); 838 __ andq(r_result, r_right); 839 __ pushq(r_result); 840 emitNextOpcode(env); 841 842 __ bind(&slow_path); 843 __ pushq(r_left); 844 __ pushq(r_right); 845 if (env->in_jit) { 846 emitJumpToDeopt(env); 847 return; 848 } 849 __ movq(kArgRegs[0], env->thread); 850 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 851 emitCurrentCacheIndex(env, kArgRegs[2]); 852 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 853 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 854 env, Interpreter::binaryOpUpdateCache); 855 emitHandleContinue(env, kGenericHandler); 856} 857 858template <> 859void emitHandler<BINARY_SUB_SMALLINT>(EmitEnv* env) { 860 ScratchReg r_right(env); 861 ScratchReg r_left(env); 862 ScratchReg r_result(env); 863 Label slow_path; 864 865 __ popq(r_right); 866 __ popq(r_left); 867 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 868 // Preserve argument values in case of overflow. 869 __ movq(r_result, r_left); 870 __ subq(r_result, r_right); 871 __ jcc(YES_OVERFLOW, &slow_path, Assembler::kNearJump); 872 __ pushq(r_result); 873 emitNextOpcode(env); 874 875 __ bind(&slow_path); 876 __ pushq(r_left); 877 __ pushq(r_right); 878 if (env->in_jit) { 879 emitJumpToDeopt(env); 880 return; 881 } 882 __ movq(kArgRegs[0], env->thread); 883 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 884 emitCurrentCacheIndex(env, kArgRegs[2]); 885 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 886 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 887 env, Interpreter::binaryOpUpdateCache); 888 emitHandleContinue(env, kGenericHandler); 889} 890 891template <> 892void emitHandler<BINARY_MUL_SMALLINT>(EmitEnv* env) { 893 ScratchReg r_right(env); 894 ScratchReg r_left(env); 895 ScratchReg r_result(env); 896 Label slow_path; 897 898 __ popq(r_right); 899 __ popq(r_left); 900 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 901 // Preserve argument values in case of overflow. 902 __ movq(r_result, r_left); 903 emitConvertFromSmallInt(env, r_result); 904 __ imulq(r_result, r_right); 905 __ jcc(YES_OVERFLOW, &slow_path, Assembler::kNearJump); 906 __ pushq(r_result); 907 emitNextOpcode(env); 908 909 __ bind(&slow_path); 910 __ pushq(r_left); 911 __ pushq(r_right); 912 if (env->in_jit) { 913 emitJumpToDeopt(env); 914 return; 915 } 916 __ movq(kArgRegs[0], env->thread); 917 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 918 emitCurrentCacheIndex(env, kArgRegs[2]); 919 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 920 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 921 env, Interpreter::binaryOpUpdateCache); 922 emitHandleContinue(env, kGenericHandler); 923} 924 925template <> 926void emitHandler<BINARY_OR_SMALLINT>(EmitEnv* env) { 927 ScratchReg r_right(env); 928 ScratchReg r_left(env); 929 ScratchReg r_result(env); 930 Label slow_path; 931 932 __ popq(r_right); 933 __ popq(r_left); 934 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 935 // There is not __ orq instruction here because it is in the 936 // emitJumpIfNotSmallInt implementation. 937 __ pushq(r_result); 938 emitNextOpcode(env); 939 940 __ bind(&slow_path); 941 __ pushq(r_left); 942 __ pushq(r_right); 943 if (env->in_jit) { 944 emitJumpToDeopt(env); 945 return; 946 } 947 __ movq(kArgRegs[0], env->thread); 948 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 949 emitCurrentCacheIndex(env, kArgRegs[2]); 950 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 951 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 952 env, Interpreter::binaryOpUpdateCache); 953 emitHandleContinue(env, kGenericHandler); 954} 955 956// Push r_list[r_index_smallint] into the stack. 957static void emitPushListAt(EmitEnv* env, Register r_list, 958 Register r_index_smallint) { 959 ScratchReg r_scratch(env); 960 __ movq(r_scratch, Address(r_list, heapObjectDisp(List::kItemsOffset))); 961 // r_index is a SmallInt, so r_key already stores the index value * 2. 962 // Therefore, applying TIMES_4 will compute index * 8. 963 static_assert(Object::kSmallIntTag == 0, "unexpected tag for SmallInt"); 964 static_assert(Object::kSmallIntTagBits == 1, "unexpected tag for SmallInt"); 965 __ pushq(Address(r_scratch, r_index_smallint, TIMES_4, heapObjectDisp(0))); 966} 967 968template <> 969void emitHandler<BINARY_SUBSCR_LIST>(EmitEnv* env) { 970 ScratchReg r_container(env); 971 ScratchReg r_key(env); 972 Label slow_path; 973 974 __ popq(r_key); 975 __ popq(r_container); 976 // if (container.isList() && key.isSmallInt()) { 977 emitJumpIfNotHeapObjectWithLayoutId(env, r_container, LayoutId::kList, 978 &slow_path); 979 emitJumpIfNotSmallInt(env, r_key, &slow_path); 980 981 // if (0 <= index && index < length) { 982 // length >= 0 always holds. Therefore, ABOVE_EQUAL == NOT_CARRY if r_key 983 // contains a negative value (sign bit == 1) or r_key >= r_list_length. 984 __ cmpq(r_key, Address(r_container, heapObjectDisp(List::kNumItemsOffset))); 985 __ jcc(ABOVE_EQUAL, &slow_path, Assembler::kNearJump); 986 987 // Push list.at(index) 988 emitPushListAt(env, r_container, r_key); 989 emitNextOpcode(env); 990 991 __ bind(&slow_path); 992 __ pushq(r_container); 993 __ pushq(r_key); 994 if (env->in_jit) { 995 emitJumpToDeopt(env); 996 return; 997 } 998 __ movq(kArgRegs[0], env->thread); 999 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1000 emitCurrentCacheIndex(env, kArgRegs[1]); 1001 emitCall<Interpreter::Continue (*)(Thread*, word)>( 1002 env, Interpreter::binarySubscrUpdateCache); 1003 emitHandleContinue(env, kGenericHandler); 1004} 1005 1006void emitHeaderCountOrOverflow(EmitEnv* env, Register r_dst, 1007 Register r_container) { 1008 // Load header().count() as a SmallInt 1009 // r_dst = header().count() 1010 __ movq(r_dst, 1011 Address(r_container, heapObjectDisp(RawHeapObject::kHeaderOffset))); 1012 __ shrq(r_dst, Immediate(RawHeader::kCountOffset - Object::kSmallIntTagBits)); 1013 __ andq(r_dst, Immediate(Header::kCountMask << Object::kSmallIntTagBits)); 1014 // if (r_dst == kCountOverflowFlag) 1015 __ cmpq(r_dst, smallIntImmediate(RawHeader::kCountOverflowFlag)); 1016 Label done; 1017 __ jcc(NOT_EQUAL, &done, Assembler::kNearJump); 1018 __ movq(r_dst, Address(r_container, 1019 heapObjectDisp(RawHeapObject::kHeaderOverflowOffset))); 1020 __ bind(&done); 1021} 1022 1023// Push r_tuple[r_index_smallint] into the stack. 1024static void emitPushTupleAt(EmitEnv* env, Register r_tuple, 1025 Register r_index_smallint) { 1026 // r_index is a SmallInt, so r_key already stores the index value * 2. 1027 // Therefore, applying TIMES_4 will compute index * 8. 1028 static_assert(Object::kSmallIntTag == 0, "unexpected tag for SmallInt"); 1029 static_assert(Object::kSmallIntTagBits == 1, "unexpected tag for SmallInt"); 1030 __ pushq(Address(r_tuple, r_index_smallint, TIMES_4, heapObjectDisp(0))); 1031} 1032 1033template <> 1034void emitHandler<BINARY_SUBSCR_TUPLE>(EmitEnv* env) { 1035 ScratchReg r_container(env); 1036 ScratchReg r_key(env); 1037 ScratchReg r_num_items(env); 1038 Label slow_path; 1039 1040 __ popq(r_key); 1041 __ popq(r_container); 1042 // if (container.isTuple() && key.isSmallInt()) { 1043 emitJumpIfNotHeapObjectWithLayoutId(env, r_container, LayoutId::kTuple, 1044 &slow_path); 1045 emitJumpIfNotSmallInt(env, r_key, &slow_path); 1046 // r_num_items = container.headerCountOrOverflow() 1047 emitHeaderCountOrOverflow(env, r_num_items, r_container); 1048 // if (r_index >= r_num_items) goto terminate; 1049 // if (0 <= index && index < length) { 1050 // length >= 0 always holds. Therefore, ABOVE_EQUAL == NOT_CARRY if r_key 1051 // contains a negative value (sign bit == 1) or r_key >= r_num_items. 1052 __ cmpq(r_key, r_num_items); 1053 __ jcc(ABOVE_EQUAL, &slow_path, Assembler::kNearJump); 1054 1055 // Push tuple.at(index) 1056 emitPushTupleAt(env, r_container, r_key); 1057 emitNextOpcode(env); 1058 1059 __ bind(&slow_path); 1060 __ pushq(r_container); 1061 __ pushq(r_key); 1062 if (env->in_jit) { 1063 emitJumpToDeopt(env); 1064 return; 1065 } 1066 __ movq(kArgRegs[0], env->thread); 1067 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1068 emitCurrentCacheIndex(env, kArgRegs[1]); 1069 emitCall<Interpreter::Continue (*)(Thread*, word)>( 1070 env, Interpreter::binarySubscrUpdateCache); 1071 emitHandleContinue(env, kGenericHandler); 1072} 1073 1074static void emitSetReturnMode(EmitEnv* env) { 1075 env->register_state.assign(&env->return_mode, kReturnModeReg); 1076 if (env->in_jit) { 1077 __ movq(env->return_mode, Immediate(word{Frame::ReturnMode::kJitReturn} 1078 << Frame::kReturnModeOffset)); 1079 } else { 1080 __ xorl(env->return_mode, env->return_mode); 1081 } 1082} 1083 1084static void emitJumpToEntryAsm(EmitEnv* env, Register r_function) { 1085 env->register_state.check(env->function_entry_assignment); 1086 __ jmp(Address(r_function, heapObjectDisp(Function::kEntryAsmOffset))); 1087} 1088 1089// Functions called from JIT-compiled functions emulate call/ret on the C++ 1090// stack to avoid putting random pointers on the Python stack. This emulates 1091// `call'. 1092static void emitPseudoCall(EmitEnv* env, Register r_function) { 1093 DCHECK(env->in_jit, "pseudo-call not supported for non-JIT assembly"); 1094 ScratchReg r_next(env); 1095 Label next; 1096 1097 __ subq(RBP, Immediate(kCallStackAlignment)); 1098 __ leaq(r_next, &next); 1099 __ movq(Address(RBP, -kNativeStackFrameSize), r_next); 1100 emitJumpToEntryAsm(env, r_function); 1101 // `next' label address must be able to fit in a SmallInt. 1102 __ align(1 << Object::kSmallIntTagBits); 1103 __ bind(&next); 1104} 1105 1106static void emitFunctionCall(EmitEnv* env, Register r_function) { 1107 emitSetReturnMode(env); 1108 if (env->in_jit) { 1109 // TODO(T91716080): Push next opcode as return address instead of 1110 // emitNextOpcode second jump 1111 emitPseudoCall(env, r_function); 1112 emitNextOpcode(env); 1113 } else { 1114 emitJumpToEntryAsm(env, r_function); 1115 } 1116} 1117 1118template <> 1119void emitHandler<BINARY_SUBSCR_MONOMORPHIC>(EmitEnv* env) { 1120 ScratchReg r_receiver(env); 1121 ScratchReg r_layout_id(env); 1122 ScratchReg r_key(env); 1123 ScratchReg r_caches(env); 1124 1125 Label slow_path; 1126 __ popq(r_key); 1127 __ popq(r_receiver); 1128 emitGetLayoutId(env, r_layout_id, r_receiver); 1129 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1130 env->register_state.assign(&env->callable, kCallableReg); 1131 emitIcLookupMonomorphic(env, &slow_path, env->callable, r_layout_id, 1132 r_caches); 1133 1134 // Call __getitem__(receiver, key) 1135 __ pushq(env->callable); 1136 __ pushq(r_receiver); 1137 __ pushq(r_key); 1138 __ movq(env->oparg, Immediate(2)); 1139 emitFunctionCall(env, env->callable); 1140 1141 __ bind(&slow_path); 1142 __ pushq(r_receiver); 1143 __ pushq(r_key); 1144 if (env->in_jit) { 1145 emitJumpToDeopt(env); 1146 return; 1147 } 1148 emitJumpToGenericHandler(env); 1149} 1150 1151template <> 1152void emitHandler<STORE_SUBSCR_LIST>(EmitEnv* env) { 1153 ScratchReg r_container(env); 1154 ScratchReg r_key(env); 1155 ScratchReg r_layout_id(env); 1156 Label slow_path_non_list; 1157 Label slow_path; 1158 1159 __ popq(r_key); 1160 __ popq(r_container); 1161 // if (container.isList() && key.isSmallInt()) { 1162 emitJumpIfNotHeapObjectWithLayoutId(env, r_container, LayoutId::kList, 1163 &slow_path_non_list); 1164 emitJumpIfNotSmallInt(env, r_key, &slow_path); 1165 1166 // Re-use r_layout_id to store the value (right hand side). 1167 __ popq(r_layout_id); 1168 1169 // if (0 <= index && index < length) { 1170 // length >= 0 always holds. Therefore, ABOVE_EQUAL == NOT_CARRY if r_key 1171 // contains a negative value (sign bit == 1) or r_key >= r_list_length. 1172 __ cmpq(r_key, Address(r_container, heapObjectDisp(List::kNumItemsOffset))); 1173 __ jcc(ABOVE_EQUAL, &slow_path, Assembler::kNearJump); 1174 1175 // &list.at(index) 1176 __ movq(r_container, 1177 Address(r_container, heapObjectDisp(List::kItemsOffset))); 1178 // r_key is a SmallInt, so r_key already stores the index value * 2. 1179 // Therefore, applying TIMES_4 will compute index * 8. 1180 static_assert(Object::kSmallIntTag == 0, "unexpected tag for SmallInt"); 1181 static_assert(Object::kSmallIntTagBits == 1, "unexpected tag for SmallInt"); 1182 __ movq(Address(r_container, r_key, TIMES_4, heapObjectDisp(0)), r_layout_id); 1183 1184 emitNextOpcode(env); 1185 1186 __ bind(&slow_path); 1187 __ pushq(r_layout_id); 1188 __ bind(&slow_path_non_list); 1189 __ pushq(r_container); 1190 __ pushq(r_key); 1191 if (env->in_jit) { 1192 emitJumpToDeopt(env); 1193 return; 1194 } 1195 __ movq(kArgRegs[0], env->thread); 1196 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1197 emitCurrentCacheIndex(env, kArgRegs[1]); 1198 emitCall<Interpreter::Continue (*)(Thread*, word)>( 1199 env, Interpreter::storeSubscrUpdateCache); 1200 emitHandleContinue(env, kGenericHandler); 1201} 1202 1203// TODO(T59397957): Split this into two opcodes. 1204template <> 1205void emitHandler<LOAD_ATTR_INSTANCE>(EmitEnv* env) { 1206 ScratchReg r_base(env); 1207 ScratchReg r_layout_id(env); 1208 ScratchReg r_scratch(env); 1209 ScratchReg r_caches(env); 1210 Label slow_path; 1211 __ popq(r_base); 1212 emitGetLayoutId(env, r_layout_id, r_base); 1213 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1214 emitIcLookupMonomorphic(env, &slow_path, r_scratch, r_layout_id, r_caches); 1215 1216 Label next; 1217 emitAttrWithOffset(env, &Assembler::pushq, &next, r_base, r_scratch, 1218 r_layout_id); 1219 1220 __ bind(&slow_path); 1221 __ pushq(r_base); 1222 if (env->in_jit) { 1223 emitJumpToDeopt(env); 1224 return; 1225 } 1226 emitJumpToGenericHandler(env); 1227} 1228 1229static void emitJumpIfNotTypeHasFlag(EmitEnv* env, Register r_type, 1230 Type::Flag flag, Label* target) { 1231 ScratchReg r_flags(env); 1232 __ movq(r_flags, Address(r_type, heapObjectDisp(Type::kFlagsOffset))); 1233 __ andq(r_flags, smallIntImmediate(flag)); 1234 __ jcc(ZERO, target, Assembler::kNearJump); 1235} 1236 1237template <> 1238void emitHandler<LOAD_TYPE>(EmitEnv* env) { 1239 ScratchReg r_receiver(env); 1240 ScratchReg r_layout_id(env); 1241 ScratchReg r_result(env); 1242 Label slow_path; 1243 __ popq(r_receiver); 1244 emitGetLayoutId(env, r_layout_id, r_receiver); 1245 // Load thread->runtime() 1246 __ movq(r_result, Address(env->thread, Thread::runtimeOffset())); 1247 // Load runtime->layouts_ 1248 __ movq(r_result, Address(r_result, Runtime::layoutsOffset())); 1249 // Load layouts_[r_layout_id] 1250 __ movq(r_result, Address(r_result, r_layout_id, TIMES_4, heapObjectDisp(0))); 1251 // Load layout.describedType() 1252 __ movq(r_result, 1253 Address(r_result, heapObjectDisp(Layout::kDescribedTypeOffset))); 1254 // if (!r_result.isType()) { bail out } 1255 emitJumpIfNotHasLayoutId(env, r_result, LayoutId::kType, &slow_path); 1256 // if (!r_result.hasFlag(Type::Flag::kHasObjectDunderClass)) { bail out } 1257 emitJumpIfNotTypeHasFlag(env, r_result, Type::Flag::kHasObjectDunderClass, 1258 &slow_path); 1259 __ pushq(r_result); 1260 emitNextOpcode(env); 1261 1262 __ bind(&slow_path); 1263 __ pushq(r_receiver); 1264 if (env->in_jit) { 1265 emitJumpToDeopt(env); 1266 return; 1267 } 1268 emitJumpToGenericHandler(env); 1269} 1270 1271template <> 1272void emitHandler<LOAD_ATTR_INSTANCE_TYPE_BOUND_METHOD>(EmitEnv* env) { 1273 ScratchReg r_base(env); 1274 ScratchReg r_scratch(env); 1275 ScratchReg r_caches(env); 1276 Label slow_path; 1277 __ popq(r_base); 1278 { 1279 ScratchReg r_layout_id(env); 1280 emitGetLayoutId(env, r_layout_id, r_base); 1281 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1282 emitIcLookupMonomorphic(env, &slow_path, r_scratch, r_layout_id, r_caches); 1283 } 1284 emitPushBoundMethod(env, &slow_path, r_base, r_scratch, r_caches); 1285 emitNextOpcode(env); 1286 1287 __ bind(&slow_path); 1288 __ pushq(r_base); 1289 if (env->in_jit) { 1290 emitJumpToDeopt(env); 1291 return; 1292 } 1293 emitJumpToGenericHandler(env); 1294} 1295 1296// Used when transitioning from a JIT handler to an interpreter handler. 1297void jitEmitGenericHandlerSetup(JitEnv* env) { 1298 word arg = env->currentOp().arg; 1299 env->register_state.assign(&env->oparg, kOpargReg); 1300 __ movq(env->oparg, Immediate(arg)); 1301 env->register_state.assign(&env->pc, kPCReg); 1302 __ movq(env->pc, Immediate(env->virtualPC())); 1303 env->register_state.check(env->handler_assignment); 1304} 1305 1306template <> 1307void emitHandler<LOAD_ATTR_POLYMORPHIC>(EmitEnv* env) { 1308 ScratchReg r_base(env); 1309 ScratchReg r_scratch(env); 1310 ScratchReg r_caches(env); 1311 Label slow_path; 1312 Label is_function; 1313 Label next; 1314 1315 __ popq(r_base); 1316 { 1317 ScratchReg r_layout_id(env); 1318 emitGetLayoutId(env, r_layout_id, r_base); 1319 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1320 emitIcLookupPolymorphic(env, &slow_path, r_scratch, r_layout_id, r_caches); 1321 1322 emitJumpIfNotSmallInt(env, r_scratch, &is_function); 1323 emitAttrWithOffset(env, &Assembler::pushq, &next, r_base, r_scratch, 1324 r_layout_id); 1325 } 1326 1327 __ bind(&is_function); 1328 emitPushBoundMethod(env, &slow_path, r_base, r_scratch, r_caches); 1329 __ jmp(&next, Assembler::kNearJump); 1330 1331 __ bind(&slow_path); 1332 __ pushq(r_base); 1333 // Don't deopt because this won't rewrite. 1334 if (env->in_jit) { 1335 jitEmitGenericHandlerSetup(static_cast<JitEnv*>(env)); 1336 } 1337 emitJumpToGenericHandler(env); 1338} 1339 1340template <> 1341void emitHandler<LOAD_ATTR_INSTANCE_PROPERTY>(EmitEnv* env) { 1342 ScratchReg r_base(env); 1343 ScratchReg r_layout_id(env); 1344 ScratchReg r_caches(env); 1345 1346 Label slow_path; 1347 __ popq(r_base); 1348 emitGetLayoutId(env, r_layout_id, r_base); 1349 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1350 env->register_state.assign(&env->callable, kCallableReg); 1351 emitIcLookupMonomorphic(env, &slow_path, env->callable, r_layout_id, 1352 r_caches); 1353 // Call getter(receiver) 1354 __ pushq(env->callable); 1355 __ pushq(r_base); 1356 __ movq(env->oparg, Immediate(1)); 1357 emitFunctionCall(env, env->callable); 1358 1359 __ bind(&slow_path); 1360 __ pushq(r_base); 1361 if (env->in_jit) { 1362 emitJumpToDeopt(env); 1363 return; 1364 } 1365 emitJumpToGenericHandler(env); 1366} 1367 1368template <> 1369void emitHandler<LOAD_CONST>(EmitEnv* env) { 1370 ScratchReg r_scratch(env); 1371 1372 __ movq(r_scratch, Address(env->frame, Frame::kLocalsOffsetOffset)); 1373 __ movq(r_scratch, Address(env->frame, r_scratch, TIMES_1, 1374 Frame::kFunctionOffsetFromLocals * kPointerSize)); 1375 __ movq(r_scratch, 1376 Address(r_scratch, heapObjectDisp(RawFunction::kCodeOffset))); 1377 __ movq(r_scratch, 1378 Address(r_scratch, heapObjectDisp(RawCode::kConstsOffset))); 1379 __ movq(r_scratch, 1380 Address(r_scratch, env->oparg, TIMES_8, heapObjectDisp(0))); 1381 __ pushq(r_scratch); 1382 emitNextOpcodeFallthrough(env); 1383} 1384 1385template <> 1386void emitHandler<LOAD_DEREF>(EmitEnv* env) { 1387 ScratchReg r_locals_offset(env); 1388 ScratchReg r_n_locals(env); 1389 1390 // r_n_locals = frame->code()->nlocals(); 1391 __ movq(r_locals_offset, Address(env->frame, Frame::kLocalsOffsetOffset)); 1392 __ movq(r_n_locals, Address(env->frame, r_locals_offset, TIMES_1, 1393 Frame::kFunctionOffsetFromLocals * kPointerSize)); 1394 __ movq(r_n_locals, 1395 Address(r_n_locals, heapObjectDisp(RawFunction::kCodeOffset))); 1396 __ movq(r_n_locals, 1397 Address(r_n_locals, heapObjectDisp(Code::kNlocalsOffset))); 1398 1399 { 1400 ScratchReg r_idx(env); 1401 // r_idx = code.nlocals() + arg; 1402 static_assert(kPointerSize == 8, "kPointerSize is expected to be 8"); 1403 static_assert(Object::kSmallIntTagBits == 1, 1404 "kSmallIntTagBits is expected to be 1"); 1405 // nlocals already shifted by 1 as a SmallInt, so nlocals << 2 makes it 1406 // word-aligned. 1407 __ shll(r_n_locals, Immediate(2)); 1408 __ leaq(r_idx, Address(r_n_locals, env->oparg, TIMES_8, 0)); 1409 1410 // cell = frame->local(r_idx) == *(locals() - r_idx - 1); 1411 // See Frame::local. 1412 __ subq(r_locals_offset, r_idx); 1413 } 1414 // Object value(&scope, cell.value()); 1415 ScratchReg r_cell_value(env); 1416 __ movq(r_cell_value, 1417 Address(env->frame, r_locals_offset, TIMES_1, -kPointerSize)); 1418 __ movq(r_cell_value, 1419 Address(r_cell_value, heapObjectDisp(Cell::kValueOffset))); 1420 __ cmpl(r_cell_value, Immediate(Unbound::object().raw())); 1421 Label slow_path; 1422 __ jcc(EQUAL, &slow_path, Assembler::kNearJump); 1423 __ pushq(r_cell_value); 1424 emitNextOpcode(env); 1425 1426 // Handle unbound cells in the generic handler. 1427 __ bind(&slow_path); 1428 if (env->in_jit) { 1429 emitJumpToDeopt(env); 1430 return; 1431 } 1432 emitJumpToGenericHandler(env); 1433} 1434 1435template <> 1436void emitHandler<LOAD_METHOD_INSTANCE_FUNCTION>(EmitEnv* env) { 1437 ScratchReg r_base(env); 1438 ScratchReg r_layout_id(env); 1439 ScratchReg r_scratch(env); 1440 ScratchReg r_caches(env); 1441 Label slow_path; 1442 1443 __ popq(r_base); 1444 emitGetLayoutId(env, r_layout_id, r_base); 1445 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1446 emitIcLookupMonomorphic(env, &slow_path, r_scratch, r_layout_id, r_caches); 1447 1448 // Only functions are cached. 1449 __ pushq(r_scratch); 1450 __ pushq(r_base); 1451 emitNextOpcode(env); 1452 1453 __ bind(&slow_path); 1454 __ pushq(r_base); 1455 if (env->in_jit) { 1456 emitJumpToDeopt(env); 1457 return; 1458 } 1459 emitJumpToGenericHandler(env); 1460} 1461 1462static void emitHeaderHashcode(EmitEnv* env, Register r_dst, 1463 Register r_container) { 1464 // Load header().hashCode() as a SmallInt 1465 // r_dst = header().hashCode() 1466 __ movq(r_dst, 1467 Address(r_container, heapObjectDisp(RawHeapObject::kHeaderOffset))); 1468 __ shrq(r_dst, Immediate(RawHeader::kHashCodeOffset)); 1469 static_assert(RawHeader::kHashCodeOffset == 32, 1470 "expected hash code to occupy top half of qword"); 1471 static_assert(RawHeader::kHashCodeBits == 32, 1472 "expected hash code to occupy top half of qword"); 1473 __ shlq(r_dst, Immediate(Object::kSmallIntTagBits)); 1474} 1475 1476void emitPushImmediate(EmitEnv* env, word value) { 1477 if (Utils::fits<int32_t>(value)) { 1478 __ pushq(Immediate(value)); 1479 } else { 1480 ScratchReg r_scratch(env); 1481 1482 __ movq(r_scratch, Immediate(value)); 1483 __ pushq(r_scratch); 1484 } 1485} 1486 1487template <> 1488void emitHandler<LOAD_METHOD_MODULE>(EmitEnv* env) { 1489 ScratchReg r_base(env); 1490 ScratchReg r_module_id(env); 1491 ScratchReg r_function(env); 1492 ScratchReg r_caches(env); 1493 Label slow_path; 1494 1495 __ popq(r_base); 1496 emitJumpIfImmediate(env, r_base, &slow_path, Assembler::kNearJump); 1497 emitJumpIfNotHasLayoutId(env, r_base, LayoutId::kModule, &slow_path); 1498 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1499 // TODO(emacs): Avoid a second header lookup here. I am not sure what a 1500 // reasonable API for that would look like. 1501 emitHeaderHashcode(env, r_module_id, r_base); 1502 emitIcLookupMonomorphic(env, &slow_path, r_function, r_module_id, r_caches); 1503 __ movq(r_function, 1504 Address(r_function, heapObjectDisp(ValueCell::kValueOffset))); 1505 emitPushImmediate(env, Unbound::object().raw()); 1506 __ pushq(r_function); 1507 emitNextOpcode(env); 1508 1509 __ bind(&slow_path); 1510 __ pushq(r_base); 1511 if (env->in_jit) { 1512 emitJumpToDeopt(env); 1513 return; 1514 } 1515 emitJumpToGenericHandler(env); 1516} 1517 1518template <> 1519void emitHandler<LOAD_METHOD_POLYMORPHIC>(EmitEnv* env) { 1520 ScratchReg r_base(env); 1521 ScratchReg r_layout_id(env); 1522 ScratchReg r_scratch(env); 1523 ScratchReg r_caches(env); 1524 Label slow_path; 1525 1526 __ popq(r_base); 1527 emitGetLayoutId(env, r_layout_id, r_base); 1528 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1529 emitIcLookupPolymorphic(env, &slow_path, r_scratch, r_layout_id, r_caches); 1530 1531 // Only functions are cached. 1532 __ pushq(r_scratch); 1533 __ pushq(r_base); 1534 emitNextOpcode(env); 1535 1536 __ bind(&slow_path); 1537 __ pushq(r_base); 1538 // Don't deopt because this won't rewrite. 1539 emitJumpToGenericHandler(env); 1540} 1541 1542template <> 1543void emitHandler<STORE_ATTR_INSTANCE>(EmitEnv* env) { 1544 ScratchReg r_base(env); 1545 ScratchReg r_layout_id(env); 1546 ScratchReg r_cache_value(env); 1547 ScratchReg r_caches(env); 1548 Label slow_path; 1549 1550 __ popq(r_base); 1551 emitGetLayoutId(env, r_layout_id, r_base); 1552 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1553 emitIcLookupMonomorphic(env, &slow_path, r_cache_value, r_layout_id, 1554 r_caches); 1555 emitConvertFromSmallInt(env, r_cache_value); 1556 __ popq(Address(r_base, r_cache_value, TIMES_1, heapObjectDisp(0))); 1557 emitNextOpcode(env); 1558 1559 __ bind(&slow_path); 1560 __ pushq(r_base); 1561 if (env->in_jit) { 1562 emitJumpToDeopt(env); 1563 return; 1564 } 1565 emitJumpToGenericHandler(env); 1566} 1567 1568template <> 1569void emitHandler<STORE_ATTR_INSTANCE_OVERFLOW>(EmitEnv* env) { 1570 ScratchReg r_base(env); 1571 ScratchReg r_layout_id(env); 1572 ScratchReg r_cache_value(env); 1573 ScratchReg r_caches(env); 1574 Label slow_path; 1575 1576 __ popq(r_base); 1577 emitGetLayoutId(env, r_layout_id, r_base); 1578 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1579 emitIcLookupMonomorphic(env, &slow_path, r_cache_value, r_layout_id, 1580 r_caches); 1581 emitConvertFromSmallInt(env, r_cache_value); 1582 1583 { 1584 ScratchReg r_scratch(env); 1585 emitLoadOverflowTuple(env, r_scratch, r_layout_id, r_base); 1586 // The real tuple index is -offset - 1, which is the same as ~offset. 1587 __ notq(r_cache_value); 1588 __ popq(Address(r_scratch, r_cache_value, TIMES_8, heapObjectDisp(0))); 1589 emitNextOpcode(env); 1590 } 1591 1592 __ bind(&slow_path); 1593 __ pushq(r_base); 1594 if (env->in_jit) { 1595 emitJumpToDeopt(env); 1596 return; 1597 } 1598 emitJumpToGenericHandler(env); 1599} 1600 1601template <> 1602void emitHandler<STORE_ATTR_POLYMORPHIC>(EmitEnv* env) { 1603 ScratchReg r_base(env); 1604 ScratchReg r_layout_id(env); 1605 ScratchReg r_scratch(env); 1606 ScratchReg r_caches(env); 1607 Label slow_path; 1608 1609 __ popq(r_base); 1610 emitGetLayoutId(env, r_layout_id, r_base); 1611 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 1612 emitIcLookupPolymorphic(env, &slow_path, r_scratch, r_layout_id, r_caches); 1613 1614 Label next; 1615 // We only cache SmallInt values for STORE_ATTR. 1616 emitAttrWithOffset(env, &Assembler::popq, &next, r_base, r_scratch, 1617 r_layout_id); 1618 1619 __ bind(&slow_path); 1620 __ pushq(r_base); 1621 // Don't deopt because this won't rewrite. 1622 emitJumpToGenericHandler(env); 1623} 1624 1625static void emitPushCallFrame(EmitEnv* env, Label* stack_overflow) { 1626 ScratchReg r_initial_size(env); 1627 1628 { 1629 ScratchReg r_total_vars(env); 1630 __ movq( 1631 r_total_vars, 1632 Address(env->callable, heapObjectDisp(RawFunction::kTotalVarsOffset))); 1633 static_assert(kPointerSize == 8, "unexpected size"); 1634 static_assert(Object::kSmallIntTag == 0 && Object::kSmallIntTagBits == 1, 1635 "unexpected tag"); 1636 // Note: SmallInt::cast(r_total_vars).value() * kPointerSize 1637 // <=> r_total_vars * 4! 1638 __ leaq(r_initial_size, Address(r_total_vars, TIMES_4, Frame::kSize)); 1639 } 1640 { 1641 ScratchReg r_max_size(env); 1642 __ movq(r_max_size, 1643 Address(env->callable, 1644 heapObjectDisp(RawFunction::kStacksizeOrBuiltinOffset))); 1645 // Same reasoning as above. 1646 __ leaq(r_max_size, Address(r_initial_size, r_max_size, TIMES_4, 0)); 1647 1648 // if (sp - max_size < thread->limit_) { goto stack_overflow; } 1649 __ negq(r_max_size); 1650 __ addq(r_max_size, RSP); 1651 __ cmpq(r_max_size, Address(env->thread, Thread::limitOffset())); 1652 env->register_state.check(env->call_interpreted_slow_path_assignment); 1653 __ jcc(BELOW, stack_overflow, Assembler::kFarJump); 1654 } 1655 1656 __ subq(RSP, r_initial_size); 1657 1658 // Setup the new frame: 1659 { 1660 // locals_offset = initial_size + (function.totalArgs() * kPointerSize) 1661 // Note that the involved registers contain smallints. 1662 ScratchReg r_scratch(env); 1663 ScratchReg r_locals_offset(env); 1664 __ movq(r_scratch, Address(env->callable, 1665 heapObjectDisp(RawFunction::kTotalArgsOffset))); 1666 __ leaq(r_locals_offset, Address(r_initial_size, r_scratch, TIMES_4, 0)); 1667 // new_frame.setLocalsOffset(locals_offset) 1668 __ movq(Address(RSP, Frame::kLocalsOffsetOffset), r_locals_offset); 1669 } 1670 // new_frame.setBlockStackDepthReturnMode(return_mode) 1671 __ movq(Address(RSP, Frame::kBlockStackDepthReturnModeOffset), 1672 env->return_mode); 1673 // new_frame.setPreviousFrame(kFrameReg) 1674 __ movq(Address(RSP, Frame::kPreviousFrameOffset), env->frame); 1675 // kBCReg = callable.rewritteBytecode(); new_frame.setBytecode(kBCReg); 1676 env->register_state.assign(&env->bytecode, kBCReg); 1677 __ movq(env->bytecode, 1678 Address(env->callable, 1679 heapObjectDisp(RawFunction::kRewrittenBytecodeOffset))); 1680 __ movq(Address(RSP, Frame::kBytecodeOffset), env->bytecode); 1681 // new_frame.setCaches(callable.caches()) 1682 ScratchReg r_scratch(env); 1683 __ movq(r_scratch, 1684 Address(env->callable, heapObjectDisp(RawFunction::kCachesOffset))); 1685 __ movq(Address(RSP, Frame::kCachesOffset), r_scratch); 1686 // caller_frame.setVirtualPC(kPCReg); kPCReg = 0 1687 emitSaveInterpreterState(env, SaveRestoreFlags::kVMPC); 1688 env->register_state.assign(&env->pc, kPCReg); 1689 __ xorl(env->pc, env->pc); 1690 1691 // kFrameReg = new_frame 1692 __ movq(env->frame, RSP); 1693} 1694 1695void emitPrepareCallable(EmitEnv* env, Register r_layout_id, 1696 Label* prepare_callable_immediate) { 1697 __ cmpl(r_layout_id, Immediate(static_cast<word>(LayoutId::kBoundMethod) 1698 << RawHeader::kLayoutIdOffset)); 1699 Label slow_path; 1700 __ jcc(NOT_EQUAL, &slow_path, Assembler::kFarJump); 1701 1702 { 1703 ScratchReg r_self(env); 1704 ScratchReg r_oparg_saved(env); 1705 ScratchReg r_saved_callable(env); 1706 ScratchReg r_saved_bc(env); 1707 __ movl(r_oparg_saved, env->oparg); 1708 __ movq(r_saved_callable, env->callable); 1709 __ movq(r_saved_bc, env->bytecode); 1710 1711 // thread->stackInsertAt(callable_idx, 1712 // BoundMethod::cast(callable).function()); Use `rep movsq` to copy RCX 1713 // words from RSI to RDI. 1714 ScratchReg r_words(env, RCX); 1715 __ movl(r_words, env->oparg); 1716 ScratchReg r_src(env, RSI); 1717 __ movq(r_src, RSP); 1718 __ subq(RSP, Immediate(kPointerSize)); 1719 ScratchReg r_dst(env, RDI); 1720 __ movq(r_dst, RSP); 1721 __ repMovsq(); 1722 // restore and increment kOparg. 1723 env->register_state.assign(&env->oparg, kOpargReg); 1724 __ leaq(env->oparg, Address(r_oparg_saved, 1)); 1725 // Insert bound_method.function() and bound_method.self(). 1726 __ movq(r_self, Address(r_saved_callable, 1727 heapObjectDisp(RawBoundMethod::kSelfOffset))); 1728 __ movq(Address(RSP, r_oparg_saved, TIMES_8, 0), r_self); 1729 env->register_state.assign(&env->callable, kCallableReg); 1730 __ movq(env->callable, 1731 Address(r_saved_callable, 1732 heapObjectDisp(RawBoundMethod::kFunctionOffset))); 1733 __ movq(Address(RSP, env->oparg, TIMES_8, 0), env->callable); 1734 } 1735 1736 emitJumpIfNotHeapObjectWithLayoutId(env, env->callable, LayoutId::kFunction, 1737 &slow_path); 1738 emitFunctionCall(env, env->callable); 1739 1740 // res = Interpreter::prepareCallableDunderCall(thread, nargs, nargs) 1741 // callable = res.function 1742 // nargs = res.nargs 1743 __ bind(prepare_callable_immediate); 1744 __ bind(&slow_path); 1745 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1746 { 1747 ScratchReg arg0(env, kArgRegs[0]); 1748 __ movq(arg0, env->thread); 1749 CHECK(kArgRegs[1] == env->oparg, "mismatch"); 1750 ScratchReg arg2(env, kArgRegs[2]); 1751 __ movq(arg2, env->oparg); 1752 emitCall<Interpreter::PrepareCallableResult (*)(Thread*, word, word)>( 1753 env, Interpreter::prepareCallableCallDunderCall); 1754 } 1755 __ cmpl(kReturnRegs[0], Immediate(Error::exception().raw())); 1756 __ jcc(EQUAL, &env->unwind_handler, Assembler::kFarJump); 1757 emitRestoreInterpreterState(env, kHandlerWithoutFrameChange); 1758 env->register_state.assign(&env->callable, kCallableReg); 1759 __ movq(env->callable, kReturnRegs[0]); 1760 env->register_state.assign(&env->oparg, kOpargReg); 1761 __ movq(env->oparg, kReturnRegs[1]); 1762 1763 emitFunctionCall(env, env->callable); 1764} 1765 1766static void emitFunctionEntrySimpleInterpretedHandler(EmitEnv* env, 1767 word nargs) { 1768 CHECK(!env->in_jit, 1769 "should not be emitting function entrypoints in JIT mode"); 1770 CHECK(nargs < kMaxNargs, "only support up to %ld arguments", kMaxNargs); 1771 1772 // Check that we received the right number of arguments. 1773 __ cmpl(env->oparg, Immediate(nargs)); 1774 env->register_state.check(env->call_interpreted_slow_path_assignment); 1775 __ jcc(NOT_EQUAL, &env->call_interpreted_slow_path, Assembler::kFarJump); 1776 1777 emitPushCallFrame(env, /*stack_overflow=*/&env->call_interpreted_slow_path); 1778 emitNextOpcode(env); 1779 1780 env->register_state.check(env->call_interpreted_slow_path_assignment); 1781 __ jmp(&env->call_interpreted_slow_path, Assembler::kFarJump); 1782} 1783 1784// Functions called from JIT-compiled functions emulate call/ret on the C++ 1785// stack to avoid putting random pointers on the Python stack. If returning 1786// back to the JIT, find the return address. This emulates `ret'. 1787static void emitPseudoRet(EmitEnv* env) { 1788 ScratchReg r_return_address(env); 1789 1790 // Load the return address from the C++ stack 1791 __ movq(r_return_address, Address(RBP, -kNativeStackFrameSize)); 1792 __ addq(RBP, Immediate(kCallStackAlignment)); 1793 // Ret. 1794 __ jmp(r_return_address); 1795} 1796 1797void emitCallTrampoline(EmitEnv* env) { 1798 ScratchReg r_scratch(env); 1799 1800 // Function::cast(callable).entry()(thread, nargs); 1801 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1802 __ movq(r_scratch, 1803 Address(env->callable, heapObjectDisp(RawFunction::kEntryOffset))); 1804 ScratchReg r_arg0(env, kArgRegs[0]); 1805 __ movq(r_arg0, env->thread); 1806 CHECK(kArgRegs[1] == env->oparg, "register mismatch"); 1807 static_assert(std::is_same<Function::Entry, RawObject (*)(Thread*, word)>(), 1808 "type mismatch"); 1809 emitCallReg(env, r_scratch); 1810 ScratchReg r_result(env, kReturnRegs[0]); 1811 // if (result.isErrorException()) return UNWIND; 1812 __ cmpl(r_result, Immediate(Error::exception().raw())); 1813 __ jcc(EQUAL, &env->unwind_handler, Assembler::kFarJump); 1814 emitRestoreInterpreterState(env, kHandlerWithoutFrameChange); 1815 __ pushq(r_result); 1816 // if (return_to_jit) ret; 1817 Label return_to_jit; 1818 __ shrq(env->return_mode, Immediate(Frame::kReturnModeOffset)); 1819 __ cmpq(env->return_mode, Immediate(Frame::ReturnMode::kJitReturn)); 1820 __ jcc(EQUAL, &return_to_jit, Assembler::kNearJump); 1821 emitNextOpcode(env); 1822 1823 __ bind(&return_to_jit); 1824 emitPseudoRet(env); 1825} 1826 1827void emitFunctionEntryWithNoIntrinsicHandler(EmitEnv* env, Label* next_opcode) { 1828 CHECK(!env->in_jit, 1829 "should not be emitting function entrypoints in JIT mode"); 1830 ScratchReg r_scratch(env); 1831 1832 // Check whether the call is interpreted. 1833 __ movl(r_scratch, 1834 Address(env->callable, heapObjectDisp(RawFunction::kFlagsOffset))); 1835 __ testl(r_scratch, smallIntImmediate(Function::Flags::kInterpreted)); 1836 env->register_state.check(env->call_trampoline_assignment); 1837 __ jcc(ZERO, &env->call_trampoline, Assembler::kFarJump); 1838 1839 // We only support "SimpleCall" functions. This implies `kNofree` is set 1840 // `kwonlyargcount==0` and no varargs/varkeyargs. 1841 __ testl(r_scratch, smallIntImmediate(Function::Flags::kSimpleCall)); 1842 env->register_state.check(env->call_interpreted_slow_path_assignment); 1843 __ jcc(ZERO, &env->call_interpreted_slow_path, Assembler::kFarJump); 1844 1845 // prepareDefaultArgs. 1846 __ movl(r_scratch, 1847 Address(env->callable, heapObjectDisp(RawFunction::kArgcountOffset))); 1848 __ shrl(r_scratch, Immediate(SmallInt::kSmallIntTagBits)); 1849 __ cmpl(r_scratch, env->oparg); 1850 env->register_state.check(env->call_interpreted_slow_path_assignment); 1851 __ jcc(NOT_EQUAL, &env->call_interpreted_slow_path, Assembler::kFarJump); 1852 1853 emitPushCallFrame(env, &env->call_interpreted_slow_path); 1854 1855 __ bind(next_opcode); 1856 emitNextOpcode(env); 1857} 1858 1859static void emitCallInterpretedSlowPath(EmitEnv* env) { 1860 // Interpreter::callInterpreted(thread, nargs, function) 1861 ScratchReg r_arg2(env, kArgRegs[2]); 1862 __ movq(r_arg2, env->callable); 1863 ScratchReg r_arg0(env, kArgRegs[0]); 1864 __ movq(r_arg0, env->thread); 1865 CHECK(kArgRegs[1] == env->oparg, "reg mismatch"); 1866 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1867 emitCall<Interpreter::Continue (*)(Thread*, word, RawFunction)>( 1868 env, Interpreter::callInterpreted); 1869 emitRestoreInterpreterState(env, kHandlerBase); 1870 emitHandleContinueIntoInterpreter(env, kGenericHandler); 1871} 1872 1873void emitFunctionEntryWithIntrinsicHandler(EmitEnv* env) { 1874 CHECK(!env->in_jit, 1875 "should not be emitting function entrypoints in JIT mode"); 1876 ScratchReg r_intrinsic(env); 1877 // if (function.intrinsic() != nullptr) 1878 __ movq(r_intrinsic, Address(env->callable, 1879 heapObjectDisp(RawFunction::kIntrinsicOffset))); 1880 1881 // if (r_intrinsic(thread)) return Continue::NEXT; 1882 static_assert(std::is_same<IntrinsicFunction, bool (*)(Thread*)>(), 1883 "type mismatch"); 1884 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 1885 __ pushq(env->callable); 1886 __ pushq(env->oparg); 1887 ScratchReg r_arg0(env, kArgRegs[0]); 1888 __ movq(r_arg0, env->thread); 1889 emitCallReg(env, r_intrinsic); 1890 ScratchReg r_result(env, kReturnRegs[0]); 1891 env->register_state.assign(&env->oparg, kOpargReg); 1892 __ popq(env->oparg); 1893 env->register_state.assign(&env->callable, kCallableReg); 1894 __ popq(env->callable); 1895 emitRestoreInterpreterState(env, kHandlerWithoutFrameChange); 1896 __ testb(r_result, r_result); 1897 Label next_opcode; 1898 __ jcc(NOT_ZERO, &next_opcode, Assembler::kFarJump); 1899 1900 emitFunctionEntryWithNoIntrinsicHandler(env, &next_opcode); 1901} 1902 1903void emitFunctionEntryBuiltin(EmitEnv* env, word nargs) { 1904 CHECK(!env->in_jit, 1905 "should not be emitting function entrypoints in JIT mode"); 1906 Label stack_overflow; 1907 Label unwind; 1908 1909 // prepareDefaultArgs. 1910 __ cmpl(env->oparg, Immediate(nargs)); 1911 __ jcc(NOT_EQUAL, &env->call_trampoline, Assembler::kFarJump); 1912 1913 // Thread::pushNativeFrame() (roughly) 1914 word locals_offset = Frame::kSize + nargs * kPointerSize; 1915 { 1916 // RSP -= Frame::kSize; 1917 // if (RSP < thread->limit_) { goto stack_overflow; } 1918 __ subq(RSP, Immediate(Frame::kSize)); 1919 __ cmpq(RSP, Address(env->thread, Thread::limitOffset())); 1920 env->register_state.check(env->call_trampoline_assignment); 1921 __ jcc(BELOW, &stack_overflow, Assembler::kFarJump); 1922 1923 emitSaveInterpreterState(env, kVMPC); 1924 1925 // new_frame.setPreviousFrame(kFrameReg) 1926 __ movq(Address(RSP, Frame::kPreviousFrameOffset), env->frame); 1927 // new_frame.setLocalsOffset(locals_offset) 1928 __ movq(Address(RSP, Frame::kLocalsOffsetOffset), Immediate(locals_offset)); 1929 __ movq(env->frame, RSP); 1930 } 1931 1932 // r_code = Function::cast(callable).code().code().asCPtr() 1933 { 1934 ScratchReg r_code(env); 1935 __ movq(r_code, 1936 Address(env->callable, 1937 heapObjectDisp(RawFunction::kStacksizeOrBuiltinOffset))); 1938 1939 // function = Function::cast(callable).stacksizeOrBuiltin().asAlignedCPtr() 1940 // result = ((BuiltinFunction)scratch) (thread, Arguments(frame->locals())); 1941 emitSaveInterpreterState(env, kVMStack | kVMFrame); 1942 static_assert(sizeof(Arguments) == kPointerSize, "Arguments changed"); 1943 static_assert( 1944 std::is_same<BuiltinFunction, RawObject (*)(Thread*, Arguments)>(), 1945 "type mismatch"); 1946 ScratchReg arg0(env, kArgRegs[0]); 1947 __ movq(arg0, env->thread); 1948 ScratchReg arg1(env, kArgRegs[1]); 1949 __ leaq(arg1, Address(env->frame, locals_offset)); 1950 emitCallReg(env, r_code); 1951 } 1952 ScratchReg r_result(env, kReturnRegs[0]); 1953 1954 // if (return.isErrorException()) return UNWIND; 1955 __ cmpl(r_result, Immediate(Error::exception().raw())); 1956 __ jcc(EQUAL, &unwind, Assembler::kFarJump); 1957 1958 // thread->popFrame() 1959 __ leaq(RSP, Address(env->frame, 1960 locals_offset + (Frame::kFunctionOffsetFromLocals + 1) * 1961 kPointerSize)); 1962 __ movq(env->frame, Address(env->frame, Frame::kPreviousFrameOffset)); 1963 1964 emitRestoreInterpreterState(env, kBytecode | kVMPC); 1965 // thread->stackPush(result) 1966 __ pushq(r_result); 1967 // Check return_mode == kJitReturn 1968 Label return_to_jit; 1969 __ shrq(env->return_mode, Immediate(Frame::kReturnModeOffset)); 1970 __ cmpq(env->return_mode, Immediate(Frame::ReturnMode::kJitReturn)); 1971 __ jcc(EQUAL, &return_to_jit, Assembler::kNearJump); 1972 emitNextOpcode(env); 1973 1974 __ bind(&unwind); 1975 __ movq(env->frame, Address(env->frame, Frame::kPreviousFrameOffset)); 1976 emitSaveInterpreterState(env, kVMFrame); 1977 env->register_state.check(env->return_handler_assignment); 1978 __ jmp(&env->unwind_handler, Assembler::kFarJump); 1979 1980 __ bind(&stack_overflow); 1981 __ addq(RSP, Immediate(Frame::kSize)); 1982 __ jmp(&env->call_trampoline, Assembler::kFarJump); 1983 1984 // TODO(T91716258): Split LOAD_FAST into LOAD_PARAM and LOAD_FAST. This will 1985 // allow us to put additional metadata in the frame (such as a return 1986 // address) and not have to do these shenanigans. 1987 __ bind(&return_to_jit); 1988 emitPseudoRet(env); 1989} 1990 1991void emitCallHandler(EmitEnv* env) { 1992 // Check callable. 1993 env->register_state.assign(&env->callable, kCallableReg); 1994 __ movq(env->callable, Address(RSP, env->oparg, TIMES_8, 0)); 1995 // Check whether callable is a heap object. 1996 static_assert(Object::kHeapObjectTag == 1, "unexpected tag"); 1997 Label prepare_callable_immediate; 1998 emitJumpIfImmediate(env, env->callable, &prepare_callable_immediate, 1999 Assembler::kFarJump); 2000 // Check whether callable is a function. 2001 static_assert(Header::kLayoutIdMask <= kMaxInt32, "big layout id mask"); 2002 ScratchReg r_layout_id(env); 2003 __ movl(r_layout_id, 2004 Address(env->callable, heapObjectDisp(RawHeapObject::kHeaderOffset))); 2005 __ andl(r_layout_id, 2006 Immediate(Header::kLayoutIdMask << RawHeader::kLayoutIdOffset)); 2007 __ cmpl(r_layout_id, Immediate(static_cast<word>(LayoutId::kFunction) 2008 << RawHeader::kLayoutIdOffset)); 2009 Label prepare_callable_generic; 2010 __ jcc(NOT_EQUAL, &prepare_callable_generic, Assembler::kNearJump); 2011 // Jump to the function's specialized entry point. 2012 emitFunctionCall(env, env->callable); 2013 2014 __ bind(&prepare_callable_generic); 2015 emitPrepareCallable(env, r_layout_id, &prepare_callable_immediate); 2016} 2017 2018template <> 2019void emitHandler<CALL_FUNCTION>(EmitEnv* env) { 2020 // The CALL_FUNCTION handler is generated out-of-line after the handler table. 2021 __ jmp(&env->call_handler, Assembler::kFarJump); 2022} 2023 2024template <> 2025void emitHandler<CALL_FUNCTION_TYPE_NEW>(EmitEnv* env) { 2026 ScratchReg r_receiver(env); 2027 ScratchReg r_ctor(env); 2028 Label slow_path; 2029 2030 // r_receiver = thread->stackAt(callable_idx); 2031 __ movq(r_receiver, Address(RSP, env->oparg, TIMES_8, 0)); 2032 // if (!r_receiver.isType()) goto slow_path; 2033 emitJumpIfNotHeapObjectWithLayoutId(env, r_receiver, LayoutId::kType, 2034 &slow_path); 2035 { 2036 ScratchReg r_caches(env); 2037 ScratchReg r_layout_id(env); 2038 2039 __ movq(r_layout_id, 2040 Address(r_receiver, heapObjectDisp(Type::kInstanceLayoutIdOffset))); 2041 __ movq(r_caches, Address(env->frame, Frame::kCachesOffset)); 2042 emitIcLookupMonomorphic(env, &slow_path, r_ctor, r_layout_id, r_caches); 2043 } 2044 // Use `rep movsq` to copy RCX words from RSI to RDI. 2045 { 2046 ScratchReg r_saved_bc(env); 2047 ScratchReg r_saved_oparg(env); 2048 2049 __ movq(r_saved_bc, env->bytecode); 2050 __ movq(r_saved_oparg, env->oparg); 2051 2052 ScratchReg r_words(env, RCX); 2053 __ movl(r_words, env->oparg); 2054 ScratchReg r_src(env, RSI); 2055 __ movq(r_src, RSP); 2056 __ subq(RSP, Immediate(kPointerSize)); 2057 ScratchReg r_dst(env, RDI); 2058 __ movq(r_dst, RSP); 2059 __ repMovsq(); 2060 // Restore and increment kOpargReg (nargs) 2061 env->register_state.assign(&env->oparg, kOpargReg); 2062 __ leaq(env->oparg, Address(r_saved_oparg, 1)); 2063 // Insert cached type as cls argument to cached __new__ function 2064 __ movq(Address(RSP, r_saved_oparg, TIMES_8, 0), r_receiver); 2065 // Restore bytecode 2066 env->register_state.assign(&env->bytecode, kBCReg); 2067 __ movq(env->bytecode, r_saved_bc); 2068 // Put the cached ctor function as the callable 2069 env->register_state.assign(&env->callable, kCallableReg); 2070 __ movq(env->callable, r_ctor); 2071 __ movq(Address(RSP, env->oparg, TIMES_8, 0), env->callable); 2072 } 2073 emitFunctionCall(env, env->callable); 2074 2075 __ bind(&slow_path); 2076 emitJumpToGenericHandler(env); 2077} 2078 2079template <> 2080void emitHandler<CALL_METHOD>(EmitEnv* env) { 2081 Label remove_value_and_call; 2082 2083 // if (thread->stackPeek(arg + 1).isUnbound()) goto remove_value_and_call; 2084 env->register_state.assign(&env->callable, kCallableReg); 2085 __ movq(env->callable, Address(RSP, env->oparg, TIMES_8, kPointerSize)); 2086 __ cmpq(env->callable, Immediate(Unbound::object().raw())); 2087 __ jcc(EQUAL, &remove_value_and_call, Assembler::kNearJump); 2088 2089 // Increment argument count by 1 and jump into a call handler. 2090 __ incl(env->oparg); 2091 // Jump to the function's specialized entry point. 2092 emitFunctionCall(env, env->callable); 2093 2094 // thread->removeValueAt(arg + 1) 2095 __ bind(&remove_value_and_call); 2096 ScratchReg r_saved_rdi(env); 2097 __ movq(r_saved_rdi, RDI); 2098 ScratchReg r_saved_rsi(env); 2099 __ movq(r_saved_rsi, RSI); 2100 ScratchReg r_saved_bc(env); 2101 __ movq(r_saved_bc, env->bytecode); 2102 CHECK(env->bytecode == RCX, "rcx used as arg to repmovsq"); 2103 // Use `rep movsq` to copy RCX words from RSI to RDI. 2104 { 2105 __ std(); 2106 ScratchReg r_num_words(env, RCX); 2107 __ leaq(r_num_words, Address(env->oparg, 1)); 2108 CHECK(env->oparg == RSI, "mismatching register"); 2109 __ leaq(RSI, Address(RSP, env->oparg, TIMES_8, 0)); 2110 env->oparg.free(); 2111 ScratchReg r_dst(env, RDI); 2112 __ leaq(r_dst, Address(RSI, kPointerSize)); 2113 __ repMovsq(); 2114 __ cld(); 2115 } 2116 __ addq(RSP, Immediate(kPointerSize)); 2117 __ movq(RDI, r_saved_rdi); 2118 __ movq(RSI, r_saved_rsi); 2119 env->register_state.assign(&env->bytecode, kBCReg); 2120 __ movq(env->bytecode, r_saved_bc); 2121 __ jmp(&env->call_handler, Assembler::kFarJump); 2122} 2123 2124void jitEmitJumpForward(EmitEnv* env) { 2125 DCHECK(env->in_jit, "not supported for non-JIT"); 2126 JitEnv* jenv = static_cast<JitEnv*>(env); 2127 __ jmp(jenv->opcodeAtByteOffset(jenv->virtualPC() + 2128 jenv->currentOp().arg * kCodeUnitScale), 2129 Assembler::kFarJump); 2130} 2131 2132void emitJumpForward(EmitEnv* env, Label* next) { 2133 if (env->in_jit) { 2134 jitEmitJumpForward(env); 2135 return; 2136 } 2137 static_assert(kCodeUnitScale == 2, "expect to multiply arg by 2"); 2138 __ leaq(env->pc, Address(env->pc, env->oparg, TIMES_2, 0)); 2139 __ jmp(next, Assembler::kNearJump); 2140} 2141 2142void emitJumpAbsolute(EmitEnv* env) { 2143 if (env->in_jit) { 2144 JitEnv* jenv = static_cast<JitEnv*>(env); 2145 __ jmp(jenv->opcodeAtByteOffset(jenv->currentOp().arg * kCodeUnitScale), 2146 Assembler::kFarJump); 2147 return; 2148 } 2149 static_assert(kCodeUnitScale == 2, "expect to multiply arg by 2"); 2150 env->register_state.assign(&env->pc, kPCReg); 2151 __ leaq(env->pc, Address(env->oparg, TIMES_2, 0)); 2152} 2153 2154template <> 2155void emitHandler<FOR_ITER_TUPLE>(EmitEnv* env) { 2156 ScratchReg r_iter(env); 2157 Label next_opcode; 2158 Label slow_path; 2159 Label terminate; 2160 2161 { 2162 ScratchReg r_index(env); 2163 ScratchReg r_num_items(env); 2164 ScratchReg r_container(env); 2165 2166 __ popq(r_iter); 2167 emitJumpIfNotHeapObjectWithLayoutId(env, r_iter, LayoutId::kTupleIterator, 2168 &slow_path); 2169 __ movq(r_index, 2170 Address(r_iter, heapObjectDisp(TupleIterator::kIndexOffset))); 2171 __ movq(r_container, 2172 Address(r_iter, heapObjectDisp(TupleIterator::kIterableOffset))); 2173 // if (r_index >= r_num_items) goto terminate; 2174 emitHeaderCountOrOverflow(env, r_num_items, r_container); 2175 __ cmpq(r_index, r_num_items); 2176 __ jcc(GREATER_EQUAL, &terminate, Assembler::kNearJump); 2177 // r_index < r_num_items. 2178 __ pushq(r_iter); 2179 // Push tuple.at(index). 2180 emitPushTupleAt(env, r_container, r_index); 2181 __ addq(r_index, smallIntImmediate(1)); 2182 __ movq(Address(r_iter, heapObjectDisp(TupleIterator::kIndexOffset)), 2183 r_index); 2184 __ bind(&next_opcode); 2185 emitNextOpcode(env); 2186 } 2187 2188 __ bind(&terminate); 2189 emitJumpForward(env, &next_opcode); 2190 2191 __ bind(&slow_path); 2192 __ pushq(r_iter); 2193 if (env->in_jit) { 2194 emitJumpToDeopt(env); 2195 return; 2196 } 2197 emitGenericHandler(env, FOR_ITER_ANAMORPHIC); 2198} 2199 2200template <> 2201void emitHandler<FOR_ITER_LIST>(EmitEnv* env) { 2202 ScratchReg r_iter(env); 2203 Label next_opcode; 2204 Label slow_path; 2205 Label terminate; 2206 2207 { 2208 ScratchReg r_index(env); 2209 ScratchReg r_num_items(env); 2210 ScratchReg r_container(env); 2211 2212 __ popq(r_iter); 2213 emitJumpIfNotHeapObjectWithLayoutId(env, r_iter, LayoutId::kListIterator, 2214 &slow_path); 2215 __ movq(r_index, 2216 Address(r_iter, heapObjectDisp(ListIterator::kIndexOffset))); 2217 __ movq(r_container, 2218 Address(r_iter, heapObjectDisp(ListIterator::kIterableOffset))); 2219 // if (r_index >= r_num_items) goto terminate; 2220 __ movq(r_num_items, 2221 Address(r_container, heapObjectDisp(List::kNumItemsOffset))); 2222 __ cmpq(r_index, r_num_items); 2223 __ jcc(GREATER_EQUAL, &terminate, Assembler::kNearJump); 2224 // r_index < r_num_items. 2225 __ pushq(r_iter); 2226 // Push list.at(index). 2227 emitPushListAt(env, r_container, r_index); 2228 __ addq(r_index, smallIntImmediate(1)); 2229 __ movq(Address(r_iter, heapObjectDisp(ListIterator::kIndexOffset)), 2230 r_index); 2231 __ bind(&next_opcode); 2232 emitNextOpcode(env); 2233 } 2234 2235 __ bind(&terminate); 2236 emitJumpForward(env, &next_opcode); 2237 2238 __ bind(&slow_path); 2239 __ pushq(r_iter); 2240 if (env->in_jit) { 2241 emitJumpToDeopt(env); 2242 return; 2243 } 2244 emitGenericHandler(env, FOR_ITER_ANAMORPHIC); 2245} 2246 2247template <> 2248void emitHandler<FOR_ITER_RANGE>(EmitEnv* env) { 2249 ScratchReg r_iter(env); 2250 Label next_opcode; 2251 Label slow_path; 2252 Label terminate; 2253 2254 { 2255 ScratchReg r_length(env); 2256 ScratchReg r_next(env); 2257 2258 __ popq(r_iter); 2259 emitJumpIfNotHeapObjectWithLayoutId(env, r_iter, LayoutId::kRangeIterator, 2260 &slow_path); 2261 __ movq(r_length, 2262 Address(r_iter, heapObjectDisp(RangeIterator::kLengthOffset))); 2263 __ cmpq(r_length, smallIntImmediate(0)); 2264 __ jcc(EQUAL, &terminate, Assembler::kNearJump); 2265 2266 // if length > 0, push iter back and the current value of next. 2267 __ pushq(r_iter); 2268 __ movq(r_next, 2269 Address(r_iter, heapObjectDisp(RangeIterator::kNextOffset))); 2270 __ pushq(r_next); 2271 // if length > 1 decrement next. 2272 __ cmpq(r_length, smallIntImmediate(1)); 2273 Label dec_length; 2274 __ jcc(EQUAL, &dec_length, Assembler::kNearJump); 2275 // iter.setNext(next + step); 2276 __ addq(r_next, 2277 Address(r_iter, heapObjectDisp(RangeIterator::kStepOffset))); 2278 __ movq(Address(r_iter, heapObjectDisp(RangeIterator::kNextOffset)), 2279 r_next); 2280 // iter.setLength(length - 1); 2281 __ bind(&dec_length); 2282 __ subq(r_length, smallIntImmediate(1)); 2283 __ movq(Address(r_iter, heapObjectDisp(RangeIterator::kLengthOffset)), 2284 r_length); 2285 __ bind(&next_opcode); 2286 emitNextOpcode(env); 2287 } 2288 2289 __ bind(&terminate); 2290 // length == 0. 2291 emitJumpForward(env, &next_opcode); 2292 2293 __ bind(&slow_path); 2294 __ pushq(r_iter); 2295 if (env->in_jit) { 2296 emitJumpToDeopt(env); 2297 return; 2298 } 2299 emitGenericHandler(env, FOR_ITER_ANAMORPHIC); 2300} 2301 2302template <> 2303void emitHandler<LOAD_BOOL>(EmitEnv* env) { 2304 ScratchReg r_scratch(env); 2305 2306 __ leaq(r_scratch, Address(env->oparg, TIMES_2, Bool::kBoolTag)); 2307 __ pushq(r_scratch); 2308 emitNextOpcodeFallthrough(env); 2309} 2310 2311template <> 2312void emitHandler<LOAD_FAST_REVERSE>(EmitEnv* env) { 2313 ScratchReg r_scratch(env); 2314 2315 __ movq(r_scratch, Address(env->frame, env->oparg, TIMES_8, Frame::kSize)); 2316 __ cmpl(r_scratch, Immediate(Error::notFound().raw())); 2317 env->register_state.check(env->handler_assignment); 2318 __ jcc(EQUAL, genericHandlerLabel(env), Assembler::kFarJump); 2319 __ pushq(r_scratch); 2320 emitNextOpcodeFallthrough(env); 2321} 2322 2323template <> 2324void emitHandler<LOAD_FAST_REVERSE_UNCHECKED>(EmitEnv* env) { 2325 __ pushq(Address(env->frame, env->oparg, TIMES_8, Frame::kSize)); 2326 emitNextOpcodeFallthrough(env); 2327} 2328 2329template <> 2330void emitHandler<STORE_FAST_REVERSE>(EmitEnv* env) { 2331 __ popq(Address(env->frame, env->oparg, TIMES_8, Frame::kSize)); 2332 emitNextOpcodeFallthrough(env); 2333} 2334 2335template <> 2336void emitHandler<DELETE_FAST_REVERSE_UNCHECKED>(EmitEnv* env) { 2337 __ movq(Address(env->frame, env->oparg, TIMES_8, Frame::kSize), 2338 Immediate(Error::notFound().raw())); 2339 emitNextOpcodeFallthrough(env); 2340} 2341 2342template <> 2343void emitHandler<LOAD_IMMEDIATE>(EmitEnv* env) { 2344 ScratchReg r_scratch(env); 2345 2346 __ movsbq(r_scratch, env->oparg); 2347 __ pushq(r_scratch); 2348 emitNextOpcodeFallthrough(env); 2349} 2350 2351template <> 2352void emitHandler<LOAD_GLOBAL_CACHED>(EmitEnv* env) { 2353 ScratchReg r_scratch(env); 2354 2355 __ movq(r_scratch, Address(env->frame, Frame::kCachesOffset)); 2356 __ movq(r_scratch, 2357 Address(r_scratch, env->oparg, TIMES_8, heapObjectDisp(0))); 2358 __ pushq(Address(r_scratch, heapObjectDisp(RawValueCell::kValueOffset))); 2359 emitNextOpcodeFallthrough(env); 2360} 2361 2362template <> 2363void emitHandler<UNARY_NOT>(EmitEnv* env) { 2364 Label slow_path; 2365 ScratchReg r_scratch(env); 2366 2367 // Handle RawBools directly; fall back to C++ for other types 2368 __ popq(r_scratch); 2369 static_assert(Bool::kTagMask == 0xff, "expected full byte tag"); 2370 __ cmpb(r_scratch, Immediate(RawObject::kBoolTag)); 2371 // If it had kBoolTag, then negate and push. 2372 __ jcc(NOT_ZERO, &slow_path, Assembler::kNearJump); 2373 __ xorl(r_scratch, 2374 Immediate(RawBool::trueObj().raw() ^ RawBool::falseObj().raw())); 2375 __ pushq(r_scratch); 2376 emitNextOpcode(env); 2377 2378 // Fall back to Interpreter::isTrue 2379 __ bind(&slow_path); 2380 __ pushq(r_scratch); 2381 emitGenericHandler(env, UNARY_NOT); 2382} 2383 2384template <> 2385void emitHandler<UNARY_NEGATIVE_SMALLINT>(EmitEnv* env) { 2386 Label slow_path; 2387 ScratchReg r_obj(env); 2388 ScratchReg r_scratch(env); 2389 2390 // Handle SmallInt directly; fall back to C++ for other types 2391 __ popq(r_obj); 2392 emitJumpIfNotSmallInt(env, r_obj, &slow_path); 2393 __ movq(r_scratch, Immediate(-SmallInt::kMinValue)); 2394 __ addq(r_scratch, r_obj); 2395 __ jcc(SIGN, &slow_path, Assembler::kNearJump); 2396 // This little dance is shorter (and probably faster) than converting from a 2397 // SmallInt, negating, and converting back to SmallInt. 2398 __ andq(r_obj, Immediate(~uint64_t{1})); 2399 __ negq(r_obj); 2400 __ pushq(r_obj); 2401 emitNextOpcode(env); 2402 2403 __ bind(&slow_path); 2404 __ pushq(r_obj); 2405 emitGenericHandler(env, UNARY_NEGATIVE_SMALLINT); 2406} 2407 2408static void emitPopJumpIfBool(EmitEnv* env, bool jump_value) { 2409 ScratchReg r_scratch(env); 2410 Label jump; 2411 Label next; 2412 2413 Label* true_target = jump_value ? &jump : &next; 2414 Label* false_target = jump_value ? &next : &jump; 2415 __ popq(r_scratch); 2416 2417 __ cmpl(r_scratch, boolImmediate(true)); 2418 __ jcc(EQUAL, true_target, Assembler::kNearJump); 2419 __ cmpl(r_scratch, boolImmediate(false)); 2420 __ jcc(EQUAL, false_target, Assembler::kNearJump); 2421 __ cmpq(r_scratch, smallIntImmediate(0)); 2422 __ jcc(EQUAL, false_target, Assembler::kNearJump); 2423 __ cmpb(r_scratch, Immediate(NoneType::object().raw())); 2424 __ jcc(EQUAL, false_target, Assembler::kNearJump); 2425 // Fall back to C++ for other types. 2426 __ pushq(r_scratch); 2427 if (env->in_jit) { 2428 emitJumpToDeopt(env); 2429 } else { 2430 emitJumpToGenericHandler(env); 2431 } 2432 2433 __ bind(&jump); 2434 emitJumpAbsolute(env); 2435 __ bind(&next); 2436 emitNextOpcodeFallthrough(env); 2437} 2438 2439template <> 2440void emitHandler<POP_JUMP_IF_FALSE>(EmitEnv* env) { 2441 emitPopJumpIfBool(env, false); 2442} 2443 2444template <> 2445void emitHandler<POP_JUMP_IF_TRUE>(EmitEnv* env) { 2446 emitPopJumpIfBool(env, true); 2447} 2448 2449void emitJumpIfBoolOrPop(EmitEnv* env, bool jump_value) { 2450 Label next; 2451 Label slow_path; 2452 ScratchReg r_scratch(env); 2453 2454 // Handle RawBools directly; fall back to C++ for other types. 2455 __ popq(r_scratch); 2456 __ cmpl(r_scratch, boolImmediate(!jump_value)); 2457 __ jcc(EQUAL, &next, Assembler::kNearJump); 2458 __ cmpl(r_scratch, boolImmediate(jump_value)); 2459 __ jcc(NOT_EQUAL, &slow_path, Assembler::kNearJump); 2460 __ pushq(r_scratch); 2461 emitJumpAbsolute(env); 2462 __ bind(&next); 2463 emitNextOpcode(env); 2464 2465 __ bind(&slow_path); 2466 __ pushq(r_scratch); 2467 if (env->in_jit) { 2468 emitJumpToDeopt(env); 2469 return; 2470 } 2471 emitJumpToGenericHandler(env); 2472} 2473 2474template <> 2475void emitHandler<JUMP_IF_FALSE_OR_POP>(EmitEnv* env) { 2476 emitJumpIfBoolOrPop(env, false); 2477} 2478 2479template <> 2480void emitHandler<JUMP_IF_TRUE_OR_POP>(EmitEnv* env) { 2481 emitJumpIfBoolOrPop(env, true); 2482} 2483 2484template <> 2485void emitHandler<JUMP_ABSOLUTE>(EmitEnv* env) { 2486 emitJumpAbsolute(env); 2487 emitNextOpcodeFallthrough(env); 2488} 2489 2490template <> 2491void emitHandler<JUMP_FORWARD>(EmitEnv* env) { 2492 DCHECK(!env->in_jit, "JUMP_FORWARD should have its own JIT handler"); 2493 static_assert(kCodeUnitScale == 2, "expect to multiply arg by 2"); 2494 __ leaq(env->pc, Address(env->pc, env->oparg, TIMES_2, 0)); 2495 emitNextOpcodeFallthrough(env); 2496} 2497 2498template <> 2499void emitHandler<DUP_TOP>(EmitEnv* env) { 2500 __ pushq(Address(RSP, 0)); 2501 emitNextOpcodeFallthrough(env); 2502} 2503 2504template <> 2505void emitHandler<ROT_TWO>(EmitEnv* env) { 2506 ScratchReg r_scratch(env); 2507 2508 __ popq(r_scratch); 2509 __ pushq(Address(RSP, 0)); 2510 __ movq(Address(RSP, 8), r_scratch); 2511 emitNextOpcodeFallthrough(env); 2512} 2513 2514template <> 2515void emitHandler<POP_TOP>(EmitEnv* env) { 2516 ScratchReg r_scratch(env); 2517 2518 __ popq(r_scratch); 2519 emitNextOpcodeFallthrough(env); 2520} 2521 2522template <> 2523void emitHandler<EXTENDED_ARG>(EmitEnv* env) { 2524 ScratchReg r_scratch(env); 2525 2526 __ shll(env->oparg, Immediate(kBitsPerByte)); 2527 __ movzbl(r_scratch, 2528 Address(env->bytecode, env->pc, TIMES_1, heapObjectDisp(0))); 2529 __ movb(env->oparg, 2530 Address(env->bytecode, env->pc, TIMES_1, heapObjectDisp(1))); 2531 __ shll(r_scratch, Immediate(kHandlerSizeShift)); 2532 __ addl(env->pc, Immediate(kCodeUnitSize)); 2533 __ addq(r_scratch, env->handlers_base); 2534 env->register_state.check(env->handler_assignment); 2535 __ jmp(r_scratch); 2536 // Hint to the branch predictor that the indirect jmp never falls through to 2537 // here. 2538 __ ud2(); 2539} 2540 2541void emitCompareIs(EmitEnv* env, bool eq_value) { 2542 ScratchReg r_lhs(env); 2543 ScratchReg r_rhs(env); 2544 ScratchReg r_eq_value(env); 2545 ScratchReg r_neq_value(env); 2546 2547 __ popq(r_rhs); 2548 __ popq(r_lhs); 2549 __ movl(r_eq_value, boolImmediate(eq_value)); 2550 __ movl(r_neq_value, boolImmediate(!eq_value)); 2551 __ cmpq(r_rhs, r_lhs); 2552 __ cmovnel(r_eq_value, r_neq_value); 2553 __ pushq(r_eq_value); 2554 emitNextOpcodeFallthrough(env); 2555} 2556 2557static void emitCompareOpSmallIntHandler(EmitEnv* env, Condition cond) { 2558 ScratchReg r_right(env); 2559 ScratchReg r_left(env); 2560 ScratchReg r_true(env); 2561 ScratchReg r_result(env); 2562 Label slow_path; 2563 2564 __ popq(r_right); 2565 __ popq(r_left); 2566 // Use the fast path only when both arguments are SmallInt. 2567 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 2568 __ movq(r_true, boolImmediate(true)); 2569 __ movq(r_result, boolImmediate(false)); 2570 __ cmpq(r_left, r_right); 2571 switch (cond) { 2572 case EQUAL: 2573 __ cmoveq(r_result, r_true); 2574 break; 2575 case NOT_EQUAL: 2576 __ cmovneq(r_result, r_true); 2577 break; 2578 case GREATER: 2579 __ cmovgq(r_result, r_true); 2580 break; 2581 case GREATER_EQUAL: 2582 __ cmovgeq(r_result, r_true); 2583 break; 2584 case LESS: 2585 __ cmovlq(r_result, r_true); 2586 break; 2587 case LESS_EQUAL: 2588 __ cmovleq(r_result, r_true); 2589 break; 2590 default: 2591 UNREACHABLE("unhandled cond"); 2592 } 2593 __ pushq(r_result); 2594 emitNextOpcode(env); 2595 2596 __ bind(&slow_path); 2597 __ pushq(r_left); 2598 __ pushq(r_right); 2599 if (env->in_jit) { 2600 emitJumpToDeopt(env); 2601 return; 2602 } 2603 __ movq(kArgRegs[0], env->thread); 2604 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 2605 emitCurrentCacheIndex(env, kArgRegs[2]); 2606 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 2607 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 2608 env, Interpreter::compareOpUpdateCache); 2609 emitHandleContinue(env, kGenericHandler); 2610} 2611 2612template <> 2613void emitHandler<COMPARE_EQ_SMALLINT>(EmitEnv* env) { 2614 emitCompareOpSmallIntHandler(env, EQUAL); 2615} 2616 2617template <> 2618void emitHandler<COMPARE_NE_SMALLINT>(EmitEnv* env) { 2619 emitCompareOpSmallIntHandler(env, NOT_EQUAL); 2620} 2621 2622template <> 2623void emitHandler<COMPARE_GT_SMALLINT>(EmitEnv* env) { 2624 emitCompareOpSmallIntHandler(env, GREATER); 2625} 2626 2627template <> 2628void emitHandler<COMPARE_GE_SMALLINT>(EmitEnv* env) { 2629 emitCompareOpSmallIntHandler(env, GREATER_EQUAL); 2630} 2631 2632template <> 2633void emitHandler<COMPARE_LT_SMALLINT>(EmitEnv* env) { 2634 emitCompareOpSmallIntHandler(env, LESS); 2635} 2636 2637template <> 2638void emitHandler<COMPARE_LE_SMALLINT>(EmitEnv* env) { 2639 emitCompareOpSmallIntHandler(env, LESS_EQUAL); 2640} 2641 2642template <> 2643void emitHandler<COMPARE_IS>(EmitEnv* env) { 2644 emitCompareIs(env, true); 2645} 2646 2647template <> 2648void emitHandler<COMPARE_IS_NOT>(EmitEnv* env) { 2649 emitCompareIs(env, false); 2650} 2651 2652template <> 2653void emitHandler<INPLACE_ADD_SMALLINT>(EmitEnv* env) { 2654 ScratchReg r_right(env); 2655 ScratchReg r_left(env); 2656 ScratchReg r_result(env); 2657 Label slow_path; 2658 2659 __ popq(r_right); 2660 __ popq(r_left); 2661 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 2662 // Preserve argument values in case of overflow. 2663 __ movq(r_result, r_left); 2664 __ addq(r_result, r_right); 2665 __ jcc(YES_OVERFLOW, &slow_path, Assembler::kNearJump); 2666 __ pushq(r_result); 2667 emitNextOpcode(env); 2668 2669 __ bind(&slow_path); 2670 __ pushq(r_left); 2671 __ pushq(r_right); 2672 if (env->in_jit) { 2673 emitJumpToDeopt(env); 2674 return; 2675 } 2676 __ movq(kArgRegs[0], env->thread); 2677 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 2678 emitCurrentCacheIndex(env, kArgRegs[2]); 2679 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 2680 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 2681 env, Interpreter::inplaceOpUpdateCache); 2682 emitHandleContinue(env, kGenericHandler); 2683} 2684 2685template <> 2686void emitHandler<INPLACE_SUB_SMALLINT>(EmitEnv* env) { 2687 ScratchReg r_right(env); 2688 ScratchReg r_left(env); 2689 ScratchReg r_result(env); 2690 Label slow_path; 2691 __ popq(r_right); 2692 __ popq(r_left); 2693 emitJumpIfNotBothSmallInt(env, r_left, r_right, r_result, &slow_path); 2694 // Preserve argument values in case of overflow. 2695 __ movq(r_result, r_left); 2696 __ subq(r_result, r_right); 2697 __ jcc(YES_OVERFLOW, &slow_path, Assembler::kNearJump); 2698 __ pushq(r_result); 2699 emitNextOpcode(env); 2700 2701 __ bind(&slow_path); 2702 __ pushq(r_left); 2703 __ pushq(r_right); 2704 if (env->in_jit) { 2705 emitJumpToDeopt(env); 2706 return; 2707 } 2708 __ movq(kArgRegs[0], env->thread); 2709 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 2710 emitCurrentCacheIndex(env, kArgRegs[2]); 2711 CHECK(env->oparg == kArgRegs[1], "oparg expect to be in rsi"); 2712 emitCall<Interpreter::Continue (*)(Thread*, word, word)>( 2713 env, Interpreter::inplaceOpUpdateCache); 2714 emitHandleContinue(env, kGenericHandler); 2715} 2716 2717template <> 2718void emitHandler<RETURN_VALUE>(EmitEnv* env) { 2719 Label slow_path; 2720 ScratchReg r_return_value(env); 2721 2722 // Go to slow_path if frame->returnMode() != Frame::kNormal; 2723 // frame->blockStackDepth() should always be 0 here. 2724 __ cmpq(Address(env->frame, Frame::kBlockStackDepthReturnModeOffset), 2725 Immediate(0)); 2726 __ jcc(NOT_EQUAL, &slow_path, Assembler::kNearJump); 2727 2728 // Fast path: pop return value, restore caller frame, push return value. 2729 __ popq(r_return_value); 2730 2731 { 2732 ScratchReg r_scratch(env); 2733 // RSP = frame->frameEnd() 2734 // = locals() + (kFunctionOffsetFromLocals + 1) * kPointerSize) 2735 // (The +1 is because we have to point behind the field) 2736 __ movq(r_scratch, Address(env->frame, Frame::kLocalsOffsetOffset)); 2737 __ leaq(RSP, 2738 Address(env->frame, r_scratch, TIMES_1, 2739 (Frame::kFunctionOffsetFromLocals + 1) * kPointerSize)); 2740 __ movq(env->frame, Address(env->frame, Frame::kPreviousFrameOffset)); 2741 } 2742 2743 emitRestoreInterpreterState(env, kBytecode | kVMPC); 2744 __ pushq(r_return_value); 2745 emitNextOpcode(env); 2746 2747 __ bind(&slow_path); 2748 emitSaveInterpreterState(env, kVMStack | kVMFrame); 2749 const word handler_offset = 2750 -(Interpreter::kNumContinues - 2751 static_cast<int>(Interpreter::Continue::RETURN)) * 2752 kHandlerSize; 2753 ScratchReg r_scratch(env); 2754 __ leaq(r_scratch, Address(env->handlers_base, handler_offset)); 2755 env->register_state.check(env->return_handler_assignment); 2756 __ jmp(r_scratch); 2757} 2758 2759template <> 2760void emitHandler<POP_BLOCK>(EmitEnv* env) { 2761 ScratchReg r_depth(env); 2762 ScratchReg r_block(env); 2763 2764 // frame->blockstack()->pop() 2765 static_assert(Frame::kBlockStackDepthMask == 0xffffffff, 2766 "exepcted blockstackdepth to be low 32 bits"); 2767 __ movl(r_depth, 2768 Address(env->frame, Frame::kBlockStackDepthReturnModeOffset)); 2769 __ subl(r_depth, Immediate(kPointerSize)); 2770 __ movq(r_block, 2771 Address(env->frame, r_depth, TIMES_1, Frame::kBlockStackOffset)); 2772 __ movl(Address(env->frame, Frame::kBlockStackDepthReturnModeOffset), 2773 r_depth); 2774 2775 emitNextOpcodeFallthrough(env); 2776} 2777 2778word emitHandlerTable(EmitEnv* env); 2779void emitSharedCode(EmitEnv* env); 2780 2781void emitInterpreter(EmitEnv* env) { 2782 // Set up a frame and save callee-saved registers we'll use. 2783 __ pushq(RBP); 2784 __ movq(RBP, RSP); 2785 for (Register r : kUsedCalleeSavedRegs) { 2786 __ pushq(r); 2787 } 2788 2789 RegisterAssignment function_entry_assignment[] = { 2790 {&env->pc, kPCReg}, 2791 {&env->oparg, kOpargReg}, 2792 {&env->frame, kFrameReg}, 2793 {&env->thread, kThreadReg}, 2794 {&env->handlers_base, kHandlersBaseReg}, 2795 {&env->callable, kCallableReg}, 2796 {&env->return_mode, kReturnModeReg}, 2797 }; 2798 env->function_entry_assignment = function_entry_assignment; 2799 2800 RegisterAssignment handler_assignment[] = { 2801 {&env->bytecode, kBCReg}, {&env->pc, kPCReg}, 2802 {&env->oparg, kOpargReg}, {&env->frame, kFrameReg}, 2803 {&env->thread, kThreadReg}, {&env->handlers_base, kHandlersBaseReg}, 2804 }; 2805 env->handler_assignment = handler_assignment; 2806 2807 RegisterAssignment call_interpreted_slow_path_assignment[] = { 2808 {&env->pc, kPCReg}, {&env->callable, kCallableReg}, 2809 {&env->frame, kFrameReg}, {&env->thread, kThreadReg}, 2810 {&env->oparg, kOpargReg}, {&env->handlers_base, kHandlersBaseReg}, 2811 }; 2812 env->call_interpreted_slow_path_assignment = 2813 call_interpreted_slow_path_assignment; 2814 2815 RegisterAssignment call_trampoline_assignment[] = { 2816 {&env->pc, kPCReg}, 2817 {&env->callable, kCallableReg}, 2818 {&env->frame, kFrameReg}, 2819 {&env->thread, kThreadReg}, 2820 {&env->oparg, kOpargReg}, 2821 {&env->handlers_base, kHandlersBaseReg}, 2822 {&env->return_mode, kReturnModeReg}, 2823 }; 2824 env->call_trampoline_assignment = call_trampoline_assignment; 2825 2826 RegisterAssignment return_handler_assignment[] = { 2827 {&env->thread, kThreadReg}, 2828 {&env->handlers_base, kHandlersBaseReg}, 2829 }; 2830 env->return_handler_assignment = return_handler_assignment; 2831 2832 RegisterAssignment do_return_assignment[] = { 2833 {&env->return_value, kReturnRegs[0]}, 2834 }; 2835 env->do_return_assignment = do_return_assignment; 2836 2837 env->register_state.reset(); 2838 env->register_state.assign(&env->thread, kThreadReg); 2839 __ movq(env->thread, kArgRegs[0]); 2840 env->register_state.assign(&env->frame, kFrameReg); 2841 __ movq(env->frame, Address(env->thread, Thread::currentFrameOffset())); 2842 2843 // frame->addReturnMode(Frame::kExitRecursiveInterpreter) 2844 __ orl(Address(env->frame, Frame::kBlockStackDepthReturnModeOffset + 2845 (Frame::kReturnModeOffset / kBitsPerByte)), 2846 Immediate(static_cast<word>(Frame::kExitRecursiveInterpreter))); 2847 2848 // Load VM state into registers and jump to the first opcode handler. 2849 emitRestoreInterpreterState(env, kAllState); 2850 emitNextOpcode(env); 2851 2852 __ bind(&env->do_return); 2853 env->register_state.resetTo(do_return_assignment); 2854 __ leaq(RSP, Address(RBP, -kNumCalleeSavedRegs * int{kPointerSize})); 2855 for (word i = kNumCalleeSavedRegs - 1; i >= 0; --i) { 2856 __ popq(kUsedCalleeSavedRegs[i]); 2857 } 2858 __ popq(RBP); 2859 __ ret(); 2860 2861 __ align(kInstructionCacheLineSize); 2862 2863 env->count_opcodes = false; 2864 env->handler_offset = emitHandlerTable(env); 2865 2866 env->count_opcodes = true; 2867 env->counting_handler_offset = emitHandlerTable(env); 2868 2869 emitSharedCode(env); 2870 env->register_state.reset(); 2871} 2872 2873template <Bytecode bc> 2874void jitEmitGenericHandler(JitEnv* env) { 2875 jitEmitGenericHandlerSetup(env); 2876 emitHandler<bc>(env); 2877} 2878 2879template <Bytecode bc> 2880void jitEmitHandler(JitEnv* env) { 2881 jitEmitGenericHandler<bc>(env); 2882} 2883 2884template <> 2885void jitEmitHandler<CALL_FUNCTION>(JitEnv* env) { 2886 env->register_state.assign(&env->callable, kCallableReg); 2887 __ movq(env->callable, Address(RSP, env->currentOp().arg * kWordSize)); 2888 jitEmitGenericHandlerSetup(env); 2889 Label prepare_callable; 2890 emitJumpIfNotHeapObjectWithLayoutId(env, env->callable, LayoutId::kFunction, 2891 &prepare_callable); 2892 emitFunctionCall(env, env->callable); 2893 2894 __ bind(&prepare_callable); 2895 env->register_state.assign(&env->pc, kPCReg); 2896 __ movq(env->pc, Immediate(env->virtualPC())); 2897 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 2898 { 2899 ScratchReg arg0(env, kArgRegs[0]); 2900 __ movq(arg0, env->thread); 2901 CHECK(kArgRegs[1] == env->oparg, "mismatch"); 2902 ScratchReg arg2(env, kArgRegs[2]); 2903 __ movq(arg2, Immediate(env->currentOp().arg)); 2904 emitCall<Interpreter::PrepareCallableResult (*)(Thread*, word, word)>( 2905 env, Interpreter::prepareCallableCallDunderCall); 2906 } 2907 __ cmpl(kReturnRegs[0], Immediate(Error::exception().raw())); 2908 __ jcc(EQUAL, &env->unwind_handler, Assembler::kFarJump); 2909 emitRestoreInterpreterState(env, kHandlerWithoutFrameChange); 2910 env->register_state.assign(&env->callable, kCallableReg); 2911 __ movq(env->callable, kReturnRegs[0]); 2912 env->register_state.assign(&env->oparg, kOpargReg); 2913 __ movq(env->oparg, kReturnRegs[1]); 2914 2915 env->register_state.check(env->call_trampoline_assignment); 2916 emitCallTrampoline(env); 2917} 2918 2919template <> 2920void jitEmitHandler<LOAD_BOOL>(JitEnv* env) { 2921 word arg = env->currentOp().arg; 2922 DCHECK(arg == 0x80 || arg == 0, "unexpected arg"); 2923 word value = Bool::fromBool(arg).raw(); 2924 __ pushq(Immediate(value)); 2925} 2926 2927template <> 2928void jitEmitHandler<LOAD_CONST>(JitEnv* env) { 2929 HandleScope scope(env->compilingThread()); 2930 Code code(&scope, Function::cast(env->function()).code()); 2931 Tuple consts(&scope, code.consts()); 2932 word arg = env->currentOp().arg; 2933 Object value(&scope, consts.at(arg)); 2934 if (!value.isHeapObject()) { 2935 emitPushImmediate(env, value.raw()); 2936 return; 2937 } 2938 // Fall back to runtime LOAD_CONST for non-immediates like tuples, etc. 2939 jitEmitGenericHandler<LOAD_CONST>(env); 2940} 2941 2942template <> 2943void jitEmitHandler<LOAD_IMMEDIATE>(JitEnv* env) { 2944 word arg = env->currentOp().arg; 2945 emitPushImmediate(env, objectFromOparg(arg).raw()); 2946} 2947 2948template <> 2949void jitEmitHandler<LOAD_FAST_REVERSE>(JitEnv* env) { 2950 Label slow_path; 2951 ScratchReg r_scratch(env); 2952 2953 word arg = env->currentOp().arg; 2954 word frame_offset = arg * kWordSize + Frame::kSize; 2955 __ movq(r_scratch, Address(env->frame, frame_offset)); 2956 __ cmpl(r_scratch, Immediate(Error::notFound().raw())); 2957 __ jcc(EQUAL, &slow_path, Assembler::kNearJump); 2958 __ pushq(r_scratch); 2959 emitNextOpcode(env); 2960 2961 __ bind(&slow_path); 2962 // TODO(T90560373): Instead of deoptimizing, raise UnboundLocalError. 2963 jitEmitGenericHandlerSetup(env); 2964 emitJumpToDeopt(env); 2965} 2966 2967template <> 2968void jitEmitHandler<LOAD_FAST_REVERSE_UNCHECKED>(JitEnv* env) { 2969 word arg = env->currentOp().arg; 2970 word frame_offset = arg * kWordSize + Frame::kSize; 2971 __ pushq(Address(env->frame, frame_offset)); 2972} 2973 2974template <> 2975void jitEmitHandler<JUMP_FORWARD>(JitEnv* env) { 2976 jitEmitJumpForward(env); 2977} 2978 2979template <> 2980void jitEmitHandler<RETURN_VALUE>(JitEnv* env) { 2981 ScratchReg r_return_value(env); 2982 2983 // The return mode for simple interpreted functions is normally 0 (see 2984 // emitPushCallFrame/Thread::pushCallFrameImpl), so we can skip the slow 2985 // path. 2986 // TODO(T89514778): When profiling is enabled, discard all JITed functions 2987 // and stop JITing. 2988 2989 // Fast path: pop return value, restore caller frame, push return value. 2990 __ popq(r_return_value); 2991 2992 { 2993 ScratchReg r_scratch(env); 2994 // RSP = frame->frameEnd() 2995 // = locals() + (kFunctionOffsetFromLocals + 1) * kPointerSize) 2996 // (The +1 is because we have to point behind the field) 2997 __ movq(r_scratch, Address(env->frame, Frame::kLocalsOffsetOffset)); 2998 __ leaq(RSP, 2999 Address(env->frame, r_scratch, TIMES_1, 3000 (Frame::kFunctionOffsetFromLocals + 1) * kPointerSize)); 3001 __ movq(env->frame, Address(env->frame, Frame::kPreviousFrameOffset)); 3002 } 3003 3004 // Need to restore handler base from the calling frame, which (so far) is 3005 // always the assembly interpreter. This allows emitNextOpcode to find a 3006 // handler for the next opcode. 3007 emitRestoreInterpreterState(env, kBytecode | kVMPC | kHandlerBase); 3008 __ pushq(r_return_value); 3009 emitNextOpcodeImpl(env); 3010} 3011 3012bool isSupportedInJIT(Bytecode bc) { 3013 switch (bc) { 3014 case BINARY_ADD: 3015 case BINARY_ADD_SMALLINT: 3016 case BINARY_AND: 3017 case BINARY_AND_SMALLINT: 3018 case BINARY_FLOOR_DIVIDE: 3019 case BINARY_LSHIFT: 3020 case BINARY_MATRIX_MULTIPLY: 3021 case BINARY_MODULO: 3022 case BINARY_MULTIPLY: 3023 case BINARY_MUL_SMALLINT: 3024 case BINARY_OP_MONOMORPHIC: 3025 case BINARY_OR: 3026 case BINARY_OR_SMALLINT: 3027 case BINARY_POWER: 3028 case BINARY_RSHIFT: 3029 case BINARY_SUBSCR: 3030 case BINARY_SUBSCR_LIST: 3031 case BINARY_SUBSCR_MONOMORPHIC: 3032 case BINARY_SUBTRACT: 3033 case BINARY_SUB_SMALLINT: 3034 case BINARY_TRUE_DIVIDE: 3035 case BINARY_XOR: 3036 case BUILD_CONST_KEY_MAP: 3037 case BUILD_LIST: 3038 case BUILD_LIST_UNPACK: 3039 case BUILD_MAP: 3040 case BUILD_MAP_UNPACK: 3041 case BUILD_MAP_UNPACK_WITH_CALL: 3042 case BUILD_SET: 3043 case BUILD_SET_UNPACK: 3044 case BUILD_SLICE: 3045 case BUILD_STRING: 3046 case BUILD_TUPLE: 3047 case BUILD_TUPLE_UNPACK: 3048 case BUILD_TUPLE_UNPACK_WITH_CALL: 3049 case CALL_FUNCTION: 3050 case COMPARE_EQ_SMALLINT: 3051 case COMPARE_GE_SMALLINT: 3052 case COMPARE_GT_SMALLINT: 3053 case COMPARE_IS: 3054 case COMPARE_IS_NOT: 3055 case COMPARE_LE_SMALLINT: 3056 case COMPARE_LT_SMALLINT: 3057 case COMPARE_NE_SMALLINT: 3058 case COMPARE_OP: 3059 case DELETE_ATTR: 3060 case DELETE_FAST: 3061 case DELETE_FAST_REVERSE_UNCHECKED: 3062 case DELETE_NAME: 3063 case DELETE_SUBSCR: 3064 case DUP_TOP: 3065 case DUP_TOP_TWO: 3066 case FORMAT_VALUE: 3067 case FOR_ITER: 3068 case FOR_ITER_LIST: 3069 case FOR_ITER_RANGE: 3070 case GET_ANEXT: 3071 case GET_ITER: 3072 case GET_YIELD_FROM_ITER: 3073 case IMPORT_FROM: 3074 case IMPORT_STAR: 3075 case INPLACE_ADD: 3076 case INPLACE_ADD_SMALLINT: 3077 case INPLACE_AND: 3078 case INPLACE_FLOOR_DIVIDE: 3079 case INPLACE_LSHIFT: 3080 case INPLACE_MATRIX_MULTIPLY: 3081 case INPLACE_MODULO: 3082 case INPLACE_MULTIPLY: 3083 case INPLACE_OR: 3084 case INPLACE_POWER: 3085 case INPLACE_RSHIFT: 3086 case INPLACE_SUBTRACT: 3087 case INPLACE_SUB_SMALLINT: 3088 case INPLACE_TRUE_DIVIDE: 3089 case INPLACE_XOR: 3090 case JUMP_ABSOLUTE: 3091 case JUMP_FORWARD: 3092 case JUMP_IF_FALSE_OR_POP: 3093 case JUMP_IF_TRUE_OR_POP: 3094 case LIST_APPEND: 3095 case LOAD_ATTR: 3096 case LOAD_ATTR_INSTANCE: 3097 case LOAD_ATTR_INSTANCE_TYPE_BOUND_METHOD: 3098 case LOAD_ATTR_POLYMORPHIC: 3099 case LOAD_BOOL: 3100 case LOAD_BUILD_CLASS: 3101 case LOAD_CONST: 3102 case LOAD_FAST: 3103 case LOAD_FAST_REVERSE: 3104 case LOAD_FAST_REVERSE_UNCHECKED: 3105 case LOAD_GLOBAL_CACHED: 3106 case LOAD_IMMEDIATE: 3107 case LOAD_METHOD: 3108 case LOAD_NAME: 3109 case MAKE_FUNCTION: 3110 case MAP_ADD: 3111 case NOP: 3112 case POP_JUMP_IF_FALSE: 3113 case POP_JUMP_IF_TRUE: 3114 case POP_TOP: 3115 case PRINT_EXPR: 3116 case RETURN_VALUE: 3117 case ROT_FOUR: 3118 case ROT_THREE: 3119 case ROT_TWO: 3120 case SETUP_ANNOTATIONS: 3121 case SETUP_ASYNC_WITH: 3122 case SETUP_WITH: 3123 case SET_ADD: 3124 case STORE_ATTR: 3125 case STORE_ATTR_INSTANCE: 3126 case STORE_ATTR_INSTANCE_OVERFLOW: 3127 case STORE_ATTR_INSTANCE_UPDATE: 3128 case STORE_ATTR_POLYMORPHIC: 3129 case STORE_FAST: 3130 case STORE_FAST_REVERSE: 3131 case STORE_NAME: 3132 case STORE_SUBSCR: 3133 case STORE_SUBSCR_LIST: 3134 case UNARY_INVERT: 3135 case UNARY_NEGATIVE: 3136 case UNARY_NEGATIVE_SMALLINT: 3137 case UNARY_NOT: 3138 case UNARY_POSITIVE: 3139 case UNPACK_EX: 3140 case UNPACK_SEQUENCE: 3141 return true; 3142 default: 3143 return false; 3144 } 3145} 3146 3147void emitBeforeHandler(EmitEnv* env) { 3148 if (env->count_opcodes) { 3149 __ incq(Address(env->thread, Thread::opcodeCountOffset())); 3150 } 3151} 3152 3153word emitHandlerTable(EmitEnv* env) { 3154 // UNWIND pseudo-handler 3155 static_assert(static_cast<int>(Interpreter::Continue::UNWIND) == 1, 3156 "Unexpected UNWIND value"); 3157 { 3158 env->current_handler = "UNWIND pseudo-handler"; 3159 env->register_state.resetTo(env->return_handler_assignment); 3160 HandlerSizer sizer(env, kHandlerSize); 3161 if (!env->unwind_handler.isBound()) { 3162 __ bind(&env->unwind_handler); 3163 } 3164 __ movq(kArgRegs[0], env->thread); 3165 3166 // TODO(T91716258): Add JIT support here for isErrorNotFound and 3167 // appropriate jmp. 3168 emitCall<RawObject (*)(Thread*)>(env, Interpreter::unwind); 3169 ScratchReg r_result(env, kReturnRegs[0]); 3170 // Check result.isErrorError() 3171 __ cmpl(r_result, Immediate(Error::error().raw())); 3172 env->register_state.assign(&env->return_value, r_result); 3173 env->register_state.check(env->do_return_assignment); 3174 __ jcc(NOT_EQUAL, &env->do_return, Assembler::kFarJump); 3175 emitRestoreInterpreterState(env, kGenericHandler); 3176 emitNextOpcode(env); 3177 } 3178 3179 // RETURN pseudo-handler 3180 static_assert(static_cast<int>(Interpreter::Continue::RETURN) == 2, 3181 "Unexpected RETURN value"); 3182 { 3183 env->current_handler = "RETURN pseudo-handler"; 3184 env->register_state.resetTo(env->return_handler_assignment); 3185 HandlerSizer sizer(env, kHandlerSize); 3186 __ movq(kArgRegs[0], env->thread); 3187 emitCall<RawObject (*)(Thread*)>(env, Interpreter::handleReturn); 3188 Label return_to_jit; 3189 { 3190 ScratchReg r_result(env, kReturnRegs[0]); 3191 // Check result.isErrorNotFound() 3192 __ cmpl(r_result, Immediate(Error::notFound().raw())); 3193 __ jcc(EQUAL, &return_to_jit, Assembler::kNearJump); 3194 // Check result.isErrorError() 3195 __ cmpl(r_result, Immediate(Error::error().raw())); 3196 env->register_state.assign(&env->return_value, r_result); 3197 } 3198 env->register_state.check(env->do_return_assignment); 3199 __ jcc(NOT_EQUAL, &env->do_return, Assembler::kFarJump); 3200 emitRestoreInterpreterState(env, kGenericHandler); 3201 emitNextOpcode(env); 3202 3203 // TODO(T91716258): Split LOAD_FAST into LOAD_PARAM and LOAD_FAST. This 3204 // will allow us to put additional metadata in the frame (such as a return 3205 // address) and not have to do these shenanigans. 3206 __ bind(&return_to_jit); 3207 emitRestoreInterpreterState(env, kGenericHandler); 3208 emitPseudoRet(env); 3209 } 3210 3211 // YIELD pseudo-handler 3212 static_assert(static_cast<int>(Interpreter::Continue::YIELD) == 3, 3213 "Unexpected YIELD value"); 3214 { 3215 env->current_handler = "YIELD pseudo-handler"; 3216 env->register_state.resetTo(env->return_handler_assignment); 3217 HandlerSizer sizer(env, kHandlerSize); 3218 // result = thread->stackPop() 3219 ScratchReg r_scratch_top(env, RDX); 3220 __ movq(r_scratch_top, Address(env->thread, Thread::stackPointerOffset())); 3221 env->register_state.assign(&env->return_value, kReturnRegs[0]); 3222 __ movq(env->return_value, Address(r_scratch_top, 0)); 3223 __ addq(r_scratch_top, Immediate(kPointerSize)); 3224 __ movq(Address(env->thread, Thread::stackPointerOffset()), r_scratch_top); 3225 3226 env->register_state.check(env->do_return_assignment); 3227 __ jmp(&env->do_return, Assembler::kFarJump); 3228 } 3229 3230 // DEOPT pseudo-handler 3231 static_assert(static_cast<int>(Interpreter::Continue::DEOPT) == 4, 3232 "Unexpected DEOPT value"); 3233 { 3234 env->current_handler = "DEOPT pseudo-handler"; 3235 env->register_state.resetTo(env->return_handler_assignment); 3236 HandlerSizer sizer(env, kHandlerSize); 3237 DCHECK(!env->in_jit, "DEOPT handler should not get hit"); 3238 __ breakpoint(); 3239 __ ud2(); 3240 } 3241 3242 word offset_0 = env->as.codeSize(); 3243 3244#define BC(name, i, handler) \ 3245 { \ 3246 env->current_op = name; \ 3247 env->current_handler = #name; \ 3248 HandlerSizer sizer(env, kHandlerSize); \ 3249 env->register_state.resetTo(env->handler_assignment); \ 3250 emitBeforeHandler(env); \ 3251 emitHandler<name>(env); \ 3252 } 3253 FOREACH_BYTECODE(BC) 3254#undef BC 3255 3256 env->register_state.reset(); 3257 return offset_0; 3258} 3259 3260void emitSharedCode(EmitEnv* env) { 3261 { 3262 // This register is shared between the following three functions. 3263 __ bind(&env->call_handler); 3264 env->register_state.resetTo(env->handler_assignment); 3265 emitCallHandler(env); 3266 3267 __ align(16); 3268 __ bind(&env->function_entry_with_intrinsic_handler); 3269 env->register_state.resetTo(env->function_entry_assignment); 3270 emitFunctionEntryWithIntrinsicHandler(env); 3271 3272 __ align(16); 3273 __ bind(&env->function_entry_with_no_intrinsic_handler); 3274 env->register_state.resetTo(env->function_entry_assignment); 3275 Label next_opcode; 3276 emitFunctionEntryWithNoIntrinsicHandler(env, &next_opcode); 3277 3278 for (word i = 0; i < kMaxNargs; i++) { 3279 __ align(16); 3280 __ bind(&env->function_entry_simple_interpreted_handler[i]); 3281 env->register_state.resetTo(env->function_entry_assignment); 3282 emitFunctionEntrySimpleInterpretedHandler(env, /*nargs=*/i); 3283 } 3284 3285 for (word i = 0; i < kMaxNargs; i++) { 3286 __ align(16); 3287 __ bind(&env->function_entry_simple_builtin[i]); 3288 env->register_state.resetTo(env->function_entry_assignment); 3289 emitFunctionEntryBuiltin(env, /*nargs=*/i); 3290 } 3291 } 3292 3293 __ bind(&env->call_interpreted_slow_path); 3294 env->register_state.resetTo(env->call_interpreted_slow_path_assignment); 3295 emitCallInterpretedSlowPath(env); 3296 3297 __ bind(&env->call_trampoline); 3298 env->register_state.resetTo(env->call_trampoline_assignment); 3299 emitCallTrampoline(env); 3300 3301 // Emit the generic handler stubs at the end, out of the way of the 3302 // interesting code. 3303 for (word i = 0; i < 256; ++i) { 3304 __ bind(&env->opcode_handlers[i]); 3305 env->register_state.resetTo(env->handler_assignment); 3306 emitGenericHandler(env, static_cast<Bytecode>(i)); 3307 } 3308} 3309 3310class X64Interpreter : public Interpreter { 3311 public: 3312 X64Interpreter(); 3313 ~X64Interpreter() override; 3314 void setupThread(Thread* thread) override; 3315 void* entryAsm(const Function& function) override; 3316 void setOpcodeCounting(bool enabled) override; 3317 3318 private: 3319 byte* code_; 3320 word size_; 3321 3322 void* function_entry_with_intrinsic_; 3323 void* function_entry_with_no_intrinsic_; 3324 void* function_entry_simple_interpreted_[kMaxNargs]; 3325 void* function_entry_simple_builtin_[kMaxNargs]; 3326 3327 void* default_handler_table_ = nullptr; 3328 void* counting_handler_table_ = nullptr; 3329 bool count_opcodes_ = false; 3330}; 3331 3332X64Interpreter::X64Interpreter() { 3333 EmitEnv env; 3334 emitInterpreter(&env); 3335 3336 // Finalize the code. 3337 size_ = env.as.codeSize(); 3338 code_ = OS::allocateMemory(size_, &size_); 3339 env.as.finalizeInstructions(MemoryRegion(code_, size_)); 3340 OS::protectMemory(code_, size_, OS::kReadExecute); 3341 3342 // Generate jump targets. 3343 function_entry_with_intrinsic_ = 3344 code_ + env.function_entry_with_intrinsic_handler.position(); 3345 function_entry_with_no_intrinsic_ = 3346 code_ + env.function_entry_with_no_intrinsic_handler.position(); 3347 for (word i = 0; i < kMaxNargs; i++) { 3348 function_entry_simple_interpreted_[i] = 3349 code_ + env.function_entry_simple_interpreted_handler[i].position(); 3350 } 3351 for (word i = 0; i < kMaxNargs; i++) { 3352 function_entry_simple_builtin_[i] = 3353 code_ + env.function_entry_simple_builtin[i].position(); 3354 } 3355 3356 default_handler_table_ = code_ + env.handler_offset; 3357 counting_handler_table_ = code_ + env.counting_handler_offset; 3358} 3359 3360X64Interpreter::~X64Interpreter() { OS::freeMemory(code_, size_); } 3361 3362void X64Interpreter::setupThread(Thread* thread) { 3363 thread->setInterpreterFunc(reinterpret_cast<Thread::InterpreterFunc>(code_)); 3364 thread->setInterpreterData(count_opcodes_ ? counting_handler_table_ 3365 : default_handler_table_); 3366} 3367 3368void X64Interpreter::setOpcodeCounting(bool enabled) { 3369 count_opcodes_ = enabled; 3370} 3371 3372void* X64Interpreter::entryAsm(const Function& function) { 3373 if (function.intrinsic() != nullptr) { 3374 return function_entry_with_intrinsic_; 3375 } 3376 word argcount = function.argcount(); 3377 if (function.hasSimpleCall() && function.isInterpreted() && 3378 argcount < kMaxNargs) { 3379 CHECK(argcount >= 0, "can't have negative argcount"); 3380 return function_entry_simple_interpreted_[argcount]; 3381 } 3382 if (function.entry() == builtinTrampoline && function.hasSimpleCall() && 3383 argcount < kMaxNargs) { 3384 DCHECK(function.intrinsic() == nullptr, "expected no intrinsic"); 3385 CHECK(Code::cast(function.code()).code().isSmallInt(), 3386 "expected SmallInt code"); 3387 return function_entry_simple_builtin_[argcount]; 3388 } 3389 return function_entry_with_no_intrinsic_; 3390} 3391 3392} // namespace 3393 3394static void deoptimizeCurrentFunction(Thread* thread) { 3395 EVENT(DEOPT_FUNCTION); 3396 Frame* frame = thread->currentFrame(); 3397 // Reset the PC because we're about to jump back into the assembly 3398 // interpreter and we want to re-try the current opcode. 3399 frame->setVirtualPC(frame->virtualPC() - kCodeUnitSize); 3400 HandleScope scope(thread); 3401 Function function(&scope, frame->function()); 3402 thread->runtime()->populateEntryAsm(function); 3403 function.setFlags(function.flags() & ~Function::Flags::kCompiled); 3404} 3405 3406bool canCompileFunction(Thread* thread, const Function& function) { 3407 if (!function.isInterpreted()) { 3408 std::fprintf( 3409 stderr, "Could not compile '%s' (not interpreted)\n", 3410 unique_c_ptr<char>(Str::cast(function.qualname()).toCStr()).get()); 3411 return false; 3412 } 3413 if (!function.hasSimpleCall()) { 3414 std::fprintf( 3415 stderr, "Could not compile '%s' (not simple)\n", 3416 unique_c_ptr<char>(Str::cast(function.qualname()).toCStr()).get()); 3417 return false; 3418 } 3419 if (function.isCompiled()) { 3420 std::fprintf( 3421 stderr, "Could not compile '%s' (already compiled)\n", 3422 unique_c_ptr<char>(Str::cast(function.qualname()).toCStr()).get()); 3423 return false; 3424 } 3425 HandleScope scope(thread); 3426 MutableBytes code(&scope, function.rewrittenBytecode()); 3427 word num_opcodes = rewrittenBytecodeLength(code); 3428 for (word i = 0; i < num_opcodes;) { 3429 BytecodeOp op = nextBytecodeOp(code, &i); 3430 if (!isSupportedInJIT(op.bc)) { 3431 std::fprintf( 3432 stderr, "Could not compile '%s' (%s)\n", 3433 unique_c_ptr<char>(Str::cast(function.qualname()).toCStr()).get(), 3434 kBytecodeNames[op.bc]); 3435 return false; 3436 } 3437 } 3438 return true; 3439} 3440 3441void compileFunction(Thread* thread, const Function& function) { 3442 EVENT(COMPILE_FUNCTION); 3443 HandleScope scope(thread); 3444 MutableBytes code(&scope, function.rewrittenBytecode()); 3445 word num_opcodes = rewrittenBytecodeLength(code); 3446 JitEnv environ(&scope, thread, function, num_opcodes); 3447 JitEnv* env = &environ; 3448 3449 // TODO(T89721395): Deduplicate these assignments with emitInterpreter. 3450 RegisterAssignment function_entry_assignment[] = { 3451 {&env->pc, kPCReg}, 3452 {&env->oparg, kOpargReg}, 3453 {&env->frame, kFrameReg}, 3454 {&env->thread, kThreadReg}, 3455 {&env->handlers_base, kHandlersBaseReg}, 3456 {&env->callable, kCallableReg}, 3457 }; 3458 env->function_entry_assignment = function_entry_assignment; 3459 3460 RegisterAssignment handler_assignment[] = { 3461 {&env->bytecode, kBCReg}, {&env->pc, kPCReg}, 3462 {&env->oparg, kOpargReg}, {&env->frame, kFrameReg}, 3463 {&env->thread, kThreadReg}, {&env->handlers_base, kHandlersBaseReg}, 3464 }; 3465 env->handler_assignment = handler_assignment; 3466 3467 // Similar to handler_assignment but no PC or oparg. 3468 RegisterAssignment jit_handler_assignment[] = { 3469 {&env->bytecode, kBCReg}, 3470 {&env->frame, kFrameReg}, 3471 {&env->thread, kThreadReg}, 3472 {&env->handlers_base, kHandlersBaseReg}, 3473 }; 3474 env->jit_handler_assignment = jit_handler_assignment; 3475 3476 RegisterAssignment call_interpreted_slow_path_assignment[] = { 3477 {&env->pc, kPCReg}, {&env->callable, kCallableReg}, 3478 {&env->frame, kFrameReg}, {&env->thread, kThreadReg}, 3479 {&env->oparg, kOpargReg}, {&env->handlers_base, kHandlersBaseReg}, 3480 }; 3481 env->call_interpreted_slow_path_assignment = 3482 call_interpreted_slow_path_assignment; 3483 3484 RegisterAssignment call_trampoline_assignment[] = { 3485 {&env->pc, kPCReg}, {&env->callable, kCallableReg}, 3486 {&env->frame, kFrameReg}, {&env->thread, kThreadReg}, 3487 {&env->oparg, kOpargReg}, {&env->handlers_base, kHandlersBaseReg}, 3488 }; 3489 env->call_trampoline_assignment = call_trampoline_assignment; 3490 3491 RegisterAssignment return_handler_assignment[] = { 3492 {&env->thread, kThreadReg}, 3493 {&env->handlers_base, kHandlersBaseReg}, 3494 }; 3495 env->return_handler_assignment = return_handler_assignment; 3496 3497 RegisterAssignment do_return_assignment[] = { 3498 {&env->return_value, kReturnRegs[0]}, 3499 }; 3500 env->do_return_assignment = do_return_assignment; 3501 3502 RegisterAssignment deopt_assignment[] = { 3503 {&env->thread, kThreadReg}, 3504 {&env->frame, kFrameReg}, 3505 {&env->pc, kPCReg}, 3506 }; 3507 env->deopt_assignment = deopt_assignment; 3508 3509 DCHECK(function.isInterpreted(), "function must be interpreted"); 3510 DCHECK(function.hasSimpleCall(), 3511 "function must have a simple calling convention"); 3512 3513 // JIT entrypoints are in entryAsm and are called with the function entry 3514 // assignment. 3515 env->register_state.resetTo(function_entry_assignment); 3516 3517 COMMENT("Function <%s>", 3518 unique_c_ptr<char>(Str::cast(function.qualname()).toCStr()).get()); 3519 COMMENT("Prologue"); 3520 // Check that we received the right number of arguments. 3521 Label call_interpreted_slow_path; 3522 __ cmpl(env->oparg, Immediate(function.argcount())); 3523 env->register_state.check(env->call_interpreted_slow_path_assignment); 3524 __ jcc(NOT_EQUAL, &call_interpreted_slow_path, Assembler::kFarJump); 3525 3526 // Open a new frame. 3527 env->register_state.assign(&env->return_mode, kReturnModeReg); 3528 __ xorl(env->return_mode, env->return_mode); 3529 emitPushCallFrame(env, /*stack_overflow=*/&call_interpreted_slow_path); 3530 3531 for (word i = 0; i < num_opcodes;) { 3532 word current_pc = i * kCodeUnitSize; 3533 BytecodeOp op = nextBytecodeOp(code, &i); 3534 if (!isSupportedInJIT(op.bc)) { 3535 UNIMPLEMENTED("unsupported jit opcode %s", kBytecodeNames[op.bc]); 3536 } 3537 env->current_op = op.bc; 3538 env->setCurrentOp(op); 3539 env->setVirtualPC(i * kCodeUnitSize); 3540 env->register_state.resetTo(env->jit_handler_assignment); 3541 COMMENT("%s %d (%d)", kBytecodeNames[op.bc], op.arg, op.cache); 3542 __ bind(env->opcodeAtByteOffset(current_pc)); 3543 switch (op.bc) { 3544#define BC(name, _0, _1) \ 3545 case name: { \ 3546 jitEmitHandler<name>(env); \ 3547 break; \ 3548 } 3549 FOREACH_BYTECODE(BC) 3550#undef BC 3551 } 3552 } 3553 3554 if (!env->unwind_handler.isUnused()) { 3555 COMMENT("Unwind"); 3556 __ bind(&env->unwind_handler); 3557 // TODO(T91715866): Unwind. 3558 __ ud2(); 3559 } 3560 3561 COMMENT("Call interpreted slow path"); 3562 __ bind(&call_interpreted_slow_path); 3563 // TODO(T89721522): Have one canonical slow path chunk of code that all JIT 3564 // functions jump to, instead of one per function. 3565 env->register_state.resetTo(env->call_interpreted_slow_path_assignment); 3566 emitCallInterpretedSlowPath(env); 3567 3568 if (!env->deopt_handler.isUnused()) { 3569 COMMENT("Deopt"); 3570 // Handle deoptimization by resetting the entrypoint to an assembly 3571 // entrypoint and then jumping back into the interpreter. 3572 __ bind(&env->deopt_handler); 3573 env->register_state.resetTo(env->deopt_assignment); 3574 __ movq(kArgRegs[0], env->thread); 3575 emitSaveInterpreterState(env, kVMPC | kVMStack | kVMFrame); 3576 emitCall<void (*)(Thread*)>(env, deoptimizeCurrentFunction); 3577 // Jump back into the interpreter. 3578 emitRestoreInterpreterState(env, kAllState); 3579 emitNextOpcodeImpl(env); 3580 } 3581 3582 COMMENT("<END>"); 3583 __ ud2(); 3584 3585 // Finalize the code. 3586 word jit_size = Utils::roundUp(env->as.codeSize(), kBitsPerByte); 3587 uword address; 3588 bool allocated = 3589 thread->runtime()->allocateForMachineCode(jit_size, &address); 3590 CHECK(allocated, "could not allocate memory for JIT function"); 3591 byte* jit_code = reinterpret_cast<byte*>(address); 3592 env->as.finalizeInstructions(MemoryRegion(jit_code, jit_size)); 3593 // TODO(T83754516): Mark memory as RX. 3594 3595 // Replace the entrypoint. 3596 function.setEntryAsm(jit_code); 3597 function.setFlags(function.flags() | Function::Flags::kCompiled); 3598} 3599 3600Interpreter* createAsmInterpreter() { return new X64Interpreter; } 3601 3602} // namespace py