master
   1//! This file encapsules all arithmetic operations on comptime-known integers, floats, and vectors.
   2//!
   3//! It is only used in cases where both operands are comptime-known; a single comptime-known operand
   4//! is handled directly by `Sema.zig`.
   5//!
   6//! All public functions sanitize their inputs to the best of their knowledge.
   7//!
   8//! Functions starting with `int`, `comptimeInt`, or `float` are low-level primitives which operate
   9//! on defined scalar values; generally speaking, they are at the bottom of this file and non-`pub`.
  10
  11/// Asserts that `ty` is a scalar integer type, and that `prev_val` is of type `ty`.
  12/// Returns a value one greater than `prev_val`. If this would overflow `ty,` then the
  13/// return value has `overflow` set, and `val` is instead a `comptime_int`.
  14pub fn incrementDefinedInt(
  15    sema: *Sema,
  16    ty: Type,
  17    prev_val: Value,
  18) CompileError!struct { overflow: bool, val: Value } {
  19    const pt = sema.pt;
  20    const zcu = pt.zcu;
  21    assert(prev_val.typeOf(zcu).toIntern() == ty.toIntern());
  22    assert(!prev_val.isUndef(zcu));
  23    const res = try intAdd(sema, prev_val, try pt.intValue(ty, 1), ty);
  24    return .{ .overflow = res.overflow, .val = res.val };
  25}
  26
  27/// `val` is of type `ty`.
  28/// `ty` is a float, comptime_float, or vector thereof.
  29pub fn negateFloat(
  30    sema: *Sema,
  31    ty: Type,
  32    val: Value,
  33) CompileError!Value {
  34    const pt = sema.pt;
  35    const zcu = pt.zcu;
  36    if (val.isUndef(zcu)) return val;
  37    switch (ty.zigTypeTag(zcu)) {
  38        .vector => {
  39            const scalar_ty = ty.childType(zcu);
  40            const len = ty.vectorLen(zcu);
  41            const result_elems = try sema.arena.alloc(InternPool.Index, len);
  42            for (result_elems, 0..) |*result_elem, elem_idx| {
  43                const elem = try val.elemValue(pt, elem_idx);
  44                if (elem.isUndef(zcu)) {
  45                    result_elem.* = elem.toIntern();
  46                } else {
  47                    result_elem.* = (try floatNeg(sema, elem, scalar_ty)).toIntern();
  48                }
  49            }
  50            return pt.aggregateValue(ty, result_elems);
  51        },
  52        .float, .comptime_float => return floatNeg(sema, val, ty),
  53        else => unreachable,
  54    }
  55}
  56
  57/// Wraps on integers, but accepts floats.
  58/// `lhs_val` and `rhs_val` are both of type `ty`.
  59/// `ty` is an int, float, comptime_int, or comptime_float; *not* a vector.
  60pub fn addMaybeWrap(
  61    sema: *Sema,
  62    ty: Type,
  63    lhs: Value,
  64    rhs: Value,
  65) CompileError!Value {
  66    const zcu = sema.pt.zcu;
  67    if (lhs.isUndef(zcu)) return lhs;
  68    if (rhs.isUndef(zcu)) return rhs;
  69    switch (ty.zigTypeTag(zcu)) {
  70        .int, .comptime_int => return (try intAddWithOverflow(sema, lhs, rhs, ty)).wrapped_result,
  71        .float, .comptime_float => return floatAdd(sema, lhs, rhs, ty),
  72        else => unreachable,
  73    }
  74}
  75
  76/// Wraps on integers, but accepts floats.
  77/// `lhs_val` and `rhs_val` are both of type `ty`.
  78/// `ty` is an int, float, comptime_int, or comptime_float; *not* a vector.
  79pub fn subMaybeWrap(
  80    sema: *Sema,
  81    ty: Type,
  82    lhs: Value,
  83    rhs: Value,
  84) CompileError!Value {
  85    const zcu = sema.pt.zcu;
  86    if (lhs.isUndef(zcu)) return lhs;
  87    if (rhs.isUndef(zcu)) return rhs;
  88    switch (ty.zigTypeTag(zcu)) {
  89        .int, .comptime_int => return (try intSubWithOverflow(sema, lhs, rhs, ty)).wrapped_result,
  90        .float, .comptime_float => return floatSub(sema, lhs, rhs, ty),
  91        else => unreachable,
  92    }
  93}
  94
  95/// Wraps on integers, but accepts floats.
  96/// `lhs_val` and `rhs_val` are both of type `ty`.
  97/// `ty` is an int, float, comptime_int, or comptime_float; *not* a vector.
  98pub fn mulMaybeWrap(
  99    sema: *Sema,
 100    ty: Type,
 101    lhs: Value,
 102    rhs: Value,
 103) CompileError!Value {
 104    const zcu = sema.pt.zcu;
 105    if (lhs.isUndef(zcu)) return lhs;
 106    if (rhs.isUndef(zcu)) return rhs;
 107    switch (ty.zigTypeTag(zcu)) {
 108        .int, .comptime_int => return (try intMulWithOverflow(sema, lhs, rhs, ty)).wrapped_result,
 109        .float, .comptime_float => return floatMul(sema, lhs, rhs, ty),
 110        else => unreachable,
 111    }
 112}
 113
 114/// `lhs` and `rhs` are of type `ty`.
 115/// `ty` is an int, comptime_int, or vector thereof.
 116pub fn addWithOverflow(
 117    sema: *Sema,
 118    ty: Type,
 119    lhs: Value,
 120    rhs: Value,
 121) CompileError!Value.OverflowArithmeticResult {
 122    const pt = sema.pt;
 123    const zcu = pt.zcu;
 124    switch (ty.zigTypeTag(zcu)) {
 125        .int, .comptime_int => return addWithOverflowScalar(sema, ty, lhs, rhs),
 126        .vector => {
 127            const scalar_ty = ty.childType(zcu);
 128            const len = ty.vectorLen(zcu);
 129            switch (scalar_ty.zigTypeTag(zcu)) {
 130                .int, .comptime_int => {},
 131                else => unreachable,
 132            }
 133            const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
 134            const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
 135            for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
 136                const lhs_elem = try lhs.elemValue(pt, elem_idx);
 137                const rhs_elem = try rhs.elemValue(pt, elem_idx);
 138                const elem_result = try addWithOverflowScalar(sema, scalar_ty, lhs_elem, rhs_elem);
 139                ob.* = elem_result.overflow_bit.toIntern();
 140                wr.* = elem_result.wrapped_result.toIntern();
 141            }
 142            return .{
 143                .overflow_bit = try pt.aggregateValue(
 144                    try pt.vectorType(.{ .len = @intCast(overflow_bits.len), .child = .u1_type }),
 145                    overflow_bits,
 146                ),
 147                .wrapped_result = try pt.aggregateValue(ty, wrapped_results),
 148            };
 149        },
 150        else => unreachable,
 151    }
 152}
 153fn addWithOverflowScalar(
 154    sema: *Sema,
 155    ty: Type,
 156    lhs: Value,
 157    rhs: Value,
 158) CompileError!Value.OverflowArithmeticResult {
 159    const pt = sema.pt;
 160    const zcu = pt.zcu;
 161    switch (ty.zigTypeTag(zcu)) {
 162        .int, .comptime_int => {},
 163        else => unreachable,
 164    }
 165    if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return .{
 166        .overflow_bit = .undef_u1,
 167        .wrapped_result = try pt.undefValue(ty),
 168    };
 169    return intAddWithOverflow(sema, lhs, rhs, ty);
 170}
 171
 172/// `lhs` and `rhs` are of type `ty`.
 173/// `ty` is an int, comptime_int, or vector thereof.
 174pub fn subWithOverflow(
 175    sema: *Sema,
 176    ty: Type,
 177    lhs: Value,
 178    rhs: Value,
 179) CompileError!Value.OverflowArithmeticResult {
 180    const pt = sema.pt;
 181    const zcu = pt.zcu;
 182    switch (ty.zigTypeTag(zcu)) {
 183        .int, .comptime_int => return subWithOverflowScalar(sema, ty, lhs, rhs),
 184        .vector => {
 185            const scalar_ty = ty.childType(zcu);
 186            const len = ty.vectorLen(zcu);
 187            switch (scalar_ty.zigTypeTag(zcu)) {
 188                .int, .comptime_int => {},
 189                else => unreachable,
 190            }
 191            const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
 192            const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
 193            for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
 194                const lhs_elem = try lhs.elemValue(pt, elem_idx);
 195                const rhs_elem = try rhs.elemValue(pt, elem_idx);
 196                const elem_result = try subWithOverflowScalar(sema, scalar_ty, lhs_elem, rhs_elem);
 197                ob.* = elem_result.overflow_bit.toIntern();
 198                wr.* = elem_result.wrapped_result.toIntern();
 199            }
 200            return .{
 201                .overflow_bit = try pt.aggregateValue(
 202                    try pt.vectorType(.{ .len = @intCast(overflow_bits.len), .child = .u1_type }),
 203                    overflow_bits,
 204                ),
 205                .wrapped_result = try pt.aggregateValue(ty, wrapped_results),
 206            };
 207        },
 208        else => unreachable,
 209    }
 210}
 211fn subWithOverflowScalar(
 212    sema: *Sema,
 213    ty: Type,
 214    lhs: Value,
 215    rhs: Value,
 216) CompileError!Value.OverflowArithmeticResult {
 217    const pt = sema.pt;
 218    const zcu = pt.zcu;
 219    switch (ty.zigTypeTag(zcu)) {
 220        .int, .comptime_int => {},
 221        else => unreachable,
 222    }
 223    if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return .{
 224        .overflow_bit = .undef_u1,
 225        .wrapped_result = try pt.undefValue(ty),
 226    };
 227    return intSubWithOverflow(sema, lhs, rhs, ty);
 228}
 229
 230/// `lhs` and `rhs` are of type `ty`.
 231/// `ty` is an int, comptime_int, or vector thereof.
 232pub fn mulWithOverflow(
 233    sema: *Sema,
 234    ty: Type,
 235    lhs: Value,
 236    rhs: Value,
 237) CompileError!Value.OverflowArithmeticResult {
 238    const pt = sema.pt;
 239    const zcu = pt.zcu;
 240    switch (ty.zigTypeTag(zcu)) {
 241        .int, .comptime_int => return mulWithOverflowScalar(sema, ty, lhs, rhs),
 242        .vector => {
 243            const scalar_ty = ty.childType(zcu);
 244            const len = ty.vectorLen(zcu);
 245            switch (scalar_ty.zigTypeTag(zcu)) {
 246                .int, .comptime_int => {},
 247                else => unreachable,
 248            }
 249            const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
 250            const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
 251            for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
 252                const lhs_elem = try lhs.elemValue(pt, elem_idx);
 253                const rhs_elem = try rhs.elemValue(pt, elem_idx);
 254                const elem_result = try mulWithOverflowScalar(sema, scalar_ty, lhs_elem, rhs_elem);
 255                ob.* = elem_result.overflow_bit.toIntern();
 256                wr.* = elem_result.wrapped_result.toIntern();
 257            }
 258            return .{
 259                .overflow_bit = try pt.aggregateValue(
 260                    try pt.vectorType(.{ .len = @intCast(overflow_bits.len), .child = .u1_type }),
 261                    overflow_bits,
 262                ),
 263                .wrapped_result = try pt.aggregateValue(ty, wrapped_results),
 264            };
 265        },
 266        else => unreachable,
 267    }
 268}
 269fn mulWithOverflowScalar(
 270    sema: *Sema,
 271    ty: Type,
 272    lhs: Value,
 273    rhs: Value,
 274) CompileError!Value.OverflowArithmeticResult {
 275    const pt = sema.pt;
 276    const zcu = pt.zcu;
 277    switch (ty.zigTypeTag(zcu)) {
 278        .int, .comptime_int => {},
 279        else => unreachable,
 280    }
 281    if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return .{
 282        .overflow_bit = .undef_u1,
 283        .wrapped_result = try pt.undefValue(ty),
 284    };
 285    return intMulWithOverflow(sema, lhs, rhs, ty);
 286}
 287
 288/// Applies the `+` operator to comptime-known values.
 289/// `lhs_val` and `rhs_val` are both of type `ty`.
 290/// `ty` is an int, float, comptime_int, comptime_float, or vector.
 291pub fn add(
 292    sema: *Sema,
 293    block: *Block,
 294    ty: Type,
 295    lhs_val: Value,
 296    rhs_val: Value,
 297    src: LazySrcLoc,
 298    lhs_src: LazySrcLoc,
 299    rhs_src: LazySrcLoc,
 300) CompileError!Value {
 301    const pt = sema.pt;
 302    const zcu = pt.zcu;
 303    switch (ty.zigTypeTag(zcu)) {
 304        .int, .comptime_int => return addScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, true, null),
 305        .float, .comptime_float => return addScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, false, null),
 306        .vector => {
 307            const elem_ty = ty.childType(zcu);
 308            const len = ty.vectorLen(zcu);
 309
 310            const is_int = switch (elem_ty.zigTypeTag(zcu)) {
 311                .int, .comptime_int => true,
 312                .float, .comptime_float => false,
 313                else => unreachable,
 314            };
 315
 316            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 317            for (elem_vals, 0..) |*result_elem, elem_idx| {
 318                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 319                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 320                result_elem.* = (try addScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, is_int, elem_idx)).toIntern();
 321            }
 322            return pt.aggregateValue(ty, elem_vals);
 323        },
 324        else => unreachable,
 325    }
 326}
 327fn addScalar(
 328    sema: *Sema,
 329    block: *Block,
 330    ty: Type,
 331    lhs_val: Value,
 332    rhs_val: Value,
 333    src: LazySrcLoc,
 334    lhs_src: LazySrcLoc,
 335    rhs_src: LazySrcLoc,
 336    is_int: bool,
 337    vec_idx: ?usize,
 338) CompileError!Value {
 339    const pt = sema.pt;
 340    const zcu = pt.zcu;
 341
 342    if (is_int) {
 343        if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
 344        if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
 345        const res = try intAdd(sema, lhs_val, rhs_val, ty);
 346        if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
 347        return res.val;
 348    } else {
 349        if (lhs_val.isUndef(zcu)) return lhs_val;
 350        if (rhs_val.isUndef(zcu)) return rhs_val;
 351        return floatAdd(sema, lhs_val, rhs_val, ty);
 352    }
 353}
 354
 355/// Applies the `+%` operator to comptime-known values.
 356/// `lhs_val` and `rhs_val` are both of type `ty`.
 357/// `ty` is an int, comptime_int, or vector thereof.
 358pub fn addWrap(
 359    sema: *Sema,
 360    ty: Type,
 361    lhs_val: Value,
 362    rhs_val: Value,
 363) CompileError!Value {
 364    const pt = sema.pt;
 365    const zcu = pt.zcu;
 366    switch (ty.zigTypeTag(zcu)) {
 367        .vector => {
 368            const elem_ty = ty.childType(zcu);
 369            const len = ty.vectorLen(zcu);
 370
 371            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 372            for (elem_vals, 0..) |*result_elem, elem_idx| {
 373                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 374                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 375                result_elem.* = (try addWrapScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
 376            }
 377            return pt.aggregateValue(ty, elem_vals);
 378        },
 379        else => return addWrapScalar(sema, ty, lhs_val, rhs_val),
 380    }
 381}
 382fn addWrapScalar(
 383    sema: *Sema,
 384    ty: Type,
 385    lhs_val: Value,
 386    rhs_val: Value,
 387) CompileError!Value {
 388    return (try addWithOverflowScalar(sema, ty, lhs_val, rhs_val)).wrapped_result;
 389}
 390
 391/// Applies the `+|` operator to comptime-known values.
 392/// `lhs_val` and `rhs_val` are both of type `ty`.
 393/// `ty` is an int, comptime_int, or vector thereof.
 394pub fn addSat(
 395    sema: *Sema,
 396    ty: Type,
 397    lhs_val: Value,
 398    rhs_val: Value,
 399) CompileError!Value {
 400    const pt = sema.pt;
 401    const zcu = pt.zcu;
 402    switch (ty.zigTypeTag(zcu)) {
 403        .vector => {
 404            const elem_ty = ty.childType(zcu);
 405            const len = ty.vectorLen(zcu);
 406
 407            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 408            for (elem_vals, 0..) |*result_elem, elem_idx| {
 409                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 410                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 411                result_elem.* = (try addSatScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
 412            }
 413            return pt.aggregateValue(ty, elem_vals);
 414        },
 415        else => return addSatScalar(sema, ty, lhs_val, rhs_val),
 416    }
 417}
 418fn addSatScalar(
 419    sema: *Sema,
 420    ty: Type,
 421    lhs_val: Value,
 422    rhs_val: Value,
 423) CompileError!Value {
 424    const pt = sema.pt;
 425    const zcu = pt.zcu;
 426    const is_comptime_int = switch (ty.zigTypeTag(zcu)) {
 427        .int => false,
 428        .comptime_int => true,
 429        else => unreachable,
 430    };
 431    if (lhs_val.isUndef(zcu)) return lhs_val;
 432    if (rhs_val.isUndef(zcu)) return rhs_val;
 433    if (is_comptime_int) {
 434        const res = try intAdd(sema, lhs_val, rhs_val, ty);
 435        assert(!res.overflow);
 436        return res.val;
 437    } else {
 438        return intAddSat(sema, lhs_val, rhs_val, ty);
 439    }
 440}
 441
 442/// Applies the `-` operator to comptime-known values.
 443/// `lhs_val` and `rhs_val` are both of type `ty`.
 444/// `ty` is an int, float, comptime_int, comptime_float, or vector.
 445pub fn sub(
 446    sema: *Sema,
 447    block: *Block,
 448    ty: Type,
 449    lhs_val: Value,
 450    rhs_val: Value,
 451    src: LazySrcLoc,
 452    lhs_src: LazySrcLoc,
 453    rhs_src: LazySrcLoc,
 454) CompileError!Value {
 455    const pt = sema.pt;
 456    const zcu = pt.zcu;
 457    switch (ty.zigTypeTag(zcu)) {
 458        .int, .comptime_int => return subScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, true, null),
 459        .float, .comptime_float => return subScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, false, null),
 460        .vector => {
 461            const elem_ty = ty.childType(zcu);
 462            const len = ty.vectorLen(zcu);
 463
 464            const is_int = switch (elem_ty.zigTypeTag(zcu)) {
 465                .int, .comptime_int => true,
 466                .float, .comptime_float => false,
 467                else => unreachable,
 468            };
 469
 470            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 471            for (elem_vals, 0..) |*result_elem, elem_idx| {
 472                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 473                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 474                result_elem.* = (try subScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, is_int, elem_idx)).toIntern();
 475            }
 476            return pt.aggregateValue(ty, elem_vals);
 477        },
 478        else => unreachable,
 479    }
 480}
 481fn subScalar(
 482    sema: *Sema,
 483    block: *Block,
 484    ty: Type,
 485    lhs_val: Value,
 486    rhs_val: Value,
 487    src: LazySrcLoc,
 488    lhs_src: LazySrcLoc,
 489    rhs_src: LazySrcLoc,
 490    is_int: bool,
 491    vec_idx: ?usize,
 492) CompileError!Value {
 493    const pt = sema.pt;
 494    const zcu = pt.zcu;
 495
 496    if (is_int) {
 497        if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
 498        if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
 499        const res = try intSub(sema, lhs_val, rhs_val, ty);
 500        if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
 501        return res.val;
 502    } else {
 503        if (lhs_val.isUndef(zcu)) return lhs_val;
 504        if (rhs_val.isUndef(zcu)) return rhs_val;
 505        return floatSub(sema, lhs_val, rhs_val, ty);
 506    }
 507}
 508
 509/// Applies the `-%` operator to comptime-known values.
 510/// `lhs_val` and `rhs_val` are both of type `ty`.
 511/// `ty` is an int, comptime_int, or vector thereof.
 512pub fn subWrap(
 513    sema: *Sema,
 514    ty: Type,
 515    lhs_val: Value,
 516    rhs_val: Value,
 517) CompileError!Value {
 518    const pt = sema.pt;
 519    const zcu = pt.zcu;
 520    switch (ty.zigTypeTag(zcu)) {
 521        .vector => {
 522            const elem_ty = ty.childType(zcu);
 523            const len = ty.vectorLen(zcu);
 524
 525            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 526            for (elem_vals, 0..) |*result_elem, elem_idx| {
 527                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 528                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 529                result_elem.* = (try subWrapScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
 530            }
 531            return pt.aggregateValue(ty, elem_vals);
 532        },
 533        else => return subWrapScalar(sema, ty, lhs_val, rhs_val),
 534    }
 535}
 536fn subWrapScalar(
 537    sema: *Sema,
 538    ty: Type,
 539    lhs_val: Value,
 540    rhs_val: Value,
 541) CompileError!Value {
 542    const pt = sema.pt;
 543    const zcu = pt.zcu;
 544    switch (ty.zigTypeTag(zcu)) {
 545        .int, .comptime_int => {},
 546        else => unreachable,
 547    }
 548    if (lhs_val.isUndef(zcu)) return lhs_val;
 549    if (rhs_val.isUndef(zcu)) return rhs_val;
 550    const result = try intSubWithOverflow(sema, lhs_val, rhs_val, ty);
 551    return result.wrapped_result;
 552}
 553
 554/// Applies the `-|` operator to comptime-known values.
 555/// `lhs_val` and `rhs_val` are both of type `ty`.
 556/// `ty` is an int, comptime_int, or vector thereof.
 557pub fn subSat(
 558    sema: *Sema,
 559    ty: Type,
 560    lhs_val: Value,
 561    rhs_val: Value,
 562) CompileError!Value {
 563    const pt = sema.pt;
 564    const zcu = pt.zcu;
 565    switch (ty.zigTypeTag(zcu)) {
 566        .vector => {
 567            const elem_ty = ty.childType(zcu);
 568            const len = ty.vectorLen(zcu);
 569
 570            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 571            for (elem_vals, 0..) |*result_elem, elem_idx| {
 572                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 573                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 574                result_elem.* = (try subSatScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
 575            }
 576            return pt.aggregateValue(ty, elem_vals);
 577        },
 578        else => return subSatScalar(sema, ty, lhs_val, rhs_val),
 579    }
 580}
 581fn subSatScalar(
 582    sema: *Sema,
 583    ty: Type,
 584    lhs_val: Value,
 585    rhs_val: Value,
 586) CompileError!Value {
 587    const pt = sema.pt;
 588    const zcu = pt.zcu;
 589    const is_comptime_int = switch (ty.zigTypeTag(zcu)) {
 590        .int => false,
 591        .comptime_int => true,
 592        else => unreachable,
 593    };
 594    if (lhs_val.isUndef(zcu)) return lhs_val;
 595    if (rhs_val.isUndef(zcu)) return rhs_val;
 596    if (is_comptime_int) {
 597        const res = try intSub(sema, lhs_val, rhs_val, ty);
 598        assert(!res.overflow);
 599        return res.val;
 600    } else {
 601        return intSubSat(sema, lhs_val, rhs_val, ty);
 602    }
 603}
 604
 605/// Applies the `*` operator to comptime-known values.
 606/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
 607/// `ty` is an int, float, comptime_int, comptime_float, or vector.
 608pub fn mul(
 609    sema: *Sema,
 610    block: *Block,
 611    ty: Type,
 612    lhs_val: Value,
 613    rhs_val: Value,
 614    src: LazySrcLoc,
 615    lhs_src: LazySrcLoc,
 616    rhs_src: LazySrcLoc,
 617) CompileError!Value {
 618    const pt = sema.pt;
 619    const zcu = pt.zcu;
 620    switch (ty.zigTypeTag(zcu)) {
 621        .int, .comptime_int => return mulScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, true, null),
 622        .float, .comptime_float => return mulScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, false, null),
 623        .vector => {
 624            const elem_ty = ty.childType(zcu);
 625            const len = ty.vectorLen(zcu);
 626
 627            const is_int = switch (elem_ty.zigTypeTag(zcu)) {
 628                .int, .comptime_int => true,
 629                .float, .comptime_float => false,
 630                else => unreachable,
 631            };
 632
 633            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 634            for (elem_vals, 0..) |*result_elem, elem_idx| {
 635                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 636                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 637                result_elem.* = (try mulScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, is_int, elem_idx)).toIntern();
 638            }
 639            return pt.aggregateValue(ty, elem_vals);
 640        },
 641        else => unreachable,
 642    }
 643}
 644fn mulScalar(
 645    sema: *Sema,
 646    block: *Block,
 647    ty: Type,
 648    lhs_val: Value,
 649    rhs_val: Value,
 650    src: LazySrcLoc,
 651    lhs_src: LazySrcLoc,
 652    rhs_src: LazySrcLoc,
 653    is_int: bool,
 654    vec_idx: ?usize,
 655) CompileError!Value {
 656    const pt = sema.pt;
 657    const zcu = pt.zcu;
 658
 659    if (is_int) {
 660        if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
 661        if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
 662        const res = try intMul(sema, lhs_val, rhs_val, ty);
 663        if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
 664        return res.val;
 665    } else {
 666        if (lhs_val.isUndef(zcu)) return lhs_val;
 667        if (rhs_val.isUndef(zcu)) return rhs_val;
 668        return floatMul(sema, lhs_val, rhs_val, ty);
 669    }
 670}
 671
 672/// Applies the `*%` operator to comptime-known values.
 673/// `lhs_val` and `rhs_val` are both of type `ty`.
 674/// `ty` is an int, comptime_int, or vector thereof.
 675pub fn mulWrap(
 676    sema: *Sema,
 677    ty: Type,
 678    lhs_val: Value,
 679    rhs_val: Value,
 680) CompileError!Value {
 681    const pt = sema.pt;
 682    const zcu = pt.zcu;
 683    switch (ty.zigTypeTag(zcu)) {
 684        .vector => {
 685            const elem_ty = ty.childType(zcu);
 686            const len = ty.vectorLen(zcu);
 687
 688            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 689            for (elem_vals, 0..) |*result_elem, elem_idx| {
 690                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 691                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 692                result_elem.* = (try mulWrapScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
 693            }
 694            return pt.aggregateValue(ty, elem_vals);
 695        },
 696        else => return mulWrapScalar(sema, ty, lhs_val, rhs_val),
 697    }
 698}
 699fn mulWrapScalar(
 700    sema: *Sema,
 701    ty: Type,
 702    lhs_val: Value,
 703    rhs_val: Value,
 704) CompileError!Value {
 705    const pt = sema.pt;
 706    const zcu = pt.zcu;
 707    switch (ty.zigTypeTag(zcu)) {
 708        .int, .comptime_int => {},
 709        else => unreachable,
 710    }
 711    if (lhs_val.isUndef(zcu)) return lhs_val;
 712    if (rhs_val.isUndef(zcu)) return rhs_val;
 713    const result = try intMulWithOverflow(sema, lhs_val, rhs_val, ty);
 714    return result.wrapped_result;
 715}
 716
 717/// Applies the `*|` operator to comptime-known values.
 718/// `lhs_val` and `rhs_val` are both of type `ty`.
 719/// `ty` is an int, comptime_int, or vector thereof.
 720pub fn mulSat(
 721    sema: *Sema,
 722    ty: Type,
 723    lhs_val: Value,
 724    rhs_val: Value,
 725) CompileError!Value {
 726    const pt = sema.pt;
 727    const zcu = pt.zcu;
 728    switch (ty.zigTypeTag(zcu)) {
 729        .vector => {
 730            const elem_ty = ty.childType(zcu);
 731            const len = ty.vectorLen(zcu);
 732
 733            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 734            for (elem_vals, 0..) |*result_elem, elem_idx| {
 735                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 736                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 737                result_elem.* = (try mulSatScalar(sema, elem_ty, lhs_elem, rhs_elem)).toIntern();
 738            }
 739            return pt.aggregateValue(ty, elem_vals);
 740        },
 741        else => return mulSatScalar(sema, ty, lhs_val, rhs_val),
 742    }
 743}
 744fn mulSatScalar(
 745    sema: *Sema,
 746    ty: Type,
 747    lhs_val: Value,
 748    rhs_val: Value,
 749) CompileError!Value {
 750    const pt = sema.pt;
 751    const zcu = pt.zcu;
 752    const is_comptime_int = switch (ty.zigTypeTag(zcu)) {
 753        .int => false,
 754        .comptime_int => true,
 755        else => unreachable,
 756    };
 757    if (lhs_val.isUndef(zcu)) return lhs_val;
 758    if (rhs_val.isUndef(zcu)) return rhs_val;
 759    if (is_comptime_int) {
 760        const res = try intMul(sema, lhs_val, rhs_val, ty);
 761        assert(!res.overflow);
 762        return res.val;
 763    } else {
 764        return intMulSat(sema, lhs_val, rhs_val, ty);
 765    }
 766}
 767
 768pub const DivOp = enum { div, div_trunc, div_floor, div_exact };
 769
 770/// Applies the `/` operator to comptime-known values.
 771/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
 772/// `ty` is an int, float, comptime_int, comptime_float, or vector.
 773pub fn div(
 774    sema: *Sema,
 775    block: *Block,
 776    ty: Type,
 777    lhs_val: Value,
 778    rhs_val: Value,
 779    src: LazySrcLoc,
 780    lhs_src: LazySrcLoc,
 781    rhs_src: LazySrcLoc,
 782    op: DivOp,
 783) CompileError!Value {
 784    const pt = sema.pt;
 785    const zcu = pt.zcu;
 786    switch (ty.zigTypeTag(zcu)) {
 787        .int, .comptime_int => return divScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, true, null),
 788        .float, .comptime_float => return divScalar(sema, block, ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, false, null),
 789        .vector => {
 790            const elem_ty = ty.childType(zcu);
 791            const len = ty.vectorLen(zcu);
 792
 793            const is_int = switch (elem_ty.zigTypeTag(zcu)) {
 794                .int, .comptime_int => true,
 795                .float, .comptime_float => false,
 796                else => unreachable,
 797            };
 798
 799            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 800            for (elem_vals, 0..) |*result_elem, elem_idx| {
 801                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 802                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 803                result_elem.* = (try divScalar(sema, block, elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, op, is_int, elem_idx)).toIntern();
 804            }
 805            return pt.aggregateValue(ty, elem_vals);
 806        },
 807        else => unreachable,
 808    }
 809}
 810fn divScalar(
 811    sema: *Sema,
 812    block: *Block,
 813    ty: Type,
 814    lhs_val: Value,
 815    rhs_val: Value,
 816    src: LazySrcLoc,
 817    lhs_src: LazySrcLoc,
 818    rhs_src: LazySrcLoc,
 819    op: DivOp,
 820    is_int: bool,
 821    vec_idx: ?usize,
 822) CompileError!Value {
 823    const pt = sema.pt;
 824    const zcu = pt.zcu;
 825
 826    if (is_int) {
 827        if (rhs_val.eqlScalarNum(.zero_comptime_int, zcu)) return sema.failWithDivideByZero(block, rhs_src);
 828
 829        if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
 830        if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
 831
 832        switch (op) {
 833            .div, .div_trunc => {
 834                const res = try intDivTrunc(sema, lhs_val, rhs_val, ty);
 835                if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
 836                return res.val;
 837            },
 838            .div_floor => {
 839                const res = try intDivFloor(sema, lhs_val, rhs_val, ty);
 840                if (res.overflow) return sema.failWithIntegerOverflow(block, src, ty, res.val, vec_idx);
 841                return res.val;
 842            },
 843            .div_exact => switch (try intDivExact(sema, lhs_val, rhs_val, ty)) {
 844                .remainder => return sema.fail(block, src, "exact division produced remainder", .{}),
 845                .overflow => |val| return sema.failWithIntegerOverflow(block, src, ty, val, vec_idx),
 846                .success => |val| return val,
 847            },
 848        }
 849    } else {
 850        const allow_div_zero = switch (op) {
 851            .div, .div_trunc, .div_floor => ty.toIntern() != .comptime_float_type and block.float_mode == .strict,
 852            .div_exact => false,
 853        };
 854        if (!allow_div_zero) {
 855            if (rhs_val.eqlScalarNum(.zero_comptime_int, zcu)) return sema.failWithDivideByZero(block, rhs_src);
 856        }
 857
 858        const can_exhibit_ib = !allow_div_zero or op == .div_exact;
 859        if (can_exhibit_ib) {
 860            if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
 861            if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
 862        } else {
 863            if (lhs_val.isUndef(zcu)) return lhs_val;
 864            if (rhs_val.isUndef(zcu)) return rhs_val;
 865        }
 866
 867        switch (op) {
 868            .div => return floatDiv(sema, lhs_val, rhs_val, ty),
 869            .div_trunc => return floatDivTrunc(sema, lhs_val, rhs_val, ty),
 870            .div_floor => return floatDivFloor(sema, lhs_val, rhs_val, ty),
 871            .div_exact => {
 872                if (!floatDivIsExact(sema, lhs_val, rhs_val, ty)) {
 873                    return sema.fail(block, src, "exact division produced remainder", .{});
 874                }
 875                return floatDivTrunc(sema, lhs_val, rhs_val, ty);
 876            },
 877        }
 878    }
 879}
 880
 881pub const ModRemOp = enum { mod, rem };
 882
 883/// Applies `@mod` or `@rem` to comptime-known values.
 884/// `lhs_val` and `rhs_val` are fully-resolved values of type `ty`.
 885/// `ty` is an int, float, comptime_int, comptime_float, or vector.
 886pub fn modRem(
 887    sema: *Sema,
 888    block: *Block,
 889    ty: Type,
 890    lhs_val: Value,
 891    rhs_val: Value,
 892    lhs_src: LazySrcLoc,
 893    rhs_src: LazySrcLoc,
 894    op: ModRemOp,
 895) CompileError!Value {
 896    const pt = sema.pt;
 897    const zcu = pt.zcu;
 898    switch (ty.zigTypeTag(zcu)) {
 899        .vector => {
 900            const elem_ty = ty.childType(zcu);
 901            const len = ty.vectorLen(zcu);
 902
 903            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 904            for (elem_vals, 0..) |*result_elem, elem_idx| {
 905                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 906                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 907                result_elem.* = (try modRemScalar(sema, block, elem_ty, lhs_elem, rhs_elem, lhs_src, rhs_src, op, elem_idx)).toIntern();
 908            }
 909            return pt.aggregateValue(ty, elem_vals);
 910        },
 911        else => return modRemScalar(sema, block, ty, lhs_val, rhs_val, lhs_src, rhs_src, op, null),
 912    }
 913}
 914fn modRemScalar(
 915    sema: *Sema,
 916    block: *Block,
 917    ty: Type,
 918    lhs_val: Value,
 919    rhs_val: Value,
 920    lhs_src: LazySrcLoc,
 921    rhs_src: LazySrcLoc,
 922    op: ModRemOp,
 923    vec_idx: ?usize,
 924) CompileError!Value {
 925    const pt = sema.pt;
 926    const zcu = pt.zcu;
 927    const is_int = switch (ty.zigTypeTag(zcu)) {
 928        .int, .comptime_int => true,
 929        .float, .comptime_float => false,
 930        else => unreachable,
 931    };
 932
 933    const allow_div_zero = !is_int and block.float_mode == .strict;
 934    if (allow_div_zero) {
 935        if (lhs_val.isUndef(zcu)) return lhs_val;
 936        if (rhs_val.isUndef(zcu)) return rhs_val;
 937    } else {
 938        if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
 939        if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
 940        if (rhs_val.eqlScalarNum(.zero_comptime_int, zcu)) return sema.failWithDivideByZero(block, rhs_src);
 941    }
 942
 943    if (is_int) {
 944        switch (op) {
 945            .mod => return intMod(sema, lhs_val, rhs_val, ty),
 946            .rem => return intRem(sema, lhs_val, rhs_val, ty),
 947        }
 948    } else {
 949        switch (op) {
 950            .mod => return floatMod(sema, lhs_val, rhs_val, ty),
 951            .rem => return floatRem(sema, lhs_val, rhs_val, ty),
 952        }
 953    }
 954}
 955
 956pub const ShlOp = enum { shl, shl_sat, shl_exact };
 957
 958/// Applies the `<<` operator to comptime-known values.
 959/// `lhs_ty` is an int, comptime_int, or vector thereof.
 960/// If it is a vector, the type of `rhs` has to also be a vector of the same length.
 961pub fn shl(
 962    sema: *Sema,
 963    block: *Block,
 964    lhs_ty: Type,
 965    lhs_val: Value,
 966    rhs_val: Value,
 967    src: LazySrcLoc,
 968    lhs_src: LazySrcLoc,
 969    rhs_src: LazySrcLoc,
 970    op: ShlOp,
 971) CompileError!Value {
 972    const pt = sema.pt;
 973    const zcu = pt.zcu;
 974    switch (lhs_ty.zigTypeTag(zcu)) {
 975        .int, .comptime_int => return shlScalar(sema, block, lhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, null),
 976        .vector => {
 977            const lhs_elem_ty = lhs_ty.childType(zcu);
 978            const len = lhs_ty.vectorLen(zcu);
 979
 980            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
 981            for (elem_vals, 0..) |*result_elem, elem_idx| {
 982                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
 983                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
 984                result_elem.* = (try shlScalar(sema, block, lhs_elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, op, elem_idx)).toIntern();
 985            }
 986            return pt.aggregateValue(lhs_ty, elem_vals);
 987        },
 988        else => unreachable,
 989    }
 990}
 991/// `lhs_ty` is an int, comptime_int, or vector thereof.
 992/// If it is a vector, the type of `rhs` has to also be a vector of the same length.
 993pub fn shlWithOverflow(
 994    sema: *Sema,
 995    block: *Block,
 996    lhs_ty: Type,
 997    lhs_val: Value,
 998    rhs_val: Value,
 999    lhs_src: LazySrcLoc,
1000    rhs_src: LazySrcLoc,
1001) CompileError!Value.OverflowArithmeticResult {
1002    const pt = sema.pt;
1003    const zcu = pt.zcu;
1004    switch (lhs_ty.zigTypeTag(zcu)) {
1005        .int, .comptime_int => return shlWithOverflowScalar(sema, block, lhs_ty, lhs_val, rhs_val, lhs_src, rhs_src, null),
1006        .vector => {
1007            const lhs_elem_ty = lhs_ty.childType(zcu);
1008            const len = lhs_ty.vectorLen(zcu);
1009
1010            const overflow_bits = try sema.arena.alloc(InternPool.Index, len);
1011            const wrapped_results = try sema.arena.alloc(InternPool.Index, len);
1012            for (overflow_bits, wrapped_results, 0..) |*ob, *wr, elem_idx| {
1013                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1014                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1015                const elem_result = try shlWithOverflowScalar(sema, block, lhs_elem_ty, lhs_elem, rhs_elem, lhs_src, rhs_src, elem_idx);
1016                ob.* = elem_result.overflow_bit.toIntern();
1017                wr.* = elem_result.wrapped_result.toIntern();
1018            }
1019            return .{
1020                .overflow_bit = try pt.aggregateValue(try pt.vectorType(.{
1021                    .len = @intCast(overflow_bits.len),
1022                    .child = .u1_type,
1023                }), overflow_bits),
1024                .wrapped_result = try pt.aggregateValue(lhs_ty, wrapped_results),
1025            };
1026        },
1027        else => unreachable,
1028    }
1029}
1030
1031fn shlScalar(
1032    sema: *Sema,
1033    block: *Block,
1034    lhs_ty: Type,
1035    lhs_val: Value,
1036    rhs_val: Value,
1037    src: LazySrcLoc,
1038    lhs_src: LazySrcLoc,
1039    rhs_src: LazySrcLoc,
1040    op: ShlOp,
1041    vec_idx: ?usize,
1042) CompileError!Value {
1043    const pt = sema.pt;
1044    const zcu = pt.zcu;
1045
1046    switch (op) {
1047        .shl, .shl_exact => {
1048            if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
1049            if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
1050        },
1051        .shl_sat => {
1052            if (lhs_val.isUndef(zcu)) return lhs_val;
1053            if (rhs_val.isUndef(zcu)) return rhs_val;
1054        },
1055    }
1056    switch (try rhs_val.orderAgainstZeroSema(pt)) {
1057        .gt => {},
1058        .eq => return lhs_val,
1059        .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
1060    }
1061    switch (lhs_ty.zigTypeTag(zcu)) {
1062        .int => switch (op) {
1063            .shl => return intShl(sema, block, lhs_ty, lhs_val, rhs_val, rhs_src, vec_idx),
1064            .shl_sat => return intShlSat(sema, lhs_ty, lhs_val, rhs_val),
1065            .shl_exact => {
1066                const shifted = try intShlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, rhs_src, false, vec_idx);
1067                if (shifted.overflow) {
1068                    return sema.failWithIntegerOverflow(block, src, lhs_ty, shifted.val, vec_idx);
1069                }
1070                return shifted.val;
1071            },
1072        },
1073        .comptime_int => return comptimeIntShl(sema, block, lhs_val, rhs_val, rhs_src, vec_idx),
1074        else => unreachable,
1075    }
1076}
1077fn shlWithOverflowScalar(
1078    sema: *Sema,
1079    block: *Block,
1080    lhs_ty: Type,
1081    lhs_val: Value,
1082    rhs_val: Value,
1083    lhs_src: LazySrcLoc,
1084    rhs_src: LazySrcLoc,
1085    vec_idx: ?usize,
1086) CompileError!Value.OverflowArithmeticResult {
1087    const pt = sema.pt;
1088    const zcu = pt.zcu;
1089
1090    if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
1091    if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
1092
1093    switch (try rhs_val.orderAgainstZeroSema(pt)) {
1094        .gt => {},
1095        .eq => return .{ .overflow_bit = .zero_u1, .wrapped_result = lhs_val },
1096        .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
1097    }
1098    switch (lhs_ty.zigTypeTag(zcu)) {
1099        .int => {
1100            const result = try intShlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, rhs_src, true, vec_idx);
1101            return .{
1102                .overflow_bit = try pt.intValue(.u1, @intFromBool(result.overflow)),
1103                .wrapped_result = result.val,
1104            };
1105        },
1106        .comptime_int => return .{
1107            .overflow_bit = .zero_u1,
1108            .wrapped_result = try comptimeIntShl(sema, block, lhs_val, rhs_val, rhs_src, vec_idx),
1109        },
1110        else => unreachable,
1111    }
1112}
1113
1114pub const ShrOp = enum { shr, shr_exact };
1115
1116/// Applies the `>>` operator to comptime-known values.
1117/// `lhs_ty` is an int, comptime_int, or vector thereof.
1118/// If it is a vector, the type of `rhs` has to also be a vector of the same length.
1119pub fn shr(
1120    sema: *Sema,
1121    block: *Block,
1122    lhs_ty: Type,
1123    rhs_ty: Type,
1124    lhs_val: Value,
1125    rhs_val: Value,
1126    src: LazySrcLoc,
1127    lhs_src: LazySrcLoc,
1128    rhs_src: LazySrcLoc,
1129    op: ShrOp,
1130) CompileError!Value {
1131    const pt = sema.pt;
1132    const zcu = pt.zcu;
1133
1134    switch (lhs_ty.zigTypeTag(zcu)) {
1135        .int, .comptime_int => return shrScalar(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, op, null),
1136        .vector => {
1137            const lhs_elem_ty = lhs_ty.childType(zcu);
1138            const rhs_elem_ty = rhs_ty.childType(zcu);
1139            const len = lhs_ty.vectorLen(zcu);
1140
1141            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1142            for (elem_vals, 0..) |*result_elem, elem_idx| {
1143                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1144                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1145                result_elem.* = (try shrScalar(sema, block, lhs_elem_ty, rhs_elem_ty, lhs_elem, rhs_elem, src, lhs_src, rhs_src, op, elem_idx)).toIntern();
1146            }
1147            return pt.aggregateValue(lhs_ty, elem_vals);
1148        },
1149        else => unreachable,
1150    }
1151}
1152
1153fn shrScalar(
1154    sema: *Sema,
1155    block: *Block,
1156    lhs_ty: Type,
1157    rhs_ty: Type,
1158    lhs_val: Value,
1159    rhs_val: Value,
1160    src: LazySrcLoc,
1161    lhs_src: LazySrcLoc,
1162    rhs_src: LazySrcLoc,
1163    op: ShrOp,
1164    vec_idx: ?usize,
1165) CompileError!Value {
1166    const pt = sema.pt;
1167    const zcu = pt.zcu;
1168
1169    if (lhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src, vec_idx);
1170    if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, vec_idx);
1171
1172    switch (try rhs_val.orderAgainstZeroSema(pt)) {
1173        .gt => {},
1174        .eq => return lhs_val,
1175        .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, vec_idx),
1176    }
1177    return intShr(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, rhs_src, op, vec_idx);
1178}
1179
1180/// Applies `@truncate` to comptime-known values.
1181/// `ty` is an int, comptime_int, or vector thereof.
1182/// `val` is of type `ty`.
1183/// The returned value is of type `dest_ty`. The caller guarantees that the
1184/// truncated value fits into `dest_ty`.
1185/// If `ty` is a vector, `dest_ty` has to also be a vector of the same length.
1186pub fn truncate(
1187    sema: *Sema,
1188    val: Value,
1189    ty: Type,
1190    dest_ty: Type,
1191    dest_signedness: std.builtin.Signedness,
1192    dest_bits: u16,
1193) CompileError!Value {
1194    const pt = sema.pt;
1195    const zcu = pt.zcu;
1196    if (val.isUndef(zcu)) return pt.undefValue(dest_ty);
1197    switch (ty.zigTypeTag(zcu)) {
1198        .int, .comptime_int => return intTruncate(sema, val, dest_ty, dest_signedness, dest_bits),
1199        .vector => {
1200            const dest_elem_ty = dest_ty.childType(zcu);
1201            const len = ty.vectorLen(zcu);
1202
1203            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1204            for (elem_vals, 0..) |*result_elem, elem_idx| {
1205                const elem_val = try val.elemValue(pt, elem_idx);
1206                result_elem.* = if (elem_val.isUndef(zcu))
1207                    (try pt.undefValue(dest_elem_ty)).toIntern()
1208                else
1209                    (try intTruncate(
1210                        sema,
1211                        elem_val,
1212                        dest_elem_ty,
1213                        dest_signedness,
1214                        dest_bits,
1215                    )).toIntern();
1216            }
1217            return pt.aggregateValue(dest_ty, elem_vals);
1218        },
1219        else => unreachable,
1220    }
1221}
1222
1223/// Applies the `~` operator to a comptime-known value.
1224/// `val` is of type `ty`.
1225/// `ty` is a bool, int, comptime_int, or vector thereof.
1226pub fn bitwiseNot(sema: *Sema, ty: Type, val: Value) CompileError!Value {
1227    const pt = sema.pt;
1228    const zcu = pt.zcu;
1229    if (val.isUndef(zcu)) return val;
1230    switch (ty.zigTypeTag(zcu)) {
1231        .bool, .int, .comptime_int => return intBitwiseNot(sema, val, ty),
1232        .vector => {
1233            const elem_ty = ty.childType(zcu);
1234            switch (elem_ty.zigTypeTag(zcu)) {
1235                .bool, .int, .comptime_int => {},
1236                else => unreachable,
1237            }
1238            const len = ty.vectorLen(zcu);
1239
1240            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1241            for (elem_vals, 0..) |*result_elem, elem_idx| {
1242                const elem_val = try val.elemValue(pt, elem_idx);
1243                result_elem.* = if (elem_val.isUndef(zcu))
1244                    elem_val.toIntern()
1245                else
1246                    (try intBitwiseNot(sema, elem_val, elem_ty)).toIntern();
1247            }
1248            return pt.aggregateValue(ty, elem_vals);
1249        },
1250        else => unreachable,
1251    }
1252}
1253
1254pub const BitwiseBinOp = enum { @"and", nand, @"or", xor };
1255
1256/// Applies a binary bitwise operator to comptime-known values.
1257/// `lhs_val` and `rhs_val` are both of type `ty`.
1258/// `ty` is a bool, int, comptime_int, or vector thereof.
1259pub fn bitwiseBin(
1260    sema: *Sema,
1261    ty: Type,
1262    lhs_val: Value,
1263    rhs_val: Value,
1264    op: BitwiseBinOp,
1265) CompileError!Value {
1266    const pt = sema.pt;
1267    const zcu = pt.zcu;
1268    switch (ty.zigTypeTag(zcu)) {
1269        .vector => {
1270            const elem_ty = ty.childType(zcu);
1271            switch (elem_ty.zigTypeTag(zcu)) {
1272                .bool, .int, .comptime_int => {},
1273                else => unreachable,
1274            }
1275            const len = ty.vectorLen(zcu);
1276
1277            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1278            for (elem_vals, 0..) |*result_elem, elem_idx| {
1279                const lhs_elem = try lhs_val.elemValue(pt, elem_idx);
1280                const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
1281                result_elem.* = (try bitwiseBinScalar(sema, elem_ty, lhs_elem, rhs_elem, op)).toIntern();
1282            }
1283            return pt.aggregateValue(ty, elem_vals);
1284        },
1285        .bool, .int, .comptime_int => return bitwiseBinScalar(sema, ty, lhs_val, rhs_val, op),
1286        else => unreachable,
1287    }
1288}
1289fn bitwiseBinScalar(
1290    sema: *Sema,
1291    ty: Type,
1292    lhs_val: Value,
1293    rhs_val: Value,
1294    op: BitwiseBinOp,
1295) CompileError!Value {
1296    const pt = sema.pt;
1297    const zcu = pt.zcu;
1298    // Special case: the method used below doesn't make sense for xor.
1299    if (op == .xor and (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu))) return pt.undefValue(ty);
1300    // If one operand is defined, we turn the other into `0xAA` so the bitwise op can
1301    // still zero out some bits.
1302    // TODO: ideally we'd still like tracking for the undef bits. Related: #19634.
1303    const def_lhs: Value, const def_rhs: Value = make_defined: {
1304        const lhs_undef = lhs_val.isUndef(zcu);
1305        const rhs_undef = rhs_val.isUndef(zcu);
1306        break :make_defined switch ((@as(u2, @intFromBool(lhs_undef)) << 1) | @intFromBool(rhs_undef)) {
1307            0b00 => .{ lhs_val, rhs_val },
1308            0b01 => .{ lhs_val, try intValueAa(sema, ty) },
1309            0b10 => .{ try intValueAa(sema, ty), rhs_val },
1310            0b11 => return pt.undefValue(ty),
1311        };
1312    };
1313    if (ty.toIntern() == .u0_type or ty.toIntern() == .i0_type) return pt.intValue(ty, 0);
1314    // zig fmt: off
1315    switch (op) {
1316        .@"and" => return intBitwiseAnd(sema, def_lhs, def_rhs, ty),
1317        .nand   => return intBitwiseNand(sema, def_lhs, def_rhs, ty),
1318        .@"or"  => return intBitwiseOr(sema, def_lhs, def_rhs, ty),
1319        .xor    => return intBitwiseXor(sema, def_lhs, def_rhs, ty),
1320    }
1321    // zig fmt: on
1322}
1323
1324/// Applies `@bitReverse` to a comptime-known value.
1325/// `val` is of type `ty`.
1326/// `ty` is an int or a vector thereof.
1327pub fn bitReverse(sema: *Sema, val: Value, ty: Type) CompileError!Value {
1328    const pt = sema.pt;
1329    const zcu = pt.zcu;
1330    if (val.isUndef(zcu)) return val;
1331    switch (ty.zigTypeTag(zcu)) {
1332        .int => return intBitReverse(sema, val, ty),
1333        .vector => {
1334            const elem_ty = ty.childType(zcu);
1335            assert(elem_ty.isInt(zcu));
1336            const len = ty.vectorLen(zcu);
1337
1338            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1339            for (elem_vals, 0..) |*result_elem, elem_idx| {
1340                const elem_val = try val.elemValue(pt, elem_idx);
1341                result_elem.* = if (elem_val.isUndef(zcu))
1342                    elem_val.toIntern()
1343                else
1344                    (try intBitReverse(sema, elem_val, elem_ty)).toIntern();
1345            }
1346            return pt.aggregateValue(ty, elem_vals);
1347        },
1348        else => unreachable,
1349    }
1350}
1351
1352/// Applies `@byteSwap` to a comptime-known value.
1353/// `val` is of type `ty`.
1354/// `ty` is an int or a vector thereof.
1355/// The bit width of the scalar int type of `ty` has to be a multiple of 8.
1356pub fn byteSwap(sema: *Sema, val: Value, ty: Type) CompileError!Value {
1357    const pt = sema.pt;
1358    const zcu = pt.zcu;
1359    if (val.isUndef(zcu)) return val;
1360    switch (ty.zigTypeTag(zcu)) {
1361        .int => return intByteSwap(sema, val, ty),
1362        .vector => {
1363            const elem_ty = ty.childType(zcu);
1364            assert(elem_ty.isInt(zcu));
1365            const len = ty.vectorLen(zcu);
1366
1367            const elem_vals = try sema.arena.alloc(InternPool.Index, len);
1368            for (elem_vals, 0..) |*result_elem, elem_idx| {
1369                const elem_val = try val.elemValue(pt, elem_idx);
1370                result_elem.* = if (elem_val.isUndef(zcu))
1371                    elem_val.toIntern()
1372                else
1373                    (try intByteSwap(sema, elem_val, elem_ty)).toIntern();
1374            }
1375            return pt.aggregateValue(ty, elem_vals);
1376        },
1377        else => unreachable,
1378    }
1379}
1380
1381/// If the value overflowed the type, returns a comptime_int instead.
1382/// Only supports scalars.
1383fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1384    const pt = sema.pt;
1385    const zcu = pt.zcu;
1386    switch (ty.toIntern()) {
1387        .comptime_int_type => return .{ .overflow = false, .val = try comptimeIntAdd(sema, lhs, rhs) },
1388        else => {
1389            const res = try intAddWithOverflowInner(sema, lhs, rhs, ty);
1390            return switch (res.overflow_bit.toUnsignedInt(zcu)) {
1391                0 => .{ .overflow = false, .val = res.wrapped_result },
1392                1 => .{ .overflow = true, .val = try comptimeIntAdd(sema, lhs, rhs) },
1393                else => unreachable,
1394            };
1395        },
1396    }
1397}
1398/// Add two integers, returning a `comptime_int` regardless of the input types.
1399fn comptimeIntAdd(sema: *Sema, lhs: Value, rhs: Value) !Value {
1400    const pt = sema.pt;
1401    const zcu = pt.zcu;
1402    // TODO is this a performance issue? maybe we should try the operation without
1403    // resorting to BigInt first.
1404    var lhs_space: Value.BigIntSpace = undefined;
1405    var rhs_space: Value.BigIntSpace = undefined;
1406    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1407    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1408    const limbs = try sema.arena.alloc(
1409        std.math.big.Limb,
1410        @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1411    );
1412    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1413    result_bigint.add(lhs_bigint, rhs_bigint);
1414    return pt.intValue_big(.comptime_int, result_bigint.toConst());
1415}
1416fn intAddWithOverflow(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResult {
1417    switch (ty.toIntern()) {
1418        .comptime_int_type => return .{
1419            .overflow_bit = .zero_u1,
1420            .wrapped_result = try comptimeIntAdd(sema, lhs, rhs),
1421        },
1422        else => return intAddWithOverflowInner(sema, lhs, rhs, ty),
1423    }
1424}
1425/// Like `intAddWithOverflow`, but asserts that `ty` is not `Type.comptime_int`.
1426fn intAddWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResult {
1427    assert(ty.toIntern() != .comptime_int_type);
1428    const pt = sema.pt;
1429    const zcu = pt.zcu;
1430    const info = ty.intInfo(zcu);
1431    var lhs_space: Value.BigIntSpace = undefined;
1432    var rhs_space: Value.BigIntSpace = undefined;
1433    const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
1434    const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt);
1435    const limbs = try sema.arena.alloc(
1436        std.math.big.Limb,
1437        std.math.big.int.calcTwosCompLimbCount(info.bits),
1438    );
1439    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1440    const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1441    return .{
1442        .overflow_bit = try pt.intValue(.u1, @intFromBool(overflowed)),
1443        .wrapped_result = try pt.intValue_big(ty, result_bigint.toConst()),
1444    };
1445}
1446fn intAddSat(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1447    const pt = sema.pt;
1448    const zcu = pt.zcu;
1449    const info = ty.intInfo(zcu);
1450    var lhs_space: Value.BigIntSpace = undefined;
1451    var rhs_space: Value.BigIntSpace = undefined;
1452    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1453    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1454    const limbs = try sema.arena.alloc(
1455        std.math.big.Limb,
1456        std.math.big.int.calcTwosCompLimbCount(info.bits),
1457    );
1458    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1459    result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1460    return pt.intValue_big(ty, result_bigint.toConst());
1461}
1462
1463/// If the value overflowed the type, returns a comptime_int instead.
1464/// Only supports scalars.
1465fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1466    const pt = sema.pt;
1467    const zcu = pt.zcu;
1468    switch (ty.toIntern()) {
1469        .comptime_int_type => return .{ .overflow = false, .val = try comptimeIntSub(sema, lhs, rhs) },
1470        else => {
1471            const res = try intSubWithOverflowInner(sema, lhs, rhs, ty);
1472            return switch (res.overflow_bit.toUnsignedInt(zcu)) {
1473                0 => .{ .overflow = false, .val = res.wrapped_result },
1474                1 => .{ .overflow = true, .val = try comptimeIntSub(sema, lhs, rhs) },
1475                else => unreachable,
1476            };
1477        },
1478    }
1479}
1480/// Subtract two integers, returning a `comptime_int` regardless of the input types.
1481fn comptimeIntSub(sema: *Sema, lhs: Value, rhs: Value) !Value {
1482    const pt = sema.pt;
1483    const zcu = pt.zcu;
1484    // TODO is this a performance issue? maybe we should try the operation without
1485    // resorting to BigInt first.
1486    var lhs_space: Value.BigIntSpace = undefined;
1487    var rhs_space: Value.BigIntSpace = undefined;
1488    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1489    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1490    const limbs = try sema.arena.alloc(
1491        std.math.big.Limb,
1492        @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1493    );
1494    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1495    result_bigint.sub(lhs_bigint, rhs_bigint);
1496    return pt.intValue_big(.comptime_int, result_bigint.toConst());
1497}
1498fn intSubWithOverflow(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResult {
1499    switch (ty.toIntern()) {
1500        .comptime_int_type => return .{
1501            .overflow_bit = .zero_u1,
1502            .wrapped_result = try comptimeIntSub(sema, lhs, rhs),
1503        },
1504        else => return intSubWithOverflowInner(sema, lhs, rhs, ty),
1505    }
1506}
1507/// Like `intSubWithOverflow`, but asserts that `ty` is not `Type.comptime_int`.
1508fn intSubWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResult {
1509    assert(ty.toIntern() != .comptime_int_type);
1510    const pt = sema.pt;
1511    const zcu = pt.zcu;
1512    const info = ty.intInfo(zcu);
1513    var lhs_space: Value.BigIntSpace = undefined;
1514    var rhs_space: Value.BigIntSpace = undefined;
1515    const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
1516    const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt);
1517    const limbs = try sema.arena.alloc(
1518        std.math.big.Limb,
1519        std.math.big.int.calcTwosCompLimbCount(info.bits),
1520    );
1521    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1522    const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1523    return .{
1524        .overflow_bit = try pt.intValue(.u1, @intFromBool(overflowed)),
1525        .wrapped_result = try pt.intValue_big(ty, result_bigint.toConst()),
1526    };
1527}
1528fn intSubSat(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1529    const pt = sema.pt;
1530    const zcu = pt.zcu;
1531    const info = ty.intInfo(zcu);
1532    var lhs_space: Value.BigIntSpace = undefined;
1533    var rhs_space: Value.BigIntSpace = undefined;
1534    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1535    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1536    const limbs = try sema.arena.alloc(
1537        std.math.big.Limb,
1538        std.math.big.int.calcTwosCompLimbCount(info.bits),
1539    );
1540    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1541    result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1542    return pt.intValue_big(ty, result_bigint.toConst());
1543}
1544
1545/// If the value overflowed the type, returns a comptime_int instead.
1546/// Only supports scalars.
1547fn intMul(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1548    const pt = sema.pt;
1549    const zcu = pt.zcu;
1550    switch (ty.toIntern()) {
1551        .comptime_int_type => return .{ .overflow = false, .val = try comptimeIntMul(sema, lhs, rhs) },
1552        else => {
1553            const res = try intMulWithOverflowInner(sema, lhs, rhs, ty);
1554            return switch (res.overflow_bit.toUnsignedInt(zcu)) {
1555                0 => .{ .overflow = false, .val = res.wrapped_result },
1556                1 => .{ .overflow = true, .val = try comptimeIntMul(sema, lhs, rhs) },
1557                else => unreachable,
1558            };
1559        },
1560    }
1561}
1562/// Multiply two integers, returning a `comptime_int` regardless of the input types.
1563fn comptimeIntMul(sema: *Sema, lhs: Value, rhs: Value) !Value {
1564    const pt = sema.pt;
1565    const zcu = pt.zcu;
1566    // TODO is this a performance issue? maybe we should try the operation without
1567    // resorting to BigInt first.
1568    var lhs_space: Value.BigIntSpace = undefined;
1569    var rhs_space: Value.BigIntSpace = undefined;
1570    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1571    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1572    const limbs = try sema.arena.alloc(
1573        std.math.big.Limb,
1574        lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1575    );
1576    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1577    const limbs_buffer = try sema.arena.alloc(
1578        std.math.big.Limb,
1579        std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
1580    );
1581    result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, sema.arena);
1582    return pt.intValue_big(.comptime_int, result_bigint.toConst());
1583}
1584fn intMulWithOverflow(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResult {
1585    switch (ty.toIntern()) {
1586        .comptime_int_type => return .{
1587            .overflow_bit = .zero_u1,
1588            .wrapped_result = try comptimeIntMul(sema, lhs, rhs),
1589        },
1590        else => return intMulWithOverflowInner(sema, lhs, rhs, ty),
1591    }
1592}
1593/// Like `intMulWithOverflow`, but asserts that `ty` is not `Type.comptime_int`.
1594fn intMulWithOverflowInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value.OverflowArithmeticResult {
1595    const pt = sema.pt;
1596    const zcu = pt.zcu;
1597    const info = ty.intInfo(zcu);
1598    var lhs_space: Value.BigIntSpace = undefined;
1599    var rhs_space: Value.BigIntSpace = undefined;
1600    const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
1601    const rhs_bigint = try rhs.toBigIntSema(&rhs_space, pt);
1602    const limbs = try sema.arena.alloc(
1603        std.math.big.Limb,
1604        lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1605    );
1606    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1607    result_bigint.mulNoAlias(lhs_bigint, rhs_bigint, sema.arena);
1608    const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
1609    if (overflowed) result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1610    return .{
1611        .overflow_bit = try pt.intValue(.u1, @intFromBool(overflowed)),
1612        .wrapped_result = try pt.intValue_big(ty, result_bigint.toConst()),
1613    };
1614}
1615fn intMulSat(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1616    const pt = sema.pt;
1617    const zcu = pt.zcu;
1618    const info = ty.intInfo(zcu);
1619    var lhs_space: Value.BigIntSpace = undefined;
1620    var rhs_space: Value.BigIntSpace = undefined;
1621    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1622    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1623    const limbs = try sema.arena.alloc(
1624        std.math.big.Limb,
1625        lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1626    );
1627    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1628    result_bigint.mulNoAlias(lhs_bigint, rhs_bigint, sema.arena);
1629    result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
1630    return pt.intValue_big(ty, result_bigint.toConst());
1631}
1632fn intDivTrunc(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1633    const result = intDivTruncInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1634        error.Overflow => {
1635            const result = intDivTruncInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1636                error.Overflow => unreachable,
1637                else => |e| return e,
1638            };
1639            return .{ .overflow = true, .val = result };
1640        },
1641        else => |e| return e,
1642    };
1643    return .{ .overflow = false, .val = result };
1644}
1645fn intDivTruncInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1646    const pt = sema.pt;
1647    const zcu = pt.zcu;
1648    var lhs_space: Value.BigIntSpace = undefined;
1649    var rhs_space: Value.BigIntSpace = undefined;
1650    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1651    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1652    const limbs_q = try sema.arena.alloc(
1653        std.math.big.Limb,
1654        lhs_bigint.limbs.len,
1655    );
1656    const limbs_r = try sema.arena.alloc(
1657        std.math.big.Limb,
1658        rhs_bigint.limbs.len,
1659    );
1660    const limbs_buf = try sema.arena.alloc(
1661        std.math.big.Limb,
1662        std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1663    );
1664    var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1665    var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1666    result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1667    if (ty.toIntern() != .comptime_int_type) {
1668        const info = ty.intInfo(zcu);
1669        if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1670            return error.Overflow;
1671        }
1672    }
1673    return pt.intValue_big(ty, result_q.toConst());
1674}
1675fn intDivExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !union(enum) {
1676    remainder,
1677    overflow: Value,
1678    success: Value,
1679} {
1680    const pt = sema.pt;
1681    const zcu = pt.zcu;
1682    var lhs_space: Value.BigIntSpace = undefined;
1683    var rhs_space: Value.BigIntSpace = undefined;
1684    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1685    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1686    const limbs_q = try sema.arena.alloc(
1687        std.math.big.Limb,
1688        lhs_bigint.limbs.len,
1689    );
1690    const limbs_r = try sema.arena.alloc(
1691        std.math.big.Limb,
1692        rhs_bigint.limbs.len,
1693    );
1694    const limbs_buf = try sema.arena.alloc(
1695        std.math.big.Limb,
1696        std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1697    );
1698    var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1699    var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1700    result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1701    if (!result_r.toConst().eqlZero()) {
1702        return .remainder;
1703    }
1704    if (ty.toIntern() != .comptime_int_type) {
1705        const info = ty.intInfo(zcu);
1706        if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1707            return .{ .overflow = try pt.intValue_big(.comptime_int, result_q.toConst()) };
1708        }
1709    }
1710    return .{ .success = try pt.intValue_big(ty, result_q.toConst()) };
1711}
1712fn intDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !struct { overflow: bool, val: Value } {
1713    const result = intDivFloorInner(sema, lhs, rhs, ty) catch |err| switch (err) {
1714        error.Overflow => {
1715            const result = intDivFloorInner(sema, lhs, rhs, .comptime_int) catch |err1| switch (err1) {
1716                error.Overflow => unreachable,
1717                else => |e| return e,
1718            };
1719            return .{ .overflow = true, .val = result };
1720        },
1721        else => |e| return e,
1722    };
1723    return .{ .overflow = false, .val = result };
1724}
1725fn intDivFloorInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1726    const pt = sema.pt;
1727    const zcu = pt.zcu;
1728    var lhs_space: Value.BigIntSpace = undefined;
1729    var rhs_space: Value.BigIntSpace = undefined;
1730    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1731    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1732    const limbs_q = try sema.arena.alloc(
1733        std.math.big.Limb,
1734        lhs_bigint.limbs.len,
1735    );
1736    const limbs_r = try sema.arena.alloc(
1737        std.math.big.Limb,
1738        rhs_bigint.limbs.len,
1739    );
1740    const limbs_buf = try sema.arena.alloc(
1741        std.math.big.Limb,
1742        std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1743    );
1744    var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1745    var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1746    result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1747    if (ty.toIntern() != .comptime_int_type) {
1748        const info = ty.intInfo(zcu);
1749        if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
1750            return error.Overflow;
1751        }
1752    }
1753    return pt.intValue_big(ty, result_q.toConst());
1754}
1755fn intMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1756    const pt = sema.pt;
1757    const zcu = pt.zcu;
1758    var lhs_space: Value.BigIntSpace = undefined;
1759    var rhs_space: Value.BigIntSpace = undefined;
1760    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1761    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1762    const limbs_q = try sema.arena.alloc(
1763        std.math.big.Limb,
1764        lhs_bigint.limbs.len,
1765    );
1766    const limbs_r = try sema.arena.alloc(
1767        std.math.big.Limb,
1768        rhs_bigint.limbs.len,
1769    );
1770    const limbs_buf = try sema.arena.alloc(
1771        std.math.big.Limb,
1772        std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1773    );
1774    var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1775    var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1776    result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1777    return pt.intValue_big(ty, result_r.toConst());
1778}
1779fn intRem(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
1780    const pt = sema.pt;
1781    const zcu = pt.zcu;
1782    var lhs_space: Value.BigIntSpace = undefined;
1783    var rhs_space: Value.BigIntSpace = undefined;
1784    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1785    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
1786    const limbs_q = try sema.arena.alloc(
1787        std.math.big.Limb,
1788        lhs_bigint.limbs.len,
1789    );
1790    const limbs_r = try sema.arena.alloc(
1791        std.math.big.Limb,
1792        rhs_bigint.limbs.len,
1793    );
1794    const limbs_buf = try sema.arena.alloc(
1795        std.math.big.Limb,
1796        std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1797    );
1798    var result_q: BigIntMutable = .{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1799    var result_r: BigIntMutable = .{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1800    result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buf);
1801    return pt.intValue_big(ty, result_r.toConst());
1802}
1803
1804fn intTruncate(
1805    sema: *Sema,
1806    val: Value,
1807    dest_ty: Type,
1808    dest_signedness: std.builtin.Signedness,
1809    dest_bits: u16,
1810) !Value {
1811    const pt = sema.pt;
1812    const zcu = pt.zcu;
1813
1814    var val_space: Value.BigIntSpace = undefined;
1815    const val_bigint = val.toBigInt(&val_space, zcu);
1816
1817    const limbs = try sema.arena.alloc(
1818        std.math.big.Limb,
1819        std.math.big.int.calcTwosCompLimbCount(dest_bits),
1820    );
1821    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1822
1823    result_bigint.truncate(val_bigint, dest_signedness, dest_bits);
1824    return pt.intValue_big(dest_ty, result_bigint.toConst());
1825}
1826
1827fn intShl(
1828    sema: *Sema,
1829    block: *Block,
1830    lhs_ty: Type,
1831    lhs: Value,
1832    rhs: Value,
1833    rhs_src: LazySrcLoc,
1834    vec_idx: ?usize,
1835) !Value {
1836    const pt = sema.pt;
1837    const zcu = pt.zcu;
1838    const info = lhs_ty.intInfo(zcu);
1839
1840    var lhs_space: Value.BigIntSpace = undefined;
1841    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1842
1843    const shift_amt: usize = @intCast(try rhs.toUnsignedIntSema(pt));
1844    if (shift_amt >= info.bits) {
1845        return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
1846    }
1847    var result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
1848    result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1849    return pt.intValue_big(lhs_ty, result_bigint.toConst());
1850}
1851fn intShlSat(
1852    sema: *Sema,
1853    lhs_ty: Type,
1854    lhs: Value,
1855    rhs: Value,
1856) !Value {
1857    const pt = sema.pt;
1858    const zcu = pt.zcu;
1859    const info = lhs_ty.intInfo(zcu);
1860
1861    var lhs_space: Value.BigIntSpace = undefined;
1862    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1863
1864    const shift_amt: usize = amt: {
1865        if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| {
1866            if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt;
1867        }
1868        // We only support ints with up to 2^16 - 1 bits, so this
1869        // shift will fully saturate every non-zero int (assuming
1870        // that `usize` is at least 16 bits wide).
1871        return if (lhs_bigint.eqlZero()) lhs else lhs_ty.maxIntScalar(pt, lhs_ty);
1872    };
1873
1874    const limbs = try sema.arena.alloc(
1875        std.math.big.Limb,
1876        std.math.big.int.calcTwosCompLimbCount(info.bits),
1877    );
1878    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1879    result_bigint.shiftLeftSat(lhs_bigint, shift_amt, info.signedness, info.bits);
1880    return pt.intValue_big(lhs_ty, result_bigint.toConst());
1881}
1882/// If the value overflowed the type and `truncate_result` is `false`, returns a `comptime_int` instead.
1883fn intShlWithOverflow(
1884    sema: *Sema,
1885    block: *Block,
1886    lhs_ty: Type,
1887    lhs: Value,
1888    rhs: Value,
1889    rhs_src: LazySrcLoc,
1890    truncate_result: bool,
1891    vec_idx: ?usize,
1892) !struct { overflow: bool, val: Value } {
1893    const pt = sema.pt;
1894    const zcu = pt.zcu;
1895    const info = lhs_ty.intInfo(zcu);
1896
1897    var lhs_space: Value.BigIntSpace = undefined;
1898    const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
1899
1900    const shift_amt: usize = @intCast(try rhs.toUnsignedIntSema(pt));
1901    if (shift_amt >= info.bits) {
1902        return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
1903    }
1904    var result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
1905    const overflow = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
1906    const result = result: {
1907        if (overflow) {
1908            if (truncate_result) {
1909                result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1910            } else {
1911                break :result try pt.intValue_big(.comptime_int, result_bigint.toConst());
1912            }
1913        }
1914        break :result try pt.intValue_big(lhs_ty, result_bigint.toConst());
1915    };
1916    return .{ .overflow = overflow, .val = result };
1917}
1918fn comptimeIntShl(
1919    sema: *Sema,
1920    block: *Block,
1921    lhs: Value,
1922    rhs: Value,
1923    rhs_src: LazySrcLoc,
1924    vec_idx: ?usize,
1925) !Value {
1926    const pt = sema.pt;
1927    var lhs_space: Value.BigIntSpace = undefined;
1928    const lhs_bigint = try lhs.toBigIntSema(&lhs_space, pt);
1929    if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| {
1930        if (std.math.cast(usize, shift_amt_u64)) |shift_amt| {
1931            const result_bigint = try intShlInner(sema, lhs_bigint, shift_amt);
1932            return pt.intValue_big(.comptime_int, result_bigint.toConst());
1933        }
1934    }
1935    return sema.failWithUnsupportedComptimeShiftAmount(block, rhs_src, vec_idx);
1936}
1937fn intShlInner(sema: *Sema, operand: std.math.big.int.Const, shift_amt: usize) !BigIntMutable {
1938    const limbs = try sema.arena.alloc(
1939        std.math.big.Limb,
1940        operand.limbs.len + (shift_amt / (@sizeOf(std.math.big.Limb) * 8)) + 1,
1941    );
1942    var result: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1943    result.shiftLeft(operand, shift_amt);
1944    return result;
1945}
1946
1947fn intShr(
1948    sema: *Sema,
1949    block: *Block,
1950    lhs_ty: Type,
1951    rhs_ty: Type,
1952    lhs: Value,
1953    rhs: Value,
1954    src: LazySrcLoc,
1955    rhs_src: LazySrcLoc,
1956    op: ShrOp,
1957    vec_idx: ?usize,
1958) !Value {
1959    const pt = sema.pt;
1960    const zcu = pt.zcu;
1961
1962    var lhs_space: Value.BigIntSpace = undefined;
1963    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
1964
1965    const shift_amt: usize = if (rhs_ty.toIntern() == .comptime_int_type) amt: {
1966        if (try rhs.getUnsignedIntSema(pt)) |shift_amt_u64| {
1967            if (std.math.cast(usize, shift_amt_u64)) |shift_amt| break :amt shift_amt;
1968        }
1969        if (try rhs.compareAllWithZeroSema(.lt, pt)) {
1970            return sema.failWithNegativeShiftAmount(block, rhs_src, rhs, vec_idx);
1971        } else {
1972            return sema.failWithUnsupportedComptimeShiftAmount(block, rhs_src, vec_idx);
1973        }
1974    } else @intCast(try rhs.toUnsignedIntSema(pt));
1975
1976    if (lhs_ty.toIntern() != .comptime_int_type and shift_amt >= lhs_ty.intInfo(zcu).bits) {
1977        return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs, rhs_src, vec_idx);
1978    }
1979    if (op == .shr_exact and lhs_bigint.ctz(shift_amt) < shift_amt) {
1980        return sema.failWithOwnedErrorMsg(block, msg: {
1981            const msg = try sema.errMsg(src, "exact shift shifted out 1 bits", .{});
1982            errdefer msg.destroy(sema.gpa);
1983            if (vec_idx) |i| try sema.errNote(rhs_src, msg, "when computing vector element at index '{d}'", .{i});
1984            break :msg msg;
1985        });
1986    }
1987    const result_limbs = lhs_bigint.limbs.len -| (shift_amt / (@sizeOf(std.math.big.Limb) * 8));
1988    if (result_limbs == 0) {
1989        // The shift is enough to remove all the bits from the number, which
1990        // means the result is 0 or -1 depending on the sign.
1991        if (lhs_bigint.positive) {
1992            return pt.intValue(lhs_ty, 0);
1993        } else {
1994            return pt.intValue(lhs_ty, -1);
1995        }
1996    }
1997    const limbs = try sema.arena.alloc(std.math.big.Limb, result_limbs);
1998    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
1999    result_bigint.shiftRight(lhs_bigint, shift_amt);
2000    return pt.intValue_big(lhs_ty, result_bigint.toConst());
2001}
2002
2003fn intBitReverse(sema: *Sema, val: Value, ty: Type) !Value {
2004    const pt = sema.pt;
2005    const zcu = pt.zcu;
2006    const info = ty.intInfo(zcu);
2007
2008    var val_space: Value.BigIntSpace = undefined;
2009    const val_bigint = try val.toBigIntSema(&val_space, pt);
2010
2011    const limbs = try sema.arena.alloc(
2012        std.math.big.Limb,
2013        std.math.big.int.calcTwosCompLimbCount(info.bits),
2014    );
2015    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2016    result_bigint.bitReverse(val_bigint, info.signedness, info.bits);
2017    return pt.intValue_big(ty, result_bigint.toConst());
2018}
2019
2020fn intByteSwap(sema: *Sema, val: Value, ty: Type) !Value {
2021    const pt = sema.pt;
2022    const zcu = pt.zcu;
2023    const info = ty.intInfo(zcu);
2024
2025    var val_space: Value.BigIntSpace = undefined;
2026    const val_bigint = val.toBigInt(&val_space, zcu);
2027
2028    const limbs = try sema.arena.alloc(
2029        std.math.big.Limb,
2030        std.math.big.int.calcTwosCompLimbCount(info.bits),
2031    );
2032    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2033    result_bigint.byteSwap(val_bigint, info.signedness, @divExact(info.bits, 8));
2034    return pt.intValue_big(ty, result_bigint.toConst());
2035}
2036
2037fn floatAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2038    const pt = sema.pt;
2039    const zcu = pt.zcu;
2040    const target = zcu.getTarget();
2041    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2042        16 => .{ .f16 = lhs.toFloat(f16, zcu) + rhs.toFloat(f16, zcu) },
2043        32 => .{ .f32 = lhs.toFloat(f32, zcu) + rhs.toFloat(f32, zcu) },
2044        64 => .{ .f64 = lhs.toFloat(f64, zcu) + rhs.toFloat(f64, zcu) },
2045        80 => .{ .f80 = lhs.toFloat(f80, zcu) + rhs.toFloat(f80, zcu) },
2046        128 => .{ .f128 = lhs.toFloat(f128, zcu) + rhs.toFloat(f128, zcu) },
2047        else => unreachable,
2048    };
2049    return .fromInterned(try pt.intern(.{ .float = .{
2050        .ty = ty.toIntern(),
2051        .storage = storage,
2052    } }));
2053}
2054fn floatSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2055    const pt = sema.pt;
2056    const zcu = pt.zcu;
2057    const target = zcu.getTarget();
2058    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2059        16 => .{ .f16 = lhs.toFloat(f16, zcu) - rhs.toFloat(f16, zcu) },
2060        32 => .{ .f32 = lhs.toFloat(f32, zcu) - rhs.toFloat(f32, zcu) },
2061        64 => .{ .f64 = lhs.toFloat(f64, zcu) - rhs.toFloat(f64, zcu) },
2062        80 => .{ .f80 = lhs.toFloat(f80, zcu) - rhs.toFloat(f80, zcu) },
2063        128 => .{ .f128 = lhs.toFloat(f128, zcu) - rhs.toFloat(f128, zcu) },
2064        else => unreachable,
2065    };
2066    return .fromInterned(try pt.intern(.{ .float = .{
2067        .ty = ty.toIntern(),
2068        .storage = storage,
2069    } }));
2070}
2071fn floatMul(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2072    const pt = sema.pt;
2073    const zcu = pt.zcu;
2074    const target = zcu.getTarget();
2075    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2076        16 => .{ .f16 = lhs.toFloat(f16, zcu) * rhs.toFloat(f16, zcu) },
2077        32 => .{ .f32 = lhs.toFloat(f32, zcu) * rhs.toFloat(f32, zcu) },
2078        64 => .{ .f64 = lhs.toFloat(f64, zcu) * rhs.toFloat(f64, zcu) },
2079        80 => .{ .f80 = lhs.toFloat(f80, zcu) * rhs.toFloat(f80, zcu) },
2080        128 => .{ .f128 = lhs.toFloat(f128, zcu) * rhs.toFloat(f128, zcu) },
2081        else => unreachable,
2082    };
2083    return .fromInterned(try pt.intern(.{ .float = .{
2084        .ty = ty.toIntern(),
2085        .storage = storage,
2086    } }));
2087}
2088fn floatDiv(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2089    const pt = sema.pt;
2090    const zcu = pt.zcu;
2091    const target = zcu.getTarget();
2092    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2093        16 => .{ .f16 = lhs.toFloat(f16, zcu) / rhs.toFloat(f16, zcu) },
2094        32 => .{ .f32 = lhs.toFloat(f32, zcu) / rhs.toFloat(f32, zcu) },
2095        64 => .{ .f64 = lhs.toFloat(f64, zcu) / rhs.toFloat(f64, zcu) },
2096        80 => .{ .f80 = lhs.toFloat(f80, zcu) / rhs.toFloat(f80, zcu) },
2097        128 => .{ .f128 = lhs.toFloat(f128, zcu) / rhs.toFloat(f128, zcu) },
2098        else => unreachable,
2099    };
2100    return .fromInterned(try pt.intern(.{ .float = .{
2101        .ty = ty.toIntern(),
2102        .storage = storage,
2103    } }));
2104}
2105fn floatDivTrunc(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2106    const pt = sema.pt;
2107    const zcu = pt.zcu;
2108    const target = zcu.getTarget();
2109    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2110        16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2111        32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2112        64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2113        80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2114        128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2115        else => unreachable,
2116    };
2117    return .fromInterned(try pt.intern(.{ .float = .{
2118        .ty = ty.toIntern(),
2119        .storage = storage,
2120    } }));
2121}
2122fn floatDivFloor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2123    const pt = sema.pt;
2124    const zcu = pt.zcu;
2125    const target = zcu.getTarget();
2126    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2127        16 => .{ .f16 = @divFloor(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2128        32 => .{ .f32 = @divFloor(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2129        64 => .{ .f64 = @divFloor(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2130        80 => .{ .f80 = @divFloor(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2131        128 => .{ .f128 = @divFloor(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2132        else => unreachable,
2133    };
2134    return .fromInterned(try pt.intern(.{ .float = .{
2135        .ty = ty.toIntern(),
2136        .storage = storage,
2137    } }));
2138}
2139fn floatDivIsExact(sema: *Sema, lhs: Value, rhs: Value, ty: Type) bool {
2140    const zcu = sema.pt.zcu;
2141    const target = zcu.getTarget();
2142    return switch (ty.floatBits(target)) {
2143        16 => @mod(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) == 0,
2144        32 => @mod(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) == 0,
2145        64 => @mod(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) == 0,
2146        80 => @mod(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) == 0,
2147        128 => @mod(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) == 0,
2148        else => unreachable,
2149    };
2150}
2151fn floatNeg(sema: *Sema, val: Value, ty: Type) !Value {
2152    const pt = sema.pt;
2153    const zcu = pt.zcu;
2154    const target = zcu.getTarget();
2155    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2156        16 => .{ .f16 = -val.toFloat(f16, zcu) },
2157        32 => .{ .f32 = -val.toFloat(f32, zcu) },
2158        64 => .{ .f64 = -val.toFloat(f64, zcu) },
2159        80 => .{ .f80 = -val.toFloat(f80, zcu) },
2160        128 => .{ .f128 = -val.toFloat(f128, zcu) },
2161        else => unreachable,
2162    };
2163    return .fromInterned(try pt.intern(.{ .float = .{
2164        .ty = ty.toIntern(),
2165        .storage = storage,
2166    } }));
2167}
2168fn floatMod(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2169    const pt = sema.pt;
2170    const zcu = pt.zcu;
2171    const target = zcu.getTarget();
2172    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2173        16 => .{ .f16 = @mod(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2174        32 => .{ .f32 = @mod(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2175        64 => .{ .f64 = @mod(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2176        80 => .{ .f80 = @mod(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2177        128 => .{ .f128 = @mod(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2178        else => unreachable,
2179    };
2180    return .fromInterned(try pt.intern(.{ .float = .{
2181        .ty = ty.toIntern(),
2182        .storage = storage,
2183    } }));
2184}
2185fn floatRem(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2186    const pt = sema.pt;
2187    const zcu = pt.zcu;
2188    const target = zcu.getTarget();
2189    const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
2190        16 => .{ .f16 = @rem(lhs.toFloat(f16, zcu), rhs.toFloat(f16, zcu)) },
2191        32 => .{ .f32 = @rem(lhs.toFloat(f32, zcu), rhs.toFloat(f32, zcu)) },
2192        64 => .{ .f64 = @rem(lhs.toFloat(f64, zcu), rhs.toFloat(f64, zcu)) },
2193        80 => .{ .f80 = @rem(lhs.toFloat(f80, zcu), rhs.toFloat(f80, zcu)) },
2194        128 => .{ .f128 = @rem(lhs.toFloat(f128, zcu), rhs.toFloat(f128, zcu)) },
2195        else => unreachable,
2196    };
2197    return .fromInterned(try pt.intern(.{ .float = .{
2198        .ty = ty.toIntern(),
2199        .storage = storage,
2200    } }));
2201}
2202
2203fn intBitwiseNot(sema: *Sema, val: Value, ty: Type) !Value {
2204    const pt = sema.pt;
2205    const zcu = pt.zcu;
2206
2207    if (val.isUndef(zcu)) return pt.undefValue(ty);
2208    if (ty.toIntern() == .bool_type) return .makeBool(!val.toBool());
2209    const info = ty.intInfo(zcu);
2210    if (info.bits == 0) return val;
2211
2212    var val_space: Value.BigIntSpace = undefined;
2213    const val_bigint = val.toBigInt(&val_space, zcu);
2214    const limbs = try sema.arena.alloc(
2215        std.math.big.Limb,
2216        std.math.big.int.calcTwosCompLimbCount(info.bits),
2217    );
2218    var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2219    result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2220    return pt.intValue_big(ty, result_bigint.toConst());
2221}
2222/// Given an integer or boolean type, creates an value of that with the bit pattern 0xAA.
2223/// This is used to convert undef values into 0xAA when performing e.g. bitwise operations.
2224/// TODO: Eliminate this function and everything it stands for (related: #19634).
2225fn intValueAa(sema: *Sema, ty: Type) !Value {
2226    const pt = sema.pt;
2227    const zcu = pt.zcu;
2228
2229    if (ty.toIntern() == .bool_type) return .true;
2230    if (ty.toIntern() == .u0_type or ty.toIntern() == .i0_type) return pt.intValue(ty, 0);
2231    const info = ty.intInfo(zcu);
2232
2233    const buf = try sema.arena.alloc(u8, (info.bits + 7) / 8);
2234    @memset(buf, 0xAA);
2235
2236    const limbs = try sema.arena.alloc(
2237        std.math.big.Limb,
2238        std.math.big.int.calcTwosCompLimbCount(info.bits),
2239    );
2240    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2241    result_bigint.readTwosComplement(buf, info.bits, zcu.getTarget().cpu.arch.endian(), info.signedness);
2242    return pt.intValue_big(ty, result_bigint.toConst());
2243}
2244fn intBitwiseAnd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2245    const pt = sema.pt;
2246    const zcu = pt.zcu;
2247
2248    if (ty.toIntern() == .bool_type) return .makeBool(lhs.toBool() and rhs.toBool());
2249
2250    var lhs_space: Value.BigIntSpace = undefined;
2251    var rhs_space: Value.BigIntSpace = undefined;
2252    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2253    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2254    const limbs = try sema.arena.alloc(
2255        std.math.big.Limb,
2256        // + 1 for negatives
2257        @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2258    );
2259    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2260    result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2261    return pt.intValue_big(ty, result_bigint.toConst());
2262}
2263fn intBitwiseNand(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2264    const pt = sema.pt;
2265    const zcu = pt.zcu;
2266
2267    if (ty.toIntern() == .bool_type) return .makeBool(!(lhs.toBool() and rhs.toBool()));
2268    const info = ty.intInfo(zcu);
2269
2270    var lhs_space: Value.BigIntSpace = undefined;
2271    var rhs_space: Value.BigIntSpace = undefined;
2272    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2273    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2274    const limbs = try sema.arena.alloc(
2275        std.math.big.Limb,
2276        @max(
2277            // + 1 for negatives
2278            @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2279            std.math.big.int.calcTwosCompLimbCount(info.bits),
2280        ),
2281    );
2282    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2283    result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2284    result_bigint.bitNotWrap(result_bigint.toConst(), info.signedness, info.bits);
2285    return pt.intValue_big(ty, result_bigint.toConst());
2286}
2287fn intBitwiseOr(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2288    const pt = sema.pt;
2289    const zcu = pt.zcu;
2290
2291    if (ty.toIntern() == .bool_type) return .makeBool(lhs.toBool() or rhs.toBool());
2292
2293    var lhs_space: Value.BigIntSpace = undefined;
2294    var rhs_space: Value.BigIntSpace = undefined;
2295    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2296    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2297    const limbs = try sema.arena.alloc(
2298        std.math.big.Limb,
2299        @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2300    );
2301    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2302    result_bigint.bitOr(lhs_bigint, rhs_bigint);
2303    return pt.intValue_big(ty, result_bigint.toConst());
2304}
2305fn intBitwiseXor(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
2306    const pt = sema.pt;
2307    const zcu = pt.zcu;
2308
2309    if (ty.toIntern() == .bool_type) return .makeBool(lhs.toBool() != rhs.toBool());
2310
2311    var lhs_space: Value.BigIntSpace = undefined;
2312    var rhs_space: Value.BigIntSpace = undefined;
2313    const lhs_bigint = lhs.toBigInt(&lhs_space, zcu);
2314    const rhs_bigint = rhs.toBigInt(&rhs_space, zcu);
2315    const limbs = try sema.arena.alloc(
2316        std.math.big.Limb,
2317        // + 1 for negatives
2318        @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2319    );
2320    var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
2321    result_bigint.bitXor(lhs_bigint, rhs_bigint);
2322    return pt.intValue_big(ty, result_bigint.toConst());
2323}
2324
2325const Sema = @import("../Sema.zig");
2326const Block = Sema.Block;
2327const InternPool = @import("../InternPool.zig");
2328const Type = @import("../Type.zig");
2329const Value = @import("../Value.zig");
2330const Zcu = @import("../Zcu.zig");
2331const CompileError = Zcu.CompileError;
2332const LazySrcLoc = Zcu.LazySrcLoc;
2333
2334const std = @import("std");
2335const assert = std.debug.assert;
2336const Allocator = std.mem.Allocator;
2337const BigIntMutable = std.math.big.int.Mutable;