diff options
-rw-r--r-- | misc/radare2/Makefile | 7 | ||||
-rw-r--r-- | misc/radare2/asm_fspec.c | 29 | ||||
-rw-r--r-- | spec/ability.fspec | 106 | ||||
-rw-r--r-- | src/bin/fspec-dump.c | 65 | ||||
-rw-r--r-- | src/compiler/compiler.lm | 57 | ||||
-rw-r--r-- | src/compiler/native.c | 30 |
6 files changed, 165 insertions, 129 deletions
diff --git a/misc/radare2/Makefile b/misc/radare2/Makefile index aff2fdb..b8fcdd2 100644 --- a/misc/radare2/Makefile +++ b/misc/radare2/Makefile @@ -8,7 +8,7 @@ WARNINGS := -Wall -Wextra -Wformat=2 -Wstrict-aliasing=3 -Wstrict-overflow=5 -Ws override CFLAGS ?= -g override CFLAGS += -std=c11 $(WARNINGS) -libs = asm_fspec.so +libs = asm_fspec.so anal_fspec.so all: $(libs) %.so: @@ -18,11 +18,16 @@ asm_fspec.so: private CFLAGS += $(shell pkg-config --cflags r_anal) asm_fspec.so: private LDLIBS += $(shell pkg-config --libs-only-l r_anal) asm_fspec.so: asm_fspec.c +anal_fspec.so: private CFLAGS += $(shell pkg-config --cflags r_anal) +anal_fspec.so: private LDLIBS += $(shell pkg-config --libs-only-l r_anal) +anal_fspec.so: anal_fspec.c + install: $(libs) install -Dm755 $^ -t "$(R2_PLUGIN_PATH)" uninstall: $(RM) "$(R2_PLUGIN_PATH)"/asm_fspec.so + $(RM) "$(R2_PLUGIN_PATH)"/anal_fspec.so clean: $(RM) $(libs) diff --git a/misc/radare2/asm_fspec.c b/misc/radare2/asm_fspec.c index fa7c1ad..9eb28ea 100644 --- a/misc/radare2/asm_fspec.c +++ b/misc/radare2/asm_fspec.c @@ -12,7 +12,8 @@ enum fspec_instruction { INS_REG, INS_PUSH, INS_PUSHR, - INS_STORE, + INS_POP, + INS_INCR, INS_OP, INS_QUEUE, INS_IO, @@ -54,7 +55,8 @@ ins_name_str(const enum fspec_instruction name) case INS_REG: return "reg"; case INS_PUSH: return "push"; case INS_PUSHR: return "pushr"; - case INS_STORE: return "store"; + case INS_INCR: return "incr"; + case INS_POP: return "pop"; case INS_OP: return "op"; case INS_QUEUE: return "queue"; case INS_IO: return "io"; @@ -98,26 +100,27 @@ op_name_str(const enum fspec_operation op) static int disassemble(RAsm *a, RAsmOp *op, const ut8 *buf, int len) { + (void)a; + union { struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; - uint8_t v[16]; + uint8_t v[sizeof(uint64_t)]; } u = {0}; - memcpy(u.v, buf, R_MIN(sizeof(u.v[0]), len)); - const uint8_t insw = sizeof(uint16_t) * (1 << u.ins.n); + memcpy(u.v, buf, R_MIN(1, (size_t)len)); + const uint8_t insw = 1 << u.ins.n; memcpy(u.v, buf, R_MIN(insw, len)); + const char *buf_asm = "invalid"; + const bool reg_arg = (u.ins.name == INS_PUSHR || u.ins.name == INS_INCR || u.ins.name == INS_POP || + u.ins.name == INS_EXEC || u.ins.name == INS_CALL); if (u.ins.name == INS_OP) buf_asm = sdb_fmt("%s %s", ins_name_str(u.ins.name), op_name_str(u.ins.v)); - else if (u.ins.n == 0) - buf_asm = sdb_fmt("%s 0x%02x", ins_name_str(u.ins.name), (uint16_t)u.ins.v); - else if (u.ins.n == 1) - buf_asm = sdb_fmt("%s 0x%04x", ins_name_str(u.ins.name), (uint32_t)u.ins.v); - else if (u.ins.n == 2) - buf_asm = sdb_fmt("%s 0x%08x", ins_name_str(u.ins.name), (uint64_t)u.ins.v); + else if (reg_arg) + buf_asm = sdb_fmt("%s r%" PRIu64, ins_name_str(u.ins.name), (uint64_t)u.ins.v); else - return 0; + buf_asm = sdb_fmt("%s %" PRIu64, ins_name_str(u.ins.name), (uint64_t)u.ins.v); r_strbuf_set(&op->buf_asm, buf_asm); return (op->size = insw + (u.ins.name == INS_REG ? u.ins.v : 0)); @@ -128,7 +131,7 @@ RAsmPlugin r_asm_plugin_fspec = { .license = "LGPL3", .desc = "fspec disassembly plugin", .arch = "fspec", - .bits = 16 | 32 | 64, + .bits = 8 | 16 | 32 | 64, .endian = R_SYS_ENDIAN_LITTLE, .disassemble = disassemble }; diff --git a/spec/ability.fspec b/spec/ability.fspec index aab2bcf..5a695a0 100644 --- a/spec/ability.fspec +++ b/spec/ability.fspec @@ -30,60 +30,66 @@ struct dat { // Inside `[]` brackets are the arguments that will be popped from the stack. `()` parenthesis are used instead, // if the stack argument is optional. // +// Reserved registers: +// r0: Always empty, used as NULL / empty data indicator +// r1: Field counter +// // List of instructions: -// Name | Hex | Description -// VERSION<version> | 0x00 | Indicates the version of this bytecode. -// REG<len> | 0x01 | Allocates a new register. -// * * If `len` is not zero, the next `len` bytes will be stored in this register. -// PUSH<v> | 0x02 | Pushes `v` to the stack. -// PUSHR<R> | 0x03 | Pushes the contents of register at index `R` to the stack. -// STORE<R> [v] | 0x04 | Stores `v` into register at index `R`. -// OP<op> [...] | 0x05 | Performs operation specified by the `op`, and pushes the result to the stack. -// QUEUE<len> (...) | 0x06 | Queues next `len` bytes for execution for the next `IO` instruction. -// * * The code is executed before or after `IO` instruction, depending whether VM is packing or unpacking. -// IO<sz> [R] (...) | 0x07 | Unpacking: Reads data from external VM input (usually a file) to register at index `R`. -// * * Packing: Writes data to external VM output (usually a file) from register at index `R`. -// * * `sz` is the size of the element in bits. -// * * Rest of the stack is the number of elements, if empty, the elements to read/write is 1. -// EXEC<R> (...) | 0x08 | Executes instructions stored in register at index `R`. -// * * Rest of the stack is the number of times to execute, if empty, execution happens only once. -// CALL<R> (...) | 0x09 | Calls a function. The name of the function is stored in register at index `R`. -// JMP<off> | 0x0A | Jumps to the `off` (in bytes). -// JMPIF<off> [v] | 0x0B | Performs `JMP`, if `v` is true. -// STRUCT<R> [R2] | 0x0C | Describes register at index `R` as struct structure. Register at index `R2` contains the name of the struct. -// SELECT<R> | 0x0D | Describes register at index `R` as select structure. -// FIELD<R> (...) | 0x0E | Links field to last structure. Register at index `R` contains the name of the field. -// * * Rest of the stack contains register indices for registers that are instances of this field. -// BSZ<s:1, v> [sz] | 0x0F | Describes last field as a primitive field. `s` describes whether the field is signed. `v` describes how the -// * * field should be represented. `sz` contains the size of field in bits. -// REF<R> | 0x10 | Describes field as a substructure. Register at index `R` contains the structure definition. -// FDIMENSION<sz> | 0x11 | Adds fixed dimension to a field. `sz` indicates the size of the dimension. -// VDIMENSION | 0x12 | Adds variable length dimension to a field. -// ENUM<R> | 0x13 | Links field to a enum +// Name | Hex | Description +// VERSION<version> | 0x00 | Indicates the version of this bytecode. +// REG<len> | 0x01 | Allocates a new register. +// * * If `len` is not zero, the next `len` bytes will be stored in this register. +// PUSH<v> | 0x02 | Pushes `v` to the stack. +// PUSHR<r?> | 0x03 | Pushes the contents of register `r?` to the stack. +// POP<r?> [v] | 0x04 | Pops `v` into register `r?`. +// INCR<r?> | 0x05 | Increments the register `r?` by one. +// OP<op> [...] | 0x06 | Performs operation specified by the `op`, and pushes the result to the stack. +// QUEUE<len> (...) | 0x07 | Queues next `len` bytes for execution for the next `IO` instruction. +// * * The code is executed before or after `IO` instruction, depending whether VM is packing or unpacking. +// IO<sz> (...) | 0x08 | Unpacking: Reads data from external VM input (usually a file) to register pointed by `r1`. +// * * Packing: Writes data to external VM output (usually a file) from register pointed by `r1`. +// * * `sz` is the size of the element in bits. +// * * Rest of the stack is the number of elements, if empty, the elements to read/write is 1. +// EXEC<r?> [r??] (...) | 0x09 | Sets `r1` to `r??` and executes instructions stored in register `r?`. +// * * Rest of the stack is the number of times to execute, if empty, execution happens only once. +// * * `r1` will be saved and restored during this instruction. +// CALL<r?> (...) | 0x0A | Calls a function. The name of the function is stored in register `r?`. +// JMP<off> | 0x0B | Jumps to the `off` (in bytes). +// JMPIF<off> [v] | 0x0C | Performs `JMP`, if `v` is true. +// STRUCT<r?> [r??] | 0x0D | Describes register `r?` as struct structure. register `r??` contains the name of the struct. +// SELECT<r?> | 0x0E | Describes register `r?` as select structure. +// FIELD<r?> (...) | 0x0F | Links field to last structure. register `r?` contains the name of the field. +// * * Rest of the stack contains register indices for registers that are instances of this field. +// BSZ<s:1, v> [sz] | 0x10 | Describes last field as a primitive field. `s` describes whether the field is signed. `v` describes how the +// * * field should be represented. `sz` contains the size of field in bits. +// REF<r?> | 0x11 | Describes field as a substructure. register `r?` contains the structure definition. +// FDIMENSION<sz> | 0x12 | Adds fixed dimension to a field. `sz` indicates the size of the dimension. +// VDIMENSION | 0x13 | Adds variable length dimension to a field. +// ENUM<r?> | 0x14 | Links field to a enum // // List of operations for the `OP` instruction: // Name | Hex | Argc | Description -// UNM | 0x00 | 1 | Unary minus operation `-r1`. -// LNOT | 0x01 | 1 | Logical not operation `!r1`. -// BNOT | 0x02 | 1 | Bitwise not operation `~r1`. -// MUL | 0x03 | 2 | Multiplication operation `r1 * r2`. -// DIV | 0x04 | 2 | Division operation `r1 * r2`. -// MOD | 0x05 | 2 | Modulo operation `r1 % r2`. -// ADD | 0x06 | 2 | Addition operation `r1 + r2`. -// SUB | 0x07 | 2 | Substraction operation `r1 - r2`. -// SHIFTL | 0x08 | 2 | Left shift operation `r1 << r2`. -// SHIFTR | 0x09 | 2 | Right shift operation `r1 >> r2`. -// LESS | 0x0A | 2 | Less than operation `r1 < r2`. -// LESSEQ | 0x0B | 2 | Less or equal operation `r1 <= r2`. -// EQ | 0x0C | 2 | Equal operation `r1 == r2`. -// NOTEQ | 0x0D | 2 | Not equal operation `r1 != r2`. -// BAND | 0x0E | 2 | Bitwise and operation `r1 & r2`. -// BOR | 0x0F | 2 | Bitwise or operation `r1 | r2`. -// BXOR | 0x10 | 2 | Bitwise xor operation `r1 ^ r2`. -// LAND | 0x11 | 2 | Logical and operation `r1 && r2`. -// LOR | 0x12 | 2 | Logical or operation `r1 || r2`. -// CTERNARY | 0x13 | 3 | Conditional ternary operation `r1 ? r2 : r3` -// SUBSCRIPT | 0x14 | 2 | Subscript operation `r1[r2]` +// UNM | 0x00 | 1 | Unary minus operation `-v1`. +// LNOT | 0x01 | 1 | Logical not operation `!v1`. +// BNOT | 0x02 | 1 | Bitwise not operation `~v1`. +// MUL | 0x03 | 2 | Multiplication operation `v1 * v2`. +// DIV | 0x04 | 2 | Division operation `v1 * v2`. +// MOD | 0x05 | 2 | Modulo operation `v1 % v2`. +// ADD | 0x06 | 2 | Addition operation `v1 + v2`. +// SUB | 0x07 | 2 | Substraction operation `v1 - v2`. +// SHIFTL | 0x08 | 2 | Left shift operation `v1 << v2`. +// SHIFTR | 0x09 | 2 | Right shift operation `v1 >> v2`. +// LESS | 0x0A | 2 | Less than operation `v1 < v2`. +// LESSEQ | 0x0B | 2 | Less or equal operation `v1 <= v2`. +// EQ | 0x0C | 2 | Equal operation `v1 == v2`. +// NOTEQ | 0x0D | 2 | Not equal operation `v1 != v2`. +// BAND | 0x0E | 2 | Bitwise and operation `v1 & v2`. +// BOR | 0x0F | 2 | Bitwise or operation `v1 | v2`. +// BXOR | 0x10 | 2 | Bitwise xor operation `v1 ^ v2`. +// LAND | 0x11 | 2 | Logical and operation `v1 && v2`. +// LOR | 0x12 | 2 | Logical or operation `v1 || v2`. +// CTERNARY | 0x13 | 3 | Conditional ternary operation `v1 ? v2 : v3` +// SUBSCRIPT | 0x14 | 2 | Subscript operation `v1[v2]` // // List of visuals for the `PIO` instruction: // Name | Hex | Representation diff --git a/src/bin/fspec-dump.c b/src/bin/fspec-dump.c index 195674f..e7993e6 100644 --- a/src/bin/fspec-dump.c +++ b/src/bin/fspec-dump.c @@ -1,3 +1,4 @@ +#include <inttypes.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -162,7 +163,8 @@ enum fspec_instruction { INS_REG, INS_PUSH, INS_PUSHR, - INS_STORE, + INS_POP, + INS_INCR, INS_OP, INS_QUEUE, INS_IO, @@ -229,16 +231,42 @@ struct fspec_ctx { struct fspec_istream ir, binary; }; +static uint8_t +bytes_for_v(const uint64_t v) +{ + for (uint8_t n = 0; n < 4; ++n) { + const uint8_t bytes = (1 << n); + if (v < (uint64_t)(1 << (CHAR_BIT * bytes))) + return bytes; + } + errx(EXIT_FAILURE, "number `%" PRIu64 "` is too big", v); + return 0; +} + static void -stack_push_num(struct fspec_stack *stack, struct fspec_buffer *buf, const uint64_t v) +register_set_num(struct fspec_register *r, struct fspec_buffer *buf, const uint64_t v) { - assert(stack->numv < ARRAY_SIZE(stack->value)); - const uint8_t bsz = DIV_ROUND_UP(__builtin_ctzl((v ? v : 1)), CHAR_BIT); - stack->value[stack->numv++] = (struct fspec_register){ .off = buf->ptr, .len = bsz }; + const uint8_t bsz = bytes_for_v(v); + *r = (struct fspec_register){ .off = buf->ptr, .len = bsz }; const union { uint8_t u8[sizeof(v)]; uint64_t v; } u = { .v = v }; fspec_buffer_write(buf, u.u8, bsz); } +static uint64_t +register_get_num(const struct fspec_register *r, const struct fspec_buffer *buf) +{ + union { uint8_t u8[sizeof(uint64_t)]; uint64_t v; } u = {0}; + memcpy(u.u8, buf->data + r->off, MIN(r->len, sizeof(u.u8))); + return (u.v << r->shift[0]) >> r->shift[1]; +} + +static void +stack_push_num(struct fspec_stack *stack, struct fspec_buffer *buf, const uint64_t v) +{ + assert(stack->numv < ARRAY_SIZE(stack->value)); + register_set_num(&stack->value[stack->numv++], buf, v); +} + static void stack_push(struct fspec_stack *stack, struct fspec_register *value) { @@ -257,10 +285,7 @@ static uint64_t stack_pop_num(struct fspec_stack *stack, const struct fspec_buffer *buf) { assert(stack->numv > 0); - const struct fspec_register v = stack->value[--stack->numv]; - union { uint8_t u8[sizeof(uint64_t)]; uint64_t v; } u = {0}; - memcpy(u.u8, buf->data + v.off, MIN(v.len, sizeof(u.u8))); - return (u.v << v.shift[0]) >> v.shift[1]; + return register_get_num(&stack->value[--stack->numv], buf); } static bool @@ -356,11 +381,11 @@ fspec_execute(struct fspec_ctx *ctx, const uint8_t *ir, const uint64_t irlen, co for (const uint8_t *pc = ir; pc < ir + irlen;) { union { struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; - uint8_t v[16]; + uint8_t v[sizeof(uint64_t)]; } u = {0}; - memcpy(u.v, pc, sizeof(u.v[0])); - const uint8_t insw = sizeof(uint16_t) * (1 << u.ins.n); + memcpy(u.v, pc, 1); + const uint8_t insw = 1 << u.ins.n; memcpy(u.v, pc, insw); pc += insw; @@ -386,10 +411,14 @@ fspec_execute(struct fspec_ctx *ctx, const uint8_t *ir, const uint64_t irlen, co fprintf(stderr, "PUSHR R: %lu\n", insv); stack_push(&ctx->S, &ctx->R.value[insv]); break; - case INS_STORE: - fprintf(stderr, "STORE R: %lu\n", insv); + case INS_POP: + fprintf(stderr, "POP R: %lu\n", insv); stack_pop(&ctx->S, &ctx->R.value[insv]); break; + case INS_INCR: + fprintf(stderr, "INCR R: %lu\n", insv); + register_set_num(&ctx->R.value[insv], &ctx->mem, register_get_num(&ctx->R.value[insv], &ctx->mem) + 1); + break; case INS_OP: fprintf(stderr, "OP op: %lu\n", insv); do_op(ctx, insv); @@ -408,20 +437,24 @@ fspec_execute(struct fspec_ctx *ctx, const uint8_t *ir, const uint64_t irlen, co assert(ctx->mem.ptr + szb * nmemb <= ctx->mem.size); ctx->mem.ptr += ctx->binary.read(ctx, ctx->mem.data + ctx->mem.ptr, szb * nmemb); } while (ctx->S.numv); + ctx->R.value[R].len = ctx->mem.ptr - ctx->R.value[R].off; if (ctx->mem.ptr == ctx->R.value[R].off) return true; - ctx->R.value[R].len = ctx->mem.ptr - ctx->R.value[R].off; } break; case INS_EXEC: { fprintf(stderr, "EXEC R: %lu\n", insv); + const struct fspec_register old_r1 = ctx->R.value[1]; + stack_pop(&ctx->S, &ctx->R.value[1]); uint64_t nmemb = 1; do { nmemb *= (ctx->S.numv ? stack_pop_num(&ctx->S, &ctx->mem) : 1); + fprintf(stderr, "off: %lu len: %lu nmemb: %lu\n", ctx->R.value[insv].off, ctx->R.value[insv].len, nmemb); for (uint64_t i = 0; i < nmemb; ++i) if (fspec_execute(ctx, ctx->mem.data + ctx->R.value[insv].off, ctx->R.value[insv].len, ind + INDSTP)) return true; } while (ctx->S.numv); + ctx->R.value[1] = old_r1; } break; case INS_CALL: { @@ -494,7 +527,7 @@ fspec_execute(struct fspec_ctx *ctx, const uint8_t *ir, const uint64_t irlen, co pc = (r1 ? ir + insv : pc); break; default: - errx(EXIT_FAILURE, "unknown instruction: %u\n", u.ins.name); + errx(EXIT_FAILURE, "unknown instruction: %u :: %u\n", u.ins.name, insw); } } return false; diff --git a/src/compiler/compiler.lm b/src/compiler/compiler.lm index 1ca7705..502422d 100644 --- a/src/compiler/compiler.lm +++ b/src/compiler/compiler.lm @@ -167,6 +167,7 @@ if (!source) { struct scope names:map<str, map<str, any>> + roff:int end global g_scopes:list<scope> = new list<scope>() @@ -281,21 +282,23 @@ global INS_VERSION:int = 0 global INS_REG:int = 1 global INS_PUSH:int = 2 global INS_PUSHR:int = 3 -global INS_STORE:int = 4 -global INS_OP:int = 5 -global INS_QUEUE:int = 6 -global INS_IO:int = 7 -global INS_EXEC:int = 8 -global INS_CALL:int = 9 -global INS_JMP:int = 10 -global INS_JMPIF:int = 11 +global INS_POP:int = 4 +global INS_INCR:int = 5 +global INS_OP:int = 6 +global INS_QUEUE:int = 7 +global INS_IO:int = 8 +global INS_EXEC:int = 9 +global INS_CALL:int = 10 +global INS_JMP:int = 11 +global INS_JMPIF:int = 12 int insbuf_written() = c_insbuf_written str flush_insbuf() = c_flush_insbuf void write_ins(ins:int, num:int) = c_write_ins void write_ins_with_data(ins:int, data:str) = c_write_ins_with_data -global g_regc:int = 1 +global g_r1:int = 1 +global g_regc:int = g_r1 + 1 void new_reg(v:any, data:str) @@ -329,6 +332,8 @@ find_data_in(s:any) } write_ins(INS_VERSION, 1) +write_ins_with_data(INS_REG, '') + for e:expr::paren::type in source find_data_in(e.collapsed) for e:expr::bracket::type in source @@ -344,10 +349,6 @@ for d:fspec::declaration::type in source { write_data_if_not_there($d.container.data.name) } -global g_fcr:int = g_regc -write_ins_with_data(INS_REG, '') -g_regc = g_regc + 1 - void write_expr(expr:collapser::reducer::collapsed) { @@ -381,7 +382,7 @@ write_expr(expr:collapser::reducer::collapsed) off = g_offs->find(%d.container) } } - write_ins(INS_PUSHR, off + g_regs->find(%d)) + write_ins(INS_PUSHR, off + g_regs->find(%d) - off) } else { write_ins(INS_OP, g_ops->find($vop.op)) } @@ -417,6 +418,9 @@ write_declaration(d:fspec::declaration::type, index:int) c:fspec::container::type = d.container if (d.cref) c = lookup($d.cref, $d.parent, nil) + if (!c && index > 0) + write_ins(INS_INCR, g_r1) + locs:map<any, int> = new map<any, int>() if (d.extra) { for l:fspec::declaration::dimension in repeat(d.extra.dimension) { @@ -432,15 +436,9 @@ write_declaration(d:fspec::declaration::type, index:int) } if (!c) { - write_ins(INS_PUSHR, g_fcr) - if (index != 0) { - write_ins(INS_PUSH, index) - write_ins(INS_OP, g_ops->find('#+')) - } write_ins(INS_IO, d.primitive.bits) } else { write_ins(INS_PUSH, g_offs->find(%c)) - write_ins(INS_STORE, g_fcr) write_ins(INS_EXEC, g_regs->find(%c)) } @@ -453,20 +451,19 @@ write_declaration(d:fspec::declaration::type, index:int) void walk1(d:fspec::declaration::type) { - if (!d.container) { - print('something went wrong!\n') - exit(1) - } + c:fspec::container::type = d.container + if (d.cref) c = lookup($d.cref, $d.parent, nil) + + if (!c || !d.name) + return 0 for i:fspec::container::strukt::item in repeat(d.container.data.items) - if (i.data.container) - walk1(i.data) + walk1(i.data) for i:fspec::container::select::item in repeat(d.container.data.items) - if (i.data.container) - walk1(i.data) + walk1(i.data) - g_offs->insert(%d.container, g_regc) + g_offs->insert(%d, g_regc) for i:fspec::container::enum::item in repeat(d.container.data.items) { # somehow need to get this constant time (not reg) @@ -515,7 +512,7 @@ walk2(d:fspec::declaration::type) if (!d.name) { write_ins(INS_PUSH, g_offs->find(%d.container)) - write_ins(INS_STORE, g_fcr) + write_ins(INS_POP, g_r1) } index:int = 0 diff --git a/src/compiler/native.c b/src/compiler/native.c index bbb0060..feac349 100644 --- a/src/compiler/native.c +++ b/src/compiler/native.c @@ -1,5 +1,6 @@ #include <colm/tree.h> #include <colm/bytecode.h> +#include <inttypes.h> #include <stdlib.h> #include <stdint.h> #include <string.h> @@ -111,35 +112,26 @@ c_op_stack_pop(program_t *prg, tree_t **sp, value_t a) } static uint8_t -bits_for_n(const uint8_t n, const uint8_t used) -{ - return 16 * (1 << n) - used; -} - -static uint8_t n_for_v(const uint64_t v, const uint8_t used) { - const uint8_t bits = __builtin_ctzl((v ? v : 1)); - if (used <= 16 && bits < bits_for_n(0, used)) - return 0; - else if (used <= 32 && bits < bits_for_n(1, used)) - return 1; - else if (used <= 64 && bits < bits_for_n(2, used)) - return 2; - - errx(EXIT_FAILURE, "numbers over 57 bits not supported right now.. sorry :D"); + for (uint8_t n = 0; n < 4; ++n) { + const uint8_t bits = CHAR_BIT * (1 << n); + if (used < bits && v < (uint64_t)(1 << (bits - used))) + return n; + } + errx(EXIT_FAILURE, "number `%" PRIu64 "` is too big to be compiled in instruction", v); return 3; } static void -vle_instruction(const uint8_t name, const uint64_t v, uint8_t out[16], uint8_t *out_written) +vle_instruction(const uint8_t name, const uint64_t v, uint8_t out[8], uint8_t *out_written) { assert(out && out_written); const union { struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; - uint8_t v[16]; + uint8_t v[sizeof(uint64_t)]; } u = { .ins = { .name = name, .n = n_for_v(v, 7), .v = v } }; - *out_written = sizeof(uint16_t) * (1 << u.ins.n); + *out_written = sizeof(u.v[0]) * (1 << u.ins.n); memcpy(out, u.v, *out_written); } @@ -165,7 +157,7 @@ c_insbuf_written(program_t *prg, tree_t **sp) void c_write_ins(program_t *prg, tree_t **sp, value_t a, value_t b) { - uint8_t out[16], written; + uint8_t out[8], written; vle_instruction(a, b, out, &written); memcpy(&insbuf.data[insbuf.written], out, written); insbuf.written += written; |