Remove typedef decl errors
[hiphop-php.git] / hphp / util / asm-x64.h
blobb1a26a913a0ddc1db683b1d240a378254a73c1f6
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 crc32q(Reg64 src, Reg64 dest) = 0;
656 virtual void lea(MemoryRef p, Reg64 reg) = 0;
657 virtual void lea(RIPRelativeRef p, Reg64 reg) = 0;
659 virtual void xchgq(Reg64 r1, Reg64 r2) = 0;
660 virtual void xchgl(Reg32 r1, Reg32 r2) = 0;
661 virtual void xchgb(Reg8 r1, Reg8 r2) = 0;
663 virtual void imul(Reg64 r1, Reg64 r2) = 0;
665 virtual void push(Reg64 r) = 0;
666 virtual void pushl(Reg32 r) = 0;
667 virtual void pop (Reg64 r) = 0;
668 virtual void idiv(Reg64 r) = 0;
669 virtual void incq(Reg64 r) = 0;
670 virtual void incl(Reg32 r) = 0;
671 virtual void incw(Reg16 r) = 0;
672 virtual void decq(Reg64 r) = 0;
673 virtual void decl(Reg32 r) = 0;
674 virtual void decw(Reg16 r) = 0;
675 virtual void notb(Reg8 r) = 0;
676 virtual void not(Reg64 r) = 0;
677 virtual void neg(Reg64 r) = 0;
678 virtual void negb(Reg8 r) = 0;
679 virtual void ret() = 0;
680 virtual void ret(Immed i) = 0;
681 virtual void cqo() = 0;
682 virtual void nop() = 0;
683 virtual void int3() = 0;
684 virtual void ud2() = 0;
685 virtual void pushf() = 0;
686 virtual void popf() = 0;
687 virtual void lock() = 0;
689 virtual void push(MemoryRef m) = 0;
690 virtual void pop (MemoryRef m) = 0;
691 virtual void incq(MemoryRef m) = 0;
692 virtual void incl(MemoryRef m) = 0;
693 virtual void incw(MemoryRef m) = 0;
694 virtual void decqlock(MemoryRef m) = 0;
695 virtual void decq(MemoryRef m) = 0;
696 virtual void decl(MemoryRef m) = 0;
697 virtual void decw(MemoryRef m) = 0;
699 virtual void push(Immed64 i) = 0;
701 virtual void movups(RegXMM x, MemoryRef m) = 0;
702 virtual void movups(MemoryRef m, RegXMM x) = 0;
703 virtual void movdqu(RegXMM x, MemoryRef m) = 0;
704 virtual void movdqu(MemoryRef m, RegXMM x) = 0;
705 virtual void movdqa(RegXMM x, RegXMM y) = 0;
706 virtual void movdqa(RegXMM x, MemoryRef m) = 0;
707 virtual void movdqa(MemoryRef m, RegXMM x) = 0;
708 virtual void movsd (RegXMM x, RegXMM y) = 0;
709 virtual void movsd (RegXMM x, MemoryRef m) = 0;
710 virtual void movsd (MemoryRef m, RegXMM x) = 0;
711 virtual void movsd (RIPRelativeRef m, RegXMM x) = 0;
712 virtual void lddqu (MemoryRef m, RegXMM x) = 0;
713 virtual void unpcklpd(RegXMM s, RegXMM d) = 0;
715 virtual void rorq (Immed i, Reg64 r) = 0;
716 virtual void shlq (Immed i, Reg64 r) = 0;
717 virtual void shrq (Immed i, Reg64 r) = 0;
718 virtual void sarq (Immed i, Reg64 r) = 0;
719 virtual void shll (Immed i, Reg32 r) = 0;
720 virtual void shrl (Immed i, Reg32 r) = 0;
721 virtual void shlw (Immed i, Reg16 r) = 0;
722 virtual void shrw (Immed i, Reg16 r) = 0;
724 virtual void shlq (Reg64 r) = 0;
725 virtual void shrq (Reg64 r) = 0;
726 virtual void sarq (Reg64 r) = 0;
728 virtual void roundsd (RoundDirection d, RegXMM src, RegXMM dst) = 0;
729 virtual void cmpsd(RegXMM src, RegXMM dst, ComparisonPred pred) = 0;
732 * The following utility functions do more than emit specific code.
733 * (E.g. combine common idioms or patterns, smash code, etc.)
736 void emitImmReg(Immed64 imm, Reg64 dest) {
737 if (imm.q() == 0) {
738 // Zeros the top bits also.
739 xorl (r32(dest), r32(dest));
740 return;
742 if (LIKELY(imm.q() > 0 && imm.fits(sz::dword))) {
743 // This will zero out the high-order bits.
744 movl (imm.l(), r32(dest));
745 return;
747 movq (imm.q(), dest);
750 bool jmpDeltaFits(CodeAddress dest) {
751 int64_t delta = dest - (codeBlock.frontier() + 5);
752 return deltaFits(delta, sz::dword);
755 virtual void jmp(Reg64 r) = 0;
756 virtual void jmp(MemoryRef m) = 0;
757 virtual void jmp(RIPRelativeRef m) = 0;
758 virtual void call(Reg64 r) = 0;
759 virtual void call(MemoryRef m) = 0;
760 virtual void call(RIPRelativeRef m) = 0;
761 virtual void jmp8(CodeAddress dest) = 0;
762 virtual void jmp(CodeAddress dest) = 0;
763 virtual void call(CodeAddress dest) = 0;
764 virtual void jcc(ConditionCode cond, CodeAddress dest) = 0;
765 virtual void jcc8(ConditionCode cond, CodeAddress dest)= 0;
767 void jmpAuto(CodeAddress dest) {
768 auto delta = dest - (codeBlock.frontier() + 2);
769 if (deltaFits(delta, sz::byte)) {
770 jmp8(dest);
771 } else {
772 jmp(dest);
776 void jccAuto(ConditionCode cc, CodeAddress dest) {
777 auto delta = dest - (codeBlock.frontier() + 2);
778 if (deltaFits(delta, sz::byte)) {
779 jcc8(cc, dest);
780 } else {
781 jcc(cc, dest);
785 void call(Label&);
786 void jmp(Label&);
787 void jmp8(Label&);
788 void jcc(ConditionCode, Label&);
789 void jcc8(ConditionCode, Label&);
791 #define CCS \
792 CC(o, CC_O) \
793 CC(no, CC_NO) \
794 CC(nae, CC_NAE) \
795 CC(ae, CC_AE) \
796 CC(nb, CC_NB) \
797 CC(e, CC_E) \
798 CC(z, CC_Z) \
799 CC(ne, CC_NE) \
800 CC(nz, CC_NZ) \
801 CC(b, CC_B) \
802 CC(be, CC_BE) \
803 CC(nbe, CC_NBE) \
804 CC(s, CC_S) \
805 CC(ns, CC_NS) \
806 CC(p, CC_P) \
807 CC(np, CC_NP) \
808 CC(nge, CC_NGE) \
809 CC(g, CC_G) \
810 CC(l, CC_L) \
811 CC(ge, CC_GE) \
812 CC(nl, CC_NL) \
813 CC(ng, CC_NG) \
814 CC(le, CC_LE) \
815 CC(nle, CC_NLE)
817 #define CC(_nm, _code) \
818 void j ## _nm(CodeAddress dest) { jcc(_code, dest); } \
819 void j ## _nm ## 8(CodeAddress dest) { jcc8(_code, dest); } \
820 void j ## _nm(Label&); \
821 void j ## _nm ## 8(Label&);
823 #undef CC
825 #define CC(_nm, _cond) \
826 void set ## _nm(Reg8 byteReg) { \
827 setcc(_cond, byteReg); \
830 #undef CC
832 virtual void setcc(int cc, Reg8 byteReg) = 0;
833 virtual void psllq(Immed i, RegXMM r) = 0;
834 virtual void psrlq(Immed i, RegXMM r) = 0;
835 virtual void movq_rx(Reg64 rSrc, RegXMM rdest) = 0;
836 virtual void movq_xr(RegXMM rSrc, Reg64 rdest) = 0;
837 virtual void addsd(RegXMM src, RegXMM srcdest) = 0;
838 virtual void mulsd(RegXMM src, RegXMM srcdest) = 0;
839 virtual void subsd(RegXMM src, RegXMM srcdest) = 0;
840 virtual void pxor(RegXMM src, RegXMM srcdest) = 0;
841 virtual void cvtsi2sd(Reg64 src, RegXMM dest) = 0;
842 virtual void cvtsi2sd(MemoryRef m, RegXMM dest) = 0;
843 virtual void ucomisd(RegXMM l, RegXMM r) = 0;
844 virtual void sqrtsd(RegXMM src, RegXMM dest) = 0;
845 virtual void divsd(RegXMM src, RegXMM srcdest) = 0;
846 virtual void cvttsd2siq(RegXMM src, Reg64 dest) = 0;
848 static void patchJcc(CodeAddress jmp, CodeAddress from, CodeAddress dest) {
849 assert(jmp[0] == 0x0F && (jmp[1] & 0xF0) == 0x80);
850 ssize_t diff = dest - (from + 6);
851 *(int32_t*)(jmp + 2) = safe_cast<int32_t>(diff);
854 static void patchJcc8(CodeAddress jmp, CodeAddress from, CodeAddress dest) {
855 assert((jmp[0] & 0xF0) == 0x70);
856 ssize_t diff = dest - (from + 2); // one for opcode, one for offset
857 *(int8_t*)(jmp + 1) = safe_cast<int8_t>(diff);
860 static void patchJmp(CodeAddress jmp, CodeAddress from, CodeAddress dest) {
861 assert(jmp[0] == 0xE9);
862 ssize_t diff = dest - (from + 5);
863 *(int32_t*)(jmp + 1) = safe_cast<int32_t>(diff);
866 static void patchJmp8(CodeAddress jmp, CodeAddress from, CodeAddress dest) {
867 assert(jmp[0] == 0xEB);
868 ssize_t diff = dest - (from + 2); // one for opcode, one for offset
869 *(int8_t*)(jmp + 1) = safe_cast<int8_t>(diff);
872 static void patchCall(CodeAddress call, CodeAddress from, CodeAddress dest) {
873 assert(call[0] == 0xE8);
874 ssize_t diff = dest - (from + 5);
875 *(int32_t*)(call + 1) = safe_cast<int32_t>(diff);
878 virtual void emitInt3s(int n) = 0;
879 virtual void emitNop(int n) = 0;
880 virtual void pad() = 0;
882 void byte(uint8_t b) {
883 codeBlock.byte(b);
885 void word(uint16_t w) {
886 codeBlock.word(w);
888 void dword(uint32_t dw) {
889 codeBlock.dword(dw);
891 void qword(uint64_t qw) {
892 codeBlock.qword(qw);
894 void bytes(size_t n, const uint8_t* bs) {
895 codeBlock.bytes(n, bs);
898 virtual void cload_reg64_disp_reg64(ConditionCode cc, Reg64 rbase,
899 int off, Reg64 rdest) = 0;
900 virtual void cload_reg64_disp_reg32(ConditionCode cc, Reg64 rbase,
901 int off, Reg32 rdest) = 0;
902 virtual void cmov_reg64_reg64(ConditionCode cc, Reg64 rsrc,
903 Reg64 rdest) = 0;
905 protected:
906 RegNumber rn(Reg8 r) { return RegNumber(int(r)); }
907 RegNumber rn(Reg16 r) { return RegNumber(int(r)); }
908 RegNumber rn(Reg32 r) { return RegNumber(int(r)); }
909 RegNumber rn(Reg64 r) { return RegNumber(int(r)); }
910 RegNumber rn(RegXMM r) { return RegNumber(int(r)); }
912 CodeBlock& codeBlock;
916 #ifdef HAVE_LIBXED
917 #include "hphp/util/asm-x64-intelxed.h"
918 #endif
920 #include "hphp/util/asm-x64-legacy.h"
922 namespace HPHP { namespace jit {
923 inline MemoryRef IndexedDispReg::operator*() const {
924 return MemoryRef(*this);
927 inline MemoryRef IndexedDispReg::operator[](intptr_t x) const {
928 return *(*this + x);
931 inline MemoryRef DispReg::operator*() const {
932 return MemoryRef(*this);
935 inline MemoryRef DispReg::operator[](intptr_t x) const {
936 return *(*this + x);
939 inline RIPRelativeRef DispRIP::operator*() const {
940 return RIPRelativeRef(*this);
943 inline RIPRelativeRef DispRIP::operator[](intptr_t x) const {
944 return *(*this + x);
947 inline DispReg operator+(Reg64 r, intptr_t d) { return DispReg(r, d); }
948 inline DispReg operator-(Reg64 r, intptr_t d) { return DispReg(r, -d); }
949 inline DispRIP operator+(RegRIP /*r*/, intptr_t d) {
950 return DispRIP(d);
952 inline DispRIP operator-(RegRIP /*r*/, intptr_t d) {
953 return DispRIP(d);
956 inline ScaledIndex operator*(Reg64 r, int scale) {
957 return ScaledIndex(r, scale);
959 inline IndexedDispReg operator+(Reg64 base, ScaledIndex sr) {
960 return IndexedDispReg(base, sr);
962 inline ScaledIndexDisp operator+(ScaledIndex si, intptr_t disp) {
963 return ScaledIndexDisp(si, disp);
965 inline IndexedDispReg operator+(Reg64 b, Reg64 i) {
966 return b + ScaledIndex(i, 0x1);
969 inline MemoryRef operator*(Reg64 r) { return MemoryRef(DispReg(r)); }
970 inline DispRIP operator*(RegRIP /*r*/) {
971 return DispRIP(0);
974 inline MemoryRef Reg64::operator[](intptr_t disp) const {
975 return *(*this + disp);
978 inline MemoryRef Reg64::operator[](Reg64 idx) const {
979 return *(*this + idx * 1);
982 inline MemoryRef Reg64::operator[](ScaledIndex si) const {
983 return *(*this + si);
986 inline MemoryRef Reg64::operator[](DispReg dr) const {
987 return *(*this + ScaledIndex(dr.base, 0x1) + dr.disp);
990 inline MemoryRef Reg64::operator[](ScaledIndexDisp sid) const {
991 return *(*this + sid.si + sid.disp);
994 inline RIPRelativeRef RegRIP::operator[](intptr_t disp) const {
995 return *(*this + disp);
999 * Used for the x64 addressing mode where there is a displacement,
1000 * possibly with a scaled index, but no base register.
1002 inline MemoryRef baseless(intptr_t disp) { return *(DispReg { disp }); }
1003 inline MemoryRef baseless(ScaledIndexDisp sid) {
1004 return *(IndexedDispReg { sid });
1007 //////////////////////////////////////////////////////////////////////
1009 struct Label {
1010 explicit Label()
1011 : m_a(nullptr)
1012 , m_address(nullptr)
1015 ~Label() {
1016 if (!m_toPatch.empty()) {
1017 assert(m_a && m_address && "Label had jumps but was never set");
1019 for (auto& ji : m_toPatch) {
1020 auto realSrc = ji.a->toDestAddress(ji.addr);
1021 switch (ji.type) {
1022 case Branch::Jmp: ji.a->patchJmp(realSrc, ji.addr, m_address); break;
1023 case Branch::Jmp8: ji.a->patchJmp8(realSrc, ji.addr, m_address); break;
1024 case Branch::Jcc: ji.a->patchJcc(realSrc, ji.addr, m_address); break;
1025 case Branch::Jcc8: ji.a->patchJcc8(realSrc, ji.addr, m_address); break;
1026 case Branch::Call: ji.a->patchCall(realSrc, ji.addr, m_address); break;
1031 Label(const Label&) = delete;
1032 Label& operator=(const Label&) = delete;
1034 void jmp(X64AssemblerBase& a) {
1035 addJump(&a, Branch::Jmp);
1036 a.jmp(m_address ? m_address : a.frontier());
1039 void jmp8(X64AssemblerBase& a) {
1040 addJump(&a, Branch::Jmp8);
1041 a.jmp8(m_address ? m_address : a.frontier());
1044 void jcc(X64AssemblerBase& a, ConditionCode cc) {
1045 addJump(&a, Branch::Jcc);
1046 a.jcc(cc, m_address ? m_address : a.frontier());
1049 void jcc8(X64AssemblerBase& a, ConditionCode cc) {
1050 addJump(&a, Branch::Jcc8);
1051 a.jcc8(cc, m_address ? m_address : a.frontier());
1054 void call(X64AssemblerBase& a) {
1055 addJump(&a, Branch::Call);
1056 a.call(m_address ? m_address : a.frontier());
1059 void jmpAuto(X64AssemblerBase& a) {
1060 assert(m_address);
1061 auto delta = m_address - (a.frontier() + 2);
1062 if (deltaFits(delta, sz::byte)) {
1063 jmp8(a);
1064 } else {
1065 jmp(a);
1069 void jccAuto(X64AssemblerBase& a, ConditionCode cc) {
1070 assert(m_address);
1071 auto delta = m_address - (a.frontier() + 2);
1072 if (deltaFits(delta, sz::byte)) {
1073 jcc8(a, cc);
1074 } else {
1075 jcc(a, cc);
1079 friend void asm_label(X64AssemblerBase& a, Label& l) {
1080 assert(!l.m_address && !l.m_a && "Label was already set");
1081 l.m_a = &a;
1082 l.m_address = a.frontier();
1085 private:
1086 enum class Branch {
1087 Jcc,
1088 Jcc8,
1089 Jmp,
1090 Jmp8,
1091 Call
1094 struct JumpInfo {
1095 Branch type;
1096 X64AssemblerBase* a;
1097 CodeAddress addr;
1100 private:
1101 void addJump(X64AssemblerBase* a, Branch type) {
1102 if (m_address) return;
1103 JumpInfo info;
1104 info.type = type;
1105 info.a = a;
1106 info.addr = a->codeBlock.frontier();
1107 m_toPatch.push_back(info);
1110 private:
1111 X64AssemblerBase* m_a;
1112 CodeAddress m_address;
1113 std::vector<JumpInfo> m_toPatch;
1116 inline void X64AssemblerBase::jmp(Label& l) { l.jmp(*this); }
1117 inline void X64AssemblerBase::jmp8(Label& l) { l.jmp8(*this); }
1118 inline void X64AssemblerBase::jcc(ConditionCode c, Label& l) {
1119 l.jcc(*this, c);
1121 inline void X64AssemblerBase::jcc8(ConditionCode c, Label& l) {
1122 l.jcc8(*this, c);
1124 inline void X64AssemblerBase::call(Label& l) { l.call(*this); }
1126 #define CC(nm, code) \
1127 inline void X64AssemblerBase::j##nm(Label& l) { l.jcc(*this, code); } \
1128 inline void X64AssemblerBase::j##nm##8(Label& l) { l.jcc8(*this, code); }
1130 #undef CC
1132 //////////////////////////////////////////////////////////////////////
1135 * Select the assembler which contains a given address.
1137 * E.g.:
1139 * Asm& a = codeBlockChoose(toPatch, a, acold);
1140 * a.patchJmp(...);
1142 inline CodeBlock& codeBlockChoose(CodeAddress addr) {
1143 always_assert_flog(false,
1144 "address {} was not part of any known code block", addr);
1146 template<class... Blocks>
1147 CodeBlock& codeBlockChoose(CodeAddress addr, CodeBlock& a, Blocks&... as) {
1148 if (a.contains(addr)) return a;
1149 return codeBlockChoose(addr, as...);
1152 //////////////////////////////////////////////////////////////////////
1154 namespace x64 {
1156 struct DecodedInstruction {
1157 DecodedInstruction(uint8_t* ip, uint8_t* base)
1158 : m_base(base)
1159 { decode(ip); }
1161 explicit DecodedInstruction(uint8_t* ip) : DecodedInstruction(ip, ip) {}
1163 std::string toString();
1164 size_t size() { return m_size; }
1166 bool hasPicOffset() const { return m_flags.picOff; }
1167 uint8_t* picAddress() const;
1168 bool setPicAddress(uint8_t* target);
1170 bool hasOffset() const { return m_offSz != 0; }
1171 int32_t offset() const;
1173 bool hasImmediate() const { return m_immSz; }
1174 int64_t immediate() const;
1175 bool setImmediate(int64_t value);
1176 bool isNop() const;
1177 enum BranchType {
1178 Conditional = 1,
1179 Unconditional = 1 << 1,
1181 bool isBranch(BranchType branchType = BranchType(Conditional |
1182 Unconditional)) const;
1183 bool isCall() const;
1184 bool isJmp() const;
1185 bool isLea() const;
1186 bool isFuseable(const DecodedInstruction& next) const;
1187 ConditionCode jccCondCode() const;
1188 bool shrinkBranch();
1189 void widenBranch();
1190 uint8_t getModRm() const;
1191 private:
1192 void decode(uint8_t* ip);
1193 bool decodePrefix(uint8_t* ip);
1194 int decodeRexVexXop(uint8_t* ip);
1195 int decodeOpcode(uint8_t* ip);
1196 void determineOperandsMap0(uint8_t* ip);
1197 void determineOperandsMap1(uint8_t* ip);
1198 void determineOperandsMap2(uint8_t* ip);
1199 void determineOperandsMap3(uint8_t* ip);
1200 int decodeModRm(uint8_t* ip);
1201 int decodeImm(uint8_t* ip);
1203 // We may wish to decode an instruction whose address is m_ip, but treat all
1204 // PIC references as relative to m_base.
1205 uint8_t* m_base;
1207 uint8_t* m_ip;
1208 uint32_t m_size;
1210 union {
1211 uint32_t m_flagsVal;
1212 struct {
1213 uint32_t lock : 1;
1214 uint32_t repNE : 1;
1215 uint32_t rep : 1;
1217 uint32_t cs : 1;
1218 uint32_t ss : 1;
1219 uint32_t ds : 1;
1220 uint32_t es : 1;
1221 uint32_t fs : 1;
1222 uint32_t gs : 1;
1223 uint32_t bTaken : 1;
1224 uint32_t bNotTaken : 1;
1226 uint32_t opndSzOvr : 1;
1227 uint32_t addrSzOvr : 1;
1229 uint32_t rex : 1;
1230 uint32_t vex : 1;
1231 uint32_t xop : 1;
1233 uint32_t w : 1;
1234 uint32_t r : 1;
1235 uint32_t x : 1;
1236 uint32_t b : 1;
1237 uint32_t l : 1;
1239 uint32_t def64 : 1;
1240 uint32_t immIsAddr : 1;
1241 uint32_t picOff : 1;
1242 uint32_t hasModRm : 1;
1243 uint32_t hasSib : 1;
1244 } m_flags;
1247 uint8_t m_map_select;
1248 uint8_t m_xtra_op;
1249 uint8_t m_opcode;
1250 uint8_t m_immSz;
1251 uint8_t m_offSz;
1254 constexpr DecodedInstruction::BranchType operator|(
1255 DecodedInstruction::BranchType a,
1256 DecodedInstruction::BranchType b
1258 return DecodedInstruction::BranchType((int)a | (int)b);
1261 inline DecodedInstruction::BranchType& operator|=(
1262 DecodedInstruction::BranchType& a,
1263 const DecodedInstruction::BranchType& b
1265 return (a = DecodedInstruction::BranchType((int)a | (int)b));
1268 #undef TRACEMOD
1269 #undef logical_const
1270 #undef CCS
1274 #endif