| 1 | const builtin = @import("builtin"); |
| 2 | const compiler_rt = @This(); |
| 3 | const ofmt_c = builtin.object_format == .c; |
| 4 | const native_endian = builtin.cpu.arch.endian(); |
| 5 | |
| 6 | const std = @import("std"); |
| 7 | |
| 8 | /// Avoid dragging in the runtime safety mechanisms into this .o file, unless |
| 9 | /// we're trying to test compiler-rt. |
| 10 | pub const panic = if (test_safety) |
| 11 | std.debug.FullPanic(std.debug.defaultPanic) |
| 12 | else |
| 13 | std.debug.no_panic; |
| 14 | |
| 15 | pub const std_options_debug_threaded_io: ?*std.Io.Threaded = if (builtin.is_test) |
| 16 | std.Io.Threaded.global_single_threaded |
| 17 | else |
| 18 | null; |
| 19 | |
| 20 | pub const std_options_debug_io: std.Io = if (builtin.is_test) |
| 21 | std.Io.Threaded.global_single_threaded.io() |
| 22 | else |
| 23 | unreachable; |
| 24 | |
| 25 | pub inline fn symbol(comptime func: *const anyopaque, comptime name: []const u8) void { |
| 26 | @export(func, .{ .name = name, .linkage = linkage, .visibility = visibility }); |
| 27 | } |
| 28 | |
| 29 | /// For now, we prefer weak linkage because some of the routines we implement here may also be |
| 30 | /// provided by system/dynamic libc. Eventually we should be more disciplined about this on a |
| 31 | /// per-symbol, per-target basis: https://github.com/ziglang/zig/issues/11883 |
| 32 | pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) |
| 33 | .internal |
| 34 | else if (ofmt_c) |
| 35 | .strong |
| 36 | else |
| 37 | .weak; |
| 38 | |
| 39 | /// Determines the symbol's visibility to other objects. |
| 40 | /// For WebAssembly this allows the symbol to be resolved to other modules, but will not |
| 41 | /// export it to the host runtime. |
| 42 | pub const visibility: std.builtin.SymbolVisibility = if (linkage == .internal or builtin.link_mode == .dynamic) |
| 43 | .default |
| 44 | else |
| 45 | .hidden; |
| 46 | |
| 47 | pub const test_safety = switch (builtin.zig_backend) { |
| 48 | .stage2_aarch64 => false, |
| 49 | else => builtin.is_test, |
| 50 | }; |
| 51 | |
| 52 | comptime { |
| 53 | // Integer routines |
| 54 | _ = @import("compiler_rt/count0bits.zig"); |
| 55 | _ = @import("compiler_rt/parity.zig"); |
| 56 | _ = @import("compiler_rt/popcount.zig"); |
| 57 | _ = @import("compiler_rt/bitreverse.zig"); |
| 58 | _ = @import("compiler_rt/bswap.zig"); |
| 59 | _ = @import("compiler_rt/cmp.zig"); |
| 60 | |
| 61 | _ = @import("compiler_rt/shift.zig"); |
| 62 | symbol(&__negsi2, "__negsi2"); |
| 63 | symbol(&__negdi2, "__negdi2"); |
| 64 | symbol(&__negti2, "__negti2"); |
| 65 | _ = @import("compiler_rt/int.zig"); |
| 66 | _ = @import("compiler_rt/mulXi3.zig"); |
| 67 | _ = @import("compiler_rt/udivmod.zig"); |
| 68 | |
| 69 | _ = @import("compiler_rt/absv.zig"); |
| 70 | _ = @import("compiler_rt/absvsi2.zig"); |
| 71 | _ = @import("compiler_rt/absvdi2.zig"); |
| 72 | _ = @import("compiler_rt/absvti2.zig"); |
| 73 | _ = @import("compiler_rt/negv.zig"); |
| 74 | |
| 75 | _ = @import("compiler_rt/addvsi3.zig"); |
| 76 | _ = @import("compiler_rt/addvdi3.zig"); |
| 77 | |
| 78 | _ = @import("compiler_rt/subvsi3.zig"); |
| 79 | _ = @import("compiler_rt/subvdi3.zig"); |
| 80 | |
| 81 | _ = @import("compiler_rt/mulvsi3.zig"); |
| 82 | |
| 83 | _ = @import("compiler_rt/mulo.zig"); |
| 84 | |
| 85 | // Float routines |
| 86 | // conversion |
| 87 | _ = @import("compiler_rt/extendf.zig"); |
| 88 | _ = @import("compiler_rt/truncf.zig"); |
| 89 | _ = @import("compiler_rt/int_from_float.zig"); |
| 90 | _ = @import("compiler_rt/float_from_int.zig"); |
| 91 | |
| 92 | // comparison |
| 93 | _ = @import("compiler_rt/comparef.zig"); |
| 94 | |
| 95 | // arithmetic |
| 96 | _ = @import("compiler_rt/addf3.zig"); |
| 97 | _ = @import("compiler_rt/mulf3.zig"); |
| 98 | |
| 99 | _ = @import("compiler_rt/divsf3.zig"); |
| 100 | _ = @import("compiler_rt/divdf3.zig"); |
| 101 | _ = @import("compiler_rt/divxf3.zig"); |
| 102 | _ = @import("compiler_rt/divtf3.zig"); |
| 103 | |
| 104 | symbol(&__neghf2, "__neghf2"); |
| 105 | if (want_aeabi) { |
| 106 | symbol(&__aeabi_fneg, "__aeabi_fneg"); |
| 107 | symbol(&__aeabi_dneg, "__aeabi_dneg"); |
| 108 | } else { |
| 109 | symbol(&__negsf2, "__negsf2"); |
| 110 | symbol(&__negdf2, "__negdf2"); |
| 111 | } |
| 112 | if (want_ppc_abi) { |
| 113 | symbol(&__negtf2, "__negkf2"); |
| 114 | } else { |
| 115 | symbol(&__negtf2, "__negtf2"); |
| 116 | } |
| 117 | symbol(&__negxf2, "__negxf2"); |
| 118 | |
| 119 | // other |
| 120 | _ = @import("compiler_rt/powiXf2.zig"); |
| 121 | _ = @import("compiler_rt/mulc3.zig"); |
| 122 | _ = @import("compiler_rt/divc3.zig"); |
| 123 | |
| 124 | // Math routines. Alphabetically sorted. |
| 125 | _ = @import("compiler_rt/cos.zig"); |
| 126 | _ = @import("compiler_rt/exp.zig"); |
| 127 | _ = @import("compiler_rt/exp2.zig"); |
| 128 | _ = @import("compiler_rt/fabs.zig"); |
| 129 | _ = @import("compiler_rt/floor_ceil.zig"); |
| 130 | _ = @import("compiler_rt/fma.zig"); |
| 131 | _ = @import("compiler_rt/fmax.zig"); |
| 132 | _ = @import("compiler_rt/fmin.zig"); |
| 133 | _ = @import("compiler_rt/fmod.zig"); |
| 134 | _ = @import("compiler_rt/log.zig"); |
| 135 | _ = @import("compiler_rt/log10.zig"); |
| 136 | _ = @import("compiler_rt/log2.zig"); |
| 137 | _ = @import("compiler_rt/round.zig"); |
| 138 | _ = @import("compiler_rt/sin.zig"); |
| 139 | _ = @import("compiler_rt/sincos.zig"); |
| 140 | _ = @import("compiler_rt/sqrt.zig"); |
| 141 | _ = @import("compiler_rt/tan.zig"); |
| 142 | _ = @import("compiler_rt/trunc.zig"); |
| 143 | |
| 144 | // BigInt. Alphabetically sorted. |
| 145 | _ = @import("compiler_rt/divmodei4.zig"); |
| 146 | _ = @import("compiler_rt/udivmodei4.zig"); |
| 147 | |
| 148 | if (builtin.cpu.arch.isWasm()) _ = @import("compiler_rt/limb64.zig"); |
| 149 | |
| 150 | // extra |
| 151 | _ = @import("compiler_rt/os_version_check.zig"); |
| 152 | _ = @import("compiler_rt/emutls.zig"); |
| 153 | _ = @import("compiler_rt/arm.zig"); |
| 154 | _ = @import("compiler_rt/aulldiv.zig"); |
| 155 | _ = @import("compiler_rt/aullrem.zig"); |
| 156 | _ = @import("compiler_rt/clear_cache.zig"); |
| 157 | _ = @import("compiler_rt/hexagon.zig"); |
| 158 | |
| 159 | if (builtin.object_format != .c) { |
| 160 | if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/atomics.zig"); |
| 161 | _ = @import("compiler_rt/stack_probe.zig"); |
| 162 | |
| 163 | // macOS has these functions inside libSystem. |
| 164 | if (builtin.cpu.arch.isAARCH64() and !builtin.os.tag.isDarwin()) { |
| 165 | if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/aarch64_outline_atomics.zig"); |
| 166 | } |
| 167 | |
| 168 | _ = @import("compiler_rt/memcpy.zig"); |
| 169 | if (!ofmt_c) { |
| 170 | symbol(&memset, "memset"); |
| 171 | } |
| 172 | _ = @import("compiler_rt/memmove.zig"); |
| 173 | symbol(&memcmp, "memcmp"); |
| 174 | symbol(&bcmp, "bcmp"); |
| 175 | _ = @import("compiler_rt/ssp.zig"); |
| 176 | symbol(&strlen, "strlen"); |
| 177 | } |
| 178 | |
| 179 | // Temporarily used for uefi until https://github.com/ziglang/zig/issues/21630 is addressed. |
| 180 | if (!builtin.link_libc and (builtin.os.tag == .windows or builtin.os.tag == .uefi) and (builtin.abi == .none or builtin.abi == .msvc)) { |
| 181 | symbol(&_fltused, "_fltused"); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | var _fltused: c_int = 1; |
| 186 | |
| 187 | fn strlen(s: [*:0]const c_char) callconv(.c) usize { |
| 188 | return std.mem.len(s); |
| 189 | } |
| 190 | |
| 191 | fn memcmp(vl: [*]const u8, vr: [*]const u8, n: usize) callconv(.c) c_int { |
| 192 | var i: usize = 0; |
| 193 | while (i < n) : (i += 1) { |
| 194 | const compared = @as(c_int, vl[i]) -% @as(c_int, vr[i]); |
| 195 | if (compared != 0) return compared; |
| 196 | } |
| 197 | return 0; |
| 198 | } |
| 199 | |
| 200 | test "memcmp" { |
| 201 | const arr0 = &[_]u8{ 1, 1, 1 }; |
| 202 | const arr1 = &[_]u8{ 1, 1, 1 }; |
| 203 | const arr2 = &[_]u8{ 1, 0, 1 }; |
| 204 | const arr3 = &[_]u8{ 1, 2, 1 }; |
| 205 | const arr4 = &[_]u8{ 1, 0xff, 1 }; |
| 206 | |
| 207 | try std.testing.expect(memcmp(arr0, arr1, 3) == 0); |
| 208 | try std.testing.expect(memcmp(arr0, arr2, 3) > 0); |
| 209 | try std.testing.expect(memcmp(arr0, arr3, 3) < 0); |
| 210 | |
| 211 | try std.testing.expect(memcmp(arr0, arr4, 3) < 0); |
| 212 | try std.testing.expect(memcmp(arr4, arr0, 3) > 0); |
| 213 | } |
| 214 | |
| 215 | pub const PreferredLoadStoreElement = element: { |
| 216 | if (std.simd.suggestVectorLength(u8)) |vec_size| { |
| 217 | const Vec = @Vector(vec_size, u8); |
| 218 | |
| 219 | if (@sizeOf(Vec) == vec_size and std.math.isPowerOfTwo(vec_size)) { |
| 220 | break :element Vec; |
| 221 | } |
| 222 | } |
| 223 | break :element usize; |
| 224 | }; |
| 225 | |
| 226 | pub const want_aeabi = switch (builtin.abi) { |
| 227 | .eabi, |
| 228 | .eabihf, |
| 229 | .musleabi, |
| 230 | .musleabihf, |
| 231 | .gnueabi, |
| 232 | .gnueabihf, |
| 233 | .android, |
| 234 | .androideabi, |
| 235 | => builtin.cpu.arch.isArm(), |
| 236 | else => false, |
| 237 | }; |
| 238 | |
| 239 | /// These functions are required on Windows on ARM. They are provided by MSVC libc, but in libc-less |
| 240 | /// builds or when linking MinGW libc they are our responsibility. |
| 241 | /// Temporarily used for thumb-uefi until https://github.com/ziglang/zig/issues/21630 is addressed. |
| 242 | pub const want_windows_arm_abi = e: { |
| 243 | if (!builtin.cpu.arch.isArm()) break :e false; |
| 244 | switch (builtin.os.tag) { |
| 245 | .windows, .uefi => {}, |
| 246 | else => break :e false, |
| 247 | } |
| 248 | // The ABI is needed, but it's only our reponsibility if libc won't provide it. |
| 249 | break :e builtin.abi.isGnu() or !builtin.link_libc; |
| 250 | }; |
| 251 | |
| 252 | /// These functions are required by on Windows on x86 on some ABIs. They are provided by MSVC libc, |
| 253 | /// but in libc-less builds they are our responsibility. |
| 254 | pub const want_windows_x86_msvc_abi = e: { |
| 255 | if (builtin.cpu.arch != .x86) break :e false; |
| 256 | if (builtin.os.tag != .windows) break :e false; |
| 257 | switch (builtin.abi) { |
| 258 | .none, .msvc, .itanium => {}, |
| 259 | else => break :e false, |
| 260 | } |
| 261 | // The ABI is needed, but it's only our responsibility if libc won't provide it. |
| 262 | break :e !builtin.link_libc; |
| 263 | }; |
| 264 | |
| 265 | pub const want_ppc_abi = builtin.cpu.arch.isPowerPC(); |
| 266 | |
| 267 | pub const want_float_exceptions = !builtin.cpu.arch.isWasm(); |
| 268 | |
| 269 | /// This governs whether to use these symbol names for f16/f32 conversions |
| 270 | /// rather than the standard names: |
| 271 | /// * __gnu_f2h_ieee |
| 272 | /// * __gnu_h2f_ieee |
| 273 | /// Known correct configurations: |
| 274 | /// x86_64-freestanding-none => true |
| 275 | /// x86_64-linux-none => true |
| 276 | /// x86_64-linux-gnu => true |
| 277 | /// x86_64-linux-musl => true |
| 278 | /// x86_64-linux-eabi => true |
| 279 | /// arm-linux-musleabihf => true |
| 280 | /// arm-linux-gnueabihf => true |
| 281 | /// arm-linux-eabihf => false |
| 282 | /// wasm32-wasi-musl => false |
| 283 | /// wasm32-freestanding-none => false |
| 284 | /// x86_64-windows-gnu => true |
| 285 | /// x86_64-windows-msvc => true |
| 286 | /// any-macos-any => false |
| 287 | pub const gnu_f16_abi = switch (builtin.cpu.arch) { |
| 288 | .wasm32, |
| 289 | .wasm64, |
| 290 | .riscv64, |
| 291 | .riscv64be, |
| 292 | .riscv32, |
| 293 | .riscv32be, |
| 294 | => false, |
| 295 | |
| 296 | .x86, .x86_64 => true, |
| 297 | |
| 298 | .arm, .armeb, .thumb, .thumbeb => switch (builtin.abi) { |
| 299 | .eabi, .eabihf => false, |
| 300 | else => true, |
| 301 | }, |
| 302 | |
| 303 | else => !builtin.os.tag.isDarwin(), |
| 304 | }; |
| 305 | |
| 306 | pub const want_sparc64_abi = builtin.cpu.arch == .sparc64; |
| 307 | pub const want_sparc32_abi = builtin.cpu.arch == .sparc; |
| 308 | |
| 309 | /// For operations converting between `f16` and another floating point type. |
| 310 | pub fn f16Conv(comptime OtherType: type) type { |
| 311 | switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 16)) { |
| 312 | .hard => {}, |
| 313 | .soft => return softFloatAbi(f16), |
| 314 | } |
| 315 | if (builtin.cpu.arch.isX86() and builtin.os.tag.isDarwin()) switch (OtherType) { |
| 316 | else => unreachable, |
| 317 | // Starting with LLVM 16, Darwin uses different abi for f16 |
| 318 | // depending on the type of the other return/argument..??? |
| 319 | f32, f64 => return softFloatAbi(f16), |
| 320 | f80, f128 => {}, |
| 321 | }; |
| 322 | return hardFloatAbi(f16); |
| 323 | } |
| 324 | pub const @"f16" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 16)) { |
| 325 | .hard => hardFloatAbi(f16), |
| 326 | .soft => softFloatAbi(f16), |
| 327 | }; |
| 328 | pub const @"f32" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 32)) { |
| 329 | .hard => hardFloatAbi(f32), |
| 330 | .soft => softFloatAbi(f32), |
| 331 | }; |
| 332 | pub const @"f64" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 64)) { |
| 333 | .hard => hardFloatAbi(f64), |
| 334 | .soft => softFloatAbi(f64), |
| 335 | }; |
| 336 | pub const @"f80" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 80)) { |
| 337 | .hard => hardFloatAbi(f80), |
| 338 | .soft => struct { |
| 339 | pub const Abi = extern struct { mantissa: u64, exponent: u16 }; |
| 340 | const Repr = packed struct { mantissa: u64, exponent: u16 }; |
| 341 | pub inline fn toAbi(raw: f80) Abi { |
| 342 | const repr: Repr = @bitCast(raw); |
| 343 | return .{ .mantissa = repr.mantissa, .exponent = repr.exponent }; |
| 344 | } |
| 345 | pub inline fn fromAbi(abi: Abi) f80 { |
| 346 | const repr: Repr = .{ .mantissa = abi.mantissa, .exponent = abi.exponent }; |
| 347 | return @bitCast(repr); |
| 348 | } |
| 349 | pub const complex = complexAbi(f80, @This()); |
| 350 | }, |
| 351 | }; |
| 352 | pub const @"f128" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 128)) { |
| 353 | .hard => hardFloatAbi(f128), |
| 354 | .soft => struct { |
| 355 | pub const Abi = switch (builtin.cpu.arch.endian()) { |
| 356 | .big => extern struct { hi: u64, lo: u64 }, |
| 357 | .little => extern struct { lo: u64, hi: u64 }, |
| 358 | }; |
| 359 | const Repr = packed struct { lo: u64, hi: u64 }; |
| 360 | pub inline fn toAbi(raw: f128) Abi { |
| 361 | const repr: Repr = @bitCast(raw); |
| 362 | return .{ .lo = repr.lo, .hi = repr.hi }; |
| 363 | } |
| 364 | pub inline fn fromAbi(abi: Abi) f128 { |
| 365 | const repr: Repr = .{ .lo = abi.lo, .hi = abi.hi }; |
| 366 | return @bitCast(repr); |
| 367 | } |
| 368 | pub const complex = complexAbi(f128, @This()); |
| 369 | }, |
| 370 | }; |
| 371 | fn hardFloatAbi(comptime Float: type) type { |
| 372 | return struct { |
| 373 | pub const Abi = Float; |
| 374 | pub inline fn toAbi(raw: Float) Abi { |
| 375 | return raw; |
| 376 | } |
| 377 | pub inline fn fromAbi(abi: Abi) Float { |
| 378 | return abi; |
| 379 | } |
| 380 | pub const complex = complexAbi(Float, @This()); |
| 381 | }; |
| 382 | } |
| 383 | fn softFloatAbi(comptime Float: type) type { |
| 384 | return struct { |
| 385 | pub const Abi = @Int(.unsigned, @bitSizeOf(Float)); |
| 386 | pub inline fn toAbi(raw: Float) Abi { |
| 387 | return @bitCast(raw); |
| 388 | } |
| 389 | pub inline fn fromAbi(abi: Abi) Float { |
| 390 | return @bitCast(abi); |
| 391 | } |
| 392 | pub const complex = complexAbi(Float, @This()); |
| 393 | }; |
| 394 | } |
| 395 | fn complexAbi(comptime Float: type, comptime float: type) type { |
| 396 | return struct { |
| 397 | pub const Abi = extern struct { real: float.Abi, imag: float.Abi }; |
| 398 | pub inline fn toAbi(raw: Complex(Float)) Abi { |
| 399 | return .{ .real = float.toAbi(raw.real), .imag = float.toAbi(raw.imag) }; |
| 400 | } |
| 401 | pub inline fn fromAbi(abi: Abi) Complex(Float) { |
| 402 | return .{ .real = float.fromAbi(abi.real), .imag = float.fromAbi(abi.imag) }; |
| 403 | } |
| 404 | }; |
| 405 | } |
| 406 | |
| 407 | pub fn Complex(comptime Float: type) type { |
| 408 | return struct { real: Float, imag: Float }; |
| 409 | } |
| 410 | |
| 411 | pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { |
| 412 | switch (Z) { |
| 413 | u16 => { |
| 414 | // 16x16 --> 32 bit multiply |
| 415 | const product = @as(u32, a) * @as(u32, b); |
| 416 | hi.* = @intCast(product >> 16); |
| 417 | lo.* = @truncate(product); |
| 418 | }, |
| 419 | u32 => { |
| 420 | // 32x32 --> 64 bit multiply |
| 421 | const product = @as(u64, a) * @as(u64, b); |
| 422 | hi.* = @truncate(product >> 32); |
| 423 | lo.* = @truncate(product); |
| 424 | }, |
| 425 | u64 => { |
| 426 | const S = struct { |
| 427 | fn loWord(x: u64) u64 { |
| 428 | return @as(u32, @truncate(x)); |
| 429 | } |
| 430 | fn hiWord(x: u64) u64 { |
| 431 | return @as(u32, @truncate(x >> 32)); |
| 432 | } |
| 433 | }; |
| 434 | // 64x64 -> 128 wide multiply for platforms that don't have such an operation; |
| 435 | // many 64-bit platforms have this operation, but they tend to have hardware |
| 436 | // floating-point, so we don't bother with a special case for them here. |
| 437 | // Each of the component 32x32 -> 64 products |
| 438 | const plolo: u64 = S.loWord(a) * S.loWord(b); |
| 439 | const plohi: u64 = S.loWord(a) * S.hiWord(b); |
| 440 | const philo: u64 = S.hiWord(a) * S.loWord(b); |
| 441 | const phihi: u64 = S.hiWord(a) * S.hiWord(b); |
| 442 | // Sum terms that contribute to lo in a way that allows us to get the carry |
| 443 | const r0: u64 = S.loWord(plolo); |
| 444 | const r1: u64 = S.hiWord(plolo) +% S.loWord(plohi) +% S.loWord(philo); |
| 445 | lo.* = r0 +% (r1 << 32); |
| 446 | // Sum terms contributing to hi with the carry from lo |
| 447 | hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; |
| 448 | }, |
| 449 | u128 => { |
| 450 | const Word_LoMask: u64 = 0x00000000ffffffff; |
| 451 | const Word_HiMask: u64 = 0xffffffff00000000; |
| 452 | const Word_FullMask: u64 = 0xffffffffffffffff; |
| 453 | const S = struct { |
| 454 | fn Word_1(x: u128) u64 { |
| 455 | return @as(u32, @truncate(x >> 96)); |
| 456 | } |
| 457 | fn Word_2(x: u128) u64 { |
| 458 | return @as(u32, @truncate(x >> 64)); |
| 459 | } |
| 460 | fn Word_3(x: u128) u64 { |
| 461 | return @as(u32, @truncate(x >> 32)); |
| 462 | } |
| 463 | fn Word_4(x: u128) u64 { |
| 464 | return @as(u32, @truncate(x)); |
| 465 | } |
| 466 | }; |
| 467 | // 128x128 -> 256 wide multiply for platforms that don't have such an operation; |
| 468 | // many 64-bit platforms have this operation, but they tend to have hardware |
| 469 | // floating-point, so we don't bother with a special case for them here. |
| 470 | |
| 471 | const product11: u64 = S.Word_1(a) * S.Word_1(b); |
| 472 | const product12: u64 = S.Word_1(a) * S.Word_2(b); |
| 473 | const product13: u64 = S.Word_1(a) * S.Word_3(b); |
| 474 | const product14: u64 = S.Word_1(a) * S.Word_4(b); |
| 475 | const product21: u64 = S.Word_2(a) * S.Word_1(b); |
| 476 | const product22: u64 = S.Word_2(a) * S.Word_2(b); |
| 477 | const product23: u64 = S.Word_2(a) * S.Word_3(b); |
| 478 | const product24: u64 = S.Word_2(a) * S.Word_4(b); |
| 479 | const product31: u64 = S.Word_3(a) * S.Word_1(b); |
| 480 | const product32: u64 = S.Word_3(a) * S.Word_2(b); |
| 481 | const product33: u64 = S.Word_3(a) * S.Word_3(b); |
| 482 | const product34: u64 = S.Word_3(a) * S.Word_4(b); |
| 483 | const product41: u64 = S.Word_4(a) * S.Word_1(b); |
| 484 | const product42: u64 = S.Word_4(a) * S.Word_2(b); |
| 485 | const product43: u64 = S.Word_4(a) * S.Word_3(b); |
| 486 | const product44: u64 = S.Word_4(a) * S.Word_4(b); |
| 487 | |
| 488 | const sum0: u128 = @as(u128, product44); |
| 489 | const sum1: u128 = @as(u128, product34) +% |
| 490 | @as(u128, product43); |
| 491 | const sum2: u128 = @as(u128, product24) +% |
| 492 | @as(u128, product33) +% |
| 493 | @as(u128, product42); |
| 494 | const sum3: u128 = @as(u128, product14) +% |
| 495 | @as(u128, product23) +% |
| 496 | @as(u128, product32) +% |
| 497 | @as(u128, product41); |
| 498 | const sum4: u128 = @as(u128, product13) +% |
| 499 | @as(u128, product22) +% |
| 500 | @as(u128, product31); |
| 501 | const sum5: u128 = @as(u128, product12) +% |
| 502 | @as(u128, product21); |
| 503 | const sum6: u128 = @as(u128, product11); |
| 504 | |
| 505 | const r0: u128 = (sum0 & Word_FullMask) +% |
| 506 | ((sum1 & Word_LoMask) << 32); |
| 507 | const r1: u128 = (sum0 >> 64) +% |
| 508 | ((sum1 >> 32) & Word_FullMask) +% |
| 509 | (sum2 & Word_FullMask) +% |
| 510 | ((sum3 << 32) & Word_HiMask); |
| 511 | |
| 512 | lo.* = r0 +% (r1 << 64); |
| 513 | hi.* = (r1 >> 64) +% |
| 514 | (sum1 >> 96) +% |
| 515 | (sum2 >> 64) +% |
| 516 | (sum3 >> 32) +% |
| 517 | sum4 +% |
| 518 | (sum5 << 32) +% |
| 519 | (sum6 << 64); |
| 520 | }, |
| 521 | else => @compileError("unsupported"), |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | pub fn normalize(comptime T: type, significand: *@Int(.unsigned, @typeInfo(T).float.bits)) i32 { |
| 526 | const Z = @Int(.unsigned, @typeInfo(T).float.bits); |
| 527 | const integerBit = @as(Z, 1) << std.math.floatFractionalBits(T); |
| 528 | |
| 529 | const shift = @clz(significand.*) - @clz(integerBit); |
| 530 | significand.* <<= @as(std.math.Log2Int(Z), @intCast(shift)); |
| 531 | return @as(i32, 1) - shift; |
| 532 | } |
| 533 | |
| 534 | pub inline fn fneg(a: anytype) @TypeOf(a) { |
| 535 | const F = @TypeOf(a); |
| 536 | const bits = @typeInfo(F).float.bits; |
| 537 | const U = @Int(.unsigned, bits); |
| 538 | const sign_bit_mask = @as(U, 1) << (bits - 1); |
| 539 | const negated = @as(U, @bitCast(a)) ^ sign_bit_mask; |
| 540 | return @bitCast(negated); |
| 541 | } |
| 542 | |
| 543 | fn __neghf2(a: compiler_rt.f16.Abi) callconv(.c) compiler_rt.f16.Abi { |
| 544 | return compiler_rt.f16.toAbi(fneg(compiler_rt.f16.fromAbi(a))); |
| 545 | } |
| 546 | |
| 547 | fn __negsf2(a: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f32.Abi { |
| 548 | return compiler_rt.f32.toAbi(fneg(compiler_rt.f32.fromAbi(a))); |
| 549 | } |
| 550 | |
| 551 | fn __negdf2(a: compiler_rt.f64.Abi) callconv(.c) compiler_rt.f64.Abi { |
| 552 | return compiler_rt.f64.toAbi(fneg(compiler_rt.f64.fromAbi(a))); |
| 553 | } |
| 554 | |
| 555 | fn __negxf2(a: compiler_rt.f80.Abi) callconv(.c) compiler_rt.f80.Abi { |
| 556 | return compiler_rt.f80.toAbi(fneg(compiler_rt.f80.fromAbi(a))); |
| 557 | } |
| 558 | |
| 559 | fn __negtf2(a: compiler_rt.f128.Abi) callconv(.c) compiler_rt.f128.Abi { |
| 560 | return compiler_rt.f128.toAbi(fneg(compiler_rt.f128.fromAbi(a))); |
| 561 | } |
| 562 | |
| 563 | fn __aeabi_fneg(a: f32) callconv(.{ .arm_aapcs = .{} }) f32 { |
| 564 | return fneg(a); |
| 565 | } |
| 566 | |
| 567 | fn __aeabi_dneg(a: f64) callconv(.{ .arm_aapcs = .{} }) f64 { |
| 568 | return fneg(a); |
| 569 | } |
| 570 | |
| 571 | /// Allows to access underlying bits as two equally sized lower and higher |
| 572 | /// signed or unsigned integers. |
| 573 | pub fn HalveInt(comptime T: type, comptime signed_half: bool) type { |
| 574 | return extern union { |
| 575 | pub const bits = @divExact(@typeInfo(T).int.bits, 2); |
| 576 | pub const HalfTU = @Int(.unsigned, bits); |
| 577 | pub const HalfTS = @Int(.signed, bits); |
| 578 | pub const HalfT = if (signed_half) HalfTS else HalfTU; |
| 579 | |
| 580 | all: T, |
| 581 | s: if (native_endian == .little) |
| 582 | extern struct { low: HalfT, high: HalfT } |
| 583 | else |
| 584 | extern struct { high: HalfT, low: HalfT }, |
| 585 | }; |
| 586 | } |
| 587 | |
| 588 | pub fn __negsi2(a: i32) callconv(.c) i32 { |
| 589 | return negXi2(i32, a); |
| 590 | } |
| 591 | |
| 592 | pub fn __negdi2(a: i64) callconv(.c) i64 { |
| 593 | return negXi2(i64, a); |
| 594 | } |
| 595 | |
| 596 | pub fn __negti2(a: i128) callconv(.c) i128 { |
| 597 | return negXi2(i128, a); |
| 598 | } |
| 599 | |
| 600 | inline fn negXi2(comptime T: type, a: T) T { |
| 601 | return -a; |
| 602 | } |
| 603 | |
| 604 | fn memsetSmallPowerOf2(d: [*]u8, b: u8, comptime size: usize) void { |
| 605 | @disableIntrinsics(); |
| 606 | |
| 607 | if (size > @sizeOf(usize)) { |
| 608 | d[0..size].* = @splat(b); |
| 609 | } else { |
| 610 | const T = @Int(.unsigned, 8 * size); |
| 611 | var splatted: T = 0; // Setting this to undefined causes a memset call and thus infinite recursion in Debug test-compiler-rt. |
| 612 | @as(*[size]u8, @ptrCast(&splatted)).* = @splat(b); |
| 613 | @as(*align(1) T, @ptrCast(d)).* = splatted; |
| 614 | } |
| 615 | } |
| 616 | |
| 617 | fn shortMemset( |
| 618 | log_min: comptime_int, |
| 619 | log_max: comptime_int, |
| 620 | d: [*]u8, |
| 621 | b: u8, |
| 622 | len: usize, |
| 623 | ) void { |
| 624 | @disableIntrinsics(); |
| 625 | |
| 626 | if (log_min + 1 != log_max) { |
| 627 | const mid = (log_min + log_max) / 2; |
| 628 | if (len > 1 << mid) { |
| 629 | shortMemset(mid, log_max, d, b, len); |
| 630 | } else { |
| 631 | shortMemset(log_min, mid, d, b, len); |
| 632 | } |
| 633 | } else { |
| 634 | const size = 1 << log_min; |
| 635 | |
| 636 | memsetSmallPowerOf2(d, b, size); |
| 637 | memsetSmallPowerOf2(d + len - size, b, size); |
| 638 | } |
| 639 | } |
| 640 | |
| 641 | fn fastMemset(dest: ?[*]u8, c: c_int, len: usize) callconv(.c) ?[*]u8 { |
| 642 | @disableIntrinsics(); |
| 643 | |
| 644 | const b: u8 = @truncate(@as(c_uint, @bitCast(c))); |
| 645 | const n = std.simd.suggestVectorLength(u8) orelse @sizeOf(usize); |
| 646 | |
| 647 | const d = dest.?; |
| 648 | |
| 649 | if (len > 2 * n) { |
| 650 | memsetSmallPowerOf2(d, b, n); |
| 651 | |
| 652 | const begin_aligned = std.mem.alignBackward(usize, @intFromPtr(d) + n, n); |
| 653 | const end_aligned = std.mem.alignForward(usize, @intFromPtr(d) + len - n, n); |
| 654 | |
| 655 | const aligned_ptr: [*]align(n) u8 = @ptrFromInt(begin_aligned); |
| 656 | |
| 657 | var i: usize = 0; |
| 658 | while (true) { |
| 659 | memsetSmallPowerOf2(aligned_ptr + n * i, b, n); |
| 660 | |
| 661 | i += 1; |
| 662 | if (i == @divExact(end_aligned - begin_aligned, n)) |
| 663 | break; |
| 664 | } |
| 665 | |
| 666 | memsetSmallPowerOf2(d + len - n, b, n); |
| 667 | } else { |
| 668 | if (len == 0) return dest; |
| 669 | |
| 670 | shortMemset(0, @ctz(@as(usize, 2 * n)), d, b, len); |
| 671 | } |
| 672 | |
| 673 | return dest; |
| 674 | } |
| 675 | |
| 676 | fn smallMemset(dest: ?[*]u8, c: c_int, len: usize) callconv(.c) ?[*]u8 { |
| 677 | @disableIntrinsics(); |
| 678 | |
| 679 | const b: u8 = @truncate(@as(c_uint, @bitCast(c))); |
| 680 | |
| 681 | if (len != 0) { |
| 682 | var d = dest.?; |
| 683 | var n = len; |
| 684 | while (true) { |
| 685 | d[0] = b; |
| 686 | n -= 1; |
| 687 | if (n == 0) break; |
| 688 | d += 1; |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | return dest; |
| 693 | } |
| 694 | |
| 695 | pub const memset = if (builtin.optimize == .small) |
| 696 | smallMemset |
| 697 | else |
| 698 | fastMemset; |
| 699 | |
| 700 | pub fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.c) c_int { |
| 701 | @setRuntimeSafety(false); |
| 702 | |
| 703 | var index: usize = 0; |
| 704 | while (index != n) : (index += 1) { |
| 705 | if (vl[index] != vr[index]) { |
| 706 | return 1; |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | return 0; |
| 711 | } |
| 712 | |
| 713 | test "bcmp" { |
| 714 | const base_arr = &[_]u8{ 1, 1, 1 }; |
| 715 | const arr1 = &[_]u8{ 1, 1, 1 }; |
| 716 | const arr2 = &[_]u8{ 1, 0, 1 }; |
| 717 | const arr3 = &[_]u8{ 1, 2, 1 }; |
| 718 | |
| 719 | try std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); |
| 720 | try std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0); |
| 721 | try std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0); |
| 722 | } |
| 723 | |
| 724 | test { |
| 725 | _ = @import("compiler_rt/negsi2_test.zig"); |
| 726 | _ = @import("compiler_rt/negdi2_test.zig"); |
| 727 | _ = @import("compiler_rt/negti2_test.zig"); |
| 728 | } |