1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-18 06:34:48 +00:00

Change how labels are recorded.

Disallow jumping to arbitrary instructions - instead, only allow jumps
to label ids. This will make various transformations and validations
easier since adding or remove instructions does not break jumps.
This commit is contained in:
Calvin Rose 2024-06-02 09:43:33 -05:00
parent 8a394f2506
commit 480c5b5e9d
2 changed files with 236 additions and 117 deletions

View File

@ -45,7 +45,8 @@
t)
(defn setup-default-types
[into]
[ctx]
(def into @[])
(defn add-prim-type
[name native-name]
(array/push into ~(type-prim ,name ,native-name))
@ -54,7 +55,9 @@
(add-prim-type 'double 'f64)
(add-prim-type 'int 's32)
(add-prim-type 'pointer 'pointer)
(add-prim-type 'boolean 'boolean))
(add-prim-type 'boolean 'boolean)
(sysir/asm ctx into)
ctx)
(defn type-extract
"Given a symbol:type combination, extract the proper name and the type separately"
@ -66,9 +69,13 @@
(var do-binop nil)
(var do-comp nil)
###
### Inside functions
###
(defn visit1
"Take in a form and compile code and put it into `into`. Return result slot."
[code into]
[code into &opt no-return]
(cond
# Compile a constant
@ -79,6 +86,14 @@
(array/push into ~(constant ,slot ,code))
slot)
# Booleans
(boolean? code)
(let [slot (get-slot)
slottype 'boolean]
(array/push into ~(bind ,slot ,slottype))
(array/push into ~(constant ,slot ,(if code -1 0)))
slot)
# Binding
(symbol? code)
(named-slot code)
@ -136,13 +151,27 @@
(array/push into ~(move ,slot ,result))
slot)
# Named variables
'var
(do
(assert (= 2 (length args)))
(def [full-name value] args)
(assert (symbol? full-name))
(def [name tp] (type-extract full-name 'int))
(def result (visit1 value into))
(def slot (get-slot name))
(when tp
(array/push into ~(bind ,slot ,tp)))
(array/push into ~(move ,slot ,result))
slot)
# Assignment
'set
(do
(assert (= 2 (length args)))
(def [to x] args)
(def result (visit1 x into))
(def toslot (get-slot to))
(def toslot (named-slot to))
(array/push into ~(move ,toslot ,result))
toslot)
@ -160,9 +189,24 @@
# Sequence of operations
'do
(do
(var ret nil)
(each form args (set ret (visit1 form into)))
ret)
(each form (slice args 0 -2) (visit1 form into true))
(visit1 (last args) into))
# While loop
'while
(do
(def lab-test (keyword (gensym)))
(def lab-exit (keyword (gensym)))
(assert (< 1 (length args)))
(def [cnd & body] args)
(array/push into lab-test)
(def condition-slot (visit1 cnd into))
(array/push into ~(branch-not ,condition-slot ,lab-exit))
(each code body
(visit1 code into true))
(array/push into ~(jump ,lab-test))
(array/push into lab-exit)
nil)
# Branch
'if
@ -186,7 +230,7 @@
# Assume function call
(do
(def slots @[])
(def ret (get-slot))
(def ret (if no-return nil (get-slot)))
(each arg args
(array/push slots (visit1 arg into)))
(array/push into ~(call ,ret ,op ,;slots))
@ -235,28 +279,79 @@
(set left right))
result)
###
### Top level
###
(defn top
"Visit and emit code for a top level form."
[ctx form]
(assert (tuple? form))
(def [head & rest] form)
(case head
# Top level function definition
'defn
(do
# TODO doc strings
(table/clear name-to-slot)
(array/clear slot-to-name)
(def [name args & body] rest)
(assert (tuple? args))
(def [fn-name fn-tp] (type-extract name 'int))
(def pcount (length args)) #TODO - more complicated signatures
(def ir-asm
@[~(link-name ,(string fn-name))
~(parameter-count ,pcount)])
(each arg args
(def [name tp] (type-extract arg 'int))
(def slot (get-slot name))
(array/push ir-asm ~(bind ,slot ,tp)))
(each part body
(visit1 part ir-asm true))
(eprintf "%.99M\n" ir-asm)
(sysir/asm ctx ir-asm))
(errorf "unknown form %v" form)))
###
###
###
(def myprog
'(do
'(defn myprog []
(def xyz:int (+ 1 2 3))
(def abc:int (* 4 5 6))
(def x:boolean (= 5 7))
(the int (printf "hello, world!\n%d\n" (the int (if x abc xyz))))
(var i:int 0)
(while (< i 10)
(set i (+ 1 i))
(printf "i = %d\n" (the int i)))
(printf "hello, world!\n%d\n" (the int (if x abc xyz)))
(return (/ abc xyz))))
(def doloop
'(defn doloop [x:int y:int]
(var i:int x)
(while (< i y)
(set i (+ 1 i))
(printf "i = %d\n" (the int i)))
(return x)))
(def main-fn
'(defn main:int []
(var x:int 10)
(doloop 10 20)
(printf "done!\n")
(return (the int 0))))
(defn dotest
[]
(def ctx (sysir/context))
(def ir-asm
@['(link-name "main")
'(parameter-count 0)])
(setup-default-types ir-asm)
(visit1 myprog ir-asm)
(eprintf "%.99M" ir-asm)
(sysir/asm ctx ir-asm)
(setup-default-types ctx)
#(top ctx myprog)
(top ctx doloop)
(top ctx main-fn)
(print (sysir/to-c ctx)))
(dotest)

View File

@ -51,7 +51,7 @@
* [x] support for stack allocation of arrays
* [ ] more math intrinsics
* [x] source mapping (using built in Janet source mapping metadata on tuples)
* [ ] unit type or void type
* [x] unit type or void type
* [ ] (typed) function pointer types and remove calling untyped pointers
* [x] APL array semantics for binary operands (maybe?)
* [ ] a few built-in array combinators (maybe?)
@ -66,7 +66,6 @@
#include <janet.h>
#include "util.h"
#include "vector.h"
#include <math.h>
#endif
typedef enum {
@ -85,6 +84,7 @@ typedef enum {
JANET_PRIM_STRUCT,
JANET_PRIM_UNION,
JANET_PRIM_ARRAY,
JANET_PRIM_VOID,
JANET_PRIM_UNKNOWN
} JanetPrim;
@ -109,6 +109,7 @@ static const JanetPrimName prim_names[] = {
{"u64", JANET_PRIM_U64},
{"u8", JANET_PRIM_U8},
{"union", JANET_PRIM_UNION},
{"void", JANET_PRIM_VOID},
};
typedef enum {
@ -139,6 +140,7 @@ typedef enum {
JANET_SYSOP_RETURN,
JANET_SYSOP_JUMP,
JANET_SYSOP_BRANCH,
JANET_SYSOP_BRANCH_NOT,
JANET_SYSOP_ADDRESS,
JANET_SYSOP_CALLK,
JANET_SYSOP_TYPE_PRIMITIVE,
@ -153,6 +155,7 @@ typedef enum {
JANET_SYSOP_TYPE_UNION,
JANET_SYSOP_POINTER_ADD,
JANET_SYSOP_POINTER_SUBTRACT,
JANET_SYSOP_LABEL
} JanetSysOp;
typedef struct {
@ -170,6 +173,7 @@ static const JanetSysInstrName sys_op_names[] = {
{"bnot", JANET_SYSOP_BNOT},
{"bor", JANET_SYSOP_BOR},
{"branch", JANET_SYSOP_BRANCH},
{"branch-not", JANET_SYSOP_BRANCH_NOT},
{"bxor", JANET_SYSOP_BXOR},
{"call", JANET_SYSOP_CALL},
{"cast", JANET_SYSOP_CAST},
@ -180,6 +184,7 @@ static const JanetSysInstrName sys_op_names[] = {
{"gt", JANET_SYSOP_GT},
{"gte", JANET_SYSOP_GTE},
{"jump", JANET_SYSOP_JUMP},
{"label", JANET_SYSOP_LABEL},
{"link-name", JANET_SYSOP_LINK_NAME},
{"load", JANET_SYSOP_LOAD},
{"lt", JANET_SYSOP_LT},
@ -235,7 +240,14 @@ typedef struct {
uint32_t dest;
uint32_t callee;
uint32_t arg_count;
uint32_t has_dest;
} call;
struct {
uint32_t dest;
uint32_t constant;
uint32_t arg_count;
uint32_t has_dest;
} callk;
struct {
uint32_t dest;
uint32_t src;
@ -244,27 +256,16 @@ typedef struct {
uint32_t src;
} one;
struct {
union {
uint32_t to;
Janet temp_label;
};
} jump;
struct {
uint32_t cond;
union {
uint32_t to;
Janet temp_label;
};
} branch;
struct {
uint32_t dest;
uint32_t constant;
} constant;
struct {
uint32_t dest;
uint32_t constant;
uint32_t arg_count;
} callk;
struct {
uint32_t dest_type;
uint32_t prim;
@ -294,6 +295,9 @@ typedef struct {
uint32_t type;
uint64_t fixed_count;
} array;
struct {
uint32_t id;
} label;
};
int32_t line;
int32_t column;
@ -328,12 +332,11 @@ typedef struct {
uint32_t constant_count;
uint32_t return_type;
uint32_t parameter_count;
uint32_t label_count;
uint32_t *types;
JanetSysInstruction *instructions;
JanetString *register_names;
Janet *constants;
/* Can/should we remove this info after initial compilation? */
JanetTable *register_name_lookup;
JanetTable *labels;
} JanetSysIR;
@ -406,16 +409,21 @@ static uint64_t instr_read_u64(Janet x, JanetSysIR *ir) {
return janet_getuinteger64(&x, 0);
}
static uint32_t instr_read_type_operand(Janet x, JanetSysIR *ir) {
static uint32_t instr_read_type_operand(Janet x, JanetSysIR *ir, int is_definition) {
JanetSysIRLinkage *linkage = ir->linkage;
if (janet_checktype(x, JANET_SYMBOL)) {
Janet check = janet_table_get(linkage->type_name_lookup, x);
if (janet_checktype(check, JANET_NUMBER)) {
if (is_definition) {
janet_panicf("cannot redefine type %v", x);
}
return (uint32_t) janet_unwrap_number(check);
} else {
} else if (is_definition) {
uint32_t operand = linkage->type_def_count++;
janet_table_put(linkage->type_name_lookup, x, janet_wrap_number(operand));
return operand;
} else {
janet_panicf("unknown type %v", x);
}
}
if (!janet_checkuint(x)) janet_panicf("expected non-negative integer operand, got %v", x);
@ -440,15 +448,13 @@ static JanetPrim instr_read_prim(Janet x) {
}
static uint32_t instr_read_label(JanetSysIR *sysir, Janet x) {
(void) sysir;
uint32_t ret = 0;
Janet check = janet_table_get(sysir->labels, x);
if (!janet_checktype(check, JANET_NIL)) {
ret = (uint32_t) janet_unwrap_number(check);
if (janet_checktype(check, JANET_NIL)) {
ret = sysir->label_count++;
janet_table_put(sysir->labels, x, janet_wrap_number(ret));
} else {
if (janet_checktype(x, JANET_KEYWORD)) janet_panicf("unknown label %v", x);
if (!janet_checkuint(x)) janet_panicf("expected non-negative integer label, got %v", x);
ret = (uint32_t) janet_unwrap_number(x);
ret = (uint32_t) janet_unwrap_number(check);
}
return ret;
}
@ -465,8 +471,22 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
Janet x = janet_wrap_nil();
for (int32_t i = 0; i < instructions.len; i++) {
x = instructions.items[i];
/* Shorthand for labels */
if (janet_checktype(x, JANET_KEYWORD)) {
janet_table_put(labels, x, janet_wrap_number(janet_v_count(ir)));
/* Check for existing label */
JanetSysInstruction instruction;
instruction.opcode = JANET_SYSOP_LABEL;
instruction.line = -1;
instruction.column = -1;
instruction.label.id = instr_read_label(out, x);
Janet label_id = janet_wrap_number(instruction.label.id);
Janet check_defined = janet_table_get(labels, label_id);
if (janet_checktype(check_defined, JANET_NIL)) {
janet_table_put(labels, label_id, janet_wrap_number(janet_v_count(ir)));
} else {
janet_panicf("label %v already defined", x);
}
janet_v_push(ir, instruction);
continue;
}
if (!janet_checktype(x, JANET_TUPLE)) {
@ -540,7 +560,13 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
break;
case JANET_SYSOP_CALL:
instr_assert_min_length(tuple, 2, opvalue);
if (janet_checktype(tuple[1], JANET_NIL)) {
instruction.call.dest = 0;
instruction.call.has_dest = 0;
} else {
instruction.call.dest = instr_read_operand(tuple[1], out);
instruction.call.has_dest = 1;
}
Janet c = tuple[2];
if (janet_checktype(c, JANET_SYMBOL)) {
instruction.callk.arg_count = janet_tuple_length(tuple) - 3;
@ -599,14 +625,15 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
janet_v_push(ir, instruction);
break;
case JANET_SYSOP_BRANCH:
case JANET_SYSOP_BRANCH_NOT:
instr_assert_length(tuple, 3, opvalue);
instruction.branch.cond = instr_read_operand(tuple[1], out);
instruction.branch.temp_label = tuple[2];
instruction.branch.to = instr_read_label(out, tuple[2]);
janet_v_push(ir, instruction);
break;
case JANET_SYSOP_JUMP:
instr_assert_length(tuple, 2, opvalue);
instruction.jump.temp_label = tuple[1];
instruction.jump.to = instr_read_label(out, tuple[1]);
janet_v_push(ir, instruction);
break;
case JANET_SYSOP_CONSTANT: {
@ -626,22 +653,22 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
}
case JANET_SYSOP_TYPE_PRIMITIVE: {
instr_assert_length(tuple, 3, opvalue);
instruction.type_prim.dest_type = instr_read_type_operand(tuple[1], out);
instruction.type_prim.dest_type = instr_read_type_operand(tuple[1], out, 1);
instruction.type_prim.prim = instr_read_prim(tuple[2]);
janet_v_push(ir, instruction);
break;
}
case JANET_SYSOP_TYPE_POINTER: {
instr_assert_length(tuple, 3, opvalue);
instruction.pointer.dest_type = instr_read_type_operand(tuple[1], out);
instruction.pointer.type = instr_read_type_operand(tuple[2], out);
instruction.pointer.dest_type = instr_read_type_operand(tuple[1], out, 1);
instruction.pointer.type = instr_read_type_operand(tuple[2], out, 0);
janet_v_push(ir, instruction);
break;
}
case JANET_SYSOP_TYPE_ARRAY: {
instr_assert_length(tuple, 4, opvalue);
instruction.array.dest_type = instr_read_type_operand(tuple[1], out);
instruction.array.type = instr_read_type_operand(tuple[2], out);
instruction.array.dest_type = instr_read_type_operand(tuple[1], out, 1);
instruction.array.type = instr_read_type_operand(tuple[2], out, 0);
instruction.array.fixed_count = instr_read_u64(tuple[3], out);
janet_v_push(ir, instruction);
break;
@ -649,7 +676,7 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
case JANET_SYSOP_TYPE_STRUCT:
case JANET_SYSOP_TYPE_UNION: {
instr_assert_min_length(tuple, 1, opvalue);
instruction.type_types.dest_type = instr_read_type_operand(tuple[1], out);
instruction.type_types.dest_type = instr_read_type_operand(tuple[1], out, 1);
instruction.type_types.arg_count = janet_tuple_length(tuple) - 2;
janet_v_push(ir, instruction);
for (int32_t j = 2; j < janet_tuple_length(tuple); j += 3) {
@ -663,7 +690,7 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
int32_t remaining = janet_tuple_length(tuple) - j;
if (remaining > 3) remaining = 3;
for (int32_t k = 0; k < remaining; k++) {
arginstr.arg.args[k] = instr_read_type_operand(tuple[j + k], out);
arginstr.arg.args[k] = instr_read_type_operand(tuple[j + k], out, 0);
}
janet_v_push(ir, arginstr);
}
@ -672,7 +699,24 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
case JANET_SYSOP_TYPE_BIND: {
instr_assert_length(tuple, 3, opvalue);
instruction.type_bind.dest = instr_read_operand(tuple[1], out);
instruction.type_bind.type = instr_read_type_operand(tuple[2], out);
instruction.type_bind.type = instr_read_type_operand(tuple[2], out, 0);
janet_v_push(ir, instruction);
break;
}
case JANET_SYSOP_LABEL: {
instr_assert_length(tuple, 2, opvalue);
Janet x = tuple[1];
if (!janet_checktype(x, JANET_KEYWORD)) {
janet_panicf("expected keyword label, got %v", x);
}
instruction.label.id = instr_read_label(out, x);
Janet label_id = janet_wrap_number(instruction.label.id);
Janet check_defined = janet_table_get(labels, label_id);
if (janet_checktype(check_defined, JANET_NIL)) {
janet_table_put(labels, label_id, janet_wrap_number(janet_v_count(ir)));
} else {
janet_panicf("label %v already defined", x);
}
janet_v_push(ir, instruction);
break;
}
@ -705,7 +749,7 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
}
int32_t lasti = ircount - 1;
if ((ir[lasti].opcode != JANET_SYSOP_JUMP) && (ir[lasti].opcode != JANET_SYSOP_RETURN)) {
janet_panicf("last instruction must be jump or return, got %v", x);
janet_panicf("last instruction must be jump or return, got %q", x);
}
@ -716,24 +760,6 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction
out->register_count, out->parameter_count);
}
/* Fix up labels */
for (uint32_t i = 0; i < ircount; i++) {
JanetSysInstruction instruction = out->instructions[i];
uint32_t label_target;
switch (instruction.opcode) {
default:
break;
case JANET_SYSOP_BRANCH:
label_target = instr_read_label(out, instruction.branch.temp_label);
out->instructions[i].branch.to = label_target;
break;
case JANET_SYSOP_JUMP:
label_target = instr_read_label(out, instruction.jump.temp_label);
out->instructions[i].jump.to = label_target;
break;
}
}
/* Build constants */
out->constant_count = next_constant;
out->constants = next_constant ? janet_malloc(sizeof(Janet) * out->constant_count) : NULL;
@ -986,6 +1012,7 @@ static int tcheck_cast_type(JanetSysIR *sysir, uint32_t td, uint32_t ts) {
case JANET_PRIM_UNKNOWN:
case JANET_PRIM_ARRAY:
case JANET_PRIM_POINTER:
case JANET_PRIM_VOID:
return -1;
}
}
@ -1069,7 +1096,7 @@ static void tcheck_pointer_math(JanetSysIR *sysir, uint32_t dest, uint32_t lhs,
static JanetString rname(JanetSysIR *sysir, uint32_t regid) {
JanetString name = sysir->register_names[regid];
if (NULL == name) {
return janet_formatc("value%u", regid);
return janet_formatc("value[%u]", regid);
}
return name;
}
@ -1150,6 +1177,7 @@ static void janet_sysir_type_check(JanetSysIR *sysir) {
tcheck_pointer(sysir, sysir->types[instruction.two.dest]);
break;
case JANET_SYSOP_BRANCH:
case JANET_SYSOP_BRANCH_NOT:
tcheck_boolean(sysir, sysir->types[instruction.branch.cond]);
if (instruction.branch.to >= sysir->instruction_count) {
janet_panicf("label outside of range [0, %u), got %u", sysir->instruction_count, instruction.branch.to);
@ -1201,12 +1229,8 @@ static void janet_sysir_type_check(JanetSysIR *sysir) {
case JANET_SYSOP_ARG:
case JANET_SYSOP_LINK_NAME:
case JANET_SYSOP_PARAMETER_COUNT:
break;
case JANET_SYSOP_JUMP:
;
if (instruction.jump.to >= sysir->instruction_count) {
janet_panicf("label outside of range [0, %u), got %u", sysir->instruction_count, instruction.jump.to);
}
case JANET_SYSOP_LABEL:
break;
case JANET_SYSOP_RETURN: {
uint32_t ret_type = sysir->types[instruction.one.src];
@ -1279,10 +1303,8 @@ static void janet_sysir_type_check(JanetSysIR *sysir) {
tcheck_pointer(sysir, sysir->types[instruction.two.dest]);
break;
case JANET_SYSOP_BRANCH:
case JANET_SYSOP_BRANCH_NOT:
tcheck_boolean(sysir, sysir->types[instruction.branch.cond]);
if (instruction.branch.to >= sysir->instruction_count) {
janet_panicf("label outside of range [0, %u), got %u", sysir->instruction_count, instruction.branch.to);
}
break;
case JANET_SYSOP_CONSTANT:
tcheck_constant(sysir, instruction.constant.dest, sysir->constants[instruction.constant.constant]);
@ -1492,23 +1514,9 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
/* Emit body */
for (uint32_t i = 0; i < ir->instruction_count; i++) {
JanetSysInstruction instruction = ir->instructions[i];
/* Skip instruction label for some opcodes */
switch (instruction.opcode) {
case JANET_SYSOP_TYPE_PRIMITIVE:
case JANET_SYSOP_TYPE_BIND:
case JANET_SYSOP_TYPE_STRUCT:
case JANET_SYSOP_TYPE_UNION:
case JANET_SYSOP_TYPE_POINTER:
case JANET_SYSOP_TYPE_ARRAY:
continue;
default:
break;
}
janet_formatb(buffer, "_i%u:\n", i);
if (instruction.line > 0) {
janet_formatb(buffer, "#line %d\n ", instruction.line);
}
janet_buffer_push_cstring(buffer, " ");
switch (instruction.opcode) {
case JANET_SYSOP_TYPE_PRIMITIVE:
case JANET_SYSOP_TYPE_BIND:
@ -1520,6 +1528,10 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
case JANET_SYSOP_LINK_NAME:
case JANET_SYSOP_PARAMETER_COUNT:
break;
case JANET_SYSOP_LABEL: {
janet_formatb(buffer, "\n_label_%u:\n", instruction.label.id);
break;
}
case JANET_SYSOP_CONSTANT: {
uint32_t cast = ir->types[instruction.two.dest];
janet_formatb(buffer, " _r%u = (_t%u) %j;\n", instruction.two.dest, cast, ir->constants[instruction.two.src]);
@ -1529,10 +1541,13 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
janet_formatb(buffer, " _r%u = (char *) &_r%u;\n", instruction.two.dest, instruction.two.src);
break;
case JANET_SYSOP_JUMP:
janet_formatb(buffer, "goto _i%u;\n", instruction.jump.to);
janet_formatb(buffer, " goto _label_%u;\n", instruction.jump.to);
break;
case JANET_SYSOP_BRANCH:
janet_formatb(buffer, "if (_r%u) goto _i%u;\n", instruction.branch.cond, instruction.branch.to);
janet_formatb(buffer, " if (_r%u) goto _label_%u;\n", instruction.branch.cond, instruction.branch.to);
break;
case JANET_SYSOP_BRANCH_NOT:
janet_formatb(buffer, " if (!_r%u) goto _label_%u;\n", instruction.branch.cond, instruction.branch.to);
break;
case JANET_SYSOP_RETURN:
janet_formatb(buffer, " return _r%u;\n", instruction.one.src);
@ -1584,25 +1599,34 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
case JANET_SYSOP_SHR:
EMITBINOP(">>");
break;
case JANET_SYSOP_CALL:
case JANET_SYSOP_CALL: {
if (instruction.call.has_dest) {
janet_formatb(buffer, " _r%u = _r%u(", instruction.call.dest, instruction.call.callee);
} else {
janet_formatb(buffer, " _r%u(", instruction.call.callee);
}
for (uint32_t j = 0; j < instruction.call.arg_count; j++) {
uint32_t offset = j / 3 + 1;
uint32_t index = j % 3;
JanetSysInstruction arg_instruction = ir->instructions[i + offset];
janet_formatb(buffer, j ? ", _r%u" : "_r%u", arg_instruction.arg.args[index]);
}
janet_formatb(buffer, ");\n");
janet_formatb(buffer, "); // CALL\n");
break;
}
case JANET_SYSOP_CALLK:
janet_formatb(buffer, "_r%u = %j(", instruction.callk.dest, ir->constants[instruction.callk.constant]);
if (instruction.callk.has_dest) {
janet_formatb(buffer, " _r%u = %v(", instruction.callk.dest, ir->constants[instruction.callk.constant]);
} else {
janet_formatb(buffer, " %v(", ir->constants[instruction.callk.constant]);
}
for (uint32_t j = 0; j < instruction.callk.arg_count; j++) {
uint32_t offset = j / 3 + 1;
uint32_t index = j % 3;
JanetSysInstruction arg_instruction = ir->instructions[i + offset];
janet_formatb(buffer, j ? ", _r%u" : "_r%u", arg_instruction.arg.args[index]);
}
janet_formatb(buffer, ");\n");
janet_formatb(buffer, "); // CALLK\n");
break;
case JANET_SYSOP_CAST: {
uint32_t to = ir->types[instruction.two.dest];