1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-01 04:19:55 +00:00

Add some inlining for a few builtins.

This commit is contained in:
Calvin Rose 2018-07-01 19:35:45 -04:00
parent f1e1b89c67
commit 79225ad3d5
6 changed files with 208 additions and 88 deletions

View File

@ -23,110 +23,201 @@
#include <dst/dst.h> #include <dst/dst.h>
#include <dst/dstcorelib.h> #include <dst/dstcorelib.h>
#include "compile.h" #include "compile.h"
#define DST_V_NODEF_GROW
#include <headerlibs/vector.h> #include <headerlibs/vector.h>
#undef DST_V_NODEF_GROW
#include "emit.h" #include "emit.h"
/* This logic needs to be expanded for more types */ static int fixarity0(DstFopts opts, DstSlot *args) {
/* Check if a function received only numbers */
static int numbers(DstFopts opts, DstSlot *args) {
int32_t i;
int32_t len = dst_v_count(args);
(void) opts; (void) opts;
for (i = 0; i < len; i++) { return dst_v_count(args) == 0;
DstSlot s = args[i];
if (s.flags & DST_SLOT_CONSTANT) {
Dst c = s.constant;
if (!dst_checktype(c, DST_INTEGER) &&
!dst_checktype(c, DST_REAL)) {
/*dstc_cerror(opts.compiler, args[i].map, "expected number");*/
return 0;
}
}
}
return 1;
} }
static int fixarity1(DstFopts opts, DstSlot *args) {
/* Fold constants in a DstSlot [] */ (void) opts;
static DstSlot *foldc(DstSlot *slots, Dst (*fn)(Dst lhs, Dst rhs)) { return dst_v_count(args) == 1;
int32_t ccount; }
int32_t i; static int fixarity2(DstFopts opts, DstSlot *args) {
DstSlot *ret = NULL; (void) opts;
DstSlot s; return dst_v_count(args) == 2;
Dst current;
for (ccount = 0; ccount < dst_v_count(slots); ccount++) {
if (slots[ccount].flags & DST_SLOT_CONSTANT) continue;
break;
}
if (ccount < 2) return slots;
current = fn(slots[0].constant, slots[1].constant);
for (i = 2; i < ccount; i++) {
Dst nextarg = slots[i].constant;
current = fn(current, nextarg);
}
s = dstc_cslot(current);
dst_v_push(ret, s);
for (; i < dst_v_count(slots); i++) {
dst_v_push(ret, slots[i]);
}
return ret;
} }
/* Emit a series of instructions instead of a function call to a math op */ /* Emit a series of instructions instead of a function call to a math op */
static DstSlot opreduce(DstFopts opts, DstSlot *args, int op) { static DstSlot opreduce(
DstFopts opts,
DstSlot *args,
int op,
Dst zeroArity,
DstSlot (*unary)(DstFopts opts, DstSlot s)) {
DstCompiler *c = opts.compiler; DstCompiler *c = opts.compiler;
int32_t i, len; int32_t i, len;
len = dst_v_count(args); len = dst_v_count(args);
DstSlot t; DstSlot t;
if (len == 0) { if (len == 0) {
return dstc_cslot(dst_wrap_integer(0)); return dstc_cslot(zeroArity);
} else if (len == 1) { } else if (len == 1) {
if (unary)
return unary(opts, args[0]);
return args[0]; return args[0];
} }
t = dstc_gettarget(opts); t = dstc_gettarget(opts);
/* Compile initial two arguments */ /* Compile initial two arguments */
dstc_emit_sss(c, op, t, args[0], args[1]); int32_t lhs = dstc_regnear(c, args[0], DSTC_REGTEMP_0);
int32_t rhs = dstc_regnear(c, args[1], DSTC_REGTEMP_1);
dstc_emit(c, op | (t.index << 8) | (lhs << 16) | (rhs << 24));
dstc_free_reg(c, args[0], lhs);
dstc_free_reg(c, args[1], rhs);
/* Don't release t */
/* Compile the rest of the arguments */
for (i = 2; i < len; i++) { for (i = 2; i < len; i++) {
dstc_emit_sss(c, op, t, t, args[i]); rhs = dstc_regnear(c, args[i], DSTC_REGTEMP_0);
dstc_emit(c, op | (t.index << 8) | (t.index << 16) | (rhs << 24));
dstc_free_reg(c, args[i], rhs);
} }
return t; return t;
} }
/* Generic hanldling for $A = B op $C */
static DstSlot genericSSS(DstFopts opts, int op, Dst leftval, DstSlot s) {
DstSlot target = dstc_gettarget(opts);
DstSlot zero = dstc_cslot(leftval);
int32_t lhs = dstc_regnear(opts.compiler, zero, DSTC_REGTEMP_0);
int32_t rhs = dstc_regnear(opts.compiler, s, DSTC_REGTEMP_1);
dstc_emit(opts.compiler, op |
(target.index << 8) |
(lhs << 16) |
(rhs << 24));
dstc_free_reg(opts.compiler, zero, lhs);
dstc_free_reg(opts.compiler, s, rhs);
return target;
}
/* Generic hanldling for $A = op $B */
static DstSlot genericSS(DstFopts opts, int op, DstSlot s) {
DstSlot target = dstc_gettarget(opts);
int32_t rhs = dstc_regfar(opts.compiler, s, DSTC_REGTEMP_0);
dstc_emit(opts.compiler, op |
(target.index << 8) |
(rhs << 16));
dstc_free_reg(opts.compiler, s, rhs);
return target;
}
/* Generic hanldling for $A = $B op I */
static DstSlot genericSSI(DstFopts opts, int op, DstSlot s, int32_t imm) {
DstSlot target = dstc_gettarget(opts);
int32_t rhs = dstc_regnear(opts.compiler, s, DSTC_REGTEMP_0);
dstc_emit(opts.compiler, op |
(target.index << 8) |
(rhs << 16) |
(imm << 24));
dstc_free_reg(opts.compiler, s, rhs);
return target;
}
static DstSlot add(DstFopts opts, DstSlot *args) { static DstSlot add(DstFopts opts, DstSlot *args) {
DstSlot *newargs = foldc(args, dst_op_add); return opreduce(opts, args, DOP_ADD, dst_wrap_integer(0), NULL);
DstSlot ret = opreduce(opts, newargs, DOP_ADD);
if (newargs != args) dstc_freeslots(opts.compiler, newargs);
return ret;
} }
static DstSlot mul(DstFopts opts, DstSlot *args) {
return opreduce(opts, args, DOP_MULTIPLY, dst_wrap_integer(1), NULL);
}
static DstSlot subUnary(DstFopts opts, DstSlot onearg) {
return genericSSS(opts, DOP_SUBTRACT, dst_wrap_integer(0), onearg);
}
static DstSlot sub(DstFopts opts, DstSlot *args) { static DstSlot sub(DstFopts opts, DstSlot *args) {
DstSlot *newargs; return opreduce(opts, args, DOP_SUBTRACT, dst_wrap_integer(0), subUnary);
if (dst_v_count(args) == 1) {
newargs = NULL;
dst_v_push(newargs, args[0]);
dst_v_push(newargs, args[0]);
newargs[0] = dstc_cslot(dst_wrap_integer(0));
} else {
newargs = foldc(args, dst_op_subtract);
}
DstSlot ret = opreduce(opts, newargs, DOP_SUBTRACT);
if (newargs != args) dstc_freeslots(opts.compiler, newargs);
return ret;
} }
/* Keep in lexographic order */ static DstSlot divUnary(DstFopts opts, DstSlot onearg) {
static const DstCFunOptimizer optimizers[] = { return genericSSS(opts, DOP_DIVIDE, dst_wrap_integer(1), onearg);
{dst_add, numbers, add}, }
{dst_subtract, numbers, sub} static DstSlot divide(DstFopts opts, DstSlot *args) {
return opreduce(opts, args, DOP_DIVIDE, dst_wrap_integer(1), divUnary);
}
static const DstCFunOptimizer coptimizers[] = {
{dst_add, NULL, add},
{dst_subtract, NULL, sub},
{dst_multiply, NULL, mul},
{dst_divide, NULL, divide},
}; };
/* Get a cfunction optimizer. Return NULL if none exists. */ /* Get a cfunction optimizer. Return NULL if none exists. */
const DstCFunOptimizer *dstc_cfunopt(DstCFunction cfun) { const DstCFunOptimizer *dstc_cfunopt(DstCFunction cfun) {
size_t i; size_t i;
size_t n = sizeof(optimizers)/sizeof(DstCFunOptimizer); size_t n = sizeof(coptimizers)/sizeof(DstCFunOptimizer);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
if (optimizers[i].cfun == cfun) if (coptimizers[i].cfun == cfun)
return optimizers + i; return coptimizers + i;
return NULL; return NULL;
} }
/* Normal function optimizers */
/* Get, put, etc. */
static DstSlot do_error(DstFopts opts, DstSlot *args) {
dstc_emit_s(opts.compiler, DOP_ERROR, args[0]);
return dstc_cslot(dst_wrap_nil());
}
static DstSlot do_debug(DstFopts opts, DstSlot *args) {
(void)args;
dstc_emit(opts.compiler, DOP_SIGNAL | (2 << 24));
return dstc_cslot(dst_wrap_nil());
}
static DstSlot do_get(DstFopts opts, DstSlot *args) {
return opreduce(opts, args, DOP_GET, dst_wrap_nil(), NULL);
}
static DstSlot do_put(DstFopts opts, DstSlot *args) {
return opreduce(opts, args, DOP_PUT, dst_wrap_nil(), NULL);
}
static DstSlot do_length(DstFopts opts, DstSlot *args) {
return genericSS(opts, DOP_LENGTH, args[0]);
}
static DstSlot do_yield(DstFopts opts, DstSlot *args) {
return genericSSI(opts, DOP_SIGNAL, args[0], 3);
}
static DstSlot do_resume(DstFopts opts, DstSlot *args) {
return opreduce(opts, args, DOP_RESUME, dst_wrap_nil(), NULL);
}
static DstSlot do_apply1(DstFopts opts, DstSlot *args) {
/* Push phase */
int32_t array_reg = dstc_regfar(opts.compiler, args[1], DSTC_REGTEMP_1);
dstc_emit(opts.compiler, DOP_PUSH_ARRAY | (array_reg << 8));
dstc_free_reg(opts.compiler, args[1], array_reg);
/* Call phase */
int32_t fun_reg = dstc_regnear(opts.compiler, args[0], DSTC_REGTEMP_0);
DstSlot target;
if (opts.flags & DST_FOPTS_TAIL) {
dstc_emit(opts.compiler, DOP_TAILCALL | (fun_reg << 8));
target = dstc_cslot(dst_wrap_nil());
target.flags |= DST_SLOT_RETURNED;
} else {
target = dstc_gettarget(opts);
dstc_emit(opts.compiler, DOP_CALL |
(target.index << 8) |
(fun_reg << 16));
}
dstc_free_reg(opts.compiler, args[0], fun_reg);
return target;
}
/* Arranged by tag */
static const DstFunOptimizer optimizers[] = {
{NULL, NULL},
{fixarity0, do_debug},
{fixarity1, do_error},
{fixarity2, do_apply1},
{fixarity1, do_yield},
{fixarity2, do_resume},
{fixarity2, do_get},
{fixarity2, do_put},
{fixarity1, do_length}
};
const DstFunOptimizer *dstc_funopt(uint32_t flags) {
uint32_t tag = flags & DST_FUNCDEF_FLAG_TAG;
if (tag == 0 || tag > 8) return NULL;
return optimizers + tag;
}

View File

@ -314,7 +314,7 @@ DstSlot dstc_gettarget(DstFopts opts) {
slot.envindex = -1; slot.envindex = -1;
slot.constant = dst_wrap_nil(); slot.constant = dst_wrap_nil();
slot.flags = 0; slot.flags = 0;
slot.index = dstc_allocnear(opts.compiler, DSTC_REGTEMP_3); slot.index = dstc_allocnear(opts.compiler, DSTC_REGTEMP_TARGET);
} }
return slot; return slot;
} }
@ -388,7 +388,14 @@ static DstSlot dstc_call(DstFopts opts, DstSlot *slots, DstSlot fun) {
if (fun.flags & DST_SLOT_CONSTANT) { if (fun.flags & DST_SLOT_CONSTANT) {
if (dst_checktype(fun.constant, DST_CFUNCTION)) { if (dst_checktype(fun.constant, DST_CFUNCTION)) {
const DstCFunOptimizer *o = dstc_cfunopt(dst_unwrap_cfunction(fun.constant)); const DstCFunOptimizer *o = dstc_cfunopt(dst_unwrap_cfunction(fun.constant));
if (o && o->can_optimize(opts, slots)) { if (o && (!o->can_optimize || o->can_optimize(opts, slots))) {
specialized = 1;
retslot = o->optimize(opts, slots);
}
} else if (dst_checktype(fun.constant, DST_FUNCTION)) {
DstFunction *f = dst_unwrap_function(fun.constant);
const DstFunOptimizer *o = dstc_funopt(f->def->flags);
if (o && (!o->can_optimize || o->can_optimize(opts, slots))) {
specialized = 1; specialized = 1;
retslot = o->optimize(opts, slots); retslot = o->optimize(opts, slots);
} }

View File

@ -28,6 +28,16 @@
#include <dst/dstopcodes.h> #include <dst/dstopcodes.h>
#include "regalloc.h" #include "regalloc.h"
/* Tags for some functions for the prepared inliner */
#define DST_FUN_DEBUG 1
#define DST_FUN_ERROR 2
#define DST_FUN_APPLY1 3
#define DST_FUN_YIELD 4
#define DST_FUN_RESUME 5
#define DST_FUN_GET 6
#define DST_FUN_PUT 7
#define DST_FUN_LENGTH 8
/* Compiler typedefs */ /* Compiler typedefs */
typedef struct DstCompiler DstCompiler; typedef struct DstCompiler DstCompiler;
typedef struct FormOptions FormOptions; typedef struct FormOptions FormOptions;
@ -36,6 +46,7 @@ typedef struct DstScope DstScope;
typedef struct DstSlot DstSlot; typedef struct DstSlot DstSlot;
typedef struct DstFopts DstFopts; typedef struct DstFopts DstFopts;
typedef struct DstCFunOptimizer DstCFunOptimizer; typedef struct DstCFunOptimizer DstCFunOptimizer;
typedef struct DstFunOptimizer DstFunOptimizer;
typedef struct DstSpecial DstSpecial; typedef struct DstSpecial DstSpecial;
#define DST_SLOT_CONSTANT 0x10000 #define DST_SLOT_CONSTANT 0x10000
@ -146,6 +157,12 @@ struct DstCFunOptimizer {
DstSlot (*optimize)(DstFopts opts, DstSlot *args); DstSlot (*optimize)(DstFopts opts, DstSlot *args);
}; };
/* For optimizing builtin normal functions. */
struct DstFunOptimizer {
int (*can_optimize)(DstFopts opts, DstSlot *args);
DstSlot (*optimize)(DstFopts opts, DstSlot *args);
};
/* A grouping of a named special and the corresponding compiler fragment */ /* A grouping of a named special and the corresponding compiler fragment */
struct DstSpecial { struct DstSpecial {
const char *name; const char *name;
@ -154,8 +171,9 @@ struct DstSpecial {
/****************************************************/ /****************************************************/
/* Get a cfunction optimizer. Return NULL if none exists. */ /* Get an optimizer if it exists, otherwise NULL */
const DstCFunOptimizer *dstc_cfunopt(DstCFunction cfun); const DstCFunOptimizer *dstc_cfunopt(DstCFunction cfun);
const DstFunOptimizer *dstc_funopt(uint32_t flags);
/* Get a special. Return NULL if none exists */ /* Get a special. Return NULL if none exists */
const DstSpecial *dstc_special(const uint8_t *name); const DstSpecial *dstc_special(const uint8_t *name);

View File

@ -36,7 +36,7 @@ typedef enum {
DSTC_REGTEMP_4, DSTC_REGTEMP_4,
DSTC_REGTEMP_5, DSTC_REGTEMP_5,
DSTC_REGTEMP_6, DSTC_REGTEMP_6,
DSTC_REGTEMP_7 DSTC_REGTEMP_TARGET
} DstcRegisterTemp; } DstcRegisterTemp;
typedef struct { typedef struct {

View File

@ -24,6 +24,7 @@
#include <dst/dstopcodes.h> #include <dst/dstopcodes.h>
#include <dst/dstcorelib.h> #include <dst/dstcorelib.h>
#include <dst/dstcompile.h> #include <dst/dstcompile.h>
#include "compile.h"
/* Generated header */ /* Generated header */
#include "dststlbootstrap.gen.h" #include "dststlbootstrap.gen.h"
@ -54,16 +55,17 @@ static const DstReg cfuns[] = {
}; };
/* Utility for inline assembly */ /* Utility for inline assembly */
static DstFunction *dst_quick_asm( static void dst_quick_asm(
DstTable *env,
int32_t flags,
const char *name, const char *name,
int32_t arity, int32_t arity,
int varargs,
int32_t slots, int32_t slots,
const uint32_t *bytecode, const uint32_t *bytecode,
size_t bytecode_size) { size_t bytecode_size) {
DstFuncDef *def = dst_funcdef_alloc(); DstFuncDef *def = dst_funcdef_alloc();
def->arity = arity; def->arity = arity;
def->flags = varargs ? DST_FUNCDEF_FLAG_VARARG : 0; def->flags = flags;
def->slotcount = slots; def->slotcount = slots;
def->bytecode = malloc(bytecode_size); def->bytecode = malloc(bytecode_size);
def->bytecode_length = bytecode_size / sizeof(uint32_t); def->bytecode_length = bytecode_size / sizeof(uint32_t);
@ -72,7 +74,7 @@ static DstFunction *dst_quick_asm(
DST_OUT_OF_MEMORY; DST_OUT_OF_MEMORY;
} }
memcpy(def->bytecode, bytecode, bytecode_size); memcpy(def->bytecode, bytecode, bytecode_size);
return dst_thunk(def); dst_env_def(env, name, dst_wrap_function(dst_thunk(def)));
} }
DstTable *dst_stl_env(int flags) { DstTable *dst_stl_env(int flags) {
@ -114,14 +116,14 @@ DstTable *dst_stl_env(int flags) {
/* Load main functions */ /* Load main functions */
dst_env_cfuns(env, cfuns); dst_env_cfuns(env, cfuns);
dst_env_def(env, "debug", dst_wrap_function(dst_quick_asm("debug", 0, 0, 1, debug_asm, sizeof(debug_asm)))); dst_quick_asm(env, DST_FUN_YIELD, "debug", 0, 1, debug_asm, sizeof(debug_asm));
dst_env_def(env, "error", dst_wrap_function(dst_quick_asm("error", 1, 0, 1, error_asm, sizeof(error_asm)))); dst_quick_asm(env, DST_FUN_ERROR, "error", 1, 1, error_asm, sizeof(error_asm));
dst_env_def(env, "apply1", dst_wrap_function(dst_quick_asm("apply1", 2, 0, 2, apply_asm, sizeof(apply_asm)))); dst_quick_asm(env, DST_FUN_APPLY1, "apply1", 2, 2, apply_asm, sizeof(apply_asm));
dst_env_def(env, "yield", dst_wrap_function(dst_quick_asm("yield", 1, 0, 2, yield_asm, sizeof(yield_asm)))); dst_quick_asm(env, DST_FUN_YIELD, "yield", 1, 2, yield_asm, sizeof(yield_asm));
dst_env_def(env, "resume", dst_wrap_function(dst_quick_asm("resume", 2, 0, 2, resume_asm, sizeof(resume_asm)))); dst_quick_asm(env, DST_FUN_RESUME, "resume", 2, 2, resume_asm, sizeof(resume_asm));
dst_env_def(env, "get", dst_wrap_function(dst_quick_asm("get", 2, 0, 2, get_asm, sizeof(get_asm)))); dst_quick_asm(env, DST_FUN_GET, "get", 2, 2, get_asm, sizeof(get_asm));
dst_env_def(env, "put", dst_wrap_function(dst_quick_asm("put", 3, 0, 3, put_asm, sizeof(put_asm)))); dst_quick_asm(env, DST_FUN_PUT, "put", 3, 3, put_asm, sizeof(put_asm));
dst_env_def(env, "length", dst_wrap_function(dst_quick_asm("length", 1, 0, 1, length_asm, sizeof(length_asm)))); dst_quick_asm(env, DST_FUN_LENGTH, "length", 1, 1, length_asm, sizeof(length_asm));
dst_env_def(env, "VERSION", dst_cstringv(DST_VERSION)); dst_env_def(env, "VERSION", dst_cstringv(DST_VERSION));

View File

@ -459,8 +459,10 @@ struct DstKV {
}; };
/* Some function defintion flags */ /* Some function defintion flags */
#define DST_FUNCDEF_FLAG_VARARG 1 #define DST_FUNCDEF_FLAG_VARARG 0x10000
#define DST_FUNCDEF_FLAG_NEEDSENV 4 #define DST_FUNCDEF_FLAG_NEEDSENV 0x20000
#define DST_FUNCDEF_FLAG_FIXARITY 0x40000
#define DST_FUNCDEF_FLAG_TAG 0xFFFF
/* Source mapping structure for a bytecode instruction */ /* Source mapping structure for a bytecode instruction */
struct DstSourceMapping { struct DstSourceMapping {