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 {...@@ -302,7 +302,6 @@ comptime {
302 _ = @import("compiler_rt/memcpy.zig");302 _ = @import("compiler_rt/memcpy.zig");
303 if (!ofmt_c) {303 if (!ofmt_c) {
304 symbol(&memset, "memset");304 symbol(&memset, "memset");
305 symbol(&__memset, "__memset");
306 }305 }
307 _ = @import("compiler_rt/memmove.zig");306 _ = @import("compiler_rt/memmove.zig");
308 symbol(&memcmp, "memcmp");307 symbol(&memcmp, "memcmp");
...@@ -650,14 +649,80 @@ inline fn negXi2(comptime T: type, a: T) T {...@@ -650,14 +649,80 @@ inline fn negXi2(comptime T: type, a: T) T {
650 return -a;649 return -a;
651}650}
652651
653pub fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {652fn memsetSmallPowerOf2(d: [*]u8, b: u8, comptime size: usize) void {
654 @setRuntimeSafety(false);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
656 if (len != 0) {721 if (len != 0) {
657 var d = dest.?;722 var d = dest.?;
658 var n = len;723 var n = len;
659 while (true) {724 while (true) {
660 d[0] = c;725 d[0] = b;
661 n -= 1;726 n -= 1;
662 if (n == 0) break;727 if (n == 0) break;
663 d += 1;728 d += 1;
...@@ -667,11 +732,10 @@ pub fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {...@@ -667,11 +732,10 @@ pub fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
667 return dest;732 return dest;
668}733}
669734
670pub fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.c) ?[*]u8 {735pub const memset = if (builtin.optimize == .small)
671 if (dest_n < n)736 smallMemset
672 @panic("buffer overflow");737else
673 return memset(dest, c, n);738 fastMemset;
674}
675739
676pub fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.c) c_int {740pub fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.c) c_int {
677 @setRuntimeSafety(false);741 @setRuntimeSafety(false);
lib/compiler_rt/ssp.zig+2-2
...@@ -17,7 +17,7 @@ const compiler_rt = @import("../compiler_rt.zig");...@@ -17,7 +17,7 @@ const compiler_rt = @import("../compiler_rt.zig");
17const symbol = compiler_rt.symbol;17const symbol = compiler_rt.symbol;
18const builtin = @import("builtin");18const 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;
21extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;21extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;
22extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.c) ?[*]u8;22extern 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...@@ -138,7 +138,7 @@ fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callc
138 return memmove(dest, src, n);138 return memmove(dest, src, n);
139}139}
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 {
142 if (dest_n < n) __chk_fail();142 if (dest_n < n) __chk_fail();
143 return memset(dest, c, n);143 return memset(dest, c, n);
144}144}
test/standalone/compiler_rt_panic/main.c+2-2
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1#include <stddef.h> 1#include <stddef.h>
2 2
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);
4 4
5char foo[128]; 5char foo[128];
6 6
7int main() { 7int main() {
8 __memset(&foo[0], 0xff, 128, 128); 8 __memset_chk(&foo[0], 0xff, 128, 128);
9 return foo[64]; 9 return foo[64];
10} 10}
11 11