From 5bc8a491a7768da0019b7b17da637e681f2ace90 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 30 Oct 2021 23:24:34 +0100 Subject: [PATCH 01/27] Begin work on supporting `wasm64`; Correct `wasm32` compilation behaviour --- core/runtime/default_temporary_allocator.odin | 320 +++++++++--------- src/build_settings.cpp | 30 +- src/llvm_abi.cpp | 16 +- src/llvm_backend.cpp | 1 + src/llvm_backend_expr.cpp | 2 + src/llvm_backend_utility.cpp | 1 + src/main.cpp | 19 +- 7 files changed, 223 insertions(+), 166 deletions(-) diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin index 216ed99ff..b3602469d 100644 --- a/core/runtime/default_temporary_allocator.odin +++ b/core/runtime/default_temporary_allocator.odin @@ -17,175 +17,189 @@ Default_Temp_Allocator :: struct { leaked_allocations: [dynamic][]byte, } -default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) { - s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) - s.curr_offset = 0 - s.prev_allocation = nil - s.backup_allocator = backup_allocator - s.leaked_allocations.allocator = backup_allocator -} - -default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) { - if s == nil { - return +when ODIN_OS == "freestanding" { + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) { } - for ptr in s.leaked_allocations { - free(raw_data(ptr), s.backup_allocator) + + default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) { } - delete(s.leaked_allocations) - delete(s.data, s.backup_allocator) - s^ = {} -} - -@(private) -default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - size := size - size = align_forward_int(size, alignment) - - switch { - case s.curr_offset+size <= len(s.data): - start := uintptr(raw_data(s.data)) - ptr := start + uintptr(s.curr_offset) - ptr = align_forward_uintptr(ptr, uintptr(alignment)) - mem_zero(rawptr(ptr), size) - - s.prev_allocation = rawptr(ptr) - offset := int(ptr - start) - s.curr_offset = offset + size - return byte_slice(rawptr(ptr), size), .None - - case size <= len(s.data): - start := uintptr(raw_data(s.data)) - ptr := align_forward_uintptr(start, uintptr(alignment)) - mem_zero(rawptr(ptr), size) - - s.prev_allocation = rawptr(ptr) - offset := int(ptr - start) - s.curr_offset = offset + size - return byte_slice(rawptr(ptr), size), .None + + default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { + return nil, nil } - a := s.backup_allocator - if a.procedure == nil { - a = context.allocator - s.backup_allocator = a - } - - data, err := mem_alloc_bytes(size, alignment, a, loc) - if err != nil { - return data, err - } - if s.leaked_allocations == nil { - s.leaked_allocations = make([dynamic][]byte, a) - } - append(&s.leaked_allocations, data) - - // TODO(bill): Should leaks be notified about? - if logger := context.logger; logger.lowest_level <= .Warning { - if logger.procedure != nil { - logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc) - } - } - - return data, .None -} - -@(private) -default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error { - if old_memory == nil { - return .None - } - - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) - old_ptr := uintptr(old_memory) - - if s.prev_allocation == old_memory { - s.curr_offset = int(uintptr(s.prev_allocation) - start) +} else { + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) { + s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) + s.curr_offset = 0 s.prev_allocation = nil - return .None + s.backup_allocator = backup_allocator + s.leaked_allocations.allocator = backup_allocator } - if start <= old_ptr && old_ptr < end { - // NOTE(bill): Cannot free this pointer but it is valid - return .None + default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) { + if s == nil { + return + } + for ptr in s.leaked_allocations { + free(raw_data(ptr), s.backup_allocator) + } + delete(s.leaked_allocations) + delete(s.data, s.backup_allocator) + s^ = {} } - if len(s.leaked_allocations) != 0 { - for data, i in s.leaked_allocations { - ptr := raw_data(data) - if ptr == old_memory { - free(ptr, s.backup_allocator) - ordered_remove(&s.leaked_allocations, i) - return .None + @(private) + default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + size := size + size = align_forward_int(size, alignment) + + switch { + case s.curr_offset+size <= len(s.data): + start := uintptr(raw_data(s.data)) + ptr := start + uintptr(s.curr_offset) + ptr = align_forward_uintptr(ptr, uintptr(alignment)) + mem_zero(rawptr(ptr), size) + + s.prev_allocation = rawptr(ptr) + offset := int(ptr - start) + s.curr_offset = offset + size + return byte_slice(rawptr(ptr), size), .None + + case size <= len(s.data): + start := uintptr(raw_data(s.data)) + ptr := align_forward_uintptr(start, uintptr(alignment)) + mem_zero(rawptr(ptr), size) + + s.prev_allocation = rawptr(ptr) + offset := int(ptr - start) + s.curr_offset = offset + size + return byte_slice(rawptr(ptr), size), .None + } + a := s.backup_allocator + if a.procedure == nil { + a = context.allocator + s.backup_allocator = a + } + + data, err := mem_alloc_bytes(size, alignment, a, loc) + if err != nil { + return data, err + } + if s.leaked_allocations == nil { + s.leaked_allocations = make([dynamic][]byte, a) + } + append(&s.leaked_allocations, data) + + // TODO(bill): Should leaks be notified about? + if logger := context.logger; logger.lowest_level <= .Warning { + if logger.procedure != nil { + logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc) } } - } - return .Invalid_Pointer - // panic("invalid pointer passed to default_temp_allocator"); -} -@(private) -default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) { - s.curr_offset = 0 - s.prev_allocation = nil - for data in s.leaked_allocations { - free(raw_data(data), s.backup_allocator) - } - clear(&s.leaked_allocations) -} - -@(private) -default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - begin := uintptr(raw_data(s.data)) - end := begin + uintptr(len(s.data)) - old_ptr := uintptr(old_memory) - if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 { - if old_ptr+uintptr(size) < end { - s.curr_offset = int(old_ptr-begin)+size - return byte_slice(old_memory, size), .None - } - } - data, err := default_temp_allocator_alloc(s, size, alignment, loc) - if err == .None { - copy(data, byte_slice(old_memory, old_size)) - err = default_temp_allocator_free(s, old_memory, loc) - } - return data, err -} - -default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { - - s := (^Default_Temp_Allocator)(allocator_data) - - if s.data == nil { - default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator()) + return data, .None } - switch mode { - case .Alloc: - data, err = default_temp_allocator_alloc(s, size, alignment, loc) - case .Free: - err = default_temp_allocator_free(s, old_memory, loc) - - case .Free_All: - default_temp_allocator_free_all(s, loc) - - case .Resize: - data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc) - - case .Query_Features: - set := (^Allocator_Mode_Set)(old_memory) - if set != nil { - set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features} + @(private) + default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error { + if old_memory == nil { + return .None } - case .Query_Info: - // Nothing to give + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) + old_ptr := uintptr(old_memory) + + if s.prev_allocation == old_memory { + s.curr_offset = int(uintptr(s.prev_allocation) - start) + s.prev_allocation = nil + return .None + } + + if start <= old_ptr && old_ptr < end { + // NOTE(bill): Cannot free this pointer but it is valid + return .None + } + + if len(s.leaked_allocations) != 0 { + for data, i in s.leaked_allocations { + ptr := raw_data(data) + if ptr == old_memory { + free(ptr, s.backup_allocator) + ordered_remove(&s.leaked_allocations, i) + return .None + } + } + } + return .Invalid_Pointer + // panic("invalid pointer passed to default_temp_allocator"); } - return + @(private) + default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) { + s.curr_offset = 0 + s.prev_allocation = nil + for data in s.leaked_allocations { + free(raw_data(data), s.backup_allocator) + } + clear(&s.leaked_allocations) + } + + @(private) + default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + begin := uintptr(raw_data(s.data)) + end := begin + uintptr(len(s.data)) + old_ptr := uintptr(old_memory) + if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 { + if old_ptr+uintptr(size) < end { + s.curr_offset = int(old_ptr-begin)+size + return byte_slice(old_memory, size), .None + } + } + data, err := default_temp_allocator_alloc(s, size, alignment, loc) + if err == .None { + copy(data, byte_slice(old_memory, old_size)) + err = default_temp_allocator_free(s, old_memory, loc) + } + return data, err + } + + default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { + + s := (^Default_Temp_Allocator)(allocator_data) + + if s.data == nil { + default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator()) + } + + switch mode { + case .Alloc: + data, err = default_temp_allocator_alloc(s, size, alignment, loc) + case .Free: + err = default_temp_allocator_free(s, old_memory, loc) + + case .Free_All: + default_temp_allocator_free_all(s, loc) + + case .Resize: + data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc) + + case .Query_Features: + set := (^Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features} + } + + case .Query_Info: + // Nothing to give + } + + return + } } default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator { @@ -193,4 +207,4 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator procedure = default_temp_allocator_proc, data = allocator, } -} +} \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 69e1ec5f0..a906f6712 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -29,6 +29,7 @@ enum TargetArchKind { TargetArch_386, TargetArch_arm64, TargetArch_wasm32, + TargetArch_wasm64, TargetArch_COUNT, }; @@ -59,6 +60,7 @@ String target_arch_names[TargetArch_COUNT] = { str_lit("386"), str_lit("arm64"), str_lit("wasm32"), + str_lit("wasm64"), }; String target_endian_names[TargetEndian_COUNT] = { @@ -72,6 +74,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = { TargetEndian_Little, TargetEndian_Little, TargetEndian_Little, + TargetEndian_Little, }; #ifndef ODIN_VERSION_RAW @@ -335,6 +338,16 @@ gb_global TargetMetrics target_freestanding_wasm32 = { str_lit(""), }; +gb_global TargetMetrics target_freestanding_wasm64 = { + TargetOs_freestanding, + TargetArch_wasm64, + 8, + 16, + str_lit("wasm64-freestanding-js"), + str_lit(""), +}; + + struct NamedTargetMetrics { @@ -353,6 +366,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_386"), &target_freebsd_386 }, { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, + { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 }, }; NamedTargetMetrics *selected_target_metrics; @@ -458,11 +472,21 @@ bool find_library_collection_path(String name, String *path) { } bool is_arch_wasm(void) { - return build_context.metrics.arch == TargetArch_wasm32; + switch (build_context.metrics.arch) { + case TargetArch_wasm32: + case TargetArch_wasm64: + return true; + } + return false; } bool allow_check_foreign_filepath(void) { - return build_context.metrics.arch != TargetArch_wasm32; + switch (build_context.metrics.arch) { + case TargetArch_wasm32: + case TargetArch_wasm64: + return false; + } + return true; } @@ -870,7 +894,7 @@ void init_build_context(TargetMetrics *cross_target) { break; } - } else if (bc->metrics.arch == TargetArch_wasm32) { + } else if (is_arch_wasm()) { bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined "); } else { gb_printf_err("Compiler Error: Unsupported architecture\n");; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 9e7f4b290..aa12cc352 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1061,19 +1061,27 @@ LB_ABI_INFO(lb_get_abi_info) { } } - if (build_context.metrics.arch == TargetArch_amd64) { + switch (build_context.metrics.arch) { + case TargetArch_amd64: if (build_context.metrics.os == TargetOs_windows) { return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } else { return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } - } else if (build_context.metrics.arch == TargetArch_386) { + case TargetArch_386: return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); - } else if (build_context.metrics.arch == TargetArch_arm64) { + case TargetArch_arm64: return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); - } else if (build_context.metrics.arch == TargetArch_wasm32) { + case TargetArch_wasm32: + // TODO(bill): implement wasm32's ABI correct + // NOTE(bill): this ABI is only an issue for WASI compatibility return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + case TargetArch_wasm64: + // TODO(bill): implement wasm64's ABI correct + // NOTE(bill): this ABI is only an issue for WASI compatibility + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } + GB_PANIC("Unsupported ABI"); return {}; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4d1245c98..7a70ee478 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1148,6 +1148,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMInitializeAArch64Disassembler(); break; case TargetArch_wasm32: + case TargetArch_wasm64: LLVMInitializeWebAssemblyTargetInfo(); LLVMInitializeWebAssemblyTarget(); LLVMInitializeWebAssemblyTargetMC(); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c9827ae3a..fd9b10a4f 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -496,6 +496,7 @@ bool lb_is_matrix_simdable(Type *t) { break; case TargetArch_386: case TargetArch_wasm32: + case TargetArch_wasm64: // nope return false; } @@ -513,6 +514,7 @@ bool lb_is_matrix_simdable(Type *t) { return true; case TargetArch_386: case TargetArch_wasm32: + case TargetArch_wasm64: return false; } } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 14fb8280e..b58f07d49 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1504,6 +1504,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t break; case TargetArch_386: case TargetArch_wasm32: + case TargetArch_wasm64: is_possible = false; break; } diff --git a/src/main.cpp b/src/main.cpp index 92e541384..fad749e34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -135,13 +135,20 @@ i32 linker_stage(lbGenerator *gen) { if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); - result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm-obj\" -o \"%.*s.wasm\" %.*s %.*s", - LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); - if (result) { - return result; + + if (build_context.metrics.arch == TargetArch_wasm32) { + result = system_exec_command_line_app("wasm-ld", + "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", + LIT(build_context.ODIN_ROOT), + LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + } else { + GB_ASSERT(build_context.metrics.arch == TargetArch_wasm64); + result = system_exec_command_line_app("wasm-ld", + "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", + LIT(build_context.ODIN_ROOT), + LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); } + return result; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { From 8ef6f9dd7bbb1611dd7166c4e14034e53df4a8b6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 00:11:38 +0100 Subject: [PATCH 02/27] Compile `wasm64`; Add `lb_run_remove_unused_function_pass` --- core/runtime/default_temporary_allocator.odin | 25 +++++----- core/runtime/internal.odin | 5 ++ src/build_settings.cpp | 6 ++- src/check_decl.cpp | 8 +++ src/entity.cpp | 3 +- src/llvm_backend.cpp | 16 ++++-- src/llvm_backend.hpp | 21 ++++++++ src/llvm_backend_opt.cpp | 49 +++++++++++++++++++ src/llvm_backend_proc.cpp | 13 +++-- src/main.cpp | 19 +++---- 10 files changed, 128 insertions(+), 37 deletions(-) diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin index b3602469d..afe3ee922 100644 --- a/core/runtime/default_temporary_allocator.odin +++ b/core/runtime/default_temporary_allocator.odin @@ -1,23 +1,12 @@ package runtime -@(private) -byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte { - return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)} -} - - DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22) -Default_Temp_Allocator :: struct { - data: []byte, - curr_offset: int, - prev_allocation: rawptr, - backup_allocator: Allocator, - leaked_allocations: [dynamic][]byte, -} - when ODIN_OS == "freestanding" { + Default_Temp_Allocator :: struct { + } + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) { } @@ -30,6 +19,14 @@ when ODIN_OS == "freestanding" { return nil, nil } } else { + Default_Temp_Allocator :: struct { + data: []byte, + curr_offset: int, + prev_allocation: rawptr, + backup_allocator: Allocator, + leaked_allocations: [dynamic][]byte, + } + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) { s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) s.curr_offset = 0 diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 4347f28c0..0a6d07467 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -2,6 +2,11 @@ package runtime import "core:intrinsics" +@(private) +byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check { + return ([^]byte)(data)[:max(len, 0)] +} + bswap_16 :: proc "contextless" (x: u16) -> u16 { return x>>8 | x<<8 } diff --git a/src/build_settings.cpp b/src/build_settings.cpp index a906f6712..e34330ea8 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -895,7 +895,11 @@ void init_build_context(TargetMetrics *cross_target) { } } else if (is_arch_wasm()) { - bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined "); + if (bc->metrics.arch == TargetArch_wasm32) { + bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined "); + } else { + bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined -mwasm64 "); + } } else { gb_printf_err("Compiler Error: Unsupported architecture\n");; gb_exit(1); diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0591eca4d..c2d23e70c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -899,6 +899,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { mutex_unlock(&ctx->info->foreign_mutex); } } + + if (e->Procedure.link_name.len > 0 ) { + e->flags |= EntityFlag_CustomLinkName; + } } void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) { @@ -990,6 +994,10 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, string_map_set(fp, key, e); } } + + if (e->Variable.link_name.len > 0) { + e->flags |= EntityFlag_CustomLinkName; + } if (init_expr == nullptr) { if (type_expr == nullptr) { diff --git a/src/entity.cpp b/src/entity.cpp index 86fefcf89..d95c74f22 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -74,9 +74,10 @@ enum EntityFlag : u64 { EntityFlag_Test = 1ull<<30, EntityFlag_Init = 1ull<<31, + + EntityFlag_CustomLinkName = 1ull<<40, EntityFlag_Overridden = 1ull<<63, - }; enum EntityState : u32 { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7a70ee478..892f615df 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1064,14 +1064,10 @@ struct lbLLVMModulePassWorkerData { }; WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { - GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); - auto wd = cast(lbLLVMModulePassWorkerData *)data; - LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level); LLVMRunPassManager(module_pass_manager, wd->m->mod); - return 0; } @@ -1661,6 +1657,8 @@ void lb_generate_code(lbGenerator *gen) { for_array(i, gen->modules.entries) { lbModule *m = gen->modules.entries[i].value; + + lb_run_remove_unused_function_pass(m->mod); auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); wd->m = m; @@ -1738,8 +1736,16 @@ void lb_generate_code(lbGenerator *gen) { } TIME_SECTION("LLVM Object Generation"); + + isize non_empty_module_count = 0; + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (!lb_is_module_empty(m)) { + non_empty_module_count += 1; + } + } - if (do_threading) { + if (do_threading && non_empty_module_count > 1) { for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; if (lb_is_module_empty(m)) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 9aa9920f2..5fc5dfebf 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -585,3 +585,24 @@ enum : LLVMAttributeIndex { LLVMAttributeIndex_FunctionIndex = ~0u, LLVMAttributeIndex_FirstArgIndex = 1, }; + + +char const *llvm_linkage_strings[] = { + "external linkage", + "available externally linkage", + "link once any linkage", + "link once odr linkage", + "link once odr auto hide linkage", + "weak any linkage", + "weak odr linkage", + "appending linkage", + "internal linkage", + "private linkage", + "dllimport linkage", + "dllexport linkage", + "external weak linkage", + "ghost linkage", + "common linkage", + "linker private linkage", + "linker private weak linkage" +}; \ No newline at end of file diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index d5ea90aea..25e290d70 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -355,3 +355,52 @@ void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) { // are not removed lb_run_remove_dead_instruction_pass(p); } + + +void lb_run_remove_unused_function_pass(LLVMModuleRef mod) { + isize removal_count = 0; + isize pass_count = 0; + isize const max_pass_count = 10; + // Custom remove dead function pass + for (; pass_count < max_pass_count; pass_count++) { + bool was_dead_function = false; + for (LLVMValueRef func = LLVMGetFirstFunction(mod); + func != nullptr; + /**/ + ) { + LLVMValueRef curr_func = func; + func = LLVMGetNextFunction(func); + + LLVMUseRef first_use = LLVMGetFirstUse(curr_func); + if (first_use != nullptr) { + continue; + } + String name = {}; + name.text = cast(u8 *)LLVMGetValueName2(curr_func, cast(size_t *)&name.len); + + if (LLVMIsDeclaration(curr_func)) { + // Ignore for the time being + continue; + } + + + LLVMLinkage linkage = LLVMGetLinkage(curr_func); + + switch (linkage) { + case LLVMExternalLinkage: + case LLVMDLLImportLinkage: + case LLVMDLLExportLinkage: + default: + continue; + case LLVMInternalLinkage: + break; + } + LLVMDeleteFunction(curr_func); + was_dead_function = true; + removal_count += 1; + } + if (!was_dead_function) { + break; + } + } +} diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 15689da36..29f7b6655 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -195,13 +195,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) // then it is very likely it is required by LLVM and thus cannot have internal linkage if (entity->pkg != nullptr && entity->pkg->kind == Package_Runtime && p->body != nullptr) { GB_ASSERT(entity->kind == Entity_Procedure); - if (entity->Procedure.link_name != "") { - LLVMSetLinkage(p->value, LLVMExternalLinkage); + String link_name = entity->Procedure.link_name; + if (entity->flags & EntityFlag_CustomLinkName && + link_name != "") { + if (string_starts_with(link_name, str_lit("__"))) { + LLVMSetLinkage(p->value, LLVMExternalLinkage); + } else { + LLVMSetLinkage(p->value, LLVMInternalLinkage); + } } } } } - + if (p->is_foreign) { if (is_arch_wasm()) { char const *import_name = alloc_cstring(permanent_allocator(), p->name); @@ -217,6 +223,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) LLVMAddTargetDependentFunctionAttr(p->value, "wasm-import-module", module_name); } } + // NOTE(bill): offset==0 is the return value isize offset = 1; diff --git a/src/main.cpp b/src/main.cpp index fad749e34..7338ad45d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -135,19 +135,12 @@ i32 linker_stage(lbGenerator *gen) { if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); - - if (build_context.metrics.arch == TargetArch_wasm32) { - result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", - LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); - } else { - GB_ASSERT(build_context.metrics.arch == TargetArch_wasm64); - result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", - LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); - } + + GB_ASSERT(build_context.metrics.arch == TargetArch_wasm64); + result = system_exec_command_line_app("wasm-ld", + "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", + LIT(build_context.ODIN_ROOT), + LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); return result; } From 841a96691bcbe62add30530777b26c38bc0a3fff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 00:37:37 +0100 Subject: [PATCH 03/27] Attempt to get wasm64 compiling with the correct features enabled --- src/build_settings.cpp | 13 +++++-------- src/main.cpp | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e34330ea8..4c7faa1b2 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -893,15 +893,12 @@ void init_build_context(TargetMetrics *cross_target) { bc->link_flags = str_lit("-arch arm64 "); break; } - - } else if (is_arch_wasm()) { - if (bc->metrics.arch == TargetArch_wasm32) { - bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined "); - } else { - bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined -mwasm64 "); - } + } else if (bc->metrics.arch == TargetArch_wasm32) { + bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined --features=wasm-feature-atomics "); + } else if (bc->metrics.arch == TargetArch_wasm64) { + bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined -mwasm64 --features=wasm-feature-memory64,wasm-feature-atomics --verbose "); } else { - gb_printf_err("Compiler Error: Unsupported architecture\n");; + gb_printf_err("Compiler Error: Unsupported architecture\n"); gb_exit(1); } diff --git a/src/main.cpp b/src/main.cpp index 7338ad45d..173c70a4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -136,7 +136,6 @@ i32 linker_stage(lbGenerator *gen) { if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); - GB_ASSERT(build_context.metrics.arch == TargetArch_wasm64); result = system_exec_command_line_app("wasm-ld", "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", LIT(build_context.ODIN_ROOT), From 8a2078aa9027daa863dc0a3603a1afedd5836cb8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 01:06:27 +0000 Subject: [PATCH 04/27] Add `core:sys/wasi` --- core/sys/wasi/wasi.odin | 1819 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1819 insertions(+) create mode 100644 core/sys/wasi/wasi.odin diff --git a/core/sys/wasi/wasi.odin b/core/sys/wasi/wasi.odin new file mode 100644 index 000000000..fde7601a2 --- /dev/null +++ b/core/sys/wasi/wasi.odin @@ -0,0 +1,1819 @@ +//+build wasm32 +package sys_wasi + +foreign import "env" + +DIRCOOKIE_START :: u64(0) +size_t :: uint + +filesize_t :: distinct u64 +timestamp_t :: distinct u64 + +clockid_t :: distinct u32 +CLOCK_MONOTONIC :: clockid_t(0) +CLOCK_PROCESS_CPUTIME_ID :: clockid_t(1) +CLOCK_REALTIME :: clockid_t(2) +CLOCK_THREAD_CPUTIME_ID :: clockid_t(3) + +errno_t :: enum u16 { + // No error occurred. System call completed successfully. + SUCCESS = 0, + // Argument list too long. + TOOBIG = 1, + // Permission denied. + ACCESS = 2, + // Address in use. + ADDRINUSE = 3, + // Address not available. + ADDRNOTAVAIL = 4, + // Address family not supported. + AFNOSUPPORT = 5, + // Resource unavailable, or operation would block. + AGAIN = 6, + // Connection already in progress. + ALREADY = 7, + // Bad file descriptor. + BADF = 8, + // Bad message. + BADMSG = 9, + // Device or resource busy. + BUSY = 10, + // Operation canceled. + CANCELED = 11, + // No child processes. + CHILD = 12, + // Connection aborted. + CONNABORTED = 13, + // Connection refused. + CONNREFUSED = 14, + // Connection reset. + CONNRESET = 15, + // Resource deadlock would occur. + DEADLK = 16, + // Destination address required. + DESTADDRREQ = 17, + // Mathematics argument out of domain of function. + DOM = 18, + // Reserved. + DQUOT = 19, + // File exists. + EXIST = 20, + // Bad address. + FAULT = 21, + // File too large. + FBIG = 22, + // Host is unreachable. + HOSTUNREACH = 23, + // Identifier removed. + IDRM = 24, + // Illegal byte sequence. + ILSEQ = 25, + // Operation in progress. + INPROGRESS = 26, + // Interrupted function. + INTR = 27, + // Invalid argument. + INVAL = 28, + // I/O error. + IO = 29, + // Socket is connected. + ISCONN = 30, + // Is a directory. + ISDIR = 31, + // Too many levels of symbolic links. + LOOP = 32, + // File descriptor value too large. + MFILE = 33, + // Too many links. + MLINK = 34, + // Message too large. + MSGSIZE = 35, + // Reserved. + MULTIHOP = 36, + // Filename too long. + NAMETOOLONG = 37, + // Network is down. + NETDOWN = 38, + // Connection aborted by network. + NETRESET = 39, + // Network unreachable. + NETUNREACH = 40, + // Too many files open in system. + NFILE = 41, + // No buffer space available. + NOBUFS = 42, + // No such device. + NODEV = 43, + // No such file or directory. + NOENT = 44, + // Executable file format error. + NOEXEC = 45, + // No locks available. + NOLCK = 46, + // Reserved. + NOLINK = 47, + // Not enough space. + NOMEM = 48, + // No message of the desired type. + NOMSG = 49, + // Protocol not available. + NOPROTOOPT = 50, + // No space left on device. + NOSPC = 51, + // Function not supported. + NOSYS = 52, + // The socket is not connected. + NOTCONN = 53, + // Not a directory or a symbolic link to a directory. + NOTDIR = 54, + // Directory not empty. + NOTEMPTY = 55, + // State not recoverable. + NOTRECOVERABLE = 56, + // Not a socket. + NOTSOCK = 57, + // Not supported, or operation not supported on socket. + NOTSUP = 58, + // Inappropriate I/O control operation. + NOTTY = 59, + // No such device or address. + NXIO = 60, + // Value too large to be stored in data type. + OVERFLOW = 61, + // Previous owner died. + OWNERDEAD = 62, + // Operation not permitted. + PERM = 63, + // Broken pipe. + PIPE = 64, + // Protocol error. + PROTO = 65, + // Protocol not supported. + PROTONOSUPPORT = 66, + // Protocol wrong type for socket. + PROTOTYPE = 67, + // Result too large. + RANGE = 68, + // Read-only file system. + ROFS = 69, + // Invalid seek. + SPIPE = 70, + // No such process. + SRCH = 71, + // Reserved. + STALE = 72, + // Connection timed out. + TIMEDOUT = 73, + // Text file busy. + TXTBSY = 74, + // Cross-device link. + XDEV = 75, + // Extension: Capabilities insufficient. + NOTCAPABLE = 76, +} + + +rights_flag_t :: enum u64 { + /** + * The right to invoke `fd_datasync`. + * If `path_open` is set, includes the right to invoke + * `path_open` with `fdflags::dsync`. + */ + FD_DATASYNC = 0, + + /** + * The right to invoke `fd_read` and `sock_recv`. + * If `rights::fd_seek` is set, includes the right to invoke `fd_pread`. + */ + FD_READ = 1, + + /** + * The right to invoke `fd_seek`. This flag implies `rights::fd_tell`. + */ + FD_SEEK = 2, + + /** + * The right to invoke `fd_fdstat_set_flags`. + */ + FD_FDSTAT_SET_FLAGS = 3, + + /** + * The right to invoke `fd_sync`. + * If `path_open` is set, includes the right to invoke + * `path_open` with `fdflags::rsync` and `fdflags::dsync`. + */ + FD_SYNC = 4, + + /** + * The right to invoke `fd_seek` in such a way that the file offset + * remains unaltered (i.e., `whence::cur` with offset zero), or to + * invoke `fd_tell`. + */ + FD_TELL = 5, + + /** + * The right to invoke `fd_write` and `sock_send`. + * If `rights::fd_seek` is set, includes the right to invoke `fd_pwrite`. + */ + FD_WRITE = 6, + + /** + * The right to invoke `fd_advise`. + */ + FD_ADVISE = 7, + + /** + * The right to invoke `fd_allocate`. + */ + FD_ALLOCATE = 8, + + /** + * The right to invoke `path_create_directory`. + */ + PATH_CREATE_DIRECTORY = 9, + + /** + * If `path_open` is set, the right to invoke `path_open` with `oflags::creat`. + */ + PATH_CREATE_FILE = 10, + + /** + * The right to invoke `path_link` with the file descriptor as the + * source directory. + */ + PATH_LINK_SOURCE = 11, + + /** + * The right to invoke `path_link` with the file descriptor as the + * target directory. + */ + PATH_LINK_TARGET = 12, + + /** + * The right to invoke `path_open`. + */ + PATH_OPEN = 13, + + /** + * The right to invoke `fd_readdir`. + */ + FD_READDIR = 14, + + /** + * The right to invoke `path_readlink`. + */ + PATH_READLINK = 15, + + /** + * The right to invoke `path_rename` with the file descriptor as the source directory. + */ + PATH_RENAME_SOURCE = 16, + + /** + * The right to invoke `path_rename` with the file descriptor as the target directory. + */ + PATH_RENAME_TARGET = 17, + + /** + * The right to invoke `path_filestat_get`. + */ + PATH_FILESTAT_GET = 18, + + /** + * The right to change a file's size (there is no `path_filestat_set_size`). + * If `path_open` is set, includes the right to invoke `path_open` with `oflags::trunc`. + */ + PATH_FILESTAT_SET_SIZE = 19, + + /** + * The right to invoke `path_filestat_set_times`. + */ + PATH_FILESTAT_SET_TIMES = 20, + + /** + * The right to invoke `fd_filestat_get`. + */ + FD_FILESTAT_GET = 21, + + /** + * The right to invoke `fd_filestat_set_size`. + */ + FD_FILESTAT_SET_SIZE = 22, + + /** + * The right to invoke `fd_filestat_set_times`. + */ + FD_FILESTAT_SET_TIMES = 23, + + /** + * The right to invoke `path_symlink`. + */ + PATH_SYMLINK = 24, + + /** + * The right to invoke `path_remove_directory`. + */ + PATH_REMOVE_DIRECTORY = 25, + + /** + * The right to invoke `path_unlink_file`. + */ + PATH_UNLINK_FILE = 26, + + /** + * If `rights::fd_read` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_read`. + * If `rights::fd_write` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_write`. + */ + POLL_FD_READWRITE = 27, + + /** + * The right to invoke `sock_shutdown`. + */ + SOCK_SHUTDOWN = 28, +} + +rights_t :: distinct bit_set[rights_flag_t; u64] + + +fd_t :: distinct i32 + +// iovec_t :: struct { +// buf: [^]u8, +// buf_len: size_t, +// } +// ciovec_t :: struct { +// buf: [^]u8, +// buf_len: size_t, +// } +iovec_t :: distinct []byte +ciovec_t :: distinct []byte + + +filedelta_t :: distinct i64 + +whence_t :: enum u8 { + SET = 0, + CUR = 1, + END = 2, +} + +dircookie_t :: distinct u64 +dirnamlen_t :: distinct u32 +inode_t :: distinct u64 + +filetype_t :: enum u8 { + /** + * The type of the file descriptor or file is unknown or is different from any of the other types specified. + */ + UNKNOWN = 0, + + /** + * The file descriptor or file refers to a block device inode. + */ + BLOCK_DEVICE = 1, + + /** + * The file descriptor or file refers to a character device inode. + */ + CHARACTER_DEVICE = 2, + + /** + * The file descriptor or file refers to a directory inode. + */ + DIRECTORY = 3, + + /** + * The file descriptor or file refers to a regular file inode. + */ + REGULAR_FILE = 4, + + /** + * The file descriptor or file refers to a datagram socket. + */ + SOCKET_DGRAM = 5, + + /** + * The file descriptor or file refers to a byte-stream socket. + */ + SOCKET_STREAM = 6, + + /** + * The file refers to a symbolic link inode. + */ + SYMBOLIC_LINK = 7, +} + +dirent_t :: struct { + d_next: dircookie_t, + d_ino: inode_t, + d_namlen: dirnamlen_t, + d_type: filetype_t, +} + +advice_t :: enum u8 { + /** + * The application has no advice to give on its behavior with respect to the specified data. + */ + NORMAL = 0, + + /** + * The application expects to access the specified data sequentially from lower offsets to higher offsets. + */ + SEQUENTIAL = 1, + + /** + * The application expects to access the specified data in a random order. + */ + RANDOM = 2, + + /** + * The application expects to access the specified data in the near future. + */ + WILLNEED = 3, + + /** + * The application expects that it will not access the specified data in the near future. + */ + DONTNEED = 4, + + /** + * The application expects to access the specified data once and then not reuse it thereafter. + */ + NOREUSE = 5, +} + +fdflags_t :: distinct bit_set[fdflag_t; u16] +fdflag_t :: enum u16 { + /** + * Append mode: Data written to the file is always appended to the file's end. + */ + APPEND = 0, + + /** + * Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. + */ + DSYNC = 1, + + /** + * Non-blocking mode. + */ + NONBLOCK = 2, + + /** + * Synchronized read I/O operations. + */ + RSYNC = 3, + + /** + * Write according to synchronized I/O file integrity completion. In + * addition to synchronizing the data stored in the file, the implementation + * may also synchronously update the file's metadata. + */ + SYNC = 4, +} + +fdstat_t :: struct { + /** + * File type. + */ + fs_filetype: filetype_t, + + /** + * File descriptor flags. + */ + fs_flags: fdflags_t, + + /** + * Rights that apply to this file descriptor. + */ + fs_rights_base: rights_t, + + /** + * Maximum set of rights that may be installed on new file descriptors that + * are created through this file descriptor, e.g., through `path_open`. + */ + fs_rights_inheriting: rights_t, +} + +device_t :: distinct u64 + + +fstflags_t :: distinct bit_set[fstflag_t; u16] +fstflag_t :: enum u16 { + /** + * Adjust the last data access timestamp to the value stored in `filestat::atim`. + */ + ATIM = 0, + + /** + * Adjust the last data access timestamp to the time of clock `clockid::realtime`. + */ + ATIM_NOW = 1, + + /** + * Adjust the last data modification timestamp to the value stored in `filestat::mtim`. + */ + MTIM = 2, + + /** + * Adjust the last data modification timestamp to the time of clock `clockid::realtime`. + */ + MTIM_NOW = 3, + +} + +lookupflags_t :: distinct bit_set[lookupflag_t; u32] +lookupflag_t :: enum u32 { + /** + * As long as the resolved path corresponds to a symbolic link, it is expanded. + */ + SYMLINK_FOLLOW = 0, +} + +oflags_t :: distinct bit_set[oflag_t; u16] +oflag_t :: enum u16 { + /** + * Create file if it does not exist. + */ + CREATE = 0, + + /** + * Fail if not a directory. + */ + DIRECTORY = 1, + + /** + * Fail if file already exists. + */ + EXCL = 2, + + /** + * Truncate file to size 0. + */ + TRUNC = 3, +} + +linkcount_t :: distinct u64 + +filestat_t :: struct { + /** + * Device ID of device containing the file. + */ + dev: device_t, + + /** + * File serial number. + */ + ino: inode_t, + + /** + * File type. + */ + filetype: filetype_t, + + /** + * Number of hard links to the file. + */ + nlink: linkcount_t, + + /** + * For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link. + */ + size: filesize_t, + + /** + * Last data access timestamp. + */ + atim: timestamp_t, + + /** + * Last data modification timestamp. + */ + mtim: timestamp_t, + + /** + * Last file status change timestamp. + */ + ctim: timestamp_t, +} + +userdata_t :: distinct u64 + +eventtype_t :: enum u8 { + /** + * The time value of clock `subscription_clock::id` has + * reached timestamp `subscription_clock::timeout`. + */ + CLOCK = 0, + + /** + * File descriptor `subscription_fd_readwrite::file_descriptor` has data + * available for reading. This event always triggers for regular files. + */ + FD_READ = 1, + + /** + * File descriptor `subscription_fd_readwrite::file_descriptor` has capacity + * available for writing. This event always triggers for regular files. + */ + FD_WRITE = 2, +} + +eventrwflags_t :: distinct bit_set[eventrwflag_t; u16] +eventrwflag_t :: enum u16 { + /** + * The peer of this socket has closed or disconnected. + */ + FD_READWRITE_HANGUP = 0, +} + +event_fd_readwrite_t :: struct { + /** + * The number of bytes available for reading or writing. + */ + nbytes: filesize_t, + + /** + * The state of the file descriptor. + */ + flags: eventrwflags_t, +} + +event_t :: struct { + /** + * User-provided value that got attached to `subscription::userdata`. + */ + userdata: userdata_t, + + /** + * If non-zero, an error that occurred while processing the subscription request. + */ + error: errno_t, + + /** + * The type of event that occured + */ + type: eventtype_t, + + /** + * The contents of the event, if it is an `eventtype::fd_read` or + * `eventtype::fd_write`. `eventtype::clock` events ignore this field. + */ + fd_readwrite: event_fd_readwrite_t, +} + +subclockflags_t :: distinct bit_set[subclockflag_t; u16] +subclockflag_t :: enum u16 { + /** + * If set, treat the timestamp provided in + * `subscription_clock::timeout` as an absolute timestamp of clock + * `subscription_clock::id`. If clear, treat the timestamp + * provided in `subscription_clock::timeout` relative to the + * current time value of clock `subscription_clock::id`. + */ + SUBSCRIPTION_CLOCK_ABSTIME = 0, + +} + +subscription_clock_t :: struct { + /** + * The clock against which to compare the timestamp. + */ + id: clockid_t, + + /** + * The absolute or relative timestamp. + */ + timeout: timestamp_t, + + /** + * The amount of time that the implementation may wait additionally + * to coalesce with other events. + */ + precision: timestamp_t, + + /** + * Flags specifying whether the timeout is absolute or relative + */ + flags: subclockflags_t, +} + +subscription_fd_readwrite_t :: struct { + /** + * The file descriptor on which to wait for it to become ready for reading or writing. + */ + file_descriptor: fd_t, +} + +subscription_t :: struct { + /** + * User-provided value that is attached to the subscription in the + * implementation and returned through `event::userdata`. + */ + userdata: userdata_t, + + /** + * The type of the event to which to subscribe, and its contents + */ + using contents: struct { + tag: u8, + using u: struct #raw_union { + clock: subscription_clock_t, + fd_read: subscription_fd_readwrite_t, + fd_write: subscription_fd_readwrite_t, + }, + }, +} + +exitcode_t :: distinct u32 + +signal_t :: enum u8 { + /** + * No signal. Note that POSIX has special semantics for `kill(pid, 0)`, + * so this value is reserved. + */ + NONE = 0, + + /** + * Hangup. + * Action: Terminates the process. + */ + HUP = 1, + + /** + * Terminate interrupt signal. + * Action: Terminates the process. + */ + INT = 2, + + /** + * Terminal quit signal. + * Action: Terminates the process. + */ + QUIT = 3, + + /** + * Illegal instruction. + * Action: Terminates the process. + */ + ILL = 4, + + /** + * Trace/breakpoint trap. + * Action: Terminates the process. + */ + TRAP = 5, + + /** + * Process abort signal. + * Action: Terminates the process. + */ + ABRT = 6, + + /** + * Access to an undefined portion of a memory object. + * Action: Terminates the process. + */ + BUS = 7, + + /** + * Erroneous arithmetic operation. + * Action: Terminates the process. + */ + FPE = 8, + + /** + * Kill. + * Action: Terminates the process. + */ + KILL = 9, + + /** + * User-defined signal 1. + * Action: Terminates the process. + */ + USR1 = 10, + + /** + * Invalid memory reference. + * Action: Terminates the process. + */ + SEGV = 11, + + /** + * User-defined signal 2. + * Action: Terminates the process. + */ + USR2 = 12, + + /** + * Write on a pipe with no one to read it. + * Action: Ignored. + */ + PIPE = 13, + + /** + * Alarm clock. + * Action: Terminates the process. + */ + ALRM = 14, + + /** + * Termination signal. + * Action: Terminates the process. + */ + TERM = 15, + + /** + * Child process terminated, stopped, or continued. + * Action: Ignored. + */ + CHLD = 16, + + /** + * Continue executing, if stopped. + * Action: Continues executing, if stopped. + */ + CONT = 17, + + /** + * Stop executing. + * Action: Stops executing. + */ + STOP = 18, + + /** + * Terminal stop signal. + * Action: Stops executing. + */ + TSTP = 19, + + /** + * Background process attempting read. + * Action: Stops executing. + */ + TTIN = 20, + + /** + * Background process attempting write. + * Action: Stops executing. + */ + TTOU = 21, + + /** + * High bandwidth data is available at a socket. + * Action: Ignored. + */ + URG = 22, + + /** + * CPU time limit exceeded. + * Action: Terminates the process. + */ + XCPU = 23, + + /** + * File size limit exceeded. + * Action: Terminates the process. + */ + XFSZ = 24, + + /** + * Virtual timer expired. + * Action: Terminates the process. + */ + VTALRM = 25, + + /** + * Profiling timer expired. + * Action: Terminates the process. + */ + PROF = 26, + + /** + * Window changed. + * Action: Ignored. + */ + WINCH = 27, + + /** + * I/O possible. + * Action: Terminates the process. + */ + POLL = 28, + + /** + * Power failure. + * Action: Terminates the process. + */ + PWR = 29, + + /** + * Bad system call. + * Action: Terminates the process. + */ + SYS = 30, +} + + +riflags_t :: distinct bit_set[riflag_t; u16] +riflag_t :: enum u16 { + /** + * Returns the message without removing it from the socket's receive queue. + */ + RECV_PEEK = 0, + + /** + * On byte-stream sockets, block until the full amount of data can be returned. + */ + RECV_WAITALL = 1, +} + +roflags_t :: distinct bit_set[roflag_t; u16] +roflag_t :: enum u16 { + /** + * Returned by `sock_recv`: Message data has been truncated. + */ + RECV_DATA_TRUNCATED = 0, +} + +siflags_t :: distinct bit_set[siflag_t; u16] +siflag_t :: enum u16 { +} + + +sdflags_t :: distinct bit_set[sdflag_t; u8] +sdflag_t :: enum u8 { + /** + * Disables further receive operations. + */ + RD = 0, + + /** + * Disables further send operations. + */ + WR = 1, +} + +preopentype_t :: enum u8 { + DIR = 0, +} + +prestat_dir_t :: struct { + pr_name_len: size_t, +} + +prestat_t :: struct { + tag: u8, + using u: struct { + dir: prestat_dir_t, + }, +} + +@(link_prefix="__wasi_") +foreign env { + /** + * Read command-line argument data. + * The size of the array should match that returned by `args_sizes_get` + */ + args_get :: proc( + argv: [^]cstring, + argv_buf: [^]byte, + ) -> errno_t --- + /** + * Read environment variable data. + * The sizes of the buffers should match that returned by `environ_sizes_get`. + */ + environ_get :: proc( + environ: [^]cstring, + environ_buf: [^]byte, + ) -> errno_t --- + /** + * Provide file advisory information on a file descriptor. + * Note: This is similar to `posix_fadvise` in POSIX. + */ + fd_advise :: proc( + fd: fd_t, + /** + * The offset within the file to which the advisory applies. + */ + offset: filesize_t, + /** + * The length of the region to which the advisory applies. + */ + len: filesize_t, + /** + * The advice. + */ + advice: advice_t, + ) -> errno_t --- + /** + * Force the allocation of space in a file. + * Note: This is similar to `posix_fallocate` in POSIX. + */ + fd_allocate :: proc( + fd: fd_t, + /** + * The offset at which to start the allocation. + */ + offset: filesize_t, + /** + * The length of the area that is allocated. + */ + len: filesize_t, + ) -> errno_t --- + /** + * Close a file descriptor. + * Note: This is similar to `close` in POSIX. + */ + fd_close :: proc( + fd: fd_t, + ) -> errno_t --- + /** + * Synchronize the data of a file to disk. + * Note: This is similar to `fdatasync` in POSIX. + */ + fd_datasync :: proc( + fd: fd_t, + ) -> errno_t --- + /** + * Adjust the flags associated with a file descriptor. + * Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. + */ + fd_fdstat_set_flags :: proc( + fd: fd_t, + /** + * The desired values of the file descriptor flags. + */ + flags: fdflags_t, + ) -> errno_t --- + /** + * Adjust the rights associated with a file descriptor. + * This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights + */ + fd_fdstat_set_rights :: proc( + fd: fd_t, + /** + * The desired rights of the file descriptor. + */ + fs_rights_base: rights_t, + fs_rights_inheritin: rights_t, + ) -> errno_t --- + /** + * Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. + * Note: This is similar to `ftruncate` in POSIX. + */ + fd_filestat_set_size :: proc( + fd: fd_t, + /** + * The desired file size. + */ + size: filesize_t, + ) -> errno_t --- + /** + * Adjust the timestamps of an open file or directory. + * Note: This is similar to `futimens` in POSIX. + */ + fd_filestat_set_times :: proc( + fd: fd_t, + /** + * The desired values of the data access timestamp. + */ + atim: timestamp_t, + /** + * The desired values of the data modification timestamp. + */ + mtim: timestamp_t, + /** + * A bitmask indicating which timestamps to adjust. + */ + fst_flags: fstflags_t, + ) -> errno_t --- + /** + * Return a description of the given preopened file descriptor. + */ + fd_prestat_dir_name :: proc( + fd: fd_t, + /** + * A buffer into which to write the preopened directory name. + */ + path: [^]u8, + path_len: size_t, + ) -> errno_t --- + /** + * Atomically replace a file descriptor by renumbering another file descriptor. + * Due to the strong focus on thread safety, this environment does not provide + * a mechanism to duplicate or renumber a file descriptor to an arbitrary + * number, like `dup2()`. This would be prone to race conditions, as an actual + * file descriptor with the same number could be allocated by a different + * thread at the same time. + * This function provides a way to atomically renumber file descriptors, which + * would disappear if `dup2()` were to be removed entirely. + */ + fd_renumber :: proc( + fd: fd_t, + /** + * The file descriptor to overwrite. + */ + to: fd_t, + ) -> errno_t --- + /** + * Synchronize the data and metadata of a file to disk. + * Note: This is similar to `fsync` in POSIX. + */ + fd_sync :: proc( + f: fd_t, + ) -> errno_t --- + /** + * Create a directory. + * Note: This is similar to `mkdirat` in POSIX. + */ + path_create_directory :: proc( + fd: fd_t, + /** + * The path at which to create the directory. + */ + path: cstring, + ) -> errno_t --- + /** + * Adjust the timestamps of a file or directory. + * Note: This is similar to `utimensat` in POSIX. + */ + path_filestat_set_times :: proc( + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to operate on. + */ + path: cstring, + /** + * The desired values of the data access timestamp. + */ + atim: timestamp_t, + /** + * The desired values of the data modification timestamp. + */ + mtim: timestamp_t, + /** + * A bitmask indicating which timestamps to adjust. + */ + fst_flags: fstflags_t, + ) -> errno_t --- + /** + * Create a hard link. + * Note: This is similar to `linkat` in POSIX. + */ + path_link :: proc( + old_fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + old_flags: lookupflags_t, + /** + * The source path from which to link. + */ + old_path: cstring, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path at which to create the hard link. + */ + new_path: cstring, + ) -> errno_t --- + /** + * Remove a directory. + * Return `errno::notempty` if the directory is not empty. + * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + */ + path_remove_directory :: proc( + fd: fd_t, + /** + * The path to a directory to remove. + */ + path: cstring, + ) -> errno_t --- + /** + * Rename a file or directory. + * Note: This is similar to `renameat` in POSIX. + */ + path_rename :: proc( + fd: fd_t, + /** + * The source path of the file or directory to rename. + */ + old_path: cstring, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path to which to rename the file or directory. + */ + new_path: cstring, + ) -> errno_t --- + /** + * Create a symbolic link. + * Note: This is similar to `symlinkat` in POSIX. + */ + path_symlink :: proc( + /** + * The contents of the symbolic link. + */ + old_path: cstring, + fd: fd_t, + /** + * The destination path at which to create the symbolic link. + */ + new_path: cstring, + ) -> errno_t --- + /** + * Unlink a file. + * Return `errno::isdir` if the path refers to a directory. + * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + */ + path_unlink_file :: proc( + fd: fd_t, + /** + * The path to a file to unlink. + */ + path: cstring, + ) -> errno_t --- + /** + * Terminate the process normally. An exit code of 0 indicates successful + * termination of the program. The meanings of other values is dependent on + * the environment. + */ + proc_exit :: proc( + /** + * The exit code returned by the process. + */ + rval: exitcode_t, + ) -> ! --- + /** + * Send a signal to the process of the calling thread. + * Note: This is similar to `raise` in POSIX. + */ + proc_raise :: proc( + /** + * The signal condition to trigger. + */ + sig: signal_t, + ) -> errno_t --- + /** + * Temporarily yield execution of the calling thread. + * Note: This is similar to `sched_yield` in POSIX. + */ + sched_yield :: proc() -> errno_t --- + /** + * Write high-quality random data into a buffer. + * This function blocks when the implementation is unable to immediately + * provide sufficient high-quality random data. + * This function may execute slowly, so when large mounts of random data are + * required, it's advisable to use this function to seed a pseudo-random + * number generator, rather than to provide the random data directly. + */ + random_get :: proc( + /** + * The buffer to fill with random data. + */ + buf: [^]u8, + buf_len: size_t, + ) -> errno_t --- + /** + * Shut down socket send and receive channels. + * Note: This is similar to `shutdown` in POSIX. + */ + sock_shutdown :: proc( + fd: fd_t, + /** + * Which channels on the socket to shut down. + */ + how: sdflags_t, + ) -> errno_t --- +} + + +foreign env { + __wasi_args_sizes_get :: proc( + retptr0: ^size_t, + retptr1: ^size_t, + ) -> errno_t --- + __wasi_environ_sizes_get :: proc( + retptr0: ^size_t, + retptr1: ^size_t, + ) -> errno_t --- + __wasi_clock_res_get :: proc( + id: clockid_t, + retptr0: ^timestamp_t, + ) -> errno_t --- + __wasi_clock_time_get :: proc( + id: clockid_t, + precision: timestamp_t, + retptr0: ^timestamp_t, + ) -> errno_t --- + __wasi_fd_fdstat_get :: proc( + fd: fd_t, + retptr0: ^fdstat_t, + ) -> errno_t --- + __wasi_fd_filestat_get :: proc( + fd: fd_t, + retptr0: ^filestat_t, + ) -> errno_t --- + + + __wasi_fd_pread :: proc( + fd: fd_t, + iovs: [^]iovec_t, + iovs_len: size_t, + offset: filesize_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_prestat_get :: proc( + fd: fd_t, + retptr0: ^prestat_t, + ) -> errno_t --- + __wasi_fd_pwrite :: proc( + fd: fd_t, + iovs: [^]ciovec_t, + iovs_len: size_t, + offset: filesize_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_read :: proc( + fd: fd_t, + iovs: [^]iovec_t, + iovs_len: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_readdir :: proc( + fd: fd_t, + buf: [^]u8, + buf_len: size_t, + cookie: dircookie_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_seek :: proc( + fd: fd_t, + offset: filedelta_t, + whence: whence_t, + retptr0: ^filesize_t, + ) -> errno_t --- + __wasi_fd_tell :: proc( + fd: fd_t, + retptr0: ^filesize_t, + ) -> errno_t --- + __wasi_fd_write :: proc( + fd: fd_t, + iovs: [^]ciovec_t, + iovs_len: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_path_filestat_get :: proc( + fd: fd_t, + flags: lookupflags_t, + /** + * The path of the file or directory to inspect. + */ + path: cstring, + retptr0: ^filestat_t, + ) -> errno_t --- + __wasi_path_open :: proc( + fd: fd_t, + dirflags: lookupflags_t, + path: cstring, + oflags: oflags_t, + fs_rights_base: rights_t, + fs_rights_inheriting: rights_t, + fdflags: fdflags_t, + retptr: ^fd_t, + ) -> errno_t --- + __wasi_path_readlink :: proc( + fd: fd_t, + path: cstring, + buf: [^]u8, + buf_len: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_poll_oneoff :: proc( + subscription_in: ^subscription_t, + event_out: ^event_t, + nsubscriptions: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_sock_recv :: proc( + fd: fd_t, + ri_data: [^]iovec_t, + ri_data_len: size_t, + ri_flags: riflags_t, + retptr0: ^size_t, + retptr1: ^roflags_t, + ) -> errno_t --- + __wasi_sock_send :: proc( + fd: fd_t, + si_data: [^]ciovec_t, + si_data_len: size_t, + si_flags: siflags_t, + retptr0: ^size_t, + ) -> errno_t --- +} + +/** + * Return command-line argument data sizes. + * @return + * Returns the number of arguments and the size of the argument string + * data, or an error. + */ +args_sizes_get :: proc() -> (num_args, size_of_args: size_t, err: errno_t) { + err = __wasi_args_sizes_get(&num_args, &size_of_args) + return +} +/** + * Return environment variable data sizes. + * @return + * Returns the number of environment variable arguments and the size of the + * environment variable data. + */ +environ_sizes_get :: proc() -> (num_envs, size_of_envs: size_t, err: errno_t) { + err = __wasi_environ_sizes_get(&num_envs, &size_of_envs) + return +} +/** + * Return the resolution of a clock. + * Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, + * return `errno::inval`. + * Note: This is similar to `clock_getres` in POSIX. + * @return + * The resolution of the clock, or an error if one happened. + */ +clock_res_get :: proc( + /** + * The clock for which to return the resolution. + */ + id: clockid_t, +) -> (ts: timestamp_t, err: errno_t) { + err = __wasi_clock_res_get(id, &ts) + return +} +/** + * Return the time value of a clock. + * Note: This is similar to `clock_gettime` in POSIX. + * @return + * The time value of the clock. + */ +clock_time_get :: proc( + /** + * The clock for which to return the time. + */ + id: clockid_t, + /** + * The maximum lag (exclusive) that the returned time value may have, compared to its actual value. + */ + precision: timestamp_t, +) -> (ts: timestamp_t, err: errno_t) { + err = __wasi_clock_time_get(id, precision, &ts) + return +} +/** + * Get the attributes of a file descriptor. + * Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. + * @return + * The buffer where the file descriptor's attributes are stored. + */ +fd_fdstat_get :: proc( + fd: fd_t, +) -> (stat: fdstat_t, err: errno_t) { + err = __wasi_fd_fdstat_get(fd, &stat) + return +} +/** + * Return the attributes of an open file. + * @return + * The buffer where the file's attributes are stored. + */ +fd_filestat_get :: proc( + fd: fd_t, +) -> (stat: filestat_t, err: errno_t) { + err = __wasi_fd_filestat_get(fd, &stat) + return +} + + + + +/** + * Read from a file descriptor, without using and updating the file descriptor's offset. + * Note: This is similar to `preadv` in POSIX. + * @return + * The number of bytes read. + */ +fd_pread :: proc( + fd: fd_t, + /** + * List of scatter/gather vectors in which to store data. + */ + iovs: [^]iovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, + /** + * The offset within the file at which to read. + */ + offset: filesize_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_fd_pread(fd, iovs, iovs_len, offset, &n) + return +} +/** + * Return a description of the given preopened file descriptor. + * @return + * The buffer where the description is stored. + */ +fd_prestat_get :: proc( + fd: fd_t, +) -> (desc: prestat_t, err: errno_t) { + err = __wasi_fd_prestat_get(fd, &desc) + return +} +/** + * Write to a file descriptor, without using and updating the file descriptor's offset. + * Note: This is similar to `pwritev` in POSIX. + * @return + * The number of bytes written. + */ +fd_pwrite :: proc( + fd: fd_t, + /** + * List of scatter/gather vectors from which to retrieve data. + */ + iovs: [^]ciovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, + /** + * The offset within the file at which to write. + */ + offset: filesize_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_fd_pwrite(fd, iovs, iovs_len, offset, &n) + return +} +/** + * Read from a file descriptor. + * Note: This is similar to `readv` in POSIX. + * @return + * The number of bytes read. + */ +fd_read :: proc( + fd: fd_t, + /** + * List of scatter/gather vectors to which to store data. + */ + iovs: [^]iovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_fd_read(fd, iovs, iovs_len, &n) + return +} +/** + * Read directory entries from a directory. + * When successful, the contents of the output buffer consist of a sequence of + * directory entries. Each directory entry consists of a `dirent` object, + * followed by `dirent::d_namlen` bytes holding the name of the directory + * entry. + * This function fills the output buffer as much as possible, potentially + * truncating the last directory entry. This allows the caller to grow its + * read buffer size in case it's too small to fit a single large directory + * entry, or skip the oversized directory entry. + * @return + * The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. + */ +fd_readdir :: proc( + fd: fd_t, + /** + * The buffer where directory entries are stored + */ + buf: [^]u8, + buf_len: size_t, + /** + * The location within the directory to start reading + */ + cookie: dircookie_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_fd_readdir(fd, buf, buf_len, cookie, &n) + return +} +/** + * Move the offset of a file descriptor. + * Note: This is similar to `lseek` in POSIX. + * @return + * The new offset of the file descriptor, relative to the start of the file. + */ +fd_seek :: proc( + fd: fd_t, + /** + * The number of bytes to move. + */ + offset: filedelta_t, + /** + * The base from which the offset is relative. + */ + whence: whence_t, +) -> (new_offset: filesize_t, err: errno_t) { + err = __wasi_fd_seek(fd, offset, whence, &new_offset) + return +} +/** + * Return the current offset of a file descriptor. + * Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. + * @return + * The current offset of the file descriptor, relative to the start of the file. + */ +fd_tell :: proc( + fd: fd_t, +) -> (offset: filesize_t, err: errno_t) { + err = __wasi_fd_tell(fd, &offset) + return +} +/** + * Write to a file descriptor. + * Note: This is similar to `writev` in POSIX. + */ +fd_write :: proc( + fd: fd_t, + /** + * List of scatter/gather vectors from which to retrieve data. + */ + iovs: [^]ciovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_fd_write(fd, iovs, iovs_len, &n) + return +} +/** + * Return the attributes of a file or directory. + * Note: This is similar to `stat` in POSIX. + * @return + * The buffer where the file's attributes are stored. + */ +path_filestat_get :: proc( + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to inspect. + */ + path: cstring, +) -> (offset: filestat_t, err: errno_t) { + err = __wasi_path_filestat_get(fd, flags, path, &offset) + return +} +/** + * Open a file or directory. + * The returned file descriptor is not guaranteed to be the lowest-numbered + * file descriptor not currently open; it is randomized to prevent + * applications from depending on making assumptions about indexes, since this + * is error-prone in multi-threaded contexts. The returned file descriptor is + * guaranteed to be less than 2**31. + * Note: This is similar to `openat` in POSIX. + * @return + * The file descriptor of the file that has been opened. + */ +path_open :: proc( + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + dirflags: lookupflags_t, + /** + * The relative path of the file or directory to open, relative to the + * `path_open::fd` directory. + */ + path: cstring, + /** + * The method by which to open the file. + */ + oflags: oflags_t, + /** + * The initial rights of the newly created file descriptor. The + * implementation is allowed to return a file descriptor with fewer rights + * than specified, if and only if those rights do not apply to the type of + * file being opened. + * The *base* rights are rights that will apply to operations using the file + * descriptor itself, while the *inheriting* rights are rights that apply to + * file descriptors derived from it. + */ + fs_rights_base: rights_t, + fs_rights_inheriting: rights_t, + fdflags: fdflags_t, +) -> (file: fd_t, err: errno_t) { + err = __wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) + return +} +/** + * Read the contents of a symbolic link. + * Note: This is similar to `readlinkat` in POSIX. + * @return + * The number of bytes placed in the buffer. + */ +path_readlink :: proc( + fd: fd_t, + /** + * The path of the symbolic link from which to read. + */ + path: cstring, + /** + * The buffer to which to write the contents of the symbolic link. + */ + buf: [^]u8, + buf_len: size_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_path_readlink(fd, path, buf, buf_len, &n) + return +} +/** + * Concurrently poll for the occurrence of a set of events. + * @return + * The number of events stored. + */ +poll_oneoff :: proc( + /** + * The events to which to subscribe. + */ + subscription_in: ^subscription_t, + /** + * The events that have occurred. + */ + event_out: ^event_t, + /** + * Both the number of subscriptions and events. + */ + nsubscriptions: size_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_poll_oneoff(subscription_in, event_out, nsubscriptions, &n) + return +} +/** + * Receive a message from a socket. + * Note: This is similar to `recv` in POSIX, though it also supports reading + * the data into multiple buffers in the manner of `readv`. + * @return + * Number of bytes stored in ri_data and message flags. + */ +sock_recv :: proc( + fd: fd_t, + /** + * List of scatter/gather vectors to which to store data. + */ + ri_data: [^]iovec_t, + /** + * The length of the array pointed to by `ri_data`. + */ + ri_data_len: size_t, + /** + * Message flags. + */ + ri_flags: riflags_t, +) -> (n: size_t, flags: roflags_t, err: errno_t) { + err = __wasi_sock_recv(fd, ri_data, ri_data_len, ri_flags, &n, &flags) + return +} +/** + * Send a message on a socket. + * Note: This is similar to `send` in POSIX, though it also supports writing + * the data from multiple buffers in the manner of `writev`. + * @return + * Number of bytes transmitted. + */ +sock_send :: proc( + fd: fd_t, + /** + * List of scatter/gather vectors to which to retrieve data + */ + si_data: [^]ciovec_t, + /** + * The length of the array pointed to by `si_data`. + */ + si_data_len: size_t, + /** + * Message flags. + */ + si_flags: siflags_t, +) -> (n: size_t, err: errno_t) { + err = __wasi_sock_send(fd, si_data, si_data_len, si_flags, &n) + return +} \ No newline at end of file From 9a5216921ca44fe25e66c81928f812f13f2d59e0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 01:08:17 +0000 Subject: [PATCH 05/27] Add `wasi_wasm32` --- core/os/os_js_wasm32.odin | 62 +++++++------- core/runtime/default_allocators_general.odin | 1 + core/runtime/internal.odin | 89 ++++++++++++++++++++ core/runtime/internal_linux.odin | 88 ------------------- core/runtime/internal_windows.odin | 88 ------------------- core/runtime/os_specific_any.odin | 3 +- src/build_settings.cpp | 31 ++++++- src/llvm_backend.cpp | 10 +++ src/string.cpp | 7 +- 9 files changed, 163 insertions(+), 216 deletions(-) diff --git a/core/os/os_js_wasm32.odin b/core/os/os_js_wasm32.odin index b70bdda1a..d2269cf90 100644 --- a/core/os/os_js_wasm32.odin +++ b/core/os/os_js_wasm32.odin @@ -1,70 +1,70 @@ package os -Handle :: distinct i32; -Errno :: distinct i32; +Handle :: distinct i32 +Errno :: distinct i32 -ERROR_NONE :: Errno(0); +ERROR_NONE :: Errno(0) -O_RDONLY :: 0x00000; -O_WRONLY :: 0x00001; -O_RDWR :: 0x00002; -O_CREATE :: 0x00040; -O_EXCL :: 0x00080; -O_NOCTTY :: 0x00100; -O_TRUNC :: 0x00200; -O_NONBLOCK :: 0x00800; -O_APPEND :: 0x00400; -O_SYNC :: 0x01000; -O_ASYNC :: 0x02000; -O_CLOEXEC :: 0x80000; +O_RDONLY :: 0x00000 +O_WRONLY :: 0x00001 +O_RDWR :: 0x00002 +O_CREATE :: 0x00040 +O_EXCL :: 0x00080 +O_NOCTTY :: 0x00100 +O_TRUNC :: 0x00200 +O_NONBLOCK :: 0x00800 +O_APPEND :: 0x00400 +O_SYNC :: 0x01000 +O_ASYNC :: 0x02000 +O_CLOEXEC :: 0x80000 -stdout: Handle; -stderr: Handle; -stdin: Handle; +stdout: Handle +stderr: Handle +stdin: Handle write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - return 0, 0; + return 0, 0 } read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - return 0, 0; + return 0, 0 } open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { - return 0, 0; + return 0, 0 } close :: proc(fd: Handle) -> Errno { - return 0; + return 0 } seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - return 0, 0; + return 0, 0 } current_thread_id :: proc "contextless" () -> int { - return 0; + return 0 } file_size :: proc(fd: Handle) -> (i64, Errno) { - return 0, 0; + return 0, 0 } heap_alloc :: proc(size: int) -> rawptr { - return nil; + return nil } heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { if new_size == 0 { - heap_free(ptr); - return nil; + heap_free(ptr) + return nil } if ptr == nil { - return heap_alloc(new_size); + return heap_alloc(new_size) } - return nil; + return nil } heap_free :: proc(ptr: rawptr) { if ptr == nil { - return; + return } } diff --git a/core/runtime/default_allocators_general.odin b/core/runtime/default_allocators_general.odin index f25f8f178..6bcfb68ae 100644 --- a/core/runtime/default_allocators_general.odin +++ b/core/runtime/default_allocators_general.odin @@ -1,5 +1,6 @@ //+build !windows //+build !freestanding +//+build !wasi package runtime when ODIN_DEFAULT_TO_NIL_ALLOCATOR { diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 0a6d07467..d681e581b 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -838,3 +838,92 @@ fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 { x := i64(a) return i128(x) } + + + + +@(link_name="__umodti3") +umodti3 :: proc "c" (a, b: u128) -> u128 { + r: u128 = --- + _ = udivmod128(a, b, &r) + return r +} + + +@(link_name="__udivmodti4") +udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + return udivmod128(a, b, rem) +} + +@(link_name="__udivti3") +udivti3 :: proc "c" (a, b: u128) -> u128 { + return udivmodti4(a, b, nil) +} + + +@(link_name="__modti3") +modti3 :: proc "c" (a, b: i128) -> i128 { + s_a := a >> (128 - 1) + s_b := b >> (128 - 1) + an := (a ~ s_a) - s_a + bn := (b ~ s_b) - s_b + + r: u128 = --- + _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r) + return (transmute(i128)r ~ s_a) - s_a +} + + +@(link_name="__divmodti4") +divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { + u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem) + return transmute(i128)u +} + +@(link_name="__divti3") +divti3 :: proc "c" (a, b: i128) -> i128 { + u := udivmodti4(transmute(u128)a, transmute(u128)b, nil) + return transmute(i128)u +} + + +@(link_name="__fixdfti") +fixdfti :: proc(a: u64) -> i128 { + significandBits :: 52 + typeWidth :: (size_of(u64)*8) + exponentBits :: (typeWidth - significandBits - 1) + maxExponent :: ((1 << exponentBits) - 1) + exponentBias :: (maxExponent >> 1) + + implicitBit :: (u64(1) << significandBits) + significandMask :: (implicitBit - 1) + signBit :: (u64(1) << (significandBits + exponentBits)) + absMask :: (signBit - 1) + exponentMask :: (absMask ~ significandMask) + + // Break a into sign, exponent, significand + aRep := a + aAbs := aRep & absMask + sign := i128(-1 if aRep & signBit != 0 else 1) + exponent := u64((aAbs >> significandBits) - exponentBias) + significand := u64((aAbs & significandMask) | implicitBit) + + // If exponent is negative, the result is zero. + if exponent < 0 { + return 0 + } + + // If the value is too large for the integer type, saturate. + if exponent >= size_of(i128) * 8 { + return max(i128) if sign == 1 else min(i128) + } + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if exponent < significandBits { + return sign * i128(significand >> (significandBits - exponent)) + } else { + return sign * (i128(significand) << (exponent - significandBits)) + } + +} diff --git a/core/runtime/internal_linux.odin b/core/runtime/internal_linux.odin index 359293de3..7ccdf5f69 100644 --- a/core/runtime/internal_linux.odin +++ b/core/runtime/internal_linux.odin @@ -1,89 +1 @@ package runtime - -import "core:intrinsics" - -@(link_name="__umodti3") -umodti3 :: proc "c" (a, b: u128) -> u128 { - r: u128 = --- - _ = udivmod128(a, b, &r) - return r -} - - -@(link_name="__udivmodti4") -udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { - return udivmod128(a, b, rem) -} - -@(link_name="__udivti3") -udivti3 :: proc "c" (a, b: u128) -> u128 { - return udivmodti4(a, b, nil) -} - - -@(link_name="__modti3") -modti3 :: proc "c" (a, b: i128) -> i128 { - s_a := a >> (128 - 1) - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a - bn := (b ~ s_b) - s_b - - r: u128 = --- - _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r) - return (transmute(i128)r ~ s_a) - s_a -} - - -@(link_name="__divmodti4") -divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { - u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem) - return transmute(i128)u -} - -@(link_name="__divti3") -divti3 :: proc "c" (a, b: i128) -> i128 { - u := udivmodti4(transmute(u128)a, transmute(u128)b, nil) - return transmute(i128)u -} - - -@(link_name="__fixdfti") -fixdfti :: proc(a: u64) -> i128 { - significandBits :: 52 - typeWidth :: (size_of(u64)*8) - exponentBits :: (typeWidth - significandBits - 1) - maxExponent :: ((1 << exponentBits) - 1) - exponentBias :: (maxExponent >> 1) - - implicitBit :: (u64(1) << significandBits) - significandMask :: (implicitBit - 1) - signBit :: (u64(1) << (significandBits + exponentBits)) - absMask :: (signBit - 1) - exponentMask :: (absMask ~ significandMask) - - // Break a into sign, exponent, significand - aRep := a - aAbs := aRep & absMask - sign := i128(-1 if aRep & signBit != 0 else 1) - exponent := u64((aAbs >> significandBits) - exponentBias) - significand := u64((aAbs & significandMask) | implicitBit) - - // If exponent is negative, the result is zero. - if exponent < 0 { - return 0 - } - - // If the value is too large for the integer type, saturate. - if exponent >= size_of(i128) * 8 { - return max(i128) if sign == 1 else min(i128) - } - - // If 0 <= exponent < significandBits, right shift to get the result. - // Otherwise, shift left. - if exponent < significandBits { - return sign * i128(significand >> (significandBits - exponent)) - } else { - return sign * (i128(significand) << (exponent - significandBits)) - } - -} diff --git a/core/runtime/internal_windows.odin b/core/runtime/internal_windows.odin index 4b932cd5f..7ccdf5f69 100644 --- a/core/runtime/internal_windows.odin +++ b/core/runtime/internal_windows.odin @@ -1,89 +1 @@ package runtime - -import "core:intrinsics" - -@(link_name="__umodti3") -umodti3 :: proc "c" (a, b: u128) -> u128 { - r: u128 = --- - _ = udivmod128(a, b, &r) - return r -} - - -@(link_name="__udivmodti4") -udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { - return udivmod128(a, b, rem) -} - -@(link_name="__udivti3") -udivti3 :: proc "c" (a, b: u128) -> u128 { - return udivmodti4(a, b, nil) -} - - -@(link_name="__modti3") -modti3 :: proc "c" (a, b: i128) -> i128 { - s_a := a >> (128 - 1) - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a - bn := (b ~ s_b) - s_b - - r: u128 = --- - _ = udivmod128(transmute(u128)an, transmute(u128)bn, &r) - return (transmute(i128)r ~ s_a) - s_a -} - - -@(link_name="__divmodti4") -divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { - u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem) - return transmute(i128)u -} - -@(link_name="__divti3") -divti3 :: proc "c" (a, b: i128) -> i128 { - u := udivmodti4(transmute(u128)a, transmute(u128)b, nil) - return transmute(i128)u -} - - -@(link_name="__fixdfti") -fixdfti :: proc(a: u64) -> i128 { - significandBits :: 52 - typeWidth :: (size_of(u64)*8) - exponentBits :: (typeWidth - significandBits - 1) - maxExponent :: ((1 << exponentBits) - 1) - exponentBias :: (maxExponent >> 1) - - implicitBit :: (u64(1) << significandBits) - significandMask :: (implicitBit - 1) - signBit :: (u64(1) << (significandBits + exponentBits)) - absMask :: (signBit - 1) - exponentMask :: (absMask ~ significandMask) - - // Break a into sign, exponent, significand - aRep := a - aAbs := aRep & absMask - sign := i128(-1 if aRep & signBit != 0 else 1) - exponent := (aAbs >> significandBits) - exponentBias - significand := (aAbs & significandMask) | implicitBit - - // If exponent is negative, the result is zero. - if exponent < 0 { - return 0 - } - - // If the value is too large for the integer type, saturate. - if exponent >= size_of(i128) * 8 { - return max(i128) if sign == 1 else min(i128) - } - - // If 0 <= exponent < significandBits, right shift to get the result. - // Otherwise, shift left. - if exponent < significandBits { - return sign * i128(significand >> (significandBits - exponent)) - } else { - return sign * (i128(significand) << (exponent - significandBits)) - } - -} diff --git a/core/runtime/os_specific_any.odin b/core/runtime/os_specific_any.odin index 397340798..d8608afdb 100644 --- a/core/runtime/os_specific_any.odin +++ b/core/runtime/os_specific_any.odin @@ -1,5 +1,4 @@ -//+build !freestanding -//+build !windows +//+build !freestanding !wasi !windows package runtime import "core:os" diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4c7faa1b2..d817c87f1 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -16,6 +16,8 @@ enum TargetOsKind { TargetOs_linux, TargetOs_essence, TargetOs_freebsd, + + TargetOs_wasi, TargetOs_freestanding, @@ -50,6 +52,8 @@ String target_os_names[TargetOs_COUNT] = { str_lit("linux"), str_lit("essence"), str_lit("freebsd"), + + str_lit("wasi"), str_lit("freestanding"), }; @@ -347,6 +351,16 @@ gb_global TargetMetrics target_freestanding_wasm64 = { str_lit(""), }; +gb_global TargetMetrics target_wasi_wasm32 = { + TargetOs_wasi, + TargetArch_wasm32, + 4, + 8, + str_lit("wasm32-wasi-js"), + str_lit(""), +}; + + @@ -367,6 +381,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 }, + { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, }; NamedTargetMetrics *selected_target_metrics; @@ -893,10 +908,18 @@ void init_build_context(TargetMetrics *cross_target) { bc->link_flags = str_lit("-arch arm64 "); break; } - } else if (bc->metrics.arch == TargetArch_wasm32) { - bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined --features=wasm-feature-atomics "); - } else if (bc->metrics.arch == TargetArch_wasm64) { - bc->link_flags = str_lit("--no-entry --export-table --export-all --allow-undefined -mwasm64 --features=wasm-feature-memory64,wasm-feature-atomics --verbose "); + } else if (is_arch_wasm()) { + gbString link_flags = gb_string_make(heap_allocator(), "--export-all "); + link_flags = gb_string_appendc(link_flags, "--export-table "); + link_flags = gb_string_appendc(link_flags, "--allow-undefined "); + if (bc->metrics.arch == TargetArch_wasm64) { + link_flags = gb_string_appendc(link_flags, "-mwas64 "); + } + if (bc->metrics.os == TargetOs_freestanding) { + link_flags = gb_string_appendc(link_flags, "--no-entry "); + } + + bc->link_flags = make_string_c(link_flags); } else { gb_printf_err("Compiler Error: Unsupported architecture\n"); gb_exit(1); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 892f615df..27914efb2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -784,6 +784,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true); } else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { name = str_lit("mainCRTStartup"); + } else if (build_context.metrics.os == TargetOs_wasi) { + name = str_lit("_start"); } else { has_args = true; slice_init(¶ms->Tuple.variables, permanent_allocator(), 2); @@ -885,6 +887,14 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) } lb_end_procedure_body(p); + + + if (build_context.metrics.os == TargetOs_wasi) { + LLVMSetLinkage(p->value, LLVMDLLExportLinkage); + } else { + LLVMSetLinkage(p->value, LLVMExternalLinkage); + } + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); diff --git a/src/string.cpp b/src/string.cpp index ca53fb2fc..800378689 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -21,13 +21,14 @@ struct String { }; // NOTE(bill): used for printf style arguments #define LIT(x) ((int)(x).len), (x).text -#define STR_LIT(c_str) {cast(u8 *)c_str, gb_size_of(c_str)-1} #if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700 - #define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1) + #define STR_LIT(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1) #else - #define str_lit(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1} + #define STR_LIT(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1} #endif +#define str_lit(c_str) STR_LIT(c_str) + // NOTE(bill): String16 is only used for Windows due to its file directories struct String16 { wchar_t *text; From c6e08b059b416ddcbaa4f33d051e04736398047e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 12:30:10 +0000 Subject: [PATCH 06/27] Move `sys/wasi` to `sys/wasm/wasi` --- .../wasi.odin => wasm/wasi/wasi_api.odin} | 454 +++++++++--------- 1 file changed, 226 insertions(+), 228 deletions(-) rename core/sys/{wasi/wasi.odin => wasm/wasi/wasi_api.odin} (90%) diff --git a/core/sys/wasi/wasi.odin b/core/sys/wasm/wasi/wasi_api.odin similarity index 90% rename from core/sys/wasi/wasi.odin rename to core/sys/wasm/wasi/wasi_api.odin index fde7601a2..ed905f2a6 100644 --- a/core/sys/wasi/wasi.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1,7 +1,7 @@ //+build wasm32 package sys_wasi -foreign import "env" +foreign import wasi "env" DIRCOOKIE_START :: u64(0) size_t :: uint @@ -971,7 +971,7 @@ prestat_t :: struct { } @(link_prefix="__wasi_") -foreign env { +foreign wasi { /** * Read command-line argument data. * The size of the array should match that returned by `args_sizes_get` @@ -1093,12 +1093,12 @@ foreign env { * Return a description of the given preopened file descriptor. */ fd_prestat_dir_name :: proc( - fd: fd_t, - /** - * A buffer into which to write the preopened directory name. - */ - path: [^]u8, - path_len: size_t, + fd: fd_t, + /** + * A buffer into which to write the preopened directory name. + */ + path: [^]u8, + path_len: size_t, ) -> errno_t --- /** * Atomically replace a file descriptor by renumbering another file descriptor. @@ -1111,11 +1111,11 @@ foreign env { * would disappear if `dup2()` were to be removed entirely. */ fd_renumber :: proc( - fd: fd_t, - /** - * The file descriptor to overwrite. - */ - to: fd_t, + fd: fd_t, + /** + * The file descriptor to overwrite. + */ + to: fd_t, ) -> errno_t --- /** * Synchronize the data and metadata of a file to disk. @@ -1129,61 +1129,61 @@ foreign env { * Note: This is similar to `mkdirat` in POSIX. */ path_create_directory :: proc( - fd: fd_t, - /** - * The path at which to create the directory. - */ - path: cstring, + fd: fd_t, + /** + * The path at which to create the directory. + */ + path: cstring, ) -> errno_t --- /** * Adjust the timestamps of a file or directory. * Note: This is similar to `utimensat` in POSIX. */ path_filestat_set_times :: proc( - fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - flags: lookupflags_t, - /** - * The path of the file or directory to operate on. - */ - path: cstring, - /** - * The desired values of the data access timestamp. - */ - atim: timestamp_t, - /** - * The desired values of the data modification timestamp. - */ - mtim: timestamp_t, - /** - * A bitmask indicating which timestamps to adjust. - */ - fst_flags: fstflags_t, + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to operate on. + */ + path: cstring, + /** + * The desired values of the data access timestamp. + */ + atim: timestamp_t, + /** + * The desired values of the data modification timestamp. + */ + mtim: timestamp_t, + /** + * A bitmask indicating which timestamps to adjust. + */ + fst_flags: fstflags_t, ) -> errno_t --- /** * Create a hard link. * Note: This is similar to `linkat` in POSIX. */ path_link :: proc( - old_fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - old_flags: lookupflags_t, - /** - * The source path from which to link. - */ - old_path: cstring, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path at which to create the hard link. - */ - new_path: cstring, + old_fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + old_flags: lookupflags_t, + /** + * The source path from which to link. + */ + old_path: cstring, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path at which to create the hard link. + */ + new_path: cstring, ) -> errno_t --- /** * Remove a directory. @@ -1191,45 +1191,45 @@ foreign env { * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. */ path_remove_directory :: proc( - fd: fd_t, - /** - * The path to a directory to remove. - */ - path: cstring, + fd: fd_t, + /** + * The path to a directory to remove. + */ + path: cstring, ) -> errno_t --- /** * Rename a file or directory. * Note: This is similar to `renameat` in POSIX. */ path_rename :: proc( - fd: fd_t, - /** - * The source path of the file or directory to rename. - */ - old_path: cstring, - /** - * The working directory at which the resolution of the new path starts. - */ + fd: fd_t, + /** + * The source path of the file or directory to rename. + */ + old_path: cstring, + /** + * The working directory at which the resolution of the new path starts. + */ new_fd: fd_t, - /** - * The destination path to which to rename the file or directory. - */ - new_path: cstring, + /** + * The destination path to which to rename the file or directory. + */ + new_path: cstring, ) -> errno_t --- /** * Create a symbolic link. * Note: This is similar to `symlinkat` in POSIX. */ path_symlink :: proc( - /** - * The contents of the symbolic link. - */ - old_path: cstring, + /** + * The contents of the symbolic link. + */ + old_path: cstring, fd: fd_t, - /** - * The destination path at which to create the symbolic link. - */ - new_path: cstring, + /** + * The destination path at which to create the symbolic link. + */ + new_path: cstring, ) -> errno_t --- /** * Unlink a file. @@ -1238,10 +1238,10 @@ foreign env { */ path_unlink_file :: proc( fd: fd_t, - /** - * The path to a file to unlink. - */ - path: cstring, + /** + * The path to a file to unlink. + */ + path: cstring, ) -> errno_t --- /** * Terminate the process normally. An exit code of 0 indicates successful @@ -1249,20 +1249,20 @@ foreign env { * the environment. */ proc_exit :: proc( - /** - * The exit code returned by the process. - */ - rval: exitcode_t, + /** + * The exit code returned by the process. + */ + rval: exitcode_t, ) -> ! --- /** * Send a signal to the process of the calling thread. * Note: This is similar to `raise` in POSIX. */ proc_raise :: proc( - /** - * The signal condition to trigger. - */ - sig: signal_t, + /** + * The signal condition to trigger. + */ + sig: signal_t, ) -> errno_t --- /** * Temporarily yield execution of the calling thread. @@ -1278,11 +1278,11 @@ foreign env { * number generator, rather than to provide the random data directly. */ random_get :: proc( - /** - * The buffer to fill with random data. - */ - buf: [^]u8, - buf_len: size_t, + /** + * The buffer to fill with random data. + */ + buf: [^]u8, + buf_len: size_t, ) -> errno_t --- /** * Shut down socket send and receive channels. @@ -1290,135 +1290,10 @@ foreign env { */ sock_shutdown :: proc( fd: fd_t, - /** - * Which channels on the socket to shut down. - */ - how: sdflags_t, - ) -> errno_t --- -} - - -foreign env { - __wasi_args_sizes_get :: proc( - retptr0: ^size_t, - retptr1: ^size_t, - ) -> errno_t --- - __wasi_environ_sizes_get :: proc( - retptr0: ^size_t, - retptr1: ^size_t, - ) -> errno_t --- - __wasi_clock_res_get :: proc( - id: clockid_t, - retptr0: ^timestamp_t, - ) -> errno_t --- - __wasi_clock_time_get :: proc( - id: clockid_t, - precision: timestamp_t, - retptr0: ^timestamp_t, - ) -> errno_t --- - __wasi_fd_fdstat_get :: proc( - fd: fd_t, - retptr0: ^fdstat_t, - ) -> errno_t --- - __wasi_fd_filestat_get :: proc( - fd: fd_t, - retptr0: ^filestat_t, - ) -> errno_t --- - - - __wasi_fd_pread :: proc( - fd: fd_t, - iovs: [^]iovec_t, - iovs_len: size_t, - offset: filesize_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_fd_prestat_get :: proc( - fd: fd_t, - retptr0: ^prestat_t, - ) -> errno_t --- - __wasi_fd_pwrite :: proc( - fd: fd_t, - iovs: [^]ciovec_t, - iovs_len: size_t, - offset: filesize_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_fd_read :: proc( - fd: fd_t, - iovs: [^]iovec_t, - iovs_len: size_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_fd_readdir :: proc( - fd: fd_t, - buf: [^]u8, - buf_len: size_t, - cookie: dircookie_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_fd_seek :: proc( - fd: fd_t, - offset: filedelta_t, - whence: whence_t, - retptr0: ^filesize_t, - ) -> errno_t --- - __wasi_fd_tell :: proc( - fd: fd_t, - retptr0: ^filesize_t, - ) -> errno_t --- - __wasi_fd_write :: proc( - fd: fd_t, - iovs: [^]ciovec_t, - iovs_len: size_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_path_filestat_get :: proc( - fd: fd_t, - flags: lookupflags_t, - /** - * The path of the file or directory to inspect. - */ - path: cstring, - retptr0: ^filestat_t, - ) -> errno_t --- - __wasi_path_open :: proc( - fd: fd_t, - dirflags: lookupflags_t, - path: cstring, - oflags: oflags_t, - fs_rights_base: rights_t, - fs_rights_inheriting: rights_t, - fdflags: fdflags_t, - retptr: ^fd_t, - ) -> errno_t --- - __wasi_path_readlink :: proc( - fd: fd_t, - path: cstring, - buf: [^]u8, - buf_len: size_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_poll_oneoff :: proc( - subscription_in: ^subscription_t, - event_out: ^event_t, - nsubscriptions: size_t, - retptr0: ^size_t, - ) -> errno_t --- - __wasi_sock_recv :: proc( - fd: fd_t, - ri_data: [^]iovec_t, - ri_data_len: size_t, - ri_flags: riflags_t, - retptr0: ^size_t, - retptr1: ^roflags_t, - ) -> errno_t --- - __wasi_sock_send :: proc( - fd: fd_t, - si_data: [^]ciovec_t, - si_data_len: size_t, - si_flags: siflags_t, - retptr0: ^size_t, + /** + * Which channels on the socket to shut down. + */ + how: sdflags_t, ) -> errno_t --- } @@ -1816,4 +1691,127 @@ sock_send :: proc( ) -> (n: size_t, err: errno_t) { err = __wasi_sock_send(fd, si_data, si_data_len, si_flags, &n) return -} \ No newline at end of file +} + + +foreign wasi { + __wasi_args_sizes_get :: proc( + retptr0: ^size_t, + retptr1: ^size_t, + ) -> errno_t --- + __wasi_environ_sizes_get :: proc( + retptr0: ^size_t, + retptr1: ^size_t, + ) -> errno_t --- + __wasi_clock_res_get :: proc( + id: clockid_t, + retptr0: ^timestamp_t, + ) -> errno_t --- + __wasi_clock_time_get :: proc( + id: clockid_t, + precision: timestamp_t, + retptr0: ^timestamp_t, + ) -> errno_t --- + __wasi_fd_fdstat_get :: proc( + fd: fd_t, + retptr0: ^fdstat_t, + ) -> errno_t --- + __wasi_fd_filestat_get :: proc( + fd: fd_t, + retptr0: ^filestat_t, + ) -> errno_t --- + __wasi_fd_pread :: proc( + fd: fd_t, + iovs: [^]iovec_t, + iovs_len: size_t, + offset: filesize_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_prestat_get :: proc( + fd: fd_t, + retptr0: ^prestat_t, + ) -> errno_t --- + __wasi_fd_pwrite :: proc( + fd: fd_t, + iovs: [^]ciovec_t, + iovs_len: size_t, + offset: filesize_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_read :: proc( + fd: fd_t, + iovs: [^]iovec_t, + iovs_len: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_readdir :: proc( + fd: fd_t, + buf: [^]u8, + buf_len: size_t, + cookie: dircookie_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_fd_seek :: proc( + fd: fd_t, + offset: filedelta_t, + whence: whence_t, + retptr0: ^filesize_t, + ) -> errno_t --- + __wasi_fd_tell :: proc( + fd: fd_t, + retptr0: ^filesize_t, + ) -> errno_t --- + __wasi_fd_write :: proc( + fd: fd_t, + iovs: [^]ciovec_t, + iovs_len: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_path_filestat_get :: proc( + fd: fd_t, + flags: lookupflags_t, + /** + * The path of the file or directory to inspect. + */ + path: cstring, + retptr0: ^filestat_t, + ) -> errno_t --- + __wasi_path_open :: proc( + fd: fd_t, + dirflags: lookupflags_t, + path: cstring, + oflags: oflags_t, + fs_rights_base: rights_t, + fs_rights_inheriting: rights_t, + fdflags: fdflags_t, + retptr: ^fd_t, + ) -> errno_t --- + __wasi_path_readlink :: proc( + fd: fd_t, + path: cstring, + buf: [^]u8, + buf_len: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_poll_oneoff :: proc( + subscription_in: ^subscription_t, + event_out: ^event_t, + nsubscriptions: size_t, + retptr0: ^size_t, + ) -> errno_t --- + __wasi_sock_recv :: proc( + fd: fd_t, + ri_data: [^]iovec_t, + ri_data_len: size_t, + ri_flags: riflags_t, + retptr0: ^size_t, + retptr1: ^roflags_t, + ) -> errno_t --- + __wasi_sock_send :: proc( + fd: fd_t, + si_data: [^]ciovec_t, + si_data_len: size_t, + si_flags: siflags_t, + retptr0: ^size_t, + ) -> errno_t --- +} From 2a5b8f53fe7cb7a4261b765020f9342005046b63 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 12:47:50 +0000 Subject: [PATCH 07/27] Add `memmove` and `memset` support for `wasm` --- core/runtime/procs.odin | 41 +++++++++++++++++++++++++++++++++------- src/build_settings.cpp | 5 +++-- src/llvm_backend.cpp | 12 +++++++++--- src/llvm_backend_opt.cpp | 5 +++++ 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin index d97a3fadf..09ad25379 100644 --- a/core/runtime/procs.odin +++ b/core/runtime/procs.odin @@ -1,12 +1,39 @@ package runtime -memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { - if ptr != nil && len != 0 { - b := byte(val) - p := ([^]byte)(ptr) - for i in 0.. rawptr { + if ptr != nil && len != 0 { + b := byte(val) + p := ([^]byte)(ptr) + for i in 0.. rawptr { + if dst != src { + d, s := ([^]byte)(dst), ([^]byte)(src) + d_end, s_end := d[len:], s[len:] + for i := len-1; i >= 0; i -= 1 { + d[i] = s[i] + } + } + return dst + + } +} else { + memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { + if ptr != nil && len != 0 { + b := byte(val) + p := ([^]byte)(ptr) + for i in 0..metrics.arch == TargetArch_wasm64) { link_flags = gb_string_appendc(link_flags, "-mwas64 "); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 27914efb2..d21ff8e5a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -771,6 +771,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) Type *results = alloc_type_tuple(); Type *t_ptr_cstring = alloc_type_pointer(t_cstring); + + bool call_cleanup = true; bool has_args = false; bool is_dll_main = false; @@ -782,10 +784,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("hinstDLL"), t_rawptr, false, true); params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true); params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true); + call_cleanup = false; } else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { name = str_lit("mainCRTStartup"); } else if (build_context.metrics.os == TargetOs_wasi) { name = str_lit("_start"); + call_cleanup = false; } else { has_args = true; slice_init(¶ms->Tuple.variables, permanent_allocator(), 2); @@ -876,8 +880,10 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) } - lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime")); - lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, false); + if (call_cleanup) { + lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime")); + lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, false); + } if (is_dll_main) { @@ -890,7 +896,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) if (build_context.metrics.os == TargetOs_wasi) { - LLVMSetLinkage(p->value, LLVMDLLExportLinkage); + LLVMSetLinkage(p->value, LLVMDLLExportLinkage); } else { LLVMSetLinkage(p->value, LLVMExternalLinkage); } diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 25e290d70..8ddd3360d 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -383,6 +383,11 @@ void lb_run_remove_unused_function_pass(LLVMModuleRef mod) { continue; } + if (name == "memset" || + name == "memmove" || + name == "memcpy") { + continue; + } LLVMLinkage linkage = LLVMGetLinkage(curr_func); From 2a7937e2bac16e587882a0a26e6d5929ed81d700 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 31 Oct 2021 13:48:13 +0100 Subject: [PATCH 08/27] Add `odin report` command to help with bug reports. Add new Odin command, `odin report`, which prints information helpful to resolving or reporting a bug. ``` W:\Odin> odin report Where to find more information and get into contact when you encounter a bug: Website: https://odin-lang.org GitHub: https://github.com/odin-lang/Odin/issues Useful information to add to a bug report: Odin: dev-2021-10:256bebfe OS: Windows 10 Professional (version: 20H2), build 19042.1266 CPU: AMD Ryzen 7 1800X Eight-Core Processor RAM: 65469 MiB W:\Odin> TODO: - CPU name on ARM/ARM64 ``` --- .github/workflows/ci.yml | 9 + Makefile | 3 + src/bug_report.cpp | 641 +++++++++++++++++++++++++++++++++++++++ src/build_settings.cpp | 15 +- src/main.cpp | 31 +- 5 files changed, 678 insertions(+), 21 deletions(-) create mode 100644 src/bug_report.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce4f198e6..608538e90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,9 @@ jobs: - name: Odin version run: ./odin version timeout-minutes: 1 + - name: Odin report + run: ./odin report + timeout-minutes: 1 - name: Odin check run: ./odin check examples/demo/demo.odin -vet timeout-minutes: 10 @@ -39,6 +42,9 @@ jobs: - name: Odin version run: ./odin version timeout-minutes: 1 + - name: Odin report + run: ./odin report + timeout-minutes: 1 - name: Odin check run: ./odin check examples/demo/demo.odin -vet timeout-minutes: 10 @@ -57,6 +63,9 @@ jobs: - name: Odin version run: ./odin version timeout-minutes: 1 + - name: Odin report + run: ./odin report + timeout-minutes: 1 - name: Odin check shell: cmd run: | diff --git a/Makefile b/Makefile index cdf4f0fc8..fa07ec689 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,9 @@ all: debug demo demo: ./odin run examples/demo/demo.odin +report: + ./odin report + debug: $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin diff --git a/src/bug_report.cpp b/src/bug_report.cpp new file mode 100644 index 000000000..d6b5be551 --- /dev/null +++ b/src/bug_report.cpp @@ -0,0 +1,641 @@ +/* + Gather and print platform and version info to help with reporting Odin bugs. +*/ + +#if !defined(GB_COMPILER_MSVC) + #if defined(GB_CPU_X86) + #include + #endif +#endif + +#if defined(GB_SYSTEM_LINUX) + #include + #include +#endif + +#if defined(GB_SYSTEM_OSX) + #include +#endif + +/* + NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`. +*/ +#if defined(GB_SYSTEM_WINDOWS) +void report_windows_product_type(DWORD ProductType) { + switch (ProductType) { + case PRODUCT_ULTIMATE: + gb_printf("Ultimate"); + break; + + case PRODUCT_HOME_BASIC: + gb_printf("Home Basic"); + break; + + case PRODUCT_HOME_PREMIUM: + gb_printf("Home Premium"); + break; + + case PRODUCT_ENTERPRISE: + gb_printf("Enterprise"); + break; + + case PRODUCT_HOME_BASIC_N: + gb_printf("Home Basic N"); + break; + + case PRODUCT_BUSINESS: + gb_printf("Business"); + break; + + case PRODUCT_STANDARD_SERVER: + gb_printf("Standard Server"); + break; + + case PRODUCT_DATACENTER_SERVER: + gb_printf("Datacenter"); + break; + + case PRODUCT_SMALLBUSINESS_SERVER: + gb_printf("Windows Small Business Server"); + break; + + case PRODUCT_ENTERPRISE_SERVER: + gb_printf("Enterprise Server"); + break; + + case PRODUCT_STARTER: + gb_printf("Starter"); + break; + + case PRODUCT_DATACENTER_SERVER_CORE: + gb_printf("Datacenter Server Core"); + break; + + case PRODUCT_STANDARD_SERVER_CORE: + gb_printf("Server Standard Core"); + break; + + case PRODUCT_ENTERPRISE_SERVER_CORE: + gb_printf("Enterprise Server Core"); + break; + + case PRODUCT_BUSINESS_N: + gb_printf("Business N"); + break; + + case PRODUCT_HOME_SERVER: + gb_printf("Home Server"); + break; + + case PRODUCT_SERVER_FOR_SMALLBUSINESS: + gb_printf("Windows Server 2008 for Windows Essential Server Solutions"); + break; + + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + gb_printf("Small Business Server Premium"); + break; + + case PRODUCT_HOME_PREMIUM_N: + gb_printf("Home Premium N"); + break; + + case PRODUCT_ENTERPRISE_N: + gb_printf("Enterprise N"); + break; + + case PRODUCT_ULTIMATE_N: + gb_printf("Ultimate N"); + break; + + case PRODUCT_HYPERV: + gb_printf("HyperV"); + break; + + case PRODUCT_STARTER_N: + gb_printf("Starter N"); + break; + + case PRODUCT_PROFESSIONAL: + gb_printf("Professional"); + break; + + case PRODUCT_PROFESSIONAL_N: + gb_printf("Professional N"); + break; + + case PRODUCT_UNLICENSED: + gb_printf("Unlicensed"); + break; + + default: + gb_printf("Unknown Edition (%08x)", ProductType); + } +} +#endif + +void odin_cpuid(int leaf, int result[]) { + #if defined(GB_COMPILER_MSVC) + __cpuid(result, leaf); + #else + __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]); + #endif +} + +void report_cpu_info() { + gb_printf("\tCPU: "); + + #if defined(GB_CPU_X86) + + /* + Get extended leaf info + */ + int cpu[4]; + + odin_cpuid(0x80000000, &cpu[0]); + int number_of_extended_ids = cpu[0]; + + int brand[0x12] = {}; + + /* + Read CPU brand if supported. + */ + if (number_of_extended_ids >= 0x80000004) { + odin_cpuid(0x80000002, &brand[0]); + odin_cpuid(0x80000003, &brand[4]); + odin_cpuid(0x80000004, &brand[8]); + + /* + Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them. + */ + char * brand_name = (char *)&brand[0]; + for (; brand_name[0] == ' '; brand_name++) {} + + gb_printf("%s\n", brand_name); + } else { + gb_printf("Unable to retrieve.\n"); + } + + #elif defined(GB_CPU_ARM) + /* + TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`. + */ + #if defined(GB_ARCH_64_BIT) + gb_printf("ARM64\n"); + #else + gb_printf("ARM\n"); + #endif + #else + gb_printf("Unknown\n"); + #endif +} + +/* + Report the amount of installed RAM. +*/ +void report_ram_info() { + + gb_printf("\tRAM: "); + + #if defined(GB_SYSTEM_WINDOWS) + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx (&statex); + + gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1)); + + #elif defined(GB_SYSTEM_LINUX) + /* + Retrieve RAM info using `sysinfo()`, + */ + struct sysinfo info; + int result = sysinfo(&info); + + if (result == 0x0) { + gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1)); + } else { + gb_printf("Unknown.\n"); + } + #elif defined(GB_SYSTEM_OSX) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int sysctls[] = { CTL_HW, HW_MEMSIZE }; + if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); + } + #else + gb_printf("Unknown.\n"); + #endif +} + +/* + NOTE(Jeroen): `odin report` prints some system information for easier bug reporting. +*/ +void print_bug_report_help() { + gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n"); + gb_printf("\tWebsite: https://odin-lang.org\n"); + gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n"); + /* + Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site. + gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n"); + */ + gb_printf("\n\n"); + + gb_printf("Useful information to add to a bug report:\n\n"); + + gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION)); + + #ifdef NIGHTLY + gb_printf("-nightly"); + #endif + + #ifdef GIT_SHA + gb_printf(":%s", GIT_SHA); + #endif + + gb_printf("\n"); + + /* + Print OS information. + */ + gb_printf("\tOS: "); + + #if defined(GB_SYSTEM_WINDOWS) + /* + NOTE(Jeroen): + `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. + `RtlGetVersion` will return the true version. + + Rather than include the WinDDK, we ask the kernel directly. + + `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release) + + */ + OSVERSIONINFOEXW osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + + typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*); + typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType); + + // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion"); + // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo + GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); + + NTSTATUS status = {}; + DWORD ProductType = {}; + if (RtlGetVersion != nullptr) { + status = RtlGetVersion((OSVERSIONINFOW*)&osvi); + } + + if (RtlGetVersion == nullptr || status != 0x0) { + gb_printf("Windows (Unknown Version)"); + } else { + if (GetProductInfo != nullptr) { + GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType); + } + + if (false) { + gb_printf("dwMajorVersion: %d\n", osvi.dwMajorVersion); + gb_printf("dwMinorVersion: %d\n", osvi.dwMinorVersion); + gb_printf("dwBuildNumber: %d\n", osvi.dwBuildNumber); + gb_printf("dwPlatformId: %d\n", osvi.dwPlatformId); + gb_printf("wServicePackMajor: %d\n", osvi.wServicePackMajor); + gb_printf("wServicePackMinor: %d\n", osvi.wServicePackMinor); + gb_printf("wSuiteMask: %d\n", osvi.wSuiteMask); + gb_printf("wProductType: %d\n", osvi.wProductType); + } + + gb_printf("Windows "); + + switch (osvi.dwMajorVersion) { + case 10: + /* + Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server + */ + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: // Workstation + if (osvi.dwBuildNumber < 22000) { + gb_printf("10 "); + } else { + gb_printf("11 "); + } + + report_windows_product_type(ProductType); + + break; + default: // Server or Domain Controller + switch(osvi.dwBuildNumber) { + case 14393: + gb_printf("2016 Server"); + break; + case 17763: + gb_printf("2019 Server"); + break; + case 20348: + gb_printf("2022 Server"); + break; + default: + gb_printf("Unknown Server"); + break; + } + } + break; + case 6: + switch (osvi.dwMinorVersion) { + case 0: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows Vista "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2008"); + break; + } + break; + + case 1: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 7 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2008 R2"); + break; + } + break; + case 2: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 8 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2012"); + break; + } + break; + case 3: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 8.1 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2012 R2"); + break; + } + break; + } + break; + case 5: + switch (osvi.dwMinorVersion) { + case 0: + gb_printf("Windows 2000"); + break; + case 1: + gb_printf("Windows XP"); + break; + case 2: + gb_printf("Windows Server 2003"); + break; + } + break; + default: + break; + } + + /* + Grab Windows DisplayVersion (like 20H02) + */ + LPDWORD ValueType = {}; + DWORD UBR; + char DisplayVersion[256]; + DWORD ValueSize = 256; + + status = RegGetValue( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + TEXT("DisplayVersion"), + RRF_RT_REG_SZ, + ValueType, + &DisplayVersion, + &ValueSize + ); + + if (status == 0x0) { + gb_printf(" (version: %s)", &DisplayVersion); + } + + /* + Now print build number. + */ + gb_printf(", build %d", osvi.dwBuildNumber); + + ValueSize = sizeof(UBR); + status = RegGetValue( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + TEXT("UBR"), + RRF_RT_REG_DWORD, + ValueType, + &UBR, + &ValueSize + ); + + if (status == 0x0) { + gb_printf(".%d", UBR); + } + gb_printf("\n"); + } + + #elif defined(GB_SYSTEM_LINUX) + /* + Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` + */ + gbAllocator a = heap_allocator(); + + gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release"); + defer (gb_file_free_contents(&release)); + + b32 found = 0; + if (release.size) { + char *start = (char *)release.data; + char *end = (char *)release.data + release.size; + const char *needle = "PRETTY_NAME=\""; + isize needle_len = gb_strlen((needle)); + + char *c = start; + for (; c < end; c++) { + if (gb_strncmp(c, needle, needle_len) == 0) { + found = 1; + start = c + needle_len; + break; + } + } + + if (found) { + for (c = start; c < end; c++) { + if (*c == '"') { + // Found the closing quote. Replace it with \0 + *c = 0; + gb_printf("%s", (char *)start); + break; + } else if (*c == '\n') { + found = 0; + } + } + } + } + + if (!found) { + gb_printf("Unknown Linux Distro"); + } + + /* + Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname + */ + char buffer[1024]; + uname((struct utsname *)&buffer[0]); + + struct utsname *info; + info = (struct utsname *)&buffer[0]; + + gb_printf(", %s %s\n", info->sysname, info->release); + + #elif defined(GB_SYSTEM_OSX) + b32 found = 1; + uint32_t major, minor; + + #define osx_version_buffer 100 + char buffer[osx_version_buffer]; + size_t buffer_size = osx_version_buffer - 1; + #undef osx_version_buffer + + int sysctls[] = { CTL_KERN, KERN_OSRELEASE }; + if (sysctl(sysctls, 2, buffer, &buffer_size, NULL, 0) == -1) { + found = 0; + } else { + if (sscanf(buffer, "%u.%u", &major, &minor) != 2) { + found = 0; + } + } + + if (found) { + switch (major) { + case 21: + /* + macOS Big Sur and newer + */ + major -= 9; + + gb_printf("macOS 12.0.%d Monterey\n", minor); + + break; + case 20: + /* + macOS Big Sur and newer + */ + major -= 9; + + gb_printf("macOS 11.%d Big Sur\n", minor); + + break; + case 14: + /* + macOS 10.1.1 and newer + */ + major -= 4; + + switch (minor) { + case 1: + gb_printf("macOS Puma 10.1\n"); + break; + + case 2: + gb_printf("macOS Jaguar 10.2\n"); + break; + + case 3: + gb_printf("macOS Panther 10.3\n"); + break; + + case 4: + gb_printf("macOS Tiger 10.4\n"); + break; + + case 5: + gb_printf("macOS Leopard 10.5\n"); + break; + + case 6: + gb_printf("macOS Snow Leopard 10.6\n"); + break; + + case 7: + gb_printf("macOS Lion 10.7\n"); + break; + + case 8: + gb_printf("macOS Mountain Lion 10.8\n"); + break; + + case 9: + gb_printf("macOS Mavericks 10.9\n"); + break; + + case 10: + gb_printf("macOS Yosemite 10.10\n"); + break; + + case 11: + gb_printf("macOS El Capitan 10.11\n"); + break; + + case 12: + gb_printf("macOS Sierra 10.12\n"); + break; + + case 13: + gb_printf("macOS High Sierra 10.13\n"); + break; + + case 14: + gb_printf("macOS Mojave 10.14\n"); + break; + + case 15: + gb_printf("macOS Catalina 10.15\n"); + break; + + default: + gb_printf("macOS %d.%d\n", major, minor); + break; + } + + break; + default: + gb_printf("macOS Unknown (kernel version %d.%d)\n", major, minor); + break; + } + } else { + gb_printf("macOS: Unknown\n"); + } + #else + gb_printf("Unknown\n"); + + #endif + + /* + Now print CPU info. + */ + report_cpu_info(); + + /* + And RAM info. + */ + report_ram_info(); +} \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 69e1ec5f0..830e34ac3 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -113,15 +113,16 @@ enum BuildModeKind { }; enum CommandKind : u32 { - Command_run = 1<<0, - Command_build = 1<<1, - Command_check = 1<<3, - Command_query = 1<<4, - Command_doc = 1<<5, - Command_version = 1<<6, - Command_test = 1<<7, + Command_run = 1<<0, + Command_build = 1<<1, + Command_check = 1<<3, + Command_query = 1<<4, + Command_doc = 1<<5, + Command_version = 1<<6, + Command_test = 1<<7, Command_strip_semicolon = 1<<8, + Command_bug_report = 1<<9, Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test|Command_strip_semicolon, Command__does_build = Command_run|Command_build|Command_test, diff --git a/src/main.cpp b/src/main.cpp index 92e541384..f6f7740e9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,6 +64,8 @@ gb_global Timings global_timings = {0}; #include "microsoft_craziness.h" #endif +#include "bug_report.cpp" + // NOTE(bill): 'name' is used in debugging and profiling modes i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { @@ -96,8 +98,8 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1)); if (CreateProcessW(nullptr, wcmd.text, - nullptr, nullptr, true, 0, nullptr, nullptr, - &start_info, &pi)) { + nullptr, nullptr, true, 0, nullptr, nullptr, + &start_info, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); @@ -212,7 +214,7 @@ i32 linker_stage(lbGenerator *gen) { String lib = m->foreign_library_paths[i]; GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1); gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), - " \"%.*s\"", LIT(lib)); + " \"%.*s\"", LIT(lib)); lib_str = gb_string_appendc(lib_str, lib_str_buf); } } @@ -221,7 +223,7 @@ i32 linker_stage(lbGenerator *gen) { String lib = gen->default_module.foreign_library_paths[i]; GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1); gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), - " \"%.*s\"", LIT(lib)); + " \"%.*s\"", LIT(lib)); lib_str = gb_string_appendc(lib_str, lib_str_buf); } @@ -320,7 +322,7 @@ i32 linker_stage(lbGenerator *gen) { ); if (result) { - return result; + return result; } } #else @@ -513,10 +515,6 @@ Array setup_args(int argc, char const **argv) { #endif } - - - - void print_usage_line(i32 indent, char const *fmt, ...) { while (indent --> 0) { gb_printf_err("\t"); @@ -540,6 +538,7 @@ void usage(String argv0) { print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files"); print_usage_line(1, "version print version"); + print_usage_line(1, "report print information useful to reporting a bug"); print_usage_line(0, ""); print_usage_line(0, "For more information of flags, apply the flag to see what is possible"); print_usage_line(1, "-help"); @@ -856,12 +855,12 @@ bool parse_build_flags(Array args) { break; case BuildFlagParam_Boolean: { if (str_eq_ignore_case(param, str_lit("t")) || - str_eq_ignore_case(param, str_lit("true")) || - param == "1") { + str_eq_ignore_case(param, str_lit("true")) || + param == "1") { value = exact_value_bool(true); } else if (str_eq_ignore_case(param, str_lit("f")) || - str_eq_ignore_case(param, str_lit("false")) || - param == "0") { + str_eq_ignore_case(param, str_lit("false")) || + param == "0") { value = exact_value_bool(false); } else { gb_printf_err("Invalid flag parameter for '%.*s' : '%.*s'\n", LIT(name), LIT(param)); @@ -2373,6 +2372,10 @@ int main(int arg_count, char const **arg_ptr) { gb_printf("\n"); return 0; + } else if (command == "report") { + build_context.command_kind = Command_bug_report; + print_bug_report_help(); + return 0; } else { usage(args[0]); return 1; @@ -2396,7 +2399,7 @@ int main(int arg_count, char const **arg_ptr) { // NOTE(bill): add 'shared' directory if it is not already set if (!find_library_collection_path(str_lit("shared"), nullptr)) { add_library_collection(str_lit("shared"), - get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); + get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); } From 906c7ef0fccde75fca9a8b68acf073f2736084b1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 13:04:28 +0000 Subject: [PATCH 09/27] Correct wasi linking --- core/sys/wasm/wasi/wasi_api.odin | 416 ++++++++++++++++--------------- 1 file changed, 218 insertions(+), 198 deletions(-) diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index ed905f2a6..3226d5a36 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1,7 +1,7 @@ //+build wasm32 package sys_wasi -foreign import wasi "env" +foreign import wasi "wasi_unstable" DIRCOOKIE_START :: u64(0) size_t :: uint @@ -970,7 +970,7 @@ prestat_t :: struct { }, } -@(link_prefix="__wasi_") +@(default_calling_convention="c") foreign wasi { /** * Read command-line argument data. @@ -1303,8 +1303,8 @@ foreign wasi { * Returns the number of arguments and the size of the argument string * data, or an error. */ -args_sizes_get :: proc() -> (num_args, size_of_args: size_t, err: errno_t) { - err = __wasi_args_sizes_get(&num_args, &size_of_args) +args_sizes_get :: proc "c" () -> (num_args, size_of_args: size_t, err: errno_t) { + err = wasi_args_sizes_get(&num_args, &size_of_args) return } /** @@ -1313,8 +1313,8 @@ args_sizes_get :: proc() -> (num_args, size_of_args: size_t, err: errno_t) { * Returns the number of environment variable arguments and the size of the * environment variable data. */ -environ_sizes_get :: proc() -> (num_envs, size_of_envs: size_t, err: errno_t) { - err = __wasi_environ_sizes_get(&num_envs, &size_of_envs) +environ_sizes_get :: proc "c" () -> (num_envs, size_of_envs: size_t, err: errno_t) { + err = wasi_environ_sizes_get(&num_envs, &size_of_envs) return } /** @@ -1325,13 +1325,13 @@ environ_sizes_get :: proc() -> (num_envs, size_of_envs: size_t, err: errno_t) { * @return * The resolution of the clock, or an error if one happened. */ -clock_res_get :: proc( +clock_res_get :: proc "c" ( /** * The clock for which to return the resolution. */ id: clockid_t, ) -> (ts: timestamp_t, err: errno_t) { - err = __wasi_clock_res_get(id, &ts) + err = wasi_clock_res_get(id, &ts) return } /** @@ -1340,7 +1340,7 @@ clock_res_get :: proc( * @return * The time value of the clock. */ -clock_time_get :: proc( +clock_time_get :: proc "c" ( /** * The clock for which to return the time. */ @@ -1350,7 +1350,7 @@ clock_time_get :: proc( */ precision: timestamp_t, ) -> (ts: timestamp_t, err: errno_t) { - err = __wasi_clock_time_get(id, precision, &ts) + err = wasi_clock_time_get(id, precision, &ts) return } /** @@ -1359,10 +1359,10 @@ clock_time_get :: proc( * @return * The buffer where the file descriptor's attributes are stored. */ -fd_fdstat_get :: proc( +fd_fdstat_get :: proc "c" ( fd: fd_t, ) -> (stat: fdstat_t, err: errno_t) { - err = __wasi_fd_fdstat_get(fd, &stat) + err = wasi_fd_fdstat_get(fd, &stat) return } /** @@ -1370,10 +1370,10 @@ fd_fdstat_get :: proc( * @return * The buffer where the file's attributes are stored. */ -fd_filestat_get :: proc( +fd_filestat_get :: proc "c" ( fd: fd_t, ) -> (stat: filestat_t, err: errno_t) { - err = __wasi_fd_filestat_get(fd, &stat) + err = wasi_fd_filestat_get(fd, &stat) return } @@ -1386,22 +1386,22 @@ fd_filestat_get :: proc( * @return * The number of bytes read. */ -fd_pread :: proc( +fd_pread :: proc "c" ( fd: fd_t, - /** - * List of scatter/gather vectors in which to store data. - */ - iovs: [^]iovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, - /** - * The offset within the file at which to read. - */ - offset: filesize_t, + /** + * List of scatter/gather vectors in which to store data. + */ + iovs: [^]iovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, + /** + * The offset within the file at which to read. + */ + offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_fd_pread(fd, iovs, iovs_len, offset, &n) + err = wasi_fd_pread(fd, iovs, iovs_len, offset, &n) return } /** @@ -1409,10 +1409,10 @@ fd_pread :: proc( * @return * The buffer where the description is stored. */ -fd_prestat_get :: proc( +fd_prestat_get :: proc "c" ( fd: fd_t, ) -> (desc: prestat_t, err: errno_t) { - err = __wasi_fd_prestat_get(fd, &desc) + err = wasi_fd_prestat_get(fd, &desc) return } /** @@ -1421,22 +1421,22 @@ fd_prestat_get :: proc( * @return * The number of bytes written. */ -fd_pwrite :: proc( +fd_pwrite :: proc "c" ( fd: fd_t, - /** - * List of scatter/gather vectors from which to retrieve data. - */ - iovs: [^]ciovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, - /** - * The offset within the file at which to write. - */ - offset: filesize_t, + /** + * List of scatter/gather vectors from which to retrieve data. + */ + iovs: [^]ciovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, + /** + * The offset within the file at which to write. + */ + offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_fd_pwrite(fd, iovs, iovs_len, offset, &n) + err = wasi_fd_pwrite(fd, iovs, iovs_len, offset, &n) return } /** @@ -1445,18 +1445,18 @@ fd_pwrite :: proc( * @return * The number of bytes read. */ -fd_read :: proc( +fd_read :: proc "c" ( fd: fd_t, - /** - * List of scatter/gather vectors to which to store data. - */ - iovs: [^]iovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, + /** + * List of scatter/gather vectors to which to store data. + */ + iovs: [^]iovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_fd_read(fd, iovs, iovs_len, &n) + err = wasi_fd_read(fd, iovs, iovs_len, &n) return } /** @@ -1472,19 +1472,19 @@ fd_read :: proc( * @return * The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. */ -fd_readdir :: proc( +fd_readdir :: proc "c" ( fd: fd_t, - /** - * The buffer where directory entries are stored - */ - buf: [^]u8, - buf_len: size_t, - /** - * The location within the directory to start reading - */ - cookie: dircookie_t, + /** + * The buffer where directory entries are stored + */ + buf: [^]u8, + buf_len: size_t, + /** + * The location within the directory to start reading + */ + cookie: dircookie_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_fd_readdir(fd, buf, buf_len, cookie, &n) + err = wasi_fd_readdir(fd, buf, buf_len, cookie, &n) return } /** @@ -1493,18 +1493,18 @@ fd_readdir :: proc( * @return * The new offset of the file descriptor, relative to the start of the file. */ -fd_seek :: proc( +fd_seek :: proc "c" ( fd: fd_t, - /** - * The number of bytes to move. - */ - offset: filedelta_t, - /** - * The base from which the offset is relative. - */ - whence: whence_t, + /** + * The number of bytes to move. + */ + offset: filedelta_t, + /** + * The base from which the offset is relative. + */ + whence: whence_t, ) -> (new_offset: filesize_t, err: errno_t) { - err = __wasi_fd_seek(fd, offset, whence, &new_offset) + err = wasi_fd_seek(fd, offset, whence, &new_offset) return } /** @@ -1513,28 +1513,28 @@ fd_seek :: proc( * @return * The current offset of the file descriptor, relative to the start of the file. */ -fd_tell :: proc( +fd_tell :: proc "c" ( fd: fd_t, ) -> (offset: filesize_t, err: errno_t) { - err = __wasi_fd_tell(fd, &offset) + err = wasi_fd_tell(fd, &offset) return } /** * Write to a file descriptor. * Note: This is similar to `writev` in POSIX. */ -fd_write :: proc( +fd_write :: proc "c" ( fd: fd_t, - /** - * List of scatter/gather vectors from which to retrieve data. - */ - iovs: [^]ciovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, + /** + * List of scatter/gather vectors from which to retrieve data. + */ + iovs: [^]ciovec_t, + /** + * The length of the array pointed to by `iovs`. + */ + iovs_len: size_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_fd_write(fd, iovs, iovs_len, &n) + err = wasi_fd_write(fd, iovs, iovs_len, &n) return } /** @@ -1543,18 +1543,18 @@ fd_write :: proc( * @return * The buffer where the file's attributes are stored. */ -path_filestat_get :: proc( +path_filestat_get :: proc "c" ( fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - flags: lookupflags_t, - /** - * The path of the file or directory to inspect. - */ - path: cstring, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to inspect. + */ + path: cstring, ) -> (offset: filestat_t, err: errno_t) { - err = __wasi_path_filestat_get(fd, flags, path, &offset) + err = wasi_path_filestat_get(fd, flags, path, &offset) return } /** @@ -1568,35 +1568,35 @@ path_filestat_get :: proc( * @return * The file descriptor of the file that has been opened. */ -path_open :: proc( +path_open :: proc "c" ( fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - dirflags: lookupflags_t, - /** - * The relative path of the file or directory to open, relative to the - * `path_open::fd` directory. - */ - path: cstring, - /** - * The method by which to open the file. - */ - oflags: oflags_t, - /** - * The initial rights of the newly created file descriptor. The - * implementation is allowed to return a file descriptor with fewer rights - * than specified, if and only if those rights do not apply to the type of - * file being opened. - * The *base* rights are rights that will apply to operations using the file - * descriptor itself, while the *inheriting* rights are rights that apply to - * file descriptors derived from it. - */ - fs_rights_base: rights_t, - fs_rights_inheriting: rights_t, - fdflags: fdflags_t, + /** + * Flags determining the method of how the path is resolved. + */ + dirflags: lookupflags_t, + /** + * The relative path of the file or directory to open, relative to the + * `path_open::fd` directory. + */ + path: cstring, + /** + * The method by which to open the file. + */ + oflags: oflags_t, + /** + * The initial rights of the newly created file descriptor. The + * implementation is allowed to return a file descriptor with fewer rights + * than specified, if and only if those rights do not apply to the type of + * file being opened. + * The *base* rights are rights that will apply to operations using the file + * descriptor itself, while the *inheriting* rights are rights that apply to + * file descriptors derived from it. + */ + fs_rights_base: rights_t, + fs_rights_inheriting: rights_t, + fdflags: fdflags_t, ) -> (file: fd_t, err: errno_t) { - err = __wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) + err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) return } /** @@ -1605,19 +1605,19 @@ path_open :: proc( * @return * The number of bytes placed in the buffer. */ -path_readlink :: proc( +path_readlink :: proc "c" ( fd: fd_t, - /** - * The path of the symbolic link from which to read. - */ - path: cstring, - /** - * The buffer to which to write the contents of the symbolic link. - */ - buf: [^]u8, - buf_len: size_t, + /** + * The path of the symbolic link from which to read. + */ + path: cstring, + /** + * The buffer to which to write the contents of the symbolic link. + */ + buf: [^]u8, + buf_len: size_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_path_readlink(fd, path, buf, buf_len, &n) + err = wasi_path_readlink(fd, path, buf, buf_len, &n) return } /** @@ -1625,21 +1625,21 @@ path_readlink :: proc( * @return * The number of events stored. */ -poll_oneoff :: proc( - /** - * The events to which to subscribe. - */ - subscription_in: ^subscription_t, - /** - * The events that have occurred. - */ - event_out: ^event_t, - /** - * Both the number of subscriptions and events. - */ - nsubscriptions: size_t, +poll_oneoff :: proc "c" ( + /** + * The events to which to subscribe. + */ + subscription_in: ^subscription_t, + /** + * The events that have occurred. + */ + event_out: ^event_t, + /** + * Both the number of subscriptions and events. + */ + nsubscriptions: size_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_poll_oneoff(subscription_in, event_out, nsubscriptions, &n) + err = wasi_poll_oneoff(subscription_in, event_out, nsubscriptions, &n) return } /** @@ -1649,22 +1649,22 @@ poll_oneoff :: proc( * @return * Number of bytes stored in ri_data and message flags. */ -sock_recv :: proc( +sock_recv :: proc "c" ( fd: fd_t, - /** - * List of scatter/gather vectors to which to store data. - */ - ri_data: [^]iovec_t, - /** - * The length of the array pointed to by `ri_data`. - */ - ri_data_len: size_t, - /** - * Message flags. - */ - ri_flags: riflags_t, + /** + * List of scatter/gather vectors to which to store data. + */ + ri_data: [^]iovec_t, + /** + * The length of the array pointed to by `ri_data`. + */ + ri_data_len: size_t, + /** + * Message flags. + */ + ri_flags: riflags_t, ) -> (n: size_t, flags: roflags_t, err: errno_t) { - err = __wasi_sock_recv(fd, ri_data, ri_data_len, ri_flags, &n, &flags) + err = wasi_sock_recv(fd, ri_data, ri_data_len, ri_flags, &n, &flags) return } /** @@ -1674,100 +1674,115 @@ sock_recv :: proc( * @return * Number of bytes transmitted. */ -sock_send :: proc( +sock_send :: proc "c" ( fd: fd_t, - /** - * List of scatter/gather vectors to which to retrieve data - */ - si_data: [^]ciovec_t, - /** - * The length of the array pointed to by `si_data`. - */ - si_data_len: size_t, - /** - * Message flags. - */ - si_flags: siflags_t, + /** + * List of scatter/gather vectors to which to retrieve data + */ + si_data: [^]ciovec_t, + /** + * The length of the array pointed to by `si_data`. + */ + si_data_len: size_t, + /** + * Message flags. + */ + si_flags: siflags_t, ) -> (n: size_t, err: errno_t) { - err = __wasi_sock_send(fd, si_data, si_data_len, si_flags, &n) + err = wasi_sock_send(fd, si_data, si_data_len, si_flags, &n) return } - +@(default_calling_convention="c") foreign wasi { - __wasi_args_sizes_get :: proc( + @(link_name="args_sizes_get") + wasi_args_sizes_get :: proc( retptr0: ^size_t, retptr1: ^size_t, ) -> errno_t --- - __wasi_environ_sizes_get :: proc( + @(link_name="environ_sizes_get") + wasi_environ_sizes_get :: proc( retptr0: ^size_t, retptr1: ^size_t, ) -> errno_t --- - __wasi_clock_res_get :: proc( + @(link_name="clock_res_get") + wasi_clock_res_get :: proc( id: clockid_t, retptr0: ^timestamp_t, ) -> errno_t --- - __wasi_clock_time_get :: proc( + @(link_name="clock_time_get") + wasi_clock_time_get :: proc( id: clockid_t, precision: timestamp_t, retptr0: ^timestamp_t, ) -> errno_t --- - __wasi_fd_fdstat_get :: proc( + @(link_name="fd_fdstat_get") + wasi_fd_fdstat_get :: proc( fd: fd_t, retptr0: ^fdstat_t, ) -> errno_t --- - __wasi_fd_filestat_get :: proc( + @(link_name="fd_filestat_get") + wasi_fd_filestat_get :: proc( fd: fd_t, retptr0: ^filestat_t, ) -> errno_t --- - __wasi_fd_pread :: proc( + @(link_name="fd_pread") + wasi_fd_pread :: proc( fd: fd_t, iovs: [^]iovec_t, iovs_len: size_t, offset: filesize_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_fd_prestat_get :: proc( + @(link_name="fd_prestat_get") + wasi_fd_prestat_get :: proc( fd: fd_t, retptr0: ^prestat_t, ) -> errno_t --- - __wasi_fd_pwrite :: proc( + @(link_name="fd_pwrite") + wasi_fd_pwrite :: proc( fd: fd_t, iovs: [^]ciovec_t, iovs_len: size_t, offset: filesize_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_fd_read :: proc( + @(link_name="fd_read") + wasi_fd_read :: proc( fd: fd_t, iovs: [^]iovec_t, iovs_len: size_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_fd_readdir :: proc( + @(link_name="fd_readdir") + wasi_fd_readdir :: proc( fd: fd_t, buf: [^]u8, buf_len: size_t, cookie: dircookie_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_fd_seek :: proc( + @(link_name="fd_seek") + wasi_fd_seek :: proc( fd: fd_t, offset: filedelta_t, whence: whence_t, retptr0: ^filesize_t, ) -> errno_t --- - __wasi_fd_tell :: proc( + @(link_name="fd_tell") + wasi_fd_tell :: proc( fd: fd_t, retptr0: ^filesize_t, ) -> errno_t --- - __wasi_fd_write :: proc( + @(link_name="fd_write") + wasi_fd_write :: proc( fd: fd_t, iovs: [^]ciovec_t, iovs_len: size_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_path_filestat_get :: proc( + @(link_name="path_filestat_get") + wasi_path_filestat_get :: proc( fd: fd_t, flags: lookupflags_t, /** @@ -1776,7 +1791,8 @@ foreign wasi { path: cstring, retptr0: ^filestat_t, ) -> errno_t --- - __wasi_path_open :: proc( + @(link_name="path_open") + wasi_path_open :: proc( fd: fd_t, dirflags: lookupflags_t, path: cstring, @@ -1786,20 +1802,23 @@ foreign wasi { fdflags: fdflags_t, retptr: ^fd_t, ) -> errno_t --- - __wasi_path_readlink :: proc( + @(link_name="path_readlink") + wasi_path_readlink :: proc( fd: fd_t, path: cstring, buf: [^]u8, buf_len: size_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_poll_oneoff :: proc( + @(link_name="poll_oneoff") + wasi_poll_oneoff :: proc( subscription_in: ^subscription_t, event_out: ^event_t, nsubscriptions: size_t, retptr0: ^size_t, ) -> errno_t --- - __wasi_sock_recv :: proc( + @(link_name="sock_recv") + wasi_sock_recv :: proc( fd: fd_t, ri_data: [^]iovec_t, ri_data_len: size_t, @@ -1807,7 +1826,8 @@ foreign wasi { retptr0: ^size_t, retptr1: ^roflags_t, ) -> errno_t --- - __wasi_sock_send :: proc( + @(link_name="sock_send") + wasi_sock_send :: proc( fd: fd_t, si_data: [^]ciovec_t, si_data_len: size_t, From 305e965bcbd8f0755fa848021b96688a84b143ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 13:08:19 +0000 Subject: [PATCH 10/27] Add os_specific_wasi.odin --- core/runtime/os_specific_wasi.odin | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 core/runtime/os_specific_wasi.odin diff --git a/core/runtime/os_specific_wasi.odin b/core/runtime/os_specific_wasi.odin new file mode 100644 index 000000000..94cf4c85c --- /dev/null +++ b/core/runtime/os_specific_wasi.odin @@ -0,0 +1,10 @@ +//+build wasi +package runtime + +import "core:sys/wasm/wasi" + +_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { + data := (wasi.ciovec_t)(data) + n, err := wasi.fd_write(1, &data, 1) + return int(n), _OS_Errno(err) +} From 0d2c8dfeecad04fb8f42abeea6dfa3e52687db01 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 13:44:21 +0000 Subject: [PATCH 11/27] Separate os-specific things to separate file --- core/fmt/fmt.odin | 33 --------------------------------- core/fmt/fmt_os.odin | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 core/fmt/fmt_os.odin diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 1f8949002..2cf032b73 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2,7 +2,6 @@ package fmt import "core:math/bits" import "core:mem" -import "core:os" import "core:io" import "core:reflect" import "core:runtime" @@ -62,38 +61,6 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist } -fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { - w := io.to_writer(os.stream_from_handle(fd)) - return wprint(w=w, args=args, sep=sep) -} - -fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { - w := io.to_writer(os.stream_from_handle(fd)) - return wprintln(w=w, args=args, sep=sep) -} -fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { - w := io.to_writer(os.stream_from_handle(fd)) - return wprintf(w, fmt, ..args) -} -fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) { - w := io.to_writer(os.stream_from_handle(fd)) - return wprint_type(w, info) -} -fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) { - w := io.to_writer(os.stream_from_handle(fd)) - return wprint_typeid(w, id) -} - -// print* procedures return the number of bytes written -print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) } -println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) } -printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) } - -eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) } -eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) } -eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) } - - // aprint* procedures return a string that was allocated with the current context // They must be freed accordingly aprint :: proc(args: ..any, sep := " ") -> string { diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin new file mode 100644 index 000000000..b31deb8fc --- /dev/null +++ b/core/fmt/fmt_os.odin @@ -0,0 +1,37 @@ +//+build !freestanding +package fmt + +import "core:runtime" +import "core:os" +import "core:io" + +fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { + w := io.to_writer(os.stream_from_handle(fd)) + return wprint(w=w, args=args, sep=sep) +} + +fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { + w := io.to_writer(os.stream_from_handle(fd)) + return wprintln(w=w, args=args, sep=sep) +} +fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { + w := io.to_writer(os.stream_from_handle(fd)) + return wprintf(w, fmt, ..args) +} +fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) { + w := io.to_writer(os.stream_from_handle(fd)) + return wprint_type(w, info) +} +fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) { + w := io.to_writer(os.stream_from_handle(fd)) + return wprint_typeid(w, id) +} + +// print* procedures return the number of bytes written +print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) } +println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) } +printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) } + +eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) } +eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) } +eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) } From 22982586f1f617192c05fd9ba31e3f8225f36731 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 13:45:00 +0000 Subject: [PATCH 12/27] Add basic support for wasi in package os --- core/os/{os_js_wasm32.odin => os_wasi.odin} | 42 +++++++++++++++++---- core/os/stream.odin | 4 +- 2 files changed, 36 insertions(+), 10 deletions(-) rename core/os/{os_js_wasm32.odin => os_wasi.odin} (50%) diff --git a/core/os/os_js_wasm32.odin b/core/os/os_wasi.odin similarity index 50% rename from core/os/os_js_wasm32.odin rename to core/os/os_wasi.odin index d2269cf90..266833a31 100644 --- a/core/os/os_js_wasm32.odin +++ b/core/os/os_wasi.odin @@ -1,9 +1,11 @@ package os +import "core:sys/wasm/wasi" + Handle :: distinct i32 Errno :: distinct i32 -ERROR_NONE :: Errno(0) +ERROR_NONE :: Errno(wasi.errno_t.SUCCESS) O_RDONLY :: 0x00000 O_WRONLY :: 0x00001 @@ -18,16 +20,30 @@ O_SYNC :: 0x01000 O_ASYNC :: 0x02000 O_CLOEXEC :: 0x80000 -stdout: Handle -stderr: Handle -stdin: Handle +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - return 0, 0 + iovs := wasi.ciovec_t(data) + n, err := wasi.fd_write(wasi.fd_t(fd), &iovs, 1) + return int(n), Errno(err) } read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - return 0, 0 + iovs := wasi.iovec_t(data) + n, err := wasi.fd_read(wasi.fd_t(fd), &iovs, 1) + return int(n), Errno(err) +} +write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { + iovs := wasi.ciovec_t(data) + n, err := wasi.fd_pwrite(wasi.fd_t(fd), &iovs, 1, wasi.filesize_t(offset)) + return int(n), Errno(err) +} +read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { + iovs := wasi.iovec_t(data) + n, err := wasi.fd_pread(wasi.fd_t(fd), &iovs, 1, wasi.filesize_t(offset)) + return int(n), Errno(err) } open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { return 0, 0 @@ -36,7 +52,8 @@ close :: proc(fd: Handle) -> Errno { return 0 } seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - return 0, 0 + n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence)) + return i64(n), Errno(err) } current_thread_id :: proc "contextless" () -> int { return 0 @@ -44,7 +61,11 @@ current_thread_id :: proc "contextless" () -> int { file_size :: proc(fd: Handle) -> (i64, Errno) { - return 0, 0 + stat, err := wasi.fd_filestat_get(wasi.fd_t(fd)) + if err != nil { + return 0, Errno(err) + } + return i64(stat.size), 0 } @@ -68,3 +89,8 @@ heap_free :: proc(ptr: rawptr) { return } } + + +exit :: proc "contextless" (code: int) -> ! { + wasi.proc_exit(wasi.exitcode_t(code)) +} \ No newline at end of file diff --git a/core/os/stream.odin b/core/os/stream.odin index f80633a3f..5cf5c8405 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{ return }, impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - when ODIN_OS == "windows" { + when ODIN_OS == "windows" || ODIN_OS == "wasi" { fd := Handle(uintptr(s.stream_data)) os_err: Errno n, os_err = read_at(fd, p, offset) @@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{ return }, impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { - when ODIN_OS == "windows" { + when ODIN_OS == "windows" || ODIN_OS == "wasi" { fd := Handle(uintptr(s.stream_data)) os_err: Errno n, os_err = write_at(fd, p, offset) From 3d89c6b35d9bf7fe387b373a9f554738a62d87ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <49562770+awwdev@users.noreply.github.com> Date: Sun, 31 Oct 2021 15:45:29 +0100 Subject: [PATCH 13/27] remove prefix from Mix_LoadWAV --- vendor/sdl2/mixer/sdl_mixer.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sdl2/mixer/sdl_mixer.odin b/vendor/sdl2/mixer/sdl_mixer.odin index ecb3f5a23..90334d91a 100644 --- a/vendor/sdl2/mixer/sdl_mixer.odin +++ b/vendor/sdl2/mixer/sdl_mixer.odin @@ -94,7 +94,7 @@ SetError :: SDL.SetError GetError :: SDL.GetError ClearError :: SDL.ClearError -Mix_LoadWAV :: #force_inline proc "c" (file: cstring) -> ^Chunk { +LoadWAV :: #force_inline proc "c" (file: cstring) -> ^Chunk { return LoadWAV_RW(SDL.RWFromFile(file, "rb"), true) } From 0c1675c8b08a63d80909308606801879ac64877f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:01:13 +0000 Subject: [PATCH 14/27] Correct string usage --- core/sys/wasm/wasi/wasi_api.odin | 64 ++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index 3226d5a36..e7cb98bcf 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1,7 +1,7 @@ //+build wasm32 package sys_wasi -foreign import wasi "wasi_unstable" +foreign import wasi "wasi_snapshot_preview1" DIRCOOKIE_START :: u64(0) size_t :: uint @@ -173,6 +173,7 @@ errno_t :: enum u16 { } +rights_t :: distinct bit_set[rights_flag_t; u64] rights_flag_t :: enum u64 { /** * The right to invoke `fd_datasync`. @@ -332,9 +333,6 @@ rights_flag_t :: enum u64 { SOCK_SHUTDOWN = 28, } -rights_t :: distinct bit_set[rights_flag_t; u64] - - fd_t :: distinct i32 // iovec_t :: struct { @@ -345,8 +343,8 @@ fd_t :: distinct i32 // buf: [^]u8, // buf_len: size_t, // } -iovec_t :: distinct []byte -ciovec_t :: distinct []byte +iovec_t :: []byte +ciovec_t :: []byte filedelta_t :: distinct i64 @@ -1133,7 +1131,8 @@ foreign wasi { /** * The path at which to create the directory. */ - path: cstring, + path: [^]byte, + path_len: size_t, ) -> errno_t --- /** * Adjust the timestamps of a file or directory. @@ -1148,7 +1147,8 @@ foreign wasi { /** * The path of the file or directory to operate on. */ - path: cstring, + path: [^]byte, + path_len: size_t, /** * The desired values of the data access timestamp. */ @@ -1175,7 +1175,8 @@ foreign wasi { /** * The source path from which to link. */ - old_path: cstring, + old_path: [^]byte, + old_path_len: size_t, /** * The working directory at which the resolution of the new path starts. */ @@ -1183,7 +1184,8 @@ foreign wasi { /** * The destination path at which to create the hard link. */ - new_path: cstring, + new_path: [^]byte, + new_path_len: size_t, ) -> errno_t --- /** * Remove a directory. @@ -1195,7 +1197,8 @@ foreign wasi { /** * The path to a directory to remove. */ - path: cstring, + path: [^]byte, + path_len: size_t, ) -> errno_t --- /** * Rename a file or directory. @@ -1206,7 +1209,8 @@ foreign wasi { /** * The source path of the file or directory to rename. */ - old_path: cstring, + old_path: [^]byte, + old_path_len: size_t, /** * The working directory at which the resolution of the new path starts. */ @@ -1214,7 +1218,8 @@ foreign wasi { /** * The destination path to which to rename the file or directory. */ - new_path: cstring, + new_path: [^]byte, + new_path_len: size_t, ) -> errno_t --- /** * Create a symbolic link. @@ -1224,12 +1229,14 @@ foreign wasi { /** * The contents of the symbolic link. */ - old_path: cstring, + old_path: [^]byte, + old_path_len: size_t, fd: fd_t, /** * The destination path at which to create the symbolic link. */ - new_path: cstring, + new_path: [^]byte, + new_path_len: size_t, ) -> errno_t --- /** * Unlink a file. @@ -1241,7 +1248,8 @@ foreign wasi { /** * The path to a file to unlink. */ - path: cstring, + path: [^]byte, + path_len: size_t, ) -> errno_t --- /** * Terminate the process normally. An exit code of 0 indicates successful @@ -1552,9 +1560,9 @@ path_filestat_get :: proc "c" ( /** * The path of the file or directory to inspect. */ - path: cstring, + path: string, ) -> (offset: filestat_t, err: errno_t) { - err = wasi_path_filestat_get(fd, flags, path, &offset) + err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset) return } /** @@ -1578,7 +1586,7 @@ path_open :: proc "c" ( * The relative path of the file or directory to open, relative to the * `path_open::fd` directory. */ - path: cstring, + path: string, /** * The method by which to open the file. */ @@ -1596,7 +1604,7 @@ path_open :: proc "c" ( fs_rights_inheriting: rights_t, fdflags: fdflags_t, ) -> (file: fd_t, err: errno_t) { - err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) + err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) return } /** @@ -1610,14 +1618,13 @@ path_readlink :: proc "c" ( /** * The path of the symbolic link from which to read. */ - path: cstring, + path: string, /** * The buffer to which to write the contents of the symbolic link. */ - buf: [^]u8, - buf_len: size_t, + buf: []u8, ) -> (n: size_t, err: errno_t) { - err = wasi_path_readlink(fd, path, buf, buf_len, &n) + err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n) return } /** @@ -1788,14 +1795,16 @@ foreign wasi { /** * The path of the file or directory to inspect. */ - path: cstring, + path: [^]byte, + path_len: size_t, retptr0: ^filestat_t, ) -> errno_t --- @(link_name="path_open") wasi_path_open :: proc( fd: fd_t, dirflags: lookupflags_t, - path: cstring, + path: [^]byte, + path_len: size_t, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, @@ -1805,7 +1814,8 @@ foreign wasi { @(link_name="path_readlink") wasi_path_readlink :: proc( fd: fd_t, - path: cstring, + path: [^]byte, + path_len: size_t, buf: [^]u8, buf_len: size_t, retptr0: ^size_t, From c13c30b46657a45771365566ded453b49568c701 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:21:39 +0000 Subject: [PATCH 15/27] Update wasi to use `string` and slice types --- core/os/os_wasi.odin | 59 +++- core/runtime/os_specific_wasi.odin | 2 +- core/sys/wasm/wasi/wasi_api.odin | 446 ++++++++++++++++------------- 3 files changed, 303 insertions(+), 204 deletions(-) diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index 266833a31..584f2f85f 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -27,29 +27,76 @@ stderr: Handle = 2 write :: proc(fd: Handle, data: []byte) -> (int, Errno) { iovs := wasi.ciovec_t(data) - n, err := wasi.fd_write(wasi.fd_t(fd), &iovs, 1) + n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) return int(n), Errno(err) } read :: proc(fd: Handle, data: []byte) -> (int, Errno) { iovs := wasi.iovec_t(data) - n, err := wasi.fd_read(wasi.fd_t(fd), &iovs, 1) + n, err := wasi.fd_read(wasi.fd_t(fd), {iovs}) return int(n), Errno(err) } write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { iovs := wasi.ciovec_t(data) - n, err := wasi.fd_pwrite(wasi.fd_t(fd), &iovs, 1, wasi.filesize_t(offset)) + n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) return int(n), Errno(err) } read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { iovs := wasi.iovec_t(data) - n, err := wasi.fd_pread(wasi.fd_t(fd), &iovs, 1, wasi.filesize_t(offset)) + n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) return int(n), Errno(err) } open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { - return 0, 0 + flags: wasi.oflags_t + fs_rights_base: wasi.rights_t + fs_rights_inheriting: wasi.rights_t + fdflags: wasi.fdflags_t + + if mode & O_RDONLY == O_RDONLY { + fs_rights_base += {.FD_READ} + } + if mode & O_WRONLY == O_WRONLY { + fs_rights_base += {.FD_WRITE} + } + if mode & O_RDWR == O_RDWR { + fs_rights_base += {.FD_READ, .FD_WRITE} + } + if mode & O_CREATE == O_CREATE { + flags += {.CREATE} + fs_rights_base += {.FD_WRITE} + } + if mode & O_EXCL == O_EXCL { + flags += {.EXCL} + } + if mode & O_NOCTTY == O_NOCTTY { + + } + if mode & O_TRUNC == O_TRUNC { + flags += {.TRUNC} + } + if mode & O_NONBLOCK == O_NONBLOCK { + fdflags += {.NONBLOCK} + } + if mode & O_APPEND == O_APPEND { + fdflags += {.APPEND} + } + if mode & O_SYNC == O_SYNC { + fdflags += {.DSYNC, .RSYNC, .SYNC} + } + if mode & O_ASYNC == O_ASYNC { + + } + if mode & O_CLOEXEC == O_CLOEXEC { + + } + fs_rights_inheriting = fs_rights_base + + + fd, err := wasi.path_open(0, {}, path, flags, fs_rights_base, fs_rights_inheriting, fdflags) + return Handle(fd), Errno(err) } close :: proc(fd: Handle) -> Errno { - return 0 + err := wasi.fd_close(wasi.fd_t(fd)) + return Errno(err) } seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence)) diff --git a/core/runtime/os_specific_wasi.odin b/core/runtime/os_specific_wasi.odin index 94cf4c85c..3f69504ee 100644 --- a/core/runtime/os_specific_wasi.odin +++ b/core/runtime/os_specific_wasi.odin @@ -5,6 +5,6 @@ import "core:sys/wasm/wasi" _os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { data := (wasi.ciovec_t)(data) - n, err := wasi.fd_write(1, &data, 1) + n, err := wasi.fd_write(1, {data}) return int(n), _OS_Errno(err) } diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index e7cb98bcf..2e2a99617 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1087,17 +1087,6 @@ foreign wasi { */ fst_flags: fstflags_t, ) -> errno_t --- - /** - * Return a description of the given preopened file descriptor. - */ - fd_prestat_dir_name :: proc( - fd: fd_t, - /** - * A buffer into which to write the preopened directory name. - */ - path: [^]u8, - path_len: size_t, - ) -> errno_t --- /** * Atomically replace a file descriptor by renumbering another file descriptor. * Due to the strong focus on thread safety, this environment does not provide @@ -1122,135 +1111,6 @@ foreign wasi { fd_sync :: proc( f: fd_t, ) -> errno_t --- - /** - * Create a directory. - * Note: This is similar to `mkdirat` in POSIX. - */ - path_create_directory :: proc( - fd: fd_t, - /** - * The path at which to create the directory. - */ - path: [^]byte, - path_len: size_t, - ) -> errno_t --- - /** - * Adjust the timestamps of a file or directory. - * Note: This is similar to `utimensat` in POSIX. - */ - path_filestat_set_times :: proc( - fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - flags: lookupflags_t, - /** - * The path of the file or directory to operate on. - */ - path: [^]byte, - path_len: size_t, - /** - * The desired values of the data access timestamp. - */ - atim: timestamp_t, - /** - * The desired values of the data modification timestamp. - */ - mtim: timestamp_t, - /** - * A bitmask indicating which timestamps to adjust. - */ - fst_flags: fstflags_t, - ) -> errno_t --- - /** - * Create a hard link. - * Note: This is similar to `linkat` in POSIX. - */ - path_link :: proc( - old_fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - old_flags: lookupflags_t, - /** - * The source path from which to link. - */ - old_path: [^]byte, - old_path_len: size_t, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path at which to create the hard link. - */ - new_path: [^]byte, - new_path_len: size_t, - ) -> errno_t --- - /** - * Remove a directory. - * Return `errno::notempty` if the directory is not empty. - * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - */ - path_remove_directory :: proc( - fd: fd_t, - /** - * The path to a directory to remove. - */ - path: [^]byte, - path_len: size_t, - ) -> errno_t --- - /** - * Rename a file or directory. - * Note: This is similar to `renameat` in POSIX. - */ - path_rename :: proc( - fd: fd_t, - /** - * The source path of the file or directory to rename. - */ - old_path: [^]byte, - old_path_len: size_t, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path to which to rename the file or directory. - */ - new_path: [^]byte, - new_path_len: size_t, - ) -> errno_t --- - /** - * Create a symbolic link. - * Note: This is similar to `symlinkat` in POSIX. - */ - path_symlink :: proc( - /** - * The contents of the symbolic link. - */ - old_path: [^]byte, - old_path_len: size_t, - fd: fd_t, - /** - * The destination path at which to create the symbolic link. - */ - new_path: [^]byte, - new_path_len: size_t, - ) -> errno_t --- - /** - * Unlink a file. - * Return `errno::isdir` if the path refers to a directory. - * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - */ - path_unlink_file :: proc( - fd: fd_t, - /** - * The path to a file to unlink. - */ - path: [^]byte, - path_len: size_t, - ) -> errno_t --- /** * Terminate the process normally. An exit code of 0 indicates successful * termination of the program. The meanings of other values is dependent on @@ -1277,21 +1137,6 @@ foreign wasi { * Note: This is similar to `sched_yield` in POSIX. */ sched_yield :: proc() -> errno_t --- - /** - * Write high-quality random data into a buffer. - * This function blocks when the implementation is unable to immediately - * provide sufficient high-quality random data. - * This function may execute slowly, so when large mounts of random data are - * required, it's advisable to use this function to seed a pseudo-random - * number generator, rather than to provide the random data directly. - */ - random_get :: proc( - /** - * The buffer to fill with random data. - */ - buf: [^]u8, - buf_len: size_t, - ) -> errno_t --- /** * Shut down socket send and receive channels. * Note: This is similar to `shutdown` in POSIX. @@ -1399,17 +1244,13 @@ fd_pread :: proc "c" ( /** * List of scatter/gather vectors in which to store data. */ - iovs: [^]iovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, + iovs: []iovec_t, /** * The offset within the file at which to read. */ offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_pread(fd, iovs, iovs_len, offset, &n) + err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n) return } /** @@ -1434,17 +1275,13 @@ fd_pwrite :: proc "c" ( /** * List of scatter/gather vectors from which to retrieve data. */ - iovs: [^]ciovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, + iovs: []ciovec_t, /** * The offset within the file at which to write. */ offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_pwrite(fd, iovs, iovs_len, offset, &n) + err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n) return } /** @@ -1458,13 +1295,9 @@ fd_read :: proc "c" ( /** * List of scatter/gather vectors to which to store data. */ - iovs: [^]iovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, + iovs: []iovec_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_read(fd, iovs, iovs_len, &n) + err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n) return } /** @@ -1485,14 +1318,13 @@ fd_readdir :: proc "c" ( /** * The buffer where directory entries are stored */ - buf: [^]u8, - buf_len: size_t, + buf: []byte, /** * The location within the directory to start reading */ cookie: dircookie_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_readdir(fd, buf, buf_len, cookie, &n) + err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n) return } /** @@ -1536,13 +1368,9 @@ fd_write :: proc "c" ( /** * List of scatter/gather vectors from which to retrieve data. */ - iovs: [^]ciovec_t, - /** - * The length of the array pointed to by `iovs`. - */ - iovs_len: size_t, + iovs: []ciovec_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_write(fd, iovs, iovs_len, &n) + err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n) return } /** @@ -1661,17 +1489,13 @@ sock_recv :: proc "c" ( /** * List of scatter/gather vectors to which to store data. */ - ri_data: [^]iovec_t, - /** - * The length of the array pointed to by `ri_data`. - */ - ri_data_len: size_t, + ri_data: []iovec_t, /** * Message flags. */ ri_flags: riflags_t, ) -> (n: size_t, flags: roflags_t, err: errno_t) { - err = wasi_sock_recv(fd, ri_data, ri_data_len, ri_flags, &n, &flags) + err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags) return } /** @@ -1686,20 +1510,182 @@ sock_send :: proc "c" ( /** * List of scatter/gather vectors to which to retrieve data */ - si_data: [^]ciovec_t, - /** - * The length of the array pointed to by `si_data`. - */ - si_data_len: size_t, + si_data: []ciovec_t, /** * Message flags. */ si_flags: siflags_t, ) -> (n: size_t, err: errno_t) { - err = wasi_sock_send(fd, si_data, si_data_len, si_flags, &n) + err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n) return } +/** + * Return a description of the given preopened file descriptor. + */ +fd_prestat_dir_name :: proc( + fd: fd_t, + /** + * A buffer into which to write the preopened directory name. + */ + path: string, +) -> errno_t { + return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path)) +} +/** + * Create a directory. + * Note: This is similar to `mkdirat` in POSIX. + */ +path_create_directory :: proc( + fd: fd_t, + /** + * The path at which to create the directory. + */ + path: string, +) -> errno_t { + return wasm_path_create_directory(fd, raw_data(path), len(path)) +} +/** + * Adjust the timestamps of a file or directory. + * Note: This is similar to `utimensat` in POSIX. + */ +path_filestat_set_times :: proc( + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to operate on. + */ + path: string, + /** + * The desired values of the data access timestamp. + */ + atim: timestamp_t, + /** + * The desired values of the data modification timestamp. + */ + mtim: timestamp_t, + /** + * A bitmask indicating which timestamps to adjust. + */ + fst_flags: fstflags_t, +) -> errno_t { + return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags) +} +/** + * Remove a directory. + * Return `errno::notempty` if the directory is not empty. + * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + */ +path_remove_directory :: proc( + fd: fd_t, + /** + * The path to a directory to remove. + */ + path: string, +) -> errno_t { + return wasm_path_remove_directory(fd, raw_data(path), len(path)) +} +/** + * Create a hard link. + * Note: This is similar to `linkat` in POSIX. + */ +path_link :: proc( + old_fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + old_flags: lookupflags_t, + /** + * The source path from which to link. + */ + old_path: string, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path at which to create the hard link. + */ + new_path: string, +) -> errno_t { + return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path)) +} + +/** + * Rename a file or directory. + * Note: This is similar to `renameat` in POSIX. + */ +path_rename :: proc( + fd: fd_t, + /** + * The source path of the file or directory to rename. + */ + old_path: string, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path to which to rename the file or directory. + */ + new_path: string, +) -> errno_t { + return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path)) +} +/** + * Create a symbolic link. + * Note: This is similar to `symlinkat` in POSIX. + */ +path_symlink :: proc( + /** + * The contents of the symbolic link. + */ + old_path: string, + fd: fd_t, + /** + * The destination path at which to create the symbolic link. + */ + new_path: string, +) -> errno_t { + return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path)) +} +/** + * Unlink a file. + * Return `errno::isdir` if the path refers to a directory. + * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + */ +path_unlink_file :: proc( + fd: fd_t, + /** + * The path to a file to unlink. + */ + path: string, +) -> errno_t { + return wasm_path_unlink_file(fd, raw_data(path), len(path)) +} +/** + * Write high-quality random data into a buffer. + * This function blocks when the implementation is unable to immediately + * provide sufficient high-quality random data. + * This function may execute slowly, so when large mounts of random data are + * required, it's advisable to use this function to seed a pseudo-random + * number generator, rather than to provide the random data directly. + */ +random_get :: proc( + /** + * The buffer to fill with random data. + */ + buf: []u8, +) -> errno_t { + return wasm_random_get(raw_data(buf), len(buf)) +} + + + + @(default_calling_convention="c") foreign wasi { @(link_name="args_sizes_get") @@ -1795,7 +1781,7 @@ foreign wasi { /** * The path of the file or directory to inspect. */ - path: [^]byte, + path: [^]u8, path_len: size_t, retptr0: ^filestat_t, ) -> errno_t --- @@ -1803,7 +1789,7 @@ foreign wasi { wasi_path_open :: proc( fd: fd_t, dirflags: lookupflags_t, - path: [^]byte, + path: [^]u8, path_len: size_t, oflags: oflags_t, fs_rights_base: rights_t, @@ -1814,7 +1800,7 @@ foreign wasi { @(link_name="path_readlink") wasi_path_readlink :: proc( fd: fd_t, - path: [^]byte, + path: [^]u8, path_len: size_t, buf: [^]u8, buf_len: size_t, @@ -1844,4 +1830,70 @@ foreign wasi { si_flags: siflags_t, retptr0: ^size_t, ) -> errno_t --- + @(link_name="fd_prestat_dir_name") + wasm_fd_prestat_dir_name :: proc( + fd: fd_t, + path: [^]u8, + path_len: size_t, + ) -> errno_t --- + @(link_name="path_create_directory") + wasm_path_create_directory :: proc( + fd: fd_t, + path: [^]u8, + path_len: size_t, + ) -> errno_t --- + @(link_name="path_filestat_set_times") + wasm_path_filestat_set_times :: proc( + fd: fd_t, + flags: lookupflags_t, + path: [^]u8, + path_len: size_t, + atim: timestamp_t, + mtim: timestamp_t, + fst_flags: fstflags_t, + ) -> errno_t --- + @(link_name="path_remove_directory") + wasm_path_remove_directory :: proc( + fd: fd_t, + path: [^]u8, + path_len: size_t, + ) -> errno_t --- + @(link_name="path_link") + wasm_path_link :: proc( + old_fd: fd_t, + old_flags: lookupflags_t, + old_path: [^]u8, + old_path_len: size_t, + new_fd: fd_t, + new_path: [^]u8, + new_path_len: size_t, + ) -> errno_t --- + @(link_name="path_rename") + wasm_path_rename :: proc( + fd: fd_t, + old_path: [^]u8, + old_path_len: size_t, + new_fd: fd_t, + new_path: [^]u8, + new_path_len: size_t, + ) -> errno_t --- + @(link_name="path_symlink") + wasm_path_symlink :: proc( + old_path: [^]u8, + old_path_len: size_t, + fd: fd_t, + new_path: [^]u8, + new_path_len: size_t, + ) -> errno_t --- + @(link_name="path_unlink_file") + wasm_path_unlink_file :: proc( + fd: fd_t, + path: [^]u8, + path_len: size_t, + ) -> errno_t --- + @(link_name="random_get") + wasm_random_get :: proc( + buf: [^]u8, + buf_len: size_t, + ) -> errno_t --- } From 3224d04df8ccbcfa712ced07e270bee95df04f2f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:22:54 +0000 Subject: [PATCH 16/27] Stub out os.open for wasi --- core/os/os_wasi.odin | 48 +------------------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index 584f2f85f..d2ba166bd 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -46,53 +46,7 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { return int(n), Errno(err) } open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { - flags: wasi.oflags_t - fs_rights_base: wasi.rights_t - fs_rights_inheriting: wasi.rights_t - fdflags: wasi.fdflags_t - - if mode & O_RDONLY == O_RDONLY { - fs_rights_base += {.FD_READ} - } - if mode & O_WRONLY == O_WRONLY { - fs_rights_base += {.FD_WRITE} - } - if mode & O_RDWR == O_RDWR { - fs_rights_base += {.FD_READ, .FD_WRITE} - } - if mode & O_CREATE == O_CREATE { - flags += {.CREATE} - fs_rights_base += {.FD_WRITE} - } - if mode & O_EXCL == O_EXCL { - flags += {.EXCL} - } - if mode & O_NOCTTY == O_NOCTTY { - - } - if mode & O_TRUNC == O_TRUNC { - flags += {.TRUNC} - } - if mode & O_NONBLOCK == O_NONBLOCK { - fdflags += {.NONBLOCK} - } - if mode & O_APPEND == O_APPEND { - fdflags += {.APPEND} - } - if mode & O_SYNC == O_SYNC { - fdflags += {.DSYNC, .RSYNC, .SYNC} - } - if mode & O_ASYNC == O_ASYNC { - - } - if mode & O_CLOEXEC == O_CLOEXEC { - - } - fs_rights_inheriting = fs_rights_base - - - fd, err := wasi.path_open(0, {}, path, flags, fs_rights_base, fs_rights_inheriting, fdflags) - return Handle(fd), Errno(err) + return 0, -1 } close :: proc(fd: Handle) -> Errno { err := wasi.fd_close(wasi.fd_t(fd)) From 32506a34ffa7a7cc3c12e14f31e36e9b22a91218 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:23:39 +0000 Subject: [PATCH 17/27] Separate out the ABI for wasm32 from 386 --- src/llvm_abi.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index aa12cc352..ad8a45df7 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1039,6 +1039,70 @@ namespace lbAbiArm64 { } } +namespace lbAbiWasm32 { + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count); + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + + LB_ABI_INFO(abi_info) { + lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); + ft->ctx = c; + ft->args = compute_arg_types(c, arg_types, arg_count); + ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->calling_convention = calling_convention; + return ft; + } + + lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) { + if (!is_return && lb_sizeof(type) > 8) { + return lb_arg_type_indirect(type, nullptr); + } + + LLVMAttributeRef attr = nullptr; + LLVMTypeRef i1 = LLVMInt1TypeInContext(c); + if (type == i1) { + attr = lb_create_enum_attribute(c, "zeroext"); + } + return lb_arg_type_direct(type, nullptr, nullptr, attr); + } + + Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { + auto args = array_make(heap_allocator(), arg_count); + + for (unsigned i = 0; i < arg_count; i++) { + LLVMTypeRef t = arg_types[i]; + LLVMTypeKind kind = LLVMGetTypeKind(t); + i64 sz = lb_sizeof(t); + if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) { + if (sz == 0) { + args[i] = lb_arg_type_ignore(t); + } else { + args[i] = lb_arg_type_indirect(t, nullptr); + } + } else { + args[i] = non_struct(c, t, false); + } + } + return args; + } + + lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) { + i64 sz = lb_sizeof(return_type); + switch (sz) { + case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); + case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); + } + LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); + return lb_arg_type_indirect(return_type, attr); + } + return non_struct(c, return_type, true); + } +} + LB_ABI_INFO(lb_get_abi_info) { switch (calling_convention) { @@ -1075,7 +1139,7 @@ LB_ABI_INFO(lb_get_abi_info) { case TargetArch_wasm32: // TODO(bill): implement wasm32's ABI correct // NOTE(bill): this ABI is only an issue for WASI compatibility - return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + return lbAbiWasm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); case TargetArch_wasm64: // TODO(bill): implement wasm64's ABI correct // NOTE(bill): this ABI is only an issue for WASI compatibility From a36c1ad406f5cef12300d7ce8ace385530f0e4f1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:28:51 +0000 Subject: [PATCH 18/27] Add default_allocators_wasi.odin --- core/runtime/default_allocators_wasi.odin | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 core/runtime/default_allocators_wasi.odin diff --git a/core/runtime/default_allocators_wasi.odin b/core/runtime/default_allocators_wasi.odin new file mode 100644 index 000000000..e2bb7516e --- /dev/null +++ b/core/runtime/default_allocators_wasi.odin @@ -0,0 +1,32 @@ +//+build wasi +package runtime + +default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + switch mode { + case .Alloc: + return nil, .Out_Of_Memory + case .Free: + return nil, .None + case .Free_All: + return nil, .Mode_Not_Implemented + case .Resize: + if size == 0 { + return nil, .None + } + return nil, .Out_Of_Memory + case .Query_Features: + return nil, .Mode_Not_Implemented + case .Query_Info: + return nil, .Mode_Not_Implemented + } + return nil, .None +} + +default_allocator :: proc() -> Allocator { + return Allocator{ + procedure = default_allocator_proc, + data = nil, + } +} From 235dae552a1ce3fe745c334f354038c0d5f45f48 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:35:09 +0000 Subject: [PATCH 19/27] Ignore `-use-separate-modules` when targeting wasm32/wasm64 --- src/build_settings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b33fabfda..dd30d1306 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -921,6 +921,9 @@ void init_build_context(TargetMetrics *cross_target) { } bc->link_flags = make_string_c(link_flags); + + // Disallow on wasm + build_context.use_separate_modules = false; } else { gb_printf_err("Compiler Error: Unsupported architecture\n"); gb_exit(1); From bfa33bf5d37b8ee5494f65dcb1d5b224a574f532 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 15:48:56 +0000 Subject: [PATCH 20/27] Disable `wasm64` --- src/build_settings.cpp | 2 +- src/llvm_backend.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index dd30d1306..3bd6622c7 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -380,7 +380,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_386"), &target_freebsd_386 }, { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, - { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 }, + // { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, }; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d21ff8e5a..ef764372b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -787,7 +787,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) call_cleanup = false; } else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { name = str_lit("mainCRTStartup"); - } else if (build_context.metrics.os == TargetOs_wasi) { + } else if (is_arch_wasm()) { name = str_lit("_start"); call_cleanup = false; } else { @@ -894,8 +894,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) lb_end_procedure_body(p); - - if (build_context.metrics.os == TargetOs_wasi) { + + if (is_arch_wasm()) { LLVMSetLinkage(p->value, LLVMDLLExportLinkage); } else { LLVMSetLinkage(p->value, LLVMExternalLinkage); From fca7142a3c5f942235580aec9661003727601fd3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 16:31:20 +0000 Subject: [PATCH 21/27] Correct `_start` export for wasm* targets --- src/llvm_backend.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ef764372b..1d382aa6d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -897,6 +897,11 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) if (is_arch_wasm()) { LLVMSetLinkage(p->value, LLVMDLLExportLinkage); + LLVMSetDLLStorageClass(p->value, LLVMDLLExportStorageClass); + LLVMSetVisibility(p->value, LLVMDefaultVisibility); + + char const *export_name = alloc_cstring(permanent_allocator(), p->name); + LLVMAddTargetDependentFunctionAttr(p->value, "wasm-export-name", export_name); } else { LLVMSetLinkage(p->value, LLVMExternalLinkage); } From 5f51337a01fa4a1e7a461604d564fa64601727cf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 19:00:01 +0000 Subject: [PATCH 22/27] Add procs for wasm32 --- core/runtime/procs_wasm32.odin | 7 +++++++ src/checker.cpp | 3 +++ src/llvm_abi.cpp | 5 +++++ src/llvm_backend_opt.cpp | 6 ++++++ 4 files changed, 21 insertions(+) create mode 100644 core/runtime/procs_wasm32.odin diff --git a/core/runtime/procs_wasm32.odin b/core/runtime/procs_wasm32.odin new file mode 100644 index 000000000..dbc0dfcb7 --- /dev/null +++ b/core/runtime/procs_wasm32.odin @@ -0,0 +1,7 @@ +//+build wasm32 +package runtime + +@(link_name="__ashlti3") +__ashlti3 :: proc "c" (a: i64, b: i32) -> i64 { + return a +} \ No newline at end of file diff --git a/src/checker.cpp b/src/checker.cpp index 8db9e1bd6..f0f463816 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2011,6 +2011,9 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("gnu_h2f_ieee"), str_lit("gnu_f2h_ieee"), str_lit("extendhfsf2"), + + // WASM Specific + str_lit("__ashlti3"), }; for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]); diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index ad8a45df7..9c7ced91e 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1053,6 +1053,11 @@ namespace lbAbiWasm32 { } lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) { + if (!is_return && type == LLVMIntTypeInContext(c, 128)) { + LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2); + return lb_arg_type_direct(type, cast_type, nullptr, nullptr); + } + if (!is_return && lb_sizeof(type) > 8) { return lb_arg_type_indirect(type, nullptr); } diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 8ddd3360d..75a377e5b 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -388,6 +388,12 @@ void lb_run_remove_unused_function_pass(LLVMModuleRef mod) { name == "memcpy") { continue; } + if (is_arch_wasm()) { + if (name == "__ashlti3") { + LLVMSetLinkage(curr_func, LLVMExternalLinkage); + continue; + } + } LLVMLinkage linkage = LLVMGetLinkage(curr_func); From 141299eb02bc9a7330daa30d75d279e84ba28cc3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 31 Oct 2021 19:39:01 +0000 Subject: [PATCH 23/27] Change the behaviour change is for when a `bit_set` of range/enum and the underlying type has been specified * If the lower bound is greater than zero, it will become zero (thus removing the compatification) * If the lower bound is negative, it is an error This means that an integer value N, maps directly to the N-th bit. Example ``` foo :: enum u8 { a = 2, b = 3, c = 4, } set0: bit_set[foo] set0 += {.a, .b} // internally set0 == 1<<(2-2) | 1<<(3-2) set1: bit_set[foo; u32] set1 += {.a, .b} // internally set1 == 1<<(2-0) | 1<<(3-0) ``` --- src/check_type.cpp | 54 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 813990020..7c01bd5ba 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -921,26 +921,51 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no } i64 lower = big_int_to_i64(&i); i64 upper = big_int_to_i64(&j); - + + bool lower_changed = false; i64 bits = MAX_BITS; if (type->BitSet.underlying != nullptr) { bits = 8*type_size_of(type->BitSet.underlying); + + if (lower > 0) { + lower = 0; + lower_changed = true; + } else if (lower < 0) { + error(bs->elem, "bit_set does not allow a negative lower bound (%lld) when an underlying type is set", lower); + } } + i64 bits_required = upper-lower; + switch (be->op.kind) { + case Token_Ellipsis: + case Token_RangeFull: + bits_required += 1; + break; + } + bool is_valid = true; + switch (be->op.kind) { case Token_Ellipsis: case Token_RangeFull: if (upper - lower >= bits) { - error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1)); + is_valid = false; } break; case Token_RangeHalf: if (upper - lower > bits) { - error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower)); + is_valid = false; } upper -= 1; break; } + if (!is_valid) { + if (lower_changed) { + error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required); + } else { + error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required); + } + } + type->BitSet.elem = t; type->BitSet.lower = lower; type->BitSet.upper = upper; @@ -996,7 +1021,8 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no } GB_ASSERT(lower <= upper); - + + bool lower_changed = false; i64 bits = MAX_BITS ; if (bs->underlying != nullptr) { Type *u = check_type(c, bs->underlying); @@ -1008,17 +1034,31 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no } type->BitSet.underlying = u; bits = 8*type_size_of(u); + + if (lower > 0) { + lower = 0; + lower_changed = true; + } else if (lower < 0) { + gbString s = type_to_string(elem); + error(bs->elem, "bit_set does not allow a negative lower bound (%lld) of the element type '%s' when an underlying type is set", lower, s); + gb_string_free(s); + } } - if (upper - lower >= MAX_BITS) { - error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", MAX_BITS, (upper-lower+1)); + if (upper - lower >= bits) { + i64 bits_required = upper-lower+1; + if (lower_changed) { + error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required); + } else { + error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required); + } } type->BitSet.lower = lower; type->BitSet.upper = upper; } } - } + } } From ff36bd3d8589bb9f86b1ce2caef17c1e9cb54b81 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Tue, 19 Oct 2021 23:00:45 +0000 Subject: [PATCH 24/27] build: Support the Fedora LLVM 11 package Fedora is on LLVM 12, and the backward compatibility package has a non-standard name for llvm-config. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index fa07ec689..23fb7be66 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ ifeq ($(OS), Linux) LLVM_CONFIG=llvm-config-11 ifneq ($(shell which llvm-config-11 2>/dev/null),) LLVM_CONFIG=llvm-config-11 + else ifneq ($(shell which llvm-config-11-64 2>/dev/null),) + LLVM_CONFIG=llvm-config-11-64 else ifneq ($(shell llvm-config --version | grep '^11\.'),) LLVM_CONFIG=llvm-config From 796a0c3968243f540e68e584283d862f60bf3f26 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 31 Oct 2021 21:37:22 +0000 Subject: [PATCH 25/27] core/intrinsics: Add mem_zero_volatile --- core/intrinsics/intrinsics.odin | 1 + src/check_builtin.cpp | 1 + src/checker_builtin_procs.hpp | 2 ++ src/llvm_backend_proc.cpp | 13 ++++++++++++- src/llvm_backend_utility.cpp | 6 +++--- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 67ac22c8b..2da7a7439 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -39,6 +39,7 @@ sqrt :: proc(x: $T) -> T where type_is_float(T) --- mem_copy :: proc(dst, src: rawptr, len: int) --- mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) --- mem_zero :: proc(ptr: rawptr, len: int) --- +mem_zero_volatile :: proc(ptr: rawptr, len: int) --- fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 2373317c3..482813792 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2598,6 +2598,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_mem_zero: + case BuiltinProc_mem_zero_volatile: { operand->mode = Addressing_NoValue; operand->type = t_invalid; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 3ab85f31b..abd9fc6ca 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -70,6 +70,7 @@ enum BuiltinProcId { BuiltinProc_mem_copy, BuiltinProc_mem_copy_non_overlapping, BuiltinProc_mem_zero, + BuiltinProc_mem_zero_volatile, BuiltinProc_ptr_offset, BuiltinProc_ptr_sub, @@ -322,6 +323,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("mem_zero"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("mem_zero_volatile"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("ptr_offset"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("ptr_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 29f7b6655..e1edfcac7 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1560,7 +1560,18 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, len = lb_emit_conv(p, len, t_int); unsigned alignment = 1; - lb_mem_zero_ptr_internal(p, ptr.value, len.value, alignment); + lb_mem_zero_ptr_internal(p, ptr.value, len.value, alignment, false); + return {}; + } + case BuiltinProc_mem_zero_volatile: + { + lbValue ptr = lb_build_expr(p, ce->args[0]); + lbValue len = lb_build_expr(p, ce->args[1]); + ptr = lb_emit_conv(p, ptr, t_rawptr); + len = lb_emit_conv(p, len, t_int); + + unsigned alignment = 1; + lb_mem_zero_ptr_internal(p, ptr.value, len.value, alignment, true); return {}; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index b58f07d49..709106bc4 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -48,7 +48,7 @@ lbValue lb_correct_endianness(lbProcedure *p, lbValue value) { return value; } -void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment) { +void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) { bool is_inlinable = false; i64 const_len = 0; @@ -77,7 +77,7 @@ void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], ""); args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false); args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, ""); - args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, false); // is_volatile parameter + args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false); LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); } @@ -93,7 +93,7 @@ void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alig { // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too i32 sz = cast(i32)type_size_of(type); - lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment); + lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false); } break; default: From 672fc9fc4d20d06810a66aa6133a94a0565eae05 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 31 Oct 2021 21:41:39 +0000 Subject: [PATCH 26/27] core/mem: Add zero_explicit This call is intended to provide the ability to securely scrub memory without compiler interference, in a similar manner to explicit_bzero, memset_s, SecureZeroMemory. The approach taken is a volatile memset followed by a seqentially consistent memory fence, to prevent the call from being optimized away by DSE, and from being reordered. An identical approach is currently being used by the zeroize Rust crate, and is effective in practice. LLVM IR output: ``` ; Function Attrs: nounwind define internal i8* @mem.zero_explicit(i8* %0, i64 %1) #0 { decls: call void @llvm.memset.p0i8.i64(i8* %0, i8 0, i64 %1, i1 true) fence seq_cst ret i8* %0 } ``` --- core/mem/mem.odin | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 29d124e42..8eb877e75 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -10,6 +10,15 @@ zero :: proc "contextless" (data: rawptr, len: int) -> rawptr { intrinsics.mem_zero(data, len) return data } +zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { + // This routine tries to avoid the compiler optimizing away the call, + // so that it is always executed. It is intended to provided + // equivalent semantics to those provided by the C11 Annex K 3.7.4.1 + // memset_s call. + intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero + intrinsics.atomic_fence() // Prevent reordering + return data +} zero_item :: proc "contextless" (item: $P/^$T) { intrinsics.mem_zero(item, size_of(T)) } From a422d0455e89195b2e177b553fe69a59020e84ab Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 1 Nov 2021 10:42:57 +0100 Subject: [PATCH 27/27] Fix (#1258): #load and #load_or segfault when given no params. Fixes #1258. --- src/check_builtin.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 482813792..e405343b9 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -244,7 +244,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Value; } else if (name == "load") { if (ce->args.count != 1) { - error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count); + if (ce->args.count == 0) { + error(ce->close, "'#load' expects 1 argument, got 0"); + } else { + error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count); + } + return false; } @@ -315,7 +320,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } else if (name == "load_or") { if (ce->args.count != 2) { - error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count); + if (ce->args.count == 0) { + error(ce->close, "'#load_or' expects 2 arguments, got 0"); + } else { + error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count); + } return false; }