don't use local-file-change context for mapreduce
[hiphop-php.git] / hphp / util / asm-x64.h
blob6e91d2f25568344ab04422cd2f98d3b9fd40c053
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
20 #include <memory>
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
39 * to consider:
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 //////////////////////////////////////////////////////////////////////
59 struct MemoryRef;
60 struct RIPRelativeRef;
61 struct ScaledIndex;
62 struct ScaledIndexDisp;
63 struct DispReg;
65 const uint8_t kOpsizePrefix = 0x66;
67 enum Segment : uint8_t { DS = 0, FS, GS };
69 struct Reg64 {
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; }
85 private:
86 int rn;
89 #define SIMPLE_REGTYPE(What) \
90 struct 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; } \
95 private: \
96 int 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
107 struct RegRIP {
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.
138 // reg*x
139 struct ScaledIndex {
140 explicit ScaledIndex(Reg64 index, intptr_t scale)
141 : index(index)
142 , scale(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");
149 Reg64 index;
150 intptr_t scale;
153 // reg*x + disp
154 struct ScaledIndexDisp {
155 explicit ScaledIndexDisp(ScaledIndex si, intptr_t disp)
156 : si(si)
157 , disp(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);
168 ScaledIndex si;
169 intptr_t disp;
172 // reg+x
173 struct DispReg {
174 explicit DispReg(Reg64 base, intptr_t disp = 0)
175 : base(base)
176 , disp(disp)
178 assert(int(base) != -1 && "invalid register");
181 // Constructor for baseless().
182 explicit DispReg(intptr_t disp)
183 : base(-1)
184 , disp(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);
198 Reg64 base;
199 intptr_t disp;
202 // reg + reg*x + y
203 struct IndexedDispReg {
204 explicit IndexedDispReg(Reg64 base, ScaledIndex sr)
205 : base(base)
206 , index(sr.index)
207 , scale(sr.scale)
208 , disp(0)
211 explicit IndexedDispReg(DispReg r)
212 : base(r.base)
213 , index(-1)
214 , scale(1)
215 , disp(r.disp)
218 // Constructor for baseless()
219 explicit IndexedDispReg(ScaledIndexDisp sid)
220 : base(-1)
221 , index(sid.si.index)
222 , scale(sid.si.scale)
223 , disp(sid.disp)
226 MemoryRef operator*() const;
227 MemoryRef operator[](intptr_t disp) const;
229 IndexedDispReg operator+(intptr_t disp) const {
230 auto ret = *this;
231 ret.disp += disp;
232 return ret;
235 IndexedDispReg operator-(intptr_t disp) const {
236 auto ret = *this;
237 ret.disp -= disp;
238 return ret;
241 Reg64 base;
242 Reg64 index;
243 int scale;
244 intptr_t disp; // TODO #4613274: should be int32_t
247 // rip+x
248 struct DispRIP {
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; }
265 intptr_t disp;
268 // *(rip + x)
269 struct RIPRelativeRef {
270 explicit constexpr RIPRelativeRef(DispRIP r) : r(r) {}
271 DispRIP r;
273 bool operator==(const RIPRelativeRef& o) const { return r == o.r; }
274 bool operator!=(const RIPRelativeRef& o) const { return r != o.r; }
277 //////////////////////////////////////////////////////////////////////
279 namespace reg {
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,
353 // dil, etc).
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);
380 return nullptr;
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);
385 return nullptr;
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);
390 return nullptr;
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);
396 return nullptr;
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);
401 X(xmm14); X(xmm15);
402 return nullptr;
404 inline const char* regname(RegSF /*r*/) {
405 return "%flags";
407 #undef X
411 enum class RoundDirection : ssize_t {
412 nearest = 0,
413 floor = 1,
414 ceil = 2,
415 truncate = 3,
418 const char* show(RoundDirection);
420 enum class ComparisonPred : uint8_t {
421 // True if...
422 eq_ord = 0, // ...operands are ordered AND equal
423 ne_unord = 4, // ...operands are unordered OR unequal
426 enum ConditionCode {
427 CC_None = -1,
428 CC_O = 0x00,
429 CC_NO = 0x01,
431 CC_B = 0x02,
432 CC_NAE = 0x02,
433 CC_AE = 0x03,
434 CC_NB = 0x03,
435 CC_NC = 0x03,
437 CC_E = 0x04,
438 CC_Z = 0x04,
439 CC_NE = 0x05,
440 CC_NZ = 0x05,
442 CC_BE = 0x06,
443 CC_NA = 0x06,
444 CC_A = 0x07,
445 CC_NBE = 0x07,
447 CC_S = 0x08,
448 CC_NS = 0x09,
450 CC_P = 0x0A,
451 CC_NP = 0x0B,
453 CC_L = 0x0C,
454 CC_NGE = 0x0C,
455 CC_GE = 0x0D,
456 CC_NL = 0x0D,
458 CC_LE = 0x0E,
459 CC_NG = 0x0E,
460 CC_G = 0x0F,
461 CC_NLE = 0x0F,
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!
471 // *(reg + x)
472 struct MemoryRef {
473 explicit MemoryRef(DispReg dr, Segment s = DS) : r(dr), segment(s) {}
474 explicit MemoryRef(IndexedDispReg idr, Segment s = DS) : r(idr), segment(s) {}
475 IndexedDispReg r;
476 Segment segment;
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.
483 struct SegReg {
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);
497 Segment seg;
500 #ifdef HAVE_LIBXED
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
507 #else
508 #define NEW_X64_ASM(var, cb) X64Assembler var(cb)
509 #endif
511 struct Label;
513 struct X64AssemblerBase {
514 protected:
515 friend struct Label;
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);
526 public:
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);
564 bool empty() const {
565 return codeBlock.empty();
568 void clear() {
569 codeBlock.clear();
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; \
585 BYTE_LOAD_OP(name)
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; \
597 BYTE_STORE_OP(name)
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; \
609 BYTE_REG_OP(name)
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) \
618 LOAD_OP(name) \
619 STORE_OP(name) \
620 REG_OP(name) \
621 IMM64_STORE_OP(name) \
622 IMM64R_OP(name)
624 LOAD_OP (load)
625 STORE_OP (store)
626 IMM64_STORE_OP (store)
627 REG_OP (mov)
629 FULL_OP(add)
630 FULL_OP(xor)
631 FULL_OP(sub)
632 FULL_OP(and)
633 FULL_OP(or)
634 FULL_OP(test)
635 FULL_OP(cmp)
636 FULL_OP(sbb)
638 #undef IMM64R_OP
639 #undef FULL_OP
640 #undef REG_OP
641 #undef STORE_OP
642 #undef LOAD_OP
643 #undef BYTE_LOAD_OP
644 #undef BYTE_STORE_OP
645 #undef BYTE_REG_OP
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) {
736 if (imm.q() == 0) {
737 // Zeros the top bits also.
738 xorl (r32(dest), r32(dest));
739 return;
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));
744 return;
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)) {
769 jmp8(dest);
770 } else {
771 jmp(dest);
775 void jccAuto(ConditionCode cc, CodeAddress dest) {
776 auto delta = dest - (codeBlock.frontier() + 2);
777 if (deltaFits(delta, sz::byte)) {
778 jcc8(cc, dest);
779 } else {
780 jcc(cc, dest);
784 void call(Label&);
785 void jmp(Label&);
786 void jmp8(Label&);
787 void jcc(ConditionCode, Label&);
788 void jcc8(ConditionCode, Label&);
790 #define CCS \
791 CC(o, CC_O) \
792 CC(no, CC_NO) \
793 CC(nae, CC_NAE) \
794 CC(ae, CC_AE) \
795 CC(nb, CC_NB) \
796 CC(e, CC_E) \
797 CC(z, CC_Z) \
798 CC(ne, CC_NE) \
799 CC(nz, CC_NZ) \
800 CC(b, CC_B) \
801 CC(be, CC_BE) \
802 CC(nbe, CC_NBE) \
803 CC(s, CC_S) \
804 CC(ns, CC_NS) \
805 CC(p, CC_P) \
806 CC(np, CC_NP) \
807 CC(nge, CC_NGE) \
808 CC(g, CC_G) \
809 CC(l, CC_L) \
810 CC(ge, CC_GE) \
811 CC(nl, CC_NL) \
812 CC(ng, CC_NG) \
813 CC(le, CC_LE) \
814 CC(nle, CC_NLE)
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&);
822 #undef CC
824 #define CC(_nm, _cond) \
825 void set ## _nm(Reg8 byteReg) { \
826 setcc(_cond, byteReg); \
829 #undef CC
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) {
882 codeBlock.byte(b);
884 void word(uint16_t w) {
885 codeBlock.word(w);
887 void dword(uint32_t dw) {
888 codeBlock.dword(dw);
890 void qword(uint64_t qw) {
891 codeBlock.qword(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,
902 Reg64 rdest) = 0;
904 protected:
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;
915 #ifdef HAVE_LIBXED
916 #include "hphp/util/asm-x64-intelxed.h"
917 #endif
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 {
927 return *(*this + x);
930 inline MemoryRef DispReg::operator*() const {
931 return MemoryRef(*this);
934 inline MemoryRef DispReg::operator[](intptr_t x) const {
935 return *(*this + x);
938 inline RIPRelativeRef DispRIP::operator*() const {
939 return RIPRelativeRef(*this);
942 inline RIPRelativeRef DispRIP::operator[](intptr_t x) const {
943 return *(*this + x);
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) {
949 return DispRIP(d);
951 inline DispRIP operator-(RegRIP /*r*/, intptr_t d) {
952 return DispRIP(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*/) {
970 return DispRIP(0);
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 //////////////////////////////////////////////////////////////////////
1008 struct Label {
1009 explicit Label()
1010 : m_a(nullptr)
1011 , m_address(nullptr)
1014 ~Label() {
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);
1020 switch (ji.type) {
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) {
1059 assert(m_address);
1060 auto delta = m_address - (a.frontier() + 2);
1061 if (deltaFits(delta, sz::byte)) {
1062 jmp8(a);
1063 } else {
1064 jmp(a);
1068 void jccAuto(X64AssemblerBase& a, ConditionCode cc) {
1069 assert(m_address);
1070 auto delta = m_address - (a.frontier() + 2);
1071 if (deltaFits(delta, sz::byte)) {
1072 jcc8(a, cc);
1073 } else {
1074 jcc(a, cc);
1078 friend void asm_label(X64AssemblerBase& a, Label& l) {
1079 assert(!l.m_address && !l.m_a && "Label was already set");
1080 l.m_a = &a;
1081 l.m_address = a.frontier();
1084 private:
1085 enum class Branch {
1086 Jcc,
1087 Jcc8,
1088 Jmp,
1089 Jmp8,
1090 Call
1093 struct JumpInfo {
1094 Branch type;
1095 X64AssemblerBase* a;
1096 CodeAddress addr;
1099 private:
1100 void addJump(X64AssemblerBase* a, Branch type) {
1101 if (m_address) return;
1102 JumpInfo info;
1103 info.type = type;
1104 info.a = a;
1105 info.addr = a->codeBlock.frontier();
1106 m_toPatch.push_back(info);
1109 private:
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) {
1118 l.jcc(*this, c);
1120 inline void X64AssemblerBase::jcc8(ConditionCode c, Label& l) {
1121 l.jcc8(*this, c);
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); }
1129 #undef CC
1131 //////////////////////////////////////////////////////////////////////
1134 * Select the assembler which contains a given address.
1136 * E.g.:
1138 * Asm& a = codeBlockChoose(toPatch, a, acold);
1139 * a.patchJmp(...);
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 //////////////////////////////////////////////////////////////////////
1153 namespace x64 {
1155 struct DecodedInstruction {
1156 DecodedInstruction(uint8_t* ip, uint8_t* base)
1157 : m_base(base)
1158 { decode(ip); }
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);
1175 bool isNop() const;
1176 enum BranchType {
1177 Conditional = 1,
1178 Unconditional = 1 << 1,
1180 bool isBranch(BranchType branchType = BranchType(Conditional |
1181 Unconditional)) const;
1182 bool isCall() const;
1183 bool isJmp() const;
1184 bool isLea() const;
1185 bool isFuseable(const DecodedInstruction& next) const;
1186 ConditionCode jccCondCode() const;
1187 bool shrinkBranch();
1188 void widenBranch();
1189 uint8_t getModRm() const;
1190 private:
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.
1204 uint8_t* m_base;
1206 uint8_t* m_ip;
1207 uint32_t m_size;
1209 union {
1210 uint32_t m_flagsVal;
1211 struct {
1212 uint32_t lock : 1;
1213 uint32_t repNE : 1;
1214 uint32_t rep : 1;
1216 uint32_t cs : 1;
1217 uint32_t ss : 1;
1218 uint32_t ds : 1;
1219 uint32_t es : 1;
1220 uint32_t fs : 1;
1221 uint32_t gs : 1;
1222 uint32_t bTaken : 1;
1223 uint32_t bNotTaken : 1;
1225 uint32_t opndSzOvr : 1;
1226 uint32_t addrSzOvr : 1;
1228 uint32_t rex : 1;
1229 uint32_t vex : 1;
1230 uint32_t xop : 1;
1232 uint32_t w : 1;
1233 uint32_t r : 1;
1234 uint32_t x : 1;
1235 uint32_t b : 1;
1236 uint32_t l : 1;
1238 uint32_t def64 : 1;
1239 uint32_t immIsAddr : 1;
1240 uint32_t picOff : 1;
1241 uint32_t hasModRm : 1;
1242 uint32_t hasSib : 1;
1243 } m_flags;
1246 uint8_t m_map_select;
1247 uint8_t m_xtra_op;
1248 uint8_t m_opcode;
1249 uint8_t m_immSz;
1250 uint8_t m_offSz;
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));
1267 #undef TRACEMOD
1268 #undef logical_const
1269 #undef CCS
1273 #endif