mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 18:14:13 +08:00
d1c2dd6f4d
Extremely rarely used attributes are inefficient when represented by a separate attribute. Convert it to an operand constraint, as already suggested during review. The collision with RegKludge is pretty simple to resolve.
2319 lines
53 KiB
C
2319 lines
53 KiB
C
/* Copyright (C) 2007-2024 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "sysdep.h"
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include "getopt.h"
|
|
#include "libiberty.h"
|
|
#include "hashtab.h"
|
|
#include "safe-ctype.h"
|
|
|
|
#include "i386-opc.h"
|
|
|
|
/* Build-time checks are preferrable over runtime ones. Use this construct
|
|
in preference where possible. */
|
|
#define static_assert(e) ((void)sizeof (struct { int _:1 - 2 * !(e); }))
|
|
|
|
static const char *program_name = NULL;
|
|
static int debug = 0;
|
|
|
|
typedef struct dependency
|
|
{
|
|
const char *name;
|
|
/* Note: Only direct dependencies should be enumerated. */
|
|
const char *deps;
|
|
} dependency;
|
|
|
|
static const dependency isa_dependencies[] =
|
|
{
|
|
{ "UNKNOWN",
|
|
"~IAMCU" },
|
|
{ "GENERIC32",
|
|
"386" },
|
|
{ "GENERIC64",
|
|
"PENTIUMPRO|Clflush|SYSCALL|MMX|SSE2|LM" },
|
|
{ "NONE",
|
|
"0" },
|
|
{ "PENTIUMPRO",
|
|
"686|Nop" },
|
|
{ "P2",
|
|
"PENTIUMPRO|MMX" },
|
|
{ "P3",
|
|
"P2|SSE" },
|
|
{ "P4",
|
|
"P3|Clflush|SSE2" },
|
|
{ "NOCONA",
|
|
"GENERIC64|FISTTP|SSE3|MONITOR|CX16" },
|
|
{ "CORE",
|
|
"P4|FISTTP|SSE3|MONITOR" },
|
|
{ "CORE2",
|
|
"NOCONA|SSSE3" },
|
|
{ "COREI7",
|
|
"CORE2|SSE4_2|Rdtscp|LAHF_SAHF" },
|
|
{ "K6",
|
|
"186|286|386|486|586|SYSCALL|387|MMX" },
|
|
{ "K6_2",
|
|
"K6|3dnow" },
|
|
{ "ATHLON",
|
|
"K6_2|686:min|687|Nop|3dnowA" },
|
|
{ "K8",
|
|
"ATHLON|Rdtscp|SSE2|LM" },
|
|
{ "AMDFAM10",
|
|
"K8|FISTTP|SSE4A|ABM|MONITOR" },
|
|
{ "BDVER1",
|
|
"GENERIC64|FISTTP|Rdtscp|MONITOR|CX16|LAHF_SAHF|XOP|ABM|LWP|SVME|AES|PCLMULQDQ|PRFCHW" },
|
|
{ "BDVER2",
|
|
"BDVER1|FMA|BMI|TBM|F16C" },
|
|
{ "BDVER3",
|
|
"BDVER2|Xsaveopt|FSGSBase" },
|
|
{ "BDVER4",
|
|
"BDVER3|AVX2|Movbe|BMI2|RdRnd|MWAITX" },
|
|
{ "ZNVER1",
|
|
"GENERIC64|FISTTP|Rdtscp|MONITOR|CX16|LAHF_SAHF|AVX2|SSE4A|ABM|SVME|AES|PCLMULQDQ|PRFCHW|FMA|BMI|F16C|Xsaveopt|FSGSBase|Movbe|BMI2|RdRnd|ADX|RdSeed|SMAP|SHA|XSAVEC|XSAVES|ClflushOpt|CLZERO|MWAITX" },
|
|
{ "ZNVER2",
|
|
"ZNVER1|CLWB|RDPID|RDPRU|MCOMMIT|WBNOINVD" },
|
|
{ "ZNVER3",
|
|
"ZNVER2|INVLPGB|TLBSYNC|VAES|VPCLMULQDQ|INVPCID|SNP|OSPKE" },
|
|
{ "ZNVER4",
|
|
"ZNVER3|AVX512F|AVX512DQ|AVX512IFMA|AVX512CD|AVX512BW|AVX512VL|AVX512_BF16|AVX512VBMI|AVX512_VBMI2|AVX512_VNNI|AVX512_BITALG|AVX512_VPOPCNTDQ|GFNI|RMPQUERY" },
|
|
{ "ZNVER5",
|
|
"ZNVER4|AVX_VNNI|MOVDIRI|MOVDIR64B|AVX512_VP2INTERSECT|PREFETCHI" },
|
|
{ "BTVER1",
|
|
"GENERIC64|FISTTP|MONITOR|CX16|LAHF_SAHF|Rdtscp|SSSE3|SSE4A|ABM|PRFCHW|Clflush|FISTTP|SVME" },
|
|
{ "BTVER2",
|
|
"BTVER1|AVX|BMI|F16C|AES|PCLMULQDQ|Movbe|Xsaveopt|PRFCHW" },
|
|
{ "286",
|
|
"186" },
|
|
{ "386",
|
|
"286" },
|
|
{ "486",
|
|
"386" },
|
|
{ "586",
|
|
"486|387" },
|
|
{ "586:nofpu",
|
|
"486" },
|
|
{ "686",
|
|
"586|687|CMOV|FXSR" },
|
|
{ "686:min",
|
|
"586|687" },
|
|
{ "687",
|
|
"387" },
|
|
{ "FISTTP",
|
|
"687" },
|
|
{ "SSE",
|
|
"FXSR" },
|
|
{ "SSE2",
|
|
"SSE" },
|
|
{ "SSE3",
|
|
"SSE2" },
|
|
{ "SSSE3",
|
|
"SSE3" },
|
|
{ "SSE4_1",
|
|
"SSSE3" },
|
|
{ "SSE4_2",
|
|
"SSE4_1|POPCNT" },
|
|
{ "Xsaveopt",
|
|
"XSAVE" },
|
|
{ "AES",
|
|
"SSE2" },
|
|
{ "PCLMULQDQ",
|
|
"SSE2" },
|
|
{ "FMA",
|
|
"AVX" },
|
|
{ "FMA4",
|
|
"AVX" },
|
|
{ "XOP",
|
|
"SSE4A|FMA4" },
|
|
{ "LWP",
|
|
"XSAVE" },
|
|
{ "F16C",
|
|
"AVX" },
|
|
{ "3dnow",
|
|
"MMX" },
|
|
{ "3dnowA",
|
|
"3dnow" },
|
|
{ "SSE4a",
|
|
"SSE3" },
|
|
{ "ABM",
|
|
"LZCNT|POPCNT" },
|
|
{ "AVX",
|
|
"SSE4_2|XSAVE" },
|
|
{ "AVX2",
|
|
"AVX" },
|
|
{ "AVX_VNNI",
|
|
"AVX2" },
|
|
{ "AVX_IFMA",
|
|
"AVX2" },
|
|
{ "AVX_VNNI_INT8",
|
|
"AVX2" },
|
|
{ "AVX_VNNI_INT16",
|
|
"AVX2" },
|
|
{ "AVX_NE_CONVERT",
|
|
"AVX2" },
|
|
{ "CX16",
|
|
"64" },
|
|
{ "LKGS",
|
|
"64" },
|
|
{ "FRED",
|
|
"LKGS" },
|
|
{ "AVX512F",
|
|
"AVX2" },
|
|
{ "AVX512CD",
|
|
"AVX512F" },
|
|
{ "AVX512ER",
|
|
"AVX512F" },
|
|
{ "AVX512PF",
|
|
"AVX512F" },
|
|
{ "AVX512DQ",
|
|
"AVX512F" },
|
|
{ "AVX512BW",
|
|
"AVX512F" },
|
|
{ "AVX512VL",
|
|
"AVX512F" },
|
|
{ "AVX512IFMA",
|
|
"AVX512F" },
|
|
{ "AVX512VBMI",
|
|
"AVX512BW" },
|
|
{ "AVX512_4FMAPS",
|
|
"AVX512F" },
|
|
{ "AVX512_4VNNIW",
|
|
"AVX512F" },
|
|
{ "AVX512_VPOPCNTDQ",
|
|
"AVX512F" },
|
|
{ "AVX512_VBMI2",
|
|
"AVX512BW" },
|
|
{ "AVX512_VNNI",
|
|
"AVX512F" },
|
|
{ "AVX512_BITALG",
|
|
"AVX512BW" },
|
|
{ "AVX512_VP2INTERSECT",
|
|
"AVX512F" },
|
|
{ "AVX512_BF16",
|
|
"AVX512BW" },
|
|
{ "AVX512_FP16",
|
|
"AVX512BW" },
|
|
{ "IAMCU",
|
|
"586:nofpu" },
|
|
{ "EPT",
|
|
"VMX" },
|
|
{ "VMFUNC",
|
|
"VMX" },
|
|
{ "MPX",
|
|
"XSAVE" },
|
|
{ "SHA",
|
|
"SSE2" },
|
|
{ "SHA512",
|
|
"AVX2" },
|
|
{ "SM3",
|
|
"AVX" },
|
|
{ "SM4",
|
|
"AVX2" },
|
|
{ "XSAVES",
|
|
"XSAVEC" },
|
|
{ "XSAVEC",
|
|
"XSAVE" },
|
|
{ "OSPKE",
|
|
"XSAVE" },
|
|
{ "GFNI",
|
|
"SSE2" },
|
|
{ "VAES",
|
|
"AVX2|AES" },
|
|
{ "VPCLMULQDQ",
|
|
"AVX2|PCLMULQDQ" },
|
|
{ "AVX10_1",
|
|
"AVX512VL|AVX512DQ|AVX512CD|AVX512VBMI|AVX512_VBMI2|AVX512IFMA"
|
|
"|AVX512_VNNI|AVX512_BF16|AVX512_FP16|AVX512_VPOPCNTDQ|AVX512_BITALG" },
|
|
{ "SEV_ES",
|
|
"SVME" },
|
|
{ "SNP",
|
|
"SEV_ES" },
|
|
{ "RMPQUERY",
|
|
"SNP|64" },
|
|
{ "TSX",
|
|
"RTM|HLE" },
|
|
{ "TSXLDTRK",
|
|
"RTM" },
|
|
{ "AMX_TILE",
|
|
"XSAVE|64" },
|
|
{ "AMX_INT8",
|
|
"AMX_TILE" },
|
|
{ "AMX_BF16",
|
|
"AMX_TILE" },
|
|
{ "AMX_FP16",
|
|
"AMX_TILE" },
|
|
{ "AMX_COMPLEX",
|
|
"AMX_TILE" },
|
|
{ "KL",
|
|
"SSE2" },
|
|
{ "WIDEKL",
|
|
"KL" },
|
|
{ "PBNDKB",
|
|
"64" },
|
|
{ "UINTR",
|
|
"64" },
|
|
{ "PREFETCHI",
|
|
"64" },
|
|
{ "CMPCCXADD",
|
|
"64" },
|
|
{ "MSRLIST",
|
|
"64" },
|
|
{ "USER_MSR",
|
|
"64" },
|
|
{ "APX_F",
|
|
"XSAVE|64" },
|
|
};
|
|
|
|
/* This array is populated as process_i386_initializers() walks cpu_flags[]. */
|
|
static unsigned char isa_reverse_deps[CpuMax][CpuMax];
|
|
|
|
typedef struct bitfield
|
|
{
|
|
int position;
|
|
int value;
|
|
const char *name;
|
|
} bitfield;
|
|
|
|
#define BITFIELD(n) { Cpu##n, 0, #n }
|
|
|
|
static bitfield cpu_flags[] =
|
|
{
|
|
BITFIELD (186),
|
|
BITFIELD (286),
|
|
BITFIELD (386),
|
|
BITFIELD (486),
|
|
BITFIELD (586),
|
|
BITFIELD (686),
|
|
BITFIELD (CMOV),
|
|
BITFIELD (FXSR),
|
|
BITFIELD (Clflush),
|
|
BITFIELD (Nop),
|
|
BITFIELD (SYSCALL),
|
|
BITFIELD (8087),
|
|
BITFIELD (287),
|
|
BITFIELD (387),
|
|
BITFIELD (687),
|
|
BITFIELD (FISTTP),
|
|
BITFIELD (MMX),
|
|
BITFIELD (SSE),
|
|
BITFIELD (SSE2),
|
|
BITFIELD (SSE3),
|
|
BITFIELD (SSSE3),
|
|
BITFIELD (SSE4_1),
|
|
BITFIELD (SSE4_2),
|
|
BITFIELD (AVX),
|
|
BITFIELD (AVX2),
|
|
BITFIELD (AVX512F),
|
|
BITFIELD (AVX512CD),
|
|
BITFIELD (AVX512ER),
|
|
BITFIELD (AVX512PF),
|
|
BITFIELD (AVX512VL),
|
|
BITFIELD (AVX512DQ),
|
|
BITFIELD (AVX512BW),
|
|
BITFIELD (IAMCU),
|
|
BITFIELD (SSE4a),
|
|
BITFIELD (3dnow),
|
|
BITFIELD (3dnowA),
|
|
BITFIELD (PadLock),
|
|
BITFIELD (SVME),
|
|
BITFIELD (VMX),
|
|
BITFIELD (SMX),
|
|
BITFIELD (Xsave),
|
|
BITFIELD (Xsaveopt),
|
|
BITFIELD (AES),
|
|
BITFIELD (PCLMULQDQ),
|
|
BITFIELD (FMA),
|
|
BITFIELD (FMA4),
|
|
BITFIELD (XOP),
|
|
BITFIELD (LWP),
|
|
BITFIELD (BMI),
|
|
BITFIELD (TBM),
|
|
BITFIELD (Movbe),
|
|
BITFIELD (CX16),
|
|
BITFIELD (LAHF_SAHF),
|
|
BITFIELD (EPT),
|
|
BITFIELD (Rdtscp),
|
|
BITFIELD (FSGSBase),
|
|
BITFIELD (RdRnd),
|
|
BITFIELD (F16C),
|
|
BITFIELD (BMI2),
|
|
BITFIELD (LZCNT),
|
|
BITFIELD (POPCNT),
|
|
BITFIELD (MONITOR),
|
|
BITFIELD (HLE),
|
|
BITFIELD (RTM),
|
|
BITFIELD (INVPCID),
|
|
BITFIELD (VMFUNC),
|
|
BITFIELD (RDSEED),
|
|
BITFIELD (ADX),
|
|
BITFIELD (PRFCHW),
|
|
BITFIELD (SMAP),
|
|
BITFIELD (SHA),
|
|
BITFIELD (SHA512),
|
|
BITFIELD (SM3),
|
|
BITFIELD (SM4),
|
|
BITFIELD (ClflushOpt),
|
|
BITFIELD (XSAVES),
|
|
BITFIELD (XSAVEC),
|
|
BITFIELD (PREFETCHWT1),
|
|
BITFIELD (SE1),
|
|
BITFIELD (CLWB),
|
|
BITFIELD (MPX),
|
|
BITFIELD (AVX512IFMA),
|
|
BITFIELD (AVX512VBMI),
|
|
BITFIELD (AVX512_4FMAPS),
|
|
BITFIELD (AVX512_4VNNIW),
|
|
BITFIELD (AVX512_VPOPCNTDQ),
|
|
BITFIELD (AVX512_VBMI2),
|
|
BITFIELD (AVX512_VNNI),
|
|
BITFIELD (AVX512_BITALG),
|
|
BITFIELD (AVX512_BF16),
|
|
BITFIELD (AVX512_VP2INTERSECT),
|
|
BITFIELD (TDX),
|
|
BITFIELD (AVX_VNNI),
|
|
BITFIELD (AVX512_FP16),
|
|
BITFIELD (PREFETCHI),
|
|
BITFIELD (AVX_IFMA),
|
|
BITFIELD (AVX_VNNI_INT8),
|
|
BITFIELD (AVX_VNNI_INT16),
|
|
BITFIELD (CMPCCXADD),
|
|
BITFIELD (WRMSRNS),
|
|
BITFIELD (MSRLIST),
|
|
BITFIELD (AVX_NE_CONVERT),
|
|
BITFIELD (RAO_INT),
|
|
BITFIELD (FRED),
|
|
BITFIELD (LKGS),
|
|
BITFIELD (USER_MSR),
|
|
BITFIELD (APX_F),
|
|
BITFIELD (MWAITX),
|
|
BITFIELD (CLZERO),
|
|
BITFIELD (OSPKE),
|
|
BITFIELD (RDPID),
|
|
BITFIELD (PTWRITE),
|
|
BITFIELD (IBT),
|
|
BITFIELD (SHSTK),
|
|
BITFIELD (GFNI),
|
|
BITFIELD (VAES),
|
|
BITFIELD (VPCLMULQDQ),
|
|
BITFIELD (WBNOINVD),
|
|
BITFIELD (PCONFIG),
|
|
BITFIELD (PBNDKB),
|
|
BITFIELD (WAITPKG),
|
|
BITFIELD (UINTR),
|
|
BITFIELD (CLDEMOTE),
|
|
BITFIELD (AMX_INT8),
|
|
BITFIELD (AMX_BF16),
|
|
BITFIELD (AMX_FP16),
|
|
BITFIELD (AMX_COMPLEX),
|
|
BITFIELD (AMX_TILE),
|
|
BITFIELD (MOVDIRI),
|
|
BITFIELD (MOVDIR64B),
|
|
BITFIELD (ENQCMD),
|
|
BITFIELD (SERIALIZE),
|
|
BITFIELD (RDPRU),
|
|
BITFIELD (MCOMMIT),
|
|
BITFIELD (SEV_ES),
|
|
BITFIELD (TSXLDTRK),
|
|
BITFIELD (KL),
|
|
BITFIELD (WideKL),
|
|
BITFIELD (HRESET),
|
|
BITFIELD (INVLPGB),
|
|
BITFIELD (TLBSYNC),
|
|
BITFIELD (SNP),
|
|
BITFIELD (RMPQUERY),
|
|
BITFIELD (64),
|
|
BITFIELD (No64),
|
|
#ifdef CpuUnused
|
|
BITFIELD (Unused),
|
|
#endif
|
|
};
|
|
|
|
#undef BITFIELD
|
|
#define BITFIELD(n) { n, 0, #n }
|
|
|
|
static bitfield opcode_modifiers[] =
|
|
{
|
|
BITFIELD (D),
|
|
BITFIELD (W),
|
|
BITFIELD (Load),
|
|
BITFIELD (Modrm),
|
|
BITFIELD (Jump),
|
|
BITFIELD (FloatMF),
|
|
BITFIELD (Size),
|
|
BITFIELD (CheckOperandSize),
|
|
BITFIELD (OperandConstraint),
|
|
BITFIELD (MnemonicSize),
|
|
BITFIELD (No_bSuf),
|
|
BITFIELD (No_wSuf),
|
|
BITFIELD (No_lSuf),
|
|
BITFIELD (No_sSuf),
|
|
BITFIELD (No_qSuf),
|
|
BITFIELD (FWait),
|
|
BITFIELD (IsString),
|
|
BITFIELD (RegMem),
|
|
BITFIELD (BNDPrefixOk),
|
|
BITFIELD (PrefixOk),
|
|
BITFIELD (IsPrefix),
|
|
BITFIELD (ImmExt),
|
|
BITFIELD (NoRex64),
|
|
BITFIELD (Vex),
|
|
BITFIELD (VexVVVV),
|
|
BITFIELD (VexW),
|
|
BITFIELD (OpcodePrefix),
|
|
BITFIELD (SIB),
|
|
BITFIELD (SSE2AVX),
|
|
BITFIELD (EVex),
|
|
BITFIELD (Masking),
|
|
BITFIELD (Broadcast),
|
|
BITFIELD (StaticRounding),
|
|
BITFIELD (SAE),
|
|
BITFIELD (Disp8MemShift),
|
|
BITFIELD (Optimize),
|
|
BITFIELD (Dialect),
|
|
BITFIELD (IntelSuffix),
|
|
BITFIELD (ISA64),
|
|
BITFIELD (NoEgpr),
|
|
BITFIELD (NF),
|
|
BITFIELD (Rex2),
|
|
};
|
|
|
|
#define CLASS(n) #n, n
|
|
|
|
static const struct {
|
|
const char *name;
|
|
enum operand_class value;
|
|
} operand_classes[] = {
|
|
CLASS (Reg),
|
|
CLASS (SReg),
|
|
CLASS (RegCR),
|
|
CLASS (RegDR),
|
|
CLASS (RegTR),
|
|
CLASS (RegMMX),
|
|
CLASS (RegSIMD),
|
|
CLASS (RegMask),
|
|
CLASS (RegBND),
|
|
};
|
|
|
|
#undef CLASS
|
|
|
|
#define INSTANCE(n) #n, n
|
|
|
|
static const struct {
|
|
const char *name;
|
|
enum operand_instance value;
|
|
} operand_instances[] = {
|
|
INSTANCE (Accum),
|
|
INSTANCE (RegC),
|
|
INSTANCE (RegD),
|
|
INSTANCE (RegB),
|
|
};
|
|
|
|
#undef INSTANCE
|
|
|
|
static bitfield operand_types[] =
|
|
{
|
|
BITFIELD (Imm1),
|
|
BITFIELD (Imm8),
|
|
BITFIELD (Imm8S),
|
|
BITFIELD (Imm16),
|
|
BITFIELD (Imm32),
|
|
BITFIELD (Imm32S),
|
|
BITFIELD (Imm64),
|
|
BITFIELD (BaseIndex),
|
|
BITFIELD (Disp8),
|
|
BITFIELD (Disp16),
|
|
BITFIELD (Disp32),
|
|
BITFIELD (Disp64),
|
|
BITFIELD (Byte),
|
|
BITFIELD (Word),
|
|
BITFIELD (Dword),
|
|
BITFIELD (Fword),
|
|
BITFIELD (Qword),
|
|
BITFIELD (Tbyte),
|
|
BITFIELD (Xmmword),
|
|
BITFIELD (Ymmword),
|
|
BITFIELD (Zmmword),
|
|
BITFIELD (Tmmword),
|
|
BITFIELD (Unspecified),
|
|
#ifdef OTUnused
|
|
BITFIELD (OTUnused),
|
|
#endif
|
|
};
|
|
|
|
static const char *filename;
|
|
static i386_cpu_flags active_cpu_flags;
|
|
static int active_isstring;
|
|
|
|
struct template_arg {
|
|
const struct template_arg *next;
|
|
const char *val;
|
|
};
|
|
|
|
struct template_instance {
|
|
const struct template_instance *next;
|
|
const char *name;
|
|
const struct template_arg *args;
|
|
};
|
|
|
|
struct template_param {
|
|
const struct template_param *next;
|
|
const char *name;
|
|
};
|
|
|
|
struct template {
|
|
struct template *next;
|
|
const char *name;
|
|
const struct template_instance *instances;
|
|
const struct template_param *params;
|
|
};
|
|
|
|
static struct template *templates;
|
|
|
|
static int
|
|
compare (const void *x, const void *y)
|
|
{
|
|
const bitfield *xp = (const bitfield *) x;
|
|
const bitfield *yp = (const bitfield *) y;
|
|
return xp->position - yp->position;
|
|
}
|
|
|
|
static void
|
|
fail (const char *message, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start (args, message);
|
|
fprintf (stderr, "%s: error: ", program_name);
|
|
vfprintf (stderr, message, args);
|
|
va_end (args);
|
|
xexit (1);
|
|
}
|
|
|
|
static void
|
|
process_copyright (FILE *fp)
|
|
{
|
|
fprintf (fp, "/* This file is automatically generated by i386-gen. Do not edit! */\n\
|
|
/* Copyright (C) 2007-2024 Free Software Foundation, Inc.\n\
|
|
\n\
|
|
This file is part of the GNU opcodes library.\n\
|
|
\n\
|
|
This library is free software; you can redistribute it and/or modify\n\
|
|
it under the terms of the GNU General Public License as published by\n\
|
|
the Free Software Foundation; either version 3, or (at your option)\n\
|
|
any later version.\n\
|
|
\n\
|
|
It is distributed in the hope that it will be useful, but WITHOUT\n\
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public\n\
|
|
License for more details.\n\
|
|
\n\
|
|
You should have received a copy of the GNU General Public License\n\
|
|
along with this program; if not, write to the Free Software\n\
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,\n\
|
|
MA 02110-1301, USA. */\n");
|
|
}
|
|
|
|
/* Remove leading white spaces. */
|
|
|
|
static char *
|
|
remove_leading_whitespaces (char *str)
|
|
{
|
|
while (ISSPACE (*str))
|
|
str++;
|
|
return str;
|
|
}
|
|
|
|
/* Remove trailing white spaces. */
|
|
|
|
static void
|
|
remove_trailing_whitespaces (char *str)
|
|
{
|
|
size_t last = strlen (str);
|
|
|
|
if (last == 0)
|
|
return;
|
|
|
|
do
|
|
{
|
|
last--;
|
|
if (ISSPACE (str [last]))
|
|
str[last] = '\0';
|
|
else
|
|
break;
|
|
}
|
|
while (last != 0);
|
|
}
|
|
|
|
/* Find next field separated by SEP and terminate it. Return a
|
|
pointer to the one after it. */
|
|
|
|
static char *
|
|
next_field (char *str, char sep, char **next, char *last)
|
|
{
|
|
char *p;
|
|
|
|
p = remove_leading_whitespaces (str);
|
|
for (str = p; *str != sep && *str != '\0'; str++);
|
|
|
|
*str = '\0';
|
|
remove_trailing_whitespaces (p);
|
|
|
|
*next = str + 1;
|
|
|
|
if (p >= last)
|
|
abort ();
|
|
|
|
return p;
|
|
}
|
|
|
|
static void set_bitfield (char *, bitfield *, int, unsigned int, int);
|
|
|
|
static void
|
|
set_bitfield (char *f, bitfield *array, int value,
|
|
unsigned int size, int lineno)
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Ignore empty fields; they may result from template expansions. */
|
|
if (*f == '\0')
|
|
return;
|
|
|
|
for (i = 0; i < size; i++)
|
|
if (strcasecmp (array[i].name, f) == 0)
|
|
{
|
|
array[i].value = value;
|
|
return;
|
|
}
|
|
|
|
if (value)
|
|
{
|
|
const char *v = strchr (f, '=');
|
|
|
|
if (v)
|
|
{
|
|
size_t n = v - f;
|
|
char *end;
|
|
|
|
for (i = 0; i < size; i++)
|
|
if (strncasecmp (array[i].name, f, n) == 0)
|
|
{
|
|
value = strtol (v + 1, &end, 0);
|
|
if (*end == '\0')
|
|
{
|
|
array[i].value = value;
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lineno != -1)
|
|
fail ("%s: %d: unknown bitfield: %s\n", filename, lineno, f);
|
|
else
|
|
fail ("unknown bitfield: %s\n", f);
|
|
}
|
|
|
|
static void
|
|
add_isa_dependencies (bitfield *flags, const char *f, int value,
|
|
unsigned int reverse)
|
|
{
|
|
unsigned int i;
|
|
char *str = NULL;
|
|
const char *isa = f;
|
|
static bool is_avx;
|
|
bool is_isa = false, orig_is_avx = is_avx;
|
|
|
|
/* Need to find base entry for references to auxiliary ones. */
|
|
if (strchr (f, ':'))
|
|
{
|
|
str = xstrdup (f);
|
|
*strchr (str, ':') = '\0';
|
|
isa = str;
|
|
}
|
|
/* isa_dependencies[] prefers "LM" over "64". */
|
|
else if (!strcmp (f, "LM"))
|
|
isa = "64";
|
|
for (i = 0; i < CpuMax; ++i)
|
|
if (strcasecmp (flags[i].name, isa) == 0)
|
|
{
|
|
flags[i].value = value;
|
|
if (reverse < ARRAY_SIZE (isa_reverse_deps[0])
|
|
/* Don't record the feature itself here. */
|
|
&& reverse != i
|
|
/* Don't record base architectures. */
|
|
&& reverse > Cpu686)
|
|
isa_reverse_deps[i][reverse] = 1;
|
|
is_isa = true;
|
|
if (i == CpuAVX || i == CpuXOP || i == CpuVAES || i == CpuVPCLMULQDQ)
|
|
is_avx = true;
|
|
break;
|
|
}
|
|
free (str);
|
|
|
|
/* Do not turn off dependencies. */
|
|
if (is_isa && !value)
|
|
{
|
|
is_avx = orig_is_avx;
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE (isa_dependencies); ++i)
|
|
if (strcasecmp (isa_dependencies[i].name, f) == 0)
|
|
{
|
|
char *deps = xstrdup (isa_dependencies[i].deps);
|
|
char *next = deps;
|
|
char *last = deps + strlen (deps);
|
|
|
|
for (; next && next < last; )
|
|
{
|
|
char *str = next_field (next, '|', &next, last);
|
|
|
|
/* No AVX/XOP -> SSE reverse dependencies. */
|
|
if (is_avx && strncmp (str, "SSE", 3) == 0)
|
|
add_isa_dependencies (flags, str, value, CpuMax);
|
|
else
|
|
add_isa_dependencies (flags, str, value, reverse);
|
|
}
|
|
free (deps);
|
|
|
|
/* ISA extensions with dependencies need CPU_ANY_*_FLAGS emitted,
|
|
unless the sole dependency is the "64-bit mode only" one. */
|
|
if (reverse < ARRAY_SIZE (isa_reverse_deps[0])
|
|
&& strcmp (isa_dependencies[i].deps, "64"))
|
|
isa_reverse_deps[reverse][reverse] = 1;
|
|
|
|
is_avx = orig_is_avx;
|
|
return;
|
|
}
|
|
|
|
if (!is_isa)
|
|
fail ("unknown bitfield: %s\n", f);
|
|
|
|
is_avx = orig_is_avx;
|
|
}
|
|
|
|
static void
|
|
output_cpu_flags (FILE *table, bitfield *flags, unsigned int size,
|
|
int mode, const char *comma, const char *indent, int lineno)
|
|
{
|
|
unsigned int i = 0, j = 0;
|
|
|
|
if (mode < 0)
|
|
memset (&active_cpu_flags, 0, sizeof(active_cpu_flags));
|
|
|
|
fprintf (table, "%s{ { ", indent);
|
|
|
|
if (mode <= 0)
|
|
{
|
|
for (j = ~0u; i < CpuAttrEnums; i++)
|
|
{
|
|
if (!flags[i].value)
|
|
continue;
|
|
|
|
if (j < ~0u)
|
|
fail ("%s: %d: invalid combination of CPU identifiers\n",
|
|
filename, lineno);
|
|
j = i;
|
|
if (mode)
|
|
active_cpu_flags.array[i / 32] |= 1U << (i % 32);
|
|
}
|
|
|
|
/* Write 0 to indicate "no associated flag". */
|
|
fprintf (table, "%u, ", j + 1);
|
|
|
|
j = 1;
|
|
}
|
|
|
|
for (; i < size - 1; i++, j++)
|
|
{
|
|
if (((j + 1) % 20) != 0)
|
|
fprintf (table, "%d, ", flags[i].value);
|
|
else
|
|
fprintf (table, "%d,", flags[i].value);
|
|
if (((j + 1) % 20) == 0)
|
|
{
|
|
/* We need \\ for macro. */
|
|
if (mode > 0)
|
|
fprintf (table, " \\\n %s", indent);
|
|
else
|
|
fprintf (table, "\n %s", indent);
|
|
}
|
|
if (mode < 0 && flags[i].value)
|
|
active_cpu_flags.array[i / 32] |= 1U << (i % 32);
|
|
}
|
|
|
|
#if defined(CpuAttrUnused) != defined(CpuUnused)
|
|
if (mode <= 0)
|
|
# ifdef CpuUnused
|
|
fprintf (table, " } }%s\n", comma);
|
|
# else
|
|
fprintf (table, "%d, 0 } }%s\n", flags[i].value, comma);
|
|
# endif
|
|
else
|
|
#endif
|
|
fprintf (table, "%d } }%s\n", flags[i].value, comma);
|
|
}
|
|
|
|
static void
|
|
process_i386_cpu_flag (FILE *table, char *flag,
|
|
const char *name,
|
|
const char *comma, const char *indent,
|
|
int lineno, unsigned int reverse)
|
|
{
|
|
char *str, *next = flag, *last;
|
|
unsigned int i;
|
|
int value = 1;
|
|
bool is_isa = false;
|
|
bitfield all [ARRAY_SIZE (cpu_flags)];
|
|
bitfield any [ARRAY_SIZE (cpu_flags)];
|
|
|
|
/* Copy the default cpu flags. */
|
|
memcpy (all, cpu_flags, sizeof (cpu_flags));
|
|
memcpy (any, cpu_flags, sizeof (cpu_flags));
|
|
|
|
if (flag == NULL)
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE (isa_reverse_deps[0]); ++i)
|
|
any[i].value = isa_reverse_deps[reverse][i];
|
|
goto output;
|
|
}
|
|
|
|
if (flag[0] == '~')
|
|
{
|
|
last = flag + strlen (flag);
|
|
|
|
if (flag[1] == '(')
|
|
{
|
|
last -= 1;
|
|
next = flag + 2;
|
|
if (*last != ')')
|
|
fail ("%s: %d: missing `)' in bitfield: %s\n", filename,
|
|
lineno, flag);
|
|
*last = '\0';
|
|
}
|
|
else
|
|
next = flag + 1;
|
|
|
|
/* First we turn on everything except for cpuno64 and - if
|
|
present - the padding field. */
|
|
for (i = 0; i < ARRAY_SIZE (any); i++)
|
|
if (any[i].position < CpuNo64)
|
|
any[i].value = 1;
|
|
|
|
/* Turn off selective bits. */
|
|
value = 0;
|
|
}
|
|
|
|
if (name != NULL && value != 0)
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE (any); i++)
|
|
if (strcasecmp (any[i].name, name) == 0)
|
|
{
|
|
add_isa_dependencies (any, name, 1, reverse);
|
|
is_isa = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (strcmp (flag, "0"))
|
|
{
|
|
bool combined = false;
|
|
|
|
if (is_isa)
|
|
return;
|
|
|
|
/* Turn on/off selective bits. */
|
|
last = flag + strlen (flag);
|
|
if (name == NULL && strchr (flag, '&'))
|
|
{
|
|
for (; next < last && *next != '('; )
|
|
{
|
|
str = next_field (next, '&', &next, last);
|
|
set_bitfield (str, all, value, ARRAY_SIZE (all), lineno);
|
|
}
|
|
if (*next == '(')
|
|
{
|
|
if (*--last != ')')
|
|
fail ("%s: %d: missing `)' in bitfield: %s\n", filename,
|
|
lineno, flag);
|
|
++next;
|
|
*last = '\0';
|
|
}
|
|
combined = true;
|
|
}
|
|
for (; next && next < last; )
|
|
{
|
|
str = next_field (next, '|', &next, last);
|
|
if (name)
|
|
add_isa_dependencies (any, str, value, reverse);
|
|
else if (combined || next < last)
|
|
set_bitfield (str, any, value, ARRAY_SIZE (any), lineno);
|
|
else /* Singular specifiers go into "all". */
|
|
set_bitfield (str, all, value, ARRAY_SIZE (all), lineno);
|
|
combined = true;
|
|
}
|
|
}
|
|
|
|
output:
|
|
if (name != NULL)
|
|
{
|
|
size_t len = strlen (name);
|
|
char *upper = xmalloc (len + 1);
|
|
|
|
/* Cpu64 is special: It specifies a mode dependency, not an ISA one. Zap
|
|
the flag from ISA initializer macros (and from CPU_ANY_64_FLAGS
|
|
itself we only care about tracking its dependents. Also don't emit the
|
|
(otherwise all zero) CPU_64_FLAGS. */
|
|
if (flag != NULL && reverse == Cpu64)
|
|
return;
|
|
if (is_isa || flag == NULL)
|
|
any[Cpu64].value = 0;
|
|
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
/* Don't emit #define-s for auxiliary entries. */
|
|
if (name[i] == ':')
|
|
return;
|
|
upper[i] = TOUPPER (name[i]);
|
|
}
|
|
upper[i] = '\0';
|
|
fprintf (table, "\n#define CPU_%s%s_FLAGS \\\n",
|
|
flag != NULL ? "": "ANY_", upper);
|
|
free (upper);
|
|
}
|
|
else
|
|
{
|
|
/* Synthesize "64-bit mode only" dependencies from the dependencies we
|
|
have accumulated. */
|
|
for (i = 0; i < ARRAY_SIZE (isa_reverse_deps[0]); ++i)
|
|
if (all[i].value && isa_reverse_deps[Cpu64][i])
|
|
all[Cpu64].value = 1;
|
|
|
|
output_cpu_flags(table, all, ARRAY_SIZE (all), -1, comma, indent, lineno);
|
|
}
|
|
|
|
output_cpu_flags (table, any, ARRAY_SIZE (any), name != NULL,
|
|
comma, indent, lineno);
|
|
}
|
|
|
|
static void
|
|
output_opcode_modifier (FILE *table, bitfield *modifier, unsigned int size)
|
|
{
|
|
unsigned int i;
|
|
|
|
fprintf (table, " { ");
|
|
|
|
for (i = 0; i < size - 1; i++)
|
|
{
|
|
if (((i + 1) % 20) != 0)
|
|
fprintf (table, "%d, ", modifier[i].value);
|
|
else
|
|
fprintf (table, "%d,", modifier[i].value);
|
|
if (((i + 1) % 20) == 0)
|
|
fprintf (table, "\n ");
|
|
}
|
|
|
|
fprintf (table, "%d },\n", modifier[i].value);
|
|
}
|
|
|
|
/* Returns LOG2 of element size. */
|
|
static int
|
|
get_element_size (char **opnd, int lineno)
|
|
{
|
|
char *str, *next, *last, *op;
|
|
const char *full = opnd[0];
|
|
int elem_size = INT_MAX;
|
|
|
|
/* Find the memory operand. */
|
|
while (full != NULL && strstr(full, "BaseIndex") == NULL)
|
|
full = *++opnd;
|
|
if (full == NULL)
|
|
fail ("%s: %d: no memory operand\n", filename, lineno);
|
|
|
|
op = xstrdup (full);
|
|
last = op + strlen (op);
|
|
for (next = op; next && next < last; )
|
|
{
|
|
str = next_field (next, '|', &next, last);
|
|
if (str)
|
|
{
|
|
if (strcasecmp(str, "Byte") == 0)
|
|
{
|
|
/* The smallest element size, no need to check
|
|
further. */
|
|
elem_size = 0;
|
|
break;
|
|
}
|
|
else if (strcasecmp(str, "Word") == 0)
|
|
{
|
|
if (elem_size > 1)
|
|
elem_size = 1;
|
|
}
|
|
else if (strcasecmp(str, "Dword") == 0)
|
|
{
|
|
if (elem_size > 2)
|
|
elem_size = 2;
|
|
}
|
|
else if (strcasecmp(str, "Qword") == 0)
|
|
{
|
|
if (elem_size > 3)
|
|
elem_size = 3;
|
|
}
|
|
}
|
|
}
|
|
free (op);
|
|
|
|
if (elem_size == INT_MAX)
|
|
fail ("%s: %d: unknown element size: %s\n", filename, lineno, full);
|
|
|
|
return elem_size;
|
|
}
|
|
|
|
static bool
|
|
rex2_disallowed (const unsigned long long opcode, unsigned int length,
|
|
unsigned int space, const char *cpu_flags)
|
|
{
|
|
/* Some opcodes encode a ModR/M-like byte directly in the opcode. */
|
|
unsigned int base_opcode = opcode >> (8 * length - 8);
|
|
|
|
/* All opcodes listed map0 0x4*, 0x7*, 0xa*, 0xe* and map1 0x3*, 0x8*
|
|
are reserved under REX2 and triggers #UD when prefixed with REX2 */
|
|
if (space == 0)
|
|
switch (base_opcode >> 4)
|
|
{
|
|
case 0x4:
|
|
case 0x7:
|
|
case 0xA:
|
|
case 0xE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (space == SPACE_0F)
|
|
switch (base_opcode >> 4)
|
|
{
|
|
case 0x3:
|
|
case 0x8:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
process_i386_opcode_modifier (FILE *table, char *mod, unsigned int space,
|
|
unsigned int prefix, const char *extension_opcode,
|
|
char **opnd, int lineno, bool rex2_disallowed)
|
|
{
|
|
char *str, *next, *last;
|
|
bool disp8_shift_derived = false;
|
|
bitfield modifiers [ARRAY_SIZE (opcode_modifiers)];
|
|
static const char *const spaces[] = {
|
|
#define SPACE(n) [SPACE_##n] = #n
|
|
SPACE(BASE),
|
|
SPACE(0F),
|
|
SPACE(0F38),
|
|
SPACE(0F3A),
|
|
SPACE(EVEXMAP4),
|
|
SPACE(EVEXMAP5),
|
|
SPACE(EVEXMAP6),
|
|
SPACE(VEXMAP7),
|
|
SPACE(XOP08),
|
|
SPACE(XOP09),
|
|
SPACE(XOP0A),
|
|
#undef SPACE
|
|
};
|
|
|
|
active_isstring = 0;
|
|
|
|
/* Copy the default opcode modifier. */
|
|
memcpy (modifiers, opcode_modifiers, sizeof (modifiers));
|
|
|
|
if (strcmp (mod, "0"))
|
|
{
|
|
unsigned int have_w = 0, bwlq_suf = 0xf;
|
|
|
|
last = mod + strlen (mod);
|
|
for (next = mod; next && next < last; )
|
|
{
|
|
str = next_field (next, '|', &next, last);
|
|
if (str)
|
|
{
|
|
int val = 1;
|
|
|
|
if (strncmp(str, "OpcodeSpace", 11) == 0)
|
|
{
|
|
char *end;
|
|
|
|
if (str[11] != '=')
|
|
fail ("%s:%d: Missing value for `OpcodeSpace'\n",
|
|
filename, lineno);
|
|
|
|
val = strtol (str + 12, &end, 0);
|
|
if (*end)
|
|
fail ("%s:%d: Bogus value `%s' for `OpcodeSpace'\n",
|
|
filename, lineno, end);
|
|
|
|
if (space)
|
|
{
|
|
if (val != space)
|
|
fail ("%s:%d: Conflicting opcode space specifications\n",
|
|
filename, lineno);
|
|
fprintf (stderr,
|
|
"%s:%d: Warning: redundant opcode space specification\n",
|
|
filename, lineno);
|
|
}
|
|
|
|
space = val;
|
|
continue;
|
|
}
|
|
|
|
if (strcasecmp(str, "Broadcast") == 0)
|
|
val = get_element_size (opnd, lineno) + BYTE_BROADCAST;
|
|
else if (strcasecmp(str, "Disp8MemShift") == 0)
|
|
{
|
|
val = get_element_size (opnd, lineno);
|
|
disp8_shift_derived = true;
|
|
}
|
|
|
|
set_bitfield (str, modifiers, val, ARRAY_SIZE (modifiers),
|
|
lineno);
|
|
if (strcasecmp(str, "IsString") == 0)
|
|
active_isstring = 1;
|
|
|
|
if (strcasecmp(str, "W") == 0)
|
|
have_w = 1;
|
|
|
|
if (strcasecmp(str, "No_bSuf") == 0)
|
|
bwlq_suf &= ~1;
|
|
if (strcasecmp(str, "No_wSuf") == 0)
|
|
bwlq_suf &= ~2;
|
|
if (strcasecmp(str, "No_lSuf") == 0)
|
|
bwlq_suf &= ~4;
|
|
if (strcasecmp(str, "No_qSuf") == 0)
|
|
bwlq_suf &= ~8;
|
|
}
|
|
}
|
|
|
|
if (prefix)
|
|
{
|
|
if (!modifiers[OpcodePrefix].value)
|
|
modifiers[OpcodePrefix].value = prefix;
|
|
else if (modifiers[OpcodePrefix].value != prefix)
|
|
fail ("%s:%d: Conflicting prefix specifications\n",
|
|
filename, lineno);
|
|
else
|
|
fprintf (stderr,
|
|
"%s:%d: Warning: redundant prefix specification\n",
|
|
filename, lineno);
|
|
}
|
|
|
|
if (have_w && !bwlq_suf)
|
|
fail ("%s: %d: stray W modifier\n", filename, lineno);
|
|
if (have_w && !(bwlq_suf & 1))
|
|
fprintf (stderr, "%s: %d: W modifier without Byte operand(s)\n",
|
|
filename, lineno);
|
|
if (have_w && !(bwlq_suf & ~1))
|
|
fprintf (stderr,
|
|
"%s: %d: W modifier without Word/Dword/Qword operand(s)\n",
|
|
filename, lineno);
|
|
}
|
|
|
|
if (space >= ARRAY_SIZE (spaces) || !spaces[space])
|
|
fail ("%s:%d: Unknown opcode space %u\n", filename, lineno, space);
|
|
|
|
fprintf (table, " SPACE_%s, %s,\n",
|
|
spaces[space], extension_opcode ? extension_opcode : "None");
|
|
|
|
/* Rather than evaluating multiple conditions at runtime to determine
|
|
whether an EVEX encoding is being dealt with, derive that information
|
|
right here. A missing EVex attribute means "dynamic". There's one
|
|
exception though: A value-less Disp8MemShift needs zapping rather than
|
|
respecting if no other attribute indicates EVEX encoding. This is for
|
|
certain SSE2AVX templatized templates to work reasonably. */
|
|
if (!modifiers[EVex].value)
|
|
{
|
|
if (modifiers[Broadcast].value
|
|
|| modifiers[Masking].value
|
|
|| modifiers[SAE].value)
|
|
modifiers[EVex].value = EVEXDYN;
|
|
else if (disp8_shift_derived)
|
|
modifiers[Disp8MemShift].value = 0;
|
|
else if (modifiers[Disp8MemShift].value)
|
|
modifiers[EVex].value = EVEXDYN;
|
|
}
|
|
|
|
/* Vex, legacy map2 and map3 and rex2_disallowed do not support EGPR.
|
|
For templates supporting both Vex and EVex allowing EGPR. */
|
|
if ((modifiers[Vex].value || space > SPACE_0F || rex2_disallowed)
|
|
&& !modifiers[EVex].value)
|
|
modifiers[NoEgpr].value = 1;
|
|
|
|
output_opcode_modifier (table, modifiers, ARRAY_SIZE (modifiers));
|
|
}
|
|
|
|
enum stage {
|
|
stage_macros,
|
|
stage_opcodes,
|
|
stage_registers,
|
|
};
|
|
|
|
static void
|
|
output_operand_type (FILE *table, enum operand_class class,
|
|
enum operand_instance instance,
|
|
const bitfield *types, unsigned int size,
|
|
enum stage stage, const char *indent)
|
|
{
|
|
unsigned int i;
|
|
|
|
fprintf (table, "{ { %d, %d, ", class, instance);
|
|
|
|
for (i = 0; i < size - 1; i++)
|
|
{
|
|
if (((i + 3) % 20) != 0)
|
|
fprintf (table, "%d, ", types[i].value);
|
|
else
|
|
fprintf (table, "%d,", types[i].value);
|
|
if (((i + 3) % 20) == 0)
|
|
{
|
|
/* We need \\ for macro. */
|
|
if (stage == stage_macros)
|
|
fprintf (table, " \\\n%s", indent);
|
|
else
|
|
fprintf (table, "\n%s", indent);
|
|
}
|
|
}
|
|
|
|
fprintf (table, "%d } }", types[i].value);
|
|
}
|
|
|
|
static void
|
|
process_i386_operand_type (FILE *table, char *op, enum stage stage,
|
|
const char *indent, int lineno)
|
|
{
|
|
char *str, *next, *last;
|
|
enum operand_class class = ClassNone;
|
|
enum operand_instance instance = InstanceNone;
|
|
bitfield types [ARRAY_SIZE (operand_types)];
|
|
|
|
/* Copy the default operand type. */
|
|
memcpy (types, operand_types, sizeof (types));
|
|
|
|
if (strcmp (op, "0"))
|
|
{
|
|
int baseindex = 0;
|
|
|
|
last = op + strlen (op);
|
|
for (next = op; next && next < last; )
|
|
{
|
|
str = next_field (next, '|', &next, last);
|
|
if (str)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!strncmp(str, "Class=", 6))
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE(operand_classes); ++i)
|
|
if (!strcmp(str + 6, operand_classes[i].name))
|
|
{
|
|
class = operand_classes[i].value;
|
|
str = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (str && !strncmp(str, "Instance=", 9))
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE(operand_instances); ++i)
|
|
if (!strcmp(str + 9, operand_instances[i].name))
|
|
{
|
|
instance = operand_instances[i].value;
|
|
str = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (str)
|
|
{
|
|
set_bitfield (str, types, 1, ARRAY_SIZE (types), lineno);
|
|
if (strcasecmp(str, "BaseIndex") == 0)
|
|
baseindex = 1;
|
|
}
|
|
}
|
|
|
|
if (stage == stage_opcodes && baseindex && !active_isstring)
|
|
{
|
|
set_bitfield("Disp8", types, 1, ARRAY_SIZE (types), lineno);
|
|
if (!active_cpu_flags.bitfield.cpu64
|
|
&& !active_cpu_flags.bitfield.cpumpx)
|
|
set_bitfield("Disp16", types, 1, ARRAY_SIZE (types), lineno);
|
|
set_bitfield("Disp32", types, 1, ARRAY_SIZE (types), lineno);
|
|
}
|
|
}
|
|
output_operand_type (table, class, instance, types, ARRAY_SIZE (types),
|
|
stage, indent);
|
|
}
|
|
|
|
static char *mkident (const char *mnem)
|
|
{
|
|
char *ident = xstrdup (mnem), *p = ident;
|
|
|
|
do
|
|
{
|
|
if (!ISALNUM (*p))
|
|
*p = '_';
|
|
}
|
|
while (*++p);
|
|
|
|
return ident;
|
|
}
|
|
|
|
static void
|
|
output_i386_opcode (FILE *table, const char *name, char *str,
|
|
char *last, int lineno)
|
|
{
|
|
unsigned int i, length, prefix = 0, space = 0;
|
|
char *base_opcode, *extension_opcode, *end, *ident;
|
|
char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS];
|
|
unsigned long long opcode;
|
|
|
|
/* Find base_opcode. */
|
|
base_opcode = next_field (str, ',', &str, last);
|
|
|
|
/* Find extension_opcode, if any. */
|
|
extension_opcode = strchr (base_opcode, '/');
|
|
if (extension_opcode)
|
|
*extension_opcode++ = '\0';
|
|
|
|
/* Find cpu_flags. */
|
|
cpu_flags = next_field (str, ',', &str, last);
|
|
|
|
/* Find opcode_modifier. */
|
|
opcode_modifier = next_field (str, ',', &str, last);
|
|
|
|
/* Remove the first {. */
|
|
str = remove_leading_whitespaces (str);
|
|
if (*str != '{')
|
|
abort ();
|
|
str = remove_leading_whitespaces (str + 1);
|
|
remove_trailing_whitespaces (str);
|
|
|
|
/* Remove } and trailing white space. */
|
|
i = strlen (str);
|
|
if (!i || str[i - 1] != '}')
|
|
abort ();
|
|
str[--i] = '\0';
|
|
remove_trailing_whitespaces (str);
|
|
|
|
if (!*str)
|
|
operand_types [i = 0] = NULL;
|
|
else
|
|
{
|
|
last = str + strlen (str);
|
|
|
|
/* Find operand_types. */
|
|
for (i = 0; i < ARRAY_SIZE (operand_types); i++)
|
|
{
|
|
if (str >= last)
|
|
{
|
|
operand_types [i] = NULL;
|
|
break;
|
|
}
|
|
|
|
operand_types [i] = next_field (str, ',', &str, last);
|
|
}
|
|
}
|
|
|
|
opcode = strtoull (base_opcode, &end, 0);
|
|
|
|
/* Determine opcode length. */
|
|
for (length = 1; length < 8; ++length)
|
|
if (!(opcode >> (8 * length)))
|
|
break;
|
|
|
|
/* Transform prefixes encoded in the opcode into opcode modifier
|
|
representation. */
|
|
if (length > 1)
|
|
{
|
|
switch (opcode >> (8 * length - 8))
|
|
{
|
|
case 0x66: prefix = PREFIX_0X66; break;
|
|
case 0xF3: prefix = PREFIX_0XF3; break;
|
|
case 0xF2: prefix = PREFIX_0XF2; break;
|
|
}
|
|
|
|
if (prefix)
|
|
opcode &= (1ULL << (8 * --length)) - 1;
|
|
}
|
|
|
|
/* Transform opcode space encoded in the opcode into opcode modifier
|
|
representation. */
|
|
if (length > 1 && (opcode >> (8 * length - 8)) == 0xf)
|
|
{
|
|
switch ((opcode >> (8 * length - 16)) & 0xff)
|
|
{
|
|
default: space = SPACE_0F; break;
|
|
case 0x38: space = SPACE_0F38; break;
|
|
case 0x3A: space = SPACE_0F3A; break;
|
|
}
|
|
|
|
if (space != SPACE_0F && --length == 1)
|
|
fail ("%s:%d: %s: unrecognized opcode encoding space\n",
|
|
filename, lineno, name);
|
|
opcode &= (1ULL << (8 * --length)) - 1;
|
|
}
|
|
|
|
if (length > 2)
|
|
fail ("%s:%d: %s: residual opcode (0x%0*llx) too large\n",
|
|
filename, lineno, name, 2 * length, opcode);
|
|
|
|
ident = mkident (name);
|
|
fprintf (table, " { MN_%s, 0x%0*llx%s, %u,",
|
|
ident, 2 * (int)length, opcode, end, i);
|
|
free (ident);
|
|
|
|
process_i386_opcode_modifier (table, opcode_modifier, space, prefix,
|
|
extension_opcode, operand_types, lineno,
|
|
rex2_disallowed (opcode, length, space,
|
|
cpu_flags));
|
|
|
|
process_i386_cpu_flag (table, cpu_flags, NULL, ",", " ", lineno, CpuMax);
|
|
|
|
fprintf (table, " { ");
|
|
|
|
for (i = 0; i < ARRAY_SIZE (operand_types); i++)
|
|
{
|
|
if (!operand_types[i])
|
|
{
|
|
if (i == 0)
|
|
process_i386_operand_type (table, "0", stage_opcodes, "\t ",
|
|
lineno);
|
|
break;
|
|
}
|
|
|
|
if (i != 0)
|
|
fprintf (table, ",\n ");
|
|
|
|
process_i386_operand_type (table, operand_types[i], stage_opcodes,
|
|
"\t ", lineno);
|
|
}
|
|
fprintf (table, " } },\n");
|
|
}
|
|
|
|
struct opcode_hash_entry
|
|
{
|
|
const char *name;
|
|
struct opcode_entry
|
|
{
|
|
struct opcode_entry *next;
|
|
char *opcode;
|
|
int lineno;
|
|
} entry;
|
|
};
|
|
|
|
/* Calculate the hash value of an opcode hash entry P. */
|
|
|
|
static hashval_t
|
|
opcode_hash_hash (const void *p)
|
|
{
|
|
struct opcode_hash_entry *entry = (struct opcode_hash_entry *) p;
|
|
return htab_hash_string (entry->name);
|
|
}
|
|
|
|
/* Compare a string Q against an opcode hash entry P. */
|
|
|
|
static int
|
|
opcode_hash_eq (const void *p, const void *q)
|
|
{
|
|
struct opcode_hash_entry *entry = (struct opcode_hash_entry *) p;
|
|
const char *name = (const char *) q;
|
|
return strcmp (name, entry->name) == 0;
|
|
}
|
|
|
|
static bool
|
|
parse_template (char *buf, int lineno)
|
|
{
|
|
char sep, *end, *ptr;
|
|
struct template *tmpl;
|
|
struct template_instance *last_inst = NULL;
|
|
|
|
buf = remove_leading_whitespaces (buf + 1);
|
|
end = strchr (buf, ':');
|
|
if (end == NULL)
|
|
{
|
|
struct template *prev = NULL;
|
|
|
|
end = strchr (buf, '>');
|
|
if (end == NULL)
|
|
fail ("%s: %d: missing ':' or '>'\n", filename, lineno);
|
|
if (*remove_leading_whitespaces (end + 1))
|
|
fail ("%s: %d: malformed template purge\n", filename, lineno);
|
|
*end = '\0';
|
|
remove_trailing_whitespaces (buf);
|
|
/* Don't bother freeing the various structures. */
|
|
for (tmpl = templates; tmpl != NULL; tmpl = (prev = tmpl)->next)
|
|
if (!strcmp (buf, tmpl->name))
|
|
break;
|
|
if (tmpl == NULL)
|
|
fail ("%s: %d: no template '%s'\n", filename, lineno, buf);
|
|
if (prev)
|
|
prev->next = tmpl->next;
|
|
else
|
|
templates = tmpl->next;
|
|
return true;
|
|
}
|
|
|
|
/* Check whether this actually is a reference to an existing template:
|
|
If there's '>' ahead of ':', it can't be a new template definition
|
|
(and template undefs have are dealt with above). */
|
|
ptr = strchr (buf, '>');
|
|
if (ptr != NULL && ptr < end)
|
|
return false;
|
|
|
|
*end++ = '\0';
|
|
remove_trailing_whitespaces (buf);
|
|
|
|
if (*buf == '\0')
|
|
fail ("%s: %d: missing template identifier\n", filename, lineno);
|
|
tmpl = xmalloc (sizeof (*tmpl));
|
|
tmpl->name = xstrdup (buf);
|
|
|
|
tmpl->params = NULL;
|
|
do {
|
|
struct template_param *param;
|
|
|
|
buf = remove_leading_whitespaces (end);
|
|
end = strpbrk (buf, ":,");
|
|
if (end == NULL)
|
|
fail ("%s: %d: missing ':' or ','\n", filename, lineno);
|
|
|
|
sep = *end;
|
|
*end++ = '\0';
|
|
remove_trailing_whitespaces (buf);
|
|
|
|
param = xmalloc (sizeof (*param));
|
|
param->name = xstrdup (buf);
|
|
param->next = tmpl->params;
|
|
tmpl->params = param;
|
|
} while (sep == ':');
|
|
|
|
tmpl->instances = NULL;
|
|
do {
|
|
struct template_instance *inst;
|
|
char *cur, *next;
|
|
const struct template_param *param;
|
|
|
|
buf = remove_leading_whitespaces (end);
|
|
end = strpbrk (buf, ",>");
|
|
if (end == NULL)
|
|
fail ("%s: %d: missing ',' or '>'\n", filename, lineno);
|
|
|
|
sep = *end;
|
|
*end++ = '\0';
|
|
|
|
inst = xmalloc (sizeof (*inst));
|
|
inst->next = NULL;
|
|
inst->args = NULL;
|
|
|
|
cur = next_field (buf, ':', &next, end);
|
|
inst->name = *cur != '$' ? xstrdup (cur) : "";
|
|
|
|
for (param = tmpl->params; param; param = param->next)
|
|
{
|
|
struct template_arg *arg = xmalloc (sizeof (*arg));
|
|
|
|
cur = next_field (next, ':', &next, end);
|
|
if (next > end)
|
|
fail ("%s: %d: missing argument for '%s'\n", filename, lineno, param->name);
|
|
arg->val = xstrdup (cur);
|
|
arg->next = inst->args;
|
|
inst->args = arg;
|
|
}
|
|
|
|
if (tmpl->instances)
|
|
last_inst->next = inst;
|
|
else
|
|
tmpl->instances = inst;
|
|
last_inst = inst;
|
|
} while (sep == ',');
|
|
|
|
buf = remove_leading_whitespaces (end);
|
|
if (*buf)
|
|
fprintf(stderr, "%s: %d: excess characters '%s'\n",
|
|
filename, lineno, buf);
|
|
|
|
tmpl->next = templates;
|
|
templates = tmpl;
|
|
|
|
return true;
|
|
}
|
|
|
|
static unsigned int
|
|
expand_templates (char *name, const char *str, htab_t opcode_hash_table,
|
|
struct opcode_hash_entry ***opcode_array_p, int lineno)
|
|
{
|
|
static unsigned int idx, opcode_array_size;
|
|
struct opcode_hash_entry **opcode_array = *opcode_array_p;
|
|
struct opcode_hash_entry **hash_slot;
|
|
struct opcode_entry *entry;
|
|
char *ptr1 = strchr(name, '<'), *ptr2;
|
|
|
|
if (ptr1 == NULL)
|
|
{
|
|
/* Get the slot in hash table. */
|
|
hash_slot = (struct opcode_hash_entry **)
|
|
htab_find_slot_with_hash (opcode_hash_table, name,
|
|
htab_hash_string (name),
|
|
INSERT);
|
|
|
|
if (*hash_slot == NULL)
|
|
{
|
|
/* It is the new one. Put it on opcode array. */
|
|
if (idx >= opcode_array_size)
|
|
{
|
|
/* Grow the opcode array when needed. */
|
|
opcode_array_size += 1024;
|
|
opcode_array = (struct opcode_hash_entry **)
|
|
xrealloc (opcode_array,
|
|
sizeof (*opcode_array) * opcode_array_size);
|
|
*opcode_array_p = opcode_array;
|
|
}
|
|
|
|
opcode_array[idx] = (struct opcode_hash_entry *)
|
|
xmalloc (sizeof (struct opcode_hash_entry));
|
|
opcode_array[idx]->name = xstrdup (name);
|
|
*hash_slot = opcode_array[idx];
|
|
entry = &opcode_array[idx]->entry;
|
|
idx++;
|
|
}
|
|
else
|
|
{
|
|
/* Append it to the existing one. */
|
|
struct opcode_entry **entryp = &(*hash_slot)->entry.next;
|
|
|
|
while (*entryp != NULL)
|
|
entryp = &(*entryp)->next;
|
|
entry = (struct opcode_entry *)xmalloc (sizeof (struct opcode_entry));
|
|
*entryp = entry;
|
|
}
|
|
|
|
entry->next = NULL;
|
|
entry->opcode = xstrdup (str);
|
|
entry->lineno = lineno;
|
|
}
|
|
else if ((ptr2 = strchr(ptr1 + 1, '>')) == NULL)
|
|
fail ("%s: %d: missing '>'\n", filename, lineno);
|
|
else
|
|
{
|
|
const struct template *tmpl;
|
|
const struct template_instance *inst;
|
|
|
|
*ptr1 = '\0';
|
|
ptr1 = remove_leading_whitespaces (ptr1 + 1);
|
|
remove_trailing_whitespaces (ptr1);
|
|
|
|
*ptr2++ = '\0';
|
|
|
|
for ( tmpl = templates; tmpl; tmpl = tmpl->next )
|
|
if (!strcmp(ptr1, tmpl->name))
|
|
break;
|
|
if (!tmpl)
|
|
fail ("reference to unknown template '%s'\n", ptr1);
|
|
|
|
for (inst = tmpl->instances; inst; inst = inst->next)
|
|
{
|
|
char *name2 = xmalloc(strlen(name) + strlen(inst->name) + strlen(ptr2) + 1);
|
|
char *str2 = xmalloc(2 * strlen(str));
|
|
const char *src;
|
|
|
|
strcpy (name2, name);
|
|
strcat (name2, inst->name);
|
|
strcat (name2, ptr2);
|
|
|
|
for (ptr1 = str2, src = str; *src; )
|
|
{
|
|
const char *ident = tmpl->name, *end;
|
|
const struct template_param *param;
|
|
const struct template_arg *arg;
|
|
|
|
if ((*ptr1 = *src++) != '<')
|
|
{
|
|
++ptr1;
|
|
continue;
|
|
}
|
|
while (ISSPACE(*src))
|
|
++src;
|
|
while (*ident && *src == *ident)
|
|
++src, ++ident;
|
|
while (ISSPACE(*src))
|
|
++src;
|
|
if (*src != ':' || *ident != '\0')
|
|
{
|
|
memcpy (++ptr1, tmpl->name, ident - tmpl->name);
|
|
ptr1 += ident - tmpl->name;
|
|
continue;
|
|
}
|
|
while (ISSPACE(*++src))
|
|
;
|
|
|
|
end = src;
|
|
while (*end != '\0' && !ISSPACE(*end) && *end != '>')
|
|
++end;
|
|
|
|
for (param = tmpl->params, arg = inst->args; param;
|
|
param = param->next, arg = arg->next)
|
|
{
|
|
if (end - src == strlen (param->name)
|
|
&& !memcmp (src, param->name, end - src))
|
|
{
|
|
src = end;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (param == NULL)
|
|
fail ("template '%s' has no parameter '%.*s'\n",
|
|
tmpl->name, (int)(end - src), src);
|
|
|
|
while (ISSPACE(*src))
|
|
++src;
|
|
if (*src != '>')
|
|
fail ("%s: %d: missing '>'\n", filename, lineno);
|
|
|
|
memcpy(ptr1, arg->val, strlen(arg->val));
|
|
ptr1 += strlen(arg->val);
|
|
++src;
|
|
}
|
|
|
|
*ptr1 = '\0';
|
|
|
|
expand_templates (name2, str2, opcode_hash_table, opcode_array_p,
|
|
lineno);
|
|
|
|
free (str2);
|
|
free (name2);
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
static int mnemonic_cmp(const void *p1, const void *p2)
|
|
{
|
|
const struct opcode_hash_entry *const *e1 = p1, *const *e2 = p2;
|
|
const char *s1 = (*e1)->name, *s2 = (*e2)->name;
|
|
unsigned int i;
|
|
size_t l1 = strlen (s1), l2 = strlen (s2);
|
|
|
|
for (i = 1; i <= l1 && i <= l2; ++i)
|
|
{
|
|
if (s1[l1 - i] != s2[l2 - i])
|
|
return (unsigned char)s1[l1 - i] - (unsigned char)s2[l2 - i];
|
|
}
|
|
|
|
return (int)(l1 - l2);
|
|
}
|
|
|
|
static void
|
|
process_i386_opcodes (FILE *table)
|
|
{
|
|
FILE *fp;
|
|
char buf[2048];
|
|
unsigned int i, j, nr, offs;
|
|
size_t l;
|
|
char *str, *p, *last;
|
|
htab_t opcode_hash_table;
|
|
struct opcode_hash_entry **opcode_array = NULL;
|
|
int lineno = 0, marker = 0;
|
|
|
|
filename = "i386-opc.tbl";
|
|
fp = stdin;
|
|
|
|
i = 0;
|
|
opcode_hash_table = htab_create_alloc (16, opcode_hash_hash,
|
|
opcode_hash_eq, NULL,
|
|
xcalloc, free);
|
|
|
|
fprintf (table, "\n#include \"i386-mnem.h\"\n");
|
|
fprintf (table, "\n/* i386 opcode table. */\n\n");
|
|
fprintf (table, "static const insn_template i386_optab[] =\n{\n");
|
|
|
|
/* Put everything on opcode array. */
|
|
while (!feof (fp))
|
|
{
|
|
char *name;
|
|
|
|
if (fgets (buf, sizeof (buf), fp) == NULL)
|
|
break;
|
|
|
|
p = remove_leading_whitespaces (buf);
|
|
|
|
for ( ; ; )
|
|
{
|
|
lineno++;
|
|
|
|
/* Skip comments. */
|
|
str = strstr (p, "//");
|
|
if (str != NULL)
|
|
{
|
|
str[0] = '\0';
|
|
remove_trailing_whitespaces (p);
|
|
break;
|
|
}
|
|
|
|
/* Look for line continuation character. */
|
|
remove_trailing_whitespaces (p);
|
|
j = strlen (buf);
|
|
if (!j || buf[j - 1] != '+')
|
|
break;
|
|
if (j >= sizeof (buf) - 1)
|
|
fail ("%s: %d: (continued) line too long\n", filename, lineno);
|
|
|
|
if (fgets (buf + j - 1, sizeof (buf) - j + 1, fp) == NULL)
|
|
{
|
|
fprintf (stderr, "%s: Line continuation on last line?\n",
|
|
filename);
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (p[0])
|
|
{
|
|
case '#':
|
|
if (!strcmp("### MARKER ###", buf))
|
|
marker = 1;
|
|
else
|
|
{
|
|
/* Since we ignore all included files (we only care about their
|
|
#define-s here), we don't need to monitor filenames. The final
|
|
line number directive is going to refer to the main source file
|
|
again. */
|
|
char *end;
|
|
unsigned long ln;
|
|
|
|
p = remove_leading_whitespaces (p + 1);
|
|
if (!strncmp(p, "line", 4))
|
|
p += 4;
|
|
ln = strtoul (p, &end, 10);
|
|
if (ln > 1 && ln < INT_MAX
|
|
&& *remove_leading_whitespaces (end) == '"')
|
|
lineno = ln - 1;
|
|
}
|
|
/* Ignore comments. */
|
|
case '\0':
|
|
continue;
|
|
|
|
case '<':
|
|
if (parse_template (p, lineno))
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
if (!marker)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
last = p + strlen (p);
|
|
|
|
/* Find name. */
|
|
name = next_field (p, ',', &str, last);
|
|
|
|
i = expand_templates (name, str, opcode_hash_table, &opcode_array,
|
|
lineno);
|
|
}
|
|
|
|
/* Process opcode array. */
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
const char *name = opcode_array[j]->name;
|
|
struct opcode_entry *next;
|
|
|
|
for (next = &opcode_array[j]->entry; next; next = next->next)
|
|
{
|
|
str = next->opcode;
|
|
lineno = next->lineno;
|
|
last = str + strlen (str);
|
|
output_i386_opcode (table, name, str, last, lineno);
|
|
}
|
|
}
|
|
|
|
fclose (fp);
|
|
|
|
fprintf (table, "};\n");
|
|
|
|
/* Generate opcode sets array. */
|
|
fprintf (table, "\n/* i386 opcode sets table. */\n\n");
|
|
fprintf (table, "typedef unsigned short i386_op_off_t;\n");
|
|
fprintf (table, "static const i386_op_off_t i386_op_sets[] =\n{\n ");
|
|
|
|
for (nr = j = 0; j < i; j++)
|
|
{
|
|
struct opcode_entry *next = &opcode_array[j]->entry;
|
|
|
|
if ((j + 1) % 8 != 0)
|
|
fprintf (table, "%5u,", nr);
|
|
else
|
|
fprintf (table, "%5u,\n ", nr);
|
|
do
|
|
{
|
|
++nr;
|
|
next = next->next;
|
|
}
|
|
while (next);
|
|
}
|
|
|
|
fprintf (table, "%5u\n};\n", nr);
|
|
|
|
/* Emit mnemonics and associated #define-s. */
|
|
qsort (opcode_array, i, sizeof (*opcode_array), mnemonic_cmp);
|
|
|
|
fp = fopen ("i386-mnem.h", "w");
|
|
if (fp == NULL)
|
|
fail ("can't create i386-mnem.h, errno = %s\n",
|
|
xstrerror (errno));
|
|
|
|
process_copyright (fp);
|
|
|
|
fprintf (table, "\n/* i386 mnemonics table. */\n\n");
|
|
fprintf (table, "const char i386_mnemonics[] =\n");
|
|
fprintf (fp, "\nextern const char i386_mnemonics[];\n\n");
|
|
|
|
str = NULL;
|
|
for (l = strlen (opcode_array[offs = j = 0]->name); j < i; j++)
|
|
{
|
|
const char *name = opcode_array[j]->name;
|
|
const char *next = NULL;
|
|
size_t l1 = j + 1 < i ? strlen(next = opcode_array[j + 1]->name) : 0;
|
|
|
|
if (str == NULL)
|
|
str = mkident (name);
|
|
if (l < l1 && !strcmp(name, next + l1 - l))
|
|
{
|
|
fprintf (fp, "#define MN_%s ", str);
|
|
free (str);
|
|
str = mkident (next);
|
|
fprintf (fp, "(MN_%s + %zu)\n", str, l1 - l);
|
|
}
|
|
else
|
|
{
|
|
fprintf (table, " \"\\0\"\"%s\"\n", name);
|
|
fprintf (fp, "#define MN_%s %#x\n", str, offs + 1);
|
|
offs += strlen (name) + 1;
|
|
free (str);
|
|
str = NULL;
|
|
}
|
|
l = l1;
|
|
}
|
|
|
|
fprintf (table, " \"\\0\"\".insn\"\n");
|
|
fprintf (fp, "#define MN__insn %#x\n", offs + 1);
|
|
|
|
fprintf (table, ";\n");
|
|
|
|
fclose (fp);
|
|
}
|
|
|
|
static void
|
|
process_i386_registers (FILE *table)
|
|
{
|
|
FILE *fp;
|
|
char buf[2048];
|
|
char *str, *p, *last;
|
|
char *reg_name, *reg_type, *reg_flags, *reg_num;
|
|
char *dw2_32_num, *dw2_64_num;
|
|
int lineno = 0;
|
|
|
|
filename = "i386-reg.tbl";
|
|
fp = fopen (filename, "r");
|
|
if (fp == NULL)
|
|
fail ("can't find i386-reg.tbl for reading, errno = %s\n",
|
|
xstrerror (errno));
|
|
|
|
fprintf (table, "\n/* i386 register table. */\n\n");
|
|
fprintf (table, "static const reg_entry i386_regtab[] =\n{\n");
|
|
|
|
while (!feof (fp))
|
|
{
|
|
if (fgets (buf, sizeof (buf), fp) == NULL)
|
|
break;
|
|
|
|
lineno++;
|
|
|
|
p = remove_leading_whitespaces (buf);
|
|
|
|
/* Skip comments. */
|
|
str = strstr (p, "//");
|
|
if (str != NULL)
|
|
str[0] = '\0';
|
|
|
|
/* Remove trailing white spaces. */
|
|
remove_trailing_whitespaces (p);
|
|
|
|
switch (p[0])
|
|
{
|
|
case '#':
|
|
fprintf (table, "%s\n", p);
|
|
case '\0':
|
|
continue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
last = p + strlen (p);
|
|
|
|
/* Find reg_name. */
|
|
reg_name = next_field (p, ',', &str, last);
|
|
|
|
/* Find reg_type. */
|
|
reg_type = next_field (str, ',', &str, last);
|
|
|
|
/* Find reg_flags. */
|
|
reg_flags = next_field (str, ',', &str, last);
|
|
|
|
/* Find reg_num. */
|
|
reg_num = next_field (str, ',', &str, last);
|
|
|
|
fprintf (table, " { \"%s\",\n ", reg_name);
|
|
|
|
process_i386_operand_type (table, reg_type, stage_registers, "\t",
|
|
lineno);
|
|
|
|
/* Find 32-bit Dwarf2 register number. */
|
|
dw2_32_num = next_field (str, ',', &str, last);
|
|
|
|
/* Find 64-bit Dwarf2 register number. */
|
|
dw2_64_num = next_field (str, ',', &str, last);
|
|
|
|
fprintf (table, ",\n %s, %s, { %s, %s } },\n",
|
|
reg_flags, reg_num, dw2_32_num, dw2_64_num);
|
|
}
|
|
|
|
fclose (fp);
|
|
|
|
fprintf (table, "};\n");
|
|
|
|
fprintf (table, "\nstatic const unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n");
|
|
}
|
|
|
|
static void
|
|
process_i386_initializers (void)
|
|
{
|
|
unsigned int i;
|
|
FILE *fp = fopen ("i386-init.h", "w");
|
|
|
|
if (fp == NULL)
|
|
fail ("can't create i386-init.h, errno = %s\n",
|
|
xstrerror (errno));
|
|
|
|
process_copyright (fp);
|
|
|
|
for (i = 0; i < CpuMax; i++)
|
|
process_i386_cpu_flag (fp, "0", cpu_flags[i].name, "", " ", -1, i);
|
|
|
|
for (i = 0; i < ARRAY_SIZE (isa_dependencies); i++)
|
|
{
|
|
char *deps = xstrdup (isa_dependencies[i].deps);
|
|
|
|
process_i386_cpu_flag (fp, deps, isa_dependencies[i].name,
|
|
"", " ", -1, CpuMax);
|
|
free (deps);
|
|
}
|
|
|
|
/* Early x87 is somewhat special: Both 287 and 387 not only add new insns
|
|
but also remove some. Hence 8087 isn't a prereq to 287, and 287 isn't
|
|
one to 387. We want the reverse to be true though: Disabling 8087 also
|
|
is to disable 287+ and later; disabling 287 also means disabling 387+. */
|
|
memcpy (isa_reverse_deps[Cpu287], isa_reverse_deps[Cpu387],
|
|
sizeof (isa_reverse_deps[0]));
|
|
isa_reverse_deps[Cpu287][Cpu387] = 1;
|
|
memcpy (isa_reverse_deps[Cpu8087], isa_reverse_deps[Cpu287],
|
|
sizeof (isa_reverse_deps[0]));
|
|
isa_reverse_deps[Cpu8087][Cpu287] = 1;
|
|
|
|
/* While we treat POPCNT as a prereq to SSE4.2, its disabling should not
|
|
lead to disabling of anything else. */
|
|
memset (isa_reverse_deps[CpuPOPCNT], 0, sizeof (isa_reverse_deps[0]));
|
|
|
|
for (i = Cpu686 + 1; i < ARRAY_SIZE (isa_reverse_deps); i++)
|
|
{
|
|
size_t len;
|
|
char *upper;
|
|
|
|
if (memchr(isa_reverse_deps[i], 1,
|
|
ARRAY_SIZE (isa_reverse_deps[0])) == NULL)
|
|
continue;
|
|
|
|
isa_reverse_deps[i][i] = 1;
|
|
process_i386_cpu_flag (fp, NULL, cpu_flags[i].name, "", " ", -1, i);
|
|
}
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
fclose (fp);
|
|
}
|
|
|
|
/* Program options. */
|
|
#define OPTION_SRCDIR 200
|
|
|
|
struct option long_options[] =
|
|
{
|
|
{"srcdir", required_argument, NULL, OPTION_SRCDIR},
|
|
{"debug", no_argument, NULL, 'd'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{0, no_argument, NULL, 0}
|
|
};
|
|
|
|
static void
|
|
print_version (void)
|
|
{
|
|
printf ("%s: version 1.0\n", program_name);
|
|
xexit (0);
|
|
}
|
|
|
|
static void
|
|
usage (FILE * stream, int status)
|
|
{
|
|
fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n",
|
|
program_name);
|
|
xexit (status);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
extern int chdir (char *);
|
|
char *srcdir = NULL;
|
|
int c;
|
|
unsigned int i, cpumax;
|
|
FILE *table;
|
|
|
|
program_name = *argv;
|
|
xmalloc_set_program_name (program_name);
|
|
|
|
while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF)
|
|
switch (c)
|
|
{
|
|
case OPTION_SRCDIR:
|
|
srcdir = optarg;
|
|
break;
|
|
case 'V':
|
|
case 'v':
|
|
print_version ();
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
usage (stderr, 0);
|
|
default:
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
if (optind != argc)
|
|
usage (stdout, 1);
|
|
|
|
if (srcdir != NULL)
|
|
if (chdir (srcdir) != 0)
|
|
fail ("unable to change directory to \"%s\", errno = %s\n",
|
|
srcdir, xstrerror (errno));
|
|
|
|
/* cpu_flags isn't sorted by position. */
|
|
cpumax = 0;
|
|
for (i = 0; i < ARRAY_SIZE (cpu_flags); i++)
|
|
if (cpu_flags[i].position > cpumax)
|
|
cpumax = cpu_flags[i].position;
|
|
|
|
/* Check the unused bitfield in i386_cpu_flags. */
|
|
#ifdef CpuUnused
|
|
static_assert (ARRAY_SIZE (cpu_flags) == CpuMax + 2);
|
|
|
|
if ((cpumax - 1) != CpuMax)
|
|
fail ("CpuMax != %d!\n", cpumax);
|
|
#else
|
|
static_assert (ARRAY_SIZE (cpu_flags) == CpuMax + 1);
|
|
|
|
if (cpumax != CpuMax)
|
|
fail ("CpuMax != %d!\n", cpumax);
|
|
|
|
c = CpuNumOfBits - CpuMax - 1;
|
|
if (c)
|
|
fail ("%d unused bits in i386_cpu_flags.\n", c);
|
|
#endif
|
|
|
|
/* If this triggers, CpuIsaBits needs to be increased. */
|
|
static_assert (CpuAttrEnums <= (1u << CpuIsaBits));
|
|
|
|
/* Check the unused bitfield in i386_cpu_attr. */
|
|
#ifndef CpuAttrUnused
|
|
c = CpuAttrNumOfBits - (CpuIsaBits + CpuMax + 1 - CpuAttrEnums);
|
|
if (c)
|
|
fail ("%d unused bits in i386_cpu_attr.\n", c);
|
|
#endif
|
|
|
|
static_assert (ARRAY_SIZE (opcode_modifiers) == Opcode_Modifier_Num);
|
|
|
|
/* Check the unused bitfield in i386_operand_type. */
|
|
#ifdef OTUnused
|
|
static_assert (ARRAY_SIZE (operand_types) + CLASS_WIDTH + INSTANCE_WIDTH
|
|
== OTNum + 1);
|
|
#else
|
|
static_assert (ARRAY_SIZE (operand_types) + CLASS_WIDTH + INSTANCE_WIDTH
|
|
== OTNum);
|
|
|
|
c = OTNumOfBits - OTNum;
|
|
if (c)
|
|
fail ("%d unused bits in i386_operand_type.\n", c);
|
|
#endif
|
|
|
|
qsort (cpu_flags, ARRAY_SIZE (cpu_flags), sizeof (cpu_flags [0]),
|
|
compare);
|
|
|
|
qsort (opcode_modifiers, ARRAY_SIZE (opcode_modifiers),
|
|
sizeof (opcode_modifiers [0]), compare);
|
|
|
|
qsort (operand_types, ARRAY_SIZE (operand_types),
|
|
sizeof (operand_types [0]), compare);
|
|
|
|
process_i386_initializers ();
|
|
|
|
table = fopen ("i386-tbl.h", "w");
|
|
if (table == NULL)
|
|
fail ("can't create i386-tbl.h, errno = %s\n",
|
|
xstrerror (errno));
|
|
|
|
process_copyright (table);
|
|
|
|
process_i386_opcodes (table);
|
|
process_i386_registers (table);
|
|
|
|
fclose (table);
|
|
|
|
exit (0);
|
|
}
|