harec

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

commit 596f28ad26b2d6e1a9da2a57ad9bf027fb883585
parent 10a990f1934a8f7c02636be388e3e55fae2cada3
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 13 Feb 2021 10:46:59 -0500

gen: convert between alignments on match binding

Diffstat:
Msrc/gen.c | 80++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mtests/18-match.ha | 12++++++++++++
2 files changed, 73 insertions(+), 19 deletions(-)

diff --git a/src/gen.c b/src/gen.c @@ -1124,17 +1124,14 @@ gen_expr_type_test(struct gen_context *ctx, static void gen_expr_type_assertion(struct gen_context *ctx, const struct expression *expr, + struct qbe_value *in, const struct qbe_value *out) { // XXX: ARCH - const struct type *want = type_dealias(expr->cast.secondary), - *tagged = type_dealias(expr->cast.value->result); - struct qbe_value tag = {0}, in = {0}, id = {0}, result = {0}; + const struct type *want = type_dealias(expr->cast.secondary); + struct qbe_value tag = {0}, id = {0}, result = {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); + 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); @@ -1172,7 +1169,6 @@ gen_expr_cast(struct gen_context *ctx, case C_CAST: break; // Handled below case C_ASSERTION: - gen_expr_type_assertion(ctx, expr, out); break; // Handled below case C_TEST: gen_expr_type_test(ctx, expr, out); @@ -1230,6 +1226,10 @@ gen_expr_cast(struct gen_context *ctx, } gen_expression(ctx, expr->cast.value, &in); + if (expr->cast.kind == C_ASSERTION) { + gen_expr_type_assertion(ctx, expr, &in, out); + } + // Used for various casts enum qbe_instr op; struct qbe_value ptr = {0}, offs = {0}, len = {0}; @@ -1770,7 +1770,15 @@ gen_match_tagged(struct gen_context *ctx, fbranch.kind = QV_LABEL; fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d")); - bool reinterpret = false; + 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 == TYPE_STORAGE_TAGGED); assert(tagged_subset_compat(mtype, _case->type)); @@ -1779,7 +1787,16 @@ gen_match_tagged(struct gen_context *ctx, // 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. - reinterpret = true; + // + // 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 { @@ -1790,22 +1807,47 @@ gen_match_tagged(struct gen_context *ctx, push(&ctx->current->body, &tlabel); if (_case->object) { struct qbe_value val = {0}, temp = {0}; - gen_temp(ctx, &val, - qtype_for_type(ctx, _case->type, false), - "bound.%d"); + 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; - constl(&temp, mtype->align); - val.type = &qbe_long; - if (!reinterpret) { - pushi(ctx->current, &val, Q_ADD, - &mval, &temp, NULL); - } else { + 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; } } diff --git a/tests/18-match.ha b/tests/18-match.ha @@ -181,6 +181,17 @@ export fn numeric() void = { assert(visit); }; +type align_4 = (void | int); +type align_8 = (void | int | i64); + +export fn alignment_conversion() void = { + let x: align_8 = 1234i; + match (x) { + y: align_4 => assert(y as int == 1234), + * => abort(), + }; +}; + export fn main() void = { tagged(); termination(); @@ -191,5 +202,6 @@ export fn main() void = { implicit_cast(); transitivity(); numeric(); + alignment_conversion(); // TODO: Test exhaustiveness and dupe detection };