authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-26 12:49:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-28 13:24:42-07:00
log00b690540e561391d17c65e45f818db6be8fecec
tree8e1018f7e4824ef36bba99d5defd1f291b4a5c1e
parentfd6200eda6d4fe19c34a59430a88a9ce38d6d7a4

llvm backend: fix lowering of memset

The bitcast of ABI size 1 elements was problematic for some types.

4 files changed, 141 insertions(+), 92 deletions(-)

src/codegen/llvm.zig+12-2
...@@ -7939,11 +7939,15 @@ pub const FuncGen = struct {...@@ -7939,11 +7939,15 @@ pub const FuncGen = struct {
7939 return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, "");7939 return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, "");
7940 }7940 }
79417941
7942 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7942 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !*llvm.Value {
7943 const ty_op = self.air.instructions.items(.data)[inst].ty_op;7943 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7944 const operand_ty = self.air.typeOf(ty_op.operand);7944 const operand_ty = self.air.typeOf(ty_op.operand);
7945 const inst_ty = self.air.typeOfIndex(inst);7945 const inst_ty = self.air.typeOfIndex(inst);
7946 const operand = try self.resolveInst(ty_op.operand);7946 const operand = try self.resolveInst(ty_op.operand);
7947 return self.bitCast(operand, operand_ty, inst_ty);
7948 }
7949
7950 fn bitCast(self: *FuncGen, operand: *llvm.Value, operand_ty: Type, inst_ty: Type) !*llvm.Value {
7947 const operand_is_ref = isByRef(operand_ty);7951 const operand_is_ref = isByRef(operand_ty);
7948 const result_is_ref = isByRef(inst_ty);7952 const result_is_ref = isByRef(inst_ty);
7949 const llvm_dest_ty = try self.dg.lowerType(inst_ty);7953 const llvm_dest_ty = try self.dg.lowerType(inst_ty);
...@@ -7954,6 +7958,12 @@ pub const FuncGen = struct {...@@ -7954,6 +7958,12 @@ pub const FuncGen = struct {
7954 return operand;7958 return operand;
7955 }7959 }
79567960
7961 if (llvm_dest_ty.getTypeKind() == .Integer and
7962 operand.typeOf().getTypeKind() == .Integer)
7963 {
7964 return self.builder.buildZExtOrBitCast(operand, llvm_dest_ty, "");
7965 }
7966
7957 if (operand_ty.zigTypeTag() == .Int and inst_ty.isPtrAtRuntime()) {7967 if (operand_ty.zigTypeTag() == .Int and inst_ty.isPtrAtRuntime()) {
7958 return self.builder.buildIntToPtr(operand, llvm_dest_ty, "");7968 return self.builder.buildIntToPtr(operand, llvm_dest_ty, "");
7959 }7969 }
...@@ -8442,7 +8452,7 @@ pub const FuncGen = struct {...@@ -8442,7 +8452,7 @@ pub const FuncGen = struct {
84428452
8443 if (elem_abi_size == 1) {8453 if (elem_abi_size == 1) {
8444 // In this case we can take advantage of LLVM's intrinsic.8454 // In this case we can take advantage of LLVM's intrinsic.
8445 const fill_byte = self.builder.buildBitCast(value, u8_llvm_ty, "");8455 const fill_byte = try self.bitCast(value, elem_ty, Type.u8);
8446 const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);8456 const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
8447 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());8457 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8448 return null;8458 return null;
test/behavior.zig+1
...@@ -177,6 +177,7 @@ test {...@@ -177,6 +177,7 @@ test {
177 _ = @import("behavior/math.zig");177 _ = @import("behavior/math.zig");
178 _ = @import("behavior/maximum_minimum.zig");178 _ = @import("behavior/maximum_minimum.zig");
179 _ = @import("behavior/member_func.zig");179 _ = @import("behavior/member_func.zig");
180 _ = @import("behavior/memset.zig");
180 _ = @import("behavior/merge_error_sets.zig");181 _ = @import("behavior/merge_error_sets.zig");
181 _ = @import("behavior/muladd.zig");182 _ = @import("behavior/muladd.zig");
182 _ = @import("behavior/namespace_depends_on_compile_var.zig");183 _ = @import("behavior/namespace_depends_on_compile_var.zig");
test/behavior/basic.zig-90
...@@ -353,96 +353,6 @@ fn f2(x: bool) []const u8 {...@@ -353,96 +353,6 @@ fn f2(x: bool) []const u8 {
353 return (if (x) &fA else &fB)();353 return (if (x) &fA else &fB)();
354}354}
355355
356test "@memset on array pointers" {
357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
359 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
360 if (builtin.zig_backend == .stage2_wasm) {
361 // TODO: implement memset when element ABI size > 1
362 return error.SkipZigTest;
363 }
364
365 try testMemsetArray();
366 try comptime testMemsetArray();
367}
368
369fn testMemsetArray() !void {
370 {
371 // memset array to non-undefined, ABI size == 1
372 var foo: [20]u8 = undefined;
373 @memset(&foo, 'A');
374 try expect(foo[0] == 'A');
375 try expect(foo[11] == 'A');
376 try expect(foo[19] == 'A');
377 }
378 {
379 // memset array to non-undefined, ABI size > 1
380 var foo: [20]u32 = undefined;
381 @memset(&foo, 1234);
382 try expect(foo[0] == 1234);
383 try expect(foo[11] == 1234);
384 try expect(foo[19] == 1234);
385 }
386}
387
388test "@memset on slices" {
389 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
390 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
391 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
392 if (builtin.zig_backend == .stage2_wasm) {
393 // TODO: implement memset when element ABI size > 1
394 // TODO: implement memset on slices
395 return error.SkipZigTest;
396 }
397
398 try testMemsetSlice();
399 try comptime testMemsetSlice();
400}
401
402fn testMemsetSlice() !void {
403 {
404 // memset slice to non-undefined, ABI size == 1
405 var array: [20]u8 = undefined;
406 var len = array.len;
407 var slice = array[0..len];
408 @memset(slice, 'A');
409 try expect(slice[0] == 'A');
410 try expect(slice[11] == 'A');
411 try expect(slice[19] == 'A');
412 }
413 {
414 // memset slice to non-undefined, ABI size > 1
415 var array: [20]u32 = undefined;
416 var len = array.len;
417 var slice = array[0..len];
418 @memset(slice, 1234);
419 try expect(slice[0] == 1234);
420 try expect(slice[11] == 1234);
421 try expect(slice[19] == 1234);
422 }
423}
424
425test "memcpy and memset intrinsics" {
426 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
428 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
429
430 try testMemcpyMemset();
431 try comptime testMemcpyMemset();
432}
433
434fn testMemcpyMemset() !void {
435 var foo: [20]u8 = undefined;
436 var bar: [20]u8 = undefined;
437
438 @memset(&foo, 'A');
439 @memcpy(&bar, &foo);
440
441 try expect(bar[0] == 'A');
442 try expect(bar[11] == 'A');
443 try expect(bar[19] == 'A');
444}
445
446test "variable is allowed to be a pointer to an opaque type" {356test "variable is allowed to be a pointer to an opaque type" {
447 var x: i32 = 1234;357 var x: i32 = 1234;
448 _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));358 _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));
test/behavior/memset.zig created+128
...@@ -0,0 +1,128 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "@memset on array pointers" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_wasm) {
10 // TODO: implement memset when element ABI size > 1
11 return error.SkipZigTest;
12 }
13
14 try testMemsetArray();
15 try comptime testMemsetArray();
16}
17
18fn testMemsetArray() !void {
19 {
20 // memset array to non-undefined, ABI size == 1
21 var foo: [20]u8 = undefined;
22 @memset(&foo, 'A');
23 try expect(foo[0] == 'A');
24 try expect(foo[11] == 'A');
25 try expect(foo[19] == 'A');
26 }
27 {
28 // memset array to non-undefined, ABI size > 1
29 var foo: [20]u32 = undefined;
30 @memset(&foo, 1234);
31 try expect(foo[0] == 1234);
32 try expect(foo[11] == 1234);
33 try expect(foo[19] == 1234);
34 }
35}
36
37test "@memset on slices" {
38 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
41 if (builtin.zig_backend == .stage2_wasm) {
42 // TODO: implement memset when element ABI size > 1
43 // TODO: implement memset on slices
44 return error.SkipZigTest;
45 }
46
47 try testMemsetSlice();
48 try comptime testMemsetSlice();
49}
50
51fn testMemsetSlice() !void {
52 {
53 // memset slice to non-undefined, ABI size == 1
54 var array: [20]u8 = undefined;
55 var len = array.len;
56 var slice = array[0..len];
57 @memset(slice, 'A');
58 try expect(slice[0] == 'A');
59 try expect(slice[11] == 'A');
60 try expect(slice[19] == 'A');
61 }
62 {
63 // memset slice to non-undefined, ABI size > 1
64 var array: [20]u32 = undefined;
65 var len = array.len;
66 var slice = array[0..len];
67 @memset(slice, 1234);
68 try expect(slice[0] == 1234);
69 try expect(slice[11] == 1234);
70 try expect(slice[19] == 1234);
71 }
72}
73
74test "memset with bool element" {
75 var buf: [5]bool = undefined;
76 @memset(&buf, true);
77 try expect(buf[2]);
78 try expect(buf[4]);
79}
80
81test "memset with 1-byte struct element" {
82 const S = struct { x: bool };
83 var buf: [5]S = undefined;
84 @memset(&buf, .{ .x = true });
85 try expect(buf[2].x);
86 try expect(buf[4].x);
87}
88
89test "memset with 1-byte array element" {
90 const A = [1]bool;
91 var buf: [5]A = undefined;
92 @memset(&buf, .{true});
93 try expect(buf[2][0]);
94 try expect(buf[4][0]);
95}
96
97test "memset with large array element" {
98 const A = [128]u64;
99 var buf: [5]A = undefined;
100 var runtime_known_element = [_]u64{0} ** 128;
101 @memset(&buf, runtime_known_element);
102 for (buf[0]) |elem| try expect(elem == 0);
103 for (buf[1]) |elem| try expect(elem == 0);
104 for (buf[2]) |elem| try expect(elem == 0);
105 for (buf[3]) |elem| try expect(elem == 0);
106 for (buf[4]) |elem| try expect(elem == 0);
107}
108
109test "memcpy and memset intrinsics" {
110 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
111 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
112 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
113
114 try testMemcpyMemset();
115 try comptime testMemcpyMemset();
116}
117
118fn testMemcpyMemset() !void {
119 var foo: [20]u8 = undefined;
120 var bar: [20]u8 = undefined;
121
122 @memset(&foo, 'A');
123 @memcpy(&bar, &foo);
124
125 try expect(bar[0] == 'A');
126 try expect(bar[11] == 'A');
127 try expect(bar[19] == 'A');
128}