authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-24 17:58:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-25 11:23:41-07:00
log5378fdffdcc60a5273021bc9cfc5be917e87c992
treeab4ceb8185b8a8fa3977b7fa76e1a49450f0aabb
parentd604553ee0c32caa0632a01e263a34e31a95b2b3

stage2: introduce store_safe AIR instruction

store: The value to store may be undefined, in which case the destination memory region has undefined bytes after this instruction is evaluated. In such case ignoring this instruction is legal lowering. store_safe: Same as `store`, except if the value to store is undefined, the memory region should be filled with 0xaa bytes, and any other safety metadata such as Valgrind integrations should be notified of this memory region being undefined.

13 files changed, 131 insertions(+), 80 deletions(-)

src/Air.zig+14-2
...@@ -485,7 +485,16 @@ pub const Inst = struct {...@@ -485,7 +485,16 @@ pub const Inst = struct {
485 /// Write a value to a pointer. LHS is pointer, RHS is value.485 /// Write a value to a pointer. LHS is pointer, RHS is value.
486 /// Result type is always void.486 /// Result type is always void.
487 /// Uses the `bin_op` field.487 /// Uses the `bin_op` field.
488 /// The value to store may be undefined, in which case the destination
489 /// memory region has undefined bytes after this instruction is
490 /// evaluated. In such case ignoring this instruction is legal
491 /// lowering.
488 store,492 store,
493 /// Same as `store`, except if the value to store is undefined, the
494 /// memory region should be filled with 0xaa bytes, and any other
495 /// safety metadata such as Valgrind integrations should be notified of
496 /// this memory region being undefined.
497 store_safe,
489 /// Indicates the program counter will never get to this instruction.498 /// Indicates the program counter will never get to this instruction.
490 /// Result type is always noreturn; no instructions in a block follow this one.499 /// Result type is always noreturn; no instructions in a block follow this one.
491 unreach,500 unreach,
...@@ -639,8 +648,9 @@ pub const Inst = struct {...@@ -639,8 +648,9 @@ pub const Inst = struct {
639 /// Result type is always void.648 /// Result type is always void.
640 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value.649 /// 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 destination650 /// The element value may be undefined, in which case the destination
642 /// memory region has undefined bytes after this function executes. In651 /// memory region has undefined bytes after this instruction is
643 /// such case ignoring this instruction is legal lowering.652 /// evaluated. In such case ignoring this instruction is legal
653 /// lowering.
644 /// If the length is compile-time known (due to the destination being a654 /// If the length is compile-time known (due to the destination being a
645 /// pointer-to-array), then it is guaranteed to be greater than zero.655 /// pointer-to-array), then it is guaranteed to be greater than zero.
646 memset,656 memset,
...@@ -1242,6 +1252,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -1242,6 +1252,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
1242 .dbg_var_ptr,1252 .dbg_var_ptr,
1243 .dbg_var_val,1253 .dbg_var_val,
1244 .store,1254 .store,
1255 .store_safe,
1245 .fence,1256 .fence,
1246 .atomic_store_unordered,1257 .atomic_store_unordered,
1247 .atomic_store_monotonic,1258 .atomic_store_monotonic,
...@@ -1423,6 +1434,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {...@@ -1423,6 +1434,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index) bool {
1423 .ret,1434 .ret,
1424 .ret_load,1435 .ret_load,
1425 .store,1436 .store,
1437 .store_safe,
1426 .unreach,1438 .unreach,
1427 .optional_payload_ptr_set,1439 .optional_payload_ptr_set,
1428 .errunion_payload_ptr_set,1440 .errunion_payload_ptr_set,
src/Liveness.zig+2
...@@ -299,6 +299,7 @@ pub fn categorizeOperand(...@@ -299,6 +299,7 @@ pub fn categorizeOperand(
299 },299 },
300300
301 .store,301 .store,
302 .store_safe,
302 .atomic_store_unordered,303 .atomic_store_unordered,
303 .atomic_store_monotonic,304 .atomic_store_monotonic,
304 .atomic_store_release,305 .atomic_store_release,
...@@ -965,6 +966,7 @@ fn analyzeInst(...@@ -965,6 +966,7 @@ fn analyzeInst(
965 .bool_and,966 .bool_and,
966 .bool_or,967 .bool_or,
967 .store,968 .store,
969 .store_safe,
968 .array_elem_val,970 .array_elem_val,
969 .slice_elem_val,971 .slice_elem_val,
970 .ptr_elem_val,972 .ptr_elem_val,
src/Liveness/Verify.zig+1
...@@ -239,6 +239,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -239,6 +239,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
239 .bool_and,239 .bool_and,
240 .bool_or,240 .bool_or,
241 .store,241 .store,
242 .store_safe,
242 .array_elem_val,243 .array_elem_val,
243 .slice_elem_val,244 .slice_elem_val,
244 .ptr_elem_val,245 .ptr_elem_val,
src/Sema.zig+24-9
...@@ -2500,7 +2500,7 @@ fn coerceResultPtr(...@@ -2500,7 +2500,7 @@ fn coerceResultPtr(
25002500
2501 // The last one is always `store`.2501 // The last one is always `store`.
2502 const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1];2502 const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1];
2503 if (air_tags[trash_inst] != .store) {2503 if (air_tags[trash_inst] != .store and air_tags[trash_inst] != .store_safe) {
2504 // no store instruction is generated for zero sized types2504 // no store instruction is generated for zero sized types
2505 assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null);2505 assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null);
2506 } else {2506 } else {
...@@ -3524,7 +3524,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -3524,7 +3524,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
3524 const candidate = block.instructions.items[search_index];3524 const candidate = block.instructions.items[search_index];
3525 switch (air_tags[candidate]) {3525 switch (air_tags[candidate]) {
3526 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,3526 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
3527 .store => break candidate,3527 .store, .store_safe => break candidate,
3528 else => break :ct,3528 else => break :ct,
3529 }3529 }
3530 };3530 };
...@@ -3750,7 +3750,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3750,7 +3750,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3750 const candidate = block.instructions.items[search_index];3750 const candidate = block.instructions.items[search_index];
3751 switch (air_tags[candidate]) {3751 switch (air_tags[candidate]) {
3752 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,3752 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
3753 .store => break candidate,3753 .store, .store_safe => break candidate,
3754 else => break :ct,3754 else => break :ct,
3755 }3755 }
3756 };3756 };
...@@ -3860,7 +3860,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3860,7 +3860,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3860 assert(replacement_block.instructions.items.len > 0);3860 assert(replacement_block.instructions.items.len > 0);
3861 break :result sub_ptr;3861 break :result sub_ptr;
3862 },3862 },
3863 .store => result: {3863 .store, .store_safe => result: {
3864 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;3864 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op;
3865 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);3865 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast);
3866 break :result .void_value;3866 break :result .void_value;
...@@ -4242,7 +4242,10 @@ fn validateUnionInit(...@@ -4242,7 +4242,10 @@ fn validateUnionInit(
4242 while (block_index > 0) : (block_index -= 1) {4242 while (block_index > 0) : (block_index -= 1) {
4243 const store_inst = block.instructions.items[block_index];4243 const store_inst = block.instructions.items[block_index];
4244 if (store_inst == field_ptr_air_inst) break;4244 if (store_inst == field_ptr_air_inst) break;
4245 if (air_tags[store_inst] != .store) continue;4245 switch (air_tags[store_inst]) {
4246 .store, .store_safe => {},
4247 else => continue,
4248 }
4246 const bin_op = air_datas[store_inst].bin_op;4249 const bin_op = air_datas[store_inst].bin_op;
4247 var lhs = bin_op.lhs;4250 var lhs = bin_op.lhs;
4248 if (Air.refToIndex(lhs)) |lhs_index| {4251 if (Air.refToIndex(lhs)) |lhs_index| {
...@@ -4454,7 +4457,10 @@ fn validateStructInit(...@@ -4454,7 +4457,10 @@ fn validateStructInit(
4454 struct_is_comptime = false;4457 struct_is_comptime = false;
4455 continue :field;4458 continue :field;
4456 }4459 }
4457 if (air_tags[store_inst] != .store) continue;4460 switch (air_tags[store_inst]) {
4461 .store, .store_safe => {},
4462 else => continue,
4463 }
4458 const bin_op = air_datas[store_inst].bin_op;4464 const bin_op = air_datas[store_inst].bin_op;
4459 var lhs = bin_op.lhs;4465 var lhs = bin_op.lhs;
4460 {4466 {
...@@ -4682,7 +4688,10 @@ fn zirValidateArrayInit(...@@ -4682,7 +4688,10 @@ fn zirValidateArrayInit(
4682 array_is_comptime = false;4688 array_is_comptime = false;
4683 continue :outer;4689 continue :outer;
4684 }4690 }
4685 if (air_tags[store_inst] != .store) continue;4691 switch (air_tags[store_inst]) {
4692 .store, .store_safe => {},
4693 else => continue,
4694 }
4686 const bin_op = air_datas[store_inst].bin_op;4695 const bin_op = air_datas[store_inst].bin_op;
4687 var lhs = bin_op.lhs;4696 var lhs = bin_op.lhs;
4688 {4697 {
...@@ -5025,7 +5034,12 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v...@@ -5025,7 +5034,12 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
50255034
5026 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };5035 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node };
5027 const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node };5036 const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node };
5028 const air_tag: Air.Inst.Tag = if (is_ret) .ret_ptr else .store;5037 const air_tag: Air.Inst.Tag = if (is_ret)
5038 .ret_ptr
5039 else if (block.wantSafety())
5040 .store_safe
5041 else
5042 .store;
5029 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);5043 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
5030}5044}
50315045
...@@ -26704,7 +26718,8 @@ fn storePtr(...@@ -26704,7 +26718,8 @@ fn storePtr(
26704 ptr: Air.Inst.Ref,26718 ptr: Air.Inst.Ref,
26705 uncasted_operand: Air.Inst.Ref,26719 uncasted_operand: Air.Inst.Ref,
26706) CompileError!void {26720) CompileError!void {
26707 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, .store);26721 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
26722 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
26708}26723}
2670926724
26710fn storePtr2(26725fn storePtr2(
src/arch/aarch64/CodeGen.zig+8-2
...@@ -764,7 +764,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -764,7 +764,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
764 .ptrtoint => try self.airPtrToInt(inst),764 .ptrtoint => try self.airPtrToInt(inst),
765 .ret => try self.airRet(inst),765 .ret => try self.airRet(inst),
766 .ret_load => try self.airRetLoad(inst),766 .ret_load => try self.airRetLoad(inst),
767 .store => try self.airStore(inst),767 .store => try self.airStore(inst, false),
768 .store_safe => try self.airStore(inst, true),
768 .struct_field_ptr=> try self.airStructFieldPtr(inst),769 .struct_field_ptr=> try self.airStructFieldPtr(inst),
769 .struct_field_val=> try self.airStructFieldVal(inst),770 .struct_field_val=> try self.airStructFieldVal(inst),
770 .array_to_slice => try self.airArrayToSlice(inst),771 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -4036,7 +4037,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4036,7 +4037,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4036 }4037 }
4037}4038}
40384039
4039fn airStore(self: *Self, inst: Air.Inst.Index) !void {4040fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
4041 if (safety) {
4042 // TODO if the value is undef, write 0xaa bytes to dest
4043 } else {
4044 // TODO if the value is undef, don't lower this instruction
4045 }
4040 const bin_op = self.air.instructions.items(.data)[inst].bin_op;4046 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4041 const ptr = try self.resolveInst(bin_op.lhs);4047 const ptr = try self.resolveInst(bin_op.lhs);
4042 const value = try self.resolveInst(bin_op.rhs);4048 const value = try self.resolveInst(bin_op.rhs);
src/arch/arm/CodeGen.zig+8-2
...@@ -748,7 +748,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -748,7 +748,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
748 .ptrtoint => try self.airPtrToInt(inst),748 .ptrtoint => try self.airPtrToInt(inst),
749 .ret => try self.airRet(inst),749 .ret => try self.airRet(inst),
750 .ret_load => try self.airRetLoad(inst),750 .ret_load => try self.airRetLoad(inst),
751 .store => try self.airStore(inst),751 .store => try self.airStore(inst, false),
752 .store_safe => try self.airStore(inst, true),
752 .struct_field_ptr=> try self.airStructFieldPtr(inst),753 .struct_field_ptr=> try self.airStructFieldPtr(inst),
753 .struct_field_val=> try self.airStructFieldVal(inst),754 .struct_field_val=> try self.airStructFieldVal(inst),
754 .array_to_slice => try self.airArrayToSlice(inst),755 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -2836,7 +2837,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2836,7 +2837,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2836 }2837 }
2837}2838}
28382839
2839fn airStore(self: *Self, inst: Air.Inst.Index) !void {2840fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2841 if (safety) {
2842 // TODO if the value is undef, write 0xaa bytes to dest
2843 } else {
2844 // TODO if the value is undef, don't lower this instruction
2845 }
2840 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2846 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2841 const ptr = try self.resolveInst(bin_op.lhs);2847 const ptr = try self.resolveInst(bin_op.lhs);
2842 const value = try self.resolveInst(bin_op.rhs);2848 const value = try self.resolveInst(bin_op.rhs);
src/arch/riscv64/CodeGen.zig+8-2
...@@ -578,7 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -578,7 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
578 .ptrtoint => try self.airPtrToInt(inst),578 .ptrtoint => try self.airPtrToInt(inst),
579 .ret => try self.airRet(inst),579 .ret => try self.airRet(inst),
580 .ret_load => try self.airRetLoad(inst),580 .ret_load => try self.airRetLoad(inst),
581 .store => try self.airStore(inst),581 .store => try self.airStore(inst, false),
582 .store_safe => try self.airStore(inst, true),
582 .struct_field_ptr=> try self.airStructFieldPtr(inst),583 .struct_field_ptr=> try self.airStructFieldPtr(inst),
583 .struct_field_val=> try self.airStructFieldVal(inst),584 .struct_field_val=> try self.airStructFieldVal(inst),
584 .array_to_slice => try self.airArrayToSlice(inst),585 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -1573,7 +1574,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1573,7 +1574,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1573 }1574 }
1574}1575}
15751576
1576fn airStore(self: *Self, inst: Air.Inst.Index) !void {1577fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
1578 if (safety) {
1579 // TODO if the value is undef, write 0xaa bytes to dest
1580 } else {
1581 // TODO if the value is undef, don't lower this instruction
1582 }
1577 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1583 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1578 const ptr = try self.resolveInst(bin_op.lhs);1584 const ptr = try self.resolveInst(bin_op.lhs);
1579 const value = try self.resolveInst(bin_op.rhs);1585 const value = try self.resolveInst(bin_op.rhs);
src/arch/sparc64/CodeGen.zig+8-2
...@@ -593,7 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -593,7 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
593 .ptrtoint => try self.airPtrToInt(inst),593 .ptrtoint => try self.airPtrToInt(inst),
594 .ret => try self.airRet(inst),594 .ret => try self.airRet(inst),
595 .ret_load => try self.airRetLoad(inst),595 .ret_load => try self.airRetLoad(inst),
596 .store => try self.airStore(inst),596 .store => try self.airStore(inst, false),
597 .store_safe => try self.airStore(inst, true),
597 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),598 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
598 .struct_field_val=> try self.airStructFieldVal(inst),599 .struct_field_val=> try self.airStructFieldVal(inst),
599 .array_to_slice => try self.airArrayToSlice(inst),600 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -2407,7 +2408,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {...@@ -2407,7 +2408,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
2407 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2408 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2408}2409}
24092410
2410fn airStore(self: *Self, inst: Air.Inst.Index) !void {2411fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
2412 if (safety) {
2413 // TODO if the value is undef, write 0xaa bytes to dest
2414 } else {
2415 // TODO if the value is undef, don't lower this instruction
2416 }
2411 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2417 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2412 const ptr = try self.resolveInst(bin_op.lhs);2418 const ptr = try self.resolveInst(bin_op.lhs);
2413 const value = try self.resolveInst(bin_op.rhs);2419 const value = try self.resolveInst(bin_op.rhs);
src/arch/wasm/CodeGen.zig+16-5
...@@ -1883,8 +1883,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1883,8 +1883,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18831883
1884 .load => func.airLoad(inst),1884 .load => func.airLoad(inst),
1885 .loop => func.airLoop(inst),1885 .loop => func.airLoop(inst),
1886 // TODO: elide memset when writing undef without safety1886 .memset => func.airMemset(inst, false),
1887 .memset, .memset_safe => func.airMemset(inst),1887 .memset_safe => func.airMemset(inst, true),
1888 .not => func.airNot(inst),1888 .not => func.airNot(inst),
1889 .optional_payload => func.airOptionalPayload(inst),1889 .optional_payload => func.airOptionalPayload(inst),
1890 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),1890 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),
...@@ -1914,7 +1914,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1914,7 +1914,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1914 .slice_ptr => func.airSlicePtr(inst),1914 .slice_ptr => func.airSlicePtr(inst),
1915 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),1915 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),
1916 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),1916 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),
1917 .store => func.airStore(inst),1917 .store => func.airStore(inst, false),
1918 .store_safe => func.airStore(inst, true),
19181919
1919 .set_union_tag => func.airSetUnionTag(inst),1920 .set_union_tag => func.airSetUnionTag(inst),
1920 .struct_field_ptr => func.airStructFieldPtr(inst),1921 .struct_field_ptr => func.airStructFieldPtr(inst),
...@@ -2222,7 +2223,12 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2222,7 +2223,12 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2222 func.finishAir(inst, value, &.{});2223 func.finishAir(inst, value, &.{});
2223}2224}
22242225
2225fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {2226fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
2227 if (safety) {
2228 // TODO if the value is undef, write 0xaa bytes to dest
2229 } else {
2230 // TODO if the value is undef, don't lower this instruction
2231 }
2226 const bin_op = func.air.instructions.items(.data)[inst].bin_op;2232 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
22272233
2228 const lhs = try func.resolveInst(bin_op.lhs);2234 const lhs = try func.resolveInst(bin_op.lhs);
...@@ -4384,7 +4390,12 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -4384,7 +4390,12 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
4384 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });4390 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4385}4391}
43864392
4387fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4393fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
4394 if (safety) {
4395 // TODO if the value is undef, write 0xaa bytes to dest
4396 } else {
4397 // TODO if the value is undef, don't lower this instruction
4398 }
4388 const bin_op = func.air.instructions.items(.data)[inst].bin_op;4399 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
43894400
4390 const ptr = try func.resolveInst(bin_op.lhs);4401 const ptr = try func.resolveInst(bin_op.lhs);
src/arch/x86_64/CodeGen.zig+8-2
...@@ -1035,7 +1035,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1035,7 +1035,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1035 .ptrtoint => try self.airPtrToInt(inst),1035 .ptrtoint => try self.airPtrToInt(inst),
1036 .ret => try self.airRet(inst),1036 .ret => try self.airRet(inst),
1037 .ret_load => try self.airRetLoad(inst),1037 .ret_load => try self.airRetLoad(inst),
1038 .store => try self.airStore(inst),1038 .store => try self.airStore(inst, false),
1039 .store_safe => try self.airStore(inst, true),
1039 .struct_field_ptr=> try self.airStructFieldPtr(inst),1040 .struct_field_ptr=> try self.airStructFieldPtr(inst),
1040 .struct_field_val=> try self.airStructFieldVal(inst),1041 .struct_field_val=> try self.airStructFieldVal(inst),
1041 .array_to_slice => try self.airArrayToSlice(inst),1042 .array_to_slice => try self.airArrayToSlice(inst),
...@@ -3936,7 +3937,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3936,7 +3937,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3936 }3937 }
3937}3938}
39383939
3939fn airStore(self: *Self, inst: Air.Inst.Index) !void {3940fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
3941 if (safety) {
3942 // TODO if the value is undef, write 0xaa bytes to dest
3943 } else {
3944 // TODO if the value is undef, don't lower this instruction
3945 }
3940 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3946 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3941 const ptr = try self.resolveInst(bin_op.lhs);3947 const ptr = try self.resolveInst(bin_op.lhs);
3942 const ptr_ty = self.air.typeOf(bin_op.lhs);3948 const ptr_ty = self.air.typeOf(bin_op.lhs);
src/codegen/c.zig+15-25
...@@ -2914,7 +2914,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -2914,7 +2914,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
2914 .load => try airLoad(f, inst),2914 .load => try airLoad(f, inst),
2915 .ret => try airRet(f, inst, false),2915 .ret => try airRet(f, inst, false),
2916 .ret_load => try airRet(f, inst, true),2916 .ret_load => try airRet(f, inst, true),
2917 .store => try airStore(f, inst),2917 .store => try airStore(f, inst, false),
2918 .store_safe => try airStore(f, inst, true),
2918 .loop => try airLoop(f, inst),2919 .loop => try airLoop(f, inst),
2919 .cond_br => try airCondBr(f, inst),2920 .cond_br => try airCondBr(f, inst),
2920 .br => try airBr(f, inst),2921 .br => try airBr(f, inst),
...@@ -3565,19 +3566,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3565,19 +3566,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
3565 return local;3566 return local;
3566}3567}
35673568
3568fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {3569fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
3569 if (f.wantSafety()) {
3570 const writer = f.object.writer();
3571 try writer.writeAll("memset(");
3572 try f.writeCValue(writer, dest_ptr, .FunctionArgument);
3573 try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)});
3574 try f.renderType(writer, lhs_child_ty);
3575 try writer.writeAll("));\n");
3576 }
3577 return .none;
3578}
3579
3580fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3581 // *a = b;3570 // *a = b;
3582 const bin_op = f.air.instructions.items(.data)[inst].bin_op;3571 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
35833572
...@@ -3588,18 +3577,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3588,18 +3577,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3588 const ptr_val = try f.resolveInst(bin_op.lhs);3577 const ptr_val = try f.resolveInst(bin_op.lhs);
3589 const src_ty = f.air.typeOf(bin_op.rhs);3578 const src_ty = f.air.typeOf(bin_op.rhs);
35903579
3591 // TODO Sema should emit a different instruction when the store should3580 const val_is_undef = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
3592 // possibly do the safety 0xaa bytes for undefined.3581
3593 const src_val_is_undefined =3582 if (val_is_undef) {
3594 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;3583 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3595 if (src_val_is_undefined) {3584 if (safety and ptr_info.host_size == 0) {
3596 if (ptr_info.host_size == 0) {3585 const writer = f.object.writer();
3597 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3586 try writer.writeAll("memset(");
3598 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);3587 try f.writeCValue(writer, ptr_val, .FunctionArgument);
3599 } else if (!f.wantSafety()) {3588 try writer.writeAll(", 0xaa, sizeof(");
3600 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3589 try f.renderType(writer, ptr_info.pointee_type);
3601 return .none;3590 try writer.writeAll("));\n");
3602 }3591 }
3592 return .none;
3603 }3593 }
36043594
3605 const target = f.object.dg.module.getTarget();3595 const target = f.object.dg.module.getTarget();
src/codegen/llvm.zig+18-29
...@@ -4649,7 +4649,8 @@ pub const FuncGen = struct {...@@ -4649,7 +4649,8 @@ pub const FuncGen = struct {
4649 .not => try self.airNot(inst),4649 .not => try self.airNot(inst),
4650 .ret => try self.airRet(inst),4650 .ret => try self.airRet(inst),
4651 .ret_load => try self.airRetLoad(inst),4651 .ret_load => try self.airRetLoad(inst),
4652 .store => try self.airStore(inst),4652 .store => try self.airStore(inst, false),
4653 .store_safe => try self.airStore(inst, true),
4653 .assembly => try self.airAssembly(inst),4654 .assembly => try self.airAssembly(inst),
4654 .slice_ptr => try self.airSliceField(inst, 0),4655 .slice_ptr => try self.airSliceField(inst, 0),
4655 .slice_len => try self.airSliceField(inst, 1),4656 .slice_len => try self.airSliceField(inst, 1),
...@@ -8115,48 +8116,36 @@ pub const FuncGen = struct {...@@ -8115,48 +8116,36 @@ pub const FuncGen = struct {
8115 return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget());8116 return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget());
8116 }8117 }
81178118
8118 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8119 fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value {
8119 const bin_op = self.air.instructions.items(.data)[inst].bin_op;8120 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
8120 const dest_ptr = try self.resolveInst(bin_op.lhs);8121 const dest_ptr = try self.resolveInst(bin_op.lhs);
8121 const ptr_ty = self.air.typeOf(bin_op.lhs);8122 const ptr_ty = self.air.typeOf(bin_op.lhs);
8122 const operand_ty = ptr_ty.childType();8123 const operand_ty = ptr_ty.childType();
81238124
8124 // TODO Sema should emit a different instruction when the store should
8125 // possibly do the safety 0xaa bytes for undefined.
8126 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;8125 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
8127 if (val_is_undef) {8126 if (val_is_undef) {
8128 {8127 // Even if safety is disabled, we still emit a memset to undefined since it conveys
8129 // TODO let's handle this in AIR rather than by having each backend8128 // extra information to LLVM. However, safety makes the difference between using
8130 // check the optimization mode of the compilation because the plan is8129 // 0xaa or actual undefined for the fill byte.
8131 // to support setting the optimization mode at finer grained scopes8130 const u8_llvm_ty = self.context.intType(8);
8132 // which happens in Sema. Codegen should not be aware of this logic.8131 const fill_byte = if (safety)
8133 // I think this comment is basically the same as the other TODO comment just8132 u8_llvm_ty.constInt(0xaa, .False)
8134 // above but I'm leaving them both here to make it look super messy and8133 else
8135 // thereby bait contributors (or let's be honest, probably myself) into8134 u8_llvm_ty.getUndef();
8136 // fixing this instead of letting it rot.
8137 const safety = switch (self.dg.module.comp.bin_file.options.optimize_mode) {
8138 .ReleaseSmall, .ReleaseFast => false,
8139 .Debug, .ReleaseSafe => true,
8140 };
8141 if (!safety) {
8142 return null;
8143 }
8144 }
8145 const target = self.dg.module.getTarget();8135 const target = self.dg.module.getTarget();
8146 const operand_size = operand_ty.abiSize(target);8136 const operand_size = operand_ty.abiSize(target);
8147 const u8_llvm_ty = self.context.intType(8);
8148 const fill_char = u8_llvm_ty.constInt(0xaa, .False);
8149 const dest_ptr_align = ptr_ty.ptrAlignment(target);
8150 const usize_llvm_ty = try self.dg.lowerType(Type.usize);8137 const usize_llvm_ty = try self.dg.lowerType(Type.usize);
8151 const len = usize_llvm_ty.constInt(operand_size, .False);8138 const len = usize_llvm_ty.constInt(operand_size, .False);
8152 _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());8139 const dest_ptr_align = ptr_ty.ptrAlignment(target);
8153 if (self.dg.module.comp.bin_file.options.valgrind) {8140 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
8141 if (safety and self.dg.module.comp.bin_file.options.valgrind) {
8154 self.valgrindMarkUndef(dest_ptr, len);8142 self.valgrindMarkUndef(dest_ptr, len);
8155 }8143 }
8156 } else {8144 return null;
8157 const src_operand = try self.resolveInst(bin_op.rhs);
8158 try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
8159 }8145 }
8146
8147 const src_operand = try self.resolveInst(bin_op.rhs);
8148 try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
8160 return null;8149 return null;
8161 }8150 }
81628151
src/print_air.zig+1
...@@ -140,6 +140,7 @@ const Writer = struct {...@@ -140,6 +140,7 @@ const Writer = struct {
140 .bool_and,140 .bool_and,
141 .bool_or,141 .bool_or,
142 .store,142 .store,
143 .store_safe,
143 .array_elem_val,144 .array_elem_val,
144 .slice_elem_val,145 .slice_elem_val,
145 .ptr_elem_val,146 .ptr_elem_val,