commit 6bef0ed6a0129220eed26f76647cc22fcd36a4b9
parent 7ccb4db529b38f76b2d68403c0636b99a8c5108d
Author: Drew DeVault <sir@cmpwn.com>
Date: Thu, 1 Jul 2021 09:17:15 -0400
Raze gen vN
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
M | include/gen.h | | | 51 | --------------------------------------------------- |
M | rt/configure | | | 4 | +++- |
M | src/gen.c | | | 3380 | +------------------------------------------------------------------------------ |
M | src/qinstr.c | | | 185 | +------------------------------------------------------------------------------ |
M | src/qtype.c | | | 385 | +------------------------------------------------------------------------------ |
5 files changed, 9 insertions(+), 3996 deletions(-)
diff --git a/include/gen.h b/include/gen.h
@@ -1,9 +1,5 @@
#ifndef HAREC_GEN_H
#define HAREC_GEN_H
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdint.h>
-#include "expr.h"
#include "identifier.h"
#include "qbe.h"
@@ -18,57 +14,10 @@ struct unit;
void gen(const struct unit *unit, struct qbe_program *out);
-struct gen_binding {
- const struct scope_object *object;
- char *name;
- struct gen_binding *next;
-};
-
-enum scope_class {
- SCOPE_LOOP, // used with continue, break
- SCOPE_FUNC, // used with return
- SCOPE_OTHER, // expression lists, etc
-};
-
-struct gen_deferred {
- const struct expression *expr;
- struct gen_deferred *next;
-};
-
-struct gen_scope_context {
- const char *label;
- enum scope_class class;
- struct qbe_value *after;
- struct qbe_value *end;
- struct gen_deferred *defers;
- struct gen_scope_context *parent;
-};
-
struct gen_context {
struct qbe_program *out;
struct identifier *ns;
- struct qbe_func *current;
- const struct qbe_value *end_label;
- const struct qbe_value *return_value;
- struct gen_binding *bindings;
- struct gen_scope_context *scope;
uint64_t id;
};
-struct type;
-
-// qtype.c
-enum qbe_stype qstype_for_type(const struct type *type);
-enum qbe_stype qxtype_for_type(const struct type *type);
-const struct qbe_type *qtype_for_type(struct gen_context *ctx,
- const struct type *type, bool extended);
-bool type_is_aggregate(const struct type *type);
-
-// qinstr.c
-enum qbe_instr alloc_for_align(size_t align);
-enum qbe_instr store_for_type(enum qbe_stype stype);
-enum qbe_instr load_for_type(enum qbe_stype stype, bool is_signed);
-enum qbe_instr binarithm_for_op(enum binarithm_operator op,
- const struct qbe_type *type, bool is_signed);
-
#endif
diff --git a/rt/configure b/rt/configure
@@ -1,5 +1,7 @@
#!/bin/sh
-all="$all rt"
+# Temporarily disabled
+#all="$all rt"
+all="$all"
rt() {
arch=$(uname -m)
diff --git a/src/gen.c b/src/gen.c
@@ -1,3398 +1,26 @@
#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include "check.h"
#include "expr.h"
#include "gen.h"
-#include "identifier.h"
-#include "qbe.h"
-#include "scope.h"
-#include "typedef.h"
-#include "types.h"
-#include "util.h"
-
-static void gen_expression(struct gen_context *ctx,
- const struct expression *expr, const struct qbe_value *out);
-
-static struct gen_scope_context *
-push_scope(struct gen_context *ctx,
- enum scope_class class,
- struct qbe_value *end)
-{
- struct gen_scope_context *scope =
- xcalloc(1, sizeof(struct gen_scope_context));
- scope->class = class;
- scope->end = end;
- scope->parent = ctx->scope;
- ctx->scope = scope;
- return scope;
-}
-
-static void
-pop_scope(struct gen_context *ctx)
-{
- struct gen_scope_context *scope = ctx->scope;
- assert(scope);
- struct gen_deferred *d = scope->defers;
- while (d) {
- struct gen_deferred *n = d->next;
- free(d);
- d = n;
- }
- ctx->scope = ctx->scope->parent;
- free(scope);
-}
-
-static void
-gen_defers(struct gen_context *ctx, struct gen_scope_context *scope)
-{
- struct gen_deferred *d = scope->defers;
- while (d) {
- gen_expression(ctx, d->expr, NULL);
- d = d->next;
- }
-}
-
-static char *
-gen_name(struct gen_context *ctx, const char *fmt)
-{
- int n = snprintf(NULL, 0, fmt, ctx->id);
- char *str = xcalloc(1, n + 1);
- snprintf(str, n + 1, fmt, ctx->id);
- ++ctx->id;
- return str;
-}
-
-static void
-gen_temp(struct gen_context *ctx, struct qbe_value *val,
- const struct qbe_type *type, const char *fmt)
-{
- val->kind = QV_TEMPORARY;
- val->type = type;
- val->name = gen_name(ctx, fmt);
- val->indirect = false;
-}
-
-static char *
-gen_typename(const struct type *type)
-{
- size_t sz = 0;
- char *ptr = NULL;
- FILE *f = open_memstream(&ptr, &sz);
- emit_type(type, f);
- fclose(f);
- return ptr;
-}
-
-static void
-alloc_temp(struct gen_context *ctx, struct qbe_value *val,
- const struct type *type, const char *fmt)
-{
- struct qbe_value size = {0};
- gen_temp(ctx, val, qtype_for_type(ctx, type, true), fmt);
- val->indirect = true;
- constl(&size, type->size);
- const struct qbe_type *qtype = val->type;
- val->type = &qbe_long;
- pushprei(ctx->current, val, alloc_for_align(type->align), &size, NULL);
- val->type = qtype;
-}
-
-static void qval_for_object(struct gen_context *ctx,
- struct qbe_value *val, const struct scope_object *obj);
-
-static struct gen_binding *
-binding_alloc(struct gen_context *ctx, const struct scope_object *obj,
- struct qbe_value *val, const char *fmt)
-{
- struct gen_binding *binding = xcalloc(1, sizeof(struct gen_binding));
- alloc_temp(ctx, val, obj->type, fmt);
- if (type_is_aggregate(obj->type)) {
- val->indirect = false;
- }
- binding->name = strdup(val->name);
- binding->object = obj;
- binding->next = ctx->bindings;
- ctx->bindings = binding;
- pushc(ctx->current, "alloc binding: %s -> %%%s, =%c (%s), indirect: %d",
- obj->ident.name, binding->name,
- (char)val->type->stype, val->type->name,
- val->indirect);
- return binding;
-}
-
-static const struct gen_binding *
-binding_lookup(const struct gen_context *ctx, const struct scope_object *obj)
-{
- struct gen_binding *binding = ctx->bindings;
- while (binding) {
- if (binding->object == obj) {
- return binding;
- }
- binding = binding->next;
- }
- return NULL;
-}
-
-static void
-qval_for_object(struct gen_context *ctx,
- struct qbe_value *val,
- const struct scope_object *obj)
-{
- const struct gen_binding *binding = NULL;
- switch (obj->otype) {
- case O_BIND:
- binding = binding_lookup(ctx, obj);
- val->kind = QV_TEMPORARY;
- val->indirect = true;
- val->name = strdup(binding->name);
- break;
- case O_DECL:
- val->kind = QV_GLOBAL;
- val->indirect = true;
- val->name = ident_to_sym(&obj->ident);
- break;
- case O_CONST:
- case O_TYPE:
- assert(0); // Invariant (lowered in check)
- }
-
- val->type = qtype_for_type(ctx, obj->type, true);
- if (type_is_aggregate(obj->type)) {
- val->indirect = false;
- }
-}
-
-static void
-qval_deref(struct qbe_value *val)
-{
- val->indirect = val->type->stype != Q__AGGREGATE;
-}
-
-static void
-qval_address(struct qbe_value *val)
-{
- val->indirect = val->type->stype == Q__AGGREGATE;
-}
-
-static void
-gen_copy(struct gen_context *ctx,
- const struct qbe_value *dest,
- const struct qbe_value *src)
-{
- if (!dest) {
- return;
- }
- assert(!dest->indirect && !src->indirect);
- pushc(ctx->current, "begin gen_copy for type %s (is_union? %d)",
- dest->type->name, dest->type->is_union);
-
- struct qbe_value destp = {0}, srcp = {0};
- gen_temp(ctx, &destp, &qbe_long, "dest.%d");
- gen_temp(ctx, &srcp, &qbe_long, "src.%d");
- pushi(ctx->current, &destp, Q_COPY, dest, NULL);
- pushi(ctx->current, &srcp, Q_COPY, src, NULL);
-
- if (dest->type->size > 128) {
- struct qbe_value rtfunc = {0}, size = {0};
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.memcpy");
- rtfunc.type = &qbe_long;
- constl(&size, dest->type->size);
- pushi(ctx->current, NULL, Q_CALL, &rtfunc,
- &destp, &srcp, &size, NULL);
- } else {
- enum qbe_instr load, store;
- struct qbe_value temp = {0}, align = {0};
- assert(dest->type->align
- && (dest->type->align & (dest->type->align - 1)) == 0);
- switch (dest->type->align) {
- case 1:
- load = Q_LOADUB;
- store = Q_STOREB;
- break;
- case 2:
- load = Q_LOADUH;
- store = Q_STOREH;
- break;
- case 4:
- load = Q_LOADUW;
- store = Q_STOREW; break;
- default:
- assert(dest->type->align == 8);
- load = Q_LOADL;
- store = Q_STOREL;
- break;
- }
- gen_temp(ctx, &temp, &qbe_long, "temp.%d");
- constl(&align, dest->type->align);
- for (size_t offset = 0; offset < dest->type->size;
- offset += dest->type->align) {
- pushi(ctx->current, &temp, load, &srcp, NULL);
- pushi(ctx->current, NULL, store, &temp, &destp, NULL);
- pushi(ctx->current, &srcp, Q_ADD, &srcp, &align, NULL);
- pushi(ctx->current, &destp, Q_ADD, &destp, &align, NULL);
- }
- }
-
- pushc(ctx->current, "end gen_copy for type %s", dest->type->name);
-}
-
-static void
-gen_store(struct gen_context *ctx,
- const struct qbe_value *dest,
- const struct qbe_value *src)
-{
- if (!dest) {
- // no-op
- return;
- }
-
- assert(src->type->stype != Q__VOID
- && dest->type->stype != Q__VOID); // Invariant
-
- // TODO: Revisit me (again)
- if (src->type->stype == Q__AGGREGATE) {
- if (src->indirect && dest->indirect) {
- pushi(ctx->current, NULL, Q_STOREL, src, dest, NULL); // XXX: ARCH
- } else if (!dest->indirect && dest->type->stype == Q__AGGREGATE) {
- gen_copy(ctx, dest, src);
- } else {
- pushi(ctx->current, dest, Q_COPY, src, NULL);
- }
- return;
- }
-
- assert(!src->indirect);
- if (dest->indirect) {
- pushi(ctx->current, NULL,
- store_for_type(dest->type->stype),
- src, dest, NULL);
- } else {
- pushi(ctx->current, dest, Q_COPY, src, NULL);
- }
-}
-
-static void
-gen_load(struct gen_context *ctx,
- const struct qbe_value *dest,
- const struct qbe_value *src,
- bool is_signed)
-{
- assert(src->type->stype != Q__VOID
- && dest->type->stype != Q__VOID); // Invariant
-
- if (src->type->stype == Q__AGGREGATE) {
- assert(!src->indirect);
- if (dest->type->stype == Q__AGGREGATE) {
- gen_copy(ctx, dest, src);
- } else {
- assert(dest->type == &qbe_long);
- pushi(ctx->current, dest, Q_COPY, src, NULL);
- }
- return;
- }
-
- assert(!dest->indirect);
- if (src->indirect) {
- pushi(ctx->current, dest,
- load_for_type(src->type->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
-static void
-gen_loadtemp(struct gen_context *ctx,
- struct qbe_value *dest, const struct qbe_value *src,
- const struct qbe_type *type, bool is_signed)
-{
- gen_temp(ctx, dest, type, "load.%d");
- gen_load(ctx, dest, src, is_signed);
-}
-
-static void
-gen_fixed_abort(struct gen_context *ctx, struct location loc,
- enum fixed_aborts reason)
-{
- int n = snprintf(NULL, 0, "%s:%d:%d", loc.path, loc.lineno, loc.colno);
- char *s = xcalloc(1, n + 1);
- snprintf(s, n, "%s:%d:%d", loc.path, loc.lineno, loc.colno);
-
- struct qbe_value location = {0};
- struct expression eloc = {0};
- eloc.type = EXPR_CONSTANT;
- eloc.result = &builtin_type_const_str;
- eloc.constant.string.value = s;
- eloc.constant.string.len = n - 1;
- alloc_temp(ctx, &location, &builtin_type_const_str, "str.%d");
- qval_deref(&location);
- gen_expression(ctx, &eloc, &location);
-
- struct qbe_value rtabort = {0}, tmp = {0};
- rtabort.kind = QV_GLOBAL;
- rtabort.name = strdup("rt.abort_fixed");
- rtabort.type = &qbe_long;
- constl(&tmp, reason);
- pushi(ctx->current, NULL, Q_CALL, &rtabort, &location, &tmp, NULL);
-}
-
-static void
-address_ident(struct gen_context *ctx,
- const struct expression *expr,
- struct qbe_value *out)
-{
- const struct scope_object *obj = expr->access.object;
- qval_for_object(ctx, out, obj);
-}
-
-static void
-address_index(struct gen_context *ctx,
- const struct expression *expr,
- struct qbe_value *out)
-{
- gen_temp(ctx, out, &qbe_long, "object.%d"); // XXX: ARCH
- gen_expression(ctx, expr->access.array, out);
-
- const struct type *atype = type_dealias(expr->access.array->result);
- if (atype->storage == STORAGE_POINTER) {
- // We get one dereference for free for aggregate types
- atype = type_dealias(atype->pointer.referent);
- }
- while (atype->storage == STORAGE_POINTER) {
- pushi(ctx->current, out, Q_LOADL, out, NULL);
- atype = type_dealias(atype->pointer.referent);
- }
-
- struct qbe_value index = {0};
- gen_temp(ctx, &index, &qbe_long, "index.%d");
- gen_expression(ctx, expr->access.index, &index);
-
- // XXX: ARCH
- struct qbe_value length = {0};
- if (atype->storage == STORAGE_ARRAY) {
- constl(&length, atype->array.length);
- } else if (atype->storage == STORAGE_SLICE) {
- struct qbe_value ptr = {0};
- gen_temp(ctx, &ptr, &qbe_long, "ptr.%d");
- constl(&length, 8);
- pushi(ctx->current, &ptr, Q_ADD, out, &length, NULL);
- gen_temp(ctx, &length, &qbe_long, "length.%d");
- pushi(ctx->current, &length, Q_LOADL, &ptr, NULL);
- }
-
- if (atype->storage != STORAGE_ARRAY
- || atype->array.length != SIZE_UNDEFINED) {
- struct qbe_statement validl = {0}, invalidl = {0};
- struct qbe_value bvalid = {0}, binvalid = {0};
- bvalid.kind = QV_LABEL;
- bvalid.name = strdup(genl(&validl, &ctx->id, "bounds.valid.%d"));
- binvalid.kind = QV_LABEL;
- binvalid.name = strdup(genl(&invalidl, &ctx->id, "bounds.invalid.%d"));
-
- struct qbe_value valid = {0};
- gen_temp(ctx, &valid, &qbe_word, "valid.%d");
- pushi(ctx->current, &valid, Q_CULTL, &index, &length, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &valid, &bvalid, &binvalid, NULL);
- push(&ctx->current->body, &invalidl);
-
- gen_fixed_abort(ctx, expr->loc, ABORT_OOB);
-
- push(&ctx->current->body, &validl);
- }
-
- if (atype->storage == STORAGE_SLICE) {
- pushi(ctx->current, out, Q_LOADL, out, NULL);
- }
-
- struct qbe_value temp = {0};
- constl(&temp, atype->array.members->size);
- pushi(ctx->current, &index, Q_MUL, &index, &temp, NULL);
- pushi(ctx->current, out, Q_ADD, out, &index, NULL);
- out->type = qtype_for_type(ctx, atype->array.members, true);
- if (!type_is_aggregate(atype->array.members)) {
- qval_deref(out);
- }
-}
-
-static void
-address_field(struct gen_context *ctx,
- const struct expression *expr,
- struct qbe_value *out)
-{
- gen_temp(ctx, out, &qbe_long, "object.%d"); // XXX: ARCH
- gen_expression(ctx, expr->access._struct, out);
-
- const struct type *stype = type_dealias(expr->access._struct->result);
- if (stype->storage == STORAGE_POINTER) {
- // We get one dereference for free for aggregate types
- stype = type_dealias(stype->pointer.referent);
- }
- while (stype->storage == STORAGE_POINTER) {
- pushi(ctx->current, out, Q_LOADL, out, NULL);
- stype = type_dealias(stype->pointer.referent);
- }
-
- const struct struct_field *field = expr->access.field;
-
- struct qbe_value offset = {0};
- constl(&offset, field->offset);
- pushi(ctx->current, out, Q_ADD, out, &offset, NULL);
- out->type = qtype_for_type(ctx, field->type, true);
- if (!type_is_aggregate(field->type)) {
- qval_deref(out);
- }
-}
-
-static void
-address_value(struct gen_context *ctx,
- const struct expression *expr,
- struct qbe_value *out)
-{
- gen_temp(ctx, out, &qbe_long, "object.%d"); // XXX: ARCH
- gen_expression(ctx, expr->access.tuple, out);
-
- const struct type *ttype = type_dealias(expr->access.tuple->result);
- if (ttype->storage == STORAGE_POINTER) {
- // We get one dereference for free for aggregate types
- ttype = type_dealias(ttype->pointer.referent);
- }
- while (ttype->storage == STORAGE_POINTER) {
- pushi(ctx->current, out, Q_LOADL, out, NULL);
- ttype = type_dealias(ttype->pointer.referent);
- }
-
- const struct type_tuple *value = expr->access.tvalue;
-
- struct qbe_value offset = {0};
- constl(&offset, value->offset);
- pushi(ctx->current, out, Q_ADD, out, &offset, NULL);
- out->type = qtype_for_type(ctx, value->type, false);
- if (!type_is_aggregate(value->type)) {
- qval_deref(out);
- }
-}
-
-static void
-address_object(struct gen_context *ctx,
- const struct expression *expr,
- struct qbe_value *out)
-{
- assert(expr->type == EXPR_ACCESS);
- switch (expr->access.type) {
- case ACCESS_IDENTIFIER:
- address_ident(ctx, expr, out);
- break;
- case ACCESS_INDEX:
- address_index(ctx, expr, out);
- break;
- case ACCESS_FIELD:
- address_field(ctx, expr, out);
- break;
- case ACCESS_TUPLE:
- address_value(ctx, expr, out);
- break;
- }
-}
-
-static void
-gen_expr_access(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- if (out == NULL) {
- pushc(ctx->current, "useless access expression discarded");
- return;
- }
-
- struct qbe_value src = {0}, temp = {0};
- address_object(ctx, expr, &src);
- if (src.indirect) {
- gen_loadtemp(ctx, &temp, &src,
- qtype_for_type(ctx, expr->result, true),
- type_is_signed(expr->result));
- gen_store(ctx, out, &temp);
- } else {
- gen_store(ctx, out, &src);
- }
-}
-
-static void
-gen_slice_alloc(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value len = {0}, cap = {0}, size = {0}, temp = {0};
- gen_temp(ctx, &len,
- qtype_for_type(ctx, &builtin_type_size, true),
- "slice.len.%d");
- gen_temp(ctx, &cap,
- qtype_for_type(ctx, &builtin_type_size, true),
- "slice.cap.%d");
- gen_temp(ctx, &size,
- qtype_for_type(ctx, &builtin_type_size, true),
- "slice.size.%d");
-
- const struct expression *initializer = expr->alloc.expr;
- const struct type *itype = type_dealias(initializer->result);
- if (itype->storage == STORAGE_ARRAY) {
- assert(itype->array.length != SIZE_UNDEFINED);
- constl(&len, itype->array.length);
- } else {
- assert(0); // TODO: Initialize one slice from another
- }
-
- if (expr->alloc.cap != NULL) {
- gen_expression(ctx, expr->alloc.cap, &cap);
- } else {
- cap = len;
- }
- constl(&temp, type_dealias(expr->result)->array.members->size);
- pushi(ctx->current, &size, Q_MUL, &cap, &temp, NULL);
-
- struct qbe_value ret = {0};
- gen_temp(ctx, &ret, &qbe_long, "alloc.ret.%d");
- struct qbe_value rtalloc = {0};
- rtalloc.kind = QV_GLOBAL;
- rtalloc.name = strdup("rt.malloc");
- rtalloc.type = &qbe_long;
- pushi(ctx->current, &ret, Q_CALL, &rtalloc, &size, NULL);
-
- // TODO: Generate abort if it failed
-
- assert(!out->indirect); // TODO?
- struct qbe_value ptr = {0};
- gen_temp(ctx, &ptr, &qbe_long, "alloc.ptr.%d");
- constl(&temp, 8); // XXX: ARCH
- pushi(ctx->current, &ptr, Q_COPY, out, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &ret, &ptr, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &len, &ptr, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &cap, &ptr, NULL);
-
- if (itype->storage == STORAGE_ARRAY) {
- ret.type = qtype_for_type(ctx, initializer->result, true);
- ret.indirect = false;
- gen_expression(ctx, initializer, &ret);
- } else {
- // TODO: I think this will have to be a separate branch once
- // we've implemented it
- }
-}
-
-static void
-gen_expr_alloc(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(expr->type == EXPR_ALLOC);
- if (type_dealias(expr->result)->storage == STORAGE_SLICE) {
- gen_slice_alloc(ctx, expr, out);
- return;
- }
-
- // TODO: Explicit capacity
- assert(expr->alloc.cap == NULL);
- struct qbe_value ret = {0}, size = {0};
- gen_temp(ctx, &size,
- qtype_for_type(ctx, &builtin_type_size, true),
- "alloc.size.%d");
- constl(&size, type_dealias(expr->result)->pointer.referent->size);
- // XXX: ARCH
- gen_temp(ctx, &ret, &qbe_long, "alloc.ret.%d");
- struct qbe_value rtalloc = {0};
- rtalloc.kind = QV_GLOBAL;
- rtalloc.name = strdup("rt.malloc");
- rtalloc.type = &qbe_long;
- pushi(ctx->current, &ret, Q_CALL, &rtalloc, &size, NULL);
-
- struct qbe_value load = {0};
- gen_temp(ctx, &load, &qbe_long, "alloc.storage.%d");
- pushi(ctx->current, &load, Q_COPY, &ret, NULL);
-
- struct qbe_statement nulll = {0}, validl = {0}, endl = {0};
- struct qbe_value bnull = {0}, bvalid = {0}, bend = {0};
- bvalid.kind = QV_LABEL;
- bvalid.name = strdup(genl(&validl, &ctx->id, "alloc.valid.%d"));
- bnull.kind = QV_LABEL;
- bnull.name = strdup(genl(&nulll, &ctx->id, "alloc.null.%d"));
- bend.kind = QV_LABEL;
- bend.name = strdup(genl(&endl, &ctx->id, "alloc.end.%d"));
- // XXX: null might not be 0
- pushi(ctx->current, NULL, Q_JNZ, &load, &bvalid, &bnull, NULL);
- push(&ctx->current->body, &nulll);
-
- if (type_dealias(expr->result)->pointer.flags & PTR_NULLABLE) {
- pushi(ctx->current, NULL, Q_JMP, &bend, NULL);
- } else {
- gen_fixed_abort(ctx, expr->loc, ABORT_ALLOC_FAILURE);
- }
- push(&ctx->current->body, &validl);
- if (!type_is_aggregate(type_dealias(expr->result)->pointer.referent)) {
- qval_deref(&load);
- }
- load.type = qtype_for_type(ctx,
- type_dealias(expr->result)->pointer.referent,
- false);
- gen_expression(ctx, expr->alloc.expr, &load);
- gen_store(ctx, out, &ret);
- push(&ctx->current->body, &endl);
-}
-
-static void
-gen_expr_append(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(expr->type == EXPR_APPEND);
- struct qbe_value variadic = {0}, vptr = {0}, vlen = {0}, temp = {0};
- if (expr->append.variadic) {
- struct qbe_value vlenptr = {0};
- alloc_temp(ctx, &variadic, expr->append.variadic->result,
- "append.variadic.%d");
- gen_expression(ctx, expr->append.variadic, &variadic);
- qval_deref(&variadic);
- gen_loadtemp(ctx, &vptr, &variadic, &qbe_long, false);
- qval_deref(&vptr);
- gen_temp(ctx, &vlenptr, &qbe_long, "append.vlenptr.%d");
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &vlenptr, Q_ADD, &vptr, &temp, NULL);
- qval_deref(&vlenptr);
- gen_loadtemp(ctx, &vlen, &vlenptr, &qbe_long, false);
- }
-
- struct qbe_value val = {0};
- gen_temp(ctx, &val, &qbe_long, "append.val.%d");
- if (expr->append.expr->type == EXPR_ACCESS) {
- struct qbe_value tmp = {0};
- address_object(ctx, expr->append.expr, &tmp);
- qval_address(&tmp);
- gen_store(ctx, &val, &tmp);
- } else {
- assert(expr->append.expr->type == EXPR_UNARITHM);
- assert(expr->append.expr->unarithm.op == UN_DEREF);
- gen_expression(ctx, expr->append.expr->unarithm.operand, &val);
- }
-
- struct qbe_value len = {0}, newlen = {0}, lenptr = {0};
- gen_temp(ctx, &lenptr, &qbe_long, "append.lenptr.%d");
- gen_temp(ctx, &newlen, &qbe_long, "append.newlen.%d");
- gen_loadtemp(ctx, &lenptr, &val, &qbe_long, false);
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &lenptr, Q_ADD, &lenptr, &temp, NULL);
- qval_deref(&lenptr);
- gen_loadtemp(ctx, &len, &lenptr, &qbe_long, false);
- size_t args = 0;
- for (struct append_values *value = expr->append.values; value;
- value = value->next) {
- args++;
- }
- constl(&temp, args);
- pushi(ctx->current, &newlen, Q_ADD, &len, &temp, NULL);
- if (expr->append.variadic) {
- pushi(ctx->current, &newlen, Q_ADD, &newlen, &vlen, NULL);
- }
- gen_store(ctx, &lenptr, &newlen);
-
- const struct type *mtype =
- type_dealias(expr->append.expr->result)->array.members;
-
- struct qbe_value membsz = {0};
- constl(&membsz, mtype->size);
- if (!expr->append.is_static) {
- struct qbe_value rtfunc = {0};
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.ensure");
- rtfunc.type = &qbe_long;
- pushi(ctx->current, NULL, Q_CALL, &rtfunc, &val, &membsz, NULL);
- } else {
- struct qbe_value capptr = {0}, cap = {0};
- gen_temp(ctx, &capptr, &qbe_long, "append.capptr.%d");
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &capptr, Q_ADD, &lenptr, &temp, NULL);
- qval_deref(&capptr);
- gen_loadtemp(ctx, &cap, &capptr, &qbe_long, false);
-
- struct qbe_statement validl = {0}, invalidl = {0};
- struct qbe_value bvalid = {0}, binvalid = {0};
- bvalid.kind = QV_LABEL;
- bvalid.name = strdup(genl(&validl, &ctx->id, "bounds.valid.%d"));
- binvalid.kind = QV_LABEL;
- binvalid.name = strdup(genl(&invalidl, &ctx->id, "bounds.invalid.%d"));
-
- struct qbe_value valid = {0};
- gen_temp(ctx, &valid, &qbe_word, "valid.%d");
- pushi(ctx->current, &valid, Q_CULEL, &newlen, &cap, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &valid, &bvalid, &binvalid, NULL);
- push(&ctx->current->body, &invalidl);
-
- gen_fixed_abort(ctx, expr->loc, ABORT_STATIC_EXCEEDED);
-
- push(&ctx->current->body, &validl);
- }
-
- struct qbe_value ptr = {0};
- const struct qbe_type *type = qtype_for_type(ctx, mtype, true);
- qval_deref(&val);
- gen_loadtemp(ctx, &ptr, &val, &qbe_long, "append.ptr.%d");
- qval_address(&ptr);
- pushi(ctx->current, &len, Q_MUL, &len, &membsz, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &len, NULL);
- for (struct append_values *value = expr->append.values; value;
- value = value->next) {
- struct qbe_value v = {0};
- alloc_temp(ctx, &v, value->expr->result, "append.value.%d");
- qval_deref(&v);
- gen_expression(ctx, value->expr, &v);
- v.indirect = false;
- ptr.type = type;
- gen_copy(ctx, &ptr, &v);
- ptr.type = &qbe_long;
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &membsz, NULL);
- }
- if (expr->append.variadic) {
- struct qbe_value rtmemcpy = {0}, v = {0};
- gen_loadtemp(ctx, &v, &vptr, &qbe_long, false);
- rtmemcpy.kind = QV_GLOBAL;
- rtmemcpy.name = strdup("rt.memcpy");
- rtmemcpy.type = &qbe_long;
- pushi(ctx->current, &vlen, Q_MUL, &vlen, &membsz, NULL);
- pushi(ctx->current, NULL, Q_CALL, &rtmemcpy, &ptr, &v, &vlen, NULL);
- }
-}
-
-static void
-gen_expr_assert(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(expr->assert.message); // Invariant
- if (expr->assert.is_static) {
- return;
- }
-
- struct qbe_statement failedl = {0}, passedl = {0};
- struct qbe_value bfailed = {0}, bpassed = {0};
-
- struct qbe_value msg = {0};
-
- struct qbe_value rtfunc = {0};
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.abort");
- rtfunc.type = &qbe_long;
-
- if (expr->assert.cond) {
- bfailed.kind = QV_LABEL;
- bfailed.name = strdup(genl(&failedl, &ctx->id, "failed.%d"));
- bpassed.kind = QV_LABEL;
- bpassed.name = strdup(genl(&passedl, &ctx->id, "passed.%d"));
-
- struct qbe_value cond = {0};
- gen_temp(ctx, &cond, &qbe_word, "assert.%d");
- gen_expression(ctx, expr->assert.cond, &cond);
-
- alloc_temp(ctx, &msg, &builtin_type_const_str, "msg.%d");
- qval_deref(&msg);
- gen_expression(ctx, expr->assert.message, &msg);
-
- pushi(ctx->current, NULL, Q_JNZ, &cond, &bpassed, &bfailed, NULL);
- push(&ctx->current->body, &failedl);
- } else {
- alloc_temp(ctx, &msg, &builtin_type_const_str, "msg.%d");
- qval_deref(&msg);
- gen_expression(ctx, expr->assert.message, &msg);
- }
-
- pushi(ctx->current, NULL, Q_CALL, &rtfunc, &msg, NULL);
-
- if (expr->assert.cond) {
- push(&ctx->current->body, &passedl);
- }
-}
-
-static void
-gen_expr_assign_slice(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value obj = {0}, val = {0}, temp = {0};
- assert(expr->assign.op == BIN_LEQUAL && !expr->assign.indirect);
- constl(&temp, builtin_type_size.size);
- alloc_temp(ctx, &obj, expr->assign.object->result, "assign.object.%d");
- alloc_temp(ctx, &val,expr->assign.value->result, "assign.value.%d");
- gen_expression(ctx, expr->assign.object, &obj);
- gen_expression(ctx, expr->assign.value, &val);
-
- struct qbe_value ptr = {0}, olen = {0}, vlen = {0};
- gen_temp(ctx, &ptr, &qbe_long, "assign.lenptr.%d");
- gen_temp(ctx, &olen, &qbe_long, "assign.olen.%d");
- gen_temp(ctx, &vlen, &qbe_long, "assign.vlen.%d");
-
- qval_deref(&obj);
- qval_deref(&val);
- pushi(ctx->current, &ptr, Q_COPY, &obj, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
- pushi(ctx->current, &olen, Q_LOADL, &ptr, NULL);
- pushi(ctx->current, &ptr, Q_COPY, &val, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
- pushi(ctx->current, &vlen, Q_LOADL, &ptr, NULL);
-
- struct qbe_statement equall = {0}, diffl = {0};
- struct qbe_value bequal = {0}, bdiff = {0};
- bequal.kind = QV_LABEL;
- bequal.name = strdup(genl(&equall, &ctx->id, "equal.%d"));
- bdiff.kind = QV_LABEL;
- bdiff.name = strdup(genl(&diffl, &ctx->id, "diff.%d"));
- gen_temp(ctx, &temp, &qbe_long, "assign.bounds.%d");
- pushi(ctx->current, &temp, Q_CUGEL, &olen, &vlen, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &temp, &bequal, &bdiff, NULL);
- push(&ctx->current->body, &diffl);
-
- gen_fixed_abort(ctx, expr->loc, ABORT_OOB);
- push(&ctx->current->body, &equall);
-
- struct qbe_value rtmemcpy = {0}, optr = {0}, vptr = {0};
- rtmemcpy.kind = QV_GLOBAL;
- rtmemcpy.name = strdup("rt.memcpy");
- rtmemcpy.type = &qbe_long;
- gen_temp(ctx, &optr, &qbe_long, "assign.optr.%d");
- pushi(ctx->current, &optr, Q_LOADL, &obj, NULL);
- gen_temp(ctx, &vptr, &qbe_long, "assign.vptr.%d");
- pushi(ctx->current, &vptr, Q_LOADL, &val, NULL);
- constl(&temp, expr->assign.object->result->array.members->size);
- pushi(ctx->current, &olen, Q_MUL, &olen, &temp, NULL);
- pushi(ctx->current, NULL, Q_CALL, &rtmemcpy, &optr, &vptr, &olen, NULL);
-}
-
-static void
-gen_expr_assign(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(out == NULL); // Invariant
-
- struct expression *object = expr->assign.object;
- if (object->type == EXPR_SLICE) {
- gen_expr_assign_slice(ctx, expr, out);
- return;
- }
-
- assert(object->type == EXPR_ACCESS || expr->assign.indirect); // Invariant
-
- const struct expression *value = expr->assign.value;
- const struct type *objtype = expr->assign.indirect
- ? type_dealias(object->result)->pointer.referent : object->result;
- const struct qbe_type *vtype =
- qtype_for_type(ctx, value->result, false);
- const struct qbe_type *otype = qtype_for_type(ctx, objtype, false);
-
- struct qbe_value src = {0};
- if (expr->assign.indirect) {
- gen_temp(ctx, &src, &qbe_long, "indirect.%d"); // XXX: ARCH
- gen_expression(ctx, object, &src);
- src.type = otype;
- qval_deref(&src);
- } else {
- const struct expression *object = expr->assign.object;
- address_object(ctx, object, &src);
- src.type = qtype_for_type(ctx, object->result, true);
- }
-
- if (expr->assign.op == BIN_LEQUAL) {
- if (!type_is_aggregate(value->result)) {
- qval_deref(&src);
- }
- gen_expression(ctx, value, &src);
- } else if (expr->assign.op == BIN_LAND || expr->assign.op == BIN_LOR) {
- struct qbe_statement rlabel = {0}, slabel = {0};
- struct qbe_value rbranch = {0}, sbranch = {0}, result = {0};
- rbranch.kind = QV_LABEL;
- rbranch.name = strdup(genl(&rlabel, &ctx->id, "value.%d"));
- sbranch.kind = QV_LABEL;
- sbranch.name = strdup(genl(&slabel, &ctx->id, "short_circuit.%d"));
-
- struct qbe_value load;
- gen_loadtemp(ctx, &load, &src, otype, type_is_signed(objtype));
- if (expr->binarithm.op == BIN_LAND) {
- pushi(ctx->current, NULL, Q_JNZ, &load, &rbranch,
- &sbranch, NULL);
- } else {
- pushi(ctx->current, NULL, Q_JNZ, &load, &sbranch,
- &rbranch, NULL);
- }
-
- push(&ctx->current->body, &rlabel);
- gen_temp(ctx, &result, otype, "assign.result.%d");
- gen_expression(ctx, value, &result);
- gen_store(ctx, &src, &result);
- if (!value->terminates) {
- pushi(ctx->current, NULL, Q_JMP, &sbranch, NULL);
- }
- push(&ctx->current->body, &slabel);
- } else {
- struct qbe_value v = {0};
- gen_temp(ctx, &v, vtype, "assign.value.%d");
- gen_expression(ctx, value, &v);
-
- struct qbe_value result;
- gen_temp(ctx, &result, otype, "assign.result.%d");
-
- struct qbe_value load;
- gen_loadtemp(ctx, &load, &src, otype, type_is_signed(objtype));
- pushi(ctx->current, &result,
- binarithm_for_op(expr->assign.op, otype,
- type_is_signed(objtype)),
- &load, &v, NULL);
- gen_store(ctx, &src, &result);
- }
-}
-
-static void gen_global_decl(struct gen_context *ctx,
- const struct declaration *decl);
-
-static void
-gen_expr_binding(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(out == NULL);
-
- const struct expression_binding *binding = &expr->binding;
- while (binding) {
- struct qbe_value temp = {0};
- if (binding->object->otype != O_DECL) {
- binding_alloc(ctx, binding->object, &temp, "binding.%d");
- gen_expression(ctx, binding->initializer, &temp);
- } else {
- struct declaration decl = {
- .type = DECL_GLOBAL,
- .ident = binding->object->ident,
- .global = {
- .type = binding->object->type,
- .value = binding->initializer,
- },
- };
- gen_global_decl(ctx, &decl);
- }
- binding = binding->next;
- }
-}
-
-static void
-extend(struct gen_context *ctx, struct qbe_value *v, const struct type *type)
-{
- enum qbe_instr op;
- switch (type->size) {
- case 1:
- op = type_is_signed(type) ? Q_EXTSB : Q_EXTUB;
- break;
- case 2:
- op = type_is_signed(type) ? Q_EXTSH : Q_EXTUH;
- break;
- default:
- return;
- }
-
- struct qbe_value temp = {0};
- gen_temp(ctx, &temp, &qbe_word, "ext.%d");
- pushi(ctx->current, &temp, op, v, NULL);
- *v = temp;
-}
-
-static void
-gen_expr_binarithm(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- const struct qbe_type *ltype =
- qtype_for_type(ctx, expr->binarithm.lvalue->result, false);
- const struct qbe_type *rtype =
- qtype_for_type(ctx, expr->binarithm.rvalue->result, false);
- const struct qbe_type *etype = qtype_for_type(ctx, expr->result, false);
-
- if (expr->binarithm.op == BIN_LAND || expr->binarithm.op == BIN_LOR) {
- struct qbe_statement rlabel = {0}, slabel = {0};
- struct qbe_value rbranch = {0}, sbranch = {0}, result = {0};
- rbranch.kind = QV_LABEL;
- rbranch.name = strdup(genl(&rlabel, &ctx->id, "rvalue.%d"));
- sbranch.kind = QV_LABEL;
- sbranch.name = strdup(genl(&slabel, &ctx->id, "short_circuit.%d"));
- gen_temp(ctx, &result, etype, "result.%d");
-
- gen_expression(ctx, expr->binarithm.lvalue, &result);
- if (expr->binarithm.op == BIN_LAND) {
- pushi(ctx->current, NULL, Q_JNZ, &result, &rbranch,
- &sbranch, NULL);
- } else {
- pushi(ctx->current, NULL, Q_JNZ, &result, &sbranch,
- &rbranch, NULL);
- }
-
- push(&ctx->current->body, &rlabel);
- gen_expression(ctx, expr->binarithm.rvalue, &result);
- if (!expr->binarithm.rvalue->terminates) {
- pushi(ctx->current, NULL, Q_JMP, &sbranch, NULL);
- }
-
- push(&ctx->current->body, &slabel);
- gen_store(ctx, out, &result);
- return;
- }
-
- struct qbe_value lvalue = {0}, rvalue = {0}, result = {0};
- gen_temp(ctx, &result, etype, "result.%d");
- if (type_is_aggregate(expr->binarithm.lvalue->result)) {
- alloc_temp(ctx, &lvalue, expr->binarithm.lvalue->result, "lvalue.%d");
- alloc_temp(ctx, &rvalue, expr->binarithm.lvalue->result, "rvalue.%d");
- qval_deref(&lvalue);
- qval_deref(&rvalue);
- } else {
- gen_temp(ctx, &lvalue, ltype, "lvalue.%d");
- gen_temp(ctx, &rvalue, rtype, "rvalue.%d");
- }
-
- gen_expression(ctx, expr->binarithm.lvalue, &lvalue);
- gen_expression(ctx, expr->binarithm.rvalue, &rvalue);
-
- switch (expr->binarithm.op) {
- case BIN_GREATER:
- case BIN_GREATEREQ:
- case BIN_LEQUAL:
- case BIN_LESS:
- case BIN_LESSEQ:
- case BIN_NEQUAL:
- extend(ctx, &lvalue, expr->binarithm.lvalue->result);
- extend(ctx, &rvalue, expr->binarithm.rvalue->result);
- break;
- default:
- break;
- }
-
- if (type_dealias(expr->binarithm.lvalue->result)->storage
- == STORAGE_STRING) {
- struct qbe_value rtfunc = {0};
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.strcmp");
- rtfunc.type = &qbe_long;
- pushi(ctx->current, &result, Q_CALL,
- &rtfunc, &lvalue, &rvalue, NULL);
- if (expr->binarithm.op == BIN_NEQUAL) {
- struct qbe_value temp = {0};
- constw(&temp, 1);
- pushi(ctx->current, &result, Q_XOR, &result, &temp, NULL);
- }
- gen_store(ctx, out, &result);
- return;
- }
-
- pushi(ctx->current, &result,
- binarithm_for_op(expr->binarithm.op, ltype,
- type_is_signed(expr->binarithm.lvalue->result)),
- &lvalue, &rvalue, NULL);
- gen_store(ctx, out, &result);
-}
-
-static void
-gen_expr_call(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value result = {0};
-
- struct qbe_statement call = {
- .type = Q_INSTR,
- .instr = Q_CALL,
- };
-
- struct qbe_arguments *arg, **next = &call.args;
- struct call_argument *carg = expr->call.args;
- arg = *next = xcalloc(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;
-
- const struct type *ftype = type_dealias(expr->call.lvalue->result);
- if (ftype->storage == STORAGE_POINTER) {
- // We get one dereference for free for aggregate types
- ftype = type_dealias(ftype->pointer.referent);
- }
- while (ftype->storage == STORAGE_POINTER) {
- pushi(ctx->current, &arg->value, Q_LOADL, &arg->value, NULL);
- ftype = type_dealias(ftype->pointer.referent);
- }
- assert(ftype->storage == STORAGE_FUNCTION);
- if (ftype->func.result != &builtin_type_void) {
- gen_temp(ctx, &result, qtype_for_type(ctx,
- ftype->func.result, false), "returns.%d");
- call.out = qval_dup(&result);
- }
- if (ftype->func.flags & FN_NORETURN) {
- struct gen_scope_context *scope = ctx->scope;
- while (scope) {
- gen_defers(ctx, scope);
- if (scope->class == SCOPE_FUNC) {
- break;
- }
- scope = scope->parent;
- }
- assert(scope);
- }
-
- while (carg) {
- arg = *next = xcalloc(1, sizeof(struct qbe_arguments));
- if (type_is_aggregate(carg->value->result)) {
- alloc_temp(ctx, &arg->value,
- carg->value->result, "arg.%d");
- qval_deref(&arg->value);
- } else {
- 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->body, &call);
-
- if (out) {
- gen_store(ctx, out, &result);
- }
-}
-
-static void
-gen_expr_type_test(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- // XXX: ARCH
- pushc(ctx->current, "type test");
- const struct type *want = expr->cast.secondary,
- *tagged = type_dealias(expr->cast.value->result);
- struct qbe_value tag = {0}, in = {0}, id = {0};
- gen_temp(ctx, &tag, &qbe_word, "tag.%d");
- alloc_temp(ctx, &in, tagged, "cast.in.%d");
- qval_address(&in);
- gen_expression(ctx, expr->cast.value, &in);
- pushi(ctx->current, &tag, Q_LOADUW, &in, NULL);
- char *type = gen_typename(want);
- pushc(ctx->current, "%u => %s", want->id, type);
- free(type);
- constl(&id, want->id);
- pushi(ctx->current, &tag, Q_CEQW, &tag, &id, NULL);
- gen_store(ctx, out, &tag);
-}
-
-static void
-gen_type_assertion(struct gen_context *ctx,
- const struct expression *expr,
- struct qbe_value *in)
-{
- // XXX: ARCH
- pushc(ctx->current, "type assertion");
- const struct type *want = expr->cast.secondary;
- struct qbe_value tag = {0}, id = {0}, result = {0};
- gen_temp(ctx, &tag, &qbe_word, "tag.%d");
- pushi(ctx->current, &tag, Q_LOADUW, in, NULL);
- constw(&id, want->id);
- char *type = gen_typename(want);
- pushc(ctx->current, "%u => %s", want->id, type);
- free(type);
- gen_temp(ctx, &result, &qbe_word, "valid.%d");
- pushi(ctx->current, &result, Q_CEQW, &tag, &id, NULL);
-
- struct qbe_statement validl = {0}, invalidl = {0};
- struct qbe_value bvalid = {0}, binvalid = {0};
- bvalid.kind = QV_LABEL;
- bvalid.name = strdup(genl(&validl, &ctx->id, "type.valid.%d"));
- binvalid.kind = QV_LABEL;
- binvalid.name = strdup(genl(&invalidl, &ctx->id, "type.invalid.%d"));
-
- pushi(ctx->current, NULL, Q_JNZ, &result, &bvalid, &binvalid, NULL);
-
- push(&ctx->current->body, &invalidl);
-
- gen_fixed_abort(ctx, expr->loc, ABORT_TYPE_ASSERTION);
-
- push(&ctx->current->body, &validl);
-}
-
-static void
-gen_cast_to_tagged(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out,
- const struct type *from)
-{
- const struct type *tagged = expr->result;
- const struct type *subtype = tagged_select_subtype(tagged, from);
-
- struct qbe_value tag = {0}, ptr = {0}, offs = {0};
- constl(&offs, expr->result->align);
-
- if (!subtype && type_dealias(from)->storage == STORAGE_TAGGED
- && from->align != tagged->align
- && type_dealias(tagged)->size != builtin_type_uint.size
- && type_dealias(from)->size != builtin_type_uint.size) {
- // If the alignment differs, we can't use a straight-up copy
- struct qbe_value src = {0}, dest = {0};
- pushc(ctx->current, "to_tagged; converting incompatible");
- alloc_temp(ctx, &src, from, "to_tagged.from.%d");
- qval_deref(&src);
- gen_expression(ctx, expr->cast.value, &src);
-
- gen_temp(ctx, &dest,
- qtype_for_type(ctx, tagged, false), "to_tagged.to.%d");
- pushi(ctx->current, &dest, Q_COPY, out, NULL);
-
- gen_temp(ctx, &tag, &qbe_word, "to_tagged.tag.%d");
- pushi(ctx->current, &tag, Q_LOADUW, &src, NULL);
- pushi(ctx->current, NULL, Q_STOREW, &tag, &dest, NULL);
-
- constl(&offs, tagged->align);
- pushi(ctx->current, &dest, Q_ADD, &dest, &offs, NULL);
- constl(&offs, from->align);
- pushi(ctx->current, &src, Q_ADD, &src, &offs, NULL);
-
- dest.type = dest.type->fields.next->type;
- src.type = src.type->fields.next->type;
- gen_copy(ctx, &dest, &src);
- return;
- } else if (!subtype) {
- pushc(ctx->current, "to_tagged; no subtype");
- alloc_temp(ctx, &ptr, from, "to_tagged.from.%d");
- qval_deref(&ptr);
- gen_expression(ctx, expr->cast.value, &ptr);
- gen_copy(ctx, out, &ptr);
- return;
- }
-
- gen_temp(ctx, &ptr, &qbe_long, "to_tagged.from.%d");
- pushc(ctx->current, "to_tagged; valid subtype");
- struct type ssub = *subtype, sfrom = *from;
- ssub.flags = 0;
- sfrom.flags = 0;
- assert(type_hash(&ssub) == type_hash(&sfrom)); // Lowered by check
-
- struct qbe_value *storage;
- if (expr->cast.value->result->size == 0) {
- storage = NULL;
- } else if (out) {
- pushi(ctx->current, &ptr, Q_COPY, out, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &offs, NULL);
- ptr.type = qtype_for_type(ctx, expr->cast.value->result, false);
- qval_deref(&ptr);
- storage = &ptr;
- } else {
- storage = NULL;
- }
-
- gen_expression(ctx, expr->cast.value, storage);
-
- if (out) {
- char *type = gen_typename(subtype);
- pushc(ctx->current, "%u => %s", subtype->id, type);
- free(type);
- constw(&tag, subtype->id);
- ptr.type = &qbe_long;
- pushi(ctx->current, &ptr, Q_COPY, out, NULL);
- pushi(ctx->current, NULL, Q_STOREW, &tag, &ptr, NULL);
- }
-}
-
-static void
-gen_cast_from_tagged(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out,
- const struct type *to)
-{
- if (type_dealias(to)->storage == STORAGE_VOID) {
- // TODO: generate type assertion if appropriate
- gen_expression(ctx, expr->cast.value, NULL);
- return;
- }
-
- const struct type *tagged = expr->cast.value->result;
- struct qbe_value object = {0}, offs = {0}, temp = {0};
- alloc_temp(ctx, &object, tagged, "from.tagged.%d");
- gen_expression(ctx, expr->cast.value, &object);
- if (expr->cast.kind == C_ASSERTION) {
- gen_type_assertion(ctx, expr, &object);
- }
-
- struct qbe_value ptr = {0};
- gen_temp(ctx, &ptr, &qbe_long, "ptr.%d");
- constl(&offs, expr->cast.value->result->align);
- pushi(ctx->current, &ptr, Q_ADD, &object, &offs, NULL);
-
- ptr.type = qtype_for_type(ctx, expr->result, false);
- qval_deref(&ptr);
- if (object.indirect) {
- if (!type_is_aggregate(expr->result)) {
- gen_loadtemp(ctx, &temp, &ptr,
- qtype_for_type(ctx, expr->result, false),
- type_is_signed(expr->result));
- gen_store(ctx, out, &temp);
- } else {
- gen_copy(ctx, out, &ptr);
- }
- } else {
- gen_store(ctx, out, &ptr);
- }
-}
-
-static void
-gen_expr_cast(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- switch (expr->cast.kind) {
- case C_CAST:
- break; // Handled below
- case C_ASSERTION:
- break; // Handled below
- case C_TEST:
- gen_expr_type_test(ctx, expr, out);
- return;
- }
-
- const struct type *to = expr->result, *from = expr->cast.value->result;
- if (type_dealias(to)->storage == STORAGE_TAGGED
- && expr->cast.kind == C_CAST) {
- gen_cast_to_tagged(ctx, expr, out, from);
- return;
- } else if (type_dealias(from)->storage == STORAGE_TAGGED) {
- gen_cast_from_tagged(ctx, expr, out, to);
- return;
- }
-
- assert(expr->cast.kind == C_CAST);
-
- to = type_dealias(to), from = type_dealias(from);
- if (to->storage == from->storage && to->size == from->size) {
- gen_expression(ctx, expr->cast.value, out);
- return;
- }
-
- bool is_signed = type_is_signed(from);
-
- struct qbe_value in = {0}, result = {0};
- gen_temp(ctx, &result, qtype_for_type(ctx, to, false), "cast.out.%d");
-
- // Special case: slice -> ptr
- if (to->storage == STORAGE_POINTER && from->storage == STORAGE_SLICE) {
- alloc_temp(ctx, &in, from, "cast.in.%d");
- in.indirect = false;
- gen_expression(ctx, expr->cast.value, &in);
- in.type = &qbe_long;
- qval_deref(&in);
- gen_load(ctx, &result, &in, false);
- gen_store(ctx, out, &result);
- return;
- }
-
- if (to->storage == STORAGE_VOID) {
- gen_expression(ctx, expr->cast.value, NULL);
- return;
- }
-
- if (type_is_aggregate(expr->cast.value->result)
- && expr->cast.value->type == EXPR_CONSTANT) {
- // This is a stupid fucking hack
- alloc_temp(ctx, &in, expr->cast.value->result, "cast.in.%d");
- qval_deref(&in);
- } else {
- gen_temp(ctx, &in, qtype_for_type(ctx, from, false), "cast.in.%d");
- if (type_is_aggregate(expr->cast.value->result)) {
- qval_address(&in);
- }
- }
- gen_expression(ctx, expr->cast.value, &in);
-
- // Used for various casts
- enum qbe_instr op;
- struct qbe_value ptr = {0}, offs = {0}, len = {0};
-
- switch (to->storage) {
- case STORAGE_CHAR:
- case STORAGE_ENUM:
- case STORAGE_U8:
- case STORAGE_I8:
- case STORAGE_I16:
- case STORAGE_U16:
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_INT: // XXX: ARCH
- case STORAGE_UINT: // XXX: ARCH
- case STORAGE_I64:
- case STORAGE_U64:
- case STORAGE_UINTPTR: // XXX: ARCH
- case STORAGE_RUNE:
- case STORAGE_SIZE: // XXX: ARCH
- if (type_is_integer(from) && to->size <= from->size) {
- op = Q_COPY;
- } else if (type_is_integer(from) && to->size > from->size) {
- switch (from->size) {
- case 4:
- op = is_signed ? Q_EXTSW : Q_EXTUW;
- break;
- case 2:
- op = is_signed ? Q_EXTSH : Q_EXTUH;
- break;
- case 1:
- op = is_signed ? Q_EXTSB : Q_EXTUB;
- break;
- default:
- assert(0); // Invariant
- }
- } else if (from->storage == STORAGE_POINTER
- || from->storage == STORAGE_NULL) {
- assert(to->storage == STORAGE_UINTPTR);
- op = Q_COPY;
- } else if (from->storage == STORAGE_RUNE) {
- assert(to->storage == STORAGE_U32);
- op = Q_COPY;
- } else if (type_is_float(from)) {
- if (type_is_signed(to)) {
- switch (qstype_for_type(from)) {
- case Q_SINGLE:
- op = Q_STOSI;
- break;
- case Q_DOUBLE:
- op = Q_DTOSI;
- break;
- default:
- assert(0);
- }
- } else {
- assert(0); // TODO
- }
- } else {
- assert(0); // Invariant
- }
- pushi(ctx->current, &result, op, &in, NULL);
- break;
- case STORAGE_F32:
- case STORAGE_F64:
- if (type_is_float(from) && from->size == to->size) {
- op = Q_COPY;
- } else if (type_is_float(from) && to->size < from->size) {
- assert(qstype_for_type(from) == Q_DOUBLE
- && qstype_for_type(to) == Q_SINGLE);
- op = Q_TRUNCD;
- } else if (type_is_float(from) && to->size > from->size) {
- assert(qstype_for_type(from) == Q_SINGLE
- && qstype_for_type(to) == Q_DOUBLE);
- op = Q_EXTS;
- } else if (type_is_integer(from)) {
- if (type_is_signed(from)) {
- switch (qstype_for_type(from)) {
- case Q_WORD:
- op = Q_SWTOF;
- break;
- case Q_LONG:
- op = Q_SLTOF;
- break;
- default:
- assert(0);
- }
- } else {
- assert(0); // TODO
- }
- } else {
- assert(0); // Invariant
- }
- pushi(ctx->current, &result, op, &in, NULL);
- break;
- case STORAGE_ARRAY:
- assert(from->storage == STORAGE_ARRAY);
- pushi(ctx->current, &result, Q_COPY, &in, NULL);
- break;
- case STORAGE_SLICE:
- if (from->storage == STORAGE_SLICE) {
- pushi(ctx->current, &result, Q_COPY, &in, NULL);
- break;
- }
- // XXX: ARCH
- gen_temp(ctx, &ptr, &qbe_long, "ptr.%d");
- constl(&offs, 8);
- constl(&len, from->array.length);
- pushi(ctx->current, &ptr, Q_COPY, out, NULL);
- if (from->array.length == 0) {
- struct qbe_value temp = {0};
- constl(&temp, 0);
- pushi(ctx->current, NULL, Q_STOREL, &temp, &ptr, NULL);
- } else {
- pushi(ctx->current, NULL, Q_STOREL, &in, &ptr, NULL);
- }
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &offs, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &len, &ptr, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &offs, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &len, &ptr, NULL);
- return;
- // Can be implemented with a copy
- case STORAGE_NULL:
- case STORAGE_POINTER:
- pushi(ctx->current, &result, Q_COPY, &in, NULL);
- break;
- case STORAGE_ALIAS:
- case STORAGE_TAGGED:
- assert(0); // Handled above
- case STORAGE_BOOL:
- case STORAGE_FCONST:
- case STORAGE_FUNCTION:
- case STORAGE_ICONST:
- case STORAGE_STRING:
- case STORAGE_STRUCT:
- case STORAGE_TUPLE:
- case STORAGE_UNION:
- assert(0); // Invariant
- case STORAGE_VOID:
- return; // no-op
- }
-
- gen_store(ctx, out, &result);
-}
-
-static void
-gen_array(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- const struct type *type = expr->result;
- assert(type->array.length != SIZE_UNDEFINED);
- if (type->array.length == 0) {
- return;
- }
-
- // XXX: ARCH
- struct qbe_value ptr = {0}, val = {0};
- gen_temp(ctx, &val,
- qtype_for_type(ctx, type->array.members, true),
- "ptr.%d");
- ptr = val, ptr.type = &qbe_long;
- qval_deref(&val);
- pushi(ctx->current, &ptr, Q_COPY, out, NULL);
-
- struct qbe_value size = {0};
- constl(&size, type->array.members->size);
-
- size_t n = 0;
- struct array_constant *item = expr->constant.array;
- while (item) {
- gen_expression(ctx, item->value, &val);
- ++n;
- if (item->next) {
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &size, NULL);
- }
- item = item->next;
- }
-
- if (!expr->constant.array->expand) {
- return;
- }
-
- pushc(ctx->current, "expanding array to length %zd", type->array.length);
- if (type->array.length < 16) {
- struct qbe_value last = {0};
- if (type_is_aggregate(type->array.members)) {
- alloc_temp(ctx, &last, type->array.members, "expand.%d");
- qval_deref(&last);
- } else {
- gen_temp(ctx, &last,
- qtype_for_type(ctx, type->array.members, false),
- "expand.%d");
- }
- gen_load(ctx, &last, &val, type_is_signed(type->array.members));
- for (; n < type->array.length; ++n) {
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &size, NULL);
- gen_store(ctx, &val, &last);
- }
- } else {
- struct qbe_value totalsize = {0};
- constl(&totalsize, (type->array.length - n) * type->array.members->size);
- struct qbe_value offset = {0};
- gen_temp(ctx, &offset, &qbe_long, "ptr.%d");
- pushi(ctx->current, &offset, Q_ADD, &val, &size, NULL);
- val.type = &qbe_long;
-
- struct qbe_value rtmemcpy = {0};
- rtmemcpy.kind = QV_GLOBAL;
- rtmemcpy.name = strdup("rt.memcpy");
- rtmemcpy.type = &qbe_long;
- pushi(ctx->current, NULL, Q_CALL, &rtmemcpy,
- &offset, &val, &totalsize, NULL);
- }
-}
-
-static void
-gen_string(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(!out->indirect); // Invariant
-
- struct qbe_value temp = {0};
- gen_temp(ctx, &temp, &qbe_long, "strdata.%d");
- temp.kind = QV_GLOBAL;
-
- struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def));
- def->name = temp.name;
- def->kind = Q_DATA;
- def->data.items.type = QD_STRING;
- def->data.items.str = xcalloc(1, expr->constant.string.len);
- memcpy(def->data.items.str, expr->constant.string.value,
- expr->constant.string.len);
- def->data.items.sz = expr->constant.string.len;
-
- struct qbe_value size = {0};
- constl(&size, expr->constant.string.len); // XXX: ARCH
-
- struct qbe_value str = {0};
- gen_temp(ctx, &str, &qbe_long, "str.%d"); // XXX: ARCH
- pushi(ctx->current, &str, Q_COPY, out, NULL);
- str.indirect = true;
-
- if (expr->constant.string.len != 0) {
- qbe_append_def(ctx->out, def);
- } else {
- free(def);
- constl(&temp, 0);
- }
-
- gen_store(ctx, &str, &temp);
- constl(&temp, 8); // XXX: ARCH
- pushi(ctx->current, &str, Q_ADD, &str, &temp, NULL);
- gen_store(ctx, &str, &size);
- pushi(ctx->current, &str, Q_ADD, &str, &temp, NULL);
- gen_store(ctx, &str, &size);
-}
-
-static void
-gen_expr_constant(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- if (out == NULL) {
- pushc(ctx->current, "useless constant expression discarded");
- return;
- }
-
- struct qbe_value val = {0};
-
- // Special cases
- switch (expr->result->storage) {
- case STORAGE_BOOL:
- constw(&val, expr->constant.bval ? 1 : 0);
- gen_store(ctx, out, &val);
- return;
- case STORAGE_VOID:
- const_void(&val);
- gen_store(ctx, out, &val);
- return;
- case STORAGE_NULL:
- constl(&val, 0);
- gen_store(ctx, out, &val);
- return;
- case STORAGE_ARRAY:
- gen_array(ctx, expr, out);
- return;
- case STORAGE_STRING:
- gen_string(ctx, expr, out);
- return;
- default:
- // Moving right along
- break;
- }
-
- const struct qbe_type *qtype = qtype_for_type(ctx, expr->result, false);
- switch (qtype->stype) {
- case Q_BYTE:
- case Q_HALF:
- case Q_WORD:
- constw(&val, (uint32_t)expr->constant.uval);
- break;
- case Q_LONG:
- constl(&val, (uint64_t)expr->constant.uval);
- break;
- case Q_SINGLE:
- consts(&val, (float)expr->constant.fval);
- break;
- case Q_DOUBLE:
- constd(&val, expr->constant.fval);
- break;
- case Q__VOID:
- return; // no-op
- case Q__AGGREGATE:
- assert(0); // Invariant
- }
-
- gen_store(ctx, out, &val);
-}
-
-static void
-gen_expr_control(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct gen_scope_context *scope = ctx->scope;
- while (scope != NULL) {
- gen_defers(ctx, scope);
- if (expr->control.label && scope->label) {
- if (strcmp(expr->control.label, scope->label) == 0) {
- break;
- }
- } else if (!expr->control.label && scope->class == SCOPE_LOOP) {
- break;
- }
- scope = scope->parent;
- }
- assert(scope != NULL);
- if (expr->type == EXPR_BREAK) {
- pushi(ctx->current, NULL, Q_JMP, scope->end, NULL);
- } else {
- pushi(ctx->current, NULL, Q_JMP, scope->after, NULL);
- }
-}
-
-static void
-gen_expr_defer(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct gen_deferred *d = xcalloc(1, sizeof(struct gen_deferred));
- d->expr = expr->defer.deferred;
- d->next = ctx->scope->defers;
- ctx->scope->defers = d;
-}
-
-static void
-gen_expr_delete(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(expr->type == EXPR_DELETE);
- struct qbe_value object = {0}, start = {0}, temp = {0};
- gen_temp(ctx, &object, &qbe_long, "delete.obj.%d");
- gen_temp(ctx, &start, &qbe_long, "delete.start.%d");
- const struct expression *dexpr = expr->delete.expr;
- const struct type *mtype = NULL;
- if (dexpr->type == EXPR_SLICE) {
- mtype = type_dealias(dexpr->slice.object->result);
- // XXX: Can we refactor this to use gen_expr_slice?
- gen_expression(ctx, dexpr->slice.object, &object);
- if (dexpr->slice.start) {
- gen_expression(ctx, dexpr->slice.start, &start);
- } else {
- constl(&temp, 0);
- pushi(ctx->current, &start, Q_COPY, &temp, NULL);
- }
- } else {
- assert(dexpr->type == EXPR_ACCESS
- && dexpr->access.type == ACCESS_INDEX);
- mtype = type_dealias(dexpr->access.array->result);
- // XXX: Can we refactor this to use address_index?
- gen_expression(ctx, dexpr->access.array, &object);
- gen_expression(ctx, dexpr->access.index, &start);
- }
- if (mtype->storage == STORAGE_POINTER) {
- mtype = type_dealias(mtype->pointer.referent);
- }
- while (mtype->storage == STORAGE_POINTER) {
- pushi(ctx->current, &object, Q_LOADL, &object, NULL);
- mtype = type_dealias(mtype->pointer.referent);
- }
- assert(mtype->storage == STORAGE_SLICE);
- mtype = mtype->array.members;
-
- struct qbe_value lenptr = {0}, len = {0};
- gen_loadtemp(ctx, &lenptr, &object, &qbe_long, false);
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &lenptr, Q_ADD, &lenptr, &temp, NULL);
- qval_deref(&lenptr);
- gen_loadtemp(ctx, &len, &lenptr, &qbe_long, false);
-
- struct qbe_value end = {0};
- gen_temp(ctx, &end, &qbe_long, "delete.end.%d");
- if (dexpr->type == EXPR_SLICE) {
- if (dexpr->slice.end) {
- gen_expression(ctx, dexpr->slice.end, &end);
- } else {
- pushi(ctx->current, &end, Q_COPY, &len, NULL);
- }
- } else {
- constl(&temp, 1);
- pushi(ctx->current, &end, Q_ADD, &start, &temp, NULL);
- }
-
- struct qbe_value newlen = {0};
- gen_temp(ctx, &newlen, &qbe_long, "delete.newlen.%d");
- pushi(ctx->current, &newlen, Q_SUB, &end, &start, NULL);
- pushi(ctx->current, &newlen, Q_SUB, &len, &newlen, NULL);
- gen_store(ctx, &lenptr, &newlen);
- constl(&temp, mtype->size);
- pushi(ctx->current, &len, Q_MUL, &len, &temp, NULL);
-
- // TODO: Bounds check
-
- struct qbe_value membsz = {0};
- constl(&membsz, mtype->size);
- pushi(ctx->current, &start, Q_MUL, &start, &membsz, NULL);
- pushi(ctx->current, &end, Q_MUL, &end, &membsz, NULL);
-
- struct qbe_value ptr = {0}, sptr = {0}, eptr = {0};
- qval_deref(&object);
- gen_loadtemp(ctx, &ptr, &object, &qbe_long, "delete.ptr.%d");
- gen_temp(ctx, &sptr, &qbe_long, "delete.sptr.%d");
- gen_temp(ctx, &eptr, &qbe_long, "delete.eptr.%d");
- qval_address(&ptr);
- pushi(ctx->current, &sptr, Q_ADD, &ptr, &start, NULL);
- pushi(ctx->current, &eptr, Q_ADD, &ptr, &end, NULL);
-
- pushi(ctx->current, &len, Q_SUB, &len, &end, NULL);
-
- struct qbe_value rtmemcpy = {0};
- rtmemcpy.kind = QV_GLOBAL;
- rtmemcpy.name = strdup("rt.memcpy");
- rtmemcpy.type = &qbe_long;
- pushi(ctx->current, NULL, Q_CALL, &rtmemcpy, &sptr, &eptr, &len, NULL);
-
- if (!expr->delete.is_static) {
- struct qbe_value rtunensure = {0};
- rtunensure.kind = QV_GLOBAL;
- rtunensure.name = strdup("rt.unensure");
- rtunensure.type = &qbe_long;
- pushi(ctx->current, NULL, Q_CALL, &rtunensure, &object, &membsz, NULL);
- } else {
- struct qbe_value capptr = {0}, cap = {0};
- gen_temp(ctx, &capptr, &qbe_long, "append.capptr.%d");
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &capptr, Q_ADD, &lenptr, &temp, NULL);
- qval_deref(&capptr);
- gen_loadtemp(ctx, &cap, &capptr, &qbe_long, false);
-
- struct qbe_statement validl = {0}, invalidl = {0};
- struct qbe_value bvalid = {0}, binvalid = {0};
- bvalid.kind = QV_LABEL;
- bvalid.name = strdup(genl(&validl, &ctx->id, "bounds.valid.%d"));
- binvalid.kind = QV_LABEL;
- binvalid.name = strdup(genl(&invalidl, &ctx->id, "bounds.invalid.%d"));
-
- struct qbe_value valid = {0};
- gen_temp(ctx, &valid, &qbe_word, "valid.%d");
- pushi(ctx->current, &valid, Q_CULEL, &newlen, &cap, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &valid, &bvalid, &binvalid, NULL);
- push(&ctx->current->body, &invalidl);
-
- gen_fixed_abort(ctx, expr->loc, ABORT_OOB);
-
- push(&ctx->current->body, &validl);
- }
-}
-
-static void
-gen_expr_for(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(out == NULL); // Invariant
- if (expr->_for.bindings) {
- gen_expr_binding(ctx, expr->_for.bindings, NULL);
- }
-
- struct qbe_statement loopl = {0}, bodyl = {0}, afterl = {0}, endl = {0};
- struct qbe_value loop = {0}, body = {0}, after = {0}, end = {0};
- loop.kind = QV_LABEL;
- loop.name = strdup(genl(&loopl, &ctx->id, "loop.%d"));
- body.kind = QV_LABEL;
- body.name = strdup(genl(&bodyl, &ctx->id, "body.%d"));
- after.kind = QV_LABEL;
- after.name = strdup(genl(&afterl, &ctx->id, "after.%d"));
- end.kind = QV_LABEL;
- end.name = strdup(genl(&endl, &ctx->id, "end.%d"));
-
- push(&ctx->current->body, &loopl);
-
- struct gen_scope_context *scope = push_scope(ctx, SCOPE_LOOP, &end);
- scope->after = &after;
- scope->label = expr->_for.label;
-
- struct qbe_value cond = {0};
- gen_temp(ctx, &cond, &qbe_word, "cond.%d");
- gen_expression(ctx, expr->_for.cond, &cond);
-
- pushi(ctx->current, NULL, Q_JNZ, &cond, &body, &end, NULL);
-
- push(&ctx->current->body, &bodyl);
- gen_expression(ctx, expr->_for.body, NULL);
-
- push(&ctx->current->body, &afterl);
- if (expr->_for.afterthought) {
- gen_expression(ctx, expr->_for.afterthought, NULL);
- }
-
- gen_defers(ctx, ctx->scope);
- pop_scope(ctx);
-
- pushi(ctx->current, NULL, Q_JMP, &loop, NULL);
-
- push(&ctx->current->body, &endl);
-}
-
-static void
-gen_expr_free(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value val = {0}, rtfunc = {0};
- const struct type *type = type_dealias(expr->alloc.expr->result);
- if (type->storage == STORAGE_SLICE
- || type->storage == STORAGE_STRING) {
- alloc_temp(ctx, &val, type, "free.%d");
- val.type = &qbe_long;
- gen_expression(ctx, expr->alloc.expr, &val);
- val.type = &qbe_long;
- pushi(ctx->current, &val, Q_LOADL, &val, NULL);
- } else {
- gen_temp(ctx, &val,
- qtype_for_type(ctx, expr->alloc.expr->result, false),
- "free.%d");
- gen_expression(ctx, expr->alloc.expr, &val);
- }
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.free");
- rtfunc.type = &qbe_long;
- pushi(ctx->current, NULL, Q_CALL, &rtfunc, &val, NULL);
-}
-
-static void
-gen_expr_if(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value cond = {0};
- gen_temp(ctx, &cond, &qbe_word, "cond.%d");
- gen_expression(ctx, expr->_if.cond, &cond);
-
- struct qbe_statement tlabel = {0}, flabel = {0}, endl = {0};
- struct qbe_value tbranch = {0}, fbranch = {0}, end = {0};
- tbranch.kind = QV_LABEL;
- tbranch.name = strdup(genl(&tlabel, &ctx->id, "branch_true.%d"));
- fbranch.kind = QV_LABEL;
- fbranch.name = strdup(genl(&flabel, &ctx->id, "branch_false.%d"));
- end.name = strdup(genl(&endl, &ctx->id, "end.%d"));
- end.kind = QV_LABEL;
-
- pushi(ctx->current, NULL, Q_JNZ, &cond, &tbranch, &fbranch, NULL);
-
- push(&ctx->current->body, &tlabel);
- gen_expression(ctx, expr->_if.true_branch,
- expr->_if.true_branch->terminates ? NULL : out);
- if (!expr->_if.true_branch->terminates) {
- pushi(ctx->current, NULL, Q_JMP, &end, NULL);
- }
-
- push(&ctx->current->body, &flabel);
- if (expr->_if.false_branch) {
- gen_expression(ctx, expr->_if.false_branch,
- expr->_if.false_branch->terminates ? NULL : out);
- if (!expr->_if.false_branch->terminates) {
- pushi(ctx->current, NULL, Q_JMP, &end, NULL);
- }
- }
-
- push(&ctx->current->body, &endl);
-}
-
-static void
-gen_expr_insert(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- assert(expr->type == EXPR_INSERT);
-
- struct qbe_value val = {0}, temp = {0};
- gen_temp(ctx, &val, &qbe_long, "insert.val.%d");
- assert(expr->insert.expr->type == EXPR_ACCESS
- && expr->insert.expr->access.type == ACCESS_INDEX);
-
- // TODO: Automatic dereference here
- const struct expression *expr_array = expr->insert.expr->access.array;
- struct qbe_value addr = {0};
- address_object(ctx, expr_array, &addr);
- qval_address(&addr);
- gen_store(ctx, &val, &addr);
-
- const struct expression *expr_index = expr->insert.expr->access.index;
- struct qbe_value index = {0};
- gen_temp(ctx, &index, &qbe_long, "insert.index.%d");
- gen_expression(ctx, expr_index, &index);
-
- const struct type *sltype = expr->insert.expr->access.array->result;
- const struct type *mtype = type_dealias(sltype)->array.members;
-
- struct qbe_value len = {0}, newlen = {0}, lenptr = {0}, nadd = {0};
- gen_temp(ctx, &lenptr, &qbe_long, "insert.lenptr.%d");
- gen_temp(ctx, &newlen, &qbe_long, "insert.newlen.%d");
- gen_loadtemp(ctx, &lenptr, &val, &qbe_long, false);
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &lenptr, Q_ADD, &lenptr, &temp, NULL);
- qval_deref(&lenptr);
- gen_loadtemp(ctx, &len, &lenptr, &qbe_long, false);
-
- struct qbe_value variadic = {0}, vptr = {0}, vlen = {0};
- pushi(ctx->current, &newlen, Q_COPY, &len, NULL);
-
- size_t args = 0;
- for (struct append_values *value = expr->insert.values;
- value; value = value->next) {
- args++;
- }
- constl(&nadd, args);
- pushi(ctx->current, &newlen, Q_ADD, &newlen, &nadd, NULL);
-
- if (expr->insert.variadic) {
- struct qbe_value vlenptr = {0};
- alloc_temp(ctx, &variadic,
- expr->insert.variadic->result, "insert.variadic.%d");
- gen_expression(ctx, expr->insert.variadic, &variadic);
- qval_deref(&variadic);
- gen_loadtemp(ctx, &vptr, &variadic, &qbe_long, false);
- qval_deref(&vptr);
- gen_temp(ctx, &vlenptr, &qbe_long, "insert.vlenptr.%d");
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &vlenptr, Q_ADD, &vptr, &temp, NULL);
- qval_deref(&vlenptr);
- gen_loadtemp(ctx, &vlen, &vlenptr, &qbe_long, false);
- pushi(ctx->current, &newlen, Q_ADD, &newlen, &vlen, NULL);
- }
-
- gen_store(ctx, &lenptr, &newlen);
-
- struct qbe_value rtfunc = {0}, membsz = {0};
- constl(&membsz, mtype->size);
- rtfunc.kind = QV_GLOBAL;
- rtfunc.type = &qbe_long;
-
- if (!expr->insert.is_static) {
- rtfunc.name = strdup("rt.ensure");
- pushi(ctx->current, NULL, Q_CALL, &rtfunc, &val, &membsz, NULL);
- } else {
- struct qbe_value capptr = {0}, cap = {0};
- gen_temp(ctx, &capptr, &qbe_long, "append.capptr.%d");
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &capptr, Q_ADD, &lenptr, &temp, NULL);
- qval_deref(&capptr);
- gen_loadtemp(ctx, &cap, &capptr, &qbe_long, false);
-
- struct qbe_statement validl = {0}, invalidl = {0};
- struct qbe_value bvalid = {0}, binvalid = {0};
- bvalid.kind = QV_LABEL;
- bvalid.name = strdup(genl(&validl, &ctx->id, "bounds.valid.%d"));
- binvalid.kind = QV_LABEL;
- binvalid.name = strdup(genl(&invalidl, &ctx->id, "bounds.invalid.%d"));
-
- struct qbe_value valid = {0};
- gen_temp(ctx, &valid, &qbe_word, "valid.%d");
- pushi(ctx->current, &valid, Q_CULEL, &newlen, &cap, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &valid, &bvalid, &binvalid, NULL);
- push(&ctx->current->body, &invalidl);
-
- gen_fixed_abort(ctx, expr->loc, ABORT_STATIC_EXCEEDED);
-
- push(&ctx->current->body, &validl);
- }
-
- struct qbe_value ptr = {0};
- const struct qbe_type *type = qtype_for_type(ctx, mtype, true);
- qval_deref(&val);
- gen_loadtemp(ctx, &ptr, &val, &qbe_long, "insert.ptr.%d");
- qval_address(&ptr);
-
- pushi(ctx->current, &index, Q_MUL, &index, &membsz, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &index, NULL);
-
- struct qbe_value dest = {0}, ncopy = {0}, nbytes = {0};
- gen_temp(ctx, &dest, &qbe_long, "insert.dest.%d");
- gen_temp(ctx, &ncopy, &qbe_long, "insert.ncopy.%d");
- gen_temp(ctx, &nbytes, &qbe_long, "insert.ninsert.%d");
- pushi(ctx->current, &dest, Q_COPY, &ptr, NULL);
- pushi(ctx->current, &ncopy, Q_MUL, &len, &membsz, NULL);
- pushi(ctx->current, &ncopy, Q_SUB, &ncopy, &index, NULL);
- pushi(ctx->current, &nbytes, Q_MUL, &nadd, &membsz, NULL);
- if (expr->insert.variadic) {
- pushi(ctx->current, &vlen, Q_MUL, &vlen, &membsz, NULL);
- pushi(ctx->current, &nbytes, Q_ADD, &nbytes, &vlen, NULL);
- }
- pushi(ctx->current, &dest, Q_ADD, &dest, &nbytes, NULL);
- rtfunc.name = strdup("rt.memmove");
- pushi(ctx->current, NULL, Q_CALL, &rtfunc, &dest, &ptr, &ncopy, NULL);
-
- for (struct append_values *value = expr->insert.values; value;
- value = value->next) {
- struct qbe_value v = {0};
- alloc_temp(ctx, &v, value->expr->result, "insert.value.%d");
- qval_deref(&v);
- gen_expression(ctx, value->expr, &v);
- v.indirect = false;
- ptr.type = type;
- gen_copy(ctx, &ptr, &v);
- ptr.type = &qbe_long;
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &membsz, NULL);
- }
- if (expr->append.variadic) {
- struct qbe_value rtmemcpy = {0}, v = {0};
- gen_loadtemp(ctx, &v, &vptr, &qbe_long, false);
- rtmemcpy.kind = QV_GLOBAL;
- rtmemcpy.name = strdup("rt.memcpy");
- rtmemcpy.type = &qbe_long;
- pushi(ctx->current, NULL, Q_CALL, &rtmemcpy, &ptr, &v, &vlen, NULL);
- }
-}
-
-static void
-gen_expr_list(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_statement endl = {0};
- struct qbe_value end = {0};
- end.kind = QV_LABEL;
- end.name = strdup(genl(&endl, &ctx->id, "expr_list_end.%d"));
- push_scope(ctx, SCOPE_OTHER, &end);
-
- const struct expressions *exprs = &expr->list.exprs;
- while (exprs) {
- const struct qbe_value *dest = NULL;
- if (!exprs->next) {
- dest = out; // Last value determines expression result
- }
- gen_expression(ctx, exprs->expr, dest);
- exprs = exprs->next;
- }
-
- if (!expr->terminates) {
- gen_defers(ctx, ctx->scope);
- }
- pop_scope(ctx);
- push(&ctx->current->body, &endl);
-}
-
-static void
-gen_recursive_match_tests(struct gen_context *ctx, const struct type *mtype,
- struct qbe_value *tbranch, struct qbe_value *fbranch,
- struct qbe_value *tag, struct qbe_value *mval, struct match_case *_case)
-{
- pushc(ctx->current, "recursive match");
- struct qbe_value temp = {0}, temp_tag = {0}, subval = {0}, offs = {0};
- gen_temp(ctx, &subval, &qbe_long, "subtag.ptr.%d");
- gen_temp(ctx, &temp_tag, &qbe_word, "subtag.tag.%d");
- pushi(ctx->current, &subval, Q_COPY, mval, NULL);
- gen_temp(ctx, &temp, &qbe_word, "temp.%d");
-
- struct qbe_value *curtag = tag;
- const struct type *subtype = mtype;
- const struct type *test = _case->type;
- do {
- struct qbe_statement slabel = {0};
- struct qbe_value sbranch = {0};
- test = tagged_select_subtype(subtype, _case->type);
- if (!test) {
- break;
- }
-
- struct qbe_value match = {0};
- sbranch.kind = QV_LABEL;
- sbranch.name = strdup(genl(&slabel, &ctx->id, "match.subtype.%d"));
- constw(&match, test->id);
- char *type = gen_typename(test);
- pushc(ctx->current, "%u => %s", test->id, type);
- free(type);
- pushi(ctx->current, &temp, Q_CEQW, &match, curtag, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &temp, &sbranch, fbranch, NULL);
- push(&ctx->current->body, &slabel);
-
- if (test->id != _case->type->id) {
- constl(&offs, subtype->align);
- pushi(ctx->current, &subval, Q_ADD, &subval, &offs, NULL);
- pushi(ctx->current, &temp_tag, Q_LOADUW, &subval, NULL);
- curtag = &temp_tag;
- }
-
- subtype = test;
- } while (test->id != _case->type->id);
- pushi(ctx->current, NULL, Q_JMP, tbranch, NULL);
-}
-
-static void
-gen_match_tagged_subset(struct gen_context *ctx,
- struct qbe_value *tbranch, struct qbe_value *fbranch,
- struct qbe_value *tag, struct match_case *_case)
-{
- pushc(ctx->current, "match subset-compatible type");
- struct qbe_value temp = {0}, match = {0};
- gen_temp(ctx, &temp, &qbe_word, "temp.%d");
- const struct type_tagged_union *tu = &type_dealias(_case->type)->tagged;
- while (tu) {
- struct qbe_statement nlabel = {0};
- struct qbe_value nbranch = {0};
- nbranch.kind = QV_LABEL;
- nbranch.name = strdup(genl(&nlabel, &ctx->id, "match.subtype.%d"));
- constw(&match, tu->type->id);
- char *type = gen_typename(tu->type);
- pushc(ctx->current, "%u => %s", tu->type->id, type);
- free(type);
- pushi(ctx->current, &temp, Q_CEQW, &match, tag, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &temp, tbranch, &nbranch, NULL);
- push(&ctx->current->body, &nlabel);
- tu = tu->next;
- }
- pushi(ctx->current, NULL, Q_JMP, fbranch, NULL);
-}
-
-static void
-gen_match_tagged(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- const struct type *mtype = expr->match.value->result;
- struct qbe_value mval = {0}, tag = {0}, temp = {0};
- // Kill me
- alloc_temp(ctx, &mval, mtype, "match.%d");
- qval_deref(&mval);
- gen_expression(ctx, expr->match.value, &mval);
- gen_temp(ctx, &temp, &qbe_word, "temp.%d");
-
- gen_temp(ctx, &tag, &qbe_word, "tag.%d");
- pushi(ctx->current, &tag, Q_LOADUW, &mval, NULL);
-
- struct qbe_statement olabel = {0};
- struct qbe_value obranch = {0};
- obranch.kind = QV_LABEL;
- obranch.name = strdup(genl(&olabel, &ctx->id, "out.%d"));
-
- struct match_case *_default = NULL;
- for (struct match_case *_case = expr->match.cases;
- _case; _case = _case->next) {
- if (!_case->type) {
- _default = _case;
- continue;
- }
-
- struct qbe_statement tlabel = {0}, flabel = {0};
- struct qbe_value tbranch = {0}, fbranch = {0};
- tbranch.kind = QV_LABEL;
- tbranch.name = strdup(genl(&tlabel, &ctx->id, "match.%d"));
- fbranch.kind = QV_LABEL;
- fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d"));
-
- enum {
- // Interpret the value's member field
- MEMBER,
- // Interpret as a different, compatible tagged union
- COMPATIBLE,
- // Interpret as an incompatible tagged union (convert)
- INCOMPATIBLE,
- } interpretation = MEMBER;
-
- if (tagged_select_subtype(mtype, _case->type) == NULL) {
- assert(type_dealias(_case->type)->storage == STORAGE_TAGGED);
- assert(tagged_subset_compat(mtype, _case->type));
- // Our match value can be "re-interpreted" as this case
- // type because it is a subset-compatible tagged union.
- // This causes later code to leave the pointer at the
- // tag, rather than advancing it to the value area,
- // before initializing a binding for it.
- //
- // This is only possible for types with a matching
- // alignment, or for a case type whose members are all
- // void.
- if (_case->type->align == mtype->align
- || _case->type->size == builtin_type_uint.size) {
- interpretation = COMPATIBLE;
- } else {
- interpretation = INCOMPATIBLE;
- }
- gen_match_tagged_subset(ctx, &tbranch,
- &fbranch, &tag, _case);
- } else {
- gen_recursive_match_tests(ctx, mtype,
- &tbranch, &fbranch, &tag, &mval, _case);
- }
-
- push(&ctx->current->body, &tlabel);
- if (_case->object) {
- struct qbe_value val = {0}, temp = {0};
- if (interpretation == INCOMPATIBLE) {
- alloc_temp(ctx, &val, _case->type, "bound.%d");
- } else {
- gen_temp(ctx, &val,
- qtype_for_type(ctx, _case->type, false),
- "bound.%d");
- }
- struct gen_binding *binding =
- xcalloc(1, sizeof(struct gen_binding));
- binding->name = strdup(val.name);
- binding->object = _case->object;
- binding->next = ctx->bindings;
- ctx->bindings = binding;
- switch (interpretation) {
- case MEMBER:
- constl(&temp, mtype->align);
- val.type = &qbe_long;
- pushi(ctx->current, &val, Q_ADD, &mval, &temp, NULL);
- break;
- case COMPATIBLE:
- val.type = &qbe_long;
- pushi(ctx->current, &val, Q_COPY, &mval, NULL);
- break;
- case INCOMPATIBLE:
- pushc(ctx->current, "converting to incompatible union");
- pushi(ctx->current, NULL, Q_STOREW, &tag, &val, NULL);
-
- struct qbe_value dest = {0}, src = {0};
- gen_temp(ctx, &dest, &qbe_long, "conv.dest.%d");
- gen_temp(ctx, &src, &qbe_long, "conv.src.%d");
-
- constl(&temp, _case->type->align);
- pushi(ctx->current, &dest, Q_ADD, &val, &temp, NULL);
- dest.type = val.type->fields.next->type;
-
- constl(&temp, mtype->align);
- pushi(ctx->current, &src, Q_ADD, &mval, &temp, NULL);
- src.type = mval.type->fields.next->type;
-
- gen_copy(ctx, &dest, &src);
- break;
- }
- }
-
- if (_case->value->terminates) {
- gen_expression(ctx, _case->value, NULL);
- } else {
- gen_expression(ctx, _case->value, out);
- pushi(ctx->current, NULL, Q_JMP, &obranch, NULL);
- }
- push(&ctx->current->body, &flabel);
- }
-
- if (_default && _default->value->terminates) {
- gen_expression(ctx, _default->value, NULL);
- } else if (_default) {
- gen_expression(ctx, _default->value, out);
- }
-
- push(&ctx->current->body, &olabel);
-}
-
-static void
-gen_match_nullable(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value mval = {0}, temp = {0};
- gen_temp(ctx, &mval, &qbe_long, "match.%d"); // XXX: ARCH
- gen_temp(ctx, &temp, &qbe_long, "temp.%d"); // XXX: ARCH
- gen_expression(ctx, expr->match.value, &mval);
-
- struct qbe_statement olabel = {0};
- struct qbe_value obranch = {0};
- obranch.kind = QV_LABEL;
- obranch.name = strdup(genl(&olabel, &ctx->id, "out.%d"));
-
- struct match_case *_default = NULL;
- for (struct match_case *_case = expr->match.cases;
- _case; _case = _case->next) {
- if (!_case->type) {
- _default = _case;
- continue;
- }
-
- struct qbe_statement tlabel = {0}, flabel = {0};
- struct qbe_value tbranch = {0}, fbranch = {0};
- tbranch.kind = QV_LABEL;
- tbranch.name = strdup(genl(&tlabel, &ctx->id, "match.%d"));
- fbranch.kind = QV_LABEL;
- fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d"));
-
- struct qbe_value zero = {0};
- constl(&zero, 0);
- pushi(ctx->current, &temp, Q_CEQL, &mval, &zero, NULL);
-
- if (_case->type->storage == STORAGE_NULL) {
- pushi(ctx->current, NULL, Q_JNZ,
- &temp, &tbranch, &fbranch, NULL);
- } else {
- pushi(ctx->current, NULL, Q_JNZ,
- &temp, &fbranch, &tbranch, NULL);
- }
-
- push(&ctx->current->body, &tlabel);
-
- if (_case->object) {
- struct qbe_value val = {0};
- alloc_temp(ctx, &val, _case->type, "bound.%d");
- struct gen_binding *binding =
- xcalloc(1, sizeof(struct gen_binding));
- binding->name = strdup(val.name);
- binding->object = _case->object;
- binding->next = ctx->bindings;
- ctx->bindings = binding;
- gen_store(ctx, &val, &mval);
- }
-
- if (_case->value->terminates) {
- gen_expression(ctx, _case->value, NULL);
- } else {
- gen_expression(ctx, _case->value, out);
- pushi(ctx->current, NULL, Q_JMP, &obranch, NULL);
- }
- push(&ctx->current->body, &flabel);
- }
-
- if (_default) {
- gen_expression(ctx, _default->value, out);
- }
-
- push(&ctx->current->body, &olabel);
-}
-
-static void
-gen_expr_match(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- const struct type *mtype = type_dealias(expr->match.value->result);
- switch (mtype->storage) {
- case STORAGE_TAGGED:
- gen_match_tagged(ctx, expr, out);
- break;
- case STORAGE_POINTER:
- gen_match_nullable(ctx, expr, out);
- break;
- default:
- assert(0); // Invariant
- }
-}
-
-static void
-gen_expr_measure(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value src = {0}, temp = {0}, ptr = {0};
- switch (expr->measure.op) {
- case M_LEN:
- switch (type_dealias(expr->measure.value->result)->storage) {
- case STORAGE_ARRAY:
- gen_temp(ctx, &temp,
- qtype_for_type(ctx, expr->result, false),
- "len.%d");
- constl(&temp, type_dealias(expr->measure.value->result)->array.length);
- gen_store(ctx, out, &temp);
- break;
- case STORAGE_SLICE:
- case STORAGE_STRING:
- // My god this is a fucking mess
- alloc_temp(ctx, &src,
- expr->measure.value->result,
- "len.src.%d");
- qval_deref(&src);
- gen_temp(ctx, &ptr, &qbe_long, "len.ptr.%d");
- gen_expression(ctx, expr->measure.value, &src);
- constl(&temp, builtin_type_size.size);
- pushi(ctx->current, &ptr, Q_COPY, &src, NULL);
- pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
- gen_temp(ctx, &temp, &qbe_long, "len.%d");
- pushi(ctx->current, &temp, Q_LOADL, &ptr, NULL);
- gen_store(ctx, out, &temp);
- break;
- default:
- assert(0); // Invariant
- }
- break;
- case M_SIZE:
- gen_temp(ctx, &temp,
- qtype_for_type(ctx, expr->result, false),
- "size.%d");
- constl(&temp, expr->measure.type->size);
- gen_store(ctx, out, &temp);
- break;
- case M_OFFSET:
- assert(0); // TODO
- }
-}
-
-static void
-gen_expr_return(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- if (expr->_return.value) {
- gen_expression(ctx, expr->_return.value, ctx->return_value);
- }
-
- struct gen_scope_context *scope = ctx->scope;
- while (scope) {
- gen_defers(ctx, scope);
- if (scope->class == SCOPE_FUNC) {
- break;
- }
- scope = scope->parent;
- }
- assert(scope);
-
- pushi(ctx->current, NULL, Q_JMP, scope->end, NULL);
-}
-
-static void
-gen_expr_slice(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- // XXX: ARCH
- struct qbe_value object = {0}, start = {0}, end = {0}, temp = {0};
- const struct type *otype = expr->slice.object->result;
- address_object(ctx, expr->slice.object, &temp);
- gen_temp(ctx, &object, object.type, "object.%d");
- object.type = temp.type;
- pushi(ctx->current, &object, Q_COPY, &temp, NULL);
- otype = type_dealias(otype);
- while (otype->storage == STORAGE_POINTER) {
- pushi(ctx->current, &object, Q_LOADL, &object, NULL);
- otype = type_dealias(otype->pointer.referent);
- }
-
- gen_temp(ctx, &start, &qbe_long, "start.%d");
- gen_temp(ctx, &end, &qbe_long, "end.%d");
-
- struct qbe_value src = {0}, dest = {0}, offset = {0};
- gen_temp(ctx, &dest, &qbe_long, "dest.%d");
- gen_temp(ctx, &offset, &qbe_long, "offset.%d");
-
- if (expr->slice.start) {
- gen_expression(ctx, expr->slice.start, &start);
- } else {
- constl(&start, 0);
- }
-
- if (expr->slice.end) {
- gen_expression(ctx, expr->slice.end, &end);
- } else if (otype->storage == STORAGE_ARRAY) {
- constl(&end, otype->array.length);
- } else {
- pushc(ctx->current, "load length");
- constl(&temp, 8); // XXX: ARCH
- pushi(ctx->current, &offset, Q_ADD, &object, &temp, NULL);
- pushi(ctx->current, &end, Q_LOADL, &offset, NULL);
- }
-
- // TODO: Bounds check
- pushi(ctx->current, &dest, Q_COPY, out, NULL);
- if (otype->storage == STORAGE_SLICE) {
- pushc(ctx->current, "load array");
-
- gen_temp(ctx, &src, &qbe_long, "src.%d");
- pushi(ctx->current, &src, Q_LOADL, &object, NULL);
-
- pushc(ctx->current, "add offset");
- constl(&temp, otype->array.members->size);
- pushi(ctx->current, &offset, Q_MUL, &start, &temp, NULL);
- pushi(ctx->current, &offset, Q_ADD, &src, &offset, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
-
- pushc(ctx->current, "store length");
- constl(&temp, 8); // XXX: ARCH
- pushi(ctx->current, &offset, Q_SUB, &end, &start, NULL);
- pushi(ctx->current, &dest, Q_ADD, &dest, &temp, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
-
- pushc(ctx->current, "store capacity");
- pushi(ctx->current, &dest, Q_ADD, &dest, &temp, NULL);
- constl(&temp, 16);
- pushi(ctx->current, &object, Q_ADD, &object, &temp, NULL);
- pushi(ctx->current, &src, Q_LOADL, &object, NULL);
- pushi(ctx->current, &offset, Q_SUB, &src, &start, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
- } else {
- gen_temp(ctx, &src, &qbe_long, "length.%d");
-
- constl(&temp, otype->array.members->size);
- pushi(ctx->current, &offset, Q_MUL, &start, &temp, NULL);
- pushi(ctx->current, &offset, Q_ADD, &object, &offset, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
-
- pushi(ctx->current, &offset, Q_SUB, &end, &start, NULL);
-
- constl(&temp, 8); // XXX: ARCH
- pushc(ctx->current, "store length");
- pushi(ctx->current, &dest, Q_ADD, &dest, &temp, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
-
- pushc(ctx->current, "store capacity");
- pushi(ctx->current, &dest, Q_ADD, &dest, &temp, NULL);
-
- if (otype->array.length != SIZE_UNDEFINED) {
- constl(&temp, otype->array.length);
- pushi(ctx->current, &offset, Q_SUB, &temp, &start, NULL);
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
- }
- pushi(ctx->current, NULL, Q_STOREL, &offset, &dest, NULL);
- }
-}
-
-static void
-gen_expr_struct(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- // XXX: ARCH
- struct qbe_value base = {0}, ptr = {0}, offset = {0};
- gen_temp(ctx, &base, &qbe_long, "base.%d");
- gen_temp(ctx, &ptr, &qbe_long, "ptr.%d");
- pushi(ctx->current, &base, Q_COPY, out, NULL);
-
- if (expr->_struct.autofill) {
- struct qbe_value rtfunc = {0}, size = {0}, zero = {0};
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.memset");
- rtfunc.type = &qbe_long;
- constl(&size, expr->result->size);
- constl(&zero, 0);
- pushi(ctx->current, NULL, Q_CALL, &rtfunc,
- &base, &zero, &size, NULL);
- }
-
- const struct expr_struct_field *field = &expr->_struct.fields;
- while (field) {
- if (!field->value) {
- assert(expr->_struct.autofill);
- field = field->next;
- continue;
- }
-
- constl(&offset, field->field->offset);
- ptr.type = &qbe_long;
- pushi(ctx->current, &ptr, Q_ADD, &base, &offset, NULL);
-
- ptr.type = qtype_for_type(ctx, field->field->type, true);
- ptr.indirect = !type_is_aggregate(field->field->type);
- gen_expression(ctx, field->value, &ptr);
- field = field->next;
- }
-}
-
-static void
-gen_expr_switch(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- struct qbe_value sval = {0};
- if (type_is_aggregate(expr->_switch.value->result)) {
- alloc_temp(ctx, &sval, expr->_switch.value->result, "switch.%d");
- qval_deref(&sval);
- } else {
- gen_temp(ctx, &sval,
- qtype_for_type(ctx, expr->_switch.value->result, false),
- "switch.%d");
- }
- gen_expression(ctx, expr->_switch.value, &sval);
-
- struct qbe_value match = {0}, temp = {0};
- if (type_is_aggregate(expr->_switch.value->result)) {
- alloc_temp(ctx, &match, expr->_switch.value->result, "value.%d");
- qval_deref(&match);
- } else {
- gen_temp(ctx, &match,
- qtype_for_type(ctx, expr->_switch.value->result, false),
- "value.%d");
- }
- gen_temp(ctx, &temp, &qbe_word, "temp.%d");
-
- struct qbe_statement olabel = {0};
- struct qbe_value obranch = {0};
- obranch.kind = QV_LABEL;
- obranch.name = strdup(genl(&olabel, &ctx->id, "out.%d"));
-
- struct switch_case *_default = NULL;
- for (struct switch_case *_case = expr->_switch.cases;
- _case; _case = _case->next) {
- if (!_case->options) {
- _default = _case;
- continue;
- }
-
- struct qbe_statement tlabel = {0}, flabel = {0};
- struct qbe_value tbranch = {0}, fbranch = {0};
- tbranch.kind = QV_LABEL;
- tbranch.name = strdup(genl(&tlabel, &ctx->id, "match.%d"));
- fbranch.kind = QV_LABEL;
- fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d"));
-
- for (struct case_option *opt = _case->options;
- opt; opt = opt->next) {
- struct qbe_statement nlabel = {0};
- struct qbe_value nbranch = {0};
- nbranch.kind = QV_LABEL;
- nbranch.name = strdup(genl(&nlabel, &ctx->id, "next.opt.%d"));
-
- gen_expression(ctx, opt->value, &match);
-
- if (type_dealias(opt->value->result)->storage ==
- STORAGE_STRING) {
- struct qbe_value rtfunc = {0};
- rtfunc.kind = QV_GLOBAL;
- rtfunc.name = strdup("rt.strcmp");
- rtfunc.type = &qbe_long;
- pushi(ctx->current, &temp, Q_CALL,
- &rtfunc, &match, &sval, NULL);
- } else {
- pushi(ctx->current, &temp, binarithm_for_op(
- BIN_LEQUAL, sval.type, sval.type->is_signed),
- &match, &sval, NULL);
- }
-
- pushi(ctx->current, NULL, Q_JNZ,
- &temp, &tbranch, &nbranch, NULL);
- push(&ctx->current->body, &nlabel);
- }
-
- pushi(ctx->current, NULL, Q_JMP, &fbranch, NULL);
- push(&ctx->current->body, &tlabel);
- if (_case->value->terminates) {
- gen_expression(ctx, _case->value, NULL);
- } else {
- gen_expression(ctx, _case->value, out);
- pushi(ctx->current, NULL, Q_JMP, &obranch, NULL);
- }
- push(&ctx->current->body, &flabel);
- }
-
- if (_default && _default->value->terminates) {
- gen_expression(ctx, _default->value, NULL);
- } else if (_default) {
- gen_expression(ctx, _default->value, out);
- }
-
- push(&ctx->current->body, &olabel);
-}
-
-static void
-gen_expr_tuple(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- // XXX: ARCH
- struct qbe_value base = {0}, ptr = {0}, offset = {0};
- gen_temp(ctx, &base, &qbe_long, "base.%d");
- gen_temp(ctx, &ptr, &qbe_long, "ptr.%d");
- pushi(ctx->current, &base, Q_COPY, out, NULL);
-
- const struct type_tuple *type = &type_dealias(expr->result)->tuple;
- const struct expression_tuple *tuple = &expr->tuple;
- while (tuple) {
- constl(&offset, type->offset);
- ptr.type = &qbe_long;
- pushi(ctx->current, &ptr, Q_ADD, &base, &offset, NULL);
-
- ptr.type = qtype_for_type(ctx, type->type, true);
- ptr.indirect = !type_is_aggregate(type->type);
- gen_expression(ctx, tuple->value, &ptr);
-
- tuple = tuple->next;
- type = type->next;
- }
-}
-
-static void
-gen_expr_address(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- const struct expression *operand = expr->unarithm.operand;
- assert(operand->type == EXPR_ACCESS); // Invariant
-
- struct qbe_value src = {0};
- address_object(ctx, operand, &src);
- qval_address(&src);
- gen_store(ctx, out, &src);
-}
-
-static void
-gen_expr_unarithm(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- if (expr->unarithm.op == UN_ADDRESS) {
- gen_expr_address(ctx, expr, out);
- return;
- }
-
- struct expression *operand = expr->unarithm.operand;
- struct qbe_value op = {0}, res = {0};
- gen_temp(ctx, &op, qtype_for_type(ctx, operand->result, false), "operand.%d");
- gen_temp(ctx, &res, qtype_for_type(ctx, expr->result, false), "result.%d");
- gen_expression(ctx, expr->unarithm.operand, &op);
-
- struct qbe_value temp = {0};
- temp.kind = QV_CONST;
- temp.type = op.type;
- switch (expr->unarithm.op) {
- case UN_LNOT:
- temp.lval = 1;
- pushi(ctx->current, &res, Q_XOR, &temp, &op, NULL);
- break;
- case UN_BNOT:
- temp.lval = (uint64_t)-1;
- pushi(ctx->current, &res, Q_XOR, &temp, &op, NULL);
- break;
- case UN_MINUS:
- temp.lval = 0;
- pushi(ctx->current, &res, Q_SUB, &temp, &op, NULL);
- break;
- case UN_PLUS:
- res = op; // no-op
- break;
- case UN_DEREF:
- if (!type_is_aggregate(expr->result)) {
- qval_deref(&op);
- }
- gen_load(ctx, &res, &op, type_is_signed(expr->result));
- break;
- case UN_ADDRESS:
- assert(0); // Invariant
- }
-
- gen_store(ctx, out, &res);
-}
-
-static void
-gen_expression(struct gen_context *ctx,
- const struct expression *expr,
- const struct qbe_value *out)
-{
- switch (expr->type) {
- case EXPR_ACCESS:
- gen_expr_access(ctx, expr, out);
- break;
- case EXPR_ALLOC:
- gen_expr_alloc(ctx, expr, out);
- break;
- case EXPR_APPEND:
- gen_expr_append(ctx, expr, out);
- break;
- case EXPR_ASSERT:
- gen_expr_assert(ctx, expr, out);
- break;
- case EXPR_ASSIGN:
- gen_expr_assign(ctx, expr, out);
- break;
- case EXPR_BINARITHM:
- gen_expr_binarithm(ctx, expr, out);
- break;
- case EXPR_BINDING:
- gen_expr_binding(ctx, expr, out);
- break;
- case EXPR_BREAK:
- case EXPR_CONTINUE:
- gen_expr_control(ctx, expr, out);
- break;
- case EXPR_CALL:
- gen_expr_call(ctx, expr, out);
- break;
- case EXPR_CAST:
- gen_expr_cast(ctx, expr, out);
- break;
- case EXPR_CONSTANT:
- gen_expr_constant(ctx, expr, out);
- break;
- case EXPR_DEFER:
- gen_expr_defer(ctx, expr, out);
- break;
- case EXPR_DELETE:
- gen_expr_delete(ctx, expr, out);
- break;
- case EXPR_FOR:
- gen_expr_for(ctx, expr, out);
- break;
- case EXPR_FREE:
- gen_expr_free(ctx, expr, out);
- break;
- case EXPR_IF:
- gen_expr_if(ctx, expr, out);
- break;
- case EXPR_INSERT:
- gen_expr_insert(ctx, expr, out);
- break;
- case EXPR_LIST:
- gen_expr_list(ctx, expr, out);
- break;
- case EXPR_MATCH:
- gen_expr_match(ctx, expr, out);
- break;
- case EXPR_MEASURE:
- gen_expr_measure(ctx, expr, out);
- break;
- case EXPR_PROPAGATE:
- assert(0); // Lowered in check
- case EXPR_RETURN:
- gen_expr_return(ctx, expr, out);
- break;
- case EXPR_SLICE:
- gen_expr_slice(ctx, expr, out);
- break;
- case EXPR_STRUCT:
- gen_expr_struct(ctx, expr, out);
- break;
- case EXPR_SWITCH:
- gen_expr_switch(ctx, expr, out);
- break;
- case EXPR_TUPLE:
- gen_expr_tuple(ctx, expr, out);
- break;
- case EXPR_UNARITHM:
- gen_expr_unarithm(ctx, expr, out);
- break;
- }
-}
-
-static struct qbe_data_item *
-gen_data_item(struct gen_context *ctx, struct expression *expr,
- struct qbe_data_item *item)
-{
- assert(expr->type == EXPR_CONSTANT);
-
- struct qbe_def *def;
- const struct expression_constant *constant = &expr->constant;
- const struct type *type = type_dealias(expr->result);
- if (constant->object) {
- item->type = QD_SYMOFFS;
- item->sym = ident_to_sym(&constant->object->ident);
- item->offset = constant->ival;
- return item;
- }
-
- switch (type->storage) {
- case STORAGE_I8:
- case STORAGE_U8:
- case STORAGE_CHAR:
- item->type = QD_VALUE;
- constw(&item->value, (uint8_t)constant->uval);
- item->value.type = &qbe_byte;
- break;
- case STORAGE_I16:
- case STORAGE_U16:
- item->type = QD_VALUE;
- constw(&item->value, (uint16_t)constant->uval);
- item->value.type = &qbe_half;
- break;
- case STORAGE_BOOL:
- item->type = QD_VALUE;
- constw(&item->value, constant->bval ? 1 : 0);
- break;
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_INT:
- case STORAGE_UINT:
- case STORAGE_RUNE:
- item->type = QD_VALUE;
- constw(&item->value, (uint32_t)constant->uval);
- break;
- case STORAGE_U64:
- case STORAGE_I64:
- case STORAGE_SIZE:
- item->type = QD_VALUE;
- constl(&item->value, (uint64_t)constant->uval);
- break;
- case STORAGE_F32:
- item->type = QD_VALUE;
- consts(&item->value, (float)constant->fval);
- break;
- case STORAGE_F64:
- item->type = QD_VALUE;
- constd(&item->value, (double)constant->fval);
- break;
- case STORAGE_UINTPTR:
- case STORAGE_POINTER:
- assert(expr->type == EXPR_CONSTANT); // TODO?
- item->type = QD_VALUE;
- constl(&item->value, (uint64_t)constant->uval); // XXX: ARCH
- break;
- case STORAGE_ARRAY:
- assert(type->array.length != SIZE_UNDEFINED);
- size_t n = type->array.length;
- for (struct array_constant *c = constant->array;
- c && n; c = c->next ? c->next : c, --n) {
- item = gen_data_item(ctx, c->value, item);
- if (n > 1 || c->next) {
- item->next = xcalloc(1,
- sizeof(struct qbe_data_item));
- item = item->next;
- }
- }
- break;
- case STORAGE_STRING:
- def = xcalloc(1, sizeof(struct qbe_def));
- def->name = gen_name(ctx, "strdata.%d");
- def->kind = Q_DATA;
- def->data.items.type = QD_STRING;
- def->data.items.str = xcalloc(1, expr->constant.string.len);
- def->data.items.sz = expr->constant.string.len;
- memcpy(def->data.items.str, expr->constant.string.value,
- expr->constant.string.len);
-
- item->type = QD_VALUE;
- if (expr->constant.string.len != 0) {
- qbe_append_def(ctx->out, def);
- item->value.kind = QV_GLOBAL;
- item->value.type = &qbe_long;
- item->value.name = strdup(def->name);
- } else {
- free(def);
- constl(&item->value, 0);
- }
-
- item->next = xcalloc(1, sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_VALUE;
- constl(&item->value, expr->constant.string.len);
- item->next = xcalloc(1, sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_VALUE;
- constl(&item->value, expr->constant.string.len);
- break;
- case STORAGE_SLICE:
- def = xcalloc(1, sizeof(struct qbe_def));
- def->name = gen_name(ctx, "sldata.%d");
- def->kind = Q_DATA;
-
- size_t len = 0;
- struct qbe_data_item *subitem = &def->data.items;
- for (struct array_constant *c = constant->array;
- c; c = c->next) {
- gen_data_item(ctx, c->value, subitem);
- if (c->next) {
- subitem->next = xcalloc(1,
- sizeof(struct qbe_data_item));
- subitem = subitem->next;
- }
- ++len;
- }
-
- item->type = QD_VALUE;
- if (len != 0) {
- qbe_append_def(ctx->out, def);
- item->value.kind = QV_GLOBAL;
- item->value.type = &qbe_long;
- item->value.name = strdup(def->name);
- } else {
- free(def);
- constl(&item->value, 0);
- }
-
- item->next = xcalloc(1, sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_VALUE;
- constl(&item->value, len);
- item->next = xcalloc(1, sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_VALUE;
- constl(&item->value, len);
- break;
- case STORAGE_STRUCT:
- for (struct struct_constant *f = constant->_struct;
- f; f = f->next) {
- item = gen_data_item(ctx, f->value, item);
- if (f->next) {
- const struct struct_field *f1 = f->field;
- const struct struct_field *f2 = f->next->field;
- if (f2->offset != f1->offset + f1->type->size) {
- item->next = xcalloc(1,
- sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_ZEROED;
- item->zeroed = f2->offset -
- (f1->offset + f1->type->size);
- }
-
- item->next = xcalloc(1,
- sizeof(struct qbe_data_item));
- item = item->next;
- }
- }
- break;
- case STORAGE_ENUM:
- switch (type->_enum.storage) {
- case STORAGE_I8:
- case STORAGE_U8:
- item->type = QD_VALUE;
- constw(&item->value, (uint8_t)constant->uval);
- item->value.type = &qbe_byte;
- break;
- case STORAGE_I16:
- case STORAGE_U16:
- item->type = QD_VALUE;
- constw(&item->value, (uint16_t)constant->uval);
- item->value.type = &qbe_half;
- break;
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_INT:
- case STORAGE_UINT:
- item->type = QD_VALUE;
- constw(&item->value, (uint32_t)constant->uval);
- break;
- case STORAGE_U64:
- case STORAGE_I64:
- case STORAGE_SIZE:
- item->type = QD_VALUE;
- constl(&item->value, (uint64_t)constant->uval);
- break;
- default:
- assert(0);
- }
- break;
- case STORAGE_TUPLE:
- for (const struct tuple_constant *tuple = constant->tuple;
- tuple; tuple = tuple->next) {
- item = gen_data_item(ctx, tuple->value, item);
- if (tuple->next) {
- const struct type_tuple *f1 = tuple->field;
- const struct type_tuple *f2 = tuple->next->field;
- if (f2->offset != f1->offset + f1->type->size) {
- item->next = xcalloc(1,
- sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_ZEROED;
- item->zeroed = f2->offset -
- (f1->offset + f1->type->size);
- }
-
-
- item->next = xcalloc(1,
- sizeof(struct qbe_data_item));
- item = item->next;
- }
- }
- break;
- case STORAGE_TAGGED:
- case STORAGE_UNION:
- assert(0); // TODO
- case STORAGE_ALIAS:
- case STORAGE_FCONST:
- case STORAGE_FUNCTION:
- case STORAGE_ICONST:
- case STORAGE_NULL:
- case STORAGE_VOID:
- assert(0); // Invariant
- }
-
- assert(item->type != QD_VALUE || item->value.type);
- return item;
-}
static void
gen_function_decl(struct gen_context *ctx, const struct declaration *decl)
{
- assert(decl->type == DECL_FUNC);
- const struct function_decl *func = &decl->func;
- const struct type *fntype = func->type;
-
- if (func->body == NULL) {
- return; // Prototype
- }
-
- struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def));
- qdef->kind = Q_FUNC;
- qdef->exported = decl->exported;
- if (func->flags & FN_TEST) {
- qdef->name = gen_name(ctx, "testfunc.%d");
- } else {
- qdef->name = decl->symbol ? strdup(decl->symbol)
- : ident_to_sym(&decl->ident);
- }
- qdef->func.returns = qtype_for_type(ctx, fntype->func.result, false);
- ctx->current = &qdef->func;
-
- struct qbe_statement start_label = {0};
- genl(&start_label, &ctx->id, "start.%d");
- push(&qdef->func.prelude, &start_label);
-
- struct qbe_func_param *param, **next = &qdef->func.params;
- struct scope_object *obj = decl->func.scope->objects;
- while (obj) {
- param = *next = xcalloc(1, sizeof(struct qbe_func_param));
- assert(!obj->ident.ns); // Invariant
- param->name = strdup(obj->ident.name);
- param->type = qtype_for_type(ctx, obj->type, false);
-
- if (type_is_aggregate(obj->type)) {
- struct gen_binding *binding =
- xcalloc(1, sizeof(struct gen_binding));
- binding->name = strdup(param->name);
- binding->object = obj;
- binding->next = ctx->bindings;
- ctx->bindings = binding;
- } else {
- struct qbe_value val;
- binding_alloc(ctx, obj, &val, "param.%d");
- struct qbe_value src = {
- .kind = QV_TEMPORARY,
- .type = param->type,
- .name = param->name,
- };
- gen_store(ctx, &val, &src);
- }
-
- obj = obj->lnext;
- next = ¶m->next;
- }
-
- struct qbe_statement end_label = {0};
- struct qbe_value end_label_v = {
- .kind = QV_LABEL,
- .name = strdup(genl(&end_label, &ctx->id, "end.%d")),
- };
- ctx->end_label = &end_label_v;
-
- struct qbe_value rval = {0};
- if (fntype->func.result->storage != STORAGE_VOID) {
- alloc_temp(ctx, &rval, fntype->func.result, "ret.%d");
- if (type_is_aggregate(fntype->func.result)) {
- rval.indirect = false;
- }
- ctx->return_value = &rval;
- } else {
- ctx->return_value = NULL;
- }
-
- push_scope(ctx, SCOPE_FUNC, &end_label_v);
- pushl(&qdef->func, &ctx->id, "body.%d");
- gen_expression(ctx, func->body, ctx->return_value);
- gen_defers(ctx, ctx->scope);
- push(&qdef->func.body, &end_label);
- pop_scope(ctx);
-
- if (fntype->func.result->storage != STORAGE_VOID) {
- if (type_is_aggregate(fntype->func.result)) {
- pushi(&qdef->func, NULL, Q_RET, ctx->return_value, NULL);
- } else {
- struct qbe_value load = {0};
- gen_loadtemp(ctx, &load, ctx->return_value,
- qdef->func.returns,
- type_is_signed(fntype->func.result));
- pushi(&qdef->func, NULL, Q_RET, &load, NULL);
- }
- } else {
- pushi(&qdef->func, NULL, Q_RET, NULL);
- }
-
- // Free bindings
- struct gen_binding *binding = ctx->bindings;
- while (binding) {
- struct gen_binding *next = binding->next;
- free(binding->name);
- free(binding);
- binding = next;
- }
- ctx->bindings = NULL;
-
- qbe_append_def(ctx->out, qdef);
- ctx->current = NULL;
-
- if (func->flags & FN_INIT) {
- struct qbe_def *idef = xcalloc(1, sizeof(struct qbe_def));
- idef->kind = Q_DATA;
- int l = snprintf(NULL, 0, ".init.%s", qdef->name);
- idef->name = xcalloc(l + 1, 1);
- snprintf(idef->name, l + 1, ".init.%s", qdef->name);
- idef->data.align = 8;
- idef->data.section = strdup(".init_array");
- idef->data.items.type = QD_VALUE;
- idef->data.items.value.kind = QV_GLOBAL;
- idef->data.items.value.type = &qbe_long;
- idef->data.items.value.name = strdup(qdef->name);
- qbe_append_def(ctx->out, idef);
- }
-
- if (func->flags & FN_FINI) {
- struct qbe_def *fdef = xcalloc(1, sizeof(struct qbe_def));
- fdef->kind = Q_DATA;
- int l = snprintf(NULL, 0, ".fini.%s", qdef->name);
- fdef->name = xcalloc(l + 1, 1);
- snprintf(fdef->name, l + 1, ".fini.%s", qdef->name);
- fdef->data.align = 8;
- fdef->data.section = strdup(".fini_array");
- fdef->data.items.type = QD_VALUE;
- fdef->data.items.value.kind = QV_GLOBAL;
- fdef->data.items.value.type = &qbe_long;
- fdef->data.items.value.name = strdup(qdef->name);
- qbe_append_def(ctx->out, fdef);
- }
-
- if (func->flags & FN_TEST) {
- struct qbe_def *tdef = xcalloc(1, sizeof(struct qbe_def));
- tdef->kind = Q_DATA;
- int l = snprintf(NULL, 0, ".test.%s", qdef->name);
- tdef->name = xcalloc(l + 1, 1);
- snprintf(tdef->name, l + 1, ".test.%s", qdef->name);
- tdef->data.align = 8;
- tdef->data.section = strdup(".test_array");
- tdef->data.secflags = strdup("aw");
-
- struct qbe_data_item *item = &tdef->data.items;
- struct expression name = {
- .type = EXPR_CONSTANT,
- .result = &builtin_type_str,
- };
- name.constant.string.value = identifier_unparse(&decl->ident);
- name.constant.string.len = strlen(name.constant.string.value);
- item = gen_data_item(ctx, &name, item);
- item->next = xcalloc(1, sizeof(struct qbe_data_item));
- item = item->next;
- item->type = QD_VALUE;
- item->value.kind = QV_GLOBAL;
- item->value.type = &qbe_long;
- item->value.name = strdup(qdef->name);
- qbe_append_def(ctx->out, tdef);
- }
-}
-
-static void
-gen_global_decl(struct gen_context *ctx, const struct declaration *decl)
-{
- assert(decl->type == DECL_GLOBAL);
- const struct global_decl *global = &decl->global;
- struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def));
- qdef->kind = Q_DATA;
- qdef->exported = decl->exported;
- qdef->name = ident_to_sym(&decl->ident);
- gen_data_item(ctx, global->value, &qdef->data.items);
- qbe_append_def(ctx->out, qdef);
+ assert(0); // TODO
}
static void
gen_decl(struct gen_context *ctx, const struct declaration *decl)
{
switch (decl->type) {
- case DECL_CONST:
- // Nothing to do
- break;
case DECL_FUNC:
gen_function_decl(ctx, decl);
break;
case DECL_GLOBAL:
- gen_global_decl(ctx, decl);
- break;
+ assert(0); // TODO
+ case DECL_CONST:
case DECL_TYPE:
- // Nothing to do
- break;
+ break; // Nothing to do
}
}
diff --git a/src/qinstr.c b/src/qinstr.c
@@ -1,185 +1,2 @@
-#include <assert.h>
-#include "expr.h"
-#include "gen.h"
-#include "qbe.h"
-
-enum qbe_instr
-alloc_for_align(size_t align)
-{
- switch (align) {
- case 1:
- case 2:
- case 4:
- return Q_ALLOC4;
- case 8:
- return Q_ALLOC8;
- default:
- return Q_ALLOC16;
- }
-}
-
-enum qbe_instr
-store_for_type(enum qbe_stype stype)
-{
- switch (stype) {
- case Q_BYTE:
- return Q_STOREB;
- case Q_HALF:
- return Q_STOREH;
- case Q_WORD:
- return Q_STOREW;
- case Q_LONG:
- return Q_STOREL;
- case Q_SINGLE:
- return Q_STORES;
- case Q_DOUBLE:
- return Q_STORED;
- case Q__VOID:
- case Q__AGGREGATE:
- assert(0); // Invariant
- }
- assert(0);
-}
-
-enum qbe_instr
-load_for_type(enum qbe_stype stype, bool is_signed)
-{
- switch (stype) {
- case Q_BYTE:
- return is_signed ? Q_LOADSB : Q_LOADUB;
- case Q_HALF:
- return is_signed ? Q_LOADSH : Q_LOADUH;
- case Q_WORD:
- return is_signed ? Q_LOADSW : Q_LOADUW;
- case Q_LONG:
- return Q_LOADL;
- case Q_SINGLE:
- return Q_LOADS;
- case Q_DOUBLE:
- return Q_LOADD;
- case Q__AGGREGATE:
- return Q_COPY;
- case Q__VOID:
- assert(0); // Invariant
- }
- assert(0);
-}
-
-enum qbe_instr
-binarithm_for_op(enum binarithm_operator op,
- const struct qbe_type *type, bool is_signed)
-{
- // TODO: NaN, udiv et al
- enum qbe_stype stype = type->stype;
- assert(stype != Q__AGGREGATE && stype != Q__VOID);
- switch (op) {
- case BIN_PLUS:
- return Q_ADD;
- case BIN_BAND:
- return Q_AND;
- case BIN_DIV:
- return is_signed ? Q_DIV : Q_UDIV;
- case BIN_MINUS:
- return Q_SUB;
- case BIN_TIMES:
- return Q_MUL;
- case BIN_MODULO:
- return is_signed ? Q_REM : Q_UREM;
- case BIN_BOR:
- return Q_OR;
- case BIN_BXOR:
- return Q_XOR;
- case BIN_LSHIFT:
- return Q_SHL;
- case BIN_RSHIFT:
- return is_signed ? Q_SAR : Q_SHR;
- case BIN_LEQUAL:
- switch (stype) {
- case Q_WORD:
- return Q_CEQW;
- case Q_LONG:
- return Q_CEQL;
- case Q_SINGLE:
- return Q_CEQS;
- case Q_DOUBLE:
- return Q_CEQD;
- default:
- assert(0);
- }
- break;
- case BIN_NEQUAL:
- case BIN_LXOR:
- switch (stype) {
- case Q_WORD:
- return Q_CNEW;
- case Q_LONG:
- return Q_CNEL;
- case Q_SINGLE:
- return Q_CNES;
- case Q_DOUBLE:
- return Q_CNED;
- default:
- assert(0);
- }
- break;
- case BIN_GREATER:
- switch (stype) {
- case Q_WORD:
- return is_signed ? Q_CSGTW : Q_CUGTW;
- case Q_LONG:
- return is_signed ? Q_CSGTL : Q_CUGTL;
- case Q_SINGLE:
- return Q_CGTS;
- case Q_DOUBLE:
- return Q_CGTD;
- default:
- assert(0);
- }
- case BIN_GREATEREQ:
- switch (stype) {
- case Q_WORD:
- return is_signed ? Q_CSGEW : Q_CUGEW;
- case Q_LONG:
- return is_signed ? Q_CSGEL : Q_CUGEL;
- case Q_SINGLE:
- return Q_CGES;
- case Q_DOUBLE:
- return Q_CGED;
- default:
- assert(0);
- }
- break;
- case BIN_LESS:
- switch (stype) {
- case Q_WORD:
- return is_signed ? Q_CSLTW : Q_CULTW;
- case Q_LONG:
- return is_signed ? Q_CSLTL : Q_CULTL;
- case Q_SINGLE:
- return Q_CLTS;
- case Q_DOUBLE:
- return Q_CLTD;
- default:
- assert(0);
- }
- break;
- case BIN_LESSEQ:
- switch (stype) {
- case Q_WORD:
- return is_signed ? Q_CSLEW : Q_CULEW;
- case Q_LONG:
- return is_signed ? Q_CSLEL : Q_CULEL;
- case Q_SINGLE:
- return Q_CLES;
- case Q_DOUBLE:
- return Q_CLED;
- default:
- assert(0);
- }
- break;
- case BIN_LAND:
- case BIN_LOR:
- assert(0); // Handled elsewhere to address short circuiting
- }
- assert(0); // Unreachable
+void placeholder_qinstr() {
}
diff --git a/src/qtype.c b/src/qtype.c
@@ -1,385 +1,2 @@
-#include <assert.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "gen.h"
-#include "qbe.h"
-#include "type_store.h"
-#include "types.h"
-#include "util.h"
-
-enum qbe_stype
-qstype_for_type(const struct type *type)
-{
- switch (type->storage) {
- case STORAGE_CHAR:
- case STORAGE_I8:
- case STORAGE_U8:
- // Implemented as Q_WORD
- case STORAGE_I16:
- case STORAGE_U16:
- // Implemented as Q_WORD
- case STORAGE_BOOL:
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_RUNE:
- case STORAGE_INT: // XXX: Architecture dependent
- case STORAGE_UINT: // XXX: Architecture dependent
- return Q_WORD;
- case STORAGE_I64:
- case STORAGE_U64:
- case STORAGE_SIZE:
- case STORAGE_UINTPTR: // XXX: Architecture dependent
- case STORAGE_POINTER: // XXX: Architecture dependent
- case STORAGE_NULL: // XXX: Architecture dependent
- return Q_LONG;
- case STORAGE_F32:
- return Q_SINGLE;
- case STORAGE_F64:
- return Q_DOUBLE;
- case STORAGE_VOID:
- return Q__VOID;
- case STORAGE_ALIAS:
- case STORAGE_ENUM:
- return qstype_for_type(builtin_type_for_storage(type->_enum.storage, true));
- case STORAGE_ARRAY:
- case STORAGE_FCONST:
- case STORAGE_ICONST:
- case STORAGE_SLICE:
- case STORAGE_STRING:
- case STORAGE_STRUCT:
- case STORAGE_TAGGED:
- case STORAGE_TUPLE:
- case STORAGE_UNION:
- case STORAGE_FUNCTION:
- assert(0); // Invariant
- }
- assert(0);
-}
-
-enum qbe_stype
-qxtype_for_type(const struct type *type)
-{
- switch (type->storage) {
- case STORAGE_CHAR:
- case STORAGE_I8:
- case STORAGE_U8:
- return Q_BYTE;
- case STORAGE_I16:
- case STORAGE_U16:
- return Q_HALF;
- case STORAGE_BOOL:
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_RUNE:
- case STORAGE_INT: // XXX: Architecture dependent
- case STORAGE_UINT: // XXX: Architecture dependent
- case STORAGE_I64:
- case STORAGE_U64:
- case STORAGE_SIZE:
- case STORAGE_UINTPTR: // XXX: Architecture dependent
- case STORAGE_POINTER: // XXX: Architecture dependent
- case STORAGE_NULL: // XXX: Architecture dependent
- case STORAGE_F32:
- case STORAGE_F64:
- case STORAGE_VOID:
- case STORAGE_ALIAS:
- case STORAGE_ARRAY:
- case STORAGE_SLICE:
- case STORAGE_STRING:
- case STORAGE_STRUCT:
- case STORAGE_TAGGED:
- case STORAGE_TUPLE:
- case STORAGE_UNION:
- case STORAGE_FUNCTION:
- return qstype_for_type(type);
- case STORAGE_ENUM:
- return qxtype_for_type(builtin_type_for_storage(type->_enum.storage, true));
- case STORAGE_FCONST:
- case STORAGE_ICONST:
- assert(0); // Lowered in check
- }
- assert(0);
-}
-
-static const struct qbe_type *
-tagged_qtype(struct gen_context *ctx, const struct type *type)
-{
- int n = snprintf(NULL, 0, "tags.%zd", ctx->id);
- char *name = xcalloc(1, n + 1);
- snprintf(name, n + 1, "tags.%zd", ctx->id);
- ++ctx->id;
-
- struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def));
- def->kind = Q_TYPE;
- def->name = name;
- def->exported = false;
- def->type.stype = Q__AGGREGATE;
- def->type.base = NULL;
- def->type.name = name;
- def->type.align = type->align;
- def->type.is_union = true;
- def->type.size = type->size - type->align;
-
- struct qbe_field *field = &def->type.fields;
- struct qbe_field **next = &field->next;
- for (const struct type_tagged_union *tu = &type->tagged;
- tu; tu = tu->next) {
- if (tu->type->size == 0) {
- if (!tu->next && *next) {
- free(*next);
- *next = NULL;
- }
- continue;
- }
- field->type = qtype_for_type(ctx, tu->type, true);
- field->count = 1;
- if (tu->next) {
- field->next = xcalloc(1, sizeof(struct qbe_field));
- next = &field->next;
- field = field->next;
- }
- }
-
- qbe_append_def(ctx->out, def);
- return &def->type;
-}
-
-static int
-sf_compar(const void *_a, const void *_b)
-{
- const struct struct_field **a = (const struct struct_field **)_a;
- const struct struct_field **b = (const struct struct_field **)_b;
- return (int)(*a)->offset - (int)(*b)->offset;
-}
-
-static const struct qbe_type *
-lookup_aggregate(struct gen_context *ctx, const struct type *type)
-{
- for (struct qbe_def *def = ctx->out->defs; def; def = def->next) {
- if (def->kind == Q_TYPE && def->type.base == type) {
- return &def->type;
- }
- }
-
- if (type->storage == STORAGE_ARRAY && type->array.length == SIZE_UNDEFINED) {
- // Special case
- return &qbe_long;
- }
-
- int n = snprintf(NULL, 0, "type.%zd", ctx->id);
- char *name = xcalloc(1, n + 1);
- snprintf(name, n + 1, "type.%zd", ctx->id);
- ++ctx->id;
-
- struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def));
- def->kind = Q_TYPE;
- def->name = name;
- def->type.stype = Q__AGGREGATE;
- def->type.base = type;
- def->type.name = name;
- def->type.align = type->align;
- def->type.size = type->size;
-
- struct qbe_field *field = &def->type.fields;
- switch (type->storage) {
- case STORAGE_ARRAY:
- assert(type->array.length != SIZE_UNDEFINED);
- def->type.align = type->align;
- field->type = qtype_for_type(ctx, type->array.members, true);
- field->count = type->array.length;
- break;
- case STORAGE_STRING:
- field->type = &qbe_long; // XXX: ARCH
- field->count = 3;
- break;
- case STORAGE_STRUCT:
- case STORAGE_UNION:
- if (!type->struct_union.c_compat) {
- def->type.align = type->align;
- field->type = NULL;
- field->count = type->size;
- break;
- }
- size_t n = 0;
- for (struct struct_field *tfield = type->struct_union.fields;
- tfield; tfield = tfield->next) {
- ++n;
- }
- struct struct_field **tfields =
- xcalloc(sizeof(struct struct_field *), n);
- size_t i = 0;
- for (struct struct_field *tfield = type->struct_union.fields;
- tfield; tfield = tfield->next, ++i) {
- tfields[i] = tfield;
- }
- qsort(tfields, n, sizeof(struct struct_field *), sf_compar);
- for (size_t i = 0; i < n; ++i) {
- struct struct_field *tfield = tfields[i];
- field->type = qtype_for_type(ctx, tfield->type, true);
- field->count = 1;
-
- if (i + 1 < n) {
- field->next = xcalloc(1, sizeof(struct qbe_field));
- field = field->next;
- }
- }
- free(tfields);
- break;
- case STORAGE_SLICE:
- field->type = &qbe_long; // XXX: ARCH
- field->count = 3;
- break;
- case STORAGE_TAGGED:
- def->type.align = type->align;
- field->type = &qbe_word; // XXX: ARCH
- field->count = 1;
- if (type->size != builtin_type_uint.size) {
- field->next = xcalloc(1, sizeof(struct qbe_field));
- field = field->next;
- field->type = tagged_qtype(ctx, type);
- field->count = 1;
- }
- break;
- case STORAGE_TUPLE:
- def->type.align = type->align;
- for (const struct type_tuple *tuple = &type->tuple;
- tuple; tuple = tuple->next) {
- field->type = qtype_for_type(ctx, tuple->type, true);
- field->count = 1;
-
- if (tuple->next) {
- field->next = xcalloc(1, sizeof(struct qbe_field));
- field = field->next;
- }
- }
- break;
- case STORAGE_ENUM:
- case STORAGE_ALIAS:
- case STORAGE_CHAR:
- case STORAGE_I8:
- case STORAGE_U8:
- case STORAGE_I16:
- case STORAGE_U16:
- case STORAGE_BOOL:
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_RUNE:
- case STORAGE_INT:
- case STORAGE_UINT:
- case STORAGE_I64:
- case STORAGE_U64:
- case STORAGE_ICONST:
- case STORAGE_SIZE:
- case STORAGE_UINTPTR:
- case STORAGE_POINTER:
- case STORAGE_NULL:
- case STORAGE_F32:
- case STORAGE_F64:
- case STORAGE_FCONST:
- case STORAGE_VOID:
- case STORAGE_FUNCTION:
- assert(0); // Invariant
- }
-
- qbe_append_def(ctx->out, def);
- return &def->type;
-}
-
-const struct qbe_type *
-qtype_for_type(struct gen_context *ctx, const struct type *type, bool extended)
-{
- switch (type->storage) {
- case STORAGE_CHAR:
- case STORAGE_I8:
- case STORAGE_U8:
- case STORAGE_I16:
- case STORAGE_U16:
- if (extended) {
- return qtype_for_xtype(
- qxtype_for_type(type),
- type_is_signed(type));
- }
- // Fallthrough
- case STORAGE_BOOL:
- case STORAGE_I32:
- case STORAGE_U32:
- case STORAGE_RUNE:
- case STORAGE_INT:
- case STORAGE_UINT:
- case STORAGE_I64:
- case STORAGE_U64:
- case STORAGE_SIZE:
- case STORAGE_UINTPTR:
- case STORAGE_POINTER:
- case STORAGE_NULL:
- case STORAGE_F32:
- case STORAGE_F64:
- case STORAGE_VOID:
- return qtype_for_xtype(qstype_for_type(type), extended);
- case STORAGE_ENUM:
- return qtype_for_type(ctx,
- builtin_type_for_storage(type->_enum.storage, true),
- extended);
- case STORAGE_ARRAY:
- case STORAGE_SLICE:
- case STORAGE_STRING:
- case STORAGE_STRUCT:
- case STORAGE_TAGGED:
- case STORAGE_TUPLE:
- case STORAGE_UNION:
- return lookup_aggregate(ctx, type);
- case STORAGE_FUNCTION:
- return qtype_for_xtype(Q__AGGREGATE, extended);
- case STORAGE_ALIAS:
- return qtype_for_type(ctx, type->alias.type, extended);
- case STORAGE_FCONST:
- case STORAGE_ICONST:
- assert(0); // Lowered in check
- }
- assert(0); // Unreachable
-}
-
-bool
-type_is_aggregate(const struct type *type)
-{
- switch (type->storage) {
- case STORAGE_BOOL:
- case STORAGE_CHAR:
- case STORAGE_ENUM:
- case STORAGE_F32:
- case STORAGE_F64:
- case STORAGE_I16:
- case STORAGE_I32:
- case STORAGE_I64:
- case STORAGE_I8:
- case STORAGE_INT:
- case STORAGE_POINTER:
- case STORAGE_NULL:
- case STORAGE_RUNE:
- case STORAGE_SIZE:
- case STORAGE_U16:
- case STORAGE_U32:
- case STORAGE_U64:
- case STORAGE_U8:
- case STORAGE_UINT:
- case STORAGE_UINTPTR:
- case STORAGE_VOID:
- return false;
- case STORAGE_ALIAS:
- return type_is_aggregate(type->alias.type);
- case STORAGE_ARRAY:
- case STORAGE_SLICE:
- case STORAGE_STRING:
- case STORAGE_STRUCT:
- case STORAGE_TAGGED:
- case STORAGE_TUPLE:
- case STORAGE_UNION:
- case STORAGE_FUNCTION:
- return true;
- case STORAGE_FCONST:
- case STORAGE_ICONST:
- assert(0); // Lowered in check
- }
- assert(0); // Unreachable
+void placeholder_qtype() {
}