diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index a682f7e76..09fe64b60 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1067,8 +1067,8 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { return quaternion(w=t0, x=t1, y=t2, z=t3) } -@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -truncsfhf2 :: proc "c" (value: f32) -> __float16 { +@(private="file") +f32_to_f16 :: proc "contextless" (value: f32) -> __float16 { v: struct #raw_union { i: u32, f: f32 } i, s, e, m: i32 @@ -1124,18 +1124,8 @@ truncsfhf2 :: proc "c" (value: f32) -> __float16 { } } -@(link_name="__aeabi_d2h", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -aeabi_d2h :: proc "c" (value: f64) -> __float16 { - return truncsfhf2(f32(value)) -} - -@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -truncdfhf2 :: proc "c" (value: f64) -> __float16 { - return truncsfhf2(f32(value)) -} - -@(link_name="__gnu_h2f_ieee", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -gnu_h2f_ieee :: proc "c" (value_: __float16) -> f32 { +@(private="file") +f16_to_f32 :: proc "contextless" (value_: __float16) -> f32 { fp32 :: struct #raw_union { u: u32, f: f32 } value := transmute(u16)value_ @@ -1154,14 +1144,56 @@ gnu_h2f_ieee :: proc "c" (value_: __float16) -> f32 { } +// The conversion helpers below are libgcc / compiler-rt entry points, so their calling convention +// is compiler-rt's and not the target's ordinary one. On ARM they take and return their values in +// the core registers even though the target is AAPCS-VFP, where an ordinary `proc "c"` float +// travels in `s0`. clang's call sites move the value out of the VFP register and back around the +// call: +// +// vmov r0, s0 ; bl __gnu_h2f_ieee ; vmov s0, r0 +// +// Typing the boundary as integers is what puts them in the same registers. Declared as floats they +// land in `s0` at both ends and every `_Float16` conversion in C code linked against this runtime +// reads whatever the other register happened to hold. Everywhere else the helpers really do take +// and return floats, so only arm32 changes shape. +when ODIN_ARCH == .arm32 { + __f16_abi :: u16 + __f32_abi :: u32 + __f64_abi :: u64 +} else { + __f16_abi :: __float16 + __f32_abi :: f32 + __f64_abi :: f64 +} + +@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +truncsfhf2 :: proc "c" (value: __f32_abi) -> __f16_abi { + return transmute(__f16_abi)f32_to_f16(transmute(f32)value) +} + @(link_name="__gnu_f2h_ieee", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -gnu_f2h_ieee :: proc "c" (value: f32) -> __float16 { - return truncsfhf2(value) +gnu_f2h_ieee :: proc "c" (value: __f32_abi) -> __f16_abi { + return transmute(__f16_abi)f32_to_f16(transmute(f32)value) +} + +@(link_name="__aeabi_d2h", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +aeabi_d2h :: proc "c" (value: __f64_abi) -> __f16_abi { + return transmute(__f16_abi)f32_to_f16(f32(transmute(f64)value)) +} + +@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +truncdfhf2 :: proc "c" (value: __f64_abi) -> __f16_abi { + return transmute(__f16_abi)f32_to_f16(f32(transmute(f64)value)) +} + +@(link_name="__gnu_h2f_ieee", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +gnu_h2f_ieee :: proc "c" (value: __f16_abi) -> __f32_abi { + return transmute(__f32_abi)f16_to_f32(transmute(__float16)value) } @(link_name="__extendhfsf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -extendhfsf2 :: proc "c" (value: __float16) -> f32 { - return gnu_h2f_ieee(value) +extendhfsf2 :: proc "c" (value: __f16_abi) -> __f32_abi { + return transmute(__f32_abi)f16_to_f32(transmute(__float16)value) } when .Address in ODIN_SANITIZER_FLAGS {