mirror of
https://github.com/janet-lang/janet
synced 2026-10-15 20:21:40 +00:00
Add bytecode inlining at optimization >= 2.
Currently very simple hueristic, and not supported on many functions that instatiate closures or load/set upvalues. We also now, more than ever, need a slot-compaction routine to reduce the number of slots used by functions.
This commit is contained in:
+162
-8
@@ -541,8 +541,8 @@ void janet_bytecode_local_value_numbering(JanetFuncDef *def, BytecodeBB *blocks,
|
||||
uint32_t *code = def->bytecode;
|
||||
for (int32_t blocki = 0; blocki < n_blocks; blocki++) {
|
||||
BytecodeBB block = blocks[blocki];
|
||||
int32_t nextv = 1; /* Reserve negative values */
|
||||
for (int32_t i = 0; i < def->slotcount; i++) ctx.value_numbers[i] = 0; /* initialize to unknown */
|
||||
int32_t nextv = 1 + def->slotcount; /* Reserve negative values */
|
||||
for (int32_t i = 0; i < def->slotcount; i++) ctx.value_numbers[i] = i + 1; /* initialize to some unique value */
|
||||
janet_table_clear(ctx.value_lookup);
|
||||
for (int32_t pc = block.start; pc < block.end; pc++) {
|
||||
uint32_t A = (code[pc] & 0xFF00U) >> 8;
|
||||
@@ -781,18 +781,18 @@ void janet_bytecode_local_value_numbering(JanetFuncDef *def, BytecodeBB *blocks,
|
||||
case JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE:
|
||||
case JOP_GET_INDEX: {
|
||||
int32_t Bv = ctx.value_numbers[B];
|
||||
int16_t C_imm = (int8_t) C; /* To 16 bit and sign extend */
|
||||
uint16_t encoded_C = (uint16_t)C_imm;
|
||||
int32_t Cv = -4 - (int32_t)encoded_C;
|
||||
/* Check constant prop */
|
||||
{
|
||||
int16_t C_imm = (int8_t) C; /* To 16 bit and sign extend */
|
||||
uint16_t encoded_C = (uint16_t)C_imm;
|
||||
int32_t Cv = -4 - (int32_t)encoded_C;
|
||||
VN check_const_prop = vn_const_prop(&ctx, opcode, Bv, Cv);
|
||||
if (check_const_prop.value_number) {
|
||||
code[pc] = vn_move_or_load(&ctx, A, check_const_prop, code[pc]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
double key = vn_encode_2arg(opcode, Bv, C);
|
||||
double key = vn_encode_2arg(opcode, Bv, Cv);
|
||||
VN vn = vn_check_key(&ctx, key);
|
||||
if (vn.value_number) {
|
||||
code[pc] = vn_move_or_load(&ctx, A, vn, code[pc]);
|
||||
@@ -800,7 +800,11 @@ void janet_bytecode_local_value_numbering(JanetFuncDef *def, BytecodeBB *blocks,
|
||||
}
|
||||
B = vn_find_reg(&ctx, Bv, B);
|
||||
code[pc] = recon_abc(code[pc], A, B, C);
|
||||
ctx.value_numbers[A] = nextv++;
|
||||
VN vnnext;
|
||||
vnnext.has_slot = 1;
|
||||
vnnext.slot = A;
|
||||
vnnext.value_number = nextv++;
|
||||
vn_add_lookup(&ctx, key, vnnext);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1108,6 +1112,13 @@ void janet_bytecode_jump_threading(JanetFuncDef *def) {
|
||||
switch (code[i] & 0x7F) {
|
||||
default:
|
||||
continue;
|
||||
case JOP_MOVE_NEAR:
|
||||
case JOP_MOVE_FAR:
|
||||
if (((codes[i] >> 8) & 0xFF) == (codes[i] >> 16)) {
|
||||
/* Not really jump threading, but an easy optimization */
|
||||
code[i] = JOP_NOOP;
|
||||
}
|
||||
continue;
|
||||
case JOP_JUMP:
|
||||
target = i + (codes[i] >> 8);
|
||||
break;
|
||||
@@ -1219,7 +1230,6 @@ static void janet_bytecode_movopt_basic_block(JanetFuncDef *def, BytecodeBB bb,
|
||||
pin = 1;
|
||||
break;
|
||||
/* Read A */
|
||||
case JOP_ERROR:
|
||||
case JOP_TYPECHECK:
|
||||
case JOP_JUMP_IF:
|
||||
case JOP_JUMP_IF_NOT:
|
||||
@@ -1231,6 +1241,7 @@ static void janet_bytecode_movopt_basic_block(JanetFuncDef *def, BytecodeBB bb,
|
||||
ins[0] = Ia;
|
||||
break;
|
||||
/* Read D */
|
||||
case JOP_ERROR:
|
||||
case JOP_RETURN:
|
||||
case JOP_PUSH:
|
||||
case JOP_PUSH_ARRAY:
|
||||
@@ -1435,12 +1446,155 @@ void janet_bytecode_movopt_full(JanetFuncDef *def, BytecodeBB *blocks, int32_t n
|
||||
janet_free(bitsets);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inlining
|
||||
*/
|
||||
|
||||
/* Rewrite a chunk of code for inline use. Returns a vector of uint32_ts to be freed with janet_v_free.
|
||||
* Use dest_slot = -1 to mean an inline tail call.
|
||||
* TODO - replace some of the asserts with return NULL to indicate inlining failed.
|
||||
* */
|
||||
uint32_t *janet_bytecode_inline_chunk(JanetFuncDef *idef,
|
||||
int32_t dest_slot, uint32_t slot_delta, uint32_t const_delta) {
|
||||
int is_tail = dest_slot < 0;
|
||||
uint32_t *icode = idef->bytecode;
|
||||
uint32_t *code = NULL;
|
||||
int32_t *jump_pcs = NULL;
|
||||
int32_t *remap = NULL;
|
||||
/* Skip functions that are too hard for now */
|
||||
janet_assert(slot_delta < 256, "slot delta too big");
|
||||
janet_assert(idef->defs_length == 0, "no inlining closure functions");
|
||||
janet_assert(idef->environments_length == 0, "no inlining functions that reference closure environments");
|
||||
/* Patch instructions with slot shift and constant index shift. */
|
||||
for (int32_t pc = 0; pc < idef->bytecode_length; pc++) {
|
||||
enum JanetInstructionType itype = janet_instructions[icode[pc] & 0x7F];
|
||||
uint32_t newi = icode[pc];
|
||||
/* rewrite slots. For now, assume adding slot delta will not overflow any slot indices */
|
||||
switch (itype) {
|
||||
case JINT_SES:
|
||||
janet_assert(0, "upvalue instruction not supported for inlining");
|
||||
case JINT_0:
|
||||
case JINT_L:
|
||||
break;
|
||||
case JINT_S:
|
||||
janet_assert((newi & 0x7F) != JOP_LOAD_SELF, "cannot inline lds instruction");
|
||||
/* fallthrough */
|
||||
case JINT_SI:
|
||||
case JINT_SU:
|
||||
case JINT_ST:
|
||||
case JINT_SL:
|
||||
case JINT_SD:
|
||||
newi += slot_delta << 8;
|
||||
break;
|
||||
case JINT_SC:
|
||||
newi += slot_delta << 8 | (const_delta << 16);
|
||||
break;
|
||||
case JINT_SS:
|
||||
case JINT_SSI:
|
||||
case JINT_SSU:
|
||||
newi += (slot_delta << 8) | (slot_delta << 16);
|
||||
break;
|
||||
case JINT_SSS:
|
||||
newi += (slot_delta << 8) | (slot_delta << 16) | (slot_delta << 24);
|
||||
break;
|
||||
}
|
||||
if (is_tail) {
|
||||
janet_v_push(code, newi);
|
||||
} else {
|
||||
/* Account for new locations for jump targets */
|
||||
janet_v_push(remap, janet_v_count(code));
|
||||
/* Replace returns with move and jump to end of the block
|
||||
* Also fix tail calls that are no longer in tail position. */
|
||||
switch (icode[pc] & 0x7F) {
|
||||
default:
|
||||
janet_v_push(code, newi);
|
||||
break;
|
||||
case JOP_RETURN: {
|
||||
uint32_t retreg = newi >> 8;
|
||||
janet_assert(retreg < 0x10000U, "retreg too big");
|
||||
janet_v_push(code, JOP_MOVE_NEAR | (dest_slot << 8) | (retreg << 16));
|
||||
if (pc < idef->bytecode_length - 1) {
|
||||
/* Last instruction doesn't need a jump. Jump-
|
||||
* threading would take care of this, but why not
|
||||
* generate better code earlier? */
|
||||
janet_v_push(jump_pcs, janet_v_count(code));
|
||||
janet_v_push(code, JOP_JUMP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JOP_RETURN_NIL: {
|
||||
janet_v_push(code, JOP_LOAD_NIL | (dest_slot << 8));
|
||||
if (pc < idef->bytecode_length - 1) { /* Ditto */
|
||||
janet_v_push(jump_pcs, janet_v_count(code));
|
||||
janet_v_push(code, JOP_JUMP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JOP_TAILCALL: {
|
||||
/* Convert to a normal call and jump */
|
||||
uint32_t callee = newi >> 8;
|
||||
janet_assert(callee < 0x10000U, "callee too big");
|
||||
janet_v_push(code, JOP_CALL | (dest_slot << 8) | (callee << 16));
|
||||
if (pc < idef->bytecode_length - 1) { /* Ditto */
|
||||
janet_v_push(jump_pcs, janet_v_count(code));
|
||||
janet_v_push(code, JOP_JUMP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_tail) {
|
||||
janet_assert(janet_v_count(remap) == idef->bytecode_length, "bad remap");
|
||||
/* Fix jumps to account for the inserted jumps */
|
||||
for (int32_t old_pc = 0; old_pc < idef->bytecode_length; old_pc++) {
|
||||
switch (idef->bytecode[old_pc] & 0x7F) {
|
||||
default:
|
||||
continue;
|
||||
case JOP_JUMP_IF:
|
||||
case JOP_JUMP_IF_NOT:
|
||||
case JOP_JUMP_IF_NOT_NIL:
|
||||
case JOP_JUMP_IF_NIL: {
|
||||
int32_t pc = remap[old_pc];
|
||||
int32_t old_jump = ((int32_t)(idef->bytecode[old_pc])) >> 16;
|
||||
int32_t new_jump = remap[old_pc + old_jump] - pc;
|
||||
code[pc] = (code[pc] & 0xFFFFU) | (uint32_t)(new_jump << 16);
|
||||
}
|
||||
continue;
|
||||
case JOP_JUMP: {
|
||||
int32_t pc = remap[old_pc];
|
||||
int32_t old_jump = ((int32_t)(idef->bytecode[old_pc])) >> 8;
|
||||
int32_t new_jump = remap[old_pc + old_jump] - pc;
|
||||
code[pc] = JOP_JUMP | (uint32_t)(new_jump << 8);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
janet_v_free(remap);
|
||||
}
|
||||
/* Now fix jumps to jump to the end of the generated block */
|
||||
while (janet_v_count(jump_pcs)) {
|
||||
int32_t pc = janet_v_last(jump_pcs);
|
||||
janet_v_pop(jump_pcs);
|
||||
int32_t target = janet_v_count(code) - pc;
|
||||
janet_assert(target > 0, "backwards jump");
|
||||
code[pc] = JOP_JUMP | ((uint32_t)target << 8);
|
||||
}
|
||||
janet_v_free(jump_pcs);
|
||||
return code;
|
||||
}
|
||||
|
||||
/* Entry point for optimization. The def should already be valid as determined by janet_verify.
|
||||
* The compiler may skip this check for performance but the assembler will not. */
|
||||
void janet_bytecode_optimize(JanetFuncDef *def, int32_t level) {
|
||||
int32_t delta;
|
||||
#ifdef JANET_DEBUG
|
||||
int result = janet_verify(def);
|
||||
if (result != 0) {
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
janet_eprintf("%4d: %Q\n", i, janet_asm_decode_instruction(def->bytecode[i]));
|
||||
}
|
||||
}
|
||||
janet_assert(result == 0, "input bytecode bad");
|
||||
#endif
|
||||
if (level >= 0) {
|
||||
|
||||
+87
-2
@@ -28,6 +28,7 @@
|
||||
#include "vector.h"
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
JanetFopts janetc_fopts_default(JanetCompiler *c) {
|
||||
@@ -608,6 +609,24 @@ void janetc_throwaway(JanetFopts opts, Janet x) {
|
||||
}
|
||||
}
|
||||
|
||||
static int function_can_inline(JanetFopts opts, JanetFunction *func, JanetSlot *slots) {
|
||||
JanetFuncDef *def = func->def;
|
||||
/* TODO - hoist this check to a flag on the func def */
|
||||
if (opts.compiler->optimize < 2) return 0;
|
||||
if (def->environments_length) return 0;
|
||||
if (def->defs_length) return 0;
|
||||
if (def->slotcount > 32) return 0;
|
||||
if (def->bytecode_length > 40) return 0;
|
||||
if (def->flags & (JANET_FUNCDEF_FLAG_NAMEDARGS | JANET_FUNCDEF_FLAG_STRUCTARG | JANET_FUNCDEF_FLAG_VARARG)) return 0;
|
||||
if (janet_v_count(slots) != def->min_arity) return 0; /* TODO - optional arguments */
|
||||
if (janet_v_count(slots) != def->max_arity) return 0;
|
||||
/* Check for JOP_LOAD_SELF */
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
if ((def->bytecode[i] & 0x7F) == JOP_LOAD_SELF) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compile a call or tailcall instruction */
|
||||
static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, const Janet *form) {
|
||||
JanetSlot retslot;
|
||||
@@ -620,9 +639,76 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, c
|
||||
if (o && (!o->can_optimize || o->can_optimize(opts, slots))) {
|
||||
specialized = 1;
|
||||
retslot = o->optimize(opts, slots);
|
||||
} else if (function_can_inline(opts, f, slots)) {
|
||||
/* Function inlining */
|
||||
int is_tail;
|
||||
JanetSlot target;
|
||||
if ((opts.flags & JANET_FOPTS_TAIL) &&
|
||||
/* Prevent top level tail calls for better errors */
|
||||
!(c->scope->flags & JANET_SCOPE_TOP)) {
|
||||
is_tail = 1;
|
||||
} else {
|
||||
is_tail = 0;
|
||||
}
|
||||
/* contiguous chunk of fun->def->slotcount slots */
|
||||
if (!is_tail) target = janetc_gettarget(opts);
|
||||
int32_t nslots = f->def->slotcount;
|
||||
int32_t slot_delta = janetc_regalloc_n(&c->scope->ra, nslots);
|
||||
if (slot_delta + f->def->slotcount < 256) {
|
||||
/*janet_eprintf("inlining %V\n", janet_wrap_function(f));*/
|
||||
/* Limit inlining when too many slots */
|
||||
JanetScope *scope = c->scope;
|
||||
while (scope) {
|
||||
if (scope->flags & JANET_SCOPE_FUNCTION)
|
||||
break;
|
||||
scope = scope->parent;
|
||||
}
|
||||
/* Setup the function parameters */
|
||||
JanetSlot dest;
|
||||
dest.envindex = -1;
|
||||
dest.flags = JANET_SLOTTYPE_ANY;
|
||||
dest.constant = janet_wrap_nil();
|
||||
for (int32_t i = 0; i < janet_v_count(slots); i++) {
|
||||
dest.index = slot_delta + i;
|
||||
janetc_copy(c, dest, slots[i]);
|
||||
}
|
||||
|
||||
/* Initialize other slots to nil just like in a normal function call */
|
||||
/* TODO - add pass to remove these if needed so we can add this back.
|
||||
* The current compiler doesn't rely on nil-initialization behavior but might in the future
|
||||
* (e.g. removing redundant ldn at the beginning of a function.
|
||||
for (int32_t i = janet_v_count(slots); i < nslots; i++) {
|
||||
janetc_emit(c, JOP_LOAD_NIL | ((uint32_t)(i + slot_delta) << 8));
|
||||
}
|
||||
*/
|
||||
|
||||
/* Do main inlining (patch the bytecode) */
|
||||
int32_t const_delta = janet_v_count(scope->consts); /* Get const delta _after_ moving slots. janetc_copy can add constants here! */
|
||||
uint32_t *insert_code = janet_bytecode_inline_chunk(f->def, is_tail ? -1 : target.index, slot_delta, const_delta);
|
||||
for (int32_t i = 0; i < janet_v_count(insert_code); i++) {
|
||||
/* TODO - better match up source code mapping - e.g. if inlined function
|
||||
* is from the same source file */
|
||||
janetc_emit(c, insert_code[i]);
|
||||
}
|
||||
/* Copy constants into our constants buffer */
|
||||
for (int32_t i = 0; i < f->def->constants_length; i++) {
|
||||
janet_v_push(scope->consts, f->def->constants[i]);
|
||||
}
|
||||
janet_v_free(insert_code);
|
||||
specialized = 1;
|
||||
if (is_tail) {
|
||||
retslot = janetc_cslot(janet_wrap_nil());
|
||||
retslot.flags = JANET_SLOT_RETURNED;
|
||||
} else {
|
||||
retslot = target;
|
||||
}
|
||||
} else {
|
||||
/*janet_eprintf("inlining failed %V\n", janet_wrap_function(f));*/
|
||||
;
|
||||
}
|
||||
janetc_regalloc_free_n(&c->scope->ra, slot_delta, nslots);
|
||||
}
|
||||
}
|
||||
/* TODO janet function inlining (no c functions)*/
|
||||
}
|
||||
if (!specialized) {
|
||||
int32_t min_arity = janetc_pushslots(c, slots);
|
||||
@@ -937,7 +1023,6 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
||||
} else {
|
||||
/* Function calls */
|
||||
JanetSlot head = janetc_value(subopts, tup[0]);
|
||||
subopts.flags = JANET_FUNCTION | JANET_CFUNCTION;
|
||||
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head, tup);
|
||||
janetc_freeslot(c, head);
|
||||
}
|
||||
|
||||
@@ -288,6 +288,10 @@ JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
|
||||
/* Check if a symbol is already in scope for shadowing lints */
|
||||
Shadowing janetc_shadowcheck(JanetCompiler *c, const uint8_t *sym);
|
||||
|
||||
/* Inlining. We can do inling both from bytecode and from our frontend */
|
||||
uint32_t *janet_bytecode_inline_chunk(JanetFuncDef *idef,
|
||||
int32_t dest_slot, uint32_t slot_delta, uint32_t const_delta);
|
||||
|
||||
/* Bytecode optimization */
|
||||
void janet_bytecode_optimize(JanetFuncDef *def, int32_t level);
|
||||
|
||||
|
||||
@@ -133,6 +133,29 @@ int32_t janetc_regalloc_1(JanetcRegisterAllocator *ra) {
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Find n contiguous registers but do not allocate them. */
|
||||
int32_t janetc_regalloc_findn(JanetcRegisterAllocator *ra, int32_t n) {
|
||||
int32_t run = 0;
|
||||
int32_t i;
|
||||
for (i = 0; i < (ra->count * 32); i++) {
|
||||
if (ra->chunks[i >> 5] & ithbit(i & 0x1F)) {
|
||||
run = 0;
|
||||
} else {
|
||||
run++;
|
||||
}
|
||||
if (run == n) return 1 + i - n;
|
||||
}
|
||||
return 1 + i - run;
|
||||
}
|
||||
|
||||
int32_t janetc_regalloc_n(JanetcRegisterAllocator *ra, int32_t n) {
|
||||
int32_t loc = janetc_regalloc_findn(ra, n);
|
||||
for (int32_t i = 0; i < n; i++) janetc_regalloc_touch(ra, loc + i);
|
||||
if (loc + n - 1 > ra->max)
|
||||
ra->max = loc + n - 1;
|
||||
return loc;
|
||||
}
|
||||
|
||||
/* Free a register. The register must have been previously allocated
|
||||
* without being freed. */
|
||||
void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) {
|
||||
@@ -141,6 +164,10 @@ void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) {
|
||||
ra->chunks[chunk] &= ~ithbit(bit);
|
||||
}
|
||||
|
||||
void janetc_regalloc_free_n(JanetcRegisterAllocator *ra, int32_t loc, int32_t n) {
|
||||
for (int32_t i = 0; i < n; i++) janetc_regalloc_free(ra, loc + i);
|
||||
}
|
||||
|
||||
/* Check if a register is set. */
|
||||
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg) {
|
||||
int32_t chunk = reg >> 5;
|
||||
|
||||
@@ -57,5 +57,7 @@ void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRe
|
||||
void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src);
|
||||
void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg);
|
||||
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg);
|
||||
int32_t janetc_regalloc_n(JanetcRegisterAllocator *ra, int32_t n);
|
||||
void janetc_regalloc_free_n(JanetcRegisterAllocator *ra, int32_t loc, int32_t n);
|
||||
|
||||
#endif
|
||||
|
||||
+6
-4
@@ -964,8 +964,9 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
janet_def_addflags(def);
|
||||
int32_t defindex = janetc_addfuncdef(c, def);
|
||||
/* Do basic optimization */
|
||||
int32_t optimize = (c->scope->flags & JANET_SCOPE_UNUSED) ? -1 : c->optimize;
|
||||
janet_bytecode_optimize(def, optimize);
|
||||
if (!(c->scope->flags & JANET_SCOPE_UNUSED)) {
|
||||
janet_bytecode_optimize(def, c->optimize);
|
||||
}
|
||||
/* And then load the closure and call it. */
|
||||
int32_t cloreg = janetc_regalloc_temp(&c->scope->ra, JANETC_REGTEMP_0);
|
||||
janetc_emit(c, JOP_CLOSURE | ((uint32_t) cloreg << 8) | ((uint32_t) defindex << 16));
|
||||
@@ -1026,7 +1027,6 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
|
||||
/* Begin function */
|
||||
c->scope->flags |= JANET_SCOPE_CLOSURE;
|
||||
int32_t optimize = (c->scope->flags & JANET_SCOPE_UNUSED) ? -1 : c->optimize;
|
||||
janetc_scope(&fnscope, c, JANET_SCOPE_FUNCTION, "function");
|
||||
|
||||
if (argn == 0) {
|
||||
@@ -1217,7 +1217,9 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (arity + vararg > def->slotcount) def->slotcount = arity + vararg;
|
||||
|
||||
/* Do basic optimization */
|
||||
janet_bytecode_optimize(def, optimize);
|
||||
if (!(fnscope.flags & JANET_SCOPE_UNUSED)) {
|
||||
janet_bytecode_optimize(def, c->optimize);
|
||||
}
|
||||
|
||||
/* Instantiate closure */
|
||||
ret = janetc_gettarget(opts);
|
||||
|
||||
+1
-1
@@ -669,7 +669,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_ERROR)
|
||||
vm_return(JANET_SIGNAL_ERROR, stack[A]);
|
||||
vm_return(JANET_SIGNAL_ERROR, stack[D]);
|
||||
|
||||
VM_OP(JOP_TYPECHECK)
|
||||
vm_assert_types(stack[A], E);
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
(defn check-good-compile
|
||||
[code msg]
|
||||
(each opt [-1 0 1]
|
||||
(each opt [0 1 2 3]
|
||||
(setdyn *optimize* opt)
|
||||
(def lints @[])
|
||||
(def result4 (compile code (curenv) "suite-compile.janet" lints))
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
(defn check-bad-compile
|
||||
[code msg]
|
||||
(each opt [-1 0 1]
|
||||
(each opt [0 1 2 3]
|
||||
(setdyn *optimize* opt)
|
||||
(def lints @[])
|
||||
(def result4 (compile code (curenv) "suite-compile.janet" lints))
|
||||
@@ -134,7 +134,7 @@
|
||||
|
||||
(defn check-lint-compile
|
||||
[code msg]
|
||||
(each opt [-1 0 1]
|
||||
(each opt [0 1 2 3]
|
||||
(setdyn *optimize* opt)
|
||||
(def lints @[])
|
||||
(def result4 (compile code (curenv) "suite-compile.janet" lints))
|
||||
|
||||
Reference in New Issue
Block a user