diff options
Diffstat (limited to 'src/llvm_backend_stmt.cpp')
| -rw-r--r-- | src/llvm_backend_stmt.cpp | 112 |
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); |