From 6961b3642597e855db3d1a1d57f7f3a4e73e7894 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 Oct 2026 16:56:25 +0100 Subject: [PATCH] Fix the `core:sys/llvm` bindings for current LLVM, add the missing ones; allow `#const` parameters to take their defaults, and reuse an intrinsic already declared by the compiler --- core/sys/llvm/bit_manipulation.odin | 24 ++--- core/sys/llvm/code_generator.odin | 43 +++++--- core/sys/llvm/standard_c_library.odin | 142 ++++++++++++++++++++++---- src/check_expr.cpp | 5 + src/llvm_backend_proc.cpp | 12 ++- 5 files changed, 174 insertions(+), 52 deletions(-) diff --git a/core/sys/llvm/bit_manipulation.odin b/core/sys/llvm/bit_manipulation.odin index 2e237dd32..5388799ab 100644 --- a/core/sys/llvm/bit_manipulation.odin +++ b/core/sys/llvm/bit_manipulation.odin @@ -2,7 +2,6 @@ package sys_llvm -/* @(default_calling_convention="none") foreign _ { @(link_name="llvm.bitreverse.i8") @@ -36,28 +35,28 @@ foreign _ { @(link_name="llvm.ctpop.i128") ctpop_u128 :: proc(u128) -> u128 --- + // `is_zero_poison` makes a zero input give a poison result @(link_name="llvm.ctlz.i8") - ctlz_u8 :: proc(u8) -> u8 --- + ctlz_u8 :: proc(x: u8, #const is_zero_poison: bool = false) -> u8 --- @(link_name="llvm.ctlz.i16") - ctlz_u16 :: proc(u16) -> u16 --- + ctlz_u16 :: proc(x: u16, #const is_zero_poison: bool = false) -> u16 --- @(link_name="llvm.ctlz.i32") - ctlz_u32 :: proc(u32) -> u32 --- + ctlz_u32 :: proc(x: u32, #const is_zero_poison: bool = false) -> u32 --- @(link_name="llvm.ctlz.i64") - ctlz_u64 :: proc(u64) -> u64 --- + ctlz_u64 :: proc(x: u64, #const is_zero_poison: bool = false) -> u64 --- @(link_name="llvm.ctlz.i128") - ctlz_u128 :: proc(u128) -> u128 --- + ctlz_u128 :: proc(x: u128, #const is_zero_poison: bool = false) -> u128 --- @(link_name="llvm.cttz.i8") - cttz_u8 :: proc(u8) -> u8 --- + cttz_u8 :: proc(x: u8, #const is_zero_poison: bool = false) -> u8 --- @(link_name="llvm.cttz.i16") - cttz_u16 :: proc(u16) -> u16 --- + cttz_u16 :: proc(x: u16, #const is_zero_poison: bool = false) -> u16 --- @(link_name="llvm.cttz.i32") - cttz_u32 :: proc(u32) -> u32 --- + cttz_u32 :: proc(x: u32, #const is_zero_poison: bool = false) -> u32 --- @(link_name="llvm.cttz.i64") - cttz_u64 :: proc(u64) -> u64 --- + cttz_u64 :: proc(x: u64, #const is_zero_poison: bool = false) -> u64 --- @(link_name="llvm.cttz.i128") - cttz_u128 :: proc(u128) -> u128 --- - + cttz_u128 :: proc(x: u128, #const is_zero_poison: bool = false) -> u128 --- @(link_name="llvm.fshl.i8") fshl_u8 :: proc(a, b, c: u8) -> u8 --- @@ -81,4 +80,3 @@ foreign _ { @(link_name="llvm.fshr.i128") fshr_u128 :: proc(a, b, c: u128) -> u128 --- } -*/ diff --git a/core/sys/llvm/code_generator.odin b/core/sys/llvm/code_generator.odin index 6422976c5..a2bb36b3a 100644 --- a/core/sys/llvm/code_generator.odin +++ b/core/sys/llvm/code_generator.odin @@ -4,29 +4,37 @@ package sys_llvm @(default_calling_convention="none") foreign _ { + // not supported on wasm @(link_name="llvm.returnaddress") return_address :: proc(#const level: u32 = 0) -> rawptr --- - @(link_name="llvm.addressofreturnaddress") + // x86 and AArch64 only + @(link_name="llvm.addressofreturnaddress.p0") address_of_return_address :: proc() -> rawptr --- - @(link_name="llvm.sponentry") + // ARM and AArch64 only + @(link_name="llvm.sponentry.p0") stack_pointer_on_entry :: proc() -> rawptr --- - @(link_name="llvm.frameaddress") + // a level above 0 is ignored on Windows amd64, and gives nil on wasm + @(link_name="llvm.frameaddress.p0") frame_address :: proc(#const level: u32 = 0) -> rawptr --- - @(link_name="llvm.stacksave") + // LLVM 18+, LLVM 17 names these without the `.p0` + @(link_name="llvm.stacksave.p0") stack_save :: proc() -> rawptr --- - @(link_name="llvm.stackrestore") + @(link_name="llvm.stackrestore.p0") stack_restore :: proc(ptr: rawptr) --- - @(link_name="llvm.get.dynamic.area.offset.i32") - get_dynamic_area_offset_i32 :: proc() -> i32 --- - - @(link_name="llvm.get.dynamic.area.offset.i64") - get_dynamic_area_offset_i64 :: proc() -> i64 --- + // the result is pointer sized + when size_of(rawptr) == 4 { + @(link_name="llvm.get.dynamic.area.offset.i32") + get_dynamic_area_offset_i32 :: proc() -> i32 --- + } else { + @(link_name="llvm.get.dynamic.area.offset.i64") + get_dynamic_area_offset_i64 :: proc() -> i64 --- + } } @@ -50,7 +58,7 @@ Prefetch_Cache :: enum i32 { @(default_calling_convention="none") foreign _ { - @(link_name="llvm.prefetch") + @(link_name="llvm.prefetch.p0") prefetch :: proc(address: rawptr, #const rw: Prefetch_Read_Write, #const locality: Prefetch_Locality, #const cache: Prefetch_Cache) --- } @@ -58,15 +66,18 @@ foreign _ { @(default_calling_convention="none") foreign _ { - @(link_name="llvm.pcmarker") - pc_marker :: proc(id: i32) --- - @(link_name="llvm.readcyclecounter") read_cycle_counter :: proc() -> u64 --- - @(link_name="llvm.clear_cache") - clear_cache :: proc(rawptr, rawptr) --- + // LLVM 19+, 0 on targets without one + @(link_name="llvm.readsteadycounter") + read_steady_counter :: proc() -> u64 --- + // not supported on wasm + @(link_name="llvm.clear_cache") + clear_cache :: proc(begin, end: rawptr) --- + + // not supported on Windows, nor on Darwin amd64 @(link_name="llvm.thread.pointer") thread_pointer :: proc() -> rawptr --- } diff --git a/core/sys/llvm/standard_c_library.odin b/core/sys/llvm/standard_c_library.odin index 1818e8462..0c41d631f 100644 --- a/core/sys/llvm/standard_c_library.odin +++ b/core/sys/llvm/standard_c_library.odin @@ -5,29 +5,35 @@ package sys_llvm @(default_calling_convention="none") foreign _ { when size_of(int) == 4 { - @(link_name="llvm.memcpy.p0i8.p0i8.i32") - memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memcpy.p0.p0.i32") + memcpy :: proc(dst, src: rawptr, len: int, #const is_volatile: bool = false) --- - @(link_name="llvm.memcpy.inline.p0i8.p0i8.i32") - memcpy_inline :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memcpy.inline.p0.p0.i32") + memcpy_inline :: proc(dst, src: rawptr, len: int, #const is_volatile: bool = false) --- - @(link_name="llvm.memmove.p0i8.p0i8.i32") - memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memmove.p0.p0.i32") + memmove :: proc(dst, src: rawptr, len: int, #const is_volatile: bool = false) --- - @(link_name="llvm.memset.p0i8.i32") - memset :: proc(dst: rawptr, val: byte, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memset.p0.i32") + memset :: proc(dst: rawptr, val: byte, len: int, #const is_volatile: bool = false) --- + + @(link_name="llvm.memset.inline.p0.i32") + memset_inline :: proc(dst: rawptr, val: byte, len: int, #const is_volatile: bool = false) --- } else { - @(link_name="llvm.memcpy.p0i8.p0i8.i64") - memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memcpy.p0.p0.i64") + memcpy :: proc(dst, src: rawptr, len: int, #const is_volatile: bool = false) --- - @(link_name="llvm.memcpy.inline.p0i8.p0i8.i64") - memcpy_inline :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memcpy.inline.p0.p0.i64") + memcpy_inline :: proc(dst, src: rawptr, len: int, #const is_volatile: bool = false) --- - @(link_name="llvm.memmove.p0i8.p0i8.i64") - memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memmove.p0.p0.i64") + memmove :: proc(dst, src: rawptr, len: int, #const is_volatile: bool = false) --- - @(link_name="llvm.memset.p0i8.i64") - memset :: proc(dst: rawptr, val: byte, len: int, is_volatile: bool = false) --- + @(link_name="llvm.memset.p0.i64") + memset :: proc(dst: rawptr, val: byte, len: int, #const is_volatile: bool = false) --- + + @(link_name="llvm.memset.inline.p0.i64") + memset_inline :: proc(dst: rawptr, val: byte, len: int, #const is_volatile: bool = false) --- } } @@ -39,9 +45,9 @@ foreign _ { @(link_name="llvm.sqrt.f64") sqrt_f64 :: proc(f64) -> f64 --- - @(link_name="llvm.powi.f32") + @(link_name="llvm.powi.f32.i32") powi_f32 :: proc(val: f32, power: i32) -> f32 --- - @(link_name="llvm.powi.f64") + @(link_name="llvm.powi.f64.i32") powi_f64 :: proc(val: f64, power: i32) -> f64 --- @(link_name="llvm.sin.f32") @@ -54,6 +60,60 @@ foreign _ { @(link_name="llvm.cos.f64") cos_f64 :: proc(f64) -> f64 --- + // LLVM 19+ + @(link_name="llvm.tan.f32") + tan_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.tan.f64") + tan_f64 :: proc(f64) -> f64 --- + + // LLVM 19+ + @(link_name="llvm.asin.f32") + asin_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.asin.f64") + asin_f64 :: proc(f64) -> f64 --- + + // LLVM 19+ + @(link_name="llvm.acos.f32") + acos_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.acos.f64") + acos_f64 :: proc(f64) -> f64 --- + + // LLVM 19+ + @(link_name="llvm.atan.f32") + atan_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.atan.f64") + atan_f64 :: proc(f64) -> f64 --- + + // LLVM 20+ + @(link_name="llvm.atan2.f32") + atan2_f32 :: proc(y, x: f32) -> f32 --- + @(link_name="llvm.atan2.f64") + atan2_f64 :: proc(y, x: f64) -> f64 --- + + // LLVM 19+ + @(link_name="llvm.sinh.f32") + sinh_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.sinh.f64") + sinh_f64 :: proc(f64) -> f64 --- + + // LLVM 19+ + @(link_name="llvm.cosh.f32") + cosh_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.cosh.f64") + cosh_f64 :: proc(f64) -> f64 --- + + // LLVM 19+ + @(link_name="llvm.tanh.f32") + tanh_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.tanh.f64") + tanh_f64 :: proc(f64) -> f64 --- + + // LLVM 20+ + @(link_name="llvm.sincos.f32") + sincos_f32 :: proc(f32) -> (sin, cos: f32) --- + @(link_name="llvm.sincos.f64") + sincos_f64 :: proc(f64) -> (sin, cos: f64) --- + @(link_name="llvm.pow.f32") pow_f32 :: proc(val, power: f32) -> f32 --- @(link_name="llvm.pow.f64") @@ -69,6 +129,22 @@ foreign _ { @(link_name="llvm.exp2.f64") exp2_f64 :: proc(f64) -> f64 --- + // LLVM 18+, calls `exp10` from the C library, which Windows does not have + @(link_name="llvm.exp10.f32") + exp10_f32 :: proc(f32) -> f32 --- + @(link_name="llvm.exp10.f64") + exp10_f64 :: proc(f64) -> f64 --- + + @(link_name="llvm.ldexp.f32.i32") + ldexp_f32 :: proc(val: f32, exp: i32) -> f32 --- + @(link_name="llvm.ldexp.f64.i32") + ldexp_f64 :: proc(val: f64, exp: i32) -> f64 --- + + @(link_name="llvm.frexp.f32.i32") + frexp_f32 :: proc(val: f32) -> (fraction: f32, exp: i32) --- + @(link_name="llvm.frexp.f64.i32") + frexp_f64 :: proc(val: f64) -> (fraction: f64, exp: i32) --- + @(link_name="llvm.log.f32") log_f32 :: proc(f32) -> f32 --- @(link_name="llvm.log.f64") @@ -94,14 +170,14 @@ foreign _ { @(link_name="llvm.fabs.f64") fabs_f64 :: proc(f64) -> f64 --- - @(link_name="llvm.min_num.f32") + @(link_name="llvm.minnum.f32") min_num_f32 :: proc(val0, val1: f32) -> f32 --- - @(link_name="llvm.min_num.f64") + @(link_name="llvm.minnum.f64") min_num_f64 :: proc(val0, val1: f64) -> f64 --- - @(link_name="llvm.max_num.f32") + @(link_name="llvm.maxnum.f32") max_num_f32 :: proc(val0, val1: f32) -> f32 --- - @(link_name="llvm.max_num.f64") + @(link_name="llvm.maxnum.f64") max_num_f64 :: proc(val0, val1: f64) -> f64 --- @(link_name="llvm.minimum.f32") @@ -114,6 +190,18 @@ foreign _ { @(link_name="llvm.maximum.f64") maximum_f64 :: proc(val0, val1: f64) -> f64 --- + // LLVM 20+, whose x86 code at -o:none and -o:minimal can return a NaN operand when the other is a constant + @(link_name="llvm.minimumnum.f32") + minimum_num_f32 :: proc(val0, val1: f32) -> f32 --- + @(link_name="llvm.minimumnum.f64") + minimum_num_f64 :: proc(val0, val1: f64) -> f64 --- + + // LLVM 20+, whose x86 code at -o:none and -o:minimal can return a NaN operand when the other is a constant + @(link_name="llvm.maximumnum.f32") + maximum_num_f32 :: proc(val0, val1: f32) -> f32 --- + @(link_name="llvm.maximumnum.f64") + maximum_num_f64 :: proc(val0, val1: f64) -> f64 --- + @(link_name="llvm.copysign.f32") copy_sign_f32 :: proc(mag, sgn: f32) -> f32 --- @(link_name="llvm.copysign.f64") @@ -165,6 +253,11 @@ foreign _ { @(link_name="llvm.lround.i64.f64") lround_f64_i64 :: proc(f64) -> i64 --- + @(link_name="llvm.llround.i64.f32") + llround_f32_i64 :: proc(f32) -> i64 --- + @(link_name="llvm.llround.i64.f64") + llround_f64_i64 :: proc(f64) -> i64 --- + @(link_name="llvm.lrint.i32.f32") lrint_f32_i32 :: proc(f32) -> i32 --- @@ -175,4 +268,9 @@ foreign _ { lrint_f32_i64 :: proc(f32) -> i64 --- @(link_name="llvm.lrint.i64.f64") lrint_f64_i64 :: proc(f64) -> i64 --- + + @(link_name="llvm.llrint.i64.f32") + llrint_f32_i64 :: proc(f32) -> i64 --- + @(link_name="llvm.llrint.i64.f64") + llrint_f64_i64 :: proc(f64) -> i64 --- } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ad6aff038..192eeaaa8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7210,6 +7210,11 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A ordered_operands[i].type = e->type; if (e->Variable.param_value.kind == ParameterValue_Nil) ordered_operands[i].type = t_untyped_nil; + if (e->Variable.param_value.kind == ParameterValue_Constant) { + // so a `#const` parameter accepts its own default + ordered_operands[i].mode = Addressing_Constant; + ordered_operands[i].value = e->Variable.param_value.value; + } ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 209e5e8a7..e974b9e97 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -160,7 +160,17 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i { TEMPORARY_ALLOCATOR_GUARD(); char *c_link_name = alloc_cstring(temporary_allocator(), p->name); - p->value = LLVMAddFunction(m->mod, c_link_name, func_type); + // the compiler may have declared this intrinsic itself (e.g. `llvm.memset`), and a second + // declaration would be renamed and so no longer be the intrinsic + LLVMValueRef existing = nullptr; + if (p->is_foreign && string_starts_with(p->name, str_lit("llvm."))) { + existing = LLVMGetNamedFunction(m->mod, c_link_name); + } + if (existing != nullptr && LLVMGlobalGetValueType(existing) == func_type) { + p->value = existing; + } else { + p->value = LLVMAddFunction(m->mod, c_link_name, func_type); + } } lb_ensure_abi_function_type(m, p);