authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-21 20:51:42+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-21 20:51:42+02:00
log5faa797309fbe7739b4a211b50f6954f9175f763
tree4663e4439b421b5cb54024d62d5ecd8e0624eb93
parent5fe9620b779d27fad84c3f3c11d038aa68faba2c
parent2c894ce206036cb6fa155dd431444fa4f35d5cde

Merge pull request 'compiler_rt: optimize memset' (#35754) from rpkak/zig:memset into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35754 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

3 files changed, 77 insertions(+), 13 deletions(-)

lib/compiler_rt.zig+73-9
......@@ -302,7 +302,6 @@ comptime {
302302 _ = @import("compiler_rt/memcpy.zig");
303303 if (!ofmt_c) {
304304 symbol(&memset, "memset");
305 symbol(&__memset, "__memset");
306305 }
307306 _ = @import("compiler_rt/memmove.zig");
308307 symbol(&memcmp, "memcmp");
......@@ -650,14 +649,80 @@ inline fn negXi2(comptime T: type, a: T) T {
650649 return -a;
651650}
652651
653pub fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
654 @setRuntimeSafety(false);
652fn memsetSmallPowerOf2(d: [*]u8, b: u8, comptime size: usize) void {
653 if (size > @sizeOf(usize)) {
654 d[0..size].* = @splat(b);
655 } else {
656 const T = @Int(.unsigned, 8 * size);
657 var splatted: T = 0; // Setting this to undefined causes a memset call and thus infinite recursion in Debug test-compiler-rt.
658 @as(*[size]u8, @ptrCast(&splatted)).* = @splat(b);
659 @as(*align(1) T, @ptrCast(d)).* = splatted;
660 }
661}
662
663fn shortMemset(
664 log_min: comptime_int,
665 log_max: comptime_int,
666 d: [*]u8,
667 b: u8,
668 len: usize,
669) void {
670 if (log_min + 1 != log_max) {
671 const mid = (log_min + log_max) / 2;
672 if (len > 1 << mid) {
673 shortMemset(mid, log_max, d, b, len);
674 } else {
675 shortMemset(log_min, mid, d, b, len);
676 }
677 } else {
678 const size = 1 << log_min;
679
680 memsetSmallPowerOf2(d, b, size);
681 memsetSmallPowerOf2(d + len - size, b, size);
682 }
683}
684
685fn fastMemset(dest: ?[*]u8, c: c_int, len: usize) callconv(.c) ?[*]u8 {
686 const b: u8 = @truncate(@as(c_uint, @bitCast(c)));
687 const n = std.simd.suggestVectorLength(u8) orelse @sizeOf(usize);
688
689 const d = dest.?;
690
691 if (len > 2 * n) {
692 memsetSmallPowerOf2(d, b, n);
693
694 const begin_aligned = std.mem.alignBackward(usize, @intFromPtr(d) + n, n);
695 const end_aligned = std.mem.alignForward(usize, @intFromPtr(d) + len - n, n);
696
697 const aligned_ptr: [*]align(n) u8 = @ptrFromInt(begin_aligned);
698
699 var i: usize = 0;
700 while (true) {
701 memsetSmallPowerOf2(aligned_ptr + n * i, b, n);
702
703 i += 1;
704 if (i == @divExact(end_aligned - begin_aligned, n))
705 break;
706 }
707
708 memsetSmallPowerOf2(d + len - n, b, n);
709 } else {
710 if (len == 0) return dest;
711
712 shortMemset(0, @ctz(@as(usize, 2 * n)), d, b, len);
713 }
714
715 return dest;
716}
717
718fn smallMemset(dest: ?[*]u8, c: c_int, len: usize) callconv(.c) ?[*]u8 {
719 const b: u8 = @truncate(@as(c_uint, @bitCast(c)));
655720
656721 if (len != 0) {
657722 var d = dest.?;
658723 var n = len;
659724 while (true) {
660 d[0] = c;
725 d[0] = b;
661726 n -= 1;
662727 if (n == 0) break;
663728 d += 1;
......@@ -667,11 +732,10 @@ pub fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
667732 return dest;
668733}
669734
670pub fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {
671 if (dest_n < n)
672 @panic("buffer overflow");
673 return memset(dest, c, n);
674}
735pub const memset = if (builtin.optimize == .small)
736 smallMemset
737else
738 fastMemset;
675739
676740pub fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.c) c_int {
677741 @setRuntimeSafety(false);
lib/compiler_rt/ssp.zig+2-2
......@@ -17,7 +17,7 @@ const compiler_rt = @import("../compiler_rt.zig");
1717const symbol = compiler_rt.symbol;
1818const builtin = @import("builtin");
1919
20extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.c) ?[*]u8;
20extern fn memset(dest: ?[*]u8, c: c_int, n: usize) callconv(.c) ?[*]u8;
2121extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;
2222extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;
2323
......@@ -138,7 +138,7 @@ fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callc
138138 return memmove(dest, src, n);
139139}
140140
141fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {
141fn __memset_chk(dest: ?[*]u8, c: c_int, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {
142142 if (dest_n < n) __chk_fail();
143143 return memset(dest, c, n);
144144}
test/standalone/compiler_rt_panic/main.c+2-2
......@@ -1,11 +1,11 @@
11#include <stddef.h>
22
3void* __memset(void* dest, char c, size_t n, size_t dest_n);
3void *__memset_chk(void *dest, int c, size_t n, size_t dest_n);
44
55char foo[128];
66
77int main() {
8 __memset(&foo[0], 0xff, 128, 128);
8 __memset_chk(&foo[0], 0xff, 128, 128);
99 return foo[64];
1010}
1111