| author | |
| committer | |
| log | 057c950093085e392fcdd6d6c8e7fb4356dd9959 |
| tree | f47a5292c6a6cba0800a0ab6ee8ed6cfdfd7c6bc |
| parent | 25d11283b7b79edd383163cb2a72bd79dcf02dba |
Also introduce memset_safe AIR tag and support it in C backend and LLVM
backend.14 files changed, 191 insertions(+), 30 deletions(-)
src/Air.zig+9| ... | @@ -638,7 +638,14 @@ pub const Inst = struct { | ... | @@ -638,7 +638,14 @@ pub const Inst = struct { |
| 638 | /// The element type may be any type, and the slice may have any alignment. | 638 | /// The element type may be any type, and the slice may have any alignment. |
| 639 | /// Result type is always void. | 639 | /// Result type is always void. |
| 640 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value. | 640 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value. |
| 641 | /// The element value may be undefined, in which case the destination | ||
| 642 | /// memory region has undefined bytes after this function executes. In | ||
| 643 | /// such case ignoring this instruction is legal lowering. | ||
| 641 | memset, | 644 | memset, |
| 645 | /// Same as `memset`, except if the element value is undefined, the memory region | ||
| 646 | /// should be filled with 0xaa bytes, and any other safety metadata such as Valgrind | ||
| 647 | /// integrations should be notified of this memory region being undefined. | ||
| 648 | memset_safe, | ||
| 642 | /// Given dest pointer and source pointer, copy elements from source to dest. | 649 | /// Given dest pointer and source pointer, copy elements from source to dest. |
| 643 | /// Dest pointer is either a slice or a pointer to array. | 650 | /// Dest pointer is either a slice or a pointer to array. |
| 644 | /// The dest element type may be any type. | 651 | /// The dest element type may be any type. |
| ... | @@ -1236,6 +1243,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -1236,6 +1243,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1236 | .atomic_store_release, | 1243 | .atomic_store_release, |
| 1237 | .atomic_store_seq_cst, | 1244 | .atomic_store_seq_cst, |
| 1238 | .memset, | 1245 | .memset, |
| 1246 | .memset_safe, | ||
| 1239 | .memcpy, | 1247 | .memcpy, |
| 1240 | .set_union_tag, | 1248 | .set_union_tag, |
| 1241 | .prefetch, | 1249 | .prefetch, |
| ... | @@ -1415,6 +1423,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool { | ... | @@ -1415,6 +1423,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool { |
| 1415 | .errunion_payload_ptr_set, | 1423 | .errunion_payload_ptr_set, |
| 1416 | .set_union_tag, | 1424 | .set_union_tag, |
| 1417 | .memset, | 1425 | .memset, |
| 1426 | .memset_safe, | ||
| 1418 | .memcpy, | 1427 | .memcpy, |
| 1419 | .cmpxchg_weak, | 1428 | .cmpxchg_weak, |
| 1420 | .cmpxchg_strong, | 1429 | .cmpxchg_strong, |
src/Liveness.zig+2| ... | @@ -305,6 +305,7 @@ pub fn categorizeOperand( | ... | @@ -305,6 +305,7 @@ pub fn categorizeOperand( |
| 305 | .atomic_store_seq_cst, | 305 | .atomic_store_seq_cst, |
| 306 | .set_union_tag, | 306 | .set_union_tag, |
| 307 | .memset, | 307 | .memset, |
| 308 | .memset_safe, | ||
| 308 | .memcpy, | 309 | .memcpy, |
| 309 | => { | 310 | => { |
| 310 | const o = air_datas[inst].bin_op; | 311 | const o = air_datas[inst].bin_op; |
| ... | @@ -980,6 +981,7 @@ fn analyzeInst( | ... | @@ -980,6 +981,7 @@ fn analyzeInst( |
| 980 | .min, | 981 | .min, |
| 981 | .max, | 982 | .max, |
| 982 | .memset, | 983 | .memset, |
| 984 | .memset_safe, | ||
| 983 | .memcpy, | 985 | .memcpy, |
| 984 | => { | 986 | => { |
| 985 | const o = inst_datas[inst].bin_op; | 987 | const o = inst_datas[inst].bin_op; |
src/Liveness/Verify.zig+1| ... | @@ -255,6 +255,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { | ... | @@ -255,6 +255,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 255 | .min, | 255 | .min, |
| 256 | .max, | 256 | .max, |
| 257 | .memset, | 257 | .memset, |
| 258 | .memset_safe, | ||
| 258 | .memcpy, | 259 | .memcpy, |
| 259 | => { | 260 | => { |
| 260 | const bin_op = data[inst].bin_op; | 261 | const bin_op = data[inst].bin_op; |
src/Sema.zig+1-1| ... | @@ -21972,7 +21972,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -21972,7 +21972,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 21972 | 21972 | ||
| 21973 | try sema.requireRuntimeBlock(block, src, runtime_src); | 21973 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 21974 | _ = try block.addInst(.{ | 21974 | _ = try block.addInst(.{ |
| 21975 | .tag = .memset, | 21975 | .tag = if (block.wantSafety()) .memset_safe else .memset, |
| 21976 | .data = .{ .bin_op = .{ | 21976 | .data = .{ .bin_op = .{ |
| 21977 | .lhs = dest_ptr, | 21977 | .lhs = dest_ptr, |
| 21978 | .rhs = elem, | 21978 | .rhs = elem, |
src/arch/aarch64/CodeGen.zig+8-2| ... | @@ -775,7 +775,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -775,7 +775,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 775 | .atomic_rmw => try self.airAtomicRmw(inst), | 775 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 776 | .atomic_load => try self.airAtomicLoad(inst), | 776 | .atomic_load => try self.airAtomicLoad(inst), |
| 777 | .memcpy => try self.airMemcpy(inst), | 777 | .memcpy => try self.airMemcpy(inst), |
| 778 | .memset => try self.airMemset(inst), | 778 | .memset => try self.airMemset(inst, false), |
| 779 | .memset_safe => try self.airMemset(inst, true), | ||
| 779 | .set_union_tag => try self.airSetUnionTag(inst), | 780 | .set_union_tag => try self.airSetUnionTag(inst), |
| 780 | .get_union_tag => try self.airGetUnionTag(inst), | 781 | .get_union_tag => try self.airGetUnionTag(inst), |
| 781 | .clz => try self.airClz(inst), | 782 | .clz => try self.airClz(inst), |
| ... | @@ -5975,8 +5976,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr | ... | @@ -5975,8 +5976,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr |
| 5975 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); | 5976 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 5976 | } | 5977 | } |
| 5977 | 5978 | ||
| 5978 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | 5979 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 5979 | _ = inst; | 5980 | _ = inst; |
| 5981 | if (safety) { | ||
| 5982 | // TODO if the value is undef, write 0xaa bytes to dest | ||
| 5983 | } else { | ||
| 5984 | // TODO if the value is undef, don't lower this instruction | ||
| 5985 | } | ||
| 5980 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | 5986 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 5981 | } | 5987 | } |
| 5982 | 5988 |
src/arch/arm/CodeGen.zig+8-2| ... | @@ -759,7 +759,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -759,7 +759,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 759 | .atomic_rmw => try self.airAtomicRmw(inst), | 759 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 760 | .atomic_load => try self.airAtomicLoad(inst), | 760 | .atomic_load => try self.airAtomicLoad(inst), |
| 761 | .memcpy => try self.airMemcpy(inst), | 761 | .memcpy => try self.airMemcpy(inst), |
| 762 | .memset => try self.airMemset(inst), | 762 | .memset => try self.airMemset(inst, false), |
| 763 | .memset_safe => try self.airMemset(inst, true), | ||
| 763 | .set_union_tag => try self.airSetUnionTag(inst), | 764 | .set_union_tag => try self.airSetUnionTag(inst), |
| 764 | .get_union_tag => try self.airGetUnionTag(inst), | 765 | .get_union_tag => try self.airGetUnionTag(inst), |
| 765 | .clz => try self.airClz(inst), | 766 | .clz => try self.airClz(inst), |
| ... | @@ -5921,7 +5922,12 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr | ... | @@ -5921,7 +5922,12 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr |
| 5921 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); | 5922 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 5922 | } | 5923 | } |
| 5923 | 5924 | ||
| 5924 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | 5925 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 5926 | if (safety) { | ||
| 5927 | // TODO if the value is undef, write 0xaa bytes to dest | ||
| 5928 | } else { | ||
| 5929 | // TODO if the value is undef, don't lower this instruction | ||
| 5930 | } | ||
| 5925 | _ = inst; | 5931 | _ = inst; |
| 5926 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | 5932 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 5927 | } | 5933 | } |
src/arch/riscv64/CodeGen.zig+8-2| ... | @@ -589,7 +589,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -589,7 +589,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 589 | .atomic_rmw => try self.airAtomicRmw(inst), | 589 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 590 | .atomic_load => try self.airAtomicLoad(inst), | 590 | .atomic_load => try self.airAtomicLoad(inst), |
| 591 | .memcpy => try self.airMemcpy(inst), | 591 | .memcpy => try self.airMemcpy(inst), |
| 592 | .memset => try self.airMemset(inst), | 592 | .memset => try self.airMemset(inst, false), |
| 593 | .memset_safe => try self.airMemset(inst, true), | ||
| 593 | .set_union_tag => try self.airSetUnionTag(inst), | 594 | .set_union_tag => try self.airSetUnionTag(inst), |
| 594 | .get_union_tag => try self.airGetUnionTag(inst), | 595 | .get_union_tag => try self.airGetUnionTag(inst), |
| 595 | .clz => try self.airClz(inst), | 596 | .clz => try self.airClz(inst), |
| ... | @@ -2421,8 +2422,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr | ... | @@ -2421,8 +2422,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr |
| 2421 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); | 2422 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 2422 | } | 2423 | } |
| 2423 | 2424 | ||
| 2424 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | 2425 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 2425 | _ = inst; | 2426 | _ = inst; |
| 2427 | if (safety) { | ||
| 2428 | // TODO if the value is undef, write 0xaa bytes to dest | ||
| 2429 | } else { | ||
| 2430 | // TODO if the value is undef, don't lower this instruction | ||
| 2431 | } | ||
| 2426 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | 2432 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); |
| 2427 | } | 2433 | } |
| 2428 | 2434 |
src/arch/sparc64/CodeGen.zig+8-2| ... | @@ -605,7 +605,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -605,7 +605,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 605 | .atomic_rmw => try self.airAtomicRmw(inst), | 605 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 606 | .atomic_load => try self.airAtomicLoad(inst), | 606 | .atomic_load => try self.airAtomicLoad(inst), |
| 607 | .memcpy => @panic("TODO try self.airMemcpy(inst)"), | 607 | .memcpy => @panic("TODO try self.airMemcpy(inst)"), |
| 608 | .memset => try self.airMemset(inst), | 608 | .memset => try self.airMemset(inst, false), |
| 609 | .memset_safe => try self.airMemset(inst, true), | ||
| 609 | .set_union_tag => try self.airSetUnionTag(inst), | 610 | .set_union_tag => try self.airSetUnionTag(inst), |
| 610 | .get_union_tag => try self.airGetUnionTag(inst), | 611 | .get_union_tag => try self.airGetUnionTag(inst), |
| 611 | .clz => try self.airClz(inst), | 612 | .clz => try self.airClz(inst), |
| ... | @@ -1764,7 +1765,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1764,7 +1765,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 1764 | return self.finishAirBookkeeping(); | 1765 | return self.finishAirBookkeeping(); |
| 1765 | } | 1766 | } |
| 1766 | 1767 | ||
| 1767 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | 1768 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 1769 | if (safety) { | ||
| 1770 | // TODO if the value is undef, write 0xaa bytes to dest | ||
| 1771 | } else { | ||
| 1772 | // TODO if the value is undef, don't lower this instruction | ||
| 1773 | } | ||
| 1768 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 1774 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1769 | const extra = self.air.extraData(Air.Bin, pl_op.payload); | 1775 | const extra = self.air.extraData(Air.Bin, pl_op.payload); |
| 1770 | 1776 |
src/arch/wasm/CodeGen.zig+2-1| ... | @@ -1883,7 +1883,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1883,7 +1883,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1883 | 1883 | ||
| 1884 | .load => func.airLoad(inst), | 1884 | .load => func.airLoad(inst), |
| 1885 | .loop => func.airLoop(inst), | 1885 | .loop => func.airLoop(inst), |
| 1886 | .memset => func.airMemset(inst), | 1886 | // TODO: elide memset when writing undef without safety |
| 1887 | .memset, .memset_safe => func.airMemset(inst), | ||
| 1887 | .not => func.airNot(inst), | 1888 | .not => func.airNot(inst), |
| 1888 | .optional_payload => func.airOptionalPayload(inst), | 1889 | .optional_payload => func.airOptionalPayload(inst), |
| 1889 | .optional_payload_ptr => func.airOptionalPayloadPtr(inst), | 1890 | .optional_payload_ptr => func.airOptionalPayloadPtr(inst), |
src/arch/x86_64/CodeGen.zig+9-2| ... | @@ -1046,7 +1046,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -1046,7 +1046,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1046 | .atomic_rmw => try self.airAtomicRmw(inst), | 1046 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 1047 | .atomic_load => try self.airAtomicLoad(inst), | 1047 | .atomic_load => try self.airAtomicLoad(inst), |
| 1048 | .memcpy => try self.airMemcpy(inst), | 1048 | .memcpy => try self.airMemcpy(inst), |
| 1049 | .memset => try self.airMemset(inst), | 1049 | .memset => try self.airMemset(inst, false), |
| 1050 | .memset_safe => try self.airMemset(inst, true), | ||
| 1050 | .set_union_tag => try self.airSetUnionTag(inst), | 1051 | .set_union_tag => try self.airSetUnionTag(inst), |
| 1051 | .get_union_tag => try self.airGetUnionTag(inst), | 1052 | .get_union_tag => try self.airGetUnionTag(inst), |
| 1052 | .clz => try self.airClz(inst), | 1053 | .clz => try self.airClz(inst), |
| ... | @@ -8149,7 +8150,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr | ... | @@ -8149,7 +8150,13 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr |
| 8149 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 8150 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 8150 | } | 8151 | } |
| 8151 | 8152 | ||
| 8152 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | 8153 | fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 8154 | if (safety) { | ||
| 8155 | // TODO if the value is undef, write 0xaa bytes to dest | ||
| 8156 | } else { | ||
| 8157 | // TODO if the value is undef, don't lower this instruction | ||
| 8158 | } | ||
| 8159 | |||
| 8153 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 8160 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8154 | 8161 | ||
| 8155 | const dst_ptr = try self.resolveInst(bin_op.lhs); | 8162 | const dst_ptr = try self.resolveInst(bin_op.lhs); |
src/codegen/c.zig+9-4| ... | @@ -2925,7 +2925,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -2925,7 +2925,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2925 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), | 2925 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), |
| 2926 | .atomic_rmw => try airAtomicRmw(f, inst), | 2926 | .atomic_rmw => try airAtomicRmw(f, inst), |
| 2927 | .atomic_load => try airAtomicLoad(f, inst), | 2927 | .atomic_load => try airAtomicLoad(f, inst), |
| 2928 | .memset => try airMemset(f, inst), | 2928 | .memset => try airMemset(f, inst, false), |
| 2929 | .memset_safe => try airMemset(f, inst, true), | ||
| 2929 | .memcpy => try airMemcpy(f, inst), | 2930 | .memcpy => try airMemcpy(f, inst), |
| 2930 | .set_union_tag => try airSetUnionTag(f, inst), | 2931 | .set_union_tag => try airSetUnionTag(f, inst), |
| 2931 | .get_union_tag => try airGetUnionTag(f, inst), | 2932 | .get_union_tag => try airGetUnionTag(f, inst), |
| ... | @@ -6189,7 +6190,7 @@ fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !vo | ... | @@ -6189,7 +6190,7 @@ fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !vo |
| 6189 | } | 6190 | } |
| 6190 | } | 6191 | } |
| 6191 | 6192 | ||
| 6192 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | 6193 | fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { |
| 6193 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 6194 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 6194 | const dest_ty = f.air.typeOf(bin_op.lhs); | 6195 | const dest_ty = f.air.typeOf(bin_op.lhs); |
| 6195 | const dest_slice = try f.resolveInst(bin_op.lhs); | 6196 | const dest_slice = try f.resolveInst(bin_op.lhs); |
| ... | @@ -6201,6 +6202,11 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6201,6 +6202,11 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6201 | const writer = f.object.writer(); | 6202 | const writer = f.object.writer(); |
| 6202 | 6203 | ||
| 6203 | if (val_is_undef) { | 6204 | if (val_is_undef) { |
| 6205 | if (!safety) { | ||
| 6206 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | ||
| 6207 | return .none; | ||
| 6208 | } | ||
| 6209 | |||
| 6204 | try writer.writeAll("memset("); | 6210 | try writer.writeAll("memset("); |
| 6205 | switch (dest_ty.ptrSize()) { | 6211 | switch (dest_ty.ptrSize()) { |
| 6206 | .Slice => { | 6212 | .Slice => { |
| ... | @@ -6242,8 +6248,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6242,8 +6248,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6242 | }, | 6248 | }, |
| 6243 | .One => { | 6249 | .One => { |
| 6244 | const array_ty = dest_ty.childType(); | 6250 | const array_ty = dest_ty.childType(); |
| 6245 | const len = array_ty.arrayLen() * elem_abi_size; | 6251 | try writer.print("{d}", .{array_ty.arrayLen()}); |
| 6246 | try writer.print("{d}", .{len}); | ||
| 6247 | }, | 6252 | }, |
| 6248 | .Many, .C => unreachable, | 6253 | .Many, .C => unreachable, |
| 6249 | } | 6254 | } |
src/codegen/llvm.zig+81-14| ... | @@ -4672,7 +4672,8 @@ pub const FuncGen = struct { | ... | @@ -4672,7 +4672,8 @@ pub const FuncGen = struct { |
| 4672 | .fence => try self.airFence(inst), | 4672 | .fence => try self.airFence(inst), |
| 4673 | .atomic_rmw => try self.airAtomicRmw(inst), | 4673 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 4674 | .atomic_load => try self.airAtomicLoad(inst), | 4674 | .atomic_load => try self.airAtomicLoad(inst), |
| 4675 | .memset => try self.airMemset(inst), | 4675 | .memset => try self.airMemset(inst, false), |
| 4676 | .memset_safe => try self.airMemset(inst, true), | ||
| 4676 | .memcpy => try self.airMemcpy(inst), | 4677 | .memcpy => try self.airMemcpy(inst), |
| 4677 | .set_union_tag => try self.airSetUnionTag(inst), | 4678 | .set_union_tag => try self.airSetUnionTag(inst), |
| 4678 | .get_union_tag => try self.airGetUnionTag(inst), | 4679 | .get_union_tag => try self.airGetUnionTag(inst), |
| ... | @@ -8405,29 +8406,95 @@ pub const FuncGen = struct { | ... | @@ -8405,29 +8406,95 @@ pub const FuncGen = struct { |
| 8405 | return null; | 8406 | return null; |
| 8406 | } | 8407 | } |
| 8407 | 8408 | ||
| 8408 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | 8409 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value { |
| 8409 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 8410 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 8410 | const dest_slice = try self.resolveInst(bin_op.lhs); | 8411 | const dest_slice = try self.resolveInst(bin_op.lhs); |
| 8411 | const ptr_ty = self.air.typeOf(bin_op.lhs); | 8412 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 8412 | const value = try self.resolveInst(bin_op.rhs); | ||
| 8413 | const elem_ty = self.air.typeOf(bin_op.rhs); | 8413 | const elem_ty = self.air.typeOf(bin_op.rhs); |
| 8414 | const target = self.dg.module.getTarget(); | 8414 | const target = self.dg.module.getTarget(); |
| 8415 | const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; | 8415 | const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; |
| 8416 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | 8416 | const dest_ptr_align = ptr_ty.ptrAlignment(target); |
| 8417 | const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty); | ||
| 8418 | const u8_llvm_ty = self.context.intType(8); | 8417 | const u8_llvm_ty = self.context.intType(8); |
| 8419 | const fill_byte = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else b: { | 8418 | const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty); |
| 8420 | if (elem_ty.abiSize(target) != 1) { | 8419 | |
| 8421 | return self.dg.todo("implement @memset for non-byte-sized element type", .{}); | 8420 | if (val_is_undef) { |
| 8421 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | ||
| 8422 | // extra information to LLVM. However, safety makes the difference between using | ||
| 8423 | // 0xaa or actual undefined for the fill byte. | ||
| 8424 | const fill_byte = if (safety) | ||
| 8425 | u8_llvm_ty.constInt(0xaa, .False) | ||
| 8426 | else | ||
| 8427 | u8_llvm_ty.getUndef(); | ||
| 8428 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | ||
| 8429 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr()); | ||
| 8430 | |||
| 8431 | if (safety and self.dg.module.comp.bin_file.options.valgrind) { | ||
| 8432 | self.valgrindMarkUndef(dest_ptr, len); | ||
| 8422 | } | 8433 | } |
| 8423 | break :b self.builder.buildBitCast(value, u8_llvm_ty, ""); | 8434 | return null; |
| 8424 | }; | 8435 | } |
| 8425 | const dest_ptr_align = ptr_ty.ptrAlignment(target); | 8436 | |
| 8426 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr()); | 8437 | const value = try self.resolveInst(bin_op.rhs); |
| 8438 | const elem_abi_size = elem_ty.abiSize(target); | ||
| 8427 | 8439 | ||
| 8428 | if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) { | 8440 | if (elem_abi_size == 1) { |
| 8429 | self.valgrindMarkUndef(dest_ptr, len); | 8441 | // In this case we can take advantage of LLVM's intrinsic. |
| 8442 | const fill_byte = self.builder.buildBitCast(value, u8_llvm_ty, ""); | ||
| 8443 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | ||
| 8444 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr()); | ||
| 8445 | return null; | ||
| 8430 | } | 8446 | } |
| 8447 | |||
| 8448 | // non-byte-sized element. lower with a loop. something like this: | ||
| 8449 | |||
| 8450 | // entry: | ||
| 8451 | // ... | ||
| 8452 | // %end_ptr = getelementptr %ptr, %len | ||
| 8453 | // br loop | ||
| 8454 | // loop: | ||
| 8455 | // %it_ptr = phi body %next_ptr, entry %ptr | ||
| 8456 | // %end = cmp eq %it_ptr, %end_ptr | ||
| 8457 | // cond_br %end body, end | ||
| 8458 | // body: | ||
| 8459 | // store %it_ptr, %value | ||
| 8460 | // %next_ptr = getelementptr %it_ptr, 1 | ||
| 8461 | // br loop | ||
| 8462 | // end: | ||
| 8463 | // ... | ||
| 8464 | const entry_block = self.builder.getInsertBlock(); | ||
| 8465 | const loop_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetLoop"); | ||
| 8466 | const body_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetBody"); | ||
| 8467 | const end_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetEnd"); | ||
| 8468 | |||
| 8469 | const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth()); | ||
| 8470 | const len = switch (ptr_ty.ptrSize()) { | ||
| 8471 | .Slice => self.builder.buildExtractValue(dest_slice, 1, ""), | ||
| 8472 | .One => llvm_usize_ty.constInt(ptr_ty.childType().arrayLen(), .False), | ||
| 8473 | .Many, .C => unreachable, | ||
| 8474 | }; | ||
| 8475 | const elem_llvm_ty = try self.dg.lowerType(elem_ty); | ||
| 8476 | const len_gep = [_]*llvm.Value{len}; | ||
| 8477 | const end_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, dest_ptr, &len_gep, len_gep.len, ""); | ||
| 8478 | _ = self.builder.buildBr(loop_block); | ||
| 8479 | |||
| 8480 | self.builder.positionBuilderAtEnd(loop_block); | ||
| 8481 | const it_ptr = self.builder.buildPhi(self.context.pointerType(0), ""); | ||
| 8482 | const end = self.builder.buildICmp(.NE, it_ptr, end_ptr, ""); | ||
| 8483 | _ = self.builder.buildCondBr(end, body_block, end_block); | ||
| 8484 | |||
| 8485 | self.builder.positionBuilderAtEnd(body_block); | ||
| 8486 | const store_inst = self.builder.buildStore(value, it_ptr); | ||
| 8487 | store_inst.setAlignment(@min(elem_ty.abiAlignment(target), dest_ptr_align)); | ||
| 8488 | const one_gep = [_]*llvm.Value{llvm_usize_ty.constInt(1, .False)}; | ||
| 8489 | const next_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, it_ptr, &one_gep, one_gep.len, ""); | ||
| 8490 | _ = self.builder.buildBr(loop_block); | ||
| 8491 | |||
| 8492 | self.builder.positionBuilderAtEnd(end_block); | ||
| 8493 | |||
| 8494 | const incoming_values: [2]*llvm.Value = .{ next_ptr, dest_ptr }; | ||
| 8495 | const incoming_blocks: [2]*llvm.BasicBlock = .{ body_block, entry_block }; | ||
| 8496 | it_ptr.addIncoming(&incoming_values, &incoming_blocks, 2); | ||
| 8497 | |||
| 8431 | return null; | 8498 | return null; |
| 8432 | } | 8499 | } |
| 8433 | 8500 |
src/print_air.zig+1| ... | @@ -171,6 +171,7 @@ const Writer = struct { | ... | @@ -171,6 +171,7 @@ const Writer = struct { |
| 171 | .cmp_neq_optimized, | 171 | .cmp_neq_optimized, |
| 172 | .memcpy, | 172 | .memcpy, |
| 173 | .memset, | 173 | .memset, |
| 174 | .memset_safe, | ||
| 174 | => try w.writeBinOp(s, inst), | 175 | => try w.writeBinOp(s, inst), |
| 175 | 176 | ||
| 176 | .is_null, | 177 | .is_null, |
test/behavior/basic.zig+44| ... | @@ -353,6 +353,50 @@ fn f2(x: bool) []const u8 { | ... | @@ -353,6 +353,50 @@ fn f2(x: bool) []const u8 { |
| 353 | return (if (x) &fA else &fB)(); | 353 | return (if (x) &fA else &fB)(); |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | test "@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 | |||
| 361 | try testMemsetArray(); | ||
| 362 | // TODO this doesn't pass yet | ||
| 363 | // try comptime testMemsetArray(); | ||
| 364 | } | ||
| 365 | |||
| 366 | fn testMemsetArray() !void { | ||
| 367 | { | ||
| 368 | // memset array to non-undefined, ABI size == 1 | ||
| 369 | var foo: [20]u8 = undefined; | ||
| 370 | @memset(&foo, 'A'); | ||
| 371 | try expect(foo[0] == 'A'); | ||
| 372 | try expect(foo[11] == 'A'); | ||
| 373 | try expect(foo[19] == 'A'); | ||
| 374 | |||
| 375 | // memset array to undefined, ABI size == 1 | ||
| 376 | @setRuntimeSafety(true); | ||
| 377 | @memset(&foo, undefined); | ||
| 378 | try expect(foo[0] == 0xaa); | ||
| 379 | try expect(foo[11] == 0xaa); | ||
| 380 | try expect(foo[19] == 0xaa); | ||
| 381 | } | ||
| 382 | |||
| 383 | { | ||
| 384 | // memset array to non-undefined, ABI size > 1 | ||
| 385 | var foo: [20]u32 = undefined; | ||
| 386 | @memset(&foo, 1234); | ||
| 387 | try expect(foo[0] == 1234); | ||
| 388 | try expect(foo[11] == 1234); | ||
| 389 | try expect(foo[19] == 1234); | ||
| 390 | |||
| 391 | // memset array to undefined, ABI size > 1 | ||
| 392 | @setRuntimeSafety(true); | ||
| 393 | @memset(&foo, undefined); | ||
| 394 | try expect(foo[0] == 0xaaaaaaaa); | ||
| 395 | try expect(foo[11] == 0xaaaaaaaa); | ||
| 396 | try expect(foo[19] == 0xaaaaaaaa); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 356 | test "memcpy and memset intrinsics" { | 400 | test "memcpy and memset intrinsics" { |
| 357 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 401 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 358 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 402 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |