Removed the JIT. Nobody was actually using it, and table-driven should achieve 80-90% of the perf.

pull/13171/head
Joshua Haberman 6 years ago
parent 2c26f60dbb
commit 84fb01ad0f
  1. 29
      BUILD
  2. 45
      tests/pb/test_decoder.cc
  3. 56
      third_party/dynasm/LICENSE
  4. 10
      third_party/dynasm/README.google
  5. 448
      third_party/dynasm/dasm_arm.h
  6. 949
      third_party/dynasm/dasm_arm.lua
  7. 415
      third_party/dynasm/dasm_mips.h
  8. 959
      third_party/dynasm/dasm_mips.lua
  9. 411
      third_party/dynasm/dasm_ppc.h
  10. 1230
      third_party/dynasm/dasm_ppc.lua
  11. 83
      third_party/dynasm/dasm_proto.h
  12. 12
      third_party/dynasm/dasm_x64.lua
  13. 470
      third_party/dynasm/dasm_x86.h
  14. 1931
      third_party/dynasm/dasm_x86.lua
  15. 1084
      third_party/dynasm/dynasm.lua
  16. 1
      third_party/protobuf
  17. 38
      upb/pb/compile_decoder.c
  18. 511
      upb/pb/compile_decoder_x64.c
  19. 1150
      upb/pb/compile_decoder_x64.dasc
  20. 1737
      upb/pb/compile_decoder_x64.h

29
BUILD

@ -67,15 +67,7 @@ cc_library(
"upb/pb/textprinter.c",
"upb/pb/varint.c",
"upb/pb/varint.int.h",
] + select({
":k8": [
"upb/pb/compile_decoder_x64.c",
"upb/pb/compile_decoder_x64.h",
"third_party/dynasm/dasm_proto.h",
"third_party/dynasm/dasm_x86.h",
],
"//conditions:default": [],
}),
],
hdrs = [
"upb/pb/decoder.h",
"upb/pb/encoder.h",
@ -85,10 +77,7 @@ cc_library(
"-std=c89",
"-pedantic",
"-Wno-long-long",
] + select({
":k8": ["-DUPB_USE_JIT_X64"],
"//conditions:default": [],
}),
],
deps = [
":upb",
],
@ -421,19 +410,6 @@ py_library(
srcs = ["tools/staleness_test_lib.py"],
)
genrule(
name = "make_dynasm_decoder",
srcs = [
"third_party/dynasm/dynasm.lua",
"third_party/dynasm/dasm_x64.lua",
"third_party/dynasm/dasm_x86.lua",
"upb/pb/compile_decoder_x64.dasc",
],
outs = ["generated/upb/pb/compile_decoder_x64.h"],
cmd = "LUA_PATH=third_party/dynasm/?.lua $(location @lua//:lua) third_party/dynasm/dynasm.lua -c upb/pb/compile_decoder_x64.dasc > $@",
tools = ["@lua"],
)
py_binary(
name = "make_cmakelists",
srcs = ["tools/make_cmakelists.py"],
@ -514,7 +490,6 @@ generated_file_staleness_test(
"google/protobuf/descriptor.upb.h",
"tests/json/test.proto.pb",
"upb/json/parser.c",
"upb/pb/compile_decoder_x64.h",
],
generated_pattern = "generated/%s",
)

@ -242,16 +242,8 @@ void indentbuf(string *buf, int depth) {
buf->append(2 * depth, ' ');
}
void check_stack_alignment() {
#ifdef UPB_USE_JIT_X64
void *rsp = __builtin_frame_address(0);
ASSERT(((uintptr_t)rsp % 16) == 0);
#endif
}
#define NUMERIC_VALUE_HANDLER(member, ctype, fmt) \
bool value_##member(int* depth, const uint32_t* num, ctype val) { \
check_stack_alignment(); \
indentbuf(&output, *depth); \
appendf(&output, "%" PRIu32 ":%" fmt "\n", *num, val); \
return true; \
@ -265,14 +257,12 @@ NUMERIC_VALUE_HANDLER(float, float, "g")
NUMERIC_VALUE_HANDLER(double, double, "g")
bool value_bool(int* depth, const uint32_t* num, bool val) {
check_stack_alignment();
indentbuf(&output, *depth);
appendf(&output, "%" PRIu32 ":%s\n", *num, val ? "true" : "false");
return true;
}
int* startstr(int* depth, const uint32_t* num, size_t size_hint) {
check_stack_alignment();
indentbuf(&output, *depth);
appendf(&output, "%" PRIu32 ":(%zu)\"", *num, size_hint);
return depth + 1;
@ -282,7 +272,6 @@ size_t value_string(int* depth, const uint32_t* num, const char* buf,
size_t n, const upb_bufhandle* handle) {
UPB_UNUSED(num);
UPB_UNUSED(depth);
check_stack_alignment();
output.append(buf, n);
ASSERT(handle == &global_handle);
return n;
@ -290,7 +279,6 @@ size_t value_string(int* depth, const uint32_t* num, const char* buf,
bool endstr(int* depth, const uint32_t* num) {
UPB_UNUSED(num);
check_stack_alignment();
output.append("\n");
indentbuf(&output, *depth);
appendf(&output, "%" PRIu32 ":\"\n", *num);
@ -298,7 +286,6 @@ bool endstr(int* depth, const uint32_t* num) {
}
int* startsubmsg(int* depth, const uint32_t* num) {
check_stack_alignment();
indentbuf(&output, *depth);
appendf(&output, "%" PRIu32 ":{\n", *num);
return depth + 1;
@ -306,14 +293,12 @@ int* startsubmsg(int* depth, const uint32_t* num) {
bool endsubmsg(int* depth, const uint32_t* num) {
UPB_UNUSED(num);
check_stack_alignment();
indentbuf(&output, *depth);
output.append("}\n");
return true;
}
int* startseq(int* depth, const uint32_t* num) {
check_stack_alignment();
indentbuf(&output, *depth);
appendf(&output, "%" PRIu32 ":[\n", *num);
return depth + 1;
@ -321,14 +306,12 @@ int* startseq(int* depth, const uint32_t* num) {
bool endseq(int* depth, const uint32_t* num) {
UPB_UNUSED(num);
check_stack_alignment();
indentbuf(&output, *depth);
output.append("]\n");
return true;
}
bool startmsg(int* depth) {
check_stack_alignment();
indentbuf(&output, *depth);
output.append("<\n");
return true;
@ -336,7 +319,6 @@ bool startmsg(int* depth) {
bool endmsg(int* depth, upb_status* status) {
UPB_UNUSED(status);
check_stack_alignment();
indentbuf(&output, *depth);
output.append(">\n");
return true;
@ -507,8 +489,6 @@ void do_run_decoder(VerboseParserEnvironment* env, upb::pb::DecoderPtr decoder,
if (filter_hash) {
fprintf(stderr, "RUNNING TEST CASE, hash=%x\n", testhash);
fprintf(stderr, "JIT on: %s\n",
global_method.is_native() ? "true" : "false");
fprintf(stderr, "Input (len=%u): ", (unsigned)proto.size());
PrintBinary(proto);
fprintf(stderr, "\n");
@ -1128,7 +1108,7 @@ void test_valid() {
void empty_callback(const void *closure, upb::Handlers* h_ptr) {}
void test_emptyhandlers(upb::SymbolTable* symtab, bool allowjit) {
void test_emptyhandlers(upb::SymbolTable* symtab) {
// Create an empty handlers to make sure that the decoder can handle empty
// messages.
HandlerRegisterData handlerdata;
@ -1137,8 +1117,6 @@ void test_emptyhandlers(upb::SymbolTable* symtab, bool allowjit) {
upb::HandlerCache handler_cache(empty_callback, &handlerdata);
upb::pb::CodeCache pb_code_cache(&handler_cache);
pb_code_cache.set_allow_jit(allowjit);
upb::MessageDefPtr md = upb::MessageDefPtr(Empty_getmsgdef(symtab->ptr()));
global_handlers = handler_cache.Get(md);
global_method = pb_code_cache.Get(md);
@ -1173,7 +1151,7 @@ void test_emptyhandlers(upb::SymbolTable* symtab, bool allowjit) {
}
}
void run_tests(bool use_jit) {
void run_tests() {
HandlerRegisterData handlerdata;
handlerdata.mode = test_mode;
@ -1181,26 +1159,15 @@ void run_tests(bool use_jit) {
upb::HandlerCache handler_cache(callback, &handlerdata);
upb::pb::CodeCache pb_code_cache(&handler_cache);
pb_code_cache.set_allow_jit(use_jit);
upb::MessageDefPtr md(DecoderTest_getmsgdef(symtab.ptr()));
global_handlers = handler_cache.Get(md);
global_method = pb_code_cache.Get(md);
ASSERT(use_jit == global_method.is_native());
completed = 0;
test_invalid();
test_valid();
test_emptyhandlers(&symtab, use_jit);
}
void run_test_suite() {
// Test without/with JIT.
run_tests(false);
#ifdef UPB_USE_JIT_X64
run_tests(true);
#endif
test_emptyhandlers(&symtab);
}
extern "C" {
@ -1216,16 +1183,16 @@ int run_tests(int argc, char *argv[]) {
count = &total;
total = 0;
test_mode = COUNT_ONLY;
run_test_suite();
run_tests();
count = &completed;
total *= 2; // NO_HANDLERS, ALL_HANDLERS.
test_mode = NO_HANDLERS;
run_test_suite();
run_tests();
test_mode = ALL_HANDLERS;
run_test_suite();
run_tests();
printf("All tests passed, %d assertions.\n", num_assertions);
return 0;

@ -1,56 +0,0 @@
===============================================================================
LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/
Copyright (C) 2005-2011 Mike Pall. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
===============================================================================
[ LuaJIT includes code from Lua 5.1/5.2, which has this license statement: ]
Copyright (C) 1994-2011 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================================================
[ LuaJIT includes code from dlmalloc, which has this license statement: ]
This is a version (aka dlmalloc) of malloc/free/realloc written by
Doug Lea and released to the public domain, as explained at
http://creativecommons.org/licenses/publicdomain
===============================================================================

@ -1,10 +0,0 @@
URL: http://repo.or.cz/w/luajit-2.0.git/tree/6c05739684527919293e25668589f17c35a7c129:/dynasm
Version: 6c05739684527919293e25668589f17c35a7c129
License: MIT
License File: LICENSE
Description:
Taken from the larger LuaJIT project, DynASM is a tiny preprocessor and
runtime for generating machine code at runtime.
Local Modifications:
No modifications.

@ -1,448 +0,0 @@
/*
** DynASM ARM encoding engine.
** Copyright (C) 2005-2012 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define DASM_ARCH "arm"
#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_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12,
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 int *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
static int dasm_imm12(unsigned int n)
{
int i;
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30))
if (n <= 255) return (int)(n + (i << 8));
return -1;
}
/* 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 int ins = *p++;
unsigned int action = (ins >> 16);
if (action >= DASM__MAX) {
ofs += 4;
} else {
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
switch (action) {
case DASM_STOP: goto stop;
case DASM_SECTION:
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
D->section = &D->sections[n]; goto stop;
case DASM_ESC: p++; ofs += 4; break;
case DASM_REL_EXT: break;
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
case DASM_REL_LG:
n = (ins & 2047) - 10; pl = D->lglabels + n;
if (n >= 0) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
pl += 10; n = *pl;
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
goto linkrel;
case DASM_REL_PC:
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;
}
pos++;
break;
case DASM_LABEL_LG:
pl = D->lglabels + (ins & 2047) - 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_IMM:
case DASM_IMM16:
#ifdef DASM_CHECKS
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
if ((ins & 0x8000))
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
else
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
#endif
b[pos++] = n;
break;
case DASM_IMML8:
case DASM_IMML12:
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) :
(((-n)>>((ins>>5)&31)) == 0), RANGE_I);
b[pos++] = n;
break;
case DASM_IMM12:
CK(dasm_imm12((unsigned int)n) != -1, 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 int ins = *p++;
unsigned int action = (ins >> 16);
switch (action) {
case DASM_STOP: case DASM_SECTION: goto stop;
case DASM_ESC: p++; break;
case DASM_REL_EXT: break;
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
case DASM_IMM: case DASM_IMM12: case DASM_IMM16:
case DASM_IMML8: case DASM_IMML12: 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 int *cp = (unsigned int *)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 int ins = *p++;
unsigned int action = (ins >> 16);
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, (ins&2047), !(ins&2048));
goto patchrel;
case DASM_ALIGN:
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000;
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) - 4;
patchrel:
if ((ins & 0x800) == 0) {
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL);
cp[-1] |= ((n >> 2) & 0x00ffffff);
} else if ((ins & 0x1000)) {
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL);
goto patchimml8;
} else {
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL);
goto patchimml12;
}
break;
case DASM_LABEL_LG:
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
break;
case DASM_LABEL_PC: break;
case DASM_IMM:
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
break;
case DASM_IMM12:
cp[-1] |= dasm_imm12((unsigned int)n);
break;
case DASM_IMM16:
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff);
break;
case DASM_IMML8: patchimml8:
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) :
((-n & 0x0f) | ((-n & 0xf0) << 4));
break;
case DASM_IMML12: patchimml12:
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n);
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

@ -1,949 +0,0 @@
------------------------------------------------------------------------------
-- DynASM ARM module.
--
-- Copyright (C) 2005-2012 Mike Pall. All rights reserved.
-- See dynasm.lua for full copyright notice.
------------------------------------------------------------------------------
-- Module information:
local _info = {
arch = "arm",
description = "DynASM ARM module",
version = "1.3.0",
vernum = 10300,
release = "2011-05-05",
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
-- 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", "IMM", "IMM12", "IMM16", "IMML8", "IMML12",
}
-- 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 = {}
for n,name in ipairs(action_names) do
map_action[name] = n-1
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
------------------------------------------------------------------------------
-- Return 8 digit hex number.
local function tohex(x)
return sub(format("%08x", x), -8) -- Avoid 64 bit portability problem in Lua.
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
if nn == 0 then nn = 1; actlist[0] = map_action.STOP end
out:write("static const unsigned int ", name, "[", nn, "] = {\n")
for i = 1,nn-1 do
assert(out:write("0x", tohex(actlist[i]), ",\n"))
end
assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n"))
end
------------------------------------------------------------------------------
-- Add word to action list.
local function wputxw(n)
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word 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.."'")
wputxw(w * 0x10000 + (val or 0))
if a then actargs[#actargs+1] = a end
if 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 word.
local function wputw(n)
if n <= 0x000fffff then waction("ESC") end
wputxw(n)
end
-- Reserve position for word.
local function wpos()
local pos = #actlist+1
actlist[pos] = ""
return pos
end
-- Store word to reserved position.
local function wputpos(pos, n)
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
if n <= 0x000fffff then
insert(actlist, pos+1, n)
n = map_action.ESC * 0x10000
end
actlist[pos] = n
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 = "r13", lr = "r14", pc = "r15", }
-- Int. register name -> ext. name.
local map_reg_rev = { r13 = "sp", r14 = "lr", r15 = "pc", }
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_shift = { lsl = 0, lsr = 1, asr = 2, ror = 3, }
local map_cond = {
eq = 0, ne = 1, cs = 2, cc = 3, mi = 4, pl = 5, vs = 6, vc = 7,
hi = 8, ls = 9, ge = 10, lt = 11, gt = 12, le = 13, al = 14,
hs = 2, lo = 3,
}
------------------------------------------------------------------------------
-- Template strings for ARM instructions.
local map_op = {
-- Basic data processing instructions.
and_3 = "e0000000DNPs",
eor_3 = "e0200000DNPs",
sub_3 = "e0400000DNPs",
rsb_3 = "e0600000DNPs",
add_3 = "e0800000DNPs",
adc_3 = "e0a00000DNPs",
sbc_3 = "e0c00000DNPs",
rsc_3 = "e0e00000DNPs",
tst_2 = "e1100000NP",
teq_2 = "e1300000NP",
cmp_2 = "e1500000NP",
cmn_2 = "e1700000NP",
orr_3 = "e1800000DNPs",
mov_2 = "e1a00000DPs",
bic_3 = "e1c00000DNPs",
mvn_2 = "e1e00000DPs",
and_4 = "e0000000DNMps",
eor_4 = "e0200000DNMps",
sub_4 = "e0400000DNMps",
rsb_4 = "e0600000DNMps",
add_4 = "e0800000DNMps",
adc_4 = "e0a00000DNMps",
sbc_4 = "e0c00000DNMps",
rsc_4 = "e0e00000DNMps",
tst_3 = "e1100000NMp",
teq_3 = "e1300000NMp",
cmp_3 = "e1500000NMp",
cmn_3 = "e1700000NMp",
orr_4 = "e1800000DNMps",
mov_3 = "e1a00000DMps",
bic_4 = "e1c00000DNMps",
mvn_3 = "e1e00000DMps",
lsl_3 = "e1a00000DMws",
lsr_3 = "e1a00020DMws",
asr_3 = "e1a00040DMws",
ror_3 = "e1a00060DMws",
rrx_2 = "e1a00060DMs",
-- Multiply and multiply-accumulate.
mul_3 = "e0000090NMSs",
mla_4 = "e0200090NMSDs",
umaal_4 = "e0400090DNMSs", -- v6
mls_4 = "e0600090DNMSs", -- v6T2
umull_4 = "e0800090DNMSs",
umlal_4 = "e0a00090DNMSs",
smull_4 = "e0c00090DNMSs",
smlal_4 = "e0e00090DNMSs",
-- Halfword multiply and multiply-accumulate.
smlabb_4 = "e1000080NMSD", -- v5TE
smlatb_4 = "e10000a0NMSD", -- v5TE
smlabt_4 = "e10000c0NMSD", -- v5TE
smlatt_4 = "e10000e0NMSD", -- v5TE
smlawb_4 = "e1200080NMSD", -- v5TE
smulwb_3 = "e12000a0NMS", -- v5TE
smlawt_4 = "e12000c0NMSD", -- v5TE
smulwt_3 = "e12000e0NMS", -- v5TE
smlalbb_4 = "e1400080NMSD", -- v5TE
smlaltb_4 = "e14000a0NMSD", -- v5TE
smlalbt_4 = "e14000c0NMSD", -- v5TE
smlaltt_4 = "e14000e0NMSD", -- v5TE
smulbb_3 = "e1600080NMS", -- v5TE
smultb_3 = "e16000a0NMS", -- v5TE
smulbt_3 = "e16000c0NMS", -- v5TE
smultt_3 = "e16000e0NMS", -- v5TE
-- Miscellaneous data processing instructions.
clz_2 = "e16f0f10DM", -- v5T
rev_2 = "e6bf0f30DM", -- v6
rev16_2 = "e6bf0fb0DM", -- v6
revsh_2 = "e6ff0fb0DM", -- v6
sel_3 = "e6800fb0DNM", -- v6
usad8_3 = "e780f010NMS", -- v6
usada8_4 = "e7800010NMSD", -- v6
rbit_2 = "e6ff0f30DM", -- v6T2
movw_2 = "e3000000DW", -- v6T2
movt_2 = "e3400000DW", -- v6T2
-- Note: the X encodes width-1, not width.
sbfx_4 = "e7a00050DMvX", -- v6T2
ubfx_4 = "e7e00050DMvX", -- v6T2
-- Note: the X encodes the msb field, not the width.
bfc_3 = "e7c0001fDvX", -- v6T2
bfi_4 = "e7c00010DMvX", -- v6T2
-- Packing and unpacking instructions.
pkhbt_3 = "e6800010DNM", pkhbt_4 = "e6800010DNMv", -- v6
pkhtb_3 = "e6800050DNM", pkhtb_4 = "e6800050DNMv", -- v6
sxtab_3 = "e6a00070DNM", sxtab_4 = "e6a00070DNMv", -- v6
sxtab16_3 = "e6800070DNM", sxtab16_4 = "e6800070DNMv", -- v6
sxtah_3 = "e6b00070DNM", sxtah_4 = "e6b00070DNMv", -- v6
sxtb_2 = "e6af0070DM", sxtb_3 = "e6af0070DMv", -- v6
sxtb16_2 = "e68f0070DM", sxtb16_3 = "e68f0070DMv", -- v6
sxth_2 = "e6bf0070DM", sxth_3 = "e6bf0070DMv", -- v6
uxtab_3 = "e6e00070DNM", uxtab_4 = "e6e00070DNMv", -- v6
uxtab16_3 = "e6c00070DNM", uxtab16_4 = "e6c00070DNMv", -- v6
uxtah_3 = "e6f00070DNM", uxtah_4 = "e6f00070DNMv", -- v6
uxtb_2 = "e6ef0070DM", uxtb_3 = "e6ef0070DMv", -- v6
uxtb16_2 = "e6cf0070DM", uxtb16_3 = "e6cf0070DMv", -- v6
uxth_2 = "e6ff0070DM", uxth_3 = "e6ff0070DMv", -- v6
-- Saturating instructions.
qadd_3 = "e1000050DMN", -- v5TE
qsub_3 = "e1200050DMN", -- v5TE
qdadd_3 = "e1400050DMN", -- v5TE
qdsub_3 = "e1600050DMN", -- v5TE
-- Note: the X for ssat* encodes sat_imm-1, not sat_imm.
ssat_3 = "e6a00010DXM", ssat_4 = "e6a00010DXMp", -- v6
usat_3 = "e6e00010DXM", usat_4 = "e6e00010DXMp", -- v6
ssat16_3 = "e6a00f30DXM", -- v6
usat16_3 = "e6e00f30DXM", -- v6
-- Parallel addition and subtraction.
sadd16_3 = "e6100f10DNM", -- v6
sasx_3 = "e6100f30DNM", -- v6
ssax_3 = "e6100f50DNM", -- v6
ssub16_3 = "e6100f70DNM", -- v6
sadd8_3 = "e6100f90DNM", -- v6
ssub8_3 = "e6100ff0DNM", -- v6
qadd16_3 = "e6200f10DNM", -- v6
qasx_3 = "e6200f30DNM", -- v6
qsax_3 = "e6200f50DNM", -- v6
qsub16_3 = "e6200f70DNM", -- v6
qadd8_3 = "e6200f90DNM", -- v6
qsub8_3 = "e6200ff0DNM", -- v6
shadd16_3 = "e6300f10DNM", -- v6
shasx_3 = "e6300f30DNM", -- v6
shsax_3 = "e6300f50DNM", -- v6
shsub16_3 = "e6300f70DNM", -- v6
shadd8_3 = "e6300f90DNM", -- v6
shsub8_3 = "e6300ff0DNM", -- v6
uadd16_3 = "e6500f10DNM", -- v6
uasx_3 = "e6500f30DNM", -- v6
usax_3 = "e6500f50DNM", -- v6
usub16_3 = "e6500f70DNM", -- v6
uadd8_3 = "e6500f90DNM", -- v6
usub8_3 = "e6500ff0DNM", -- v6
uqadd16_3 = "e6600f10DNM", -- v6
uqasx_3 = "e6600f30DNM", -- v6
uqsax_3 = "e6600f50DNM", -- v6
uqsub16_3 = "e6600f70DNM", -- v6
uqadd8_3 = "e6600f90DNM", -- v6
uqsub8_3 = "e6600ff0DNM", -- v6
uhadd16_3 = "e6700f10DNM", -- v6
uhasx_3 = "e6700f30DNM", -- v6
uhsax_3 = "e6700f50DNM", -- v6
uhsub16_3 = "e6700f70DNM", -- v6
uhadd8_3 = "e6700f90DNM", -- v6
uhsub8_3 = "e6700ff0DNM", -- v6
-- Load/store instructions.
str_2 = "e4000000DL", str_3 = "e4000000DL", str_4 = "e4000000DL",
strb_2 = "e4400000DL", strb_3 = "e4400000DL", strb_4 = "e4400000DL",
ldr_2 = "e4100000DL", ldr_3 = "e4100000DL", ldr_4 = "e4100000DL",
ldrb_2 = "e4500000DL", ldrb_3 = "e4500000DL", ldrb_4 = "e4500000DL",
strh_2 = "e00000b0DL", strh_3 = "e00000b0DL",
ldrh_2 = "e01000b0DL", ldrh_3 = "e01000b0DL",
ldrd_2 = "e00000d0DL", ldrd_3 = "e00000d0DL", -- v5TE
ldrsb_2 = "e01000d0DL", ldrsb_3 = "e01000d0DL",
strd_2 = "e00000f0DL", strd_3 = "e00000f0DL", -- v5TE
ldrsh_2 = "e01000f0DL", ldrsh_3 = "e01000f0DL",
ldm_2 = "e8900000nR", ldmia_2 = "e8900000nR", ldmfd_2 = "e8900000nR",
ldmda_2 = "e8100000nR", ldmfa_2 = "e8100000nR",
ldmdb_2 = "e9100000nR", ldmea_2 = "e9100000nR",
ldmib_2 = "e9900000nR", ldmed_2 = "e9900000nR",
stm_2 = "e8800000nR", stmia_2 = "e8800000nR", stmfd_2 = "e8800000nR",
stmda_2 = "e8000000nR", stmfa_2 = "e8000000nR",
stmdb_2 = "e9000000nR", stmea_2 = "e9000000nR",
stmib_2 = "e9800000nR", stmed_2 = "e9800000nR",
pop_1 = "e8bd0000R", push_1 = "e92d0000R",
-- Branch instructions.
b_1 = "ea000000B",
bl_1 = "eb000000B",
blx_1 = "e12fff30C",
bx_1 = "e12fff10M",
-- Miscellaneous instructions.
nop_0 = "e1a00000",
mrs_1 = "e10f0000D",
bkpt_1 = "e1200070K", -- v5T
svc_1 = "ef000000T", swi_1 = "ef000000T",
ud_0 = "e7f001f0",
-- NYI: Advanced SIMD and VFP instructions.
-- NYI instructions, since I have no need for them right now:
-- swp, swpb, strex, ldrex, strexd, ldrexd, strexb, ldrexb, strexh, ldrexh
-- msr, nopv6, yield, wfe, wfi, sev, dbg, bxj, smc, srs, rfe
-- cps, setend, pli, pld, pldw, clrex, dsb, dmb, isb
-- stc, ldc, mcr, mcr2, mrc, mrc2, mcrr, mcrr2, mrrc, mrrc2, cdp, cdp2
}
-- Add mnemonics for "s" variants.
do
local t = {}
for k,v in pairs(map_op) do
if sub(v, -1) == "s" then
local v2 = sub(v, 1, 2)..char(byte(v, 3)+1)..sub(v, 4, -2)
t[sub(k, 1, -3).."s"..sub(k, -2)] = v2
end
end
for k,v in pairs(t) do
map_op[k] = v
end
end
------------------------------------------------------------------------------
local function parse_gpr(expr)
local tname, ovreg = match(expr, "^([%w_]+):(r1?[0-9])$")
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, "^r(1?[0-9])$")
if r then
r = tonumber(r)
if r <= 15 then return r, tp end
end
werror("bad register name `"..expr.."'")
end
local function parse_gpr_pm(expr)
local pm, expr2 = match(expr, "^([+-]?)(.*)$")
return parse_gpr(expr2), (pm == "-")
end
local function parse_reglist(reglist)
reglist = match(reglist, "^{%s*([^}]*)}$")
if not reglist then werror("register list expected") end
local rr = 0
for p in gmatch(reglist..",", "%s*([^,]*),") do
local rbit = 2^parse_gpr(gsub(p, "%s+$", ""))
if ((rr - (rr % rbit)) / rbit) % 2 ~= 0 then
werror("duplicate register `"..p.."'")
end
rr = rr + rbit
end
return rr
end
local function parse_imm(imm, bits, shift, scale, signed)
imm = match(imm, "^#(.*)$")
if not imm then werror("expected immediate operand") end
local n = tonumber(imm)
if n then
if n % 2^scale == 0 then
n = n / 2^scale
if signed then
if n >= 0 then
if n < 2^(bits-1) then return n*2^shift end
else
if n >= -(2^(bits-1))-1 then return (n+2^bits)*2^shift end
end
else
if n >= 0 and n <= 2^bits-1 then return n*2^shift end
end
end
werror("out of range immediate `"..imm.."'")
else
waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm)
return 0
end
end
local function parse_imm12(imm)
local n = tonumber(imm)
if n then
local m = n
for i=0,-15,-1 do
if m >= 0 and m <= 255 and n % 1 == 0 then return m + (i%16) * 256 end
local t = m % 4
m = (m - t) / 4 + t * 2^30
end
werror("out of range immediate `"..imm.."'")
else
waction("IMM12", 0, imm)
return 0
end
end
local function parse_imm16(imm)
imm = match(imm, "^#(.*)$")
if not imm then werror("expected immediate operand") end
local n = tonumber(imm)
if n then
if n >= 0 and n <= 65535 and n % 1 == 0 then
local t = n % 4096
return (n - t) * 16 + t
end
werror("out of range immediate `"..imm.."'")
else
waction("IMM16", 32*16, imm)
return 0
end
end
local function parse_imm_load(imm, ext)
local n = tonumber(imm)
if n then
if ext then
if n >= -255 and n <= 255 then
local up = 0x00800000
if n < 0 then n = -n; up = 0 end
return (n-(n%16))*16+(n%16) + up
end
else
if n >= -4095 and n <= 4095 then
if n >= 0 then return n+0x00800000 end
return -n
end
end
werror("out of range immediate `"..imm.."'")
else
waction(ext and "IMML8" or "IMML12", 32768 + 32*(ext and 8 or 12), imm)
return 0
end
end
local function parse_shift(shift, gprok)
if shift == "rrx" then
return 3 * 32
else
local s, s2 = match(shift, "^(%S+)%s*(.*)$")
s = map_shift[s]
if not s then werror("expected shift operand") end
if sub(s2, 1, 1) == "#" then
return parse_imm(s2, 5, 7, 0, false) + s * 32
else
if not gprok then werror("expected immediate shift operand") end
return parse_gpr(s2) * 256 + s * 32 + 16
end
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 function parse_load(params, nparams, n, op)
local oplo = op % 256
local ext, ldrd = (oplo ~= 0), (oplo == 208)
local d
if (ldrd or oplo == 240) then
d = ((op - (op % 4096)) / 4096) % 16
if d % 2 ~= 0 then werror("odd destination register") end
end
local pn = params[n]
local p1, wb = match(pn, "^%[%s*(.-)%s*%](!?)$")
local p2 = params[n+1]
if not p1 then
if not p2 then
if match(pn, "^[<>=%-]") or match(pn, "^extern%s+") then
local mode, n, s = parse_label(pn, false)
waction("REL_"..mode, n + (ext and 0x1800 or 0x0800), s, 1)
return op + 15 * 65536 + 0x01000000 + (ext and 0x00400000 or 0)
end
local reg, tailr = match(pn, "^([%w_:]+)%s*(.*)$")
if reg and tailr ~= "" then
local d, tp = parse_gpr(reg)
if tp then
waction(ext and "IMML8" or "IMML12", 32768 + 32*(ext and 8 or 12),
format(tp.ctypefmt, tailr))
return op + d * 65536 + 0x01000000 + (ext and 0x00400000 or 0)
end
end
end
werror("expected address operand")
end
if wb == "!" then op = op + 0x00200000 end
if p2 then
if wb == "!" then werror("bad use of '!'") end
local p3 = params[n+2]
op = op + parse_gpr(p1) * 65536
local imm = match(p2, "^#(.*)$")
if imm then
local m = parse_imm_load(imm, ext)
if p3 then werror("too many parameters") end
op = op + m + (ext and 0x00400000 or 0)
else
local m, neg = parse_gpr_pm(p2)
if ldrd and (m == d or m-1 == d) then werror("register conflict") end
op = op + m + (neg and 0 or 0x00800000) + (ext and 0 or 0x02000000)
if p3 then op = op + parse_shift(p3) end
end
else
local p1a, p2 = match(p1, "^([^,%s]*)%s*(.*)$")
op = op + parse_gpr(p1a) * 65536 + 0x01000000
if p2 ~= "" then
local imm = match(p2, "^,%s*#(.*)$")
if imm then
local m = parse_imm_load(imm, ext)
op = op + m + (ext and 0x00400000 or 0)
else
local p2a, p3 = match(p2, "^,%s*([^,%s]*)%s*,?%s*(.*)$")
local m, neg = parse_gpr_pm(p2a)
if ldrd and (m == d or m-1 == d) then werror("register conflict") end
op = op + m + (neg and 0 or 0x00800000) + (ext and 0 or 0x02000000)
if p3 ~= "" then
if ext then werror("too many parameters") end
op = op + parse_shift(p3)
end
end
else
if wb == "!" then werror("bad use of '!'") end
op = op + (ext and 0x00c00000 or 0x00800000)
end
end
return op
end
------------------------------------------------------------------------------
-- Handle opcodes defined with template strings.
map_op[".template__"] = function(params, template, nparams)
if not params then return sub(template, 9) end
local op = tonumber(sub(template, 1, 8), 16)
local n = 1
-- Limit number of section buffer positions used by a single dasm_put().
-- A single opcode needs a maximum of 3 positions.
if secpos+3 > maxsecpos then wflush() end
local pos = wpos()
-- Process each character.
for p in gmatch(sub(template, 9), ".") do
if p == "D" then
op = op + parse_gpr(params[n]) * 4096; n = n + 1
elseif p == "N" then
op = op + parse_gpr(params[n]) * 65536; n = n + 1
elseif p == "S" then
op = op + parse_gpr(params[n]) * 256; n = n + 1
elseif p == "M" then
op = op + parse_gpr(params[n]); n = n + 1
elseif p == "P" then
local imm = match(params[n], "^#(.*)$")
if imm then
op = op + parse_imm12(imm) + 0x02000000
else
op = op + parse_gpr(params[n])
end
n = n + 1
elseif p == "p" then
op = op + parse_shift(params[n], true); n = n + 1
elseif p == "L" then
op = parse_load(params, nparams, n, op)
elseif p == "B" then
local mode, n, s = parse_label(params[n], false)
waction("REL_"..mode, n, s, 1)
elseif p == "C" then -- blx gpr vs. blx label.
local p = params[n]
if match(p, "^([%w_]+):(r1?[0-9])$") or match(p, "^r(1?[0-9])$") then
op = op + parse_gpr(p)
else
if op < 0xe0000000 then werror("unconditional instruction") end
local mode, n, s = parse_label(p, false)
waction("REL_"..mode, n, s, 1)
op = 0xfa000000
end
elseif p == "n" then
local r, wb = match(params[n], "^([^!]*)(!?)$")
op = op + parse_gpr(r) * 65536 + (wb == "!" and 0x00200000 or 0)
n = n + 1
elseif p == "R" then
op = op + parse_reglist(params[n]); n = n + 1
elseif p == "W" then
op = op + parse_imm16(params[n]); n = n + 1
elseif p == "v" then
op = op + parse_imm(params[n], 5, 7, 0, false); n = n + 1
elseif p == "w" then
local imm = match(params[n], "^#(.*)$")
if imm then
op = op + parse_imm(params[n], 5, 7, 0, false); n = n + 1
else
op = op + parse_gpr(params[n]) * 256 + 16
end
elseif p == "X" then
op = op + parse_imm(params[n], 5, 16, 0, false); n = n + 1
elseif p == "K" then
local imm = tonumber(match(params[n], "^#(.*)$")); n = n + 1
if not imm or imm % 1 ~= 0 or imm < 0 or imm > 0xffff then
werror("bad immediate operand")
end
local t = imm % 16
op = op + (imm - t) * 16 + t
elseif p == "T" then
op = op + parse_imm(params[n], 24, 0, 0, false); n = n + 1
elseif p == "s" then
-- Ignored.
else
assert(false)
end
end
wputpos(pos, op)
end
------------------------------------------------------------------------------
-- 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 byte 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 = function(t, k)
local v = map_coreop[k]
if v then return v end
local cc = sub(k, -4, -3)
local cv = map_cond[cc]
if cv then
local v = rawget(t, sub(k, 1, -5)..sub(k, -2))
if type(v) == "string" then return format("%x%s", cv, sub(v, 2)) end
end
end })
setmetatable(map_def, { __index = map_archdef })
return map_op, map_def
end
return _M
------------------------------------------------------------------------------

@ -1,415 +0,0 @@
/*
** DynASM MIPS encoding engine.
** Copyright (C) 2005-2012 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define DASM_ARCH "mips"
#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_IMM,
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 int *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 int ins = *p++;
unsigned int action = (ins >> 16) - 0xff00;
if (action >= DASM__MAX) {
ofs += 4;
} else {
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
switch (action) {
case DASM_STOP: goto stop;
case DASM_SECTION:
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
D->section = &D->sections[n]; goto stop;
case DASM_ESC: p++; ofs += 4; break;
case DASM_REL_EXT: break;
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
case DASM_REL_LG:
n = (ins & 2047) - 10; pl = D->lglabels + n;
if (n >= 0) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
pl += 10; n = *pl;
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
goto linkrel;
case DASM_REL_PC:
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;
}
pos++;
break;
case DASM_LABEL_LG:
pl = D->lglabels + (ins & 2047) - 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_IMM:
#ifdef DASM_CHECKS
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
#endif
n >>= ((ins>>10)&31);
#ifdef DASM_CHECKS
if (ins & 0x8000)
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
else
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
#endif
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 int ins = *p++;
unsigned int action = (ins >> 16) - 0xff00;
switch (action) {
case DASM_STOP: case DASM_SECTION: goto stop;
case DASM_ESC: p++; break;
case DASM_REL_EXT: break;
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
case DASM_IMM: 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 int *cp = (unsigned int *)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 int ins = *p++;
unsigned int action = (ins >> 16) - 0xff00;
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, (ins & 2047), 1);
goto patchrel;
case DASM_ALIGN:
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
break;
case DASM_REL_LG:
CK(n >= 0, UNDEF_LG);
case DASM_REL_PC:
CK(n >= 0, UNDEF_PC);
n = *DASM_POS2PTR(D, n);
if (ins & 2048)
n = n - (int)((char *)cp - base);
else
n = (n + (int)base) & 0x0fffffff;
patchrel:
CK((n & 3) == 0 &&
((n + ((ins & 2048) ? 0x00020000 : 0)) >>
((ins & 2048) ? 18 : 28)) == 0, RANGE_REL);
cp[-1] |= ((n>>2) & ((ins & 2048) ? 0x0000ffff: 0x03ffffff));
break;
case DASM_LABEL_LG:
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
break;
case DASM_LABEL_PC: break;
case DASM_IMM:
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
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

@ -1,959 +0,0 @@
------------------------------------------------------------------------------
-- DynASM MIPS module.
--
-- Copyright (C) 2005-2012 Mike Pall. All rights reserved.
-- See dynasm.lua for full copyright notice.
------------------------------------------------------------------------------
-- Module information:
local _info = {
arch = "mips",
description = "DynASM MIPS module",
version = "1.3.0",
vernum = 10300,
release = "2012-01-23",
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 = assert, setmetatable
local _s = string
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
local match, gmatch = _s.match, _s.gmatch
local concat, sort = table.concat, table.sort
-- 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", "IMM",
}
-- 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 = {}
for n,name in ipairs(action_names) do
map_action[name] = n-1
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
------------------------------------------------------------------------------
-- Return 8 digit hex number.
local function tohex(x)
return sub(format("%08x", x), -8) -- Avoid 64 bit portability problem in Lua.
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
if nn == 0 then nn = 1; actlist[0] = map_action.STOP end
out:write("static const unsigned int ", name, "[", nn, "] = {\n")
for i = 1,nn-1 do
assert(out:write("0x", tohex(actlist[i]), ",\n"))
end
assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n"))
end
------------------------------------------------------------------------------
-- Add word to action list.
local function wputxw(n)
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word 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.."'")
wputxw(0xff000000 + w * 0x10000 + (val or 0))
if a then actargs[#actargs+1] = a end
if 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 word.
local function wputw(n)
if n >= 0xff000000 then waction("ESC") end
wputxw(n)
end
-- Reserve position for word.
local function wpos()
local pos = #actlist+1
actlist[pos] = ""
return pos
end
-- Store word to reserved position.
local function wputpos(pos, n)
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
actlist[pos] = n
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.
local map_archdef = { sp="r29", ra="r31" } -- Ext. register name -> int. name.
local map_type = {} -- Type name -> { ctype, reg }
local ctypenum = 0 -- Type number (for Dt... macros).
-- Reverse defines for registers.
function _M.revdef(s)
if s == "r29" then return "sp"
elseif s == "r31" then return "ra" end
return s
end
------------------------------------------------------------------------------
-- Template strings for MIPS instructions.
local map_op = {
-- First-level opcodes.
j_1 = "08000000J",
jal_1 = "0c000000J",
b_1 = "10000000B",
beqz_2 = "10000000SB",
beq_3 = "10000000STB",
bnez_2 = "14000000SB",
bne_3 = "14000000STB",
blez_2 = "18000000SB",
bgtz_2 = "1c000000SB",
addi_3 = "20000000TSI",
li_2 = "24000000TI",
addiu_3 = "24000000TSI",
slti_3 = "28000000TSI",
sltiu_3 = "2c000000TSI",
andi_3 = "30000000TSU",
lu_2 = "34000000TU",
ori_3 = "34000000TSU",
xori_3 = "38000000TSU",
lui_2 = "3c000000TU",
beqzl_2 = "50000000SB",
beql_3 = "50000000STB",
bnezl_2 = "54000000SB",
bnel_3 = "54000000STB",
blezl_2 = "58000000SB",
bgtzl_2 = "5c000000SB",
lb_2 = "80000000TO",
lh_2 = "84000000TO",
lwl_2 = "88000000TO",
lw_2 = "8c000000TO",
lbu_2 = "90000000TO",
lhu_2 = "94000000TO",
lwr_2 = "98000000TO",
sb_2 = "a0000000TO",
sh_2 = "a4000000TO",
swl_2 = "a8000000TO",
sw_2 = "ac000000TO",
swr_2 = "b8000000TO",
cache_2 = "bc000000NO",
ll_2 = "c0000000TO",
lwc1_2 = "c4000000HO",
pref_2 = "cc000000NO",
ldc1_2 = "d4000000HO",
sc_2 = "e0000000TO",
swc1_2 = "e4000000HO",
sdc1_2 = "f4000000HO",
-- Opcode SPECIAL.
nop_0 = "00000000",
sll_3 = "00000000DTA",
movf_2 = "00000001DS",
movf_3 = "00000001DSC",
movt_2 = "00010001DS",
movt_3 = "00010001DSC",
srl_3 = "00000002DTA",
rotr_3 = "00200002DTA",
sra_3 = "00000003DTA",
sllv_3 = "00000004DTS",
srlv_3 = "00000006DTS",
rotrv_3 = "00000046DTS",
srav_3 = "00000007DTS",
jr_1 = "00000008S",
jalr_1 = "0000f809S",
jalr_2 = "00000009DS",
movz_3 = "0000000aDST",
movn_3 = "0000000bDST",
syscall_0 = "0000000c",
syscall_1 = "0000000cY",
break_0 = "0000000d",
break_1 = "0000000dY",
sync_0 = "0000000f",
mfhi_1 = "00000010D",
mthi_1 = "00000011S",
mflo_1 = "00000012D",
mtlo_1 = "00000013S",
mult_2 = "00000018ST",
multu_2 = "00000019ST",
div_2 = "0000001aST",
divu_2 = "0000001bST",
add_3 = "00000020DST",
move_2 = "00000021DS",
addu_3 = "00000021DST",
sub_3 = "00000022DST",
negu_2 = "00000023DT",
subu_3 = "00000023DST",
and_3 = "00000024DST",
or_3 = "00000025DST",
xor_3 = "00000026DST",
not_2 = "00000027DS",
nor_3 = "00000027DST",
slt_3 = "0000002aDST",
sltu_3 = "0000002bDST",
tge_2 = "00000030ST",
tge_3 = "00000030STZ",
tgeu_2 = "00000031ST",
tgeu_3 = "00000031STZ",
tlt_2 = "00000032ST",
tlt_3 = "00000032STZ",
tltu_2 = "00000033ST",
tltu_3 = "00000033STZ",
teq_2 = "00000034ST",
teq_3 = "00000034STZ",
tne_2 = "00000036ST",
tne_3 = "00000036STZ",
-- Opcode REGIMM.
bltz_2 = "04000000SB",
bgez_2 = "04010000SB",
bltzl_2 = "04020000SB",
bgezl_2 = "04030000SB",
tgei_2 = "04080000SI",
tgeiu_2 = "04090000SI",
tlti_2 = "040a0000SI",
tltiu_2 = "040b0000SI",
teqi_2 = "040c0000SI",
tnei_2 = "040e0000SI",
bltzal_2 = "04100000SB",
bal_1 = "04110000B",
bgezal_2 = "04110000SB",
bltzall_2 = "04120000SB",
bgezall_2 = "04130000SB",
synci_1 = "041f0000O",
-- Opcode SPECIAL2.
madd_2 = "70000000ST",
maddu_2 = "70000001ST",
mul_3 = "70000002DST",
msub_2 = "70000004ST",
msubu_2 = "70000005ST",
clz_2 = "70000020DS=",
clo_2 = "70000021DS=",
sdbbp_0 = "7000003f",
sdbbp_1 = "7000003fY",
-- Opcode SPECIAL3.
ext_4 = "7c000000TSAM", -- Note: last arg is msbd = size-1
ins_4 = "7c000004TSAM", -- Note: last arg is msb = pos+size-1
wsbh_2 = "7c0000a0DT",
seb_2 = "7c000420DT",
seh_2 = "7c000620DT",
rdhwr_2 = "7c00003bTD",
-- Opcode COP0.
mfc0_2 = "40000000TD",
mfc0_3 = "40000000TDW",
mtc0_2 = "40800000TD",
mtc0_3 = "40800000TDW",
rdpgpr_2 = "41400000DT",
di_0 = "41606000",
di_1 = "41606000T",
ei_0 = "41606020",
ei_1 = "41606020T",
wrpgpr_2 = "41c00000DT",
tlbr_0 = "42000001",
tlbwi_0 = "42000002",
tlbwr_0 = "42000006",
tlbp_0 = "42000008",
eret_0 = "42000018",
deret_0 = "4200001f",
wait_0 = "42000020",
-- Opcode COP1.
mfc1_2 = "44000000TG",
cfc1_2 = "44400000TG",
mfhc1_2 = "44600000TG",
mtc1_2 = "44800000TG",
ctc1_2 = "44c00000TG",
mthc1_2 = "44e00000TG",
bc1f_1 = "45000000B",
bc1f_2 = "45000000CB",
bc1t_1 = "45010000B",
bc1t_2 = "45010000CB",
bc1fl_1 = "45020000B",
bc1fl_2 = "45020000CB",
bc1tl_1 = "45030000B",
bc1tl_2 = "45030000CB",
["add.s_3"] = "46000000FGH",
["sub.s_3"] = "46000001FGH",
["mul.s_3"] = "46000002FGH",
["div.s_3"] = "46000003FGH",
["sqrt.s_2"] = "46000004FG",
["abs.s_2"] = "46000005FG",
["mov.s_2"] = "46000006FG",
["neg.s_2"] = "46000007FG",
["round.l.s_2"] = "46000008FG",
["trunc.l.s_2"] = "46000009FG",
["ceil.l.s_2"] = "4600000aFG",
["floor.l.s_2"] = "4600000bFG",
["round.w.s_2"] = "4600000cFG",
["trunc.w.s_2"] = "4600000dFG",
["ceil.w.s_2"] = "4600000eFG",
["floor.w.s_2"] = "4600000fFG",
["movf.s_2"] = "46000011FG",
["movf.s_3"] = "46000011FGC",
["movt.s_2"] = "46010011FG",
["movt.s_3"] = "46010011FGC",
["movz.s_3"] = "46000012FGT",
["movn.s_3"] = "46000013FGT",
["recip.s_2"] = "46000015FG",
["rsqrt.s_2"] = "46000016FG",
["cvt.d.s_2"] = "46000021FG",
["cvt.w.s_2"] = "46000024FG",
["cvt.l.s_2"] = "46000025FG",
["cvt.ps.s_3"] = "46000026FGH",
["c.f.s_2"] = "46000030GH",
["c.f.s_3"] = "46000030VGH",
["c.un.s_2"] = "46000031GH",
["c.un.s_3"] = "46000031VGH",
["c.eq.s_2"] = "46000032GH",
["c.eq.s_3"] = "46000032VGH",
["c.ueq.s_2"] = "46000033GH",
["c.ueq.s_3"] = "46000033VGH",
["c.olt.s_2"] = "46000034GH",
["c.olt.s_3"] = "46000034VGH",
["c.ult.s_2"] = "46000035GH",
["c.ult.s_3"] = "46000035VGH",
["c.ole.s_2"] = "46000036GH",
["c.ole.s_3"] = "46000036VGH",
["c.ule.s_2"] = "46000037GH",
["c.ule.s_3"] = "46000037VGH",
["c.sf.s_2"] = "46000038GH",
["c.sf.s_3"] = "46000038VGH",
["c.ngle.s_2"] = "46000039GH",
["c.ngle.s_3"] = "46000039VGH",
["c.seq.s_2"] = "4600003aGH",
["c.seq.s_3"] = "4600003aVGH",
["c.ngl.s_2"] = "4600003bGH",
["c.ngl.s_3"] = "4600003bVGH",
["c.lt.s_2"] = "4600003cGH",
["c.lt.s_3"] = "4600003cVGH",
["c.nge.s_2"] = "4600003dGH",
["c.nge.s_3"] = "4600003dVGH",
["c.le.s_2"] = "4600003eGH",
["c.le.s_3"] = "4600003eVGH",
["c.ngt.s_2"] = "4600003fGH",
["c.ngt.s_3"] = "4600003fVGH",
["add.d_3"] = "46200000FGH",
["sub.d_3"] = "46200001FGH",
["mul.d_3"] = "46200002FGH",
["div.d_3"] = "46200003FGH",
["sqrt.d_2"] = "46200004FG",
["abs.d_2"] = "46200005FG",
["mov.d_2"] = "46200006FG",
["neg.d_2"] = "46200007FG",
["round.l.d_2"] = "46200008FG",
["trunc.l.d_2"] = "46200009FG",
["ceil.l.d_2"] = "4620000aFG",
["floor.l.d_2"] = "4620000bFG",
["round.w.d_2"] = "4620000cFG",
["trunc.w.d_2"] = "4620000dFG",
["ceil.w.d_2"] = "4620000eFG",
["floor.w.d_2"] = "4620000fFG",
["movf.d_2"] = "46200011FG",
["movf.d_3"] = "46200011FGC",
["movt.d_2"] = "46210011FG",
["movt.d_3"] = "46210011FGC",
["movz.d_3"] = "46200012FGT",
["movn.d_3"] = "46200013FGT",
["recip.d_2"] = "46200015FG",
["rsqrt.d_2"] = "46200016FG",
["cvt.s.d_2"] = "46200020FG",
["cvt.w.d_2"] = "46200024FG",
["cvt.l.d_2"] = "46200025FG",
["c.f.d_2"] = "46200030GH",
["c.f.d_3"] = "46200030VGH",
["c.un.d_2"] = "46200031GH",
["c.un.d_3"] = "46200031VGH",
["c.eq.d_2"] = "46200032GH",
["c.eq.d_3"] = "46200032VGH",
["c.ueq.d_2"] = "46200033GH",
["c.ueq.d_3"] = "46200033VGH",
["c.olt.d_2"] = "46200034GH",
["c.olt.d_3"] = "46200034VGH",
["c.ult.d_2"] = "46200035GH",
["c.ult.d_3"] = "46200035VGH",
["c.ole.d_2"] = "46200036GH",
["c.ole.d_3"] = "46200036VGH",
["c.ule.d_2"] = "46200037GH",
["c.ule.d_3"] = "46200037VGH",
["c.sf.d_2"] = "46200038GH",
["c.sf.d_3"] = "46200038VGH",
["c.ngle.d_2"] = "46200039GH",
["c.ngle.d_3"] = "46200039VGH",
["c.seq.d_2"] = "4620003aGH",
["c.seq.d_3"] = "4620003aVGH",
["c.ngl.d_2"] = "4620003bGH",
["c.ngl.d_3"] = "4620003bVGH",
["c.lt.d_2"] = "4620003cGH",
["c.lt.d_3"] = "4620003cVGH",
["c.nge.d_2"] = "4620003dGH",
["c.nge.d_3"] = "4620003dVGH",
["c.le.d_2"] = "4620003eGH",
["c.le.d_3"] = "4620003eVGH",
["c.ngt.d_2"] = "4620003fGH",
["c.ngt.d_3"] = "4620003fVGH",
["add.ps_3"] = "46c00000FGH",
["sub.ps_3"] = "46c00001FGH",
["mul.ps_3"] = "46c00002FGH",
["abs.ps_2"] = "46c00005FG",
["mov.ps_2"] = "46c00006FG",
["neg.ps_2"] = "46c00007FG",
["movf.ps_2"] = "46c00011FG",
["movf.ps_3"] = "46c00011FGC",
["movt.ps_2"] = "46c10011FG",
["movt.ps_3"] = "46c10011FGC",
["movz.ps_3"] = "46c00012FGT",
["movn.ps_3"] = "46c00013FGT",
["cvt.s.pu_2"] = "46c00020FG",
["cvt.s.pl_2"] = "46c00028FG",
["pll.ps_3"] = "46c0002cFGH",
["plu.ps_3"] = "46c0002dFGH",
["pul.ps_3"] = "46c0002eFGH",
["puu.ps_3"] = "46c0002fFGH",
["c.f.ps_2"] = "46c00030GH",
["c.f.ps_3"] = "46c00030VGH",
["c.un.ps_2"] = "46c00031GH",
["c.un.ps_3"] = "46c00031VGH",
["c.eq.ps_2"] = "46c00032GH",
["c.eq.ps_3"] = "46c00032VGH",
["c.ueq.ps_2"] = "46c00033GH",
["c.ueq.ps_3"] = "46c00033VGH",
["c.olt.ps_2"] = "46c00034GH",
["c.olt.ps_3"] = "46c00034VGH",
["c.ult.ps_2"] = "46c00035GH",
["c.ult.ps_3"] = "46c00035VGH",
["c.ole.ps_2"] = "46c00036GH",
["c.ole.ps_3"] = "46c00036VGH",
["c.ule.ps_2"] = "46c00037GH",
["c.ule.ps_3"] = "46c00037VGH",
["c.sf.ps_2"] = "46c00038GH",
["c.sf.ps_3"] = "46c00038VGH",
["c.ngle.ps_2"] = "46c00039GH",
["c.ngle.ps_3"] = "46c00039VGH",
["c.seq.ps_2"] = "46c0003aGH",
["c.seq.ps_3"] = "46c0003aVGH",
["c.ngl.ps_2"] = "46c0003bGH",
["c.ngl.ps_3"] = "46c0003bVGH",
["c.lt.ps_2"] = "46c0003cGH",
["c.lt.ps_3"] = "46c0003cVGH",
["c.nge.ps_2"] = "46c0003dGH",
["c.nge.ps_3"] = "46c0003dVGH",
["c.le.ps_2"] = "46c0003eGH",
["c.le.ps_3"] = "46c0003eVGH",
["c.ngt.ps_2"] = "46c0003fGH",
["c.ngt.ps_3"] = "46c0003fVGH",
["cvt.s.w_2"] = "46800020FG",
["cvt.d.w_2"] = "46800021FG",
["cvt.s.l_2"] = "46a00020FG",
["cvt.d.l_2"] = "46a00021FG",
-- Opcode COP1X.
lwxc1_2 = "4c000000FX",
ldxc1_2 = "4c000001FX",
luxc1_2 = "4c000005FX",
swxc1_2 = "4c000008FX",
sdxc1_2 = "4c000009FX",
suxc1_2 = "4c00000dFX",
prefx_2 = "4c00000fMX",
["alnv.ps_4"] = "4c00001eFGHS",
["madd.s_4"] = "4c000020FRGH",
["madd.d_4"] = "4c000021FRGH",
["madd.ps_4"] = "4c000026FRGH",
["msub.s_4"] = "4c000028FRGH",
["msub.d_4"] = "4c000029FRGH",
["msub.ps_4"] = "4c00002eFRGH",
["nmadd.s_4"] = "4c000030FRGH",
["nmadd.d_4"] = "4c000031FRGH",
["nmadd.ps_4"] = "4c000036FRGH",
["nmsub.s_4"] = "4c000038FRGH",
["nmsub.d_4"] = "4c000039FRGH",
["nmsub.ps_4"] = "4c00003eFRGH",
}
------------------------------------------------------------------------------
local function parse_gpr(expr)
local tname, ovreg = match(expr, "^([%w_]+):(r[1-3]?[0-9])$")
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, "^r([1-3]?[0-9])$")
if r then
r = tonumber(r)
if r <= 31 then return r, tp end
end
werror("bad register name `"..expr.."'")
end
local function parse_fpr(expr)
local r = match(expr, "^f([1-3]?[0-9])$")
if r then
r = tonumber(r)
if r <= 31 then return r end
end
werror("bad register name `"..expr.."'")
end
local function parse_imm(imm, bits, shift, scale, signed)
local n = tonumber(imm)
if n then
if n % 2^scale == 0 then
n = n / 2^scale
if signed then
if n >= 0 then
if n < 2^(bits-1) then return n*2^shift end
else
if n >= -(2^(bits-1))-1 then return (n+2^bits)*2^shift end
end
else
if n >= 0 and n <= 2^bits-1 then return n*2^shift end
end
end
werror("out of range immediate `"..imm.."'")
elseif match(imm, "^[rf]([1-3]?[0-9])$") or
match(imm, "^([%w_]+):([rf][1-3]?[0-9])$") then
werror("expected immediate operand, got register")
else
waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm)
return 0
end
end
local function parse_disp(disp)
local imm, reg = match(disp, "^(.*)%(([%w_:]+)%)$")
if imm then
local r = parse_gpr(reg)*2^21
local extname = match(imm, "^extern%s+(%S+)$")
if extname then
waction("REL_EXT", map_extern[extname], nil, 1)
return r
else
return r + parse_imm(imm, 16, 0, 0, true)
end
end
local reg, tailr = match(disp, "^([%w_:]+)%s*(.*)$")
if reg and tailr ~= "" then
local r, tp = parse_gpr(reg)
if tp then
waction("IMM", 32768+16*32, format(tp.ctypefmt, tailr))
return r*2^21
end
end
werror("bad displacement `"..disp.."'")
end
local function parse_index(idx)
local rt, rs = match(idx, "^(.*)%(([%w_:]+)%)$")
if rt then
rt = parse_gpr(rt)
rs = parse_gpr(rs)
return rt*2^16 + rs*2^21
end
werror("bad index `"..idx.."'")
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
------------------------------------------------------------------------------
-- Handle opcodes defined with template strings.
map_op[".template__"] = function(params, template, nparams)
if not params then return sub(template, 9) end
local op = tonumber(sub(template, 1, 8), 16)
local n = 1
-- Limit number of section buffer positions used by a single dasm_put().
-- A single opcode needs a maximum of 2 positions (ins/ext).
if secpos+2 > maxsecpos then wflush() end
local pos = wpos()
-- Process each character.
for p in gmatch(sub(template, 9), ".") do
if p == "D" then
op = op + parse_gpr(params[n]) * 2^11; n = n + 1
elseif p == "T" then
op = op + parse_gpr(params[n]) * 2^16; n = n + 1
elseif p == "S" then
op = op + parse_gpr(params[n]) * 2^21; n = n + 1
elseif p == "F" then
op = op + parse_fpr(params[n]) * 2^6; n = n + 1
elseif p == "G" then
op = op + parse_fpr(params[n]) * 2^11; n = n + 1
elseif p == "H" then
op = op + parse_fpr(params[n]) * 2^16; n = n + 1
elseif p == "R" then
op = op + parse_fpr(params[n]) * 2^21; n = n + 1
elseif p == "I" then
op = op + parse_imm(params[n], 16, 0, 0, true); n = n + 1
elseif p == "U" then
op = op + parse_imm(params[n], 16, 0, 0, false); n = n + 1
elseif p == "O" then
op = op + parse_disp(params[n]); n = n + 1
elseif p == "X" then
op = op + parse_index(params[n]); n = n + 1
elseif p == "B" or p == "J" then
local mode, n, s = parse_label(params[n], false)
if p == "B" then n = n + 2048 end
waction("REL_"..mode, n, s, 1)
n = n + 1
elseif p == "A" then
op = op + parse_imm(params[n], 5, 6, 0, false); n = n + 1
elseif p == "M" then
op = op + parse_imm(params[n], 5, 11, 0, false); n = n + 1
elseif p == "N" then
op = op + parse_imm(params[n], 5, 16, 0, false); n = n + 1
elseif p == "C" then
op = op + parse_imm(params[n], 3, 18, 0, false); n = n + 1
elseif p == "V" then
op = op + parse_imm(params[n], 3, 8, 0, false); n = n + 1
elseif p == "W" then
op = op + parse_imm(params[n], 3, 0, 0, false); n = n + 1
elseif p == "Y" then
op = op + parse_imm(params[n], 20, 6, 0, false); n = n + 1
elseif p == "Z" then
op = op + parse_imm(params[n], 10, 6, 0, false); n = n + 1
elseif p == "=" then
local d = ((op - op % 2^11) / 2^11) % 32
op = op + d * 2^16 -- Copy D to T for clz, clo.
else
assert(false)
end
end
wputpos(pos, op)
end
------------------------------------------------------------------------------
-- 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 byte 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
------------------------------------------------------------------------------

@ -1,411 +0,0 @@
/*
** DynASM PPC encoding engine.
** Copyright (C) 2005-2012 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define DASM_ARCH "ppc"
#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_IMM,
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 int *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 int ins = *p++;
unsigned int action = (ins >> 16);
if (action >= DASM__MAX) {
ofs += 4;
} else {
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
switch (action) {
case DASM_STOP: goto stop;
case DASM_SECTION:
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
D->section = &D->sections[n]; goto stop;
case DASM_ESC: p++; ofs += 4; break;
case DASM_REL_EXT: break;
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
case DASM_REL_LG:
n = (ins & 2047) - 10; pl = D->lglabels + n;
if (n >= 0) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
pl += 10; n = *pl;
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
goto linkrel;
case DASM_REL_PC:
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;
}
pos++;
break;
case DASM_LABEL_LG:
pl = D->lglabels + (ins & 2047) - 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_IMM:
#ifdef DASM_CHECKS
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
#endif
n >>= ((ins>>10)&31);
#ifdef DASM_CHECKS
if (ins & 0x8000)
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
else
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
#endif
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 int ins = *p++;
unsigned int action = (ins >> 16);
switch (action) {
case DASM_STOP: case DASM_SECTION: goto stop;
case DASM_ESC: p++; break;
case DASM_REL_EXT: break;
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
case DASM_IMM: 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 int *cp = (unsigned int *)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 int ins = *p++;
unsigned int action = (ins >> 16);
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, (ins & 2047), 1) - 4;
goto patchrel;
case DASM_ALIGN:
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
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);
patchrel:
CK((n & 3) == 0 &&
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >>
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL);
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc));
break;
case DASM_LABEL_LG:
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
break;
case DASM_LABEL_PC: break;
case DASM_IMM:
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
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

File diff suppressed because it is too large Load Diff

@ -1,83 +0,0 @@
/*
** DynASM encoding engine prototypes.
** Copyright (C) 2005-2012 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#ifndef _DASM_PROTO_H
#define _DASM_PROTO_H
#include <stddef.h>
#include <stdarg.h>
#define DASM_IDENT "DynASM 1.3.0"
#define DASM_VERSION 10300 /* 1.3.0 */
#ifndef Dst_DECL
#define Dst_DECL dasm_State **Dst
#endif
#ifndef Dst_REF
#define Dst_REF (*Dst)
#endif
#ifndef DASM_FDEF
#define DASM_FDEF extern
#endif
#ifndef DASM_M_GROW
#define DASM_M_GROW(ctx, t, p, sz, need) \
do { \
size_t _sz = (sz), _need = (need); \
if (_sz < _need) { \
if (_sz < 16) _sz = 16; \
while (_sz < _need) _sz += _sz; \
(p) = (t *)realloc((p), _sz); \
if ((p) == NULL) exit(1); \
(sz) = _sz; \
} \
} while(0)
#endif
#ifndef DASM_M_FREE
#define DASM_M_FREE(ctx, p, sz) free(p)
#endif
/* Internal DynASM encoder state. */
typedef struct dasm_State dasm_State;
/* Initialize and free DynASM state. */
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
DASM_FDEF void dasm_free(Dst_DECL);
/* Setup global array. Must be called before dasm_setup(). */
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
/* Grow PC label array. Can be called after dasm_setup(), too. */
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
/* Setup encoder. */
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist);
/* Feed encoder with actions. Calls are generated by pre-processor. */
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
/* Link sections and return the resulting size. */
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
/* Encode sections into buffer. */
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
/* Get PC label offset. */
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
#ifdef DASM_CHECKS
/* Optional sanity checker to call between isolated encoding steps. */
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
#else
#define dasm_checkstep(a, b) 0
#endif
#endif /* _DASM_PROTO_H */

@ -1,12 +0,0 @@
------------------------------------------------------------------------------
-- DynASM x64 module.
--
-- Copyright (C) 2005-2012 Mike Pall. All rights reserved.
-- See dynasm.lua for full copyright notice.
------------------------------------------------------------------------------
-- This module just sets 64 bit mode for the combined x86/x64 module.
-- All the interesting stuff is there.
------------------------------------------------------------------------------
x64 = true -- Using a global is an ugly, but effective solution.
return require("dasm_x86")

@ -1,470 +0,0 @@
/*
** DynASM x86 encoding engine.
** Copyright (C) 2005-2012 Mike Pall. All rights reserved.
** Released under the MIT license. See dynasm.lua for full copyright notice.
*/
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define DASM_ARCH "x86"
#ifndef DASM_EXTERN
#define DASM_EXTERN(a,b,c,d) 0
#endif
/* Action definitions. DASM_STOP must be 255. */
enum {
DASM_DISP = 233,
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
};
/* 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_VREG 0x15000000
#define DASM_S_UNDEF_L 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 char *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|(int)(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|(int)(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, mrm = 4;
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) {
int action = *p++;
if (action < DASM_DISP) {
ofs++;
} else if (action <= DASM_REL_A) {
int n = va_arg(ap, int);
b[pos++] = n;
switch (action) {
case DASM_DISP:
if (n == 0) { if ((mrm&7) == 4) mrm = p[-2]; if ((mrm&7) != 5) break; }
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob;
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */
case DASM_IMM_D: ofs += 4; break;
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob;
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break;
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob;
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break;
case DASM_SPACE: p++; ofs += n; break;
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */
case DASM_VREG: CK((n&-8) == 0 && (n != 4 || (*p&1) == 0), RANGE_VREG);
if (*p++ == 1 && *p == DASM_DISP) mrm = n; continue;
}
mrm = 4;
} else {
int *pl, n;
switch (action) {
case DASM_REL_LG:
case DASM_IMM_LG:
n = *p++; pl = D->lglabels + n;
if (n <= 246) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
pl -= 246; n = *pl;
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
goto linkrel;
case DASM_REL_PC:
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); 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;
}
pos++;
ofs += 4; /* Maximum offset needed. */
if (action == DASM_REL_LG || action == DASM_REL_PC)
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel;
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); 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_ALIGN:
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_EXTERN: p += 2; ofs += 4; break;
case DASM_ESC: p++; ofs++; break;
case DASM_MARK: mrm = p[-2]; break;
case DASM_SECTION:
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n];
case DASM_STOP: goto stop;
}
}
}
stop:
va_end(ap);
sec->pos = pos;
sec->ofs = ofs;
}
#undef CK
/* Pass 2: Link sections, shrink branches/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 = 10; 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) {
int op, action = *p++;
switch (action) {
case DASM_REL_LG: p++; op = p[-3]; goto rel_pc;
case DASM_REL_PC: op = p[-2]; rel_pc: {
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0);
if (shrink) { /* Shrinkable branch opcode? */
int lofs, lpos = b[pos];
if (lpos < 0) goto noshrink; /* Ext global? */
lofs = *DASM_POS2PTR(D, lpos);
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */
int i;
for (i = secnum; i < DASM_POS2SEC(lpos); i++)
lofs += D->sections[i].ofs;
} else {
lofs -= ofs; /* Bkwd label: unfix offset. */
}
lofs -= b[pos+1]; /* Short branch ok? */
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */
else { noshrink: shrink = 0; } /* No, cannot shrink op. */
}
b[pos+1] = shrink;
pos += 2;
break;
}
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++;
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W:
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB:
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break;
case DASM_LABEL_LG: p++;
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */
case DASM_EXTERN: p += 2; break;
case DASM_ESC: p++; break;
case DASM_MARK: break;
case DASM_SECTION: case DASM_STOP: goto stop;
}
}
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;
}
#define dasmb(x) *cp++ = (unsigned char)(x)
#ifndef DASM_ALIGNED_WRITES
#define dasmw(x) \
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
#define dasmd(x) \
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
#else
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
#endif
/* Pass 3: Encode sections. */
int dasm_encode(Dst_DECL, void *buffer)
{
dasm_State *D = Dst_REF;
unsigned char *base = (unsigned char *)buffer;
unsigned char *cp = base;
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++;
unsigned char *mark = NULL;
while (1) {
int action = *p++;
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0;
switch (action) {
case DASM_DISP: if (!mark) mark = cp; {
unsigned char *mm = mark;
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL;
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7;
if (mrm != 5) { mm[-1] -= 0x80; break; } }
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40;
}
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break;
case DASM_IMM_DB: if (((n+128)&-256) == 0) {
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb;
} else mark = NULL;
case DASM_IMM_D: wd: dasmd(n); break;
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL;
case DASM_IMM_W: dasmw(n); break;
case DASM_VREG: { int t = *p++; if (t >= 2) n<<=3; cp[-1] |= n; break; }
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc;
b++; n = (int)(ptrdiff_t)D->globals[-n];
case DASM_REL_A: rel_a: n -= (int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */
case DASM_REL_PC: rel_pc: {
int shrink = *b++;
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; }
n = *pb - ((int)(cp-base) + 4-shrink);
if (shrink == 0) goto wd;
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb;
goto wb;
}
case DASM_IMM_LG:
p++; if (n < 0) { n = (int)(ptrdiff_t)D->globals[-n]; goto wd; }
case DASM_IMM_PC: {
int *pb = DASM_POS2PTR(D, n);
n = *pb < 0 ? pb[1] : (*pb + (int)(ptrdiff_t)base);
goto wd;
}
case DASM_LABEL_LG: {
int idx = *p++;
if (idx >= 10)
D->globals[idx] = (void *)(base + (*p == DASM_SETLABEL ? *b : n));
break;
}
case DASM_LABEL_PC: case DASM_SETLABEL: break;
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; }
case DASM_ALIGN:
n = *p++;
while (((cp-base) & n)) *cp++ = 0x90; /* nop */
break;
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd;
case DASM_MARK: mark = cp; break;
case DASM_ESC: action = *p++;
default: *cp++ = action; break;
case DASM_SECTION: case DASM_STOP: goto stop;
}
}
stop: (void)0;
}
}
if (base + D->codesize != cp) /* Check for phase errors. */
return DASM_S_PHASE;
return DASM_S_OK;
}
/* 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_L|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|(int)(D->section-D->sections);
return D->status;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1 +0,0 @@
Subproject commit 5aeee3dc910d37f37b75f5a6d1486fe75cb09284

@ -4,11 +4,6 @@
** Code to compile a upb::Handlers into bytecode for decoding a protobuf
** according to that specific schema and destination handlers.
**
** Compiling to bytecode is always the first step. If we are using the
** interpreted decoder we leave it as bytecode and interpret that. If we are
** using a JIT decoder we use a code generator to turn the bytecode into native
** code, LLVM IR, etc.
**
** Bytecode definition is in decoder.int.h.
*/
@ -37,7 +32,6 @@ static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers,
ret->group = group;
ret->dest_handlers_ = dest_handlers;
ret->is_native_ = false; /* If we JIT, it will update this later. */
upb_inttable_init(&ret->dispatch, UPB_CTYPE_UINT64);
return ret;
@ -69,9 +63,6 @@ static void freegroup(mgroup *g) {
}
upb_inttable_uninit(&g->methods);
#ifdef UPB_USE_JIT_X64
upb_pbdecoder_freejit(g);
#endif
upb_gfree(g->bytecode);
upb_gfree(g);
}
@ -313,7 +304,7 @@ static void putop(compiler *c, int op, ...) {
va_end(ap);
}
#if defined(UPB_USE_JIT_X64) || defined(UPB_DUMP_BYTECODE)
#if defined(UPB_DUMP_BYTECODE)
const char *upb_pbdecoder_getopname(unsigned int op) {
#define QUOTE(x) #x
@ -827,31 +818,6 @@ static void set_bytecode_handlers(mgroup *g) {
}
/* JIT setup. *****************************************************************/
#ifdef UPB_USE_JIT_X64
static void sethandlers(mgroup *g, bool allowjit) {
g->jit_code = NULL;
if (allowjit) {
/* Compile byte-code into machine code, create handlers. */
upb_pbdecoder_jit(g);
} else {
set_bytecode_handlers(g);
}
}
#else /* UPB_USE_JIT_X64 */
static void sethandlers(mgroup *g, bool allowjit) {
/* No JIT compiled in; use bytecode handlers unconditionally. */
UPB_UNUSED(allowjit);
set_bytecode_handlers(g);
}
#endif /* UPB_USE_JIT_X64 */
/* TODO(haberman): allow this to be constructed for an arbitrary set of dest
* handlers and other mgroups (but verify we have a transitive closure). */
const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy) {
@ -891,7 +857,7 @@ const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy) {
}
#endif
sethandlers(g, allowjit);
set_bytecode_handlers(g);
return g;
}

@ -1,511 +0,0 @@
/*
** Driver code for the x64 JIT compiler.
*/
/* Needed to ensure we get defines like MAP_ANON. */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include "upb/msg.h"
#include "upb/pb/decoder.h"
#include "upb/pb/decoder.int.h"
#include "upb/pb/varint.int.h"
/* To debug the JIT:
*
* 1. Uncomment:
* #define UPB_JIT_LOAD_SO
*
* Note: this mode requires that we can shell out to gcc.
*
* 2. Run the test locally. This will load the JIT code by building a
* .so (/tmp/upb-jit-code.so) and using dlopen, so more of the tooling will
* work properly (like GDB).
*
* IF YOU ALSO WANT AUTOMATIC JIT DEBUG OUTPUT:
*
* 3. Run: upb/pb/make-gdb-script.rb > script.gdb. This reads
* /tmp/upb-jit-code.so as input and generates a GDB script that is specific
* to this jit code.
*
* 4. Run: gdb --command=script.gdb --args path/to/test
* This will drop you to a GDB prompt which you can now use normally.
* But when you run the test it will print a message to stdout every time
* the JIT executes assembly for a particular bytecode. Sample output:
*
* X.enterjit bytes=18
* buf_ofs=1 data_rem=17 delim_rem=-2 X.0x6.OP_PARSE_DOUBLE
* buf_ofs=9 data_rem=9 delim_rem=-10 X.0x7.OP_CHECKDELIM
* buf_ofs=9 data_rem=9 delim_rem=-10 X.0x8.OP_TAG1
* X.0x3.dispatch.DecoderTest
* X.parse_unknown
* X.0x3.dispatch.DecoderTest
* X.decode_unknown_tag_fallback
* X.exitjit
*
* This output should roughly correspond to the output that the bytecode
* interpreter emits when compiled with UPB_DUMP_BYTECODE (modulo some
* extra JIT-specific output). */
/* These defines are necessary for DynASM codegen.
* See dynasm/dasm_proto.h for more info. */
#define Dst_DECL jitcompiler *jc
#define Dst_REF (jc->dynasm)
#define Dst (jc)
/* In debug mode, make DynASM do internal checks (must be defined before any
* dasm header is included. */
#ifndef NDEBUG
#define DASM_CHECKS
#endif
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
typedef struct {
mgroup *group;
uint32_t *pc;
/* This pointer is allocated by dasm_init() and freed by dasm_free(). */
struct dasm_State *dynasm;
/* Maps some key (an arbitrary void*) to a pclabel.
*
* The pclabel represents a location in the generated code -- DynASM exposes
* a pclabel -> (machine code offset) lookup function.
*
* The key can be anything. There are two main kinds of keys:
* - bytecode location -- the void* points to the bytecode instruction
* itself. We can then use this to generate jumps to this instruction.
* - other object (like dispatch table). We use these to represent parts
* of the generated code that do not exactly correspond to a bytecode
* instruction. */
upb_inttable jmptargets;
#ifndef NDEBUG
/* Like jmptargets, but members are present in the table when they have had
* define_jmptarget() (as opposed to jmptarget) called. Used to verify that
* define_jmptarget() is called exactly once for every target.
* The value is ignored. */
upb_inttable jmpdefined;
/* For checking that two asmlabels aren't defined for the same byte. */
int lastlabelofs;
#endif
#ifdef UPB_JIT_LOAD_SO
/* For marking labels that should go into the generated code.
* Maps pclabel -> char* label (string is owned by the table). */
upb_inttable asmlabels;
#endif
/* The total number of pclabels currently defined.
* Note that this contains both jmptargets and asmlabels, which both use
* pclabels but for different purposes. */
uint32_t pclabel_count;
/* Used by DynASM to store globals. */
void **globals;
} jitcompiler;
/* Functions called by codegen. */
static int jmptarget(jitcompiler *jc, const void *key);
static int define_jmptarget(jitcompiler *jc, const void *key);
static void asmlabel(jitcompiler *jc, const char *fmt, ...);
static int pcofs(jitcompiler* jc);
static int alloc_pclabel(jitcompiler *jc);
#ifdef UPB_JIT_LOAD_SO
static char *upb_vasprintf(const char *fmt, va_list ap);
static char *upb_asprintf(const char *fmt, ...);
#endif
#include "third_party/dynasm/dasm_proto.h"
#include "third_party/dynasm/dasm_x86.h"
#include "upb/pb/compile_decoder_x64.h"
static jitcompiler *newjitcompiler(mgroup *group) {
jitcompiler *jc = malloc(sizeof(jitcompiler));
jc->group = group;
jc->pclabel_count = 0;
upb_inttable_init(&jc->jmptargets, UPB_CTYPE_UINT32);
#ifndef NDEBUG
jc->lastlabelofs = -1;
upb_inttable_init(&jc->jmpdefined, UPB_CTYPE_BOOL);
#endif
#ifdef UPB_JIT_LOAD_SO
upb_inttable_init(&jc->asmlabels, UPB_CTYPE_PTR);
#endif
jc->globals = malloc(UPB_JIT_GLOBAL__MAX * sizeof(*jc->globals));
dasm_init(jc, 1);
dasm_setupglobal(jc, jc->globals, UPB_JIT_GLOBAL__MAX);
dasm_setup(jc, upb_jit_actionlist);
return jc;
}
static void freejitcompiler(jitcompiler *jc) {
#ifdef UPB_JIT_LOAD_SO
upb_inttable_iter i;
upb_inttable_begin(&i, &jc->asmlabels);
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
free(upb_value_getptr(upb_inttable_iter_value(&i)));
}
upb_inttable_uninit(&jc->asmlabels);
#endif
#ifndef NDEBUG
upb_inttable_uninit(&jc->jmpdefined);
#endif
upb_inttable_uninit(&jc->jmptargets);
dasm_free(jc);
free(jc->globals);
free(jc);
}
#ifdef UPB_JIT_LOAD_SO
/* Like sprintf except allocates the string, which is returned and owned by the
* caller.
*
* Like the GNU extension asprintf(), except we abort on error (since this is
* only for debugging). */
static char *upb_vasprintf(const char *fmt, va_list args) {
/* Run once to get the length of the string. */
va_list args_copy;
va_copy(args_copy, args);
int len = _upb_vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
char *ret = malloc(len + 1); /* + 1 for NULL terminator. */
if (!ret) abort();
int written = _upb_vsnprintf(ret, len + 1, fmt, args);
UPB_ASSERT(written == len);
return ret;
}
static char *upb_asprintf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
char *ret = upb_vasprintf(fmt, args);
va_end(args);
return ret;
}
#endif
static int alloc_pclabel(jitcompiler *jc) {
int newpc = jc->pclabel_count++;
dasm_growpc(jc, jc->pclabel_count);
return newpc;
}
static bool try_getjmptarget(jitcompiler *jc, const void *key, int *pclabel) {
upb_value v;
if (upb_inttable_lookupptr(&jc->jmptargets, key, &v)) {
*pclabel = upb_value_getuint32(v);
return true;
} else {
return false;
}
}
/* Gets the pclabel for this bytecode location's jmptarget. Requires that the
* jmptarget() has been previously defined. */
static int getjmptarget(jitcompiler *jc, const void *key) {
int pclabel = 0;
bool ok;
UPB_ASSERT_DEBUGVAR(upb_inttable_lookupptr(&jc->jmpdefined, key, NULL));
ok = try_getjmptarget(jc, key, &pclabel);
UPB_ASSERT(ok);
return pclabel;
}
/* Returns a pclabel that serves as a jmp target for the given bytecode pointer.
* This should only be called for code that is jumping to the target; code
* defining the target should use define_jmptarget().
*
* Creates/allocates a pclabel for this target if one does not exist already. */
static int jmptarget(jitcompiler *jc, const void *key) {
/* Optimizer sometimes can't figure out that initializing this is unnecessary.
*/
int pclabel = 0;
if (!try_getjmptarget(jc, key, &pclabel)) {
pclabel = alloc_pclabel(jc);
upb_inttable_insertptr(&jc->jmptargets, key, upb_value_uint32(pclabel));
}
return pclabel;
}
/* Defines a pclabel associated with the given bytecode location.
* Must be called exactly once by the code that is generating the code for this
* bytecode.
*
* Must be called exactly once before bytecode generation is complete (this is a
* sanity check to make sure the label is defined exactly once). */
static int define_jmptarget(jitcompiler *jc, const void *key) {
#ifndef NDEBUG
upb_inttable_insertptr(&jc->jmpdefined, key, upb_value_bool(true));
#endif
return jmptarget(jc, key);
}
/* Returns a bytecode pc offset relative to the beginning of the group's
* code. */
static int pcofs(jitcompiler *jc) {
return jc->pc - jc->group->bytecode;
}
/* Returns a machine code offset corresponding to the given key.
* Requires that this key was defined with define_jmptarget. */
static int machine_code_ofs(jitcompiler *jc, const void *key) {
int pclabel = getjmptarget(jc, key);
/* Despite its name, this function takes a pclabel and returns the
* corresponding machine code offset. */
return dasm_getpclabel(jc, pclabel);
}
/* Returns a machine code offset corresponding to the given method-relative
* bytecode offset. Note that the bytecode offset is relative to the given
* method, but the returned machine code offset is relative to the beginning of
* *all* the machine code. */
static int machine_code_ofs2(jitcompiler *jc, const upb_pbdecodermethod *method,
int pcofs) {
void *bc_target = jc->group->bytecode + method->code_base.ofs + pcofs;
return machine_code_ofs(jc, bc_target);
}
/* Given a pcofs relative to this method's base, returns a machine code offset
* relative to jmptarget(dispatch->array) (which is used in jitdispatch as the
* machine code base for dispatch table lookups). */
uint32_t dispatchofs(jitcompiler *jc, const upb_pbdecodermethod *method,
int pcofs) {
int mc_base = machine_code_ofs(jc, method->dispatch.array);
int mc_target = machine_code_ofs2(jc, method, pcofs);
int ret;
UPB_ASSERT(mc_base > 0);
UPB_ASSERT(mc_target > 0);
ret = mc_target - mc_base;
UPB_ASSERT(ret > 0);
return ret;
}
/* Rewrites the dispatch tables into machine code offsets. */
static void patchdispatch(jitcompiler *jc) {
upb_inttable_iter i;
upb_inttable_begin(&i, &jc->group->methods);
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i));
upb_inttable *dispatch = &method->dispatch;
upb_inttable_iter i2;
method->is_native_ = true;
/* Remove DISPATCH_ENDMSG -- only the bytecode interpreter needs it.
* And leaving it around will cause us to find field 0 improperly. */
upb_inttable_remove(dispatch, DISPATCH_ENDMSG, NULL);
upb_inttable_begin(&i2, dispatch);
for (; !upb_inttable_done(&i2); upb_inttable_next(&i2)) {
uintptr_t key = upb_inttable_iter_key(&i2);
uint64_t val = upb_value_getuint64(upb_inttable_iter_value(&i2));
uint64_t newval;
bool ok;
if (key <= UPB_MAX_FIELDNUMBER) {
/* Primary slot. */
uint64_t ofs;
uint8_t wt1;
uint8_t wt2;
upb_pbdecoder_unpackdispatch(val, &ofs, &wt1, &wt2);
/* Update offset and repack. */
ofs = dispatchofs(jc, method, ofs);
newval = upb_pbdecoder_packdispatch(ofs, wt1, wt2);
UPB_ASSERT((int64_t)newval > 0);
} else {
/* Secondary slot. Since we have 64 bits for the value, we use an
* absolute offset. */
int mcofs = machine_code_ofs2(jc, method, val);
newval = (uint64_t)((char*)jc->group->jit_code + mcofs);
}
ok = upb_inttable_replace(dispatch, key, upb_value_uint64(newval));
UPB_ASSERT(ok);
}
/* Update entry point for this method to point at mc base instead of bc
* base. Set this only *after* we have patched the offsets
* (machine_code_ofs2() uses this). */
method->code_base.ptr = (char*)jc->group->jit_code + machine_code_ofs(jc, method);
{
upb_byteshandler *h = &method->input_handler_;
upb_byteshandler_setstartstr(h, upb_pbdecoder_startjit, NULL);
upb_byteshandler_setstring(h, jc->group->jit_code, method->code_base.ptr);
upb_byteshandler_setendstr(h, upb_pbdecoder_end, method);
}
}
}
#ifdef UPB_JIT_LOAD_SO
static void load_so(jitcompiler *jc) {
/* Dump to a .so file in /tmp and load that, so all the tooling works right
* (for example, debuggers and profilers will see symbol names for the JIT-ted
* code). This is the same goal of the GDB JIT code below, but the GDB JIT
* interface is only used/understood by GDB. Hopefully a standard will
* develop for registering JIT-ted code that all tools will recognize,
* rendering this obsolete.
*
* jc->asmlabels maps:
* pclabel -> char* label
*
* Use this to build mclabels, which maps:
* machine code offset -> char* label
*
* Then we can use mclabels to emit the labels as we iterate over the bytes we
* are outputting. */
upb_inttable_iter i;
upb_inttable mclabels;
upb_inttable_init(&mclabels, UPB_CTYPE_PTR);
upb_inttable_begin(&i, &jc->asmlabels);
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
upb_inttable_insert(&mclabels,
dasm_getpclabel(jc, upb_inttable_iter_key(&i)),
upb_inttable_iter_value(&i));
}
/* We write a .s file in text format, as input to the assembler.
* Then we run gcc to turn it into a .so file.
*
* The last "XXXXXX" will be replaced with something randomly generated by
* mkstmemp(). We don't add ".s" to this filename because it makes the string
* processing for mkstemp() and system() more complicated. */
char s_filename[] = "/tmp/upb-jit-codeXXXXXX";
int fd = mkstemp(s_filename);
FILE *f;
if (fd >= 0 && (f = fdopen(fd, "wb")) != NULL) {
uint8_t *jit_code = (uint8_t*)jc->group->jit_code;
size_t linelen = 0;
size_t i;
fputs(" .text\n\n", f);
for (i = 0; i < jc->group->jit_size; i++) {
upb_value v;
if (upb_inttable_lookup(&mclabels, i, &v)) {
const char *label = upb_value_getptr(v);
/* "X." makes our JIT syms recognizable as such, which we build into
* other tooling. */
fprintf(f, "\n\nX.%s:\n", label);
fprintf(f, " .globl X.%s", label);
linelen = 1000;
}
if (linelen >= 77) {
linelen = fprintf(f, "\n .byte %u", jit_code[i]);
} else {
linelen += fprintf(f, ",%u", jit_code[i]);
}
}
fputs("\n", f);
fclose(f);
} else {
fprintf(stderr, "Error opening tmp file for JIT debug output.\n");
abort();
}
/* This is exploitable if you have an adversary on your machine who can write
* to this tmp directory. But this is just for debugging so we don't worry
* too much about that. It shouldn't be prone to races against concurrent
* (non-adversarial) upb JIT's because we used mkstemp(). */
char *cmd = upb_asprintf("gcc -shared -o %s.so -x assembler %s", s_filename,
s_filename);
if (system(cmd) != 0) {
fprintf(stderr, "Error compiling %s\n", s_filename);
abort();
}
free(cmd);
char *so_filename = upb_asprintf("%s.so", s_filename);
/* Some convenience symlinks.
* This is racy, but just for convenience. */
int ret;
unlink("/tmp/upb-jit-code.so");
unlink("/tmp/upb-jit-code.s");
ret = symlink(s_filename, "/tmp/upb-jit-code.s");
ret = symlink(so_filename, "/tmp/upb-jit-code.so");
UPB_UNUSED(ret); // We don't care if this fails.
jc->group->dl = dlopen(so_filename, RTLD_LAZY);
free(so_filename);
if (!jc->group->dl) {
fprintf(stderr, "Couldn't dlopen(): %s\n", dlerror());
abort();
}
munmap(jc->group->jit_code, jc->group->jit_size);
jc->group->jit_code = dlsym(jc->group->dl, "X.enterjit");
if (!jc->group->jit_code) {
fprintf(stderr, "Couldn't find enterjit sym\n");
abort();
}
upb_inttable_uninit(&mclabels);
}
#endif
void upb_pbdecoder_jit(mgroup *group) {
jitcompiler *jc;
char *jit_code;
int dasm_status;
group->debug_info = NULL;
group->dl = NULL;
UPB_ASSERT(group->bytecode);
jc = newjitcompiler(group);
emit_static_asm(jc);
jitbytecode(jc);
dasm_status = dasm_link(jc, &jc->group->jit_size);
if (dasm_status != DASM_S_OK) {
fprintf(stderr, "DynASM error; returned status: 0x%08x\n", dasm_status);
abort();
}
jit_code = mmap(NULL, jc->group->jit_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
dasm_encode(jc, jit_code);
mprotect(jit_code, jc->group->jit_size, PROT_EXEC | PROT_READ);
jc->group->jit_code = (upb_string_handlerfunc *)jit_code;
#ifdef UPB_JIT_LOAD_SO
load_so(jc);
#endif
patchdispatch(jc);
freejitcompiler(jc);
/* Now the bytecode is no longer needed. */
free(group->bytecode);
group->bytecode = NULL;
}
void upb_pbdecoder_freejit(mgroup *group) {
if (!group->jit_code) return;
if (group->dl) {
#ifdef UPB_JIT_LOAD_SO
dlclose(group->dl);
#endif
} else {
munmap((void*)group->jit_code, group->jit_size);
}
free(group->debug_info);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save