commit aca00ad93ecd947d601d4d08c320235ed288147b
parent 6961bfd6431a3a8c1b776d998c8fdd35094c6112
Author: Drew DeVault <sir@cmpwn.com>
Date: Thu, 24 Dec 2020 15:30:37 -0500
gen: implement call expressions
Diffstat:
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;