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 16 namespace v8 { 17 namespace internal { 18 namespace interpreter { 19 20 class 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: RegListNodePair(TNode<IntPtrT> base_reg_location,TNode<Word32T> reg_count)88 RegListNodePair(TNode<IntPtrT> base_reg_location, TNode<Word32T> reg_count) 89 : base_reg_location_(base_reg_location), reg_count_(reg_count) {} 90 reg_count()91 TNode<Word32T> reg_count() const { return reg_count_; } base_reg_location()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: bytecode()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 operand_scale()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