1const builtin = @import("builtin");
2const compiler_rt = @This();
3const ofmt_c = builtin.object_format == .c;
4const native_endian = builtin.cpu.arch.endian();
5
6const 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.
10pub const panic = if (test_safety)
11 std.debug.FullPanic(std.debug.defaultPanic)
12else
13 std.debug.no_panic;
14
15pub const std_options_debug_threaded_io: ?*std.Io.Threaded = if (builtin.is_test)
16 std.Io.Threaded.global_single_threaded
17else
18 null;
19
20pub const std_options_debug_io: std.Io = if (builtin.is_test)
21 std.Io.Threaded.global_single_threaded.io()
22else
23 unreachable;
24
25pub 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
32pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test)
33 .internal
34else if (ofmt_c)
35 .strong
36else
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.
42pub const visibility: std.builtin.SymbolVisibility = if (linkage == .internal or builtin.link_mode == .dynamic)
43 .default
44else
45 .hidden;
46
47pub const test_safety = switch (builtin.zig_backend) {
48 .stage2_aarch64 => false,
49 else => builtin.is_test,
50};
51
52comptime {
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
185var _fltused: c_int = 1;
186
187fn strlen(s: [*:0]const c_char) callconv(.c) usize {
188 return std.mem.len(s);
189}
190
191fn 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
200test "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
215pub 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
226pub 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.
242pub 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.
254pub 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
265pub const want_ppc_abi = builtin.cpu.arch.isPowerPC();
266
267pub 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
287pub 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
306pub const want_sparc64_abi = builtin.cpu.arch == .sparc64;
307pub const want_sparc32_abi = builtin.cpu.arch == .sparc;
308
309/// For operations converting between `f16` and another floating point type.
310pub 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}
324pub const @"f16" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 16)) {
325 .hard => hardFloatAbi(f16),
326 .soft => softFloatAbi(f16),
327};
328pub const @"f32" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 32)) {
329 .hard => hardFloatAbi(f32),
330 .soft => softFloatAbi(f32),
331};
332pub const @"f64" = switch (std.zig.target.compilerRtFloatAbi(&builtin.target, 64)) {
333 .hard => hardFloatAbi(f64),
334 .soft => softFloatAbi(f64),
335};
336pub 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};
352pub 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};
371fn 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}
383fn 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}
395fn 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
407pub fn Complex(comptime Float: type) type {
408 return struct { real: Float, imag: Float };
409}
410
411pub 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
525pub 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
534pub 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
543fn __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
547fn __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
551fn __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
555fn __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
559fn __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
563fn __aeabi_fneg(a: f32) callconv(.{ .arm_aapcs = .{} }) f32 {
564 return fneg(a);
565}
566
567fn __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.
573pub 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
588pub fn __negsi2(a: i32) callconv(.c) i32 {
589 return negXi2(i32, a);
590}
591
592pub fn __negdi2(a: i64) callconv(.c) i64 {
593 return negXi2(i64, a);
594}
595
596pub fn __negti2(a: i128) callconv(.c) i128 {
597 return negXi2(i128, a);
598}
599
600inline fn negXi2(comptime T: type, a: T) T {
601 return -a;
602}
603
604fn 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
617fn 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
641fn 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
676fn 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
695pub const memset = if (builtin.optimize == .small)
696 smallMemset
697else
698 fastMemset;
699
700pub 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
713test "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
724test {
725 _ = @import("compiler_rt/negsi2_test.zig");
726 _ = @import("compiler_rt/negdi2_test.zig");
727 _ = @import("compiler_rt/negti2_test.zig");
728}