this repo has no description
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, ¬_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(¬_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