@@ -101,21 +101,26 @@ const zva_enabled = builtin.cpu.arch == .aarch64 and
101101/// plain vector loop measures faster. Empirically mesaured.
102102const zva_threshold = 16384 ;
103103
104- /// Matches compiler_rt's memset signature (lib/compiler_rt.zig) .
105- fn memset (dest : ? [* ]u8 , c : u8 , len : usize ) callconv (.c ) ? [* ]u8 {
104+ /// Matches the C memset ABI .
105+ fn memset (dest : ? [* ]u8 , c : c_int , len : usize ) callconv (.c ) ? [* ]u8 {
106106 @setRuntimeSafety (false );
107107
108108 if (len == 0 ) return dest ;
109109 const d = dest .? ;
110110
111+ // Only the low byte of `c` is used. The C ABI allows a "int"
112+ // as the c parameter but this behavior of just grabbing the
113+ // low byte seems consistent across implementations.
114+ const byte : u8 = @truncate (@as (c_uint , @bitCast (c )));
115+
111116 // Large path: full-width vector stores.
112117 if (len >= vec_bytes ) {
113118 // Very large zero fills: zero whole cachelines with `dc zva`
114119 // instead. Only implemented for the (universal in practice)
115120 // 64-byte block size; anything else falls through to the
116121 // vector loop.
117122 if (comptime zva_enabled ) {
118- if (len >= zva_threshold and c == 0 ) zva : {
123+ if (len >= zva_threshold and byte == 0 ) zva : {
119124 if (zvaSize () != 64 ) break :zva ;
120125 const splat64 : @Vector (64 , u8 ) = @splat (0 );
121126
@@ -148,7 +153,7 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
148153 }
149154 }
150155
151- const splat : @Vector (vec_bytes , u8 ) = @splat (c );
156+ const splat : @Vector (vec_bytes , u8 ) = @splat (byte );
152157
153158 // Fill [0, N) where N is len rounded down to a multiple of
154159 // vec_bytes.
@@ -188,7 +193,7 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
188193 // pair of 64-byte stores covers len <= 128. Only emitted
189194 // when vec_bytes > 64 (e.g. AVX-512), otherwise the
190195 // vector loop above already handled these lengths.
191- const splat64 : @Vector (64 , u8 ) = @splat (c );
196+ const splat64 : @Vector (64 , u8 ) = @splat (byte );
192197 d [0.. 64].* = splat64 ;
193198 d [len - 64 .. ][0.. 64].* = splat64 ;
194199 return dest ;
@@ -200,7 +205,7 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
200205 // second pair extends the covered prefix to [0..32] and the
201206 // covered suffix to [len-32..len], which meet or overlap in
202207 // the middle.
203- const splat16 : @Vector (16 , u8 ) = @splat (c );
208+ const splat16 : @Vector (16 , u8 ) = @splat (byte );
204209 d [0.. 16].* = splat16 ;
205210 d [len - 16 .. ][0.. 16].* = splat16 ;
206211 if (len > 32 ) {
@@ -211,14 +216,14 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
211216 }
212217 if (len >= 8 ) {
213218 // 8 <= len <= 15: one 8-byte pair (covers len <= 16).
214- const splat8 : @Vector (8 , u8 ) = @splat (c );
219+ const splat8 : @Vector (8 , u8 ) = @splat (byte );
215220 d [0.. 8].* = splat8 ;
216221 d [len - 8 .. ][0.. 8].* = splat8 ;
217222 return dest ;
218223 }
219224 if (len >= 4 ) {
220225 // 4 <= len <= 7: one 4-byte pair (covers len <= 8).
221- const splat4 : @Vector (4 , u8 ) = @splat (c );
226+ const splat4 : @Vector (4 , u8 ) = @splat (byte );
222227 d [0.. 4].* = splat4 ;
223228 d [len - 4 .. ][0.. 4].* = splat4 ;
224229 return dest ;
@@ -231,7 +236,7 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.c) ?[*]u8 {
231236 // statically known.
232237 var i : usize = 0 ;
233238 while (i < len ) : (i += 1 ) {
234- d [i ] = c ;
239+ d [i ] = byte ;
235240 asm volatile ("" ::: .{ .memory = true });
236241 }
237242 return dest ;
@@ -286,6 +291,17 @@ test memset {
286291 try testing .expectEqual (0xAA , one [0 ]);
287292}
288293
294+ test "memset truncates C int fill value" {
295+ const testing = std .testing ;
296+
297+ var buf : [4 ]u8 = undefined ;
298+ _ = memset (& buf , -1 , buf .len );
299+ for (buf ) | b | try testing .expectEqual (0xFF , b );
300+
301+ _ = memset (& buf , 0x1234 , buf .len );
302+ for (buf ) | b | try testing .expectEqual (0x34 , b );
303+ }
304+
289305test "memset large zero fills (dc zva path)" {
290306 const testing = std .testing ;
291307 const alloc = testing .allocator ;
0 commit comments