harec

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

commit aca00ad93ecd947d601d4d08c320235ed288147b
parent 6961bfd6431a3a8c1b776d998c8fdd35094c6112
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 24 Dec 2020 15:30:37 -0500

gen: implement call expressions

Diffstat:
Minclude/qbe.h | 6+++++-
Minclude/scope.h | 9++++++++-
Msrc/check.c | 21+++++++++++----------
Msrc/emit.c | 35++++++++++++++++++++++++++++++++---
Msrc/gen.c | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/parse.c | 2++
Msrc/qbe.c | 10++++++++--
Msrc/qtype.c | 2+-
Msrc/scope.c | 2++
9 files changed, 136 insertions(+), 29 deletions(-)

diff --git a/include/qbe.h b/include/qbe.h @@ -28,7 +28,8 @@ extern const struct qbe_type qbe_long, qbe_single, qbe_double, - qbe_void; + qbe_void, + qbe_aggregate; const struct qbe_type *qtype_for_xtype(enum qbe_stype type); @@ -58,6 +59,7 @@ enum qbe_instr { Q_ALLOC4, Q_ALLOC8, Q_AND, + Q_CALL, Q_CAST, Q_CEQD, Q_CEQL, @@ -209,6 +211,8 @@ const char *pushl(struct qbe_func *func, uint64_t *id, const char *fmt); void pushc(struct qbe_func *func, const char *fmt, ...); void push(struct qbe_func *func, struct qbe_statement *stmt); +struct qbe_value *qval_dup(const struct qbe_value *val); + void constw(struct qbe_value *val, uint32_t l); void constl(struct qbe_value *val, uint64_t l); void consts(struct qbe_value *val, float l); diff --git a/include/scope.h b/include/scope.h @@ -3,8 +3,14 @@ #include "identifier.h" #include "trace.h" +enum object_type { + O_BIND, + O_DECL, +}; + // XXX: This might be better as a hash map struct scope_object { + enum object_type otype; struct identifier ident; const struct type *type; struct scope_object *next; @@ -28,7 +34,8 @@ void scope_free(struct scope *scope); void scope_free_all(struct scopes *scopes); const struct scope_object *scope_insert(struct scope *scope, - const struct identifier *ident, const struct type *type); + enum object_type otype, const struct identifier *ident, + const struct type *type); const struct scope_object *scope_lookup(struct scope *scope, const struct identifier *ident); diff --git a/src/check.c b/src/check.c @@ -39,13 +39,14 @@ check_expr_access(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr) { - trace(TR_CHECK, "access"); + char buf[1024]; + identifier_unparse_static(&aexpr->access.ident, buf, sizeof(buf)); + + trace(TR_CHECK, "access %s", buf); expr->type = EXPR_ACCESS; const struct scope_object *obj = scope_lookup( ctx->scope, &aexpr->access.ident); - char buf[1024]; - identifier_unparse_static(&aexpr->access.ident, buf, sizeof(buf)); expect(obj, "Unknown object %s", buf); expr->result = obj->type; @@ -166,8 +167,8 @@ check_expr_binding(struct context *ctx, initializer->result, abinding->flags); } - const struct scope_object *obj = scope_insert( - ctx->scope, &ident, type); + const struct scope_object *obj = scope_insert(ctx->scope, + O_BIND, &ident, type); binding->object = obj; binding->initializer = initializer; @@ -203,6 +204,7 @@ check_expr_call(struct context *ctx, struct ast_call_argument *aarg = aexpr->call.args; struct type_func_param *param = fntype->func.params; while (param && aarg) { + trenter(TR_CHECK, "arg"); assert(!aarg->variadic); // TODO arg = *next = calloc(1, sizeof(struct call_argument)); arg->value = calloc(1, sizeof(struct expression)); @@ -215,6 +217,7 @@ check_expr_call(struct context *ctx, aarg = aarg->next; param = param->next; next = &arg->next; + trleave(TR_CHECK, NULL); } expect(!aarg, "Too many parameters for function call"); @@ -502,7 +505,7 @@ check_function(struct context *ctx, }; const struct type *type = type_store_lookup_atype( &ctx->store, params->type); - scope_insert(decl->func.scope, &ident, type); + scope_insert(decl->func.scope, O_BIND, &ident, type); params = params->next; } @@ -523,7 +526,7 @@ check_function(struct context *ctx, "%s function cannot be exported", flags); } - scope_insert(ctx->unit, &decl->ident, decl->func.type); + scope_insert(ctx->unit, O_DECL, &decl->ident, decl->func.type); scope_pop(&ctx->scope, TR_CHECK); trleave(TR_CHECK, NULL); return decl; @@ -581,7 +584,7 @@ scan_function(struct context *ctx, const struct ast_function_decl *decl) trleave(TR_SCAN, "func %s", buf); if (!decl->body) { - scope_insert(ctx->unit, &decl->ident, fntype); + scope_insert(ctx->unit, O_DECL, &decl->ident, fntype); } } @@ -650,6 +653,4 @@ check(const struct ast_unit *aunit, struct unit *unit) } assert(unit->declarations); - scope_free_all(subunit_scopes); - scope_free(ctx.unit); } diff --git a/src/emit.c b/src/emit.c @@ -18,7 +18,8 @@ emit_qtype(const struct qbe_type *type, FILE *out) case Q__VOID: break; // no-op case Q__AGGREGATE: - assert(0); // TODO + fprintf(out, "l"); // XXX: ARCH + break; } } @@ -54,7 +55,8 @@ emit_value(struct qbe_value *val, FILE *out) emit_const(val, out); break; case QV_GLOBAL: - assert(0); // TODO + fprintf(out, "$%s", val->name); + break; case QV_LABEL: fprintf(out, "@%s", val->name); break; @@ -65,6 +67,30 @@ emit_value(struct qbe_value *val, FILE *out) } static void +emit_call(struct qbe_statement *stmt, FILE *out) +{ + fprintf(out, "%s ", qbe_instr[stmt->instr]); + + struct qbe_arguments *arg = stmt->args; + assert(arg); + emit_value(&arg->value, out); + fprintf(out, "("); + arg = arg->next; + + bool comma = false; + while (arg) { + fprintf(out, "%s", comma ? ", " : ""); + emit_qtype(arg->value.type, out); + fprintf(out, " "); + emit_value(&arg->value, out); + arg = arg->next; + comma = true; + } + + fprintf(out, ")\n"); +} + +static void emit_stmt(struct qbe_statement *stmt, FILE *out) { switch (stmt->type) { @@ -76,7 +102,6 @@ emit_stmt(struct qbe_statement *stmt, FILE *out) if (stmt->out != NULL) { emit_value(stmt->out, out); fprintf(out, " ="); - assert(stmt->out->type->stype != Q__AGGREGATE); // TODO if (stmt->out->indirect) { emit_qtype(&qbe_long, out); // XXX: ARCH } else { @@ -84,6 +109,10 @@ emit_stmt(struct qbe_statement *stmt, FILE *out) } fprintf(out, " "); } + if (stmt->instr == Q_CALL) { + emit_call(stmt, out); + break; + } fprintf(out, "%s%s", qbe_instr[stmt->instr], stmt->args ? " " : ""); struct qbe_arguments *arg = stmt->args; diff --git a/src/gen.c b/src/gen.c @@ -93,9 +93,18 @@ qval_for_object(struct gen_context *ctx, struct qbe_value *val, const struct scope_object *obj) { - const struct gen_binding *binding = binding_lookup(ctx, obj); - val->kind = QV_TEMPORARY; // XXX: Is this always the case? - val->indirect = true; // XXX: Is this always the case? + const struct gen_binding *binding = NULL; + switch (obj->otype) { + case O_BIND: + binding = binding_lookup(ctx, obj); + val->kind = QV_TEMPORARY; + val->indirect = true; + break; + case O_DECL: + val->kind = QV_GLOBAL; + val->indirect = false; + break; + } val->type = qtype_for_type(ctx, obj->type, false); val->name = binding ? strdup(binding->name) : ident_to_sym(&obj->ident); } @@ -133,7 +142,6 @@ gen_store(struct gen_context *ctx, const struct qbe_type *qtype = src->type; assert(qtype->stype != Q__VOID); // Invariant - assert(qtype->stype != Q__AGGREGATE); // TODO if (dest->indirect) { pushi(ctx->current, NULL, store_for_type(qtype->stype), src, dest, NULL); @@ -152,9 +160,13 @@ gen_load(struct gen_context *ctx, { const struct qbe_type *qtype = dest->type; assert(qtype->stype != Q__VOID); // Invariant - assert(qtype->stype != Q__AGGREGATE); // TODO - pushi(ctx->current, dest, - load_for_type(qtype->stype, is_signed), src, NULL); + + if (src->indirect) { + pushi(ctx->current, dest, + load_for_type(qtype->stype, is_signed), src, NULL); + } else { + pushi(ctx->current, dest, Q_COPY, src, NULL); + } } // Same as gen_load but dest is initialized to a new temporary @@ -182,12 +194,21 @@ gen_access(struct gen_context *ctx, assert(expr->access.type == ACCESS_IDENTIFIER); // TODO const struct scope_object *obj = expr->access.object; - struct qbe_value src; + struct qbe_value src = {0}; qval_for_object(ctx, &src, obj); - struct qbe_value temp; - gen_loadtemp(ctx, &temp, &src, src.type, type_is_signed(obj->type)); - gen_store(ctx, out, &temp); + struct qbe_value temp = {0}; + switch (obj->otype) { + case O_BIND: + gen_loadtemp(ctx, &temp, &src, src.type, type_is_signed(obj->type)); + gen_store(ctx, out, &temp); + break; + case O_DECL: + // Skip the extra load + gen_store(ctx, out, &src); + break; + } + } static void @@ -266,6 +287,38 @@ gen_binarithm(struct gen_context *ctx, } static void +gen_call(struct gen_context *ctx, + const struct expression *expr, + const struct qbe_value *out) +{ + struct qbe_statement call = { + .type = Q_INSTR, + .instr = Q_CALL, + .out = out ? qval_dup(out) : NULL, + }; + + struct qbe_arguments *arg, **next = &call.args; + struct call_argument *carg = expr->call.args; + arg = *next = calloc(1, sizeof(struct qbe_arguments)); + gen_temp(ctx, &arg->value, &qbe_long, "func.%d"); + gen_expression(ctx, expr->call.lvalue, &arg->value); + next = &arg->next; + + while (carg) { + assert(!carg->variadic); // TODO + arg = *next = calloc(1, sizeof(struct qbe_arguments)); + gen_temp(ctx, &arg->value, + qtype_for_type(ctx, carg->value->result, false), + "arg.%d"); + gen_expression(ctx, carg->value, &arg->value); + carg = carg->next; + next = &arg->next; + } + + push(ctx->current, &call); +} + +static void gen_constant(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) @@ -429,7 +482,10 @@ gen_expression(struct gen_context *ctx, gen_binding(ctx, expr, out); break; case EXPR_BREAK: + assert(0); // TODO case EXPR_CALL: + gen_call(ctx, expr, out); + break; case EXPR_CAST: assert(0); // TODO case EXPR_CONSTANT: diff --git a/src/parse.c b/src/parse.c @@ -539,6 +539,7 @@ parse_call_expression(struct parser *par, struct ast_expression *lvalue) struct ast_call_argument *arg, **next = &expr->call.args; while (lex(par->lex, &tok) != T_RPAREN) { unlex(par->lex, &tok); + trenter(TR_PARSE, "arg"); arg = *next = calloc(1, sizeof(struct ast_call_argument)); arg->value = parse_complex_expression(par); @@ -560,6 +561,7 @@ parse_call_expression(struct parser *par, struct ast_expression *lvalue) } next = &arg->next; + trleave(TR_PARSE, NULL); } trleave(TR_PARSE, NULL); diff --git a/src/qbe.c b/src/qbe.c @@ -27,6 +27,11 @@ qbe_double = { }, qbe_void = { .stype = Q__VOID, +}, +// Used for some types which are unrepresentible in the qbe type system, but +// still representable as values (e.g. functions) +qbe_aggregate = { + .stype = Q__AGGREGATE, }; const struct qbe_type * @@ -48,7 +53,7 @@ qtype_for_xtype(enum qbe_stype type) case Q__VOID: return &qbe_void; case Q__AGGREGATE: - assert(0); // Invariant + return &qbe_aggregate; } assert(0); // Unreachable } @@ -59,6 +64,7 @@ const char *qbe_instr[Q_LAST_INSTR] = { [Q_ALLOC4] = "alloc4", [Q_ALLOC8] = "alloc8", [Q_AND] = "and", + [Q_CALL] = "call", [Q_CAST] = "cast", [Q_CEQD] = "ceqd", [Q_CEQL] = "ceql", @@ -147,7 +153,7 @@ qbe_append_def(struct qbe_program *prog, struct qbe_def *def) prog->defs = def; } -static struct qbe_value * +struct qbe_value * qval_dup(const struct qbe_value *val) { struct qbe_value *new = calloc(1, sizeof(struct qbe_value)); diff --git a/src/qtype.c b/src/qtype.c @@ -127,7 +127,7 @@ qtype_for_type(struct gen_context *ctx, const struct type *type, bool extended) case TYPE_STORAGE_UNION: assert(0); // TODO case TYPE_STORAGE_FUNCTION: - assert(0); // Invariant + return qtype_for_xtype(Q__AGGREGATE); } assert(0); // Unreachable } diff --git a/src/scope.c b/src/scope.c @@ -61,11 +61,13 @@ scope_free_all(struct scopes *scopes) const struct scope_object * scope_insert(struct scope *scope, + enum object_type otype, const struct identifier *ident, const struct type *type) { struct scope_object *o = calloc(1, sizeof(struct scope_object)); identifier_dup(&o->ident, ident); + o->otype = otype; o->type = type; *scope->next = o; scope->next = &o->next;