From 759e3428723828113b5c1624ef1ae7bfa0095c2f Mon Sep 17 00:00:00 2001 From: korvahkh <92224397+korvahkh@users.noreply.github.com> Date: Fri, 29 Dec 2023 22:39:34 -0600 Subject: Fix #3056 --- src/llvm_backend_proc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f64cbd52a..1244bd377 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3385,7 +3385,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } lbValue arg = args[arg_index]; - if (arg.value == nullptr) { + if (arg.value == nullptr && arg.type == nullptr) { switch (e->kind) { case Entity_TypeName: args[arg_index] = lb_const_nil(p->module, e->type); -- cgit v1.2.3 From 0b83e3dae5c96550f1500612aa7073ee8f6ae5b6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 14:29:14 +0000 Subject: Enforce naming the parameters with `builtin.quaternion` to reduce confusion --- core/encoding/json/unmarshal.odin | 6 +- core/math/linalg/general.odin | 2 +- core/runtime/internal.odin | 12 ++-- core/sys/windows/advapi32.odin | 5 ++ examples/demo/demo.odin | 8 ++- src/check_builtin.cpp | 139 ++++++++++++++++++++++++++++++++++---- src/llvm_backend_proc.cpp | 43 ++++++++---- 7 files changed, 174 insertions(+), 41 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index afcc43c0c..c1905f6b0 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -137,9 +137,9 @@ assign_float :: proc(val: any, f: $T) -> bool { case complex64: dst = complex(f32(f), 0) case complex128: dst = complex(f64(f), 0) - case quaternion64: dst = quaternion(f16(f), 0, 0, 0) - case quaternion128: dst = quaternion(f32(f), 0, 0, 0) - case quaternion256: dst = quaternion(f64(f), 0, 0, 0) + case quaternion64: dst = quaternion(w=f16(f), x=0, y=0, z=0) + case quaternion128: dst = quaternion(w=f32(f), x=0, y=0, z=0) + case quaternion256: dst = quaternion(w=f64(f), x=0, y=0, z=0) case: return false } diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin index b58305c63..a61fe9189 100644 --- a/core/math/linalg/general.odin +++ b/core/math/linalg/general.odin @@ -70,7 +70,7 @@ outer_product :: builtin.outer_product @(require_results) quaternion_inverse :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) { - return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0) + return conj(q) * quaternion(w=1.0/dot(q, q), x=0, y=0, z=0) } diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index d0e550743..0d7bdee8b 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -730,7 +730,7 @@ mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 { t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1 t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0 - return quaternion(t0, t1, t2, t3) + return quaternion(w=t0, x=t1, y=t2, z=t3) } mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { @@ -742,7 +742,7 @@ mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1 t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0 - return quaternion(t0, t1, t2, t3) + return quaternion(w=t0, x=t1, y=t2, z=t3) } mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { @@ -754,7 +754,7 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1 t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0 - return quaternion(t0, t1, t2, t3) + return quaternion(w=t0, x=t1, y=t2, z=t3) } quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 { @@ -768,7 +768,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 { t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2 t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2 - return quaternion(t0, t1, t2, t3) + return quaternion(w=t0, x=t1, y=t2, z=t3) } quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { @@ -782,7 +782,7 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2 t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2 - return quaternion(t0, t1, t2, t3) + return quaternion(w=t0, x=t1, y=t2, z=t3) } quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { @@ -796,7 +796,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2 t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2 - return quaternion(t0, t1, t2, t3) + return quaternion(w=t0, x=t1, y=t2, z=t3) } @(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) diff --git a/core/sys/windows/advapi32.odin b/core/sys/windows/advapi32.odin index dc7ec1e08..86e173211 100644 --- a/core/sys/windows/advapi32.odin +++ b/core/sys/windows/advapi32.odin @@ -13,6 +13,11 @@ foreign advapi32 { DesiredAccess: DWORD, TokenHandle: ^HANDLE) -> BOOL --- + OpenThreadToken :: proc(ThreadHandle: HANDLE, + DesiredAccess: DWORD, + OpenAsSelf: BOOL, + TokenHandle: ^HANDLE) -> BOOL --- + CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD --- CryptGenRandom :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD --- CryptReleaseContext :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD --- diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index b890d5778..bc6a4d9ea 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1514,7 +1514,7 @@ quaternions :: proc() { { // Quaternion operations q := 1 + 2i + 3j + 4k - r := quaternion(5, 6, 7, 8) + r := quaternion(real=5, imag=6, jmag=7, kmag=8) t := q * r fmt.printf("(%v) * (%v) = %v\n", q, r, t) v := q / r @@ -1527,8 +1527,10 @@ quaternions :: proc() { { // The quaternion types q128: quaternion128 // 4xf32 q256: quaternion256 // 4xf64 - q128 = quaternion(1, 0, 0, 0) - q256 = 1 // quaternion(1, 0, 0, 0) + q128 = quaternion(w=1, x=0, y=0, z=0) + q256 = 1 // quaternion(x=0, y=0, z=0, w=1) + + // NOTE: The internal memory layout of a quaternion is xyzw } { // Built-in procedures q := 1 + 2i + 3j + 4k diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index b3aaab754..55962f89f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1666,7 +1666,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (ce->args.count > 0) { if (ce->args[0]->kind == Ast_FieldValue) { - if (id != BuiltinProc_soa_zip) { + switch (id) { + case BuiltinProc_soa_zip: + case BuiltinProc_quaternion: + // okay + break; + default: error(call, "'field = value' calling is not allowed on built-in procedures"); return false; } @@ -2299,8 +2304,32 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } case BuiltinProc_quaternion: { + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + + bool fail = false; + for (Ast *arg : ce->args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name)); + fail = true; + break; + } + } + + if (fail) { + operand->type = t_untyped_quaternion; + operand->mode = Addressing_Constant; + operand->value = exact_value_quaternion(0.0, 0.0, 0.0, 0.0); + break; + } + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type - Operand x = *operand; + Operand x = {}; Operand y = {}; Operand z = {}; Operand w = {}; @@ -2309,23 +2338,103 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As operand->type = t_invalid; operand->mode = Addressing_Invalid; - check_expr(c, &y, ce->args[1]); - if (y.mode == Addressing_Invalid) { - return false; - } - check_expr(c, &z, ce->args[2]); - if (y.mode == Addressing_Invalid) { - return false; - } - check_expr(c, &w, ce->args[3]); - if (y.mode == Addressing_Invalid) { - return false; - } + if (first_is_field_value) { + u32 fields_set[4] = {}; // 0 unset, 1 xyzw, 2 real/etc + + auto const check_field = [&fields_set, &builtin_name](CheckerContext *c, Operand *o, Ast *arg, i32 *index) -> bool { + *index = -1; + + ast_node(field, FieldValue, arg); + String name = {}; + if (field->field->kind == Ast_Ident) { + name = field->field->Ident.token.string; + } else { + error(field->field, "Expected an identifier for field argument"); + return false; + } + + u32 style = 0; + + if (name == "x") { + *index = 1; style = 1; + } else if (name == "y") { + *index = 2; style = 1; + } else if (name == "z") { + *index = 3; style = 1; + } else if (name == "w") { + *index = 0; style = 1; + } else if (name == "imag") { + *index = 1; style = 2; + } else if (name == "jmag") { + *index = 2; style = 2; + } else if (name == "kmag") { + *index = 3; style = 2; + } else if (name == "real") { + *index = 0; style = 2; + } else { + error(field->field, "Unknown name for '%.*s', expected (w, x, y, z; or real, imag, jmag, kmag), got '%.*s'", LIT(builtin_name), LIT(name)); + return false; + } + + if (fields_set[*index]) { + error(field->field, "Previously assigned field: '%.*s'", LIT(name)); + } + fields_set[*index] = style; + + check_expr(c, o, field->value); + return o->mode != Addressing_Invalid; + }; + + // TODO(bill): disallow style mixing + Operand *refs[4] = {&x, &y, &z, &w}; + + for (i32 i = 0; i < 4; i++) { + i32 index = -1; + Operand o = {}; + bool ok = check_field(c, &o, ce->args[i], &index); + if (!ok) { + return false; + } + + *refs[index] = o; + } + + for (i32 i = 0; i < 4; i++) { + GB_ASSERT(fields_set[i]); + } + for (i32 i = 1; i < 4; i++) { + if (fields_set[i] != fields_set[i-1]) { + error(call, "Mixture of xyzw and real/etc is not allowed with '%.*s'", LIT(builtin_name)); + break; + } + } + } else { + error(call, "'%.*s' requires that all arguments are named (w, x, y, z; or real, imag, jmag, kmag)", LIT(builtin_name)); + + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &z, ce->args[2]); + if (z.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &w, ce->args[3]); + if (w.mode == Addressing_Invalid) { + return false; + } + } convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && @@ -3092,7 +3201,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As mix = arg->kind == Ast_FieldValue; } if (mix) { - error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed"); + error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name)); fail = true; break; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1244bd377..3f16d07a5 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1826,24 +1826,41 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } case BuiltinProc_quaternion: { - lbValue real = lb_build_expr(p, ce->args[0]); - lbValue imag = lb_build_expr(p, ce->args[1]); - lbValue jmag = lb_build_expr(p, ce->args[2]); - lbValue kmag = lb_build_expr(p, ce->args[3]); + lbValue xyzw[4] = {}; + for (i32 i = 0; i < 4; i++) { + ast_node(f, FieldValue, ce->args[i]); + GB_ASSERT(f->field->kind == Ast_Ident); + String name = f->field->Ident.token.string; + i32 index = -1; + + // @QuaternionLayout + if (name == "x" || name == "imag") { + index = 0; + } else if (name == "y" || name == "jmag") { + index = 1; + } else if (name == "z" || name == "kmag") { + index = 2; + } else if (name == "w" || name == "real") { + index = 3; + } + GB_ASSERT(index >= 0); + + xyzw[index] = lb_build_expr(p, ce->args[i]); + } + - // @QuaternionLayout lbAddr dst_addr = lb_add_local_generated(p, tv.type, false); lbValue dst = lb_addr_get_ptr(p, dst_addr); Type *ft = base_complex_elem_type(tv.type); - real = lb_emit_conv(p, real, ft); - imag = lb_emit_conv(p, imag, ft); - jmag = lb_emit_conv(p, jmag, ft); - kmag = lb_emit_conv(p, kmag, ft); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), real); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), imag); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), jmag); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), kmag); + xyzw[0] = lb_emit_conv(p, xyzw[0], ft); + xyzw[1] = lb_emit_conv(p, xyzw[1], ft); + xyzw[2] = lb_emit_conv(p, xyzw[2], ft); + xyzw[3] = lb_emit_conv(p, xyzw[3], ft); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), xyzw[0]); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), xyzw[1]); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), xyzw[2]); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), xyzw[3]); return lb_emit_load(p, dst); } -- cgit v1.2.3 From 3bf7b416e72259059a91a785d70b50961f6c41c6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 14:36:58 +0000 Subject: Fix `builtin.quaternion` generation --- core/builtin/builtin.odin | 2 +- src/check_builtin.cpp | 126 ++++++++++++++++++++-------------------------- src/llvm_backend_proc.cpp | 2 +- 3 files changed, 56 insertions(+), 74 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/builtin/builtin.odin b/core/builtin/builtin.odin index 211db9770..5cba3c8ea 100644 --- a/core/builtin/builtin.odin +++ b/core/builtin/builtin.odin @@ -110,7 +110,7 @@ typeid_of :: proc($T: typeid) -> typeid --- swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T --- complex :: proc(real, imag: Float) -> Complex_Type --- -quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type --- +quaternion :: proc(imag, jmag, kmag, real: Float) -> Quaternion_Type --- // fields must be named real :: proc(value: Complex_Or_Quaternion) -> Float --- imag :: proc(value: Complex_Or_Quaternion) -> Float --- jmag :: proc(value: Quaternion) -> Float --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 55962f89f..0b3d65f78 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2328,11 +2328,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } - // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type - Operand x = {}; - Operand y = {}; - Operand z = {}; - Operand w = {}; + // quaternion :: proc(imag, jmag, kmag, real: float_type) -> complex_type + Operand xyzw[4] = {}; // NOTE(bill): Invalid will be the default till fixed operand->type = t_invalid; @@ -2356,21 +2353,21 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As u32 style = 0; if (name == "x") { - *index = 1; style = 1; + *index = 0; style = 1; } else if (name == "y") { - *index = 2; style = 1; + *index = 1; style = 1; } else if (name == "z") { - *index = 3; style = 1; + *index = 2; style = 1; } else if (name == "w") { - *index = 0; style = 1; + *index = 3; style = 1; } else if (name == "imag") { - *index = 1; style = 2; + *index = 0; style = 2; } else if (name == "jmag") { - *index = 2; style = 2; + *index = 1; style = 2; } else if (name == "kmag") { - *index = 3; style = 2; + *index = 2; style = 2; } else if (name == "real") { - *index = 0; style = 2; + *index = 3; style = 2; } else { error(field->field, "Unknown name for '%.*s', expected (w, x, y, z; or real, imag, jmag, kmag), got '%.*s'", LIT(builtin_name), LIT(name)); return false; @@ -2385,9 +2382,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return o->mode != Addressing_Invalid; }; - // TODO(bill): disallow style mixing - - Operand *refs[4] = {&x, &y, &z, &w}; + Operand *refs[4] = {&xyzw[0], &xyzw[1], &xyzw[2], &xyzw[3]}; for (i32 i = 0; i < 4; i++) { i32 index = -1; @@ -2412,57 +2407,40 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } else { error(call, "'%.*s' requires that all arguments are named (w, x, y, z; or real, imag, jmag, kmag)", LIT(builtin_name)); - check_expr(c, &x, ce->args[0]); - if (x.mode == Addressing_Invalid) { - return false; - } - - check_expr(c, &y, ce->args[1]); - if (y.mode == Addressing_Invalid) { - return false; - } - check_expr(c, &z, ce->args[2]); - if (z.mode == Addressing_Invalid) { - return false; - } - check_expr(c, &w, ce->args[3]); - if (w.mode == Addressing_Invalid) { - return false; + for (i32 i = 0; i < 4; i++) { + check_expr(c, &xyzw[i], ce->args[i]); + if (xyzw[i].mode == Addressing_Invalid) { + return false; + } } } - convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; - convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; - convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; - convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + convert_to_typed(c, &xyzw[0], xyzw[1].type); if (xyzw[0].mode == Addressing_Invalid) return false; + convert_to_typed(c, &xyzw[1], xyzw[0].type); if (xyzw[1].mode == Addressing_Invalid) return false; + convert_to_typed(c, &xyzw[2], xyzw[0].type); if (xyzw[2].mode == Addressing_Invalid) return false; + convert_to_typed(c, &xyzw[3], xyzw[0].type); if (xyzw[3].mode == Addressing_Invalid) return false; - if (x.mode == Addressing_Constant && - y.mode == Addressing_Constant && - z.mode == Addressing_Constant && - w.mode == Addressing_Constant) { - x.value = exact_value_to_float(x.value); - y.value = exact_value_to_float(y.value); - z.value = exact_value_to_float(z.value); - w.value = exact_value_to_float(w.value); - if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) { - x.type = t_untyped_float; - } - if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) { - y.type = t_untyped_float; - } - if (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) { - z.type = t_untyped_float; + if (xyzw[0].mode == Addressing_Constant && + xyzw[1].mode == Addressing_Constant && + xyzw[2].mode == Addressing_Constant && + xyzw[3].mode == Addressing_Constant) { + for (i32 i = 0; i < 4; i++) { + xyzw[i].value = exact_value_to_float(xyzw[i].value); } - if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) { - w.type = t_untyped_float; + for (i32 i = 0; i < 4; i++) { + if (is_type_numeric(xyzw[i].type) && xyzw[i].value.kind == ExactValue_Float) { + xyzw[i].type = t_untyped_float; + } } } - if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { - gbString tx = type_to_string(x.type); - gbString ty = type_to_string(y.type); - gbString tz = type_to_string(z.type); - gbString tw = type_to_string(w.type); - error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + if (!(are_types_identical(xyzw[0].type, xyzw[1].type) && + are_types_identical(xyzw[0].type, xyzw[2].type) && + are_types_identical(xyzw[0].type, xyzw[3].type))) { + gbString tx = type_to_string(xyzw[0].type); + gbString ty = type_to_string(xyzw[1].type); + gbString tz = type_to_string(xyzw[2].type); + gbString tw = type_to_string(xyzw[3].type); + error(call, "Mismatched types to 'quaternion', 'x=%s' vs 'y=%s' vs 'z=%s' vs 'w=%s'", tx, ty, tz, tw); gb_string_free(tw); gb_string_free(tz); gb_string_free(ty); @@ -2470,31 +2448,35 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - if (!is_type_float(x.type)) { - gbString s = type_to_string(x.type); + if (!is_type_float(xyzw[0].type)) { + gbString s = type_to_string(xyzw[0].type); error(call, "Arguments have type '%s', expected a floating point", s); gb_string_free(s); return false; } - if (is_type_endian_specific(x.type)) { - gbString s = type_to_string(x.type); + if (is_type_endian_specific(xyzw[0].type)) { + gbString s = type_to_string(xyzw[0].type); error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s); gb_string_free(s); return false; } - if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { - f64 r = exact_value_to_float(x.value).value_float; - f64 i = exact_value_to_float(y.value).value_float; - f64 j = exact_value_to_float(z.value).value_float; - f64 k = exact_value_to_float(w.value).value_float; + + operand->mode = Addressing_Value; + + if (xyzw[0].mode == Addressing_Constant && + xyzw[1].mode == Addressing_Constant && + xyzw[2].mode == Addressing_Constant && + xyzw[3].mode == Addressing_Constant) { + f64 r = exact_value_to_float(xyzw[3].value).value_float; + f64 i = exact_value_to_float(xyzw[0].value).value_float; + f64 j = exact_value_to_float(xyzw[1].value).value_float; + f64 k = exact_value_to_float(xyzw[2].value).value_float; operand->value = exact_value_quaternion(r, i, j, k); operand->mode = Addressing_Constant; - } else { - operand->mode = Addressing_Value; } - BasicKind kind = core_type(x.type)->Basic.kind; + BasicKind kind = core_type(xyzw[0].type)->Basic.kind; switch (kind) { case Basic_f16: operand->type = t_quaternion64; break; case Basic_f32: operand->type = t_quaternion128; break; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3f16d07a5..d4eae84bc 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1845,7 +1845,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } GB_ASSERT(index >= 0); - xyzw[index] = lb_build_expr(p, ce->args[i]); + xyzw[index] = lb_build_expr(p, f->value); } -- cgit v1.2.3 From f4782157d3b586a88983d3b770c2c0eeb17946d1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 7 Jan 2024 21:34:44 +0000 Subject: Implement instrumentation pass --- core/runtime/core.odin | 1 + core/runtime/entry_unix.odin | 1 + core/runtime/entry_wasm.odin | 1 + core/runtime/entry_windows.odin | 1 + core/runtime/error_checks.odin | 24 ++++++----- core/runtime/procs_windows_amd64.odin | 1 + core/runtime/procs_windows_i386.odin | 1 + src/check_decl.cpp | 26 ++++++++---- src/entity.cpp | 2 +- src/llvm_backend.cpp | 2 - src/llvm_backend.hpp | 2 +- src/llvm_backend_general.cpp | 13 ++++++ src/llvm_backend_opt.cpp | 77 +++++++++++++++++++++++++++++++++++ src/llvm_backend_proc.cpp | 12 ++++++ 14 files changed, 143 insertions(+), 21 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 2d176f909..e12d9a43d 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -18,6 +18,7 @@ // This could change at a later date if the all these data structures are // implemented within the compiler rather than in this "preload" file // +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin index 78e545c22..f494a509e 100644 --- a/core/runtime/entry_unix.odin +++ b/core/runtime/entry_unix.odin @@ -1,5 +1,6 @@ //+private //+build linux, darwin, freebsd, openbsd +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_wasm.odin b/core/runtime/entry_wasm.odin index 235d5611b..e7f3f156f 100644 --- a/core/runtime/entry_wasm.odin +++ b/core/runtime/entry_wasm.odin @@ -1,5 +1,6 @@ //+private //+build wasm32, wasm64p32 +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin index a315c1209..277daecca 100644 --- a/core/runtime/entry_windows.odin +++ b/core/runtime/entry_windows.odin @@ -1,5 +1,6 @@ //+private //+build windows +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 9d484979a..ea6333c29 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -1,5 +1,6 @@ package runtime +@(no_instrumentation) bounds_trap :: proc "contextless" () -> ! { when ODIN_OS == .Windows { windows_trap_array_bounds() @@ -8,6 +9,7 @@ bounds_trap :: proc "contextless" () -> ! { } } +@(no_instrumentation) type_assertion_trap :: proc "contextless" () -> ! { when ODIN_OS == .Windows { windows_trap_type_assertion() @@ -21,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index if uint(index) < uint(count) { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") @@ -34,6 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index handle_error(file, line, column, index, count) } +@(no_instrumentation) slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid slice indices ") @@ -46,6 +49,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h bounds_trap() } +@(no_instrumentation) multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid slice indices ") @@ -82,7 +86,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, if 0 <= low && low <= high && high <= max { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid dynamic array indices ") @@ -103,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 uint(column_index) < uint(column_count) { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") @@ -127,7 +131,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") @@ -140,7 +144,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") @@ -153,7 +157,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion from ") @@ -198,7 +202,7 @@ when ODIN_NO_RTTI { return id } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { actual := variant_type(from, from_data) @@ -224,7 +228,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio if 0 <= len { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! { print_caller_location(loc) print_string(" Invalid slice length for make: ") @@ -239,7 +243,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller if 0 <= len && len <= cap { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") @@ -256,7 +260,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca if 0 <= cap { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid map capacity for make: ") diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index e430357be..a30985d5c 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -1,4 +1,5 @@ //+private +//+no-instrumentation package runtime foreign import kernel32 "system:Kernel32.lib" diff --git a/core/runtime/procs_windows_i386.odin b/core/runtime/procs_windows_i386.odin index f810197f1..4f606da8f 100644 --- a/core/runtime/procs_windows_i386.odin +++ b/core/runtime/procs_windows_i386.odin @@ -1,4 +1,5 @@ //+private +//+no-instrumentation package runtime @require foreign import "system:int64.lib" diff --git a/src/check_decl.cpp b/src/check_decl.cpp index c69d8185e..a530945f9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -910,24 +910,24 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; - bool no_instrumentation = false; + bool has_instrumentation = false; if (pl->body == nullptr) { - no_instrumentation = true; + has_instrumentation = false; if (ac.no_instrumentation != Instrumentation_Default) { error(e->token, "@(no_instrumentation) is not allowed on foreign procedures"); } } else { - if (e->file) { - no_instrumentation = (e->file->flags & AstFile_NoInstrumentation) != 0; + AstFile *file = e->token.pos.file_id ? global_files[e->token.pos.file_id] : nullptr; + if (file) { + has_instrumentation = (file->flags & AstFile_NoInstrumentation) == 0; } switch (ac.no_instrumentation) { - case Instrumentation_Enabled: no_instrumentation = false; break; + case Instrumentation_Enabled: has_instrumentation = true; break; case Instrumentation_Default: break; - case Instrumentation_Disabled: no_instrumentation = true; break; + case Instrumentation_Disabled: has_instrumentation = false; break; } } - e->Procedure.no_instrumentation = no_instrumentation; auto const is_valid_instrumentation_call = [](Type *type) -> bool { if (type == nullptr || type->kind != Type_Proc) { @@ -949,6 +949,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (ac.instrumentation_enter && ac.instrumentation_exit) { error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } else if (ac.instrumentation_enter) { if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); @@ -961,6 +964,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { ctx->info->instrumentation_enter_entity = e; } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } else if (ac.instrumentation_exit) { if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); @@ -973,8 +979,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { ctx->info->instrumentation_exit_entity = e; } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } + e->Procedure.has_instrumentation = has_instrumentation; + + e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/entity.cpp b/src/entity.cpp index 0539386d0..e6c46d37e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -251,7 +251,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool target_feature_disabled : 1; bool entry_point_only : 1; - bool no_instrumentation : 1; + bool has_instrumentation : 1; String target_feature; } Procedure; struct { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b90fd8495..0175d039e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1497,8 +1497,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { auto passes = array_make(heap_allocator(), 0, 64); defer (array_free(&passes)); - - LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions(); defer (LLVMDisposePassBuilderOptions(pb_options)); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4e193bcea..b645d66d6 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -563,7 +563,7 @@ gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type); gb_internal String lb_filepath_ll_for_module(lbModule *m); - +gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type); gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) { #if LB_USE_NEW_PASS_SYSTEM diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 54327cc54..f0f5327c6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2348,6 +2348,15 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char c return LLVMCreateEnumAttribute(ctx, kind, value); } +gb_internal LLVMAttributeRef lb_create_string_attribute(LLVMContextRef ctx, String const &key, String const &value) { + LLVMAttributeRef attr = LLVMCreateStringAttribute( + ctx, + cast(char const *)key.text, cast(unsigned)key.len, + cast(char const *)value.text, cast(unsigned)value.len); + return attr; +} + + gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value) { LLVMAttributeRef attr = lb_create_enum_attribute(p->module->ctx, name, value); GB_ASSERT(attr != nullptr); @@ -2361,6 +2370,10 @@ gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, cha gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) { LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value)); } +gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) { + LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value); + LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr); +} diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 2e03b7974..d5487d259 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -380,6 +380,80 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) { } } +gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder) { + lbModule *m = p->module; + + lbValue cc = lb_find_procedure_value_from_entity(m, entity); + + LLVMValueRef args[2] = {}; + args[0] = p->value; + + LLVMValueRef returnaddress_args[1] = {}; + + returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false); + + char const *instrinsic_name = "llvm.returnaddress"; + unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0); + LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0); + args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), ""); + + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type); + return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, 2, ""); +} + + +gb_internal void lb_run_instrumentation_pass(lbProcedure *p) { + lbModule *m = p->module; + Entity *enter = m->info->instrumentation_enter_entity; + Entity *exit = m->info->instrumentation_exit_entity; + if (enter == nullptr || exit == nullptr) { + return; + } + if (!(p->entity && + p->entity->kind == Entity_Procedure && + p->entity->Procedure.has_instrumentation)) { + return; + } + +#define LLVM_V_NAME(x) x, cast(unsigned)(gb_count_of(x)-1) + + LLVMBuilderRef dummy_builder = LLVMCreateBuilderInContext(m->ctx); + defer (LLVMDisposeBuilder(dummy_builder)); + + LLVMBasicBlockRef entry_bb = p->entry_block->block; + LLVMPositionBuilder(dummy_builder, entry_bb, LLVMGetFirstInstruction(entry_bb)); + lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder); + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-entry")); + + unsigned bb_count = LLVMCountBasicBlocks(p->value); + LLVMBasicBlockRef *bbs = gb_alloc_array(temporary_allocator(), LLVMBasicBlockRef, bb_count); + LLVMGetBasicBlocks(p->value, bbs); + for (unsigned i = 0; i < bb_count; i++) { + LLVMBasicBlockRef bb = bbs[i]; + LLVMValueRef terminator = LLVMGetBasicBlockTerminator(bb); + if (terminator == nullptr || + !LLVMIsAReturnInst(terminator)) { + continue; + } + + // TODO(bill): getTerminatingMustTailCall() + // If T is preceded by a musttail call, that's the real terminator. + // if (CallInst *CI = BB.getTerminatingMustTailCall()) + // T = CI; + + + LLVMPositionBuilderBefore(dummy_builder, terminator); + lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder); + } + + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-exit")); + +#undef LLVM_V_NAME +} + + gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind) { if (p == nullptr) { @@ -401,6 +475,7 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur } break; } + lb_run_instrumentation_pass(p); LLVMRunFunctionPassManager(fpm, p->value); } @@ -552,3 +627,5 @@ gb_internal void lb_run_remove_unused_globals_pass(lbModule *m) { } } } + + diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d4eae84bc..09bebd0cf 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -329,6 +329,18 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } + if (p->body && entity->Procedure.has_instrumentation) { + Entity *instrumentation_enter = m->info->instrumentation_enter_entity; + Entity *instrumentation_exit = m->info->instrumentation_exit_entity; + if (instrumentation_enter && instrumentation_exit) { + String enter = lb_get_entity_name(m, instrumentation_enter); + String exit = lb_get_entity_name(m, instrumentation_exit); + + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-entry"), enter); + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-exit"), exit); + } + } + lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); -- cgit v1.2.3