view third_party/luajit/dynasm/dasm_x86.lua @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents 94705b5986b3
children
line wrap: on
line source

------------------------------------------------------------------------------
-- DynASM x86/x64 module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- See dynasm.lua for full copyright notice.
------------------------------------------------------------------------------

local x64 = x64

-- Module information:
local _info = {
  arch =	x64 and "x64" or "x86",
  description =	"DynASM x86/x64 module",
  version =	"1.5.0",
  vernum =	 10500,
  release =	"2021-05-02",
  author =	"Mike Pall",
  license =	"MIT",
}

-- Exported glue functions for the arch-specific module.
local _M = { _info = _info }

-- Cache library functions.
local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs
local assert, unpack, setmetatable = assert, unpack or table.unpack, setmetatable
local _s = string
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
local find, match, gmatch, gsub = _s.find, _s.match, _s.gmatch, _s.gsub
local concat, sort, remove = table.concat, table.sort, table.remove
local bit = bit or require("bit")
local band, bxor, shl, shr = bit.band, bit.bxor, bit.lshift, bit.rshift

-- Inherited tables and callbacks.
local g_opt, g_arch
local wline, werror, wfatal, wwarn

-- Action name list.
-- CHECK: Keep this in sync with the C code!
local action_names = {
  -- int arg, 1 buffer pos:
  "DISP",  "IMM_S", "IMM_B", "IMM_W", "IMM_D",  "IMM_WB", "IMM_DB",
  -- action arg (1 byte), int arg, 1 buffer pos (reg/num):
  "VREG", "SPACE",
  -- ptrdiff_t arg, 1 buffer pos (address): !x64
  "SETLABEL", "REL_A",
  -- action arg (1 byte) or int arg, 2 buffer pos (link, offset):
  "REL_LG", "REL_PC",
  -- action arg (1 byte) or int arg, 1 buffer pos (link):
  "IMM_LG", "IMM_PC",
  -- action arg (1 byte) or int arg, 1 buffer pos (offset):
  "LABEL_LG", "LABEL_PC",
  -- action arg (1 byte), 1 buffer pos (offset):
  "ALIGN",
  -- action args (2 bytes), no buffer pos.
  "EXTERN",
  -- action arg (1 byte), no buffer pos.
  "ESC",
  -- no action arg, no buffer pos.
  "MARK",
  -- action arg (1 byte), no buffer pos, terminal action:
  "SECTION",
  -- no args, no buffer pos, terminal action:
  "STOP"
}

-- Maximum number of section buffer positions for dasm_put().
-- CHECK: Keep this in sync with the C code!
local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines.

-- Action name -> action number (dynamically generated below).
local map_action = {}
-- First action number. Everything below does not need to be escaped.
local actfirst = 256-#action_names

-- Action list buffer and string (only used to remove dupes).
local actlist = {}
local actstr = ""

-- Argument list for next dasm_put(). Start with offset 0 into action list.
local actargs = { 0 }

-- Current number of section buffer positions for dasm_put().
local secpos = 1

-- VREG kind encodings, pre-shifted by 5 bits.
local map_vreg = {
  ["modrm.rm.m"] = 0x00,
  ["modrm.rm.r"] = 0x20,
  ["opcode"] =     0x20,
  ["sib.base"] =   0x20,
  ["sib.index"] =  0x40,
  ["modrm.reg"] =  0x80,
  ["vex.v"] =      0xa0,
  ["imm.hi"] =     0xc0,
}

-- Current number of VREG actions contributing to REX/VEX shrinkage.
local vreg_shrink_count = 0

------------------------------------------------------------------------------

-- Compute action numbers for action names.
for n,name in ipairs(action_names) do
  local num = actfirst + n - 1
  map_action[name] = num
end

-- Dump action names and numbers.
local function dumpactions(out)
  out:write("DynASM encoding engine action codes:\n")
  for n,name in ipairs(action_names) do
    local num = map_action[name]
    out:write(format("  %-10s %02X  %d\n", name, num, num))
  end
  out:write("\n")
end

-- Write action list buffer as a huge static C array.
local function writeactions(out, name)
  local nn = #actlist
  local last = actlist[nn] or 255
  actlist[nn] = nil -- Remove last byte.
  if nn == 0 then nn = 1 end
  out:write("static const unsigned char ", name, "[", nn, "] = {\n")
  local s = "  "
  for n,b in ipairs(actlist) do
    s = s..b..","
    if #s >= 75 then
      assert(out:write(s, "\n"))
      s = "  "
    end
  end
  out:write(s, last, "\n};\n\n") -- Add last byte back.
end

------------------------------------------------------------------------------

-- Add byte to action list.
local function wputxb(n)
  assert(n >= 0 and n <= 255 and n % 1 == 0, "byte out of range")
  actlist[#actlist+1] = n
end

-- Add action to list with optional arg. Advance buffer pos, too.
local function waction(action, a, num)
  wputxb(assert(map_action[action], "bad action name `"..action.."'"))
  if a then actargs[#actargs+1] = a end
  if a or num then secpos = secpos + (num or 1) end
end

-- Optionally add a VREG action.
local function wvreg(kind, vreg, psz, sk, defer)
  if not vreg then return end
  waction("VREG", vreg)
  local b = assert(map_vreg[kind], "bad vreg kind `"..vreg.."'")
  if b < (sk or 0) then
    vreg_shrink_count = vreg_shrink_count + 1
  end
  if not defer then
    b = b + vreg_shrink_count * 8
    vreg_shrink_count = 0
  end
  wputxb(b + (psz or 0))
end

-- Add call to embedded DynASM C code.
local function wcall(func, args)
  wline(format("dasm_%s(Dst, %s);", func, concat(args, ", ")), true)
end

-- Delete duplicate action list chunks. A tad slow, but so what.
local function dedupechunk(offset)
  local al, as = actlist, actstr
  local chunk = char(unpack(al, offset+1, #al))
  local orig = find(as, chunk, 1, true)
  if orig then
    actargs[1] = orig-1 -- Replace with original offset.
    for i=offset+1,#al do al[i] = nil end -- Kill dupe.
  else
    actstr = as..chunk
  end
end

-- Flush action list (intervening C code or buffer pos overflow).
local function wflush(term)
  local offset = actargs[1]
  if #actlist == offset then return end -- Nothing to flush.
  if not term then waction("STOP") end -- Terminate action list.
  dedupechunk(offset)
  wcall("put", actargs) -- Add call to dasm_put().
  actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
  secpos = 1 -- The actionlist offset occupies a buffer position, too.
end

-- Put escaped byte.
local function wputb(n)
  if n >= actfirst then waction("ESC") end -- Need to escape byte.
  wputxb(n)
end

------------------------------------------------------------------------------

-- Global label name -> global label number. With auto assignment on 1st use.
local next_global = 10
local map_global = setmetatable({}, { __index = function(t, name)
  if not match(name, "^[%a_][%w_@]*$") then werror("bad global label") end
  local n = next_global
  if n > 246 then werror("too many global labels") end
  next_global = n + 1
  t[name] = n
  return n
end})

-- Dump global labels.
local function dumpglobals(out, lvl)
  local t = {}
  for name, n in pairs(map_global) do t[n] = name end
  out:write("Global labels:\n")
  for i=10,next_global-1 do
    out:write(format("  %s\n", t[i]))
  end
  out:write("\n")
end

-- Write global label enum.
local function writeglobals(out, prefix)
  local t = {}
  for name, n in pairs(map_global) do t[n] = name end
  out:write("enum {\n")
  for i=10,next_global-1 do
    out:write("  ", prefix, gsub(t[i], "@.*", ""), ",\n")
  end
  out:write("  ", prefix, "_MAX\n};\n")
end

-- Write global label names.
local function writeglobalnames(out, name)
  local t = {}
  for name, n in pairs(map_global) do t[n] = name end
  out:write("static const char *const ", name, "[] = {\n")
  for i=10,next_global-1 do
    out:write("  \"", t[i], "\",\n")
  end
  out:write("  (const char *)0\n};\n")
end

------------------------------------------------------------------------------

-- Extern label name -> extern label number. With auto assignment on 1st use.
local next_extern = -1
local map_extern = setmetatable({}, { __index = function(t, name)
  -- No restrictions on the name for now.
  local n = next_extern
  if n < -256 then werror("too many extern labels") end
  next_extern = n - 1
  t[name] = n
  return n
end})

-- Dump extern labels.
local function dumpexterns(out, lvl)
  local t = {}
  for name, n in pairs(map_extern) do t[-n] = name end
  out:write("Extern labels:\n")
  for i=1,-next_extern-1 do
    out:write(format("  %s\n", t[i]))
  end
  out:write("\n")
end

-- Write extern label names.
local function writeexternnames(out, name)
  local t = {}
  for name, n in pairs(map_extern) do t[-n] = name end
  out:write("static const char *const ", name, "[] = {\n")
  for i=1,-next_extern-1 do
    out:write("  \"", t[i], "\",\n")
  end
  out:write("  (const char *)0\n};\n")
end

------------------------------------------------------------------------------

-- Arch-specific maps.
local map_archdef = {}		-- Ext. register name -> int. name.
local map_reg_rev = {}		-- Int. register name -> ext. name.
local map_reg_num = {}		-- Int. register name -> register number.
local map_reg_opsize = {}	-- Int. register name -> operand size.
local map_reg_valid_base = {}	-- Int. register name -> valid base register?
local map_reg_valid_index = {}	-- Int. register name -> valid index register?
local map_reg_needrex = {}	-- Int. register name -> need rex vs. no rex.
local reg_list = {}		-- Canonical list of int. register names.

local map_type = {}		-- Type name -> { ctype, reg }
local ctypenum = 0		-- Type number (for _PTx macros).

local addrsize = x64 and "q" or "d"	-- Size for address operands.

-- Helper functions to fill register maps.
local function mkrmap(sz, cl, names)
  local cname = format("@%s", sz)
  reg_list[#reg_list+1] = cname
  map_archdef[cl] = cname
  map_reg_rev[cname] = cl
  map_reg_num[cname] = -1
  map_reg_opsize[cname] = sz
  if sz == addrsize or sz == "d" then
    map_reg_valid_base[cname] = true
    map_reg_valid_index[cname] = true
  end
  if names then
    for n,name in ipairs(names) do
      local iname = format("@%s%x", sz, n-1)
      reg_list[#reg_list+1] = iname
      map_archdef[name] = iname
      map_reg_rev[iname] = name
      map_reg_num[iname] = n-1
      map_reg_opsize[iname] = sz
      if sz == "b" and n > 4 then map_reg_needrex[iname] = false end
      if sz == addrsize or sz == "d" then
	map_reg_valid_base[iname] = true
	map_reg_valid_index[iname] = true
      end
    end
  end
  for i=0,(x64 and sz ~= "f") and 15 or 7 do
    local needrex = sz == "b" and i > 3
    local iname = format("@%s%x%s", sz, i, needrex and "R" or "")
    if needrex then map_reg_needrex[iname] = true end
    local name
    if sz == "o" or sz == "y" then name = format("%s%d", cl, i)
    elseif sz == "f" then name = format("st%d", i)
    else name = format("r%d%s", i, sz == addrsize and "" or sz) end
    map_archdef[name] = iname
    if not map_reg_rev[iname] then
      reg_list[#reg_list+1] = iname
      map_reg_rev[iname] = name
      map_reg_num[iname] = i
      map_reg_opsize[iname] = sz
      if sz == addrsize or sz == "d" then
	map_reg_valid_base[iname] = true
	map_reg_valid_index[iname] = true
      end
    end
  end
  reg_list[#reg_list+1] = ""
end

-- Integer registers (qword, dword, word and byte sized).
if x64 then
  mkrmap("q", "Rq", {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"})
end
mkrmap("d", "Rd", {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"})
mkrmap("w", "Rw", {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"})
mkrmap("b", "Rb", {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"})
map_reg_valid_index[map_archdef.esp] = false
if x64 then map_reg_valid_index[map_archdef.rsp] = false end
if x64 then map_reg_needrex[map_archdef.Rb] = true end
map_archdef["Ra"] = "@"..addrsize

-- FP registers (internally tword sized, but use "f" as operand size).
mkrmap("f", "Rf")

-- SSE registers (oword sized, but qword and dword accessible).
mkrmap("o", "xmm")

-- AVX registers (yword sized, but oword, qword and dword accessible).
mkrmap("y", "ymm")

-- Operand size prefixes to codes.
local map_opsize = {
  byte = "b", word = "w", dword = "d", qword = "q", oword = "o", yword = "y",
  tword = "t", aword = addrsize,
}

-- Operand size code to number.
local map_opsizenum = {
  b = 1, w = 2, d = 4, q = 8, o = 16, y = 32, t = 10,
}

-- Operand size code to name.
local map_opsizename = {
  b = "byte", w = "word", d = "dword", q = "qword", o = "oword", y = "yword",
  t = "tword", f = "fpword",
}

-- Valid index register scale factors.
local map_xsc = {
  ["1"] = 0, ["2"] = 1, ["4"] = 2, ["8"] = 3,
}

-- Condition codes.
local map_cc = {
  o = 0, no = 1, b = 2, nb = 3, e = 4, ne = 5, be = 6, nbe = 7,
  s = 8, ns = 9, p = 10, np = 11, l = 12, nl = 13, le = 14, nle = 15,
  c = 2, nae = 2, nc = 3, ae = 3, z = 4, nz = 5, na = 6, a = 7,
  pe = 10, po = 11, nge = 12, ge = 13, ng = 14, g = 15,
}


-- Reverse defines for registers.
function _M.revdef(s)
  return gsub(s, "@%w+", map_reg_rev)
end

-- Dump register names and numbers
local function dumpregs(out)
  out:write("Register names, sizes and internal numbers:\n")
  for _,reg in ipairs(reg_list) do
    if reg == "" then
      out:write("\n")
    else
      local name = map_reg_rev[reg]
      local num = map_reg_num[reg]
      local opsize = map_opsizename[map_reg_opsize[reg]]
      out:write(format("  %-5s %-8s %s\n", name, opsize,
		       num < 0 and "(variable)" or num))
    end
  end
end

------------------------------------------------------------------------------

-- Put action for label arg (IMM_LG, IMM_PC, REL_LG, REL_PC).
local function wputlabel(aprefix, imm, num)
  if type(imm) == "number" then
    if imm < 0 then
      waction("EXTERN")
      wputxb(aprefix == "IMM_" and 0 or 1)
      imm = -imm-1
    else
      waction(aprefix.."LG", nil, num);
    end
    wputxb(imm)
  else
    waction(aprefix.."PC", imm, num)
  end
end

-- Put signed byte or arg.
local function wputsbarg(n)
  if type(n) == "number" then
    if n < -128 or n > 127 then
      werror("signed immediate byte out of range")
    end
    if n < 0 then n = n + 256 end
    wputb(n)
  else waction("IMM_S", n) end
end

-- Put unsigned byte or arg.
local function wputbarg(n)
  if type(n) == "number" then
    if n < 0 or n > 255 then
      werror("unsigned immediate byte out of range")
    end
    wputb(n)
  else waction("IMM_B", n) end
end

-- Put unsigned word or arg.
local function wputwarg(n)
  if type(n) == "number" then
    if shr(n, 16) ~= 0 then
      werror("unsigned immediate word out of range")
    end
    wputb(band(n, 255)); wputb(shr(n, 8));
  else waction("IMM_W", n) end
end

-- Put signed or unsigned dword or arg.
local function wputdarg(n)
  local tn = type(n)
  if tn == "number" then
    wputb(band(n, 255))
    wputb(band(shr(n, 8), 255))
    wputb(band(shr(n, 16), 255))
    wputb(shr(n, 24))
  elseif tn == "table" then
    wputlabel("IMM_", n[1], 1)
  else
    waction("IMM_D", n)
  end
end

-- Put signed or unsigned qword or arg.
local function wputqarg(n)
  local tn = type(n)
  if tn == "number" then -- This is only used for numbers from -2^31..2^32-1.
    wputb(band(n, 255))
    wputb(band(shr(n, 8), 255))
    wputb(band(shr(n, 16), 255))
    wputb(shr(n, 24))
    local sign = n < 0 and 255 or 0
    wputb(sign); wputb(sign); wputb(sign); wputb(sign)
  else
    waction("IMM_D", format("(unsigned int)(%s)", n))
    waction("IMM_D", format("(unsigned int)((unsigned long long)(%s)>>32)", n))
  end
end

-- Put operand-size dependent number or arg (defaults to dword).
local function wputszarg(sz, n)
  if not sz or sz == "d" or sz == "q" then wputdarg(n)
  elseif sz == "w" then wputwarg(n)
  elseif sz == "b" then wputbarg(n)
  elseif sz == "s" then wputsbarg(n)
  else werror("bad operand size") end
end

-- Put multi-byte opcode with operand-size dependent modifications.
local function wputop(sz, op, rex, vex, vregr, vregxb)
  local psz, sk = 0, nil
  if vex then
    local tail
    if vex.m == 1 and band(rex, 11) == 0 then
      if x64 and vregxb then
	sk = map_vreg["modrm.reg"]
      else
	wputb(0xc5)
      tail = shl(bxor(band(rex, 4), 4), 5)
      psz = 3
      end
    end
    if not tail then
      wputb(0xc4)
      wputb(shl(bxor(band(rex, 7), 7), 5) + vex.m)
      tail = shl(band(rex, 8), 4)
      psz = 4
    end
    local reg, vreg = 0, nil
    if vex.v then
      reg = vex.v.reg
      if not reg then werror("bad vex operand") end
      if reg < 0 then reg = 0; vreg = vex.v.vreg end
    end
    if sz == "y" or vex.l then tail = tail + 4 end
    wputb(tail + shl(bxor(reg, 15), 3) + vex.p)
    wvreg("vex.v", vreg)
    rex = 0
    if op >= 256 then werror("bad vex opcode") end
  else
    if rex ~= 0 then
      if not x64 then werror("bad operand size") end
    elseif (vregr or vregxb) and x64 then
      rex = 0x10
      sk = map_vreg["vex.v"]
    end
  end
  local r
  if sz == "w" then wputb(102) end
  -- Needs >32 bit numbers, but only for crc32 eax, word [ebx]
  if op >= 4294967296 then r = op%4294967296 wputb((op-r)/4294967296) op = r end
  if op >= 16777216 then wputb(shr(op, 24)); op = band(op, 0xffffff) end
  if op >= 65536 then
    if rex ~= 0 then
      local opc3 = band(op, 0xffff00)
      if opc3 == 0x0f3a00 or opc3 == 0x0f3800 then
	wputb(64 + band(rex, 15)); rex = 0; psz = 2
      end
    end
    wputb(shr(op, 16)); op = band(op, 0xffff); psz = psz + 1
  end
  if op >= 256 then
    local b = shr(op, 8)
    if b == 15 and rex ~= 0 then wputb(64 + band(rex, 15)); rex = 0; psz = 2 end
    wputb(b); op = band(op, 255); psz = psz + 1
  end
  if rex ~= 0 then wputb(64 + band(rex, 15)); psz = 2 end
  if sz == "b" then op = op - 1 end
  wputb(op)
  return psz, sk
end

-- Put ModRM or SIB formatted byte.
local function wputmodrm(m, s, rm, vs, vrm)
  assert(m < 4 and s < 16 and rm < 16, "bad modrm operands")
  wputb(shl(m, 6) + shl(band(s, 7), 3) + band(rm, 7))
end

-- Put ModRM/SIB plus optional displacement.
local function wputmrmsib(t, imark, s, vsreg, psz, sk)
  local vreg, vxreg
  local reg, xreg = t.reg, t.xreg
  if reg and reg < 0 then reg = 0; vreg = t.vreg end
  if xreg and xreg < 0 then xreg = 0; vxreg = t.vxreg end
  if s < 0 then s = 0 end

  -- Register mode.
  if sub(t.mode, 1, 1) == "r" then
    wputmodrm(3, s, reg)
    wvreg("modrm.reg", vsreg, psz+1, sk, vreg)
    wvreg("modrm.rm.r", vreg, psz+1, sk)
    return
  end

  local disp = t.disp
  local tdisp = type(disp)
  -- No base register?
  if not reg then
    local riprel = false
    if xreg then
      -- Indexed mode with index register only.
      -- [xreg*xsc+disp] -> (0, s, esp) (xsc, xreg, ebp)
      wputmodrm(0, s, 4)
      if imark == "I" then waction("MARK") end
      wvreg("modrm.reg", vsreg, psz+1, sk, vxreg)
      wputmodrm(t.xsc, xreg, 5)
      wvreg("sib.index", vxreg, psz+2, sk)
    else
      -- Pure 32 bit displacement.
      if x64 and tdisp ~= "table" then
	wputmodrm(0, s, 4) -- [disp] -> (0, s, esp) (0, esp, ebp)
	wvreg("modrm.reg", vsreg, psz+1, sk)
	if imark == "I" then waction("MARK") end
	wputmodrm(0, 4, 5)
      else
	riprel = x64
	wputmodrm(0, s, 5) -- [disp|rip-label] -> (0, s, ebp)
	wvreg("modrm.reg", vsreg, psz+1, sk)
	if imark == "I" then waction("MARK") end
      end
    end
    if riprel then -- Emit rip-relative displacement.
      if match("UWSiI", imark) then
	werror("NYI: rip-relative displacement followed by immediate")
      end
      -- The previous byte in the action buffer cannot be 0xe9 or 0x80-0x8f.
      wputlabel("REL_", disp[1], 2)
    else
      wputdarg(disp)
    end
    return
  end

  local m
  if tdisp == "number" then -- Check displacement size at assembly time.
    if disp == 0 and band(reg, 7) ~= 5 then -- [ebp] -> [ebp+0] (in SIB, too)
      if not vreg then m = 0 end -- Force DISP to allow [Rd(5)] -> [ebp+0]
    elseif disp >= -128 and disp <= 127 then m = 1
    else m = 2 end
  elseif tdisp == "table" then
    m = 2
  end

  -- Index register present or esp as base register: need SIB encoding.
  if xreg or band(reg, 7) == 4 then
    wputmodrm(m or 2, s, 4) -- ModRM.
    if m == nil or imark == "I" then waction("MARK") end
    wvreg("modrm.reg", vsreg, psz+1, sk, vxreg or vreg)
    wputmodrm(t.xsc or 0, xreg or 4, reg) -- SIB.
    wvreg("sib.index", vxreg, psz+2, sk, vreg)
    wvreg("sib.base", vreg, psz+2, sk)
  else
    wputmodrm(m or 2, s, reg) -- ModRM.
    if (imark == "I" and (m == 1 or m == 2)) or
       (m == nil and (vsreg or vreg)) then waction("MARK") end
    wvreg("modrm.reg", vsreg, psz+1, sk, vreg)
    wvreg("modrm.rm.m", vreg, psz+1, sk)
  end

  -- Put displacement.
  if m == 1 then wputsbarg(disp)
  elseif m == 2 then wputdarg(disp)
  elseif m == nil then waction("DISP", disp) end
end

------------------------------------------------------------------------------

-- Return human-readable operand mode string.
local function opmodestr(op, args)
  local m = {}
  for i=1,#args do
    local a = args[i]
    m[#m+1] = sub(a.mode, 1, 1)..(a.opsize or "?")
  end
  return op.." "..concat(m, ",")
end

-- Convert number to valid integer or nil.
local function toint(expr, isqword)
  local n = tonumber(expr)
  if n then
    if n % 1 ~= 0 then
      werror("not an integer number `"..expr.."'")
    elseif isqword then
      if n < -2147483648 or n > 2147483647 then
	n = nil -- Handle it as an expression to avoid precision loss.
      end
    elseif n < -2147483648 or n > 4294967295 then
      werror("bad integer number `"..expr.."'")
    end
    return n
  end
end

-- Parse immediate expression.
local function immexpr(expr)
  -- &expr (pointer)
  if sub(expr, 1, 1) == "&" then
    return "iPJ", format("(ptrdiff_t)(%s)", sub(expr,2))
  end

  local prefix = sub(expr, 1, 2)
  -- =>expr (pc label reference)
  if prefix == "=>" then
    return "iJ", sub(expr, 3)
  end
  -- ->name (global label reference)
  if prefix == "->" then
    return "iJ", map_global[sub(expr, 3)]
  end

  -- [<>][1-9] (local label reference)
  local dir, lnum = match(expr, "^([<>])([1-9])$")
  if dir then -- Fwd: 247-255, Bkwd: 1-9.
    return "iJ", lnum + (dir == ">" and 246 or 0)
  end

  local extname = match(expr, "^extern%s+(%S+)$")
  if extname then
    return "iJ", map_extern[extname]
  end

  -- expr (interpreted as immediate)
  return "iI", expr
end

-- Parse displacement expression: +-num, +-expr, +-opsize*num
local function dispexpr(expr)
  local disp = expr == "" and 0 or toint(expr)
  if disp then return disp end
  local c, dispt = match(expr, "^([+-])%s*(.+)$")
  if c == "+" then
    expr = dispt
  elseif not c then
    werror("bad displacement expression `"..expr.."'")
  end
  local opsize, tailops = match(dispt, "^(%w+)%s*%*%s*(.+)$")
  local ops, imm = map_opsize[opsize], toint(tailops)
  if ops and imm then
    if c == "-" then imm = -imm end
    return imm*map_opsizenum[ops]
  end
  local mode, iexpr = immexpr(dispt)
  if mode == "iJ" then
    if c == "-" then werror("cannot invert label reference") end
    return { iexpr }
  end
  return expr -- Need to return original signed expression.
end

-- Parse register or type expression.
local function rtexpr(expr)
  if not expr then return end
  local tname, ovreg = match(expr, "^([%w_]+):(@[%w_]+)$")
  local tp = map_type[tname or expr]
  if tp then
    local reg = ovreg or tp.reg
    local rnum = map_reg_num[reg]
    if not rnum then
      werror("type `"..(tname or expr).."' needs a register override")
    end
    if not map_reg_valid_base[reg] then
      werror("bad base register override `"..(map_reg_rev[reg] or reg).."'")
    end
    return reg, rnum, tp
  end
  return expr, map_reg_num[expr]
end

-- Parse operand and return { mode, opsize, reg, xreg, xsc, disp, imm }.
local function parseoperand(param, isqword)
  local t = {}

  local expr = param
  local opsize, tailops = match(param, "^(%w+)%s*(.+)$")
  if opsize then
    t.opsize = map_opsize[opsize]
    if t.opsize then expr = tailops end
  end

  local br = match(expr, "^%[%s*(.-)%s*%]$")
  repeat
    if br then
      t.mode = "xm"

      -- [disp]
      t.disp = toint(br)
      if t.disp then
	t.mode = x64 and "xm" or "xmO"
	break
      end

      -- [reg...]
      local tp
      local reg, tailr = match(br, "^([@%w_:]+)%s*(.*)$")
      reg, t.reg, tp = rtexpr(reg)
      if not t.reg then
	-- [expr]
	t.mode = x64 and "xm" or "xmO"
	t.disp = dispexpr("+"..br)
	break
      end

      if t.reg == -1 then
	t.vreg, tailr = match(tailr, "^(%b())(.*)$")
	if not t.vreg then werror("bad variable register expression") end
      end

      -- [xreg*xsc] or [xreg*xsc+-disp] or [xreg*xsc+-expr]
      local xsc, tailsc = match(tailr, "^%*%s*([1248])%s*(.*)$")
      if xsc then
	if not map_reg_valid_index[reg] then
	  werror("bad index register `"..map_reg_rev[reg].."'")
	end
	t.xsc = map_xsc[xsc]
	t.xreg = t.reg
	t.vxreg = t.vreg
	t.reg = nil
	t.vreg = nil
	t.disp = dispexpr(tailsc)
	break
      end
      if not map_reg_valid_base[reg] then
	werror("bad base register `"..map_reg_rev[reg].."'")
      end

      -- [reg] or [reg+-disp]
      t.disp = toint(tailr) or (tailr == "" and 0)
      if t.disp then break end

      -- [reg+xreg...]
      local xreg, tailx = match(tailr, "^%+%s*([@%w_:]+)%s*(.*)$")
      xreg, t.xreg, tp = rtexpr(xreg)
      if not t.xreg then
	-- [reg+-expr]
	t.disp = dispexpr(tailr)
	break
      end
      if not map_reg_valid_index[xreg] then
	werror("bad index register `"..map_reg_rev[xreg].."'")
      end

      if t.xreg == -1 then
	t.vxreg, tailx = match(tailx, "^(%b())(.*)$")
	if not t.vxreg then werror("bad variable register expression") end
      end

      -- [reg+xreg*xsc...]
      local xsc, tailsc = match(tailx, "^%*%s*([1248])%s*(.*)$")
      if xsc then
	t.xsc = map_xsc[xsc]
	tailx = tailsc
      end

      -- [...] or [...+-disp] or [...+-expr]
      t.disp = dispexpr(tailx)
    else
      -- imm or opsize*imm
      local imm = toint(expr, isqword)
      if not imm and sub(expr, 1, 1) == "*" and t.opsize then
	imm = toint(sub(expr, 2))
	if imm then
	  imm = imm * map_opsizenum[t.opsize]
	  t.opsize = nil
	end
      end
      if imm then
	if t.opsize then werror("bad operand size override") end
	local m = "i"
	if imm == 1 then m = m.."1" end
	if imm >= 4294967168 and imm <= 4294967295 then imm = imm-4294967296 end
	if imm >= -128 and imm <= 127 then m = m.."S" end
	t.imm = imm
	t.mode = m
	break
      end

      local tp
      local reg, tailr = match(expr, "^([@%w_:]+)%s*(.*)$")
      reg, t.reg, tp = rtexpr(reg)
      if t.reg then
	if t.reg == -1 then
	  t.vreg, tailr = match(tailr, "^(%b())(.*)$")
	  if not t.vreg then werror("bad variable register expression") end
	end
	-- reg
	if tailr == "" then
	  if t.opsize then werror("bad operand size override") end
	  t.opsize = map_reg_opsize[reg]
	  if t.opsize == "f" then
	    t.mode = t.reg == 0 and "fF" or "f"
	  else
	    if reg == "@w4" or (x64 and reg == "@d4") then
	      wwarn("bad idea, try again with `"..(x64 and "rsp'" or "esp'"))
	    end
	    t.mode = t.reg == 0 and "rmR" or (reg == "@b1" and "rmC" or "rm")
	  end
	  t.needrex = map_reg_needrex[reg]
	  break
	end

	-- type[idx], type[idx].field, type->field -> [reg+offset_expr]
	if not tp then werror("bad operand `"..param.."'") end
	t.mode = "xm"
	t.disp = format(tp.ctypefmt, tailr)
      else
	t.mode, t.imm = immexpr(expr)
	if sub(t.mode, -1) == "J" then
	  if t.opsize and t.opsize ~= addrsize then
	    werror("bad operand size override")
	  end
	  t.opsize = addrsize
	end
      end
    end
  until true
  return t
end

------------------------------------------------------------------------------
-- x86 Template String Description
-- ===============================
--
-- Each template string is a list of [match:]pattern pairs,
-- separated by "|". The first match wins. No match means a
-- bad or unsupported combination of operand modes or sizes.
--
-- The match part and the ":" is omitted if the operation has
-- no operands. Otherwise the first N characters are matched
-- against the mode strings of each of the N operands.
--
-- The mode string for each operand type is (see parseoperand()):
--   Integer register: "rm", +"R" for eax, ax, al, +"C" for cl
--   FP register:      "f",  +"F" for st0
--   Index operand:    "xm", +"O" for [disp] (pure offset)
--   Immediate:        "i",  +"S" for signed 8 bit, +"1" for 1,
--                     +"I" for arg, +"P" for pointer
--   Any:              +"J" for valid jump targets
--
-- So a match character "m" (mixed) matches both an integer register
-- and an index operand (to be encoded with the ModRM/SIB scheme).
-- But "r" matches only a register and "x" only an index operand
-- (e.g. for FP memory access operations).
--
-- The operand size match string starts right after the mode match
-- characters and ends before the ":". "dwb" or "qdwb" is assumed, if empty.
-- The effective data size of the operation is matched against this list.
--
-- If only the regular "b", "w", "d", "q", "t" operand sizes are
-- present, then all operands must be the same size. Unspecified sizes
-- are ignored, but at least one operand must have a size or the pattern
-- won't match (use the "byte", "word", "dword", "qword", "tword"
-- operand size overrides. E.g.: mov dword [eax], 1).
--
-- If the list has a "1" or "2" prefix, the operand size is taken
-- from the respective operand and any other operand sizes are ignored.
-- If the list contains only ".", all operand sizes are ignored.
-- If the list has a "/" prefix, the concatenated (mixed) operand sizes
-- are compared to the match.
--
-- E.g. "rrdw" matches for either two dword registers or two word
-- registers. "Fx2dq" matches an st0 operand plus an index operand
-- pointing to a dword (float) or qword (double).
--
-- Every character after the ":" is part of the pattern string:
--   Hex chars are accumulated to form the opcode (left to right).
--   "n"       disables the standard opcode mods
--             (otherwise: -1 for "b", o16 prefix for "w", rex.w for "q")
--   "X"       Force REX.W.
--   "r"/"R"   adds the reg. number from the 1st/2nd operand to the opcode.
--   "m"/"M"   generates ModRM/SIB from the 1st/2nd operand.
--             The spare 3 bits are either filled with the last hex digit or
--             the result from a previous "r"/"R". The opcode is restored.
--   "u"       Use VEX encoding, vvvv unused.
--   "v"/"V"   Use VEX encoding, vvvv from 1st/2nd operand (the operand is
--             removed from the list used by future characters).
--   "w"       Use VEX encoding, vvvv from 3rd operand.
--   "L"       Force VEX.L
--
-- All of the following characters force a flush of the opcode:
--   "o"/"O"   stores a pure 32 bit disp (offset) from the 1st/2nd operand.
--   "s"       stores a 4 bit immediate from the last register operand,
--             followed by 4 zero bits.
--   "S"       stores a signed 8 bit immediate from the last operand.
--   "U"       stores an unsigned 8 bit immediate from the last operand.
--   "W"       stores an unsigned 16 bit immediate from the last operand.
--   "i"       stores an operand sized immediate from the last operand.
--   "I"       dito, but generates an action code to optionally modify
--             the opcode (+2) for a signed 8 bit immediate.
--   "J"       generates one of the REL action codes from the last operand.
--
------------------------------------------------------------------------------

-- Template strings for x86 instructions. Ordered by first opcode byte.
-- Unimplemented opcodes (deliberate omissions) are marked with *.
local map_op = {
  -- 00-05: add...
  -- 06: *push es
  -- 07: *pop es
  -- 08-0D: or...
  -- 0E: *push cs
  -- 0F: two byte opcode prefix
  -- 10-15: adc...
  -- 16: *push ss
  -- 17: *pop ss
  -- 18-1D: sbb...
  -- 1E: *push ds
  -- 1F: *pop ds
  -- 20-25: and...
  es_0 =	"26",
  -- 27: *daa
  -- 28-2D: sub...
  cs_0 =	"2E",
  -- 2F: *das
  -- 30-35: xor...
  ss_0 =	"36",
  -- 37: *aaa
  -- 38-3D: cmp...
  ds_0 =	"3E",
  -- 3F: *aas
  inc_1 =	x64 and "m:FF0m" or "rdw:40r|m:FF0m",
  dec_1 =	x64 and "m:FF1m" or "rdw:48r|m:FF1m",
  push_1 =	(x64 and "rq:n50r|rw:50r|mq:nFF6m|mw:FF6m" or
			 "rdw:50r|mdw:FF6m").."|S.:6AS|ib:n6Ai|i.:68i",
  pop_1 =	x64 and "rq:n58r|rw:58r|mq:n8F0m|mw:8F0m" or "rdw:58r|mdw:8F0m",
  -- 60: *pusha, *pushad, *pushaw
  -- 61: *popa, *popad, *popaw
  -- 62: *bound rdw,x
  -- 63: x86: *arpl mw,rw
  movsxd_2 =	x64 and "rm/qd:63rM",
  fs_0 =	"64",
  gs_0 =	"65",
  o16_0 =	"66",
  a16_0 =	not x64 and "67" or nil,
  a32_0 =	x64 and "67",
  -- 68: push idw
  -- 69: imul rdw,mdw,idw
  -- 6A: push ib
  -- 6B: imul rdw,mdw,S
  -- 6C: *insb
  -- 6D: *insd, *insw
  -- 6E: *outsb
  -- 6F: *outsd, *outsw
  -- 70-7F: jcc lb
  -- 80: add... mb,i
  -- 81: add... mdw,i
  -- 82: *undefined
  -- 83: add... mdw,S
  test_2 =	"mr:85Rm|rm:85rM|Ri:A9ri|mi:F70mi",
  -- 86: xchg rb,mb
  -- 87: xchg rdw,mdw
  -- 88: mov mb,r
  -- 89: mov mdw,r
  -- 8A: mov r,mb
  -- 8B: mov r,mdw
  -- 8C: *mov mdw,seg
  lea_2 =	"rx1dq:8DrM",
  -- 8E: *mov seg,mdw
  -- 8F: pop mdw
  nop_0 =	"90",
  xchg_2 =	"Rrqdw:90R|rRqdw:90r|rm:87rM|mr:87Rm",
  cbw_0 =	"6698",
  cwde_0 =	"98",
  cdqe_0 =	"4898",
  cwd_0 =	"6699",
  cdq_0 =	"99",
  cqo_0 =	"4899",
  -- 9A: *call iw:idw
  wait_0 =	"9B",
  fwait_0 =	"9B",
  pushf_0 =	"9C",
  pushfd_0 =	not x64 and "9C",
  pushfq_0 =	x64 and "9C",
  popf_0 =	"9D",
  popfd_0 =	not x64 and "9D",
  popfq_0 =	x64 and "9D",
  sahf_0 =	"9E",
  lahf_0 =	"9F",
  mov_2 =	"OR:A3o|RO:A1O|mr:89Rm|rm:8BrM|rib:nB0ri|ridw:B8ri|mi:C70mi",
  movsb_0 =	"A4",
  movsw_0 =	"66A5",
  movsd_0 =	"A5",
  cmpsb_0 =	"A6",
  cmpsw_0 =	"66A7",
  cmpsd_0 =	"A7",
  -- A8: test Rb,i
  -- A9: test Rdw,i
  stosb_0 =	"AA",
  stosw_0 =	"66AB",
  stosd_0 =	"AB",
  lodsb_0 =	"AC",
  lodsw_0 =	"66AD",
  lodsd_0 =	"AD",
  scasb_0 =	"AE",
  scasw_0 =	"66AF",
  scasd_0 =	"AF",
  -- B0-B7: mov rb,i
  -- B8-BF: mov rdw,i
  -- C0: rol... mb,i
  -- C1: rol... mdw,i
  ret_1 =	"i.:nC2W",
  ret_0 =	"C3",
  -- C4: *les rdw,mq
  -- C5: *lds rdw,mq
  -- C6: mov mb,i
  -- C7: mov mdw,i
  -- C8: *enter iw,ib
  leave_0 =	"C9",
  -- CA: *retf iw
  -- CB: *retf
  int3_0 =	"CC",
  int_1 =	"i.:nCDU",
  into_0 =	"CE",
  -- CF: *iret
  -- D0: rol... mb,1
  -- D1: rol... mdw,1
  -- D2: rol... mb,cl
  -- D3: rol... mb,cl
  -- D4: *aam ib
  -- D5: *aad ib
  -- D6: *salc
  -- D7: *xlat
  -- D8-DF: floating point ops
  -- E0: *loopne
  -- E1: *loope
  -- E2: *loop
  -- E3: *jcxz, *jecxz
  -- E4: *in Rb,ib
  -- E5: *in Rdw,ib
  -- E6: *out ib,Rb
  -- E7: *out ib,Rdw
  call_1 =	x64 and "mq:nFF2m|J.:E8nJ" or "md:FF2m|J.:E8J",
  jmp_1 =	x64 and "mq:nFF4m|J.:E9nJ" or "md:FF4m|J.:E9J", -- short: EB
  -- EA: *jmp iw:idw
  -- EB: jmp ib
  -- EC: *in Rb,dx
  -- ED: *in Rdw,dx
  -- EE: *out dx,Rb
  -- EF: *out dx,Rdw
  lock_0 =	"F0",
  int1_0 =	"F1",
  repne_0 =	"F2",
  repnz_0 =	"F2",
  rep_0 =	"F3",
  repe_0 =	"F3",
  repz_0 =	"F3",
  -- F4: *hlt
  cmc_0 =	"F5",
  -- F6: test... mb,i; div... mb
  -- F7: test... mdw,i; div... mdw
  clc_0 =	"F8",
  stc_0 =	"F9",
  -- FA: *cli
  cld_0 =	"FC",
  std_0 =	"FD",
  -- FE: inc... mb
  -- FF: inc... mdw

  -- misc ops
  not_1 =	"m:F72m",
  neg_1 =	"m:F73m",
  mul_1 =	"m:F74m",
  imul_1 =	"m:F75m",
  div_1 =	"m:F76m",
  idiv_1 =	"m:F77m",

  imul_2 =	"rmqdw:0FAFrM|rIqdw:69rmI|rSqdw:6BrmS|riqdw:69rmi",
  imul_3 =	"rmIqdw:69rMI|rmSqdw:6BrMS|rmiqdw:69rMi",

  movzx_2 =	"rm/db:0FB6rM|rm/qb:|rm/wb:0FB6rM|rm/dw:0FB7rM|rm/qw:",
  movsx_2 =	"rm/db:0FBErM|rm/qb:|rm/wb:0FBErM|rm/dw:0FBFrM|rm/qw:",

  bswap_1 =	"rqd:0FC8r",
  bsf_2 =	"rmqdw:0FBCrM",
  bsr_2 =	"rmqdw:0FBDrM",
  bt_2 =	"mrqdw:0FA3Rm|miqdw:0FBA4mU",
  btc_2 =	"mrqdw:0FBBRm|miqdw:0FBA7mU",
  btr_2 =	"mrqdw:0FB3Rm|miqdw:0FBA6mU",
  bts_2 =	"mrqdw:0FABRm|miqdw:0FBA5mU",

  shld_3 =	"mriqdw:0FA4RmU|mrC/qq:0FA5Rm|mrC/dd:|mrC/ww:",
  shrd_3 =	"mriqdw:0FACRmU|mrC/qq:0FADRm|mrC/dd:|mrC/ww:",

  rdtsc_0 =	"0F31", -- P1+
  rdpmc_0 =	"0F33", -- P6+
  cpuid_0 =	"0FA2", -- P1+

  -- floating point ops
  fst_1 =	"ff:DDD0r|xd:D92m|xq:nDD2m",
  fstp_1 =	"ff:DDD8r|xd:D93m|xq:nDD3m|xt:DB7m",
  fld_1 =	"ff:D9C0r|xd:D90m|xq:nDD0m|xt:DB5m",

  fpop_0 =	"DDD8", -- Alias for fstp st0.

  fist_1 =	"xw:nDF2m|xd:DB2m",
  fistp_1 =	"xw:nDF3m|xd:DB3m|xq:nDF7m",
  fild_1 =	"xw:nDF0m|xd:DB0m|xq:nDF5m",

  fxch_0 =	"D9C9",
  fxch_1 =	"ff:D9C8r",
  fxch_2 =	"fFf:D9C8r|Fff:D9C8R",

  fucom_1 =	"ff:DDE0r",
  fucom_2 =	"Fff:DDE0R",
  fucomp_1 =	"ff:DDE8r",
  fucomp_2 =	"Fff:DDE8R",
  fucomi_1 =	"ff:DBE8r", -- P6+
  fucomi_2 =	"Fff:DBE8R", -- P6+
  fucomip_1 =	"ff:DFE8r", -- P6+
  fucomip_2 =	"Fff:DFE8R", -- P6+
  fcomi_1 =	"ff:DBF0r", -- P6+
  fcomi_2 =	"Fff:DBF0R", -- P6+
  fcomip_1 =	"ff:DFF0r", -- P6+
  fcomip_2 =	"Fff:DFF0R", -- P6+
  fucompp_0 =	"DAE9",
  fcompp_0 =	"DED9",

  fldenv_1 =	"x.:D94m",
  fnstenv_1 =	"x.:D96m",
  fstenv_1 =	"x.:9BD96m",
  fldcw_1 =	"xw:nD95m",
  fstcw_1 =	"xw:n9BD97m",
  fnstcw_1 =	"xw:nD97m",
  fstsw_1 =	"Rw:n9BDFE0|xw:n9BDD7m",
  fnstsw_1 =	"Rw:nDFE0|xw:nDD7m",
  fclex_0 =	"9BDBE2",
  fnclex_0 =	"DBE2",

  fnop_0 =	"D9D0",
  -- D9D1-D9DF: unassigned

  fchs_0 =	"D9E0",
  fabs_0 =	"D9E1",
  -- D9E2: unassigned
  -- D9E3: unassigned
  ftst_0 =	"D9E4",
  fxam_0 =	"D9E5",
  -- D9E6: unassigned
  -- D9E7: unassigned
  fld1_0 =	"D9E8",
  fldl2t_0 =	"D9E9",
  fldl2e_0 =	"D9EA",
  fldpi_0 =	"D9EB",
  fldlg2_0 =	"D9EC",
  fldln2_0 =	"D9ED",
  fldz_0 =	"D9EE",
  -- D9EF: unassigned

  f2xm1_0 =	"D9F0",
  fyl2x_0 =	"D9F1",
  fptan_0 =	"D9F2",
  fpatan_0 =	"D9F3",
  fxtract_0 =	"D9F4",
  fprem1_0 =	"D9F5",
  fdecstp_0 =	"D9F6",
  fincstp_0 =	"D9F7",
  fprem_0 =	"D9F8",
  fyl2xp1_0 =	"D9F9",
  fsqrt_0 =	"D9FA",
  fsincos_0 =	"D9FB",
  frndint_0 =	"D9FC",
  fscale_0 =	"D9FD",
  fsin_0 =	"D9FE",
  fcos_0 =	"D9FF",

  -- SSE, SSE2
  andnpd_2 =	"rmo:660F55rM",
  andnps_2 =	"rmo:0F55rM",
  andpd_2 =	"rmo:660F54rM",
  andps_2 =	"rmo:0F54rM",
  clflush_1 =	"x.:0FAE7m",
  cmppd_3 =	"rmio:660FC2rMU",
  cmpps_3 =	"rmio:0FC2rMU",
  cmpsd_3 =	"rrio:F20FC2rMU|rxi/oq:",
  cmpss_3 =	"rrio:F30FC2rMU|rxi/od:",
  comisd_2 =	"rro:660F2FrM|rx/oq:",
  comiss_2 =	"rro:0F2FrM|rx/od:",
  cvtdq2pd_2 =	"rro:F30FE6rM|rx/oq:",
  cvtdq2ps_2 =	"rmo:0F5BrM",
  cvtpd2dq_2 =	"rmo:F20FE6rM",
  cvtpd2ps_2 =	"rmo:660F5ArM",
  cvtpi2pd_2 =	"rx/oq:660F2ArM",
  cvtpi2ps_2 =	"rx/oq:0F2ArM",
  cvtps2dq_2 =	"rmo:660F5BrM",
  cvtps2pd_2 =	"rro:0F5ArM|rx/oq:",
  cvtsd2si_2 =	"rr/do:F20F2DrM|rr/qo:|rx/dq:|rxq:",
  cvtsd2ss_2 =	"rro:F20F5ArM|rx/oq:",
  cvtsi2sd_2 =	"rm/od:F20F2ArM|rm/oq:F20F2ArXM",
  cvtsi2ss_2 =	"rm/od:F30F2ArM|rm/oq:F30F2ArXM",
  cvtss2sd_2 =	"rro:F30F5ArM|rx/od:",
  cvtss2si_2 =	"rr/do:F30F2DrM|rr/qo:|rxd:|rx/qd:",
  cvttpd2dq_2 =	"rmo:660FE6rM",
  cvttps2dq_2 =	"rmo:F30F5BrM",
  cvttsd2si_2 =	"rr/do:F20F2CrM|rr/qo:|rx/dq:|rxq:",
  cvttss2si_2 =	"rr/do:F30F2CrM|rr/qo:|rxd:|rx/qd:",
  fxsave_1 =	"x.:0FAE0m",
  fxrstor_1 =	"x.:0FAE1m",
  ldmxcsr_1 =	"xd:0FAE2m",
  lfence_0 =	"0FAEE8",
  maskmovdqu_2 = "rro:660FF7rM",
  mfence_0 =	"0FAEF0",
  movapd_2 =	"rmo:660F28rM|mro:660F29Rm",
  movaps_2 =	"rmo:0F28rM|mro:0F29Rm",
  movd_2 =	"rm/od:660F6ErM|rm/oq:660F6ErXM|mr/do:660F7ERm|mr/qo:",
  movdqa_2 =	"rmo:660F6FrM|mro:660F7FRm",
  movdqu_2 =	"rmo:F30F6FrM|mro:F30F7FRm",
  movhlps_2 =	"rro:0F12rM",
  movhpd_2 =	"rx/oq:660F16rM|xr/qo:n660F17Rm",
  movhps_2 =	"rx/oq:0F16rM|xr/qo:n0F17Rm",
  movlhps_2 =	"rro:0F16rM",
  movlpd_2 =	"rx/oq:660F12rM|xr/qo:n660F13Rm",
  movlps_2 =	"rx/oq:0F12rM|xr/qo:n0F13Rm",
  movmskpd_2 =	"rr/do:660F50rM",
  movmskps_2 =	"rr/do:0F50rM",
  movntdq_2 =	"xro:660FE7Rm",
  movnti_2 =	"xrqd:0FC3Rm",
  movntpd_2 =	"xro:660F2BRm",
  movntps_2 =	"xro:0F2BRm",
  movq_2 =	"rro:F30F7ErM|rx/oq:|xr/qo:n660FD6Rm",
  movsd_2 =	"rro:F20F10rM|rx/oq:|xr/qo:nF20F11Rm",
  movss_2 =	"rro:F30F10rM|rx/od:|xr/do:F30F11Rm",
  movupd_2 =	"rmo:660F10rM|mro:660F11Rm",
  movups_2 =	"rmo:0F10rM|mro:0F11Rm",
  orpd_2 =	"rmo:660F56rM",
  orps_2 =	"rmo:0F56rM",
  pause_0 =	"F390",
  pextrw_3 =	"rri/do:660FC5rMU|xri/wo:660F3A15nRmU", -- Mem op: SSE4.1 only.
  pinsrw_3 =	"rri/od:660FC4rMU|rxi/ow:",
  pmovmskb_2 =	"rr/do:660FD7rM",
  prefetchnta_1 = "xb:n0F180m",
  prefetcht0_1 = "xb:n0F181m",
  prefetcht1_1 = "xb:n0F182m",
  prefetcht2_1 = "xb:n0F183m",
  pshufd_3 =	"rmio:660F70rMU",
  pshufhw_3 =	"rmio:F30F70rMU",
  pshuflw_3 =	"rmio:F20F70rMU",
  pslld_2 =	"rmo:660FF2rM|rio:660F726mU",
  pslldq_2 =	"rio:660F737mU",
  psllq_2 =	"rmo:660FF3rM|rio:660F736mU",
  psllw_2 =	"rmo:660FF1rM|rio:660F716mU",
  psrad_2 =	"rmo:660FE2rM|rio:660F724mU",
  psraw_2 =	"rmo:660FE1rM|rio:660F714mU",
  psrld_2 =	"rmo:660FD2rM|rio:660F722mU",
  psrldq_2 =	"rio:660F733mU",
  psrlq_2 =	"rmo:660FD3rM|rio:660F732mU",
  psrlw_2 =	"rmo:660FD1rM|rio:660F712mU",
  rcpps_2 =	"rmo:0F53rM",
  rcpss_2 =	"rro:F30F53rM|rx/od:",
  rsqrtps_2 =	"rmo:0F52rM",
  rsqrtss_2 =	"rmo:F30F52rM",
  sfence_0 =	"0FAEF8",
  shufpd_3 =	"rmio:660FC6rMU",
  shufps_3 =	"rmio:0FC6rMU",
  stmxcsr_1 =   "xd:0FAE3m",
  ucomisd_2 =	"rro:660F2ErM|rx/oq:",
  ucomiss_2 =	"rro:0F2ErM|rx/od:",
  unpckhpd_2 =	"rmo:660F15rM",
  unpckhps_2 =	"rmo:0F15rM",
  unpcklpd_2 =	"rmo:660F14rM",
  unpcklps_2 =	"rmo:0F14rM",
  xorpd_2 =	"rmo:660F57rM",
  xorps_2 =	"rmo:0F57rM",

  -- SSE3 ops
  fisttp_1 =	"xw:nDF1m|xd:DB1m|xq:nDD1m",
  addsubpd_2 =	"rmo:660FD0rM",
  addsubps_2 =	"rmo:F20FD0rM",
  haddpd_2 =	"rmo:660F7CrM",
  haddps_2 =	"rmo:F20F7CrM",
  hsubpd_2 =	"rmo:660F7DrM",
  hsubps_2 =	"rmo:F20F7DrM",
  lddqu_2 =	"rxo:F20FF0rM",
  movddup_2 =	"rmo:F20F12rM",
  movshdup_2 =	"rmo:F30F16rM",
  movsldup_2 =	"rmo:F30F12rM",

  -- SSSE3 ops
  pabsb_2 =	"rmo:660F381CrM",
  pabsd_2 =	"rmo:660F381ErM",
  pabsw_2 =	"rmo:660F381DrM",
  palignr_3 =	"rmio:660F3A0FrMU",
  phaddd_2 =	"rmo:660F3802rM",
  phaddsw_2 =	"rmo:660F3803rM",
  phaddw_2 =	"rmo:660F3801rM",
  phsubd_2 =	"rmo:660F3806rM",
  phsubsw_2 =	"rmo:660F3807rM",
  phsubw_2 =	"rmo:660F3805rM",
  pmaddubsw_2 =	"rmo:660F3804rM",
  pmulhrsw_2 =	"rmo:660F380BrM",
  pshufb_2 =	"rmo:660F3800rM",
  psignb_2 =	"rmo:660F3808rM",
  psignd_2 =	"rmo:660F380ArM",
  psignw_2 =	"rmo:660F3809rM",

  -- SSE4.1 ops
  blendpd_3 =	"rmio:660F3A0DrMU",
  blendps_3 =	"rmio:660F3A0CrMU",
  blendvpd_3 =	"rmRo:660F3815rM",
  blendvps_3 =	"rmRo:660F3814rM",
  dppd_3 =	"rmio:660F3A41rMU",
  dpps_3 =	"rmio:660F3A40rMU",
  extractps_3 =	"mri/do:660F3A17RmU|rri/qo:660F3A17RXmU",
  insertps_3 =	"rrio:660F3A41rMU|rxi/od:",
  movntdqa_2 =	"rxo:660F382ArM",
  mpsadbw_3 =	"rmio:660F3A42rMU",
  packusdw_2 =	"rmo:660F382BrM",
  pblendvb_3 =	"rmRo:660F3810rM",
  pblendw_3 =	"rmio:660F3A0ErMU",
  pcmpeqq_2 =	"rmo:660F3829rM",
  pextrb_3 =	"rri/do:660F3A14nRmU|rri/qo:|xri/bo:",
  pextrd_3 =	"mri/do:660F3A16RmU",
  pextrq_3 =	"mri/qo:660F3A16RmU",
  -- pextrw is SSE2, mem operand is SSE4.1 only
  phminposuw_2 = "rmo:660F3841rM",
  pinsrb_3 =	"rri/od:660F3A20nrMU|rxi/ob:",
  pinsrd_3 =	"rmi/od:660F3A22rMU",
  pinsrq_3 =	"rmi/oq:660F3A22rXMU",
  pmaxsb_2 =	"rmo:660F383CrM",
  pmaxsd_2 =	"rmo:660F383DrM",
  pmaxud_2 =	"rmo:660F383FrM",
  pmaxuw_2 =	"rmo:660F383ErM",
  pminsb_2 =	"rmo:660F3838rM",
  pminsd_2 =	"rmo:660F3839rM",
  pminud_2 =	"rmo:660F383BrM",
  pminuw_2 =	"rmo:660F383ArM",
  pmovsxbd_2 =	"rro:660F3821rM|rx/od:",
  pmovsxbq_2 =	"rro:660F3822rM|rx/ow:",
  pmovsxbw_2 =	"rro:660F3820rM|rx/oq:",
  pmovsxdq_2 =	"rro:660F3825rM|rx/oq:",
  pmovsxwd_2 =	"rro:660F3823rM|rx/oq:",
  pmovsxwq_2 =	"rro:660F3824rM|rx/od:",
  pmovzxbd_2 =	"rro:660F3831rM|rx/od:",
  pmovzxbq_2 =	"rro:660F3832rM|rx/ow:",
  pmovzxbw_2 =	"rro:660F3830rM|rx/oq:",
  pmovzxdq_2 =	"rro:660F3835rM|rx/oq:",
  pmovzxwd_2 =	"rro:660F3833rM|rx/oq:",
  pmovzxwq_2 =	"rro:660F3834rM|rx/od:",
  pmuldq_2 =	"rmo:660F3828rM",
  pmulld_2 =	"rmo:660F3840rM",
  ptest_2 =	"rmo:660F3817rM",
  roundpd_3 =	"rmio:660F3A09rMU",
  roundps_3 =	"rmio:660F3A08rMU",
  roundsd_3 =	"rrio:660F3A0BrMU|rxi/oq:",
  roundss_3 =	"rrio:660F3A0ArMU|rxi/od:",

  -- SSE4.2 ops
  crc32_2 =	"rmqd:F20F38F1rM|rm/dw:66F20F38F1rM|rm/db:F20F38F0rM|rm/qb:",
  pcmpestri_3 =	"rmio:660F3A61rMU",
  pcmpestrm_3 =	"rmio:660F3A60rMU",
  pcmpgtq_2 =	"rmo:660F3837rM",
  pcmpistri_3 =	"rmio:660F3A63rMU",
  pcmpistrm_3 =	"rmio:660F3A62rMU",
  popcnt_2 =	"rmqdw:F30FB8rM",

  -- SSE4a
  extrq_2 =	"rro:660F79rM",
  extrq_3 =	"riio:660F780mUU",
  insertq_2 =	"rro:F20F79rM",
  insertq_4 =	"rriio:F20F78rMUU",
  lzcnt_2 =	"rmqdw:F30FBDrM",
  movntsd_2 =	"xr/qo:nF20F2BRm",
  movntss_2 =	"xr/do:F30F2BRm",
  -- popcnt is also in SSE4.2

  -- AES-NI
  aesdec_2 =	"rmo:660F38DErM",
  aesdeclast_2 = "rmo:660F38DFrM",
  aesenc_2 =	"rmo:660F38DCrM",
  aesenclast_2 = "rmo:660F38DDrM",
  aesimc_2 =	"rmo:660F38DBrM",
  aeskeygenassist_3 = "rmio:660F3ADFrMU",
  pclmulqdq_3 =	"rmio:660F3A44rMU",

   -- AVX FP ops
  vaddsubpd_3 =	"rrmoy:660FVD0rM",
  vaddsubps_3 =	"rrmoy:F20FVD0rM",
  vandpd_3 =	"rrmoy:660FV54rM",
  vandps_3 =	"rrmoy:0FV54rM",
  vandnpd_3 =	"rrmoy:660FV55rM",
  vandnps_3 =	"rrmoy:0FV55rM",
  vblendpd_4 =	"rrmioy:660F3AV0DrMU",
  vblendps_4 =	"rrmioy:660F3AV0CrMU",
  vblendvpd_4 =	"rrmroy:660F3AV4BrMs",
  vblendvps_4 =	"rrmroy:660F3AV4ArMs",
  vbroadcastf128_2 = "rx/yo:660F38u1ArM",
  vcmppd_4 =	"rrmioy:660FVC2rMU",
  vcmpps_4 =	"rrmioy:0FVC2rMU",
  vcmpsd_4 =	"rrrio:F20FVC2rMU|rrxi/ooq:",
  vcmpss_4 =	"rrrio:F30FVC2rMU|rrxi/ood:",
  vcomisd_2 =	"rro:660Fu2FrM|rx/oq:",
  vcomiss_2 =	"rro:0Fu2FrM|rx/od:",
  vcvtdq2pd_2 =	"rro:F30FuE6rM|rx/oq:|rm/yo:",
  vcvtdq2ps_2 =	"rmoy:0Fu5BrM",
  vcvtpd2dq_2 =	"rmoy:F20FuE6rM",
  vcvtpd2ps_2 =	"rmoy:660Fu5ArM",
  vcvtps2dq_2 =	"rmoy:660Fu5BrM",
  vcvtps2pd_2 =	"rro:0Fu5ArM|rx/oq:|rm/yo:",
  vcvtsd2si_2 =	"rr/do:F20Fu2DrM|rx/dq:|rr/qo:|rxq:",
  vcvtsd2ss_3 =	"rrro:F20FV5ArM|rrx/ooq:",
  vcvtsi2sd_3 =	"rrm/ood:F20FV2ArM|rrm/ooq:F20FVX2ArM",
  vcvtsi2ss_3 =	"rrm/ood:F30FV2ArM|rrm/ooq:F30FVX2ArM",
  vcvtss2sd_3 =	"rrro:F30FV5ArM|rrx/ood:",
  vcvtss2si_2 =	"rr/do:F30Fu2DrM|rxd:|rr/qo:|rx/qd:",
  vcvttpd2dq_2 = "rmo:660FuE6rM|rm/oy:660FuLE6rM",
  vcvttps2dq_2 = "rmoy:F30Fu5BrM",
  vcvttsd2si_2 = "rr/do:F20Fu2CrM|rx/dq:|rr/qo:|rxq:",
  vcvttss2si_2 = "rr/do:F30Fu2CrM|rxd:|rr/qo:|rx/qd:",
  vdppd_4 =	"rrmio:660F3AV41rMU",
  vdpps_4 =	"rrmioy:660F3AV40rMU",
  vextractf128_3 = "mri/oy:660F3AuL19RmU",
  vextractps_3 = "mri/do:660F3Au17RmU",
  vhaddpd_3 =	"rrmoy:660FV7CrM",
  vhaddps_3 =	"rrmoy:F20FV7CrM",
  vhsubpd_3 =	"rrmoy:660FV7DrM",
  vhsubps_3 =	"rrmoy:F20FV7DrM",
  vinsertf128_4 = "rrmi/yyo:660F3AV18rMU",
  vinsertps_4 =	"rrrio:660F3AV21rMU|rrxi/ood:",
  vldmxcsr_1 =	"xd:0FuAE2m",
  vmaskmovps_3 = "rrxoy:660F38V2CrM|xrroy:660F38V2ERm",
  vmaskmovpd_3 = "rrxoy:660F38V2DrM|xrroy:660F38V2FRm",
  vmovapd_2 =	"rmoy:660Fu28rM|mroy:660Fu29Rm",
  vmovaps_2 =	"rmoy:0Fu28rM|mroy:0Fu29Rm",
  vmovd_2 =	"rm/od:660Fu6ErM|rm/oq:660FuX6ErM|mr/do:660Fu7ERm|mr/qo:",
  vmovq_2 =	"rro:F30Fu7ErM|rx/oq:|xr/qo:660FuD6Rm",
  vmovddup_2 =	"rmy:F20Fu12rM|rro:|rx/oq:",
  vmovhlps_3 =	"rrro:0FV12rM",
  vmovhpd_2 =	"xr/qo:660Fu17Rm",
  vmovhpd_3 =	"rrx/ooq:660FV16rM",
  vmovhps_2 =	"xr/qo:0Fu17Rm",
  vmovhps_3 =	"rrx/ooq:0FV16rM",
  vmovlhps_3 =	"rrro:0FV16rM",
  vmovlpd_2 =	"xr/qo:660Fu13Rm",
  vmovlpd_3 =	"rrx/ooq:660FV12rM",
  vmovlps_2 =	"xr/qo:0Fu13Rm",
  vmovlps_3 =	"rrx/ooq:0FV12rM",
  vmovmskpd_2 =	"rr/do:660Fu50rM|rr/dy:660FuL50rM",
  vmovmskps_2 =	"rr/do:0Fu50rM|rr/dy:0FuL50rM",
  vmovntpd_2 =	"xroy:660Fu2BRm",
  vmovntps_2 =	"xroy:0Fu2BRm",
  vmovsd_2 =	"rx/oq:F20Fu10rM|xr/qo:F20Fu11Rm",
  vmovsd_3 =	"rrro:F20FV10rM",
  vmovshdup_2 =	"rmoy:F30Fu16rM",
  vmovsldup_2 =	"rmoy:F30Fu12rM",
  vmovss_2 =	"rx/od:F30Fu10rM|xr/do:F30Fu11Rm",
  vmovss_3 =	"rrro:F30FV10rM",
  vmovupd_2 =	"rmoy:660Fu10rM|mroy:660Fu11Rm",
  vmovups_2 =	"rmoy:0Fu10rM|mroy:0Fu11Rm",
  vorpd_3 =	"rrmoy:660FV56rM",
  vorps_3 =	"rrmoy:0FV56rM",
  vpermilpd_3 =	"rrmoy:660F38V0DrM|rmioy:660F3Au05rMU",
  vpermilps_3 =	"rrmoy:660F38V0CrM|rmioy:660F3Au04rMU",
  vperm2f128_4 = "rrmiy:660F3AV06rMU",
  vptestpd_2 =	"rmoy:660F38u0FrM",
  vptestps_2 =	"rmoy:660F38u0ErM",
  vrcpps_2 =	"rmoy:0Fu53rM",
  vrcpss_3 =	"rrro:F30FV53rM|rrx/ood:",
  vrsqrtps_2 =	"rmoy:0Fu52rM",
  vrsqrtss_3 =	"rrro:F30FV52rM|rrx/ood:",
  vroundpd_3 =	"rmioy:660F3Au09rMU",
  vroundps_3 =	"rmioy:660F3Au08rMU",
  vroundsd_4 =	"rrrio:660F3AV0BrMU|rrxi/ooq:",
  vroundss_4 =	"rrrio:660F3AV0ArMU|rrxi/ood:",
  vshufpd_4 =	"rrmioy:660FVC6rMU",
  vshufps_4 =	"rrmioy:0FVC6rMU",
  vsqrtps_2 =	"rmoy:0Fu51rM",
  vsqrtss_2 =	"rro:F30Fu51rM|rx/od:",
  vsqrtpd_2 =	"rmoy:660Fu51rM",
  vsqrtsd_2 =	"rro:F20Fu51rM|rx/oq:",
  vstmxcsr_1 =	"xd:0FuAE3m",
  vucomisd_2 =	"rro:660Fu2ErM|rx/oq:",
  vucomiss_2 =	"rro:0Fu2ErM|rx/od:",
  vunpckhpd_3 =	"rrmoy:660FV15rM",
  vunpckhps_3 =	"rrmoy:0FV15rM",
  vunpcklpd_3 =	"rrmoy:660FV14rM",
  vunpcklps_3 =	"rrmoy:0FV14rM",
  vxorpd_3 =	"rrmoy:660FV57rM",
  vxorps_3 =	"rrmoy:0FV57rM",
  vzeroall_0 =	"0FuL77",
  vzeroupper_0 = "0Fu77",

  -- AVX2 FP ops
  vbroadcastss_2 = "rx/od:660F38u18rM|rx/yd:|rro:|rr/yo:",
  vbroadcastsd_2 = "rx/yq:660F38u19rM|rr/yo:",
  -- *vgather* (!vsib)
  vpermpd_3 =	"rmiy:660F3AuX01rMU",
  vpermps_3 =	"rrmy:660F38V16rM",

  -- AVX, AVX2 integer ops
  -- In general, xmm requires AVX, ymm requires AVX2.
  vaesdec_3 =  "rrmo:660F38VDErM",
  vaesdeclast_3 = "rrmo:660F38VDFrM",
  vaesenc_3 =  "rrmo:660F38VDCrM",
  vaesenclast_3 = "rrmo:660F38VDDrM",
  vaesimc_2 =  "rmo:660F38uDBrM",
  vaeskeygenassist_3 = "rmio:660F3AuDFrMU",
  vlddqu_2 =	"rxoy:F20FuF0rM",
  vmaskmovdqu_2 = "rro:660FuF7rM",
  vmovdqa_2 =	"rmoy:660Fu6FrM|mroy:660Fu7FRm",
  vmovdqu_2 =	"rmoy:F30Fu6FrM|mroy:F30Fu7FRm",
  vmovntdq_2 =	"xroy:660FuE7Rm",
  vmovntdqa_2 =	"rxoy:660F38u2ArM",
  vmpsadbw_4 =	"rrmioy:660F3AV42rMU",
  vpabsb_2 =	"rmoy:660F38u1CrM",
  vpabsd_2 =	"rmoy:660F38u1ErM",
  vpabsw_2 =	"rmoy:660F38u1DrM",
  vpackusdw_3 =	"rrmoy:660F38V2BrM",
  vpalignr_4 =	"rrmioy:660F3AV0FrMU",
  vpblendvb_4 =	"rrmroy:660F3AV4CrMs",
  vpblendw_4 =	"rrmioy:660F3AV0ErMU",
  vpclmulqdq_4 = "rrmio:660F3AV44rMU",
  vpcmpeqq_3 =	"rrmoy:660F38V29rM",
  vpcmpestri_3 = "rmio:660F3Au61rMU",
  vpcmpestrm_3 = "rmio:660F3Au60rMU",
  vpcmpgtq_3 =	"rrmoy:660F38V37rM",
  vpcmpistri_3 = "rmio:660F3Au63rMU",
  vpcmpistrm_3 = "rmio:660F3Au62rMU",
  vpextrb_3 =	"rri/do:660F3Au14nRmU|rri/qo:|xri/bo:",
  vpextrw_3 =	"rri/do:660FuC5rMU|xri/wo:660F3Au15nRmU",
  vpextrd_3 =	"mri/do:660F3Au16RmU",
  vpextrq_3 =	"mri/qo:660F3Au16RmU",
  vphaddw_3 =	"rrmoy:660F38V01rM",
  vphaddd_3 =	"rrmoy:660F38V02rM",
  vphaddsw_3 =	"rrmoy:660F38V03rM",
  vphminposuw_2 = "rmo:660F38u41rM",
  vphsubw_3 =	"rrmoy:660F38V05rM",
  vphsubd_3 =	"rrmoy:660F38V06rM",
  vphsubsw_3 =	"rrmoy:660F38V07rM",
  vpinsrb_4 =	"rrri/ood:660F3AV20rMU|rrxi/oob:",
  vpinsrw_4 =	"rrri/ood:660FVC4rMU|rrxi/oow:",
  vpinsrd_4 =	"rrmi/ood:660F3AV22rMU",
  vpinsrq_4 =	"rrmi/ooq:660F3AVX22rMU",
  vpmaddubsw_3 = "rrmoy:660F38V04rM",
  vpmaxsb_3 =	"rrmoy:660F38V3CrM",
  vpmaxsd_3 =	"rrmoy:660F38V3DrM",
  vpmaxuw_3 =	"rrmoy:660F38V3ErM",
  vpmaxud_3 =	"rrmoy:660F38V3FrM",
  vpminsb_3 =	"rrmoy:660F38V38rM",
  vpminsd_3 =	"rrmoy:660F38V39rM",
  vpminuw_3 =	"rrmoy:660F38V3ArM",
  vpminud_3 =	"rrmoy:660F38V3BrM",
  vpmovmskb_2 =	"rr/do:660FuD7rM|rr/dy:660FuLD7rM",
  vpmovsxbw_2 =	"rroy:660F38u20rM|rx/oq:|rx/yo:",
  vpmovsxbd_2 =	"rroy:660F38u21rM|rx/od:|rx/yq:",
  vpmovsxbq_2 =	"rroy:660F38u22rM|rx/ow:|rx/yd:",
  vpmovsxwd_2 =	"rroy:660F38u23rM|rx/oq:|rx/yo:",
  vpmovsxwq_2 =	"rroy:660F38u24rM|rx/od:|rx/yq:",
  vpmovsxdq_2 =	"rroy:660F38u25rM|rx/oq:|rx/yo:",
  vpmovzxbw_2 =	"rroy:660F38u30rM|rx/oq:|rx/yo:",
  vpmovzxbd_2 =	"rroy:660F38u31rM|rx/od:|rx/yq:",
  vpmovzxbq_2 =	"rroy:660F38u32rM|rx/ow:|rx/yd:",
  vpmovzxwd_2 =	"rroy:660F38u33rM|rx/oq:|rx/yo:",
  vpmovzxwq_2 =	"rroy:660F38u34rM|rx/od:|rx/yq:",
  vpmovzxdq_2 =	"rroy:660F38u35rM|rx/oq:|rx/yo:",
  vpmuldq_3 =	"rrmoy:660F38V28rM",
  vpmulhrsw_3 =	"rrmoy:660F38V0BrM",
  vpmulld_3 =	"rrmoy:660F38V40rM",
  vpshufb_3 =	"rrmoy:660F38V00rM",
  vpshufd_3 =	"rmioy:660Fu70rMU",
  vpshufhw_3 =	"rmioy:F30Fu70rMU",
  vpshuflw_3 =	"rmioy:F20Fu70rMU",
  vpsignb_3 =	"rrmoy:660F38V08rM",
  vpsignw_3 =	"rrmoy:660F38V09rM",
  vpsignd_3 =	"rrmoy:660F38V0ArM",
  vpslldq_3 =	"rrioy:660Fv737mU",
  vpsllw_3 =	"rrmoy:660FVF1rM|rrioy:660Fv716mU",
  vpslld_3 =	"rrmoy:660FVF2rM|rrioy:660Fv726mU",
  vpsllq_3 =	"rrmoy:660FVF3rM|rrioy:660Fv736mU",
  vpsraw_3 =	"rrmoy:660FVE1rM|rrioy:660Fv714mU",
  vpsrad_3 =	"rrmoy:660FVE2rM|rrioy:660Fv724mU",
  vpsrldq_3 =	"rrioy:660Fv733mU",
  vpsrlw_3 =	"rrmoy:660FVD1rM|rrioy:660Fv712mU",
  vpsrld_3 =	"rrmoy:660FVD2rM|rrioy:660Fv722mU",
  vpsrlq_3 =	"rrmoy:660FVD3rM|rrioy:660Fv732mU",
  vptest_2 =	"rmoy:660F38u17rM",

  -- AVX2 integer ops
  vbroadcasti128_2 = "rx/yo:660F38u5ArM",
  vinserti128_4 = "rrmi/yyo:660F3AV38rMU",
  vextracti128_3 = "mri/oy:660F3AuL39RmU",
  vpblendd_4 =	"rrmioy:660F3AV02rMU",
  vpbroadcastb_2 = "rro:660F38u78rM|rx/ob:|rr/yo:|rx/yb:",
  vpbroadcastw_2 = "rro:660F38u79rM|rx/ow:|rr/yo:|rx/yw:",
  vpbroadcastd_2 = "rro:660F38u58rM|rx/od:|rr/yo:|rx/yd:",
  vpbroadcastq_2 = "rro:660F38u59rM|rx/oq:|rr/yo:|rx/yq:",
  vpermd_3 =	"rrmy:660F38V36rM",
  vpermq_3 =	"rmiy:660F3AuX00rMU",
  -- *vpgather* (!vsib)
  vperm2i128_4 = "rrmiy:660F3AV46rMU",
  vpmaskmovd_3 = "rrxoy:660F38V8CrM|xrroy:660F38V8ERm",
  vpmaskmovq_3 = "rrxoy:660F38VX8CrM|xrroy:660F38VX8ERm",
  vpsllvd_3 =	"rrmoy:660F38V47rM",
  vpsllvq_3 =	"rrmoy:660F38VX47rM",
  vpsravd_3 =	"rrmoy:660F38V46rM",
  vpsrlvd_3 =	"rrmoy:660F38V45rM",
  vpsrlvq_3 =	"rrmoy:660F38VX45rM",

  -- Intel ADX
  adcx_2 =	"rmqd:660F38F6rM",
  adox_2 =	"rmqd:F30F38F6rM",

  -- BMI1
  andn_3 =	"rrmqd:0F38VF2rM",
  bextr_3 =	"rmrqd:0F38wF7rM",
  blsi_2 =	"rmqd:0F38vF33m",
  blsmsk_2 =	"rmqd:0F38vF32m",
  blsr_2 =	"rmqd:0F38vF31m",
  tzcnt_2 =	"rmqdw:F30FBCrM",

  -- BMI2
  bzhi_3 =	"rmrqd:0F38wF5rM",
  mulx_3 =	"rrmqd:F20F38VF6rM",
  pdep_3 =	"rrmqd:F20F38VF5rM",
  pext_3 =	"rrmqd:F30F38VF5rM",
  rorx_3 =	"rmSqd:F20F3AuF0rMS",
  sarx_3 =	"rmrqd:F30F38wF7rM",
  shrx_3 =	"rmrqd:F20F38wF7rM",
  shlx_3 =	"rmrqd:660F38wF7rM",

  -- FMA3
  vfmaddsub132pd_3 = "rrmoy:660F38VX96rM",
  vfmaddsub132ps_3 = "rrmoy:660F38V96rM",
  vfmaddsub213pd_3 = "rrmoy:660F38VXA6rM",
  vfmaddsub213ps_3 = "rrmoy:660F38VA6rM",
  vfmaddsub231pd_3 = "rrmoy:660F38VXB6rM",
  vfmaddsub231ps_3 = "rrmoy:660F38VB6rM",

  vfmsubadd132pd_3 = "rrmoy:660F38VX97rM",
  vfmsubadd132ps_3 = "rrmoy:660F38V97rM",
  vfmsubadd213pd_3 = "rrmoy:660F38VXA7rM",
  vfmsubadd213ps_3 = "rrmoy:660F38VA7rM",
  vfmsubadd231pd_3 = "rrmoy:660F38VXB7rM",
  vfmsubadd231ps_3 = "rrmoy:660F38VB7rM",

  vfmadd132pd_3 = "rrmoy:660F38VX98rM",
  vfmadd132ps_3 = "rrmoy:660F38V98rM",
  vfmadd132sd_3 = "rrro:660F38VX99rM|rrx/ooq:",
  vfmadd132ss_3 = "rrro:660F38V99rM|rrx/ood:",
  vfmadd213pd_3 = "rrmoy:660F38VXA8rM",
  vfmadd213ps_3 = "rrmoy:660F38VA8rM",
  vfmadd213sd_3 = "rrro:660F38VXA9rM|rrx/ooq:",
  vfmadd213ss_3 = "rrro:660F38VA9rM|rrx/ood:",
  vfmadd231pd_3 = "rrmoy:660F38VXB8rM",
  vfmadd231ps_3 = "rrmoy:660F38VB8rM",
  vfmadd231sd_3 = "rrro:660F38VXB9rM|rrx/ooq:",
  vfmadd231ss_3 = "rrro:660F38VB9rM|rrx/ood:",

  vfmsub132pd_3 = "rrmoy:660F38VX9ArM",
  vfmsub132ps_3 = "rrmoy:660F38V9ArM",
  vfmsub132sd_3 = "rrro:660F38VX9BrM|rrx/ooq:",
  vfmsub132ss_3 = "rrro:660F38V9BrM|rrx/ood:",
  vfmsub213pd_3 = "rrmoy:660F38VXAArM",
  vfmsub213ps_3 = "rrmoy:660F38VAArM",
  vfmsub213sd_3 = "rrro:660F38VXABrM|rrx/ooq:",
  vfmsub213ss_3 = "rrro:660F38VABrM|rrx/ood:",
  vfmsub231pd_3 = "rrmoy:660F38VXBArM",
  vfmsub231ps_3 = "rrmoy:660F38VBArM",
  vfmsub231sd_3 = "rrro:660F38VXBBrM|rrx/ooq:",
  vfmsub231ss_3 = "rrro:660F38VBBrM|rrx/ood:",

  vfnmadd132pd_3 = "rrmoy:660F38VX9CrM",
  vfnmadd132ps_3 = "rrmoy:660F38V9CrM",
  vfnmadd132sd_3 = "rrro:660F38VX9DrM|rrx/ooq:",
  vfnmadd132ss_3 = "rrro:660F38V9DrM|rrx/ood:",
  vfnmadd213pd_3 = "rrmoy:660F38VXACrM",
  vfnmadd213ps_3 = "rrmoy:660F38VACrM",
  vfnmadd213sd_3 = "rrro:660F38VXADrM|rrx/ooq:",
  vfnmadd213ss_3 = "rrro:660F38VADrM|rrx/ood:",
  vfnmadd231pd_3 = "rrmoy:660F38VXBCrM",
  vfnmadd231ps_3 = "rrmoy:660F38VBCrM",
  vfnmadd231sd_3 = "rrro:660F38VXBDrM|rrx/ooq:",
  vfnmadd231ss_3 = "rrro:660F38VBDrM|rrx/ood:",

  vfnmsub132pd_3 = "rrmoy:660F38VX9ErM",
  vfnmsub132ps_3 = "rrmoy:660F38V9ErM",
  vfnmsub132sd_3 = "rrro:660F38VX9FrM|rrx/ooq:",
  vfnmsub132ss_3 = "rrro:660F38V9FrM|rrx/ood:",
  vfnmsub213pd_3 = "rrmoy:660F38VXAErM",
  vfnmsub213ps_3 = "rrmoy:660F38VAErM",
  vfnmsub213sd_3 = "rrro:660F38VXAFrM|rrx/ooq:",
  vfnmsub213ss_3 = "rrro:660F38VAFrM|rrx/ood:",
  vfnmsub231pd_3 = "rrmoy:660F38VXBErM",
  vfnmsub231ps_3 = "rrmoy:660F38VBErM",
  vfnmsub231sd_3 = "rrro:660F38VXBFrM|rrx/ooq:",
  vfnmsub231ss_3 = "rrro:660F38VBFrM|rrx/ood:",
}

------------------------------------------------------------------------------

-- Arithmetic ops.
for name,n in pairs{ add = 0, ["or"] = 1, adc = 2, sbb = 3,
		     ["and"] = 4, sub = 5, xor = 6, cmp = 7 } do
  local n8 = shl(n, 3)
  map_op[name.."_2"] = format(
    "mr:%02XRm|rm:%02XrM|mI1qdw:81%XmI|mS1qdw:83%XmS|Ri1qdwb:%02Xri|mi1qdwb:81%Xmi",
    1+n8, 3+n8, n, n, 5+n8, n)
end

-- Shift ops.
for name,n in pairs{ rol = 0, ror = 1, rcl = 2, rcr = 3,
		     shl = 4, shr = 5,          sar = 7, sal = 4 } do
  map_op[name.."_2"] = format("m1:D1%Xm|mC1qdwb:D3%Xm|mi:C1%XmU", n, n, n)
end

-- Conditional ops.
for cc,n in pairs(map_cc) do
  map_op["j"..cc.."_1"] = format("J.:n0F8%XJ", n) -- short: 7%X
  map_op["set"..cc.."_1"] = format("mb:n0F9%X2m", n)
  map_op["cmov"..cc.."_2"] = format("rmqdw:0F4%XrM", n) -- P6+
end

-- FP arithmetic ops.
for name,n in pairs{ add = 0, mul = 1, com = 2, comp = 3,
		     sub = 4, subr = 5, div = 6, divr = 7 } do
  local nc = 0xc0 + shl(n, 3)
  local nr = nc + (n < 4 and 0 or (n % 2 == 0 and 8 or -8))
  local fn = "f"..name
  map_op[fn.."_1"] = format("ff:D8%02Xr|xd:D8%Xm|xq:nDC%Xm", nc, n, n)
  if n == 2 or n == 3 then
    map_op[fn.."_2"] = format("Fff:D8%02XR|Fx2d:D8%XM|Fx2q:nDC%XM", nc, n, n)
  else
    map_op[fn.."_2"] = format("Fff:D8%02XR|fFf:DC%02Xr|Fx2d:D8%XM|Fx2q:nDC%XM", nc, nr, n, n)
    map_op[fn.."p_1"] = format("ff:DE%02Xr", nr)
    map_op[fn.."p_2"] = format("fFf:DE%02Xr", nr)
  end
  map_op["fi"..name.."_1"] = format("xd:DA%Xm|xw:nDE%Xm", n, n)
end

-- FP conditional moves.
for cc,n in pairs{ b=0, e=1, be=2, u=3, nb=4, ne=5, nbe=6, nu=7 } do
  local nc = 0xdac0 + shl(band(n, 3), 3) + shl(band(n, 4), 6)
  map_op["fcmov"..cc.."_1"] = format("ff:%04Xr", nc) -- P6+
  map_op["fcmov"..cc.."_2"] = format("Fff:%04XR", nc) -- P6+
end

-- SSE / AVX FP arithmetic ops.
for name,n in pairs{ sqrt = 1, add = 8, mul = 9,
		     sub = 12, min = 13, div = 14, max = 15 } do
  map_op[name.."ps_2"] = format("rmo:0F5%XrM", n)
  map_op[name.."ss_2"] = format("rro:F30F5%XrM|rx/od:", n)
  map_op[name.."pd_2"] = format("rmo:660F5%XrM", n)
  map_op[name.."sd_2"] = format("rro:F20F5%XrM|rx/oq:", n)
  if n ~= 1 then
    map_op["v"..name.."ps_3"] = format("rrmoy:0FV5%XrM", n)
    map_op["v"..name.."ss_3"] = format("rrro:F30FV5%XrM|rrx/ood:", n)
    map_op["v"..name.."pd_3"] = format("rrmoy:660FV5%XrM", n)
    map_op["v"..name.."sd_3"] = format("rrro:F20FV5%XrM|rrx/ooq:", n)
  end
end

-- SSE2 / AVX / AVX2 integer arithmetic ops (66 0F leaf).
for name,n in pairs{
  paddb = 0xFC, paddw = 0xFD, paddd = 0xFE, paddq = 0xD4,
  paddsb = 0xEC, paddsw = 0xED, packssdw = 0x6B,
  packsswb = 0x63, packuswb = 0x67, paddusb = 0xDC,
  paddusw = 0xDD, pand = 0xDB, pandn = 0xDF, pavgb = 0xE0,
  pavgw = 0xE3, pcmpeqb = 0x74, pcmpeqd = 0x76,
  pcmpeqw = 0x75, pcmpgtb = 0x64, pcmpgtd = 0x66,
  pcmpgtw = 0x65, pmaddwd = 0xF5, pmaxsw = 0xEE,
  pmaxub = 0xDE, pminsw = 0xEA, pminub = 0xDA,
  pmulhuw = 0xE4, pmulhw = 0xE5, pmullw = 0xD5,
  pmuludq = 0xF4, por = 0xEB, psadbw = 0xF6, psubb = 0xF8,
  psubw = 0xF9, psubd = 0xFA, psubq = 0xFB, psubsb = 0xE8,
  psubsw = 0xE9, psubusb = 0xD8, psubusw = 0xD9,
  punpckhbw = 0x68, punpckhwd = 0x69, punpckhdq = 0x6A,
  punpckhqdq = 0x6D, punpcklbw = 0x60, punpcklwd = 0x61,
  punpckldq = 0x62, punpcklqdq = 0x6C, pxor = 0xEF
} do
  map_op[name.."_2"] = format("rmo:660F%02XrM", n)
  map_op["v"..name.."_3"] = format("rrmoy:660FV%02XrM", n)
end

------------------------------------------------------------------------------

local map_vexarg = { u = false, v = 1, V = 2, w = 3 }

-- Process pattern string.
local function dopattern(pat, args, sz, op, needrex)
  local digit, addin, vex
  local opcode = 0
  local szov = sz
  local narg = 1
  local rex = 0

  -- Limit number of section buffer positions used by a single dasm_put().
  -- A single opcode needs a maximum of 6 positions.
  if secpos+6 > maxsecpos then wflush() end

  -- Process each character.
  for c in gmatch(pat.."|", ".") do
    if match(c, "%x") then	-- Hex digit.
      digit = byte(c) - 48
      if digit > 48 then digit = digit - 39
      elseif digit > 16 then digit = digit - 7 end
      opcode = opcode*16 + digit
      addin = nil
    elseif c == "n" then	-- Disable operand size mods for opcode.
      szov = nil
    elseif c == "X" then	-- Force REX.W.
      rex = 8
    elseif c == "L" then	-- Force VEX.L.
      vex.l = true
    elseif c == "r" then	-- Merge 1st operand regno. into opcode.
      addin = args[1]; opcode = opcode + (addin.reg % 8)
      if narg < 2 then narg = 2 end
    elseif c == "R" then	-- Merge 2nd operand regno. into opcode.
      addin = args[2]; opcode = opcode + (addin.reg % 8)
      narg = 3
    elseif c == "m" or c == "M" then	-- Encode ModRM/SIB.
      local s
      if addin then
	s = addin.reg
	opcode = opcode - band(s, 7)	-- Undo regno opcode merge.
      else
	s = band(opcode, 15)	-- Undo last digit.
	opcode = shr(opcode, 4)
      end
      local nn = c == "m" and 1 or 2
      local t = args[nn]
      if narg <= nn then narg = nn + 1 end
      if szov == "q" and rex == 0 then rex = rex + 8 end
      if t.reg and t.reg > 7 then rex = rex + 1 end
      if t.xreg and t.xreg > 7 then rex = rex + 2 end
      if s > 7 then rex = rex + 4 end
      if needrex then rex = rex + 16 end
      local psz, sk = wputop(szov, opcode, rex, vex, s < 0, t.vreg or t.vxreg)
      opcode = nil
      local imark = sub(pat, -1) -- Force a mark (ugly).
      -- Put ModRM/SIB with regno/last digit as spare.
      wputmrmsib(t, imark, s, addin and addin.vreg, psz, sk)
      addin = nil
    elseif map_vexarg[c] ~= nil then -- Encode using VEX prefix
      local b = band(opcode, 255); opcode = shr(opcode, 8)
      local m = 1
      if b == 0x38 then m = 2
      elseif b == 0x3a then m = 3 end
      if m ~= 1 then b = band(opcode, 255); opcode = shr(opcode, 8) end
      if b ~= 0x0f then
	werror("expected `0F', `0F38', or `0F3A' to precede `"..c..
	  "' in pattern `"..pat.."' for `"..op.."'")
      end
      local v = map_vexarg[c]
      if v then v = remove(args, v) end
      b = band(opcode, 255)
      local p = 0
      if b == 0x66 then p = 1
      elseif b == 0xf3 then p = 2
      elseif b == 0xf2 then p = 3 end
      if p ~= 0 then opcode = shr(opcode, 8) end
      if opcode ~= 0 then wputop(nil, opcode, 0); opcode = 0 end
      vex = { m = m, p = p, v = v }
    else
      if opcode then -- Flush opcode.
	if szov == "q" and rex == 0 then rex = rex + 8 end
	if needrex then rex = rex + 16 end
	if addin and addin.reg == -1 then
	  local psz, sk = wputop(szov, opcode - 7, rex, vex, true)
	  wvreg("opcode", addin.vreg, psz, sk)
	else
	  if addin and addin.reg > 7 then rex = rex + 1 end
	  wputop(szov, opcode, rex, vex)
	end
	opcode = nil
      end
      if c == "|" then break end
      if c == "o" then -- Offset (pure 32 bit displacement).
	wputdarg(args[1].disp); if narg < 2 then narg = 2 end
      elseif c == "O" then
	wputdarg(args[2].disp); narg = 3
      else
	-- Anything else is an immediate operand.
	local a = args[narg]
	narg = narg + 1
	local mode, imm = a.mode, a.imm
	if mode == "iJ" and not match(x64 and "J" or "iIJ", c) then
	  werror("bad operand size for label")
	end
	if c == "S" then
	  wputsbarg(imm)
	elseif c == "U" then
	  wputbarg(imm)
	elseif c == "W" then
	  wputwarg(imm)
	elseif c == "i" or c == "I" then
	  if mode == "iJ" then
	    wputlabel("IMM_", imm, 1)
	  elseif mode == "iI" and c == "I" then
	    waction(sz == "w" and "IMM_WB" or "IMM_DB", imm)
	  else
	    wputszarg(sz, imm)
	  end
	elseif c == "J" then
	  if mode == "iPJ" then
	    waction("REL_A", imm) -- !x64 (secpos)
	  else
	    wputlabel("REL_", imm, 2)
	  end
	elseif c == "s" then
	  local reg = a.reg
	  if reg < 0 then
	    wputb(0)
	    wvreg("imm.hi", a.vreg)
	  else
	    wputb(shl(reg, 4))
	  end
	else
	  werror("bad char `"..c.."' in pattern `"..pat.."' for `"..op.."'")
	end
      end
    end
  end
end

------------------------------------------------------------------------------

-- Mapping of operand modes to short names. Suppress output with '#'.
local map_modename = {
  r = "reg", R = "eax", C = "cl", x = "mem", m = "mrm", i = "imm",
  f = "stx", F = "st0", J = "lbl", ["1"] = "1",
  I = "#", S = "#", O = "#",
}

-- Return a table/string showing all possible operand modes.
local function templatehelp(template, nparams)
  if nparams == 0 then return "" end
  local t = {}
  for tm in gmatch(template, "[^%|]+") do
    local s = map_modename[sub(tm, 1, 1)]
    s = s..gsub(sub(tm, 2, nparams), ".", function(c)
      return ", "..map_modename[c]
    end)
    if not match(s, "#") then t[#t+1] = s end
  end
  return t
end

-- Match operand modes against mode match part of template.
local function matchtm(tm, args)
  for i=1,#args do
    if not match(args[i].mode, sub(tm, i, i)) then return end
  end
  return true
end

-- Handle opcodes defined with template strings.
map_op[".template__"] = function(params, template, nparams)
  if not params then return templatehelp(template, nparams) end
  local args = {}

  -- Zero-operand opcodes have no match part.
  if #params == 0 then
    dopattern(template, args, "d", params.op, nil)
    return
  end

  -- Determine common operand size (coerce undefined size) or flag as mixed.
  local sz, szmix, needrex
  for i,p in ipairs(params) do
    args[i] = parseoperand(p)
    local nsz = args[i].opsize
    if nsz then
      if sz and sz ~= nsz then szmix = true else sz = nsz end
    end
    local nrex = args[i].needrex
    if nrex ~= nil then
      if needrex == nil then
	needrex = nrex
      elseif needrex ~= nrex then
	werror("bad mix of byte-addressable registers")
      end
    end
  end

  -- Try all match:pattern pairs (separated by '|').
  local gotmatch, lastpat
  for tm in gmatch(template, "[^%|]+") do
    -- Split off size match (starts after mode match) and pattern string.
    local szm, pat = match(tm, "^(.-):(.*)$", #args+1)
    if pat == "" then pat = lastpat else lastpat = pat end
    if matchtm(tm, args) then
      local prefix = sub(szm, 1, 1)
      if prefix == "/" then -- Exactly match leading operand sizes.
	for i = #szm,1,-1 do
	  if i == 1 then
	    dopattern(pat, args, sz, params.op, needrex) -- Process pattern.
	    return
	  elseif args[i-1].opsize ~= sub(szm, i, i) then
	    break
	  end
	end
      else -- Match common operand size.
	local szp = sz
	if szm == "" then szm = x64 and "qdwb" or "dwb" end -- Default sizes.
	if prefix == "1" then szp = args[1].opsize; szmix = nil
	elseif prefix == "2" then szp = args[2].opsize; szmix = nil end
	if not szmix and (prefix == "." or match(szm, szp or "#")) then
	  dopattern(pat, args, szp, params.op, needrex) -- Process pattern.
	  return
	end
      end
      gotmatch = true
    end
  end

  local msg = "bad operand mode"
  if gotmatch then
    if szmix then
      msg = "mixed operand size"
    else
      msg = sz and "bad operand size" or "missing operand size"
    end
  end

  werror(msg.." in `"..opmodestr(params.op, args).."'")
end

------------------------------------------------------------------------------

-- x64-specific opcode for 64 bit immediates and displacements.
if x64 then
  function map_op.mov64_2(params)
    if not params then return { "reg, imm", "reg, [disp]", "[disp], reg" } end
    if secpos+2 > maxsecpos then wflush() end
    local opcode, op64, sz, rex, vreg
    local op64 = match(params[1], "^%[%s*(.-)%s*%]$")
    if op64 then
      local a = parseoperand(params[2])
      if a.mode ~= "rmR" then werror("bad operand mode") end
      sz = a.opsize
      rex = sz == "q" and 8 or 0
      opcode = 0xa3
    else
      op64 = match(params[2], "^%[%s*(.-)%s*%]$")
      local a = parseoperand(params[1])
      if op64 then
	if a.mode ~= "rmR" then werror("bad operand mode") end
	sz = a.opsize
	rex = sz == "q" and 8 or 0
	opcode = 0xa1
      else
	if sub(a.mode, 1, 1) ~= "r" or a.opsize ~= "q" then
	  werror("bad operand mode")
	end
	op64 = params[2]
	if a.reg == -1 then
	  vreg = a.vreg
	  opcode = 0xb8
	else
	  opcode = 0xb8 + band(a.reg, 7)
	end
	rex = a.reg > 7 and 9 or 8
      end
    end
    local psz, sk = wputop(sz, opcode, rex, nil, vreg)
    wvreg("opcode", vreg, psz, sk)
    waction("IMM_D", format("(unsigned int)(%s)", op64))
    waction("IMM_D", format("(unsigned int)((%s)>>32)", op64))
  end
end

------------------------------------------------------------------------------

-- Pseudo-opcodes for data storage.
local function op_data(params)
  if not params then return "imm..." end
  local sz = sub(params.op, 2, 2)
  if sz == "l" then sz = "d" elseif sz == "a" then sz = addrsize end
  for _,p in ipairs(params) do
    local a = parseoperand(p, sz == "q")
    if sub(a.mode, 1, 1) ~= "i" or (a.opsize and a.opsize ~= sz) then
      werror("bad mode or size in `"..p.."'")
    end
    if a.mode == "iJ" then
      wputlabel("IMM_", a.imm, 1)
    elseif sz == "q" then
      wputqarg(a.imm)
    else
      wputszarg(sz, a.imm)
    end
    if secpos+2 > maxsecpos then wflush() end
  end
end

map_op[".byte_*"] = op_data
map_op[".sbyte_*"] = op_data
map_op[".word_*"] = op_data
map_op[".dword_*"] = op_data
map_op[".qword_*"] = op_data
map_op[".aword_*"] = op_data
map_op[".long_*"] = op_data
map_op[".quad_*"] = op_data
map_op[".addr_*"] = op_data

------------------------------------------------------------------------------

-- Pseudo-opcode to mark the position where the action list is to be emitted.
map_op[".actionlist_1"] = function(params)
  if not params then return "cvar" end
  local name = params[1] -- No syntax check. You get to keep the pieces.
  wline(function(out) writeactions(out, name) end)
end

-- Pseudo-opcode to mark the position where the global enum is to be emitted.
map_op[".globals_1"] = function(params)
  if not params then return "prefix" end
  local prefix = params[1] -- No syntax check. You get to keep the pieces.
  wline(function(out) writeglobals(out, prefix) end)
end

-- Pseudo-opcode to mark the position where the global names are to be emitted.
map_op[".globalnames_1"] = function(params)
  if not params then return "cvar" end
  local name = params[1] -- No syntax check. You get to keep the pieces.
  wline(function(out) writeglobalnames(out, name) end)
end

-- Pseudo-opcode to mark the position where the extern names are to be emitted.
map_op[".externnames_1"] = function(params)
  if not params then return "cvar" end
  local name = params[1] -- No syntax check. You get to keep the pieces.
  wline(function(out) writeexternnames(out, name) end)
end

------------------------------------------------------------------------------

-- Label pseudo-opcode (converted from trailing colon form).
map_op[".label_2"] = function(params)
  if not params then return "[1-9] | ->global | =>pcexpr  [, addr]" end
  if secpos+2 > maxsecpos then wflush() end
  local a = parseoperand(params[1])
  local mode, imm = a.mode, a.imm
  if type(imm) == "number" and (mode == "iJ" or (imm >= 1 and imm <= 9)) then
    -- Local label (1: ... 9:) or global label (->global:).
    waction("LABEL_LG", nil, 1)
    wputxb(imm)
  elseif mode == "iJ" then
    -- PC label (=>pcexpr:).
    waction("LABEL_PC", imm)
  else
    werror("bad label definition")
  end
  -- SETLABEL must immediately follow LABEL_LG/LABEL_PC.
  local addr = params[2]
  if addr then
    local a = parseoperand(addr)
    if a.mode == "iPJ" then
      waction("SETLABEL", a.imm)
    else
      werror("bad label assignment")
    end
  end
end
map_op[".label_1"] = map_op[".label_2"]

------------------------------------------------------------------------------

-- Alignment pseudo-opcode.
map_op[".align_1"] = function(params)
  if not params then return "numpow2" end
  if secpos+1 > maxsecpos then wflush() end
  local align = tonumber(params[1]) or map_opsizenum[map_opsize[params[1]]]
  if align then
    local x = align
    -- Must be a power of 2 in the range (2 ... 256).
    for i=1,8 do
      x = x / 2
      if x == 1 then
	waction("ALIGN", nil, 1)
	wputxb(align-1) -- Action byte is 2**n-1.
	return
      end
    end
  end
  werror("bad alignment")
end

-- Spacing pseudo-opcode.
map_op[".space_2"] = function(params)
  if not params then return "num [, filler]" end
  if secpos+1 > maxsecpos then wflush() end
  waction("SPACE", params[1])
  local fill = params[2]
  if fill then
    fill = tonumber(fill)
    if not fill or fill < 0 or fill > 255 then werror("bad filler") end
  end
  wputxb(fill or 0)
end
map_op[".space_1"] = map_op[".space_2"]

------------------------------------------------------------------------------

-- Pseudo-opcode for (primitive) type definitions (map to C types).
map_op[".type_3"] = function(params, nparams)
  if not params then
    return nparams == 2 and "name, ctype" or "name, ctype, reg"
  end
  local name, ctype, reg = params[1], params[2], params[3]
  if not match(name, "^[%a_][%w_]*$") then
    werror("bad type name `"..name.."'")
  end
  local tp = map_type[name]
  if tp then
    werror("duplicate type `"..name.."'")
  end
  if reg and not map_reg_valid_base[reg] then
    werror("bad base register `"..(map_reg_rev[reg] or reg).."'")
  end
  -- Add #type to defines. A bit unclean to put it in map_archdef.
  map_archdef["#"..name] = "sizeof("..ctype..")"
  -- Add new type and emit shortcut define.
  local num = ctypenum + 1
  map_type[name] = {
    ctype = ctype,
    ctypefmt = format("Dt%X(%%s)", num),
    reg = reg,
  }
  wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
  ctypenum = num
end
map_op[".type_2"] = map_op[".type_3"]

-- Dump type definitions.
local function dumptypes(out, lvl)
  local t = {}
  for name in pairs(map_type) do t[#t+1] = name end
  sort(t)
  out:write("Type definitions:\n")
  for _,name in ipairs(t) do
    local tp = map_type[name]
    local reg = tp.reg and map_reg_rev[tp.reg] or ""
    out:write(format("  %-20s %-20s %s\n", name, tp.ctype, reg))
  end
  out:write("\n")
end

------------------------------------------------------------------------------

-- Set the current section.
function _M.section(num)
  waction("SECTION")
  wputxb(num)
  wflush(true) -- SECTION is a terminal action.
end

------------------------------------------------------------------------------

-- Dump architecture description.
function _M.dumparch(out)
  out:write(format("DynASM %s version %s, released %s\n\n",
    _info.arch, _info.version, _info.release))
  dumpregs(out)
  dumpactions(out)
end

-- Dump all user defined elements.
function _M.dumpdef(out, lvl)
  dumptypes(out, lvl)
  dumpglobals(out, lvl)
  dumpexterns(out, lvl)
end

------------------------------------------------------------------------------

-- Pass callbacks from/to the DynASM core.
function _M.passcb(wl, we, wf, ww)
  wline, werror, wfatal, wwarn = wl, we, wf, ww
  return wflush
end

-- Setup the arch-specific module.
function _M.setup(arch, opt)
  g_arch, g_opt = arch, opt
end

-- Merge the core maps and the arch-specific maps.
function _M.mergemaps(map_coreop, map_def)
  setmetatable(map_op, { __index = map_coreop })
  setmetatable(map_def, { __index = map_archdef })
  return map_op, map_def
end

return _M

------------------------------------------------------------------------------