| author | |
| committer | |
| log | 7d75c3d3b80c86bbd47e60f85a98e8decc52c611 |
| tree | c52ddac06bfb3495f19e426020c5a693d3f8f480 |
| parent | 4dfca01de4fda9a195048011b3339686dce4e936 |
Closes #1317815 files changed, 95 insertions(+), 5 deletions(-)
src/Air.zig+7| ... | @@ -516,6 +516,11 @@ pub const Inst = struct { | ... | @@ -516,6 +516,11 @@ pub const Inst = struct { |
| 516 | /// Uses the `un_op` field. | 516 | /// Uses the `un_op` field. |
| 517 | /// Triggers `resolveTypeLayout` on the return type. | 517 | /// Triggers `resolveTypeLayout` on the return type. |
| 518 | ret, | 518 | ret, |
| 519 | /// Same as `ret`, except if the operand is undefined, the | ||
| 520 | /// returned value is 0xaa bytes, and any other safety metadata | ||
| 521 | /// such as Valgrind integrations should be notified of | ||
| 522 | /// this value being undefined. | ||
| 523 | ret_safe, | ||
| 519 | /// This instruction communicates that the function's result value is pointed to by | 524 | /// This instruction communicates that the function's result value is pointed to by |
| 520 | /// the operand. If the function will pass the result by-ref, the operand is a | 525 | /// the operand. If the function will pass the result by-ref, the operand is a |
| 521 | /// `ret_ptr` instruction. Otherwise, this instruction is equivalent to a `load` | 526 | /// `ret_ptr` instruction. Otherwise, this instruction is equivalent to a `load` |
| ... | @@ -1439,6 +1444,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) | ... | @@ -1439,6 +1444,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1439 | .cond_br, | 1444 | .cond_br, |
| 1440 | .switch_br, | 1445 | .switch_br, |
| 1441 | .ret, | 1446 | .ret, |
| 1447 | .ret_safe, | ||
| 1442 | .ret_load, | 1448 | .ret_load, |
| 1443 | .unreach, | 1449 | .unreach, |
| 1444 | .trap, | 1450 | .trap, |
| ... | @@ -1613,6 +1619,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { | ... | @@ -1613,6 +1619,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1613 | .dbg_var_ptr, | 1619 | .dbg_var_ptr, |
| 1614 | .dbg_var_val, | 1620 | .dbg_var_val, |
| 1615 | .ret, | 1621 | .ret, |
| 1622 | .ret_safe, | ||
| 1616 | .ret_load, | 1623 | .ret_load, |
| 1617 | .store, | 1624 | .store, |
| 1618 | .store_safe, | 1625 | .store_safe, |
src/Liveness.zig+2| ... | @@ -435,6 +435,7 @@ pub fn categorizeOperand( | ... | @@ -435,6 +435,7 @@ pub fn categorizeOperand( |
| 435 | }, | 435 | }, |
| 436 | 436 | ||
| 437 | .ret, | 437 | .ret, |
| 438 | .ret_safe, | ||
| 438 | .ret_load, | 439 | .ret_load, |
| 439 | => { | 440 | => { |
| 440 | const o = air_datas[@intFromEnum(inst)].un_op; | 441 | const o = air_datas[@intFromEnum(inst)].un_op; |
| ... | @@ -1070,6 +1071,7 @@ fn analyzeInst( | ... | @@ -1070,6 +1071,7 @@ fn analyzeInst( |
| 1070 | }, | 1071 | }, |
| 1071 | 1072 | ||
| 1072 | .ret, | 1073 | .ret, |
| 1074 | .ret_safe, | ||
| 1073 | .ret_load, | 1075 | .ret_load, |
| 1074 | => { | 1076 | => { |
| 1075 | const operand = inst_datas[@intFromEnum(inst)].un_op; | 1077 | const operand = inst_datas[@intFromEnum(inst)].un_op; |
src/Liveness/Verify.zig+1| ... | @@ -151,6 +151,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { | ... | @@ -151,6 +151,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 151 | try self.verifyInstOperands(inst, .{ un_op, .none, .none }); | 151 | try self.verifyInstOperands(inst, .{ un_op, .none, .none }); |
| 152 | }, | 152 | }, |
| 153 | .ret, | 153 | .ret, |
| 154 | .ret_safe, | ||
| 154 | .ret_load, | 155 | .ret_load, |
| 155 | => { | 156 | => { |
| 156 | const un_op = data[@intFromEnum(inst)].un_op; | 157 | const un_op = data[@intFromEnum(inst)].un_op; |
src/Sema.zig+3-2| ... | @@ -19437,14 +19437,15 @@ fn analyzeRet( | ... | @@ -19437,14 +19437,15 @@ fn analyzeRet( |
| 19437 | 19437 | ||
| 19438 | try sema.resolveTypeLayout(sema.fn_ret_ty); | 19438 | try sema.resolveTypeLayout(sema.fn_ret_ty); |
| 19439 | 19439 | ||
| 19440 | const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret; | ||
| 19440 | if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { | 19441 | if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { |
| 19441 | // Avoid adding a frame to the error return trace in case the value is comptime-known | 19442 | // Avoid adding a frame to the error return trace in case the value is comptime-known |
| 19442 | // to be not an error. | 19443 | // to be not an error. |
| 19443 | const is_non_err = try sema.analyzeIsNonErr(block, src, operand); | 19444 | const is_non_err = try sema.analyzeIsNonErr(block, src, operand); |
| 19444 | return sema.retWithErrTracing(block, src, is_non_err, .ret, operand); | 19445 | return sema.retWithErrTracing(block, src, is_non_err, air_tag, operand); |
| 19445 | } | 19446 | } |
| 19446 | 19447 | ||
| 19447 | _ = try block.addUnOp(.ret, operand); | 19448 | _ = try block.addUnOp(air_tag, operand); |
| 19448 | 19449 | ||
| 19449 | return always_noreturn; | 19450 | return always_noreturn; |
| 19450 | } | 19451 | } |
src/arch/aarch64/CodeGen.zig+1| ... | @@ -766,6 +766,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -766,6 +766,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 766 | .not => try self.airNot(inst), | 766 | .not => try self.airNot(inst), |
| 767 | .int_from_ptr => try self.airIntFromPtr(inst), | 767 | .int_from_ptr => try self.airIntFromPtr(inst), |
| 768 | .ret => try self.airRet(inst), | 768 | .ret => try self.airRet(inst), |
| 769 | .ret_safe => try self.airRet(inst), // TODO | ||
| 769 | .ret_load => try self.airRetLoad(inst), | 770 | .ret_load => try self.airRetLoad(inst), |
| 770 | .store => try self.airStore(inst, false), | 771 | .store => try self.airStore(inst, false), |
| 771 | .store_safe => try self.airStore(inst, true), | 772 | .store_safe => try self.airStore(inst, true), |
src/arch/arm/CodeGen.zig+1| ... | @@ -752,6 +752,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -752,6 +752,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 752 | .not => try self.airNot(inst), | 752 | .not => try self.airNot(inst), |
| 753 | .int_from_ptr => try self.airIntFromPtr(inst), | 753 | .int_from_ptr => try self.airIntFromPtr(inst), |
| 754 | .ret => try self.airRet(inst), | 754 | .ret => try self.airRet(inst), |
| 755 | .ret_safe => try self.airRet(inst), // TODO | ||
| 755 | .ret_load => try self.airRetLoad(inst), | 756 | .ret_load => try self.airRetLoad(inst), |
| 756 | .store => try self.airStore(inst, false), | 757 | .store => try self.airStore(inst, false), |
| 757 | .store_safe => try self.airStore(inst, true), | 758 | .store_safe => try self.airStore(inst, true), |
src/arch/riscv64/CodeGen.zig+1| ... | @@ -585,6 +585,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -585,6 +585,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 585 | .not => try self.airNot(inst), | 585 | .not => try self.airNot(inst), |
| 586 | .int_from_ptr => try self.airIntFromPtr(inst), | 586 | .int_from_ptr => try self.airIntFromPtr(inst), |
| 587 | .ret => try self.airRet(inst), | 587 | .ret => try self.airRet(inst), |
| 588 | .ret_safe => try self.airRet(inst), // TODO | ||
| 588 | .ret_load => try self.airRetLoad(inst), | 589 | .ret_load => try self.airRetLoad(inst), |
| 589 | .store => try self.airStore(inst, false), | 590 | .store => try self.airStore(inst, false), |
| 590 | .store_safe => try self.airStore(inst, true), | 591 | .store_safe => try self.airStore(inst, true), |
src/arch/sparc64/CodeGen.zig+1| ... | @@ -598,6 +598,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -598,6 +598,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 598 | .not => try self.airNot(inst), | 598 | .not => try self.airNot(inst), |
| 599 | .int_from_ptr => try self.airIntFromPtr(inst), | 599 | .int_from_ptr => try self.airIntFromPtr(inst), |
| 600 | .ret => try self.airRet(inst), | 600 | .ret => try self.airRet(inst), |
| 601 | .ret_safe => try self.airRet(inst), // TODO | ||
| 601 | .ret_load => try self.airRetLoad(inst), | 602 | .ret_load => try self.airRetLoad(inst), |
| 602 | .store => try self.airStore(inst, false), | 603 | .store => try self.airStore(inst, false), |
| 603 | .store_safe => try self.airStore(inst, true), | 604 | .store_safe => try self.airStore(inst, true), |
src/arch/wasm/CodeGen.zig+1| ... | @@ -1957,6 +1957,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1957,6 +1957,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1957 | .ptr_elem_val => func.airPtrElemVal(inst), | 1957 | .ptr_elem_val => func.airPtrElemVal(inst), |
| 1958 | .int_from_ptr => func.airIntFromPtr(inst), | 1958 | .int_from_ptr => func.airIntFromPtr(inst), |
| 1959 | .ret => func.airRet(inst), | 1959 | .ret => func.airRet(inst), |
| 1960 | .ret_safe => func.airRet(inst), // TODO | ||
| 1960 | .ret_ptr => func.airRetPtr(inst), | 1961 | .ret_ptr => func.airRetPtr(inst), |
| 1961 | .ret_load => func.airRetLoad(inst), | 1962 | .ret_load => func.airRetLoad(inst), |
| 1962 | .splat => func.airSplat(inst), | 1963 | .splat => func.airSplat(inst), |
src/arch/x86_64/CodeGen.zig+1| ... | @@ -2018,6 +2018,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -2018,6 +2018,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 2018 | .loop => try self.airLoop(inst), | 2018 | .loop => try self.airLoop(inst), |
| 2019 | .int_from_ptr => try self.airIntFromPtr(inst), | 2019 | .int_from_ptr => try self.airIntFromPtr(inst), |
| 2020 | .ret => try self.airRet(inst), | 2020 | .ret => try self.airRet(inst), |
| 2021 | .ret_safe => try self.airRet(inst), // TODO | ||
| 2021 | .ret_load => try self.airRetLoad(inst), | 2022 | .ret_load => try self.airRetLoad(inst), |
| 2022 | .store => try self.airStore(inst, false), | 2023 | .store => try self.airStore(inst, false), |
| 2023 | .store_safe => try self.airStore(inst, true), | 2024 | .store_safe => try self.airStore(inst, true), |
src/codegen/c.zig+1| ... | @@ -3225,6 +3225,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -3225,6 +3225,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 3225 | .int_from_bool => try airIntFromBool(f, inst), | 3225 | .int_from_bool => try airIntFromBool(f, inst), |
| 3226 | .load => try airLoad(f, inst), | 3226 | .load => try airLoad(f, inst), |
| 3227 | .ret => try airRet(f, inst, false), | 3227 | .ret => try airRet(f, inst, false), |
| 3228 | .ret_safe => try airRet(f, inst, false), // TODO | ||
| 3228 | .ret_load => try airRet(f, inst, true), | 3229 | .ret_load => try airRet(f, inst, true), |
| 3229 | .store => try airStore(f, inst, false), | 3230 | .store => try airStore(f, inst, false), |
| 3230 | .store_safe => try airStore(f, inst, true), | 3231 | .store_safe => try airStore(f, inst, true), |
src/codegen/llvm.zig+51-3| ... | @@ -5078,7 +5078,8 @@ pub const FuncGen = struct { | ... | @@ -5078,7 +5078,8 @@ pub const FuncGen = struct { |
| 5078 | .load => try self.airLoad(body[i..]), | 5078 | .load => try self.airLoad(body[i..]), |
| 5079 | .loop => try self.airLoop(inst), | 5079 | .loop => try self.airLoop(inst), |
| 5080 | .not => try self.airNot(inst), | 5080 | .not => try self.airNot(inst), |
| 5081 | .ret => try self.airRet(inst), | 5081 | .ret => try self.airRet(inst, false), |
| 5082 | .ret_safe => try self.airRet(inst, true), | ||
| 5082 | .ret_load => try self.airRetLoad(inst), | 5083 | .ret_load => try self.airRetLoad(inst), |
| 5083 | .store => try self.airStore(inst, false), | 5084 | .store => try self.airStore(inst, false), |
| 5084 | .store_safe => try self.airStore(inst, true), | 5085 | .store_safe => try self.airStore(inst, true), |
| ... | @@ -5551,15 +5552,42 @@ pub const FuncGen = struct { | ... | @@ -5551,15 +5552,42 @@ pub const FuncGen = struct { |
| 5551 | _ = try fg.wip.@"unreachable"(); | 5552 | _ = try fg.wip.@"unreachable"(); |
| 5552 | } | 5553 | } |
| 5553 | 5554 | ||
| 5554 | fn airRet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | 5555 | fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value { |
| 5555 | const o = self.dg.object; | 5556 | const o = self.dg.object; |
| 5556 | const mod = o.module; | 5557 | const mod = o.module; |
| 5557 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 5558 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5558 | const ret_ty = self.typeOf(un_op); | 5559 | const ret_ty = self.typeOf(un_op); |
| 5560 | |||
| 5559 | if (self.ret_ptr != .none) { | 5561 | if (self.ret_ptr != .none) { |
| 5560 | const operand = try self.resolveInst(un_op); | ||
| 5561 | const ptr_ty = try mod.singleMutPtrType(ret_ty); | 5562 | const ptr_ty = try mod.singleMutPtrType(ret_ty); |
| 5562 | 5563 | ||
| 5564 | const operand = try self.resolveInst(un_op); | ||
| 5565 | const val_is_undef = if (try self.air.value(un_op, mod)) |val| val.isUndefDeep(mod) else false; | ||
| 5566 | if (val_is_undef and safety) undef: { | ||
| 5567 | const ptr_info = ptr_ty.ptrInfo(mod); | ||
| 5568 | const needs_bitmask = (ptr_info.packed_offset.host_size != 0); | ||
| 5569 | if (needs_bitmask) { | ||
| 5570 | // TODO: only some bits are to be undef, we cannot write with a simple memset. | ||
| 5571 | // meanwhile, ignore the write rather than stomping over valid bits. | ||
| 5572 | // https://github.com/ziglang/zig/issues/15337 | ||
| 5573 | break :undef; | ||
| 5574 | } | ||
| 5575 | const len = try o.builder.intValue(try o.lowerType(Type.usize), ret_ty.abiSize(mod)); | ||
| 5576 | _ = try self.wip.callMemSet( | ||
| 5577 | self.ret_ptr, | ||
| 5578 | ptr_ty.ptrAlignment(mod).toLlvm(), | ||
| 5579 | try o.builder.intValue(.i8, 0xaa), | ||
| 5580 | len, | ||
| 5581 | if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal, | ||
| 5582 | ); | ||
| 5583 | const owner_mod = self.dg.ownerModule(); | ||
| 5584 | if (owner_mod.valgrind) { | ||
| 5585 | try self.valgrindMarkUndef(self.ret_ptr, len); | ||
| 5586 | } | ||
| 5587 | _ = try self.wip.retVoid(); | ||
| 5588 | return .none; | ||
| 5589 | } | ||
| 5590 | |||
| 5563 | const unwrapped_operand = operand.unwrap(); | 5591 | const unwrapped_operand = operand.unwrap(); |
| 5564 | const unwrapped_ret = self.ret_ptr.unwrap(); | 5592 | const unwrapped_ret = self.ret_ptr.unwrap(); |
| 5565 | 5593 | ||
| ... | @@ -5588,8 +5616,28 @@ pub const FuncGen = struct { | ... | @@ -5588,8 +5616,28 @@ pub const FuncGen = struct { |
| 5588 | 5616 | ||
| 5589 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); | 5617 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); |
| 5590 | const operand = try self.resolveInst(un_op); | 5618 | const operand = try self.resolveInst(un_op); |
| 5619 | const val_is_undef = if (try self.air.value(un_op, mod)) |val| val.isUndefDeep(mod) else false; | ||
| 5591 | const alignment = ret_ty.abiAlignment(mod).toLlvm(); | 5620 | const alignment = ret_ty.abiAlignment(mod).toLlvm(); |
| 5592 | 5621 | ||
| 5622 | if (val_is_undef and safety) { | ||
| 5623 | const llvm_ret_ty = operand.typeOfWip(&self.wip); | ||
| 5624 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); | ||
| 5625 | const len = try o.builder.intValue(try o.lowerType(Type.usize), ret_ty.abiSize(mod)); | ||
| 5626 | _ = try self.wip.callMemSet( | ||
| 5627 | rp, | ||
| 5628 | alignment, | ||
| 5629 | try o.builder.intValue(.i8, 0xaa), | ||
| 5630 | len, | ||
| 5631 | .normal, | ||
| 5632 | ); | ||
| 5633 | const owner_mod = self.dg.ownerModule(); | ||
| 5634 | if (owner_mod.valgrind) { | ||
| 5635 | try self.valgrindMarkUndef(rp, len); | ||
| 5636 | } | ||
| 5637 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, "")); | ||
| 5638 | return .none; | ||
| 5639 | } | ||
| 5640 | |||
| 5593 | if (isByRef(ret_ty, mod)) { | 5641 | if (isByRef(ret_ty, mod)) { |
| 5594 | // operand is a pointer however self.ret_ptr is null so that means | 5642 | // operand is a pointer however self.ret_ptr is null so that means |
| 5595 | // we need to return a value. | 5643 | // we need to return a value. |
src/codegen/spirv.zig+1| ... | @@ -2177,6 +2177,7 @@ const DeclGen = struct { | ... | @@ -2177,6 +2177,7 @@ const DeclGen = struct { |
| 2177 | .cond_br => return self.airCondBr(inst), | 2177 | .cond_br => return self.airCondBr(inst), |
| 2178 | .loop => return self.airLoop(inst), | 2178 | .loop => return self.airLoop(inst), |
| 2179 | .ret => return self.airRet(inst), | 2179 | .ret => return self.airRet(inst), |
| 2180 | .ret_safe => return self.airRet(inst), // TODO | ||
| 2180 | .ret_load => return self.airRetLoad(inst), | 2181 | .ret_load => return self.airRetLoad(inst), |
| 2181 | .@"try" => try self.airTry(inst), | 2182 | .@"try" => try self.airTry(inst), |
| 2182 | .switch_br => return self.airSwitchBr(inst), | 2183 | .switch_br => return self.airSwitchBr(inst), |
src/print_air.zig+1| ... | @@ -175,6 +175,7 @@ const Writer = struct { | ... | @@ -175,6 +175,7 @@ const Writer = struct { |
| 175 | .int_from_ptr, | 175 | .int_from_ptr, |
| 176 | .int_from_bool, | 176 | .int_from_bool, |
| 177 | .ret, | 177 | .ret, |
| 178 | .ret_safe, | ||
| 178 | .ret_load, | 179 | .ret_load, |
| 179 | .is_named_enum_value, | 180 | .is_named_enum_value, |
| 180 | .tag_name, | 181 | .tag_name, |
test/behavior/undefined.zig+22| ... | @@ -97,3 +97,25 @@ test "reslice of undefined global var slice" { | ... | @@ -97,3 +97,25 @@ test "reslice of undefined global var slice" { |
| 97 | const x = buf[0..1]; | 97 | const x = buf[0..1]; |
| 98 | try @import("std").testing.expect(x.len == 1 and x[0] == 0); | 98 | try @import("std").testing.expect(x.len == 1 and x[0] == 0); |
| 99 | } | 99 | } |
| 100 | |||
| 101 | test "returned undef is 0xaa bytes when runtime safety is enabled" { | ||
| 102 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 103 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 104 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 105 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 106 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 107 | |||
| 108 | const Rect = struct { | ||
| 109 | x: f32, | ||
| 110 | fn getUndefStruct() @This() { | ||
| 111 | @setRuntimeSafety(true); | ||
| 112 | return undefined; | ||
| 113 | } | ||
| 114 | fn getUndefInt() u32 { | ||
| 115 | @setRuntimeSafety(true); | ||
| 116 | return undefined; | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | try std.testing.expect(@as(u32, @bitCast(Rect.getUndefStruct().x)) == 0xAAAAAAAA); | ||
| 120 | try std.testing.expect(Rect.getUndefInt() == 0xAAAAAAAA); | ||
| 121 | } |