From 644ac8caf8e8a1fa5b91d1827769c99d8009d37a Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Wed, 31 May 2023 12:26:27 -0500 Subject: [PATCH] Add compiler optimizations for #1163 - eachp Should result in much better bytecode in the simple case. --- src/boot/boot.janet | 2 +- src/core/specials.c | 101 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 22 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 1d2a2b31..87953502 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -445,7 +445,7 @@ ,(case kind :each ~(,in ,ds ,k) :keys k - :pairs ~(,tuple ,k (,in ,ds ,k)))) + :pairs ~[,k (,in ,ds ,k)])) ,;body (set ,k (,next ,ds ,k)))))) diff --git a/src/core/specials.c b/src/core/specials.c index 30977416..d2a08909 100644 --- a/src/core/specials.c +++ b/src/core/specials.c @@ -306,12 +306,16 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) } /* Add attributes to a global def or var table */ -static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) { +static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, const Janet *argv) { int32_t i; JanetTable *tab = janet_table(2); const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL ? ((const char *)janet_unwrap_symbol(argv[0])) : ""; + if (argn < 2) { + janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind)); + return NULL; + } for (i = 1; i < argn - 1; i++) { Janet attr = argv[i]; switch (janet_type(attr)) { @@ -335,18 +339,51 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) return tab; } -static JanetSlot dohead(const char *kind, JanetCompiler *c, JanetFopts opts, Janet *head, int32_t argn, const Janet *argv) { +typedef struct SlotHeadPair { + Janet lhs; + JanetSlot rhs; +} SlotHeadPair; + +SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopts opts, Janet lhs, Janet rhs) { + + /* Detect if we can do an optimization to avoid some allocations */ + int can_destructure_lhs = janet_checktype(lhs, JANET_TUPLE) + || janet_checktype(lhs, JANET_ARRAY); + int rhs_is_indexed = janet_checktype(rhs, JANET_ARRAY) + || (janet_checktype(rhs, JANET_TUPLE) && (janet_tuple_flag(janet_unwrap_tuple(rhs)) & JANET_TUPLE_FLAG_BRACKETCTOR)); + uint32_t has_drop = opts.flags & JANET_FOPTS_DROP; + JanetFopts subopts = janetc_fopts_default(c); - JanetSlot ret; - if (argn < 2) { - janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind)); - return janetc_cslot(janet_wrap_nil()); - } - *head = argv[0]; subopts.flags = opts.flags & ~(JANET_FOPTS_TAIL | JANET_FOPTS_DROP); + + if (has_drop && can_destructure_lhs && rhs_is_indexed) { + /* Code is of the form (def [a b] [1 2]), avoid the allocation of two tuples */ + JanetView view_lhs, view_rhs; + janet_indexed_view(lhs, &view_lhs.items, &view_lhs.len); + janet_indexed_view(rhs, &view_rhs.items, &view_rhs.len); + int found_amp = 0; + for (int32_t i = 0; i < view_lhs.len; i++) { + if (janet_symeq(view_lhs.items[i], "&")) { + found_amp = 1; + /* Good error will be generated later. */ + break; + } + } + if (!found_amp) { + for (int32_t i = 0; i < view_lhs.len; i++) { + Janet sub_rhs = view_rhs.len <= i ? janet_wrap_nil() : view_rhs.items[i]; + into = dohead_destructure(c, into, subopts, view_lhs.items[i], sub_rhs); + } + return into; + } + } + + /* No optimization, do the simple way */ subopts.hint = opts.hint; - ret = janetc_value(subopts, argv[argn - 1]); - return ret; + JanetSlot ret = janetc_value(subopts, rhs); + SlotHeadPair shp = {lhs, ret}; + janet_v_push(into, shp); + return into; } /* Def or var a symbol in a local scope */ @@ -412,12 +449,23 @@ static int varleaf( static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) { JanetCompiler *c = opts.compiler; - Janet head; - JanetTable *attr_table = handleattr(c, argn, argv); - JanetSlot ret = dohead("var", c, opts, &head, argn, argv); - if (c->result.status == JANET_COMPILE_ERROR) + JanetTable *attr_table = handleattr(c, "var", argn, argv); + if (c->result.status == JANET_COMPILE_ERROR) { return janetc_cslot(janet_wrap_nil()); - destructure(c, argv[0], ret, varleaf, attr_table); + } + SlotHeadPair *into = NULL; + into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]); + if (c->result.status == JANET_COMPILE_ERROR) { + janet_v_free(into); + return janetc_cslot(janet_wrap_nil()); + } + JanetSlot ret; + janet_assert(janet_v_count(into) > 0, "bad destructure"); + for (int32_t i = 0; i < janet_v_count(into); i++) { + destructure(c, into[i].lhs, into[i].rhs, varleaf, attr_table); + ret = into[i].rhs; + } + janet_v_free(into); return ret; } @@ -461,13 +509,24 @@ static int defleaf( static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) { JanetCompiler *c = opts.compiler; - Janet head; - opts.flags &= ~JANET_FOPTS_HINT; - JanetTable *attr_table = handleattr(c, argn, argv); - JanetSlot ret = dohead("def", c, opts, &head, argn, argv); - if (c->result.status == JANET_COMPILE_ERROR) + JanetTable *attr_table = handleattr(c, "def", argn, argv); + if (c->result.status == JANET_COMPILE_ERROR) { return janetc_cslot(janet_wrap_nil()); - destructure(c, argv[0], ret, defleaf, attr_table); + } + opts.flags &= ~JANET_FOPTS_HINT; + SlotHeadPair *into = NULL; + into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]); + if (c->result.status == JANET_COMPILE_ERROR) { + janet_v_free(into); + return janetc_cslot(janet_wrap_nil()); + } + JanetSlot ret; + janet_assert(janet_v_count(into) > 0, "bad destructure"); + for (int32_t i = 0; i < janet_v_count(into); i++) { + destructure(c, into[i].lhs, into[i].rhs, defleaf, attr_table); + ret = into[i].rhs; + } + janet_v_free(into); return ret; }