`.arch cpu_type' pseudo for x86.

This commit is contained in:
Alan Modra 2000-05-13 09:26:23 +00:00
parent 5ee1baa27d
commit e413e4e996
6 changed files with 1030 additions and 860 deletions

View File

@ -1,3 +1,23 @@
2000-05-13 Alan Modra <alan@linuxcare.com.au>
Alexander Sokolov <robocop@netlink.ru>
* doc/c-i386.texi (i386-Arch): New section.
(i386-Syntax): Mention .intel_syntax and .att_syntax.
* config/tc-i386.c (cpu_arch_name, cpu_arch_flags): New.
(smallest_imm_type): Use smallest opcode for shift by one if cpu
architecture has been given and is not 486.
(set_cpu_arch): New.
(md_pseudo_table): Add .arch.
(md_assemble): Warn if cpu architecture has been given and an
unsupported instruction.
* config/tc-i386.h (SMALLEST_DISP_TYPE): Delete.
Move operand_types bit defines after relevant template field.
(template): Add cpu_flags.
(Cpu*): Define.
(arch_entry): New.
2000-05-12 Alexandre Oliva <aoliva@cygnus.com>
* config/tc-mn10300.h (md_end): Define.

View File

@ -62,6 +62,7 @@ static int add_prefix PARAMS ((unsigned int));
static void set_16bit_code_flag PARAMS ((int));
static void set_16bit_gcc_code_flag PARAMS((int));
static void set_intel_syntax PARAMS ((int));
static void set_cpu_arch PARAMS ((int));
#ifdef BFD_ASSEMBLER
static bfd_reloc_code_real_type reloc
@ -216,15 +217,20 @@ static const templates *current_templates;
/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
static expressionS disp_expressions[2], im_expressions[2];
static int this_operand; /* current operand we are working on */
static int this_operand; /* Current operand we are working on. */
static int flag_do_long_jump; /* FIXME what does this do? */
static int flag_do_long_jump; /* FIXME what does this do? */
static int flag_16bit_code; /* 1 if we're writing 16-bit code, 0 if 32-bit */
static int flag_16bit_code; /* 1 if we're writing 16-bit code,
0 if 32-bit. */
static int intel_syntax = 0; /* 1 for intel syntax, 0 if att syntax */
static int intel_syntax = 0; /* 1 for intel syntax, 0 if att syntax. */
static int allow_naked_reg = 0; /* 1 if register prefix % not required */
static const char *cpu_arch_name = NULL; /* cpu name */
static unsigned int cpu_arch_flags = 0; /* cpu feature flags */
static int allow_naked_reg = 0; /* 1 if register prefix % not required */
static char stackop_size = '\0'; /* Used in 16 bit gcc mode to add an l
suffix to call, ret, enter, leave, push,
@ -301,6 +307,21 @@ const relax_typeS md_relax_table[] =
};
static const arch_entry cpu_arch[] = {
{"i8086", Cpu086 },
{"i186", Cpu086|Cpu186 },
{"i286", Cpu086|Cpu186|Cpu286 },
{"i386", Cpu086|Cpu186|Cpu286|Cpu386 },
{"i486", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486 },
{"i586", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuMMX },
{"i686", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX|CpuSSE },
{"pentium", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuMMX },
{"pentiumpro",Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX|CpuSSE },
{"k6", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|CpuMMX|Cpu3dnow },
{"athlon", Cpu086|Cpu186|Cpu286|Cpu386|Cpu486|Cpu586|Cpu686|CpuMMX|Cpu3dnow },
{NULL, 0 }
};
void
i386_align_code (fragP, count)
@ -441,16 +462,17 @@ static int
smallest_imm_type (num)
offsetT num;
{
#if 0
/* This code is disabled because all the Imm1 forms in the opcode table
are slower on the i486, and they're the versions with the implicitly
specified single-position displacement, which has another syntax if
you really want to use that form. If you really prefer to have the
one-byte-shorter Imm1 form despite these problems, re-enable this
code. */
if (num == 1)
return Imm1 | Imm8 | Imm8S | Imm16 | Imm32;
#endif
if (cpu_arch_flags != 0
&& cpu_arch_flags != (Cpu086|Cpu186|Cpu286|Cpu386|Cpu486))
{
/* This code is disabled on the 486 because all the Imm1 forms
in the opcode table are slower on the i486. They're the
versions with the implicitly specified single-position
displacement, which has another syntax if you really want to
use that form. */
if (num == 1)
return Imm1 | Imm8 | Imm8S | Imm16 | Imm32;
}
return (fits_in_signed_byte (num)
? (Imm8S | Imm8 | Imm16 | Imm32)
: fits_in_unsigned_byte (num)
@ -600,15 +622,48 @@ set_intel_syntax (syntax_flag)
allow_naked_reg = (ask_naked_reg < 0);
}
static void
set_cpu_arch (dummy)
int dummy ATTRIBUTE_UNUSED;
{
SKIP_WHITESPACE();
if (! is_end_of_line[(unsigned char) *input_line_pointer])
{
char *string = input_line_pointer;
int e = get_symbol_end ();
int i;
for (i = 0; cpu_arch[i].name; i++)
{
if (strcmp (string, cpu_arch[i].name) == 0)
{
cpu_arch_name = cpu_arch[i].name;
cpu_arch_flags = cpu_arch[i].flags;
break;
}
}
if (!cpu_arch[i].name)
as_bad (_("no such architecture: `%s'"), string);
*input_line_pointer = e;
}
else
as_bad (_("missing cpu architecture"));
demand_empty_rest_of_line ();
}
const pseudo_typeS md_pseudo_table[] =
{
#ifndef I386COFF
{"bss", s_bss, 0},
#endif
#if !defined(OBJ_AOUT) && !defined(USE_ALIGN_PTWO)
{"align", s_align_bytes, 0},
#else
{"align", s_align_ptwo, 0},
#endif
{"arch", set_cpu_arch, 0},
#ifndef I386COFF
{"bss", s_bss, 0},
#endif
{"ffloat", float_cons, 'f'},
{"dfloat", float_cons, 'd'},
@ -1058,7 +1113,7 @@ md_assemble (line)
mnem_p++;
if (mnem_p >= mnemonic + sizeof (mnemonic))
{
as_bad (_("no such 386 instruction: `%s'"), token_start);
as_bad (_("no such instruction: `%s'"), token_start);
return;
}
l++;
@ -1141,11 +1196,25 @@ md_assemble (line)
}
if (!current_templates)
{
as_bad (_("no such 386 instruction: `%s'"), token_start);
as_bad (_("no such instruction: `%s'"), token_start);
return;
}
}
/* Check if instruction is supported on specified architecture. */
if (cpu_arch_flags != 0)
{
if (current_templates->start->cpu_flags & ~ cpu_arch_flags)
{
as_warn (_("`%s' is not supported on `%s'"),
current_templates->start->name, cpu_arch_name);
}
else if ((Cpu386 & ~ cpu_arch_flags) && !flag_16bit_code)
{
as_warn (_("use .code16 to ensure correct addressing mode"));
}
}
/* check for rep/repne without a string instruction */
if (expecting_string_instruction
&& !(current_templates->start->opcode_modifier & IsString))

View File

@ -239,77 +239,7 @@ extern const char extra_symbol_chars[];
#define OFFSET_FLAT 6
#define FLAT 7
#define NONE_FOUND 8
/*
When an operand is read in it is classified by its type. This type includes
all the possible ways an operand can be used. Thus, '%eax' is both 'register
# 0' and 'The Accumulator'. In our language this is expressed by OR'ing
'Reg32' (any 32 bit register) and 'Acc' (the accumulator).
Operands are classified so that we can match given operand types with
the opcode table in opcode/i386.h.
*/
/* register */
#define Reg8 0x1 /* 8 bit reg */
#define Reg16 0x2 /* 16 bit reg */
#define Reg32 0x4 /* 32 bit reg */
/* immediate */
#define Imm8 0x8 /* 8 bit immediate */
#define Imm8S 0x10 /* 8 bit immediate sign extended */
#define Imm16 0x20 /* 16 bit immediate */
#define Imm32 0x40 /* 32 bit immediate */
#define Imm1 0x80 /* 1 bit immediate */
/* memory */
#define BaseIndex 0x100
/* Disp8,16,32 are used in different ways, depending on the
instruction. For jumps, they specify the size of the PC relative
displacement, for baseindex type instructions, they specify the
size of the offset relative to the base register, and for memory
offset instructions such as `mov 1234,%al' they specify the size of
the offset relative to the segment base. */
#define Disp8 0x200 /* 8 bit displacement */
#define Disp16 0x400 /* 16 bit displacement */
#define Disp32 0x800 /* 32 bit displacement */
/* specials */
#define InOutPortReg 0x1000 /* register to hold in/out port addr = dx */
#define ShiftCount 0x2000 /* register to hold shift cound = cl */
#define Control 0x4000 /* Control register */
#define Debug 0x8000 /* Debug register */
#define Test 0x10000 /* Test register */
#define FloatReg 0x20000 /* Float register */
#define FloatAcc 0x40000 /* Float stack top %st(0) */
#define SReg2 0x80000 /* 2 bit segment register */
#define SReg3 0x100000 /* 3 bit segment register */
#define Acc 0x200000 /* Accumulator %al or %ax or %eax */
#define JumpAbsolute 0x400000
#define RegMMX 0x800000 /* MMX register */
#define RegXMM 0x1000000 /* XMM registers in PIII */
#define EsSeg 0x2000000 /* String insn operand with fixed es segment */
/* InvMem is for instructions with a modrm byte that only allow a
general register encoding in the i.tm.mode and i.tm.regmem fields,
eg. control reg moves. They really ought to support a memory form,
but don't, so we add an InvMem flag to the register operand to
indicate that it should be encoded in the i.tm.regmem field. */
#define InvMem 0x4000000
#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
#define WordReg (Reg16|Reg32)
#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
#define Disp (Disp8|Disp16|Disp32) /* General displacement */
#define AnyMem (Disp|BaseIndex|InvMem) /* General memory */
/* The following aliases are defined because the opcode table
carefully specifies the allowed memory types for each instruction.
At the moment we can only tell a memory reference size by the
instruction suffix, so there's not much point in defining Mem8,
Mem16, Mem32 and Mem64 opcode modifiers - We might as well just use
the suffix directly to check memory operands. */
#define LLongMem AnyMem /* 64 bits (or more) */
#define LongMem AnyMem /* 32 bit memory ref */
#define ShortMem AnyMem /* 16 bit memory ref */
#define WordMem AnyMem /* 16 or 32 bit memory ref */
#define ByteMem AnyMem /* 8 bit memory ref */
#define SMALLEST_DISP_TYPE(num) \
(fits_in_signed_byte(num) ? (Disp8|Disp32) : Disp32)
typedef struct
{
@ -330,6 +260,19 @@ typedef struct
unsigned int extension_opcode;
#define None 0xffff /* If no extension_opcode is possible. */
/* cpu feature flags */
unsigned int cpu_flags;
#define Cpu086 0x1 /* Any old cpu will do, 0 does the same */
#define Cpu186 0x2 /* i186 or better required */
#define Cpu286 0x4 /* i286 or better required */
#define Cpu386 0x8 /* i386 or better required */
#define Cpu486 0x10 /* i486 or better required */
#define Cpu586 0x20 /* i585 or better required */
#define Cpu686 0x40 /* i686 or better required */
#define CpuMMX 0x80 /* MMX support required */
#define CpuSSE 0x100 /* Streaming SIMD extensions required */
#define Cpu3dnow 0x200 /* 3dnow! support required */
/* the bits in opcode_modifier are used to generate the final opcode from
the base_opcode. These bits also are used to detect alternate forms of
the same instruction */
@ -371,8 +314,70 @@ typedef struct
/* operand_types[i] describes the type of operand i. This is made
by OR'ing together all of the possible type masks. (e.g.
'operand_types[i] = Reg|Imm' specifies that operand i can be
either a register or an immediate operand */
either a register or an immediate operand. */
unsigned int operand_types[3];
/* operand_types[i] bits */
/* register */
#define Reg8 0x1 /* 8 bit reg */
#define Reg16 0x2 /* 16 bit reg */
#define Reg32 0x4 /* 32 bit reg */
/* immediate */
#define Imm8 0x8 /* 8 bit immediate */
#define Imm8S 0x10 /* 8 bit immediate sign extended */
#define Imm16 0x20 /* 16 bit immediate */
#define Imm32 0x40 /* 32 bit immediate */
#define Imm1 0x80 /* 1 bit immediate */
/* memory */
#define BaseIndex 0x100
/* Disp8,16,32 are used in different ways, depending on the
instruction. For jumps, they specify the size of the PC relative
displacement, for baseindex type instructions, they specify the
size of the offset relative to the base register, and for memory
offset instructions such as `mov 1234,%al' they specify the size of
the offset relative to the segment base. */
#define Disp8 0x200 /* 8 bit displacement */
#define Disp16 0x400 /* 16 bit displacement */
#define Disp32 0x800 /* 32 bit displacement */
/* specials */
#define InOutPortReg 0x1000 /* register to hold in/out port addr = dx */
#define ShiftCount 0x2000 /* register to hold shift cound = cl */
#define Control 0x4000 /* Control register */
#define Debug 0x8000 /* Debug register */
#define Test 0x10000 /* Test register */
#define FloatReg 0x20000 /* Float register */
#define FloatAcc 0x40000 /* Float stack top %st(0) */
#define SReg2 0x80000 /* 2 bit segment register */
#define SReg3 0x100000 /* 3 bit segment register */
#define Acc 0x200000 /* Accumulator %al or %ax or %eax */
#define JumpAbsolute 0x400000
#define RegMMX 0x800000 /* MMX register */
#define RegXMM 0x1000000 /* XMM registers in PIII */
#define EsSeg 0x2000000 /* String insn operand with fixed es segment */
/* InvMem is for instructions with a modrm byte that only allow a
general register encoding in the i.tm.mode and i.tm.regmem fields,
eg. control reg moves. They really ought to support a memory form,
but don't, so we add an InvMem flag to the register operand to
indicate that it should be encoded in the i.tm.regmem field. */
#define InvMem 0x4000000
#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
#define WordReg (Reg16|Reg32)
#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
#define Disp (Disp8|Disp16|Disp32) /* General displacement */
#define AnyMem (Disp|BaseIndex|InvMem) /* General memory */
/* The following aliases are defined because the opcode table
carefully specifies the allowed memory types for each instruction.
At the moment we can only tell a memory reference size by the
instruction suffix, so there's not much point in defining Mem8,
Mem16, Mem32 and Mem64 opcode modifiers - We might as well just use
the suffix directly to check memory operands. */
#define LLongMem AnyMem /* 64 bits (or more) */
#define LongMem AnyMem /* 32 bit memory ref */
#define ShortMem AnyMem /* 16 bit memory ref */
#define WordMem AnyMem /* 16 or 32 bit memory ref */
#define ByteMem AnyMem /* 8 bit memory ref */
}
template;
@ -384,45 +389,54 @@ template;
END.
*/
typedef struct
{
const template *start;
const template *end;
} templates;
{
const template *start;
const template *end;
}
templates;
/* these are for register name --> number & type hash lookup */
typedef struct
{
char *reg_name;
unsigned int reg_type;
unsigned int reg_num;
}
{
char *reg_name;
unsigned int reg_type;
unsigned int reg_num;
}
reg_entry;
typedef struct
{
char *seg_name;
unsigned int seg_prefix;
}
{
char *seg_name;
unsigned int seg_prefix;
}
seg_entry;
/* 386 operand encoding bytes: see 386 book for details of this. */
typedef struct
{
unsigned int regmem; /* codes register or memory operand */
unsigned int reg; /* codes register operand (or extended opcode) */
unsigned int mode; /* how to interpret regmem & reg */
}
{
unsigned int regmem; /* codes register or memory operand */
unsigned int reg; /* codes register operand (or extended opcode) */
unsigned int mode; /* how to interpret regmem & reg */
}
modrm_byte;
/* 386 opcode byte to code indirect addressing. */
typedef struct
{
unsigned base;
unsigned index;
unsigned scale;
}
{
unsigned base;
unsigned index;
unsigned scale;
}
sib_byte;
/* x86 arch names and features */
typedef struct
{
const char *name; /* arch name */
unsigned int flags; /* cpu feature flags */
}
arch_entry;
/* The name of the global offset table generated by the compiler. Allow
this to be overridden if need be. */
#ifndef GLOBAL_OFFSET_TABLE_NAME

View File

@ -24,6 +24,7 @@
* i386-Float:: Floating Point
* i386-SIMD:: Intel's MMX and AMD's 3DNow! SIMD Operations
* i386-16bit:: Writing 16-bit Code
* i386-Arch:: Specifying an x86 CPU architecture
* i386-Bugs:: AT&T Syntax bugs
* i386-Notes:: Notes
@end menu
@ -35,13 +36,23 @@
@cindex i386 options (none)
The 80386 has no machine dependent options.
@node i386-Syntax
@section AT&T Syntax versus Intel Syntax
@cindex i386 intel_syntax pseudo op
@cindex intel_syntax pseudo op, i386
@cindex i386 att_syntax pseudo op
@cindex att_syntax pseudo op, i386
@cindex i386 syntax compatibility
@cindex syntax compatibility, i386
In order to maintain compatibility with the output of @code{@value{GCC}},
@code{@value{AS}} supports AT&T System V/386 assembler syntax. This is quite
@code{@value{AS}} now supports assembly using Intel assembler syntax.
@code{.intel_syntax} selects Intel mode, and @code{.att_syntax} switches
back to the usual AT&T mode for compatibility with the output of
@code{@value{GCC}}. Either of these directives may have an optional
argument, @code{prefix}, or @code{noprefix} specifying whether registers
require a @samp{%} prefix. AT&T System V/386 assembler syntax is quite
different from Intel syntax. We mention these differences because
almost all 80386 documents use Intel syntax. Notable differences
between the two syntaxes are:
@ -506,6 +517,31 @@ non-commutative arithmetic floating point operations with two register
operands where the source register is @samp{%st} and the destination
register is @samp{%st(i)}.
@node i386-Arch
@section Specifying CPU Architecture
@cindex arch directive, i386
@cindex i386 arch directive
@code{@value{AS}} may be told to assemble for a particular CPU
architecture with the @code{.arch @var{cpu_type}} directive. This
directive enables a warning when gas detects an instruction that is not
supported on the CPU specified. The choices for @var{cpu_type} are:
@multitable @columnfractions .20 .20 .20 .20
@item @samp{i8086} @tab @samp{i186} @tab @samp{i286} @tab @samp{i386}
@item @samp{i486} @tab @samp{i586} @tab @samp{i686} @tab @samp{pentium}
@item @samp{pentiumpro} @tab @samp{k6} @tab @samp{athlon}
@end multitable
Apart from the warning, there is only one other effect on
@code{@value{AS}} operation; If you specify a CPU other than
@samp{i486}, then shift by one instructions such as @samp{sarl $1, %eax}
will automatically use a two byte opcode sequence. The larger three
byte opcode sequence is used on the 486 (and when no architecture is
specified) because it executes faster on the 486. Note that you can
explicitly request the two byte opcode by writing @samp{sarl %eax}.
@node i386-Notes
@section Notes

View File

@ -1,3 +1,13 @@
2000-05-13 Alan Modra <alan@linuxcare.com.au>,
Alexander Sokolov <robocop@netlink.ru>
* i386.h (i386_optab): Add cpu_flags for all instructions.
2000-05-13 Alan Modra <alan@linuxcare.com.au>
From Gavin Romig-Koch <gavin@cygnus.com>
* i386.h (wld_Suf): Define. Use on pushf, popf, pusha, popa.
2000-05-04 Timothy Wall <twall@cygnus.com>
* tic54x.h: New.

File diff suppressed because it is too large Load Diff