diff options
| author | Justus Klausecker <justus@klausecker.de> | 2025-08-03 13:43:03 +0200 |
|---|---|---|
| committer | Justus Klausecker <justus@klausecker.de> | 2025-08-12 16:33:57 +0200 |
| commit | d0586da18e08d0b8bdc2347fabdc0ba531901641 (patch) | |
| tree | 97ce80427e777a766cb3e3521141325f1b588e3c /src/codegen | |
| parent | 749f10af49022597d873d41df5c600e97e5c4a37 (diff) | |
| download | zig-d0586da18e08d0b8bdc2347fabdc0ba531901641.tar.gz zig-d0586da18e08d0b8bdc2347fabdc0ba531901641.zip | |
Sema: Improve comptime arithmetic undef handling
This commit expands on the foundations laid by https://github.com/ziglang/zig/pull/23177
and moves even more `Sema`-only functionality from `Value`
to `Sema.arith`. Specifically all shift and bitwise operations,
`@truncate`, `@bitReverse` and `@byteSwap` have been moved and
adapted to the new rules around `undefined`.
Especially the comptime shift operations have been basically
rewritten, fixing many open issues in the process.
New rules applied to operators:
* `<<`, `@shlExact`, `@shlWithOverflow`, `>>`, `@shrExact`: compile error if any operand is undef
* `<<|`, `~`, `^`, `@truncate`, `@bitReverse`, `@byteSwap`: return undef if any operand is undef
* `&`, `|`: Return undef if both operands are undef, turn undef into actual `0xAA` bytes otherwise
Additionally this commit canonicalizes the representation of
aggregates with all-undefined members in the `InternPool` by
disallowing them and enforcing the usage of a single typed
`undef` value instead. This reduces the amount of edge cases
and fixes a bunch of bugs related to partially undefined vecs.
List of operations directly affected by this patch:
* `<<`, `<<|`, `@shlExact`, `@shlWithOverflow`
* `>>`, `@shrExact`
* `&`, `|`, `~`, `^` and their atomic rmw + reduce pendants
* `@truncate`, `@bitReverse`, `@byteSwap`
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 10 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 12 |
2 files changed, 11 insertions, 11 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 48ca1a07d8..295eecea29 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1012,7 +1012,7 @@ pub const DeclGen = struct { }; const ty = val.typeOf(zcu); - if (val.isUndefDeep(zcu)) return dg.renderUndefValue(w, ty, location); + if (val.isUndef(zcu)) return dg.renderUndefValue(w, ty, location); const ctype = try dg.ctypeFromType(ty, location.toCTypeKind()); switch (ip.indexToKey(val.toIntern())) { // types, not values @@ -4216,7 +4216,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const ptr_val = try f.resolveInst(bin_op.lhs); const src_ty = f.typeOf(bin_op.rhs); - const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndefDeep(zcu) else false; + const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndef(zcu) else false; const w = &f.object.code.writer; if (val_is_undef) { @@ -4942,7 +4942,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)]; const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - const operand_is_undef = if (try f.air.value(pl_op.operand, pt)) |v| v.isUndefDeep(zcu) else false; + const operand_is_undef = if (try f.air.value(pl_op.operand, pt)) |v| v.isUndef(zcu) else false; if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{pl_op.operand}); @@ -7117,7 +7117,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const value = try f.resolveInst(bin_op.rhs); const elem_ty = f.typeOf(bin_op.rhs); const elem_abi_size = elem_ty.abiSize(zcu); - const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(zcu) else false; + const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndef(zcu) else false; const w = &f.object.code.writer; if (val_is_undef) { @@ -8338,7 +8338,7 @@ fn formatIntLiteral(data: FormatIntLiteralContext, w: *std.io.Writer) std.io.Wri defer allocator.free(undef_limbs); var int_buf: Value.BigIntSpace = undefined; - const int = if (data.val.isUndefDeep(zcu)) blk: { + const int = if (data.val.isUndef(zcu)) blk: { undef_limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)) catch return error.WriteFailed; @memset(undef_limbs, undefPattern(BigIntLimb)); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 15b6ce7f12..e03d4e39c3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3575,7 +3575,7 @@ pub const Object = struct { const val = Value.fromInterned(arg_val); const val_key = ip.indexToKey(val.toIntern()); - if (val.isUndefDeep(zcu)) return o.builder.undefConst(llvm_int_ty); + if (val.isUndef(zcu)) return o.builder.undefConst(llvm_int_ty); const ty = Type.fromInterned(val_key.typeOf()); switch (val_key) { @@ -3666,7 +3666,7 @@ pub const Object = struct { const val = Value.fromInterned(arg_val); const val_key = ip.indexToKey(val.toIntern()); - if (val.isUndefDeep(zcu)) { + if (val.isUndef(zcu)) { return o.builder.undefConst(try o.lowerType(pt, Type.fromInterned(val_key.typeOf()))); } @@ -5574,7 +5574,7 @@ pub const FuncGen = struct { const ptr_ty = try pt.singleMutPtrType(ret_ty); const operand = try self.resolveInst(un_op); - const val_is_undef = if (try self.air.value(un_op, pt)) |val| val.isUndefDeep(zcu) else false; + const val_is_undef = if (try self.air.value(un_op, pt)) |val| val.isUndef(zcu) else false; if (val_is_undef and safety) undef: { const ptr_info = ptr_ty.ptrInfo(zcu); const needs_bitmask = (ptr_info.packed_offset.host_size != 0); @@ -5629,7 +5629,7 @@ pub const FuncGen = struct { const abi_ret_ty = try lowerFnRetTy(o, pt, fn_info); const operand = try self.resolveInst(un_op); - const val_is_undef = if (try self.air.value(un_op, pt)) |val| val.isUndefDeep(zcu) else false; + const val_is_undef = if (try self.air.value(un_op, pt)) |val| val.isUndef(zcu) else false; const alignment = ret_ty.abiAlignment(zcu).toLlvm(); if (val_is_undef and safety) { @@ -9673,7 +9673,7 @@ pub const FuncGen = struct { const ptr_ty = self.typeOf(bin_op.lhs); const operand_ty = ptr_ty.childType(zcu); - const val_is_undef = if (try self.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(zcu) else false; + const val_is_undef = if (try self.air.value(bin_op.rhs, pt)) |val| val.isUndef(zcu) else false; if (val_is_undef) { const owner_mod = self.ng.ownerModule(); @@ -10014,7 +10014,7 @@ pub const FuncGen = struct { self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); if (try self.air.value(bin_op.rhs, pt)) |elem_val| { - if (elem_val.isUndefDeep(zcu)) { + if (elem_val.isUndef(zcu)) { // Even if safety is disabled, we still emit a memset to undefined since it conveys // extra information to LLVM. However, safety makes the difference between using // 0xaa or actual undefined for the fill byte. |
