1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1982-1998 by Symantec
6  *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     Mike Cote, John Micco, $(LINK2 http://www.digitalmars.com, Walter Bright),
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/iasm.d, backend/iasm.d)
10  */
11 
12 module dmd.backend.iasm;
13 
14 // Online documentation: https://dlang.org/phobos/dmd_backend_iasm.html
15 
16 import dmd.backend.cc : block;
17 import dmd.backend.code_x86 : opcode_t;
18 
19 extern (C++):
20 @nogc:
21 nothrow:
22 
23 //#include <setjmp.h>
24 
25 /////////////////////////////////////////////////
26 // Instruction flags (usFlags)
27 //
28 //
29 
30 enum _modrm = 0x10;
31 
32 // This is for when the reg field of modregrm specifies which instruction it is
33 enum
34 {
35     NUM_MASK  = 0x7,
36     NUM_MASKR = 0x8,             // for REX extended registers
37     _0      = (0x0 | _modrm),    // insure that some _modrm bit is set
38     _1      = 0x1,               // with _0
39     _2      = 0x2,
40     _3      = 0x3,
41     _4      = 0x4,
42     _5      = 0x5,
43     _6      = 0x6,
44     _7      = 0x7,
45 }
46 
47 enum
48 {
49     _r           = _modrm,
50     _cb          = _modrm,
51     _cw          = _modrm,
52     _cd          = _modrm,
53     _cq          = _modrm,
54     _cp          = _modrm,
55     _ib          = 0,
56     _iw          = 0,
57     _id          = 0,
58     _rb          = 0,
59     _rw          = 0,
60     _rd          = 0,
61     _16_bit      = 0x20,
62     _32_bit      = 0x40,
63     _64_bit      = 0x10000,
64     _i64_bit     = 0x20000,  // opcode is invalid in 64bit mode
65     _I386        = 0x80,     // opcode is only for 386 and later
66     _16_bit_addr = 0x100,
67     _32_bit_addr = 0x200,
68     _fwait       = 0x400,    // Add an FWAIT prior to the instruction opcode
69     _nfwait      = 0x800,    // Do not add an FWAIT prior to the instruction
70 }
71 
72 enum
73 {
74     MOD_MASK        = 0xF000,  // Mod mask
75     _modsi          = 0x1000,  // Instruction modifies SI
76     _moddx          = 0x2000,  // Instruction modifies DX
77     _mod2           = 0x3000,  // Instruction modifies second operand
78     _modax          = 0x4000,  // Instruction modifies AX
79     _modnot1        = 0x5000,  // Instruction does not modify first operand
80     _modaxdx        = 0x6000,  // instruction modifies AX and DX
81     _moddi          = 0x7000,  // Instruction modifies DI
82     _modsidi        = 0x8000,  // Instruction modifies SI and DI
83     _modcx          = 0x9000,  // Instruction modifies CX
84     _modes          = 0xa000,  // Instruction modifies ES
85     _modall         = 0xb000,  // Instruction modifies all register values
86     _modsiax        = 0xc000,  // Instruction modifies AX and SI
87     _modsinot1      = 0xd000,  // Instruction modifies SI and not first param
88     _modcxr11       = 0xe000,  // Instruction modifies CX and R11
89     _modxmm0        = 0xf000,  // Instruction modifies XMM0
90 }
91 
92 // translates opcode into equivalent vex encoding
93 uint VEX_128_W0(opcode_t op)            { return _VEX(op)|_VEX_NOO; }
94 uint VEX_128_W1(opcode_t op)            { return _VEX(op)|_VEX_NOO|_VEX_W; }
95 uint VEX_128_WIG(opcode_t op)           { return  VEX_128_W0(op); }
96 uint VEX_256_W0(opcode_t op)            { return _VEX(op)|_VEX_NOO|_VEX_L; }
97 uint VEX_256_W1(opcode_t op)            { return _VEX(op)|_VEX_NOO|_VEX_W|_VEX_L; }
98 uint VEX_256_WIG(opcode_t op)           { return  VEX_256_W0(op); }
99 uint VEX_NDS_128_W0(opcode_t op)        { return _VEX(op)|_VEX_NDS; }
100 uint VEX_NDS_128_W1(opcode_t op)        { return _VEX(op)|_VEX_NDS|_VEX_W; }
101 uint VEX_NDS_128_WIG(opcode_t op)       { return  VEX_NDS_128_W0(op); }
102 uint VEX_NDS_256_W0(opcode_t op)        { return _VEX(op)|_VEX_NDS|_VEX_L; }
103 uint VEX_NDS_256_W1(opcode_t op)        { return _VEX(op)|_VEX_NDS|_VEX_W|_VEX_L; }
104 uint VEX_NDS_256_WIG(opcode_t op)       { return  VEX_NDS_256_W0(op); }
105 uint VEX_NDD_128_W0(opcode_t op)        { return _VEX(op)|_VEX_NDD; }
106 uint VEX_NDD_128_W1(opcode_t op)        { return _VEX(op)|_VEX_NDD|_VEX_W; }
107 uint VEX_NDD_128_WIG(opcode_t op)       { return  VEX_NDD_128_W0(op); }
108 uint VEX_NDD_256_W0(opcode_t op)        { return _VEX(op)|_VEX_NDD|_VEX_L; }
109 uint VEX_NDD_256_W1(opcode_t op)        { return _VEX(op)|_VEX_NDD|_VEX_W|_VEX_L; }
110 uint VEX_NDD_256_WIG(opcode_t op)       { return  VEX_NDD_256_W0(op); }
111 uint VEX_DDS_128_W0(opcode_t op)        { return _VEX(op)|_VEX_DDS; }
112 uint VEX_DDS_128_W1(opcode_t op)        { return _VEX(op)|_VEX_DDS|_VEX_W; }
113 uint VEX_DDS_128_WIG(opcode_t op)       { return  VEX_DDS_128_W0(op); }
114 uint VEX_DDS_256_W0(opcode_t op)        { return _VEX(op)|_VEX_DDS|_VEX_L; }
115 uint VEX_DDS_256_W1(opcode_t op)        { return _VEX(op)|_VEX_DDS|_VEX_W|_VEX_L; }
116 uint VEX_DDS_256_WIG(opcode_t op)       { return  VEX_DDS_256_W0(op); }
117 
118 enum _VEX_W   = 0x8000;
119 /* Don't encode LIG/LZ use 128 for these.
120  */
121 enum _VEX_L   = 0x0400;
122 /* Encode nds, ndd, dds in the vvvv field, it gets
123  * overwritten with the actual register later.
124  */
125 enum
126 {
127      VEX_NOO = 0, // neither of nds, ndd, dds
128      VEX_NDS = 1,
129      VEX_NDD = 2,
130      VEX_DDS = 3,
131     _VEX_NOO  = VEX_NOO << 11,
132     _VEX_NDS  = VEX_NDS << 11,
133     _VEX_NDD  = VEX_NDD << 11,
134     _VEX_DDS  = VEX_DDS << 11,
135 }
136 
137 uint _VEX(opcode_t op) { return (0xC4 << 24) | _VEX_MM(op >> 8) | (op & 0xFF); }
138 
139 uint _VEX_MM(opcode_t op)
140 {
141     return
142         (op & 0x00FF) == 0x000F ? (0x1 << 16 | _VEX_PP(op >>  8)) :
143         (op & 0xFFFF) == 0x0F38 ? (0x2 << 16 | _VEX_PP(op >> 16)) :
144         (op & 0xFFFF) == 0x0F3A ? (0x3 << 16 | _VEX_PP(op >> 16)) :
145         _VEX_ASSERT0;
146 }
147 
148 uint _VEX_PP(opcode_t op)
149 {
150     return
151         op == 0x00 ? 0x00 << 8 :
152         op == 0x66 ? 0x01 << 8 :
153         op == 0xF3 ? 0x02 << 8 :
154         op == 0xF2 ? 0x03 << 8 :
155         _VEX_ASSERT0;
156 }
157 
158 // avoid dynamic initialization of the asm tables
159 debug
160 {
161     @property uint _VEX_ASSERT0() { assert(0); }
162 }
163 else
164 {
165     @property uint _VEX_ASSERT0() { return 0; }
166 }
167 
168 
169 /////////////////////////////////////////////////
170 // Operand flags - usOp1, usOp2, usOp3
171 //
172 
173 alias opflag_t = uint;
174 
175 // Operand flags for normal opcodes
176 enum
177 {
178     _r8     = CONSTRUCT_FLAGS( _8, _reg, _normal, 0 ),
179     _r16    = CONSTRUCT_FLAGS(_16, _reg, _normal, 0 ),
180     _r32    = CONSTRUCT_FLAGS(_32, _reg, _normal, 0 ),
181     _r64    = CONSTRUCT_FLAGS(_64, _reg, _normal, 0 ),
182     _m8     = CONSTRUCT_FLAGS(_8, _m, _normal, 0 ),
183     _m16    = CONSTRUCT_FLAGS(_16, _m, _normal, 0 ),
184     _m32    = CONSTRUCT_FLAGS(_32, _m, _normal, 0 ),
185     _m48    = CONSTRUCT_FLAGS( _48, _m, _normal, 0 ),
186     _m64    = CONSTRUCT_FLAGS( _64, _m, _normal, 0 ),
187     _m128   = CONSTRUCT_FLAGS( _anysize, _m, _normal, 0 ),
188     _m256   = CONSTRUCT_FLAGS( _anysize, _m, _normal, 0 ),
189     _rm8    = CONSTRUCT_FLAGS(_8, _rm, _normal, 0 ),
190     _rm16   = CONSTRUCT_FLAGS(_16, _rm, _normal, 0 ),
191     _rm32   = CONSTRUCT_FLAGS(_32, _rm, _normal, 0),
192     _rm64   = CONSTRUCT_FLAGS(_64, _rm, _normal, 0),
193     _r32m8  = CONSTRUCT_FLAGS(_32|_8, _rm, _normal, 0),
194     _r32m16 = CONSTRUCT_FLAGS(_32|_16, _rm, _normal, 0),
195     _regm8  = CONSTRUCT_FLAGS(_64|_32|_8, _rm, _normal, 0),
196     _imm8   = CONSTRUCT_FLAGS(_8, _imm, _normal, 0 ),
197     _imm16  = CONSTRUCT_FLAGS(_16, _imm, _normal, 0),
198     _imm32  = CONSTRUCT_FLAGS(_32, _imm, _normal, 0),
199     _imm64  = CONSTRUCT_FLAGS(_64, _imm, _normal, 0),
200     _rel8   = CONSTRUCT_FLAGS(_8, _rel, _normal, 0),
201     _rel16  = CONSTRUCT_FLAGS(_16, _rel, _normal, 0),
202     _rel32  = CONSTRUCT_FLAGS(_32, _rel, _normal, 0),
203     _p1616  = CONSTRUCT_FLAGS(_32, _p, _normal, 0),
204     _m1616  = CONSTRUCT_FLAGS(_32, _mnoi, _normal, 0),
205     _p1632  = CONSTRUCT_FLAGS(_48, _p, _normal, 0 ),
206     _m1632  = CONSTRUCT_FLAGS(_48, _mnoi, _normal, 0),
207     _special  = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0 ),
208     _seg    = CONSTRUCT_FLAGS( 0, 0, _rseg, 0 ),
209     _a16    = CONSTRUCT_FLAGS( 0, 0, _addr16, 0 ),
210     _a32    = CONSTRUCT_FLAGS( 0, 0, _addr32, 0 ),
211     _f16    = CONSTRUCT_FLAGS( 0, 0, _fn16, 0),
212                                                 // Near function pointer
213     _f32    = CONSTRUCT_FLAGS( 0, 0, _fn32, 0),
214                                                 // Far function pointer
215     _lbl    = CONSTRUCT_FLAGS( 0, 0, _flbl, 0 ),
216                                                 // Label (in current function)
217 
218     _mmm32  = CONSTRUCT_FLAGS( 0, _m, 0, _32),
219     _mmm64  = CONSTRUCT_FLAGS( _64, _m, 0, _f64),
220     _mmm128 = CONSTRUCT_FLAGS( 0, _m, 0, _f128),
221 
222     _xmm_m16  = CONSTRUCT_FLAGS( _16,      _m, _rspecial, ASM_GET_uRegmask(_xmm)),
223     _xmm_m32  = CONSTRUCT_FLAGS( _32,      _m, _rspecial, ASM_GET_uRegmask(_xmm)),
224     _xmm_m64  = CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm)),
225     _xmm_m128 = CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm)),
226     _ymm_m256 = CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_ymm)),
227 
228     _moffs8  = _rel8,
229     _moffs16 = _rel16,
230     _moffs32 = _rel32,
231 }
232 
233 ////////////////////////////////////////////////////////////////////
234 // Operand flags for floating point opcodes are all just aliases for
235 // normal opcode variants and only asm_determine_operator_flags should
236 // need to care.
237 
238 enum
239 {
240     _fm80   = CONSTRUCT_FLAGS( 0, _m, 0, _f80 ),
241     _fm64   = CONSTRUCT_FLAGS( 0, _m, 0, _f64 ),
242     _fm128  = CONSTRUCT_FLAGS( 0, _m, 0, _f128 ),
243     _fanysize = (_f64 | _f80 | _f112 ),
244 
245     _float_m = CONSTRUCT_FLAGS( _anysize, _float, 0, _fanysize),
246 
247     _st     = CONSTRUCT_FLAGS( 0, _float, 0, _rst ),   // stack register 0
248     _m112   = CONSTRUCT_FLAGS( 0, _m, 0, _f112 ),
249     _m224   = _m112,
250     _m512   = _m224,
251     _sti    = CONSTRUCT_FLAGS( 0, _float, 0, _rsti ),
252 }
253 
254 ////////////////// FLAGS /////////////////////////////////////
255 
256 // bit size                      5            3          3              7
257 uint CONSTRUCT_FLAGS(uint uSizemask, uint aopty, uint amod, uint uRegmask)
258 {
259     return uSizemask | (aopty << 5) | (amod << 8) | (uRegmask << 11);
260 }
261 
262 uint ASM_GET_uSizemask(uint us) { return us & 0x1F; }
263 uint ASM_GET_aopty(uint us)     { return cast(ASM_OPERAND_TYPE)((us >> 5) & 7); }
264 uint ASM_GET_amod(uint us)      { return cast(ASM_MODIFIERS)((us >> 8) & 7); }
265 uint ASM_GET_uRegmask(uint us)  { return (us >> 11) & 0x7F; }
266 
267 // For uSizemask (5 bits)
268 enum
269 {
270     _8  = 0x1,
271     _16 = 0x2,
272     _32 = 0x4,
273     _48 = 0x8,
274     _64 = 0x10,
275     _anysize = (_8 | _16 | _32 | _48 | _64 ),
276 }
277 
278 // For aopty (3 bits)
279 alias ASM_OPERAND_TYPE = uint;
280 enum
281 {
282     _reg,           // _r8, _r16, _r32
283     _m,             // _m8, _m16, _m32, _m48
284     _imm,           // _imm8, _imm16, _imm32, _imm64
285     _rel,           // _rel8, _rel16, _rel32
286     _mnoi,          // _m1616, _m1632
287     _p,             // _p1616, _p1632
288     _rm,            // _rm8, _rm16, _rm32
289     _float          // Floating point operand, look at cRegmask for the
290                     // actual size
291 }
292 
293 // For amod (3 bits)
294 alias ASM_MODIFIERS = uint;
295 enum
296 {
297     _normal,        // Normal register value
298     _rseg,          // Segment registers
299     _rspecial,      // Special registers
300     _addr16,        // 16 bit address
301     _addr32,        // 32 bit address
302     _fn16,          // 16 bit function call
303     _fn32,          // 32 bit function call
304     _flbl           // Label
305 }
306 
307 // For uRegmask (7 bits)
308 
309 // uRegmask flags when aopty == _float
310 enum
311 {
312     _rst    = 0x1,
313     _rsti   = 0x2,
314     _f64    = 0x4,
315     _f80    = 0x8,
316     _f112   = 0x10,
317     _f128   = 0x20,
318 }
319 
320 // _seg register values (amod == _rseg)
321 //
322 enum
323 {
324     _ds     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x01 ),
325     _es     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x02 ),
326     _ss     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x04 ),
327     _fs     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x08 ),
328     _gs     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x10 ),
329     _cs     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x20 ),
330 }
331 
332 //
333 // _special register values
334 //
335 enum
336 {
337     _crn    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x01 ), // CRn register (0,2,3)
338     _drn    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x02 ), // DRn register (0-3,6-7)
339     _trn    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x04 ), // TRn register (3-7)
340     _mm     = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x08 ), // MMn register (0-7)
341     _xmm    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x10 ), // XMMn register (0-7)
342     _xmm0   = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x20 ), // XMM0 register
343     _ymm    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x40 ), // YMMn register (0-15)
344 }
345 
346 //
347 // Default register values
348 //
349 enum
350 {
351     _al     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x01 ),  // AL register
352     _ax     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x02 ),  // AX register
353     _eax    = CONSTRUCT_FLAGS( 0, 0, _normal, 0x04 ),  // EAX register
354     _dx     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x08 ),  // DX register
355     _cl     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x10 ),  // CL register
356     _rax    = CONSTRUCT_FLAGS( 0, 0, _normal, 0x40 ),  // RAX register
357 }
358 
359 
360 enum _rplus_r        = 0x20;
361 enum _plus_r = CONSTRUCT_FLAGS( 0, 0, 0, _rplus_r );
362                 // Add the register to the opcode (no mod r/m)
363 
364 
365 
366 //////////////////////////////////////////////////////////////////
367 
368 enum
369 {
370     ITprefix        = 0x10,    // special prefix
371     ITjump          = 0x20,    // jump instructions CALL, Jxx and LOOPxx
372     ITimmed         = 0x30,    // value of an immediate operand controls
373                                // code generation
374     ITopt           = 0x40,    // not all operands are required
375     ITshift         = 0x50,    // rotate and shift instructions
376     ITfloat         = 0x60,    // floating point coprocessor instructions
377     ITdata          = 0x70,    // DB, DW, DD, DQ, DT pseudo-ops
378     ITaddr          = 0x80,    // DA (define addresss) pseudo-op
379     ITMASK          = 0xF0,
380     ITSIZE          = 0x0F,    // mask for size
381 }
382 
383 version (SCPP)
384 {
385     alias OP_DB = int;
386     enum
387     {
388         // These are the number of bytes
389         OPdb = 1,
390         OPdw = 2,
391         OPdd = 4,
392         OPdq = 8,
393         OPdt = 10,
394         OPdf = 4,
395         OPde = 10,
396         OPds = 2,
397         OPdi = 4,
398         OPdl = 8,
399     }
400 }
401 version (MARS)
402 {
403     alias OP_DB = int;
404     enum
405     {
406         // Integral types
407         OPdb,
408         OPds,
409         OPdi,
410         OPdl,
411 
412         // Float types
413         OPdf,
414         OPdd,
415         OPde,
416 
417         // Deprecated
418         OPdw = OPds,
419         OPdq = OPdl,
420         OPdt = OPde,
421     }
422 }
423 
424 
425 /* from iasm.c */
426 int asm_state(int iFlags);
427 
428 void asm_process_fixup( block **ppblockLabels );
429 
430 struct PTRNTAB4
431 {
432         opcode_t opcode;
433         uint usFlags;
434         opflag_t usOp1;
435         opflag_t usOp2;
436         opflag_t usOp3;
437         opflag_t usOp4;
438 }
439 
440 struct PTRNTAB3 {
441         opcode_t opcode;
442         uint usFlags;
443         opflag_t usOp1;
444         opflag_t usOp2;
445         opflag_t usOp3;
446 }
447 
448 struct PTRNTAB2 {
449         opcode_t opcode;
450         uint usFlags;
451         opflag_t usOp1;
452         opflag_t usOp2;
453 }
454 
455 struct PTRNTAB1 {
456         opcode_t opcode;
457         uint usFlags;
458         opflag_t usOp1;
459 }
460 
461 enum ASM_END = 0xffff;      // special opcode meaning end of PTRNTABx table
462 
463 struct PTRNTAB0 {
464         opcode_t opcode;
465         uint usFlags;
466 }
467 
468 union PTRNTAB {
469         void            *ppt;
470         PTRNTAB0        *pptb0;
471         PTRNTAB1        *pptb1;
472         PTRNTAB2        *pptb2;
473         PTRNTAB3        *pptb3;
474         PTRNTAB4        *pptb4;
475 }
476 
477 struct OP
478 {
479     const(char)* str;   // opcode string
480     ubyte usNumops;
481     PTRNTAB ptb;
482 }
483