Skip to content

Commit 5a4026e

Browse files
committed
Value: convert undefined values to 0xAA for bitwise operations
The operation `undefined & 0` ought to result in the value `0`, and likewise for zeroing only some bits. `std/packed_int_array.zig` tests were failing because this behavior was not implemented -- this issue was previously masked by faulty bitcast logic which turned `undefined` values into `0xAA` on pointer loads. Ideally, we would like to be able to track the undefined bits at comptime. This is related to #19634.
1 parent 42e5baa commit 5a4026e

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

src/Value.zig

+54-10
Original file line numberDiff line numberDiff line change
@@ -1917,24 +1917,55 @@ pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *
19171917
}
19181918

19191919
/// operands must be integers; handles undefined.
1920-
pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
1921-
if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
1920+
pub fn bitwiseAndScalar(orig_lhs: Value, orig_rhs: Value, ty: Type, arena: Allocator, zcu: *Zcu) !Value {
1921+
// If one operand is defined, we turn the other into `0xAA` so the bitwise AND can
1922+
// still zero out some bits.
1923+
// TODO: ideally we'd still like tracking for the undef bits. Related: #19634.
1924+
const lhs: Value, const rhs: Value = make_defined: {
1925+
const lhs_undef = orig_lhs.isUndef(zcu);
1926+
const rhs_undef = orig_rhs.isUndef(zcu);
1927+
break :make_defined switch ((@as(u2, @intFromBool(lhs_undef)) << 1) | rhs_undef) {
1928+
0b00 => .{ orig_lhs, orig_rhs },
1929+
0b01 => .{ orig_lhs, try intValueAa(ty, arena, zcu) },
1930+
0b10 => .{ try intValueAa(ty, arena, zcu), orig_rhs },
1931+
0b11 => return zcu.undefValue(ty),
1932+
};
1933+
};
1934+
19221935
if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
19231936

19241937
// TODO is this a performance issue? maybe we should try the operation without
19251938
// resorting to BigInt first.
19261939
var lhs_space: Value.BigIntSpace = undefined;
19271940
var rhs_space: Value.BigIntSpace = undefined;
1928-
const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
1929-
const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
1941+
const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1942+
const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
19301943
const limbs = try arena.alloc(
19311944
std.math.big.Limb,
19321945
// + 1 for negatives
19331946
@max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
19341947
);
19351948
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
19361949
result_bigint.bitAnd(lhs_bigint, rhs_bigint);
1937-
return mod.intValue_big(ty, result_bigint.toConst());
1950+
return zcu.intValue_big(ty, result_bigint.toConst());
1951+
}
1952+
1953+
/// Given an integer or boolean type, creates an value of that with the bit pattern 0xAA.
1954+
/// This is used to convert undef values into 0xAA when performing e.g. bitwise operations.
1955+
fn intValueAa(ty: Type, arena: Allocator, zcu: *Zcu) !Value {
1956+
if (ty.toIntern() == .bool_type) return Value.true;
1957+
const info = ty.intInfo(zcu);
1958+
1959+
const buf = try arena.alloc(u8, (info.bits + 7) / 8);
1960+
@memset(buf, 0xAA);
1961+
1962+
const limbs = try arena.alloc(
1963+
std.math.big.Limb,
1964+
std.math.big.int.calcTwosCompLimbCount(info.bits),
1965+
);
1966+
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1967+
result_bigint.readTwosComplement(buf, info.bits, zcu.getTarget().cpu.arch.endian(), info.signedness);
1968+
return zcu.intValue_big(ty, result_bigint.toConst());
19381969
}
19391970

19401971
/// operands must be (vectors of) integers; handles undefined scalars.
@@ -1984,23 +2015,36 @@ pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *M
19842015
}
19852016

19862017
/// operands must be integers; handles undefined.
1987-
pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
1988-
if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2018+
pub fn bitwiseOrScalar(orig_lhs: Value, orig_rhs: Value, ty: Type, arena: Allocator, zcu: *Zcu) !Value {
2019+
// If one operand is defined, we turn the other into `0xAA` so the bitwise AND can
2020+
// still zero out some bits.
2021+
// TODO: ideally we'd still like tracking for the undef bits. Related: #19634.
2022+
const lhs: Value, const rhs: Value = make_defined: {
2023+
const lhs_undef = orig_lhs.isUndef(zcu);
2024+
const rhs_undef = orig_rhs.isUndef(zcu);
2025+
break :make_defined switch ((@as(u2, @intFromBool(lhs_undef)) << 1) | rhs_undef) {
2026+
0b00 => .{ orig_lhs, orig_rhs },
2027+
0b01 => .{ orig_lhs, try intValueAa(ty, arena, zcu) },
2028+
0b10 => .{ try intValueAa(ty, arena, zcu), orig_rhs },
2029+
0b11 => return zcu.undefValue(ty),
2030+
};
2031+
};
2032+
19892033
if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
19902034

19912035
// TODO is this a performance issue? maybe we should try the operation without
19922036
// resorting to BigInt first.
19932037
var lhs_space: Value.BigIntSpace = undefined;
19942038
var rhs_space: Value.BigIntSpace = undefined;
1995-
const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
1996-
const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2039+
const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2040+
const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
19972041
const limbs = try arena.alloc(
19982042
std.math.big.Limb,
19992043
@max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
20002044
);
20012045
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
20022046
result_bigint.bitOr(lhs_bigint, rhs_bigint);
2003-
return mod.intValue_big(ty, result_bigint.toConst());
2047+
return zcu.intValue_big(ty, result_bigint.toConst());
20042048
}
20052049

20062050
/// operands must be (vectors of) integers; handles undefined scalars.

0 commit comments

Comments
 (0)