OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 #ifndef VM_CONSTANTS_DBC_H_ |
| 6 #define VM_CONSTANTS_DBC_H_ |
| 7 |
| 8 #include "platform/globals.h" |
| 9 #include "platform/assert.h" |
| 10 #include "platform/utils.h" |
| 11 |
| 12 |
| 13 namespace dart { |
| 14 |
| 15 // List of Dart Bytecode instructions. |
| 16 // |
| 17 // INTERPRETER STATE |
| 18 // |
| 19 // current frame info (see stack_frame_dbc.h for layout) |
| 20 // v-----^-----v |
| 21 // ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~ |
| 22 // ~ | ~ ~ | FP[0] | FP[1] | ~ ~ | SP[-1]| SP[0] | |
| 23 // ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~ |
| 24 // ^ ^ |
| 25 // FP SP |
| 26 // |
| 27 // |
| 28 // The state of execution is captured in few interpreter registers: |
| 29 // |
| 30 // FP - base of the current frame |
| 31 // SP - top of the stack (TOS) for the current frame |
| 32 // PP - object pool for the currently execution function |
| 33 // |
| 34 // Frame info stored below FP additionally contains pointers to the currently |
| 35 // executing function and code (see stack_frame_dbc.h for more information). |
| 36 // |
| 37 // In the unoptimized code most of bytecodes take operands implicitly from |
| 38 // stack and store results again on the stack. Constant operands are usually |
| 39 // taken from the object pool by index. |
| 40 // |
| 41 // ENCODING |
| 42 // |
| 43 // Each instruction is a 32-bit integer with opcode stored in the least |
| 44 // significant byte. The following operand encodings are used: |
| 45 // |
| 46 // 0........8.......16.......24.......32 |
| 47 // +--------+--------+--------+--------+ |
| 48 // | opcode |~~~~~~~~~~~~~~~~~~~~~~~~~~| 0: no operands |
| 49 // +--------+--------+--------+--------+ |
| 50 // |
| 51 // +--------+--------+--------+--------+ |
| 52 // | opcode | A |~~~~~~~~~~~~~~~~~| A: single unsigned 8-bit operand |
| 53 // +--------+--------+--------+--------+ |
| 54 // |
| 55 // +--------+--------+--------+--------+ |
| 56 // | opcode | A | D | A_D: unsigned 8-bit operand and |
| 57 // +--------+--------+--------+--------+ unsigned 16-bit operand |
| 58 // |
| 59 // +--------+--------+--------+--------+ |
| 60 // | opcode | A | X | A_X: unsigned 8-bit operand and |
| 61 // +--------+--------+--------+--------+ signed 16-bit operand |
| 62 // |
| 63 // +--------+--------+--------+--------+ |
| 64 // | opcode |~~~~~~~~| D | D: unsigned 16-bit operand |
| 65 // +--------+--------+--------+--------+ |
| 66 // |
| 67 // +--------+--------+--------+--------+ |
| 68 // | opcode |~~~~~~~~| X | X: signed 16-bit operand |
| 69 // +--------+--------+--------+--------+ |
| 70 // |
| 71 // +--------+--------+--------+--------+ |
| 72 // | opcode | A | B | C | A_B_C: 3 unsigned 8-bit operands |
| 73 // +--------+--------+--------+--------+ |
| 74 // |
| 75 // +--------+--------+--------+--------+ |
| 76 // | opcode | T | T: signed 24-bit operand |
| 77 // +--------+--------+--------+--------+ |
| 78 // |
| 79 // |
| 80 // INSTRUCTIONS |
| 81 // |
| 82 // - Trap |
| 83 // |
| 84 // Unreachable instruction. |
| 85 // |
| 86 // - Compile |
| 87 // |
| 88 // Compile current function and start executing newly produced code |
| 89 // (used to implement LazyCompileStub); |
| 90 // |
| 91 // - Intrinsic id |
| 92 // |
| 93 // Execute intrinsic with the given id. If intrinsic returns true then |
| 94 // return from the current function to the caller passing value produced |
| 95 // by the intrinsic as a result; |
| 96 // |
| 97 // - Drop1; DropR n; Drop n |
| 98 // |
| 99 // Drop 1 or n values from the stack, if instruction is DropR push the first |
| 100 // dropped value to the stack; |
| 101 // |
| 102 // - Jump target |
| 103 // |
| 104 // Jump to the given target. Target is specified as offset from the PC of the |
| 105 // jump instruction. |
| 106 // |
| 107 // - Return R; ReturnTOS |
| 108 // |
| 109 // Return to the caller using either a value from the given register or a |
| 110 // value from the top-of-stack as a result. |
| 111 // |
| 112 // Note: return instruction knows how many arguments to remove from the |
| 113 // stack because it can look at the call instruction at caller's PC and |
| 114 // take argument count from it. |
| 115 // |
| 116 // - Move rA, rX |
| 117 // |
| 118 // FP[rA] <- FP[rX] |
| 119 // Note: rX is signed so it can be used to address parameters which are |
| 120 // at negative indices with respect to FP. |
| 121 // |
| 122 // - Push rX |
| 123 // |
| 124 // Push FP[rX] to the stack. |
| 125 // |
| 126 // - LoadConstant rA, D; PushConstant D |
| 127 // |
| 128 // Load value at index D from constant pool into FP[rA] or push it onto the |
| 129 // stack. |
| 130 // |
| 131 // - StoreLocal rX; PopLocal rX |
| 132 // |
| 133 // Store top of the stack into FP[rX] and pop it if needed. |
| 134 // |
| 135 // - StaticCall ArgC, D |
| 136 // |
| 137 // Invoke function in SP[0] with arguments SP[-(1+ArgC)], ..., SP[-1] and |
| 138 // argument descriptor PP[D]. |
| 139 // |
| 140 // - InstanceCall ArgC, D; InstanceCall2 ArgC, D; InstanceCall3 ArgC, D |
| 141 // |
| 142 // Lookup and invoke method using ICData in PP[D] with arguments |
| 143 // SP[-(1+ArgC)], ..., SP[-1]. |
| 144 // |
| 145 // - NativeCall, NativeBootstrapCall |
| 146 // |
| 147 // Invoke native function SP[-1] with argc_tag SP[0]. |
| 148 // |
| 149 // - AddTOS; SubTOS; MulTOS; BitOrTOS; BitAndTOS; EqualTOS; LessThanTOS; |
| 150 // GreaterThanTOS; |
| 151 // |
| 152 // Smi fast-path for a corresponding method. Checks if SP[0] and SP[-1] are |
| 153 // both smis and result of SP[0] <op> SP[-1] is a smi - if this is true |
| 154 // then pops operands and pushes result on the stack and skips the next |
| 155 // instruction (which implements a slow path fallback). |
| 156 // |
| 157 // - StoreStaticTOS D |
| 158 // |
| 159 // Stores TOS into the static field PP[D]. |
| 160 // |
| 161 // - PushStatic |
| 162 // |
| 163 // Pushes value of the static field PP[D] on to the stack. |
| 164 // |
| 165 // - InitStaticTOS |
| 166 // |
| 167 // Takes static field from TOS and ensures that it is initialized. |
| 168 // |
| 169 // - IfNeStrictTOS; IfEqStrictTOS; IfNeStrictNumTOS; IfEqStrictNumTOS |
| 170 // |
| 171 // Skips the next instruction unless the given condition holds. 'Num' |
| 172 // variants perform number check while non-Num variants just compare |
| 173 // RawObject pointers. |
| 174 // |
| 175 // Used to implement conditional jump: |
| 176 // |
| 177 // IfNeStrictTOS |
| 178 // Jump T ;; jump if not equal |
| 179 // |
| 180 // - CreateArrayTOS |
| 181 // |
| 182 // Allocate array of length SP[0] with type arguments SP[-1]. |
| 183 // |
| 184 // - Allocate D |
| 185 // |
| 186 // Allocate object of class PP[D] with no type arguments. |
| 187 // |
| 188 // - AllocateT |
| 189 // |
| 190 // Allocate object of class SP[0] with type arguments SP[-1]. |
| 191 // |
| 192 // - StoreIndexedTOS |
| 193 // |
| 194 // Store SP[0] into array SP[-2] at index SP[-1]. No typechecking is done. |
| 195 // SP[-2] is assumed to be a RawArray, SP[-1] to be a smi. |
| 196 // |
| 197 // - StoreField rA, B, rC |
| 198 // |
| 199 // Store value FP[rC] into object FP[rA] at offset (in words) B. |
| 200 // |
| 201 // - StoreFieldTOS D |
| 202 // |
| 203 // Store value SP[0] into object SP[-1] at offset (in words) D. |
| 204 // |
| 205 // - LoadField rA, rB, C |
| 206 // |
| 207 // Load value at offset (in words) C from object FP[rB] into FP[rA]. |
| 208 // |
| 209 // - LoadFieldTOS D |
| 210 // |
| 211 // Push value at offset (in words) D from object SP[0]. |
| 212 // |
| 213 // - BooleanNegateTOS |
| 214 // |
| 215 // SP[0] = !SP[0] |
| 216 // |
| 217 // - Throw A |
| 218 // |
| 219 // Throw (Rethrow if A != 0) exception. Exception object and stack object |
| 220 // are taken from TOS. |
| 221 // |
| 222 // - Entry A, B, rC |
| 223 // |
| 224 // Function prologue for the function with no optional or named arguments: |
| 225 // A - expected number of positional arguments; |
| 226 // B - number of local slots to reserve; |
| 227 // rC - specifies context register to initialize with empty context. |
| 228 // |
| 229 // - EntryOpt A, B, C |
| 230 // |
| 231 // Function prologue for the function with optional or named arguments: |
| 232 // A - expected number of positional arguments; |
| 233 // B - number of optional arguments; |
| 234 // C - number of named arguments; |
| 235 // |
| 236 // Only one of B and C can be not 0. |
| 237 // |
| 238 // If B is not 0 then EntryOpt bytecode is followed by B LoadConstant |
| 239 // bytecodes specifying default values for optional arguments. |
| 240 // |
| 241 // If C is not 0 then EntryOpt is followed by 2 * B LoadConstant bytecodes. |
| 242 // Bytecode at 2 * i specifies name of the i-th named argument and at |
| 243 // 2 * i + 1 default value. rA part of the LoadConstant bytecode specifies |
| 244 // the location of the parameter on the stack. Here named arguments are |
| 245 // sorted alphabetically to enable linear matching similar to how function |
| 246 // prologues are implemented on other architectures. |
| 247 // |
| 248 // Note: Unlike Entry bytecode EntryOpt does not setup the frame for |
| 249 // local variables this is done by a separate bytecode Frame. |
| 250 // |
| 251 // - Frame D |
| 252 // |
| 253 // Reserve and initialize with null space for D local variables. |
| 254 // |
| 255 // - SetFrame A |
| 256 // |
| 257 // Reinitialize SP assuming that current frame has size A. |
| 258 // Used to drop temporaries from the stack in the exception handler. |
| 259 // |
| 260 // - AllocateContext D |
| 261 // |
| 262 // Allocate Context object assuming for D context variables. |
| 263 // |
| 264 // - CloneContext |
| 265 // |
| 266 // Clone context stored in TOS. |
| 267 // |
| 268 // - MoveSpecial rA, D |
| 269 // |
| 270 // Copy special values from inside interpreter to FP[rA]. Currently only |
| 271 // used to pass exception object (D = 0) and stack trace object (D = 1) to |
| 272 // catch handler. |
| 273 // |
| 274 // - InstantiateType D |
| 275 // |
| 276 // Instantiate type PP[D] with instantiator type arguments SP[0]. |
| 277 // |
| 278 // - InstantiateTypeArgumentsTOS D |
| 279 // |
| 280 // Instantiate type arguments PP[D] with instantiator SP[0]. |
| 281 // |
| 282 // - AssertAssignable D |
| 283 // |
| 284 // Assert that SP[-3] is assignable to variable named SP[0] of type |
| 285 // SP[-1] with type arguments SP[-2] using SubtypeTestCache PP[D]. |
| 286 // |
| 287 // - AssertBoolean A |
| 288 // |
| 289 // Assert that TOS is a boolean (A = 1) or that TOS is not null (A = 0). |
| 290 // |
| 291 // - CheckStack |
| 292 // |
| 293 // Compare SP against isolate stack limit and call StackOverflow handler if |
| 294 // necessary. |
| 295 // |
| 296 // - DebugStep, DebugBreak A |
| 297 // |
| 298 // Debugger support. DebugBreak is bytecode that can be patched into the |
| 299 // instruction stream to trigger in place breakpoint. |
| 300 // |
| 301 // When patching instance or static call with DebugBreak we set A to |
| 302 // match patched call's argument count so that Return instructions continue |
| 303 // to work. |
| 304 // |
| 305 // TODO(vegorov) the way we replace calls with DebugBreak does not work |
| 306 // with our smi fast paths because DebugBreak is simply skipped. |
| 307 // |
| 308 // BYTECODE LIST FORMAT |
| 309 // |
| 310 // Bytecode list below is specified using the following format: |
| 311 // |
| 312 // V(BytecodeName, OperandForm, Op1, Op2, Op3) |
| 313 // |
| 314 // - OperandForm specifies operand encoding and should be one of 0, A, T, A_D, |
| 315 // A_X, X, D (see ENCODING section above). |
| 316 // |
| 317 // - Op1, Op2, Op2 specify operand meaning. Possible values: |
| 318 // |
| 319 // ___ ignored / non-existent operand |
| 320 // num immediate operand |
| 321 // lit constant literal from object pool |
| 322 // reg register (unsigned FP relative local) |
| 323 // xeg x-register (signed FP relative local) |
| 324 // tgt jump target relative to the PC of the current instruction |
| 325 // |
| 326 // TODO(vegorov) jump targets should be encoded relative to PC of the next |
| 327 // instruction because PC is incremeted immediately after fetch |
| 328 // and before decoding. |
| 329 // |
| 330 #define BYTECODES_LIST(V) \ |
| 331 V(Trap, 0, ___, ___, ___) \ |
| 332 V(Compile, 0, ___, ___, ___) \ |
| 333 V(Intrinsic, A, num, ___, ___) \ |
| 334 V(Drop1, 0, ___, ___, ___) \ |
| 335 V(DropR, A, num, ___, ___) \ |
| 336 V(Drop, A, num, ___, ___) \ |
| 337 V(Jump, T, tgt, ___, ___) \ |
| 338 V(Return, A, num, ___, ___) \ |
| 339 V(ReturnTOS, 0, ___, ___, ___) \ |
| 340 V(Move, A_X, reg, xeg, ___) \ |
| 341 V(Push, X, xeg, ___, ___) \ |
| 342 V(LoadConstant, A_D, reg, lit, ___) \ |
| 343 V(PushConstant, D, lit, ___, ___) \ |
| 344 V(StoreLocal, X, xeg, ___, ___) \ |
| 345 V(PopLocal, X, xeg, ___, ___) \ |
| 346 V(StaticCall, A_D, num, num, ___) \ |
| 347 V(InstanceCall, A_D, num, num, ___) \ |
| 348 V(InstanceCall2, A_D, num, num, ___) \ |
| 349 V(InstanceCall3, A_D, num, num, ___) \ |
| 350 V(NativeCall, 0, ___, ___, ___) \ |
| 351 V(NativeBootstrapCall, 0, ___, ___, ___) \ |
| 352 V(AddTOS, 0, ___, ___, ___) \ |
| 353 V(SubTOS, 0, ___, ___, ___) \ |
| 354 V(MulTOS, 0, ___, ___, ___) \ |
| 355 V(BitOrTOS, 0, ___, ___, ___) \ |
| 356 V(BitAndTOS, 0, ___, ___, ___) \ |
| 357 V(EqualTOS, 0, ___, ___, ___) \ |
| 358 V(LessThanTOS, 0, ___, ___, ___) \ |
| 359 V(GreaterThanTOS, 0, ___, ___, ___) \ |
| 360 V(StoreStaticTOS, D, lit, ___, ___) \ |
| 361 V(PushStatic, D, lit, ___, ___) \ |
| 362 V(InitStaticTOS, 0, ___, ___, ___) \ |
| 363 V(IfNeStrictTOS, 0, ___, ___, ___) \ |
| 364 V(IfEqStrictTOS, 0, ___, ___, ___) \ |
| 365 V(IfNeStrictNumTOS, 0, ___, ___, ___) \ |
| 366 V(IfEqStrictNumTOS, 0, ___, ___, ___) \ |
| 367 V(CreateArrayTOS, 0, ___, ___, ___) \ |
| 368 V(Allocate, D, lit, ___, ___) \ |
| 369 V(AllocateT, 0, ___, ___, ___) \ |
| 370 V(StoreIndexedTOS, 0, ___, ___, ___) \ |
| 371 V(StoreField, A_B_C, reg, reg, reg) \ |
| 372 V(StoreFieldTOS, D, num, ___, ___) \ |
| 373 V(LoadField, A_B_C, reg, reg, reg) \ |
| 374 V(LoadFieldTOS, D, num, ___, ___) \ |
| 375 V(BooleanNegateTOS, 0, ___, ___, ___) \ |
| 376 V(Throw, A, num, ___, ___) \ |
| 377 V(Entry, A_B_C, num, num, num) \ |
| 378 V(EntryOpt, A_B_C, num, num, num) \ |
| 379 V(Frame, D, num, ___, ___) \ |
| 380 V(SetFrame, A, num, ___, num) \ |
| 381 V(AllocateContext, D, num, ___, ___) \ |
| 382 V(CloneContext, 0, ___, ___, ___) \ |
| 383 V(MoveSpecial, A_D, reg, num, ___) \ |
| 384 V(InstantiateType, D, lit, ___, ___) \ |
| 385 V(InstantiateTypeArgumentsTOS, A_D, num, lit, ___) \ |
| 386 V(AssertAssignable, D, num, lit, ___) \ |
| 387 V(AssertBoolean, A, num, ___, ___) \ |
| 388 V(CheckStack, 0, ___, ___, ___) \ |
| 389 V(DebugStep, 0, ___, ___, ___) \ |
| 390 V(DebugBreak, A, num, ___, ___) \ |
| 391 |
| 392 typedef uint32_t Instr; |
| 393 |
| 394 class Bytecode { |
| 395 public: |
| 396 enum Opcode { |
| 397 #define DECLARE_BYTECODE(name, encoding, op1, op2, op3) k##name, |
| 398 BYTECODES_LIST(DECLARE_BYTECODE) |
| 399 #undef DECLARE_BYTECODE |
| 400 }; |
| 401 |
| 402 static const intptr_t kOpShift = 0; |
| 403 static const intptr_t kAShift = 8; |
| 404 static const intptr_t kAMask = 0xFF; |
| 405 static const intptr_t kBShift = 16; |
| 406 static const intptr_t kBMask = 0xFF; |
| 407 static const intptr_t kCShift = 24; |
| 408 static const intptr_t kCMask = 0xFF; |
| 409 static const intptr_t kDShift = 16; |
| 410 static const intptr_t kDMask = 0xFFFF; |
| 411 |
| 412 static Instr Encode(Opcode op, uintptr_t a, uintptr_t b, uintptr_t c) { |
| 413 ASSERT((a & kAMask) == a); |
| 414 ASSERT((b & kBMask) == b); |
| 415 ASSERT((c & kCMask) == c); |
| 416 return op | (a << kAShift) | (b << kBShift) | (c << kCShift); |
| 417 } |
| 418 |
| 419 static Instr Encode(Opcode op, uintptr_t a, uintptr_t d) { |
| 420 ASSERT((a & kAMask) == a); |
| 421 ASSERT((d & kDMask) == d); |
| 422 return op | (a << kAShift) | (d << kDShift); |
| 423 } |
| 424 |
| 425 static Instr EncodeSigned(Opcode op, uintptr_t a, intptr_t x) { |
| 426 ASSERT((a & kAMask) == a); |
| 427 ASSERT((x << kDShift) >> kDShift == x); |
| 428 return op | (a << kAShift) | (x << kDShift); |
| 429 } |
| 430 |
| 431 static Instr EncodeSigned(Opcode op, intptr_t x) { |
| 432 ASSERT((x << kAShift) >> kAShift == x); |
| 433 return op | (x << kAShift); |
| 434 } |
| 435 |
| 436 static Instr Encode(Opcode op) { |
| 437 return op; |
| 438 } |
| 439 |
| 440 DART_FORCE_INLINE static uint8_t DecodeA(Instr bc) { |
| 441 return (bc >> kAShift) & kAMask; |
| 442 } |
| 443 |
| 444 DART_FORCE_INLINE static uint16_t DecodeD(Instr bc) { |
| 445 return (bc >> kDShift) & kDMask; |
| 446 } |
| 447 |
| 448 DART_FORCE_INLINE static Opcode DecodeOpcode(Instr bc) { |
| 449 return static_cast<Opcode>(bc & 0xFF); |
| 450 } |
| 451 |
| 452 DART_FORCE_INLINE static uint8_t DecodeArgc(Instr call) { |
| 453 #if defined(DEBUG) |
| 454 const Opcode op = DecodeOpcode(call); |
| 455 ASSERT((op == Bytecode::kStaticCall) || |
| 456 (op == Bytecode::kInstanceCall) || |
| 457 (op == Bytecode::kInstanceCall2) || |
| 458 (op == Bytecode::kInstanceCall3) || |
| 459 (op == Bytecode::kDebugBreak)); |
| 460 #endif |
| 461 return (call >> 8) & 0xFF; |
| 462 } |
| 463 }; |
| 464 |
| 465 // Various dummy declarations to make shared code compile. |
| 466 // TODO(vegorov) we need to prune away as much dead code as possible instead |
| 467 // of just making it compile. |
| 468 typedef int16_t Register; |
| 469 |
| 470 const int16_t FPREG = 0; |
| 471 const int16_t SPREG = 1; |
| 472 const intptr_t kNumberOfCpuRegisters = 20; |
| 473 const intptr_t kDartAvailableCpuRegs = 0; |
| 474 const intptr_t kNoRegister = -1; |
| 475 const intptr_t kReservedCpuRegisters = 0; |
| 476 const intptr_t ARGS_DESC_REG = 0; |
| 477 const intptr_t CODE_REG = 0; |
| 478 const intptr_t kExceptionObjectReg = 0; |
| 479 const intptr_t kStackTraceObjectReg = 0; |
| 480 const intptr_t CTX = 0; |
| 481 |
| 482 enum FpuRegister { kNoFpuRegister = -1, kFakeFpuRegister }; |
| 483 const FpuRegister FpuTMP = kFakeFpuRegister; |
| 484 const intptr_t kNumberOfFpuRegisters = 1; |
| 485 |
| 486 enum Condition { EQ, NE }; |
| 487 |
| 488 } // namespace dart |
| 489 |
| 490 #endif // VM_CONSTANTS_DBC_H_ |
OLD | NEW |