mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-28 14:10:42 +03:00
4044 lines
112 KiB
C++
4044 lines
112 KiB
C++
//===-- gen/asm-x86.h - x86/x86_64 inline assembler handling ----*- C++ -*-===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file originates from work by David Friedman for GDC released under
|
||
// the GPL 2 and Artistic licenses. See the LICENSE file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
//
|
||
// Parses "DMD-style" x86/x86_64 inline assembly blocks and converts them to
|
||
// GDC/LLVM inline assembler syntax.
|
||
//
|
||
// This file is designed to be included twice, once with the ASM_X86_64 define
|
||
// set to get the 64 bit asm parser, and once without to get the 32 bit one.
|
||
// This is a direct result of merging two disparate but largely identical
|
||
// implementations, and should be refactored to just use a runtime parameter
|
||
// for choosing the architecture.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "dmd/expression.h"
|
||
#include "dmd/id.h"
|
||
#include "dmd/identifier.h"
|
||
#include "dmd/ldcbindings.h"
|
||
#include "dmd/mangle.h"
|
||
#include "gen/llvmhelpers.h" // printLabelName
|
||
#include <cctype>
|
||
#include "llvm/ADT/StringRef.h"
|
||
|
||
#ifndef ASM_X86_64
|
||
namespace AsmParserx8632 {
|
||
#else
|
||
namespace AsmParserx8664 {
|
||
#endif
|
||
|
||
typedef enum {
|
||
Reg_Invalid = -1,
|
||
Reg_EAX = 0,
|
||
Reg_EBX,
|
||
Reg_ECX,
|
||
Reg_EDX,
|
||
Reg_ESI,
|
||
Reg_EDI,
|
||
Reg_EBP,
|
||
Reg_ESP,
|
||
Reg_EIP,
|
||
Reg_ST,
|
||
Reg_ST1,
|
||
Reg_ST2,
|
||
Reg_ST3,
|
||
Reg_ST4,
|
||
Reg_ST5,
|
||
Reg_ST6,
|
||
Reg_ST7,
|
||
Reg_MM0,
|
||
Reg_MM1,
|
||
Reg_MM2,
|
||
Reg_MM3,
|
||
Reg_MM4,
|
||
Reg_MM5,
|
||
Reg_MM6,
|
||
Reg_MM7,
|
||
Reg_XMM0,
|
||
Reg_XMM1,
|
||
Reg_XMM2,
|
||
Reg_XMM3,
|
||
Reg_XMM4,
|
||
Reg_XMM5,
|
||
Reg_XMM6,
|
||
Reg_XMM7,
|
||
|
||
#ifdef ASM_X86_64
|
||
Reg_RAX,
|
||
Reg_RBX,
|
||
Reg_RCX,
|
||
Reg_RDX,
|
||
Reg_RSI,
|
||
Reg_RDI,
|
||
Reg_RBP,
|
||
Reg_RSP,
|
||
Reg_R8,
|
||
Reg_R9,
|
||
Reg_R10,
|
||
Reg_R11,
|
||
Reg_R12,
|
||
Reg_R13,
|
||
Reg_R14,
|
||
Reg_R15,
|
||
Reg_R8B,
|
||
Reg_R9B,
|
||
Reg_R10B,
|
||
Reg_R11B,
|
||
Reg_R12B,
|
||
Reg_R13B,
|
||
Reg_R14B,
|
||
Reg_R15B,
|
||
Reg_R8W,
|
||
Reg_R9W,
|
||
Reg_R10W,
|
||
Reg_R11W,
|
||
Reg_R12W,
|
||
Reg_R13W,
|
||
Reg_R14W,
|
||
Reg_R15W,
|
||
Reg_R8D,
|
||
Reg_R9D,
|
||
Reg_R10D,
|
||
Reg_R11D,
|
||
Reg_R12D,
|
||
Reg_R13D,
|
||
Reg_R14D,
|
||
Reg_R15D,
|
||
Reg_XMM8,
|
||
Reg_XMM9,
|
||
Reg_XMM10,
|
||
Reg_XMM11,
|
||
Reg_XMM12,
|
||
Reg_XMM13,
|
||
Reg_XMM14,
|
||
Reg_XMM15,
|
||
Reg_RIP,
|
||
Reg_SIL,
|
||
Reg_DIL,
|
||
Reg_BPL,
|
||
Reg_SPL,
|
||
#endif
|
||
|
||
Reg_EFLAGS,
|
||
Reg_CS,
|
||
Reg_DS,
|
||
Reg_SS,
|
||
Reg_ES,
|
||
Reg_FS,
|
||
Reg_GS,
|
||
Reg_AX,
|
||
Reg_BX,
|
||
Reg_CX,
|
||
Reg_DX,
|
||
Reg_SI,
|
||
Reg_DI,
|
||
Reg_BP,
|
||
Reg_SP,
|
||
Reg_AL,
|
||
Reg_AH,
|
||
Reg_BL,
|
||
Reg_BH,
|
||
Reg_CL,
|
||
Reg_CH,
|
||
Reg_DL,
|
||
Reg_DH,
|
||
Reg_CR0,
|
||
Reg_CR2,
|
||
Reg_CR3,
|
||
Reg_CR4,
|
||
Reg_DR0,
|
||
Reg_DR1,
|
||
Reg_DR2,
|
||
Reg_DR3,
|
||
Reg_DR6,
|
||
Reg_DR7,
|
||
Reg_TR3,
|
||
Reg_TR4,
|
||
Reg_TR5,
|
||
Reg_TR6,
|
||
Reg_TR7
|
||
} Reg;
|
||
|
||
static const int N_Regs = /*gp*/ 8 + /*EIP*/ 1 + /*fp*/ 8 + /*mmx*/ 8 + /*sse*/ 8 +
|
||
/*seg*/ 6 + /*16bit*/ 8 + /*8bit*/ 8 + /*sys*/ 4 + 6 +
|
||
5 + /*flags*/ 1
|
||
#ifdef ASM_X86_64
|
||
+ 8 /*RAX, etc*/
|
||
+ 8 /*R8-15*/
|
||
+ 4 /*SIL, etc. 8-bit*/
|
||
+ 8 /*R8-15B*/
|
||
+ 8 /*R8-15W*/
|
||
+ 8 /*R8-15D*/
|
||
+ 8 /*XMM8-15*/
|
||
+ 1 /*RIP*/
|
||
#endif
|
||
;
|
||
|
||
#define NULL_TREE ""
|
||
|
||
static struct {
|
||
llvm::StringRef name;
|
||
std::string gccName; // GAS will take upper case, but GCC won't (needed for
|
||
// the clobber list)
|
||
Identifier *ident;
|
||
char size;
|
||
signed char baseReg; // %% todo: Reg, Reg_XX
|
||
} regInfo[N_Regs] = {
|
||
{"EAX", NULL_TREE, nullptr, 4, Reg_EAX},
|
||
{"EBX", NULL_TREE, nullptr, 4, Reg_EBX},
|
||
{"ECX", NULL_TREE, nullptr, 4, Reg_ECX},
|
||
{"EDX", NULL_TREE, nullptr, 4, Reg_EDX},
|
||
{"ESI", NULL_TREE, nullptr, 4, Reg_ESI},
|
||
{"EDI", NULL_TREE, nullptr, 4, Reg_EDI},
|
||
{"EBP", NULL_TREE, nullptr, 4, Reg_EBP},
|
||
{"ESP", NULL_TREE, nullptr, 4, Reg_ESP},
|
||
{"EIP", NULL_TREE, nullptr, 4, Reg_EIP},
|
||
{"ST", NULL_TREE, nullptr, 10, Reg_ST},
|
||
{"ST(1)", NULL_TREE, nullptr, 10, Reg_ST1},
|
||
{"ST(2)", NULL_TREE, nullptr, 10, Reg_ST2},
|
||
{"ST(3)", NULL_TREE, nullptr, 10, Reg_ST3},
|
||
{"ST(4)", NULL_TREE, nullptr, 10, Reg_ST4},
|
||
{"ST(5)", NULL_TREE, nullptr, 10, Reg_ST5},
|
||
{"ST(6)", NULL_TREE, nullptr, 10, Reg_ST6},
|
||
{"ST(7)", NULL_TREE, nullptr, 10, Reg_ST7},
|
||
{"MM0", NULL_TREE, nullptr, 8, Reg_MM0},
|
||
{"MM1", NULL_TREE, nullptr, 8, Reg_MM1},
|
||
{"MM2", NULL_TREE, nullptr, 8, Reg_MM2},
|
||
{"MM3", NULL_TREE, nullptr, 8, Reg_MM3},
|
||
{"MM4", NULL_TREE, nullptr, 8, Reg_MM4},
|
||
{"MM5", NULL_TREE, nullptr, 8, Reg_MM5},
|
||
{"MM6", NULL_TREE, nullptr, 8, Reg_MM6},
|
||
{"MM7", NULL_TREE, nullptr, 8, Reg_MM7},
|
||
{"XMM0", NULL_TREE, nullptr, 16, Reg_XMM0},
|
||
{"XMM1", NULL_TREE, nullptr, 16, Reg_XMM1},
|
||
{"XMM2", NULL_TREE, nullptr, 16, Reg_XMM2},
|
||
{"XMM3", NULL_TREE, nullptr, 16, Reg_XMM3},
|
||
{"XMM4", NULL_TREE, nullptr, 16, Reg_XMM4},
|
||
{"XMM5", NULL_TREE, nullptr, 16, Reg_XMM5},
|
||
{"XMM6", NULL_TREE, nullptr, 16, Reg_XMM6},
|
||
{"XMM7", NULL_TREE, nullptr, 16, Reg_XMM7},
|
||
|
||
#ifdef ASM_X86_64
|
||
{"RAX", NULL_TREE, nullptr, 8, Reg_RAX},
|
||
{"RBX", NULL_TREE, nullptr, 8, Reg_RBX},
|
||
{"RCX", NULL_TREE, nullptr, 8, Reg_RCX},
|
||
{"RDX", NULL_TREE, nullptr, 8, Reg_RDX},
|
||
{"RSI", NULL_TREE, nullptr, 8, Reg_RSI},
|
||
{"RDI", NULL_TREE, nullptr, 8, Reg_RDI},
|
||
{"RBP", NULL_TREE, nullptr, 8, Reg_RBP},
|
||
{"RSP", NULL_TREE, nullptr, 8, Reg_RSP},
|
||
{"R8", NULL_TREE, nullptr, 8, Reg_R8},
|
||
{"R9", NULL_TREE, nullptr, 8, Reg_R9},
|
||
{"R10", NULL_TREE, nullptr, 8, Reg_R10},
|
||
{"R11", NULL_TREE, nullptr, 8, Reg_R11},
|
||
{"R12", NULL_TREE, nullptr, 8, Reg_R12},
|
||
{"R13", NULL_TREE, nullptr, 8, Reg_R13},
|
||
{"R14", NULL_TREE, nullptr, 8, Reg_R14},
|
||
{"R15", NULL_TREE, nullptr, 8, Reg_R15},
|
||
{"R8B", NULL_TREE, nullptr, 1, Reg_R8},
|
||
{"R9B", NULL_TREE, nullptr, 1, Reg_R9},
|
||
{"R10B", NULL_TREE, nullptr, 1, Reg_R10},
|
||
{"R11B", NULL_TREE, nullptr, 1, Reg_R11},
|
||
{"R12B", NULL_TREE, nullptr, 1, Reg_R12},
|
||
{"R13B", NULL_TREE, nullptr, 1, Reg_R13},
|
||
{"R14B", NULL_TREE, nullptr, 1, Reg_R14},
|
||
{"R15B", NULL_TREE, nullptr, 1, Reg_R15},
|
||
{"R8W", NULL_TREE, nullptr, 2, Reg_R8},
|
||
{"R9W", NULL_TREE, nullptr, 2, Reg_R9},
|
||
{"R10W", NULL_TREE, nullptr, 2, Reg_R10},
|
||
{"R11W", NULL_TREE, nullptr, 2, Reg_R11},
|
||
{"R12W", NULL_TREE, nullptr, 2, Reg_R12},
|
||
{"R13W", NULL_TREE, nullptr, 2, Reg_R13},
|
||
{"R14W", NULL_TREE, nullptr, 2, Reg_R14},
|
||
{"R15W", NULL_TREE, nullptr, 2, Reg_R15},
|
||
{"R8D", NULL_TREE, nullptr, 4, Reg_R8},
|
||
{"R9D", NULL_TREE, nullptr, 4, Reg_R9},
|
||
{"R10D", NULL_TREE, nullptr, 4, Reg_R10},
|
||
{"R11D", NULL_TREE, nullptr, 4, Reg_R11},
|
||
{"R12D", NULL_TREE, nullptr, 4, Reg_R12},
|
||
{"R13D", NULL_TREE, nullptr, 4, Reg_R13},
|
||
{"R14D", NULL_TREE, nullptr, 4, Reg_R14},
|
||
{"R15D", NULL_TREE, nullptr, 4, Reg_R15},
|
||
{"XMM8", NULL_TREE, nullptr, 16, Reg_XMM8},
|
||
{"XMM9", NULL_TREE, nullptr, 16, Reg_XMM9},
|
||
{"XMM10", NULL_TREE, nullptr, 16, Reg_XMM10},
|
||
{"XMM11", NULL_TREE, nullptr, 16, Reg_XMM11},
|
||
{"XMM12", NULL_TREE, nullptr, 16, Reg_XMM12},
|
||
{"XMM13", NULL_TREE, nullptr, 16, Reg_XMM13},
|
||
{"XMM14", NULL_TREE, nullptr, 16, Reg_XMM14},
|
||
{"XMM15", NULL_TREE, nullptr, 16, Reg_XMM15},
|
||
{"RIP", NULL_TREE, nullptr, 8, Reg_RIP},
|
||
{"SIL", NULL_TREE, nullptr, 1, Reg_SIL},
|
||
{"DIL", NULL_TREE, nullptr, 1, Reg_DIL},
|
||
{"BPL", NULL_TREE, nullptr, 1, Reg_BPL},
|
||
{"SPL", NULL_TREE, nullptr, 1, Reg_SPL},
|
||
#endif
|
||
|
||
{"FLAGS", NULL_TREE, nullptr, 0,
|
||
Reg_EFLAGS}, // the gcc name is "flags"; not used in assembler input
|
||
{"CS", NULL_TREE, nullptr, 2, -1},
|
||
{"DS", NULL_TREE, nullptr, 2, -1},
|
||
{"SS", NULL_TREE, nullptr, 2, -1},
|
||
{"ES", NULL_TREE, nullptr, 2, -1},
|
||
{"FS", NULL_TREE, nullptr, 2, -1},
|
||
{"GS", NULL_TREE, nullptr, 2, -1},
|
||
{"AX", NULL_TREE, nullptr, 2, Reg_EAX},
|
||
{"BX", NULL_TREE, nullptr, 2, Reg_EBX},
|
||
{"CX", NULL_TREE, nullptr, 2, Reg_ECX},
|
||
{"DX", NULL_TREE, nullptr, 2, Reg_EDX},
|
||
{"SI", NULL_TREE, nullptr, 2, Reg_ESI},
|
||
{"DI", NULL_TREE, nullptr, 2, Reg_EDI},
|
||
{"BP", NULL_TREE, nullptr, 2, Reg_EBP},
|
||
{"SP", NULL_TREE, nullptr, 2, Reg_ESP},
|
||
{"AL", NULL_TREE, nullptr, 1, Reg_EAX},
|
||
{"AH", NULL_TREE, nullptr, 1, Reg_EAX},
|
||
{"BL", NULL_TREE, nullptr, 1, Reg_EBX},
|
||
{"BH", NULL_TREE, nullptr, 1, Reg_EBX},
|
||
{"CL", NULL_TREE, nullptr, 1, Reg_ECX},
|
||
{"CH", NULL_TREE, nullptr, 1, Reg_ECX},
|
||
{"DL", NULL_TREE, nullptr, 1, Reg_EDX},
|
||
{"DH", NULL_TREE, nullptr, 1, Reg_EDX},
|
||
{"CR0", NULL_TREE, nullptr, 0, -1},
|
||
{"CR2", NULL_TREE, nullptr, 0, -1},
|
||
{"CR3", NULL_TREE, nullptr, 0, -1},
|
||
{"CR4", NULL_TREE, nullptr, 0, -1},
|
||
{"DR0", NULL_TREE, nullptr, 0, -1},
|
||
{"DR1", NULL_TREE, nullptr, 0, -1},
|
||
{"DR2", NULL_TREE, nullptr, 0, -1},
|
||
{"DR3", NULL_TREE, nullptr, 0, -1},
|
||
{"DR6", NULL_TREE, nullptr, 0, -1},
|
||
{"DR7", NULL_TREE, nullptr, 0, -1},
|
||
{"TR3", NULL_TREE, nullptr, 0, -1},
|
||
{"TR4", NULL_TREE, nullptr, 0, -1},
|
||
{"TR5", NULL_TREE, nullptr, 0, -1},
|
||
{"TR6", NULL_TREE, nullptr, 0, -1},
|
||
{"TR7", NULL_TREE, nullptr, 0, -1}};
|
||
|
||
typedef enum {
|
||
No_Type_Needed,
|
||
Int_Types,
|
||
Word_Types, // same as Int_Types, but byte is not allowed
|
||
FP_Types,
|
||
FPInt_Types,
|
||
Byte_NoType, // byte only, but no type suffix
|
||
} TypeNeeded;
|
||
|
||
typedef enum { No_Link, Out_Mnemonic, Next_Form } OpLink;
|
||
|
||
typedef enum {
|
||
Clb_SizeAX = 0x01,
|
||
Clb_SizeDXAX = 0x02,
|
||
Clb_EAX = 0x03,
|
||
#ifndef ASM_X86_64
|
||
Clb_DXAX_Mask = 0x03,
|
||
#else
|
||
Clb_DXAX_Mask = 0x103,
|
||
Clb_SizeRDXRAX = 0x100,
|
||
#endif
|
||
|
||
Clb_Flags = 0x04,
|
||
Clb_DI = 0x08,
|
||
Clb_SI = 0x10,
|
||
Clb_CX = 0x20,
|
||
Clb_ST = 0x40,
|
||
Clb_SP = 0x80 // Doesn't actually let GCC know the frame pointer is modified
|
||
} ImplicitClober;
|
||
|
||
// "^ +/..\([A-Za-z_0-9]+\).*" -> " \1,"
|
||
typedef enum {
|
||
Op_Invalid,
|
||
Op_Adjust,
|
||
Op_Dst,
|
||
Op_Upd,
|
||
Op_DstW,
|
||
Op_DstF,
|
||
Op_UpdF,
|
||
Op_DstSrc,
|
||
Op_DstSrcF,
|
||
Op_UpdSrcF,
|
||
Op_DstSrcFW,
|
||
Op_UpdSrcFW,
|
||
Op_DstSrcSSE,
|
||
Op_UpdSrcSSE,
|
||
Op_DstSrcMMX,
|
||
Op_DstSrcImmS,
|
||
Op_DstSrcImmM,
|
||
Op_ExtSrcImmS,
|
||
Op_UpdSrcShft,
|
||
Op_DstSrcNT,
|
||
Op_UpdSrcNT,
|
||
Op_DstMemNT,
|
||
Op_DstRMBNT,
|
||
Op_DstRMWNT,
|
||
Op_UpdUpd,
|
||
Op_UpdUpdF,
|
||
Op_Src,
|
||
Op_SrcRMWNT,
|
||
Op_SrcW,
|
||
Op_SrcImm,
|
||
Op_Src_DXAXF,
|
||
Op_SrcMemNT,
|
||
Op_SrcMemNTF,
|
||
Op_SrcSrc,
|
||
Op_SrcSrcF,
|
||
Op_SrcSrcFW,
|
||
Op_SrcSrcSSEF,
|
||
Op_SrcSrcMMX,
|
||
Op_Shift,
|
||
Op_Branch,
|
||
Op_CBranch,
|
||
Op_0,
|
||
Op_0_AX,
|
||
Op_0_DXAX,
|
||
Op_0_DXCXAX,
|
||
Op_Loop,
|
||
Op_Flags,
|
||
Op_F0_ST,
|
||
Op_F0_P,
|
||
Op_Fs_P,
|
||
Op_Fis,
|
||
Op_Fis_ST,
|
||
Op_Fis_P,
|
||
Op_Fid,
|
||
Op_Fid_P,
|
||
Op_FidR_P,
|
||
Op_Ffd,
|
||
Op_FfdR,
|
||
Op_Ffd_P,
|
||
Op_FfdR_P,
|
||
Op_FfdRR_P,
|
||
Op_Fd_P,
|
||
Op_FdST,
|
||
Op_FMath,
|
||
Op_FMath0,
|
||
Op_FMath2,
|
||
Op_FdSTiSTi,
|
||
Op_FdST0ST1,
|
||
Op_FPMath,
|
||
Op_FCmp,
|
||
Op_FCmp1,
|
||
Op_FCmpP,
|
||
Op_FCmpP1,
|
||
Op_FCmpFlg,
|
||
Op_FCmpFlgP,
|
||
Op_fld,
|
||
Op_fldR,
|
||
Op_fxch,
|
||
Op_fxch1,
|
||
Op_fxch0,
|
||
Op_SizedStack,
|
||
Op_bound,
|
||
Op_bswap,
|
||
Op_cmps,
|
||
Op_cmpsd,
|
||
Op_cmpsX,
|
||
Op_cmpxchg,
|
||
#ifdef ASM_X86_64
|
||
Op_cmpxchg16b,
|
||
#endif
|
||
Op_cmpxchg8b,
|
||
Op_cpuid,
|
||
Op_enter,
|
||
Op_fdisi,
|
||
Op_feni,
|
||
Op_fsetpm,
|
||
Op_fXstsw,
|
||
Op_imul,
|
||
Op_imul2,
|
||
Op_imul1,
|
||
Op_in,
|
||
Op_ins,
|
||
Op_insX,
|
||
Op_iret,
|
||
Op_iretd,
|
||
#ifdef ASM_X86_64
|
||
Op_iretq,
|
||
#endif
|
||
Op_lods,
|
||
Op_lodsX,
|
||
Op_movs,
|
||
Op_movsd,
|
||
Op_movsX,
|
||
Op_movsx,
|
||
Op_movzx,
|
||
Op_mul,
|
||
Op_out,
|
||
Op_outs,
|
||
Op_outsX,
|
||
Op_push,
|
||
Op_ret,
|
||
Op_retf,
|
||
Op_scas,
|
||
Op_scasX,
|
||
Op_stos,
|
||
Op_stosX,
|
||
Op_xgetbv,
|
||
Op_xlat,
|
||
N_AsmOpInfo,
|
||
Op_Align,
|
||
Op_Even,
|
||
Op_Naked,
|
||
Op_db,
|
||
Op_ds,
|
||
Op_di,
|
||
Op_dl,
|
||
Op_df,
|
||
Op_dd,
|
||
Op_de
|
||
} AsmOp;
|
||
|
||
typedef enum {
|
||
Opr_None = 0,
|
||
OprC_MRI = 1,
|
||
OprC_MR = 2,
|
||
OprC_Mem = 3,
|
||
OprC_Reg = 4,
|
||
OprC_Imm = 5,
|
||
OprC_SSE = 6,
|
||
OprC_SSE_Mem = 7,
|
||
OprC_R32 = 8,
|
||
OprC_RWord = 9,
|
||
OprC_RFP = 10,
|
||
OprC_AbsRel = 11,
|
||
OprC_Relative = 12,
|
||
OprC_Port = 13, // DX or imm
|
||
OprC_AX = 14, // AL,AX,EAX
|
||
OprC_DX = 15, // only DX
|
||
OprC_MMX = 16,
|
||
OprC_MMX_Mem = 17,
|
||
OprC_Shift = 18, // imm or CL
|
||
|
||
Opr_ClassMask = 0x1f,
|
||
|
||
Opr_Dest = 0x20,
|
||
Opr_Update = 0x60,
|
||
|
||
Opr_NoType = 0x80,
|
||
} OprVals;
|
||
|
||
typedef unsigned char Opr;
|
||
|
||
struct AsmOpInfo {
|
||
Opr operands[3];
|
||
#ifndef ASM_X86_64
|
||
unsigned char needsType : 3, implicitClobbers : 8, linkType : 2;
|
||
#else
|
||
unsigned short needsType : 3, implicitClobbers : 9, linkType : 2;
|
||
#endif
|
||
unsigned link;
|
||
|
||
unsigned nOperands() {
|
||
if (!operands[0]) {
|
||
return 0;
|
||
}
|
||
if (!operands[1]) {
|
||
return 1;
|
||
}
|
||
if (!operands[2]) {
|
||
return 2;
|
||
}
|
||
return 3;
|
||
}
|
||
};
|
||
|
||
typedef enum {
|
||
Mn_fdisi,
|
||
Mn_feni,
|
||
Mn_fsetpm,
|
||
Mn_iretw,
|
||
Mn_iret,
|
||
#ifdef ASM_X86_64
|
||
Mn_iretq,
|
||
#endif
|
||
Mn_lret,
|
||
Mn_cmpxchg8b,
|
||
#ifdef ASM_X86_64
|
||
Mn_cmpxchg16b,
|
||
#endif
|
||
N_AltMn
|
||
} Alternate_Mnemonics;
|
||
|
||
static const char *alternateMnemonics[N_AltMn] = {
|
||
".byte 0xdb, 0xe1",
|
||
".byte 0xdb, 0xe0",
|
||
".byte 0xdb, 0xe4",
|
||
"iretw",
|
||
"iret",
|
||
#ifdef ASM_X86_64
|
||
"iretq",
|
||
#endif
|
||
"lret",
|
||
"cmpxchg8b",
|
||
#ifdef ASM_X86_64
|
||
"cmpxchg16b",
|
||
#endif
|
||
};
|
||
|
||
#define mri OprC_MRI
|
||
#define mr OprC_MR
|
||
#define mem OprC_Mem
|
||
// for now mfp=mem
|
||
#define mfp OprC_Mem
|
||
#define reg OprC_Reg
|
||
#define imm OprC_Imm
|
||
#define sse OprC_SSE
|
||
#define ssem OprC_SSE_Mem
|
||
#define mmx OprC_MMX
|
||
#define mmxm OprC_MMX_Mem
|
||
#define r32 OprC_R32
|
||
#define rw OprC_RWord
|
||
#define rfp OprC_RFP
|
||
#define port OprC_Port
|
||
#define ax OprC_AX
|
||
#define dx OprC_DX
|
||
#define shft OprC_Shift
|
||
#define D Opr_Dest
|
||
#define U Opr_Update
|
||
#define N Opr_NoType
|
||
|
||
// D=dest, N=notype
|
||
// clang-format off
|
||
static AsmOpInfo asmOpInfo[N_AsmOpInfo] = {
|
||
/* Op_Invalid */ {},
|
||
/* Op_Adjust */ {{0, 0, 0}, 0, Clb_EAX /*just AX*/},
|
||
/* Op_Dst */ {{D | mr, 0, 0}, 1},
|
||
/* Op_Upd */ {{U | mr, 0, 0}, 1},
|
||
/* Op_DstW */ {{D | mr, 0, 0}, Word_Types},
|
||
/* Op_DstF */ {{D | mr, 0, 0}, 1, Clb_Flags},
|
||
/* Op_UpdF */ {{U | mr, 0, 0}, 1, Clb_Flags},
|
||
/* Op_DstSrc */ {{D | mr, mri, 0}, /**/ 1},
|
||
/* Op_DstSrcF */ {{D | mr, mri, 0}, /**/ 1, Clb_Flags},
|
||
/* Op_UpdSrcF */ {{U | mr, mri, 0}, /**/ 1, Clb_Flags},
|
||
/* Op_DstSrcFW */ {{D | mr, mri, 0}, /**/ Word_Types, Clb_Flags},
|
||
/* Op_UpdSrcFW */ {{U | mr, mri, 0}, /**/ Word_Types, Clb_Flags},
|
||
/* Op_DstSrcSSE */ {{D | sse, ssem, 0}},
|
||
/* Op_UpdSrcSSE */ {{U | sse, ssem, 0}}, // some may not be update %%
|
||
/* Op_DstSrcMMX */ {{U | mmx, mmxm, 0}}, // some may not be update %%
|
||
/* Op_DstSrcImmS*/ {{U | sse, ssem, N | imm}}, // some may not be update %%
|
||
/* Op_DstSrcImmM*/ {{U | mmx, mmxm, N | imm}}, // some may not be update %%
|
||
/* Op_ExtSrcImmS*/ {{D | mr, sse, N | imm}}, // used for extractps
|
||
/* Op_UpdSrcShft*/ {{U | mr, reg, N | shft}, 1, Clb_Flags}, // 16/32 only
|
||
/* Op_DstSrcNT */ {{D | mr, mr, 0},
|
||
0}, // used for movd .. operands can be rm32,sse,mmx
|
||
/* Op_UpdSrcNT */ {{U | mr, mr, 0},
|
||
0}, // used for movd .. operands can be rm32,sse,mmx
|
||
/* Op_DstMemNT */ {{D | mem, 0, 0}},
|
||
/* Op_DstRMBNT */ {{D | mr, 0, 0}, Byte_NoType},
|
||
/* Op_DstRMWNT */ {{D | mr, 0, 0}},
|
||
/* Op_UpdUpd */ {{U | mr, U | mr, 0}, /**/ 1},
|
||
/* Op_UpdUpdF */ {{U | mr, U | mr, 0}, /**/ 1, Clb_Flags},
|
||
/* Op_Src */ {{mri, 0, 0}, 1},
|
||
/* Op_SrcRMWNT */ {{mr, 0, 0}, 0},
|
||
/* Op_SrcW */ {{mri, 0, 0}, Word_Types},
|
||
/* Op_SrcImm */ {{imm, 0, 0}},
|
||
/* Op_Src_DXAXF */ {{mr, 0, 0}, 1, Clb_SizeDXAX | Clb_Flags},
|
||
/* Op_SrcMemNT */ {{mem, 0, 0}},
|
||
/* Op_SrcMemNTF */ {{mem, 0, 0}, 0, Clb_Flags},
|
||
/* Op_SrcSrc */ {{mr, mri, 0}, 1},
|
||
/* Op_SrcSrcF */ {{mr, mri, 0}, 1, Clb_Flags},
|
||
/* Op_SrcSrcFW */ {{mr, mri, 0}, Word_Types, Clb_Flags},
|
||
/* Op_SrcSrcSSEF*/ {{sse, ssem, 0}, 0, Clb_Flags},
|
||
/* Op_SrcSrcMMX */ {{mmx, mmx, 0}},
|
||
/* Op_Shift */ {{D | mr, N | shft, 0}, /**/ 1, Clb_Flags},
|
||
/* Op_Branch */ {{mri, 0, 0}},
|
||
/* Op_CBranch */ {{imm, 0, 0}},
|
||
/* Op_0 */ {{0, 0, 0}},
|
||
/* Op_0_AX */ {{0, 0, 0}, 0, Clb_SizeAX},
|
||
/* Op_0_DXAX */ {{0, 0, 0}, 0, Clb_SizeDXAX}, // but for cwd/cdq -- how
|
||
// do know the size..
|
||
/* Op_0_DXCXAX*/ {{0, 0, 0}, 0, Clb_SizeDXAX | Clb_CX},
|
||
/* Op_Loop */ {{imm, 0, 0}, 0, Clb_CX},
|
||
/* Op_Flags */ {{0, 0, 0}, 0, Clb_Flags},
|
||
/* Op_F0_ST */ {{0, 0, 0}, 0, Clb_ST},
|
||
/* Op_F0_P */ {{0, 0, 0}, 0, Clb_ST}, // push, pops, etc. not sure how
|
||
// to inform gcc..
|
||
/* Op_Fs_P */ {{mem, 0, 0}, 0, Clb_ST}, // "
|
||
/* Op_Fis */ {{mem, 0, 0}, FPInt_Types}, // only 16bit and 32bit, DMD
|
||
// defaults to 16bit
|
||
/* Op_Fis_ST */ {{mem, 0, 0}, FPInt_Types, Clb_ST}, // "
|
||
/* Op_Fis_P */ {{mem, 0, 0},
|
||
FPInt_Types,
|
||
Clb_ST}, // push and pop, fild so also 64 bit
|
||
/* Op_Fid */ {{D | mem, 0, 0}, FPInt_Types}, // only 16bit and 32bit,
|
||
// DMD defaults to 16bit
|
||
/* Op_Fid_P */ {{D | mem, 0, 0},
|
||
FPInt_Types,
|
||
Clb_ST,
|
||
Next_Form,
|
||
Op_FidR_P}, // push and pop, fild so also 64 bit
|
||
/* Op_FidR_P */ {{D | mem, rfp, 0},
|
||
FPInt_Types,
|
||
Clb_ST}, // push and pop, fild so also 64 bit
|
||
/* Op_Ffd */ {{D | mfp, 0, 0},
|
||
FP_Types,
|
||
0,
|
||
Next_Form,
|
||
Op_FfdR}, // only 16bit and 32bit, DMD defaults to
|
||
// 16bit, reg form doesn't need type
|
||
/* Op_FfdR */ {{D | rfp, 0, 0}},
|
||
/* Op_Ffd_P */ {{D | mfp, 0, 0},
|
||
FP_Types,
|
||
Clb_ST,
|
||
Next_Form,
|
||
Op_FfdR_P}, // pop, fld so also 80 bit, "
|
||
/* Op_FfdR_P */ {{D | rfp, 0, 0}, 0, Clb_ST, Next_Form, Op_FfdRR_P},
|
||
/* Op_FfdRR_P */ {{D | rfp, rfp, 0}, 0, Clb_ST},
|
||
/* Op_Fd_P */ {{D | mem, 0, 0}, 0, Clb_ST}, // "
|
||
/* Op_FdST */ {{D | rfp, 0, 0}},
|
||
/* Op_FMath */ {{mfp, 0, 0},
|
||
FP_Types,
|
||
Clb_ST,
|
||
Next_Form,
|
||
Op_FMath0}, // and only single or double prec
|
||
/* Op_FMath0 */ {{0, 0, 0}, 0, Clb_ST, Next_Form, Op_FMath2}, // pops
|
||
/* Op_FMath2 */ {{D | rfp, rfp, 0},
|
||
0,
|
||
Clb_ST,
|
||
Next_Form,
|
||
Op_FdST0ST1}, // and only single or double prec
|
||
/* Op_FdSTiSTi */ {{D | rfp, rfp, 0}},
|
||
/* Op_FdST0ST1 */ {{0, 0, 0}},
|
||
/* Op_FPMath */ {{D | rfp, rfp, 0},
|
||
0,
|
||
Clb_ST,
|
||
Next_Form,
|
||
Op_F0_P}, // pops
|
||
/* Op_FCmp */ {{mfp, 0, 0},
|
||
FP_Types,
|
||
0,
|
||
Next_Form,
|
||
Op_FCmp1}, // DMD defaults to float ptr
|
||
/* Op_FCmp1 */ {{rfp, 0, 0}, 0, 0, Next_Form, Op_0},
|
||
/* Op_FCmpP */ {{mfp, 0, 0}, FP_Types, 0, Next_Form, Op_FCmpP1}, // pops
|
||
/* Op_FCmpP1 */ {{rfp, 0, 0}, 0, 0, Next_Form, Op_F0_P}, // pops
|
||
/* Op_FCmpFlg */ {{rfp, 0, 0}, 0, Clb_Flags},
|
||
/* Op_FCmpFlgP */ {{rfp, 0, 0}, 0, Clb_Flags}, // pops
|
||
/* Op_fld */ {{mfp, 0, 0}, FP_Types, Clb_ST, Next_Form, Op_fldR},
|
||
/* Op_fldR */ {{rfp, 0, 0}, 0, Clb_ST},
|
||
/* Op_fxch */ {{D | rfp, D | rfp, 0},
|
||
0,
|
||
Clb_ST,
|
||
Next_Form,
|
||
Op_fxch1}, // not in intel manual?, but DMD allows it
|
||
// (gas won't), second arg must be ST
|
||
/* Op_fxch1 */ {{D | rfp, 0, 0}, 0, Clb_ST, Next_Form, Op_fxch0},
|
||
/* Op_fxch0 */ {{0, 0, 0}, 0, Clb_ST}, // Also clobbers ST(1)
|
||
/* Op_SizedStack*/ {{0, 0, 0}, 0, Clb_SP}, // type suffix special case
|
||
/* Op_bound */ {{mr, mri, 0},
|
||
Word_Types}, // operands *not* reversed for gas
|
||
/* Op_bswap */ {{D | r32, 0, 0}},
|
||
/* Op_cmps */ {{mem, mem, 0}, 1, Clb_DI | Clb_SI | Clb_Flags},
|
||
/* Op_cmpsd */ {{0, 0, 0},
|
||
0,
|
||
Clb_DI | Clb_SI | Clb_Flags,
|
||
Next_Form,
|
||
Op_DstSrcImmS},
|
||
/* Op_cmpsX */ {{0, 0, 0}, 0, Clb_DI | Clb_SI | Clb_Flags},
|
||
/* Op_cmpxchg */ {{D | mr, reg, 0}, 1, Clb_SizeAX | Clb_Flags},
|
||
#ifdef ASM_X86_64
|
||
/* Op_cmpxchg16b */ {{D | mem /*128*/, 0, 0},
|
||
0,
|
||
Clb_SizeRDXRAX /*64*/ | Clb_Flags,
|
||
Out_Mnemonic,
|
||
Mn_cmpxchg16b},
|
||
#endif
|
||
/* Op_cmpxchg8b */ {{D | mem /*64*/, 0, 0},
|
||
0,
|
||
Clb_SizeDXAX /*32*/ | Clb_Flags,
|
||
Out_Mnemonic,
|
||
Mn_cmpxchg8b},
|
||
/* Op_cpuid */ {{0, 0, 0}}, // Clobbers eax, ebx, ecx, and edx. Handled
|
||
// specially below.
|
||
/* Op_enter */ {{imm, imm, 0}}, // operands *not* reversed for gas, %%
|
||
// inform gcc of EBP clobber?,
|
||
/* Op_fdisi */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_fdisi},
|
||
/* Op_feni */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_feni},
|
||
/* Op_fsetpm */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_fsetpm},
|
||
/* Op_fXstsw */ {{D | mr, 0, 0}}, // ax is the only allowed register
|
||
/* Op_imul */ {{D | reg, mr, imm},
|
||
1,
|
||
Clb_Flags,
|
||
Next_Form,
|
||
Op_imul2}, // 16/32 only
|
||
/* Op_imul2 */ {{D | reg, mri, 0},
|
||
1,
|
||
Clb_Flags,
|
||
Next_Form,
|
||
Op_imul1}, // 16/32 only
|
||
/* Op_imul1 */ {{mr, 0, 0}, 1, Clb_Flags | Clb_SizeDXAX},
|
||
/* Op_in */ {{D | ax, N | port, 0}, 1},
|
||
/* Op_ins */ {{mem, N | dx, 0},
|
||
1,
|
||
Clb_DI}, // can't override ES segment for this one
|
||
/* Op_insX */ {{0, 0, 0},
|
||
0,
|
||
Clb_DI}, // output segment overrides %% needs work
|
||
/* Op_iret */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_iretw},
|
||
/* Op_iretd */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_iret},
|
||
#ifdef ASM_X86_64
|
||
/* Op_iretq */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_iretq},
|
||
#endif
|
||
/* Op_lods */ {{mem, 0, 0}, 1, Clb_SI},
|
||
/* Op_lodsX */ {{0, 0, 0}, 0, Clb_SI},
|
||
/* Op_movs */ {{mem, mem, 0},
|
||
1,
|
||
Clb_DI | Clb_SI}, // only src/DS can be overridden
|
||
/* Op_movsd */ {{0, 0, 0},
|
||
0,
|
||
Clb_DI | Clb_SI,
|
||
Next_Form,
|
||
Op_DstSrcSSE}, // %% gas doesn't accept movsd .. has to
|
||
// movsl
|
||
/* Op_movsX */ {{0, 0, 0}, 0, Clb_DI | Clb_SI},
|
||
/* Op_movsx */ {{D | reg, mr, 0}, 1}, // type suffix is special case
|
||
/* Op_movzx */ {{D | reg, mr, 0}, 1}, // type suffix is special case
|
||
/* Op_mul */ {{U | ax, mr, 0},
|
||
1,
|
||
Clb_SizeDXAX | Clb_Flags,
|
||
Next_Form,
|
||
Op_Src_DXAXF},
|
||
/* Op_out */ {{N | port, ax, 0}, 1},
|
||
/* Op_outs */ {{N | dx, mem, 0}, 1, Clb_SI},
|
||
/* Op_outsX */ {{0, 0, 0}, 0, Clb_SI},
|
||
#ifndef ASM_X86_64
|
||
/* Op_push */ {{mri, 0, 0}, Word_Types, Clb_SP}, // would be Op_SrcW,
|
||
// but DMD defaults to
|
||
// 32-bit for
|
||
// immediate form
|
||
#else
|
||
/* Op_push */ {{mri, 0, 0}, 0, Clb_SP}, // would be Op_SrcW, but DMD
|
||
// defaults to 32-bit for
|
||
// immediate form
|
||
#endif
|
||
/* Op_ret */ {{imm, 0, 0}, 0, 0, Next_Form, Op_0},
|
||
/* Op_retf */ {{0, 0, 0}, 0, 0, Out_Mnemonic, Mn_lret},
|
||
/* Op_scas */ {{mem, 0, 0}, 1, Clb_DI | Clb_Flags},
|
||
/* Op_scasX */ {{0, 0, 0}, 0, Clb_DI | Clb_Flags},
|
||
/* Op_stos */ {{mem, 0, 0}, 1, Clb_DI},
|
||
/* Op_stosX */ {{0, 0, 0}, 0, Clb_DI},
|
||
#ifndef ASM_X86_64
|
||
/* Op_xgetbv */ {{0, 0, 0}, 0, Clb_SizeDXAX},
|
||
#else
|
||
/* Op_xgetbv */ {{0, 0, 0}, 0, Clb_SizeRDXRAX},
|
||
#endif
|
||
/* Op_xlat */ {{mem, 0, 0}, 0, Clb_SizeAX}
|
||
|
||
/// * Op_arpl */ { D|mr, reg }, // 16 only -> DstSrc
|
||
/// * Op_bsX */ { rw, mrw, 0, 1, Clb_Flags },//->srcsrcf
|
||
/// * Op_bt */ { mrw, riw, 0, 1, Clb_Flags },//->srcsrcf
|
||
/// * Op_btX */ { D|mrw, riw, 0, 1, Clb_Flags },//->dstsrcf ..
|
||
/// immediate does not contribute to size
|
||
/// * Op_cmovCC */ { D|rw, mrw, 0, 1 } // ->dstsrc
|
||
};
|
||
// clang-format on
|
||
|
||
#undef mri
|
||
#undef mr
|
||
#undef mem
|
||
#undef mfp
|
||
#undef reg
|
||
#undef imm
|
||
#undef sse
|
||
#undef ssem
|
||
#undef mmx
|
||
#undef mmxm
|
||
#undef r32
|
||
#undef rw
|
||
#undef rfp
|
||
#undef port
|
||
#undef ax
|
||
#undef dx
|
||
#undef shft
|
||
#undef D
|
||
#undef U
|
||
#undef N
|
||
|
||
struct AsmOpEnt {
|
||
const char *inMnemonic;
|
||
AsmOp asmOp;
|
||
};
|
||
|
||
/* Some opcodes have data size restrictions, which we don't check
|
||
|
||
cmov, l<segreg> ?, lea, lsl, shld
|
||
|
||
todo: push <immediate> is always the 32-bit form, even tho push <mem> is
|
||
16-bit
|
||
*/
|
||
|
||
// WARNING: This table is expected to be SORTED ALPHABETICALLY.
|
||
static AsmOpEnt opData[] = {
|
||
#ifndef ASM_X86_64
|
||
{"aaa", Op_Adjust},
|
||
{"aad", Op_Adjust},
|
||
{"aam", Op_Adjust},
|
||
{"aas", Op_Adjust},
|
||
#endif
|
||
{"adc", Op_UpdSrcF},
|
||
{"add", Op_UpdSrcF},
|
||
{"addpd", Op_UpdSrcSSE},
|
||
{"addps", Op_UpdSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"addq", Op_UpdSrcSSE}, // ?
|
||
#endif
|
||
{"addsd", Op_UpdSrcSSE},
|
||
{"addss", Op_UpdSrcSSE},
|
||
{"addsubpd", Op_UpdSrcSSE},
|
||
{"addsubps", Op_UpdSrcSSE},
|
||
{"aesdec", Op_UpdSrcSSE},
|
||
{"aesdeclast", Op_UpdSrcSSE},
|
||
{"aesenc", Op_UpdSrcSSE},
|
||
{"aesenclast", Op_UpdSrcSSE},
|
||
{"aesimc", Op_UpdSrcSSE},
|
||
{"aeskeygenassist", Op_DstSrcImmS},
|
||
#ifndef ASM_X86_64
|
||
{"align", Op_Align},
|
||
#endif
|
||
{"and", Op_UpdSrcF},
|
||
{"andnpd", Op_UpdSrcSSE},
|
||
{"andnps", Op_UpdSrcSSE},
|
||
{"andpd", Op_UpdSrcSSE},
|
||
{"andps", Op_UpdSrcSSE},
|
||
#ifndef ASM_X86_64
|
||
{"arpl", Op_UpdSrcNT},
|
||
#endif
|
||
{"blendpd", Op_DstSrcImmS},
|
||
{"blendps", Op_DstSrcImmS},
|
||
{"blendvpd", Op_DstSrcSSE},
|
||
{"blendvps", Op_DstSrcSSE},
|
||
{"bound", Op_bound},
|
||
{"bsf", Op_SrcSrcFW},
|
||
{"bsr", Op_SrcSrcFW},
|
||
{"bswap", Op_bswap},
|
||
{"bt", Op_SrcSrcFW},
|
||
{"btc", Op_UpdSrcFW},
|
||
{"btr", Op_UpdSrcFW},
|
||
{"bts", Op_UpdSrcFW},
|
||
{"call", Op_Branch},
|
||
#ifdef ASM_X86_64
|
||
{"callf", Op_Branch}, // ?
|
||
#endif
|
||
{"cbw", Op_0_AX},
|
||
#ifndef ASM_X86_64
|
||
{"cdq", Op_0_DXAX},
|
||
#else
|
||
{"cdqe", Op_0_DXAX},
|
||
#endif
|
||
{"clc", Op_Flags},
|
||
{"cld", Op_Flags},
|
||
{"clflush", Op_SrcMemNT},
|
||
{"cli", Op_Flags},
|
||
{"clts", Op_0},
|
||
{"cmc", Op_Flags},
|
||
{"cmova", Op_DstSrc},
|
||
{"cmovae", Op_DstSrc},
|
||
{"cmovb", Op_DstSrc},
|
||
{"cmovbe", Op_DstSrc},
|
||
{"cmovc", Op_DstSrc},
|
||
{"cmove", Op_DstSrc},
|
||
{"cmovg", Op_DstSrc},
|
||
{"cmovge", Op_DstSrc},
|
||
{"cmovl", Op_DstSrc},
|
||
{"cmovle", Op_DstSrc},
|
||
{"cmovna", Op_DstSrc},
|
||
{"cmovnae", Op_DstSrc},
|
||
{"cmovnb", Op_DstSrc},
|
||
{"cmovnbe", Op_DstSrc},
|
||
{"cmovnc", Op_DstSrc},
|
||
{"cmovne", Op_DstSrc},
|
||
{"cmovng", Op_DstSrc},
|
||
{"cmovnge", Op_DstSrc},
|
||
{"cmovnl", Op_DstSrc},
|
||
{"cmovnle", Op_DstSrc},
|
||
{"cmovno", Op_DstSrc},
|
||
{"cmovnp", Op_DstSrc},
|
||
{"cmovns", Op_DstSrc},
|
||
{"cmovnz", Op_DstSrc},
|
||
{"cmovo", Op_DstSrc},
|
||
{"cmovp", Op_DstSrc},
|
||
{"cmovpe", Op_DstSrc},
|
||
{"cmovpo", Op_DstSrc},
|
||
{"cmovs", Op_DstSrc},
|
||
{"cmovz", Op_DstSrc},
|
||
{"cmp", Op_SrcSrcF},
|
||
{"cmppd", Op_DstSrcImmS},
|
||
{"cmpps", Op_DstSrcImmS},
|
||
#ifdef ASM_X86_64
|
||
{"cmpq", Op_DstSrcNT}, // ?
|
||
#endif
|
||
{"cmps", Op_cmps},
|
||
{"cmpsb", Op_cmpsX},
|
||
{"cmpsd", Op_cmpsd}, // string cmp, and SSE cmp
|
||
#ifdef ASM_X86_64
|
||
{"cmpsq", Op_cmpsX},
|
||
#endif
|
||
{"cmpss", Op_DstSrcImmS},
|
||
{"cmpsw", Op_cmpsX},
|
||
{"cmpxchg", Op_cmpxchg},
|
||
#ifdef ASM_X86_64
|
||
{"cmpxchg16b", Op_cmpxchg16b},
|
||
#endif
|
||
{"cmpxchg8b", Op_cmpxchg8b},
|
||
{"comisd", Op_SrcSrcSSEF},
|
||
{"comiss", Op_SrcSrcSSEF},
|
||
{"cpuid", Op_cpuid},
|
||
#ifdef ASM_X86_64
|
||
{"cqo", Op_0_DXAX},
|
||
#endif
|
||
{"crc32", Op_DstSrc},
|
||
{"cvtdq2pd", Op_DstSrcSSE},
|
||
{"cvtdq2ps", Op_DstSrcSSE},
|
||
{"cvtpd2dq", Op_DstSrcSSE},
|
||
{"cvtpd2pi", Op_DstSrcSSE},
|
||
{"cvtpd2ps", Op_DstSrcSSE},
|
||
{"cvtpi2pd", Op_DstSrcSSE},
|
||
{"cvtpi2ps", Op_DstSrcSSE},
|
||
{"cvtps2dq", Op_DstSrcSSE},
|
||
{"cvtps2pd", Op_DstSrcSSE},
|
||
{"cvtps2pi", Op_DstSrcSSE},
|
||
{"cvtsd2si", Op_DstSrcSSE},
|
||
{"cvtsd2ss", Op_DstSrcSSE},
|
||
{"cvtsi2sd", Op_DstSrcSSE},
|
||
{"cvtsi2ss", Op_DstSrcSSE},
|
||
{"cvtss2sd", Op_DstSrcSSE},
|
||
{"cvtss2si", Op_DstSrcSSE},
|
||
{"cvttpd2dq", Op_DstSrcSSE},
|
||
{"cvttpd2pi", Op_DstSrcSSE},
|
||
{"cvttps2dq", Op_DstSrcSSE},
|
||
{"cvttps2pi", Op_DstSrcSSE},
|
||
{"cvttsd2si", Op_DstSrcSSE},
|
||
{"cvttss2si", Op_DstSrcSSE},
|
||
{"cwd", Op_0_DXAX},
|
||
#ifndef ASM_X86_64
|
||
{"cwde", Op_0_AX},
|
||
#else
|
||
{"cwde", Op_0_DXAX},
|
||
#endif
|
||
// {"da", XXXX}, // dunno what this is -- takes labels?
|
||
#ifndef ASM_X86_64
|
||
{"daa", Op_Adjust},
|
||
{"das", Op_Adjust},
|
||
#endif
|
||
{"db", Op_db},
|
||
{"dd", Op_dd},
|
||
{"de", Op_de},
|
||
{"dec", Op_UpdF},
|
||
{"df", Op_df},
|
||
{"di", Op_di},
|
||
{"div", Op_Src_DXAXF},
|
||
{"divpd", Op_UpdSrcSSE},
|
||
{"divps", Op_UpdSrcSSE},
|
||
{"divsd", Op_UpdSrcSSE},
|
||
{"divss", Op_UpdSrcSSE},
|
||
{"dl", Op_dl},
|
||
{"dppd", Op_DstSrcImmS},
|
||
{"dpps", Op_DstSrcImmS},
|
||
{"dq", Op_dl},
|
||
{"ds", Op_ds},
|
||
{"dt", Op_de},
|
||
{"dw", Op_ds},
|
||
{"emms", Op_0}, // clobber all mmx/fp?
|
||
{"enter", Op_enter},
|
||
{"even", Op_Even},
|
||
{"extractps", Op_ExtSrcImmS},
|
||
{"f2xm1", Op_F0_ST}, // %% most of these are update...
|
||
{"fabs", Op_F0_ST},
|
||
{"fadd", Op_FMath},
|
||
{"faddp", Op_FPMath},
|
||
{"fbld", Op_Fs_P},
|
||
{"fbstp", Op_Fd_P},
|
||
{"fchs", Op_F0_ST},
|
||
{"fclex", Op_0},
|
||
{"fcmovb", Op_FdSTiSTi}, // but only ST(0) can be the destination -- should
|
||
// be FdST0STi
|
||
{"fcmovbe", Op_FdSTiSTi},
|
||
{"fcmove", Op_FdSTiSTi},
|
||
{"fcmovnb", Op_FdSTiSTi},
|
||
{"fcmovnbe", Op_FdSTiSTi},
|
||
{"fcmovne", Op_FdSTiSTi},
|
||
{"fcmovnu", Op_FdSTiSTi},
|
||
{"fcmovu", Op_FdSTiSTi},
|
||
{"fcom", Op_FCmp},
|
||
{"fcomi", Op_FCmpFlg},
|
||
{"fcomip", Op_FCmpFlgP},
|
||
{"fcomp", Op_FCmpP},
|
||
{"fcompp", Op_F0_P}, // pops twice
|
||
{"fcos", Op_F0_ST},
|
||
{"fdecstp", Op_F0_P}, // changes stack
|
||
{"fdisi", Op_fdisi},
|
||
{"fdiv", Op_FMath},
|
||
{"fdivp", Op_FPMath},
|
||
{"fdivr", Op_FMath},
|
||
{"fdivrp", Op_FPMath},
|
||
{"feni", Op_feni},
|
||
{"ffree", Op_FdST},
|
||
{"fiadd", Op_Fis_ST},
|
||
{"ficom", Op_Fis},
|
||
{"ficomp", Op_Fis_P},
|
||
{"fidiv", Op_Fis_ST},
|
||
{"fidivr", Op_Fis_ST},
|
||
{"fild", Op_Fis_P},
|
||
{"fimul", Op_Fis_ST},
|
||
{"fincstp", Op_F0_P},
|
||
{"finit", Op_F0_P},
|
||
{"fist", Op_Fid}, // only 16,32bit
|
||
{"fistp", Op_Fid_P},
|
||
{"fisttp", Op_Fid_P},
|
||
{"fisub", Op_Fis_ST},
|
||
{"fisubr", Op_Fis_ST},
|
||
{"fld", Op_fld},
|
||
{"fld1", Op_F0_P},
|
||
{"fldcw", Op_SrcMemNT},
|
||
{"fldenv", Op_SrcMemNT},
|
||
{"fldl2e", Op_F0_P},
|
||
{"fldl2t", Op_F0_P},
|
||
{"fldlg2", Op_F0_P},
|
||
{"fldln2", Op_F0_P},
|
||
{"fldpi", Op_F0_P},
|
||
{"fldz", Op_F0_P},
|
||
{"fmul", Op_FMath},
|
||
{"fmulp", Op_FPMath},
|
||
{"fnclex", Op_0},
|
||
{"fndisi", Op_fdisi}, // ??
|
||
{"fneni", Op_feni}, // ??
|
||
{"fninit", Op_0},
|
||
{"fnop", Op_0},
|
||
{"fnsave", Op_DstMemNT},
|
||
{"fnstcw", Op_DstMemNT},
|
||
{"fnstenv", Op_DstMemNT},
|
||
{"fnstsw", Op_fXstsw},
|
||
{"fpatan", Op_F0_P}, // pop and modify new ST
|
||
{"fprem", Op_F0_ST},
|
||
{"fprem1", Op_F0_ST},
|
||
{"fptan", Op_F0_P}, // modify ST and push 1.0
|
||
{"frndint", Op_F0_ST},
|
||
{"frstor", Op_SrcMemNT}, // but clobbers everything
|
||
{"fsave", Op_DstMemNT},
|
||
{"fscale", Op_F0_ST},
|
||
{"fsetpm", Op_fsetpm},
|
||
{"fsin", Op_F0_ST},
|
||
{"fsincos", Op_F0_P},
|
||
{"fsqrt", Op_F0_ST},
|
||
{"fst", Op_Ffd},
|
||
{"fstcw", Op_DstMemNT},
|
||
{"fstenv", Op_DstMemNT},
|
||
{"fstp", Op_Ffd_P},
|
||
{"fstsw", Op_fXstsw},
|
||
{"fsub", Op_FMath},
|
||
{"fsubp", Op_FPMath},
|
||
{"fsubr", Op_FMath},
|
||
{"fsubrp", Op_FPMath},
|
||
{"ftst", Op_0},
|
||
{"fucom", Op_FCmp},
|
||
{"fucomi", Op_FCmpFlg},
|
||
{"fucomip", Op_FCmpFlgP},
|
||
{"fucomp", Op_FCmpP},
|
||
{"fucompp", Op_F0_P}, // pops twice
|
||
{"fwait", Op_0},
|
||
{"fxam", Op_0},
|
||
{"fxch", Op_fxch},
|
||
{"fxrstor", Op_SrcMemNT}, // clobbers FP,MMX,SSE
|
||
{"fxsave", Op_DstMemNT},
|
||
{"fxtract", Op_F0_P}, // pushes
|
||
{"fyl2x", Op_F0_P}, // pops
|
||
{"fyl2xp1", Op_F0_P}, // pops
|
||
{"haddpd", Op_UpdSrcSSE},
|
||
{"haddps", Op_UpdSrcSSE},
|
||
{"hlt", Op_0},
|
||
{"hsubpd", Op_UpdSrcSSE},
|
||
{"hsubps", Op_UpdSrcSSE},
|
||
{"idiv", Op_Src_DXAXF},
|
||
{"imul", Op_imul},
|
||
{"in", Op_in},
|
||
{"inc", Op_UpdF},
|
||
{"ins", Op_ins},
|
||
{"insb", Op_insX},
|
||
{"insd", Op_insX},
|
||
{"insertps", Op_DstSrcImmS},
|
||
{"insw", Op_insX},
|
||
{"int", Op_SrcImm},
|
||
{"into", Op_0},
|
||
{"invd", Op_0},
|
||
{"invlpg", Op_SrcMemNT},
|
||
{"iret", Op_iret},
|
||
{"iretd", Op_iretd},
|
||
#ifdef ASM_X86_64
|
||
{"iretq", Op_iretq}, // ?
|
||
#endif
|
||
{"ja", Op_CBranch},
|
||
{"jae", Op_CBranch},
|
||
{"jb", Op_CBranch},
|
||
{"jbe", Op_CBranch},
|
||
{"jc", Op_CBranch},
|
||
{"jcxz", Op_CBranch},
|
||
{"je", Op_CBranch},
|
||
{"jecxz", Op_CBranch},
|
||
{"jg", Op_CBranch},
|
||
{"jge", Op_CBranch},
|
||
{"jl", Op_CBranch},
|
||
{"jle", Op_CBranch},
|
||
{"jmp", Op_Branch},
|
||
#ifdef ASM_X86_64
|
||
{"jmpe", Op_Branch}, // ?
|
||
{"jmpf", Op_Branch}, // ?
|
||
#endif
|
||
{"jna", Op_CBranch},
|
||
{"jnae", Op_CBranch},
|
||
{"jnb", Op_CBranch},
|
||
{"jnbe", Op_CBranch},
|
||
{"jnc", Op_CBranch},
|
||
{"jne", Op_CBranch},
|
||
{"jng", Op_CBranch},
|
||
{"jnge", Op_CBranch},
|
||
{"jnl", Op_CBranch},
|
||
{"jnle", Op_CBranch},
|
||
{"jno", Op_CBranch},
|
||
{"jnp", Op_CBranch},
|
||
{"jns", Op_CBranch},
|
||
{"jnz", Op_CBranch},
|
||
{"jo", Op_CBranch},
|
||
{"jp", Op_CBranch},
|
||
{"jpe", Op_CBranch},
|
||
{"jpo", Op_CBranch},
|
||
#ifdef ASM_X86_64
|
||
{"jrcxz", Op_CBranch}, // Not supported by DMD
|
||
#endif
|
||
{"js", Op_CBranch},
|
||
{"jz", Op_CBranch},
|
||
{"lahf", Op_0_AX},
|
||
{"lar", Op_DstSrcFW}, // reg dest only
|
||
{"lddqu", Op_DstSrcSSE},
|
||
{"ldmxcsr", Op_SrcMemNT},
|
||
{"lds", Op_DstSrc}, // reg dest only
|
||
{"lea", Op_DstSrc}, // "
|
||
#ifdef ASM_X86_64
|
||
{"leaq", Op_DstSrcSSE}, // ?
|
||
#endif
|
||
{"leave", Op_0}, // EBP,ESP clobbers
|
||
#ifndef ASM_X86_64
|
||
{"les", Op_DstSrc},
|
||
#endif
|
||
{"lfence", Op_0},
|
||
{"lfs", Op_DstSrc},
|
||
{"lgdt", Op_SrcMemNT},
|
||
{"lgs", Op_DstSrc},
|
||
{"lidt", Op_SrcMemNT},
|
||
{"lldt", Op_SrcRMWNT},
|
||
{"lmsw", Op_SrcRMWNT},
|
||
{"lock", Op_0},
|
||
{"lods", Op_lods},
|
||
{"lodsb", Op_lodsX},
|
||
{"lodsd", Op_lodsX},
|
||
#ifdef ASM_X86_64
|
||
{"lodsq", Op_lodsX},
|
||
#endif
|
||
{"lodsw", Op_lodsX},
|
||
{"loop", Op_Loop},
|
||
{"loope", Op_Loop},
|
||
{"loopne", Op_Loop},
|
||
{"loopnz", Op_Loop},
|
||
{"loopz", Op_Loop},
|
||
{"lsl", Op_DstSrcFW}, // reg dest only
|
||
{"lss", Op_DstSrc},
|
||
{"ltr", Op_DstMemNT},
|
||
{"maskmovdqu", Op_SrcSrcMMX}, // writes to [edi]
|
||
{"maskmovq", Op_SrcSrcMMX},
|
||
{"maxpd", Op_UpdSrcSSE},
|
||
{"maxps", Op_UpdSrcSSE},
|
||
{"maxsd", Op_UpdSrcSSE},
|
||
{"maxss", Op_UpdSrcSSE},
|
||
{"mfence", Op_0},
|
||
{"minpd", Op_UpdSrcSSE},
|
||
{"minps", Op_UpdSrcSSE},
|
||
{"minsd", Op_UpdSrcSSE},
|
||
{"minss", Op_UpdSrcSSE},
|
||
{"monitor", Op_0},
|
||
{"mov", Op_DstSrc},
|
||
{"movapd", Op_DstSrcSSE},
|
||
{"movaps", Op_DstSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"movb", Op_DstSrcNT}, // ?
|
||
#endif
|
||
{"movd", Op_DstSrcNT}, // also mmx and sse
|
||
{"movddup", Op_DstSrcSSE},
|
||
{"movdq2q", Op_DstSrcNT}, // mmx/sse
|
||
{"movdqa", Op_DstSrcSSE},
|
||
{"movdqu", Op_DstSrcSSE},
|
||
{"movhlps", Op_DstSrcSSE},
|
||
{"movhpd", Op_DstSrcSSE},
|
||
{"movhps", Op_DstSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"movl", Op_DstSrc}, // ?
|
||
#endif
|
||
{"movlhps", Op_DstSrcSSE},
|
||
{"movlpd", Op_DstSrcSSE},
|
||
{"movlps", Op_DstSrcSSE},
|
||
{"movmskpd", Op_DstSrcSSE},
|
||
{"movmskps", Op_DstSrcSSE},
|
||
{"movntdq", Op_DstSrcNT}, // limited to sse, but mem dest
|
||
{"movntdqa", Op_DstSrcNT},
|
||
{"movnti", Op_DstSrcNT}, // limited to gpr, but mem dest
|
||
{"movntpd", Op_DstSrcNT}, // limited to sse, but mem dest
|
||
{"movntps", Op_DstSrcNT}, // limited to sse, but mem dest
|
||
{"movntq", Op_DstSrcNT}, // limited to mmx, but mem dest
|
||
{"movq", Op_DstSrcNT}, // limited to sse and mmx
|
||
{"movq2dq", Op_DstSrcNT}, // limited to sse <- mmx regs
|
||
{"movs", Op_movs},
|
||
{"movsb", Op_movsX},
|
||
{"movsd", Op_movsd},
|
||
{"movshdup", Op_DstSrcSSE},
|
||
{"movsldup", Op_DstSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"movsq", Op_movsd},
|
||
#endif
|
||
{"movss", Op_DstSrcSSE},
|
||
{"movsw", Op_movsX},
|
||
{"movsx", Op_movsx}, // word-only, reg dest
|
||
#ifdef ASM_X86_64
|
||
{"movsxd", Op_movsx},
|
||
#endif
|
||
{"movupd", Op_DstSrcSSE},
|
||
{"movups", Op_DstSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"movzbl", Op_DstSrcNT},
|
||
#endif
|
||
{"movzx", Op_movzx},
|
||
{"mpsadbw", Op_DstSrcImmS},
|
||
{"mul", Op_mul},
|
||
{"mulpd", Op_UpdSrcSSE},
|
||
{"mulps", Op_UpdSrcSSE},
|
||
{"mulsd", Op_UpdSrcSSE},
|
||
{"mulss", Op_UpdSrcSSE},
|
||
{"mwait", Op_0},
|
||
{"naked", Op_Naked},
|
||
{"neg", Op_UpdF},
|
||
{"nop", Op_0},
|
||
{"not", Op_Upd},
|
||
{"or", Op_UpdSrcF},
|
||
{"orpd", Op_UpdSrcSSE},
|
||
{"orps", Op_UpdSrcSSE},
|
||
{"out", Op_out},
|
||
{"outs", Op_outs},
|
||
{"outsb", Op_outsX},
|
||
{"outsd", Op_outsX},
|
||
{"outsw", Op_outsX},
|
||
#ifdef ASM_X86_64
|
||
{"pabsb", Op_DstSrcSSE},
|
||
{"pabsq", Op_DstSrcSSE},
|
||
{"pabsw", Op_DstSrcSSE},
|
||
#endif
|
||
{"packssdw", Op_DstSrcMMX}, // %% also SSE
|
||
{"packsswb", Op_DstSrcMMX},
|
||
{"packusdw", Op_DstSrcSSE},
|
||
{"packuswb", Op_DstSrcMMX},
|
||
{"paddb", Op_DstSrcMMX},
|
||
{"paddd", Op_DstSrcMMX},
|
||
{"paddq", Op_DstSrcMMX},
|
||
{"paddsb", Op_DstSrcMMX},
|
||
{"paddsw", Op_DstSrcMMX},
|
||
{"paddusb", Op_DstSrcMMX},
|
||
{"paddusw", Op_DstSrcMMX},
|
||
{"paddw", Op_DstSrcMMX},
|
||
{"palignr", Op_DstSrcImmS},
|
||
{"pand", Op_DstSrcMMX},
|
||
{"pandn", Op_DstSrcMMX},
|
||
#ifdef ASM_X86_64
|
||
{"pause", Op_DstSrcMMX}, // ?
|
||
#endif
|
||
{"pavgb", Op_DstSrcMMX},
|
||
{"pavgusb", Op_DstSrcMMX}, // AMD 3dNow!
|
||
{"pavgw", Op_DstSrcMMX},
|
||
{"pblendvb", Op_UpdSrcSSE}, // implicit xmm0
|
||
{"pblendw", Op_DstSrcImmS},
|
||
{"pcmpeqb", Op_DstSrcMMX},
|
||
{"pcmpeqd", Op_DstSrcMMX},
|
||
{"pcmpeqq", Op_UpdSrcSSE},
|
||
{"pcmpeqw", Op_DstSrcMMX},
|
||
{"pcmpestri", Op_DstSrcImmS},
|
||
{"pcmpestrm", Op_DstSrcImmS},
|
||
{"pcmpgtb", Op_DstSrcMMX},
|
||
{"pcmpgtd", Op_DstSrcMMX},
|
||
{"pcmpgtq", Op_UpdSrcSSE},
|
||
{"pcmpgtw", Op_DstSrcMMX},
|
||
{"pcmpistri", Op_DstSrcImmS},
|
||
{"pcmpistrm", Op_DstSrcImmS},
|
||
{"pextrb", Op_DstSrcImmM}, // gpr32 dest
|
||
{"pextrd", Op_DstSrcImmM}, // gpr32 dest
|
||
{"pextrq", Op_DstSrcImmM}, // gpr32 dest
|
||
{"pextrw", Op_DstSrcImmM}, // gpr32 dest
|
||
{"pf2id", Op_DstSrcMMX}, // %% AMD 3dNow! opcodes
|
||
{"pfacc", Op_DstSrcMMX},
|
||
{"pfadd", Op_DstSrcMMX},
|
||
{"pfcmpeq", Op_DstSrcMMX},
|
||
{"pfcmpge", Op_DstSrcMMX},
|
||
{"pfcmpgt", Op_DstSrcMMX},
|
||
{"pfmax", Op_DstSrcMMX},
|
||
{"pfmin", Op_DstSrcMMX},
|
||
{"pfmul", Op_DstSrcMMX},
|
||
{"pfnacc", Op_DstSrcMMX}, // 3dNow values are returned in MM0 register,
|
||
{"pfpnacc", Op_DstSrcMMX}, // so should be correct to use Op_DstSrcMMX.
|
||
{"pfrcp", Op_DstSrcMMX},
|
||
{"pfrcpit1", Op_DstSrcMMX},
|
||
{"pfrcpit2", Op_DstSrcMMX},
|
||
{"pfrsqit1", Op_DstSrcMMX},
|
||
{"pfrsqrt", Op_DstSrcMMX},
|
||
{"pfsub", Op_DstSrcMMX},
|
||
{"pfsubr", Op_DstSrcMMX},
|
||
#ifdef ASM_X86_64
|
||
{"phaddd", Op_UpdSrcSSE},
|
||
{"phaddsw", Op_UpdSrcSSE},
|
||
{"phaddw", Op_UpdSrcSSE},
|
||
{"phminposuw", Op_UpdSrcSSE},
|
||
{"phsubd", Op_UpdSrcSSE},
|
||
{"phsubsw", Op_UpdSrcSSE},
|
||
{"phsubw", Op_UpdSrcSSE},
|
||
#endif
|
||
{"pi2fd", Op_DstSrcMMX}, // %%
|
||
{"pinsrb", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too
|
||
{"pinsrd", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too
|
||
{"pinsrq", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too
|
||
{"pinsrw", Op_DstSrcImmM}, // gpr32(16), mem16 src, sse too
|
||
#ifdef ASM_X86_64
|
||
{"pmaddubsw", Op_UpdSrcSSE},
|
||
#endif
|
||
{"pmaddwd", Op_DstSrcMMX},
|
||
{"pmaxsb", Op_UpdSrcSSE},
|
||
{"pmaxsd", Op_UpdSrcSSE},
|
||
{"pmaxsw", Op_DstSrcMMX},
|
||
{"pmaxub", Op_DstSrcMMX},
|
||
{"pmaxud", Op_UpdSrcSSE},
|
||
{"pmaxuw", Op_UpdSrcSSE},
|
||
{"pminsb", Op_UpdSrcSSE},
|
||
{"pminsd", Op_UpdSrcSSE},
|
||
{"pminsw", Op_DstSrcMMX},
|
||
{"pminub", Op_DstSrcMMX},
|
||
{"pminud", Op_UpdSrcSSE},
|
||
{"pminuw", Op_UpdSrcSSE},
|
||
{"pmovmskb", Op_DstSrcMMX},
|
||
{"pmovsxbd", Op_DstSrcSSE},
|
||
{"pmovsxbq", Op_DstSrcSSE},
|
||
{"pmovsxbw", Op_DstSrcSSE},
|
||
{"pmovsxdq", Op_DstSrcSSE},
|
||
{"pmovsxwd", Op_DstSrcSSE},
|
||
{"pmovsxwq", Op_DstSrcSSE},
|
||
{"pmovzxbd", Op_DstSrcSSE},
|
||
{"pmovzxbq", Op_DstSrcSSE},
|
||
{"pmovzxbw", Op_DstSrcSSE},
|
||
{"pmovzxdq", Op_DstSrcSSE},
|
||
{"pmovzxwd", Op_DstSrcSSE},
|
||
{"pmovzxwq", Op_DstSrcSSE},
|
||
{"pmuldq", Op_DstSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"pmulhrsw", Op_DstSrcMMX},
|
||
#endif
|
||
{"pmulhrw", Op_DstSrcMMX}, // AMD 3dNow!
|
||
{"pmulhuw", Op_DstSrcMMX},
|
||
{"pmulhw", Op_DstSrcMMX},
|
||
{"pmulld", Op_UpdSrcSSE},
|
||
{"pmullw", Op_DstSrcMMX},
|
||
{"pmuludq", Op_DstSrcMMX}, // also sse
|
||
{"pop", Op_DstW},
|
||
#ifndef ASM_X86_64
|
||
{"popa", Op_SizedStack}, // For intel this is always 16-bit
|
||
{"popad", Op_SizedStack}, // GAS doesn't accept 'popad' -- these clobber
|
||
// everything, but supposedly it would be used to
|
||
// preserve clobbered regs
|
||
#endif
|
||
{"popcnt", Op_DstSrc},
|
||
{"popf", Op_0}, // rewrite the insn with a special case
|
||
#ifndef ASM_X86_64
|
||
{"popfd", Op_0},
|
||
#else
|
||
{"popfq", Op_0}, // Op_SizedStack?
|
||
{"popq", Op_push},
|
||
#endif
|
||
{"por", Op_DstSrcMMX},
|
||
{"prefetchnta", Op_SrcMemNT},
|
||
{"prefetcht0", Op_SrcMemNT},
|
||
{"prefetcht1", Op_SrcMemNT},
|
||
{"prefetcht2", Op_SrcMemNT},
|
||
{"psadbw", Op_DstSrcMMX},
|
||
{"pshufb", Op_UpdSrcSSE},
|
||
#ifndef ASM_X86_64
|
||
{"pshufd", Op_DstSrcImmM},
|
||
{"pshufhw", Op_DstSrcImmM},
|
||
{"pshuflw", Op_DstSrcImmM},
|
||
{"pshufw", Op_DstSrcImmM},
|
||
#else
|
||
{"pshufd", Op_DstSrcImmS},
|
||
{"pshufhw", Op_DstSrcImmS},
|
||
{"pshuflw", Op_DstSrcImmS},
|
||
{"pshufw", Op_DstSrcImmS},
|
||
{"psignb", Op_UpdSrcSSE},
|
||
{"psignd", Op_UpdSrcSSE},
|
||
{"psignw", Op_UpdSrcSSE},
|
||
#endif
|
||
{"pslld", Op_DstSrcMMX}, // immediate operands...
|
||
{"pslldq", Op_DstSrcMMX},
|
||
{"psllq", Op_DstSrcMMX},
|
||
{"psllw", Op_DstSrcMMX},
|
||
{"psrad", Op_DstSrcMMX},
|
||
{"psraw", Op_DstSrcMMX},
|
||
{"psrld", Op_DstSrcMMX},
|
||
{"psrldq", Op_DstSrcMMX},
|
||
{"psrlq", Op_DstSrcMMX},
|
||
{"psrlw", Op_DstSrcMMX},
|
||
{"psubb", Op_DstSrcMMX},
|
||
{"psubd", Op_DstSrcMMX},
|
||
{"psubq", Op_DstSrcMMX},
|
||
{"psubsb", Op_DstSrcMMX},
|
||
{"psubsw", Op_DstSrcMMX},
|
||
{"psubusb", Op_DstSrcMMX},
|
||
{"psubusw", Op_DstSrcMMX},
|
||
{"psubw", Op_DstSrcMMX},
|
||
{"pswapd", Op_DstSrcMMX}, // AMD 3dNow!
|
||
{"ptest", Op_SrcSrcSSEF},
|
||
{"punpckhbw", Op_DstSrcMMX},
|
||
{"punpckhdq", Op_DstSrcMMX},
|
||
{"punpckhqdq", Op_DstSrcMMX},
|
||
{"punpckhwd", Op_DstSrcMMX},
|
||
{"punpcklbw", Op_DstSrcMMX},
|
||
{"punpckldq", Op_DstSrcMMX},
|
||
{"punpcklqdq", Op_DstSrcMMX},
|
||
{"punpcklwd", Op_DstSrcMMX},
|
||
{"push", Op_push},
|
||
#ifndef ASM_X86_64
|
||
{"pusha", Op_SizedStack},
|
||
{"pushad", Op_SizedStack},
|
||
#endif
|
||
{"pushf", Op_0},
|
||
#ifndef ASM_X86_64
|
||
{"pushfd", Op_0},
|
||
#else
|
||
{"pushfq", Op_0}, // Op_SizedStack?
|
||
{"pushq", Op_push}, // ?
|
||
#endif
|
||
{"pxor", Op_DstSrcMMX},
|
||
{"rcl", Op_Shift}, // limited src operands -- change to shift
|
||
{"rcpps", Op_DstSrcSSE},
|
||
{"rcpss", Op_DstSrcSSE},
|
||
{"rcr", Op_Shift},
|
||
//{"rdfsbase", XXXX},
|
||
//{"rdgsbase", XXXX},
|
||
{"rdmsr", Op_0_DXAX},
|
||
{"rdpmc", Op_0_DXAX},
|
||
//{"rdrand", XXXX},
|
||
{"rdtsc", Op_0_DXAX},
|
||
/*
|
||
RDTSCP clobbers EDX:EAX and ECX
|
||
From the Intel manual:
|
||
EDX:EAX ← TimeStampCounter;
|
||
ECX ← IA32_TSC_AUX[31:0];
|
||
*/
|
||
{"rdtscp", Op_0_DXCXAX},
|
||
{"rep", Op_0},
|
||
{"repe", Op_0},
|
||
{"repne", Op_0},
|
||
{"repnz", Op_0},
|
||
{"repz", Op_0},
|
||
{"ret", Op_ret},
|
||
{"retf", Op_retf},
|
||
#ifdef ASM_X86_64
|
||
{"retn", Op_retf}, // ?
|
||
#endif
|
||
{"rol", Op_Shift},
|
||
{"ror", Op_Shift},
|
||
{"roundpd", Op_DstSrcImmS},
|
||
{"roundps", Op_DstSrcImmS},
|
||
{"roundsd", Op_DstSrcImmS},
|
||
{"roundss", Op_DstSrcImmS},
|
||
{"rsm", Op_0},
|
||
{"rsqrtps", Op_DstSrcSSE},
|
||
{"rsqrtss", Op_DstSrcSSE},
|
||
{"sahf", Op_Flags},
|
||
{"sal", Op_Shift},
|
||
#ifdef ASM_X86_64
|
||
{"salq", Op_DstSrcNT},
|
||
#endif
|
||
{"sar", Op_Shift},
|
||
{"sbb", Op_UpdSrcF},
|
||
{"scas", Op_scas},
|
||
{"scasb", Op_scasX},
|
||
{"scasd", Op_scasX},
|
||
#ifdef ASM_X86_64
|
||
{"scasq", Op_scasX},
|
||
#endif
|
||
{"scasw", Op_scasX},
|
||
{"seta", Op_DstRMBNT}, // also gpr8
|
||
{"setae", Op_DstRMBNT},
|
||
{"setb", Op_DstRMBNT},
|
||
{"setbe", Op_DstRMBNT},
|
||
{"setc", Op_DstRMBNT},
|
||
{"sete", Op_DstRMBNT},
|
||
{"setg", Op_DstRMBNT},
|
||
{"setge", Op_DstRMBNT},
|
||
{"setl", Op_DstRMBNT},
|
||
{"setle", Op_DstRMBNT},
|
||
{"setna", Op_DstRMBNT},
|
||
{"setnae", Op_DstRMBNT},
|
||
{"setnb", Op_DstRMBNT},
|
||
{"setnbe", Op_DstRMBNT},
|
||
{"setnc", Op_DstRMBNT},
|
||
{"setne", Op_DstRMBNT},
|
||
{"setng", Op_DstRMBNT},
|
||
{"setnge", Op_DstRMBNT},
|
||
{"setnl", Op_DstRMBNT},
|
||
{"setnle", Op_DstRMBNT},
|
||
{"setno", Op_DstRMBNT},
|
||
{"setnp", Op_DstRMBNT},
|
||
{"setns", Op_DstRMBNT},
|
||
{"setnz", Op_DstRMBNT},
|
||
{"seto", Op_DstRMBNT},
|
||
{"setp", Op_DstRMBNT},
|
||
{"setpe", Op_DstRMBNT},
|
||
{"setpo", Op_DstRMBNT},
|
||
{"sets", Op_DstRMBNT},
|
||
{"setz", Op_DstRMBNT},
|
||
{"sfence", Op_0},
|
||
{"sgdt", Op_DstMemNT},
|
||
{"sha1msg1", Op_UpdSrcSSE},
|
||
{"sha1msg2", Op_UpdSrcSSE},
|
||
{"sha1nexte", Op_UpdSrcSSE},
|
||
{"sha1rnds4", Op_DstSrcImmS},
|
||
{"sha256msg1", Op_UpdSrcSSE},
|
||
{"sha256msg2", Op_UpdSrcSSE},
|
||
{"sha256rnds2", Op_UpdSrcSSE}, // implicit xmm0
|
||
{"shl", Op_Shift},
|
||
{"shld", Op_UpdSrcShft},
|
||
{"shr", Op_Shift},
|
||
{"shrd", Op_UpdSrcShft},
|
||
{"shufpd", Op_DstSrcImmS},
|
||
{"shufps", Op_DstSrcImmS},
|
||
{"sidt", Op_DstMemNT},
|
||
{"sldt", Op_DstRMWNT},
|
||
{"smsw", Op_DstRMWNT},
|
||
{"sqrtpd", Op_DstSrcSSE},
|
||
{"sqrtps", Op_DstSrcSSE},
|
||
{"sqrtsd", Op_DstSrcSSE},
|
||
{"sqrtss", Op_DstSrcSSE},
|
||
{"stc", Op_Flags},
|
||
{"std", Op_Flags},
|
||
{"sti", Op_Flags},
|
||
{"stmxcsr", Op_DstMemNT},
|
||
{"stos", Op_stos},
|
||
{"stosb", Op_stosX},
|
||
{"stosd", Op_stosX},
|
||
#ifdef ASM_X86_64
|
||
{"stosq", Op_stosX},
|
||
#endif
|
||
{"stosw", Op_stosX},
|
||
{"str", Op_DstMemNT}, // also r16
|
||
{"sub", Op_UpdSrcF},
|
||
{"subpd", Op_UpdSrcSSE},
|
||
{"subps", Op_UpdSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"subq", Op_UpdSrcSSE}, // ?
|
||
#endif
|
||
{"subsd", Op_UpdSrcSSE},
|
||
{"subss", Op_UpdSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"swapgs", Op_0}, // Not supported by DMD
|
||
#endif
|
||
{"syscall", Op_0},
|
||
{"sysenter", Op_0},
|
||
{"sysexit", Op_0},
|
||
{"sysret", Op_0},
|
||
#ifdef ASM_X86_64
|
||
{"sysretq", Op_0}, // ?
|
||
#endif
|
||
{"test", Op_SrcSrcF},
|
||
{"ucomisd", Op_SrcSrcSSEF},
|
||
{"ucomiss", Op_SrcSrcSSEF},
|
||
{"ud2", Op_0},
|
||
{"unpckhpd", Op_UpdSrcSSE},
|
||
{"unpckhps", Op_UpdSrcSSE},
|
||
{"unpcklpd", Op_UpdSrcSSE},
|
||
{"unpcklps", Op_UpdSrcSSE},
|
||
#if 0 // TODO
|
||
{"vaddpd", XXXX},
|
||
{"vaddps", XXXX},
|
||
{"vaddsd", XXXX},
|
||
{"vaddss", XXXX},
|
||
{"vaddsubpd", XXXX},
|
||
{"vaddsubps", XXXX},
|
||
{"vaesdec", XXXX},
|
||
{"vaesdeclast", XXXX},
|
||
{"vaesenc", XXXX},
|
||
{"vaesenclast", XXXX},
|
||
{"vaesimc", XXXX},
|
||
{"vaeskeygenassist", XXXX},
|
||
{"vandnpd", XXXX},
|
||
{"vandnps", XXXX},
|
||
{"vandpd", XXXX},
|
||
{"vandps", XXXX},
|
||
{"vblendpd", XXXX},
|
||
{"vblendps", XXXX},
|
||
{"vblendvpd", XXXX},
|
||
{"vblendvps", XXXX},
|
||
{"vbroadcastf128", XXXX},
|
||
{"vbroadcastsd", XXXX},
|
||
{"vbroadcastss", XXXX},
|
||
{"vcmppd", XXXX},
|
||
{"vcmpps", XXXX},
|
||
{"vcmpsd", XXXX},
|
||
{"vcmpss", XXXX},
|
||
{"vcomisd", XXXX},
|
||
{"vcomiss", XXXX},
|
||
{"vcvtdq2pd", XXXX},
|
||
{"vcvtdq2ps", XXXX},
|
||
{"vcvtpd2dq", XXXX},
|
||
{"vcvtpd2ps", XXXX},
|
||
{"vcvtph2ps", XXXX},
|
||
{"vcvtps2dq", XXXX},
|
||
{"vcvtps2pd", XXXX},
|
||
{"vcvtps2ph", XXXX},
|
||
{"vcvtsd2si", XXXX},
|
||
{"vcvtsd2ss", XXXX},
|
||
{"vcvtsi2sd", XXXX},
|
||
{"vcvtsi2ss", XXXX},
|
||
{"vcvtss2sd", XXXX},
|
||
{"vcvtss2si", XXXX},
|
||
{"vcvttpd2dq", XXXX},
|
||
{"vcvttps2dq", XXXX},
|
||
{"vcvttsd2si", XXXX},
|
||
{"vcvttss2si", XXXX},
|
||
{"vdivpd", XXXX},
|
||
{"vdivps", XXXX},
|
||
{"vdivsd", XXXX},
|
||
{"vdivss", XXXX},
|
||
{"vdppd", XXXX},
|
||
{"vdpps", XXXX},
|
||
#endif
|
||
{"verr", Op_SrcMemNTF},
|
||
{"verw", Op_SrcMemNTF},
|
||
#if 0 // TODO
|
||
{"vextractf128", XXXX},
|
||
{"vextractps", XXXX},
|
||
{"vfmadd132pd", XXXX},
|
||
{"vfmadd132ps", XXXX},
|
||
{"vfmadd132sd", XXXX},
|
||
{"vfmadd132ss", XXXX},
|
||
{"vfmadd213pd", XXXX},
|
||
{"vfmadd213ps", XXXX},
|
||
{"vfmadd213sd", XXXX},
|
||
{"vfmadd213ss", XXXX},
|
||
{"vfmadd231pd", XXXX},
|
||
{"vfmadd231ps", XXXX},
|
||
{"vfmadd231sd", XXXX},
|
||
{"vfmadd231ss", XXXX},
|
||
{"vfmaddsub132pd", XXXX},
|
||
{"vfmaddsub132ps", XXXX},
|
||
{"vfmaddsub213pd", XXXX},
|
||
{"vfmaddsub213ps", XXXX},
|
||
{"vfmaddsub231pd", XXXX},
|
||
{"vfmaddsub231ps", XXXX},
|
||
{"vfmsub132pd", XXXX},
|
||
{"vfmsub132ps", XXXX},
|
||
{"vfmsub132sd", XXXX},
|
||
{"vfmsub132ss", XXXX},
|
||
{"vfmsub213pd", XXXX},
|
||
{"vfmsub213ps", XXXX},
|
||
{"vfmsub213sd", XXXX},
|
||
{"vfmsub213ss", XXXX},
|
||
{"vfmsub231pd", XXXX},
|
||
{"vfmsub231ps", XXXX},
|
||
{"vfmsub231sd", XXXX},
|
||
{"vfmsub231ss", XXXX},
|
||
{"vfmsubadd132pd", XXXX},
|
||
{"vfmsubadd132ps", XXXX},
|
||
{"vfmsubadd213pd", XXXX},
|
||
{"vfmsubadd213ps", XXXX},
|
||
{"vfmsubadd231pd", XXXX},
|
||
{"vfmsubadd231ps", XXXX},
|
||
{"vhaddpd", XXXX},
|
||
{"vhaddps", XXXX},
|
||
{"vinsertf128", XXXX},
|
||
{"vinsertps", XXXX},
|
||
{"vlddqu", XXXX},
|
||
{"vldmxcsr", XXXX},
|
||
{"vmaskmovdqu", XXXX},
|
||
{"vmaskmovpd", XXXX},
|
||
{"vmaskmovps", XXXX},
|
||
{"vmaxpd", XXXX},
|
||
{"vmaxps", XXXX},
|
||
{"vmaxsd", XXXX},
|
||
{"vmaxss", XXXX},
|
||
{"vminpd", XXXX},
|
||
{"vminps", XXXX},
|
||
{"vminsd", XXXX},
|
||
{"vminss", XXXX},
|
||
{"vmovapd", XXXX},
|
||
{"vmovaps", XXXX},
|
||
{"vmovd", XXXX},
|
||
{"vmovddup", XXXX},
|
||
{"vmovdqa", XXXX},
|
||
{"vmovdqu", XXXX},
|
||
{"vmovhlps", XXXX},
|
||
{"vmovhpd", XXXX},
|
||
{"vmovhps", XXXX},
|
||
{"vmovlhps", XXXX},
|
||
{"vmovlpd", XXXX},
|
||
{"vmovlps", XXXX},
|
||
{"vmovmskpd", XXXX},
|
||
{"vmovmskps", XXXX},
|
||
{"vmovntdq", XXXX},
|
||
{"vmovntdqa", XXXX},
|
||
{"vmovntpd", XXXX},
|
||
{"vmovntps", XXXX},
|
||
{"vmovq", XXXX},
|
||
{"vmovsd", XXXX},
|
||
{"vmovshdup", XXXX},
|
||
{"vmovsldup", XXXX},
|
||
{"vmovss", XXXX},
|
||
{"vmovupd", XXXX},
|
||
{"vmovups", XXXX},
|
||
{"vmpsadbw", XXXX},
|
||
{"vmulpd", XXXX},
|
||
{"vmulps", XXXX},
|
||
{"vmulsd", XXXX},
|
||
{"vmulss", XXXX},
|
||
{"vorpd", XXXX},
|
||
{"vorps", XXXX},
|
||
{"vpabsb", XXXX},
|
||
{"vpabsd", XXXX},
|
||
{"vpabsw", XXXX},
|
||
{"vpackssdw", XXXX},
|
||
{"vpacksswb", XXXX},
|
||
{"vpackusdw", XXXX},
|
||
{"vpackuswb", XXXX},
|
||
{"vpaddb", XXXX},
|
||
{"vpaddd", XXXX},
|
||
{"vpaddq", XXXX},
|
||
{"vpaddsb", XXXX},
|
||
{"vpaddsw", XXXX},
|
||
{"vpaddusb", XXXX},
|
||
{"vpaddusw", XXXX},
|
||
{"vpaddw", XXXX},
|
||
{"vpalignr", XXXX},
|
||
{"vpand", XXXX},
|
||
{"vpandn", XXXX},
|
||
{"vpavgb", XXXX},
|
||
{"vpavgw", XXXX},
|
||
{"vpblendvb", XXXX},
|
||
{"vpblendw", XXXX},
|
||
{"vpclmulqdq", XXXX},
|
||
{"vpcmpeqb", XXXX},
|
||
{"vpcmpeqd", XXXX},
|
||
{"vpcmpeqq", XXXX},
|
||
{"vpcmpeqw", XXXX},
|
||
{"vpcmpestri", XXXX},
|
||
{"vpcmpestrm", XXXX},
|
||
{"vpcmpgtb", XXXX},
|
||
{"vpcmpgtd", XXXX},
|
||
{"vpcmpgtq", XXXX},
|
||
{"vpcmpgtw", XXXX},
|
||
{"vpcmpistri", XXXX},
|
||
{"vpcmpistrm", XXXX},
|
||
{"vperm2f128", XXXX},
|
||
{"vpermilpd", XXXX},
|
||
{"vpermilps", XXXX},
|
||
{"vpextrb", XXXX},
|
||
{"vpextrd", XXXX},
|
||
{"vpextrq", XXXX},
|
||
{"vpextrw", XXXX},
|
||
{"vphaddd", XXXX},
|
||
{"vphaddsw", XXXX},
|
||
{"vphaddw", XXXX},
|
||
{"vphminposuw", XXXX},
|
||
{"vphsubd", XXXX},
|
||
{"vphsubsw", XXXX},
|
||
{"vphsubw", XXXX},
|
||
{"vpinsrb", XXXX},
|
||
{"vpinsrd", XXXX},
|
||
{"vpinsrq", XXXX},
|
||
{"vpinsrw", XXXX},
|
||
{"vpmaddubsw", XXXX},
|
||
{"vpmaddwd", XXXX},
|
||
{"vpmaxsb", XXXX},
|
||
{"vpmaxsd", XXXX},
|
||
{"vpmaxsw", XXXX},
|
||
{"vpmaxub", XXXX},
|
||
{"vpmaxud", XXXX},
|
||
{"vpmaxuw", XXXX},
|
||
{"vpminsb", XXXX},
|
||
{"vpminsd", XXXX},
|
||
{"vpminsw", XXXX},
|
||
{"vpminub", XXXX},
|
||
{"vpminud", XXXX},
|
||
{"vpminuw", XXXX},
|
||
{"vpmovmskb", XXXX},
|
||
{"vpmovsxbd", XXXX},
|
||
{"vpmovsxbq", XXXX},
|
||
{"vpmovsxbw", XXXX},
|
||
{"vpmovsxdq", XXXX},
|
||
{"vpmovsxwd", XXXX},
|
||
{"vpmovsxwq", XXXX},
|
||
{"vpmovzxbd", XXXX},
|
||
{"vpmovzxbq", XXXX},
|
||
{"vpmovzxbw", XXXX},
|
||
{"vpmovzxdq", XXXX},
|
||
{"vpmovzxwd", XXXX},
|
||
{"vpmovzxwq", XXXX},
|
||
{"vpmuldq", XXXX},
|
||
{"vpmulhrsw", XXXX},
|
||
{"vpmulhuw", XXXX},
|
||
{"vpmulhw", XXXX},
|
||
{"vpmulld", XXXX},
|
||
{"vpmullw", XXXX},
|
||
{"vpmuludq", XXXX},
|
||
{"vpor", XXXX},
|
||
{"vpsadbw", XXXX},
|
||
{"vpshufb", XXXX},
|
||
{"vpshufd", XXXX},
|
||
{"vpshufhw", XXXX},
|
||
{"vpshuflw", XXXX},
|
||
{"vpsignb", XXXX},
|
||
{"vpsignd", XXXX},
|
||
{"vpsignw", XXXX},
|
||
{"vpslld", XXXX},
|
||
{"vpslldq", XXXX},
|
||
{"vpsllq", XXXX},
|
||
{"vpsllw", XXXX},
|
||
{"vpsrad", XXXX},
|
||
{"vpsraw", XXXX},
|
||
{"vpsrld", XXXX},
|
||
{"vpsrldq", XXXX},
|
||
{"vpsrlq", XXXX},
|
||
{"vpsrlw", XXXX},
|
||
{"vpsubb", XXXX},
|
||
{"vpsubd", XXXX},
|
||
{"vpsubq", XXXX},
|
||
{"vpsubsb", XXXX},
|
||
{"vpsubsw", XXXX},
|
||
{"vpsubusb", XXXX},
|
||
{"vpsubusw", XXXX},
|
||
{"vpsubw", XXXX},
|
||
{"vptest", XXXX},
|
||
{"vpunpckhbw", XXXX},
|
||
{"vpunpckhdq", XXXX},
|
||
{"vpunpckhqdq", XXXX},
|
||
{"vpunpckhwd", XXXX},
|
||
{"vpunpcklbw", XXXX},
|
||
{"vpunpckldq", XXXX},
|
||
{"vpunpcklqdq", XXXX},
|
||
{"vpunpcklwd", XXXX},
|
||
{"vpxor", XXXX},
|
||
{"vrcpps", XXXX},
|
||
{"vrcpss", XXXX},
|
||
{"vroundpd", XXXX},
|
||
{"vroundps", XXXX},
|
||
{"vroundsd", XXXX},
|
||
{"vroundss", XXXX},
|
||
{"vshufpd", XXXX},
|
||
{"vshufps", XXXX},
|
||
{"vsqrtpd", XXXX},
|
||
{"vsqrtps", XXXX},
|
||
{"vsqrtsd", XXXX},
|
||
{"vsqrtss", XXXX},
|
||
{"vstmxcsr", XXXX},
|
||
{"vsubpd", XXXX},
|
||
{"vsubps", XXXX},
|
||
{"vsubsd", XXXX},
|
||
{"vsubss", XXXX},
|
||
{"vucomisd", XXXX},
|
||
{"vucomiss", XXXX},
|
||
{"vunpckhpd", XXXX},
|
||
{"vunpckhps", XXXX},
|
||
{"vunpcklpd", XXXX},
|
||
{"vunpcklps", XXXX},
|
||
{"vxorpd", XXXX},
|
||
{"vxorps", XXXX},
|
||
{"vzeroall", XXXX},
|
||
{"vzeroupper", XXXX},
|
||
#endif
|
||
#ifndef ASM_X86_64
|
||
{"wait", Op_0},
|
||
#endif
|
||
{"wbinvd", Op_0},
|
||
//{"wrfsbase", XXXX},
|
||
//{"wrgsbase", XXXX},
|
||
{"wrmsr", Op_0},
|
||
{"xadd", Op_UpdUpdF},
|
||
{"xchg", Op_UpdUpd},
|
||
{"xgetbv", Op_xgetbv},
|
||
{"xlat", Op_xlat},
|
||
{"xlatb", Op_0_AX},
|
||
{"xor", Op_DstSrcF},
|
||
{"xorpd", Op_UpdSrcSSE},
|
||
{"xorps", Op_UpdSrcSSE},
|
||
#ifdef ASM_X86_64
|
||
{"xorq", Op_DstSrcNT}, //?
|
||
#endif
|
||
//{"xrstor", XXXX},
|
||
//{"xrstor64", XXXX},
|
||
//{"xsave", XXXX},
|
||
//{"xsave64", XXXX},
|
||
//{"xsaveopt", XXXX},
|
||
//{"xsaveopt64", XXXX},
|
||
//{"xsetbv", XXXX},
|
||
};
|
||
|
||
typedef enum {
|
||
Default_Ptr = 0,
|
||
Byte_Ptr = 1,
|
||
Short_Ptr = 2,
|
||
Int_Ptr = 4,
|
||
QWord_Ptr = 8,
|
||
Float_Ptr = 4,
|
||
Double_Ptr = 8,
|
||
Extended_Ptr = 10,
|
||
Near_Ptr = 98,
|
||
Far_Ptr = 99,
|
||
N_PtrTypes
|
||
} PtrType;
|
||
|
||
static const int N_PtrNames = 8;
|
||
static const char *ptrTypeNameTable[N_PtrNames] = {
|
||
"word", "dword", "qword", "float", "double", "extended", "near", "far"};
|
||
|
||
static Identifier *ptrTypeIdentTable[N_PtrNames];
|
||
static PtrType ptrTypeValueTable[N_PtrNames] = {
|
||
Short_Ptr, Int_Ptr, QWord_Ptr, Float_Ptr,
|
||
Double_Ptr, Extended_Ptr, Near_Ptr, Far_Ptr};
|
||
|
||
typedef enum { Opr_Invalid, Opr_Immediate, Opr_Reg, Opr_Mem } OperandClass;
|
||
|
||
/* kill inlining if we reference a local? */
|
||
|
||
/* DMD seems to allow only one 'symbol' per operand .. include __LOCAL_SIZE */
|
||
|
||
/* DMD offset usage: <parm>[<reg>] seems to always be relative to EBP+8 .. even
|
||
if naked.. */
|
||
|
||
// mov eax, 4
|
||
// mov eax, fs:4
|
||
// -- have to assume we know whether or not to use '$'
|
||
|
||
static Token eof_tok;
|
||
static Expression *Handled;
|
||
static Identifier *ident_seg;
|
||
|
||
struct AsmProcessor {
|
||
struct Operand {
|
||
int inBracket;
|
||
int hasBracket;
|
||
int hasNumber;
|
||
int isOffset;
|
||
|
||
Reg segmentPrefix;
|
||
Reg reg;
|
||
sinteger_t constDisplacement; // use to build up.. should be int constant in
|
||
// the end..
|
||
Expressions symbolDisplacement; // array of expressions or..
|
||
Reg baseReg;
|
||
Reg indexReg;
|
||
int scale;
|
||
|
||
OperandClass cls;
|
||
PtrType dataSize;
|
||
PtrType dataSizeHint; // DMD can use the type of a referenced variable
|
||
};
|
||
|
||
static const unsigned Max_Operands = 3;
|
||
|
||
InlineAsmStatement *stmt;
|
||
Scope *sc;
|
||
|
||
Token *token;
|
||
std::ostringstream insnTemplate;
|
||
|
||
AsmOp op;
|
||
AsmOpInfo *opInfo;
|
||
Operand operands[Max_Operands];
|
||
Identifier *opIdent;
|
||
Operand *operand;
|
||
|
||
AsmProcessor(Scope *sc, InlineAsmStatement *stmt) {
|
||
this->sc = sc;
|
||
this->stmt = stmt;
|
||
token = stmt->tokens;
|
||
|
||
opInfo = nullptr;
|
||
|
||
if (!regInfo[0].ident) {
|
||
char buf[8], *p;
|
||
|
||
for (int i = 0; i < N_Regs; i++) {
|
||
strncpy(buf, regInfo[i].name.data(), sizeof(buf) - 1);
|
||
for (p = buf; *p; p++) {
|
||
*p = std::tolower(*p);
|
||
}
|
||
regInfo[i].gccName = std::string(buf, p - buf);
|
||
if ((i <= Reg_ST || i > Reg_ST7) && i != Reg_EFLAGS) {
|
||
regInfo[i].ident =
|
||
Identifier::idPool(regInfo[i].name.data(),
|
||
static_cast<unsigned>(regInfo[i].name.size()));
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < N_PtrNames; i++) {
|
||
ptrTypeIdentTable[i] = Identifier::idPool(ptrTypeNameTable[i]);
|
||
}
|
||
|
||
Handled = createExpression(Loc(), EXP::void_);
|
||
|
||
ident_seg = Identifier::idPool("seg");
|
||
|
||
eof_tok.value = TOK::endOfFile;
|
||
eof_tok.next = nullptr;
|
||
}
|
||
}
|
||
|
||
void run() { parse(); }
|
||
|
||
void nextToken() {
|
||
if (token->next) {
|
||
token = token->next;
|
||
} else {
|
||
token = &eof_tok;
|
||
}
|
||
}
|
||
|
||
Token *peekToken() {
|
||
if (token->next) {
|
||
return token->next;
|
||
}
|
||
return &eof_tok;
|
||
}
|
||
|
||
void expectEnd() {
|
||
if (token->value != TOK::endOfFile) {
|
||
error(stmt->loc, "expected end of statement"); // %% extra at end...
|
||
}
|
||
}
|
||
|
||
void parse() {
|
||
op = parseOpcode();
|
||
|
||
switch (op) {
|
||
case Op_Align:
|
||
doAlign();
|
||
expectEnd();
|
||
break;
|
||
case Op_Even:
|
||
doEven();
|
||
expectEnd();
|
||
break;
|
||
case Op_Naked:
|
||
doNaked();
|
||
expectEnd();
|
||
break;
|
||
case Op_Invalid:
|
||
break;
|
||
default:
|
||
if (op >= Op_db && op <= Op_de) {
|
||
doData();
|
||
} else {
|
||
doInstruction();
|
||
}
|
||
}
|
||
}
|
||
|
||
AsmOp parseOpcode() {
|
||
static const int N_ents = sizeof(opData) / sizeof(AsmOpEnt);
|
||
|
||
switch (token->value) {
|
||
case TOK::align_:
|
||
nextToken();
|
||
return Op_Align;
|
||
case TOK::in_:
|
||
nextToken();
|
||
opIdent = Id::___in;
|
||
return Op_in;
|
||
case TOK::int32: // "int"
|
||
nextToken();
|
||
opIdent = Id::__int;
|
||
return Op_SrcImm;
|
||
case TOK::out_:
|
||
nextToken();
|
||
opIdent = Id::___out;
|
||
return Op_out;
|
||
case TOK::identifier:
|
||
// search for mnemonic below
|
||
break;
|
||
default:
|
||
error(stmt->loc, "expected opcode");
|
||
return Op_Invalid;
|
||
}
|
||
|
||
opIdent = token->ident;
|
||
const char *opcode = token->ident->toChars();
|
||
|
||
nextToken();
|
||
|
||
// %% okay to use bsearch?
|
||
int i = 0, j = N_ents, k, l;
|
||
do {
|
||
k = (i + j) / 2;
|
||
l = strcmp(opcode, opData[k].inMnemonic);
|
||
if (!l) {
|
||
return opData[k].asmOp;
|
||
}
|
||
if (l < 0) {
|
||
j = k;
|
||
} else {
|
||
i = k + 1;
|
||
}
|
||
} while (i != j);
|
||
|
||
error(stmt->loc, "unknown opcode `%s`", opcode);
|
||
|
||
return Op_Invalid;
|
||
}
|
||
|
||
// need clobber information.. use information is good too...
|
||
void doInstruction() {
|
||
unsigned operand_i = 0;
|
||
|
||
opInfo = &asmOpInfo[op];
|
||
memset(operands, 0, sizeof(operands));
|
||
|
||
if (token->value == TOK::endOfFile && op == Op_FMath0) {
|
||
// no iteration for x86 vs. single iteration for x64
|
||
#ifndef ASM_X86_64
|
||
while (false)
|
||
#else
|
||
for (operand_i = 0; operand_i < 1; operand_i++)
|
||
#endif
|
||
{
|
||
operand = &operands[operand_i];
|
||
operand->reg = operand->baseReg = operand->indexReg =
|
||
operand->segmentPrefix = Reg_Invalid;
|
||
operand->cls = Opr_Reg;
|
||
if (operand_i == 0) {
|
||
operand->reg = Reg_ST;
|
||
} else {
|
||
operand->reg = Reg_ST1;
|
||
}
|
||
operand->hasNumber = 0;
|
||
operand->constDisplacement = 0;
|
||
parseOperand();
|
||
|
||
if (matchOperands(operand_i)) {
|
||
auto asmcode = new AsmCode(N_Regs);
|
||
|
||
if (formatInstruction(operand_i, asmcode)) {
|
||
stmt->asmcode = asmcode;
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
while (token->value != TOK::endOfFile) {
|
||
if (operand_i < Max_Operands) {
|
||
operand = &operands[operand_i];
|
||
operand->reg = operand->baseReg = operand->indexReg =
|
||
operand->segmentPrefix = Reg_Invalid;
|
||
parseOperand();
|
||
operand_i++;
|
||
} else {
|
||
error(stmt->loc, "too many operands for instruction");
|
||
break;
|
||
}
|
||
|
||
if (token->value == TOK::comma) {
|
||
nextToken();
|
||
} else if (token->value != TOK::endOfFile) {
|
||
error(stmt->loc, "end of instruction expected, not `%s`", token->toChars());
|
||
return;
|
||
}
|
||
}
|
||
/*
|
||
if (operand_i < opInfo->minOperands) {
|
||
error(stmt->loc, "too few operands for instruction");
|
||
}
|
||
*/
|
||
|
||
if (matchOperands(operand_i)) {
|
||
auto asmcode = new AsmCode(N_Regs);
|
||
|
||
if (formatInstruction(operand_i, asmcode)) {
|
||
stmt->asmcode = asmcode;
|
||
}
|
||
}
|
||
}
|
||
|
||
void setAsmCode() {
|
||
auto asmcode = new AsmCode(N_Regs);
|
||
asmcode->insnTemplate = insnTemplate.str();
|
||
Logger::cout() << "insnTemplate = " << asmcode->insnTemplate << '\n';
|
||
stmt->asmcode = asmcode;
|
||
}
|
||
|
||
// note: doesn't update AsmOp op
|
||
bool matchOperands(unsigned nOperands) {
|
||
bool wrong_number = true;
|
||
|
||
for (unsigned i = 0; i < nOperands; i++) {
|
||
classifyOperand(&operands[i]);
|
||
}
|
||
|
||
while (1) {
|
||
if (nOperands == opInfo->nOperands()) {
|
||
wrong_number = false;
|
||
/* Cases in which number of operands is not
|
||
enough for a match: Op_FCmp/Op_FCmp1,
|
||
Op_FCmpP/Op_FCmpP1 */
|
||
for (unsigned i = 0; i < nOperands; i++) {
|
||
Operand *operand = &operands[i];
|
||
|
||
switch (opInfo->operands[i] & Opr_ClassMask) {
|
||
case OprC_Mem: // no FPMem currently
|
||
if (operand->cls != Opr_Mem) {
|
||
goto no_match;
|
||
}
|
||
break;
|
||
case OprC_RFP:
|
||
if (!(operand->reg >= Reg_ST && operand->reg <= Reg_ST7)) {
|
||
goto no_match;
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
no_match:
|
||
if (opInfo->linkType == Next_Form) {
|
||
opInfo = &asmOpInfo[op = static_cast<AsmOp>(opInfo->link)];
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
if (wrong_number) {
|
||
error(stmt->loc, "wrong number of operands");
|
||
} else {
|
||
error(stmt->loc, "wrong operand types");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// OSX and 32-bit Windows need an extra leading underscore when mangling a
|
||
// symbol name.
|
||
static bool prependExtraUnderscore(LINK link) {
|
||
const auto &triple = *global.params.targetTriple;
|
||
return triple.isOSDarwin() ||
|
||
// Win32: all symbols except for MSVC++ ones
|
||
(triple.isOSWindows() && triple.isArch32Bit() && link != LINK::cpp);
|
||
}
|
||
|
||
void addOperand(const char *fmt, AsmArgType type, Expression *e,
|
||
AsmCode *asmcode, AsmArgMode mode = Mode_Input) {
|
||
using namespace dmd;
|
||
|
||
if (sc->func->isNaked()) {
|
||
switch (type) {
|
||
case Arg_Integer:
|
||
if (e->type->isunsigned()) {
|
||
insnTemplate << "$" << e->toUInteger();
|
||
} else {
|
||
#ifndef ASM_X86_64
|
||
insnTemplate << "$" << static_cast<sinteger_t>(e->toInteger());
|
||
#else
|
||
insnTemplate << "$" << e->toInteger();
|
||
#endif
|
||
}
|
||
break;
|
||
|
||
case Arg_Pointer:
|
||
error(stmt->loc, "unsupported pointer reference to `%s` in naked asm",
|
||
e->toChars());
|
||
break;
|
||
|
||
case Arg_Memory:
|
||
// Peel off one layer of explicitly taking the address, if present.
|
||
if (auto ae = e->isAddrExp()) {
|
||
e = ae->e1;
|
||
}
|
||
|
||
if (auto v = e->isVarExp()) {
|
||
if (VarDeclaration *vd = v->var->isVarDeclaration()) {
|
||
if (!vd->isDataseg()) {
|
||
error(stmt->loc, "only global variables can be referenced by "
|
||
"identifier in naked asm");
|
||
break;
|
||
}
|
||
|
||
// print out the mangle
|
||
if (prependExtraUnderscore(vd->resolvedLinkage())) {
|
||
insnTemplate << "_";
|
||
}
|
||
OutBuffer buf;
|
||
mangleToBuffer(vd, buf);
|
||
insnTemplate << buf.peekChars();
|
||
getIrGlobal(vd, true)->nakedUse = true;
|
||
break;
|
||
}
|
||
}
|
||
error(stmt->loc, "unsupported memory reference to `%s` in naked asm",
|
||
e->toChars());
|
||
break;
|
||
|
||
default:
|
||
llvm_unreachable("Unsupported argument in asm.");
|
||
break;
|
||
}
|
||
} else {
|
||
insnTemplate << fmt << "<<" << (mode == Mode_Input ? "in" : "out")
|
||
<< asmcode->args.size() << ">>";
|
||
asmcode->args.push_back(AsmArg(type, e, mode));
|
||
}
|
||
}
|
||
void addOperand2(const char *fmtpre, const char *fmtpost, AsmArgType type,
|
||
Expression *e, AsmCode *asmcode,
|
||
AsmArgMode mode = Mode_Input) {
|
||
if (sc->func->isNaked()) {
|
||
// taken from above
|
||
error(stmt->loc, "only global variables can be referenced by identifier in "
|
||
"naked asm");
|
||
return;
|
||
}
|
||
|
||
insnTemplate << fmtpre << "<<" << (mode == Mode_Input ? "in" : "out")
|
||
<< ">>" << fmtpost;
|
||
asmcode->args.push_back(AsmArg(type, e, mode));
|
||
}
|
||
|
||
void addLabel(const char *id) {
|
||
// We need to delay emitting the actual function name, see
|
||
// replace_func_name in asmstmt.cpp for details.
|
||
printLabelName(insnTemplate, "<<func>>", id);
|
||
}
|
||
|
||
/* Determines whether the operand is a register, memory reference
|
||
or immediate. Immediate addresses are currently classified as
|
||
memory. This function is called before the exact instructions
|
||
is known and thus, should not use opInfo. */
|
||
void classifyOperand(Operand *operand) {
|
||
operand->cls = classifyOperand1(operand);
|
||
}
|
||
|
||
OperandClass classifyOperand1(Operand *operand) {
|
||
bool is_localsize = false;
|
||
bool really_have_symbol = false;
|
||
|
||
if (operand->symbolDisplacement.length) {
|
||
is_localsize = isLocalSize(operand->symbolDisplacement[0]);
|
||
really_have_symbol = !is_localsize;
|
||
}
|
||
|
||
if (operand->isOffset && !operand->hasBracket) {
|
||
return Opr_Immediate;
|
||
}
|
||
|
||
if (operand->hasBracket ||
|
||
really_have_symbol) // %% redo for 'offset' function
|
||
{
|
||
if (operand->reg != Reg_Invalid) {
|
||
invalidExpression();
|
||
return Opr_Invalid;
|
||
}
|
||
|
||
return Opr_Mem;
|
||
}
|
||
|
||
if (operand->reg != Reg_Invalid && operand->constDisplacement != 0) {
|
||
invalidExpression();
|
||
return Opr_Invalid;
|
||
}
|
||
|
||
if (operand->segmentPrefix != Reg_Invalid) {
|
||
if (operand->reg != Reg_Invalid) {
|
||
invalidExpression();
|
||
return Opr_Invalid;
|
||
}
|
||
|
||
return Opr_Mem;
|
||
}
|
||
|
||
if (operand->reg != Reg_Invalid && !operand->hasNumber) {
|
||
return Opr_Reg;
|
||
}
|
||
|
||
// should check immediate given (operand->hasNumber);
|
||
//
|
||
if (operand->hasNumber || is_localsize) {
|
||
// size determination not correct if there are symbols Opr_Immediate
|
||
if (operand->dataSize == Default_Ptr) {
|
||
if (operand->constDisplacement < 0x100) {
|
||
operand->dataSize = Byte_Ptr;
|
||
} else if (operand->constDisplacement < 0x10000) {
|
||
operand->dataSize = Short_Ptr;
|
||
}
|
||
#ifndef ASM_X86_64
|
||
else {
|
||
operand->dataSize = Int_Ptr;
|
||
}
|
||
#else
|
||
else if (operand->constDisplacement <= 0xFFFFFFFF) {
|
||
operand->dataSize = Int_Ptr;
|
||
} else {
|
||
// This could be possible since we are using 48 bits
|
||
operand->dataSize = QWord_Ptr;
|
||
}
|
||
#endif
|
||
}
|
||
return Opr_Immediate;
|
||
}
|
||
|
||
// probably a bug,?
|
||
error(stmt->loc, "invalid operand");
|
||
return Opr_Invalid;
|
||
}
|
||
|
||
void writeReg(Reg reg) { insnTemplate << "%" << regInfo[reg].gccName; }
|
||
|
||
bool opTakesLabel() {
|
||
switch (op) {
|
||
case Op_Branch:
|
||
case Op_CBranch:
|
||
case Op_Loop:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool getTypeSuffix(TypeNeeded needed, PtrType ptrtype,
|
||
std::string &type_suffix) {
|
||
switch (needed) {
|
||
case Byte_NoType:
|
||
return ptrtype == Byte_Ptr;
|
||
case Word_Types:
|
||
if (ptrtype == Byte_Ptr) {
|
||
return false;
|
||
}
|
||
// fallthrough
|
||
case Int_Types:
|
||
switch (ptrtype) {
|
||
case Byte_Ptr:
|
||
type_suffix = 'b';
|
||
break;
|
||
case Short_Ptr:
|
||
type_suffix = 'w';
|
||
break;
|
||
case Int_Ptr:
|
||
type_suffix = 'l';
|
||
break;
|
||
#ifdef ASM_X86_64
|
||
case QWord_Ptr:
|
||
type_suffix = 'q';
|
||
break;
|
||
#endif
|
||
default:
|
||
// %% these may be too strict
|
||
return false;
|
||
}
|
||
break;
|
||
case FPInt_Types:
|
||
switch (ptrtype) {
|
||
case Short_Ptr:
|
||
type_suffix = 's';
|
||
break;
|
||
case Int_Ptr:
|
||
type_suffix = 'l';
|
||
break;
|
||
case QWord_Ptr:
|
||
type_suffix = "ll";
|
||
break;
|
||
default:
|
||
return false;
|
||
}
|
||
break;
|
||
case FP_Types:
|
||
switch (ptrtype) {
|
||
case Float_Ptr:
|
||
type_suffix = 's';
|
||
break;
|
||
case Double_Ptr:
|
||
type_suffix = 'l';
|
||
break;
|
||
case Extended_Ptr:
|
||
type_suffix = 't';
|
||
break;
|
||
default:
|
||
return false;
|
||
}
|
||
break;
|
||
default:
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// also set impl clobbers
|
||
bool formatInstruction(int nOperands, AsmCode *asmcode) {
|
||
using namespace dmd;
|
||
|
||
const char *fmt;
|
||
const char *mnemonic;
|
||
std::string type_suffix;
|
||
bool use_star;
|
||
AsmArgMode mode;
|
||
|
||
insnTemplate.str("");
|
||
// %% todo: special case for something..
|
||
if (opInfo->linkType == Out_Mnemonic) {
|
||
mnemonic = alternateMnemonics[opInfo->link];
|
||
} else {
|
||
mnemonic = opIdent->toChars();
|
||
}
|
||
|
||
// handle two-operand form where second arg is ignored.
|
||
// must be done before type_suffix detection
|
||
if (op == Op_FidR_P || op == Op_fxch || op == Op_FfdRR_P) {
|
||
if (operands[1].cls == Opr_Reg && operands[1].reg == Reg_ST) {
|
||
nOperands = 1;
|
||
} else {
|
||
error(stmt->loc, "instruction allows only `ST` as second argument");
|
||
}
|
||
}
|
||
|
||
#ifdef ASM_X86_64
|
||
if (op == Op_FCmpFlgP) {
|
||
// Explicitly add %st as second argument to fucomip – it should
|
||
// be implicit, but the GNU as shipping with Mac OS X (Apple Inc
|
||
// version cctools-800~26, GNU assembler version 1.38) chokes on
|
||
// it otherwise.
|
||
assert(nOperands == 1);
|
||
nOperands = 2;
|
||
operands[1] = operands[0];
|
||
memset(operands, 0, sizeof(Operand));
|
||
operands[0].cls = Opr_Reg;
|
||
operands[0].reg = Reg_ST;
|
||
}
|
||
#endif
|
||
|
||
if (opInfo->needsType) {
|
||
PtrType exact_type = Default_Ptr;
|
||
PtrType min_type = Default_Ptr;
|
||
PtrType hint_type = Default_Ptr;
|
||
|
||
/* Default types: This attempts to match the observed behavior of DMD
|
||
*/
|
||
switch (opInfo->needsType) {
|
||
case Int_Types:
|
||
min_type = Byte_Ptr;
|
||
break;
|
||
case Word_Types:
|
||
min_type = Short_Ptr;
|
||
break;
|
||
case FPInt_Types:
|
||
if (op == Op_Fis_ST) { // integer math instructions
|
||
min_type = Int_Ptr;
|
||
} else { // compare, load, store
|
||
min_type = Short_Ptr;
|
||
}
|
||
break;
|
||
case FP_Types:
|
||
min_type = Float_Ptr;
|
||
break;
|
||
}
|
||
if (op == Op_push && operands[0].cls == Opr_Immediate) {
|
||
#ifndef ASM_X86_64
|
||
min_type = Int_Ptr;
|
||
#else
|
||
min_type = QWord_Ptr;
|
||
#endif
|
||
}
|
||
|
||
for (int i = 0; i < nOperands; i++) {
|
||
if (hint_type == Default_Ptr && !(opInfo->operands[i] & Opr_NoType)) {
|
||
hint_type = operands[i].dataSizeHint;
|
||
}
|
||
|
||
if ((opInfo->operands[i] & Opr_NoType) ||
|
||
operands[i].dataSize == Default_Ptr) {
|
||
continue;
|
||
}
|
||
if (operands[i].cls == Opr_Immediate) {
|
||
min_type =
|
||
operands[i].dataSize > min_type ? operands[i].dataSize : min_type;
|
||
} else {
|
||
exact_type =
|
||
operands[i].dataSize; // could check for conflicting types
|
||
break;
|
||
}
|
||
}
|
||
|
||
bool type_ok;
|
||
if (exact_type == Default_Ptr) {
|
||
type_ok = getTypeSuffix(static_cast<TypeNeeded>(opInfo->needsType),
|
||
hint_type, type_suffix);
|
||
if (!type_ok) {
|
||
type_ok = getTypeSuffix(static_cast<TypeNeeded>(opInfo->needsType),
|
||
min_type, type_suffix);
|
||
}
|
||
} else {
|
||
type_ok = getTypeSuffix(static_cast<TypeNeeded>(opInfo->needsType),
|
||
exact_type, type_suffix);
|
||
}
|
||
|
||
if (!type_ok) {
|
||
error(stmt->loc, "invalid operand size");
|
||
return false;
|
||
}
|
||
} else if (op == Op_Branch) {
|
||
if (operands[0].dataSize == Far_Ptr) { // %% type=Far_Ptr not set by
|
||
// Seg:Ofss OTOH, we don't support
|
||
// that..
|
||
insnTemplate << 'l';
|
||
}
|
||
} else if (op == Op_FMath0 || op == Op_FdST0ST1) {
|
||
operands[0].cls = Opr_Reg;
|
||
operands[0].reg = Reg_ST1;
|
||
operands[1].cls = Opr_Reg;
|
||
operands[1].reg = Reg_ST;
|
||
nOperands = 2;
|
||
}
|
||
|
||
switch (op) {
|
||
case Op_SizedStack: {
|
||
int mlen = strlen(mnemonic);
|
||
if (mnemonic[mlen - 1] == 'd') {
|
||
insnTemplate.write(mnemonic, mlen - 1);
|
||
} else {
|
||
insnTemplate << mnemonic << 'w';
|
||
}
|
||
} break;
|
||
case Op_cmpsd:
|
||
case Op_insX:
|
||
case Op_lodsX:
|
||
case Op_movsd:
|
||
case Op_outsX:
|
||
case Op_scasX:
|
||
case Op_stosX: {
|
||
int mlen = strlen(mnemonic);
|
||
if (mnemonic[mlen - 1] == 'd') {
|
||
insnTemplate.write(mnemonic, mlen - 1) << 'l';
|
||
} else {
|
||
insnTemplate << mnemonic;
|
||
}
|
||
} break;
|
||
case Op_movsx:
|
||
case Op_movzx: {
|
||
char tc_1;
|
||
int mlen = strlen(mnemonic);
|
||
PtrType op1_size = operands[1].dataSize;
|
||
if (op1_size == Default_Ptr) {
|
||
op1_size = operands[1].dataSizeHint;
|
||
}
|
||
// Need type char for source arg
|
||
switch (op1_size) {
|
||
case Byte_Ptr:
|
||
case Default_Ptr:
|
||
tc_1 = 'b';
|
||
break;
|
||
case Short_Ptr:
|
||
tc_1 = 'w';
|
||
break;
|
||
default:
|
||
error(stmt->loc, "invalid operand size/type");
|
||
return false;
|
||
}
|
||
assert(!type_suffix.empty());
|
||
insnTemplate.write(mnemonic, mlen - 1) << tc_1 << type_suffix;
|
||
} break;
|
||
|
||
default:
|
||
// special case fdiv, fsub: see dmd 840, ldc 256
|
||
if ((strncmp(mnemonic, "fsub", 4) == 0 ||
|
||
strncmp(mnemonic, "fdiv", 4) == 0) &&
|
||
operands[0].reg != Reg_ST && op != Op_FMath) {
|
||
// replace:
|
||
// f{sub,div}r{p,} <-> f{sub,div}{p,}
|
||
if (mnemonic[4] == 'r') {
|
||
insnTemplate.write(mnemonic, 4);
|
||
insnTemplate.write(mnemonic + 5, strlen(mnemonic) - 5);
|
||
} else {
|
||
insnTemplate.write(mnemonic, 4) << "r";
|
||
insnTemplate.write(mnemonic + 4, strlen(mnemonic) - 4);
|
||
}
|
||
} else {
|
||
insnTemplate << mnemonic;
|
||
}
|
||
// the no-operand versions of floating point ops always pop
|
||
if (op == Op_FMath0) {
|
||
insnTemplate << "p";
|
||
}
|
||
insnTemplate << type_suffix;
|
||
break;
|
||
}
|
||
|
||
switch (opInfo->implicitClobbers & Clb_DXAX_Mask) {
|
||
case Clb_SizeAX:
|
||
case Clb_EAX:
|
||
asmcode->regs[Reg_EAX] = true;
|
||
break;
|
||
case Clb_SizeDXAX:
|
||
asmcode->regs[Reg_EAX] = true;
|
||
if (type_suffix != "b") {
|
||
asmcode->regs[Reg_EDX] = true;
|
||
}
|
||
break;
|
||
#ifdef ASM_X86_64
|
||
case Clb_SizeRDXRAX:
|
||
asmcode->regs[Reg_RAX] = true;
|
||
asmcode->regs[Reg_RDX] = true;
|
||
break;
|
||
#endif
|
||
default:
|
||
// nothing
|
||
break;
|
||
}
|
||
|
||
if (opInfo->implicitClobbers & Clb_DI) {
|
||
asmcode->regs[Reg_EDI] = true;
|
||
}
|
||
if (opInfo->implicitClobbers & Clb_SI) {
|
||
asmcode->regs[Reg_ESI] = true;
|
||
}
|
||
if (opInfo->implicitClobbers & Clb_CX) {
|
||
asmcode->regs[Reg_ECX] = true;
|
||
}
|
||
if (opInfo->implicitClobbers & Clb_SP) {
|
||
asmcode->regs[Reg_ESP] = true;
|
||
}
|
||
if (opInfo->implicitClobbers & Clb_ST) {
|
||
/* Can't figure out how to tell GCC that an
|
||
asm statement leaves an arg pushed on the stack.
|
||
Maybe if the statment had and input or output
|
||
operand it would work... In any case, clobbering
|
||
all FP prevents incorrect code generation. */
|
||
asmcode->regs[Reg_ST] = true;
|
||
asmcode->regs[Reg_ST1] = true;
|
||
asmcode->regs[Reg_ST2] = true;
|
||
asmcode->regs[Reg_ST3] = true;
|
||
asmcode->regs[Reg_ST4] = true;
|
||
asmcode->regs[Reg_ST5] = true;
|
||
asmcode->regs[Reg_ST6] = true;
|
||
asmcode->regs[Reg_ST7] = true;
|
||
}
|
||
if (opInfo->implicitClobbers & Clb_Flags) {
|
||
asmcode->regs[Reg_EFLAGS] = true;
|
||
}
|
||
if (op == Op_cpuid) {
|
||
asmcode->regs[Reg_EAX] = true;
|
||
asmcode->regs[Reg_EBX] = true;
|
||
asmcode->regs[Reg_ECX] = true;
|
||
asmcode->regs[Reg_EDX] = true;
|
||
}
|
||
|
||
insnTemplate << ' ';
|
||
for (int i__ = 0; i__ < nOperands; i__++) {
|
||
int i;
|
||
if (i__ != 0) {
|
||
insnTemplate << ", ";
|
||
}
|
||
fmt = "$";
|
||
|
||
switch (op) {
|
||
case Op_mul:
|
||
// gas won't accept the two-operand form; skip to the source
|
||
// operand
|
||
i__ = 1;
|
||
// fallthrough
|
||
case Op_bound:
|
||
case Op_enter:
|
||
i = i__;
|
||
break;
|
||
default:
|
||
i = nOperands - 1 - i__; // operand = & operands[ nOperands - 1 - i ];
|
||
break;
|
||
}
|
||
operand = &operands[i];
|
||
|
||
// This is used only in the case of `operand->cls` being `Opr_Mem`.
|
||
bool hasConstDisplacement = false;
|
||
|
||
switch (operand->cls) {
|
||
case Opr_Immediate:
|
||
// for implementing offset:
|
||
// $var + $7 // fails
|
||
// $var + 7 // ok
|
||
// $7 + $var // ok
|
||
|
||
// DMD doesn't seem to allow this
|
||
/*
|
||
if ( opTakesLabel() ) tho... (near ptr <Number> would be abs?)
|
||
fmt = "$a"; // GAS won't accept "jmp $42"; must be "jmp 42"
|
||
(rel) or
|
||
"jmp *42" (abs)
|
||
*/
|
||
if (opTakesLabel()) {
|
||
// "relative addressing not allowed in branch instructions" ..
|
||
error(stmt->loc, "integer constant not allowed in branch instructions");
|
||
return false;
|
||
}
|
||
|
||
if (operand->symbolDisplacement.length &&
|
||
isLocalSize(operand->symbolDisplacement[0])) {
|
||
// handle __LOCAL_SIZE, which in this constant, is an immediate
|
||
// should do this in slotexp..
|
||
addOperand("$", Arg_LocalSize, operand->symbolDisplacement[0],
|
||
asmcode);
|
||
if (operand->constDisplacement) {
|
||
insnTemplate << '+';
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (operand->symbolDisplacement.length) {
|
||
fmt = "$a";
|
||
addOperand("$", Arg_Pointer, operand->symbolDisplacement[0], asmcode);
|
||
|
||
if (operand->constDisplacement) {
|
||
insnTemplate << '+';
|
||
} else {
|
||
// skip the addOperand(fmt, Arg_Integer...) below
|
||
break;
|
||
}
|
||
}
|
||
addOperand(fmt, Arg_Integer, newIntExp(operand->constDisplacement),
|
||
asmcode);
|
||
break;
|
||
case Opr_Reg:
|
||
if (opInfo->operands[i] & Opr_Dest) {
|
||
Reg clbr_reg = static_cast<Reg>(regInfo[operand->reg].baseReg);
|
||
if (clbr_reg != Reg_Invalid) {
|
||
asmcode->regs[clbr_reg] = true;
|
||
}
|
||
}
|
||
if (opTakesLabel()) {
|
||
insnTemplate << '*';
|
||
}
|
||
writeReg(operand->reg);
|
||
/*
|
||
insnTemplate << "%";
|
||
insnTemplate << regInfo[operand->reg].name;
|
||
*/
|
||
break;
|
||
case Opr_Mem:
|
||
// better: use output operands for simple variable references
|
||
if ((opInfo->operands[i] & Opr_Update) == Opr_Update) {
|
||
mode = Mode_Update;
|
||
} else if (opInfo->operands[i] & Opr_Dest) {
|
||
mode = Mode_Output;
|
||
} else {
|
||
mode = Mode_Input;
|
||
}
|
||
|
||
use_star = opTakesLabel();
|
||
|
||
if (Logger::enabled()) {
|
||
Logger::cout() << "Opr_Mem\n";
|
||
LOG_SCOPE
|
||
Logger::cout() << "baseReg: " << operand->baseReg << '\n';
|
||
Logger::cout() << "segmentPrefix: " << operand->segmentPrefix << '\n';
|
||
Logger::cout() << "constDisplacement: " << operand->constDisplacement
|
||
<< '\n';
|
||
for (unsigned i = 0; i < operand->symbolDisplacement.length; i++) {
|
||
Expression *expr = operand->symbolDisplacement[i];
|
||
Logger::cout() << "symbolDisplacement[" << i
|
||
<< "] = " << expr->toChars() << '\n';
|
||
}
|
||
}
|
||
if (operand->segmentPrefix != Reg_Invalid) {
|
||
if (op != Op_Branch) {
|
||
writeReg(operand->segmentPrefix);
|
||
insnTemplate << ':';
|
||
} else {
|
||
error(stmt->loc, "Cannot generate a segment prefix for a "
|
||
"branching instruction");
|
||
}
|
||
}
|
||
|
||
// We make note of this for later on, as if a const-displacement is in play we
|
||
// may need to change the asm template to avoid it expanding to an invalid syntax.
|
||
hasConstDisplacement = (operand->segmentPrefix != Reg_Invalid &&
|
||
operand->symbolDisplacement.length == 0) ||
|
||
operand->constDisplacement;
|
||
if (hasConstDisplacement) {
|
||
insnTemplate << operand->constDisplacement;
|
||
if (operand->symbolDisplacement.length) {
|
||
insnTemplate << '+';
|
||
}
|
||
operand->constDisplacement = 0;
|
||
// addOperand(fmt, Arg_Integer,
|
||
// newIntExp(operand->constDisplacement),
|
||
// asmcode);
|
||
if (opInfo->operands[i] & Opr_Dest) {
|
||
asmcode->clobbersMemory = 1;
|
||
}
|
||
}
|
||
if (operand->symbolDisplacement.length) {
|
||
Expression *e = operand->symbolDisplacement[0];
|
||
Declaration *decl = nullptr;
|
||
|
||
if (auto ve = e->isVarExp()) {
|
||
decl = ve->var;
|
||
}
|
||
|
||
if (operand->baseReg != Reg_Invalid && decl && !decl->isDataseg()) {
|
||
|
||
// Use the offset from frame pointer
|
||
|
||
/* GCC doesn't give the front end access to stack offsets
|
||
when optimization is turned on (3.x) or at all (4.x).
|
||
|
||
Try to convert var[EBP] (or var[ESP] for naked funcs) to
|
||
a memory expression that does not require us to know
|
||
the stack offset.
|
||
*/
|
||
|
||
if (operand->indexReg == Reg_Invalid && decl->isVarDeclaration() &&
|
||
#ifndef ASM_X86_64
|
||
((operand->baseReg == Reg_EBP && !sc->func->isNaked()) ||
|
||
(operand->baseReg == Reg_ESP && sc->func->isNaked())))
|
||
#else
|
||
(((operand->baseReg == Reg_EBP ||
|
||
operand->baseReg == Reg_RBP) &&
|
||
!sc->func->isNaked()) ||
|
||
((operand->baseReg == Reg_ESP ||
|
||
operand->baseReg == Reg_RSP) &&
|
||
sc->func->isNaked())))
|
||
#endif
|
||
{
|
||
|
||
e = createAddrExp(Loc(), e);
|
||
e->type = pointerTo(decl->type);
|
||
|
||
operand->constDisplacement = 0;
|
||
operand->baseReg = Reg_Invalid;
|
||
|
||
addOperand(fmt, Arg_Memory, e, asmcode, mode);
|
||
|
||
} else {
|
||
// FIXME: what is this ?
|
||
addOperand2("${", ":a}", Arg_FrameRelative, e, asmcode);
|
||
}
|
||
if (opInfo->operands[i] & Opr_Dest) {
|
||
asmcode->clobbersMemory = 1;
|
||
}
|
||
} else {
|
||
// Plain memory reference to variable or reference to label.
|
||
|
||
/* If in a reg, DMD moves to memory.. even with -O, so we'll
|
||
do the
|
||
same
|
||
by always using the "m" contraint.
|
||
|
||
In order to get the correct output for function and label
|
||
symbols,
|
||
the %an format must be used with the "p" constraint.
|
||
*/
|
||
if (isDollar(e)) {
|
||
error(stmt->loc, "dollar labels are not supported");
|
||
asmcode->dollarLabel = 1;
|
||
} else if (auto dse = e->isDsymbolExp()) {
|
||
LabelDsymbol *lbl = dse->s->isLabel();
|
||
stmt->isBranchToLabel = lbl;
|
||
|
||
use_star = false;
|
||
addLabel(lbl->ident->toChars());
|
||
} else if ((decl && decl->isCodeseg())) // if function or label
|
||
{
|
||
use_star = false;
|
||
// simply write out the mangle
|
||
if (prependExtraUnderscore(decl->resolvedLinkage())) {
|
||
insnTemplate << "_";
|
||
}
|
||
OutBuffer buf;
|
||
mangleToBuffer(decl, buf);
|
||
insnTemplate << buf.peekChars();
|
||
// addOperand2("${", ":c}", Arg_Pointer, e,
|
||
// asmcode);
|
||
} else {
|
||
// We don't know ahead of time what kind of memory operand will be generated for
|
||
// our template: it could be a symbolic reference, or a Scale-Index-Base with or without
|
||
// an offset. So, if we have a const-displacement of 4, it could look like one of:
|
||
// `4+someGlobalVariable`, or `4+8(%ebp)`, or `4+(%ebp)`.
|
||
// Notice how that last possibility is an invalid syntax, it should be `4(%ebp)`.
|
||
// But, if we removed the `+`, then the other possibilities would become invalid or wrong.
|
||
// We don't know which form will be generated, but we can force the third possibility to
|
||
// be like the second possibility.
|
||
//
|
||
// For x86 assembly templates, memory operands can be marked with the `H` modifier, which
|
||
// unconditionally adds an offset of 8 to the memory operand.
|
||
// If we subtract 8 from our const-displacement, then we can use the `H` modifier to ensure
|
||
// that we always end up with a valid syntax for a memory operand with an offset.
|
||
// So, we do just that when we have const-displacement in play.
|
||
// (Only for non-naked asm, as this isn't an issue for naked asm.)
|
||
//
|
||
// See also: https://lists.llvm.org/pipermail/llvm-dev/2017-August/116244.html
|
||
const auto forceLeadingDisplacement = hasConstDisplacement && !sc->func->isNaked();
|
||
if (forceLeadingDisplacement) {
|
||
// Subtract 8 from our const-displacement, and prepare to add the 8 from the `H` modifier.
|
||
insnTemplate << "-8+";
|
||
}
|
||
|
||
if (use_star) {
|
||
insnTemplate << '*';
|
||
use_star = false;
|
||
}
|
||
|
||
if (!sc->func->isNaked()) // no addrexp in naked asm please :)
|
||
{
|
||
Type *tt = pointerTo(e->type);
|
||
e = createAddrExp(Loc(), e);
|
||
e->type = tt;
|
||
}
|
||
|
||
if (forceLeadingDisplacement) {
|
||
// We have a const-displacement in play, so we add the `H` modifier, as described earlier.
|
||
insnTemplate << "${" << "<<" << (mode == Mode_Input ? "in" : "out")
|
||
<< asmcode->args.size() << ">>" << ":H}";
|
||
asmcode->args.push_back(AsmArg(Arg_Memory, e, mode));
|
||
} else {
|
||
addOperand(fmt, Arg_Memory, e, asmcode, mode);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (use_star) {
|
||
insnTemplate << '*';
|
||
}
|
||
if (operand->baseReg != Reg_Invalid ||
|
||
operand->indexReg != Reg_Invalid) {
|
||
insnTemplate << '(';
|
||
if (operand->baseReg != Reg_Invalid) {
|
||
writeReg(operand->baseReg);
|
||
}
|
||
if (operand->indexReg != Reg_Invalid) {
|
||
insnTemplate << ',';
|
||
writeReg(operand->indexReg);
|
||
if (operand->scale) {
|
||
insnTemplate << "," << operand->scale;
|
||
}
|
||
}
|
||
insnTemplate << ')';
|
||
if (opInfo->operands[i] & Opr_Dest) {
|
||
asmcode->clobbersMemory = 1;
|
||
}
|
||
}
|
||
break;
|
||
case Opr_Invalid:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
mem.addRange(asmcode->args.data(), asmcode->args.size() * sizeof(AsmArg));
|
||
|
||
asmcode->insnTemplate = insnTemplate.str();
|
||
Logger::cout() << "insnTemplate = " << asmcode->insnTemplate << '\n';
|
||
return true;
|
||
}
|
||
|
||
bool isIntExp(Expression *exp) { return exp->op == EXP::int64; }
|
||
bool isRegExp(Expression *exp) { return exp->op == EXP::mod; } // ewww.%%
|
||
bool isLocalSize(Expression *exp) {
|
||
// cleanup: make a static var
|
||
auto ie = exp->isIdentifierExp();
|
||
return ie && ie->ident == Id::__LOCAL_SIZE;
|
||
}
|
||
bool isDollar(Expression *exp) {
|
||
auto ie = exp->isIdentifierExp();
|
||
return ie && ie->ident == Id::dollar;
|
||
}
|
||
|
||
Expression *newRegExp(int regno) {
|
||
auto e = IntegerExp::create(Loc(), regno, Type::tint32);
|
||
e->op = EXP::mod;
|
||
return e;
|
||
}
|
||
|
||
#ifndef ASM_X86_64
|
||
Expression *newIntExp(int v /* %% type */) {
|
||
// Only handles 32-bit numbers as this is IA-32.
|
||
return IntegerExp::create(stmt->loc, v, Type::tint32);
|
||
}
|
||
#else
|
||
Expression *newIntExp(long long v /* %% type */) {
|
||
// Handle 64 bit
|
||
return IntegerExp::create(stmt->loc, v, Type::tint64);
|
||
}
|
||
#endif
|
||
|
||
void slotExp(Expression *exp) {
|
||
/*
|
||
if offset, make a note
|
||
|
||
if integer, add to immediate
|
||
if reg:
|
||
if not in bracket, set reg (else error)
|
||
if in bracket:
|
||
if not base, set base
|
||
if not index, set index
|
||
else, error
|
||
if symbol:
|
||
set symbol field
|
||
*/
|
||
|
||
bool is_offset = false;
|
||
if (auto ae = exp->isAddrExp()) {
|
||
exp = ae->e1;
|
||
is_offset = true;
|
||
}
|
||
|
||
if (isIntExp(exp)) {
|
||
if (is_offset) {
|
||
invalidExpression();
|
||
}
|
||
operand->constDisplacement += exp->toInteger();
|
||
if (!operand->inBracket) {
|
||
operand->hasNumber = 1;
|
||
}
|
||
} else if (isRegExp(exp)) {
|
||
if (is_offset) {
|
||
invalidExpression();
|
||
}
|
||
if (!operand->inBracket) {
|
||
if (operand->reg == Reg_Invalid) {
|
||
operand->reg = static_cast<Reg>(exp->toInteger());
|
||
} else {
|
||
error(stmt->loc, "too many registers in operand (use brackets)");
|
||
}
|
||
} else {
|
||
if (operand->baseReg == Reg_Invalid) {
|
||
operand->baseReg = static_cast<Reg>(exp->toInteger());
|
||
} else if (operand->indexReg == Reg_Invalid) {
|
||
operand->indexReg = static_cast<Reg>(exp->toInteger());
|
||
operand->scale = 1;
|
||
} else {
|
||
error(stmt->loc, "too many registers memory operand");
|
||
}
|
||
}
|
||
} else if (auto ve = exp->isVarExp()) {
|
||
VarDeclaration *v = ve->var->isVarDeclaration();
|
||
|
||
if (v && v->storage_class & STCfield) {
|
||
operand->constDisplacement += v->offset;
|
||
if (!operand->inBracket) {
|
||
operand->hasNumber = 1;
|
||
}
|
||
} else {
|
||
if (v && v->type->isscalar()) {
|
||
// DMD doesn't check Tcomplex*, and counts Tcomplex32 as
|
||
// Tfloat64
|
||
TY ty = v->type->toBasetype()->ty;
|
||
operand->dataSizeHint =
|
||
(ty == TY::Tfloat80 || ty == TY::Timaginary80) &&
|
||
!global.params.targetTriple->isWindowsMSVCEnvironment()
|
||
? Extended_Ptr
|
||
: static_cast<PtrType>(dmd::size(v->type));
|
||
}
|
||
|
||
if (!operand->symbolDisplacement.length) {
|
||
if (is_offset && !operand->inBracket) {
|
||
operand->isOffset = 1;
|
||
}
|
||
operand->symbolDisplacement.push(exp);
|
||
} else {
|
||
error(stmt->loc, "too many symbols in operand");
|
||
}
|
||
}
|
||
} else if (exp->op == EXP::identifier || exp->op == EXP::dSymbol) {
|
||
// %% localsize could be treated as a simple constant..
|
||
// change to addSymbolDisp(e)
|
||
if (!operand->symbolDisplacement.length) {
|
||
operand->symbolDisplacement.push(exp);
|
||
} else {
|
||
error(stmt->loc, "too many symbols in operand");
|
||
}
|
||
} else if (exp == Handled) {
|
||
// nothing
|
||
} else {
|
||
error(stmt->loc, "invalid operand");
|
||
}
|
||
}
|
||
|
||
void invalidExpression() {
|
||
// %% report operand number
|
||
error(stmt->loc, "invalid expression");
|
||
}
|
||
|
||
Expression *intOp(TOK op, Expression *e1, Expression *e2) {
|
||
using namespace dmd;
|
||
|
||
if (isIntExp(e1) && (!e2 || isIntExp(e2))) {
|
||
Expression *e = createExpressionForIntOp(stmt->loc, op, e1, e2);
|
||
e = expressionSemantic(e, sc);
|
||
return ctfeInterpret(e);
|
||
}
|
||
|
||
error(stmt->loc, "expected integer operand(s) for `%s`", Token::toChars(op));
|
||
return newIntExp(0);
|
||
}
|
||
|
||
void parseOperand() {
|
||
Expression *exp = parseAsmExp();
|
||
slotExp(exp);
|
||
if (isRegExp(exp)) {
|
||
operand->dataSize = static_cast<PtrType>(regInfo[exp->toInteger()].size);
|
||
}
|
||
}
|
||
|
||
Expression *parseAsmExp() { return parseCondExp(); }
|
||
|
||
Expression *parseCondExp() {
|
||
Expression *exp = parseLogOrExp();
|
||
if (token->value == TOK::question) {
|
||
nextToken();
|
||
Expression *exp2 = parseCondExp();
|
||
if (token->value != TOK::colon) {
|
||
return exp;
|
||
}
|
||
nextToken();
|
||
Expression *exp3 = parseCondExp();
|
||
exp = exp->toUInteger() ? exp2 : exp3;
|
||
}
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseLogOrExp() {
|
||
Expression *exp = parseLogAndExp();
|
||
while (token->value == TOK::orOr) {
|
||
nextToken();
|
||
Expression *exp2 = parseLogAndExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
exp = intOp(TOK::andAnd, exp, exp2);
|
||
} else {
|
||
error(stmt->loc, "bad integral operand");
|
||
}
|
||
}
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseLogAndExp() {
|
||
Expression *exp = parseIncOrExp();
|
||
while (token->value == TOK::orOr) {
|
||
nextToken();
|
||
Expression *exp2 = parseIncOrExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
exp = intOp(TOK::orOr, exp, exp2);
|
||
} else {
|
||
error(stmt->loc, "bad integral operand");
|
||
}
|
||
}
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseIncOrExp() {
|
||
Expression *exp = parseXOrExp();
|
||
while (token->value == TOK::or_) {
|
||
nextToken();
|
||
Expression *exp2 = parseXOrExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
exp = intOp(TOK::or_, exp, exp2);
|
||
} else {
|
||
error(stmt->loc, "bad integral operand");
|
||
}
|
||
}
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseXOrExp() {
|
||
Expression *exp = parseAndExp();
|
||
while (token->value == TOK::xor_) {
|
||
nextToken();
|
||
Expression *exp2 = parseAndExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
exp = intOp(TOK::xor_, exp, exp2);
|
||
} else {
|
||
error(stmt->loc, "bad integral operand");
|
||
}
|
||
}
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseAndExp() {
|
||
Expression *exp = parseEqualExp();
|
||
while (token->value == TOK::and_) {
|
||
nextToken();
|
||
Expression *exp2 = parseEqualExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
exp = intOp(TOK::and_, exp, exp2);
|
||
} else {
|
||
error(stmt->loc, "bad integral operand");
|
||
}
|
||
}
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseEqualExp() {
|
||
const auto exp = parseRelExp();
|
||
const auto tok = token->value;
|
||
if (tok != TOK::equal && tok != TOK::notEqual) {
|
||
return exp;
|
||
}
|
||
|
||
nextToken();
|
||
const auto exp2 = parseRelExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
return intOp(tok, exp, exp2);
|
||
}
|
||
|
||
error(stmt->loc, "bad integral operand");
|
||
// TODO: Return ErrorExp?
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseRelExp() {
|
||
const auto exp = parseShiftExp();
|
||
const auto tok = token->value;
|
||
if (tok != TOK::greaterThan && tok != TOK::greaterOrEqual &&
|
||
tok != TOK::lessThan && tok != TOK::lessOrEqual) {
|
||
return exp;
|
||
}
|
||
|
||
nextToken();
|
||
const auto exp2 = parseShiftExp();
|
||
if (isIntExp(exp) && isIntExp(exp2)) {
|
||
return intOp(tok, exp, exp2);
|
||
}
|
||
|
||
error(stmt->loc, "bad integral operand");
|
||
// TODO: Return ErrorExp?
|
||
return exp;
|
||
}
|
||
|
||
Expression *parseShiftExp() {
|
||
Expression *e1 = parseAddExp();
|
||
Expression *e2;
|
||
|
||
while (1) {
|
||
TOK tv = token->value;
|
||
switch (tv) {
|
||
case TOK::leftShift:
|
||
case TOK::rightShift:
|
||
case TOK::unsignedRightShift:
|
||
nextToken();
|
||
e2 = parseAddExp();
|
||
e1 = intOp(tv, e1, e2);
|
||
continue;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
return e1;
|
||
}
|
||
|
||
Expression *parseAddExp() {
|
||
Expression *e1 = parseMultExp();
|
||
Expression *e2;
|
||
|
||
while (1) {
|
||
TOK tv = token->value;
|
||
switch (tv) {
|
||
case TOK::add:
|
||
nextToken();
|
||
e2 = parseMultExp();
|
||
if (isIntExp(e1) && isIntExp(e2)) {
|
||
e1 = intOp(tv, e1, e2);
|
||
} else {
|
||
slotExp(e1);
|
||
slotExp(e2);
|
||
e1 = Handled;
|
||
}
|
||
continue;
|
||
case TOK::min:
|
||
// Note: no support for symbol address difference
|
||
nextToken();
|
||
e2 = parseMultExp();
|
||
if (isIntExp(e1) && isIntExp(e2)) {
|
||
e1 = intOp(tv, e1, e2);
|
||
} else {
|
||
slotExp(e1);
|
||
e2 = intOp(TOK::min, e2, nullptr); // verifies e2 is an int
|
||
slotExp(e2);
|
||
e1 = Handled;
|
||
}
|
||
continue;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
return e1;
|
||
}
|
||
|
||
bool tryScale(Expression *e1, Expression *e2) {
|
||
Expression *et;
|
||
if (isIntExp(e1) && isRegExp(e2)) {
|
||
et = e1;
|
||
e1 = e2;
|
||
e2 = et;
|
||
goto do_scale;
|
||
} else if (isRegExp(e1) && isIntExp(e2)) {
|
||
do_scale:
|
||
if (!operand->inBracket) {
|
||
invalidExpression(); // maybe should allow, e.g. DS:EBX+EAX*4
|
||
}
|
||
|
||
if (operand->scale || operand->indexReg != Reg_Invalid) {
|
||
invalidExpression();
|
||
return true;
|
||
}
|
||
|
||
operand->indexReg = static_cast<Reg>(e1->toInteger());
|
||
operand->scale = e2->toInteger();
|
||
switch (operand->scale) {
|
||
case 1:
|
||
case 2:
|
||
case 4:
|
||
case 8:
|
||
// ok; do nothing
|
||
break;
|
||
default:
|
||
error(stmt->loc, "invalid index register scale '%d'", operand->scale);
|
||
return true;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
Expression *parseMultExp() {
|
||
Expression *e1 = parseBrExp();
|
||
Expression *e2;
|
||
|
||
while (1) {
|
||
TOK tv = token->value;
|
||
switch (tv) {
|
||
case TOK::mul:
|
||
nextToken();
|
||
e2 = parseMultExp();
|
||
if (isIntExp(e1) && isIntExp(e2)) {
|
||
e1 = intOp(tv, e1, e2);
|
||
} else if (tryScale(e1, e2)) {
|
||
e1 = Handled;
|
||
} else {
|
||
invalidExpression();
|
||
}
|
||
continue;
|
||
case TOK::div:
|
||
case TOK::mod:
|
||
nextToken();
|
||
e2 = parseMultExp();
|
||
e1 = intOp(tv, e1, e2);
|
||
continue;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
return e1;
|
||
}
|
||
|
||
Expression *parseBrExp() {
|
||
// %% check (why is bracket lower precends..)
|
||
// 3+4[eax] -> 3 + (4 [EAX]) ..
|
||
|
||
// only one bracked allowed, so this doesn't quite handle
|
||
// the spec'd syntax
|
||
Expression *e;
|
||
|
||
if (token->value == TOK::leftBracket) {
|
||
e = Handled;
|
||
} else {
|
||
e = parseUnaExp();
|
||
}
|
||
|
||
// DMD allows multiple bracket expressions.
|
||
while (token->value == TOK::leftBracket) {
|
||
nextToken();
|
||
|
||
operand->inBracket = operand->hasBracket = 1;
|
||
slotExp(parseAsmExp());
|
||
operand->inBracket = 0;
|
||
|
||
if (token->value == TOK::rightBracket) {
|
||
nextToken();
|
||
} else {
|
||
error(stmt->loc, "missing `]`");
|
||
}
|
||
}
|
||
|
||
return e;
|
||
}
|
||
|
||
PtrType isPtrType(Token *tok) {
|
||
switch (tok->value) {
|
||
case TOK::int8:
|
||
return Byte_Ptr;
|
||
case TOK::int16:
|
||
return Short_Ptr;
|
||
case TOK::int32:
|
||
return Int_Ptr;
|
||
#ifndef ASM_X86_64
|
||
// 'long ptr' isn't accepted?
|
||
#else
|
||
case TOK::int64:
|
||
return QWord_Ptr;
|
||
#endif
|
||
case TOK::float32:
|
||
return Float_Ptr;
|
||
case TOK::float64:
|
||
return Double_Ptr;
|
||
case TOK::float80:
|
||
return global.params.targetTriple->isWindowsMSVCEnvironment()
|
||
? Double_Ptr
|
||
: Extended_Ptr;
|
||
case TOK::identifier:
|
||
for (int i = 0; i < N_PtrNames; i++) {
|
||
if (tok->ident == ptrTypeIdentTable[i]) {
|
||
return ptrTypeValueTable[i];
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return Default_Ptr;
|
||
}
|
||
|
||
Expression *parseUnaExp() {
|
||
Expression *e = nullptr;
|
||
PtrType ptr_type;
|
||
|
||
// First, check for type prefix.
|
||
if (token->value != TOK::endOfFile &&
|
||
peekToken()->value == TOK::identifier &&
|
||
peekToken()->ident == Id::ptr) {
|
||
|
||
ptr_type = isPtrType(token);
|
||
if (ptr_type != Default_Ptr) {
|
||
if (operand->dataSize == Default_Ptr) {
|
||
operand->dataSize = ptr_type;
|
||
} else {
|
||
error(stmt->loc, "multiple specifications of operand size");
|
||
}
|
||
} else {
|
||
error(stmt->loc, "unknown operand size `%s`", token->toChars());
|
||
}
|
||
nextToken();
|
||
nextToken();
|
||
return parseAsmExp();
|
||
}
|
||
|
||
TOK tv = token->value;
|
||
switch (tv) {
|
||
case TOK::identifier:
|
||
if (token->ident == ident_seg) {
|
||
nextToken();
|
||
error(stmt->loc, "`seg` not supported");
|
||
e = parseAsmExp();
|
||
} else if (token->ident == Id::offset || token->ident == Id::offsetof) {
|
||
if (token->ident == Id::offset &&
|
||
global.params.useDeprecated == DIAGNOSTICerror) {
|
||
error(stmt->loc, "offset deprecated, use `offsetof`");
|
||
}
|
||
nextToken();
|
||
e = parseAsmExp();
|
||
e = createAddrExp(stmt->loc, e);
|
||
} else {
|
||
// primary exp
|
||
break;
|
||
}
|
||
return e;
|
||
case TOK::add:
|
||
case TOK::min:
|
||
case TOK::not_:
|
||
case TOK::tilde:
|
||
nextToken();
|
||
e = parseUnaExp();
|
||
return intOp(tv, e, nullptr);
|
||
default:
|
||
// primary exp
|
||
break;
|
||
}
|
||
return parsePrimaryExp();
|
||
}
|
||
|
||
Expression *parsePrimaryExp() {
|
||
using namespace dmd;
|
||
|
||
Expression *e;
|
||
Identifier *ident = nullptr;
|
||
|
||
// get rid of short/long prefixes for branches
|
||
if (opTakesLabel() &&
|
||
(token->value == TOK::int16 || token->value == TOK::int64)) {
|
||
nextToken();
|
||
}
|
||
|
||
switch (token->value) {
|
||
case TOK::int32Literal:
|
||
case TOK::uns32Literal:
|
||
case TOK::int64Literal:
|
||
case TOK::uns64Literal:
|
||
// semantic here?
|
||
#ifndef ASM_X86_64
|
||
// %% for tok64 really should use 64bit type
|
||
e = IntegerExp::create(stmt->loc, token->unsvalue, Type::tint32);
|
||
#else
|
||
e = IntegerExp::create(stmt->loc, token->unsvalue, Type::tint64);
|
||
#endif
|
||
nextToken();
|
||
break;
|
||
case TOK::float32Literal:
|
||
case TOK::float64Literal:
|
||
case TOK::float80Literal:
|
||
// %% need different types?
|
||
e = RealExp::create(stmt->loc, token->floatvalue, Type::tfloat80);
|
||
nextToken();
|
||
break;
|
||
case TOK::identifier: {
|
||
ident = token->ident;
|
||
nextToken();
|
||
|
||
if (ident == Id::__LOCAL_SIZE) {
|
||
return IdentifierExp::create(stmt->loc, ident);
|
||
}
|
||
if (ident == Id::dollar) {
|
||
do_dollar:
|
||
return IdentifierExp::create(stmt->loc, ident);
|
||
} else {
|
||
e = IdentifierExp::create(stmt->loc, ident);
|
||
}
|
||
|
||
// If this is more than one component ref, it gets complicated:
|
||
// *(&Field +
|
||
// n)
|
||
// maybe just do one step at a time..
|
||
// simple case is Type.f -> VarDecl(field)
|
||
// actually, DMD only supports on level...
|
||
// X.y+Y.z[EBX] is supported, tho..
|
||
// %% doesn't handle properties (check%%)
|
||
while (token->value == TOK::dot) {
|
||
nextToken();
|
||
if (token->value == TOK::identifier) {
|
||
e = DotIdExp::create(stmt->loc, e, token->ident);
|
||
nextToken();
|
||
} else {
|
||
error(stmt->loc, "expected identifier");
|
||
return Handled;
|
||
}
|
||
}
|
||
|
||
// check for reg first then dotexp is an error?
|
||
if (e->op == EXP::identifier) {
|
||
for (int i = 0; i < N_Regs; i++) {
|
||
const auto reg = regInfo[i].ident;
|
||
const auto matchesRegister =
|
||
stmt->caseSensitive
|
||
? ident == reg
|
||
: reg && llvm::StringRef(ident->toChars())
|
||
.equals_insensitive(reg->toChars());
|
||
if (matchesRegister) {
|
||
if (static_cast<Reg>(i) == Reg_ST &&
|
||
token->value == TOK::leftParenthesis) {
|
||
nextToken();
|
||
switch (token->value) {
|
||
case TOK::int32Literal:
|
||
case TOK::uns32Literal:
|
||
case TOK::int64Literal:
|
||
case TOK::uns64Literal:
|
||
if (token->unsvalue < 8) {
|
||
e = newRegExp(static_cast<Reg>(Reg_ST + token->unsvalue));
|
||
} else {
|
||
error(stmt->loc, "invalid floating point register index");
|
||
e = Handled;
|
||
}
|
||
nextToken();
|
||
if (token->value == TOK::rightParenthesis) {
|
||
nextToken();
|
||
} else {
|
||
error(stmt->loc, "expected `)`");
|
||
}
|
||
return e;
|
||
default:
|
||
break;
|
||
}
|
||
invalidExpression();
|
||
return Handled;
|
||
}
|
||
if (token->value == TOK::colon) {
|
||
nextToken();
|
||
if (operand->segmentPrefix != Reg_Invalid) {
|
||
error(stmt->loc, "too many segment prefixes");
|
||
} else if (i >= Reg_CS && i <= Reg_GS) {
|
||
operand->segmentPrefix = static_cast<Reg>(i);
|
||
} else {
|
||
error(stmt->loc, "`%s` is not a segment register", ident->toChars());
|
||
}
|
||
return parseAsmExp();
|
||
}
|
||
return newRegExp(static_cast<Reg>(i));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (e->op == EXP::identifier) {
|
||
// DMD uses labels secondarily to other symbols, so check
|
||
// if IdentifierExp::semantic won't find anything.
|
||
Dsymbol *scopesym;
|
||
if (!sc->search(stmt->loc, ident, scopesym)) {
|
||
if (LabelDsymbol *labelsym = sc->func->searchLabel(ident, stmt->loc)) {
|
||
e = createDsymbolExp(stmt->loc, labelsym);
|
||
if (opTakesLabel()) {
|
||
return e;
|
||
}
|
||
return createAddrExp(stmt->loc, e);
|
||
}
|
||
}
|
||
}
|
||
|
||
e = expressionSemantic(e, sc);
|
||
e = optimize(e, WANTvalue);
|
||
|
||
// Special case for floating point constant declarations.
|
||
if (e->op == EXP::float64) {
|
||
Dsymbol *scopesym;
|
||
Dsymbol *sym = sc->search(stmt->loc, ident, scopesym);
|
||
if (sym) {
|
||
VarDeclaration *v = sym->isVarDeclaration();
|
||
if (v) {
|
||
Expression *ve = VarExp::create(stmt->loc, v);
|
||
ve->type = e->type;
|
||
e = ve;
|
||
}
|
||
}
|
||
}
|
||
return e;
|
||
} break;
|
||
case TOK::dollar:
|
||
nextToken();
|
||
ident = Id::dollar;
|
||
goto do_dollar;
|
||
break;
|
||
default:
|
||
if (op == Op_FMath0 || op == Op_FdST0ST1 || op == Op_FMath) {
|
||
return Handled;
|
||
}
|
||
// DMD does not emit an error message here.
|
||
// invalidExpression();
|
||
return Handled;
|
||
}
|
||
return e;
|
||
}
|
||
|
||
void doAlign() {
|
||
using namespace dmd;
|
||
|
||
// .align bits vs. bytes...
|
||
// apparently a.out platforms use bits instead of bytes...
|
||
|
||
// parse primary: DMD allows 'MyAlign' (const int) but not '2+2'
|
||
// GAS is padding with NOPs last time I checked.
|
||
Expression *e = ctfeInterpret(parseAsmExp());
|
||
uinteger_t align = e->toUInteger();
|
||
|
||
if ((align & (align - 1)) == 0) {
|
||
// FIXME: This printf is not portable. The use of `align` varies from
|
||
// system to system; on i386 using a.out, .align `n` will align on a 2^`n`
|
||
// boundary instead of an `n` boundary
|
||
#ifdef HAVE_GAS_BALIGN_AND_P2ALIGN
|
||
insnTemplate << ".balign\t" << align;
|
||
#else
|
||
insnTemplate << ".align\t" << align;
|
||
#endif
|
||
} else {
|
||
error(stmt->loc, "alignment must be a power of 2, not %u",
|
||
static_cast<unsigned>(align));
|
||
}
|
||
|
||
setAsmCode();
|
||
}
|
||
|
||
void doEven() {
|
||
// .align for GAS is in bits, others probably use bytes..
|
||
#ifdef HAVE_GAS_BALIGN_AND_P2ALIGN
|
||
insnTemplate << ".align\t2";
|
||
#else
|
||
insnTemplate << ".align\t2";
|
||
#endif
|
||
setAsmCode();
|
||
}
|
||
|
||
void doNaked() {
|
||
// %% can we assume sc->func != 0?
|
||
sc->func->isNaked(true);
|
||
}
|
||
|
||
void doData() {
|
||
static const char *directives[] = {".byte", ".short", ".long", ".quad",
|
||
".long", ".quad", ".quad"};
|
||
|
||
insnTemplate << directives[op - Op_db] << ' ';
|
||
|
||
const llvm::fltSemantics *realSemantics = nullptr;
|
||
unsigned realSizeInBits = 0;
|
||
|
||
do {
|
||
// DMD is pretty strict here, not even constant expressions are allowed..
|
||
switch (op) {
|
||
case Op_db:
|
||
case Op_ds:
|
||
case Op_di:
|
||
case Op_dl:
|
||
if (token->value == TOK::int32Literal ||
|
||
token->value == TOK::uns32Literal ||
|
||
token->value == TOK::int64Literal ||
|
||
token->value == TOK::uns64Literal) {
|
||
// As per usual with GNU, assume at least 32-bit host
|
||
if (op != Op_dl) {
|
||
insnTemplate << static_cast<uint32_t>(token->unsvalue);
|
||
} else {
|
||
insnTemplate << token->unsvalue;
|
||
}
|
||
} else {
|
||
error(stmt->loc, "expected integer constant");
|
||
}
|
||
break;
|
||
case Op_df:
|
||
case Op_dd:
|
||
case Op_de:
|
||
if (token->value == TOK::float32Literal ||
|
||
token->value == TOK::float64Literal ||
|
||
token->value == TOK::float80Literal) {
|
||
if (op == Op_df) {
|
||
const float value = static_cast<float>(token->floatvalue);
|
||
uint32_t reinterpreted; // explicitly use memcpy because of strict-aliasing
|
||
std::memcpy(&reinterpreted, &value, sizeof value);
|
||
insnTemplate << reinterpreted;
|
||
} else if (op == Op_dd) {
|
||
const double value = static_cast<double>(token->floatvalue);
|
||
uint64_t reinterpreted; // explicitly use memcpy because of strict-aliasing
|
||
std::memcpy(&reinterpreted, &value, sizeof value);
|
||
insnTemplate << reinterpreted;
|
||
} else if (op == Op_de) {
|
||
llvm::APFloat value(0.0);
|
||
CTFloat::toAPFloat(token->floatvalue, value);
|
||
|
||
if (!realSemantics)
|
||
realSemantics = &DtoType(Type::tfloat80)->getFltSemantics();
|
||
|
||
if (&value.getSemantics() != realSemantics) {
|
||
bool ignored;
|
||
value.convert(*realSemantics, APFloat::rmNearestTiesToEven,
|
||
&ignored);
|
||
}
|
||
|
||
const auto asInt = value.bitcastToAPInt();
|
||
if (realSizeInBits == 0)
|
||
realSizeInBits = asInt.getBitWidth();
|
||
|
||
auto *ptr = reinterpret_cast<const uint64_t *>(asInt.getRawData());
|
||
insnTemplate << ptr[0];
|
||
if (realSizeInBits == 64) { // e.g., MSVC x86(_64)
|
||
// nothing left to do
|
||
} else if (realSizeInBits == 128) { // e.g., Android x64
|
||
insnTemplate << ',' << ptr[1];
|
||
} else if (realSizeInBits == 80) {
|
||
// DMD outputs 10 bytes, so we need to switch to .short here
|
||
insnTemplate << "\n\t.short "
|
||
<< *reinterpret_cast<const uint16_t *>(ptr + 1);
|
||
} else {
|
||
error(stmt->loc, "unsupported target `real` size");
|
||
}
|
||
} else {
|
||
llvm_unreachable("unexpected op");
|
||
}
|
||
} else {
|
||
error(stmt->loc, "expected float constant");
|
||
}
|
||
break;
|
||
default:
|
||
error(stmt->loc, "Unsupported data definition directive inside inline asm.");
|
||
break;
|
||
}
|
||
|
||
nextToken();
|
||
if (token->value == TOK::comma) {
|
||
if (op == Op_de && realSizeInBits == 80) {
|
||
insnTemplate << "\n\t.quad "; // switch from .short back to .quad
|
||
} else {
|
||
insnTemplate << ',';
|
||
}
|
||
nextToken();
|
||
} else if (token->value == TOK::endOfFile) {
|
||
break;
|
||
} else {
|
||
error(stmt->loc, "expected comma");
|
||
}
|
||
} while (1);
|
||
|
||
setAsmCode();
|
||
}
|
||
};
|
||
|
||
// FIXME
|
||
#define HOST_WIDE_INT long
|
||
bool getFrameRelativeValue(LLValue *decl, HOST_WIDE_INT *result) {
|
||
llvm_unreachable("getFrameRelativeValue not implemented.");
|
||
// FIXME
|
||
// // Using this instead of DECL_RTL for struct args seems like a
|
||
// // good way to get hit by a truck because it may not agree with
|
||
// // non-asm access, but asm code wouldn't know what GCC does
|
||
// anyway. */
|
||
// rtx r = DECL_INCOMING_RTL(decl);
|
||
// rtx e1, e2;
|
||
//
|
||
// // Local variables don't have DECL_INCOMING_RTL
|
||
// if (r == NULL_RTX)
|
||
// r = DECL_RTL(decl);
|
||
//
|
||
// if (r != NULL_RTX && GET_CODE(r) == MEM /* && r->frame_related */
|
||
// ) {
|
||
// r = XEXP(r, 0);
|
||
// if (GET_CODE(r) == PLUS) {
|
||
// e1 = XEXP(r, 0);
|
||
// e2 = XEXP(r, 1);
|
||
// if (e1 == virtual_incoming_args_rtx && GET_CODE(e2) ==
|
||
// CONST_INT) {
|
||
// *result = INTVAL(e2) + 8; // %% 8 is 32-bit specific...
|
||
// return true;
|
||
// } else if (e1 == virtual_stack_vars_rtx && GET_CODE(e2) ==
|
||
// CONST_INT)
|
||
// {
|
||
// *result = INTVAL(e2); // %% 8 is 32-bit specific...
|
||
// return true;
|
||
// }
|
||
// } else if (r == virtual_incoming_args_rtx) {
|
||
// *result = 8;
|
||
// return true; // %% same as above
|
||
// }
|
||
// // shouldn't have virtual_stack_vars_rtx by itself
|
||
// }
|
||
//
|
||
return false;
|
||
}
|
||
|
||
struct AsmParser : public AsmParserCommon {
|
||
void run(Scope *sc, InlineAsmStatement *asmst) override {
|
||
AsmProcessor ap(sc, asmst);
|
||
ap.run();
|
||
}
|
||
|
||
std::string getRegName(int i) override { return regInfo[i].gccName; }
|
||
};
|
||
|
||
} // namespace
|