2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_UTIL_ASM_X64_H_
17 #define incl_HPHP_UTIL_ASM_X64_H_
19 #include <type_traits>
22 #include "hphp/util/atomic.h"
23 #include "hphp/util/data-block.h"
24 #include "hphp/util/immed.h"
25 #include "hphp/util/safe-cast.h"
26 #include "hphp/util/trace.h"
29 * An experimental macro assembler for x64, that strives for low coupling to
30 * the runtime environment.
32 * There are more complete assemblers out there; if you use this one
33 * yourself, expect not to find all the instructions you wanted to use. You
34 * may have to go spelunking in the Intel manuals:
36 * http://www.intel.com/products/processor/manuals/
38 * If you're looking for something more fully baked, here are some options
41 * 1. Nanojit or llvm, both of which translate abstract virtual machine
42 * instructions to the native target architecture, or
43 * 2. The embedded assemblers from v8, the Sun JVM, etc.
47 * Some members cannot be const because their values aren't known in
48 * an initialization list. Like the opposite of the "mutable" keyword.
49 * This declares this property to readers.
51 #define logical_const /* nothing */
53 namespace HPHP
{ namespace jit
{
55 #define TRACEMOD ::HPHP::Trace::asmx64
57 //////////////////////////////////////////////////////////////////////
60 struct RIPRelativeRef
;
62 struct ScaledIndexDisp
;
65 const uint8_t kOpsizePrefix
= 0x66;
67 enum Segment
: uint8_t { DS
= 0, FS
, GS
};
70 explicit constexpr Reg64(int rn
) : rn(rn
) {}
72 // Integer conversion is allowed but only explicitly. (It's not
73 // unusual to want to printf registers, etc. Just cast it first.)
74 explicit constexpr operator int() const { return rn
; }
76 MemoryRef
operator[](intptr_t disp
) const;
77 MemoryRef
operator[](Reg64
) const;
78 MemoryRef
operator[](ScaledIndex
) const;
79 MemoryRef
operator[](ScaledIndexDisp
) const;
80 MemoryRef
operator[](DispReg
) const;
82 constexpr bool operator==(Reg64 o
) const { return rn
== o
.rn
; }
83 constexpr bool operator!=(Reg64 o
) const { return rn
!= o
.rn
; }
89 #define SIMPLE_REGTYPE(What) \
91 explicit constexpr What(int rn) : rn(rn) {} \
92 explicit constexpr operator int() const { return rn; } \
93 constexpr bool operator==(What o) const { return rn == o.rn; } \
94 constexpr bool operator!=(What o) const { return rn != o.rn; } \
99 SIMPLE_REGTYPE(Reg32
);
100 SIMPLE_REGTYPE(Reg16
);
101 SIMPLE_REGTYPE(Reg8
);
102 SIMPLE_REGTYPE(RegXMM
);
103 SIMPLE_REGTYPE(RegSF
);
105 #undef SIMPLE_REGTYPE
108 RIPRelativeRef
operator[](intptr_t disp
) const;
111 // Convert between physical registers of different sizes
112 inline Reg8
rbyte(Reg32 r
) { return Reg8(int(r
)); }
113 inline Reg8
rbyte(Reg64 r
) { return Reg8(int(r
)); }
114 inline Reg16
r16(Reg8 r
) { return Reg16(int(r
)); }
115 inline Reg32
r32(Reg8 r
) { return Reg32(int(r
)); }
116 inline Reg32
r32(Reg16 r
) { return Reg32(int(r
)); }
117 inline Reg32
r32(Reg32 r
) { return r
; }
118 inline Reg32
r32(Reg64 r
) { return Reg32(int(r
)); }
119 inline Reg64
r64(Reg8 r
) { return Reg64(int(r
)); }
120 inline Reg64
r64(Reg16 r
) { return Reg64(int(r
)); }
121 inline Reg64
r64(Reg32 r
) { return Reg64(int(r
)); }
122 inline Reg64
r64(Reg64 r
) { return r
; }
124 //////////////////////////////////////////////////////////////////////
127 * The following structures define intermediate types for various
128 * addressing modes. They overload some operators to allow using
129 * registers to look somewhat like pointers.
131 * E.g. rax[rbx*2 + 3] or *(rax + rbx*2 + 3).
133 * These operators are not defined commutatively; the thought is it
134 * mandates the order you normally write them in a .S, but it could be
135 * changed if this proves undesirable.
140 explicit ScaledIndex(Reg64 index
, intptr_t scale
)
144 assert((scale
== 0x1 || scale
== 0x2 || scale
== 0x4 || scale
== 0x8) &&
145 "Invalid index register scaling (must be 1,2,4 or 8).");
146 assert(int(index
) != -1 && "invalid register");
154 struct ScaledIndexDisp
{
155 explicit ScaledIndexDisp(ScaledIndex si
, intptr_t disp
)
160 ScaledIndexDisp
operator+(intptr_t x
) const {
161 return ScaledIndexDisp(si
, disp
+ x
);
164 ScaledIndexDisp
operator-(intptr_t x
) const {
165 return ScaledIndexDisp(si
, disp
- x
);
174 explicit DispReg(Reg64 base
, intptr_t disp
= 0)
178 assert(int(base
) != -1 && "invalid register");
181 // Constructor for baseless().
182 explicit DispReg(intptr_t disp
)
187 MemoryRef
operator*() const;
188 MemoryRef
operator[](intptr_t) const;
190 DispReg
operator+(intptr_t x
) const {
191 return DispReg(base
, disp
+ x
);
194 DispReg
operator-(intptr_t x
) const {
195 return DispReg(base
, disp
- x
);
203 struct IndexedDispReg
{
204 explicit IndexedDispReg(Reg64 base
, ScaledIndex sr
)
211 explicit IndexedDispReg(DispReg r
)
218 // Constructor for baseless()
219 explicit IndexedDispReg(ScaledIndexDisp sid
)
221 , index(sid
.si
.index
)
222 , scale(sid
.si
.scale
)
226 MemoryRef
operator*() const;
227 MemoryRef
operator[](intptr_t disp
) const;
229 IndexedDispReg
operator+(intptr_t disp
) const {
235 IndexedDispReg
operator-(intptr_t disp
) const {
244 intptr_t disp
; // TODO #4613274: should be int32_t
249 explicit constexpr DispRIP(intptr_t disp
) : disp(disp
) {}
251 RIPRelativeRef
operator*() const;
252 RIPRelativeRef
operator[](intptr_t x
) const;
254 DispRIP
operator+(intptr_t x
) const {
255 return DispRIP(disp
+ x
);
258 DispRIP
operator-(intptr_t x
) const {
259 return DispRIP(disp
- x
);
262 bool operator==(DispRIP o
) const { return disp
== o
.disp
; }
263 bool operator!=(DispRIP o
) const { return disp
!= o
.disp
; }
269 struct RIPRelativeRef
{
270 explicit constexpr RIPRelativeRef(DispRIP r
) : r(r
) {}
273 bool operator==(const RIPRelativeRef
& o
) const { return r
== o
.r
; }
274 bool operator!=(const RIPRelativeRef
& o
) const { return r
!= o
.r
; }
277 //////////////////////////////////////////////////////////////////////
280 constexpr Reg64
rax(0);
281 constexpr Reg64
rcx(1);
282 constexpr Reg64
rdx(2);
283 constexpr Reg64
rbx(3);
284 constexpr Reg64
rsp(4);
285 constexpr Reg64
rbp(5);
286 constexpr Reg64
rsi(6);
287 constexpr Reg64
rdi(7);
289 constexpr Reg64
r8 (8);
290 constexpr Reg64
r9 (9);
291 constexpr Reg64
r10(10);
292 constexpr Reg64
r11(11);
293 constexpr Reg64
r12(12);
294 constexpr Reg64
r13(13);
295 constexpr Reg64
r14(14);
296 constexpr Reg64
r15(15);
298 constexpr RegRIP rip
= RegRIP();
300 constexpr Reg32
eax (0);
301 constexpr Reg32
ecx (1);
302 constexpr Reg32
edx (2);
303 constexpr Reg32
ebx (3);
304 constexpr Reg32
esp (4);
305 constexpr Reg32
ebp (5);
306 constexpr Reg32
esi (6);
307 constexpr Reg32
edi (7);
308 constexpr Reg32
r8d (8);
309 constexpr Reg32
r9d (9);
310 constexpr Reg32
r10d(10);
311 constexpr Reg32
r11d(11);
312 constexpr Reg32
r12d(12);
313 constexpr Reg32
r13d(13);
314 constexpr Reg32
r14d(14);
315 constexpr Reg32
r15d(15);
317 constexpr Reg16
ax (0);
318 constexpr Reg16
cx (1);
319 constexpr Reg16
dx (2);
320 constexpr Reg16
bx (3);
321 constexpr Reg16
sp (4);
322 constexpr Reg16
bp (5);
323 constexpr Reg16
si (6);
324 constexpr Reg16
di (7);
325 constexpr Reg16
r8w (8);
326 constexpr Reg16
r9w (9);
327 constexpr Reg16
r10w(10);
328 constexpr Reg16
r11w(11);
329 constexpr Reg16
r12w(12);
330 constexpr Reg16
r13w(13);
331 constexpr Reg16
r14w(14);
332 constexpr Reg16
r15w(15);
334 constexpr Reg8
al (0);
335 constexpr Reg8
cl (1);
336 constexpr Reg8
dl (2);
337 constexpr Reg8
bl (3);
338 constexpr Reg8
spl (4);
339 constexpr Reg8
bpl (5);
340 constexpr Reg8
sil (6);
341 constexpr Reg8
dil (7);
342 constexpr Reg8
r8b (8);
343 constexpr Reg8
r9b (9);
344 constexpr Reg8
r10b(10);
345 constexpr Reg8
r11b(11);
346 constexpr Reg8
r12b(12);
347 constexpr Reg8
r13b(13);
348 constexpr Reg8
r14b(14);
349 constexpr Reg8
r15b(15);
351 // Reminder: these registers may not be mixed in any instruction
352 // using a REX prefix (i.e. anything using r8-r15, spl, bpl, sil,
354 constexpr Reg8
ah(0x80 | 4);
355 constexpr Reg8
ch(0x80 | 5);
356 constexpr Reg8
dh(0x80 | 6);
357 constexpr Reg8
bh(0x80 | 7);
359 constexpr RegXMM
xmm0(0);
360 constexpr RegXMM
xmm1(1);
361 constexpr RegXMM
xmm2(2);
362 constexpr RegXMM
xmm3(3);
363 constexpr RegXMM
xmm4(4);
364 constexpr RegXMM
xmm5(5);
365 constexpr RegXMM
xmm6(6);
366 constexpr RegXMM
xmm7(7);
367 constexpr RegXMM
xmm8(8);
368 constexpr RegXMM
xmm9(9);
369 constexpr RegXMM
xmm10(10);
370 constexpr RegXMM
xmm11(11);
371 constexpr RegXMM
xmm12(12);
372 constexpr RegXMM
xmm13(13);
373 constexpr RegXMM
xmm14(14);
374 constexpr RegXMM
xmm15(15);
376 #define X(x) if (r == x) return "%"#x
377 inline const char* regname(Reg64 r
) {
378 X(rax
); X(rbx
); X(rcx
); X(rdx
); X(rsp
); X(rbp
); X(rsi
); X(rdi
);
379 X(r8
); X(r9
); X(r10
); X(r11
); X(r12
); X(r13
); X(r14
); X(r15
);
382 inline const char* regname(Reg32 r
) {
383 X(eax
); X(ecx
); X(edx
); X(ebx
); X(esp
); X(ebp
); X(esi
); X(edi
);
384 X(r8d
); X(r9d
); X(r10d
); X(r11d
); X(r12d
); X(r13d
); X(r14d
); X(r15d
);
387 inline const char* regname(Reg16 r
) {
388 X(ax
); X(cx
); X(dx
); X(bx
); X(sp
); X(bp
); X(si
); X(di
);
389 X(r8w
); X(r9w
); X(r10w
); X(r11w
); X(r12w
); X(r13w
); X(r14w
); X(r15w
);
392 inline const char* regname(Reg8 r
) {
393 X(al
); X(cl
); X(dl
); X(bl
); X(spl
); X(bpl
); X(sil
); X(dil
);
394 X(r8b
); X(r9b
); X(r10b
); X(r11b
); X(r12b
); X(r13b
); X(r14b
); X(r15b
);
395 X(ah
); X(ch
); X(dh
); X(bh
);
398 inline const char* regname(RegXMM r
) {
399 X(xmm0
); X(xmm1
); X(xmm2
); X(xmm3
); X(xmm4
); X(xmm5
); X(xmm6
);
400 X(xmm7
); X(xmm8
); X(xmm9
); X(xmm10
); X(xmm11
); X(xmm12
); X(xmm13
);
404 inline const char* regname(RegSF
/*r*/) {
411 enum class RoundDirection
: ssize_t
{
418 const char* show(RoundDirection
);
420 enum class ComparisonPred
: uint8_t {
422 eq_ord
= 0, // ...operands are ordered AND equal
423 ne_unord
= 4, // ...operands are unordered OR unequal
464 // names of condition codes, indexable by the ConditionCode enum value.
465 extern const char* cc_names
[];
467 inline ConditionCode
ccNegate(ConditionCode c
) {
468 return ConditionCode(int(c
) ^ 1); // And you thought x86 was irregular!
473 explicit MemoryRef(DispReg dr
, Segment s
= DS
) : r(dr
), segment(s
) {}
474 explicit MemoryRef(IndexedDispReg idr
, Segment s
= DS
) : r(idr
), segment(s
) {}
480 * Simple wrapper over a Segment value used to obtain MemoryRefs that have
481 * MemoryRef::segment set to something different than the default (DS) value.
484 explicit constexpr SegReg(Segment seg
) : seg(seg
) {};
485 MemoryRef
operator[](const IndexedDispReg
& idr
) {
486 return MemoryRef(idr
, seg
);
488 MemoryRef
operator[](const ScaledIndexDisp
& sid
) {
489 return MemoryRef(IndexedDispReg(sid
), seg
);
491 MemoryRef
operator[](const DispReg
& dr
) {
492 return MemoryRef(dr
, seg
);
494 MemoryRef
operator[](const intptr_t disp
) {
495 return MemoryRef(DispReg(disp
), seg
);
501 #define NEW_X64_ASM(var, cb) \
502 std::unique_ptr<X64AssemblerBase> _assembler( \
503 RuntimeOption::EvalUseXedAssembler ? \
504 (X64AssemblerBase*)new XedAssembler(cb) : \
505 (X64AssemblerBase*)new X64Assembler(cb)); \
506 X64AssemblerBase& var = *_assembler
508 #define NEW_X64_ASM(var, cb) X64Assembler var(cb)
513 struct X64AssemblerBase
{
518 * Type for register numbers, independent of the size we're going to
519 * be using it as. Also, the same register number may mean different
520 * physical registers for different instructions (e.g. xmm0 and rax
521 * are both 0). Only for internal use in X64Assembler.
523 enum class RegNumber
: int {};
524 static const RegNumber noreg
= RegNumber(-1);
527 explicit X64AssemblerBase(CodeBlock
& cb
) : codeBlock(cb
) {}
528 virtual ~X64AssemblerBase() {}
530 CodeBlock
& code() const { return codeBlock
; }
532 CodeAddress
base() const {
533 return codeBlock
.base();
536 CodeAddress
frontier() const {
537 return codeBlock
.frontier();
540 CodeAddress
toDestAddress(CodeAddress addr
) const {
541 return codeBlock
.toDestAddress(addr
);
544 void setFrontier(CodeAddress newFrontier
) {
545 codeBlock
.setFrontier(newFrontier
);
548 size_t capacity() const {
549 return codeBlock
.capacity();
552 size_t used() const {
553 return codeBlock
.used();
556 size_t available() const {
557 return codeBlock
.available();
560 bool contains(CodeAddress addr
) const {
561 return codeBlock
.contains(addr
);
565 return codeBlock
.empty();
572 bool canEmit(size_t nBytes
) const {
573 assert(capacity() >= used());
574 return nBytes
< (capacity() - used());
577 #define BYTE_LOAD_OP(name) \
578 virtual void name##b(MemoryRef m, Reg8 r) = 0;
580 #define LOAD_OP(name) \
581 virtual void name##q(MemoryRef m, Reg64 r) = 0; \
582 virtual void name##l(MemoryRef m, Reg32 r) = 0; \
583 virtual void name##w(MemoryRef m, Reg16 r) = 0; \
584 virtual void name##q(RIPRelativeRef m, Reg64 r) = 0; \
587 #define BYTE_STORE_OP(name) \
588 virtual void name##b(Reg8 r, MemoryRef m) = 0; \
589 virtual void name##b(Immed i, MemoryRef m) = 0;
591 #define STORE_OP(name) \
592 virtual void name##w(Immed i, MemoryRef m) = 0; \
593 virtual void name##l(Immed i, MemoryRef m) = 0; \
594 virtual void name##w(Reg16 r, MemoryRef m) = 0; \
595 virtual void name##l(Reg32 r, MemoryRef m) = 0; \
596 virtual void name##q(Reg64 r, MemoryRef m) = 0; \
599 #define BYTE_REG_OP(name) \
600 virtual void name##b(Reg8 r1, Reg8 r2) = 0; \
601 virtual void name##b(Immed i, Reg8 r) = 0;
603 #define REG_OP(name) \
604 virtual void name##q(Reg64 r1, Reg64 r2) = 0; \
605 virtual void name##l(Reg32 r1, Reg32 r2) = 0; \
606 virtual void name##w(Reg16 r1, Reg16 r2) = 0; \
607 virtual void name##l(Immed i, Reg32 r) = 0; \
608 virtual void name##w(Immed i, Reg16 r) = 0; \
611 #define IMM64_STORE_OP(name) \
612 virtual void name##q(Immed i, MemoryRef m) = 0;
614 #define IMM64R_OP(name) \
615 virtual void name##q(Immed imm, Reg64 r) = 0;
617 #define FULL_OP(name) \
621 IMM64_STORE_OP(name) \
626 IMM64_STORE_OP (store
)
646 #undef IMM64_STORE_OP
648 virtual void movq(Immed64 imm
, Reg64 r
) = 0;
649 virtual void loadzbl(MemoryRef m
, Reg32 r
) = 0;
650 virtual void movzbl(Reg8 src
, Reg32 dest
) = 0;
651 virtual void movsbl(Reg8 src
, Reg32 dest
) = 0;
652 virtual void movzwl(Reg16 src
, Reg32 dest
) = 0;
653 virtual void loadsbq(MemoryRef m
, Reg64 r
) = 0;
654 virtual void movsbq(Reg8 src
, Reg64 dest
) = 0;
655 virtual void lea(MemoryRef p
, Reg64 reg
) = 0;
656 virtual void lea(RIPRelativeRef p
, Reg64 reg
) = 0;
658 virtual void xchgq(Reg64 r1
, Reg64 r2
) = 0;
659 virtual void xchgl(Reg32 r1
, Reg32 r2
) = 0;
660 virtual void xchgb(Reg8 r1
, Reg8 r2
) = 0;
662 virtual void imul(Reg64 r1
, Reg64 r2
) = 0;
664 virtual void push(Reg64 r
) = 0;
665 virtual void pushl(Reg32 r
) = 0;
666 virtual void pop (Reg64 r
) = 0;
667 virtual void idiv(Reg64 r
) = 0;
668 virtual void incq(Reg64 r
) = 0;
669 virtual void incl(Reg32 r
) = 0;
670 virtual void incw(Reg16 r
) = 0;
671 virtual void decq(Reg64 r
) = 0;
672 virtual void decl(Reg32 r
) = 0;
673 virtual void decw(Reg16 r
) = 0;
674 virtual void notb(Reg8 r
) = 0;
675 virtual void not(Reg64 r
) = 0;
676 virtual void neg(Reg64 r
) = 0;
677 virtual void negb(Reg8 r
) = 0;
678 virtual void ret() = 0;
679 virtual void ret(Immed i
) = 0;
680 virtual void cqo() = 0;
681 virtual void nop() = 0;
682 virtual void int3() = 0;
683 virtual void ud2() = 0;
684 virtual void pushf() = 0;
685 virtual void popf() = 0;
686 virtual void lock() = 0;
688 virtual void push(MemoryRef m
) = 0;
689 virtual void pop (MemoryRef m
) = 0;
690 virtual void incq(MemoryRef m
) = 0;
691 virtual void incl(MemoryRef m
) = 0;
692 virtual void incw(MemoryRef m
) = 0;
693 virtual void decqlock(MemoryRef m
) = 0;
694 virtual void decq(MemoryRef m
) = 0;
695 virtual void decl(MemoryRef m
) = 0;
696 virtual void decw(MemoryRef m
) = 0;
698 virtual void push(Immed64 i
) = 0;
700 virtual void movups(RegXMM x
, MemoryRef m
) = 0;
701 virtual void movups(MemoryRef m
, RegXMM x
) = 0;
702 virtual void movdqu(RegXMM x
, MemoryRef m
) = 0;
703 virtual void movdqu(MemoryRef m
, RegXMM x
) = 0;
704 virtual void movdqa(RegXMM x
, RegXMM y
) = 0;
705 virtual void movdqa(RegXMM x
, MemoryRef m
) = 0;
706 virtual void movdqa(MemoryRef m
, RegXMM x
) = 0;
707 virtual void movsd (RegXMM x
, RegXMM y
) = 0;
708 virtual void movsd (RegXMM x
, MemoryRef m
) = 0;
709 virtual void movsd (MemoryRef m
, RegXMM x
) = 0;
710 virtual void movsd (RIPRelativeRef m
, RegXMM x
) = 0;
711 virtual void lddqu (MemoryRef m
, RegXMM x
) = 0;
712 virtual void unpcklpd(RegXMM s
, RegXMM d
) = 0;
714 virtual void rorq (Immed i
, Reg64 r
) = 0;
715 virtual void shlq (Immed i
, Reg64 r
) = 0;
716 virtual void shrq (Immed i
, Reg64 r
) = 0;
717 virtual void sarq (Immed i
, Reg64 r
) = 0;
718 virtual void shll (Immed i
, Reg32 r
) = 0;
719 virtual void shrl (Immed i
, Reg32 r
) = 0;
720 virtual void shlw (Immed i
, Reg16 r
) = 0;
721 virtual void shrw (Immed i
, Reg16 r
) = 0;
723 virtual void shlq (Reg64 r
) = 0;
724 virtual void shrq (Reg64 r
) = 0;
725 virtual void sarq (Reg64 r
) = 0;
727 virtual void roundsd (RoundDirection d
, RegXMM src
, RegXMM dst
) = 0;
728 virtual void cmpsd(RegXMM src
, RegXMM dst
, ComparisonPred pred
) = 0;
731 * The following utility functions do more than emit specific code.
732 * (E.g. combine common idioms or patterns, smash code, etc.)
735 void emitImmReg(Immed64 imm
, Reg64 dest
) {
737 // Zeros the top bits also.
738 xorl (r32(dest
), r32(dest
));
741 if (LIKELY(imm
.q() > 0 && imm
.fits(sz::dword
))) {
742 // This will zero out the high-order bits.
743 movl (imm
.l(), r32(dest
));
746 movq (imm
.q(), dest
);
749 bool jmpDeltaFits(CodeAddress dest
) {
750 int64_t delta
= dest
- (codeBlock
.frontier() + 5);
751 return deltaFits(delta
, sz::dword
);
754 virtual void jmp(Reg64 r
) = 0;
755 virtual void jmp(MemoryRef m
) = 0;
756 virtual void jmp(RIPRelativeRef m
) = 0;
757 virtual void call(Reg64 r
) = 0;
758 virtual void call(MemoryRef m
) = 0;
759 virtual void call(RIPRelativeRef m
) = 0;
760 virtual void jmp8(CodeAddress dest
) = 0;
761 virtual void jmp(CodeAddress dest
) = 0;
762 virtual void call(CodeAddress dest
) = 0;
763 virtual void jcc(ConditionCode cond
, CodeAddress dest
) = 0;
764 virtual void jcc8(ConditionCode cond
, CodeAddress dest
)= 0;
766 void jmpAuto(CodeAddress dest
) {
767 auto delta
= dest
- (codeBlock
.frontier() + 2);
768 if (deltaFits(delta
, sz::byte
)) {
775 void jccAuto(ConditionCode cc
, CodeAddress dest
) {
776 auto delta
= dest
- (codeBlock
.frontier() + 2);
777 if (deltaFits(delta
, sz::byte
)) {
787 void jcc(ConditionCode
, Label
&);
788 void jcc8(ConditionCode
, Label
&);
816 #define CC(_nm, _code) \
817 void j ## _nm(CodeAddress dest) { jcc(_code, dest); } \
818 void j ## _nm ## 8(CodeAddress dest) { jcc8(_code, dest); } \
819 void j ## _nm(Label&); \
820 void j ## _nm ## 8(Label&);
824 #define CC(_nm, _cond) \
825 void set ## _nm(Reg8 byteReg) { \
826 setcc(_cond, byteReg); \
831 virtual void setcc(int cc
, Reg8 byteReg
) = 0;
832 virtual void psllq(Immed i
, RegXMM r
) = 0;
833 virtual void psrlq(Immed i
, RegXMM r
) = 0;
834 virtual void movq_rx(Reg64 rSrc
, RegXMM rdest
) = 0;
835 virtual void movq_xr(RegXMM rSrc
, Reg64 rdest
) = 0;
836 virtual void addsd(RegXMM src
, RegXMM srcdest
) = 0;
837 virtual void mulsd(RegXMM src
, RegXMM srcdest
) = 0;
838 virtual void subsd(RegXMM src
, RegXMM srcdest
) = 0;
839 virtual void pxor(RegXMM src
, RegXMM srcdest
) = 0;
840 virtual void cvtsi2sd(Reg64 src
, RegXMM dest
) = 0;
841 virtual void cvtsi2sd(MemoryRef m
, RegXMM dest
) = 0;
842 virtual void ucomisd(RegXMM l
, RegXMM r
) = 0;
843 virtual void sqrtsd(RegXMM src
, RegXMM dest
) = 0;
844 virtual void divsd(RegXMM src
, RegXMM srcdest
) = 0;
845 virtual void cvttsd2siq(RegXMM src
, Reg64 dest
) = 0;
847 static void patchJcc(CodeAddress jmp
, CodeAddress from
, CodeAddress dest
) {
848 assert(jmp
[0] == 0x0F && (jmp
[1] & 0xF0) == 0x80);
849 ssize_t diff
= dest
- (from
+ 6);
850 *(int32_t*)(jmp
+ 2) = safe_cast
<int32_t>(diff
);
853 static void patchJcc8(CodeAddress jmp
, CodeAddress from
, CodeAddress dest
) {
854 assert((jmp
[0] & 0xF0) == 0x70);
855 ssize_t diff
= dest
- (from
+ 2); // one for opcode, one for offset
856 *(int8_t*)(jmp
+ 1) = safe_cast
<int8_t>(diff
);
859 static void patchJmp(CodeAddress jmp
, CodeAddress from
, CodeAddress dest
) {
860 assert(jmp
[0] == 0xE9);
861 ssize_t diff
= dest
- (from
+ 5);
862 *(int32_t*)(jmp
+ 1) = safe_cast
<int32_t>(diff
);
865 static void patchJmp8(CodeAddress jmp
, CodeAddress from
, CodeAddress dest
) {
866 assert(jmp
[0] == 0xEB);
867 ssize_t diff
= dest
- (from
+ 2); // one for opcode, one for offset
868 *(int8_t*)(jmp
+ 1) = safe_cast
<int8_t>(diff
);
871 static void patchCall(CodeAddress call
, CodeAddress from
, CodeAddress dest
) {
872 assert(call
[0] == 0xE8);
873 ssize_t diff
= dest
- (from
+ 5);
874 *(int32_t*)(call
+ 1) = safe_cast
<int32_t>(diff
);
877 virtual void emitInt3s(int n
) = 0;
878 virtual void emitNop(int n
) = 0;
879 virtual void pad() = 0;
881 void byte(uint8_t b
) {
884 void word(uint16_t w
) {
887 void dword(uint32_t dw
) {
890 void qword(uint64_t qw
) {
893 void bytes(size_t n
, const uint8_t* bs
) {
894 codeBlock
.bytes(n
, bs
);
897 virtual void cload_reg64_disp_reg64(ConditionCode cc
, Reg64 rbase
,
898 int off
, Reg64 rdest
) = 0;
899 virtual void cload_reg64_disp_reg32(ConditionCode cc
, Reg64 rbase
,
900 int off
, Reg32 rdest
) = 0;
901 virtual void cmov_reg64_reg64(ConditionCode cc
, Reg64 rsrc
,
905 RegNumber
rn(Reg8 r
) { return RegNumber(int(r
)); }
906 RegNumber
rn(Reg16 r
) { return RegNumber(int(r
)); }
907 RegNumber
rn(Reg32 r
) { return RegNumber(int(r
)); }
908 RegNumber
rn(Reg64 r
) { return RegNumber(int(r
)); }
909 RegNumber
rn(RegXMM r
) { return RegNumber(int(r
)); }
911 CodeBlock
& codeBlock
;
916 #include "hphp/util/asm-x64-intelxed.h"
919 #include "hphp/util/asm-x64-legacy.h"
921 namespace HPHP
{ namespace jit
{
922 inline MemoryRef
IndexedDispReg::operator*() const {
923 return MemoryRef(*this);
926 inline MemoryRef
IndexedDispReg::operator[](intptr_t x
) const {
930 inline MemoryRef
DispReg::operator*() const {
931 return MemoryRef(*this);
934 inline MemoryRef
DispReg::operator[](intptr_t x
) const {
938 inline RIPRelativeRef
DispRIP::operator*() const {
939 return RIPRelativeRef(*this);
942 inline RIPRelativeRef
DispRIP::operator[](intptr_t x
) const {
946 inline DispReg
operator+(Reg64 r
, intptr_t d
) { return DispReg(r
, d
); }
947 inline DispReg
operator-(Reg64 r
, intptr_t d
) { return DispReg(r
, -d
); }
948 inline DispRIP
operator+(RegRIP
/*r*/, intptr_t d
) {
951 inline DispRIP
operator-(RegRIP
/*r*/, intptr_t d
) {
955 inline ScaledIndex
operator*(Reg64 r
, int scale
) {
956 return ScaledIndex(r
, scale
);
958 inline IndexedDispReg
operator+(Reg64 base
, ScaledIndex sr
) {
959 return IndexedDispReg(base
, sr
);
961 inline ScaledIndexDisp
operator+(ScaledIndex si
, intptr_t disp
) {
962 return ScaledIndexDisp(si
, disp
);
964 inline IndexedDispReg
operator+(Reg64 b
, Reg64 i
) {
965 return b
+ ScaledIndex(i
, 0x1);
968 inline MemoryRef
operator*(Reg64 r
) { return MemoryRef(DispReg(r
)); }
969 inline DispRIP
operator*(RegRIP
/*r*/) {
973 inline MemoryRef
Reg64::operator[](intptr_t disp
) const {
974 return *(*this + disp
);
977 inline MemoryRef
Reg64::operator[](Reg64 idx
) const {
978 return *(*this + idx
* 1);
981 inline MemoryRef
Reg64::operator[](ScaledIndex si
) const {
982 return *(*this + si
);
985 inline MemoryRef
Reg64::operator[](DispReg dr
) const {
986 return *(*this + ScaledIndex(dr
.base
, 0x1) + dr
.disp
);
989 inline MemoryRef
Reg64::operator[](ScaledIndexDisp sid
) const {
990 return *(*this + sid
.si
+ sid
.disp
);
993 inline RIPRelativeRef
RegRIP::operator[](intptr_t disp
) const {
994 return *(*this + disp
);
998 * Used for the x64 addressing mode where there is a displacement,
999 * possibly with a scaled index, but no base register.
1001 inline MemoryRef
baseless(intptr_t disp
) { return *(DispReg
{ disp
}); }
1002 inline MemoryRef
baseless(ScaledIndexDisp sid
) {
1003 return *(IndexedDispReg
{ sid
});
1006 //////////////////////////////////////////////////////////////////////
1011 , m_address(nullptr)
1015 if (!m_toPatch
.empty()) {
1016 assert(m_a
&& m_address
&& "Label had jumps but was never set");
1018 for (auto& ji
: m_toPatch
) {
1019 auto realSrc
= ji
.a
->toDestAddress(ji
.addr
);
1021 case Branch::Jmp
: ji
.a
->patchJmp(realSrc
, ji
.addr
, m_address
); break;
1022 case Branch::Jmp8
: ji
.a
->patchJmp8(realSrc
, ji
.addr
, m_address
); break;
1023 case Branch::Jcc
: ji
.a
->patchJcc(realSrc
, ji
.addr
, m_address
); break;
1024 case Branch::Jcc8
: ji
.a
->patchJcc8(realSrc
, ji
.addr
, m_address
); break;
1025 case Branch::Call
: ji
.a
->patchCall(realSrc
, ji
.addr
, m_address
); break;
1030 Label(const Label
&) = delete;
1031 Label
& operator=(const Label
&) = delete;
1033 void jmp(X64AssemblerBase
& a
) {
1034 addJump(&a
, Branch::Jmp
);
1035 a
.jmp(m_address
? m_address
: a
.frontier());
1038 void jmp8(X64AssemblerBase
& a
) {
1039 addJump(&a
, Branch::Jmp8
);
1040 a
.jmp8(m_address
? m_address
: a
.frontier());
1043 void jcc(X64AssemblerBase
& a
, ConditionCode cc
) {
1044 addJump(&a
, Branch::Jcc
);
1045 a
.jcc(cc
, m_address
? m_address
: a
.frontier());
1048 void jcc8(X64AssemblerBase
& a
, ConditionCode cc
) {
1049 addJump(&a
, Branch::Jcc8
);
1050 a
.jcc8(cc
, m_address
? m_address
: a
.frontier());
1053 void call(X64AssemblerBase
& a
) {
1054 addJump(&a
, Branch::Call
);
1055 a
.call(m_address
? m_address
: a
.frontier());
1058 void jmpAuto(X64AssemblerBase
& a
) {
1060 auto delta
= m_address
- (a
.frontier() + 2);
1061 if (deltaFits(delta
, sz::byte
)) {
1068 void jccAuto(X64AssemblerBase
& a
, ConditionCode cc
) {
1070 auto delta
= m_address
- (a
.frontier() + 2);
1071 if (deltaFits(delta
, sz::byte
)) {
1078 friend void asm_label(X64AssemblerBase
& a
, Label
& l
) {
1079 assert(!l
.m_address
&& !l
.m_a
&& "Label was already set");
1081 l
.m_address
= a
.frontier();
1095 X64AssemblerBase
* a
;
1100 void addJump(X64AssemblerBase
* a
, Branch type
) {
1101 if (m_address
) return;
1105 info
.addr
= a
->codeBlock
.frontier();
1106 m_toPatch
.push_back(info
);
1110 X64AssemblerBase
* m_a
;
1111 CodeAddress m_address
;
1112 std::vector
<JumpInfo
> m_toPatch
;
1115 inline void X64AssemblerBase::jmp(Label
& l
) { l
.jmp(*this); }
1116 inline void X64AssemblerBase::jmp8(Label
& l
) { l
.jmp8(*this); }
1117 inline void X64AssemblerBase::jcc(ConditionCode c
, Label
& l
) {
1120 inline void X64AssemblerBase::jcc8(ConditionCode c
, Label
& l
) {
1123 inline void X64AssemblerBase::call(Label
& l
) { l
.call(*this); }
1125 #define CC(nm, code) \
1126 inline void X64AssemblerBase::j##nm(Label& l) { l.jcc(*this, code); } \
1127 inline void X64AssemblerBase::j##nm##8(Label& l) { l.jcc8(*this, code); }
1131 //////////////////////////////////////////////////////////////////////
1134 * Select the assembler which contains a given address.
1138 * Asm& a = codeBlockChoose(toPatch, a, acold);
1141 inline CodeBlock
& codeBlockChoose(CodeAddress addr
) {
1142 always_assert_flog(false,
1143 "address {} was not part of any known code block", addr
);
1145 template<class... Blocks
>
1146 CodeBlock
& codeBlockChoose(CodeAddress addr
, CodeBlock
& a
, Blocks
&... as
) {
1147 if (a
.contains(addr
)) return a
;
1148 return codeBlockChoose(addr
, as
...);
1151 //////////////////////////////////////////////////////////////////////
1155 struct DecodedInstruction
{
1156 DecodedInstruction(uint8_t* ip
, uint8_t* base
)
1160 explicit DecodedInstruction(uint8_t* ip
) : DecodedInstruction(ip
, ip
) {}
1162 std::string
toString();
1163 size_t size() { return m_size
; }
1165 bool hasPicOffset() const { return m_flags
.picOff
; }
1166 uint8_t* picAddress() const;
1167 bool setPicAddress(uint8_t* target
);
1169 bool hasOffset() const { return m_offSz
!= 0; }
1170 int32_t offset() const;
1172 bool hasImmediate() const { return m_immSz
; }
1173 int64_t immediate() const;
1174 bool setImmediate(int64_t value
);
1178 Unconditional
= 1 << 1,
1180 bool isBranch(BranchType branchType
= BranchType(Conditional
|
1181 Unconditional
)) const;
1182 bool isCall() const;
1185 bool isFuseable(const DecodedInstruction
& next
) const;
1186 ConditionCode
jccCondCode() const;
1187 bool shrinkBranch();
1189 uint8_t getModRm() const;
1191 void decode(uint8_t* ip
);
1192 bool decodePrefix(uint8_t* ip
);
1193 int decodeRexVexXop(uint8_t* ip
);
1194 int decodeOpcode(uint8_t* ip
);
1195 void determineOperandsMap0(uint8_t* ip
);
1196 void determineOperandsMap1(uint8_t* ip
);
1197 void determineOperandsMap2(uint8_t* ip
);
1198 void determineOperandsMap3(uint8_t* ip
);
1199 int decodeModRm(uint8_t* ip
);
1200 int decodeImm(uint8_t* ip
);
1202 // We may wish to decode an instruction whose address is m_ip, but treat all
1203 // PIC references as relative to m_base.
1210 uint32_t m_flagsVal
;
1222 uint32_t bTaken
: 1;
1223 uint32_t bNotTaken
: 1;
1225 uint32_t opndSzOvr
: 1;
1226 uint32_t addrSzOvr
: 1;
1239 uint32_t immIsAddr
: 1;
1240 uint32_t picOff
: 1;
1241 uint32_t hasModRm
: 1;
1242 uint32_t hasSib
: 1;
1246 uint8_t m_map_select
;
1253 constexpr DecodedInstruction::BranchType
operator|(
1254 DecodedInstruction::BranchType a
,
1255 DecodedInstruction::BranchType b
1257 return DecodedInstruction::BranchType((int)a
| (int)b
);
1260 inline DecodedInstruction::BranchType
& operator|=(
1261 DecodedInstruction::BranchType
& a
,
1262 const DecodedInstruction::BranchType
& b
1264 return (a
= DecodedInstruction::BranchType((int)a
| (int)b
));
1268 #undef logical_const