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