From dee73f516f0da49e930dcfa1dd61720dcb69b7dd Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 31 Aug 2023 11:18:32 +0200 Subject: [PATCH] Add s390x architecture support to DynASM s390x (IBM Z) is an architecture of server computers produced by IBM. It is supported by a number of open source code generators, such as GCC, LLVM, OpenJDK, eBPF, QEMU, Valgrind and Cranelift. One of the missing pieces in the ecosystem support is LuaJIT. The s390x support for LuaJIT was initially developed by @ketank-new, @mundaym and @niravthakkar. It found its way into moonjit and luajit2 forks, as well as Fedora distro (as a patch). There were also smaller contributions by @preetikhorjuvenkar, @Bisht13, @velemas, @AlekseiNikiforovIBM, and @iii-i. This is a cumulative patch of the DynASM changes from this work. It contains all the contributions squashed together, plus minor stylistic cleanups. Signed-off-by: Ilya Leoshkevich --- dynasm/dasm_s390x.h | 546 ++++++++++++++ dynasm/dasm_s390x.lua | 1634 +++++++++++++++++++++++++++++++++++++++++ src/host/buildvm.c | 2 + 3 files changed, 2182 insertions(+) create mode 100644 dynasm/dasm_s390x.h create mode 100644 dynasm/dasm_s390x.lua diff --git a/dynasm/dasm_s390x.h b/dynasm/dasm_s390x.h new file mode 100644 index 00000000..a35a02ac --- /dev/null +++ b/dynasm/dasm_s390x.h @@ -0,0 +1,546 @@ +/* +** DynASM s390x encoding engine. +** Copyright (C) 2005-2016 Mike Pall. All rights reserved. +** Released under the MIT license. See dynasm.lua for full copyright notice. +*/ + +#include +#include +#include +#include + +#define DASM_ARCH "s390x" + +#ifndef DASM_EXTERN +#define DASM_EXTERN(a,b,c,d) 0 +#endif + +/* Action definitions. */ +enum { + DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT, + /* The following actions need a buffer position. */ + DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG, + /* The following actions also have an argument. */ + DASM_REL_PC, DASM_LABEL_PC, + DASM_DISP12, DASM_DISP20, + DASM_IMM8, DASM_IMM16, DASM_IMM32, + DASM_LEN8R,DASM_LEN4HR,DASM_LEN4LR, + DASM__MAX +}; + +/* Maximum number of section buffer positions for a single dasm_put() call. */ +#define DASM_MAXSECPOS 25 + +/* DynASM encoder status codes. Action list offset or number are or'ed in. */ +#define DASM_S_OK 0x00000000 +#define DASM_S_NOMEM 0x01000000 +#define DASM_S_PHASE 0x02000000 +#define DASM_S_MATCH_SEC 0x03000000 +#define DASM_S_RANGE_I 0x11000000 +#define DASM_S_RANGE_SEC 0x12000000 +#define DASM_S_RANGE_LG 0x13000000 +#define DASM_S_RANGE_PC 0x14000000 +#define DASM_S_RANGE_REL 0x15000000 +#define DASM_S_UNDEF_LG 0x21000000 +#define DASM_S_UNDEF_PC 0x22000000 + +/* Macros to convert positions (8 bit section + 24 bit index). */ +#define DASM_POS2IDX(pos) ((pos)&0x00ffffff) +#define DASM_POS2BIAS(pos) ((pos)&0xff000000) +#define DASM_SEC2POS(sec) ((sec)<<24) +#define DASM_POS2SEC(pos) ((pos)>>24) +#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) + +/* Action list type. */ +typedef const unsigned short *dasm_ActList; + +/* Per-section structure. */ +typedef struct dasm_Section { + int *rbuf; /* Biased buffer pointer (negative section bias). */ + int *buf; /* True buffer pointer. */ + size_t bsize; /* Buffer size in bytes. */ + int pos; /* Biased buffer position. */ + int epos; /* End of biased buffer position - max single put. */ + int ofs; /* Byte offset into section. */ +} dasm_Section; + +/* Core structure holding the DynASM encoding state. */ +struct dasm_State { + size_t psize; /* Allocated size of this structure. */ + dasm_ActList actionlist; /* Current actionlist pointer. */ + int *lglabels; /* Local/global chain/pos ptrs. */ + size_t lgsize; + int *pclabels; /* PC label chains/pos ptrs. */ + size_t pcsize; + void **globals; /* Array of globals (bias -10). */ + dasm_Section *section; /* Pointer to active section. */ + size_t codesize; /* Total size of all code sections. */ + int maxsection; /* 0 <= sectionidx < maxsection. */ + int status; /* Status code. */ + dasm_Section sections[1]; /* All sections. Alloc-extended. */ +}; + +/* The size of the core structure depends on the max. number of sections. */ +#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) + + +/* Initialize DynASM state. */ +void dasm_init(Dst_DECL, int maxsection) +{ + dasm_State *D; + size_t psz = 0; + int i; + Dst_REF = NULL; + DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); + D = Dst_REF; + D->psize = psz; + D->lglabels = NULL; + D->lgsize = 0; + D->pclabels = NULL; + D->pcsize = 0; + D->globals = NULL; + D->maxsection = maxsection; + for (i = 0; i < maxsection; i++) { + D->sections[i].buf = NULL; /* Need this for pass3. */ + D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); + D->sections[i].bsize = 0; + D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ + } +} + +/* Free DynASM state. */ +void dasm_free(Dst_DECL) +{ + dasm_State *D = Dst_REF; + int i; + for (i = 0; i < D->maxsection; i++) + if (D->sections[i].buf) + DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); + if (D->pclabels) + DASM_M_FREE(Dst, D->pclabels, D->pcsize); + if (D->lglabels) + DASM_M_FREE(Dst, D->lglabels, D->lgsize); + DASM_M_FREE(Dst, D, D->psize); +} + +/* Setup global label array. Must be called before dasm_setup(). */ +void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) +{ + dasm_State *D = Dst_REF; + D->globals = gl - 10; /* Negative bias to compensate for locals. */ + DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10 + maxgl) * sizeof(int)); +} + +/* Grow PC label array. Can be called after dasm_setup(), too. */ +void dasm_growpc(Dst_DECL, unsigned int maxpc) +{ + dasm_State *D = Dst_REF; + size_t osz = D->pcsize; + DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc * sizeof(int)); + memset((void *)(((unsigned char *)D->pclabels) + osz), 0, D->pcsize - osz); +} + +/* Setup encoder. */ +void dasm_setup(Dst_DECL, const void *actionlist) +{ + dasm_State *D = Dst_REF; + int i; + D->actionlist = (dasm_ActList) actionlist; + D->status = DASM_S_OK; + D->section = &D->sections[0]; + memset((void *)D->lglabels, 0, D->lgsize); + if (D->pclabels) + memset((void *)D->pclabels, 0, D->pcsize); + for (i = 0; i < D->maxsection; i++) { + D->sections[i].pos = DASM_SEC2POS(i); + D->sections[i].ofs = 0; + } +} + + +#ifdef DASM_CHECKS +#define CK(x, st) \ + do { if (!(x)) { \ + D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0) +#define CKPL(kind, st) \ + do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ + D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0) +#else +#define CK(x, st) ((void)0) +#define CKPL(kind, st) ((void)0) +#endif + +/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ +void dasm_put(Dst_DECL, int start, ...) +{ + va_list ap; + dasm_State *D = Dst_REF; + dasm_ActList p = D->actionlist + start; + dasm_Section *sec = D->section; + int pos = sec->pos, ofs = sec->ofs; + int *b; + + if (pos >= sec->epos) { + DASM_M_GROW(Dst, int, sec->buf, sec->bsize, + sec->bsize + 2 * DASM_MAXSECPOS * sizeof(int)); + sec->rbuf = sec->buf - DASM_POS2BIAS(pos); + sec->epos = + (int)sec->bsize / sizeof(int) - DASM_MAXSECPOS + DASM_POS2BIAS(pos); + } + + b = sec->rbuf; + b[pos++] = start; + + va_start(ap, start); + while (1) { + unsigned short ins = *p++; + unsigned short action = ins; + if (action >= DASM__MAX) { + ofs += 2; + continue; + } + + int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0; + switch (action) { + case DASM_STOP: + goto stop; + case DASM_SECTION: + n = *p++ & 255; + CK(n < D->maxsection, RANGE_SEC); + D->section = &D->sections[n]; + goto stop; + case DASM_ESC: + p++; + ofs += 2; + break; + case DASM_REL_EXT: + p++; + ofs += 4; + break; + case DASM_ALIGN: + ofs += *p++; + b[pos++] = ofs; + break; + case DASM_REL_LG: + if (p[-2] >> 12 == 0xc) { /* RIL instruction needs 32-bit immediate. */ + ofs += 2; + } + n = *p++ - 10; + pl = D->lglabels + n; + /* Bkwd rel or global. */ + if (n >= 0) { + CK(n >= 10 || *pl < 0, RANGE_LG); + CKPL(lg, LG); + goto putrel; + } + pl += 10; + n = *pl; + if (n < 0) + n = 0; /* Start new chain for fwd rel if label exists. */ + goto linkrel; + case DASM_REL_PC: + if (p[-2] >> 12 == 0xc) { /* RIL instruction needs 32-bit immediate. */ + ofs += 2; + } + pl = D->pclabels + n; + CKPL(pc, PC); + putrel: + n = *pl; + if (n < 0) { /* Label exists. Get label pos and store it. */ + b[pos] = -n; + } else { + linkrel: + b[pos] = n; /* Else link to rel chain, anchored at label. */ + *pl = pos; + } + ofs += 2; + pos++; + break; + case DASM_LABEL_LG: + pl = D->lglabels + *p++ - 10; + CKPL(lg, LG); + goto putlabel; + case DASM_LABEL_PC: + pl = D->pclabels + n; + CKPL(pc, PC); + putlabel: + n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ + while (n > 0) { + int *pb = DASM_POS2PTR(D, n); + n = *pb; + *pb = pos; + } + *pl = -pos; /* Label exists now. */ + b[pos++] = ofs; /* Store pass1 offset estimate. */ + break; + case DASM_IMM8: + b[pos++] = n; + break; + case DASM_IMM16: + CK(((short)n) == n || ((unsigned short)n) == n, RANGE_I); /* TODO: is this the right way to handle unsigned immediates? */ + ofs += 2; + b[pos++] = n; + break; + case DASM_IMM32: + ofs += 4; + b[pos++] = n; + break; + case DASM_DISP20: + CK(-(1 << 19) <= n && n < (1 << 19), RANGE_I); + b[pos++] = n; + break; + case DASM_DISP12: + CK((n >> 12) == 0, RANGE_I); + b[pos++] = n; + break; + case DASM_LEN8R: + CK(n >= 1 && n <= 256, RANGE_I); + b[pos++] = n; + break; + case DASM_LEN4HR: + case DASM_LEN4LR: + CK(n >= 1 && n <= 128, RANGE_I); + b[pos++] = n; + break; + } + } +stop: + va_end(ap); + sec->pos = pos; + sec->ofs = ofs; +} + +#undef CK + +/* Pass 2: Link sections, shrink aligns, fix label offsets. */ +int dasm_link(Dst_DECL, size_t * szp) +{ + dasm_State *D = Dst_REF; + int secnum; + int ofs = 0; + +#ifdef DASM_CHECKS + *szp = 0; + if (D->status != DASM_S_OK) + return D->status; + { + int pc; + for (pc = 0; pc * sizeof(int) < D->pcsize; pc++) + if (D->pclabels[pc] > 0) + return DASM_S_UNDEF_PC | pc; + } +#endif + + { /* Handle globals not defined in this translation unit. */ + int idx; + for (idx = 20; idx * sizeof(int) < D->lgsize; idx++) { + int n = D->lglabels[idx]; + /* Undefined label: Collapse rel chain and replace with marker (< 0). */ + while (n > 0) { + int *pb = DASM_POS2PTR(D, n); + n = *pb; + *pb = -idx; + } + } + } + + /* Combine all code sections. No support for data sections (yet). */ + for (secnum = 0; secnum < D->maxsection; secnum++) { + dasm_Section *sec = D->sections + secnum; + int *b = sec->rbuf; + int pos = DASM_SEC2POS(secnum); + int lastpos = sec->pos; + + while (pos != lastpos) { + dasm_ActList p = D->actionlist + b[pos++]; + while (1) { + unsigned short ins = *p++; + unsigned short action = ins; + switch (action) { + case DASM_STOP: + case DASM_SECTION: + goto stop; + case DASM_ESC: + p++; + break; + case DASM_REL_EXT: + p++; + break; + case DASM_ALIGN: + ofs -= (b[pos++] + ofs) & *p++; + break; + case DASM_REL_LG: + case DASM_REL_PC: + p++; + pos++; + break; + case DASM_LABEL_LG: + case DASM_LABEL_PC: + p++; + b[pos++] += ofs; + break; + case DASM_IMM8: + case DASM_IMM16: + case DASM_IMM32: + case DASM_DISP20: + case DASM_DISP12: + case DASM_LEN8R: + case DASM_LEN4HR: + case DASM_LEN4LR: + pos++; + break; + } + } + stop:(void)0; + } + ofs += sec->ofs; /* Next section starts right after current section. */ + } + + D->codesize = ofs; /* Total size of all code sections */ + *szp = ofs; + return DASM_S_OK; +} + +#ifdef DASM_CHECKS +#define CK(x, st) \ + do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0) +#else +#define CK(x, st) ((void)0) +#endif + +/* Pass 3: Encode sections. */ +int dasm_encode(Dst_DECL, void *buffer) +{ + dasm_State *D = Dst_REF; + char *base = (char *)buffer; + unsigned short *cp = (unsigned short *)buffer; + int secnum; + + /* Encode all code sections. No support for data sections (yet). */ + for (secnum = 0; secnum < D->maxsection; secnum++) { + dasm_Section *sec = D->sections + secnum; + int *b = sec->buf; + int *endb = sec->rbuf + sec->pos; + + while (b != endb) { + dasm_ActList p = D->actionlist + *b++; + while (1) { + unsigned short ins = *p++; + unsigned short action = ins; + int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0; + switch (action) { + case DASM_STOP: + case DASM_SECTION: + goto stop; + case DASM_ESC: + *cp++ = *p++; + break; + case DASM_REL_EXT: + n = DASM_EXTERN(Dst, (unsigned char *)cp, *p++, 1) - 4; + goto patchrel; + case DASM_ALIGN: + ins = *p++; + /* TODO: emit 4-byte noprs instead of 2-byte nops where possible. */ + while ((((char *)cp - base) & ins)) + *cp++ = 0x0700; /* nop */ + break; + case DASM_REL_LG: + CK(n >= 0, UNDEF_LG); + case DASM_REL_PC: + CK(n >= 0, UNDEF_PC); + n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base); + p++; /* skip argument */ + patchrel: + /* Offsets are halfword aligned (so need to be halved). */ + n += 2; /* Offset is relative to start of instruction. */ + if (cp[-1] >> 12 == 0xc) { + *cp++ = n >> 17; + } else { + CK(-(1 << 16) <= n && n < (1 << 16) && (n & 1) == 0, RANGE_LG); + } + *cp++ = n >> 1; + break; + case DASM_LABEL_LG: + ins = *p++; + if (ins >= 20) + D->globals[ins - 10] = (void *)(base + n); + break; + case DASM_LABEL_PC: + break; + case DASM_IMM8: + cp[-1] |= n & 0xff; + break; + case DASM_IMM16: + *cp++ = n; + break; + case DASM_IMM32: + *cp++ = n >> 16; + *cp++ = n; + break; + case DASM_DISP20: + cp[-2] |= n & 0xfff; + cp[-1] |= (n >> 4) & 0xff00; + break; + case DASM_DISP12: + cp[-1] |= n & 0xfff; + break; + case DASM_LEN8R: + cp[-1] |= (n - 1) & 0xff; + break; + case DASM_LEN4HR: + cp[-1] |= ((n - 1) << 4) & 0xf0; + break; + case DASM_LEN4LR: + cp[-1] |= (n - 1) & 0x0f; + break; + default: + *cp++ = ins; + break; + } + } + stop:(void)0; + } + } + + if (base + D->codesize != (char *)cp) /* Check for phase errors. */ + return DASM_S_PHASE; + return DASM_S_OK; +} + +#undef CK + +/* Get PC label offset. */ +int dasm_getpclabel(Dst_DECL, unsigned int pc) +{ + dasm_State *D = Dst_REF; + if (pc * sizeof(int) < D->pcsize) { + int pos = D->pclabels[pc]; + if (pos < 0) + return *DASM_POS2PTR(D, -pos); + if (pos > 0) + return -1; /* Undefined. */ + } + return -2; /* Unused or out of range. */ +} + +#ifdef DASM_CHECKS +/* Optional sanity checker to call between isolated encoding steps. */ +int dasm_checkstep(Dst_DECL, int secmatch) +{ + dasm_State *D = Dst_REF; + if (D->status == DASM_S_OK) { + int i; + for (i = 1; i <= 9; i++) { + if (D->lglabels[i] > 0) { + D->status = DASM_S_UNDEF_LG | i; + break; + } + D->lglabels[i] = 0; + } + } + if (D->status == DASM_S_OK && secmatch >= 0 && + D->section != &D->sections[secmatch]) + D->status = DASM_S_MATCH_SEC | (D->section - D->sections); + return D->status; +} +#endif diff --git a/dynasm/dasm_s390x.lua b/dynasm/dasm_s390x.lua new file mode 100644 index 00000000..425dea56 --- /dev/null +++ b/dynasm/dasm_s390x.lua @@ -0,0 +1,1634 @@ +------------------------------------------------------------------------------ +-- DynASM s390x module. +-- +-- Copyright (C) 2005-2016 Mike Pall. All rights reserved. +-- See dynasm.lua for full copyright notice. +------------------------------------------------------------------------------ + +-- Module information: +local _info = { + arch = "s390x", + description = "DynASM s390x module", + version = "1.4.0", + vernum = 10400, + release = "2015-10-18", + 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, setmetatable, rawget = assert, setmetatable, rawget +local _s = string +local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char +local match, gmatch, gsub = _s.match, _s.gmatch, _s.gsub +local concat, sort, insert = table.concat, table.sort, table.insert +local bit = bit or require("bit") +local band, shl, shr, sar = bit.band, bit.lshift, bit.rshift, bit.arshift +local ror, tohex = bit.ror, bit.tohex + +-- 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 = { + "STOP", "SECTION", "ESC", "REL_EXT", + "ALIGN", "REL_LG", "LABEL_LG", + "REL_PC", "LABEL_PC", "DISP12", "DISP20", "IMM8", "IMM16", "IMM32", "LEN8R","LEN4HR","LEN4LR", +} + +-- 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. +local map_action = {} +local max_action = 0 +for n, name in ipairs(action_names) do + map_action[name] = n-1 + max_action = n +end + +-- Action list buffer. +local actlist = {} + +-- 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 + +------------------------------------------------------------------------------ + +-- 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 + +local function havearg(a) + return a == "ESC" or + a == "SECTION" or + a == "REL_LG" or + a == "LABEL_LG" or + a == "REL_EXT" +end + +-- Write action list buffer as a huge static C array. +local function writeactions(out, name) + local nn = #actlist + if nn == 0 then nn = 1; actlist[0] = map_action.STOP end + out:write("static const unsigned short ", name, "[", nn, "] = {") + local esc = false -- also need to escape for action arguments + for i = 1, nn do + assert(out:write("\n 0x", sub(tohex(actlist[i]), 5, 8))) + if i ~= nn then assert(out:write(",")) end + local name = action_names[actlist[i]+1] + if not esc and name then + assert(out:write(" /* ", name, " */")) + esc = havearg(name) + else + esc = false + end + end + assert(out:write("\n};\n\n")) +end + +------------------------------------------------------------------------------ + +-- Add halfword to action list. +local function wputxhw(n) + assert(n >= 0 and n <= 0xffff, "halfword out of range") + actlist[#actlist+1] = n +end + +-- Add action to list with optional arg. Advance buffer pos, too. +local function waction(action, val, a, num) + local w = assert(map_action[action], "bad action name `"..action.."'") + wputxhw(w) + if val then wputxhw(val) end -- Not sure about this, do we always have one arg? + if a then actargs[#actargs+1] = a end + if val or a or num then secpos = secpos + (num or 1) end +end + +-- Flush action list (intervening C code or buffer pos overflow). +local function wflush(term) + if #actlist == actargs[1] then return end -- Nothing to flush. + if not term then waction("STOP") end -- Terminate action list. + wline(format("dasm_put(Dst, %s);", concat(actargs, ", ")), true) + actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put(). + secpos = 1 -- The actionlist offset occupies a buffer position, too. +end + +-- Put escaped halfword. +local function wputhw(n) + if n <= max_action then waction("ESC") end + wputxhw(n) +end + +-- Reserve position for halfword. +local function wpos() + local pos = #actlist+1 + actlist[pos] = "" + return pos +end + +------------------------------------------------------------------------------ + +-- Global label name -> global label number. With auto assignment on 1st use. +local next_global = 20 +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 > 2047 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=20, 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=20, next_global-1 do + out:write(" ", prefix, 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=20, 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 = 0 +local map_extern_ = {} +local map_extern = setmetatable({}, { __index = function(t, name) + -- No restrictions on the name for now. + local n = next_extern + if n > 2047 then werror("too many extern labels") end + next_extern = n + 1 + t[name] = n + map_extern_[n] = name + return n +end}) + +-- Dump extern labels. +local function dumpexterns(out, lvl) + out:write("Extern labels:\n") + for i=0, next_extern-1 do + out:write(format(" %s\n", map_extern_[i])) + end + out:write("\n") +end + +-- Write extern label names. +local function writeexternnames(out, name) + out:write("static const char *const ", name, "[] = {\n") + for i=0, next_extern-1 do + out:write(" \"", map_extern_[i], "\",\n") + end + out:write(" (const char *)0\n};\n") +end + +------------------------------------------------------------------------------ + +-- Arch-specific maps. +-- Ext. register name -> int. name. +local map_archdef = { sp = "r15" } + +-- Int. register name -> ext. name. +local map_reg_rev = { r15 = "sp" } + +local map_type = {} -- Type name -> { ctype, reg } +local ctypenum = 0 -- Type number (for Dt... macros). + +-- Reverse defines for registers. +function _M.revdef(s) + return map_reg_rev[s] or s +end + +local map_cond = { + o = 1, h = 2, nle = 3, l = 4, + nhe = 5, lh = 6, ne = 7, e = 8, + nlh = 9, he = 10, nl = 11, le = 12, + nh = 13, no = 14, [""] = 15, +} + +------------------------------------------------------------------------------ + +local function parse_reg(expr) + if not expr then werror("expected register name") end + local tname, ovreg = match(expr, "^([%w_]+):(r1?%d)$") + local tp = map_type[tname or expr] + if tp then + local reg = ovreg or tp.reg + if not reg then + werror("type `"..(tname or expr).."' needs a register override") + end + expr = reg + end + local r = match(expr, "^[rf](1?%d)$") + if r then + r = tonumber(r) + if r <= 15 then return r, tp end + end + werror("bad register name `"..expr.."'") +end + +local parse_ctx = {} + +local loadenv = setfenv and function(s) + local code = loadstring(s, "") + if code then setfenv(code, parse_ctx) end + return code +end or function(s) + return load(s, "", nil, parse_ctx) +end + +-- Try to parse simple arithmetic, too, since some basic ops are aliases. +local function parse_number(n) + local x = tonumber(n) + if x then return x end + local code = loadenv("return "..n) + if code then + local ok, y = pcall(code) + if ok then return y end + end + return nil +end + +local function is_uint12(num) + return 0 <= num and num < 4096 +end + +local function is_int20(num) + return -shl(1, 19) <= num and num < shl(1, 19) +end + +local function is_int32(num) + return -2147483648 <= num and num < 2147483648 +end + +local function is_uint16(num) + return 0 <= num and num < 0xffff +end + +local function is_int16(num) + return -32768 <= num and num < 32768 +end + +local function is_int8(num) + return -128 <= num and num < 128 +end + +local function is_uint8(num) + return 0 <= num and num < 256 +end + +-- Split a memory operand of the form d(b) or d(x,b) into d, x and b. +-- If x is not specified then it is 0. +local function split_memop(arg) + local reg = "[%w_:]+" + local d, x, b = match(arg, "^(.*)%(%s*("..reg..")%s*,%s*("..reg..")%s*%)$") + if d then + return d, parse_reg(x), parse_reg(b) + end + local d, b = match(arg, "^(.*)%(%s*("..reg..")%s*%)$") + if d then + return d, 0, parse_reg(b) + end + -- Assume the two registers are passed as "(r1,r2)", and displacement(d) is not specified. TODO: not sure if we want to do this, GAS doesn't. + local x, b = match(arg,"%(%s*("..reg..")%s*,%s*("..reg..")%s*%)$") + if b then + return 0, parse_reg(x), parse_reg(b) + end + -- Accept a lone integer as a displacement. TODO: allow expressions/variables here? Interacts badly with the other rules currently. + local d = match(arg,"^(-?[%d]+)$") + if d then + return d, 0, 0 + end + local reg, tailr = match(arg, "^([%w_:]+)%s*(.*)$") + if reg then + local r, tp = parse_reg(reg) + if tp then + return format(tp.ctypefmt, tailr), 0, r + end + end + werror("bad memory operand: "..arg) + return nil +end + +-- Parse memory operand of the form d(x, b) where 0 <= d < 4096 and b and x +-- are GPRs. +-- If the fourth return value is not-nil then it needs to be called to +-- insert an action. +-- Encoded as: xbddd +local function parse_mem_bx(arg) + local d, x, b = split_memop(arg) + local dval = tonumber(d) + if dval then + if not is_uint12(dval) then + werror("displacement out of range: ", dval) + end + return dval, x, b, nil + end + if match(d, "^[rf]1?[0-9]?") then + werror("expected immediate operand, got register") + end + return 0, x, b, function() waction("DISP12", nil, d) end +end + +-- Parse memory operand of the form d(b) where 0 <= d < 4096 and b is a GPR. +-- Encoded as: bddd +local function parse_mem_b(arg) + local d, x, b, a = parse_mem_bx(arg) + if x ~= 0 then + werror("unexpected index register") + end + return d, b, a +end + +-- Parse memory operand of the form d(x, b) where -(2^20)/2 <= d < (2^20)/2 +-- and b and x are GPRs. +-- Encoded as: xblllhh (ls are the low-bits of d, and hs are the high bits). +local function parse_mem_bxy(arg) + local d, x, b = split_memop(arg) + local dval = tonumber(d) + if dval then + if not is_int20(dval) then + werror("displacement out of range: ", dval) + end + return dval, x, b, nil + end + if match(d, "^[rf]1?[0-9]?") then + werror("expected immediate operand, got register") + end + return 0, x, b, function() waction("DISP20", nil, d) end +end + +-- Parse memory operand of the form d(b) where -(2^20)/2 <= d < (2^20)/2 and +-- b is a GPR. +-- Encoded as: blllhh (ls are the low-bits of d, and hs are the high bits). +local function parse_mem_by(arg) + local d, x, b, a = parse_mem_bxy(arg) + if x ~= 0 then + werror("unexpected index register") + end + return d, b, a +end + +-- Parse memory operand of the form d(l, b) where 0 <= d < 4096, 1 <= l <= 256, +-- and b is a GPR. +local function parse_mem_lb(arg) + local reg = "r1?[0-9]" + local d, l, b = match(arg, "^(.*)%s*%(%s*(.*)%s*,%s*("..reg..")%s*%)$") + if not d then + -- TODO: handle values without registers? + -- TODO: handle registers without a displacement? + werror("bad memory operand: "..arg) + return nil + end + local dval = tonumber(d) + local dact = nil + if dval then + if not is_uint12(dval) then + werror("displacement out of range: ", dval) + end + else + dval = 0 + dact = function() waction("DISP12", nil, d) end + end + local lval = tonumber(l) + local lact = nil + if lval then + if lval < 1 or lval > 256 then + werror("length out of range: ", dval) + end + lval = lval - 1 + else + lval = 0 + lact = function() waction("LEN8R", nil, l) end + end + return dval, lval, parse_reg(b), dact, lact +end + +local function parse_mem_l2b(arg, high_l) + local reg = "r1?[0-9]" + local d, l, b = match(arg, "^(.*)%s*%(%s*(.*)%s*,%s*("..reg..")%s*%)$") + if not d then + -- TODO: handle values without registers? + -- TODO: handle registers without a displacement? + werror("bad memory operand: "..arg) + return nil + end + local dval = tonumber(d) + local dact = nil + if dval then + if not is_uint12(dval) then + werror("displacement out of range: ", dval) + end + else + dval = 0 + dact = function() waction("DISP12", nil, d) end + end + local lval = tonumber(l) + local lact = nil + if lval then + if lval < 1 or lval > 128 then + werror("length out of range: ", dval) + end + lval = lval - 1 + else + lval = 0 + if high_l then + lact = function() waction("LEN4HR", nil, l) end + else + lact = function() waction("LEN4LR", nil, l) end + end + end + return dval, lval, parse_reg(b), dact, lact +end + +local function parse_imm32(imm) + local imm_val = tonumber(imm) + if imm_val then + if not is_int32(imm_val) then + werror("immediate value out of range: ", imm_val) + end + wputhw(band(shr(imm_val, 16), 0xffff)) + wputhw(band(imm_val, 0xffff)) + elseif match(imm, "^[rfv]([1-3]?[0-9])$") or + match(imm, "^([%w_]+):(r1?[0-9])$") then + werror("expected immediate operand, got register") + else + waction("IMM32", nil, imm) -- if we get label + end +end + +local function parse_imm16(imm) + local imm_val = tonumber(imm) + if imm_val then + if not is_int16(imm_val) and not is_uint16(imm_val) then + werror("immediate value out of range: ", imm_val) + end + wputhw(band(imm_val, 0xffff)) + elseif match(imm, "^[rfv]([1-3]?[0-9])$") or + match(imm, "^([%w_]+):(r1?[0-9])$") then + werror("expected immediate operand, got register") + else + waction("IMM16", nil, imm) + end +end + +local function parse_imm8(imm) + local imm_val = tonumber(imm) + if imm_val then + if not is_int8(imm_val) and not is_uint8(imm_val) then + werror("Immediate value out of range: ", imm_val) + end + return imm_val, nil + end + return 0, function() waction("IMM8", nil, imm) end +end + +local function parse_mask(mask) + local m3 = parse_number(mask) + if m3 then + if ((m3 == 1) or (m3 == 0) or ( m3 >=3 and m3 <=7)) then + return m3 + else + werror("Mask value should be 0,1 or 3-7: ", m3) + end + end +end + +local function parse_mask2(mask) + local m4 = parse_number(mask) + if ( m4 >=0 and m4 <=1) then + return m4 + else + werror("Mask value should be 0 or 1: ", m4) + end +end + +local function parse_label(label, def) + local prefix = sub(label, 1, 2) + -- =>label (pc label reference) + if prefix == "=>" then + return "PC", 0, sub(label, 3) + end + -- ->name (global label reference) + if prefix == "->" then + return "LG", map_global[sub(label, 3)] + end + if def then + -- [1-9] (local label definition) + if match(label, "^[1-9]$") then + return "LG", 10+tonumber(label) + end + else + -- [<>][1-9] (local label reference) + local dir, lnum = match(label, "^([<>])([1-9])$") + if dir then -- Fwd: 1-9, Bkwd: 11-19. + return "LG", lnum + (dir == ">" and 0 or 10) + end + -- extern label (extern label reference) + local extname = match(label, "^extern%s+(%S+)$") + if extname then + return "EXT", map_extern[extname] + end + end + werror("bad label `"..label.."'") +end + +------------------------------------------------------------------------------ + +local map_op, op_template + +local function op_alias(opname, f) + return function(params, nparams) + if not params then return "-> "..opname:sub(1, -3) end + f(params, nparams) + op_template(params, map_op[opname], nparams) + end +end + +-- Template strings for s390x instructions. +map_op = { + a_2 = "00005a000000RX-a", + ad_2 = "00006a000000RX-a", + adb_2 = "ed000000001aRXE", + adbr_2 = "0000b31a0000RRE", + adr_2 = "000000002a00RR", + ae_2 = "00007a000000RX-a", + aeb_2 = "ed000000000aRXE", + aebr_2 = "0000b30a0000RRE", + aer_2 = "000000003a00RR", + afi_2 = "c20900000000RIL-a", + ag_2 = "e30000000008RXY-a", + agf_2 = "e30000000018RXY-a", + agfi_2 = "c20800000000RIL-a", + agfr_2 = "0000b9180000RRE", + aghi_2 = "0000a70b0000RI-a", + agr_2 = "0000b9080000RRE", + ah_2 = "00004a000000RX-a", + ahi_2 = "0000a70a0000RI-a", + ahy_2 = "e3000000007aRXY-a", + aih_2 = "cc0800000000RIL-a", + al_2 = "00005e000000RX-a", + alc_2 = "e30000000098RXY-a", + alcg_2 = "e30000000088RXY-a", + alcgr_2 = "0000b9880000RRE", + alcr_2 = "0000b9980000RRE", + alfi_2 = "c20b00000000RIL-a", + alg_2 = "e3000000000aRXY-a", + algf_2 = "e3000000001aRXY-a", + algfi_2 = "c20a00000000RIL-a", + algfr_2 = "0000b91a0000RRE", + algr_2 = "0000b90a0000RRE", + alr_2 = "000000001e00RR", + alsih_2 = "cc0a00000000RIL-a", + alsihn_2 = "cc0b00000000RIL-a", + aly_2 = "e3000000005eRXY-a", + ap_2 = "fa0000000000SS-b", + ar_2 = "000000001a00RR", + au_2 = "00007e000000RX-a", + aur_2 = "000000003e00RR", + aw_2 = "00006e000000RX-a", + awr_2 = "000000002e00RR", + axbr_2 = "0000b34a0000RRE", + axr_2 = "000000003600RR", + ay_2 = "e3000000005aRXY-a", + bakr_2 = "0000b2400000RRE", + bal_2 = "000045000000RX-a", + balr_2 = "000000000500RR", + bas_2 = "00004d000000RX-a", + basr_2 = "000000000d00RR", + bassm_2 = "000000000c00RR", + bc_2 = "000047000000RX-b", + bc_2 = "000047000000RX-b", + bcr_2 = "000000000700RR", + bct_2 = "000046000000RX-a", + bctg_2 = "e30000000046RXY-a", + bctgr_2 = "0000b9460000RRE", + bctr_2 = "000000000600RR", + bras_2 = "0000a7050000RI-b", + brasl_2 = "c00500000000RIL-b", + brc_2 = "0000a7040000RI-c", + brcl_2 = "c00400000000RIL-c", + brcl_2 = "c00400000000RIL-c", + brct_2 = "0000a7060000RI-b", + brctg_2 = "0000a7070000RI-b", + brcth_2 = "cc0600000000RIL-b", + brxh_3 = "000084000000RSI", + brxhg_3 = "ec0000000044RIE-e", + bsa_2 = "0000b25a0000RRE", + bsg_2 = "0000b2580000RRE", + bsm_2 = "000000000b00RR", + bxh_3 = "000086000000RS-a", + bxhg_3 = "eb0000000044RSY-a", + bxle_3 = "000087000000RS-a", + bxleg_3 = "eb0000000045RSY-a", + c_2 = "000059000000RX-a", + cd_2 = "000069000000RX-a", + cdb_2 = "ed0000000019RXE", + cdbr_2 = "0000b3190000RRE", + cdfbr_2 = "0000b3950000RRE", + cdfbra_4 = "0000b3950000RRF-e", + cdfr_2 = "0000b3b50000RRE", + cdftr_2 = "0000b9510000RRE", + cdgbr_2 = "0000b3a50000RRE", + cdgbra_4 = "0000b3a50000RRF-e", + cdgr_2 = "0000b3c50000RRE", + cdgtr_2 = "0000b3f10000RRE", + cdr_2 = "000000002900RR", + cds_3 = "0000bb000000RS-a", + cdsg_3 = "eb000000003eRSY-a", + cdstr_2 = "0000b3f30000RRE", + cdsy_3 = "eb0000000031RSY-a", + cdtr_2 = "0000b3e40000RRE", + cdutr_2 = "0000b3f20000RRE", + ce_2 = "000079000000RX-a", + ceb_2 = "ed0000000009RXE", + cebr_2 = "0000b3090000RRE", + cedtr_2 = "0000b3f40000RRE", + cefbr_2 = "0000b3940000RRE", + cefbra_4 = "0000b3940000RRF-e", + cefr_2 = "0000b3b40000RRE", + cegbr_2 = "0000b3a40000RRE", + cegbra_4 = "0000b3a40000RRF-e", + cegr_2 = "0000b3c40000RRE", + cer_2 = "000000003900RR", + cextr_2 = "0000b3fc0000RRE", + cfdbr_3 = "0000b3990000RRF-e", + cfdbra_4 = "0000b3990000RRF-e", + cfebr_3 = "0000b3980000RRF-e", + cfebra_4 = "0000b3980000RRF-e", + cfi_2 = "c20d00000000RIL-a", + cfxbr_3 = "0000b39a0000RRF-e", + cfxbra_4 = "0000b39a0000RRF-e", + cg_2 = "e30000000020RXY-a", + cgdbr_3 = "0000b3a90000RRF-e", + cgdbra_4 = "0000b3a90000RRF-e", + cgebr_3 = "0000b3a80000RRF-e", + cgebra_4 = "0000b3a80000RRF-e", + cgf_2 = "e30000000030RXY-a", + cgfi_2 = "c20c00000000RIL-a", + cgfr_2 = "0000b9300000RRE", + cgfrl_2 = "c60c00000000RIL-b", + cgh_2 = "e30000000034RXY-a", + cghi_2 = "0000a70f0000RI-a", + cghrl_2 = "c60400000000RIL-b", + cgr_2 = "0000b9200000RRE", + cgrl_2 = "c60800000000RIL-b", + cgxbr_3 = "0000b3aa0000RRF-e", + cgxbra_4 = "0000b3aa0000RRF-e", + ch_2 = "000049000000RX-a", + chf_2 = "e300000000cdRXY-a", + chhr_2 = "0000b9cd0000RRE", + chi_2 = "0000a70e0000RI-a", + chlr_2 = "0000b9dd0000RRE", + chrl_2 = "c60500000000RIL-b", + chy_2 = "e30000000079RXY-a", + cih_2 = "cc0d00000000RIL-a", + cksm_2 = "0000b2410000RRE", + cl_2 = "000055000000RX-a", + clc_2 = "d50000000000SS-a", + clcl_2 = "000000000f00RR", + clcle_3 = "0000a9000000RS-a", + clclu_3 = "eb000000008fRSY-a", + clfi_2 = "c20f00000000RIL-a", + clg_2 = "e30000000021RXY-a", + clgf_2 = "e30000000031RXY-a", + clgfi_2 = "c20e00000000RIL-a", + clgfr_2 = "0000b9310000RRE", + clgfrl_2 = "c60e00000000RIL-b", + clghrl_2 = "c60600000000RIL-b", + clgr_2 = "0000b9210000RRE", + clgrl_2 = "c60a00000000RIL-b", + clhf_2 = "e300000000cfRXY-a", + clhhr_2 = "0000b9cf0000RRE", + clhlr_2 = "0000b9df0000RRE", + clhrl_2 = "c60700000000RIL-b", + cli_2 = "000095000000SI", + clih_2 = "cc0f00000000RIL-a", + clm_3 = "0000bd000000RS-b", + clmh_3 = "eb0000000020RSY-b", + clmy_3 = "eb0000000021RSY-b", + clr_2 = "000000001500RR", + clrl_2 = "c60f00000000RIL-b", + clst_2 = "0000b25d0000RRE", + cly_2 = "e30000000055RXY-a", + cmpsc_2 = "0000b2630000RRE", + cpya_2 = "0000b24d0000RRE", + cr_2 = "000000001900RR", + crl_2 = "c60d00000000RIL-b", + cs_3 = "0000ba000000RS-a", + csg_3 = "eb0000000030RSY-a", + csp_2 = "0000b2500000RRE", + cspg_2 = "0000b98a0000RRE", + csy_3 = "eb0000000014RSY-a", + cu41_2 = "0000b9b20000RRE", + cu42_2 = "0000b9b30000RRE", + cudtr_2 = "0000b3e20000RRE", + cuse_2 = "0000b2570000RRE", + cuxtr_2 = "0000b3ea0000RRE", + cvb_2 = "00004f000000RX-a", + cvbg_2 = "e3000000000eRXY-a", + cvby_2 = "e30000000006RXY-a", + cvd_2 = "00004e000000RX-a", + cvdg_2 = "e3000000002eRXY-a", + cvdy_2 = "e30000000026RXY-a", + cxbr_2 = "0000b3490000RRE", + cxfbr_2 = "0000b3960000RRE", + cxfbra_4 = "0000b3960000RRF-e", + cxfr_2 = "0000b3b60000RRE", + cxftr_2 = "0000b9590000RRE", + cxgbr_2 = "0000b3a60000RRE", + cxgbra_4 = "0000b3a60000RRF-e", + cxgr_2 = "0000b3c60000RRE", + cxgtr_2 = "0000b3f90000RRE", + cxr_2 = "0000b3690000RRE", + cxstr_2 = "0000b3fb0000RRE", + cxtr_2 = "0000b3ec0000RRE", + cxutr_2 = "0000b3fa0000RRE", + cy_2 = "e30000000059RXY-a", + d_2 = "00005d000000RX-a", + dd_2 = "00006d000000RX-a", + ddb_2 = "ed000000001dRXE", + ddbr_2 = "0000b31d0000RRE", + ddr_2 = "000000002d00RR", + de_2 = "00007d000000RX-a", + deb_2 = "ed000000000dRXE", + debr_2 = "0000b30d0000RRE", + der_2 = "000000003d00RR", + didbr_4 = "0000b35b0000RRF-b", + dl_2 = "e30000000097RXY-a", + dlg_2 = "e30000000087RXY-a", + dlgr_2 = "0000b9870000RRE", + dlr_2 = "0000b9970000RRE", + dr_2 = "000000001d00RR", + dsg_2 = "e3000000000dRXY-a", + dsgf_2 = "e3000000001dRXY-a", + dsgfr_2 = "0000b91d0000RRE", + dsgr_2 = "0000b90d0000RRE", + dxbr_2 = "0000b34d0000RRE", + dxr_2 = "0000b22d0000RRE", + ear_2 = "0000b24f0000RRE", + ecag_3 = "eb000000004cRSY-a", + ed_2 = "de0000000000SS-a", + edmk_2 = "df0000000000SS-a", + eedtr_2 = "0000b3e50000RRE", + eextr_2 = "0000b3ed0000RRE", + efpc_2 = "0000b38c0000RRE", + epair_2 = "0000b99a0000RRE", + epar_2 = "0000b2260000RRE", + epsw_2 = "0000b98d0000RRE", + ereg_2 = "0000b2490000RRE", + eregg_2 = "0000b90e0000RRE", + esair_2 = "0000b99b0000RRE", + esar_2 = "0000b2270000RRE", + esdtr_2 = "0000b3e70000RRE", + esea_2 = "0000b99d0000RRE", + esta_2 = "0000b24a0000RRE", + esxtr_2 = "0000b3ef0000RRE", + ex_2 = "000044000000RX-a", + exrl_2 = "c60000000000RIL-b", + fidbra_4 = "0000b35f0000RRF-e", + fidr_2 = "0000b37f0000RRE", + fier_2 = "0000b3770000RRE", + fixr_2 = "0000b3670000RRE", + flogr_2 = "0000b9830000RRE", + hdr_2 = "000000002400RR", + her_2 = "000000003400RR", + iac_2 = "0000b2240000RRE", + ic_2 = "000043000000RX-a", + icm_3 = "0000bf000000RS-b", + icmh_3 = "eb0000000080RSY-b", + icmy_3 = "eb0000000081RSY-b", + icy_2 = "e30000000073RXY-a", + iihf_2 = "c00800000000RIL-a", + iihh_2 = "0000a5000000RI-a", + iihl_2 = "0000a5010000RI-a", + iilf_2 = "c00900000000RIL-a", + iilh_2 = "0000a5020000RI-a", + iill_2 = "0000a5030000RI-a", + ipm_2 = "0000b2220000RRE", + iske_2 = "0000b2290000RRE", + ivsk_2 = "0000b2230000RRE", + kdbr_2 = "0000b3180000RRE", + kdtr_2 = "0000b3e00000RRE", + kebr_2 = "0000b3080000RRE", + kimd_2 = "0000b93e0000RRE", + klmd_2 = "0000b93f0000RRE", + km_2 = "0000b92e0000RRE", + kmac_2 = "0000b91e0000RRE", + kmc_2 = "0000b92f0000RRE", + kmf_2 = "0000b92a0000RRE", + kmo_2 = "0000b92b0000RRE", + kxbr_2 = "0000b3480000RRE", + kxtr_2 = "0000b3e80000RRE", + l_2 = "000058000000RX-a", + la_2 = "000041000000RX-a", + laa_3 = "eb00000000f8RSY-a", + laag_3 = "eb00000000e8RSY-a", + laal_3 = "eb00000000faRSY-a", + laalg_3 = "eb00000000eaRSY-a", + lae_2 = "000051000000RX-a", + laey_2 = "e30000000075RXY-a", + lam_3 = "00009a000000RS-a", + lamy_3 = "eb000000009aRSY-a", + lan_3 = "eb00000000f4RSY-a", + lang_3 = "eb00000000e4RSY-a", + lao_3 = "eb00000000f6RSY-a", + laog_3 = "eb00000000e6RSY-a", + larl_2 = "c00000000000RIL-b", + lax_3 = "eb00000000f7RSY-a", + laxg_3 = "eb00000000e7RSY-a", + lay_2 = "e30000000071RXY-a", + lb_2 = "e30000000076RXY-a", + lbh_2 = "e300000000c0RXY-a", + lbr_2 = "0000b9260000RRE", + lcdbr_2 = "0000b3130000RRE", + lcdfr_2 = "0000b3730000RRE", + lcdr_2 = "000000002300RR", + lcebr_2 = "0000b3030000RRE", + lcer_2 = "000000003300RR", + lcgfr_2 = "0000b9130000RRE", + lcgr_2 = "0000b9030000RRE", + lcr_2 = "000000001300RR", + lctl_3 = "0000b7000000RS-a", + lctlg_3 = "eb000000002fRSY-a", + lcxbr_2 = "0000b3430000RRE", + lcxr_2 = "0000b3630000RRE", + ld_2 = "000068000000RX-a", + ldebr_2 = "0000b3040000RRE", + lder_2 = "0000b3240000RRE", + ldgr_2 = "0000b3c10000RRE", + ldr_2 = "000000002800RR", + ldxbr_2 = "0000b3450000RRE", + ldxr_2 = "000000002500RR", + ldy_2 = "ed0000000065RXY-a", + le_2 = "000078000000RX-a", + ledbr_2 = "0000b3440000RRE", + ledr_2 = "000000003500RR", + ler_2 = "000000003800RR", + lexbr_2 = "0000b3460000RRE", + lexr_2 = "0000b3660000RRE", + ley_2 = "ed0000000064RXY-a", + lfh_2 = "e300000000caRXY-a", + lg_2 = "e30000000004RXY-a", + lgb_2 = "e30000000077RXY-a", + lgbr_2 = "0000b9060000RRE", + lgdr_2 = "0000b3cd0000RRE", + lgf_2 = "e30000000014RXY-a", + lgfi_2 = "c00100000000RIL-a", + lgfr_2 = "0000b9140000RRE", + lgfrl_2 = "c40c00000000RIL-b", + lgh_2 = "e30000000015RXY-a", + lghi_2 = "0000a7090000RI-a", + lghr_2 = "0000b9070000RRE", + lghrl_2 = "c40400000000RIL-b", + lgr_2 = "0000b9040000RRE", + lgrl_2 = "c40800000000RIL-b", + lh_2 = "000048000000RX-a", + lhh_2 = "e300000000c4RXY-a", + lhi_2 = "0000a7080000RI-a", + lhr_2 = "0000b9270000RRE", + lhrl_2 = "c40500000000RIL-b", + lhy_2 = "e30000000078RXY-a", + llc_2 = "e30000000094RXY-a", + llch_2 = "e300000000c2RXY-a", + llcr_2 = "0000b9940000RRE", + llgc_2 = "e30000000090RXY-a", + llgcr_2 = "0000b9840000RRE", + llgf_2 = "e30000000016RXY-a", + llgfr_2 = "0000b9160000RRE", + llgfrl_2 = "c40e00000000RIL-b", + llgh_2 = "e30000000091RXY-a", + llghr_2 = "0000b9850000RRE", + llghrl_2 = "c40600000000RIL-b", + llgt_2 = "e30000000017RXY-a", + llgtr_2 = "0000b9170000RRE", + llh_2 = "e30000000095RXY-a", + llhh_2 = "e300000000c6RXY-a", + llhr_2 = "0000b9950000RRE", + llhrl_2 = "c40200000000RIL-b", + llihf_2 = "c00e00000000RIL-a", + llihh_2 = "0000a50c0000RI-a", + llihl_2 = "0000a50d0000RI-a", + llilf_2 = "c00f00000000RIL-a", + llilh_2 = "0000a50e0000RI-a", + llill_2 = "0000a50f0000RI-a", + lm_3 = "000098000000RS-a", + lmg_3 = "eb0000000004RSY-a", + lmh_3 = "eb0000000096RSY-a", + lmy_3 = "eb0000000098RSY-a", + lndbr_2 = "0000b3110000RRE", + lndfr_2 = "0000b3710000RRE", + lndr_2 = "000000002100RR", + lnebr_2 = "0000b3010000RRE", + lner_2 = "000000003100RR", + lngfr_2 = "0000b9110000RRE", + lngr_2 = "0000b9010000RRE", + lnr_2 = "000000001100RR", + lnxbr_2 = "0000b3410000RRE", + lnxr_2 = "0000b3610000RRE", + loc_3 = "eb00000000f2RSY-b", + locg_3 = "eb00000000e2RSY-b", + lpdbr_2 = "0000b3100000RRE", + lpdfr_2 = "0000b3700000RRE", + lpdr_2 = "000000002000RR", + lpebr_2 = "0000b3000000RRE", + lper_2 = "000000003000RR", + lpgfr_2 = "0000b9100000RRE", + lpgr_2 = "0000b9000000RRE", + lpq_2 = "e3000000008fRXY-a", + lpr_2 = "000000001000RR", + lpxbr_2 = "0000b3400000RRE", + lpxr_2 = "0000b3600000RRE", + lr_2 = "000000001800RR", + lra_2 = "0000b1000000RX-a", + lrag_2 = "e30000000003RXY-a", + lray_2 = "e30000000013RXY-a", + lrdr_2 = "000000002500RR", + lrer_2 = "000000003500RR", + lrl_2 = "c40d00000000RIL-b", + lrv_2 = "e3000000001eRXY-a", + lrvg_2 = "e3000000000fRXY-a", + lrvgr_2 = "0000b90f0000RRE", + lrvh_2 = "e3000000001fRXY-a", + lrvr_2 = "0000b91f0000RRE", + lt_2 = "e30000000012RXY-a", + ltdbr_2 = "0000b3120000RRE", + ltdr_2 = "000000002200RR", + ltdtr_2 = "0000b3d60000RRE", + ltebr_2 = "0000b3020000RRE", + lter_2 = "000000003200RR", + ltg_2 = "e30000000002RXY-a", + ltgf_2 = "e30000000032RXY-a", + ltgfr_2 = "0000b9120000RRE", + ltgr_2 = "0000b9020000RRE", + ltr_2 = "000000001200RR", + ltxbr_2 = "0000b3420000RRE", + ltxr_2 = "0000b3620000RRE", + ltxtr_2 = "0000b3de0000RRE", + lura_2 = "0000b24b0000RRE", + lurag_2 = "0000b9050000RRE", + lxdbr_2 = "0000b3050000RRE", + lxdr_2 = "0000b3250000RRE", + lxebr_2 = "0000b3060000RRE", + lxer_2 = "0000b3260000RRE", + lxr_2 = "0000b3650000RRE", + ly_2 = "e30000000058RXY-a", + lzdr_2 = "0000b3750000RRE", + lzer_2 = "0000b3740000RRE", + lzxr_2 = "0000b3760000RRE", + m_2 = "00005c000000RX-a", + madb_3 = "ed000000001eRXF", + maeb_3 = "ed000000000eRXF", + maebr_3 = "0000b30e0000RRD", + maer_3 = "0000b32e0000RRD", + md_2 = "00006c000000RX-a", + mdb_2 = "ed000000001cRXE", + mdbr_2 = "0000b31c0000RRE", + mde_2 = "00007c000000RX-a", + mdeb_2 = "ed000000000cRXE", + mdebr_2 = "0000b30c0000RRE", + mder_2 = "000000003c00RR", + mdr_2 = "000000002c00RR", + me_2 = "00007c000000RX-a", + meeb_2 = "ed0000000017RXE", + meebr_2 = "0000b3170000RRE", + meer_2 = "0000b3370000RRE", + mer_2 = "000000003c00RR", + mfy_2 = "e3000000005cRXY-a", + mghi_2 = "0000a70d0000RI-a", + mh_2 = "00004c000000RX-a", + mhi_2 = "0000a70c0000RI-a", + mhy_2 = "e3000000007cRXY-a", + ml_2 = "e30000000096RXY-a", + mlg_2 = "e30000000086RXY-a", + mlgr_2 = "0000b9860000RRE", + mlr_2 = "0000b9960000RRE", + mr_2 = "000000001c00RR", + ms_2 = "000071000000RX-a", + msfi_2 = "c20100000000RIL-a", + msg_2 = "e3000000000cRXY-a", + msgf_2 = "e3000000001cRXY-a", + msgfi_2 = "c20000000000RIL-a", + msgfr_2 = "0000b91c0000RRE", + msgr_2 = "0000b90c0000RRE", + msr_2 = "0000b2520000RRE", + msta_2 = "0000b2470000RRE", + msy_2 = "e30000000051RXY-a", + mvc_2 = "d20000000000SS-a", + mvcin_2 = "e80000000000SS-a", + mvcl_2 = "000000000e00RR", + mvcle_3 = "0000a8000000RS-a", + mvclu_3 = "eb000000008eRSY-a", + mvghi_2 = "e54800000000SIL", + mvhhi_2 = "e54400000000SIL", + mvhi_2 = "e54c00000000SIL", + mvi_2 = "000092000000SI", + mvn_2 = "d10000000000SS-a", + mvpg_2 = "0000b2540000RRE", + mvst_2 = "0000b2550000RRE", + mvz_2 = "d30000000000SS-a", + mxbr_2 = "0000b34c0000RRE", + mxd_2 = "000067000000RX-a", + mxdb_2 = "ed0000000007RXE", + mxdbr_2 = "0000b3070000RRE", + mxdr_2 = "000000002700RR", + mxr_2 = "000000002600RR", + n_2 = "000054000000RX-a", + nc_2 = "d40000000000SS-a", + ng_2 = "e30000000080RXY-a", + ngr_2 = "0000b9800000RRE", + ni_2 = "000094000000SI", + nihf_2 = "c00a00000000RIL-a", + nihh_2 = "0000a5040000RI-a", + nihl_2 = "0000a5050000RI-a", + nilf_2 = "c00b00000000RIL-a", + nilh_2 = "0000a5060000RI-a", + nill_2 = "0000a5070000RI-a", + nr_2 = "000000001400RR", + ny_2 = "e30000000054RXY-a", + o_2 = "000056000000RX-a", + oc_2 = "d60000000000SS-a", + og_2 = "e30000000081RXY-a", + ogr_2 = "0000b9810000RRE", + oi_2 = "000096000000SI", + oihf_2 = "c00c00000000RIL-a", + oihh_2 = "0000a5080000RI-a", + oihl_2 = "0000a5090000RI-a", + oilf_2 = "c00d00000000RIL-a", + oilh_2 = "0000a50a0000RI-a", + oill_2 = "0000a50b0000RI-a", + or_2 = "000000001600RR", + oy_2 = "e30000000056RXY-a", + palb_2 = "0000b2480000RRE", + pcc_2 = "0000b92c0000RRE", + pckmo_2 = "0000b9280000RRE", + pfd_2 = "e30000000036m", + pfdrl_2 = "c60200000000RIL-c", + pfmf_2 = "0000b9af0000RRE", + pgin_2 = "0000b22e0000RRE", + pgout_2 = "0000b22f0000RRE", + popcnt_2 = "0000b9e10000RRE", + pt_2 = "0000b2280000RRE", + ptf_2 = "0000b9a20000RRE", + pti_2 = "0000b99e0000RRE", + rll_3 = "eb000000001dRSY-a", + rllg_3 = "eb000000001cRSY-a", + rrbe_2 = "0000b22a0000RRE", + rrbm_2 = "0000b9ae0000RRE", + s_2 = "00005b000000RX-a", + sar_2 = "0000b24e0000RRE", + sd_2 = "00006b000000RX-a", + sdb_2 = "ed000000001bRXE", + sdbr_2 = "0000b31b0000RRE", + sdr_2 = "000000002b00RR", + se_2 = "00007b000000RX-a", + seb_2 = "ed000000000bRXE", + sebr_2 = "0000b30b0000RRE", + ser_2 = "000000003b00RR", + sfasr_2 = "0000b3850000RRE", + sfpc_2 = "0000b3840000RRE", + sg_2 = "e30000000009RXY-a", + sgf_2 = "e30000000019RXY-a", + sgfr_2 = "0000b9190000RRE", + sgr_2 = "0000b9090000RRE", + sh_2 = "00004b000000RX-a", + shy_2 = "e3000000007bRXY-a", + sl_2 = "00005f000000RX-a", + sla_2 = "00008b000000RS-a", + slag_3 = "eb000000000bRSY-a", + slak_3 = "eb00000000ddRSY-a", + slb_2 = "e30000000099RXY-a", + slbg_2 = "e30000000089RXY-a", + slbgr_2 = "0000b9890000RRE", + slbr_2 = "0000b9990000RRE", + slda_2 = "00008f000000RS-a", + sldl_2 = "00008d000000RS-a", + slfi_2 = "c20500000000RIL-a", + slg_2 = "e3000000000bRXY-a", + slgf_2 = "e3000000001bRXY-a", + slgfi_2 = "c20400000000RIL-a", + slgfr_2 = "0000b91b0000RRE", + slgr_2 = "0000b90b0000RRE", + sll_2 = "000089000000RS-a", + sllg_3 = "eb000000000dRSY-a", + sllk_3 = "eb00000000dfRSY-a", + slr_2 = "000000001f00RR", + sly_2 = "e3000000005fRXY-a", + spm_2 = "000000000400RR", + sqdb_2 = "ed0000000015RXE", + sqdbr_2 = "0000b3150000RRE", + sqdr_2 = "0000b2440000RRE", + sqeb_2 = "ed0000000014RXE", + sqebr_2 = "0000b3140000RRE", + sqer_2 = "0000b2450000RRE", + sqxbr_2 = "0000b3160000RRE", + sqxr_2 = "0000b3360000RRE", + sr_2 = "000000001b00RR", + sra_2 = "00008a000000RS-a", + srag_3 = "eb000000000aRSY-a", + srak_3 = "eb00000000dcRSY-a", + srda_2 = "00008e000000RS-a", + srdl_2 = "00008c000000RS-a", + srl_2 = "000088000000RS-a", + srlg_3 = "eb000000000cRSY-a", + srlk_3 = "eb00000000deRSY-a", + srst_2 = "0000b25e0000RRE", + srstu_2 = "0000b9be0000RRE", + ssair_2 = "0000b99f0000RRE", + ssar_2 = "0000b2250000RRE", + st_2 = "000050000000RX-a", + stam_3 = "00009b000000RS-a", + stamy_3 = "eb000000009bRSY-a", + stc_2 = "000042000000RX-a", + stch_2 = "e300000000c3RXY-a", + stcm_3 = "0000be000000RS-b", + stcmh_3 = "eb000000002cRSY-b", + stcmy_3 = "eb000000002dRSY-b", + stctg_3 = "eb0000000025RSY-a", + stctl_3 = "0000b6000000RS-a", + stcy_2 = "e30000000072RXY-a", + std_2 = "000060000000RX-a", + stdy_2 = "ed0000000067RXY-a", + ste_2 = "000070000000RX-a", + stey_2 = "ed0000000066RXY-a", + stfh_2 = "e300000000cbRXY-a", + stfl_1 = "0000b2b10000S", + stg_2 = "e30000000024RXY-a", + stgrl_2 = "c40b00000000RIL-b", + sth_2 = "000040000000RX-a", + sthh_2 = "e300000000c7RXY-a", + sthrl_2 = "c40700000000RIL-b", + sthy_2 = "e30000000070RXY-a", + stm_3 = "000090000000RS-a", + stmg_3 = "eb0000000024RSY-a", + stmh_3 = "eb0000000026RSY-a", + stmy_3 = "eb0000000090RSY-a", + stoc_3 = "eb00000000f3RSY-b", + stocg_3 = "eb00000000e3RSY-b", + stpq_2 = "e3000000008eRXY-a", + strl_2 = "c40f00000000RIL-b", + strv_2 = "e3000000003eRXY-a", + strvg_2 = "e3000000002fRXY-a", + strvh_2 = "e3000000003fRXY-a", + stura_2 = "0000b2460000RRE", + sturg_2 = "0000b9250000RRE", + sty_2 = "e30000000050RXY-a", + su_2 = "00007f000000RX-a", + sur_2 = "000000003f00RR", + svc_1 = "000000000a00I", + sw_2 = "00006f000000RX-a", + swr_2 = "000000002f00RR", + sxbr_2 = "0000b34b0000RRE", + sxr_2 = "000000003700RR", + sy_2 = "e3000000005bRXY-a", + tar_2 = "0000b24c0000RRE", + tb_2 = "0000b22c0000RRE", + thder_2 = "0000b3580000RRE", + thdr_2 = "0000b3590000RRE", + tm_2 = "000091000000SI", + tmhh_2 = "0000a7020000RI-a", + tmhl_2 = "0000a7030000RI-a", + tmlh_2 = "0000a7000000RI-a", + tmll_2 = "0000a7010000RI-a", + tmy_2 = "eb0000000051SIY", + tr_2 = "dc0000000000SS-a", + trace_3 = "000099000000RS-a", + tracg_3 = "eb000000000fRSY-a", + tre_2 = "0000b2a50000RRE", + trt_2 = "dd0000000000SS-a", + trtr_2 = "d00000000000SS-a", + unpka_2 = "ea0000000000SS-a", + unpku_2 = "e20000000000SS-a", + x_2 = "000057000000RX-a", + xc_2 = "d70000000000SS-a", + xg_2 = "e30000000082RXY-a", + xgr_2 = "0000b9820000RRE", + xi_2 = "000097000000SI", + xihf_2 = "c00600000000RIL-a", + xilf_2 = "c00700000000RIL-a", + xr_2 = "000000001700RR", + xy_2 = "e30000000057RXY-a", +} +for cond, c in pairs(map_cond) do + -- Extended mnemonics for branches. + -- TODO: replace 'B' with correct encoding. + -- brc + map_op["j"..cond.."_1"] = "0000"..tohex(0xa7040000+shl(c, 20)).."RI-c" + -- brcl + map_op["jg"..cond.."_1"] = tohex(0xc0040000+shl(c, 20)).."0000".."RIL-c" + -- bc + map_op["b"..cond.."_1"] = "0000"..tohex(0x47000000+shl(c, 20)).."RX-b" + -- bcr + map_op["b"..cond.."r_1"] = "0000"..tohex(0x0700+shl(c, 4)).."RR" +end +------------------------------------------------------------------------------ +-- Handle opcodes defined with template strings. +local function parse_template(params, template, nparams, pos) + -- Read the template in 16-bit chunks. + -- Leading halfword zeroes should not be written out. + local op0 = tonumber(sub(template, 1, 4), 16) + local op1 = tonumber(sub(template, 5, 8), 16) + local op2 = tonumber(sub(template, 9, 12), 16) + + -- Process each character. + local p = sub(template, 13) + if p == "I" then + local imm_val, a = parse_imm8(params[1]) + op2 = op2 + imm_val + wputhw(op2) + if a then a() end + elseif p == "RI-a" then + op1 = op1 + shl(parse_reg(params[1]), 4) + wputhw(op1) + parse_imm16(params[2]) + elseif p == "RI-b" then + op1 = op1 + shl(parse_reg(params[1]), 4) + wputhw(op1) + local mode, n, s = parse_label(params[2]) + waction("REL_"..mode, n, s) + elseif p == "RI-c" then + if #params > 1 then + op1 = op1 + shl(parse_num(params[1]), 4) + end + wputhw(op1) + local mode, n, s = parse_label(params[#params]) + waction("REL_"..mode, n, s) + elseif p == "RIE-e" then + op0 = op0 + shl(parse_reg(params[1]), 4) + parse_reg(params[2]) + wputhw1(op0) + local mode, n, s = parse_label(params[3]) + waction("REL_"..mode, n, s) + wputhw(op2) + elseif p == "RIL-a" then + op0 = op0 + shl(parse_reg(params[1]), 4) + wputhw(op0); + parse_imm32(params[2]) + elseif p == "RIL-b" then + op0 = op0 + shl(parse_reg(params[1]), 4) + wputhw(op0) + local mode, n, s = parse_label(params[2]) + waction("REL_"..mode, n, s) + elseif p == "RIL-c" then + if #params > 1 then + op0 = op0 + shl(parse_num(params[1]), 4) + end + wputhw(op0) + local mode, n, s = parse_label(params[#params]) + waction("REL_"..mode, n, s) + elseif p == "RR" then + if #params > 1 then + op2 = op2 + shl(parse_reg(params[1]), 4) + end + op2 = op2 + parse_reg(params[#params]) + wputhw(op2) + elseif p == "RRD" then + wputhw(op1) + op2 = op2 + shl(parse_reg(params[1]), 12) + shl(parse_reg(params[2]), 4) + parse_reg(params[3]) + wputhw(op2) + elseif p == "RRE" then + op2 = op2 + shl(parse_reg(params[1]), 4) + parse_reg(params[2]) + wputhw(op1); wputhw(op2) + elseif p == "RRF-b" then + wputhw(op1) + op2 = op2 + shl(parse_reg(params[1]), 4) + shl(parse_reg(params[2]), 12) + parse_reg(params[3]) + shl(parse_mask(params[4]), 8) + wputhw(op2) + elseif p == "RRF-e" then + wputhw(op1) + op2 = op2 + shl(parse_reg(params[1]), 4) + shl(parse_mask(params[2]), 12) + parse_reg(params[3]) + if params[4] then + op2 = op2 + shl(parse_mask2(params[4]), 8) + end + wputhw(op2) + elseif p == "RS-a" then + if (params[3]) then + local d, b, a = parse_mem_b(params[3]) + op1 = op1 + shl(parse_reg(params[1]), 4) + parse_reg(params[2]) + op2 = op2 + shl(b, 12) + d + else + local d, b, a = parse_mem_b(params[2]) + op1 = op1 + shl(parse_reg(params[1]), 4) + op2 = op2 + shl(b, 12) + d + end + wputhw(op1); wputhw(op2) + if a then a() end + elseif p == "RS-b" then + local m = parse_mask(params[2]) + local d, b, a = parse_mem_b(params[3]) + op1 = op1 + shl(parse_reg(params[1]), 4) + m + op2 = op2 + shl(b, 12) + d + wputhw(op1); wputhw(op2) + if a then a() end + elseif p == "RSI" then + op1 = op1 + shl(parse_reg(params[1]), 4) + parse_reg(params[2]) + wputhw(op1) + local mode, n, s = parse_label(params[3]) + waction("REL_"..mode, n, s) + elseif p == "RSY-a" then + local d, b, a = parse_mem_by(params[3]) + op0 = op0 + shl(parse_reg(params[1]), 4) + parse_reg(params[2]) + op1 = op1 + shl(b, 12) + band(d, 0xfff) + op2 = op2 + band(shr(d, 4), 0xff00) + wputhw(op0); wputhw(op1); wputhw(op2) + if a then a() end -- a() emits action. + elseif p == "RX-a" then + local d, x, b, a = parse_mem_bx(params[2]) + op1 = op1 + shl(parse_reg(params[1]), 4) + x + op2 = op2 + shl(b, 12) + d + wputhw(op1); wputhw(op2) + if a then a() end + elseif p == "RX-b" then + local d, x, b, a = parse_mem_bx(params[#params]) + if #params > 1 then + op1 = op1 + shl(parse_num(params[1]), 4) + end + op1 = op1 + x + op2 = op2 + shl(b, 12) + d + wputhw(op1); wputhw(op2) + if a then a() end + elseif p == "RXE" then + local d, x, b, a = parse_mem_bx(params[2]) + op0 = op0 + shl(parse_reg(params[1]), 4) + x + op1 = op1 + shl(b, 12) + d + wputhw(op0); wputhw(op1) + if a then a() end + wputhw(op2); + elseif p == "RXF" then + local d, x, b, a = parse_mem_bx(params[3]) + op0 = op0 + shl(parse_reg(params[2]), 4) + x + op1 = op1 + shl(b, 12) + d + wputhw(op0); wputhw(op1) + if a then a() end + op2 = op2 + shl(parse_reg(params[1]), 12) + wputhw(op2) + elseif p == "RXY-a" then + local d, x, b, a = parse_mem_bxy(params[2]) + op0 = op0 + shl(parse_reg(params[1]), 4) + x + op1 = op1 + shl(b, 12) + band(d, 0xfff) + op2 = op2 + band(shr(d, 4), 0xff00) + wputhw(op0); wputhw(op1); wputhw(op2) + if a then a() end + elseif p == "S" then + wputhw(op1); + local d, b, a = parse_mem_b(params[1]) + op2 = op2 + shl(b, 12) + d + wputhw(op2) + if a then a() end + elseif p == "SI" then + local imm_val, a = parse_imm8(params[2]) + op1 = op1 + imm_val + wputhw(op1) + if a then a() end + local d, b, a = parse_mem_b(params[1]) + op2 = op2 + shl(b, 12) + d + wputhw(op2) + if a then a() end + elseif p == "SIL" then + wputhw(op0) + local d, b, a = parse_mem_b(params[1]) + op1 = op1 + shl(b, 12) + d + wputhw(op1) + if a then a() end + parse_imm16(params[2]) + elseif p == "SIY" then + local imm8, iact = parse_imm8(params[2]) + op0 = op0 + shl(imm8, 8) + wputhw(op0) + if iact then iact() end + local d, b, a = parse_mem_by(params[1]) + op1 = op1 + shl(b, 12) + band(d, 0xfff) + op2 = op2 + band(shr(d, 4), 0xff00) + wputhw(op1); wputhw(op2) + if a then a() end + elseif p == "SS-a" then + local d1, l1, b1, d1a, l1a = parse_mem_lb(params[1]) + local d2, b2, d2a = parse_mem_b(params[2]) + op0 = op0 + l1 + op1 = op1 + shl(b1, 12) + d1 + op2 = op2 + shl(b2, 12) + d2 + wputhw(op0) + if l1a then l1a() end + wputhw(op1) + if d1a then d1a() end + wputhw(op2) + if d2a then d2a() end + elseif p == "SS-b" then + local high_l = true + local d1, l1, b1, d1a, l1a = parse_mem_l2b(params[1], high_l) + high_l = false + local d2, l2, b2, d2a, l2a = parse_mem_l2b(params[2], high_l) + op0 = op0 + shl(l1, 4) + l2 + op1 = op1 + shl(b1, 12) + d1 + op2 = op2 + shl(b2, 12) + d2 + wputhw(op0) + if l1a then l1a() end + if l2a then l2a() end + wputhw(op1) + if d1a then d1a() end + wputhw(op2) + if d2a then d2a() end + else + werror("unrecognized encoding") + end +end + +function op_template(params, template, nparams) + if not params then return template:gsub("%x%x%x%x%x%x%x%x%x%x%x%x", "") end + -- Limit number of section buffer positions used by a single dasm_put(). + -- A single opcode needs a maximum of 5 positions. + if secpos+5 > maxsecpos then wflush() end + local lpos, apos, spos = #actlist, #actargs, secpos + local ok, err + for t in gmatch(template, "[^|]+") do + ok, err = pcall(parse_template, params, t, nparams) + if ok then return end + secpos = spos + actlist[lpos+1] = nil + actlist[lpos+2] = nil + actlist[lpos+3] = nil + actargs[apos+1] = nil + actargs[apos+2] = nil + actargs[apos+3] = nil + end + error(err, 0) +end +map_op[".template__"] = op_template +------------------------------------------------------------------------------ +-- 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_1"] = function(params) + if not params then return "[1-9] | ->global | =>pcexpr" end + if secpos+1 > maxsecpos then wflush() end + local mode, n, s = parse_label(params[1], true) + if mode == "EXT" then werror("bad label definition") end + waction("LABEL_"..mode, n, s, 1) +end +------------------------------------------------------------------------------ +-- Pseudo-opcodes for data storage. +map_op[".long_*"] = function(params) + if not params then return "imm..." end + for _, p in ipairs(params) do + local n = tonumber(p) + if not n then werror("bad immediate `"..p.."'") end + if n < 0 then n = n + 2^32 end + wputw(n) + if secpos+2 > maxsecpos then wflush() end + end +end +-- 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]) + 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", align-1, nil, 1) -- Action halfword is 2**n-1. + return + end + end + end + werror("bad alignment") +end +------------------------------------------------------------------------------ +-- 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 + -- 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 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", 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)) + 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 +------------------------------------------------------------------------------ diff --git a/src/host/buildvm.c b/src/host/buildvm.c index ec99e501..d460b314 100644 --- a/src/host/buildvm.c +++ b/src/host/buildvm.c @@ -67,6 +67,8 @@ static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type); #include "../dynasm/dasm_ppc.h" #elif LJ_TARGET_MIPS #include "../dynasm/dasm_mips.h" +#elif LJ_TARGET_S390X +#include "../dynasm/dasm_s390x.h" #else #error "No support for this architecture (yet)" #endif