| ... | @@ -1917,16 +1917,29 @@ pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: * | ... | @@ -1917,16 +1917,29 @@ pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: * |
| 1917 | } | 1917 | } |
| 1918 | | 1918 | |
| 1919 | /// operands must be integers; handles undefined. | 1919 | /// operands must be integers; handles undefined. |
| 1920 | pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value { | 1920 | pub fn bitwiseAndScalar(orig_lhs: Value, orig_rhs: Value, ty: Type, arena: Allocator, zcu: *Zcu) !Value { |
| 1921 | if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() }))); | 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) | @intFromBool(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 | |
| 1922 | if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool()); | 1935 | if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool()); |
| 1923 | | 1936 | |
| 1924 | // TODO is this a performance issue? maybe we should try the operation without | 1937 | // TODO is this a performance issue? maybe we should try the operation without |
| 1925 | // resorting to BigInt first. | 1938 | // resorting to BigInt first. |
| 1926 | var lhs_space: Value.BigIntSpace = undefined; | 1939 | var lhs_space: Value.BigIntSpace = undefined; |
| 1927 | var rhs_space: Value.BigIntSpace = undefined; | 1940 | var rhs_space: Value.BigIntSpace = undefined; |
| 1928 | const lhs_bigint = lhs.toBigInt(&lhs_space, mod); | 1941 | const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); |
| 1929 | const rhs_bigint = rhs.toBigInt(&rhs_space, mod); | 1942 | const rhs_bigint = rhs.toBigInt(&rhs_space, zcu); |
| 1930 | const limbs = try arena.alloc( | 1943 | const limbs = try arena.alloc( |
| 1931 | std.math.big.Limb, | 1944 | std.math.big.Limb, |
| 1932 | // + 1 for negatives | 1945 | // + 1 for negatives |
| ... | @@ -1934,7 +1947,25 @@ pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: | ... | @@ -1934,7 +1947,25 @@ pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: |
| 1934 | ); | 1947 | ); |
| 1935 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | 1948 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| 1936 | result_bigint.bitAnd(lhs_bigint, rhs_bigint); | 1949 | 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()); |
| 1938 | } | 1969 | } |
| 1939 | | 1970 | |
| 1940 | /// operands must be (vectors of) integers; handles undefined scalars. | 1971 | /// 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 | ... | @@ -1984,23 +2015,36 @@ pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *M |
| 1984 | } | 2015 | } |
| 1985 | | 2016 | |
| 1986 | /// operands must be integers; handles undefined. | 2017 | /// operands must be integers; handles undefined. |
| 1987 | pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value { | 2018 | pub fn bitwiseOrScalar(orig_lhs: Value, orig_rhs: Value, ty: Type, arena: Allocator, zcu: *Zcu) !Value { |
| 1988 | if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() }))); | 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) | @intFromBool(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 | |
| 1989 | if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool()); | 2033 | if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool()); |
| 1990 | | 2034 | |
| 1991 | // TODO is this a performance issue? maybe we should try the operation without | 2035 | // TODO is this a performance issue? maybe we should try the operation without |
| 1992 | // resorting to BigInt first. | 2036 | // resorting to BigInt first. |
| 1993 | var lhs_space: Value.BigIntSpace = undefined; | 2037 | var lhs_space: Value.BigIntSpace = undefined; |
| 1994 | var rhs_space: Value.BigIntSpace = undefined; | 2038 | var rhs_space: Value.BigIntSpace = undefined; |
| 1995 | const lhs_bigint = lhs.toBigInt(&lhs_space, mod); | 2039 | const lhs_bigint = lhs.toBigInt(&lhs_space, zcu); |
| 1996 | const rhs_bigint = rhs.toBigInt(&rhs_space, mod); | 2040 | const rhs_bigint = rhs.toBigInt(&rhs_space, zcu); |
| 1997 | const limbs = try arena.alloc( | 2041 | const limbs = try arena.alloc( |
| 1998 | std.math.big.Limb, | 2042 | std.math.big.Limb, |
| 1999 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | 2043 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), |
| 2000 | ); | 2044 | ); |
| 2001 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | 2045 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| 2002 | result_bigint.bitOr(lhs_bigint, rhs_bigint); | 2046 | 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()); |
| 2004 | } | 2048 | } |
| 2005 | | 2049 | |
| 2006 | /// operands must be (vectors of) integers; handles undefined scalars. | 2050 | /// operands must be (vectors of) integers; handles undefined scalars. |