1
0
mirror of https://github.com/janet-lang/janet synced 2025-08-03 20:43:55 +00:00

Begin C Function specialization in the compiler.

This commit is contained in:
bakpakin 2018-01-24 17:59:00 -05:00
parent aa68ef49f1
commit 5460ff19bf
15 changed files with 191 additions and 41 deletions

View File

@ -39,7 +39,8 @@ src/assembler/asm.c
set(COMPILER_SOURCES set(COMPILER_SOURCES
src/compiler/compile.c src/compiler/compile.c
src/compiler/compile_specials.c src/compiler/specials.c
src/compiler/cfuns.c
src/compiler/context.c src/compiler/context.c
src/compiler/compile.h src/compiler/compile.h

View File

@ -7,8 +7,7 @@ Lisp with several native useful datatypes. Some of the more interesting and
useful features are first class functions and closures, immutable and mutable useful features are first class functions and closures, immutable and mutable
hashtables, arrays, and bytebuffers, macros (NYI), tail-call optimization, hashtables, arrays, and bytebuffers, macros (NYI), tail-call optimization,
and continuations (coroutines, error handling). The runtime and and continuations (coroutines, error handling). The runtime and
compiler are written in C99, but should eventually be completely compatible compiler are written in C99.
with C89 compilers.
There is a repl for trying out the language, as well as the ability There is a repl for trying out the language, as well as the ability
to run script files. This client program is separate from the core runtime, so to run script files. This client program is separate from the core runtime, so
@ -16,16 +15,20 @@ dst could be embedded into other programs.
## Features ## Features
First class closures * First class closures
Garbage collection * Garbage collection
lexical scoping * Lexical scoping
First class green threads (continuations) * First class green threads (continuations)
Mutable and immutable arrays (array/tuple) * Mutable and immutable arrays (array/tuple)
Mutable and immutable hashtables (table/struct) * Mutable and immutable hashtables (table/struct)
Mutable and immutable strings (buffer/string) * Mutable and immutable strings (buffer/string)
Byte code interpreter with an assembly interface * Byte code interpreter with an assembly interface, as well as bytecode verification
Proper tail calls for functional code * Proper tail calls for functional code
Direct interop with C * Direct interop with C
* REPL (read eval print loop)
The code can be compiled to be either a bytecode interpreter and runtime, or
a full language.
## Compiling and Running ## Compiling and Running

96
src/compiler/cfuns.c Normal file
View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <dst/dst.h>
#include <dst/dststl.h>
#include "compile.h"
#define DST_V_NODEF_GROW
#include <headerlibs/vector.h>
#undef DST_V_NODEF_GROW
/* This logic needs to be expanded for more types */
/* Check if a function recieved only numbers */
static int numbers(DstFopts opts, DstSM *args) {
int32_t i;
int32_t len = dst_v_count(args);
(void) opts;
for (i = 0; i < len; i++) {
DstSlot s = args[i].slot;
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 can_add(DstFopts opts, DstAst *ast, DstSM *args) {
(void) ast;
return numbers(opts, args);
}
static DstSlot add(DstFopts opts, DstAst *ast, DstSM *args) {
DstCompiler *c = opts.compiler;
int32_t i, len;
int32_t op1, op2;
len = dst_v_count(args);
DstSlot t;
if (len == 0) {
return dstc_cslot(dst_wrap_integer(0));
} else if (len == 1) {
return args[0].slot;
}
t = dstc_gettarget(opts);
/* Compile initial two arguments */
op1 = dstc_preread(c, args[0].map, 0xFF, 1, args[0].slot);
op2 = dstc_preread(c, args[1].map, 0xFF, 2, args[1].slot);
dstc_emit(c, ast, (t.index << 8) | (op1 << 16) | (op2 << 24) | DOP_ADD);
dstc_postread(c, args[0].slot, op1);
dstc_postread(c, args[1].slot, op2);
for (i = 2; i < len; i++) {
op1 = dstc_preread(c, args[i].map, 0xFF, 1, args[i].slot);
dstc_emit(c, ast, (t.index << 8) | (t.index << 16) | (op1 << 24) | DOP_ADD);
dstc_postread(c, args[i].slot, op1);
}
return t;
}
/* Keep in lexographic order */
static const DstCFunOptimizer optimizers[] = {
{dst_add, can_add, add}
};
/* 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);
for (i = 0; i < n; i++)
if (optimizers[i].cfun == cfun)
return optimizers + i;
return NULL;
}

View File

@ -703,18 +703,31 @@ static DstSlot dstc_call(DstFopts opts, DstAst *ast, DstSM *sms, DstSlot fun) {
DstSlot retslot; DstSlot retslot;
int32_t localindex; int32_t localindex;
DstCompiler *c = opts.compiler; DstCompiler *c = opts.compiler;
dstc_pushslots(c, ast, sms); int specialized = 0;
dstc_freeslots(c, sms); if (fun.flags & DST_SLOT_CONSTANT) {
localindex = dstc_preread(c, ast, 0xFF, 1, fun); if (dst_checktype(fun.constant, DST_CFUNCTION)) {
if (opts.flags & DST_FOPTS_TAIL) { const DstCFunOptimizer *o = dstc_cfunopt(dst_unwrap_cfunction(fun.constant));
dstc_emit(c, ast, (localindex << 8) | DOP_TAILCALL); if (o && o->can_optimize(opts, ast, sms)) {
retslot = dstc_cslot(dst_wrap_nil()); specialized = 1;
retslot.flags = DST_SLOT_RETURNED; retslot = o->optimize(opts, ast, sms);
} else { }
retslot = dstc_gettarget(opts); }
dstc_emit(c, ast, (localindex << 16) | (retslot.index << 8) | DOP_CALL); /* TODO dst function inlining (no c functions)*/
} }
dstc_postread(c, fun, localindex); if (!specialized) {
dstc_pushslots(c, ast, sms);
localindex = dstc_preread(c, ast, 0xFF, 1, fun);
if (opts.flags & DST_FOPTS_TAIL) {
dstc_emit(c, ast, (localindex << 8) | DOP_TAILCALL);
retslot = dstc_cslot(dst_wrap_nil());
retslot.flags = DST_SLOT_RETURNED;
} else {
retslot = dstc_gettarget(opts);
dstc_emit(c, ast, (localindex << 16) | (retslot.index << 8) | DOP_CALL);
}
dstc_postread(c, fun, localindex);
}
dstc_freeslots(c, sms);
return retslot; return retslot;
} }

View File

@ -34,7 +34,8 @@ typedef struct SlotTracker SlotTracker;
typedef struct DstScope DstScope; typedef struct DstScope DstScope;
typedef struct DstSlot DstSlot; typedef struct DstSlot DstSlot;
typedef struct DstFopts DstFopts; typedef struct DstFopts DstFopts;
typedef struct DstCFunctionOptimizer DstCFunctionOptimizer; typedef struct DstCFunOptimizer DstCFunOptimizer;
typedef struct DstSpecial DstSpecial;
#define DST_SLOT_CONSTANT 0x10000 #define DST_SLOT_CONSTANT 0x10000
#define DST_SLOT_NAMED 0x20000 #define DST_SLOT_NAMED 0x20000
@ -130,16 +131,17 @@ DstFopts dstc_fopts_default(DstCompiler *c);
/* A grouping of optimizations on a cfunction given certain conditions /* A grouping of optimizations on a cfunction given certain conditions
* on the arguments (such as all constants, or some known types). The appropriate * on the arguments (such as all constants, or some known types). The appropriate
* optimizations should be tried before compiling a normal function call. */ * optimizations should be tried before compiling a normal function call. */
typedef struct DstCFunOptimizer { struct DstCFunOptimizer {
DstCFunction cfun; DstCFunction cfun;
DstSlot (*optimize)(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv); int (*can_optimize)(DstFopts opts, DstAst *ast, DstSM *args);
} DstCFunOptimizer; DstSlot (*optimize)(DstFopts opts, DstAst *ast, DstSM *args);
};
/* A grouping of a named special and the corresponding compiler fragment */ /* A grouping of a named special and the corresponding compiler fragment */
typedef struct DstSpecial { struct DstSpecial {
const char *name; const char *name;
DstSlot (*compile)(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv); DstSlot (*compile)(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv);
} DstSpecial; };
/****************************************************/ /****************************************************/

View File

@ -27,11 +27,11 @@
#define CHUNKSIZE 1024 #define CHUNKSIZE 1024
/* Read input for a repl */ /* Read input for a repl */
static int replread(DstContext *c) { static int replread(DstContext *c, DstParserStatus status) {
if (c->buffer.count == 0) if (status == DST_PARSE_PENDING)
printf("> ");
else
printf(">> "); printf(">> ");
else
printf("> ");
for (;;) { for (;;) {
int x = fgetc(stdin); int x = fgetc(stdin);
if (x == EOF) { if (x == EOF) {
@ -76,9 +76,10 @@ static void filedeinit(DstContext *c) {
fclose((FILE *) (c->user)); fclose((FILE *) (c->user));
} }
static int fileread(DstContext *c) { static int fileread(DstContext *c, DstParserStatus status) {
size_t nread; size_t nread;
FILE *f = (FILE *) c->user; FILE *f = (FILE *) c->user;
(void) status;
dst_buffer_ensure(&c->buffer, CHUNKSIZE); dst_buffer_ensure(&c->buffer, CHUNKSIZE);
nread = fread(c->buffer.data, 1, CHUNKSIZE, f); nread = fread(c->buffer.data, 1, CHUNKSIZE, f);
if (nread != CHUNKSIZE && ferror(f)) { if (nread != CHUNKSIZE && ferror(f)) {
@ -149,11 +150,12 @@ int dst_context_run(DstContext *c, int flags) {
int done = 0; int done = 0;
int errflags = 0; int errflags = 0;
DstParser parser; DstParser parser;
DstParserStatus status;
dst_parser_init(&parser, flags); dst_parser_init(&parser, flags);
while (!done) { while (!done) {
int bufferdone = 0; int bufferdone = 0;
while (!bufferdone) { while (!bufferdone) {
DstParserStatus status = dst_parser_status(&parser); status = dst_parser_status(&parser);
switch (status) { switch (status) {
case DST_PARSE_FULL: case DST_PARSE_FULL:
{ {
@ -196,7 +198,7 @@ int dst_context_run(DstContext *c, int flags) {
/* Refill the buffer */ /* Refill the buffer */
c->buffer.count = 0; c->buffer.count = 0;
c->index = 0; c->index = 0;
if (c->read_chunk(c) || c->buffer.count == 0) { if (c->read_chunk(c, status) || c->buffer.count == 0) {
done = 1; done = 1;
} }
} }

View File

@ -184,3 +184,5 @@ Dst dst_ast_unwrap(Dst x) {
} }
} }

View File

@ -388,5 +388,6 @@ int dst_lib_math(DstArgs args) {
dst_env_def(env, "pi", dst_wrap_real(3.1415926535897931)); dst_env_def(env, "pi", dst_wrap_real(3.1415926535897931));
dst_env_def(env, "e", dst_wrap_real(2.7182818284590451)); dst_env_def(env, "e", dst_wrap_real(2.7182818284590451));
dst_env_def(env, "inf", dst_wrap_real(1.0 / 0.0));
return 0; return 0;
} }

View File

@ -67,7 +67,7 @@ struct DstContext {
void *user; void *user;
int32_t index; int32_t index;
int (*read_chunk)(DstContext *self); int (*read_chunk)(DstContext *self, DstParserStatus status);
void (*on_error)(DstContext *self, DstContextErrorType type, Dst err, size_t start, size_t end); void (*on_error)(DstContext *self, DstContextErrorType type, Dst err, size_t start, size_t end);
void (*on_value)(DstContext *self, Dst value); void (*on_value)(DstContext *self, Dst value);
void (*deinit)(DstContext *self); void (*deinit)(DstContext *self);

View File

@ -54,6 +54,7 @@
/* Vector code */ /* Vector code */
/* Grow the buffer dynamically. Used for push operations. */ /* Grow the buffer dynamically. Used for push operations. */
#ifndef DST_V_NODEF_GROW
static void *dst_v_grow(void *v, int32_t increment, int32_t itemsize) { static void *dst_v_grow(void *v, int32_t increment, int32_t itemsize) {
int32_t dbl_cur = (NULL != v) ? 2 * dst_v__cap(v) : 0; int32_t dbl_cur = (NULL != v) ? 2 * dst_v__cap(v) : 0;
int32_t min_needed = dst_v_count(v) + increment; int32_t min_needed = dst_v_count(v) + increment;
@ -70,6 +71,7 @@ static void *dst_v_grow(void *v, int32_t increment, int32_t itemsize) {
return (void *) (2 * sizeof(int32_t)); // try to force a NULL pointer exception later return (void *) (2 * sizeof(int32_t)); // try to force a NULL pointer exception later
} }
} }
#endif
/* Clone a buffer. */ /* Clone a buffer. */
#ifdef DST_V_DEF_COPYMEM #ifdef DST_V_DEF_COPYMEM

View File

@ -155,6 +155,7 @@ struct DstParseState {
}; };
#define PFLAG_CONTAINER 1 #define PFLAG_CONTAINER 1
#define PFLAG_WASTOKEN 2
static void pushstate(DstParser *p, Consumer consumer, int flags) { static void pushstate(DstParser *p, Consumer consumer, int flags) {
DstParseState s; DstParseState s;
@ -475,10 +476,14 @@ const char *dst_parser_error(DstParser *parser) {
Dst dst_parser_produce(DstParser *parser) { Dst dst_parser_produce(DstParser *parser) {
Dst ret; Dst ret;
int32_t i;
DstParserStatus status = dst_parser_status(parser); DstParserStatus status = dst_parser_status(parser);
if (status != DST_PARSE_FULL) return dst_wrap_nil(); if (status != DST_PARSE_FULL) return dst_wrap_nil();
ret = dst_v_last(parser->argstack); ret = parser->argstack[0];
dst_v_pop(parser->argstack); for (i = 1; i < dst_v_count(parser->argstack); i++) {
parser->argstack[i - 1] = parser->argstack[i];
}
dst_v__cnt(parser->argstack)--;
return ret; return ret;
} }

1
test/hello.dst Normal file
View File

@ -0,0 +1 @@
(print "Hello, World!")

18
test/scratch.dst Normal file
View File

@ -0,0 +1,18 @@
(def fib (asm '{
bytecode [
(load-integer 2 2)
(less-than 2 0 2)
(jump-if-not 2 2)
(return 0)
(load-self 1)
(add-immediate 0 0 -1)
(push 0)
(call 2 1)
(add-immediate 0 0 -1)
(push 0)
(call 3 1)
(add-integer 0 2 3)
(return 0)
]
arity 1
}))

View File

@ -151,6 +151,10 @@
'(1 2 3) 5 '(1 2 3) 5
:apple 1)) "struct order does not matter 2") :apple 1)) "struct order does not matter 2")
# Symbol function
(assert (= (symbol "abc" 1 2 3) 'abc123) "symbol function")
# Fiber tests # Fiber tests
(def afiber (fiber (fn [x] (def afiber (fiber (fn [x]
@ -163,7 +167,7 @@
# yield tests # yield tests
(def t (fiber (fn [] (transfer nil 1) (transfer nil 2) 3))) (def t (fiber (fn [] (transfer nil 1) (yield 2) 3)))
(assert (= 1 (transfer t)) "initial transfer to new fiber") (assert (= 1 (transfer t)) "initial transfer to new fiber")
(assert (= 2 (transfer t)) "second transfer to fiber") (assert (= 2 (transfer t)) "second transfer to fiber")