From 8a2a70a3c2a7ac14687d100325e12b8d3d02256e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 17 Feb 2023 13:00:37 +0000 Subject: Fix overriding procedure information for literals --- src/llvm_backend_const.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/llvm_backend_const.cpp') diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index ee564bbf1..fc1598024 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -411,7 +411,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo Ast *expr = unparen_expr(value.value_procedure); if (expr->kind == Ast_ProcLit) { res = lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr); - } else { Entity *e = entity_from_expr(expr); res = lb_find_procedure_value_from_entity(m, e); -- cgit v1.2.3 From 99460c9e3293738607764d2c3d0cecddd7576e01 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 17 Feb 2023 14:26:22 +0000 Subject: Minimize stack wastage with compound literals defining variables --- src/llvm_backend.hpp | 2 ++ src/llvm_backend_const.cpp | 4 ++++ src/llvm_backend_general.cpp | 1 + src/llvm_backend_stmt.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 45 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend_const.cpp') diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 0b3bc2a5a..4a78ca3a9 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -182,6 +182,8 @@ struct lbModule { PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info + PtrMap exact_value_compound_literal_addr_map; // Key: Ast_CompoundLit + LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT]; }; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index fc1598024..72c2a0495 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -460,6 +460,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, ""); LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true); lbAddr slice = lb_add_local_generated(p, type, false); + map_set(&m->exact_value_compound_literal_addr_map, value.value_compound, slice); + lb_fill_slice(p, slice, {ptr, alloc_type_pointer(elem)}, {len, t_int}); return lb_addr_load(p, slice); } @@ -1042,6 +1044,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT(is_local); lbProcedure *p = m->curr_procedure; lbAddr v = lb_add_local_generated(p, res.type, true); + map_set(&m->exact_value_compound_literal_addr_map, value.value_compound, v); + LLVMBuildStore(p->builder, constant_value, v.addr.value); for (isize i = 0; i < value_count; i++) { LLVMValueRef val = old_values[i]; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 246613724..e398873ee 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -82,6 +82,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { map_init(&m->map_info_map, 0); map_init(&m->map_cell_info_map, 0); + map_init(&m->exact_value_compound_literal_addr_map, 1024); } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 2284649e2..fad74e9eb 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1523,7 +1523,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 *dst_values, lbValue src_value) { +gb_internal isize lb_append_tuple_values(lbProcedure *p, Array *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 +1541,7 @@ gb_internal void lb_append_tuple_values(lbProcedure *p, Array *dst_valu } else { array_add(dst_values, src_value); } + return dst_values->count - init_count; } @@ -2218,7 +2220,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(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); -- cgit v1.2.3 From a9182cfd8cf9dd8cec125221e39c0fcd7a3fd3e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 26 Feb 2023 13:26:35 +0000 Subject: Allow compound literals to access fields through `using` --- src/check_expr.cpp | 205 +++++++++++++++++++++++++------------------ src/common.cpp | 5 ++ src/error.cpp | 4 +- src/llvm_backend_const.cpp | 77 +++++++++++++++- src/llvm_backend_expr.cpp | 20 ++++- src/llvm_backend_utility.cpp | 2 +- src/ptr_set.cpp | 2 +- src/types.cpp | 1 - 8 files changed, 219 insertions(+), 97 deletions(-) (limited to 'src/llvm_backend_const.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 00d394966..b9588a798 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7890,6 +7890,124 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no return Expr_Expr; } + +gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice const &elems, Operand *o, Type *type, bool &is_constant) { + Type *bt = base_type(type); + + StringSet fields_visited = {}; + defer (string_set_destroy(&fields_visited)); + + StringMap fields_visited_through_raw_union = {}; + defer (string_map_destroy(&fields_visited_through_raw_union)); + + for (Ast *elem : elems) { + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in structure literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; + + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + bool is_unknown = sel.entity == nullptr; + if (is_unknown) { + error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name)); + continue; + } + + Entity *field = bt->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); + if (string_set_update(&fields_visited, name)) { + if (sel.index.count > 1) { + if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { + error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + } else { + error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string)); + } + } else { + error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string)); + } + continue; + } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { + error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + continue; + } + if (sel.indirect) { + error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name)); + continue; + } + + if (sel.index.count > 1) { + if (is_constant) { + Type *ft = type; + for (i32 index : sel.index) { + Type *bt = base_type(ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + is_constant = false; + break; + } + ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + ft = bt->Array.elem; + break; + default: + GB_PANIC("invalid type: %s", type_to_string(ft)); + break; + } + } + if (is_constant && + (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) { + is_constant = false; + } + } + + Type *nested_ft = bt; + for (i32 index : sel.index) { + Type *bt = base_type(nested_ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + for (Entity *re : bt->Struct.fields) { + string_map_set(&fields_visited_through_raw_union, re->token.string, sel.entity->token.string); + } + } + nested_ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + nested_ft = bt->Array.elem; + break; + default: + GB_PANIC("invalid type %s", type_to_string(nested_ft)); + break; + } + } + field = sel.entity; + } + + + Operand o = {}; + check_expr_or_type(c, &o, fv->value, field->type); + + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); @@ -7977,45 +8095,13 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); gb_string_free(type_str); } else { - Ast *elem = cl->elems[0]; - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - break; - } - - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - break; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - break; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - - check_assignment(c, &o, field->type, str_lit("structure literal")); + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); } - } } break; } - isize field_count = t->Struct.fields.count; isize min_field_count = t->Struct.fields.count; for (isize i = min_field_count-1; i >= 0; i--) { @@ -8029,58 +8115,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (cl->elems[0]->kind == Ast_FieldValue) { - TEMPORARY_ALLOCATOR_GUARD(); - - bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); - - for (Ast *elem : cl->elems) { - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - continue; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - continue; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - if (fields_visited[sel.index[0]]) { - error(elem, "Duplicate field '%.*s' in structure literal", LIT(name)); - continue; - } - - fields_visited[sel.index[0]] = true; - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } - - check_assignment(c, &o, field->type, str_lit("structure literal")); - } + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); } else { bool seen_field_value = false; diff --git a/src/common.cpp b/src/common.cpp index 859aa4a56..90632def3 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -53,6 +53,11 @@ struct TypeIsPointer { enum {value = true}; }; +template struct TypeIsPtrSizedInteger { enum {value = false}; }; +template <> struct TypeIsPtrSizedInteger { enum {value = true}; }; +template <> struct TypeIsPtrSizedInteger { enum {value = true}; }; + + #include "unicode.cpp" #include "array.cpp" #include "threading.cpp" diff --git a/src/error.cpp b/src/error.cpp index d20373be9..c684be1e7 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -423,7 +423,7 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); - show_error_on_line(pos, end); + // show_error_on_line(pos, end); } else if (pos.line == 0) { error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); @@ -451,7 +451,7 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); - show_error_on_line(pos, end); + // show_error_on_line(pos, end); } else if (pos.line == 0) { error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 72c2a0495..3da768cd1 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -386,6 +386,31 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi return value; } +gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, Ast *elem) { + GB_ASSERT(!sel.indirect); + for (i32 index : sel.index) { + Type *bt = base_type(ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + return false; + } + ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + ft = bt->Array.elem; + break; + default: + return false; + } + } + + + if (is_type_raw_union(ft) || is_type_typeid(ft)) { + return false; + } + return lb_is_elem_const(elem, ft); +} gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) { LLVMContextRef ctx = m->ctx; @@ -979,12 +1004,58 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT(tav.mode != Addressing_Invalid); Selection sel = lookup_field(type, name, false); + GB_ASSERT(!sel.indirect); + Entity *f = type->Struct.fields[sel.index[0]]; - i32 index = field_remapping[f->Variable.field_index]; if (elem_type_can_be_constant(f->type)) { - values[index] = lb_const_value(m, f->type, tav.value, allow_local).value; - visited[index] = true; + if (sel.index.count == 1) { + values[index] = lb_const_value(m, f->type, tav.value, allow_local).value; + visited[index] = true; + } else { + if (!visited[index]) { + values[index] = lb_const_value(m, f->type, {}, false).value; + visited[index] = true; + } + unsigned idx_list_len = cast(unsigned)sel.index.count-1; + unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len); + + if (lb_is_nested_possibly_constant(type, sel, fv->value)) { + bool is_constant = true; + Type *cv_type = f->type; + for (isize j = 1; j < sel.index.count; j++) { + i32 index = sel.index[j]; + Type *cvt = base_type(cv_type); + + if (cvt->kind == Type_Struct) { + if (cvt->Struct.is_raw_union) { + // sanity check which should have been caught by `lb_is_nested_possibly_constant` + is_constant = false; + break; + } + cv_type = cvt->Struct.fields[index]->type; + + if (is_type_struct(cv_type)) { + auto cv_field_remapping = lb_get_struct_remapping(m, cv_type); + idx_list[j-1] = cast(unsigned)cv_field_remapping[index]; + } else { + idx_list[j-1] = cast(unsigned)index; + } + } else if (cvt->kind == Type_Array) { + cv_type = cvt->Array.elem; + + idx_list[j-1] = cast(unsigned)index; + } else { + GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type)); + } + } + if (is_constant) { + LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value; + GB_ASSERT(LLVMIsConstant(elem_value)); + values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len); + } + } + } } } } else { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 5bffc7226..3676847b4 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4044,7 +4044,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { return {}; } - gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { ast_node(cl, CompoundLit, expr); @@ -4093,12 +4092,25 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { ast_node(fv, FieldValue, elem); String name = fv->field->Ident.token.string; Selection sel = lookup_field(bt, name, false); - index = sel.index[0]; + GB_ASSERT(!sel.indirect); + elem = fv->value; - TypeAndValue tav = type_and_value_of_expr(elem); + if (sel.index.count > 1) { + if (lb_is_nested_possibly_constant(type, sel, elem)) { + continue; + } + lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel); + field_expr = lb_build_expr(p, elem); + field_expr = lb_emit_conv(p, field_expr, sel.entity->type); + lb_emit_store(p, dst, field_expr); + continue; + } + + index = sel.index[0]; } else { - TypeAndValue tav = type_and_value_of_expr(elem); Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index); + GB_ASSERT(sel.index.count == 1); + GB_ASSERT(!sel.indirect); index = sel.index[0]; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index a4ef61531..19df9ab06 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -915,7 +915,7 @@ gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t) if (field_remapping == nullptr) { field_remapping = map_get(&m->struct_field_remapping, cast(void *)t); } - GB_ASSERT(field_remapping != nullptr); + GB_ASSERT_MSG(field_remapping != nullptr, "%s", type_to_string(t)); return *field_remapping; } diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 2b8f38fef..ff4befc37 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -1,6 +1,6 @@ template struct PtrSet { - static_assert(TypeIsPointer::value, "PtrSet::T must be a pointer"); + static_assert(TypeIsPointer::value || TypeIsPtrSizedInteger::value, "PtrSet::T must be a pointer"); static constexpr uintptr TOMBSTONE = ~(uintptr)(0ull); T * keys; diff --git a/src/types.cpp b/src/types.cpp index 85364667d..ee610a2ce 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -430,7 +430,6 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) { return res; } - gb_global Type basic_types[] = { {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, -- cgit v1.2.3