js/src/jit/shared/Assembler-shared.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef jit_shared_Assembler_shared_h
michael@0 8 #define jit_shared_Assembler_shared_h
michael@0 9
michael@0 10 #include "mozilla/PodOperations.h"
michael@0 11
michael@0 12 #include <limits.h>
michael@0 13
michael@0 14 #include "jsworkers.h"
michael@0 15
michael@0 16 #include "jit/IonAllocPolicy.h"
michael@0 17 #include "jit/Registers.h"
michael@0 18 #include "jit/RegisterSets.h"
michael@0 19
michael@0 20 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
michael@0 21 // JS_SMALL_BRANCH means the range on a branch instruction
michael@0 22 // is smaller than the whole address space
michael@0 23 # define JS_SMALL_BRANCH
michael@0 24 #endif
michael@0 25 namespace js {
michael@0 26 namespace jit {
michael@0 27
michael@0 28 enum Scale {
michael@0 29 TimesOne = 0,
michael@0 30 TimesTwo = 1,
michael@0 31 TimesFour = 2,
michael@0 32 TimesEight = 3
michael@0 33 };
michael@0 34
michael@0 35 static inline unsigned
michael@0 36 ScaleToShift(Scale scale)
michael@0 37 {
michael@0 38 return unsigned(scale);
michael@0 39 }
michael@0 40
michael@0 41 static inline bool
michael@0 42 IsShiftInScaleRange(int i)
michael@0 43 {
michael@0 44 return i >= TimesOne && i <= TimesEight;
michael@0 45 }
michael@0 46
michael@0 47 static inline Scale
michael@0 48 ShiftToScale(int i)
michael@0 49 {
michael@0 50 JS_ASSERT(IsShiftInScaleRange(i));
michael@0 51 return Scale(i);
michael@0 52 }
michael@0 53
michael@0 54 static inline Scale
michael@0 55 ScaleFromElemWidth(int shift)
michael@0 56 {
michael@0 57 switch (shift) {
michael@0 58 case 1:
michael@0 59 return TimesOne;
michael@0 60 case 2:
michael@0 61 return TimesTwo;
michael@0 62 case 4:
michael@0 63 return TimesFour;
michael@0 64 case 8:
michael@0 65 return TimesEight;
michael@0 66 }
michael@0 67
michael@0 68 MOZ_ASSUME_UNREACHABLE("Invalid scale");
michael@0 69 }
michael@0 70
michael@0 71 // Used for 32-bit immediates which do not require relocation.
michael@0 72 struct Imm32
michael@0 73 {
michael@0 74 int32_t value;
michael@0 75
michael@0 76 explicit Imm32(int32_t value) : value(value)
michael@0 77 { }
michael@0 78
michael@0 79 static inline Imm32 ShiftOf(enum Scale s) {
michael@0 80 switch (s) {
michael@0 81 case TimesOne:
michael@0 82 return Imm32(0);
michael@0 83 case TimesTwo:
michael@0 84 return Imm32(1);
michael@0 85 case TimesFour:
michael@0 86 return Imm32(2);
michael@0 87 case TimesEight:
michael@0 88 return Imm32(3);
michael@0 89 };
michael@0 90 MOZ_ASSUME_UNREACHABLE("Invalid scale");
michael@0 91 }
michael@0 92
michael@0 93 static inline Imm32 FactorOf(enum Scale s) {
michael@0 94 return Imm32(1 << ShiftOf(s).value);
michael@0 95 }
michael@0 96 };
michael@0 97
michael@0 98 // Pointer-sized integer to be embedded as an immediate in an instruction.
michael@0 99 struct ImmWord
michael@0 100 {
michael@0 101 uintptr_t value;
michael@0 102
michael@0 103 explicit ImmWord(uintptr_t value) : value(value)
michael@0 104 { }
michael@0 105 };
michael@0 106
michael@0 107 #ifdef DEBUG
michael@0 108 static inline bool
michael@0 109 IsCompilingAsmJS()
michael@0 110 {
michael@0 111 // asm.js compilation pushes an IonContext with a null JSCompartment.
michael@0 112 IonContext *ictx = MaybeGetIonContext();
michael@0 113 return ictx && ictx->compartment == nullptr;
michael@0 114 }
michael@0 115 #endif
michael@0 116
michael@0 117 // Pointer to be embedded as an immediate in an instruction.
michael@0 118 struct ImmPtr
michael@0 119 {
michael@0 120 void *value;
michael@0 121
michael@0 122 explicit ImmPtr(const void *value) : value(const_cast<void*>(value))
michael@0 123 {
michael@0 124 // To make code serialization-safe, asm.js compilation should only
michael@0 125 // compile pointer immediates using AsmJSImmPtr.
michael@0 126 JS_ASSERT(!IsCompilingAsmJS());
michael@0 127 }
michael@0 128
michael@0 129 template <class R>
michael@0 130 explicit ImmPtr(R (*pf)())
michael@0 131 : value(JS_FUNC_TO_DATA_PTR(void *, pf))
michael@0 132 {
michael@0 133 JS_ASSERT(!IsCompilingAsmJS());
michael@0 134 }
michael@0 135
michael@0 136 template <class R, class A1>
michael@0 137 explicit ImmPtr(R (*pf)(A1))
michael@0 138 : value(JS_FUNC_TO_DATA_PTR(void *, pf))
michael@0 139 {
michael@0 140 JS_ASSERT(!IsCompilingAsmJS());
michael@0 141 }
michael@0 142
michael@0 143 template <class R, class A1, class A2>
michael@0 144 explicit ImmPtr(R (*pf)(A1, A2))
michael@0 145 : value(JS_FUNC_TO_DATA_PTR(void *, pf))
michael@0 146 {
michael@0 147 JS_ASSERT(!IsCompilingAsmJS());
michael@0 148 }
michael@0 149
michael@0 150 template <class R, class A1, class A2, class A3>
michael@0 151 explicit ImmPtr(R (*pf)(A1, A2, A3))
michael@0 152 : value(JS_FUNC_TO_DATA_PTR(void *, pf))
michael@0 153 {
michael@0 154 JS_ASSERT(!IsCompilingAsmJS());
michael@0 155 }
michael@0 156
michael@0 157 template <class R, class A1, class A2, class A3, class A4>
michael@0 158 explicit ImmPtr(R (*pf)(A1, A2, A3, A4))
michael@0 159 : value(JS_FUNC_TO_DATA_PTR(void *, pf))
michael@0 160 {
michael@0 161 JS_ASSERT(!IsCompilingAsmJS());
michael@0 162 }
michael@0 163
michael@0 164 };
michael@0 165
michael@0 166 // The same as ImmPtr except that the intention is to patch this
michael@0 167 // instruction. The initial value of the immediate is 'addr' and this value is
michael@0 168 // either clobbered or used in the patching process.
michael@0 169 struct PatchedImmPtr {
michael@0 170 void *value;
michael@0 171
michael@0 172 explicit PatchedImmPtr()
michael@0 173 : value(nullptr)
michael@0 174 { }
michael@0 175 explicit PatchedImmPtr(const void *value)
michael@0 176 : value(const_cast<void*>(value))
michael@0 177 { }
michael@0 178 };
michael@0 179
michael@0 180 // Used for immediates which require relocation.
michael@0 181 struct ImmGCPtr
michael@0 182 {
michael@0 183 uintptr_t value;
michael@0 184
michael@0 185 explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast<uintptr_t>(ptr))
michael@0 186 {
michael@0 187 JS_ASSERT(!IsPoisonedPtr(ptr));
michael@0 188 JS_ASSERT_IF(ptr, ptr->isTenured());
michael@0 189
michael@0 190 // asm.js shouldn't be creating GC things
michael@0 191 JS_ASSERT(!IsCompilingAsmJS());
michael@0 192 }
michael@0 193
michael@0 194 protected:
michael@0 195 ImmGCPtr() : value(0) {}
michael@0 196 };
michael@0 197
michael@0 198 // Used for immediates which require relocation and may be traced during minor GC.
michael@0 199 struct ImmMaybeNurseryPtr : public ImmGCPtr
michael@0 200 {
michael@0 201 explicit ImmMaybeNurseryPtr(gc::Cell *ptr)
michael@0 202 {
michael@0 203 this->value = reinterpret_cast<uintptr_t>(ptr);
michael@0 204 JS_ASSERT(!IsPoisonedPtr(ptr));
michael@0 205
michael@0 206 // asm.js shouldn't be creating GC things
michael@0 207 JS_ASSERT(!IsCompilingAsmJS());
michael@0 208 }
michael@0 209 };
michael@0 210
michael@0 211 // Pointer to be embedded as an immediate that is loaded/stored from by an
michael@0 212 // instruction.
michael@0 213 struct AbsoluteAddress {
michael@0 214 void *addr;
michael@0 215
michael@0 216 explicit AbsoluteAddress(const void *addr)
michael@0 217 : addr(const_cast<void*>(addr))
michael@0 218 {
michael@0 219 // asm.js shouldn't be creating GC things
michael@0 220 JS_ASSERT(!IsCompilingAsmJS());
michael@0 221 }
michael@0 222
michael@0 223 AbsoluteAddress offset(ptrdiff_t delta) {
michael@0 224 return AbsoluteAddress(((uint8_t *) addr) + delta);
michael@0 225 }
michael@0 226 };
michael@0 227
michael@0 228 // The same as AbsoluteAddress except that the intention is to patch this
michael@0 229 // instruction. The initial value of the immediate is 'addr' and this value is
michael@0 230 // either clobbered or used in the patching process.
michael@0 231 struct PatchedAbsoluteAddress {
michael@0 232 void *addr;
michael@0 233
michael@0 234 explicit PatchedAbsoluteAddress()
michael@0 235 : addr(nullptr)
michael@0 236 { }
michael@0 237 explicit PatchedAbsoluteAddress(const void *addr)
michael@0 238 : addr(const_cast<void*>(addr))
michael@0 239 { }
michael@0 240 };
michael@0 241
michael@0 242 // Specifies an address computed in the form of a register base and a constant,
michael@0 243 // 32-bit offset.
michael@0 244 struct Address
michael@0 245 {
michael@0 246 Register base;
michael@0 247 int32_t offset;
michael@0 248
michael@0 249 Address(Register base, int32_t offset) : base(base), offset(offset)
michael@0 250 { }
michael@0 251
michael@0 252 Address() { mozilla::PodZero(this); }
michael@0 253 };
michael@0 254
michael@0 255 // Specifies an address computed in the form of a register base, a register
michael@0 256 // index with a scale, and a constant, 32-bit offset.
michael@0 257 struct BaseIndex
michael@0 258 {
michael@0 259 Register base;
michael@0 260 Register index;
michael@0 261 Scale scale;
michael@0 262 int32_t offset;
michael@0 263
michael@0 264 BaseIndex(Register base, Register index, Scale scale, int32_t offset = 0)
michael@0 265 : base(base), index(index), scale(scale), offset(offset)
michael@0 266 { }
michael@0 267
michael@0 268 BaseIndex() { mozilla::PodZero(this); }
michael@0 269 };
michael@0 270
michael@0 271 class Relocation {
michael@0 272 public:
michael@0 273 enum Kind {
michael@0 274 // The target is immovable, so patching is only needed if the source
michael@0 275 // buffer is relocated and the reference is relative.
michael@0 276 HARDCODED,
michael@0 277
michael@0 278 // The target is the start of a JitCode buffer, which must be traced
michael@0 279 // during garbage collection. Relocations and patching may be needed.
michael@0 280 JITCODE
michael@0 281 };
michael@0 282 };
michael@0 283
michael@0 284 struct LabelBase
michael@0 285 {
michael@0 286 protected:
michael@0 287 // offset_ >= 0 means that the label is either bound or has incoming
michael@0 288 // uses and needs to be bound.
michael@0 289 int32_t offset_ : 31;
michael@0 290 bool bound_ : 1;
michael@0 291
michael@0 292 // Disallow assignment.
michael@0 293 void operator =(const LabelBase &label);
michael@0 294 public:
michael@0 295 static const int32_t INVALID_OFFSET = -1;
michael@0 296
michael@0 297 LabelBase() : offset_(INVALID_OFFSET), bound_(false)
michael@0 298 { }
michael@0 299 LabelBase(const LabelBase &label)
michael@0 300 : offset_(label.offset_),
michael@0 301 bound_(label.bound_)
michael@0 302 { }
michael@0 303
michael@0 304 // If the label is bound, all incoming edges have been patched and any
michael@0 305 // future incoming edges will be immediately patched.
michael@0 306 bool bound() const {
michael@0 307 return bound_;
michael@0 308 }
michael@0 309 int32_t offset() const {
michael@0 310 JS_ASSERT(bound() || used());
michael@0 311 return offset_;
michael@0 312 }
michael@0 313 // Returns whether the label is not bound, but has incoming uses.
michael@0 314 bool used() const {
michael@0 315 return !bound() && offset_ > INVALID_OFFSET;
michael@0 316 }
michael@0 317 // Binds the label, fixing its final position in the code stream.
michael@0 318 void bind(int32_t offset) {
michael@0 319 JS_ASSERT(!bound());
michael@0 320 offset_ = offset;
michael@0 321 bound_ = true;
michael@0 322 JS_ASSERT(offset_ == offset);
michael@0 323 }
michael@0 324 // Marks the label as neither bound nor used.
michael@0 325 void reset() {
michael@0 326 offset_ = INVALID_OFFSET;
michael@0 327 bound_ = false;
michael@0 328 }
michael@0 329 // Sets the label's latest used position, returning the old use position in
michael@0 330 // the process.
michael@0 331 int32_t use(int32_t offset) {
michael@0 332 JS_ASSERT(!bound());
michael@0 333
michael@0 334 int32_t old = offset_;
michael@0 335 offset_ = offset;
michael@0 336 JS_ASSERT(offset_ == offset);
michael@0 337
michael@0 338 return old;
michael@0 339 }
michael@0 340 };
michael@0 341
michael@0 342 // A label represents a position in an assembly buffer that may or may not have
michael@0 343 // already been generated. Labels can either be "bound" or "unbound", the
michael@0 344 // former meaning that its position is known and the latter that its position
michael@0 345 // is not yet known.
michael@0 346 //
michael@0 347 // A jump to an unbound label adds that jump to the label's incoming queue. A
michael@0 348 // jump to a bound label automatically computes the jump distance. The process
michael@0 349 // of binding a label automatically corrects all incoming jumps.
michael@0 350 class Label : public LabelBase
michael@0 351 {
michael@0 352 public:
michael@0 353 Label()
michael@0 354 { }
michael@0 355 Label(const Label &label) : LabelBase(label)
michael@0 356 { }
michael@0 357 ~Label()
michael@0 358 {
michael@0 359 #ifdef DEBUG
michael@0 360 // The assertion below doesn't hold if an error occurred.
michael@0 361 if (OOM_counter > OOM_maxAllocations)
michael@0 362 return;
michael@0 363 if (MaybeGetIonContext() && GetIonContext()->runtime->hadOutOfMemory())
michael@0 364 return;
michael@0 365
michael@0 366 MOZ_ASSERT(!used());
michael@0 367 #endif
michael@0 368 }
michael@0 369 };
michael@0 370
michael@0 371 // Label's destructor asserts that if it has been used it has also been bound.
michael@0 372 // In the case long-lived labels, however, failed compilation (e.g. OOM) will
michael@0 373 // trigger this failure innocuously. This Label silences the assertion.
michael@0 374 class NonAssertingLabel : public Label
michael@0 375 {
michael@0 376 public:
michael@0 377 ~NonAssertingLabel()
michael@0 378 {
michael@0 379 #ifdef DEBUG
michael@0 380 if (used())
michael@0 381 bind(0);
michael@0 382 #endif
michael@0 383 }
michael@0 384 };
michael@0 385
michael@0 386 class RepatchLabel
michael@0 387 {
michael@0 388 static const int32_t INVALID_OFFSET = 0xC0000000;
michael@0 389 int32_t offset_ : 31;
michael@0 390 uint32_t bound_ : 1;
michael@0 391 public:
michael@0 392
michael@0 393 RepatchLabel() : offset_(INVALID_OFFSET), bound_(0) {}
michael@0 394
michael@0 395 void use(uint32_t newOffset) {
michael@0 396 JS_ASSERT(offset_ == INVALID_OFFSET);
michael@0 397 JS_ASSERT(newOffset != (uint32_t)INVALID_OFFSET);
michael@0 398 offset_ = newOffset;
michael@0 399 }
michael@0 400 bool bound() const {
michael@0 401 return bound_;
michael@0 402 }
michael@0 403 void bind(int32_t dest) {
michael@0 404 JS_ASSERT(!bound_);
michael@0 405 JS_ASSERT(dest != INVALID_OFFSET);
michael@0 406 offset_ = dest;
michael@0 407 bound_ = true;
michael@0 408 }
michael@0 409 int32_t target() {
michael@0 410 JS_ASSERT(bound());
michael@0 411 int32_t ret = offset_;
michael@0 412 offset_ = INVALID_OFFSET;
michael@0 413 return ret;
michael@0 414 }
michael@0 415 int32_t offset() {
michael@0 416 JS_ASSERT(!bound());
michael@0 417 return offset_;
michael@0 418 }
michael@0 419 bool used() const {
michael@0 420 return !bound() && offset_ != (INVALID_OFFSET);
michael@0 421 }
michael@0 422
michael@0 423 };
michael@0 424 // An absolute label is like a Label, except it represents an absolute
michael@0 425 // reference rather than a relative one. Thus, it cannot be patched until after
michael@0 426 // linking.
michael@0 427 struct AbsoluteLabel : public LabelBase
michael@0 428 {
michael@0 429 public:
michael@0 430 AbsoluteLabel()
michael@0 431 { }
michael@0 432 AbsoluteLabel(const AbsoluteLabel &label) : LabelBase(label)
michael@0 433 { }
michael@0 434 int32_t prev() const {
michael@0 435 JS_ASSERT(!bound());
michael@0 436 if (!used())
michael@0 437 return INVALID_OFFSET;
michael@0 438 return offset();
michael@0 439 }
michael@0 440 void setPrev(int32_t offset) {
michael@0 441 use(offset);
michael@0 442 }
michael@0 443 void bind() {
michael@0 444 bound_ = true;
michael@0 445
michael@0 446 // These labels cannot be used after being bound.
michael@0 447 offset_ = -1;
michael@0 448 }
michael@0 449 };
michael@0 450
michael@0 451 // A code label contains an absolute reference to a point in the code
michael@0 452 // Thus, it cannot be patched until after linking
michael@0 453 class CodeLabel
michael@0 454 {
michael@0 455 // The destination position, where the absolute reference should get patched into
michael@0 456 AbsoluteLabel dest_;
michael@0 457
michael@0 458 // The source label (relative) in the code to where the
michael@0 459 // the destination should get patched to.
michael@0 460 Label src_;
michael@0 461
michael@0 462 public:
michael@0 463 CodeLabel()
michael@0 464 { }
michael@0 465 CodeLabel(const AbsoluteLabel &dest)
michael@0 466 : dest_(dest)
michael@0 467 { }
michael@0 468 AbsoluteLabel *dest() {
michael@0 469 return &dest_;
michael@0 470 }
michael@0 471 Label *src() {
michael@0 472 return &src_;
michael@0 473 }
michael@0 474 };
michael@0 475
michael@0 476 // Location of a jump or label in a generated JitCode block, relative to the
michael@0 477 // start of the block.
michael@0 478
michael@0 479 class CodeOffsetJump
michael@0 480 {
michael@0 481 size_t offset_;
michael@0 482
michael@0 483 #ifdef JS_SMALL_BRANCH
michael@0 484 size_t jumpTableIndex_;
michael@0 485 #endif
michael@0 486
michael@0 487 public:
michael@0 488
michael@0 489 #ifdef JS_SMALL_BRANCH
michael@0 490 CodeOffsetJump(size_t offset, size_t jumpTableIndex)
michael@0 491 : offset_(offset), jumpTableIndex_(jumpTableIndex)
michael@0 492 {}
michael@0 493 size_t jumpTableIndex() const {
michael@0 494 return jumpTableIndex_;
michael@0 495 }
michael@0 496 #else
michael@0 497 CodeOffsetJump(size_t offset) : offset_(offset) {}
michael@0 498 #endif
michael@0 499
michael@0 500 CodeOffsetJump() {
michael@0 501 mozilla::PodZero(this);
michael@0 502 }
michael@0 503
michael@0 504 size_t offset() const {
michael@0 505 return offset_;
michael@0 506 }
michael@0 507 void fixup(MacroAssembler *masm);
michael@0 508 };
michael@0 509
michael@0 510 class CodeOffsetLabel
michael@0 511 {
michael@0 512 size_t offset_;
michael@0 513
michael@0 514 public:
michael@0 515 CodeOffsetLabel(size_t offset) : offset_(offset) {}
michael@0 516 CodeOffsetLabel() : offset_(0) {}
michael@0 517
michael@0 518 size_t offset() const {
michael@0 519 return offset_;
michael@0 520 }
michael@0 521 void fixup(MacroAssembler *masm);
michael@0 522
michael@0 523 };
michael@0 524
michael@0 525 // Absolute location of a jump or a label in some generated JitCode block.
michael@0 526 // Can also encode a CodeOffset{Jump,Label}, such that the offset is initially
michael@0 527 // set and the absolute location later filled in after the final JitCode is
michael@0 528 // allocated.
michael@0 529
michael@0 530 class CodeLocationJump
michael@0 531 {
michael@0 532 uint8_t *raw_;
michael@0 533 #ifdef DEBUG
michael@0 534 enum State { Uninitialized, Absolute, Relative };
michael@0 535 State state_;
michael@0 536 void setUninitialized() {
michael@0 537 state_ = Uninitialized;
michael@0 538 }
michael@0 539 void setAbsolute() {
michael@0 540 state_ = Absolute;
michael@0 541 }
michael@0 542 void setRelative() {
michael@0 543 state_ = Relative;
michael@0 544 }
michael@0 545 #else
michael@0 546 void setUninitialized() const {
michael@0 547 }
michael@0 548 void setAbsolute() const {
michael@0 549 }
michael@0 550 void setRelative() const {
michael@0 551 }
michael@0 552 #endif
michael@0 553
michael@0 554 #ifdef JS_SMALL_BRANCH
michael@0 555 uint8_t *jumpTableEntry_;
michael@0 556 #endif
michael@0 557
michael@0 558 public:
michael@0 559 CodeLocationJump() {
michael@0 560 raw_ = nullptr;
michael@0 561 setUninitialized();
michael@0 562 #ifdef JS_SMALL_BRANCH
michael@0 563 jumpTableEntry_ = (uint8_t *) 0xdeadab1e;
michael@0 564 #endif
michael@0 565 }
michael@0 566 CodeLocationJump(JitCode *code, CodeOffsetJump base) {
michael@0 567 *this = base;
michael@0 568 repoint(code);
michael@0 569 }
michael@0 570
michael@0 571 void operator = (CodeOffsetJump base) {
michael@0 572 raw_ = (uint8_t *) base.offset();
michael@0 573 setRelative();
michael@0 574 #ifdef JS_SMALL_BRANCH
michael@0 575 jumpTableEntry_ = (uint8_t *) base.jumpTableIndex();
michael@0 576 #endif
michael@0 577 }
michael@0 578
michael@0 579 void repoint(JitCode *code, MacroAssembler* masm = nullptr);
michael@0 580
michael@0 581 uint8_t *raw() const {
michael@0 582 JS_ASSERT(state_ == Absolute);
michael@0 583 return raw_;
michael@0 584 }
michael@0 585 uint8_t *offset() const {
michael@0 586 JS_ASSERT(state_ == Relative);
michael@0 587 return raw_;
michael@0 588 }
michael@0 589
michael@0 590 #ifdef JS_SMALL_BRANCH
michael@0 591 uint8_t *jumpTableEntry() const {
michael@0 592 JS_ASSERT(state_ == Absolute);
michael@0 593 return jumpTableEntry_;
michael@0 594 }
michael@0 595 #endif
michael@0 596 };
michael@0 597
michael@0 598 class CodeLocationLabel
michael@0 599 {
michael@0 600 uint8_t *raw_;
michael@0 601 #ifdef DEBUG
michael@0 602 enum State { Uninitialized, Absolute, Relative };
michael@0 603 State state_;
michael@0 604 void setUninitialized() {
michael@0 605 state_ = Uninitialized;
michael@0 606 }
michael@0 607 void setAbsolute() {
michael@0 608 state_ = Absolute;
michael@0 609 }
michael@0 610 void setRelative() {
michael@0 611 state_ = Relative;
michael@0 612 }
michael@0 613 #else
michael@0 614 void setUninitialized() const {
michael@0 615 }
michael@0 616 void setAbsolute() const {
michael@0 617 }
michael@0 618 void setRelative() const {
michael@0 619 }
michael@0 620 #endif
michael@0 621
michael@0 622 public:
michael@0 623 CodeLocationLabel() {
michael@0 624 raw_ = nullptr;
michael@0 625 setUninitialized();
michael@0 626 }
michael@0 627 CodeLocationLabel(JitCode *code, CodeOffsetLabel base) {
michael@0 628 *this = base;
michael@0 629 repoint(code);
michael@0 630 }
michael@0 631 CodeLocationLabel(JitCode *code) {
michael@0 632 raw_ = code->raw();
michael@0 633 setAbsolute();
michael@0 634 }
michael@0 635 CodeLocationLabel(uint8_t *raw) {
michael@0 636 raw_ = raw;
michael@0 637 setAbsolute();
michael@0 638 }
michael@0 639
michael@0 640 void operator = (CodeOffsetLabel base) {
michael@0 641 raw_ = (uint8_t *)base.offset();
michael@0 642 setRelative();
michael@0 643 }
michael@0 644 ptrdiff_t operator - (const CodeLocationLabel &other) {
michael@0 645 return raw_ - other.raw_;
michael@0 646 }
michael@0 647
michael@0 648 void repoint(JitCode *code, MacroAssembler *masm = nullptr);
michael@0 649
michael@0 650 #ifdef DEBUG
michael@0 651 bool isSet() const {
michael@0 652 return state_ != Uninitialized;
michael@0 653 }
michael@0 654 #endif
michael@0 655
michael@0 656 uint8_t *raw() const {
michael@0 657 JS_ASSERT(state_ == Absolute);
michael@0 658 return raw_;
michael@0 659 }
michael@0 660 uint8_t *offset() const {
michael@0 661 JS_ASSERT(state_ == Relative);
michael@0 662 return raw_;
michael@0 663 }
michael@0 664 };
michael@0 665
michael@0 666 // Describes the user-visible properties of a callsite.
michael@0 667 //
michael@0 668 // A few general notes about the stack-walking supported by CallSite(Desc):
michael@0 669 // - This information facilitates stack-walking performed by FrameIter which
michael@0 670 // is used by Error.stack and other user-visible stack-walking functions.
michael@0 671 // - Ion/asm.js calling conventions do not maintain a frame-pointer so
michael@0 672 // stack-walking must lookup the stack depth based on the PC.
michael@0 673 // - Stack-walking only occurs from C++ after a synchronous calls (JS-to-JS and
michael@0 674 // JS-to-C++). Thus, we do not need to map arbitrary PCs to stack-depths,
michael@0 675 // just the return address at callsites.
michael@0 676 // - An exception to the above rule is the interrupt callback which can happen
michael@0 677 // at arbitrary PCs. In such cases, we drop frames from the stack-walk. In
michael@0 678 // the future when a full PC->stack-depth map is maintained, we handle this
michael@0 679 // case.
michael@0 680 class CallSiteDesc
michael@0 681 {
michael@0 682 uint32_t line_;
michael@0 683 uint32_t column_;
michael@0 684 uint32_t functionNameIndex_;
michael@0 685
michael@0 686 static const uint32_t sEntryTrampoline = UINT32_MAX;
michael@0 687 static const uint32_t sExit = UINT32_MAX - 1;
michael@0 688
michael@0 689 public:
michael@0 690 static const uint32_t FUNCTION_NAME_INDEX_MAX = UINT32_MAX - 2;
michael@0 691
michael@0 692 CallSiteDesc() {}
michael@0 693
michael@0 694 CallSiteDesc(uint32_t line, uint32_t column, uint32_t functionNameIndex)
michael@0 695 : line_(line), column_(column), functionNameIndex_(functionNameIndex)
michael@0 696 {}
michael@0 697
michael@0 698 static CallSiteDesc Entry() { return CallSiteDesc(0, 0, sEntryTrampoline); }
michael@0 699 static CallSiteDesc Exit() { return CallSiteDesc(0, 0, sExit); }
michael@0 700
michael@0 701 bool isEntry() const { return functionNameIndex_ == sEntryTrampoline; }
michael@0 702 bool isExit() const { return functionNameIndex_ == sExit; }
michael@0 703 bool isNormal() const { return !(isEntry() || isExit()); }
michael@0 704
michael@0 705 uint32_t line() const { JS_ASSERT(isNormal()); return line_; }
michael@0 706 uint32_t column() const { JS_ASSERT(isNormal()); return column_; }
michael@0 707 uint32_t functionNameIndex() const { JS_ASSERT(isNormal()); return functionNameIndex_; }
michael@0 708 };
michael@0 709
michael@0 710 // Adds to CallSiteDesc the metadata necessary to walk the stack given an
michael@0 711 // initial stack-pointer.
michael@0 712 struct CallSite : public CallSiteDesc
michael@0 713 {
michael@0 714 uint32_t returnAddressOffset_;
michael@0 715 uint32_t stackDepth_;
michael@0 716
michael@0 717 public:
michael@0 718 CallSite() {}
michael@0 719
michael@0 720 CallSite(CallSiteDesc desc, uint32_t returnAddressOffset, uint32_t stackDepth)
michael@0 721 : CallSiteDesc(desc),
michael@0 722 returnAddressOffset_(returnAddressOffset),
michael@0 723 stackDepth_(stackDepth)
michael@0 724 { }
michael@0 725
michael@0 726 void setReturnAddressOffset(uint32_t r) { returnAddressOffset_ = r; }
michael@0 727 uint32_t returnAddressOffset() const { return returnAddressOffset_; }
michael@0 728
michael@0 729 // The stackDepth measures the amount of stack space pushed since the
michael@0 730 // function was called. In particular, this includes the word pushed by the
michael@0 731 // call instruction on x86/x64.
michael@0 732 uint32_t stackDepth() const { JS_ASSERT(!isEntry()); return stackDepth_; }
michael@0 733 };
michael@0 734
michael@0 735 typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
michael@0 736
michael@0 737 // Summarizes a heap access made by asm.js code that needs to be patched later
michael@0 738 // and/or looked up by the asm.js signal handlers. Different architectures need
michael@0 739 // to know different things (x64: offset and length, ARM: where to patch in
michael@0 740 // heap length, x86: where to patch in heap length and base) hence the massive
michael@0 741 // #ifdefery.
michael@0 742 class AsmJSHeapAccess
michael@0 743 {
michael@0 744 uint32_t offset_;
michael@0 745 #if defined(JS_CODEGEN_X86)
michael@0 746 uint8_t cmpDelta_; // the number of bytes from the cmp to the load/store instruction
michael@0 747 #endif
michael@0 748 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
michael@0 749 uint8_t opLength_; // the length of the load/store instruction
michael@0 750 uint8_t isFloat32Load_;
michael@0 751 AnyRegister::Code loadedReg_ : 8;
michael@0 752 #endif
michael@0 753
michael@0 754 JS_STATIC_ASSERT(AnyRegister::Total < UINT8_MAX);
michael@0 755
michael@0 756 public:
michael@0 757 AsmJSHeapAccess() {}
michael@0 758 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
michael@0 759 // If 'cmp' equals 'offset' or if it is not supplied then the
michael@0 760 // cmpDelta_ is zero indicating that there is no length to patch.
michael@0 761 AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt,
michael@0 762 AnyRegister loadedReg, uint32_t cmp = UINT32_MAX)
michael@0 763 : offset_(offset),
michael@0 764 # if defined(JS_CODEGEN_X86)
michael@0 765 cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
michael@0 766 # endif
michael@0 767 opLength_(after - offset),
michael@0 768 isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32),
michael@0 769 loadedReg_(loadedReg.code())
michael@0 770 {}
michael@0 771 AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX)
michael@0 772 : offset_(offset),
michael@0 773 # if defined(JS_CODEGEN_X86)
michael@0 774 cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
michael@0 775 # endif
michael@0 776 opLength_(after - offset),
michael@0 777 isFloat32Load_(false),
michael@0 778 loadedReg_(UINT8_MAX)
michael@0 779 {}
michael@0 780 #elif defined(JS_CODEGEN_ARM)
michael@0 781 explicit AsmJSHeapAccess(uint32_t offset)
michael@0 782 : offset_(offset)
michael@0 783 {}
michael@0 784 #endif
michael@0 785
michael@0 786 uint32_t offset() const { return offset_; }
michael@0 787 void setOffset(uint32_t offset) { offset_ = offset; }
michael@0 788 #if defined(JS_CODEGEN_X86)
michael@0 789 bool hasLengthCheck() const { return cmpDelta_ > 0; }
michael@0 790 void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
michael@0 791 void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); }
michael@0 792 #endif
michael@0 793 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
michael@0 794 unsigned opLength() const { return opLength_; }
michael@0 795 bool isLoad() const { return loadedReg_ != UINT8_MAX; }
michael@0 796 bool isFloat32Load() const { return isFloat32Load_; }
michael@0 797 AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); }
michael@0 798 #endif
michael@0 799 };
michael@0 800
michael@0 801 typedef Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> AsmJSHeapAccessVector;
michael@0 802
michael@0 803 struct AsmJSGlobalAccess
michael@0 804 {
michael@0 805 CodeOffsetLabel patchAt;
michael@0 806 unsigned globalDataOffset;
michael@0 807
michael@0 808 AsmJSGlobalAccess(CodeOffsetLabel patchAt, unsigned globalDataOffset)
michael@0 809 : patchAt(patchAt), globalDataOffset(globalDataOffset)
michael@0 810 {}
michael@0 811 };
michael@0 812
michael@0 813 // Describes the intended pointee of an immediate to be embedded in asm.js
michael@0 814 // code. By representing the pointee as a symbolic enum, the pointee can be
michael@0 815 // patched after deserialization when the address of global things has changed.
michael@0 816 enum AsmJSImmKind
michael@0 817 {
michael@0 818 AsmJSImm_Runtime,
michael@0 819 AsmJSImm_StackLimit,
michael@0 820 AsmJSImm_ReportOverRecursed,
michael@0 821 AsmJSImm_HandleExecutionInterrupt,
michael@0 822 AsmJSImm_InvokeFromAsmJS_Ignore,
michael@0 823 AsmJSImm_InvokeFromAsmJS_ToInt32,
michael@0 824 AsmJSImm_InvokeFromAsmJS_ToNumber,
michael@0 825 AsmJSImm_CoerceInPlace_ToInt32,
michael@0 826 AsmJSImm_CoerceInPlace_ToNumber,
michael@0 827 AsmJSImm_ToInt32,
michael@0 828 #if defined(JS_CODEGEN_ARM)
michael@0 829 AsmJSImm_aeabi_idivmod,
michael@0 830 AsmJSImm_aeabi_uidivmod,
michael@0 831 #endif
michael@0 832 AsmJSImm_ModD,
michael@0 833 AsmJSImm_SinD,
michael@0 834 AsmJSImm_CosD,
michael@0 835 AsmJSImm_TanD,
michael@0 836 AsmJSImm_ASinD,
michael@0 837 AsmJSImm_ACosD,
michael@0 838 AsmJSImm_ATanD,
michael@0 839 AsmJSImm_CeilD,
michael@0 840 AsmJSImm_CeilF,
michael@0 841 AsmJSImm_FloorD,
michael@0 842 AsmJSImm_FloorF,
michael@0 843 AsmJSImm_ExpD,
michael@0 844 AsmJSImm_LogD,
michael@0 845 AsmJSImm_PowD,
michael@0 846 AsmJSImm_ATan2D,
michael@0 847 #ifdef DEBUG
michael@0 848 AsmJSImm_AssumeUnreachable,
michael@0 849 #endif
michael@0 850 AsmJSImm_Invalid
michael@0 851 };
michael@0 852
michael@0 853 // Pointer to be embedded as an immediate in asm.js code.
michael@0 854 class AsmJSImmPtr
michael@0 855 {
michael@0 856 AsmJSImmKind kind_;
michael@0 857 public:
michael@0 858 AsmJSImmKind kind() const { return kind_; }
michael@0 859 AsmJSImmPtr(AsmJSImmKind kind) : kind_(kind) { JS_ASSERT(IsCompilingAsmJS()); }
michael@0 860 AsmJSImmPtr() {}
michael@0 861 };
michael@0 862
michael@0 863 // Pointer to be embedded as an immediate that is loaded/stored from by an
michael@0 864 // instruction in asm.js code.
michael@0 865 class AsmJSAbsoluteAddress
michael@0 866 {
michael@0 867 AsmJSImmKind kind_;
michael@0 868 public:
michael@0 869 AsmJSImmKind kind() const { return kind_; }
michael@0 870 AsmJSAbsoluteAddress(AsmJSImmKind kind) : kind_(kind) { JS_ASSERT(IsCompilingAsmJS()); }
michael@0 871 AsmJSAbsoluteAddress() {}
michael@0 872 };
michael@0 873
michael@0 874 // Represents an instruction to be patched and the intended pointee. These
michael@0 875 // links are accumulated in the MacroAssembler, but patching is done outside
michael@0 876 // the MacroAssembler (in AsmJSModule::staticallyLink).
michael@0 877 struct AsmJSAbsoluteLink
michael@0 878 {
michael@0 879 AsmJSAbsoluteLink(CodeOffsetLabel patchAt, AsmJSImmKind target)
michael@0 880 : patchAt(patchAt), target(target) {}
michael@0 881 CodeOffsetLabel patchAt;
michael@0 882 AsmJSImmKind target;
michael@0 883 };
michael@0 884
michael@0 885 // The base class of all Assemblers for all archs.
michael@0 886 class AssemblerShared
michael@0 887 {
michael@0 888 Vector<CallSite, 0, SystemAllocPolicy> callsites_;
michael@0 889 Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> asmJSHeapAccesses_;
michael@0 890 Vector<AsmJSGlobalAccess, 0, SystemAllocPolicy> asmJSGlobalAccesses_;
michael@0 891 Vector<AsmJSAbsoluteLink, 0, SystemAllocPolicy> asmJSAbsoluteLinks_;
michael@0 892
michael@0 893 public:
michael@0 894 bool append(CallSite callsite) { return callsites_.append(callsite); }
michael@0 895 CallSiteVector &&extractCallSites() { return Move(callsites_); }
michael@0 896
michael@0 897 bool append(AsmJSHeapAccess access) { return asmJSHeapAccesses_.append(access); }
michael@0 898 AsmJSHeapAccessVector &&extractAsmJSHeapAccesses() { return Move(asmJSHeapAccesses_); }
michael@0 899
michael@0 900 bool append(AsmJSGlobalAccess access) { return asmJSGlobalAccesses_.append(access); }
michael@0 901 size_t numAsmJSGlobalAccesses() const { return asmJSGlobalAccesses_.length(); }
michael@0 902 AsmJSGlobalAccess asmJSGlobalAccess(size_t i) const { return asmJSGlobalAccesses_[i]; }
michael@0 903
michael@0 904 bool append(AsmJSAbsoluteLink link) { return asmJSAbsoluteLinks_.append(link); }
michael@0 905 size_t numAsmJSAbsoluteLinks() const { return asmJSAbsoluteLinks_.length(); }
michael@0 906 AsmJSAbsoluteLink asmJSAbsoluteLink(size_t i) const { return asmJSAbsoluteLinks_[i]; }
michael@0 907 };
michael@0 908
michael@0 909 } // namespace jit
michael@0 910 } // namespace js
michael@0 911
michael@0 912 #endif /* jit_shared_Assembler_shared_h */

mercurial