Compare commits

..

2 Commits

Author SHA1 Message Date
Ed_
2bbe654047 updated odin's str8_fmt_kt1l 2025-10-10 23:11:46 -04:00
Ed_
f14ef07a98 Updated lottes_hybrid str8__fmt_kt1l 2025-10-10 22:05:05 -04:00
7 changed files with 98 additions and 112 deletions

View File

@@ -401,7 +401,7 @@ void farena_allocator_proc(AllocatorProc_In in, AllocatorProc_Out_R out);
#define farena_push_mem(arena, amount, ...) farena__push(arena, amount, 1, opt_args(Opts_farena, lit(stringify(B1)), __VA_ARGS__)) #define farena_push_mem(arena, amount, ...) farena__push(arena, amount, 1, opt_args(Opts_farena, lit(stringify(B1)), __VA_ARGS__))
#define farena_push(arena, type, ...) \ #define farena_push(arena, type, ...) \
cast(type*R_, farena__push(arena, size_of(type), 1, opt_args(Opts_farena, lit(stringify(type)), __VA_ARGS__))).ptr cast(type*, farena__push(arena, size_of(type), 1, opt_args(Opts_farena, lit(stringify(type)), __VA_ARGS__))).ptr
#define farena_push_array(arena, type, amount, ...) \ #define farena_push_array(arena, type, amount, ...) \
(Slice ## type){ farena__push(arena, size_of(type), amount, opt_args(Opts_farena, lit(stringify(type)), __VA_ARGS__)).ptr, amount } (Slice ## type){ farena__push(arena, size_of(type), amount, opt_args(Opts_farena, lit(stringify(type)), __VA_ARGS__)).ptr, amount }
@@ -504,7 +504,7 @@ void arena_allocator_proc(AllocatorProc_In in, AllocatorProc_Out_R out);
#define arena_push_mem(arena, amount, ...) arena__push(arena, amount, 1, opt_args(Opts_arena, lit(stringify(B1)), __VA_ARGS__)) #define arena_push_mem(arena, amount, ...) arena__push(arena, amount, 1, opt_args(Opts_arena, lit(stringify(B1)), __VA_ARGS__))
#define arena_push(arena, type, ...) \ #define arena_push(arena, type, ...) \
cast(type*R_, arena__push(arena, 1, size_of(type), opt_args(Opts_arena, lit(stringify(type)), __VA_ARGS__) ).ptr) cast(type*, arena__push(arena, 1, size_of(type), opt_args(Opts_arena, lit(stringify(type)), __VA_ARGS__) ).ptr)
#define arena_push_array(arena, type, amount, ...) \ #define arena_push_array(arena, type, amount, ...) \
(tmpl(Slice,type)){ arena__push(arena, size_of(type), amount, opt_args(Opts_arena, lit(stringify(type)), __VA_ARGS__)).ptr, amount } (tmpl(Slice,type)){ arena__push(arena, size_of(type), amount, opt_args(Opts_arena, lit(stringify(type)), __VA_ARGS__)).ptr, amount }
@@ -1677,38 +1677,38 @@ Str8 str8__fmt_kt1l(AllocatorInfo ainfo, Slice_Mem*R_ _buffer, KT1L_Str8 table,
UTF8_R cursor_buffer = cast(UTF8_R, buffer.ptr); UTF8_R cursor_buffer = cast(UTF8_R, buffer.ptr);
U8 buffer_remaining = buffer.len; U8 buffer_remaining = buffer.len;
UTF8 curr_code = fmt_template.ptr[0];
UTF8_R cursor_fmt = fmt_template.ptr; UTF8_R cursor_fmt = fmt_template.ptr;
U8 left_fmt = fmt_template.len; U8 left_fmt = fmt_template.len;
while (left_fmt && buffer_remaining) while (left_fmt && buffer_remaining)
{ {
// Forward until we hit the delimiter '<' or the template's contents are exhausted. // Forward until we hit the delimiter '<' or the template's contents are exhausted.
while (curr_code && curr_code != '<' && cursor_fmt != slice_end(fmt_template)) { U8 copy_offset = 0;
cursor_buffer[0] = cursor_fmt[0]; while (cursor_fmt[copy_offset] != '<' && (cursor_fmt + copy_offset) < slice_end(fmt_template)) {
++ cursor_buffer; ++ copy_offset;
++ cursor_fmt;
-- buffer_remaining;
-- left_fmt;
curr_code = cursor_fmt[0];
} }
if (curr_code == '<') memory_copy(u8_(cursor_buffer), u8_(cursor_fmt), copy_offset);
buffer_remaining -= copy_offset;
left_fmt -= copy_offset;
cursor_buffer += copy_offset;
cursor_fmt += copy_offset;
if (cursor_fmt[0] == '<')
{ {
UTF8_R cursor_potential_token = cursor_fmt + 1; UTF8_R potential_token_cursor = cursor_fmt + 1;
U8 potential_token_length = 0; U8 potential_token_len = 0;
B4 fmt_overflow = false; B4 fmt_overflow = false;
for (;;) { for (;;) {
UTF8_R cursor = cursor_potential_token + potential_token_length; UTF8_R cursor = potential_token_cursor + potential_token_len;
fmt_overflow = cursor >= slice_end(fmt_template); fmt_overflow = cursor >= slice_end(fmt_template);
B4 found_terminator = (cursor_potential_token + potential_token_length)[0] == '>'; B4 found_terminator = potential_token_cursor[potential_token_len] == '>';
if (fmt_overflow || found_terminator) { break; } if (fmt_overflow || found_terminator) { break; }
++ potential_token_length; ++ potential_token_len;
} }
if (fmt_overflow) continue; if (fmt_overflow) continue;
// Hashing the potential token and cross checking it with our token table // Hashing the potential token and cross checking it with our token table
U8 key = 0; hash64_djb8(& key, slice_mem(u8_(cursor_potential_token), potential_token_length)); U8 key = 0; hash64_djb8(& key, slice_mem(u8_(potential_token_cursor), potential_token_len));
Str8_R value = nullptr; Str8_R value = nullptr;
for (slice_iter(table, token)) for (slice_iter(table, token)) {
{
// We do a linear iteration instead of a hash table lookup because the user should be never substiuting with more than 100 unqiue tokens.. // We do a linear iteration instead of a hash table lookup because the user should be never substiuting with more than 100 unqiue tokens..
if (token->key == key) { if (token->key == key) {
value = & token->value; value = & token->value;
@@ -1718,32 +1718,26 @@ Str8 str8__fmt_kt1l(AllocatorInfo ainfo, Slice_Mem*R_ _buffer, KT1L_Str8 table,
if (value) if (value)
{ {
// We're going to appending the string, make sure we have enough space in our buffer. // We're going to appending the string, make sure we have enough space in our buffer.
if (ainfo.proc != nullptr && (buffer_remaining - potential_token_length) <= 0) { if (ainfo.proc != nullptr && (buffer_remaining - potential_token_len) <= 0) {
buffer = mem_grow(ainfo, buffer, buffer.len + potential_token_length); buffer = mem_grow(ainfo, buffer, buffer.len + potential_token_len);
buffer_remaining += potential_token_length; buffer_remaining += potential_token_len;
}
U8 left = value->len;
U1_R cursor_value = value->ptr;
while (left && buffer_remaining) {
cursor_buffer[0] = cursor_value[0];
++ cursor_buffer;
++ cursor_value;
-- buffer_remaining;
-- left;
} }
assert((buffer_remaining - potential_token_len) > 0);
memory_copy(u8_(cursor_buffer), u8_(value->ptr), value->len);
// Sync cursor format to after the processed token // Sync cursor format to after the processed token
cursor_fmt = cursor_potential_token + potential_token_length + 1; cursor_buffer += value->len;
curr_code = cursor_fmt[0]; buffer_remaining -= value->len;
left_fmt -= potential_token_length + 2; // The 2 here are the '<' & '>' delimiters being omitted. cursor_fmt = potential_token_cursor + potential_token_len + 1;
left_fmt -= potential_token_len + 2; // The 2 here are the '<' & '>' delimiters being omitted.
continue; continue;
} }
// If not a subsitution, we do a single copy for the '<' and continue.
cursor_buffer[0] = cursor_fmt[0]; cursor_buffer[0] = cursor_fmt[0];
++ cursor_buffer; ++ cursor_buffer;
++ cursor_fmt; ++ cursor_fmt;
-- buffer_remaining; -- buffer_remaining;
-- left_fmt; -- left_fmt;
curr_code = cursor_fmt[0]; continue;
} }
} }
_buffer[0] = buffer; _buffer[0] = buffer;
@@ -2277,7 +2271,7 @@ int main(void)
os_init(); os_init();
VArena_R vm_file = varena_make(.reserve_size = giga(4), .flags = VArenaFlag_NoLargePages); VArena_R vm_file = varena_make(.reserve_size = giga(4), .flags = VArenaFlag_NoLargePages);
FileOpInfo file = file_read_contents(lit("watl.v0.msvc.c"), .backing = ainfo_varena(vm_file)); FileOpInfo file = file_read_contents(lit("watl.v0.llvm.lottes_hybrid.c"), .backing = ainfo_varena(vm_file));
slice_assert(file.content); slice_assert(file.content);
Arena_R a_msgs = arena_make(); Arena_R a_msgs = arena_make();

View File

@@ -1585,7 +1585,7 @@ Str8 str8__fmt_kt1l(AllocatorInfo ainfo, Slice_Byte* _buffer, KT1L_Str8 table, S
{ {
SSIZE copy_offset = 0; SSIZE copy_offset = 0;
// Forward until we hit the delimiter '<' or the template's contents are exhausted. // Forward until we hit the delimiter '<' or the template's contents are exhausted.
while (cursor_fmt[copy_offset] != cast(UTF8, '<') && (cursor_fmt + copy_offset) != slice_end(fmt_template)) { while (cursor_fmt[copy_offset] != cast(UTF8, '<') && (cursor_fmt + copy_offset) < slice_end(fmt_template)) {
++ copy_offset; ++ copy_offset;
} }
memory_copy(cursor_buffer, cursor_fmt, copy_offset); memory_copy(cursor_buffer, cursor_fmt, copy_offset);
@@ -1598,11 +1598,11 @@ Str8 str8__fmt_kt1l(AllocatorInfo ainfo, Slice_Byte* _buffer, KT1L_Str8 table, S
{ {
UTF8* potential_token_cursor = cursor_fmt + 1; UTF8* potential_token_cursor = cursor_fmt + 1;
SSIZE potential_token_len = 0; SSIZE potential_token_len = 0;
B32 fmt_overflow = false; B32 fmt_overflow = false;
for (;;) { for (;;) {
UTF8* cursor = potential_token_cursor + potential_token_len; UTF8* cursor = potential_token_cursor + potential_token_len;
fmt_overflow = cursor >= slice_end(fmt_template); fmt_overflow = cursor >= slice_end(fmt_template);
B32 found_terminator = * (potential_token_cursor + potential_token_len) == '>'; B32 found_terminator = potential_token_cursor[potential_token_len] == '>';
if (fmt_overflow || found_terminator) { break; } if (fmt_overflow || found_terminator) { break; }
++ potential_token_len; ++ potential_token_len;
} }
@@ -1633,7 +1633,7 @@ Str8 str8__fmt_kt1l(AllocatorInfo ainfo, Slice_Byte* _buffer, KT1L_Str8 table, S
left_fmt -= potential_token_len + 2; // The 2 here are the '<' & '>' delimiters being omitted. left_fmt -= potential_token_len + 2; // The 2 here are the '<' & '>' delimiters being omitted.
continue; continue;
} }
// If not a value, we do a single copy for the '<' and continue. // If not a subsitution, we do a single copy for the '<' and continue.
* cursor_buffer = * cursor_fmt; * cursor_buffer = * cursor_fmt;
++ cursor_buffer; ++ cursor_buffer;
++ cursor_fmt; ++ cursor_fmt;

View File

@@ -2,7 +2,7 @@
WATL Exercise WATL Exercise
Version: 0 (From Scratch, 1-Stage Compilation, WinAPI Only, Win CRT Multi-threaded Static Linkage) Version: 0 (From Scratch, 1-Stage Compilation, WinAPI Only, Win CRT Multi-threaded Static Linkage)
Host: Windows 11 (x86-64) Host: Windows 11 (x86-64)
Toolchain: odin-lang/Odin dev-2025-07 Toolchain: odin-lang/Odin dev-2025-09
*/ */
package odin package odin
@@ -31,9 +31,9 @@ copy :: proc {
slice_copy, slice_copy,
string_copy, string_copy,
} }
copy_non_overlapping :: proc { copy_overlapping :: proc {
memory_copy_non_overlapping, memory_copy_overlapping,
slice_copy_non_overlapping, slice_copy_overlapping,
} }
cursor :: proc { cursor :: proc {
ptr_cursor, ptr_cursor,
@@ -109,15 +109,33 @@ memory_zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data return data
} }
memory_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { memory_copy_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
intrinsics.mem_copy(dst, src, len) intrinsics.mem_copy(dst, src, len)
return dst return dst
} }
memory_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { memory_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
intrinsics.mem_copy_non_overlapping(dst, src, len) intrinsics.mem_copy_non_overlapping(dst, src, len)
return dst return dst
} }
sll_stack_push_n :: proc "contextless" (curr, n, n_link: ^^$Type) {
(n_link ^) = (curr ^)
(curr ^) = (n ^)
}
sll_queue_push_nz :: proc "contextless" (first: ^$ParentType, last, n: ^^$Type, nil_val: ^Type) {
if (first ^) == nil_val {
(first ^) = n^
(last ^) = n^
n^.next = nil_val
}
else {
(last ^).next = n^
(last ^) = n^
n^.next = nil_val
}
}
sll_queue_push_n :: #force_inline proc "contextless" (first: $ParentType, last, n: ^^$Type) { sll_queue_push_nz(first, last, n, nil) }
SliceByte :: struct { SliceByte :: struct {
data: [^]byte, data: [^]byte,
len: int len: int
@@ -139,37 +157,19 @@ slice_end :: #force_inline proc "contextless" (s : $SliceType / []$Type) -> ^Typ
slice_zero :: proc "contextless" (data: $SliceType / []$Type) { memory_zero(raw_data(data), size_of(Type) * len(data)) } slice_zero :: proc "contextless" (data: $SliceType / []$Type) { memory_zero(raw_data(data), size_of(Type) * len(data)) }
slice_copy :: proc "contextless" (dst, src: $SliceType / []$Type) -> int { slice_copy :: proc "contextless" (dst, src: $SliceType / []$Type) -> int {
n := max(0, min(len(dst), len(src)))
if n > 0 {
intrinsics.mem_copy(raw_data(dst), raw_data(src), n * size_of(Type))
}
return n
}
slice_copy_non_overlapping :: proc "contextless" (dst, src: $SliceType / []$Type) -> int {
n := max(0, min(len(dst), len(src))) n := max(0, min(len(dst), len(src)))
if n > 0 { if n > 0 {
intrinsics.mem_copy_non_overlapping(raw_data(dst), raw_data(src), n * size_of(Type)) intrinsics.mem_copy_non_overlapping(raw_data(dst), raw_data(src), n * size_of(Type))
} }
return n return n
} }
slice_copy_overlapping :: proc "contextless" (dst, src: $SliceType / []$Type) -> int {
sll_stack_push_n :: proc "contextless" (curr, n, n_link: ^^$Type) { n := max(0, min(len(dst), len(src)))
(n_link ^) = (curr ^) if n > 0 {
(curr ^) = (n ^) intrinsics.mem_copy(raw_data(dst), raw_data(src), n * size_of(Type))
}
sll_queue_push_nz :: proc "contextless" (first: ^$ParentType, last, n: ^^$Type, nil_val: ^Type) {
if (first ^) == nil_val {
(first ^) = n^
(last ^) = n^
n^.next = nil_val
}
else {
(last ^).next = n^
(last ^) = n^
n^.next = nil_val
} }
return n
} }
sll_queue_push_n :: #force_inline proc "contextless" (first: $ParentType, last, n: ^^$Type) { sll_queue_push_nz(first, last, n, nil) }
//endregion Memory //endregion Memory
//region Allocator Interface //region Allocator Interface
@@ -1220,35 +1220,36 @@ str8_fmt_kt1l :: proc(ainfo: AllocatorInfo, _buffer: ^[]byte, table: []KT1L_Slot
cursor_buffer := cursor(buffer) cursor_buffer := cursor(buffer)
buffer_remaining := len(buffer) buffer_remaining := len(buffer)
curr_code := fmt_template[0]
cursor_fmt := cursor(transmute([]u8) fmt_template) cursor_fmt := cursor(transmute([]u8) fmt_template)
left_fmt := len(fmt_template) left_fmt := len(fmt_template)
for ; left_fmt > 0 && buffer_remaining > 0; for ; left_fmt > 0 && buffer_remaining > 0;
{ {
// Forward until we hit the delimiter '<' or the template's contents are exhausted. // Forward until we hit the delimiter '<' or the template's contents are exhausted.
for ; curr_code != '<' && cursor_fmt != end(fmt_template); { copy_offset : int = 0
cursor_buffer[0] = cursor_fmt [0] for ; cursor_fmt[copy_offset] != '<' && cursor_fmt[copy_offset:] != end(fmt_template); {
cursor_buffer = cursor_buffer[1:] copy_offset += 1
cursor_fmt = cursor_fmt [1:]
curr_code = cursor_fmt [0]
buffer_remaining -= 1
left_fmt -= 1
} }
if curr_code == '<' copy(cursor_buffer, cursor_fmt, copy_offset)
buffer_remaining -= copy_offset
left_fmt -= copy_offset
cursor_buffer = cursor_buffer[copy_offset:]
cursor_fmt = cursor_fmt [copy_offset:]
if cursor_fmt[0] == '<'
{ {
cursor_potential_token := cursor_fmt[1:] potential_token_cursor := cursor_fmt[1:]
potential_token_length := 0 potential_token_len := 0
fmt_overflow := b32(false) fmt_overflow := b32(false)
for ;; { for ;; {
cursor := cursor_potential_token[potential_token_length:] cursor := potential_token_cursor[potential_token_len:]
fmt_overflow = cursor >= end(fmt_template) fmt_overflow = cursor >= end(fmt_template)
found_terminator := cast(b32) (cursor_potential_token[potential_token_length] == '>') found_terminator := cast(b32) (potential_token_cursor[potential_token_len] == '>')
if fmt_overflow || found_terminator do break if fmt_overflow || found_terminator do break
potential_token_length += 1 potential_token_len += 1
} }
if fmt_overflow do continue if fmt_overflow do continue
// Hashing the potential token and cross checking it with our token table // Hashing the potential token and cross checking it with our token table
key : u64 = 0; hash64_djb8(& key, slice(cursor_potential_token, potential_token_length)) key : u64 = 0; hash64_djb8(& key, slice(potential_token_cursor, potential_token_len))
value : ^string = nil value : ^string = nil
for & token in table for & token in table
{ {
@@ -1261,32 +1262,26 @@ str8_fmt_kt1l :: proc(ainfo: AllocatorInfo, _buffer: ^[]byte, table: []KT1L_Slot
if value != nil if value != nil
{ {
// We're going to appending the string, make sure we have enough space in our buffer. // We're going to appending the string, make sure we have enough space in our buffer.
if ainfo.procedure != nil && (buffer_remaining - potential_token_length) <= 0 { if ainfo.procedure != nil && (buffer_remaining - potential_token_len) <= 0 {
buffer = mem_grow(ainfo, buffer, len(buffer) + potential_token_length) buffer = mem_grow(ainfo, buffer, len(buffer) + potential_token_len)
buffer_remaining += potential_token_length buffer_remaining += potential_token_len
}
left := len(value)
cursor_value := cursor(transmute([]u8) value^)
for ; left > 0 && buffer_remaining > 0; {
cursor_buffer[0] = cursor_value [0]
cursor_buffer = cursor_buffer[1:]
cursor_value = cursor_value [1:]
cursor_fmt = cursor_fmt [1:]
buffer_remaining -= 1
left -= 1
} }
assert((buffer_remaining - potential_token_len) > 0)
copy(cursor_buffer, cursor(value ^), len(value))
// Sync cursor format to after the processed token // Sync cursor format to after the processed token
cursor_fmt = cursor_potential_token[potential_token_length + 1:] cursor_buffer = cursor_buffer[len(value):]
curr_code = cursor_fmt[0] buffer_remaining -= len(value)
left_fmt -= potential_token_length + 2 // The 2 here are the '<' & '>' delimiters being omitted. cursor_fmt = potential_token_cursor[potential_token_len + 1:]
left_fmt -= potential_token_len + 2 // The 2 here are the '<' & '>' delimiters being omitted.
continue continue
} }
// If not a subsitution, we do a single copy for the '<' and continue.
cursor_buffer[0] = cursor_fmt [0] cursor_buffer[0] = cursor_fmt [0]
cursor_buffer = cursor_buffer[1:] cursor_buffer = cursor_buffer[1:]
cursor_fmt = cursor_fmt [1:] cursor_fmt = cursor_fmt [1:]
curr_code = cursor_fmt [0]
buffer_remaining -= 1 buffer_remaining -= 1
left_fmt -= 1 left_fmt -= 1
continue
} }
} }
_buffer ^ = buffer _buffer ^ = buffer

View File

@@ -42,8 +42,8 @@ $flag_link_win_machine_64 = '/MACHINE:X64'
$flag_link_win_path_output = '/OUT:' $flag_link_win_path_output = '/OUT:'
$flag_link_win_rt_dll = '/MD' $flag_link_win_rt_dll = '/MD'
$flag_link_win_rt_dll_debug = '/MDd' $flag_link_win_rt_dll_debug = '/MDd'
$flag_link_win_rt_static = '/MT:STATIC' $flag_link_win_rt_static = '/MT'
$flag_link_win_rt_static_debug = '/MTd:STATIC' $flag_link_win_rt_static_debug = '/MTd'
$flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE' $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
$flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS' $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
$flag_no_optimization = '/Od' $flag_no_optimization = '/Od'

View File

@@ -126,7 +126,7 @@ $compiler_args += ($flag_define + 'BUILD_DEBUG')
$compiler_args += $flag_debug $compiler_args += $flag_debug
$compiler_args += ( $flag_path_debug + $path_build + '\' ) $compiler_args += ( $flag_path_debug + $path_build + '\' )
# Use the static, multithreaded, debug runtime library # Use the static, multithreaded, debug runtime library
$compiler_args += $flag_link_win_rt_static_debug # $compiler_args += $flag_link_win_rt_static_debug
# Include setup # Include setup
$compiler_args += ($flag_include + $path_root) $compiler_args += ($flag_include + $path_root)

View File

@@ -85,7 +85,6 @@ msvc_link_default_base_address :: 0x180000000
//endregion Script Grime //endregion Script Grime
build :: proc(working_dir : string, args : []string) -> (stdout : string, stderr : string) { build :: proc(working_dir : string, args : []string) -> (stdout : string, stderr : string) {
fmt.println("Building:", args)
res, errs : []byte; _, res, errs, _ = os.process_exec({ working_dir = working_dir, command = args}, context.allocator) res, errs : []byte; _, res, errs, _ = os.process_exec({ working_dir = working_dir, command = args}, context.allocator)
return transmute(string)res, transmute(string)errs; return transmute(string)res, transmute(string)errs;
} }
@@ -106,8 +105,8 @@ main :: proc() {
file_source, file_source,
flag_file, flag_file,
join_str(flag_output_path, file_exe), join_str(flag_output_path, file_exe),
// flag_optimize_none, flag_optimize_none,
falg_optimize_aggressive, // falg_optimize_aggressive,
flag_default_allocator_nil, flag_default_allocator_nil,
flag_debug, flag_debug,
flag_microarch_zen5, flag_microarch_zen5,
@@ -117,6 +116,4 @@ main :: proc() {
flag_use_lld, flag_use_lld,
join_str(flag_subsystem, "console"), join_str(flag_subsystem, "console"),
}) })
fmt.println(res)
fmt.println(errs)
} }

View File

@@ -44,5 +44,5 @@ $build_args += $flag_dyn_map_calls
$build_args += $flag_default_allocator_nil $build_args += $flag_default_allocator_nil
$build_args += $flag_output_path + $exe $build_args += $flag_output_path + $exe
if ($need_rebuild) { & $odin $build_args } if ($need_rebuild) { & $odin $build_args }
pop-location
& $exe & $exe
pop-location