mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-02-01 04:23:14 +01:00
431 lines
15 KiB
C
431 lines
15 KiB
C
|
|
/*
|
|
* Copyright (c) 1992-1999 by Symantec
|
|
* Copyright (c) 1999-2011 by Digital Mars
|
|
* All Rights Reserved
|
|
* http://www.digitalmars.com
|
|
* Written by Mike Cote, John Micco and Walter Bright
|
|
*
|
|
* This source file is made available for personal use
|
|
* only. The license is in /dmd/src/dmd/backendlicense.txt
|
|
* or /dm/src/dmd/backendlicense.txt
|
|
* For any other uses, please contact Digital Mars.
|
|
*/
|
|
|
|
#include <setjmp.h>
|
|
|
|
/////////////////////////////////////////////////
|
|
// Instruction flags (usFlags)
|
|
//
|
|
//
|
|
|
|
// This is for when the reg field of modregrm specifies which instruction it is
|
|
#define NUM_MASK 0x7
|
|
#define NUM_MASKR 0x8 // for REX extended registers
|
|
#define _0 (0x0 | _modrm) // insure that some _modrm bit is set
|
|
#define _1 0x1 // with _0
|
|
#define _2 0x2
|
|
#define _3 0x3
|
|
#define _4 0x4
|
|
#define _5 0x5
|
|
#define _6 0x6
|
|
#define _7 0x7
|
|
|
|
#define _modrm 0x10
|
|
|
|
#define _r _modrm
|
|
#define _cb _modrm
|
|
#define _cw _modrm
|
|
#define _cd _modrm
|
|
#define _cq _modrm
|
|
#define _cp _modrm
|
|
#define _ib 0
|
|
#define _iw 0
|
|
#define _id 0
|
|
#define _rb 0
|
|
#define _rw 0
|
|
#define _rd 0
|
|
#define _16_bit 0x20
|
|
#define _32_bit 0x40
|
|
#define _64_bit 0x10000
|
|
#define _i64_bit 0x20000 // opcode is invalid in 64bit mode
|
|
#define _I386 0x80 // opcode is only for 386 and later
|
|
#define _16_bit_addr 0x100
|
|
#define _32_bit_addr 0x200
|
|
#define _fwait 0x400 // Add an FWAIT prior to the instruction opcode
|
|
#define _nfwait 0x800 // Do not add an FWAIT prior to the instruction
|
|
|
|
#define MOD_MASK 0xF000 // Mod mask
|
|
#define _modsi 0x1000 // Instruction modifies SI
|
|
#define _moddx 0x2000 // Instruction modifies DX
|
|
#define _mod2 0x3000 // Instruction modifies second operand
|
|
#define _modax 0x4000 // Instruction modifies AX
|
|
#define _modnot1 0x5000 // Instruction does not modify first operand
|
|
#define _modaxdx 0x6000 // instruction modifies AX and DX
|
|
#define _moddi 0x7000 // Instruction modifies DI
|
|
#define _modsidi 0x8000 // Instruction modifies SI and DI
|
|
#define _modcx 0x9000 // Instruction modifies CX
|
|
#define _modes 0xa000 // Instruction modifies ES
|
|
#define _modall 0xb000 // Instruction modifies all register values
|
|
#define _modsiax 0xc000 // Instruction modifies AX and SI
|
|
#define _modsinot1 0xd000 // Instruction modifies SI and not first param
|
|
#define _modcxr11 0xe000 // Instruction modifies CX and R11
|
|
#define _modxmm0 0xf000 // Instruction modifies XMM0
|
|
|
|
// translates opcode into equivalent vex encoding
|
|
#define VEX_128_W0(op) (_VEX(op)|_VEX_NOO)
|
|
#define VEX_128_W1(op) (_VEX(op)|_VEX_NOO|_VEX_W)
|
|
#define VEX_128_WIG(op) VEX_128_W0(op)
|
|
#define VEX_256_W0(op) (_VEX(op)|_VEX_NOO|_VEX_L)
|
|
#define VEX_256_W1(op) (_VEX(op)|_VEX_NOO|_VEX_W|_VEX_L)
|
|
#define VEX_256_WIG(op) VEX_256_W0(op)
|
|
#define VEX_NDS_128_W0(op) (_VEX(op)|_VEX_NDS)
|
|
#define VEX_NDS_128_W1(op) (_VEX(op)|_VEX_NDS|_VEX_W)
|
|
#define VEX_NDS_128_WIG(op) VEX_NDS_128_W0(op)
|
|
#define VEX_NDS_256_W0(op) (_VEX(op)|_VEX_NDS|_VEX_L)
|
|
#define VEX_NDS_256_W1(op) (_VEX(op)|_VEX_NDS|_VEX_W|_VEX_L)
|
|
#define VEX_NDS_256_WIG(op) VEX_NDS_256_W0(op)
|
|
#define VEX_NDD_128_W0(op) (_VEX(op)|_VEX_NDD)
|
|
#define VEX_NDD_128_W1(op) (_VEX(op)|_VEX_NDD|_VEX_W)
|
|
#define VEX_NDD_128_WIG(op) VEX_NDD_128_W0(op)
|
|
#define VEX_NDD_256_W0(op) (_VEX(op)|_VEX_NDD|_VEX_L)
|
|
#define VEX_NDD_256_W1(op) (_VEX(op)|_VEX_NDD|_VEX_W|_VEX_L)
|
|
#define VEX_NDD_256_WIG(op) VEX_NDD_256_W0(op)
|
|
#define VEX_DDS_128_W0(op) (_VEX(op)|_VEX_DDS)
|
|
#define VEX_DDS_128_W1(op) (_VEX(op)|_VEX_DDS|_VEX_W)
|
|
#define VEX_DDS_128_WIG(op) VEX_DDS_128_W0(op)
|
|
#define VEX_DDS_256_W0(op) (_VEX(op)|_VEX_DDS|_VEX_L)
|
|
#define VEX_DDS_256_W1(op) (_VEX(op)|_VEX_DDS|_VEX_W|_VEX_L)
|
|
#define VEX_DDS_256_WIG(op) VEX_DDS_256_W0(op)
|
|
|
|
#define _VEX_W 0x8000
|
|
/* Don't encode LIG/LZ use 128 for these.
|
|
*/
|
|
#define _VEX_L 0x0400
|
|
/* Encode nds, ndd, dds in the vvvv field, it gets
|
|
* overwritten with the actual register later.
|
|
*/
|
|
#define VEX_NOO 0 // neither of nds, ndd, dds
|
|
#define VEX_NDS 1
|
|
#define VEX_NDD 2
|
|
#define VEX_DDS 3
|
|
#define _VEX_NOO ( VEX_NOO << 11)
|
|
#define _VEX_NDS ( VEX_NDS << 11)
|
|
#define _VEX_NDD ( VEX_NDD << 11)
|
|
#define _VEX_DDS ( VEX_DDS << 11)
|
|
|
|
#define _VEX(op) (0xC4 << 24 | _VEX_MM(op >> 8) | (op & 0xFF))
|
|
|
|
#define _VEX_MM(op) \
|
|
( \
|
|
((op) & 0x00FF) == 0x000F ? (0x1 << 16 | _VEX_PP((op) >> 8)) : \
|
|
((op) & 0xFFFF) == 0x0F38 ? (0x2 << 16 | _VEX_PP((op) >> 16)) : \
|
|
((op) & 0xFFFF) == 0x0F3A ? (0x3 << 16 | _VEX_PP((op) >> 16)) : \
|
|
_VEX_ASSERT0 \
|
|
)
|
|
|
|
#define _VEX_PP(op) \
|
|
( \
|
|
(op) == 0x00 ? 0x00 << 8 : \
|
|
(op) == 0x66 ? 0x01 << 8 : \
|
|
(op) == 0xF3 ? 0x02 << 8 : \
|
|
(op) == 0xF2 ? 0x03 << 8 : \
|
|
_VEX_ASSERT0 \
|
|
)
|
|
|
|
// avoid dynamic initialization of the asm tables
|
|
#if DEBUG
|
|
#define _VEX_ASSERT0 (assert(0))
|
|
#else
|
|
#define _VEX_ASSERT0 (0)
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////
|
|
// Operand flags - usOp1, usOp2, usOp3
|
|
//
|
|
|
|
typedef unsigned opflag_t;
|
|
|
|
// Operand flags for normal opcodes
|
|
|
|
#define _r8 CONSTRUCT_FLAGS( _8, _reg, _normal, 0 )
|
|
#define _r16 CONSTRUCT_FLAGS(_16, _reg, _normal, 0 )
|
|
#define _r32 CONSTRUCT_FLAGS(_32, _reg, _normal, 0 )
|
|
#define _r64 CONSTRUCT_FLAGS(_64, _reg, _normal, 0 )
|
|
#define _m8 CONSTRUCT_FLAGS(_8, _m, _normal, 0 )
|
|
#define _m16 CONSTRUCT_FLAGS(_16, _m, _normal, 0 )
|
|
#define _m32 CONSTRUCT_FLAGS(_32, _m, _normal, 0 )
|
|
#define _m48 CONSTRUCT_FLAGS( _48, _m, _normal, 0 )
|
|
#define _m64 CONSTRUCT_FLAGS( _64, _m, _normal, 0 )
|
|
#define _m128 CONSTRUCT_FLAGS( _anysize, _m, _normal, 0 )
|
|
#define _m256 CONSTRUCT_FLAGS( _anysize, _m, _normal, 0 )
|
|
#define _rm8 CONSTRUCT_FLAGS(_8, _rm, _normal, 0 )
|
|
#define _rm16 CONSTRUCT_FLAGS(_16, _rm, _normal, 0 )
|
|
#define _rm32 CONSTRUCT_FLAGS(_32, _rm, _normal, 0)
|
|
#define _rm64 CONSTRUCT_FLAGS(_64, _rm, _normal, 0)
|
|
#define _r32m8 CONSTRUCT_FLAGS(_32|_8, _rm, _normal, 0)
|
|
#define _r32m16 CONSTRUCT_FLAGS(_32|_16, _rm, _normal, 0)
|
|
#define _regm8 CONSTRUCT_FLAGS(_64|_32|_8, _rm, _normal, 0)
|
|
#define _imm8 CONSTRUCT_FLAGS(_8, _imm, _normal, 0 )
|
|
#define _imm16 CONSTRUCT_FLAGS(_16, _imm, _normal, 0)
|
|
#define _imm32 CONSTRUCT_FLAGS(_32, _imm, _normal, 0)
|
|
#define _imm64 CONSTRUCT_FLAGS(_64, _imm, _normal, 0)
|
|
#define _rel8 CONSTRUCT_FLAGS(_8, _rel, _normal, 0)
|
|
#define _rel16 CONSTRUCT_FLAGS(_16, _rel, _normal, 0)
|
|
#define _rel32 CONSTRUCT_FLAGS(_32, _rel, _normal, 0)
|
|
#define _p1616 CONSTRUCT_FLAGS(_32, _p, _normal, 0)
|
|
#define _m1616 CONSTRUCT_FLAGS(_32, _mnoi, _normal, 0)
|
|
#define _p1632 CONSTRUCT_FLAGS(_48, _p, _normal, 0 )
|
|
#define _m1632 CONSTRUCT_FLAGS(_48, _mnoi, _normal, 0)
|
|
#define _special CONSTRUCT_FLAGS( 0, 0, _rspecial, 0 )
|
|
#define _seg CONSTRUCT_FLAGS( 0, 0, _rseg, 0 )
|
|
#define _a16 CONSTRUCT_FLAGS( 0, 0, _addr16, 0 )
|
|
#define _a32 CONSTRUCT_FLAGS( 0, 0, _addr32, 0 )
|
|
#define _f16 CONSTRUCT_FLAGS( 0, 0, _fn16, 0)
|
|
// Near function pointer
|
|
#define _f32 CONSTRUCT_FLAGS( 0, 0, _fn32, 0)
|
|
// Far function pointer
|
|
#define _lbl CONSTRUCT_FLAGS( 0, 0, _flbl, 0 )
|
|
// Label (in current function)
|
|
|
|
#define _mmm32 CONSTRUCT_FLAGS( 0, _m, 0, _32)
|
|
#define _mmm64 CONSTRUCT_FLAGS( _64, _m, 0, _f64)
|
|
#define _mmm128 CONSTRUCT_FLAGS( 0, _m, 0, _f128)
|
|
|
|
#define _xmm_m16 CONSTRUCT_FLAGS( _16, _m, _rspecial, ASM_GET_uRegmask(_xmm))
|
|
#define _xmm_m32 CONSTRUCT_FLAGS( _32, _m, _rspecial, ASM_GET_uRegmask(_xmm))
|
|
#define _xmm_m64 CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm))
|
|
#define _xmm_m128 CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm))
|
|
#define _ymm_m256 CONSTRUCT_FLAGS( _anysize, _m, _rspecial, ASM_GET_uRegmask(_ymm))
|
|
|
|
#define _moffs8 (_rel8)
|
|
#define _moffs16 (_rel16 )
|
|
#define _moffs32 (_rel32 )
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Operand flags for floating point opcodes are all just aliases for
|
|
// normal opcode variants and only asm_determine_operator_flags should
|
|
// need to care.
|
|
//
|
|
#define _fm80 CONSTRUCT_FLAGS( 0, _m, 0, _f80 )
|
|
#define _fm64 CONSTRUCT_FLAGS( 0, _m, 0, _f64 )
|
|
#define _fm128 CONSTRUCT_FLAGS( 0, _m, 0, _f128 )
|
|
#define _fanysize (_f64 | _f80 | _f112 )
|
|
|
|
#define _float_m CONSTRUCT_FLAGS( _anysize, _float, 0, _fanysize)
|
|
|
|
#define _st CONSTRUCT_FLAGS( 0, _float, 0, _rst ) // stack register 0
|
|
#define _m112 CONSTRUCT_FLAGS( 0, _m, 0, _f112 )
|
|
#define _m224 _m112
|
|
#define _m512 _m224
|
|
#define _sti CONSTRUCT_FLAGS( 0, _float, 0, _rsti )
|
|
|
|
////////////////// FLAGS /////////////////////////////////////
|
|
|
|
#if 1
|
|
// bit size 5 3 3 7
|
|
#define CONSTRUCT_FLAGS( uSizemask, aopty, amod, uRegmask ) \
|
|
( (uSizemask) | (aopty) << 5 | (amod) << 8 | (uRegmask) << 11)
|
|
|
|
#define ASM_GET_uSizemask(us) ((us) & 0x1F)
|
|
#define ASM_GET_aopty(us) ((ASM_OPERAND_TYPE)(((us) >> 5) & 7))
|
|
#define ASM_GET_amod(us) ((ASM_MODIFIERS)(((us) >> 8) & 7))
|
|
#define ASM_GET_uRegmask(us) (((us) >> 11) & 0x7F)
|
|
#else
|
|
#define CONSTRUCT_FLAGS( uSizemask, aopty, amod, uRegmask ) \
|
|
( (uSizemask) | (aopty) << 4 | (amod) << 7 | (uRegmask) << 10)
|
|
|
|
#define ASM_GET_uSizemask(us) ((us) & 0x0F)
|
|
#define ASM_GET_aopty(us) ((ASM_OPERAND_TYPE)(((us) & 0x70) >> 4))
|
|
#define ASM_GET_amod(us) ((ASM_MODIFIERS)(((us) & 0x380) >> 7))
|
|
#define ASM_GET_uRegmask(us) (((us) & 0xFC00) >> 10)
|
|
#endif
|
|
|
|
// For uSizemask (5 bits)
|
|
#define _8 0x1
|
|
#define _16 0x2
|
|
#define _32 0x4
|
|
#define _48 0x8
|
|
#define _64 0x10
|
|
#define _anysize (_8 | _16 | _32 | _48 | _64 )
|
|
|
|
// For aopty (3 bits)
|
|
enum ASM_OPERAND_TYPE {
|
|
_reg, // _r8, _r16, _r32
|
|
_m, // _m8, _m16, _m32, _m48
|
|
_imm, // _imm8, _imm16, _imm32, _imm64
|
|
_rel, // _rel8, _rel16, _rel32
|
|
_mnoi, // _m1616, _m1632
|
|
_p, // _p1616, _p1632
|
|
_rm, // _rm8, _rm16, _rm32
|
|
_float // Floating point operand, look at cRegmask for the
|
|
// actual size
|
|
};
|
|
|
|
// For amod (3 bits)
|
|
enum ASM_MODIFIERS {
|
|
_normal, // Normal register value
|
|
_rseg, // Segment registers
|
|
_rspecial, // Special registers
|
|
_addr16, // 16 bit address
|
|
_addr32, // 32 bit address
|
|
_fn16, // 16 bit function call
|
|
_fn32, // 32 bit function call
|
|
_flbl // Label
|
|
};
|
|
|
|
// For uRegmask (7 bits)
|
|
|
|
// uRegmask flags when aopty == _float
|
|
#define _rst 0x1
|
|
#define _rsti 0x2
|
|
#define _f64 0x4
|
|
#define _f80 0x8
|
|
#define _f112 0x10
|
|
#define _f128 0x20
|
|
|
|
// _seg register values (amod == _rseg)
|
|
//
|
|
#define _ds CONSTRUCT_FLAGS( 0, 0, _rseg, 0x01 )
|
|
#define _es CONSTRUCT_FLAGS( 0, 0, _rseg, 0x02 )
|
|
#define _ss CONSTRUCT_FLAGS( 0, 0, _rseg, 0x04 )
|
|
#define _fs CONSTRUCT_FLAGS( 0, 0, _rseg, 0x08 )
|
|
#define _gs CONSTRUCT_FLAGS( 0, 0, _rseg, 0x10 )
|
|
#define _cs CONSTRUCT_FLAGS( 0, 0, _rseg, 0x20 )
|
|
|
|
//
|
|
// _special register values
|
|
//
|
|
#define _crn CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x01 ) // CRn register (0,2,3)
|
|
#define _drn CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x02 ) // DRn register (0-3,6-7)
|
|
#define _trn CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x04 ) // TRn register (3-7)
|
|
#define _mm CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x08 ) // MMn register (0-7)
|
|
#define _xmm CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x10 ) // XMMn register (0-7)
|
|
#define _xmm0 CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x20 ) // XMM0 register
|
|
#define _ymm CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x40 ) // YMMn register (0-15)
|
|
|
|
//
|
|
// Default register values
|
|
//
|
|
|
|
#define _al CONSTRUCT_FLAGS( 0, 0, _normal, 0x01 ) // AL register
|
|
#define _ax CONSTRUCT_FLAGS( 0, 0, _normal, 0x02 ) // AX register
|
|
#define _eax CONSTRUCT_FLAGS( 0, 0, _normal, 0x04 ) // EAX register
|
|
#define _dx CONSTRUCT_FLAGS( 0, 0, _normal, 0x08 ) // DX register
|
|
#define _cl CONSTRUCT_FLAGS( 0, 0, _normal, 0x10 ) // CL register
|
|
#define _rax CONSTRUCT_FLAGS( 0, 0, _normal, 0x40 ) // RAX register
|
|
|
|
|
|
#define _rplus_r 0x20
|
|
#define _plus_r CONSTRUCT_FLAGS( 0, 0, 0, _rplus_r )
|
|
// Add the register to the opcode (no mod r/m)
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
#define ITprefix 0x10 // special prefix
|
|
#define ITjump 0x20 // jump instructions CALL, Jxx and LOOPxx
|
|
#define ITimmed 0x30 // value of an immediate operand controls
|
|
// code generation
|
|
#define ITopt 0x40 // not all operands are required
|
|
#define ITshift 0x50 // rotate and shift instructions
|
|
#define ITfloat 0x60 // floating point coprocessor instructions
|
|
#define ITdata 0x70 // DB, DW, DD, DQ, DT pseudo-ops
|
|
#define ITaddr 0x80 // DA (define addresss) pseudo-op
|
|
#define ITMASK 0xF0
|
|
#define ITSIZE 0x0F // mask for size
|
|
|
|
enum OP_DB
|
|
{
|
|
#if SCPP
|
|
// These are the number of bytes
|
|
OPdb = 1,
|
|
OPdw = 2,
|
|
OPdd = 4,
|
|
OPdq = 8,
|
|
OPdt = 10,
|
|
OPdf = 4,
|
|
OPde = 10,
|
|
OPds = 2,
|
|
OPdi = 4,
|
|
OPdl = 8,
|
|
#endif
|
|
#if MARS
|
|
// Integral types
|
|
OPdb,
|
|
OPds,
|
|
OPdi,
|
|
OPdl,
|
|
|
|
// Float types
|
|
OPdf,
|
|
OPdd,
|
|
OPde,
|
|
|
|
// Deprecated
|
|
OPdw = OPds,
|
|
OPdq = OPdl,
|
|
OPdt = OPde,
|
|
#endif
|
|
};
|
|
|
|
|
|
/* from iasm.c */
|
|
int asm_state(int iFlags);
|
|
|
|
void asm_process_fixup( block **ppblockLabels );
|
|
|
|
typedef struct _PTRNTAB4 {
|
|
unsigned usOpcode;
|
|
unsigned usFlags;
|
|
opflag_t usOp1;
|
|
opflag_t usOp2;
|
|
opflag_t usOp3;
|
|
opflag_t usOp4;
|
|
} PTRNTAB4, * PPTRNTAB4, ** PPPTRNTAB4;
|
|
|
|
typedef struct _PTRNTAB3 {
|
|
unsigned usOpcode;
|
|
unsigned usFlags;
|
|
opflag_t usOp1;
|
|
opflag_t usOp2;
|
|
opflag_t usOp3;
|
|
} PTRNTAB3, * PPTRNTAB3, ** PPPTRNTAB3;
|
|
|
|
typedef struct _PTRNTAB2 {
|
|
unsigned usOpcode;
|
|
unsigned usFlags;
|
|
opflag_t usOp1;
|
|
opflag_t usOp2;
|
|
} PTRNTAB2, * PPTRNTAB2, ** PPPTRNTAB2;
|
|
|
|
typedef struct _PTRNTAB1 {
|
|
unsigned usOpcode;
|
|
unsigned usFlags;
|
|
opflag_t usOp1;
|
|
} PTRNTAB1, * PPTRNTAB1, ** PPPTRNTAB1;
|
|
|
|
typedef struct _PTRNTAB0 {
|
|
unsigned usOpcode;
|
|
#define ASM_END 0xffff // special opcode meaning end of table
|
|
unsigned usFlags;
|
|
} PTRNTAB0, * PPTRNTAB0, ** PPPTRNTAB0;
|
|
|
|
typedef union _PTRNTAB {
|
|
PTRNTAB0 *pptb0;
|
|
PTRNTAB1 *pptb1;
|
|
PTRNTAB2 *pptb2;
|
|
PTRNTAB3 *pptb3;
|
|
PTRNTAB4 *pptb4;
|
|
} PTRNTAB, * PPTRNTAB, ** PPPTRNTAB;
|
|
|
|
typedef struct
|
|
{
|
|
unsigned char usNumops;
|
|
PTRNTAB ptb;
|
|
} OP;
|
|
|