From edc793d7c123a38826860ef72684308902a7012c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 11:39:05 +0100 Subject: Add `#no_capture args: ..T` to reuse the backing array stack memory --- src/checker.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/checker.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 8756cce1a..abacc13cb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -184,6 +184,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->deps, 0); ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); + d->no_captures.allocator = heap_allocator(); } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { -- cgit v1.2.3 From 3dff83f3dc2914cdfb9a8f19cf990682cda41b03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 12:39:30 +0100 Subject: Mock out `#no_capture` for future use --- src/check_expr.cpp | 13 ++++++------- src/check_type.cpp | 32 ++++++++++++++++++++++++-------- src/checker.cpp | 2 +- src/checker.hpp | 4 ++-- src/llvm_abi.cpp | 6 ++++++ src/llvm_backend.hpp | 4 ++-- src/llvm_backend_proc.cpp | 32 +++++++++++++------------------- 7 files changed, 54 insertions(+), 39 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 645d8ac5a..4edd34990 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6034,19 +6034,18 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; o.type = vt->type; - // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameter that use `#no_capture` - // on the variadic parameter - if (c->decl && (vt->flags & EntityFlag_NoCapture)) { + // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array + if (c->decl) { bool found = false; - for (NoCaptureData &nc : c->decl->no_captures) { - if (are_types_identical(vt->type, nc.slice_type)) { - nc.max_count = gb_max(nc.max_count, variadic_operands.count); + for (auto &vr : c->decl->variadic_reuses) { + if (are_types_identical(vt->type, vr.slice_type)) { + vr.max_count = gb_max(vr.max_count, variadic_operands.count); found = true; break; } } if (!found) { - array_add(&c->decl->no_captures, NoCaptureData{vt->type, variadic_operands.count}); + array_add(&c->decl->variadic_reuses, VariadicReuseData{vt->type, variadic_operands.count}); } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 466b9b3cd..7b75bf503 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1954,7 +1954,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para p->flags &= ~FieldFlag_by_ptr; } if (p->flags&FieldFlag_no_capture) { - error(name, "'#no_capture' can only be applied to variable variadic fields"); + error(name, "'#no_capture' can only be applied to variable fields"); p->flags &= ~FieldFlag_no_capture; } @@ -2059,16 +2059,32 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para } } if (p->flags&FieldFlag_no_capture) { - if (!(is_variadic && variadic_index == variables.count)) { - error(name, "'#no_capture' can only be applied to a variadic parameter"); - p->flags &= ~FieldFlag_no_capture; - } else if (p->flags & FieldFlag_c_vararg) { - error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); - p->flags &= ~FieldFlag_no_capture; + if (is_variadic && variadic_index == variables.count) { + if (p->flags & FieldFlag_c_vararg) { + error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); + p->flags &= ~FieldFlag_no_capture; + } else { + error(name, "'#no_capture' is already implied on all variadic parameter"); + } + } else if (is_type_polymorphic(type)) { + // ignore } else { - error(name, "'#no_capture' is already implied on all variadic parameter"); + if (is_type_internally_pointer_like(type)) { + // okay + } else if (is_type_slice(type) || is_type_string(type)) { + // okay + } else if (is_type_dynamic_array(type)) { + // okay + } else { + ERROR_BLOCK(); + error(name, "'#no_capture' can only be applied to pointer-like types, slices, strings, and dynamic arrays"); + error_line("\t'#no_capture' does not currently do anything useful\n"); + p->flags &= ~FieldFlag_no_capture; + } } } + + if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { error(name, "'#no_alias' can only be applied to non constant values"); diff --git a/src/checker.cpp b/src/checker.cpp index abacc13cb..9adf4ef3c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -184,7 +184,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->deps, 0); ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); - d->no_captures.allocator = heap_allocator(); + d->variadic_reuses.allocator = heap_allocator(); } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 17722f6b6..2fadbe56a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -181,7 +181,7 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { "Checked", }; -struct NoCaptureData { +struct VariadicReuseData { Type *slice_type; // ..elem_type isize max_count; }; @@ -224,7 +224,7 @@ struct DeclInfo { Array labels; - Array no_captures; + Array variadic_reuses; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index b2e485d01..9a3479b34 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -15,6 +15,7 @@ struct lbArgType { LLVMAttributeRef align_attribute; // Optional i64 byval_alignment; bool is_byval; + bool no_capture; }; @@ -159,6 +160,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute); } + if (arg->no_capture) { + LLVMAddAttributeAtIndex(fn, arg_index+1, nocapture_attr); + } + + if (ft->multiple_return_original_type) { if (ft->original_arg_count <= i) { LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 71fa1dbd0..24494e2af 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,7 +296,7 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; -struct lbNoCaptureData { +struct lbVariadicReuseData { Type *slice_type; lbAddr base_array; }; @@ -341,7 +341,7 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; - Array no_captures; + Array variadic_reuses; LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ec244e185..1585df865 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -517,7 +517,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { lb_start_block(p, p->entry_block); map_init(&p->direct_parameters); - p->no_captures.allocator = heap_allocator(); + p->variadic_reuses.allocator = heap_allocator(); GB_ASSERT(p->type != nullptr); @@ -3452,28 +3452,22 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { isize slice_len = var_args.count; if (slice_len > 0) { lbAddr base_array = {}; - if (e->flags & EntityFlag_NoCapture) { - for (lbNoCaptureData const &nc : p->no_captures) { - if (are_types_identical(nc.slice_type, slice_type)) { - base_array = nc.base_array; - break; - } + for (auto const &vr : p->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + base_array = vr.base_array; + break; } - DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && base_array.addr.value == nullptr) { - for (NoCaptureData const &nc : d->no_captures) { - if (are_types_identical(nc.slice_type, slice_type)) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, nc.max_count), true); - array_add(&p->no_captures, lbNoCaptureData{slice_type, base_array}); - break; - } + } + DeclInfo *d = decl_info_of_entity(p->entity); + if (d != nullptr && base_array.addr.value == nullptr) { + for (auto const &vr : d->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + base_array = lb_add_local_generated(p, alloc_type_array(elem_type, vr.max_count), true); + array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array}); + break; } } } - - if (base_array.addr.value == nullptr) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); - } GB_ASSERT(base_array.addr.value != nullptr); lbAddr slice = lb_add_local_generated(p, slice_type, true); -- cgit v1.2.3 From 6959554040d85597300ab2ce6c25852d18e61923 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 13:44:47 +0100 Subject: Calculate size and alignment, and reuse memory for all variadic calls within a procedure body --- src/check_decl.cpp | 9 +++++++++ src/checker.cpp | 2 ++ src/checker.hpp | 4 +++- src/llvm_backend.hpp | 6 +++--- src/llvm_backend_proc.cpp | 25 +++++++++++++++++-------- 5 files changed, 34 insertions(+), 12 deletions(-) (limited to 'src/checker.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 7d81d102d..6828774e4 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1869,5 +1869,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de add_deps_from_child_to_parent(decl); + for (VariadicReuseData const &vr : decl->variadic_reuses) { + GB_ASSERT(vr.slice_type->kind == Type_Slice); + Type *elem = vr.slice_type->Slice.elem; + i64 size = type_size_of(elem); + i64 align = type_align_of(elem); + decl->variadic_reuse_max_bytes = gb_max(decl->variadic_reuse_max_bytes, size*vr.max_count); + decl->variadic_reuse_max_align = gb_max(decl->variadic_reuse_max_align, align); + } + return true; } diff --git a/src/checker.cpp b/src/checker.cpp index 9adf4ef3c..336440d32 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -185,6 +185,8 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); d->variadic_reuses.allocator = heap_allocator(); + d->variadic_reuse_max_bytes = 0; + d->variadic_reuse_max_align = 1; } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 2fadbe56a..d76e4c7d0 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -183,7 +183,7 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { struct VariadicReuseData { Type *slice_type; // ..elem_type - isize max_count; + i64 max_count; }; // DeclInfo is used to store information of certain declarations to allow for "any order" usage @@ -225,6 +225,8 @@ struct DeclInfo { Array labels; Array variadic_reuses; + i64 variadic_reuse_max_bytes; + i64 variadic_reuse_max_align; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 100748038..deb05528f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,9 +296,8 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; -struct lbVariadicReuseData { +struct lbVariadicReuseSlices { Type *slice_type; - lbAddr base_array; lbAddr slice_addr; }; @@ -342,7 +341,8 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; - Array variadic_reuses; + Array variadic_reuses; + lbAddr variadic_reuse_base_array_ptr; LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index bc85b14c2..7a895fbdd 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3456,39 +3456,48 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } isize slice_len = var_args.count; if (slice_len > 0) { - lbAddr base_array = {}; lbAddr slice = {}; for (auto const &vr : p->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { - base_array = vr.base_array; slice = vr.slice_addr; break; } } + DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && base_array.addr.value == nullptr) { + if (d != nullptr && slice.addr.value == nullptr) { for (auto const &vr : d->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, vr.max_count), true); slice = lb_add_local_generated(p, slice_type, true); - array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array, slice}); + array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); break; } } } - GB_ASSERT(base_array.addr.value != nullptr); + + lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + if (d != nullptr && base_array_ptr.value == nullptr) { + i64 max_bytes = d->variadic_reuse_max_bytes; + i64 max_align = gb_max(d->variadic_reuse_max_align, 16); + p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); + lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); + base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + } + + GB_ASSERT(base_array_ptr.value != nullptr); GB_ASSERT(slice.addr.value != nullptr); + base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len))); for (isize i = 0; i < var_args.count; i++) { - lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); + lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); lbValue var_arg = var_args[i]; var_arg = lb_emit_conv(p, var_arg, elem_type); lb_emit_store(p, addr, var_arg); } - lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); + lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); lbValue len = lb_const_int(p->module, t_int, slice_len); lb_fill_slice(p, slice, base_elem, len); -- cgit v1.2.3