1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_INTERPRETER_INTERPRETER_ASSEMBLER_H_
6#define V8_INTERPRETER_INTERPRETER_ASSEMBLER_H_
7
8#include "src/builtins/builtins.h"
9#include "src/codegen/code-stub-assembler.h"
10#include "src/common/globals.h"
11#include "src/interpreter/bytecode-register.h"
12#include "src/interpreter/bytecodes.h"
13#include "src/runtime/runtime.h"
14#include "src/utils/allocation.h"
15
16namespace v8 {
17namespace internal {
18namespace interpreter {
19
20class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
21 public:
22  InterpreterAssembler(compiler::CodeAssemblerState* state, Bytecode bytecode,
23                       OperandScale operand_scale);
24  ~InterpreterAssembler();
25  InterpreterAssembler(const InterpreterAssembler&) = delete;
26  InterpreterAssembler& operator=(const InterpreterAssembler&) = delete;
27
28  // Returns the 32-bit unsigned count immediate for bytecode operand
29  // |operand_index| in the current bytecode.
30  TNode<Uint32T> BytecodeOperandCount(int operand_index);
31  // Returns the 32-bit unsigned flag for bytecode operand |operand_index|
32  // in the current bytecode.
33  TNode<Uint32T> BytecodeOperandFlag(int operand_index);
34  // Returns the 32-bit zero-extended index immediate for bytecode operand
35  // |operand_index| in the current bytecode.
36  TNode<Uint32T> BytecodeOperandIdxInt32(int operand_index);
37  // Returns the word zero-extended index immediate for bytecode operand
38  // |operand_index| in the current bytecode.
39  TNode<UintPtrT> BytecodeOperandIdx(int operand_index);
40  // Returns the smi index immediate for bytecode operand |operand_index|
41  // in the current bytecode.
42  TNode<Smi> BytecodeOperandIdxSmi(int operand_index);
43  // Returns the TaggedIndex immediate for bytecode operand |operand_index|
44  // in the current bytecode.
45  TNode<TaggedIndex> BytecodeOperandIdxTaggedIndex(int operand_index);
46  // Returns the 32-bit unsigned immediate for bytecode operand |operand_index|
47  // in the current bytecode.
48  TNode<Uint32T> BytecodeOperandUImm(int operand_index);
49  // Returns the word-size unsigned immediate for bytecode operand
50  // |operand_index| in the current bytecode.
51  TNode<UintPtrT> BytecodeOperandUImmWord(int operand_index);
52  // Returns the unsigned smi immediate for bytecode operand |operand_index| in
53  // the current bytecode.
54  TNode<Smi> BytecodeOperandUImmSmi(int operand_index);
55  // Returns the 32-bit signed immediate for bytecode operand |operand_index|
56  // in the current bytecode.
57  TNode<Int32T> BytecodeOperandImm(int operand_index);
58  // Returns the word-size signed immediate for bytecode operand |operand_index|
59  // in the current bytecode.
60  TNode<IntPtrT> BytecodeOperandImmIntPtr(int operand_index);
61  // Returns the smi immediate for bytecode operand |operand_index| in the
62  // current bytecode.
63  TNode<Smi> BytecodeOperandImmSmi(int operand_index);
64  // Returns the 32-bit unsigned runtime id immediate for bytecode operand
65  // |operand_index| in the current bytecode.
66  TNode<Uint32T> BytecodeOperandRuntimeId(int operand_index);
67  // Returns the word zero-extended native context index immediate for bytecode
68  // operand |operand_index| in the current bytecode.
69  TNode<UintPtrT> BytecodeOperandNativeContextIndex(int operand_index);
70  // Returns the 32-bit unsigned intrinsic id immediate for bytecode operand
71  // |operand_index| in the current bytecode.
72  TNode<Uint32T> BytecodeOperandIntrinsicId(int operand_index);
73  // Accumulator.
74  TNode<Object> GetAccumulator();
75  void SetAccumulator(TNode<Object> value);
76
77  // Context.
78  TNode<Context> GetContext();
79  void SetContext(TNode<Context> value);
80
81  // Context at |depth| in the context chain starting at |context|.
82  TNode<Context> GetContextAtDepth(TNode<Context> context,
83                                   TNode<Uint32T> depth);
84
85  // A RegListNodePair provides an abstraction over lists of registers.
86  class RegListNodePair {
87   public:
88    RegListNodePair(TNode<IntPtrT> base_reg_location, TNode<Word32T> reg_count)
89        : base_reg_location_(base_reg_location), reg_count_(reg_count) {}
90
91    TNode<Word32T> reg_count() const { return reg_count_; }
92    TNode<IntPtrT> base_reg_location() const { return base_reg_location_; }
93
94   private:
95    TNode<IntPtrT> base_reg_location_;
96    TNode<Word32T> reg_count_;
97  };
98
99  // Backup/restore register file to/from a fixed array of the correct length.
100  // There is an asymmetry between suspend/export and resume/import.
101  // - Suspend copies arguments and registers to the generator.
102  // - Resume copies only the registers from the generator, the arguments
103  //   are copied by the ResumeGenerator trampoline.
104  TNode<FixedArray> ExportParametersAndRegisterFile(
105      TNode<FixedArray> array, const RegListNodePair& registers,
106      TNode<Int32T> formal_parameter_count);
107  TNode<FixedArray> ImportRegisterFile(TNode<FixedArray> array,
108                                       const RegListNodePair& registers,
109                                       TNode<Int32T> formal_parameter_count);
110
111  // Loads from and stores to the interpreter register file.
112  TNode<Object> LoadRegister(Register reg);
113  TNode<IntPtrT> LoadAndUntagRegister(Register reg);
114  TNode<Object> LoadRegisterAtOperandIndex(int operand_index);
115  std::pair<TNode<Object>, TNode<Object>> LoadRegisterPairAtOperandIndex(
116      int operand_index);
117  void StoreRegister(TNode<Object> value, Register reg);
118  void StoreRegisterAtOperandIndex(TNode<Object> value, int operand_index);
119  void StoreRegisterPairAtOperandIndex(TNode<Object> value1,
120                                       TNode<Object> value2, int operand_index);
121  void StoreRegisterTripleAtOperandIndex(TNode<Object> value1,
122                                         TNode<Object> value2,
123                                         TNode<Object> value3,
124                                         int operand_index);
125
126  RegListNodePair GetRegisterListAtOperandIndex(int operand_index);
127  TNode<Object> LoadRegisterFromRegisterList(const RegListNodePair& reg_list,
128                                             int index);
129  TNode<IntPtrT> RegisterLocationInRegisterList(const RegListNodePair& reg_list,
130                                                int index);
131
132  // Load constant at the index specified in operand |operand_index| from the
133  // constant pool.
134  TNode<Object> LoadConstantPoolEntryAtOperandIndex(int operand_index);
135  // Load and untag constant at the index specified in operand |operand_index|
136  // from the constant pool.
137  TNode<IntPtrT> LoadAndUntagConstantPoolEntryAtOperandIndex(int operand_index);
138  // Load constant at |index| in the constant pool.
139  TNode<Object> LoadConstantPoolEntry(TNode<WordT> index);
140  // Load and untag constant at |index| in the constant pool.
141  TNode<IntPtrT> LoadAndUntagConstantPoolEntry(TNode<WordT> index);
142
143  // Load the FeedbackVector for the current function. The retuned node could be
144  // undefined.
145  TNode<HeapObject> LoadFeedbackVector();
146
147  // Call JSFunction or Callable |function| with |args| arguments, possibly
148  // including the receiver depending on |receiver_mode|. After the call returns
149  // directly dispatches to the next bytecode.
150  void CallJSAndDispatch(TNode<Object> function, TNode<Context> context,
151                         const RegListNodePair& args,
152                         ConvertReceiverMode receiver_mode);
153
154  // Call JSFunction or Callable |function| with |arg_count| arguments (not
155  // including receiver) passed as |args|, possibly including the receiver
156  // depending on |receiver_mode|. After the call returns directly dispatches to
157  // the next bytecode.
158  template <class... TArgs>
159  void CallJSAndDispatch(TNode<Object> function, TNode<Context> context,
160                         TNode<Word32T> arg_count,
161                         ConvertReceiverMode receiver_mode, TArgs... args);
162
163  // Call JSFunction or Callable |function| with |args|
164  // arguments (not including receiver), and the final argument being spread.
165  // After the call returns directly dispatches to the next bytecode.
166  void CallJSWithSpreadAndDispatch(TNode<Object> function,
167                                   TNode<Context> context,
168                                   const RegListNodePair& args,
169                                   TNode<UintPtrT> slot_id,
170                                   TNode<HeapObject> maybe_feedback_vector);
171
172  // Call constructor |target| with |args| arguments (not including receiver).
173  // The |new_target| is the same as the |target| for the new keyword, but
174  // differs for the super keyword.
175  TNode<Object> Construct(TNode<Object> target, TNode<Context> context,
176                          TNode<Object> new_target, const RegListNodePair& args,
177                          TNode<UintPtrT> slot_id,
178                          TNode<HeapObject> maybe_feedback_vector);
179
180  // Call constructor |target| with |args| arguments (not including
181  // receiver). The last argument is always a spread. The |new_target| is the
182  // same as the |target| for the new keyword, but differs for the super
183  // keyword.
184  TNode<Object> ConstructWithSpread(TNode<Object> target,
185                                    TNode<Context> context,
186                                    TNode<Object> new_target,
187                                    const RegListNodePair& args,
188                                    TNode<UintPtrT> slot_id,
189                                    TNode<HeapObject> maybe_feedback_vector);
190
191  // Call runtime function with |args| arguments.
192  template <class T = Object>
193  TNode<T> CallRuntimeN(TNode<Uint32T> function_id, TNode<Context> context,
194                        const RegListNodePair& args, int return_count);
195
196  // Jump forward relative to the current bytecode by the |jump_offset|.
197  void Jump(TNode<IntPtrT> jump_offset);
198
199  // Jump backward relative to the current bytecode by the |jump_offset|.
200  void JumpBackward(TNode<IntPtrT> jump_offset);
201
202  // Jump forward relative to the current bytecode by |jump_offset| if the
203  // word values |lhs| and |rhs| are equal.
204  void JumpIfTaggedEqual(TNode<Object> lhs, TNode<Object> rhs,
205                         TNode<IntPtrT> jump_offset);
206
207  // Jump forward relative to the current bytecode by offest specified in
208  // operand |operand_index| if the word values |lhs| and |rhs| are equal.
209  void JumpIfTaggedEqual(TNode<Object> lhs, TNode<Object> rhs,
210                         int operand_index);
211
212  // Jump forward relative to the current bytecode by offest specified from the
213  // constant pool if the word values |lhs| and |rhs| are equal.
214  // The constant's index is specified in operand |operand_index|.
215  void JumpIfTaggedEqualConstant(TNode<Object> lhs, TNode<Object> rhs,
216                                 int operand_index);
217
218  // Jump forward relative to the current bytecode by |jump_offset| if the
219  // word values |lhs| and |rhs| are not equal.
220  void JumpIfTaggedNotEqual(TNode<Object> lhs, TNode<Object> rhs,
221                            TNode<IntPtrT> jump_offset);
222
223  // Jump forward relative to the current bytecode by offest specified in
224  // operand |operand_index| if the word values |lhs| and |rhs| are not equal.
225  void JumpIfTaggedNotEqual(TNode<Object> lhs, TNode<Object> rhs,
226                            int operand_index);
227
228  // Jump forward relative to the current bytecode by offest specified from the
229  // constant pool if the word values |lhs| and |rhs| are not equal.
230  // The constant's index is specified in operand |operand_index|.
231  void JumpIfTaggedNotEqualConstant(TNode<Object> lhs, TNode<Object> rhs,
232                                    int operand_index);
233
234  // Updates the profiler interrupt budget for a return.
235  void UpdateInterruptBudgetOnReturn();
236
237  // Returns the OSR urgency and install target from the bytecode header.
238  TNode<Int16T> LoadOsrUrgencyAndInstallTarget();
239
240  // Dispatch to the bytecode.
241  void Dispatch();
242
243  // Dispatch bytecode as wide operand variant.
244  void DispatchWide(OperandScale operand_scale);
245
246  // Dispatch to |target_bytecode| at |new_bytecode_offset|.
247  // |target_bytecode| should be equivalent to loading from the offset.
248  void DispatchToBytecode(TNode<WordT> target_bytecode,
249                          TNode<IntPtrT> new_bytecode_offset);
250
251  // Dispatches to |target_bytecode| at BytecodeOffset(). Includes short-star
252  // lookahead if the current bytecode_ is likely followed by a short-star
253  // instruction.
254  void DispatchToBytecodeWithOptionalStarLookahead(
255      TNode<WordT> target_bytecode);
256
257  // Abort with the given abort reason.
258  void Abort(AbortReason abort_reason);
259  void AbortIfWordNotEqual(TNode<WordT> lhs, TNode<WordT> rhs,
260                           AbortReason abort_reason);
261  // Abort if |register_count| is invalid for given register file array.
262  void AbortIfRegisterCountInvalid(
263      TNode<FixedArrayBase> parameters_and_registers,
264      TNode<IntPtrT> formal_parameter_count, TNode<UintPtrT> register_count);
265
266  // Perform OnStackReplacement.
267  void OnStackReplacement(TNode<Context> context, TNode<IntPtrT> relative_jump);
268
269  // The BytecodeOffset() is the offset from the ByteCodeArray pointer; to
270  // translate into runtime `BytecodeOffset` (defined in utils.h as the offset
271  // from the start of the bytecode section), this constant has to be applied.
272  static constexpr int kFirstBytecodeOffset =
273      BytecodeArray::kHeaderSize - kHeapObjectTag;
274
275  // Returns the offset from the BytecodeArrayPointer of the current bytecode.
276  TNode<IntPtrT> BytecodeOffset();
277
278 protected:
279  Bytecode bytecode() const { return bytecode_; }
280  static bool TargetSupportsUnalignedAccess();
281
282  void ToNumberOrNumeric(Object::Conversion mode);
283
284  void StoreRegisterForShortStar(TNode<Object> value, TNode<WordT> opcode);
285
286  // Load the bytecode at |bytecode_offset|.
287  TNode<WordT> LoadBytecode(TNode<IntPtrT> bytecode_offset);
288
289 private:
290  // Returns a pointer to the current function's BytecodeArray object.
291  TNode<BytecodeArray> BytecodeArrayTaggedPointer();
292
293  // Returns a pointer to first entry in the interpreter dispatch table.
294  TNode<ExternalReference> DispatchTablePointer();
295
296  // Returns the accumulator value without checking whether bytecode
297  // uses it. This is intended to be used only in dispatch and in
298  // tracing as these need to bypass accumulator use validity checks.
299  TNode<Object> GetAccumulatorUnchecked();
300
301  // Returns the frame pointer for the interpreted frame of the function being
302  // interpreted.
303  TNode<RawPtrT> GetInterpretedFramePointer();
304
305  // Operations on registers.
306  TNode<IntPtrT> RegisterLocation(Register reg);
307  TNode<IntPtrT> RegisterLocation(TNode<IntPtrT> reg_index);
308  TNode<IntPtrT> NextRegister(TNode<IntPtrT> reg_index);
309  TNode<Object> LoadRegister(TNode<IntPtrT> reg_index);
310  void StoreRegister(TNode<Object> value, TNode<IntPtrT> reg_index);
311
312  // Saves and restores interpreter bytecode offset to the interpreter stack
313  // frame when performing a call.
314  void CallPrologue();
315  void CallEpilogue();
316
317  // Increment the dispatch counter for the (current, next) bytecode pair.
318  void TraceBytecodeDispatch(TNode<WordT> target_bytecode);
319
320  // Traces the current bytecode by calling |function_id|.
321  void TraceBytecode(Runtime::FunctionId function_id);
322
323  // Updates the bytecode array's interrupt budget by a 32-bit unsigned |weight|
324  // and calls Runtime::kInterrupt if counter reaches zero. If |backward|, then
325  // the interrupt budget is decremented, otherwise it is incremented.
326  void UpdateInterruptBudget(TNode<Int32T> weight, bool backward);
327
328  // Returns the offset of register |index| relative to RegisterFilePointer().
329  TNode<IntPtrT> RegisterFrameOffset(TNode<IntPtrT> index);
330
331  // Returns the offset of an operand relative to the current bytecode offset.
332  TNode<IntPtrT> OperandOffset(int operand_index);
333
334  // Returns a value built from an sequence of bytes in the bytecode
335  // array starting at |relative_offset| from the current bytecode.
336  // The |result_type| determines the size and signedness.  of the
337  // value read. This method should only be used on architectures that
338  // do not support unaligned memory accesses.
339  TNode<Word32T> BytecodeOperandReadUnaligned(int relative_offset,
340                                              MachineType result_type);
341
342  // Returns zero- or sign-extended to word32 value of the operand.
343  TNode<Uint8T> BytecodeOperandUnsignedByte(int operand_index);
344  TNode<Int8T> BytecodeOperandSignedByte(int operand_index);
345  TNode<Uint16T> BytecodeOperandUnsignedShort(int operand_index);
346  TNode<Int16T> BytecodeOperandSignedShort(int operand_index);
347  TNode<Uint32T> BytecodeOperandUnsignedQuad(int operand_index);
348  TNode<Int32T> BytecodeOperandSignedQuad(int operand_index);
349
350  // Returns zero- or sign-extended to word32 value of the operand of
351  // given size.
352  TNode<Int32T> BytecodeSignedOperand(int operand_index,
353                                      OperandSize operand_size);
354  TNode<Uint32T> BytecodeUnsignedOperand(int operand_index,
355                                         OperandSize operand_size);
356
357  // Returns the word-size sign-extended register index for bytecode operand
358  // |operand_index| in the current bytecode.
359  TNode<IntPtrT> BytecodeOperandReg(int operand_index);
360
361  // Returns the word zero-extended index immediate for bytecode operand
362  // |operand_index| in the current bytecode for use when loading a constant
363  // pool element.
364  TNode<UintPtrT> BytecodeOperandConstantPoolIdx(int operand_index);
365
366  // Jump relative to the current bytecode by the |jump_offset|. If |backward|,
367  // then jump backward (subtract the offset), otherwise jump forward (add the
368  // offset). Helper function for Jump and JumpBackward.
369  void Jump(TNode<IntPtrT> jump_offset, bool backward);
370
371  // Jump forward relative to the current bytecode by |jump_offset| if the
372  // |condition| is true. Helper function for JumpIfTaggedEqual and
373  // JumpIfTaggedNotEqual.
374  void JumpConditional(TNode<BoolT> condition, TNode<IntPtrT> jump_offset);
375
376  // Jump forward relative to the current bytecode by offest specified in
377  // operand |operand_index| if the |condition| is true. Helper function for
378  // JumpIfTaggedEqual and JumpIfTaggedNotEqual.
379  void JumpConditionalByImmediateOperand(TNode<BoolT> condition,
380                                         int operand_index);
381
382  // Jump forward relative to the current bytecode by offest specified from the
383  // constant pool if the |condition| is true. The constant's index is specified
384  // in operand |operand_index|. Helper function for JumpIfTaggedEqualConstant
385  // and JumpIfTaggedNotEqualConstant.
386  void JumpConditionalByConstantOperand(TNode<BoolT> condition,
387                                        int operand_index);
388
389  // Save the bytecode offset to the interpreter frame.
390  void SaveBytecodeOffset();
391  // Reload the bytecode offset from the interpreter frame.
392  TNode<IntPtrT> ReloadBytecodeOffset();
393
394  // Updates and returns BytecodeOffset() advanced by the current bytecode's
395  // size. Traces the exit of the current bytecode.
396  TNode<IntPtrT> Advance();
397
398  // Updates and returns BytecodeOffset() advanced by delta bytecodes.
399  // Traces the exit of the current bytecode.
400  TNode<IntPtrT> Advance(int delta);
401  TNode<IntPtrT> Advance(TNode<IntPtrT> delta, bool backward = false);
402
403  // Look ahead for short Star and inline it in a branch, including subsequent
404  // dispatch. Anything after this point can assume that the following
405  // instruction was not a short Star.
406  void StarDispatchLookahead(TNode<WordT> target_bytecode);
407
408  // Build code for short Star at the current BytecodeOffset() and Advance() to
409  // the next dispatch offset.
410  void InlineShortStar(TNode<WordT> target_bytecode);
411
412  // Dispatch to the bytecode handler with code entry point |handler_entry|.
413  void DispatchToBytecodeHandlerEntry(TNode<RawPtrT> handler_entry,
414                                      TNode<IntPtrT> bytecode_offset);
415
416  int CurrentBytecodeSize() const;
417
418  OperandScale operand_scale() const { return operand_scale_; }
419
420  Bytecode bytecode_;
421  OperandScale operand_scale_;
422  CodeStubAssembler::TVariable<RawPtrT> interpreted_frame_pointer_;
423  CodeStubAssembler::TVariable<BytecodeArray> bytecode_array_;
424  CodeStubAssembler::TVariable<IntPtrT> bytecode_offset_;
425  CodeStubAssembler::TVariable<ExternalReference> dispatch_table_;
426  CodeStubAssembler::TVariable<Object> accumulator_;
427  ImplicitRegisterUse implicit_register_use_;
428  bool made_call_;
429  bool reloaded_frame_ptr_;
430  bool bytecode_array_valid_;
431};
432
433}  // namespace interpreter
434}  // namespace internal
435}  // namespace v8
436
437#endif  // V8_INTERPRETER_INTERPRETER_ASSEMBLER_H_
438