| author | |
| committer | |
| log | 5378fdffdcc60a5273021bc9cfc5be917e87c992 |
| tree | ab4ceb8185b8a8fa3977b7fa76e1a49450f0aabb |
| parent | d604553ee0c32caa0632a01e263a34e31a95b2b3 |
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 destination | 650 | /// The element value may be undefined, in which case the destination |
| 642 | /// memory region has undefined bytes after this function executes. In | 651 | /// 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 a | 654 | /// 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 | }, |
| 300 | 300 | ||
| 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( |
| 2500 | 2500 | ||
| 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 types | 2504 | // 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 |
| 5025 | 5034 | ||
| 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 | } |
| 5031 | 5045 | ||
| ... | @@ -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 | } |
| 26709 | 26724 | ||
| 26710 | fn storePtr2( | 26725 | fn 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 | } |
| 4038 | 4039 | ||
| 4039 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | 4040 | fn 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 | } |
| 2838 | 2839 | ||
| 2839 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | 2840 | fn 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 | } |
| 1575 | 1576 | ||
| 1576 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | 1577 | fn 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 | } |
| 2409 | 2410 | ||
| 2410 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | 2411 | fn 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 { |
| 1883 | 1883 | ||
| 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 safety | 1886 | .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), | ||
| 1918 | 1919 | ||
| 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 | } |
| 2224 | 2225 | ||
| 2225 | fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 2226 | fn 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; |
| 2227 | 2233 | ||
| 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 | } |
| 4386 | 4392 | ||
| 4387 | fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4393 | fn 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; |
| 4389 | 4400 | ||
| 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 | } |
| 3938 | 3939 | ||
| 3939 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | 3940 | fn 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 | } |
| 3567 | 3568 | ||
| 3568 | fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { | 3569 | fn 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 | |||
| 3580 | fn 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; |
| 3583 | 3572 | ||
| ... | @@ -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); |
| 3590 | 3579 | ||
| 3591 | // TODO Sema should emit a different instruction when the store should | 3580 | 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 | } |
| 3604 | 3594 | ||
| 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 | } |
| 8117 | 8118 | ||
| 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(); |
| 8123 | 8124 | ||
| 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 backend | 8128 | // extra information to LLVM. However, safety makes the difference between using |
| 8130 | // check the optimization mode of the compilation because the plan is | 8129 | // 0xaa or actual undefined for the fill byte. |
| 8131 | // to support setting the optimization mode at finer grained scopes | 8130 | 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 just | 8132 | u8_llvm_ty.constInt(0xaa, .False) |
| 8134 | // above but I'm leaving them both here to make it look super messy and | 8133 | else |
| 8135 | // thereby bait contributors (or let's be honest, probably myself) into | 8134 | 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 | } |
| 8162 | 8151 |
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, |