| author | |
| committer | |
| log | 3199792ade2dfcb88f6668849c54352e51fc1ae0 |
| tree | 886189ac1a1bfcae8124ac18684f1e180c8abb0f |
| parent | c76d51de975a4e2e8b549d37161f1e0056384e0b |
| parent | bfbfb7b3b04b9c98dd14f932b08d1c058e2c5b25 |
| signature |
Armv7m compiler rt5 files changed, 234 insertions(+), 8 deletions(-)
CMakeLists.txt+1| ... | @@ -664,6 +664,7 @@ set(ZIG_STD_FILES | ... | @@ -664,6 +664,7 @@ set(ZIG_STD_FILES |
| 664 | "special/compiler_rt/muloti4.zig" | 664 | "special/compiler_rt/muloti4.zig" |
| 665 | "special/compiler_rt/mulXf3.zig" | 665 | "special/compiler_rt/mulXf3.zig" |
| 666 | "special/compiler_rt/multi3.zig" | 666 | "special/compiler_rt/multi3.zig" |
| 667 | "special/compiler_rt/negXf2.zig" | ||
| 667 | "special/compiler_rt/popcountdi2.zig" | 668 | "special/compiler_rt/popcountdi2.zig" |
| 668 | "special/compiler_rt/truncXfYf2.zig" | 669 | "special/compiler_rt/truncXfYf2.zig" |
| 669 | "special/compiler_rt/udivmod.zig" | 670 | "special/compiler_rt/udivmod.zig" |
std/special/builtin.zig+25| ... | @@ -55,6 +55,31 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { | ... | @@ -55,6 +55,31 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { |
| 55 | return dest; | 55 | return dest; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize { | ||
| 59 | @setRuntimeSafety(false); | ||
| 60 | |||
| 61 | var index: usize = 0; | ||
| 62 | while (index != n) : (index += 1) { | ||
| 63 | const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]); | ||
| 64 | if (compare_val != 0) { | ||
| 65 | return compare_val; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | test "test_memcmp" { | ||
| 73 | const base_arr = []u8{ 1, 1, 1 }; | ||
| 74 | const arr1 = []u8{ 1, 1, 1 }; | ||
| 75 | const arr2 = []u8{ 1, 0, 1 }; | ||
| 76 | const arr3 = []u8{ 1, 2, 1 }; | ||
| 77 | |||
| 78 | std.testing.expect(memcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0); | ||
| 79 | std.testing.expect(memcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) == 1); | ||
| 80 | std.testing.expect(memcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) == -1); | ||
| 81 | } | ||
| 82 | |||
| 58 | comptime { | 83 | comptime { |
| 59 | if (builtin.mode != builtin.Mode.ReleaseFast and | 84 | if (builtin.mode != builtin.Mode.ReleaseFast and |
| 60 | builtin.mode != builtin.Mode.ReleaseSmall and | 85 | builtin.mode != builtin.Mode.ReleaseSmall and |
std/special/compiler_rt.zig+157| ... | @@ -21,7 +21,11 @@ comptime { | ... | @@ -21,7 +21,11 @@ comptime { |
| 21 | 21 | ||
| 22 | @export("__unordtf2", @import("compiler_rt/comparetf2.zig").__unordtf2, linkage); | 22 | @export("__unordtf2", @import("compiler_rt/comparetf2.zig").__unordtf2, linkage); |
| 23 | 23 | ||
| 24 | @export("__addsf3", @import("compiler_rt/addXf3.zig").__addsf3, linkage); | ||
| 25 | @export("__adddf3", @import("compiler_rt/addXf3.zig").__adddf3, linkage); | ||
| 24 | @export("__addtf3", @import("compiler_rt/addXf3.zig").__addtf3, linkage); | 26 | @export("__addtf3", @import("compiler_rt/addXf3.zig").__addtf3, linkage); |
| 27 | @export("__subsf3", @import("compiler_rt/addXf3.zig").__subsf3, linkage); | ||
| 28 | @export("__subdf3", @import("compiler_rt/addXf3.zig").__subdf3, linkage); | ||
| 25 | @export("__subtf3", @import("compiler_rt/addXf3.zig").__subtf3, linkage); | 29 | @export("__subtf3", @import("compiler_rt/addXf3.zig").__subtf3, linkage); |
| 26 | 30 | ||
| 27 | @export("__mulsf3", @import("compiler_rt/mulXf3.zig").__mulsf3, linkage); | 31 | @export("__mulsf3", @import("compiler_rt/mulXf3.zig").__mulsf3, linkage); |
| ... | @@ -78,10 +82,62 @@ comptime { | ... | @@ -78,10 +82,62 @@ comptime { |
| 78 | @export("__umoddi3", __umoddi3, linkage); | 82 | @export("__umoddi3", __umoddi3, linkage); |
| 79 | @export("__udivmodsi4", __udivmodsi4, linkage); | 83 | @export("__udivmodsi4", __udivmodsi4, linkage); |
| 80 | 84 | ||
| 85 | @export("__negsf2", @import("compiler_rt/negXf2.zig").__negsf2, linkage); | ||
| 86 | @export("__negdf2", @import("compiler_rt/negXf2.zig").__negdf2, linkage); | ||
| 87 | |||
| 81 | if (is_arm_arch and !is_arm_64) { | 88 | if (is_arm_arch and !is_arm_64) { |
| 82 | @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); | 89 | @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); |
| 83 | @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); | 90 | @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); |
| 84 | @export("__aeabi_uidiv", __udivsi3, linkage); | 91 | @export("__aeabi_uidiv", __udivsi3, linkage); |
| 92 | |||
| 93 | @export("__aeabi_memcpy", __aeabi_memcpy, linkage); | ||
| 94 | @export("__aeabi_memcpy4", __aeabi_memcpy, linkage); | ||
| 95 | @export("__aeabi_memcpy8", __aeabi_memcpy, linkage); | ||
| 96 | |||
| 97 | @export("__aeabi_memmove", __aeabi_memmove, linkage); | ||
| 98 | @export("__aeabi_memmove4", __aeabi_memmove, linkage); | ||
| 99 | @export("__aeabi_memmove8", __aeabi_memmove, linkage); | ||
| 100 | |||
| 101 | @export("__aeabi_memset", __aeabi_memset, linkage); | ||
| 102 | @export("__aeabi_memset4", __aeabi_memset, linkage); | ||
| 103 | @export("__aeabi_memset8", __aeabi_memset, linkage); | ||
| 104 | |||
| 105 | @export("__aeabi_memclr", __aeabi_memclr, linkage); | ||
| 106 | @export("__aeabi_memclr4", __aeabi_memclr, linkage); | ||
| 107 | @export("__aeabi_memclr8", __aeabi_memclr, linkage); | ||
| 108 | |||
| 109 | @export("__aeabi_memcmp", __aeabi_memcmp, linkage); | ||
| 110 | @export("__aeabi_memcmp4", __aeabi_memcmp, linkage); | ||
| 111 | @export("__aeabi_memcmp8", __aeabi_memcmp, linkage); | ||
| 112 | |||
| 113 | @export("__aeabi_fneg", @import("compiler_rt/negXf2.zig").__negsf2, linkage); | ||
| 114 | @export("__aeabi_dneg", @import("compiler_rt/negXf2.zig").__negdf2, linkage); | ||
| 115 | |||
| 116 | @export("__aeabi_fmul", @import("compiler_rt/mulXf3.zig").__mulsf3, linkage); | ||
| 117 | @export("__aeabi_dmul", @import("compiler_rt/mulXf3.zig").__muldf3, linkage); | ||
| 118 | |||
| 119 | @export("__aeabi_d2h", @import("compiler_rt/truncXfYf2.zig").__truncdfhf2, linkage); | ||
| 120 | |||
| 121 | @export("__aeabi_f2ulz", @import("compiler_rt/fixunssfdi.zig").__fixunssfdi, linkage); | ||
| 122 | @export("__aeabi_d2ulz", @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi, linkage); | ||
| 123 | |||
| 124 | @export("__aeabi_f2lz", @import("compiler_rt/fixsfdi.zig").__fixsfdi, linkage); | ||
| 125 | @export("__aeabi_d2lz", @import("compiler_rt/fixdfdi.zig").__fixdfdi, linkage); | ||
| 126 | |||
| 127 | @export("__aeabi_d2uiz", @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi, linkage); | ||
| 128 | |||
| 129 | @export("__aeabi_h2f", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); | ||
| 130 | @export("__aeabi_f2h", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); | ||
| 131 | |||
| 132 | @export("__aeabi_fadd", @import("compiler_rt/addXf3.zig").__addsf3, linkage); | ||
| 133 | @export("__aeabi_dadd", @import("compiler_rt/addXf3.zig").__adddf3, linkage); | ||
| 134 | @export("__aeabi_fsub", @import("compiler_rt/addXf3.zig").__subsf3, linkage); | ||
| 135 | @export("__aeabi_dsub", @import("compiler_rt/addXf3.zig").__subdf3, linkage); | ||
| 136 | |||
| 137 | @export("__aeabi_f2uiz", @import("compiler_rt/fixunssfsi.zig").__fixunssfsi, linkage); | ||
| 138 | |||
| 139 | @export("__aeabi_f2iz", @import("compiler_rt/fixsfsi.zig").__fixsfsi, linkage); | ||
| 140 | @export("__aeabi_d2iz", @import("compiler_rt/fixdfsi.zig").__fixdfsi, linkage); | ||
| 85 | } | 141 | } |
| 86 | if (builtin.os == builtin.Os.windows) { | 142 | if (builtin.os == builtin.Os.windows) { |
| 87 | switch (builtin.arch) { | 143 | switch (builtin.arch) { |
| ... | @@ -187,6 +243,17 @@ const is_arm_arch = switch (builtin.arch) { | ... | @@ -187,6 +243,17 @@ const is_arm_arch = switch (builtin.arch) { |
| 187 | else => false, | 243 | else => false, |
| 188 | }; | 244 | }; |
| 189 | 245 | ||
| 246 | const is_arm_32 = is_arm_arch and !is_arm_64; | ||
| 247 | |||
| 248 | const use_thumb_1 = is_arm_32 and switch (builtin.arch.arm) { | ||
| 249 | builtin.Arch.Arm32.v6, | ||
| 250 | builtin.Arch.Arm32.v6m, | ||
| 251 | builtin.Arch.Arm32.v6k, | ||
| 252 | builtin.Arch.Arm32.v6t2, | ||
| 253 | => true, | ||
| 254 | else => false, | ||
| 255 | }; | ||
| 256 | |||
| 190 | nakedcc fn __aeabi_uidivmod() void { | 257 | nakedcc fn __aeabi_uidivmod() void { |
| 191 | @setRuntimeSafety(false); | 258 | @setRuntimeSafety(false); |
| 192 | asm volatile ( | 259 | asm volatile ( |
| ... | @@ -203,6 +270,96 @@ nakedcc fn __aeabi_uidivmod() void { | ... | @@ -203,6 +270,96 @@ nakedcc fn __aeabi_uidivmod() void { |
| 203 | ); | 270 | ); |
| 204 | } | 271 | } |
| 205 | 272 | ||
| 273 | nakedcc fn __aeabi_memcpy() noreturn { | ||
| 274 | @setRuntimeSafety(false); | ||
| 275 | if (use_thumb_1) { | ||
| 276 | asm volatile ( | ||
| 277 | \\ push {r7, lr} | ||
| 278 | \\ bl memcpy | ||
| 279 | \\ pop {r7, pc} | ||
| 280 | ); | ||
| 281 | } else { | ||
| 282 | asm volatile ( | ||
| 283 | \\ b memcpy | ||
| 284 | ); | ||
| 285 | } | ||
| 286 | unreachable; | ||
| 287 | } | ||
| 288 | |||
| 289 | nakedcc fn __aeabi_memmove() noreturn { | ||
| 290 | @setRuntimeSafety(false); | ||
| 291 | if (use_thumb_1) { | ||
| 292 | asm volatile ( | ||
| 293 | \\ push {r7, lr} | ||
| 294 | \\ bl memmove | ||
| 295 | \\ pop {r7, pc} | ||
| 296 | ); | ||
| 297 | } else { | ||
| 298 | asm volatile ( | ||
| 299 | \\ b memmove | ||
| 300 | ); | ||
| 301 | } | ||
| 302 | unreachable; | ||
| 303 | } | ||
| 304 | |||
| 305 | nakedcc fn __aeabi_memset() noreturn { | ||
| 306 | @setRuntimeSafety(false); | ||
| 307 | if (use_thumb_1) { | ||
| 308 | asm volatile ( | ||
| 309 | \\ mov r3, r1 | ||
| 310 | \\ mov r1, r2 | ||
| 311 | \\ mov r2, r3 | ||
| 312 | \\ push {r7, lr} | ||
| 313 | \\ b memset | ||
| 314 | \\ pop {r7, pc} | ||
| 315 | ); | ||
| 316 | } else { | ||
| 317 | asm volatile ( | ||
| 318 | \\ mov r3, r1 | ||
| 319 | \\ mov r1, r2 | ||
| 320 | \\ mov r2, r3 | ||
| 321 | \\ b memset | ||
| 322 | ); | ||
| 323 | } | ||
| 324 | unreachable; | ||
| 325 | } | ||
| 326 | |||
| 327 | nakedcc fn __aeabi_memclr() noreturn { | ||
| 328 | @setRuntimeSafety(false); | ||
| 329 | if (use_thumb_1) { | ||
| 330 | asm volatile ( | ||
| 331 | \\ mov r2, r1 | ||
| 332 | \\ movs r1, #0 | ||
| 333 | \\ push {r7, lr} | ||
| 334 | \\ bl memset | ||
| 335 | \\ pop {r7, pc} | ||
| 336 | ); | ||
| 337 | } else { | ||
| 338 | asm volatile ( | ||
| 339 | \\ mov r2, r1 | ||
| 340 | \\ movs r1, #0 | ||
| 341 | \\ b memset | ||
| 342 | ); | ||
| 343 | } | ||
| 344 | unreachable; | ||
| 345 | } | ||
| 346 | |||
| 347 | nakedcc fn __aeabi_memcmp() noreturn { | ||
| 348 | @setRuntimeSafety(false); | ||
| 349 | if (use_thumb_1) { | ||
| 350 | asm volatile ( | ||
| 351 | \\ push {r7, lr} | ||
| 352 | \\ bl memcmp | ||
| 353 | \\ pop {r7, pc} | ||
| 354 | ); | ||
| 355 | } else { | ||
| 356 | asm volatile ( | ||
| 357 | \\ b memcmp | ||
| 358 | ); | ||
| 359 | } | ||
| 360 | unreachable; | ||
| 361 | } | ||
| 362 | |||
| 206 | // _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, | 363 | // _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, |
| 207 | // then decrement %esp by %eax. Preserves all registers except %esp and flags. | 364 | // then decrement %esp by %eax. Preserves all registers except %esp and flags. |
| 208 | // This routine is windows specific | 365 | // This routine is windows specific |
std/special/compiler_rt/addXf3.zig+30-8| ... | @@ -6,27 +6,49 @@ const std = @import("std"); | ... | @@ -6,27 +6,49 @@ const std = @import("std"); |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const compiler_rt = @import("../compiler_rt.zig"); | 7 | const compiler_rt = @import("../compiler_rt.zig"); |
| 8 | 8 | ||
| 9 | pub extern fn __addsf3(a: f32, b: f32) f32 { | ||
| 10 | return addXf3(f32, a, b); | ||
| 11 | } | ||
| 12 | |||
| 13 | pub extern fn __adddf3(a: f64, b: f64) f64 { | ||
| 14 | return addXf3(f64, a, b); | ||
| 15 | } | ||
| 16 | |||
| 9 | pub extern fn __addtf3(a: f128, b: f128) f128 { | 17 | pub extern fn __addtf3(a: f128, b: f128) f128 { |
| 10 | return addXf3(f128, a, b); | 18 | return addXf3(f128, a, b); |
| 11 | } | 19 | } |
| 12 | 20 | ||
| 21 | pub extern fn __subsf3(a: f32, b: f32) f32 { | ||
| 22 | const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (u32(1) << 31)); | ||
| 23 | return addXf3(f32, a, neg_b); | ||
| 24 | } | ||
| 25 | |||
| 26 | pub extern fn __subdf3(a: f64, b: f64) f64 { | ||
| 27 | const neg_b = @bitCast(f64, @bitCast(u64, b) ^ (u64(1) << 63)); | ||
| 28 | return addXf3(f64, a, neg_b); | ||
| 29 | } | ||
| 30 | |||
| 13 | pub extern fn __subtf3(a: f128, b: f128) f128 { | 31 | pub extern fn __subtf3(a: f128, b: f128) f128 { |
| 14 | const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127)); | 32 | const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127)); |
| 15 | return addXf3(f128, a, neg_b); | 33 | return addXf3(f128, a, neg_b); |
| 16 | } | 34 | } |
| 17 | 35 | ||
| 18 | inline fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { | 36 | // TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 |
| 37 | fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { | ||
| 19 | const Z = @IntType(false, T.bit_count); | 38 | const Z = @IntType(false, T.bit_count); |
| 39 | const S = @IntType(false, T.bit_count - @clz(Z(T.bit_count) - 1)); | ||
| 20 | const significandBits = std.math.floatMantissaBits(T); | 40 | const significandBits = std.math.floatMantissaBits(T); |
| 21 | const implicitBit = Z(1) << significandBits; | 41 | const implicitBit = Z(1) << significandBits; |
| 22 | 42 | ||
| 23 | const shift = @clz(significand.*) - @clz(implicitBit); | 43 | const shift = @clz(significand.*) - @clz(implicitBit); |
| 24 | significand.* <<= @intCast(u7, shift); | 44 | significand.* <<= @intCast(S, shift); |
| 25 | return 1 - shift; | 45 | return 1 - shift; |
| 26 | } | 46 | } |
| 27 | 47 | ||
| 28 | inline fn addXf3(comptime T: type, a: T, b: T) T { | 48 | // TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 |
| 49 | fn addXf3(comptime T: type, a: T, b: T) T { | ||
| 29 | const Z = @IntType(false, T.bit_count); | 50 | const Z = @IntType(false, T.bit_count); |
| 51 | const S = @IntType(false, T.bit_count - @clz(Z(T.bit_count) - 1)); | ||
| 30 | 52 | ||
| 31 | const typeWidth = T.bit_count; | 53 | const typeWidth = T.bit_count; |
| 32 | const significandBits = std.math.floatMantissaBits(T); | 54 | const significandBits = std.math.floatMantissaBits(T); |
| ... | @@ -126,8 +148,8 @@ inline fn addXf3(comptime T: type, a: T, b: T) T { | ... | @@ -126,8 +148,8 @@ inline fn addXf3(comptime T: type, a: T, b: T) T { |
| 126 | const @"align" = @intCast(Z, aExponent - bExponent); | 148 | const @"align" = @intCast(Z, aExponent - bExponent); |
| 127 | if (@"align" != 0) { | 149 | if (@"align" != 0) { |
| 128 | if (@"align" < typeWidth) { | 150 | if (@"align" < typeWidth) { |
| 129 | const sticky = if (bSignificand << @intCast(u7, typeWidth - @"align") != 0) Z(1) else 0; | 151 | const sticky = if (bSignificand << @intCast(S, typeWidth - @"align") != 0) Z(1) else 0; |
| 130 | bSignificand = (bSignificand >> @truncate(u7, @"align")) | sticky; | 152 | bSignificand = (bSignificand >> @truncate(S, @"align")) | sticky; |
| 131 | } else { | 153 | } else { |
| 132 | bSignificand = 1; // sticky; b is known to be non-zero. | 154 | bSignificand = 1; // sticky; b is known to be non-zero. |
| 133 | } | 155 | } |
| ... | @@ -141,7 +163,7 @@ inline fn addXf3(comptime T: type, a: T, b: T) T { | ... | @@ -141,7 +163,7 @@ inline fn addXf3(comptime T: type, a: T, b: T) T { |
| 141 | // and adjust the exponent: | 163 | // and adjust the exponent: |
| 142 | if (aSignificand < implicitBit << 3) { | 164 | if (aSignificand < implicitBit << 3) { |
| 143 | const shift = @intCast(i32, @clz(aSignificand)) - @intCast(i32, @clz(implicitBit << 3)); | 165 | const shift = @intCast(i32, @clz(aSignificand)) - @intCast(i32, @clz(implicitBit << 3)); |
| 144 | aSignificand <<= @intCast(u7, shift); | 166 | aSignificand <<= @intCast(S, shift); |
| 145 | aExponent -= shift; | 167 | aExponent -= shift; |
| 146 | } | 168 | } |
| 147 | } else { // addition | 169 | } else { // addition |
| ... | @@ -163,8 +185,8 @@ inline fn addXf3(comptime T: type, a: T, b: T) T { | ... | @@ -163,8 +185,8 @@ inline fn addXf3(comptime T: type, a: T, b: T) T { |
| 163 | // Result is denormal before rounding; the exponent is zero and we | 185 | // Result is denormal before rounding; the exponent is zero and we |
| 164 | // need to shift the significand. | 186 | // need to shift the significand. |
| 165 | const shift = @intCast(Z, 1 - aExponent); | 187 | const shift = @intCast(Z, 1 - aExponent); |
| 166 | const sticky = if (aSignificand << @intCast(u7, typeWidth - shift) != 0) Z(1) else 0; | 188 | const sticky = if (aSignificand << @intCast(S, typeWidth - shift) != 0) Z(1) else 0; |
| 167 | aSignificand = aSignificand >> @intCast(u7, shift | sticky); | 189 | aSignificand = aSignificand >> @intCast(S, shift | sticky); |
| 168 | aExponent = 0; | 190 | aExponent = 0; |
| 169 | } | 191 | } |
| 170 | 192 |
std/special/compiler_rt/negXf2.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub extern fn __negsf2(a: f32) f32 { | ||
| 4 | return negXf2(f32, a); | ||
| 5 | } | ||
| 6 | |||
| 7 | pub extern fn __negdf2(a: f64) f64 { | ||
| 8 | return negXf2(f64, a); | ||
| 9 | } | ||
| 10 | |||
| 11 | fn negXf2(comptime T: type, a: T) T { | ||
| 12 | const Z = @IntType(false, T.bit_count); | ||
| 13 | |||
| 14 | const typeWidth = T.bit_count; | ||
| 15 | const significandBits = std.math.floatMantissaBits(T); | ||
| 16 | const exponentBits = std.math.floatExponentBits(T); | ||
| 17 | |||
| 18 | const signBit = (Z(1) << (significandBits + exponentBits)); | ||
| 19 | |||
| 20 | return @bitCast(T, @bitCast(Z, a) ^ signBit); | ||
| 21 | } | ||