aboutsummaryrefslogtreecommitdiff
path: root/src/llvm_backend_stmt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm_backend_stmt.cpp')
-rw-r--r--src/llvm_backend_stmt.cpp112
1 files changed, 104 insertions, 8 deletions
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 2284649e2..ec6ac5886 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1397,6 +1397,52 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases);
}
+ bool all_by_reference = false;
+ for (Ast *clause : body->stmts) {
+ ast_node(cc, CaseClause, clause);
+ if (cc->list.count != 1) {
+ continue;
+ }
+ Entity *case_entity = implicit_entity_of_node(clause);
+ all_by_reference |= (case_entity->flags & EntityFlag_Value) == 0;
+ break;
+ }
+
+ // NOTE(bill, 2023-02-17): In the case of a pass by value, the value does need to be copied
+ // to prevent errors such as these:
+ //
+ // switch v in some_union {
+ // case i32:
+ // fmt.println(v) // 'i32'
+ // some_union = f32(123)
+ // fmt.println(v) // if `v` is an implicit reference, then the data is now completely corrupted
+ // case f32:
+ // fmt.println(v)
+ // }
+ //
+ lbAddr backing_data = {};
+ if (!all_by_reference) {
+ bool variants_found = false;
+ i64 max_size = 0;
+ i64 max_align = 1;
+ for (Ast *clause : body->stmts) {
+ ast_node(cc, CaseClause, clause);
+ if (cc->list.count != 1) {
+ continue;
+ }
+ Entity *case_entity = implicit_entity_of_node(clause);
+ max_size = gb_max(max_size, type_size_of(case_entity->type));
+ max_align = gb_max(max_align, type_align_of(case_entity->type));
+ variants_found = true;
+ }
+ if (variants_found) {
+ Type *t = alloc_type_array(t_u8, max_size);
+ backing_data = lb_add_local(p, t, nullptr, false, true);
+ GB_ASSERT(lb_try_update_alignment(backing_data.addr, cast(unsigned)max_align));
+ }
+ }
+ lbValue backing_ptr = backing_data.addr;
+
for (Ast *clause : body->stmts) {
ast_node(cc, CaseClause, clause);
lb_open_scope(p, cc->scope);
@@ -1427,8 +1473,6 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
Entity *case_entity = implicit_entity_of_node(clause);
- lbValue value = parent_value;
-
lb_start_block(p, body);
bool by_reference = (case_entity->flags & EntityFlag_Value) == 0;
@@ -1444,13 +1488,29 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
Type *ct = case_entity->type;
Type *ct_ptr = alloc_type_pointer(ct);
- value = lb_emit_conv(p, data, ct_ptr);
- if (!by_reference) {
- value = lb_emit_load(p, value);
+ lbValue ptr = {};
+
+ if (backing_data.addr.value) { // by value
+ GB_ASSERT(!by_reference);
+ // make a copy of the case value
+ lb_mem_copy_non_overlapping(p,
+ backing_ptr, // dst
+ data, // src
+ lb_const_int(p->module, t_int, type_size_of(case_entity->type)));
+ ptr = lb_emit_conv(p, backing_ptr, ct_ptr);
+
+ } else { // by reference
+ GB_ASSERT(by_reference);
+ ptr = lb_emit_conv(p, data, ct_ptr);
}
+ GB_ASSERT(are_types_identical(case_entity->type, type_deref(ptr.type)));
+ lb_add_entity(p->module, case_entity, ptr);
+ lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
+ } else {
+ // TODO(bill): is the correct expected behaviour?
+ lb_store_type_case_implicit(p, clause, parent_value);
}
- lb_store_type_case_implicit(p, clause, value);
lb_type_case_body(p, ss->label, clause, body, done);
}
@@ -1523,7 +1583,8 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
lb_add_member(p->module, mangled_name, global_val);
}
}
-gb_internal void lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_values, lbValue src_value) {
+gb_internal isize lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_values, lbValue src_value) {
+ isize init_count = dst_values->count;
Type *t = src_value.type;
if (t->kind == Type_Tuple) {
lbTupleFix *tf = map_get(&p->tuple_fix_map, src_value.value);
@@ -1540,6 +1601,7 @@ gb_internal void lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_valu
} else {
array_add(dst_values, src_value);
}
+ return dst_values->count - init_count;
}
@@ -2218,7 +2280,41 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
}
array_add(&lvals, lval);
}
- lb_build_assignment(p, lvals, vd->values);
+
+ auto const &values = vd->values;
+ if (values.count > 0) {
+ auto inits = array_make<lbValue>(permanent_allocator(), 0, lvals.count);
+
+ isize lval_index = 0;
+ for (Ast *rhs : values) {
+ rhs = unparen_expr(rhs);
+ lbValue init = lb_build_expr(p, rhs);
+ #if 1
+ // NOTE(bill, 2023-02-17): lb_const_value might produce a stack local variable for the
+ // compound literal, so reusing that variable should minimize the stack wastage
+ if (rhs->kind == Ast_CompoundLit) {
+ lbAddr *comp_lit_addr = map_get(&p->module->exact_value_compound_literal_addr_map, rhs);
+ if (comp_lit_addr) {
+ Entity *e = entity_of_node(vd->names[lval_index]);
+ if (e) {
+ lb_add_entity(p->module, e, comp_lit_addr->addr);
+ lvals[lval_index] = {}; // do nothing so that nothing will assign to it
+ }
+ }
+ }
+ #endif
+
+ lval_index += lb_append_tuple_values(p, &inits, init);
+ }
+ GB_ASSERT(lval_index == lvals.count);
+
+ GB_ASSERT(lvals.count == inits.count);
+ for_array(i, inits) {
+ lbAddr lval = lvals[i];
+ lbValue init = inits[i];
+ lb_addr_store(p, lval, init);
+ }
+ }
case_end;
case_ast_node(as, AssignStmt, node);