From 9d28f2e18c80b1537b9a71b907839b20973feb21 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 9 Jun 2024 22:46:45 -0400 Subject: [PATCH 01/26] Fix `or_or_` error messages --- src/parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index c004a8f65..eff5e0c6c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -555,7 +555,7 @@ gb_internal Ast *ast_unary_expr(AstFile *f, Token op, Ast *expr) { syntax_error_with_verbose(expr, "'or_return' within an unary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(expr, "'or_%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); + syntax_error_with_verbose(expr, "'%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); break; } @@ -583,7 +583,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) { syntax_error_with_verbose(left, "'or_return' within a binary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(left, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); + syntax_error_with_verbose(left, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); break; } if (right) switch (right->kind) { @@ -591,7 +591,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) { syntax_error_with_verbose(right, "'or_return' within a binary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(right, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); + syntax_error_with_verbose(right, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); break; } @@ -3102,7 +3102,7 @@ gb_internal void parse_check_or_return(Ast *operand, char const *msg) { syntax_error_with_verbose(operand, "'or_return' use within %s is not wrapped in parentheses (...)", msg); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(operand, "'or_%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); + syntax_error_with_verbose(operand, "'%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); break; } } From 8702bf00d5f5b22142e88258c288cfec2423089c Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 9 Jun 2024 22:47:22 -0400 Subject: [PATCH 02/26] Remove `_` in `Syntax_Error` verbose message --- src/error.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index eed69b779..03d96219b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -516,7 +516,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, if (pos.line == 0) { error_out_empty(); - error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); } else { @@ -527,7 +527,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, error_out_pos(pos); } if (has_ansi_terminal_colours()) { - error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); } error_out_va(fmt, va); error_out("\n"); From 45d1328a85818fc77f2aacc77fa6ce0cfbce3473 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:50:16 -0400 Subject: [PATCH 03/26] Fix typo in a `#force_inline` error message --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 13d0e277d..641f70566 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7672,7 +7672,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (decl->proc_lit) { ast_node(pl, ProcLit, decl->proc_lit); if (pl->inlining == ProcInlining_no_inline) { - error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'"); + error(call, "'#force_inline' cannot be applied to a procedure that has been marked as '#force_no_inline'"); } } } From ff7fcb6d380d1e45402de2b2e0d2b577ad9f6d59 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 10 Jun 2024 03:47:20 -0400 Subject: [PATCH 04/26] Add compilation-related constants `ODIN_VERSION_HASH` is the `git` SHA hash of the commit the Odin compiler was built with. `ODIN_MICROARCH_STRING` is the string passed to `-microarch` when the program was built. `ODIN_OPTIMIZATION_MODE` is an enum value of which optimization mode was used to build the program. --- base/runtime/core.odin | 13 +++++++++++++ src/checker.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 3e24060af..8671920f5 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -560,6 +560,19 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET) */ Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS) +/* + // Defined internally by the compiler + Odin_Optimization_Mode :: enum int { + None = -1, + Minimal = 0, + Size = 1, + Speed = 2, + Aggressive = 3, + } + + ODIN_OPTIMIZATION_MODE // is a constant +*/ +Odin_Optimization_Mode :: type_of(ODIN_OPTIMIZATION_MODE) ///////////////////////////// // Init Startup Procedures // diff --git a/src/checker.cpp b/src/checker.cpp index 8a58bb425..08a03ac62 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1040,6 +1040,8 @@ gb_internal void init_universal(void) { add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch); add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]); } + + add_global_string_constant("ODIN_MICROARCH_STRING", bc->microarch); { GlobalEnumValue values[BuildMode_COUNT] = { @@ -1130,6 +1132,17 @@ gb_internal void init_universal(void) { add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp())); + { + String version = {}; + + #ifdef GIT_SHA + version.text = cast(u8 *)GIT_SHA; + version.len = gb_strlen(GIT_SHA); + #endif + + add_global_string_constant("ODIN_VERSION_HASH", version); + } + { bool f16_supported = lb_use_new_pass_system(); if (is_arch_wasm()) { @@ -1167,6 +1180,18 @@ gb_internal void init_universal(void) { add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags)); } + { + GlobalEnumValue values[5] = { + {"None", -1}, + {"Minimal", 0}, + {"Size", 1}, + {"Speed", 2}, + {"Aggressive", 3}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Optimization_Mode"), values, gb_count_of(values)); + add_global_enum_constant(fields, "ODIN_OPTIMIZATION_MODE", bc->optimization_level); + } // Builtin Procedures From 71a812e7fe7d7200be014c2c0a7e0ea3d7988359 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 10 Jun 2024 05:30:16 -0400 Subject: [PATCH 05/26] Use `get_final_microarchitecture()` for `ODIN_MICROARCH_STRING` --- src/checker.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index 08a03ac62..8f0cc1cd1 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3,6 +3,8 @@ #include "entity.cpp" #include "types.cpp" +String get_final_microarchitecture(); + gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); @@ -1041,7 +1043,7 @@ gb_internal void init_universal(void) { add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]); } - add_global_string_constant("ODIN_MICROARCH_STRING", bc->microarch); + add_global_string_constant("ODIN_MICROARCH_STRING", get_final_microarchitecture()); { GlobalEnumValue values[BuildMode_COUNT] = { From 1945218f6df814ea95233035d0b51585e2522b2e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 14:18:33 +0100 Subject: [PATCH 06/26] Improve parsing for `label: #reverse for` and `label: #partial switch` --- core/odin/parser/parser.odin | 37 +++++++++++++++++++++++++++++++++++- src/parser.cpp | 6 ++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index ad5ee9087..6b0aa2888 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1455,7 +1455,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case "unroll": return parse_unrolled_for_loop(p, tag) case "reverse": - stmt := parse_for_stmt(p) + stmt := parse_stmt(p) if range, is_range := stmt.derived.(^ast.Range_Stmt); is_range { if range.reverse { @@ -3515,6 +3515,25 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { case op.kind == .Colon: expect_token_after(p, .Colon, "identifier list") if .Label in flags && len(lhs) == 1 { + is_partial := false + is_reverse := false + + partial_token: tokenizer.Token + if p.curr_tok.kind == .Hash { + name := peek_token(p) + if name.kind == .Ident && name.text == "partial" && + peek_token(p, 1).kind == .Switch { + partial_token = expect_token(p, .Hash) + expect_token(p, .Ident) + is_partial = true + } else if name.kind == .Ident && name.text == "reverse" && + peek_token(p, 1).kind == .For { + partial_token = expect_token(p, .Hash) + expect_token(p, .Ident) + is_reverse = true + } + } + #partial switch p.curr_tok.kind { case .Open_Brace, .If, .For, .Switch: label := lhs[0] @@ -3529,6 +3548,22 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { case ^ast.Type_Switch_Stmt: n.label = label case ^ast.Range_Stmt: n.label = label } + + if is_partial { + #partial switch n in stmt.derived_stmt { + case ^ast.Switch_Stmt: n.partial = true + case ^ast.Type_Switch_Stmt: n.partial = true + case: + error(p, partial_token.pos, "incorrect use of directive, use '%s: #partial switch'", partial_token.text) + } + } + if is_reverse { + #partial switch n in stmt.derived_stmt { + case ^ast.Range_Stmt: n.reverse = true + case: + error(p, partial_token.pos, "incorrect use of directive, use '%s: #reverse for'", partial_token.text) + } + } } return stmt diff --git a/src/parser.cpp b/src/parser.cpp index eff5e0c6c..2cdcea417 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3747,8 +3747,10 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) { case Ast_TypeSwitchStmt: stmt->TypeSwitchStmt.partial = true; break; + default: + syntax_error(partial_token, "Incorrect use of directive, use '%.*s: #partial switch'", LIT(ast_token(name).string)); + break; } - syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string)); } else if (is_reverse) { switch (stmt->kind) { case Ast_RangeStmt: @@ -5176,7 +5178,7 @@ gb_internal Ast *parse_stmt(AstFile *f) { } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); } else if (tag == "reverse") { - Ast *for_stmt = parse_for_stmt(f); + Ast *for_stmt = parse_stmt(f); if (for_stmt->kind == Ast_RangeStmt) { if (for_stmt->RangeStmt.reverse) { syntax_error(token, "#reverse already applied to a 'for in' statement"); From 71929f737bcb70db6875839b6beba020d84d24ff Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 10 Jun 2024 15:35:23 +0200 Subject: [PATCH 07/26] add forced shutdown to new test runner Currently, a Ctrl+c starts a graceful shutdown of the tests and runner. Sometimes tests get stuck and this would never complete. This simply adds an extra step, if Ctrl+c is given for the second time, just `os.exit` right away. --- core/testing/signal_handler_libc.odin | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin index d76fdd66b..0ab34776e 100644 --- a/core/testing/signal_handler_libc.odin +++ b/core/testing/signal_handler_libc.odin @@ -6,6 +6,7 @@ import "base:intrinsics" import "core:c/libc" import "core:encoding/ansi" import "core:sync" +import "core:os" @require import "core:sys/unix" @(private="file") stop_runner_flag: libc.sig_atomic_t @@ -20,7 +21,13 @@ local_test_index: libc.sig_atomic_t @(private="file") stop_runner_callback :: proc "c" (sig: libc.int) { - intrinsics.atomic_store(&stop_runner_flag, 1) + prev := intrinsics.atomic_add(&stop_runner_flag, 1) + + // If the flag was already set (if this is the second signal sent for example), + // consider this a forced (not graceful) exit. + if prev > 0 { + os.exit(int(sig)) + } } @(private="file") From fa3cae2bb04db76f52f1b2288a9c858f20332b8a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:02:34 +0100 Subject: [PATCH 08/26] Add `intrinsics.procedure_of` ```odin foo :: proc(x: $T) { fmt.println(x) } bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of` bar(333) // prints 333 ``` --- base/intrinsics/intrinsics.odin | 4 +++ src/check_builtin.cpp | 46 +++++++++++++++++++++++++++++++++ src/check_decl.cpp | 17 ++++++++---- src/check_expr.cpp | 1 + src/check_stmt.cpp | 10 ++++++- src/checker.cpp | 4 +++ src/checker.hpp | 6 +++++ src/checker_builtin_procs.hpp | 4 +++ src/parser.hpp | 1 + 9 files changed, 87 insertions(+), 6 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 8873f3bbc..4f6fa2713 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- // if all listed features are supported. has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- + +// Returns the value of the procedure where `x` must be a call expression +procedure_of :: proc(x: $T) -> T where type_is_proc(T) --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 98c695a2c..3aee804df 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1843,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: + case BuiltinProc_procedure_of: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -6157,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_procedure_of: + { + Ast *call_expr = unparen_expr(ce->args[0]); + Operand op = {}; + check_expr_base(c, &op, ce->args[0], nullptr); + if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); + return false; + } + + Ast *proc = call_expr->CallExpr.proc; + Entity *e = entity_of_node(proc); + + if (e == nullptr) { + error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure"); + return false; + } + + TypeAndValue tav = proc->tav; + + + operand->type = e->type; + operand->mode = Addressing_Value; + operand->value = tav.value; + operand->builtin_id = BuiltinProc_Invalid; + operand->proc_group = nullptr; + + if (tav.mode == Addressing_Builtin) { + operand->mode = tav.mode; + operand->builtin_id = cast(BuiltinProcId)e->Builtin.id; + break; + } + + if (!is_type_proc(e->type)) { + gbString s = type_to_string(e->type); + error(ce->args[0], "Expected a procedure value, got '%s'", s); + gb_string_free(s); + return false; + } + + + ce->entity_procedure_of = e; + break; + } + case BuiltinProc_constant_utf16_cstring: { String value = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 43947836b..13b14149a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - gbString str = type_to_string(t); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); - e->type = t_invalid; - return nullptr; + Entity *e = entity_of_node(operand->expr); + if (e->state.load() != EntityState_Resolved) { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; + } } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -479,6 +482,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr entity = check_selector(ctx, &operand, init, e->type); } else { check_expr_or_type(ctx, &operand, init, e->type); + if (init->kind == Ast_CallExpr) { + entity = init->CallExpr.entity_procedure_of; + } } switch (operand.mode) { @@ -526,6 +532,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr return; } + if (entity != nullptr) { if (e->type != nullptr) { Operand x = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 641f70566..01cba881e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E d->defer_use_checked = false; Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); + entity->state.store(EntityState_Resolved); entity->identifier = ident; add_entity_and_decl_info(&nctx, ident, entity, d); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c37c58cd6..f2e3b0242 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } if (do_require) { gbString expr_str = expr_to_string(ce->proc); + defer (gb_string_free(expr_str)); + if (builtin_id) { + String real_name = builtin_procs[builtin_id].name; + if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) { + error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str, + LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name)); + return; + } + } error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); } return; } else if (expr && expr->kind == Ast_SelectorCallExpr) { diff --git a/src/checker.cpp b/src/checker.cpp index 8f0cc1cd1..852fb89bb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1479,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; } return nullptr; } diff --git a/src/checker.hpp b/src/checker.hpp index 2ac4c8e7a..492a64fb6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -51,6 +51,12 @@ enum StmtFlag { enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, + BuiltinProcPkg_COUNT +}; + +String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = { + str_lit("builtin"), + str_lit("intrinsics"), }; struct BuiltinProc { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5f98bb7b3..35acad42f 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc__type_end, + BuiltinProc_procedure_of, + BuiltinProc___entry_point, BuiltinProc_objc_send, @@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, diff --git a/src/parser.hpp b/src/parser.hpp index 0e411d9ac..02f2af28d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ + Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ From c1e81dc14d7afec9e8924e8d93b6de6109cd1790 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:05:24 +0100 Subject: [PATCH 09/26] Fix #3726 --- core/math/linalg/specific.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin index 41d0e5344..b841f0610 100644 --- a/core/math/linalg/specific.odin +++ b/core/math/linalg/specific.odin @@ -527,7 +527,7 @@ angle_from_quaternion :: proc{ @(require_results) axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> Vector3f16 { t1 := 1 - q.w*q.w - if t1 < 0 { + if t1 <= 0 { return {0, 0, 1} } t2 := 1.0 / math.sqrt(t1) @@ -536,7 +536,7 @@ axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> Vector3f16 @(require_results) axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> Vector3f32 { t1 := 1 - q.w*q.w - if t1 < 0 { + if t1 <= 0 { return {0, 0, 1} } t2 := 1.0 / math.sqrt(t1) @@ -545,7 +545,7 @@ axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> Vector3f32 @(require_results) axis_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> Vector3f64 { t1 := 1 - q.w*q.w - if t1 < 0 { + if t1 <= 0 { return {0, 0, 1} } t2 := 1.0 / math.sqrt(t1) From eef2aef021a039e627693c73c7962ed57f4ea073 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:07:45 +0100 Subject: [PATCH 10/26] Fix #3724 --- src/llvm_backend_debug.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index afbf3e046..f1ace5f06 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -626,50 +626,50 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Basic_complex32: { LLVMMetadataRef elements[2] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0); - elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4); + elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0*16); + elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 1*16); return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements)); } case Basic_complex64: { LLVMMetadataRef elements[2] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0); - elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 4); + elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0*32); + elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 2*32); return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements)); } case Basic_complex128: { LLVMMetadataRef elements[2] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0); - elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 8); + elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0*64); + elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 1*64); return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements)); } case Basic_quaternion64: { LLVMMetadataRef elements[4] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0); - elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4); - elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8); - elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12); + elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0*16); + elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 1*16); + elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 2*16); + elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 3*16); return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements)); } case Basic_quaternion128: { LLVMMetadataRef elements[4] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0); - elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 4); - elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 8); - elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 12); + elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0*32); + elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 1*32); + elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 2*32); + elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 3*32); return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements)); } case Basic_quaternion256: { LLVMMetadataRef elements[4] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0); - elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 8); - elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 16); - elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 24); + elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0*64); + elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 1*64); + elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 2*64); + elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 3*64); return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements)); } From f1779c85dedb8bb309a9afa8cfa7e35ad727d237 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 18:50:53 +0100 Subject: [PATCH 11/26] Fix #3727 --- src/parser.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parser.cpp b/src/parser.cpp index 2cdcea417..0cd96f5b5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2091,6 +2091,9 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) { gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { Ast *expr = parse_unary_expr(f, false); Ast *e = strip_or_return_expr(expr); + if (e == nullptr) { + return expr; + } if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); return ast_bad_expr(f, token, f->curr_token); From 50464bdce374e688313961a37f12197b505a6391 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:51:58 -0400 Subject: [PATCH 12/26] Use `#any_int` for `reserve_*` and `resize_*` procs Resolves #3088 --- base/runtime/core_builtin.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 00c30d3fd..a9566c831 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -383,7 +383,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { // // Note: Prefer the procedure group `reserve` @builtin -reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error { +reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil } @@ -721,12 +721,12 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i } @builtin -reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error { +reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { return _reserve_dynamic_array(array, capacity, true, loc) } @builtin -non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error { +non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { return _reserve_dynamic_array(array, capacity, false, loc) } @@ -773,12 +773,12 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, } @builtin -resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error { +resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { return _resize_dynamic_array(array, length, true, loc=loc) } @builtin -non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error { +non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { return _resize_dynamic_array(array, length, false, loc=loc) } From bd198aeada1ea94b69fed5a6c9a7acf0759c9884 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:37:54 -0400 Subject: [PATCH 13/26] Fix #3460 --- core/time/datetime/datetime.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/time/datetime/datetime.odin b/core/time/datetime/datetime.odin index e15ced5a5..89fa2ce98 100644 --- a/core/time/datetime/datetime.odin +++ b/core/time/datetime/datetime.odin @@ -127,13 +127,13 @@ days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err: return delta.days, .None } -last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i64, err: Error) { +last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i8, err: Error) { // Not using formula 2.27 from the book. This is far simpler and gives the same answer. validate(Date{year, month, 1}) or_return month_days := MONTH_DAYS - day = i64(month_days[month]) + day = month_days[month] if month == 2 && is_leap_year(year) { day += 1 } From 76292c8ed5c78f714d64f32bb1ea231cb64152c1 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:46:10 -0400 Subject: [PATCH 14/26] Forbid all BSDs from running `core:net` tests `net` is not yet implemented on them. --- tests/core/net/test_core_net.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 6aada88a6..f806463e9 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -8,7 +8,7 @@ A test suite for `core:net` */ -//+build !netbsd +//+build !netbsd !freebsd !openbsd package test_core_net import "core:testing" From c68560c5737b16c71a25aa0b11cfddc139a7c565 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 01:03:37 -0400 Subject: [PATCH 15/26] Use correct `__error` link name for FreeBSD --- core/path/filepath/path_unix.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index a4b27b027..b44a6a344 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -56,7 +56,7 @@ foreign libc { @(link_name="free") _unix_free :: proc(ptr: rawptr) --- } -when ODIN_OS == .Darwin { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { @(private) foreign libc { @(link_name="__error") __error :: proc() -> ^i32 --- From 40e99ebb1018914813d8b74ab9db222174b705dc Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 03:34:10 -0400 Subject: [PATCH 16/26] Fix file open `O_*` flags on FreeBSD --- core/os/os_freebsd.odin | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index cdd44d301..1b32128d6 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -112,15 +112,15 @@ EOWNERDEAD: Errno : 96 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_NONBLOCK :: 0x00004 +O_APPEND :: 0x00008 +O_ASYNC :: 0x00040 +O_SYNC :: 0x00080 +O_CREATE :: 0x00200 +O_TRUNC :: 0x00400 +O_EXCL :: 0x00800 +O_NOCTTY :: 0x08000 +O_CLOEXEC :: 0100000 SEEK_DATA :: 3 From a3da796d5410efd22a991bdd82fec947a0a11471 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 03:42:13 -0400 Subject: [PATCH 17/26] Fix `file_size` on FreeBSD It was using the generic UNIX `fstat` implemented in Odin, which is more than what is needed here. This also avoids the issue of needing a proper `absolute_path_from_handle` implementation for it to work without error. --- core/os/os_freebsd.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index 1b32128d6..45f3ec23c 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -365,7 +365,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { } file_size :: proc(fd: Handle) -> (i64, Errno) { - s, err := fstat(fd) + s, err := _fstat(fd) if err != ERROR_NONE { return -1, err } From fc88de12c2284edb3eb5b1d22d1fd4924ea82aaa Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 04:13:09 -0400 Subject: [PATCH 18/26] Implement `absolute_path_from_handle` for FreeBSD --- core/os/os_freebsd.odin | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index 45f3ec23c..8fe179478 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -140,6 +140,8 @@ RTLD_NOLOAD :: 0x02000 MAX_PATH :: 1024 +KINFO_FILE_SIZE :: 1392 + args := _alloc_command_line_arguments() Unix_File_Time :: struct { @@ -191,6 +193,21 @@ OS_Stat :: struct { lspare: [10]u64, } +KInfo_File :: struct { + structsize: c.int, + type: c.int, + fd: c.int, + ref_count: c.int, + flags: c.int, + pad0: c.int, + offset: i64, + + // NOTE(Feoramund): This field represents a complicated union that I am + // avoiding implementing for now. I only need the path data below. + _union: [336]byte, + + path: [MAX_PATH]c.char, +} // since FreeBSD v12 Dirent :: struct { @@ -254,6 +271,8 @@ X_OK :: 1 // Test for execute permission W_OK :: 2 // Test for write permission R_OK :: 4 // Test for read permission +F_KINFO :: 22 + foreign libc { @(link_name="__error") __errno_location :: proc() -> ^c.int --- @@ -274,6 +293,7 @@ foreign libc { @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int --- @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- @@ -591,9 +611,26 @@ _readlink :: proc(path: string) -> (string, Errno) { return "", Errno{} } -// XXX FreeBSD absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { - return "", Errno(ENOSYS) + // NOTE(Feoramund): The situation isn't ideal, but this was the best way I + // could find to implement this. There are a couple outstanding bug reports + // regarding the desire to retrieve an absolute path from a handle, but to + // my knowledge, there hasn't been any work done on it. + // + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570 + // + // This may be unreliable, according to a comment from 2023. + + kinfo: KInfo_File + kinfo.structsize = KINFO_FILE_SIZE + + res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo) + if res == -1 { + return "", Errno(get_last_error()) + } + + path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path)) + return path, ERROR_NONE } absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { From 0e6bcd0dbb8d5d979b2c7b772907d9576ed586ff Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:30:31 -0400 Subject: [PATCH 19/26] Add FreeBSD to CI --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66c848a8a..c9c453331 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,35 @@ jobs: ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false (cd tests/issues; ./run.sh) + build_freebsd: + name: FreeBSD Build, Check, and Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build, Check, and Test + timeout-minutes: 15 + uses: vmactions/freebsd-vm@v1 + with: + usesh: true + copyback: false + prepare: | + pkg install -y gmake git bash python3 libxml2 llvm17 + run: | + # `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72 + set -e -x + git config --global --add safe.directory $(pwd) + gmake release + ./odin version + ./odin report + gmake -C vendor/stb/src + gmake -C vendor/cgltf/src + gmake -C vendor/miniaudio/src + ./odin check examples/all -vet -strict-style -target:freebsd_amd64 + ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false + ./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false + ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false + ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false + (cd tests/issues; ./run.sh) ci: strategy: fail-fast: false From 61c630bbf8367ab473897b066175990d0996bd14 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:23:43 -0400 Subject: [PATCH 20/26] Fix #3730 --- src/check_decl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 13b14149a..8f4d9f922 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -89,6 +89,9 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o return nullptr; } else if (is_type_polymorphic(t)) { Entity *e = entity_of_node(operand->expr); + if (e == nullptr) { + return nullptr; + } if (e->state.load() != EntityState_Resolved) { gbString str = type_to_string(t); defer (gb_string_free(str)); From 9b0e87544a2de1f817be78f0183c8485870d9fcf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Jun 2024 12:07:22 +0100 Subject: [PATCH 21/26] Unify LLVMVerifyFunction invocations into on place --- src/llvm_backend.cpp | 101 ++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 5dc6d94d5..01680ffa9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1125,6 +1125,49 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) { lb_end_procedure_body(p); } +gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=false) { + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + char *llvm_error = nullptr; + + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); + LLVMDumpValue(p->value); + gb_printf_err("\n"); + if (dump_ll) { + gb_printf_err("\n\n\n"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + } + } + LLVMVerifyFunction(p->value, LLVMPrintMessageAction); + exit_with_errors(); + } +} + +gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { + char *llvm_error = nullptr; + defer (LLVMDisposeMessage(llvm_error)); + lbModule *m = cast(lbModule *)data; + + if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { + gb_printf_err("LLVM Error:\n%s\n", llvm_error); + if (build_context.keep_temp_files) { + TIME_SECTION("LLVM Print Module to File"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + exit_with_errors(); + return false; + } + } + exit_with_errors(); + return 1; + } + return 0; +} + + + gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin); @@ -1227,13 +1270,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc lb_end_procedure_body(p); - if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - + lb_verify_function(main_module, p); return p; } @@ -1256,13 +1293,7 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C lb_end_procedure_body(p); - if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - + lb_verify_function(main_module, p); return p; } @@ -2523,27 +2554,6 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { return concatenate_strings(permanent_allocator(), path, ext); } -gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { - char *llvm_error = nullptr; - defer (LLVMDisposeMessage(llvm_error)); - lbModule *m = cast(lbModule *)data; - if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { - gb_printf_err("LLVM Error:\n%s\n", llvm_error); - if (build_context.keep_temp_files) { - TIME_SECTION("LLVM Print Module to File"); - String filepath_ll = lb_filepath_ll_for_module(m); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - exit_with_errors(); - return false; - } - } - exit_with_errors(); - return 1; - } - return 0; -} - gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { for (auto const &entry : gen->modules) { @@ -2777,12 +2787,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star } - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } + lb_verify_function(m, p); lb_run_function_pass_manager(default_function_pass_manager, p, lbFunctionPassManager_default); return p; @@ -2812,19 +2817,7 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { } } - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - char *llvm_error = nullptr; - - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - String filepath_ll = lb_filepath_ll_for_module(m); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - } - LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - exit_with_errors(); - } + lb_verify_function(m, p, true); } From 0b02c67cdf316d55232f0433d51f6ea781d74a22 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Jun 2024 12:19:52 +0100 Subject: [PATCH 22/26] Minor clean up for backend --- src/check_decl.cpp | 9 ++- src/entity.cpp | 1 + src/llvm_backend.cpp | 130 ++++++++++++++++++++--------------- src/llvm_backend.hpp | 3 +- src/llvm_backend_general.cpp | 3 +- 5 files changed, 89 insertions(+), 57 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 13b14149a..a5c9119ea 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1142,7 +1142,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } if (ac.link_name.len > 0) { - e->Procedure.link_name = ac.link_name; + String ln = ac.link_name; + e->Procedure.link_name = ln; + if (ln == "memcpy" || + ln == "memmove" || + ln == "mem_copy" || + ln == "mem_copy_non_overlapping") { + e->Procedure.is_memcpy_like = true; + } } if (ac.deferred_procedure.entity != nullptr) { diff --git a/src/entity.cpp b/src/entity.cpp index 7f484e308..8f55c1faf 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -256,6 +256,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool entry_point_only : 1; bool has_instrumentation : 1; + bool is_memcpy_like : 1; } Procedure; struct { Array entities; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 01680ffa9..81de2c224 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1300,18 +1300,14 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) { lbModule *m = cast(lbModule *)data; - for (Entity *e : m->global_procedures_and_types_to_create) { - if (e->kind == Entity_TypeName) { - (void)lb_get_entity_name(m, e); - lb_type(m, e->type); - } + for (Entity *e : m->global_types_to_create) { + (void)lb_get_entity_name(m, e); + (void)lb_type(m, e->type); } - for (Entity *e : m->global_procedures_and_types_to_create) { - if (e->kind == Entity_Procedure) { - (void)lb_get_entity_name(m, e); - array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); - } + for (Entity *e : m->global_procedures_to_create) { + (void)lb_get_entity_name(m, e); + array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); } return 0; } @@ -1365,16 +1361,24 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker m = lb_module_of_entity(gen, e); } - array_add(&m->global_procedures_and_types_to_create, e); + if (e->kind == Entity_Procedure) { + array_add(&m->global_procedures_to_create, e); + } else if (e->kind == Entity_TypeName) { + array_add(&m->global_types_to_create, e); + } } - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_generate_procedures_and_types_per_module, m); - } else { + } + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_generate_procedures_and_types_per_module(m); } + } thread_pool_wait(); @@ -2405,16 +2409,19 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) { } gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_generate_procedures_worker_proc, m); - } else { + } + + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_generate_procedures_worker_proc(m); } } - - thread_pool_wait(); } gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { @@ -2428,17 +2435,20 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc } gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - // NOTE(bill): procedures may be added during generation - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + // NOTE(bill): procedures may be added during generation thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m); - } else { + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + // NOTE(bill): procedures may be added during generation lb_generate_missing_procedures_to_check_worker_proc(m); } } - - thread_pool_wait(); } gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { @@ -2451,32 +2461,45 @@ gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { } gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_llvm_function_pass_per_module, m); - } else { + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_llvm_function_pass_per_module(m); } } - thread_pool_wait(); } gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); - wd->m = m; - wd->target_machine = m->target_machine; + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = m->target_machine; - if (do_threading) { - thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); - } else { + if (do_threading) { + thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); + } else { + lb_llvm_module_pass_worker_proc(wd); + } + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = m->target_machine; lb_llvm_module_pass_worker_proc(wd); } } - thread_pool_wait(); } gb_internal String lb_filepath_ll_for_module(lbModule *m) { @@ -2556,17 +2579,21 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_llvm_module_verification_worker_proc, m); - } else { + } + thread_pool_wait(); + + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; if (lb_llvm_module_verification_worker_proc(m)) { return false; } } } - thread_pool_wait(); return true; } @@ -2808,13 +2835,8 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { lb_end_procedure(p); // Add Flags - if (p->body != nullptr) { - if (p->name == "memcpy" || p->name == "memmove" || - p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || - string_starts_with(p->name, str_lit("llvm.memcpy")) || - string_starts_with(p->name, str_lit("llvm.memmove"))) { - p->flags |= lbProcedureFlag_WithoutMemcpyPass; - } + if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) { + p->flags |= lbProcedureFlag_WithoutMemcpyPass; } lb_verify_function(m, p, true); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 9f7bc8843..447e93d42 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -181,7 +181,8 @@ struct lbModule { std::atomic nested_type_name_guid; Array procedures_to_generate; - Array global_procedures_and_types_to_create; + Array global_procedures_to_create; + Array global_types_to_create; lbProcedure *curr_procedure; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index ea98fc60a..03d0f8b32 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -78,7 +78,8 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count); map_init(&m->procedure_values, c->info.all_procedures.count*2); } - array_init(&m->global_procedures_and_types_to_create, a, 0, 1024); + array_init(&m->global_procedures_to_create, a, 0, 1024); + array_init(&m->global_types_to_create, a, 0, 1024); array_init(&m->missing_procedures_to_check, a, 0, 16); map_init(&m->debug_values); From 1dc90103bd9e4c7783222d34cd16a2237a7dc377 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Jun 2024 12:30:24 +0100 Subject: [PATCH 23/26] Make verification ignorable with a define flag --- src/llvm_backend.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 81de2c224..04c4ce244 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9,6 +9,11 @@ #endif +#ifndef LLVM_IGNORE_VERIFICATION +#define LLVM_IGNORE_VERIFICATION 0 +#endif + + #include "llvm_backend.hpp" #include "llvm_abi.cpp" #include "llvm_backend_opt.cpp" @@ -1126,6 +1131,10 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) { } gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=false) { + if (LLVM_IGNORE_VERIFICATION) { + return; + } + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { char *llvm_error = nullptr; @@ -2579,6 +2588,10 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { + if (LLVM_IGNORE_VERIFICATION) { + return true; + } + if (do_threading) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; From 3ff89528134b078a7af3e1d2335e154cf94bf843 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Jun 2024 13:11:14 +0100 Subject: [PATCH 24/26] Replace `panic(fmt.tprintf(` antipattern with `fmt.panicf` --- core/crypto/rand_darwin.odin | 4 ++-- core/crypto/rand_linux.odin | 2 +- core/crypto/rand_windows.odin | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/crypto/rand_darwin.odin b/core/crypto/rand_darwin.odin index 5355f31c5..56acb5d22 100644 --- a/core/crypto/rand_darwin.odin +++ b/core/crypto/rand_darwin.odin @@ -11,7 +11,7 @@ HAS_RAND_BYTES :: true _rand_bytes :: proc(dst: []byte) { err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst)) if err != .Success { - msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err)) - panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)) + msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err)) + fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg) } } diff --git a/core/crypto/rand_linux.odin b/core/crypto/rand_linux.odin index 43b3b3075..7e0edbb7e 100644 --- a/core/crypto/rand_linux.odin +++ b/core/crypto/rand_linux.odin @@ -32,7 +32,7 @@ _rand_bytes :: proc (dst: []byte) { // All other failures are things that should NEVER happen // unless the kernel interface changes (ie: the Linux // developers break userland). - panic(fmt.tprintf("crypto: getrandom failed: %v", errno)) + fmt.panicf("crypto: getrandom failed: %v", errno) } l -= n_read dst = dst[n_read:] diff --git a/core/crypto/rand_windows.odin b/core/crypto/rand_windows.odin index a92d376cb..0ddbcaf9a 100644 --- a/core/crypto/rand_windows.odin +++ b/core/crypto/rand_windows.odin @@ -20,7 +20,7 @@ _rand_bytes :: proc(dst: []byte) { panic("crypto: BCryptGenRandom Invalid parameter") case: // Unknown error - panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret)) + fmt.panicf("crypto: BCryptGenRandom failed: %d\n", ret) } } } From 35a845b93f11b152879bd08f90d196afcb7b8b25 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Jun 2024 13:12:41 +0100 Subject: [PATCH 25/26] Fix indentation --- core/crypto/rand_windows.odin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/crypto/rand_windows.odin b/core/crypto/rand_windows.odin index 0ddbcaf9a..9cd647cc1 100644 --- a/core/crypto/rand_windows.odin +++ b/core/crypto/rand_windows.odin @@ -11,16 +11,16 @@ _rand_bytes :: proc(dst: []byte) { ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)) if ret != os.ERROR_NONE { switch ret { - case os.ERROR_INVALID_HANDLE: - // The handle to the first parameter is invalid. - // This should not happen here, since we explicitly pass nil to it - panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm") - case os.ERROR_INVALID_PARAMETER: - // One of the parameters was invalid - panic("crypto: BCryptGenRandom Invalid parameter") - case: - // Unknown error - fmt.panicf("crypto: BCryptGenRandom failed: %d\n", ret) + case os.ERROR_INVALID_HANDLE: + // The handle to the first parameter is invalid. + // This should not happen here, since we explicitly pass nil to it + panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm") + case os.ERROR_INVALID_PARAMETER: + // One of the parameters was invalid + panic("crypto: BCryptGenRandom Invalid parameter") + case: + // Unknown error + fmt.panicf("crypto: BCryptGenRandom failed: %d\n", ret) } } } From 1a22f82f94928e43bf3945cf43dc60d8daab0067 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Tue, 11 Jun 2024 18:03:20 +0200 Subject: [PATCH 26/26] Changed rl.SetShaderValue etc to take a c.int like in original raylib.h. You should be able to use other values than the ShaderLocationIndex enum, that enum is only for build in things in raylib. Added #any_int on those procs so you can pass both int and also a ShaderLocationIndex. --- vendor/raylib/raylib.odin | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index ebb1cdd27..3d1b74058 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -984,12 +984,14 @@ foreign lib { LoadShader :: proc(vsFileName, fsFileName: cstring) -> Shader --- // Load shader from files and bind default locations LoadShaderFromMemory :: proc(vsCode, fsCode: cstring) -> Shader --- // Load shader from code strings and bind default locations IsShaderReady :: proc(shader: Shader) -> bool --- // Check if a shader is ready - GetShaderLocation :: proc(shader: Shader, uniformName: cstring) -> ShaderLocationIndex --- // Get shader uniform location - GetShaderLocationAttrib :: proc(shader: Shader, attribName: cstring) -> ShaderLocationIndex --- // Get shader attribute location - SetShaderValue :: proc(shader: Shader, locIndex: ShaderLocationIndex, value: rawptr, uniformType: ShaderUniformDataType) --- // Set shader uniform value - SetShaderValueV :: proc(shader: Shader, locIndex: ShaderLocationIndex, value: rawptr, uniformType: ShaderUniformDataType, count: c.int) --- // Set shader uniform value vector - SetShaderValueMatrix :: proc(shader: Shader, locIndex: ShaderLocationIndex, mat: Matrix) --- // Set shader uniform value (matrix 4x4) - SetShaderValueTexture :: proc(shader: Shader, locIndex: ShaderLocationIndex, texture: Texture2D) --- // Set shader uniform value for texture (sampler2d) + GetShaderLocation :: proc(shader: Shader, uniformName: cstring) -> c.int --- // Get shader uniform location + GetShaderLocationAttrib :: proc(shader: Shader, attribName: cstring) -> c.int --- // Get shader attribute location + + // We use #any_int here so we can pass ShaderLocationIndex + SetShaderValue :: proc(shader: Shader, #any_int locIndex: c.int, value: rawptr, uniformType: ShaderUniformDataType) --- // Set shader uniform value + SetShaderValueV :: proc(shader: Shader, #any_int locIndex: c.int, value: rawptr, uniformType: ShaderUniformDataType, count: c.int) --- // Set shader uniform value vector + SetShaderValueMatrix :: proc(shader: Shader, #any_int locIndex: c.int, mat: Matrix) --- // Set shader uniform value (matrix 4x4) + SetShaderValueTexture :: proc(shader: Shader, #any_int locIndex: c.int, texture: Texture2D) --- // Set shader uniform value for texture (sampler2d) UnloadShader :: proc(shader: Shader) --- // Unload shader from GPU memory (VRAM) // Screen-space-related functions