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`
This commit is contained in:
@@ -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));
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user