From 0727e91aeb8e3daa7ddac7d3b234c159637db724 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 Mar 2023 16:30:48 +0000 Subject: [PATCH 1/4] Simplify the implementation of `strings.split_multi`; add `strings.index_multi` --- core/strings/strings.odin | 214 ++++++++++++++++---------------------- 1 file changed, 90 insertions(+), 124 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index cd9e9a04f..91d2defa8 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -996,6 +996,36 @@ last_index_any :: proc(s, chars: string) -> int { return -1 } +index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { + idx = -1 + if s == "" || len(substrs) <= 0 { + return + } + // disallow "" substr + for substr in substrs { + if len(substr) == 0 { + return + } + } + + lowest_index := len(s) + found := false + for substr in substrs { + if i := index(s, substr); i >= 0 { + if i < lowest_index { + lowest_index = i + width = len(substr) + found = true + } + } + } + + if found { + idx = lowest_index + } + return +} + /* returns the count of the string `substr` found in the string `s` returns the rune_count + 1 of the string `s` on empty `substr` @@ -1412,8 +1442,58 @@ trim_suffix :: proc(s, suffix: string) -> string { res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) fmt.eprintln(res) // -> [testing, this, out, nice, done, last] */ -split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> (buf: []string) #no_bounds_check { +split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> []string #no_bounds_check { if s == "" || len(substrs) <= 0 { + return nil + } + + // disallow "" substr + for substr in substrs { + if len(substr) == 0 { + return nil + } + } + + // calculate the needed len of `results` + n := 1 + for it := s; len(it) > 0; { + i, w := index_multi(it, substrs) + if i < 0 { + break + } + n += 1 + it = it[i+w:] + } + + results := make([dynamic]string, 0, n, allocator) + { + it := s + for len(it) > 0 { + i, w := index_multi(it, substrs) + if i < 0 { + break + } + part := it[:i] + append(&results, part) + it = it[i+w:] + } + append(&results, it) + } + assert(len(results) == n) + return results[:] +} + +/* + splits the input string `s` by all possible `substrs` []string in an iterator fashion + returns the split string every iteration, the full string on no match + splits := [?]string { "---", "~~~", ".", "_", "," } + it := "testing,this.out_nice---done~~~last" + for str in strings.split_multi_iterate(&it, splits[:]) { + fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last] + } +*/ +split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check { + if it == nil || len(it) == 0 || len(substrs) <= 0 { return } @@ -1424,130 +1504,16 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator } } - // TODO maybe remove duplicate substrs - // sort substrings by string size, largest to smallest - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - temp_substrs := slice.clone(substrs, context.temp_allocator) - - slice.sort_by(temp_substrs, proc(a, b: string) -> bool { - return len(a) > len(b) - }) - - substrings_found: int - temp := s - - // count substr results found in string - first_pass: for len(temp) > 0 { - for substr in temp_substrs { - size := len(substr) - - // check range and compare string to substr - if size <= len(temp) && temp[:size] == substr { - substrings_found += 1 - temp = temp[size:] - continue first_pass - } - } - - // step through string - _, skip := utf8.decode_rune_in_string(temp[:]) - temp = temp[skip:] + // calculate the needed len of `results` + i, w := index_multi(it^, substrs) + if i >= 0 { + res = it[:i] + it^ = it[i+w:] + } else { + // last value + it^ = it[len(it):] } - - // skip when no results - if substrings_found < 1 { - return - } - - buf = make([]string, substrings_found + 1, allocator) - buf_index: int - temp = s - temp_old := temp - - // gather results in the same fashion - second_pass: for len(temp) > 0 { - for substr in temp_substrs { - size := len(substr) - - // check range and compare string to substr - if size <= len(temp) && temp[:size] == substr { - buf[buf_index] = temp_old[:len(temp_old) - len(temp)] - buf_index += 1 - temp = temp[size:] - temp_old = temp - continue second_pass - } - } - - // step through string - _, skip := utf8.decode_rune_in_string(temp[:]) - temp = temp[skip:] - } - - buf[buf_index] = temp_old[:] - - return buf -} - -// state for the split multi iterator -Split_Multi :: struct { - temp: string, - temp_old: string, - substrs: []string, -} - -// returns split multi state with sorted `substrs` -split_multi_init :: proc(s: string, substrs: []string) -> Split_Multi { - // sort substrings, largest to smallest - temp_substrs := slice.clone(substrs, context.temp_allocator) - slice.sort_by(temp_substrs, proc(a, b: string) -> bool { - return len(a) > len(b) - }) - - return { - temp = s, - temp_old = s, - substrs = temp_substrs, - } -} - -/* - splits the input string `s` by all possible `substrs` []string in an iterator fashion - returns the split string every iteration, the full string on no match - - splits := [?]string { "---", "~~~", ".", "_", "," } - state := strings.split_multi_init("testing,this.out_nice---done~~~last", splits[:]) - for str in strings.split_multi_iterate(&state) { - fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last] - } -*/ -split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check { - pass: for len(temp) > 0 { - for substr in substrs { - size := len(substr) - - // check range and compare string to substr - if size <= len(temp) && temp[:size] == substr { - res = temp_old[:len(temp_old) - len(temp)] - temp = temp[size:] - temp_old = temp - ok = true - return - } - } - - // step through string - _, skip := utf8.decode_rune_in_string(temp[:]) - temp = temp[skip:] - } - - // allow last iteration - if temp_old != "" { - res = temp_old[:] - ok = true - temp_old = "" - } - + ok = true return } From 97d7e295ddc6409352d49fa896836ceb8794ea2f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 Mar 2023 16:35:30 +0000 Subject: [PATCH 2/4] Fix to `split_multi_iterator` --- core/strings/strings.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 91d2defa8..4179f7df8 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1511,6 +1511,7 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: it^ = it[i+w:] } else { // last value + res = it^ it^ = it[len(it):] } ok = true From 74fb74d9cb4de3e9e5950fe5b53ae8fc9215dbf5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 Mar 2023 16:41:22 +0000 Subject: [PATCH 3/4] Keep `-vet` happy --- core/strings/strings.odin | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 4179f7df8..33cdafef3 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -3,9 +3,7 @@ package strings import "core:io" import "core:mem" -import "core:slice" import "core:unicode" -import "core:runtime" import "core:unicode/utf8" // returns a clone of the string `s` allocated using the `allocator` From bfb231fb8adeea282a51e6903ac2e6c0d2656dda Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 Mar 2023 17:24:29 +0000 Subject: [PATCH 4/4] Simplify copy elision on variable declarations --- src/llvm_backend.hpp | 2 -- src/llvm_backend_const.cpp | 8 +---- src/llvm_backend_stmt.cpp | 64 ++++++++++++++++++++------------------ 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4104d5b5f..4a78ca3a9 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -337,8 +337,6 @@ struct lbProcedure { LLVMMetadataRef debug_info; - lbAddr current_elision_hint; - PtrMap selector_values; PtrMap selector_addr; PtrMap tuple_fix_map; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 286c01f74..8db6e2a1f 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -485,13 +485,7 @@ 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 = {}; - if (p->current_elision_hint.addr.value && are_types_identical(lb_addr_type(p->current_elision_hint), type)) { - slice = p->current_elision_hint; - p->current_elision_hint = {}; - } else { - slice = lb_add_local_generated(p, type, false); - } + 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}); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index c7f8590f9..125913ac5 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2269,54 +2269,56 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) { return; } - auto lvals = array_make(permanent_allocator(), 0, vd->names.count); - - for (Ast *name : vd->names) { - lbAddr lval = {}; - if (!is_blank_ident(name)) { - Entity *e = entity_of_node(name); - // bool zero_init = true; // Always do it - bool zero_init = vd->values.count == 0; - lval = lb_add_local(p, e->type, e, zero_init); - } - array_add(&lvals, lval); - } + TEMPORARY_ALLOCATOR_GUARD(); auto const &values = vd->values; - if (values.count > 0) { - auto inits = array_make(permanent_allocator(), 0, lvals.count); + if (values.count == 0) { + auto lvals = slice_make(temporary_allocator(), vd->names.count); + for_array(i, vd->names) { + Ast *name = vd->names[i]; + if (!is_blank_ident(name)) { + Entity *e = entity_of_node(name); + // bool zero_init = true; // Always do it + bool zero_init = values.count == 0; + lvals[i] = lb_add_local(p, e->type, e, zero_init); + } + } + } else { + auto lvals_preused = slice_make(temporary_allocator(), vd->names.count); + auto lvals = slice_make(temporary_allocator(), vd->names.count); + auto inits = array_make(temporary_allocator(), 0, lvals.count); isize lval_index = 0; for (Ast *rhs : values) { - p->current_elision_hint = lvals[lval_index]; - rhs = unparen_expr(rhs); lbValue init = lb_build_expr(p, rhs); - #if 1 - if (p->current_elision_hint.addr.value != lvals[lval_index].addr.value) { - lvals[lval_index] = {}; // do nothing so that nothing will assign to it - } else { + + if (rhs->kind == Ast_CompoundLit) { // 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) { - GB_ASSERT(p->current_elision_hint.addr.value == nullptr); - GB_ASSERT(p->current_elision_hint.addr.value != lvals[lval_index].addr.value); - lvals[lval_index] = {}; // do nothing so that nothing will assign to it - } + lbAddr *comp_lit_addr = map_get(&p->module->exact_value_compound_literal_addr_map, rhs); + if (comp_lit_addr) { + if (Entity *e = entity_of_node(vd->names[lval_index])) { + lbValue val = comp_lit_addr->addr; + lb_add_entity(p->module, e, val); + lb_add_debug_local_variable(p, val.value, e->type, e->token); + lvals_preused[lval_index] = true; } } } - #endif lval_index += lb_append_tuple_values(p, &inits, init); } GB_ASSERT(lval_index == lvals.count); - p->current_elision_hint = {}; + for_array(i, vd->names) { + Ast *name = vd->names[i]; + if (!is_blank_ident(name) && !lvals_preused[i]) { + Entity *e = entity_of_node(name); + bool zero_init = values.count == 0; + lvals[i] = lb_add_local(p, e->type, e, zero_init); + } + } GB_ASSERT(lvals.count == inits.count); for_array(i, inits) {