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