mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 11:09:54 +00:00
Add some inlining for a few builtins.
This commit is contained in:
parent
f1e1b89c67
commit
79225ad3d5
@ -23,110 +23,201 @@
|
||||
#include <dst/dst.h>
|
||||
#include <dst/dstcorelib.h>
|
||||
#include "compile.h"
|
||||
#define DST_V_NODEF_GROW
|
||||
#include <headerlibs/vector.h>
|
||||
#undef DST_V_NODEF_GROW
|
||||
#include "emit.h"
|
||||
|
||||
/* This logic needs to be expanded for more types */
|
||||
|
||||
/* Check if a function received only numbers */
|
||||
static int numbers(DstFopts opts, DstSlot *args) {
|
||||
int32_t i;
|
||||
int32_t len = dst_v_count(args);
|
||||
static int fixarity0(DstFopts opts, DstSlot *args) {
|
||||
(void) opts;
|
||||
for (i = 0; i < len; i++) {
|
||||
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;
|
||||
return dst_v_count(args) == 0;
|
||||
}
|
||||
|
||||
/* Fold constants in a DstSlot [] */
|
||||
static DstSlot *foldc(DstSlot *slots, Dst (*fn)(Dst lhs, Dst rhs)) {
|
||||
int32_t ccount;
|
||||
int32_t i;
|
||||
DstSlot *ret = NULL;
|
||||
DstSlot s;
|
||||
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;
|
||||
static int fixarity1(DstFopts opts, DstSlot *args) {
|
||||
(void) opts;
|
||||
return dst_v_count(args) == 1;
|
||||
}
|
||||
static int fixarity2(DstFopts opts, DstSlot *args) {
|
||||
(void) opts;
|
||||
return dst_v_count(args) == 2;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
int32_t i, len;
|
||||
len = dst_v_count(args);
|
||||
DstSlot t;
|
||||
if (len == 0) {
|
||||
return dstc_cslot(dst_wrap_integer(0));
|
||||
return dstc_cslot(zeroArity);
|
||||
} else if (len == 1) {
|
||||
if (unary)
|
||||
return unary(opts, args[0]);
|
||||
return args[0];
|
||||
}
|
||||
t = dstc_gettarget(opts);
|
||||
/* 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++) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
DstSlot *newargs = foldc(args, dst_op_add);
|
||||
DstSlot ret = opreduce(opts, newargs, DOP_ADD);
|
||||
if (newargs != args) dstc_freeslots(opts.compiler, newargs);
|
||||
return ret;
|
||||
return opreduce(opts, args, DOP_ADD, dst_wrap_integer(0), NULL);
|
||||
}
|
||||
|
||||
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) {
|
||||
DstSlot *newargs;
|
||||
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;
|
||||
return opreduce(opts, args, DOP_SUBTRACT, dst_wrap_integer(0), subUnary);
|
||||
}
|
||||
|
||||
/* Keep in lexographic order */
|
||||
static const DstCFunOptimizer optimizers[] = {
|
||||
{dst_add, numbers, add},
|
||||
{dst_subtract, numbers, sub}
|
||||
static DstSlot divUnary(DstFopts opts, DstSlot onearg) {
|
||||
return genericSSS(opts, DOP_DIVIDE, dst_wrap_integer(1), onearg);
|
||||
}
|
||||
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. */
|
||||
const DstCFunOptimizer *dstc_cfunopt(DstCFunction cfun) {
|
||||
size_t i;
|
||||
size_t n = sizeof(optimizers)/sizeof(DstCFunOptimizer);
|
||||
size_t n = sizeof(coptimizers)/sizeof(DstCFunOptimizer);
|
||||
for (i = 0; i < n; i++)
|
||||
if (optimizers[i].cfun == cfun)
|
||||
return optimizers + i;
|
||||
if (coptimizers[i].cfun == cfun)
|
||||
return coptimizers + i;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -314,7 +314,7 @@ DstSlot dstc_gettarget(DstFopts opts) {
|
||||
slot.envindex = -1;
|
||||
slot.constant = dst_wrap_nil();
|
||||
slot.flags = 0;
|
||||
slot.index = dstc_allocnear(opts.compiler, DSTC_REGTEMP_3);
|
||||
slot.index = dstc_allocnear(opts.compiler, DSTC_REGTEMP_TARGET);
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
@ -388,7 +388,14 @@ static DstSlot dstc_call(DstFopts opts, DstSlot *slots, DstSlot fun) {
|
||||
if (fun.flags & DST_SLOT_CONSTANT) {
|
||||
if (dst_checktype(fun.constant, DST_CFUNCTION)) {
|
||||
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;
|
||||
retslot = o->optimize(opts, slots);
|
||||
}
|
||||
|
@ -28,6 +28,16 @@
|
||||
#include <dst/dstopcodes.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 */
|
||||
typedef struct DstCompiler DstCompiler;
|
||||
typedef struct FormOptions FormOptions;
|
||||
@ -36,6 +46,7 @@ typedef struct DstScope DstScope;
|
||||
typedef struct DstSlot DstSlot;
|
||||
typedef struct DstFopts DstFopts;
|
||||
typedef struct DstCFunOptimizer DstCFunOptimizer;
|
||||
typedef struct DstFunOptimizer DstFunOptimizer;
|
||||
typedef struct DstSpecial DstSpecial;
|
||||
|
||||
#define DST_SLOT_CONSTANT 0x10000
|
||||
@ -146,6 +157,12 @@ struct DstCFunOptimizer {
|
||||
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 */
|
||||
struct DstSpecial {
|
||||
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 DstFunOptimizer *dstc_funopt(uint32_t flags);
|
||||
|
||||
/* Get a special. Return NULL if none exists */
|
||||
const DstSpecial *dstc_special(const uint8_t *name);
|
||||
|
@ -36,7 +36,7 @@ typedef enum {
|
||||
DSTC_REGTEMP_4,
|
||||
DSTC_REGTEMP_5,
|
||||
DSTC_REGTEMP_6,
|
||||
DSTC_REGTEMP_7
|
||||
DSTC_REGTEMP_TARGET
|
||||
} DstcRegisterTemp;
|
||||
|
||||
typedef struct {
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <dst/dstopcodes.h>
|
||||
#include <dst/dstcorelib.h>
|
||||
#include <dst/dstcompile.h>
|
||||
#include "compile.h"
|
||||
|
||||
/* Generated header */
|
||||
#include "dststlbootstrap.gen.h"
|
||||
@ -54,16 +55,17 @@ static const DstReg cfuns[] = {
|
||||
};
|
||||
|
||||
/* Utility for inline assembly */
|
||||
static DstFunction *dst_quick_asm(
|
||||
static void dst_quick_asm(
|
||||
DstTable *env,
|
||||
int32_t flags,
|
||||
const char *name,
|
||||
int32_t arity,
|
||||
int varargs,
|
||||
int32_t slots,
|
||||
const uint32_t *bytecode,
|
||||
size_t bytecode_size) {
|
||||
DstFuncDef *def = dst_funcdef_alloc();
|
||||
def->arity = arity;
|
||||
def->flags = varargs ? DST_FUNCDEF_FLAG_VARARG : 0;
|
||||
def->flags = flags;
|
||||
def->slotcount = slots;
|
||||
def->bytecode = malloc(bytecode_size);
|
||||
def->bytecode_length = bytecode_size / sizeof(uint32_t);
|
||||
@ -72,7 +74,7 @@ static DstFunction *dst_quick_asm(
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
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) {
|
||||
@ -114,14 +116,14 @@ DstTable *dst_stl_env(int flags) {
|
||||
/* Load main functions */
|
||||
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_env_def(env, "error", dst_wrap_function(dst_quick_asm("error", 1, 0, 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_env_def(env, "yield", dst_wrap_function(dst_quick_asm("yield", 1, 0, 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_env_def(env, "get", dst_wrap_function(dst_quick_asm("get", 2, 0, 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_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_YIELD, "debug", 0, 1, debug_asm, sizeof(debug_asm));
|
||||
dst_quick_asm(env, DST_FUN_ERROR, "error", 1, 1, error_asm, sizeof(error_asm));
|
||||
dst_quick_asm(env, DST_FUN_APPLY1, "apply1", 2, 2, apply_asm, sizeof(apply_asm));
|
||||
dst_quick_asm(env, DST_FUN_YIELD, "yield", 1, 2, yield_asm, sizeof(yield_asm));
|
||||
dst_quick_asm(env, DST_FUN_RESUME, "resume", 2, 2, resume_asm, sizeof(resume_asm));
|
||||
dst_quick_asm(env, DST_FUN_GET, "get", 2, 2, get_asm, sizeof(get_asm));
|
||||
dst_quick_asm(env, DST_FUN_PUT, "put", 3, 3, put_asm, sizeof(put_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));
|
||||
|
||||
|
@ -459,8 +459,10 @@ struct DstKV {
|
||||
};
|
||||
|
||||
/* Some function defintion flags */
|
||||
#define DST_FUNCDEF_FLAG_VARARG 1
|
||||
#define DST_FUNCDEF_FLAG_NEEDSENV 4
|
||||
#define DST_FUNCDEF_FLAG_VARARG 0x10000
|
||||
#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 */
|
||||
struct DstSourceMapping {
|
||||
|
Loading…
Reference in New Issue
Block a user