harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 99df56f9d2228fa896f6e7f6e4fa8f8a2da208e2
parent 4634ef110c2d073a3ea87f870d5044dcf089cc9a
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 11 Mar 2021 10:18:29 -0500

Store a cache of loaded modules

harec was accidentally quadratic without this. This reduces the build
time of the stdlib to basically nothing.

Diffstat:
Minclude/check.h | 19+++++++++++++++++--
Minclude/identifier.h | 2++
Minclude/mod.h | 4+++-
Msrc/check.c | 30+++++++++++++++++++++++++-----
Msrc/identifier.c | 11+++++++++++
Msrc/lex.c | 9+--------
Msrc/main.c | 2+-
Msrc/mod.c | 24++++++++++++++++++++++--
Msrc/scope.c | 3---
9 files changed, 82 insertions(+), 22 deletions(-)

diff --git a/include/check.h b/include/check.h @@ -9,7 +9,16 @@ struct build_tags; struct expression; struct scope; +#define MODCACHE_BUCKETS 256 + +struct modcache { + struct identifier ident; + struct scope *scope; + struct modcache *next; +}; + struct context { + struct modcache **modcache; struct type_store *store; const struct type *fntype; struct identifier *ns; @@ -85,8 +94,14 @@ struct ast_unit; struct scope *check(struct type_store *ts, struct build_tags *tags, const struct ast_unit *aunit, - struct unit *unit, - bool scan_only); + struct unit *unit); + +struct scope *check_internal(struct type_store *ts, + struct modcache **cache, + struct build_tags *tags, + const struct ast_unit *aunit, + struct unit *unit, + bool scan_only); void check_expression(struct context *ctx, const struct ast_expression *aexpr, diff --git a/include/identifier.h b/include/identifier.h @@ -2,12 +2,14 @@ #define HARE_IDENTIFIER_H #include <stddef.h> #include <stdbool.h> +#include <stdint.h> struct identifier { char *name; struct identifier *ns; }; +uint32_t identifier_hash(uint32_t init, const struct identifier *ident); char *identifier_unparse(const struct identifier *ident); int identifier_unparse_static( const struct identifier *ident, char *buf, size_t len); diff --git a/include/mod.h b/include/mod.h @@ -4,6 +4,8 @@ #include "scope.h" #include "type_store.h" -struct scope *module_resolve(struct identifier *ident, struct type_store *store); +struct modcache; +struct scope *module_resolve(struct modcache *cache[], + struct identifier *ident, struct type_store *store); #endif diff --git a/src/check.c b/src/check.c @@ -2628,10 +2628,10 @@ scan_declarations(struct context *ctx, const struct ast_decls *decls) } static void -load_import(struct ast_imports *import, +load_import(struct context *ctx, struct ast_imports *import, struct type_store *ts, struct scope *scope) { - struct scope *mod = module_resolve(&import->ident, ts); + struct scope *mod = module_resolve(ctx->modcache, &import->ident, ts); switch (import->mode) { case AST_IMPORT_IDENTIFIER: @@ -2720,14 +2720,19 @@ load_import(struct ast_imports *import, } struct scope * -check(struct type_store *ts, struct build_tags *tags, - const struct ast_unit *aunit, struct unit *unit, bool scan_only) +check_internal(struct type_store *ts, + struct modcache **cache, + struct build_tags *tags, + const struct ast_unit *aunit, + struct unit *unit, + bool scan_only) { struct context ctx = {0}; ctx.ns = unit->ns; ctx.tags = tags; ctx.store = ts; ctx.store->check_context = &ctx; + ctx.modcache = cache; // Top-level scope management involves: // @@ -2750,7 +2755,7 @@ check(struct type_store *ts, struct build_tags *tags, for (struct ast_imports *imports = su->imports; imports; imports = imports->next) { - load_import(imports, ts, ctx.scope); + load_import(&ctx, imports, ts, ctx.scope); bool found = false; for (struct imports *uimports = unit->imports; @@ -2776,6 +2781,10 @@ check(struct type_store *ts, struct build_tags *tags, next = &(*next)->next; } + if (scan_only) { + return ctx.unit; + } + // Second pass populates the expression graph struct scopes *scope = subunit_scopes; struct declarations **next_decl = &unit->declarations; @@ -2790,3 +2799,14 @@ check(struct type_store *ts, struct build_tags *tags, return ctx.unit; } + +struct scope * +check(struct type_store *ts, + struct build_tags *tags, + const struct ast_unit *aunit, + struct unit *unit) +{ + struct modcache *modcache[MODCACHE_BUCKETS]; + memset(modcache, 0, sizeof(modcache)); + return check_internal(ts, modcache, tags, aunit, unit, false); +} diff --git a/src/identifier.c b/src/identifier.c @@ -8,6 +8,17 @@ #include "identifier.h" #include "util.h" +uint32_t +identifier_hash(uint32_t init, const struct identifier *ident) +{ + init = fnv1a_s(init, ident->name); + init = fnv1a(init, 0); + if (ident->ns) { + init = identifier_hash(init, ident->ns); + } + return init; +} + static int _asprintf(char **strp, const char *fmt, ...) { diff --git a/src/lex.c b/src/lex.c @@ -9,7 +9,6 @@ #include <stdlib.h> #include <string.h> #include "lex.h" -#include "trace.h" #include "utf8.h" #include "util.h" @@ -710,7 +709,6 @@ lex2(struct lexer *lexer, struct token *out, uint32_t c) break; case '/': while ((c = next(lexer, NULL, false)) != UTF8_INVALID && c != '\n') ; - trace(TR_LEX, "comment"); return _lex(lexer, out); default: push(lexer, c, false); @@ -912,10 +910,7 @@ _lex(struct lexer *lexer, struct token *out) enum lexical_token lex(struct lexer *lexer, struct token *out) { - enum lexical_token l = _lex(lexer, out); - const char *s = token_str(out); - trace(TR_LEX, "%s", s); - return l; + return _lex(lexer, out); } void @@ -1107,7 +1102,5 @@ void unlex(struct lexer *lexer, struct token *in) { assert(lexer->un.token == T_ERROR && "Only one unlex is supported"); - const char *s = token_str(in); - trace(TR_LEX, "(unlex) %s", s); lexer->un = *in; } diff --git a/src/main.c b/src/main.c @@ -137,7 +137,7 @@ main(int argc, char *argv[]) struct type_store ts = {0}; builtin_types_init(); - check(&ts, tags, &aunit, &unit, false); + check(&ts, tags, &aunit, &unit); if (stage == STAGE_CHECK) { return 0; } diff --git a/src/mod.c b/src/mod.c @@ -1,6 +1,7 @@ #include <assert.h> #include <errno.h> #include <limits.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -44,8 +45,18 @@ open_typedefs(struct identifier *ident) } struct scope * -module_resolve(struct identifier *ident, struct type_store *store) +module_resolve(struct modcache *cache[], + struct identifier *ident, + struct type_store *store) { + uint32_t hash = identifier_hash(FNV1A_INIT, ident); + struct modcache **bucket = &cache[hash % MODCACHE_BUCKETS]; + for (; *bucket; bucket = &(*bucket)->next) { + if (identifier_eq(&(*bucket)->ident, ident)) { + return (*bucket)->scope; + } + } + struct lexer lexer = {0}; struct ast_unit aunit = {0}; @@ -64,5 +75,14 @@ module_resolve(struct identifier *ident, struct type_store *store) // TODO: Free unused bits struct unit u = {0}; - return check(store, NULL, &aunit, &u, true); + struct scope *scope = check_internal(store, + cache, NULL, &aunit, &u, true); + + bucket = &cache[hash % MODCACHE_BUCKETS]; + struct modcache *item = xcalloc(1, sizeof(struct modcache)); + identifier_dup(&item->ident, ident); + item->scope = scope; + item->next = *bucket; + *bucket = item; + return scope; } diff --git a/src/scope.c b/src/scope.c @@ -6,8 +6,6 @@ #include "trace.h" #include "util.h" -#include <stdio.h> - static uint32_t name_hash(uint32_t init, const struct identifier *ident) { @@ -93,7 +91,6 @@ scope_insert(struct scope *scope, enum object_type otype, uint32_t hash = name_hash(FNV1A_INIT, name); struct scope_object **bucket = &scope->buckets[hash % SCOPE_BUCKETS]; if (*bucket) { - //fprintf(stderr, "%u %u\n", hash, hash % SCOPE_BUCKETS); o->mnext = *bucket; } *bucket = o;