[RFC] x86: proposal for a new .insn directive
Jan Beulich
jbeulich@suse.com
Fri Jan 13 11:58:16 GMT 2023
All,
certain other architectures (Arm, RISC-V) have such, and x86 would imo
benefit from such even more: It is notoriously difficult to encode new
insns with operands which a certain version of gas doesn't support yet.
This is in particular related to the building of the ModR/M and SIB
bytes as well the VEX/XOP/EVEX prefixes.
I would appreciate feedback on the proposal (in form of an assembly
source file, providing examples at the same time). Besides pointing
out issues / oversights, thoughts on the various TBDs would be helpful.
Thanks, Jan
.text
insn:
# .insn [<prefix>] [<encoding>] <major-opcode>[+r|/<extension>] [,<operand>[,...]]
# Legacy encoding prefixes altering encoding space (0x0f, 0x0f38, 0x0f3a)
# have to be specified as high byte(s) of <major-opcode>. This also extends
# to certain FPU opcodes or sub-spaces like that of major opcode 0x0f01.
# Legacy encoding prefixes altering meaning (0x66, 0xF2, 0xF3) may be
# specified as high byte of <major-opcode> (perhaps already including an
# encoding space prefix). Other prefixes should be spelled out as usual
# ahead of <major-opcode> or, for segment overrides, with the memory
# operand.
# Operand order may not match that of the instruction actually being
# expressed: While for a memory operand (of which there can be only one) it
# is clear how to encode it in the resulting ModR/M byte, register operands
# are encoded strictly in the order
# - ModR/M.rm, ModR/M.reg for 2-operand insns,
# - ModR/M.rm, {E,}VEX.vvvv, ModR/M.reg for 3-operand insns, and
# - Imm{4,5}, ModR/M.rm, {E,}VEX.vvvv, ModR/M.reg for 4-operand insns,
# obviously with the ModR/M.rm slot skipped when there is a memory operand,
# and obviously with the ModR/M.reg slot skipped when there is an extension
# opcode. (For Intel syntax of course all in the opposite order.)
# Immediate operands (including immediate-like displacements, i.e. when not
# part of ModR/M addressing) should be specified by separate .byte / .word /
# .long / .quad (or alike) directives.
# TBD: How to deal with this for RIP-relative addressing?
# TBD: How to deal with this for 4-operand insns?
# When register operand size varies for an actual insn (like e.g. for MOVZX or
# VPMOVZX*), registers nevertheless need spelling out in a uniform manner, such
# that any of them could be used to derive operand size attributes (e.g.
# operand size prefix, REX.W, VEX.W, or VEX.L) as well as the EVEX Disp8
# scaling factor.
# TBD: Could also go from largest operand size, albeit that may end up confusing
# in AT&T mode, where memory operands don't have size, yet the memory
# operand may have larger size than the register one(s) (and would hence be
# the one which the <len> attribute - see below - needs deriving from).
# For VEX / XOP / EVEX <encoding> is arranged like this:
# {VEX,XOP,EVEX}[.<len>][.<prefix>][.<space>][.<w>]
# where
# - <len> can be LIG, 128, 256, or (EVEX only) 512 as well as L0/L1 for
# VEX / XOP and L0-L3 for EVEX,
# - <prefix> can be NP, 66, F3, or F2,
# - <space> can be
# - 0f, 0f38, 0f3a, or M0...M31 for VEX,
# - 08...3f (hex) for XOP,
# - 0f, 0f38, 0f3a, or M0...M15 for EVEX,
# - <w> can be WIG, W0, or W1.
# Omitted <len> means "infer from operand size" if there is at least one
# sized operand, or LIG otherwise.
# Omitted <prefix> means NP.
# Omitted <space> implies encoding is taken from <major-opcode>.
# Omitted <w> means "infer from GPR operand size" if there is at least
# one GPR operand, or WIG otherwise.
# TBD: Is operand order being dependent on AT&T vs Intel syntax okay?
.insn 0x90 # nop
.insn 0xf390 # pause
.insn rep 0x90 # pause
.insn 0xd9c9 # fxch
.insn 0xf30f01d9 # vmgexit
.insn 0x89, %ecx, %eax # mov %ecx, %eax
.insn 0x89, %ax, %cx # mov %ax, %cx
.insn 0x8b, (%eax), %ecx # mov (%eax), %ecx
.insn 0x0fc8+r, %edx # bswap %edx
.insn lock 0x80/0, %fs:(%eax); .byte 1 # lock addb $1, %fs:(%eax)
1:
.insn 0xe2; .byte 1b-.-1 # loop 1b
.insn 0xc7f8; .long 1b-.-4 # xbegin 1b
.insn 0x0fb6, %ax, %cx # movzx %al, %cx
.insn 0x0fb7, %eax, %ecx # movzx %ax, %ecx
.insn VEX.66.0F 0x58, %xmm0, %xmm1, %xmm2 # vaddpd %xmm0, %xmm1, %xmm2
.insn VEX.66 0x0f58, %ymm0, %ymm1, %ymm2 # vaddpd %ymm0, %ymm1, %ymm2
.insn VEX.LIG.F3.0F 0x58, %xmm0, %xmm1, %xmm2 # vaddss %xmm0, %xmm1, %xmm2
.insn VEX.66.0F3A.W0 0x68, %xmm0, %xmm1, (%edx), %xmm3 # vfmaddps %xmm0, %xmm1, (%edx), %xmm3
.insn VEX.66.0F3A.W1 0x68, %xmm0, %xmm1, (%edx), %xmm3 # vfmaddps %xmm0, %xmm1, %xmm3, (%edx)
.insn VEX.66.0F3A.W1 0x68, %xmm0, %xmm1, %xmm2, (%ebx) # vfmaddps %xmm0, %xmm1, %xmm2, (%ebx)
.insn VEX.66.0F3A.W0 0x48, $0, %xmm0, %xmm1, (%edx), %xmm3 # vpermil2ps $0, %xmm0, %xmm1, (%edx), %xmm3
.insn VEX.66.0F3A.W1 0x48, $1, %xmm0, %xmm1, (%edx), %xmm3 # vpermil2ps $1, %xmm0, %xmm1, %xmm3, (%edx)
.insn VEX.66.0F3A.W1 0x48, $2, %xmm0, %xmm1, %xmm2, (%ebx) # vpermil2ps $2, %xmm0, %xmm1, %xmm2, (%ebx)
.insn VEX.L0.0F.W0 0x93, %eax, %k0 # kmovw %eax, %k0
.insn VEX.256.0F.WIG 0x77 # vzeroall
.insn EVEX.NP.0F.W0 0x58, {rn-sae}, %zmm0, %zmm1, %zmm2 # vaddps {rn-sae}, %zmm0, %zmm1, %zmm2
.insn EVEX.66.0F.W1 0x58, 8(%eax){1to8}, %zmm1, %zmm2{%k2}{z} # vaddpd 8(%eax){1to8}, %zmm0, %zmm1{%k2}{z}
# TBD: How to specify the Disp8 scaling factor here? (In Intel syntax we can simply
# use memory operand size.)
.insn EVEX.66.0F38.W0 0x88, 4(%eax), %ymm1 # vexpandps 4(%eax), %ymm1
More information about the Binutils
mailing list