Skip to content

Commit

Permalink
Get rid of reset_type_context() (#303)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli authored Mar 6, 2023
1 parent 6c88b37 commit ae8405f
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 226 deletions.
59 changes: 35 additions & 24 deletions src/build_cfg.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "jou_compiler.h"

struct State {
TypeContext *typectx;
const FileTypes *filetypes;
const FunctionOrMethodTypes *fomtypes;
CfGraph *cfg;
CfBlock *current_block;
List(CfBlock *) breakstack;
Expand All @@ -10,7 +11,7 @@ struct State {

static const LocalVariable *find_local_var(const struct State *st, const char *name)
{
for (LocalVariable **var = st->typectx->locals.ptr; var < End(st->typectx->locals); var++)
for (LocalVariable **var = st->cfg->locals.ptr; var < End(st->cfg->locals); var++)
if (!strcmp((*var)->name, name))
return *var;
return NULL;
Expand All @@ -19,18 +20,19 @@ static const LocalVariable *find_local_var(const struct State *st, const char *n
static LocalVariable *add_local_var(struct State *st, const Type *t)
{
LocalVariable *var = calloc(1, sizeof *var);
var->id = st->typectx->locals.len;
var->id = st->cfg->locals.len;
var->type = t;
Append(&st->typectx->locals, var);
Append(&st->cfg->locals, var);
return var;
}

static const ExpressionTypes *get_expr_types(const struct State *st, const AstExpression *expr)
{
// TODO: a fancy binary search algorithm (need to add sorting)
for (int i = 0; i < st->typectx->expr_types.len; i++)
if (st->typectx->expr_types.ptr[i]->expr == expr)
return st->typectx->expr_types.ptr[i];
assert(st->fomtypes);
for (int i = 0; i < st->fomtypes->expr_types.len; i++)
if (st->fomtypes->expr_types.ptr[i]->expr == expr)
return st->fomtypes->expr_types.ptr[i];
return NULL;
}

Expand Down Expand Up @@ -444,7 +446,7 @@ static const LocalVariable *build_function_or_method_call(struct State *st, cons
}
}
} else {
for (const struct TypeContextFunction *f = st->typectx->functions.ptr; f < End(st->typectx->functions); f++) {
for (const struct SignatureAndUsedPtr *f = st->filetypes->functions.ptr; f < End(st->filetypes->functions); f++) {
if (!strcmp(f->signature.name, call->calledname)) {
sig = &f->signature;
break;
Expand Down Expand Up @@ -859,9 +861,23 @@ static void build_body(struct State *st, const AstBody *body)
build_statement(st, &body->statements[i]);
}

static CfGraph *build_function(struct State *st, const AstBody *body)
static CfGraph *build_function_or_method(struct State *st, const Type *selfclass, const char *name, const AstBody *body)
{
assert(!st->fomtypes);
assert(!st->cfg);

for (const FunctionOrMethodTypes *f = st->filetypes->fomtypes.ptr; f < End(st->filetypes->fomtypes); f++) {
if (!strcmp(f->signature.name, name) && get_self_class(&f->signature) == selfclass) {
st->fomtypes = f;
break;
}
}
assert(st->fomtypes);

st->cfg = calloc(1, sizeof *st->cfg);
st->cfg->signature = copy_signature(&st->fomtypes->signature);
for (LocalVariable **v = st->fomtypes->locals.ptr; v < End(st->fomtypes->locals); v++)
Append(&st->cfg->locals, *v);
Append(&st->cfg->all_blocks, &st->cfg->start_block);
Append(&st->cfg->all_blocks, &st->cfg->end_block);
st->current_block = &st->cfg->start_block;
Expand All @@ -874,43 +890,38 @@ static CfGraph *build_function(struct State *st, const AstBody *body)
st->current_block->iftrue = &st->cfg->end_block;
st->current_block->iffalse = &st->cfg->end_block;

for (LocalVariable **v = st->typectx->locals.ptr; v < End(st->typectx->locals); v++)
Append(&st->cfg->locals, *v);

reset_type_context(st->typectx);
return st->cfg;
CfGraph *cfg = st->cfg;
st->fomtypes = NULL;
st->cfg = NULL;
return cfg;
}

// TODO: passing a type context here doesn't really make sense.
// It would be better to pass only the public symbols that have been imported.
CfGraphFile build_control_flow_graphs(AstToplevelNode *ast, TypeContext *typectx)
CfGraphFile build_control_flow_graphs(AstToplevelNode *ast, FileTypes *filetypes)
{
CfGraphFile result = { .filename = ast->location.filename };
struct State st = { .typectx = typectx };
struct State st = { .filetypes = filetypes };

while (ast->kind != AST_TOPLEVEL_END_OF_FILE) {
if(ast->kind == AST_TOPLEVEL_DEFINE_FUNCTION) {
const Signature *sig = typecheck_function_or_method_body(typectx, NULL, &ast->data.funcdef);
CfGraph *g = build_function(&st, &ast->data.funcdef.body);
g->signature = copy_signature(sig);
CfGraph *g = build_function_or_method(&st, NULL, ast->data.funcdef.signature.name, &ast->data.funcdef.body);
Append(&result.graphs, g);
}

if (ast->kind == AST_TOPLEVEL_DEFINE_CLASS) {
Type *classtype = NULL;
for (Type **t = typectx->owned_types.ptr; t < End(typectx->owned_types); t++)
for (Type **t = filetypes->owned_types.ptr; t < End(filetypes->owned_types); t++) {
if (!strcmp((*t)->name, ast->data.classdef.name)) {
classtype = *t;
break;
}
}
assert(classtype);

for (AstFunctionDef *m = ast->data.classdef.methods.ptr; m < End(ast->data.classdef.methods); m++) {
const Signature *sig = typecheck_function_or_method_body(typectx, classtype, m);
CfGraph *g = build_function(&st, &m->body);
g->signature = copy_signature(sig);
CfGraph *g = build_function_or_method(&st, classtype, m->signature.name, &m->body);
Append(&result.graphs, g);
reset_type_context(typectx);
}
}
ast++;
Expand Down
4 changes: 2 additions & 2 deletions src/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ static void codegen_function_or_method_def(struct State *st, const CfGraph *cfg)
free(st->llvm_locals);
}

LLVMModuleRef codegen(const CfGraphFile *cfgfile, const TypeContext *typectx)
LLVMModuleRef codegen(const CfGraphFile *cfgfile, const FileTypes *ft)
{
struct State st = {
.module = LLVMModuleCreateWithName(cfgfile->filename),
Expand All @@ -484,7 +484,7 @@ LLVMModuleRef codegen(const CfGraphFile *cfgfile, const TypeContext *typectx)
LLVMSetTarget(st.module, get_target()->triple);
LLVMSetDataLayout(st.module, get_target()->data_layout);

for (GlobalVariable **v = typectx->globals.ptr; v < End(typectx->globals); v++) {
for (GlobalVariable **v = ft->globals.ptr; v < End(ft->globals); v++) {
LLVMTypeRef t = codegen_type((*v)->type);
LLVMValueRef globalptr = LLVMAddGlobal(st.module, t, (*v)->name);
if ((*v)->defined_in_current_file)
Expand Down
26 changes: 16 additions & 10 deletions src/free.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,20 +236,26 @@ void free_export_symbol(const ExportSymbol *es)
free_signature(&es->data.funcsignature);
}

void free_type_context(const TypeContext *ctx)
void free_file_types(const FileTypes *ft)
{
for (GlobalVariable **g = ctx->globals.ptr; g < End(ctx->globals); g++)
for (GlobalVariable **g = ft->globals.ptr; g < End(ft->globals); g++)
free(*g);
for (Type **t = ctx->owned_types.ptr; t < End(ctx->owned_types); t++)
for (Type **t = ft->owned_types.ptr; t < End(ft->owned_types); t++)
free_type(*t);
for (struct TypeContextFunction *f = ctx->functions.ptr; f < End(ctx->functions); f++)
for (struct SignatureAndUsedPtr *f = ft->functions.ptr; f < End(ft->functions); f++)
free_signature(&f->signature);
free(ctx->expr_types.ptr);
free(ctx->globals.ptr);
free(ctx->locals.ptr);
free(ctx->types.ptr);
free(ctx->owned_types.ptr);
free(ctx->functions.ptr);
for (FunctionOrMethodTypes *f = ft->fomtypes.ptr; f < End(ft->fomtypes); f++) {
for (ExpressionTypes **et = f->expr_types.ptr; et < End(f->expr_types); et++)
free(*et);
free(f->expr_types.ptr);
free(f->locals.ptr); // Don't free individual locals because they're owned by CFG now
free_signature(&f->signature);
}
free(ft->globals.ptr);
free(ft->types.ptr);
free(ft->owned_types.ptr);
free(ft->functions.ptr);
free(ft->fomtypes.ptr);
}


Expand Down
49 changes: 22 additions & 27 deletions src/jou_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ typedef struct GlobalVariable GlobalVariable;
typedef struct LocalVariable LocalVariable;
typedef struct ExpressionTypes ExpressionTypes;
typedef struct ExportSymbol ExportSymbol;
typedef struct TypeContext TypeContext;
typedef struct FileTypes FileTypes;
typedef struct FunctionOrMethodTypes FunctionOrMethodTypes;

typedef struct CfBlock CfBlock;
typedef struct CfGraph CfGraph;
Expand Down Expand Up @@ -445,16 +446,21 @@ struct ExportSymbol {
} data;
};

struct TypeContext {
const Signature *current_function_signature;
// expr_types tells what type each expression has.
// It contains nothing for calls to "-> void" functions.
// Type information about a function or method defined in the current file.
struct FunctionOrMethodTypes {
Signature signature;
List(ExpressionTypes *) expr_types;
List(GlobalVariable *) globals; // TODO: probably doesn't need to has pointers
List(LocalVariable *) locals;
};

// Type information about a file.
struct FileTypes {
FunctionOrMethodTypes *current_fom_types; // conceptually this is internal to typecheck.c
List(FunctionOrMethodTypes) fomtypes;
List(GlobalVariable *) globals; // TODO: probably doesn't need to has pointers
List(Type *) owned_types; // These will be freed later
List(struct TypeContextType { const Type *type; bool *usedptr; }) types;
List(struct TypeContextFunction { Signature signature; bool *usedptr; }) functions;
List(struct TypeAndUsedPtr { const Type *type; bool *usedptr; }) types;
List(struct SignatureAndUsedPtr { Signature signature; bool *usedptr; }) functions;
};

/*
Expand All @@ -468,30 +474,18 @@ Type checking is split into several stages:
fields each struct has.
3. Check function bodies.
Each step is ran on all files before we move on to the next step. This
Each stage is ran on all files before we move on to the next stage. This
ensures that different files can import things from each other. You
also never need to forward-declare a struct: the struct exists by the
time we check the function signature or struct body that uses it.
Steps 1 and 2 return a list of ExportSymbols for other files to use.
The list is terminated with (ExportSymbol){0}, which you can detect by
checking if the name of the ExportSymbol is empty.
Step 3 is interleaved with build_cfg (see the reset_type_context() function).
*/
ExportSymbol *typecheck_step1_create_types(TypeContext *ctx, const AstToplevelNode *ast);
ExportSymbol *typecheck_step2_signatures_globals_structbodies(TypeContext *ctx, const AstToplevelNode *ast);
const Signature *typecheck_function_or_method_body(TypeContext *ctx, const Type *classtype, const AstFunctionDef *ast);

/*
Wipes all function-specific data, making the type context suitable
for use with the next function definition. For example, the list of
local variables is emptied so that the next function doesn't have
access to the same local variables. But e.g. the list of all known
function signatures is preserved, so that the next function can
call the previous function.
*/
void reset_type_context(TypeContext *ctx);
ExportSymbol *typecheck_stage1_create_types(FileTypes *ft, const AstToplevelNode *ast);
ExportSymbol *typecheck_stage2_signatures_globals_structbodies(FileTypes *ft, const AstToplevelNode *ast);
void typecheck_stage3_function_and_method_bodies(FileTypes *ft, const AstToplevelNode *ast);


// Control Flow Graph.
Expand Down Expand Up @@ -587,9 +581,10 @@ entire compilation. It is used in error messages.
*/
Token *tokenize(FILE *f, const char *filename);
AstToplevelNode *parse(const Token *tokens, const char *stdlib_path);
CfGraphFile build_control_flow_graphs(AstToplevelNode *ast, TypeContext *typectx);
// Type checking happens between parsing and building CFGs.
CfGraphFile build_control_flow_graphs(AstToplevelNode *ast, FileTypes *ft);
void simplify_control_flow_graphs(const CfGraphFile *cfgfile);
LLVMModuleRef codegen(const CfGraphFile *cfgfile, const TypeContext *typectx);
LLVMModuleRef codegen(const CfGraphFile *cfgfile, const FileTypes *ft);
void compile_to_exe(LLVMModuleRef module, const char *exepath, const CommandLineFlags *flags);
int run_program(LLVMModuleRef module, const CommandLineFlags *flags);

Expand All @@ -603,7 +598,7 @@ but not any of the data contained within individual nodes.
void free_constant(const Constant *c);
void free_tokens(Token *tokenlist);
void free_ast(AstToplevelNode *topnodelist);
void free_type_context(const TypeContext *typectx);
void free_file_types(const FileTypes *ft);
void free_export_symbol(const ExportSymbol *es);
void free_control_flow_graphs(const CfGraphFile *cfgfile);
void free_control_flow_graph_block(const CfGraph *cfg, CfBlock *b);
Expand Down
35 changes: 21 additions & 14 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ static void parse_arguments(int argc, char **argv, CommandLineFlags *flags, cons
struct FileState {
char *path;
AstToplevelNode *ast;
TypeContext typectx;
FileTypes types;
LLVMModuleRef module;
ExportSymbol *pending_exports;
};
Expand Down Expand Up @@ -231,7 +231,7 @@ static void compile_ast_to_llvm(struct CompileState *compst, struct FileState *f
if (compst->flags.verbose)
printf("Build CFG: %s\n", fs->path);

CfGraphFile cfgfile = build_control_flow_graphs(fs->ast, &fs->typectx);
CfGraphFile cfgfile = build_control_flow_graphs(fs->ast, &fs->types);
for (AstToplevelNode *imp = fs->ast; imp->kind == AST_TOPLEVEL_IMPORT; imp++)
if (!imp->data.import.used)
show_warning(imp->location, "'%s' imported but not used", imp->data.import.symbolname);
Expand All @@ -246,7 +246,7 @@ static void compile_ast_to_llvm(struct CompileState *compst, struct FileState *f
if (compst->flags.verbose)
printf("Build LLVM IR: %s\n", fs->path);

fs->module = codegen(&cfgfile, &fs->typectx);
fs->module = codegen(&cfgfile, &fs->types);
free_control_flow_graphs(&cfgfile);

if(compst->flags.verbose)
Expand Down Expand Up @@ -330,21 +330,24 @@ static void add_imported_symbol(struct FileState *fs, const ExportSymbol *es, As

switch(es->kind) {
case EXPSYM_FUNCTION:
Append(&fs->typectx.functions, (struct TypeContextFunction){
Append(&fs->types.functions, (struct SignatureAndUsedPtr){
.signature = copy_signature(&es->data.funcsignature),
.usedptr = &imp->used,
});
break;
case EXPSYM_TYPE:
Append(&fs->types.types, (struct TypeAndUsedPtr){
.type=es->data.type,
.usedptr=&imp->used,
});
break;
case EXPSYM_GLOBAL_VAR:
g = calloc(1, sizeof(*g));
g->type = es->data.type;
g->usedptr = &imp->used;
assert(strlen(es->name) < sizeof g->name);
strcpy(g->name, es->name);
Append(&fs->typectx.globals, g);
break;
case EXPSYM_TYPE:
Append(&fs->typectx.types, (struct TypeContextType){ .type=es->data.type, .usedptr=&imp->used });
Append(&fs->types.globals, g);
break;
}
}
Expand Down Expand Up @@ -452,17 +455,21 @@ int main(int argc, char **argv)

for (struct FileState *fs = compst.files.ptr; fs < End(compst.files); fs++) {
if (compst.flags.verbose)
printf("Typecheck step 1: %s\n", fs->path);
fs->pending_exports = typecheck_step1_create_types(&fs->typectx, fs->ast);
printf("Typecheck stage 1: %s\n", fs->path);
fs->pending_exports = typecheck_stage1_create_types(&fs->types, fs->ast);
}
add_imported_symbols(&compst);

for (struct FileState *fs = compst.files.ptr; fs < End(compst.files); fs++) {
if (compst.flags.verbose)
printf("Typecheck step 2: %s\n", fs->path);
fs->pending_exports = typecheck_step2_signatures_globals_structbodies(&fs->typectx, fs->ast);
printf("Typecheck stage 2: %s\n", fs->path);
fs->pending_exports = typecheck_stage2_signatures_globals_structbodies(&fs->types, fs->ast);
}
add_imported_symbols(&compst);
for (struct FileState *fs = compst.files.ptr; fs < End(compst.files); fs++) {
if (compst.flags.verbose)
printf("Typecheck stage 3: %s\n", fs->path);
typecheck_stage3_function_and_method_bodies(&fs->types, fs->ast);
}

check_for_404_imports(&compst);

Expand Down Expand Up @@ -493,7 +500,7 @@ int main(int argc, char **argv)

for (struct FileState *fs = compst.files.ptr; fs < End(compst.files); fs++) {
free(fs->path);
free_type_context(&fs->typectx);
free_file_types(&fs->types);
}
free(compst.files.ptr);
free(compst.stdlib_path);
Expand Down
Loading

0 comments on commit ae8405f

Please sign in to comment.