Commit 75900ec1b5

Andrew Kelley <andrew@ziglang.org>
2023-05-07 04:20:52
stage2: move integer values to InternPool
1 parent 73720b6
src/arch/wasm/CodeGen.zig
@@ -3083,20 +3083,21 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
         },
         .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(mod)) },
         .Float => switch (ty.floatBits(func.target)) {
-            16 => return WValue{ .imm32 = @bitCast(u16, val.toFloat(f16)) },
-            32 => return WValue{ .float32 = val.toFloat(f32) },
-            64 => return WValue{ .float64 = val.toFloat(f64) },
+            16 => return WValue{ .imm32 = @bitCast(u16, val.toFloat(f16, mod)) },
+            32 => return WValue{ .float32 = val.toFloat(f32, mod) },
+            64 => return WValue{ .float64 = val.toFloat(f64, mod) },
             else => unreachable,
         },
-        .Pointer => switch (val.ip_index) {
-            .null_value => return WValue{ .imm32 = 0 },
+        .Pointer => return switch (val.ip_index) {
+            .null_value => WValue{ .imm32 = 0 },
             .none => switch (val.tag()) {
-                .field_ptr, .elem_ptr, .opt_payload_ptr => return func.lowerParentPtr(val, 0),
-                .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(mod)) },
-                .zero => return WValue{ .imm32 = 0 },
+                .field_ptr, .elem_ptr, .opt_payload_ptr => func.lowerParentPtr(val, 0),
                 else => return func.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}),
             },
-            else => unreachable,
+            else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                .int => |int| WValue{ .imm32 = @intCast(u32, int.storage.u64) },
+                else => unreachable,
+            },
         },
         .Enum => {
             if (val.castTag(.enum_field_index)) |field_index| {
@@ -3137,7 +3138,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
             if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) {
                 // We use the error type directly as the type.
                 const is_pl = val.errorUnionIsPayload();
-                const err_val = if (!is_pl) val else Value.initTag(.zero);
+                const err_val = if (!is_pl) val else Value.zero;
                 return func.lowerConstant(err_val, error_type);
             }
             return func.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{});
@@ -3160,11 +3161,10 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
             assert(struct_obj.layout == .Packed);
             var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer
             val.writeToPackedMemory(ty, func.bin_file.base.options.module.?, &buf, 0) catch unreachable;
-            var payload: Value.Payload.U64 = .{
-                .base = .{ .tag = .int_u64 },
-                .data = std.mem.readIntLittle(u64, &buf),
-            };
-            const int_val = Value.initPayload(&payload.base);
+            const int_val = try mod.intValue(
+                struct_obj.backing_int_ty,
+                std.mem.readIntLittle(u64, &buf),
+            );
             return func.lowerConstant(int_val, struct_obj.backing_int_ty);
         },
         .Vector => {
@@ -4899,8 +4899,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         const result = try func.allocStack(inst_ty);
 
         for (0..mask_len) |index| {
-            var buf: Value.ElemValueBuffer = undefined;
-            const value = mask.elemValueBuffer(mod, index, &buf).toSignedInt(mod);
+            const value = (try mask.elemValue(mod, index)).toSignedInt(mod);
 
             try func.emitWValue(result);
 
@@ -4920,8 +4919,7 @@ fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
 
         var lanes = std.mem.asBytes(operands[1..]);
         for (0..@intCast(usize, mask_len)) |index| {
-            var buf: Value.ElemValueBuffer = undefined;
-            const mask_elem = mask.elemValueBuffer(mod, index, &buf).toSignedInt(mod);
+            const mask_elem = (try mask.elemValue(mod, index)).toSignedInt(mod);
             const base_index = if (mask_elem >= 0)
                 @intCast(u8, @intCast(i64, elem_size) * mask_elem)
             else
src/arch/x86_64/CodeGen.zig
@@ -2757,11 +2757,8 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
                 dst_ty.fmt(self.bin_file.options.module.?),
             });
 
-            var mask_pl = Value.Payload.U64{
-                .base = .{ .tag = .int_u64 },
-                .data = @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - dst_info.bits),
-            };
-            const mask_val = Value.initPayload(&mask_pl.base);
+            const elem_ty = src_ty.childType(mod);
+            const mask_val = try mod.intValue(elem_ty, @as(u64, math.maxInt(u64)) >> @intCast(u6, 64 - dst_info.bits));
 
             var splat_pl = Value.Payload.SubValue{
                 .base = .{ .tag = .repeated },
@@ -4906,18 +4903,6 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
     defer arena.deinit();
 
     const ExpectedContents = struct {
-        scalar: union {
-            i64: Value.Payload.I64,
-            big: struct {
-                limbs: [
-                    @max(
-                        std.math.big.int.Managed.default_capacity,
-                        std.math.big.int.calcTwosCompLimbCount(128),
-                    )
-                ]std.math.big.Limb,
-                pl: Value.Payload.BigInt,
-            },
-        },
         repeated: Value.Payload.SubValue,
     };
     var stack align(@alignOf(ExpectedContents)) =
@@ -11429,8 +11414,7 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
         const field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?);
         var tag_pl = Value.Payload.U32{ .base = .{ .tag = .enum_field_index }, .data = field_index };
         const tag_val = Value.initPayload(&tag_pl.base);
-        var tag_int_pl: Value.Payload.U64 = undefined;
-        const tag_int_val = tag_val.enumToInt(tag_ty, &tag_int_pl);
+        const tag_int_val = try tag_val.enumToInt(tag_ty, mod);
         const tag_int = tag_int_val.toUnsignedInt(mod);
         const tag_off = if (layout.tag_align < layout.payload_align)
             @intCast(i32, layout.payload_size)
src/codegen/c.zig
@@ -568,11 +568,7 @@ pub const DeclGen = struct {
             var buf: Type.SlicePtrFieldTypeBuffer = undefined;
             try dg.renderValue(writer, ty.slicePtrFieldType(&buf, mod), val.slicePtr(), .Initializer);
 
-            var len_pl: Value.Payload.U64 = .{
-                .base = .{ .tag = .int_u64 },
-                .data = val.sliceLen(mod),
-            };
-            const len_val = Value.initPayload(&len_pl.base);
+            const len_val = try mod.intValue(Type.usize, val.sliceLen(mod));
 
             if (location == .StaticInitializer) {
                 return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
@@ -596,11 +592,17 @@ pub const DeclGen = struct {
         if (need_typecast) try writer.writeByte(')');
     }
 
-    // Renders a "parent" pointer by recursing to the root decl/variable
-    // that its contents are defined with respect to.
-    //
-    // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr
-    fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type, location: ValueRenderLocation) error{ OutOfMemory, AnalysisFail }!void {
+    /// Renders a "parent" pointer by recursing to the root decl/variable
+    /// that its contents are defined with respect to.
+    ///
+    /// Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr
+    fn renderParentPtr(
+        dg: *DeclGen,
+        writer: anytype,
+        ptr_val: Value,
+        ptr_ty: Type,
+        location: ValueRenderLocation,
+    ) error{ OutOfMemory, AnalysisFail }!void {
         const mod = dg.module;
 
         if (!ptr_ty.isSlice(mod)) {
@@ -608,8 +610,11 @@ pub const DeclGen = struct {
             try dg.renderType(writer, ptr_ty);
             try writer.writeByte(')');
         }
+        if (ptr_val.ip_index != .none) switch (mod.intern_pool.indexToKey(ptr_val.ip_index)) {
+            .int => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}),
+            else => unreachable,
+        };
         switch (ptr_val.tag()) {
-            .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}),
             .decl_ref_mut, .decl_ref, .variable => {
                 const decl_index = switch (ptr_val.tag()) {
                     .decl_ref => ptr_val.castTag(.decl_ref).?.data,
@@ -661,11 +666,7 @@ pub const DeclGen = struct {
                         u8_ptr_pl.data.pointee_type = Type.u8;
                         const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
 
-                        var byte_offset_pl = Value.Payload.U64{
-                            .base = .{ .tag = .int_u64 },
-                            .data = byte_offset,
-                        };
-                        const byte_offset_val = Value.initPayload(&byte_offset_pl.base);
+                        const byte_offset_val = try mod.intValue(Type.usize, byte_offset);
 
                         try writer.writeAll("((");
                         try dg.renderType(writer, u8_ptr_ty);
@@ -891,7 +892,7 @@ pub const DeclGen = struct {
                 },
                 .Array, .Vector => {
                     const ai = ty.arrayInfo(mod);
-                    if (ai.elem_type.eql(Type.u8, dg.module)) {
+                    if (ai.elem_type.eql(Type.u8, mod)) {
                         var literal = stringLiteral(writer);
                         try literal.start();
                         const c_len = ty.arrayLenIncludingSentinel(mod);
@@ -949,7 +950,7 @@ pub const DeclGen = struct {
             },
             .Float => {
                 const bits = ty.floatBits(target);
-                const f128_val = val.toFloat(f128);
+                const f128_val = val.toFloat(f128, mod);
 
                 // All unsigned ints matching float types are pre-allocated.
                 const repr_ty = mod.intType(.unsigned, bits) catch unreachable;
@@ -963,21 +964,15 @@ pub const DeclGen = struct {
                 };
 
                 switch (bits) {
-                    16 => repr_val_big.set(@bitCast(u16, val.toFloat(f16))),
-                    32 => repr_val_big.set(@bitCast(u32, val.toFloat(f32))),
-                    64 => repr_val_big.set(@bitCast(u64, val.toFloat(f64))),
-                    80 => repr_val_big.set(@bitCast(u80, val.toFloat(f80))),
+                    16 => repr_val_big.set(@bitCast(u16, val.toFloat(f16, mod))),
+                    32 => repr_val_big.set(@bitCast(u32, val.toFloat(f32, mod))),
+                    64 => repr_val_big.set(@bitCast(u64, val.toFloat(f64, mod))),
+                    80 => repr_val_big.set(@bitCast(u80, val.toFloat(f80, mod))),
                     128 => repr_val_big.set(@bitCast(u128, f128_val)),
                     else => unreachable,
                 }
 
-                var repr_val_pl = Value.Payload.BigInt{
-                    .base = .{
-                        .tag = if (repr_val_big.positive) .int_big_positive else .int_big_negative,
-                    },
-                    .data = repr_val_big.limbs[0..repr_val_big.len],
-                };
-                const repr_val = Value.initPayload(&repr_val_pl.base);
+                const repr_val = try mod.intValue_big(repr_ty, repr_val_big.toConst());
 
                 try writer.writeAll("zig_cast_");
                 try dg.renderTypeForBuiltinFnName(writer, ty);
@@ -988,10 +983,10 @@ pub const DeclGen = struct {
                     try dg.renderTypeForBuiltinFnName(writer, ty);
                     try writer.writeByte('(');
                     switch (bits) {
-                        16 => try writer.print("{x}", .{val.toFloat(f16)}),
-                        32 => try writer.print("{x}", .{val.toFloat(f32)}),
-                        64 => try writer.print("{x}", .{val.toFloat(f64)}),
-                        80 => try writer.print("{x}", .{val.toFloat(f80)}),
+                        16 => try writer.print("{x}", .{val.toFloat(f16, mod)}),
+                        32 => try writer.print("{x}", .{val.toFloat(f32, mod)}),
+                        64 => try writer.print("{x}", .{val.toFloat(f64, mod)}),
+                        80 => try writer.print("{x}", .{val.toFloat(f80, mod)}),
                         128 => try writer.print("{x}", .{f128_val}),
                         else => unreachable,
                     }
@@ -1031,10 +1026,10 @@ pub const DeclGen = struct {
                     if (std.math.isNan(f128_val)) switch (bits) {
                         // We only actually need to pass the significand, but it will get
                         // properly masked anyway, so just pass the whole value.
-                        16 => try writer.print("\"0x{x}\"", .{@bitCast(u16, val.toFloat(f16))}),
-                        32 => try writer.print("\"0x{x}\"", .{@bitCast(u32, val.toFloat(f32))}),
-                        64 => try writer.print("\"0x{x}\"", .{@bitCast(u64, val.toFloat(f64))}),
-                        80 => try writer.print("\"0x{x}\"", .{@bitCast(u80, val.toFloat(f80))}),
+                        16 => try writer.print("\"0x{x}\"", .{@bitCast(u16, val.toFloat(f16, mod))}),
+                        32 => try writer.print("\"0x{x}\"", .{@bitCast(u32, val.toFloat(f32, mod))}),
+                        64 => try writer.print("\"0x{x}\"", .{@bitCast(u64, val.toFloat(f64, mod))}),
+                        80 => try writer.print("\"0x{x}\"", .{@bitCast(u80, val.toFloat(f80, mod))}),
                         128 => try writer.print("\"0x{x}\"", .{@bitCast(u128, f128_val)}),
                         else => unreachable,
                     };
@@ -1060,19 +1055,6 @@ pub const DeclGen = struct {
                     try writer.writeAll(")NULL)");
                 },
                 .none => switch (val.tag()) {
-                    .zero => if (ty.isSlice(mod)) {
-                        var slice_pl = Value.Payload.Slice{
-                            .base = .{ .tag = .slice },
-                            .data = .{ .ptr = val, .len = Value.undef },
-                        };
-                        const slice_val = Value.initPayload(&slice_pl.base);
-
-                        return dg.renderValue(writer, ty, slice_val, location);
-                    } else {
-                        try writer.writeAll("((");
-                        try dg.renderType(writer, ty);
-                        try writer.writeAll(")NULL)");
-                    },
                     .variable => {
                         const decl = val.castTag(.variable).?.data.owner_decl;
                         return dg.renderDeclValue(writer, ty, val, decl, location);
@@ -1101,7 +1083,7 @@ pub const DeclGen = struct {
                         const extern_fn = val.castTag(.extern_fn).?.data;
                         try dg.renderDeclName(writer, extern_fn.owner_decl, 0);
                     },
-                    .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => {
+                    .lazy_align, .lazy_size => {
                         try writer.writeAll("((");
                         try dg.renderType(writer, ty);
                         return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
@@ -1116,7 +1098,14 @@ pub const DeclGen = struct {
 
                     else => unreachable,
                 },
-                else => unreachable,
+                else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                    .int => {
+                        try writer.writeAll("((");
+                        try dg.renderType(writer, ty);
+                        return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
+                    },
+                    else => unreachable,
+                },
             },
             .Array, .Vector => {
                 if (location == .FunctionArgument) {
@@ -1155,7 +1144,7 @@ pub const DeclGen = struct {
                                 .bytes => val.castTag(.bytes).?.data,
                                 .str_lit => bytes: {
                                     const str_lit = val.castTag(.str_lit).?.data;
-                                    break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
+                                    break :bytes mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
                                 },
                                 else => unreachable,
                             };
@@ -1170,21 +1159,18 @@ pub const DeclGen = struct {
                     else => {},
                 }
                 // Fall back to generic implementation.
-                var arena = std.heap.ArenaAllocator.init(dg.gpa);
-                defer arena.deinit();
-                const arena_allocator = arena.allocator();
 
                 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal
                 const max_string_initializer_len = 65535;
 
                 const ai = ty.arrayInfo(mod);
-                if (ai.elem_type.eql(Type.u8, dg.module)) {
+                if (ai.elem_type.eql(Type.u8, mod)) {
                     if (ai.len <= max_string_initializer_len) {
                         var literal = stringLiteral(writer);
                         try literal.start();
                         var index: usize = 0;
                         while (index < ai.len) : (index += 1) {
-                            const elem_val = try val.elemValue(dg.module, arena_allocator, index);
+                            const elem_val = try val.elemValue(mod, index);
                             const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod));
                             try literal.writeChar(elem_val_u8);
                         }
@@ -1198,7 +1184,7 @@ pub const DeclGen = struct {
                         var index: usize = 0;
                         while (index < ai.len) : (index += 1) {
                             if (index != 0) try writer.writeByte(',');
-                            const elem_val = try val.elemValue(dg.module, arena_allocator, index);
+                            const elem_val = try val.elemValue(mod, index);
                             const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod));
                             try writer.print("'\\x{x}'", .{elem_val_u8});
                         }
@@ -1213,7 +1199,7 @@ pub const DeclGen = struct {
                     var index: usize = 0;
                     while (index < ai.len) : (index += 1) {
                         if (index != 0) try writer.writeByte(',');
-                        const elem_val = try val.elemValue(dg.module, arena_allocator, index);
+                        const elem_val = try val.elemValue(mod, index);
                         try dg.renderValue(writer, ai.elem_type, elem_val, initializer_type);
                     }
                     if (ai.sentinel) |s| {
@@ -1361,8 +1347,7 @@ pub const DeclGen = struct {
                     const bits = Type.smallestUnsignedBits(int_info.bits - 1);
                     const bit_offset_ty = try mod.intType(.unsigned, bits);
 
-                    var bit_offset_val_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = 0 };
-                    const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
+                    var bit_offset: u64 = 0;
 
                     var eff_num_fields: usize = 0;
                     for (0..field_vals.len) |field_i| {
@@ -1394,12 +1379,13 @@ pub const DeclGen = struct {
                             if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
 
                             const cast_context = IntCastContext{ .value = .{ .value = field_val } };
-                            if (bit_offset_val_pl.data != 0) {
+                            if (bit_offset != 0) {
                                 try writer.writeAll("zig_shl_");
                                 try dg.renderTypeForBuiltinFnName(writer, ty);
                                 try writer.writeByte('(');
                                 try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument);
                                 try writer.writeAll(", ");
+                                const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
                                 try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
                                 try writer.writeByte(')');
                             } else {
@@ -1409,7 +1395,7 @@ pub const DeclGen = struct {
                             if (needs_closing_paren) try writer.writeByte(')');
                             if (eff_index != eff_num_fields - 1) try writer.writeAll(", ");
 
-                            bit_offset_val_pl.data += field_ty.bitSize(mod);
+                            bit_offset += field_ty.bitSize(mod);
                             needs_closing_paren = true;
                             eff_index += 1;
                         }
@@ -1427,15 +1413,16 @@ pub const DeclGen = struct {
                             try dg.renderType(writer, ty);
                             try writer.writeByte(')');
 
-                            if (bit_offset_val_pl.data != 0) {
+                            if (bit_offset != 0) {
                                 try dg.renderValue(writer, field_ty, field_val, .Other);
                                 try writer.writeAll(" << ");
+                                const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
                                 try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
                             } else {
                                 try dg.renderValue(writer, field_ty, field_val, .Other);
                             }
 
-                            bit_offset_val_pl.data += field_ty.bitSize(mod);
+                            bit_offset += field_ty.bitSize(mod);
                             empty = false;
                         }
                         try writer.writeByte(')');
@@ -1451,7 +1438,7 @@ pub const DeclGen = struct {
                     try writer.writeByte(')');
                 }
 
-                const field_i = ty.unionTagFieldIndex(union_obj.tag, dg.module).?;
+                const field_i = ty.unionTagFieldIndex(union_obj.tag, mod).?;
                 const field_ty = ty.unionFields().values()[field_i].ty;
                 const field_name = ty.unionFields().keys()[field_i];
                 if (ty.containerLayout() == .Packed) {
@@ -1951,10 +1938,10 @@ pub const DeclGen = struct {
 
         if (is_big) try writer.print(", {}", .{int_info.signedness == .signed});
 
-        var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits };
+        const bits_ty = if (is_big) Type.u16 else Type.u8;
         try writer.print(", {}", .{try dg.fmtIntLiteral(
-            if (is_big) Type.u16 else Type.u8,
-            Value.initPayload(&bits_pl.base),
+            bits_ty,
+            try mod.intValue(bits_ty, int_info.bits),
             .FunctionArgument,
         )});
     }
@@ -2495,8 +2482,7 @@ pub fn genErrDecls(o: *Object) !void {
     for (mod.error_name_list.items, 0..) |name, value| {
         if (value != 0) try writer.writeByte(',');
 
-        var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
-        const len_val = Value.initPayload(&len_pl.base);
+        const len_val = try mod.intValue(Type.usize, name.len);
 
         try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{
             fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
@@ -2548,8 +2534,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
                 };
                 const tag_val = Value.initPayload(&tag_pl.base);
 
-                var int_pl: Value.Payload.U64 = undefined;
-                const int_val = tag_val.enumToInt(enum_ty, &int_pl);
+                const int_val = try tag_val.enumToInt(enum_ty, mod);
 
                 const name_ty = try mod.arrayType(.{
                     .len = name.len,
@@ -2560,8 +2545,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
                 var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name };
                 const name_val = Value.initPayload(&name_pl.base);
 
-                var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
-                const len_val = Value.initPayload(&len_pl.base);
+                const len_val = try mod.intValue(Type.usize, name.len);
 
                 try w.print("  case {}: {{\n   static ", .{
                     try o.dg.fmtIntLiteral(enum_ty, int_val, .Other),
@@ -3396,12 +3380,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
         const host_ty = try mod.intType(.unsigned, host_bits);
 
         const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(host_bits - 1));
-
-        var bit_offset_val_pl: Value.Payload.U64 = .{
-            .base = .{ .tag = .int_u64 },
-            .data = ptr_info.bit_offset,
-        };
-        const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
+        const bit_offset_val = try mod.intValue(bit_offset_ty, ptr_info.bit_offset);
 
         const field_ty = try mod.intType(.unsigned, @intCast(u16, src_ty.bitSize(mod)));
 
@@ -3563,14 +3542,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
         try v.elem(f, writer);
     } else switch (dest_int_info.signedness) {
         .unsigned => {
-            var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa);
-            defer arena.deinit();
-
-            const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
-            var stack align(@alignOf(ExpectedContents)) =
-                std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());
-
-            const mask_val = try inst_scalar_ty.maxInt(stack.get(), mod);
+            const mask_val = try inst_scalar_ty.maxIntScalar(mod);
             try writer.writeAll("zig_and_");
             try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
             try writer.writeByte('(');
@@ -3581,11 +3553,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
         .signed => {
             const c_bits = toCIntBits(scalar_int_info.bits) orelse
                 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
-            var shift_pl = Value.Payload.U64{
-                .base = .{ .tag = .int_u64 },
-                .data = c_bits - dest_bits,
-            };
-            const shift_val = Value.initPayload(&shift_pl.base);
+            const shift_val = try mod.intValue(Type.u8, c_bits - dest_bits);
 
             try writer.writeAll("zig_shr_");
             try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
@@ -3705,12 +3673,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
         const host_ty = try mod.intType(.unsigned, host_bits);
 
         const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(host_bits - 1));
-
-        var bit_offset_val_pl: Value.Payload.U64 = .{
-            .base = .{ .tag = .int_u64 },
-            .data = ptr_info.bit_offset,
-        };
-        const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
+        const bit_offset_val = try mod.intValue(bit_offset_ty, ptr_info.bit_offset);
 
         const src_bits = src_ty.bitSize(mod);
 
@@ -3725,11 +3688,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
         try mask.shiftLeft(&mask, ptr_info.bit_offset);
         try mask.bitNotWrap(&mask, .unsigned, host_bits);
 
-        var mask_pl = Value.Payload.BigInt{
-            .base = .{ .tag = .int_big_positive },
-            .data = mask.limbs[0..mask.len()],
-        };
-        const mask_val = Value.initPayload(&mask_pl.base);
+        const mask_val = try mod.intValue_big(host_ty, mask.toConst());
 
         try f.writeCValueDeref(writer, ptr_val);
         try v.elem(f, writer);
@@ -5356,11 +5315,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
             u8_ptr_pl.data.pointee_type = Type.u8;
             const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
 
-            var byte_offset_pl = Value.Payload.U64{
-                .base = .{ .tag = .int_u64 },
-                .data = byte_offset,
-            };
-            const byte_offset_val = Value.initPayload(&byte_offset_pl.base);
+            const byte_offset_val = try mod.intValue(Type.usize, byte_offset);
 
             try writer.writeAll("((");
             try f.renderType(writer, u8_ptr_ty);
@@ -5412,11 +5367,7 @@ fn fieldPtr(
             u8_ptr_pl.data.pointee_type = Type.u8;
             const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
 
-            var byte_offset_pl = Value.Payload.U64{
-                .base = .{ .tag = .int_u64 },
-                .data = byte_offset,
-            };
-            const byte_offset_val = Value.initPayload(&byte_offset_pl.base);
+            const byte_offset_val = try mod.intValue(Type.usize, byte_offset);
 
             try writer.writeAll("((");
             try f.renderType(writer, u8_ptr_ty);
@@ -5466,11 +5417,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
 
                 const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
 
-                var bit_offset_val_pl: Value.Payload.U64 = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = struct_obj.packedFieldBitOffset(mod, extra.field_index),
-                };
-                const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
+                const bit_offset = struct_obj.packedFieldBitOffset(mod, extra.field_index);
+                const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
 
                 const field_int_signedness = if (inst_ty.isAbiInt(mod))
                     inst_ty.intInfo(mod).signedness
@@ -5492,13 +5440,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
                     try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
                     try writer.writeByte('(');
                 }
-                if (bit_offset_val_pl.data > 0) {
+                if (bit_offset > 0) {
                     try writer.writeAll("zig_shr_");
                     try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
                     try writer.writeByte('(');
                 }
                 try f.writeCValue(writer, struct_byval, .Other);
-                if (bit_offset_val_pl.data > 0) {
+                if (bit_offset > 0) {
                     try writer.writeAll(", ");
                     try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
                     try writer.writeByte(')');
@@ -5854,9 +5802,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
     } else try f.writeCValue(writer, operand, .Initializer);
     try writer.writeAll("; ");
 
-    const array_len = array_ty.arrayLen(mod);
-    var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len };
-    const len_val = Value.initPayload(&len_pl.base);
+    const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod));
     try f.writeCValueMember(writer, local, .{ .identifier = "len" });
     try writer.print(" = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)});
 
@@ -6632,26 +6578,17 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
     const local = try f.allocLocal(inst, inst_ty);
     try reap(f, inst, &.{ extra.a, extra.b }); // local cannot alias operands
     for (0..extra.mask_len) |index| {
-        var dst_pl = Value.Payload.U64{
-            .base = .{ .tag = .int_u64 },
-            .data = @intCast(u64, index),
-        };
-
         try f.writeCValue(writer, local, .Other);
         try writer.writeByte('[');
-        try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&dst_pl.base), .Other);
+        try f.object.dg.renderValue(writer, Type.usize, try mod.intValue(Type.usize, index), .Other);
         try writer.writeAll("] = ");
 
-        var buf: Value.ElemValueBuffer = undefined;
-        const mask_elem = mask.elemValueBuffer(mod, index, &buf).toSignedInt(mod);
-        var src_pl = Value.Payload.U64{
-            .base = .{ .tag = .int_u64 },
-            .data = @intCast(u64, mask_elem ^ mask_elem >> 63),
-        };
+        const mask_elem = (try mask.elemValue(mod, index)).toSignedInt(mod);
+        const src_val = try mod.intValue(Type.usize, @intCast(u64, mask_elem ^ mask_elem >> 63));
 
         try f.writeCValue(writer, if (mask_elem >= 0) lhs else rhs, .Other);
         try writer.writeByte('[');
-        try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&src_pl.base), .Other);
+        try f.object.dg.renderValue(writer, Type.usize, src_val, .Other);
         try writer.writeAll("];\n");
     }
 
@@ -6730,8 +6667,6 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
     defer arena.deinit();
 
     const ExpectedContents = union {
-        u: Value.Payload.U64,
-        i: Value.Payload.I64,
         f16: Value.Payload.Float_16,
         f32: Value.Payload.Float_32,
         f64: Value.Payload.Float_64,
@@ -6746,13 +6681,13 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
         .And => switch (scalar_ty.zigTypeTag(mod)) {
             .Bool => Value.one,
             else => switch (scalar_ty.intInfo(mod).signedness) {
-                .unsigned => try scalar_ty.maxInt(stack.get(), mod),
+                .unsigned => try scalar_ty.maxIntScalar(mod),
                 .signed => Value.negative_one,
             },
         },
         .Min => switch (scalar_ty.zigTypeTag(mod)) {
             .Bool => Value.one,
-            .Int => try scalar_ty.maxInt(stack.get(), mod),
+            .Int => try scalar_ty.maxIntScalar(mod),
             .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target),
             else => unreachable,
         },
@@ -6879,8 +6814,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
 
                 const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
 
-                var bit_offset_val_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = 0 };
-                const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
+                var bit_offset: u64 = 0;
 
                 var empty = true;
                 for (0..elements.len) |field_i| {
@@ -6925,12 +6859,13 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
                     }
 
                     try writer.writeAll(", ");
+                    const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
                     try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
                     try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);
                     try writer.writeByte(')');
                     if (!empty) try writer.writeByte(')');
 
-                    bit_offset_val_pl.data += field_ty.bitSize(mod);
+                    bit_offset += field_ty.bitSize(mod);
                     empty = false;
                 }
 
@@ -6976,8 +6911,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
             };
             const tag_val = Value.initPayload(&tag_pl.base);
 
-            var int_pl: Value.Payload.U64 = undefined;
-            const int_val = tag_val.enumToInt(tag_ty, &int_pl);
+            const int_val = try tag_val.enumToInt(tag_ty, mod);
 
             const a = try Assignment.start(f, writer, tag_ty);
             try f.writeCValueMember(writer, local, .{ .identifier = "tag" });
@@ -7640,10 +7574,6 @@ fn formatIntLiteral(
                 c_limb_int_info.signedness = .unsigned;
                 c_limb_cty = c_limb_info.cty;
             }
-            var c_limb_val_pl = Value.Payload.BigInt{
-                .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative },
-                .data = c_limb_mut.limbs[0..c_limb_mut.len],
-            };
 
             if (limb_offset > 0) try writer.writeAll(", ");
             try formatIntLiteral(.{
@@ -7651,7 +7581,7 @@ fn formatIntLiteral(
                 .int_info = c_limb_int_info,
                 .kind = data.kind,
                 .cty = c_limb_cty,
-                .val = Value.initPayload(&c_limb_val_pl.base),
+                .val = try mod.intValue_big(Type.comptime_int, c_limb_mut.toConst()),
             }, fmt, options, writer);
         }
     }
@@ -7750,7 +7680,7 @@ const Vectorize = struct {
     pub fn start(f: *Function, inst: Air.Inst.Index, writer: anytype, ty: Type) !Vectorize {
         const mod = f.object.dg.module;
         return if (ty.zigTypeTag(mod) == .Vector) index: {
-            var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = ty.vectorLen(mod) };
+            const len_val = try mod.intValue(Type.usize, ty.vectorLen(mod));
 
             const local = try f.allocLocal(inst, Type.usize);
 
@@ -7759,7 +7689,7 @@ const Vectorize = struct {
             try writer.print(" = {d}; ", .{try f.fmtIntLiteral(Type.usize, Value.zero)});
             try f.writeCValue(writer, local, .Other);
             try writer.print(" < {d}; ", .{
-                try f.fmtIntLiteral(Type.usize, Value.initPayload(&len_pl.base)),
+                try f.fmtIntLiteral(Type.usize, len_val),
             });
             try f.writeCValue(writer, local, .Other);
             try writer.print(" += {d}) {{\n", .{try f.fmtIntLiteral(Type.usize, Value.one)});
src/codegen/llvm.zig
@@ -12,6 +12,7 @@ const link = @import("../link.zig");
 const Compilation = @import("../Compilation.zig");
 const build_options = @import("build_options");
 const Module = @import("../Module.zig");
+const InternPool = @import("../InternPool.zig");
 const Package = @import("../Package.zig");
 const TypedValue = @import("../TypedValue.zig");
 const Air = @import("../Air.zig");
@@ -1535,8 +1536,7 @@ pub const Object = struct {
                     defer gpa.free(field_name_z);
 
                     buf_field_index.data = @intCast(u32, i);
-                    var buf_u64: Value.Payload.U64 = undefined;
-                    const field_int_val = field_index_val.enumToInt(ty, &buf_u64);
+                    const field_int_val = try field_index_val.enumToInt(ty, mod);
 
                     var bigint_space: Value.BigIntSpace = undefined;
                     const bigint = field_int_val.toBigInt(&bigint_space, mod);
@@ -3255,8 +3255,6 @@ pub const DeclGen = struct {
                 const llvm_type = try dg.lowerType(tv.ty);
                 return if (tv.val.toBool(mod)) llvm_type.constAllOnes() else llvm_type.constNull();
             },
-            // TODO this duplicates code with Pointer but they should share the handling
-            // of the tv.val.tag() and then Int should do extra constPtrToInt on top
             .Int => switch (tv.val.ip_index) {
                 .none => switch (tv.val.tag()) {
                     .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index),
@@ -3277,8 +3275,7 @@ pub const DeclGen = struct {
                 },
             },
             .Enum => {
-                var int_buffer: Value.Payload.U64 = undefined;
-                const int_val = tv.enumToInt(&int_buffer);
+                const int_val = try tv.enumToInt(mod);
 
                 var bigint_space: Value.BigIntSpace = undefined;
                 const bigint = int_val.toBigInt(&bigint_space, mod);
@@ -3307,25 +3304,25 @@ pub const DeclGen = struct {
                 const llvm_ty = try dg.lowerType(tv.ty);
                 switch (tv.ty.floatBits(target)) {
                     16 => {
-                        const repr = @bitCast(u16, tv.val.toFloat(f16));
+                        const repr = @bitCast(u16, tv.val.toFloat(f16, mod));
                         const llvm_i16 = dg.context.intType(16);
                         const int = llvm_i16.constInt(repr, .False);
                         return int.constBitCast(llvm_ty);
                     },
                     32 => {
-                        const repr = @bitCast(u32, tv.val.toFloat(f32));
+                        const repr = @bitCast(u32, tv.val.toFloat(f32, mod));
                         const llvm_i32 = dg.context.intType(32);
                         const int = llvm_i32.constInt(repr, .False);
                         return int.constBitCast(llvm_ty);
                     },
                     64 => {
-                        const repr = @bitCast(u64, tv.val.toFloat(f64));
+                        const repr = @bitCast(u64, tv.val.toFloat(f64, mod));
                         const llvm_i64 = dg.context.intType(64);
                         const int = llvm_i64.constInt(repr, .False);
                         return int.constBitCast(llvm_ty);
                     },
                     80 => {
-                        const float = tv.val.toFloat(f80);
+                        const float = tv.val.toFloat(f80, mod);
                         const repr = std.math.break_f80(float);
                         const llvm_i80 = dg.context.intType(80);
                         var x = llvm_i80.constInt(repr.exp, .False);
@@ -3338,7 +3335,7 @@ pub const DeclGen = struct {
                         }
                     },
                     128 => {
-                        var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));
+                        var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128, mod));
                         // LLVM seems to require that the lower half of the f128 be placed first
                         // in the buffer.
                         if (native_endian == .Big) {
@@ -3388,7 +3385,7 @@ pub const DeclGen = struct {
                         };
                         return dg.context.constStruct(&fields, fields.len, .False);
                     },
-                    .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => {
+                    .lazy_align, .lazy_size => {
                         const llvm_usize = try dg.lowerType(Type.usize);
                         const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(mod), .False);
                         return llvm_int.constIntToPtr(try dg.lowerType(tv.ty));
@@ -3396,10 +3393,6 @@ pub const DeclGen = struct {
                     .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => {
                         return dg.lowerParentPtr(tv.val, tv.ty.ptrInfo(mod).bit_offset % 8 == 0);
                     },
-                    .zero => {
-                        const llvm_type = try dg.lowerType(tv.ty);
-                        return llvm_type.constNull();
-                    },
                     .opt_payload => {
                         const payload = tv.val.castTag(.opt_payload).?.data;
                         return dg.lowerParentPtr(payload, tv.ty.ptrInfo(mod).bit_offset % 8 == 0);
@@ -3408,7 +3401,10 @@ pub const DeclGen = struct {
                         tv.ty.fmtDebug(), tag,
                     }),
                 },
-                else => unreachable,
+                else => switch (mod.intern_pool.indexToKey(tv.val.ip_index)) {
+                    .int => |int| return lowerIntAsPtr(dg, int),
+                    else => unreachable,
+                },
             },
             .Array => switch (tv.val.tag()) {
                 .bytes => {
@@ -3592,7 +3588,7 @@ pub const DeclGen = struct {
 
                 if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) {
                     // We use the error type directly as the type.
-                    const err_val = if (!is_pl) tv.val else Value.initTag(.zero);
+                    const err_val = if (!is_pl) tv.val else Value.zero;
                     return dg.lowerValue(.{ .ty = Type.anyerror, .val = err_val });
                 }
 
@@ -3600,7 +3596,7 @@ pub const DeclGen = struct {
                 const error_align = Type.anyerror.abiAlignment(mod);
                 const llvm_error_value = try dg.lowerValue(.{
                     .ty = Type.anyerror,
-                    .val = if (is_pl) Value.initTag(.zero) else tv.val,
+                    .val = if (is_pl) Value.zero else tv.val,
                 });
                 const llvm_payload_value = try dg.lowerValue(.{
                     .ty = payload_type,
@@ -3882,14 +3878,9 @@ pub const DeclGen = struct {
                     const llvm_elems = try dg.gpa.alloc(*llvm.Value, vector_len);
                     defer dg.gpa.free(llvm_elems);
                     for (llvm_elems, 0..) |*elem, i| {
-                        var byte_payload: Value.Payload.U64 = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = bytes[i],
-                        };
-
                         elem.* = try dg.lowerValue(.{
                             .ty = elem_ty,
-                            .val = Value.initPayload(&byte_payload.base),
+                            .val = try mod.intValue(elem_ty, bytes[i]),
                         });
                     }
                     return llvm.constVector(
@@ -3940,14 +3931,9 @@ pub const DeclGen = struct {
                     const llvm_elems = try dg.gpa.alloc(*llvm.Value, vector_len);
                     defer dg.gpa.free(llvm_elems);
                     for (llvm_elems, 0..) |*elem, i| {
-                        var byte_payload: Value.Payload.U64 = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = bytes[i],
-                        };
-
                         elem.* = try dg.lowerValue(.{
                             .ty = elem_ty,
-                            .val = Value.initPayload(&byte_payload.base),
+                            .val = try mod.intValue(elem_ty, bytes[i]),
                         });
                     }
                     return llvm.constVector(
@@ -3974,6 +3960,13 @@ pub const DeclGen = struct {
         }
     }
 
+    fn lowerIntAsPtr(dg: *DeclGen, int: InternPool.Key.Int) *llvm.Value {
+        var bigint_space: Value.BigIntSpace = undefined;
+        const bigint = int.storage.toBigInt(&bigint_space);
+        const llvm_int = lowerBigInt(dg, Type.usize, bigint);
+        return llvm_int.constIntToPtr(dg.context.pointerType(0));
+    }
+
     fn lowerBigInt(dg: *DeclGen, ty: Type, bigint: std.math.big.int.Const) *llvm.Value {
         const mod = dg.module;
         const int_info = ty.intInfo(mod);
@@ -4018,6 +4011,10 @@ pub const DeclGen = struct {
     fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, byte_aligned: bool) Error!*llvm.Value {
         const mod = dg.module;
         const target = mod.getTarget();
+        if (ptr_val.ip_index != .none) switch (mod.intern_pool.indexToKey(ptr_val.ip_index)) {
+            .int => |int| return lowerIntAsPtr(dg, int),
+            else => unreachable,
+        };
         switch (ptr_val.tag()) {
             .decl_ref_mut => {
                 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;
@@ -4031,18 +4028,6 @@ pub const DeclGen = struct {
                 const decl = ptr_val.castTag(.variable).?.data.owner_decl;
                 return dg.lowerParentPtrDecl(ptr_val, decl);
             },
-            .int_i64 => {
-                const int = ptr_val.castTag(.int_i64).?.data;
-                const llvm_usize = try dg.lowerType(Type.usize);
-                const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False);
-                return llvm_int.constIntToPtr(dg.context.pointerType(0));
-            },
-            .int_u64 => {
-                const int = ptr_val.castTag(.int_u64).?.data;
-                const llvm_usize = try dg.lowerType(Type.usize);
-                const llvm_int = llvm_usize.constInt(int, .False);
-                return llvm_int.constIntToPtr(dg.context.pointerType(0));
-            },
             .field_ptr => {
                 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
                 const parent_llvm_ptr = try dg.lowerParentPtr(field_ptr.container_ptr, byte_aligned);
@@ -4185,10 +4170,6 @@ pub const DeclGen = struct {
         if (tv.ty.isSlice(mod)) {
             var buf: Type.SlicePtrFieldTypeBuffer = undefined;
             const ptr_ty = tv.ty.slicePtrFieldType(&buf, mod);
-            var slice_len: Value.Payload.U64 = .{
-                .base = .{ .tag = .int_u64 },
-                .data = tv.val.sliceLen(mod),
-            };
             const fields: [2]*llvm.Value = .{
                 try self.lowerValue(.{
                     .ty = ptr_ty,
@@ -4196,7 +4177,7 @@ pub const DeclGen = struct {
                 }),
                 try self.lowerValue(.{
                     .ty = Type.usize,
-                    .val = Value.initPayload(&slice_len.base),
+                    .val = try mod.intValue(Type.usize, tv.val.sliceLen(mod)),
                 }),
             };
             return self.context.constStruct(&fields, fields.len, .False);
@@ -8507,8 +8488,7 @@ pub const FuncGen = struct {
         const dest_slice = try self.resolveInst(bin_op.lhs);
         const ptr_ty = self.typeOf(bin_op.lhs);
         const elem_ty = self.typeOf(bin_op.rhs);
-        const module = self.dg.module;
-        const target = module.getTarget();
+        const target = mod.getTarget();
         const dest_ptr_align = ptr_ty.ptrAlignment(mod);
         const u8_llvm_ty = self.context.intType(8);
         const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty);
@@ -8526,7 +8506,7 @@ pub const FuncGen = struct {
                 const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
                 _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile);
 
-                if (safety and module.comp.bin_file.options.valgrind) {
+                if (safety and mod.comp.bin_file.options.valgrind) {
                     self.valgrindMarkUndef(dest_ptr, len);
                 }
                 return null;
@@ -8536,8 +8516,7 @@ pub const FuncGen = struct {
             // repeating byte pattern, for example, `@as(u64, 0)` has a
             // repeating byte pattern of 0 bytes. In such case, the memset
             // intrinsic can be used.
-            var value_buffer: Value.Payload.U64 = undefined;
-            if (try elem_val.hasRepeatedByteRepr(elem_ty, module, &value_buffer)) |byte_val| {
+            if (try elem_val.hasRepeatedByteRepr(elem_ty, mod)) |byte_val| {
                 const fill_byte = try self.resolveValue(.{
                     .ty = Type.u8,
                     .val = byte_val,
@@ -8829,16 +8808,10 @@ pub const FuncGen = struct {
 
         for (names) |name| {
             const err_int = mod.global_error_set.get(name).?;
-            const this_tag_int_value = int: {
-                var tag_val_payload: Value.Payload.U64 = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = err_int,
-                };
-                break :int try self.dg.lowerValue(.{
-                    .ty = Type.err_int,
-                    .val = Value.initPayload(&tag_val_payload.base),
-                });
-            };
+            const this_tag_int_value = try self.dg.lowerValue(.{
+                .ty = Type.err_int,
+                .val = try mod.intValue(Type.err_int, err_int),
+            });
             switch_instr.addCase(this_tag_int_value, valid_block);
         }
         self.builder.positionBuilderAtEnd(valid_block);
@@ -9122,8 +9095,7 @@ pub const FuncGen = struct {
         const llvm_i32 = self.context.intType(32);
 
         for (values, 0..) |*val, i| {
-            var buf: Value.ElemValueBuffer = undefined;
-            const elem = mask.elemValueBuffer(mod, i, &buf);
+            const elem = try mask.elemValue(mod, i);
             if (elem.isUndef()) {
                 val.* = llvm_i32.getUndef();
             } else {
@@ -9457,8 +9429,7 @@ pub const FuncGen = struct {
                 .data = @intCast(u32, enum_field_index),
             };
             const tag_val = Value.initPayload(&tag_val_payload.base);
-            var int_payload: Value.Payload.U64 = undefined;
-            const tag_int_val = tag_val.enumToInt(tag_ty, &int_payload);
+            const tag_int_val = try tag_val.enumToInt(tag_ty, mod);
             break :blk tag_int_val.toUnsignedInt(mod);
         };
         if (layout.payload_size == 0) {
src/codegen/spirv.zig
@@ -555,15 +555,15 @@ pub const DeclGen = struct {
             // TODO: Swap endianess if the compiler is big endian.
             switch (ty.floatBits(target)) {
                 16 => {
-                    const float_bits = val.toFloat(f16);
+                    const float_bits = val.toFloat(f16, mod);
                     try self.addBytes(std.mem.asBytes(&float_bits)[0..@intCast(usize, len)]);
                 },
                 32 => {
-                    const float_bits = val.toFloat(f32);
+                    const float_bits = val.toFloat(f32, mod);
                     try self.addBytes(std.mem.asBytes(&float_bits)[0..@intCast(usize, len)]);
                 },
                 64 => {
-                    const float_bits = val.toFloat(f64);
+                    const float_bits = val.toFloat(f64, mod);
                     try self.addBytes(std.mem.asBytes(&float_bits)[0..@intCast(usize, len)]);
                 },
                 else => unreachable,
@@ -584,7 +584,7 @@ pub const DeclGen = struct {
                     // TODO: Properly lower function pointers. For now we are going to hack around it and
                     // just generate an empty pointer. Function pointers are represented by usize for now,
                     // though.
-                    try self.addInt(Type.usize, Value.initTag(.zero));
+                    try self.addInt(Type.usize, Value.zero);
                     // TODO: Add dependency
                     return;
                 },
@@ -743,8 +743,7 @@ pub const DeclGen = struct {
                     try self.addUndef(padding);
                 },
                 .Enum => {
-                    var int_val_buffer: Value.Payload.U64 = undefined;
-                    const int_val = val.enumToInt(ty, &int_val_buffer);
+                    const int_val = try val.enumToInt(ty, mod);
 
                     const int_ty = ty.intTagType();
 
@@ -787,22 +786,24 @@ pub const DeclGen = struct {
 
                     try self.addUndef(layout.padding);
                 },
-                .ErrorSet => switch (val.tag()) {
-                    .@"error" => {
-                        const err_name = val.castTag(.@"error").?.data.name;
-                        const kv = try dg.module.getErrorValue(err_name);
-                        try self.addConstInt(u16, @intCast(u16, kv.value));
+                .ErrorSet => switch (val.ip_index) {
+                    .none => switch (val.tag()) {
+                        .@"error" => {
+                            const err_name = val.castTag(.@"error").?.data.name;
+                            const kv = try dg.module.getErrorValue(err_name);
+                            try self.addConstInt(u16, @intCast(u16, kv.value));
+                        },
+                        else => unreachable,
                     },
-                    .zero => {
-                        // Unactivated error set.
-                        try self.addConstInt(u16, 0);
+                    else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                        .int => |int| try self.addConstInt(u16, @intCast(u16, int.storage.u64)),
+                        else => unreachable,
                     },
-                    else => unreachable,
                 },
                 .ErrorUnion => {
                     const payload_ty = ty.errorUnionPayload();
                     const is_pl = val.errorUnionIsPayload();
-                    const error_val = if (!is_pl) val else Value.initTag(.zero);
+                    const error_val = if (!is_pl) val else Value.zero;
 
                     const eu_layout = dg.errorUnionLayout(payload_ty);
                     if (!eu_layout.payload_has_bits) {
@@ -993,9 +994,9 @@ pub const DeclGen = struct {
                 .indirect => return try self.spv.constInt(result_ty_ref, @boolToInt(val.toBool(mod))),
             },
             .Float => return switch (ty.floatBits(target)) {
-                16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16) } } }),
-                32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32) } } }),
-                64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64) } } }),
+                16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16, mod) } } }),
+                32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32, mod) } } }),
+                64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64, mod) } } }),
                 80, 128 => unreachable, // TODO
                 else => unreachable,
             },
@@ -1531,6 +1532,7 @@ pub const DeclGen = struct {
     }
 
     fn genDecl(self: *DeclGen) !void {
+        if (true) @panic("TODO: update SPIR-V backend for InternPool changes");
         const mod = self.module;
         const decl = mod.declPtr(self.decl_index);
         const spv_decl_index = try self.resolveDecl(self.decl_index);
@@ -2087,8 +2089,7 @@ pub const DeclGen = struct {
 
         var i: usize = 0;
         while (i < mask_len) : (i += 1) {
-            var buf: Value.ElemValueBuffer = undefined;
-            const elem = mask.elemValueBuffer(self.module, i, &buf);
+            const elem = try mask.elemValue(self.module, i);
             if (elem.isUndef()) {
                 self.func.body.writeOperand(spec.LiteralInteger, 0xFFFF_FFFF);
             } else {
@@ -3146,9 +3147,8 @@ pub const DeclGen = struct {
                     const int_val = switch (cond_ty.zigTypeTag(mod)) {
                         .Int => if (cond_ty.isSignedInt(mod)) @bitCast(u64, value.toSignedInt(mod)) else value.toUnsignedInt(mod),
                         .Enum => blk: {
-                            var int_buffer: Value.Payload.U64 = undefined;
                             // TODO: figure out of cond_ty is correct (something with enum literals)
-                            break :blk value.enumToInt(cond_ty, &int_buffer).toUnsignedInt(mod); // TODO: composite integer constants
+                            break :blk (try value.enumToInt(cond_ty, mod)).toUnsignedInt(mod); // TODO: composite integer constants
                         },
                         else => unreachable,
                     };
src/link/Dwarf.zig
@@ -421,8 +421,7 @@ pub const DeclState = struct {
                         const value = vals.keys()[field_i];
                         // TODO do not assume a 64bit enum value - could be bigger.
                         // See https://github.com/ziglang/zig/issues/645
-                        var int_buffer: Value.Payload.U64 = undefined;
-                        const field_int_val = value.enumToInt(ty, &int_buffer);
+                        const field_int_val = try value.enumToInt(ty, mod);
                         break :value @bitCast(u64, field_int_val.toSignedInt(mod));
                     } else @intCast(u64, field_i);
                     mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian);
src/Air.zig
@@ -913,6 +913,7 @@ pub const Inst = struct {
         zero_u8 = @enumToInt(InternPool.Index.zero_u8),
         one = @enumToInt(InternPool.Index.one),
         one_usize = @enumToInt(InternPool.Index.one_usize),
+        negative_one = @enumToInt(InternPool.Index.negative_one),
         calling_convention_c = @enumToInt(InternPool.Index.calling_convention_c),
         calling_convention_inline = @enumToInt(InternPool.Index.calling_convention_inline),
         void_value = @enumToInt(InternPool.Index.void_value),
src/codegen.zig
@@ -214,15 +214,15 @@ pub fn generateSymbol(
         },
         .Float => {
             switch (typed_value.ty.floatBits(target)) {
-                16 => writeFloat(f16, typed_value.val.toFloat(f16), target, endian, try code.addManyAsArray(2)),
-                32 => writeFloat(f32, typed_value.val.toFloat(f32), target, endian, try code.addManyAsArray(4)),
-                64 => writeFloat(f64, typed_value.val.toFloat(f64), target, endian, try code.addManyAsArray(8)),
+                16 => writeFloat(f16, typed_value.val.toFloat(f16, mod), target, endian, try code.addManyAsArray(2)),
+                32 => writeFloat(f32, typed_value.val.toFloat(f32, mod), target, endian, try code.addManyAsArray(4)),
+                64 => writeFloat(f64, typed_value.val.toFloat(f64, mod), target, endian, try code.addManyAsArray(8)),
                 80 => {
-                    writeFloat(f80, typed_value.val.toFloat(f80), target, endian, try code.addManyAsArray(10));
+                    writeFloat(f80, typed_value.val.toFloat(f80, mod), target, endian, try code.addManyAsArray(10));
                     const abi_size = math.cast(usize, typed_value.ty.abiSize(mod)) orelse return error.Overflow;
                     try code.appendNTimes(0, abi_size - 10);
                 },
-                128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)),
+                128 => writeFloat(f128, typed_value.val.toFloat(f128, mod), target, endian, try code.addManyAsArray(16)),
                 else => unreachable,
             }
             return Result.ok;
@@ -328,20 +328,6 @@ pub fn generateSymbol(
                 return Result.ok;
             },
             .none => switch (typed_value.val.tag()) {
-                .zero, .one, .int_u64, .int_big_positive => {
-                    switch (target.ptrBitWidth()) {
-                        32 => {
-                            const x = typed_value.val.toUnsignedInt(mod);
-                            mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, x), endian);
-                        },
-                        64 => {
-                            const x = typed_value.val.toUnsignedInt(mod);
-                            mem.writeInt(u64, try code.addManyAsArray(8), x, endian);
-                        },
-                        else => unreachable,
-                    }
-                    return Result.ok;
-                },
                 .variable, .decl_ref, .decl_ref_mut => |tag| return lowerDeclRef(
                     bin_file,
                     src_loc,
@@ -399,7 +385,23 @@ pub fn generateSymbol(
                     ),
                 },
             },
-            else => unreachable,
+            else => switch (mod.intern_pool.indexToKey(typed_value.val.ip_index)) {
+                .int => {
+                    switch (target.ptrBitWidth()) {
+                        32 => {
+                            const x = typed_value.val.toUnsignedInt(mod);
+                            mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, x), endian);
+                        },
+                        64 => {
+                            const x = typed_value.val.toUnsignedInt(mod);
+                            mem.writeInt(u64, try code.addManyAsArray(8), x, endian);
+                        },
+                        else => unreachable,
+                    }
+                    return Result.ok;
+                },
+                else => unreachable,
+            },
         },
         .Int => {
             const info = typed_value.ty.intInfo(mod);
@@ -449,8 +451,7 @@ pub fn generateSymbol(
             return Result.ok;
         },
         .Enum => {
-            var int_buffer: Value.Payload.U64 = undefined;
-            const int_val = typed_value.enumToInt(&int_buffer);
+            const int_val = try typed_value.enumToInt(mod);
 
             const info = typed_value.ty.intInfo(mod);
             if (info.bits <= 8) {
@@ -674,7 +675,7 @@ pub fn generateSymbol(
             const is_payload = typed_value.val.errorUnionIsPayload();
 
             if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
-                const err_val = if (is_payload) Value.initTag(.zero) else typed_value.val;
+                const err_val = if (is_payload) Value.zero else typed_value.val;
                 return generateSymbol(bin_file, src_loc, .{
                     .ty = error_ty,
                     .val = err_val,
@@ -689,7 +690,7 @@ pub fn generateSymbol(
             if (error_align > payload_align) {
                 switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = error_ty,
-                    .val = if (is_payload) Value.initTag(.zero) else typed_value.val,
+                    .val = if (is_payload) Value.zero else typed_value.val,
                 }, code, debug_output, reloc_info)) {
                     .ok => {},
                     .fail => |em| return Result{ .fail = em },
@@ -721,7 +722,7 @@ pub fn generateSymbol(
                 const begin = code.items.len;
                 switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = error_ty,
-                    .val = if (is_payload) Value.initTag(.zero) else typed_value.val,
+                    .val = if (is_payload) Value.zero else typed_value.val,
                 }, code, debug_output, reloc_info)) {
                     .ok => {},
                     .fail => |em| return Result{ .fail = em },
@@ -961,13 +962,9 @@ fn lowerDeclRef(
         }
 
         // generate length
-        var slice_len: Value.Payload.U64 = .{
-            .base = .{ .tag = .int_u64 },
-            .data = typed_value.val.sliceLen(mod),
-        };
         switch (try generateSymbol(bin_file, src_loc, .{
             .ty = Type.usize,
-            .val = Value.initPayload(&slice_len.base),
+            .val = try mod.intValue(Type.usize, typed_value.val.sliceLen(mod)),
         }, code, debug_output, reloc_info)) {
             .ok => {},
             .fail => |em| return Result{ .fail = em },
@@ -1196,13 +1193,13 @@ pub fn genTypedValue(
                 .null_value => {
                     return GenResult.mcv(.{ .immediate = 0 });
                 },
-                .none => switch (typed_value.val.tag()) {
-                    .int_u64 => {
+                .none => {},
+                else => switch (mod.intern_pool.indexToKey(typed_value.val.ip_index)) {
+                    .int => {
                         return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(mod) });
                     },
                     else => {},
                 },
-                else => {},
             },
         },
         .Int => {
@@ -1283,7 +1280,7 @@ pub fn genTypedValue(
 
             if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) {
                 // We use the error type directly as the type.
-                const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
+                const err_val = if (!is_pl) typed_value.val else Value.zero;
                 return genTypedValue(bin_file, src_loc, .{
                     .ty = error_type,
                     .val = err_val,
src/InternPool.zig
@@ -390,6 +390,8 @@ pub const Index = enum(u32) {
     one,
     /// `1` (usize)
     one_usize,
+    /// `-1` (comptime_int)
+    negative_one,
     /// `std.builtin.CallingConvention.C`
     calling_convention_c,
     /// `std.builtin.CallingConvention.Inline`
@@ -624,6 +626,11 @@ pub const static_keys = [_]Key{
         .storage = .{ .u64 = 1 },
     } },
 
+    .{ .int = .{
+        .ty = .comptime_int_type,
+        .storage = .{ .i64 = -1 },
+    } },
+
     .{ .enum_tag = .{
         .ty = .calling_convention_type,
         .tag = .{
@@ -999,23 +1006,23 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
         .type_error_union => @panic("TODO"),
         .type_enum_simple => @panic("TODO"),
         .simple_internal => @panic("TODO"),
-        .int_u32 => return .{ .int = .{
+        .int_u32 => .{ .int = .{
             .ty = .u32_type,
             .storage = .{ .u64 = data },
         } },
-        .int_i32 => return .{ .int = .{
+        .int_i32 => .{ .int = .{
             .ty = .i32_type,
             .storage = .{ .i64 = @bitCast(i32, data) },
         } },
-        .int_usize => return .{ .int = .{
+        .int_usize => .{ .int = .{
             .ty = .usize_type,
             .storage = .{ .u64 = data },
         } },
-        .int_comptime_int_u32 => return .{ .int = .{
+        .int_comptime_int_u32 => .{ .int = .{
             .ty = .comptime_int_type,
             .storage = .{ .u64 = data },
         } },
-        .int_comptime_int_i32 => return .{ .int = .{
+        .int_comptime_int_i32 => .{ .int = .{
             .ty = .comptime_int_type,
             .storage = .{ .i64 = @bitCast(i32, data) },
         } },
@@ -1137,6 +1144,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
 
         .int => |int| b: {
             switch (int.ty) {
+                .none => unreachable,
                 .u32_type => switch (int.storage) {
                     .big_int => |big_int| {
                         if (big_int.to(u32)) |casted| {
src/Module.zig
@@ -6597,7 +6597,7 @@ pub fn populateTestFunctions(
             field_vals.* = .{
                 try Value.Tag.slice.create(arena, .{
                     .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl_index),
-                    .len = try Value.Tag.int_u64.create(arena, test_name_slice.len),
+                    .len = try mod.intValue(Type.usize, test_name_slice.len),
                 }), // name
                 try Value.Tag.decl_ref.create(arena, test_decl_index), // func
                 Value.null, // async_frame_size
@@ -6628,7 +6628,7 @@ pub fn populateTestFunctions(
             new_var.* = decl.val.castTag(.variable).?.data.*;
             new_var.init = try Value.Tag.slice.create(arena, .{
                 .ptr = try Value.Tag.decl_ref.create(arena, array_decl_index),
-                .len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()),
+                .len = try mod.intValue(Type.usize, mod.test_functions.count()),
             });
             const new_val = try Value.Tag.variable.create(arena, new_var);
 
@@ -6875,6 +6875,38 @@ pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
     return ptrType(mod, .{ .elem_type = child_type.ip_index, .is_const = true });
 }
 
+pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
+    if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
+    if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
+    var limbs_buffer: [4]usize = undefined;
+    var big_int = BigIntMutable.init(&limbs_buffer, x);
+    return intValue_big(mod, ty, big_int.toConst());
+}
+
+pub fn intValue_big(mod: *Module, ty: Type, x: BigIntConst) Allocator.Error!Value {
+    const i = try intern(mod, .{ .int = .{
+        .ty = ty.ip_index,
+        .storage = .{ .big_int = x },
+    } });
+    return i.toValue();
+}
+
+pub fn intValue_u64(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
+    const i = try intern(mod, .{ .int = .{
+        .ty = ty.ip_index,
+        .storage = .{ .u64 = x },
+    } });
+    return i.toValue();
+}
+
+pub fn intValue_i64(mod: *Module, ty: Type, x: i64) Allocator.Error!Value {
+    const i = try intern(mod, .{ .int = .{
+        .ty = ty.ip_index,
+        .storage = .{ .i64 = x },
+    } });
+    return i.toValue();
+}
+
 pub fn smallestUnsignedInt(mod: *Module, max: u64) Allocator.Error!Type {
     return intType(mod, .unsigned, Type.smallestUnsignedBits(max));
 }
@@ -6907,32 +6939,27 @@ pub fn intFittingRange(mod: *Module, min: Value, max: Value) !Type {
 /// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true.
 pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
     assert(!val.isUndef());
-    switch (val.tag()) {
-        .int_big_positive => {
-            const limbs = val.castTag(.int_big_positive).?.data;
-            const big: std.math.big.int.Const = .{ .limbs = limbs, .positive = true };
-            return @intCast(u16, big.bitCountAbs() + @boolToInt(sign));
-        },
-        .int_big_negative => {
-            const limbs = val.castTag(.int_big_negative).?.data;
-            // Zero is still a possibility, in which case unsigned is fine
-            for (limbs) |limb| {
-                if (limb != 0) break;
-            } else return 0; // val == 0
-            assert(sign);
-            const big: std.math.big.int.Const = .{ .limbs = limbs, .positive = false };
-            return @intCast(u16, big.bitCountTwosComp());
-        },
-        .int_i64 => {
-            const x = val.castTag(.int_i64).?.data;
-            if (x >= 0) return Type.smallestUnsignedBits(@intCast(u64, x));
+
+    const key = mod.intern_pool.indexToKey(val.ip_index);
+    switch (key.int.storage) {
+        .i64 => |x| {
+            if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted);
             assert(sign);
+            // Protect against overflow in the following negation.
+            if (x == std.math.minInt(i64)) return 64;
             return Type.smallestUnsignedBits(@intCast(u64, -x - 1)) + 1;
         },
-        else => {
-            const x = val.toUnsignedInt(mod);
+        .u64 => |x| {
             return Type.smallestUnsignedBits(x) + @boolToInt(sign);
         },
+        .big_int => |big| {
+            if (big.positive) return @intCast(u16, big.bitCountAbs() + @boolToInt(sign));
+
+            // Zero is still a possibility, in which case unsigned is fine
+            if (big.eqZero()) return 0;
+
+            return @intCast(u16, big.bitCountTwosComp());
+        },
     }
 }
 
src/RangeSet.zig
@@ -35,8 +35,8 @@ pub fn add(
     src: SwitchProngSrc,
 ) !?SwitchProngSrc {
     for (self.ranges.items) |range| {
-        if (last.compareAll(.gte, range.first, ty, self.module) and
-            first.compareAll(.lte, range.last, ty, self.module))
+        if (last.compareScalar(.gte, range.first, ty, self.module) and
+            first.compareScalar(.lte, range.last, ty, self.module))
         {
             return range.src; // They overlap.
         }
@@ -53,7 +53,7 @@ const LessThanContext = struct { ty: Type, module: *Module };
 
 /// Assumes a and b do not overlap
 fn lessThan(ctx: LessThanContext, a: Range, b: Range) bool {
-    return a.first.compareAll(.lt, b.first, ctx.ty, ctx.module);
+    return a.first.compareScalar(.lt, b.first, ctx.ty, ctx.module);
 }
 
 pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool {
src/Sema.zig
@@ -2995,7 +2995,6 @@ fn zirEnumDecl(
     var cur_bit_bag: u32 = undefined;
     var field_i: u32 = 0;
     var last_tag_val: ?Value = null;
-    var tag_val_buf: Value.Payload.U64 = undefined;
     while (field_i < fields_len) : (field_i += 1) {
         if (field_i % 32 == 0) {
             cur_bit_bag = sema.code.extra[bit_bag_index];
@@ -3084,11 +3083,7 @@ fn zirEnumDecl(
                 return sema.failWithOwnedErrorMsg(msg);
             }
         } else {
-            tag_val_buf = .{
-                .base = .{ .tag = .int_u64 },
-                .data = field_i,
-            };
-            last_tag_val = Value.initPayload(&tag_val_buf.base);
+            last_tag_val = try mod.intValue(enum_obj.tag_ty, field_i);
         }
 
         if (!(try sema.intFitsInType(last_tag_val.?, enum_obj.tag_ty, null))) {
@@ -5180,16 +5175,23 @@ fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     const tracy = trace(@src());
     defer tracy.end();
 
-    const arena = sema.arena;
+    const mod = sema.mod;
     const int = sema.code.instructions.items(.data)[inst].str;
     const byte_count = int.len * @sizeOf(std.math.big.Limb);
     const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count];
-    const limbs = try arena.alloc(std.math.big.Limb, int.len);
+
+    // TODO: this allocation and copy is only needed because the limbs may be unaligned.
+    // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these
+    // two lines can be removed.
+    const limbs = try sema.arena.alloc(std.math.big.Limb, int.len);
     @memcpy(mem.sliceAsBytes(limbs), limb_bytes);
 
     return sema.addConstant(
         Type.comptime_int,
-        try Value.Tag.int_big_positive.create(arena, limbs),
+        try mod.intValue_big(Type.comptime_int, .{
+            .limbs = limbs,
+            .positive = true,
+        }),
     );
 }
 
@@ -8095,6 +8097,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
     const tracy = trace(@src());
     defer tracy.end();
 
+    const mod = sema.mod;
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
     const src = LazySrcLoc.nodeOffset(extra.node);
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
@@ -8107,12 +8110,13 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
         }
         switch (val.tag()) {
             .@"error" => {
-                const payload = try sema.arena.create(Value.Payload.U64);
-                payload.* = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
-                };
-                return sema.addConstant(Type.err_int, Value.initPayload(&payload.base));
+                return sema.addConstant(
+                    Type.err_int,
+                    try mod.intValue(
+                        Type.err_int,
+                        (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
+                    ),
+                );
             },
 
             // This is not a valid combination with the type `anyerror`.
@@ -8280,8 +8284,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     }
 
     if (try sema.resolveMaybeUndefVal(enum_tag)) |enum_tag_val| {
-        var buffer: Value.Payload.U64 = undefined;
-        const val = enum_tag_val.enumToInt(enum_tag_ty, &buffer);
+        const val = try enum_tag_val.enumToInt(enum_tag_ty, mod);
         return sema.addConstant(int_tag_ty, try val.copy(sema.arena));
     }
 
@@ -9685,7 +9688,7 @@ fn intCast(
         // range shrinkage
         // requirement: int value fits into target type
         if (wanted_value_bits < actual_value_bits) {
-            const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, mod);
+            const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod);
             const dest_max_val = if (is_vector)
                 try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar)
             else
@@ -9946,7 +9949,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     }
 
     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
-        return sema.addConstant(dest_ty, try operand_val.floatCast(sema.arena, dest_ty, target));
+        return sema.addConstant(dest_ty, try operand_val.floatCast(sema.arena, dest_ty, mod));
     }
     if (dest_is_comptime_float) {
         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
@@ -10470,7 +10473,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     // Duplicate checking variables later also used for `inline else`.
     var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
     var seen_errors = SwitchErrorSet.init(gpa);
-    var range_set = RangeSet.init(gpa, sema.mod);
+    var range_set = RangeSet.init(gpa, mod);
     var true_count: u8 = 0;
     var false_count: u8 = 0;
 
@@ -10596,11 +10599,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                             .{field_name},
                         );
                     }
-                    try sema.mod.errNoteNonLazy(
-                        operand_ty.declSrcLoc(sema.mod),
+                    try mod.errNoteNonLazy(
+                        operand_ty.declSrcLoc(mod),
                         msg,
                         "enum '{}' declared here",
-                        .{operand_ty.fmt(sema.mod)},
+                        .{operand_ty.fmt(mod)},
                     );
                     break :msg msg;
                 };
@@ -10827,7 +10830,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     defer arena.deinit();
 
                     const min_int = try operand_ty.minInt(arena.allocator(), mod);
-                    const max_int = try operand_ty.maxInt(arena.allocator(), mod);
+                    const max_int = try operand_ty.maxIntScalar(mod);
                     if (try range_set.spans(min_int, max_int, operand_ty)) {
                         if (special_prong == .@"else") {
                             return sema.fail(
@@ -10926,13 +10929,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     block,
                     src,
                     "else prong required when switching on type '{}'",
-                    .{operand_ty.fmt(sema.mod)},
+                    .{operand_ty.fmt(mod)},
                 );
             }
 
             var seen_values = ValueSrcMap.initContext(gpa, .{
                 .ty = operand_ty,
-                .mod = sema.mod,
+                .mod = mod,
             });
             defer seen_values.deinit();
 
@@ -10996,7 +10999,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         .ComptimeFloat,
         .Float,
         => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{
-            operand_ty.fmt(sema.mod),
+            operand_ty.fmt(mod),
         }),
     }
 
@@ -11054,7 +11057,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 const item = try sema.resolveInst(item_ref);
                 // Validation above ensured these will succeed.
                 const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
-                if (operand_val.eql(item_val, operand_ty, sema.mod)) {
+                if (operand_val.eql(item_val, operand_ty, mod)) {
                     if (is_inline) child_block.inline_case_capture = operand;
 
                     if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
@@ -11080,7 +11083,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     const item = try sema.resolveInst(item_ref);
                     // Validation above ensured these will succeed.
                     const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
-                    if (operand_val.eql(item_val, operand_ty, sema.mod)) {
+                    if (operand_val.eql(item_val, operand_ty, mod)) {
                         if (is_inline) child_block.inline_case_capture = operand;
 
                         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
@@ -11128,7 +11131,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
             return Air.Inst.Ref.unreachable_value;
         }
-        if (sema.mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and
+        if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and
             (!operand_ty.isNonexhaustiveEnum() or union_originally))
         {
             try sema.zirDbgStmt(block, cond_dbg_node_index);
@@ -11182,7 +11185,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
         const analyze_body = if (union_originally) blk: {
             const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
-            const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+            const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
             break :blk field_ty.zigTypeTag(mod) != .NoReturn;
         } else true;
 
@@ -11245,9 +11248,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 const item_last_ref = try sema.resolveInst(last_ref);
                 const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
 
-                while (item.compareAll(.lte, item_last, operand_ty, sema.mod)) : ({
+                while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({
                     // Previous validation has resolved any possible lazy values.
-                    item = try sema.intAddScalar(item, Value.one);
+                    item = try sema.intAddScalar(item, Value.one, operand_ty);
                 }) {
                     cases_len += 1;
 
@@ -11260,7 +11263,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
                         error.NeededSourceLocation => {
                             const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } };
-                            const decl = sema.mod.declPtr(case_block.src_decl);
+                            const decl = mod.declPtr(case_block.src_decl);
                             try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
                             unreachable;
                         },
@@ -11289,14 +11292,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
                 const analyze_body = if (union_originally) blk: {
                     const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
-                    const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+                    const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
                     break :blk field_ty.zigTypeTag(mod) != .NoReturn;
                 } else true;
 
                 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
                     error.NeededSourceLocation => {
                         const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } };
-                        const decl = sema.mod.declPtr(case_block.src_decl);
+                        const decl = mod.declPtr(case_block.src_decl);
                         try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
                         unreachable;
                     },
@@ -11333,7 +11336,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 for (items) |item_ref| {
                     const item = try sema.resolveInst(item_ref);
                     const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
-                    const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+                    const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
                     if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
                 } else false
             else
@@ -11461,7 +11464,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
             .Enum => {
                 if (operand_ty.isNonexhaustiveEnum() and !union_originally) {
                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
-                        operand_ty.fmt(sema.mod),
+                        operand_ty.fmt(mod),
                     });
                 }
                 for (seen_enum_fields, 0..) |f, i| {
@@ -11476,7 +11479,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     case_block.wip_capture_scope = child_block.wip_capture_scope;
 
                     const analyze_body = if (union_originally) blk: {
-                        const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+                        const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
                         break :blk field_ty.zigTypeTag(mod) != .NoReturn;
                     } else true;
 
@@ -11499,7 +11502,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
             .ErrorSet => {
                 if (operand_ty.isAnyError()) {
                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
-                        operand_ty.fmt(sema.mod),
+                        operand_ty.fmt(mod),
                     });
                 }
                 for (operand_ty.errorSetNames()) |error_name| {
@@ -11587,7 +11590,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 }
             },
             else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
-                operand_ty.fmt(sema.mod),
+                operand_ty.fmt(mod),
             }),
         };
 
@@ -11598,7 +11601,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         case_block.wip_capture_scope = wip_captures.scope;
         case_block.inline_case_capture = .none;
 
-        if (sema.mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
+        if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
             operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally))
         {
             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
@@ -11679,7 +11682,7 @@ const RangeSetUnhandledIterator = struct {
     fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
         const mod = sema.mod;
         const min = try ty.minInt(sema.arena, mod);
-        const max = try ty.maxInt(sema.arena, mod);
+        const max = try ty.maxIntScalar(mod);
 
         return RangeSetUnhandledIterator{
             .sema = sema,
@@ -11693,19 +11696,19 @@ const RangeSetUnhandledIterator = struct {
     fn next(it: *RangeSetUnhandledIterator) !?Value {
         while (it.range_i < it.ranges.len) : (it.range_i += 1) {
             if (!it.first) {
-                it.cur = try it.sema.intAdd(it.cur, Value.one, it.ty);
+                it.cur = try it.sema.intAddScalar(it.cur, Value.one, it.ty);
             }
             it.first = false;
-            if (it.cur.compareAll(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
+            if (it.cur.compareScalar(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
                 return it.cur;
             }
             it.cur = it.ranges[it.range_i].last;
         }
         if (!it.first) {
-            it.cur = try it.sema.intAdd(it.cur, Value.one, it.ty);
+            it.cur = try it.sema.intAddScalar(it.cur, Value.one, it.ty);
         }
         it.first = false;
-        if (it.cur.compareAll(.lte, it.max, it.ty, it.sema.mod)) {
+        if (it.cur.compareScalar(.lte, it.max, it.ty, it.sema.mod)) {
             return it.cur;
         }
         return null;
@@ -11750,7 +11753,7 @@ fn validateSwitchRange(
 ) CompileError!void {
     const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
     const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
-    if (first_val.compareAll(.gt, last_val, operand_ty, sema.mod)) {
+    if (first_val.compareScalar(.gt, last_val, operand_ty, sema.mod)) {
         const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first);
         return sema.fail(block, src, "range start value is greater than the end value", .{});
     }
@@ -12208,16 +12211,11 @@ fn zirShl(
             return lhs;
         }
         if (scalar_ty.zigTypeTag(mod) != .ComptimeInt and air_tag != .shl_sat) {
-            var bits_payload = Value.Payload.U64{
-                .base = .{ .tag = .int_u64 },
-                .data = scalar_ty.intInfo(mod).bits,
-            };
-            const bit_value = Value.initPayload(&bits_payload.base);
+            const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits);
             if (rhs_ty.zigTypeTag(mod) == .Vector) {
                 var i: usize = 0;
                 while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
-                    var elem_value_buf: Value.ElemValueBuffer = undefined;
-                    const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf);
+                    const rhs_elem = try rhs_val.elemValue(sema.mod, i);
                     if (rhs_elem.compareHetero(.gte, bit_value, mod)) {
                         return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
                             rhs_elem.fmtValue(scalar_ty, sema.mod),
@@ -12236,8 +12234,7 @@ fn zirShl(
         if (rhs_ty.zigTypeTag(mod) == .Vector) {
             var i: usize = 0;
             while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
-                var elem_value_buf: Value.ElemValueBuffer = undefined;
-                const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf);
+                const rhs_elem = try rhs_val.elemValue(sema.mod, i);
                 if (rhs_elem.compareHetero(.lt, Value.zero, mod)) {
                     return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{
                         rhs_elem.fmtValue(scalar_ty, sema.mod),
@@ -12309,7 +12306,7 @@ fn zirShl(
     if (block.wantSafety()) {
         const bit_count = scalar_ty.intInfo(mod).bits;
         if (!std.math.isPowerOfTwo(bit_count)) {
-            const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
+            const bit_count_val = try mod.intValue(scalar_ty, bit_count);
 
             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
                 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
@@ -12396,16 +12393,11 @@ fn zirShr(
             return lhs;
         }
         if (scalar_ty.zigTypeTag(mod) != .ComptimeInt) {
-            var bits_payload = Value.Payload.U64{
-                .base = .{ .tag = .int_u64 },
-                .data = scalar_ty.intInfo(mod).bits,
-            };
-            const bit_value = Value.initPayload(&bits_payload.base);
+            const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits);
             if (rhs_ty.zigTypeTag(mod) == .Vector) {
                 var i: usize = 0;
                 while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
-                    var elem_value_buf: Value.ElemValueBuffer = undefined;
-                    const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf);
+                    const rhs_elem = try rhs_val.elemValue(sema.mod, i);
                     if (rhs_elem.compareHetero(.gte, bit_value, mod)) {
                         return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
                             rhs_elem.fmtValue(scalar_ty, sema.mod),
@@ -12424,8 +12416,7 @@ fn zirShr(
         if (rhs_ty.zigTypeTag(mod) == .Vector) {
             var i: usize = 0;
             while (i < rhs_ty.vectorLen(mod)) : (i += 1) {
-                var elem_value_buf: Value.ElemValueBuffer = undefined;
-                const rhs_elem = rhs_val.elemValueBuffer(sema.mod, i, &elem_value_buf);
+                const rhs_elem = try rhs_val.elemValue(sema.mod, i);
                 if (rhs_elem.compareHetero(.lt, Value.zero, mod)) {
                     return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{
                         rhs_elem.fmtValue(scalar_ty, sema.mod),
@@ -12465,7 +12456,7 @@ fn zirShr(
     if (block.wantSafety()) {
         const bit_count = scalar_ty.intInfo(mod).bits;
         if (!std.math.isPowerOfTwo(bit_count)) {
-            const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
+            const bit_count_val = try mod.intValue(scalar_ty, bit_count);
 
             const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: {
                 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
@@ -12587,10 +12578,9 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
             return sema.addConstUndef(operand_type);
         } else if (operand_type.zigTypeTag(mod) == .Vector) {
             const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(mod));
-            var elem_val_buf: Value.ElemValueBuffer = undefined;
             const elems = try sema.arena.alloc(Value, vec_len);
             for (elems, 0..) |*elem, i| {
-                const elem_val = val.elemValueBuffer(sema.mod, i, &elem_val_buf);
+                const elem_val = try val.elemValue(sema.mod, i);
                 elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, sema.mod);
             }
             return sema.addConstant(
@@ -12695,6 +12685,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const tracy = trace(@src());
     defer tracy.end();
 
+    const mod = sema.mod;
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const lhs = try sema.resolveInst(extra.lhs);
@@ -12714,11 +12705,11 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
         if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined);
-        return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)});
+        return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)});
     };
     const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
         assert(!rhs_is_tuple);
-        return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(sema.mod)});
+        return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(mod)});
     };
 
     const resolved_elem_ty = t: {
@@ -12780,8 +12771,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         ),
     };
 
-    const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod);
-    const mod = sema.mod;
+    const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, mod);
     const ptr_addrspace = p: {
         if (lhs_ty.zigTypeTag(mod) == .Pointer) break :p lhs_ty.ptrAddressSpace(mod);
         if (rhs_ty.zigTypeTag(mod) == .Pointer) break :p rhs_ty.ptrAddressSpace(mod);
@@ -12815,7 +12805,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const lhs_elem_i = elem_i;
                 const elem_ty = if (lhs_is_tuple) lhs_ty.structFieldType(lhs_elem_i) else lhs_info.elem_type;
                 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i) else Value.@"unreachable";
-                const elem_val = if (elem_default_val.ip_index == .unreachable_value) try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_elem_i) else elem_default_val;
+                const elem_val = if (elem_default_val.ip_index == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val;
                 const elem_val_inst = try sema.addConstant(elem_ty, elem_val);
                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
                 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
@@ -12825,7 +12815,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const rhs_elem_i = elem_i - lhs_len;
                 const elem_ty = if (rhs_is_tuple) rhs_ty.structFieldType(rhs_elem_i) else rhs_info.elem_type;
                 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i) else Value.@"unreachable";
-                const elem_val = if (elem_default_val.ip_index == .unreachable_value) try rhs_sub_val.elemValue(sema.mod, sema.arena, rhs_elem_i) else elem_default_val;
+                const elem_val = if (elem_default_val.ip_index == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val;
                 const elem_val_inst = try sema.addConstant(elem_ty, elem_val);
                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
                 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
@@ -12842,12 +12832,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (ptr_addrspace) |ptr_as| {
-        const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+        const alloc_ty = try Type.ptr(sema.arena, mod, .{
             .pointee_type = result_ty,
             .@"addrspace" = ptr_as,
         });
         const alloc = try block.addTy(.alloc, alloc_ty);
-        const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+        const elem_ptr_ty = try Type.ptr(sema.arena, mod, .{
             .pointee_type = resolved_elem_ty,
             .@"addrspace" = ptr_as,
         });
@@ -13009,6 +12999,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const tracy = trace(@src());
     defer tracy.end();
 
+    const mod = sema.mod;
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const lhs = try sema.resolveInst(extra.lhs);
@@ -13025,10 +13016,9 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     }
 
     // Analyze the lhs first, to catch the case that someone tried to do exponentiation
-    const mod = sema.mod;
     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse {
         const msg = msg: {
-            const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)});
+            const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)});
             errdefer msg.destroy(sema.gpa);
             switch (lhs_ty.zigTypeTag(mod)) {
                 .Int, .Float, .ComptimeFloat, .ComptimeInt, .Vector => {
@@ -13048,7 +13038,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         return sema.fail(block, rhs_src, "operation results in overflow", .{});
     const result_len = try sema.usizeCast(block, src, result_len_u64);
 
-    const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, sema.mod);
+    const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, mod);
 
     const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null;
     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
@@ -13065,7 +13055,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             // Optimization for the common pattern of a single element repeated N times, such
             // as zero-filling a byte array.
             if (lhs_len == 1) {
-                const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0);
+                const elem_val = try lhs_sub_val.elemValue(mod, 0);
                 break :v try Value.Tag.repeated.create(sema.arena, elem_val);
             }
 
@@ -13074,7 +13064,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             while (elem_i < result_len) {
                 var lhs_i: usize = 0;
                 while (lhs_i < lhs_len) : (lhs_i += 1) {
-                    const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_i);
+                    const elem_val = try lhs_sub_val.elemValue(mod, lhs_i);
                     element_vals[elem_i] = elem_val;
                     elem_i += 1;
                 }
@@ -13090,12 +13080,12 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     try sema.requireRuntimeBlock(block, src, lhs_src);
 
     if (ptr_addrspace) |ptr_as| {
-        const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+        const alloc_ty = try Type.ptr(sema.arena, mod, .{
             .pointee_type = result_ty,
             .@"addrspace" = ptr_as,
         });
         const alloc = try block.addTy(.alloc, alloc_ty);
-        const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+        const elem_ptr_ty = try Type.ptr(sema.arena, mod, .{
             .pointee_type = lhs_info.elem_type,
             .@"addrspace" = ptr_as,
         });
@@ -13797,7 +13787,7 @@ fn addDivIntOverflowSafety(
     }
 
     const min_int = try resolved_type.minInt(sema.arena, mod);
-    const neg_one_scalar = try Value.Tag.int_i64.create(sema.arena, -1);
+    const neg_one_scalar = try mod.intValue(lhs_scalar_ty, -1);
     const neg_one = if (resolved_type.zigTypeTag(mod) == .Vector)
         try Value.Tag.repeated.create(sema.arena, neg_one_scalar)
     else
@@ -13806,12 +13796,12 @@ fn addDivIntOverflowSafety(
     // If the LHS is comptime-known to be not equal to the min int,
     // no overflow is possible.
     if (maybe_lhs_val) |lhs_val| {
-        if (lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return;
+        if (try lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return;
     }
 
     // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
     if (maybe_rhs_val) |rhs_val| {
-        if (rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return;
+        if (try rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return;
     }
 
     var ok: Air.Inst.Ref = .none;
@@ -14038,23 +14028,18 @@ fn intRem(
     const mod = sema.mod;
     if (ty.zigTypeTag(mod) == .Vector) {
         const result_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
+        const scalar_ty = ty.scalarType(mod);
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
-            scalar.* = try sema.intRemScalar(lhs_elem, rhs_elem);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
+            scalar.* = try sema.intRemScalar(lhs_elem, rhs_elem, scalar_ty);
         }
         return Value.Tag.aggregate.create(sema.arena, result_data);
     }
-    return sema.intRemScalar(lhs, rhs);
+    return sema.intRemScalar(lhs, rhs, ty);
 }
 
-fn intRemScalar(
-    sema: *Sema,
-    lhs: Value,
-    rhs: Value,
-) CompileError!Value {
+fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileError!Value {
     const mod = sema.mod;
     // TODO is this a performance issue? maybe we should try the operation without
     // resorting to BigInt first.
@@ -14079,7 +14064,7 @@ fn intRemScalar(
     var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
     var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
     result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
-    return Value.fromBigInt(sema.arena, result_r.toConst());
+    return mod.intValue_big(scalar_ty, result_r.toConst());
 }
 
 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -15063,7 +15048,7 @@ fn analyzePtrArithmetic(
                         .ptr_sub => addr - elem_size * offset_int,
                         else => unreachable,
                     };
-                    const new_ptr_val = try Value.Tag.int_u64.create(sema.arena, new_addr);
+                    const new_ptr_val = try mod.intValue(new_ptr_ty, new_addr);
                     return sema.addConstant(new_ptr_ty, new_ptr_val);
                 }
                 if (air_tag == .ptr_sub) {
@@ -15826,9 +15811,9 @@ fn zirBuiltinSrc(
     // fn_name: [:0]const u8,
     field_values[1] = func_name_val;
     // line: u32
-    field_values[2] = try Value.Tag.runtime_value.create(sema.arena, try Value.Tag.int_u64.create(sema.arena, extra.line + 1));
+    field_values[2] = try Value.Tag.runtime_value.create(sema.arena, try mod.intValue(Type.u32, extra.line + 1));
     // column: u32,
-    field_values[3] = try Value.Tag.int_u64.create(sema.arena, extra.column + 1);
+    field_values[3] = try mod.intValue(Type.u32, extra.column + 1);
 
     return sema.addConstant(
         try sema.getBuiltinType("SourceLocation"),
@@ -15977,7 +15962,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 );
                 break :v try Value.Tag.slice.create(sema.arena, .{
                     .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
-                    .len = try Value.Tag.int_u64.create(sema.arena, param_vals.len),
+                    .len = try mod.intValue(Type.usize, param_vals.len),
                 });
             };
 
@@ -15994,7 +15979,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 // calling_convention: CallingConvention,
                 try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc)),
                 // alignment: comptime_int,
-                try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(mod)),
+                try mod.intValue(Type.comptime_int, ty.abiAlignment(mod)),
                 // is_generic: bool,
                 Value.makeBool(info.is_generic),
                 // is_var_args: bool,
@@ -16022,7 +16007,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 @enumToInt(info.signedness),
             );
             // bits: comptime_int,
-            field_values[1] = try Value.Tag.int_u64.create(sema.arena, info.bits);
+            field_values[1] = try mod.intValue(Type.comptime_int, info.bits);
 
             return sema.addConstant(
                 type_info_ty,
@@ -16035,7 +16020,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         .Float => {
             const field_values = try sema.arena.alloc(Value, 1);
             // bits: comptime_int,
-            field_values[0] = try Value.Tag.int_u64.create(sema.arena, ty.bitSize(mod));
+            field_values[0] = try mod.intValue(Type.comptime_int, ty.bitSize(mod));
 
             return sema.addConstant(
                 type_info_ty,
@@ -16048,7 +16033,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         .Pointer => {
             const info = ty.ptrInfo(mod);
             const alignment = if (info.@"align" != 0)
-                try Value.Tag.int_u64.create(sema.arena, info.@"align")
+                try mod.intValue(Type.comptime_int, info.@"align")
             else
                 try info.pointee_type.lazyAbiAlignment(mod, sema.arena);
 
@@ -16084,7 +16069,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             const info = ty.arrayInfo(mod);
             const field_values = try sema.arena.alloc(Value, 3);
             // len: comptime_int,
-            field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len);
+            field_values[0] = try mod.intValue(Type.comptime_int, info.len);
             // child: type,
             field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type);
             // sentinel: ?*const anyopaque,
@@ -16102,7 +16087,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             const info = ty.arrayInfo(mod);
             const field_values = try sema.arena.alloc(Value, 2);
             // len: comptime_int,
-            field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len);
+            field_values[0] = try mod.intValue(Type.comptime_int, info.len);
             // child: type,
             field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type);
 
@@ -16202,7 +16187,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const new_decl_val = try Value.Tag.decl_ref.create(sema.arena, new_decl);
                 const slice_val = try Value.Tag.slice.create(sema.arena, .{
                     .ptr = new_decl_val,
-                    .len = try Value.Tag.int_u64.create(sema.arena, vals.len),
+                    .len = try mod.intValue(Type.usize, vals.len),
                 });
                 break :v try Value.Tag.opt_payload.create(sema.arena, slice_val);
             } else Value.null;
@@ -16263,8 +16248,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 };
                 const tag_val = Value.initPayload(&tag_val_payload.base);
 
-                var buffer: Value.Payload.U64 = undefined;
-                const int_val = try tag_val.enumToInt(ty, &buffer).copy(fields_anon_decl.arena());
+                const int_val = try tag_val.enumToInt(ty, mod);
 
                 const name = enum_fields.keys()[i];
                 const name_val = v: {
@@ -16379,7 +16363,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     // type: type,
                     try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
                     // alignment: comptime_int,
-                    try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
+                    try mod.intValue(Type.comptime_int, alignment),
                 };
                 field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), union_field_fields);
             }
@@ -16398,7 +16382,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 );
                 break :v try Value.Tag.slice.create(sema.arena, .{
                     .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
-                    .len = try Value.Tag.int_u64.create(sema.arena, union_field_vals.len),
+                    .len = try mod.intValue(Type.usize, union_field_vals.len),
                 });
             };
 
@@ -16476,7 +16460,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                             );
                             break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{
                                 .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl),
-                                .len = try Value.Tag.int_u64.create(fields_anon_decl.arena(), bytes.len),
+                                .len = try mod.intValue(Type.usize, bytes.len),
                             });
                         };
 
@@ -16518,7 +16502,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         );
                         break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{
                             .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl),
-                            .len = try Value.Tag.int_u64.create(fields_anon_decl.arena(), bytes.len),
+                            .len = try mod.intValue(Type.usize, bytes.len),
                         });
                     };
 
@@ -16540,7 +16524,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         // is_comptime: bool,
                         Value.makeBool(field.is_comptime),
                         // alignment: comptime_int,
-                        try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
+                        try mod.intValue(Type.comptime_int, alignment),
                     };
                     field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields);
                 }
@@ -16561,7 +16545,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 );
                 break :v try Value.Tag.slice.create(sema.arena, .{
                     .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
-                    .len = try Value.Tag.int_u64.create(sema.arena, struct_field_vals.len),
+                    .len = try mod.intValue(Type.usize, struct_field_vals.len),
                 });
             };
 
@@ -16636,6 +16620,7 @@ fn typeInfoDecls(
     type_info_ty: Type,
     opt_namespace: ?*Module.Namespace,
 ) CompileError!Value {
+    const mod = sema.mod;
     var decls_anon_decl = try block.startAnonDecl();
     defer decls_anon_decl.deinit();
 
@@ -16646,9 +16631,9 @@ fn typeInfoDecls(
             type_info_ty.getNamespace().?,
             "Declaration",
         )).?;
-        try sema.mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
+        try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
         try sema.ensureDeclAnalyzed(declaration_ty_decl_index);
-        const declaration_ty_decl = sema.mod.declPtr(declaration_ty_decl_index);
+        const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index);
         break :t try declaration_ty_decl.val.toType().copy(decls_anon_decl.arena());
     };
     try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena));
@@ -16676,7 +16661,7 @@ fn typeInfoDecls(
     );
     return try Value.Tag.slice.create(sema.arena, .{
         .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
-        .len = try Value.Tag.int_u64.create(sema.arena, decl_vals.items.len),
+        .len = try mod.intValue(Type.usize, decl_vals.items.len),
     });
 }
 
@@ -16713,7 +16698,7 @@ fn typeInfoNamespaceDecls(
             );
             break :v try Value.Tag.slice.create(decls_anon_decl, .{
                 .ptr = try Value.Tag.decl_ref.create(decls_anon_decl, new_decl),
-                .len = try Value.Tag.int_u64.create(decls_anon_decl, bytes.len),
+                .len = try mod.intValue(Type.usize, bytes.len),
             });
         };
 
@@ -18620,10 +18605,9 @@ fn zirUnaryMath(
                 if (val.isUndef())
                     return sema.addConstUndef(result_ty);
 
-                var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 for (elems, 0..) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+                    const elem_val = try val.elemValue(sema.mod, i);
                     elem.* = try eval(elem_val, scalar_ty, sema.arena, sema.mod);
                 }
                 return sema.addConstant(
@@ -18717,7 +18701,12 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     return block.addUnOp(.tag_name, casted_operand);
 }
 
-fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirReify(
+    sema: *Sema,
+    block: *Block,
+    extended: Zir.Inst.Extended.InstData,
+    inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     const name_strategy = @intToEnum(Zir.Inst.NameStrategy, extended.small);
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
@@ -18730,7 +18719,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
     const union_val = val.cast(Value.Payload.Union).?.data;
     const target = mod.getTarget();
     const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?;
-    if (union_val.val.anyUndef(mod)) return sema.failWithUseOfUndef(block, src);
+    if (try union_val.val.anyUndef(mod)) return sema.failWithUseOfUndef(block, src);
     switch (@intToEnum(std.builtin.TypeId, tag_index)) {
         .Type => return Air.Inst.Ref.type_type,
         .Void => return Air.Inst.Ref.void_type,
@@ -18845,10 +18834,10 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
             } else if (ptr_size == .C) {
                 if (!try sema.validateExternType(elem_ty, .other)) {
                     const msg = msg: {
-                        const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
+                        const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
                         errdefer msg.destroy(sema.gpa);
 
-                        const src_decl = sema.mod.declPtr(block.src_decl);
+                        const src_decl = mod.declPtr(block.src_decl);
                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), elem_ty, .other);
 
                         try sema.addDeclaredHereNote(msg, elem_ty);
@@ -18893,7 +18882,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?;
             } else null;
 
-            const ty = try Type.array(sema.arena, len, sentinel, child_ty, sema.mod);
+            const ty = try Type.array(sema.arena, len, sentinel, child_ty, mod);
             return sema.addType(ty);
         },
         .Optional => {
@@ -18938,13 +18927,12 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
             try names.ensureUnusedCapacity(sema.arena, len);
             var i: usize = 0;
             while (i < len) : (i += 1) {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = slice_val.ptr.elemValueBuffer(mod, i, &buf);
+                const elem_val = try slice_val.ptr.elemValue(mod, i);
                 const struct_val = elem_val.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // error_set: type,
                 const name_val = struct_val[0];
-                const name_str = try name_val.toAllocatedBytes(Type.const_slice_u8, sema.arena, sema.mod);
+                const name_str = try name_val.toAllocatedBytes(Type.const_slice_u8, sema.arena, mod);
 
                 const kv = try mod.getErrorValue(name_str);
                 const gop = names.getOrPutAssumeCapacity(kv.key);
@@ -19061,7 +19049,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
 
             var field_i: usize = 0;
             while (field_i < fields_len) : (field_i += 1) {
-                const elem_val = try fields_val.elemValue(sema.mod, sema.arena, field_i);
+                const elem_val = try fields_val.elemValue(mod, field_i);
                 const field_struct_val: []const Value = elem_val.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // name: []const u8
@@ -19072,7 +19060,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 const field_name = try name_val.toAllocatedBytes(
                     Type.const_slice_u8,
                     new_decl_arena_allocator,
-                    sema.mod,
+                    mod,
                 );
 
                 if (!try sema.intFitsInType(value_val, enum_obj.tag_ty, null)) {
@@ -19183,7 +19171,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 Type.Tag.union_tagged
             else if (layout != .Auto)
                 Type.Tag.@"union"
-            else switch (block.sema.mod.optimizeMode()) {
+            else switch (mod.optimizeMode()) {
                 .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged,
                 .ReleaseFast, .ReleaseSmall => Type.Tag.@"union",
             };
@@ -19236,7 +19224,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
 
             var i: usize = 0;
             while (i < fields_len) : (i += 1) {
-                const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+                const elem_val = try fields_val.elemValue(mod, i);
                 const field_struct_val = elem_val.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // name: []const u8
@@ -19249,7 +19237,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 const field_name = try name_val.toAllocatedBytes(
                     Type.const_slice_u8,
                     new_decl_arena_allocator,
-                    sema.mod,
+                    mod,
                 );
 
                 if (enum_field_names) |set| {
@@ -19260,7 +19248,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                     const enum_has_field = names.orderedRemove(field_name);
                     if (!enum_has_field) {
                         const msg = msg: {
-                            const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) });
+                            const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(mod) });
                             errdefer msg.destroy(sema.gpa);
                             try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
                             break :msg msg;
@@ -19293,10 +19281,10 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 }
                 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
                     const msg = msg: {
-                        const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
+                        const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
                         errdefer msg.destroy(sema.gpa);
 
-                        const src_decl = sema.mod.declPtr(block.src_decl);
+                        const src_decl = mod.declPtr(block.src_decl);
                         try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), field_ty, .union_field);
 
                         try sema.addDeclaredHereNote(msg, field_ty);
@@ -19305,10 +19293,10 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                     return sema.failWithOwnedErrorMsg(msg);
                 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
                     const msg = msg: {
-                        const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
+                        const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
                         errdefer msg.destroy(sema.gpa);
 
-                        const src_decl = sema.mod.declPtr(block.src_decl);
+                        const src_decl = mod.declPtr(block.src_decl);
                         try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl), field_ty);
 
                         try sema.addDeclaredHereNote(msg, field_ty);
@@ -19386,8 +19374,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
             var noalias_bits: u32 = 0;
             var i: usize = 0;
             while (i < args_len) : (i += 1) {
-                var arg_buf: Value.ElemValueBuffer = undefined;
-                const arg = args_slice_val.ptr.elemValueBuffer(mod, i, &arg_buf);
+                const arg = try args_slice_val.ptr.elemValue(mod, i);
                 const arg_val = arg.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // is_generic: bool,
@@ -19486,7 +19473,7 @@ fn reifyStruct(
     try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
     var i: usize = 0;
     while (i < fields_len) : (i += 1) {
-        const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+        const elem_val = try fields_val.elemValue(sema.mod, i);
         const field_struct_val = elem_val.castTag(.aggregate).?.data;
         // TODO use reflection instead of magic numbers here
         // name: []const u8
@@ -19892,12 +19879,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)
             return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
 
-        const val_payload = try sema.arena.create(Value.Payload.U64);
-        val_payload.* = .{
-            .base = .{ .tag = .int_u64 },
-            .data = addr,
-        };
-        return sema.addConstant(ptr_ty, Value.initPayload(&val_payload.base));
+        return sema.addConstant(ptr_ty, try mod.intValue(ptr_ty, addr));
     }
 
     try sema.requireRuntimeBlock(block, src, operand_src);
@@ -19908,14 +19890,9 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         }
 
         if (ptr_align > 1) {
-            const val_payload = try sema.arena.create(Value.Payload.U64);
-            val_payload.* = .{
-                .base = .{ .tag = .int_u64 },
-                .data = ptr_align - 1,
-            };
             const align_minus_1 = try sema.addConstant(
                 Type.usize,
-                Value.initPayload(&val_payload.base),
+                try mod.intValue(Type.usize, ptr_align - 1),
             );
             const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
             const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
@@ -20254,10 +20231,9 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, sema.mod),
             );
         }
-        var elem_buf: Value.ElemValueBuffer = undefined;
         const elems = try sema.arena.alloc(Value, operand_ty.vectorLen(mod));
         for (elems, 0..) |*elem, i| {
-            const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+            const elem_val = try val.elemValue(sema.mod, i);
             elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, sema.mod);
         }
         return sema.addConstant(
@@ -20302,14 +20278,9 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     if (block.wantSafety() and dest_align > 1 and
         try sema.typeHasRuntimeBits(ptr_info.pointee_type))
     {
-        const val_payload = try sema.arena.create(Value.Payload.U64);
-        val_payload.* = .{
-            .base = .{ .tag = .int_u64 },
-            .data = dest_align - 1,
-        };
         const align_minus_1 = try sema.addConstant(
             Type.usize,
-            Value.initPayload(&val_payload.base),
+            try mod.intValue(Type.usize, dest_align - 1),
         );
         const actual_ptr = if (ptr_ty.isSlice(mod))
             try sema.analyzeSlicePtr(block, ptr_src, ptr, ptr_ty)
@@ -20359,13 +20330,12 @@ fn zirBitCount(
             if (try sema.resolveMaybeUndefVal(operand)) |val| {
                 if (val.isUndef()) return sema.addConstUndef(result_ty);
 
-                var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 const scalar_ty = operand_ty.scalarType(mod);
                 for (elems, 0..) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+                    const elem_val = try val.elemValue(sema.mod, i);
                     const count = comptimeOp(elem_val, scalar_ty, mod);
-                    elem.* = try Value.Tag.int_u64.create(sema.arena, count);
+                    elem.* = try mod.intValue(scalar_ty, count);
                 }
                 return sema.addConstant(
                     result_ty,
@@ -20429,10 +20399,9 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     return sema.addConstUndef(operand_ty);
 
                 const vec_len = operand_ty.vectorLen(mod);
-                var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 for (elems, 0..) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+                    const elem_val = try val.elemValue(sema.mod, i);
                     elem.* = try elem_val.byteSwap(operand_ty, mod, sema.arena);
                 }
                 return sema.addConstant(
@@ -20478,10 +20447,9 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
                     return sema.addConstUndef(operand_ty);
 
                 const vec_len = operand_ty.vectorLen(mod);
-                var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 for (elems, 0..) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+                    const elem_val = try val.elemValue(sema.mod, i);
                     elem.* = try elem_val.bitReverse(scalar_ty, mod, sema.arena);
                 }
                 return sema.addConstant(
@@ -21241,11 +21209,10 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
         if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty);
 
-        var accum: Value = try operand_val.elemValue(mod, sema.arena, 0);
-        var elem_buf: Value.ElemValueBuffer = undefined;
+        var accum: Value = try operand_val.elemValue(mod, 0);
         var i: u32 = 1;
         while (i < vec_len) : (i += 1) {
-            const elem_val = operand_val.elemValueBuffer(mod, i, &elem_buf);
+            const elem_val = try operand_val.elemValue(mod, i);
             switch (operation) {
                 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod),
                 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod),
@@ -21359,8 +21326,7 @@ fn analyzeShuffle(
 
     var i: usize = 0;
     while (i < mask_len) : (i += 1) {
-        var buf: Value.ElemValueBuffer = undefined;
-        const elem = mask.elemValueBuffer(sema.mod, i, &buf);
+        const elem = try mask.elemValue(sema.mod, i);
         if (elem.isUndef()) continue;
         const int = elem.toSignedInt(mod);
         var unsigned: u32 = undefined;
@@ -21398,8 +21364,7 @@ fn analyzeShuffle(
 
             i = 0;
             while (i < mask_len) : (i += 1) {
-                var buf: Value.ElemValueBuffer = undefined;
-                const mask_elem_val = mask.elemValueBuffer(sema.mod, i, &buf);
+                const mask_elem_val = try mask.elemValue(sema.mod, i);
                 if (mask_elem_val.isUndef()) {
                     values[i] = Value.undef;
                     continue;
@@ -21407,9 +21372,9 @@ fn analyzeShuffle(
                 const int = mask_elem_val.toSignedInt(mod);
                 const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int);
                 if (int >= 0) {
-                    values[i] = try a_val.elemValue(sema.mod, sema.arena, unsigned);
+                    values[i] = try a_val.elemValue(sema.mod, unsigned);
                 } else {
-                    values[i] = try b_val.elemValue(sema.mod, sema.arena, unsigned);
+                    values[i] = try b_val.elemValue(sema.mod, unsigned);
                 }
             }
             const res_val = try Value.Tag.aggregate.create(sema.arena, values);
@@ -21430,7 +21395,7 @@ fn analyzeShuffle(
         const expand_mask_values = try sema.arena.alloc(Value, max_len);
         i = 0;
         while (i < min_len) : (i += 1) {
-            expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i);
+            expand_mask_values[i] = try mod.intValue(Type.comptime_int, i);
         }
         while (i < max_len) : (i += 1) {
             expand_mask_values[i] = Value.negative_one;
@@ -21509,15 +21474,14 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
             if (maybe_b) |b_val| {
                 if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
 
-                var buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.gpa.alloc(Value, vec_len);
                 for (elems, 0..) |*elem, i| {
-                    const pred_elem_val = pred_val.elemValueBuffer(sema.mod, i, &buf);
+                    const pred_elem_val = try pred_val.elemValue(sema.mod, i);
                     const should_choose_a = pred_elem_val.toBool(mod);
                     if (should_choose_a) {
-                        elem.* = a_val.elemValueBuffer(sema.mod, i, &buf);
+                        elem.* = try a_val.elemValue(sema.mod, i);
                     } else {
-                        elem.* = b_val.elemValueBuffer(sema.mod, i, &buf);
+                        elem.* = try b_val.elemValue(sema.mod, i);
                     }
                 }
 
@@ -22067,12 +22031,10 @@ fn analyzeMinMax(
                 cur_minmax = try sema.addConstant(simd_op.result_ty, result_val);
                 continue;
             };
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
             const elems = try sema.arena.alloc(Value, vec_len);
             for (elems, 0..) |*elem, i| {
-                const lhs_elem_val = cur_val.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem_val = operand_val.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem_val = try cur_val.elemValue(mod, i);
+                const rhs_elem_val = try operand_val.elemValue(mod, i);
                 elem.* = opFunc(lhs_elem_val, rhs_elem_val, mod);
             }
             cur_minmax = try sema.addConstant(
@@ -22105,10 +22067,10 @@ fn analyzeMinMax(
             if (len == 0) break :blk orig_ty;
             if (elem_ty.isAnyFloat()) break :blk orig_ty; // can't refine floats
 
-            var cur_min: Value = try val.elemValue(mod, sema.arena, 0);
+            var cur_min: Value = try val.elemValue(mod, 0);
             var cur_max: Value = cur_min;
             for (1..len) |idx| {
-                const elem_val = try val.elemValue(mod, sema.arena, idx);
+                const elem_val = try val.elemValue(mod, idx);
                 if (elem_val.isUndef()) break :blk orig_ty; // can't refine undef
                 if (Value.order(elem_val, cur_min, mod).compare(.lt)) cur_min = elem_val;
                 if (Value.order(elem_val, cur_max, mod).compare(.gt)) cur_max = elem_val;
@@ -23987,7 +23949,7 @@ fn fieldVal(
             if (mem.eql(u8, field_name, "len")) {
                 return sema.addConstant(
                     Type.usize,
-                    try Value.Tag.int_u64.create(arena, inner_ty.arrayLen(mod)),
+                    try mod.intValue(Type.usize, inner_ty.arrayLen(mod)),
                 );
             } else if (mem.eql(u8, field_name, "ptr") and is_pointer_to) {
                 const ptr_info = object_ty.ptrInfo(mod);
@@ -24179,7 +24141,7 @@ fn fieldPtr(
                 defer anon_decl.deinit();
                 return sema.analyzeDeclRef(try anon_decl.finish(
                     Type.usize,
-                    try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen(mod)),
+                    try mod.intValue(Type.usize, inner_ty.arrayLen(mod)),
                     0, // default alignment
                 ));
             } else {
@@ -25352,7 +25314,7 @@ fn elemValArray(
         }
         if (maybe_index_val) |index_val| {
             const index = @intCast(usize, index_val.toUnsignedInt(mod));
-            const elem_val = try array_val.elemValue(sema.mod, sema.arena, index);
+            const elem_val = try array_val.elemValue(mod, index);
             return sema.addConstant(elem_ty, elem_val);
         }
     }
@@ -25914,7 +25876,7 @@ fn coerceExtra(
                         // we use a dummy pointer value with the required alignment.
                         const slice_val = try Value.Tag.slice.create(sema.arena, .{
                             .ptr = if (dest_info.@"align" != 0)
-                                try Value.Tag.int_u64.create(sema.arena, dest_info.@"align")
+                                try mod.intValue(Type.usize, dest_info.@"align")
                             else
                                 try dest_info.pointee_type.lazyAbiAlignment(mod, sema.arena),
                             .len = Value.zero,
@@ -26022,7 +25984,7 @@ fn coerceExtra(
         .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) {
             .ComptimeFloat => {
                 const val = try sema.resolveConstValue(block, .unneeded, inst, "");
-                const result_val = try val.floatCast(sema.arena, dest_ty, target);
+                const result_val = try val.floatCast(sema.arena, dest_ty, mod);
                 return try sema.addConstant(dest_ty, result_val);
             },
             .Float => {
@@ -26030,7 +25992,7 @@ fn coerceExtra(
                     return sema.addConstUndef(dest_ty);
                 }
                 if (try sema.resolveMaybeUndefVal(inst)) |val| {
-                    const result_val = try val.floatCast(sema.arena, dest_ty, target);
+                    const result_val = try val.floatCast(sema.arena, dest_ty, mod);
                     if (!val.eql(result_val, inst_ty, sema.mod)) {
                         return sema.fail(
                             block,
@@ -27431,11 +27393,13 @@ fn storePtrVal(
             const buffer = try sema.gpa.alloc(u8, abi_size);
             defer sema.gpa.free(buffer);
             reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer) catch |err| switch (err) {
+                error.OutOfMemory => return error.OutOfMemory,
                 error.ReinterpretDeclRef => unreachable,
                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(sema.mod)}),
             };
             operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) {
+                error.OutOfMemory => return error.OutOfMemory,
                 error.ReinterpretDeclRef => unreachable,
                 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
                 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(sema.mod)}),
@@ -27589,7 +27553,7 @@ fn beginComptimePtrMutation(
                                     assert(bytes.len >= dest_len);
                                     const elems = try arena.alloc(Value, @intCast(usize, dest_len));
                                     for (elems, 0..) |*elem, i| {
-                                        elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
+                                        elem.* = try mod.intValue(elem_ty, bytes[i]);
                                     }
 
                                     val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
@@ -27618,7 +27582,7 @@ fn beginComptimePtrMutation(
                                     const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
                                     const elems = try arena.alloc(Value, @intCast(usize, dest_len));
                                     for (bytes, 0..) |byte, i| {
-                                        elems[i] = try Value.Tag.int_u64.create(arena, byte);
+                                        elems[i] = try mod.intValue(elem_ty, byte);
                                     }
                                     if (parent.ty.sentinel(mod)) |sent_val| {
                                         assert(elems.len == bytes.len + 1);
@@ -28111,7 +28075,7 @@ fn beginComptimePtrLoad(
     maybe_array_ty: ?Type,
 ) ComptimePtrLoadError!ComptimePtrLoadKit {
     const mod = sema.mod;
-    const target = sema.mod.getTarget();
+    const target = mod.getTarget();
 
     var deref: ComptimePtrLoadKit = switch (ptr_val.ip_index) {
         .null_value => {
@@ -28128,7 +28092,7 @@ fn beginComptimePtrLoad(
                     else => unreachable,
                 };
                 const is_mutable = ptr_val.tag() == .decl_ref_mut;
-                const decl = sema.mod.declPtr(decl_index);
+                const decl = mod.declPtr(decl_index);
                 const decl_tv = try decl.typedValue();
                 if (decl_tv.val.tagIsVariable()) return error.RuntimeLoad;
 
@@ -28150,7 +28114,7 @@ fn beginComptimePtrLoad(
                 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
                 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
                 if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| {
-                    assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod)));
+                    assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, mod)));
                 }
 
                 if (elem_ptr.index != 0) {
@@ -28184,11 +28148,11 @@ fn beginComptimePtrLoad(
                 if (maybe_array_ty) |load_ty| {
                     // It's possible that we're loading a [N]T, in which case we'd like to slice
                     // the pointee array directly from our parent array.
-                    if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, sema.mod)) {
+                    if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
                         const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
                         deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{
-                            .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod),
-                            .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N),
+                            .ty = try Type.array(sema.arena, N, null, elem_ty, mod),
+                            .val = try array_tv.val.sliceArray(mod, sema.arena, elem_ptr.index, elem_ptr.index + N),
                         } else null;
                         break :blk deref;
                     }
@@ -28209,7 +28173,7 @@ fn beginComptimePtrLoad(
                 }
                 deref.pointee = TypedValue{
                     .ty = elem_ty,
-                    .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index),
+                    .val = try array_tv.val.elemValue(mod, elem_ptr.index),
                 };
                 break :blk deref;
             },
@@ -28329,12 +28293,6 @@ fn beginComptimePtrLoad(
                 break :blk try sema.beginComptimePtrLoad(block, src, opt_payload, null);
             },
 
-            .zero,
-            .one,
-            .int_u64,
-            .int_i64,
-            .int_big_positive,
-            .int_big_negative,
             .variable,
             .extern_fn,
             .function,
@@ -28342,7 +28300,10 @@ fn beginComptimePtrLoad(
 
             else => unreachable,
         },
-        else => unreachable,
+        else => switch (mod.intern_pool.indexToKey(ptr_val.ip_index)) {
+            .int => return error.RuntimeLoad,
+            else => unreachable,
+        },
     };
 
     if (deref.pointee) |tv| {
@@ -28373,9 +28334,9 @@ fn bitCast(
 
     if (old_bits != dest_bits) {
         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{
-            dest_ty.fmt(sema.mod),
+            dest_ty.fmt(mod),
             dest_bits,
-            old_ty.fmt(sema.mod),
+            old_ty.fmt(mod),
             old_bits,
         });
     }
@@ -28407,6 +28368,7 @@ fn bitCastVal(
     const buffer = try sema.gpa.alloc(u8, abi_size);
     defer sema.gpa.free(buffer);
     val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) {
+        error.OutOfMemory => return error.OutOfMemory,
         error.ReinterpretDeclRef => return null,
         error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
         error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
@@ -28427,7 +28389,7 @@ fn coerceArrayPtrToSlice(
         const array_ty = ptr_array_ty.childType(mod);
         const slice_val = try Value.Tag.slice.create(sema.arena, .{
             .ptr = val,
-            .len = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen(mod)),
+            .len = try mod.intValue(Type.usize, array_ty.arrayLen(mod)),
         });
         return sema.addConstant(dest_ty, slice_val);
     }
@@ -28781,7 +28743,7 @@ fn coerceArrayLike(
     for (element_vals, 0..) |*elem, i| {
         const index_ref = try sema.addConstant(
             Type.usize,
-            try Value.Tag.int_u64.create(sema.arena, i),
+            try mod.intValue(Type.usize, i),
         );
         const src = inst_src; // TODO better source location
         const elem_src = inst_src; // TODO better source location
@@ -29634,7 +29596,7 @@ fn analyzeSlice(
     var end_is_len = uncasted_end_opt == .none;
     const end = e: {
         if (array_ty.zigTypeTag(mod) == .Array) {
-            const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen(mod));
+            const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod));
 
             if (!end_is_len) {
                 const end = if (by_length) end: {
@@ -29643,8 +29605,8 @@ fn analyzeSlice(
                     break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
                 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
                 if (try sema.resolveMaybeUndefVal(end)) |end_val| {
-                    const len_s_val = try Value.Tag.int_u64.create(
-                        sema.arena,
+                    const len_s_val = try mod.intValue(
+                        Type.usize,
                         array_ty.arrayLenIncludingSentinel(mod),
                     );
                     if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) {
@@ -29689,12 +29651,10 @@ fn analyzeSlice(
                             return sema.fail(block, src, "slice of undefined", .{});
                         }
                         const has_sentinel = slice_ty.sentinel(mod) != null;
-                        var int_payload: Value.Payload.U64 = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = slice_val.sliceLen(mod) + @boolToInt(has_sentinel),
-                        };
-                        const slice_len_val = Value.initPayload(&int_payload.base);
-                        if (!(try sema.compareAll(end_val, .lte, slice_len_val, Type.usize))) {
+                        const slice_len = slice_val.sliceLen(mod);
+                        const len_plus_sent = slice_len + @boolToInt(has_sentinel);
+                        const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent);
+                        if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) {
                             const sentinel_label: []const u8 = if (has_sentinel)
                                 " +1 (sentinel)"
                             else
@@ -29712,13 +29672,10 @@ fn analyzeSlice(
                             );
                         }
 
-                        // If the slice has a sentinel, we subtract one so that
-                        // end_is_len is only true if it equals the length WITHOUT
-                        // the sentinel, so we don't add a sentinel type.
-                        if (has_sentinel) {
-                            int_payload.data -= 1;
-                        }
-
+                        // If the slice has a sentinel, we consider end_is_len
+                        // is only true if it equals the length WITHOUT the
+                        // sentinel, so we don't add a sentinel type.
+                        const slice_len_val = try mod.intValue(Type.usize, slice_len);
                         if (end_val.eql(slice_len_val, Type.usize, mod)) {
                             end_is_len = true;
                         }
@@ -30134,7 +30091,7 @@ fn cmpNumeric(
                 }
             }
 
-            var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128));
+            var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod));
             defer bigint.deinit();
             if (lhs_val.floatHasFraction()) {
                 if (lhs_is_signed) {
@@ -30193,7 +30150,7 @@ fn cmpNumeric(
                 }
             }
 
-            var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128));
+            var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod));
             defer bigint.deinit();
             if (rhs_val.floatHasFraction()) {
                 if (rhs_is_signed) {
@@ -31835,6 +31792,7 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type {
         .zero_u8 => unreachable,
         .one => unreachable,
         .one_usize => unreachable,
+        .negative_one => unreachable,
         .calling_convention_c => unreachable,
         .calling_convention_inline => unreachable,
         .void_value => unreachable,
@@ -32462,11 +32420,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
             }
 
             if (fields_len > 0) {
-                var field_count_val: Value.Payload.U64 = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = fields_len - 1,
-                };
-                if (!(try sema.intFitsInType(Value.initPayload(&field_count_val.base), int_tag_ty, null))) {
+                const field_count_val = try mod.intValue(int_tag_ty, fields_len - 1);
+                if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
                     const msg = msg: {
                         const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{});
                         errdefer msg.destroy(sema.gpa);
@@ -33207,7 +33162,8 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
 }
 
 fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
-    return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
+    const mod = sema.mod;
+    return sema.addConstant(ty, try mod.intValue(ty, int));
 }
 
 fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
@@ -33223,7 +33179,11 @@ pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
             .tag = .interned,
             .data = .{ .interned = val.ip_index },
         });
-        return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
+        const result = Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
+        // This assertion can be removed when the `ty` parameter is removed from
+        // this function thanks to the InternPool transition being complete.
+        assert(Type.eql(sema.typeOf(result), ty, sema.mod));
+        return result;
     }
     const ty_inst = try sema.addType(ty);
     try sema.air_values.append(gpa, val);
@@ -33833,19 +33793,18 @@ fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
     const mod = sema.mod;
     if (ty.zigTypeTag(mod) == .Vector) {
         const result_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
+        const scalar_ty = ty.scalarType(mod);
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-            scalar.* = try sema.intAddScalar(lhs_elem, rhs_elem);
+            const lhs_elem = try lhs.elemValue(mod, i);
+            const rhs_elem = try rhs.elemValue(mod, i);
+            scalar.* = try sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty);
         }
         return Value.Tag.aggregate.create(sema.arena, result_data);
     }
-    return sema.intAddScalar(lhs, rhs);
+    return sema.intAddScalar(lhs, rhs, ty);
 }
 
-fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value) !Value {
+fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
     const mod = sema.mod;
     // TODO is this a performance issue? maybe we should try the operation without
     // resorting to BigInt first.
@@ -33859,7 +33818,7 @@ fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value) !Value {
     );
     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
     result_bigint.add(lhs_bigint, rhs_bigint);
-    return Value.fromBigInt(sema.arena, result_bigint.toConst());
+    return mod.intValue_big(scalar_ty, result_bigint.toConst());
 }
 
 /// Supports both floats and ints; handles undefined.
@@ -33884,28 +33843,22 @@ fn numberAddWrapScalar(
     return overflow_result.wrapped_result;
 }
 
-fn intSub(
-    sema: *Sema,
-    lhs: Value,
-    rhs: Value,
-    ty: Type,
-) !Value {
+fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type) !Value {
     const mod = sema.mod;
     if (ty.zigTypeTag(mod) == .Vector) {
         const result_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
+        const scalar_ty = ty.scalarType(mod);
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
-            scalar.* = try sema.intSubScalar(lhs_elem, rhs_elem);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
+            scalar.* = try sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty);
         }
         return Value.Tag.aggregate.create(sema.arena, result_data);
     }
-    return sema.intSubScalar(lhs, rhs);
+    return sema.intSubScalar(lhs, rhs, ty);
 }
 
-fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value) !Value {
+fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value {
     const mod = sema.mod;
     // TODO is this a performance issue? maybe we should try the operation without
     // resorting to BigInt first.
@@ -33919,7 +33872,7 @@ fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value) !Value {
     );
     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
     result_bigint.sub(lhs_bigint, rhs_bigint);
-    return Value.fromBigInt(sema.arena, result_bigint.toConst());
+    return mod.intValue_big(scalar_ty, result_bigint.toConst());
 }
 
 /// Supports both floats and ints; handles undefined.
@@ -33954,10 +33907,8 @@ fn floatAdd(
     if (float_type.zigTypeTag(mod) == .Vector) {
         const result_data = try sema.arena.alloc(Value, float_type.vectorLen(mod));
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
             scalar.* = try sema.floatAddScalar(lhs_elem, rhs_elem, float_type.scalarType(mod));
         }
         return Value.Tag.aggregate.create(sema.arena, result_data);
@@ -33971,31 +33922,32 @@ fn floatAddScalar(
     rhs: Value,
     float_type: Type,
 ) !Value {
+    const mod = sema.mod;
     const target = sema.mod.getTarget();
     switch (float_type.floatBits(target)) {
         16 => {
-            const lhs_val = lhs.toFloat(f16);
-            const rhs_val = rhs.toFloat(f16);
+            const lhs_val = lhs.toFloat(f16, mod);
+            const rhs_val = rhs.toFloat(f16, mod);
             return Value.Tag.float_16.create(sema.arena, lhs_val + rhs_val);
         },
         32 => {
-            const lhs_val = lhs.toFloat(f32);
-            const rhs_val = rhs.toFloat(f32);
+            const lhs_val = lhs.toFloat(f32, mod);
+            const rhs_val = rhs.toFloat(f32, mod);
             return Value.Tag.float_32.create(sema.arena, lhs_val + rhs_val);
         },
         64 => {
-            const lhs_val = lhs.toFloat(f64);
-            const rhs_val = rhs.toFloat(f64);
+            const lhs_val = lhs.toFloat(f64, mod);
+            const rhs_val = rhs.toFloat(f64, mod);
             return Value.Tag.float_64.create(sema.arena, lhs_val + rhs_val);
         },
         80 => {
-            const lhs_val = lhs.toFloat(f80);
-            const rhs_val = rhs.toFloat(f80);
+            const lhs_val = lhs.toFloat(f80, mod);
+            const rhs_val = rhs.toFloat(f80, mod);
             return Value.Tag.float_80.create(sema.arena, lhs_val + rhs_val);
         },
         128 => {
-            const lhs_val = lhs.toFloat(f128);
-            const rhs_val = rhs.toFloat(f128);
+            const lhs_val = lhs.toFloat(f128, mod);
+            const rhs_val = rhs.toFloat(f128, mod);
             return Value.Tag.float_128.create(sema.arena, lhs_val + rhs_val);
         },
         else => unreachable,
@@ -34012,10 +33964,8 @@ fn floatSub(
     if (float_type.zigTypeTag(mod) == .Vector) {
         const result_data = try sema.arena.alloc(Value, float_type.vectorLen(mod));
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
             scalar.* = try sema.floatSubScalar(lhs_elem, rhs_elem, float_type.scalarType(mod));
         }
         return Value.Tag.aggregate.create(sema.arena, result_data);
@@ -34029,31 +33979,32 @@ fn floatSubScalar(
     rhs: Value,
     float_type: Type,
 ) !Value {
+    const mod = sema.mod;
     const target = sema.mod.getTarget();
     switch (float_type.floatBits(target)) {
         16 => {
-            const lhs_val = lhs.toFloat(f16);
-            const rhs_val = rhs.toFloat(f16);
+            const lhs_val = lhs.toFloat(f16, mod);
+            const rhs_val = rhs.toFloat(f16, mod);
             return Value.Tag.float_16.create(sema.arena, lhs_val - rhs_val);
         },
         32 => {
-            const lhs_val = lhs.toFloat(f32);
-            const rhs_val = rhs.toFloat(f32);
+            const lhs_val = lhs.toFloat(f32, mod);
+            const rhs_val = rhs.toFloat(f32, mod);
             return Value.Tag.float_32.create(sema.arena, lhs_val - rhs_val);
         },
         64 => {
-            const lhs_val = lhs.toFloat(f64);
-            const rhs_val = rhs.toFloat(f64);
+            const lhs_val = lhs.toFloat(f64, mod);
+            const rhs_val = rhs.toFloat(f64, mod);
             return Value.Tag.float_64.create(sema.arena, lhs_val - rhs_val);
         },
         80 => {
-            const lhs_val = lhs.toFloat(f80);
-            const rhs_val = rhs.toFloat(f80);
+            const lhs_val = lhs.toFloat(f80, mod);
+            const rhs_val = rhs.toFloat(f80, mod);
             return Value.Tag.float_80.create(sema.arena, lhs_val - rhs_val);
         },
         128 => {
-            const lhs_val = lhs.toFloat(f128);
-            const rhs_val = rhs.toFloat(f128);
+            const lhs_val = lhs.toFloat(f128, mod);
+            const rhs_val = rhs.toFloat(f128, mod);
             return Value.Tag.float_128.create(sema.arena, lhs_val - rhs_val);
         },
         else => unreachable,
@@ -34071,10 +34022,8 @@ fn intSubWithOverflow(
         const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
         const result_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
             const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, ty.scalarType(mod));
             overflowed_data[i] = of_math_result.overflow_bit;
             scalar.* = of_math_result.wrapped_result;
@@ -34106,7 +34055,7 @@ fn intSubWithOverflowScalar(
     );
     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
     const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
-    const wrapped_result = try Value.fromBigInt(sema.arena, result_bigint.toConst());
+    const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst());
     return Value.OverflowArithmeticResult{
         .overflow_bit = Value.boolToInt(overflowed),
         .wrapped_result = wrapped_result,
@@ -34126,8 +34075,7 @@ fn floatToInt(
         const elem_ty = float_ty.childType(mod);
         const result_data = try sema.arena.alloc(Value, float_ty.vectorLen(mod));
         for (result_data, 0..) |*scalar, i| {
-            var buf: Value.ElemValueBuffer = undefined;
-            const elem_val = val.elemValueBuffer(sema.mod, i, &buf);
+            const elem_val = try val.elemValue(sema.mod, i);
             scalar.* = try sema.floatToIntScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod));
         }
         return Value.Tag.aggregate.create(sema.arena, result_data);
@@ -34168,9 +34116,9 @@ fn floatToIntScalar(
     float_ty: Type,
     int_ty: Type,
 ) CompileError!Value {
-    const Limb = std.math.big.Limb;
+    const mod = sema.mod;
 
-    const float = val.toFloat(f128);
+    const float = val.toFloat(f128, mod);
     if (std.math.isNan(float)) {
         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{
             int_ty.fmt(sema.mod),
@@ -34185,11 +34133,7 @@ fn floatToIntScalar(
     var big_int = try float128IntPartToBigInt(sema.arena, float);
     defer big_int.deinit();
 
-    const result_limbs = try sema.arena.dupe(Limb, big_int.toConst().limbs);
-    const result = if (!big_int.isPositive())
-        try Value.Tag.int_big_negative.create(sema.arena, result_limbs)
-    else
-        try Value.Tag.int_big_positive.create(sema.arena, result_limbs);
+    const result = try mod.intValue_big(int_ty, big_int.toConst());
 
     if (!(try sema.intFitsInType(result, int_ty, null))) {
         return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{
@@ -34209,8 +34153,8 @@ fn intFitsInType(
     ty: Type,
     vector_index: ?*usize,
 ) CompileError!bool {
+    if (ty.ip_index == .comptime_int_type) return true;
     const mod = sema.mod;
-    const target = mod.getTarget();
     switch (val.ip_index) {
         .undef,
         .zero,
@@ -34218,103 +34162,26 @@ fn intFitsInType(
         .zero_u8,
         => return true,
 
-        .one,
-        .one_usize,
-        => switch (ty.zigTypeTag(mod)) {
-            .Int => {
-                const info = ty.intInfo(mod);
-                return switch (info.signedness) {
-                    .signed => info.bits >= 2,
-                    .unsigned => info.bits >= 1,
-                };
-            },
-            .ComptimeInt => return true,
-            else => unreachable,
-        },
-
         .none => switch (val.tag()) {
-            .zero => return true,
-
-            .one => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const info = ty.intInfo(mod);
-                    return switch (info.signedness) {
-                        .signed => info.bits >= 2,
-                        .unsigned => info.bits >= 1,
-                    };
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
-            },
-
-            .lazy_align => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const info = ty.intInfo(mod);
-                    const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed);
-                    // If it is u16 or bigger we know the alignment fits without resolving it.
-                    if (info.bits >= max_needed_bits) return true;
-                    const x = try sema.typeAbiAlignment(val.castTag(.lazy_align).?.data);
-                    if (x == 0) return true;
-                    const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
-                    return info.bits >= actual_needed_bits;
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
-            },
-            .lazy_size => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const info = ty.intInfo(mod);
-                    const max_needed_bits = @as(u16, 64) + @boolToInt(info.signedness == .signed);
-                    // If it is u64 or bigger we know the size fits without resolving it.
-                    if (info.bits >= max_needed_bits) return true;
-                    const x = try sema.typeAbiSize(val.castTag(.lazy_size).?.data);
-                    if (x == 0) return true;
-                    const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
-                    return info.bits >= actual_needed_bits;
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
-            },
-
-            .int_u64 => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const x = val.castTag(.int_u64).?.data;
-                    if (x == 0) return true;
-                    const info = ty.intInfo(mod);
-                    const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
-                    return info.bits >= needed_bits;
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
-            },
-            .int_i64 => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const x = val.castTag(.int_i64).?.data;
-                    if (x == 0) return true;
-                    const info = ty.intInfo(mod);
-                    if (info.signedness == .unsigned and x < 0)
-                        return false;
-                    var buffer: Value.BigIntSpace = undefined;
-                    return (try val.toBigIntAdvanced(&buffer, mod, sema)).fitsInTwosComp(info.signedness, info.bits);
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
-            },
-            .int_big_positive => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const info = ty.intInfo(mod);
-                    return val.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits);
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
+            .lazy_align => {
+                const info = ty.intInfo(mod);
+                const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed);
+                // If it is u16 or bigger we know the alignment fits without resolving it.
+                if (info.bits >= max_needed_bits) return true;
+                const x = try sema.typeAbiAlignment(val.castTag(.lazy_align).?.data);
+                if (x == 0) return true;
+                const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
+                return info.bits >= actual_needed_bits;
             },
-            .int_big_negative => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const info = ty.intInfo(mod);
-                    return val.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.bits);
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
+            .lazy_size => {
+                const info = ty.intInfo(mod);
+                const max_needed_bits = @as(u16, 64) + @boolToInt(info.signedness == .signed);
+                // If it is u64 or bigger we know the size fits without resolving it.
+                if (info.bits >= max_needed_bits) return true;
+                const x = try sema.typeAbiSize(val.castTag(.lazy_size).?.data);
+                if (x == 0) return true;
+                const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
+                return info.bits >= actual_needed_bits;
             },
 
             .the_only_possible_value => {
@@ -34327,17 +34194,14 @@ fn intFitsInType(
             .decl_ref,
             .function,
             .variable,
-            => switch (ty.zigTypeTag(mod)) {
-                .Int => {
-                    const info = ty.intInfo(mod);
-                    const ptr_bits = target.ptrBitWidth();
-                    return switch (info.signedness) {
-                        .signed => info.bits > ptr_bits,
-                        .unsigned => info.bits >= ptr_bits,
-                    };
-                },
-                .ComptimeInt => return true,
-                else => unreachable,
+            => {
+                const info = ty.intInfo(mod);
+                const target = mod.getTarget();
+                const ptr_bits = target.ptrBitWidth();
+                return switch (info.signedness) {
+                    .signed => info.bits > ptr_bits,
+                    .unsigned => info.bits >= ptr_bits,
+                };
             },
 
             .aggregate => {
@@ -34354,22 +34218,22 @@ fn intFitsInType(
             else => unreachable,
         },
 
-        else => @panic("TODO"),
+        else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
+            .int => |int| {
+                const info = ty.intInfo(mod);
+                var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
+                const big_int = int.storage.toBigInt(&buffer);
+                return big_int.fitsInTwosComp(info.signedness, info.bits);
+            },
+            else => unreachable,
+        },
     }
 }
 
-fn intInRange(
-    sema: *Sema,
-    tag_ty: Type,
-    int_val: Value,
-    end: usize,
-) !bool {
+fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
+    const mod = sema.mod;
     if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false;
-    var end_payload: Value.Payload.U64 = .{
-        .base = .{ .tag = .int_u64 },
-        .data = end,
-    };
-    const end_val = Value.initPayload(&end_payload.base);
+    const end_val = try mod.intValue(tag_ty, end);
     if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
     return true;
 }
@@ -34426,10 +34290,8 @@ fn intAddWithOverflow(
         const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
         const result_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
         for (result_data, 0..) |*scalar, i| {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
             const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, ty.scalarType(mod));
             overflowed_data[i] = of_math_result.overflow_bit;
             scalar.* = of_math_result.wrapped_result;
@@ -34461,7 +34323,7 @@ fn intAddWithOverflowScalar(
     );
     var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
     const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
-    const result = try Value.fromBigInt(sema.arena, result_bigint.toConst());
+    const result = try mod.intValue_big(ty, result_bigint.toConst());
     return Value.OverflowArithmeticResult{
         .overflow_bit = Value.boolToInt(overflowed),
         .wrapped_result = result,
@@ -34483,10 +34345,8 @@ fn compareAll(
     if (ty.zigTypeTag(mod) == .Vector) {
         var i: usize = 0;
         while (i < ty.vectorLen(mod)) : (i += 1) {
-            var lhs_buf: Value.ElemValueBuffer = undefined;
-            var rhs_buf: Value.ElemValueBuffer = undefined;
-            const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-            const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
+            const lhs_elem = try lhs.elemValue(sema.mod, i);
+            const rhs_elem = try rhs.elemValue(sema.mod, i);
             if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) {
                 return false;
             }
@@ -34532,10 +34392,8 @@ fn compareVector(
     assert(ty.zigTypeTag(mod) == .Vector);
     const result_data = try sema.arena.alloc(Value, ty.vectorLen(mod));
     for (result_data, 0..) |*scalar, i| {
-        var lhs_buf: Value.ElemValueBuffer = undefined;
-        var rhs_buf: Value.ElemValueBuffer = undefined;
-        const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
-        const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
+        const lhs_elem = try lhs.elemValue(sema.mod, i);
+        const rhs_elem = try rhs.elemValue(sema.mod, i);
         const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod));
         scalar.* = Value.makeBool(res_bool);
     }
src/type.zig
@@ -2077,10 +2077,10 @@ pub const Type = struct {
     }
 
     /// May capture a reference to `ty`.
-    pub fn lazyAbiAlignment(ty: Type, mod: *const Module, arena: Allocator) !Value {
+    pub fn lazyAbiAlignment(ty: Type, mod: *Module, arena: Allocator) !Value {
         switch (try ty.abiAlignmentAdvanced(mod, .{ .lazy = arena })) {
             .val => |val| return val,
-            .scalar => |x| return Value.Tag.int_u64.create(arena, x),
+            .scalar => |x| return mod.intValue(ty, x),
         }
     }
 
@@ -2468,10 +2468,10 @@ pub const Type = struct {
     }
 
     /// May capture a reference to `ty`.
-    pub fn lazyAbiSize(ty: Type, mod: *const Module, arena: Allocator) !Value {
+    pub fn lazyAbiSize(ty: Type, mod: *Module, arena: Allocator) !Value {
         switch (try ty.abiSizeAdvanced(mod, .{ .lazy = arena })) {
             .val => |val| return val,
-            .scalar => |x| return Value.Tag.int_u64.create(arena, x),
+            .scalar => |x| return mod.intValue(ty, x),
         }
     }
 
@@ -4310,8 +4310,8 @@ pub const Type = struct {
     }
 
     // Works for vectors and vectors of integers.
-    pub fn minInt(ty: Type, arena: Allocator, mod: *const Module) !Value {
-        const scalar = try minIntScalar(ty.scalarType(mod), arena, mod);
+    pub fn minInt(ty: Type, arena: Allocator, mod: *Module) !Value {
+        const scalar = try minIntScalar(ty.scalarType(mod), mod);
         if (ty.zigTypeTag(mod) == .Vector and scalar.tag() != .the_only_possible_value) {
             return Value.Tag.repeated.create(arena, scalar);
         } else {
@@ -4319,38 +4319,28 @@ pub const Type = struct {
         }
     }
 
-    /// Asserts that self.zigTypeTag(mod) == .Int.
-    pub fn minIntScalar(ty: Type, arena: Allocator, mod: *const Module) !Value {
-        assert(ty.zigTypeTag(mod) == .Int);
+    /// Asserts that the type is an integer.
+    pub fn minIntScalar(ty: Type, mod: *Module) !Value {
         const info = ty.intInfo(mod);
-
-        if (info.bits == 0) {
-            return Value.initTag(.the_only_possible_value);
-        }
-
-        if (info.signedness == .unsigned) {
-            return Value.zero;
-        }
+        if (info.signedness == .unsigned) return Value.zero;
+        if (info.bits == 0) return Value.negative_one;
 
         if (std.math.cast(u6, info.bits - 1)) |shift| {
             const n = @as(i64, std.math.minInt(i64)) >> (63 - shift);
-            return Value.Tag.int_i64.create(arena, n);
+            return mod.intValue(Type.comptime_int, n);
         }
 
-        var res = try std.math.big.int.Managed.init(arena);
+        var res = try std.math.big.int.Managed.init(mod.gpa);
+        defer res.deinit();
+
         try res.setTwosCompIntLimit(.min, info.signedness, info.bits);
 
-        const res_const = res.toConst();
-        if (res_const.positive) {
-            return Value.Tag.int_big_positive.create(arena, res_const.limbs);
-        } else {
-            return Value.Tag.int_big_negative.create(arena, res_const.limbs);
-        }
+        return mod.intValue_big(Type.comptime_int, res.toConst());
     }
 
     // Works for vectors and vectors of integers.
-    pub fn maxInt(ty: Type, arena: Allocator, mod: *const Module) !Value {
-        const scalar = try maxIntScalar(ty.scalarType(mod), arena, mod);
+    pub fn maxInt(ty: Type, arena: Allocator, mod: *Module) !Value {
+        const scalar = try maxIntScalar(ty.scalarType(mod), mod);
         if (ty.zigTypeTag(mod) == .Vector and scalar.tag() != .the_only_possible_value) {
             return Value.Tag.repeated.create(arena, scalar);
         } else {
@@ -4358,41 +4348,39 @@ pub const Type = struct {
         }
     }
 
-    /// Asserts that self.zigTypeTag() == .Int.
-    pub fn maxIntScalar(self: Type, arena: Allocator, mod: *const Module) !Value {
-        assert(self.zigTypeTag(mod) == .Int);
+    /// Asserts that the type is an integer.
+    pub fn maxIntScalar(self: Type, mod: *Module) !Value {
         const info = self.intInfo(mod);
 
-        if (info.bits == 0) {
-            return Value.initTag(.the_only_possible_value);
-        }
-
-        switch (info.bits - @boolToInt(info.signedness == .signed)) {
-            0 => return Value.zero,
-            1 => return Value.one,
+        switch (info.bits) {
+            0 => return switch (info.signedness) {
+                .signed => Value.negative_one,
+                .unsigned => Value.zero,
+            },
+            1 => return switch (info.signedness) {
+                .signed => Value.zero,
+                .unsigned => Value.one,
+            },
             else => {},
         }
 
         if (std.math.cast(u6, info.bits - 1)) |shift| switch (info.signedness) {
             .signed => {
                 const n = @as(i64, std.math.maxInt(i64)) >> (63 - shift);
-                return Value.Tag.int_i64.create(arena, n);
+                return mod.intValue(Type.comptime_int, n);
             },
             .unsigned => {
                 const n = @as(u64, std.math.maxInt(u64)) >> (63 - shift);
-                return Value.Tag.int_u64.create(arena, n);
+                return mod.intValue(Type.comptime_int, n);
             },
         };
 
-        var res = try std.math.big.int.Managed.init(arena);
+        var res = try std.math.big.int.Managed.init(mod.gpa);
+        defer res.deinit();
+
         try res.setTwosCompIntLimit(.max, info.signedness, info.bits);
 
-        const res_const = res.toConst();
-        if (res_const.positive) {
-            return Value.Tag.int_big_positive.create(arena, res_const.limbs);
-        } else {
-            return Value.Tag.int_big_negative.create(arena, res_const.limbs);
-        }
+        return mod.intValue_big(Type.comptime_int, res.toConst());
     }
 
     /// Asserts the type is an enum or a union.
@@ -4497,12 +4485,11 @@ pub const Type = struct {
         const S = struct {
             fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, m: *Module) ?usize {
                 if (int_val.compareAllWithZero(.lt, m)) return null;
-                var end_payload: Value.Payload.U64 = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = end,
+                const end_val = m.intValue(int_ty, end) catch |err| switch (err) {
+                    // TODO: eliminate this failure condition
+                    error.OutOfMemory => @panic("OOM"),
                 };
-                const end_val = Value.initPayload(&end_payload.base);
-                if (int_val.compareAll(.gte, end_val, int_ty, m)) return null;
+                if (int_val.compareScalar(.gte, end_val, int_ty, m)) return null;
                 return @intCast(usize, int_val.toUnsignedInt(m));
             }
         };
src/TypedValue.zig
@@ -41,8 +41,8 @@ pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash, mod: *Module) void {
     return tv.val.hash(tv.ty, hasher, mod);
 }
 
-pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value {
-    return tv.val.enumToInt(tv.ty, buffer);
+pub fn enumToInt(tv: TypedValue, mod: *Module) Allocator.Error!Value {
+    return tv.val.enumToInt(tv.ty, mod);
 }
 
 const max_aggregate_items = 100;
@@ -157,14 +157,8 @@ pub fn print(
 
                 return writer.writeAll(" }");
             },
-            .zero => return writer.writeAll("0"),
-            .one => return writer.writeAll("1"),
             .the_only_possible_value => return writer.writeAll("0"),
             .ty => return val.castTag(.ty).?.data.print(writer, mod),
-            .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", .{}, writer),
-            .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", .{}, writer),
-            .int_big_positive => return writer.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
-            .int_big_negative => return writer.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
             .lazy_align => {
                 const sub_ty = val.castTag(.lazy_align).?.data;
                 const x = sub_ty.abiAlignment(mod);
@@ -313,8 +307,9 @@ pub fn print(
 
                     var i: u32 = 0;
                     while (i < max_len) : (i += 1) {
-                        var elem_buf: Value.ElemValueBuffer = undefined;
-                        const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf);
+                        const elem_val = payload.ptr.elemValue(mod, i) catch |err| switch (err) {
+                            error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic
+                        };
                         if (elem_val.isUndef()) break :str;
                         buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(mod)) orelse break :str;
                     }
@@ -330,10 +325,12 @@ pub fn print(
                 var i: u32 = 0;
                 while (i < max_len) : (i += 1) {
                     if (i != 0) try writer.writeAll(", ");
-                    var buf: Value.ElemValueBuffer = undefined;
+                    const elem_val = payload.ptr.elemValue(mod, i) catch |err| switch (err) {
+                        error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic
+                    };
                     try print(.{
                         .ty = elem_ty,
-                        .val = payload.ptr.elemValueBuffer(mod, i, &buf),
+                        .val = elem_val,
                     }, writer, level - 1, mod);
                 }
                 if (len > max_aggregate_items) {
src/value.zig
@@ -33,8 +33,6 @@ pub const Value = struct {
     // Keep in sync with tools/stage2_pretty_printers_common.py
     pub const Tag = enum(usize) {
         // The first section of this enum are tags that require no payload.
-        zero,
-        one,
         /// The only possible value for a particular type, which is stored externally.
         the_only_possible_value,
 
@@ -43,10 +41,6 @@ pub const Value = struct {
         // After this, the tag requires a payload.
 
         ty,
-        int_u64,
-        int_i64,
-        int_big_positive,
-        int_big_negative,
         function,
         extern_fn,
         /// A comptime-known pointer can point to the address of a global
@@ -129,17 +123,11 @@ pub const Value = struct {
 
         pub fn Type(comptime t: Tag) type {
             return switch (t) {
-                .zero,
-                .one,
                 .the_only_possible_value,
                 .empty_struct_value,
                 .empty_array,
                 => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"),
 
-                .int_big_positive,
-                .int_big_negative,
-                => Payload.BigInt,
-
                 .extern_fn => Payload.ExternFn,
 
                 .decl_ref => Payload.Decl,
@@ -169,8 +157,6 @@ pub const Value = struct {
                 .lazy_size,
                 => Payload.Ty,
 
-                .int_u64 => Payload.U64,
-                .int_i64 => Payload.I64,
                 .function => Payload.Function,
                 .variable => Payload.Variable,
                 .decl_ref_mut => Payload.DeclRefMut,
@@ -281,8 +267,6 @@ pub const Value = struct {
                 .legacy = .{ .tag_if_small_enough = self.legacy.tag_if_small_enough },
             };
         } else switch (self.legacy.ptr_otherwise.tag) {
-            .zero,
-            .one,
             .the_only_possible_value,
             .empty_array,
             .empty_struct_value,
@@ -300,20 +284,6 @@ pub const Value = struct {
                     .legacy = .{ .ptr_otherwise = &new_payload.base },
                 };
             },
-            .int_u64 => return self.copyPayloadShallow(arena, Payload.U64),
-            .int_i64 => return self.copyPayloadShallow(arena, Payload.I64),
-            .int_big_positive, .int_big_negative => {
-                const old_payload = self.cast(Payload.BigInt).?;
-                const new_payload = try arena.create(Payload.BigInt);
-                new_payload.* = .{
-                    .base = .{ .tag = self.legacy.ptr_otherwise.tag },
-                    .data = try arena.dupe(std.math.big.Limb, old_payload.data),
-                };
-                return Value{
-                    .ip_index = .none,
-                    .legacy = .{ .ptr_otherwise = &new_payload.base },
-                };
-            },
             .function => return self.copyPayloadShallow(arena, Payload.Function),
             .extern_fn => return self.copyPayloadShallow(arena, Payload.ExternFn),
             .variable => return self.copyPayloadShallow(arena, Payload.Variable),
@@ -525,8 +495,6 @@ pub const Value = struct {
             .@"union" => {
                 return out_stream.writeAll("(union value)");
             },
-            .zero => return out_stream.writeAll("0"),
-            .one => return out_stream.writeAll("1"),
             .the_only_possible_value => return out_stream.writeAll("(the only possible value)"),
             .ty => return val.castTag(.ty).?.data.dump("", options, out_stream),
             .lazy_align => {
@@ -539,10 +507,6 @@ pub const Value = struct {
                 try val.castTag(.lazy_size).?.data.dump("", options, out_stream);
                 return try out_stream.writeAll(")");
             },
-            .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", options, out_stream),
-            .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
-            .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
-            .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
             .runtime_value => return out_stream.writeAll("[runtime value]"),
             .function => return out_stream.print("(function decl={d})", .{val.castTag(.function).?.data.owner_decl}),
             .extern_fn => return out_stream.writeAll("(extern function)"),
@@ -661,9 +625,8 @@ pub const Value = struct {
 
     fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 {
         const result = try allocator.alloc(u8, @intCast(usize, len));
-        var elem_value_buf: ElemValueBuffer = undefined;
         for (result, 0..) |*elem, i| {
-            const elem_val = val.elemValueBuffer(mod, i, &elem_value_buf);
+            const elem_val = try val.elemValue(mod, i);
             elem.* = @intCast(u8, elem_val.toUnsignedInt(mod));
         }
         return result;
@@ -695,7 +658,7 @@ pub const Value = struct {
         }
     }
 
-    pub fn enumToInt(val: Value, ty: Type, buffer: *Payload.U64) Value {
+    pub fn enumToInt(val: Value, ty: Type, mod: *Module) Allocator.Error!Value {
         const field_index = switch (val.tag()) {
             .enum_field_index => val.castTag(.enum_field_index).?.data,
             .the_only_possible_value => blk: {
@@ -717,11 +680,7 @@ pub const Value = struct {
                     return enum_full.values.keys()[field_index];
                 } else {
                     // Field index and integer values are the same.
-                    buffer.* = .{
-                        .base = .{ .tag = .int_u64 },
-                        .data = field_index,
-                    };
-                    return Value.initPayload(&buffer.base);
+                    return mod.intValue(enum_full.tag_ty, field_index);
                 }
             },
             .enum_numbered => {
@@ -730,20 +689,13 @@ pub const Value = struct {
                     return enum_obj.values.keys()[field_index];
                 } else {
                     // Field index and integer values are the same.
-                    buffer.* = .{
-                        .base = .{ .tag = .int_u64 },
-                        .data = field_index,
-                    };
-                    return Value.initPayload(&buffer.base);
+                    return mod.intValue(enum_obj.tag_ty, field_index);
                 }
             },
             .enum_simple => {
                 // Field index and integer values are the same.
-                buffer.* = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = field_index,
-                };
-                return Value.initPayload(&buffer.base);
+                const tag_ty = ty.intTagType();
+                return mod.intValue(tag_ty, field_index);
             },
             else => unreachable,
         }
@@ -802,12 +754,9 @@ pub const Value = struct {
             .undef => unreachable,
             .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
             .none => switch (val.tag()) {
-                .zero,
                 .the_only_possible_value, // i0, u0
                 => BigIntMutable.init(&space.limbs, 0).toConst(),
 
-                .one => BigIntMutable.init(&space.limbs, 1).toConst(),
-
                 .enum_field_index => {
                     const index = val.castTag(.enum_field_index).?.data;
                     return BigIntMutable.init(&space.limbs, index).toConst();
@@ -816,11 +765,6 @@ pub const Value = struct {
                     const sub_val = val.castTag(.runtime_value).?.data;
                     return sub_val.toBigIntAdvanced(space, mod, opt_sema);
                 },
-                .int_u64 => BigIntMutable.init(&space.limbs, val.castTag(.int_u64).?.data).toConst(),
-                .int_i64 => BigIntMutable.init(&space.limbs, val.castTag(.int_i64).?.data).toConst(),
-                .int_big_positive => val.castTag(.int_big_positive).?.asBigInt(),
-                .int_big_negative => val.castTag(.int_big_negative).?.asBigInt(),
-
                 .lazy_align => {
                     const ty = val.castTag(.lazy_align).?.data;
                     if (opt_sema) |sema| {
@@ -869,17 +813,9 @@ pub const Value = struct {
             .bool_true => return 1,
             .undef => unreachable,
             .none => switch (val.tag()) {
-                .zero,
                 .the_only_possible_value, // i0, u0
                 => return 0,
 
-                .one => return 1,
-
-                .int_u64 => return val.castTag(.int_u64).?.data,
-                .int_i64 => return @intCast(u64, val.castTag(.int_i64).?.data),
-                .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null,
-                .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null,
-
                 .lazy_align => {
                     const ty = val.castTag(.lazy_align).?.data;
                     if (opt_sema) |sema| {
@@ -922,17 +858,9 @@ pub const Value = struct {
             .bool_true => return 1,
             .undef => unreachable,
             .none => switch (val.tag()) {
-                .zero,
                 .the_only_possible_value, // i0, u0
                 => return 0,
 
-                .one => return 1,
-
-                .int_u64 => return @intCast(i64, val.castTag(.int_u64).?.data),
-                .int_i64 => return val.castTag(.int_i64).?.data,
-                .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(i64) catch unreachable,
-                .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(i64) catch unreachable,
-
                 .lazy_align => {
                     const ty = val.castTag(.lazy_align).?.data;
                     return @intCast(i64, ty.abiAlignment(mod));
@@ -959,22 +887,7 @@ pub const Value = struct {
         return switch (val.ip_index) {
             .bool_true => true,
             .bool_false => false,
-            .none => switch (val.tag()) {
-                .one => true,
-                .zero => false,
-
-                .int_u64 => switch (val.castTag(.int_u64).?.data) {
-                    0 => false,
-                    1 => true,
-                    else => unreachable,
-                },
-                .int_i64 => switch (val.castTag(.int_i64).?.data) {
-                    0 => false,
-                    1 => true,
-                    else => unreachable,
-                },
-                else => unreachable,
-            },
+            .none => unreachable,
             else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
                 .int => |int| switch (int.storage) {
                     .big_int => |big_int| !big_int.eqZero(),
@@ -1004,6 +917,7 @@ pub const Value = struct {
         ReinterpretDeclRef,
         IllDefinedMemoryLayout,
         Unimplemented,
+        OutOfMemory,
     }!void {
         const target = mod.getTarget();
         const endian = target.cpu.arch.endian();
@@ -1022,16 +936,14 @@ pub const Value = struct {
                 const bits = int_info.bits;
                 const byte_count = (bits + 7) / 8;
 
-                var enum_buffer: Payload.U64 = undefined;
-                const int_val = val.enumToInt(ty, &enum_buffer);
+                const int_val = try val.enumToInt(ty, mod);
 
                 if (byte_count <= @sizeOf(u64)) {
-                    const int: u64 = switch (int_val.tag()) {
-                        .zero => 0,
-                        .one => 1,
-                        .int_u64 => int_val.castTag(.int_u64).?.data,
-                        .int_i64 => @bitCast(u64, int_val.castTag(.int_i64).?.data),
-                        else => unreachable,
+                    const ip_key = mod.intern_pool.indexToKey(int_val.ip_index);
+                    const int: u64 = switch (ip_key.int.storage) {
+                        .u64 => |x| x,
+                        .i64 => |x| @bitCast(u64, x),
+                        .big_int => unreachable,
                     };
                     for (buffer[0..byte_count], 0..) |_, i| switch (endian) {
                         .Little => buffer[i] = @truncate(u8, (int >> @intCast(u6, (8 * i)))),
@@ -1044,11 +956,11 @@ pub const Value = struct {
                 }
             },
             .Float => switch (ty.floatBits(target)) {
-                16 => std.mem.writeInt(u16, buffer[0..2], @bitCast(u16, val.toFloat(f16)), endian),
-                32 => std.mem.writeInt(u32, buffer[0..4], @bitCast(u32, val.toFloat(f32)), endian),
-                64 => std.mem.writeInt(u64, buffer[0..8], @bitCast(u64, val.toFloat(f64)), endian),
-                80 => std.mem.writeInt(u80, buffer[0..10], @bitCast(u80, val.toFloat(f80)), endian),
-                128 => std.mem.writeInt(u128, buffer[0..16], @bitCast(u128, val.toFloat(f128)), endian),
+                16 => std.mem.writeInt(u16, buffer[0..2], @bitCast(u16, val.toFloat(f16, mod)), endian),
+                32 => std.mem.writeInt(u32, buffer[0..4], @bitCast(u32, val.toFloat(f32, mod)), endian),
+                64 => std.mem.writeInt(u64, buffer[0..8], @bitCast(u64, val.toFloat(f64, mod)), endian),
+                80 => std.mem.writeInt(u80, buffer[0..10], @bitCast(u80, val.toFloat(f80, mod)), endian),
+                128 => std.mem.writeInt(u128, buffer[0..16], @bitCast(u128, val.toFloat(f128, mod)), endian),
                 else => unreachable,
             },
             .Array => {
@@ -1056,10 +968,9 @@ pub const Value = struct {
                 const elem_ty = ty.childType(mod);
                 const elem_size = @intCast(usize, elem_ty.abiSize(mod));
                 var elem_i: usize = 0;
-                var elem_value_buf: ElemValueBuffer = undefined;
                 var buf_off: usize = 0;
                 while (elem_i < len) : (elem_i += 1) {
-                    const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf);
+                    const elem_val = try val.elemValue(mod, elem_i);
                     try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]);
                     buf_off += elem_size;
                 }
@@ -1122,7 +1033,13 @@ pub const Value = struct {
     ///
     /// Both the start and the end of the provided buffer must be tight, since
     /// big-endian packed memory layouts start at the end of the buffer.
-    pub fn writeToPackedMemory(val: Value, ty: Type, mod: *Module, buffer: []u8, bit_offset: usize) error{ReinterpretDeclRef}!void {
+    pub fn writeToPackedMemory(
+        val: Value,
+        ty: Type,
+        mod: *Module,
+        buffer: []u8,
+        bit_offset: usize,
+    ) error{ ReinterpretDeclRef, OutOfMemory }!void {
         const target = mod.getTarget();
         const endian = target.cpu.arch.endian();
         if (val.isUndef()) {
@@ -1147,16 +1064,14 @@ pub const Value = struct {
                 const bits = ty.intInfo(mod).bits;
                 const abi_size = @intCast(usize, ty.abiSize(mod));
 
-                var enum_buffer: Payload.U64 = undefined;
-                const int_val = val.enumToInt(ty, &enum_buffer);
+                const int_val = try val.enumToInt(ty, mod);
 
                 if (abi_size == 0) return;
                 if (abi_size <= @sizeOf(u64)) {
-                    const int: u64 = switch (int_val.tag()) {
-                        .zero => 0,
-                        .one => 1,
-                        .int_u64 => int_val.castTag(.int_u64).?.data,
-                        .int_i64 => @bitCast(u64, int_val.castTag(.int_i64).?.data),
+                    const ip_key = mod.intern_pool.indexToKey(int_val.ip_index);
+                    const int: u64 = switch (ip_key.int.storage) {
+                        .u64 => |x| x,
+                        .i64 => |x| @bitCast(u64, x),
                         else => unreachable,
                     };
                     std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian);
@@ -1167,11 +1082,11 @@ pub const Value = struct {
                 }
             },
             .Float => switch (ty.floatBits(target)) {
-                16 => std.mem.writePackedInt(u16, buffer, bit_offset, @bitCast(u16, val.toFloat(f16)), endian),
-                32 => std.mem.writePackedInt(u32, buffer, bit_offset, @bitCast(u32, val.toFloat(f32)), endian),
-                64 => std.mem.writePackedInt(u64, buffer, bit_offset, @bitCast(u64, val.toFloat(f64)), endian),
-                80 => std.mem.writePackedInt(u80, buffer, bit_offset, @bitCast(u80, val.toFloat(f80)), endian),
-                128 => std.mem.writePackedInt(u128, buffer, bit_offset, @bitCast(u128, val.toFloat(f128)), endian),
+                16 => std.mem.writePackedInt(u16, buffer, bit_offset, @bitCast(u16, val.toFloat(f16, mod)), endian),
+                32 => std.mem.writePackedInt(u32, buffer, bit_offset, @bitCast(u32, val.toFloat(f32, mod)), endian),
+                64 => std.mem.writePackedInt(u64, buffer, bit_offset, @bitCast(u64, val.toFloat(f64, mod)), endian),
+                80 => std.mem.writePackedInt(u80, buffer, bit_offset, @bitCast(u80, val.toFloat(f80, mod)), endian),
+                128 => std.mem.writePackedInt(u128, buffer, bit_offset, @bitCast(u128, val.toFloat(f128, mod)), endian),
                 else => unreachable,
             },
             .Vector => {
@@ -1181,11 +1096,10 @@ pub const Value = struct {
 
                 var bits: u16 = 0;
                 var elem_i: usize = 0;
-                var elem_value_buf: ElemValueBuffer = undefined;
                 while (elem_i < len) : (elem_i += 1) {
                     // On big-endian systems, LLVM reverses the element order of vectors by default
                     const tgt_elem_i = if (endian == .Big) len - elem_i - 1 else elem_i;
-                    const elem_val = val.elemValueBuffer(mod, tgt_elem_i, &elem_value_buf);
+                    const elem_val = try val.elemValue(mod, tgt_elem_i);
                     try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
                     bits += elem_bit_size;
                 }
@@ -1264,11 +1178,13 @@ pub const Value = struct {
                 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
                     .signed => {
                         const val = std.mem.readVarInt(i64, buffer[0..byte_count], endian);
-                        return Value.Tag.int_i64.create(arena, (val << @intCast(u6, 64 - bits)) >> @intCast(u6, 64 - bits));
+                        const result = (val << @intCast(u6, 64 - bits)) >> @intCast(u6, 64 - bits);
+                        return mod.intValue(ty, result);
                     },
                     .unsigned => {
                         const val = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
-                        return Value.Tag.int_u64.create(arena, (val << @intCast(u6, 64 - bits)) >> @intCast(u6, 64 - bits));
+                        const result = (val << @intCast(u6, 64 - bits)) >> @intCast(u6, 64 - bits);
+                        return mod.intValue(ty, result);
                     },
                 } else { // Slow path, we have to construct a big-int
                     const Limb = std.math.big.Limb;
@@ -1277,7 +1193,7 @@ pub const Value = struct {
 
                     var bigint = BigIntMutable.init(limbs_buffer, 0);
                     bigint.readTwosComplement(buffer[0..byte_count], bits, endian, int_info.signedness);
-                    return fromBigInt(arena, bigint.toConst());
+                    return mod.intValue_big(ty, bigint.toConst());
                 }
             },
             .Float => switch (ty.floatBits(target)) {
@@ -1381,8 +1297,8 @@ pub const Value = struct {
                 const bits = int_info.bits;
                 if (bits == 0) return Value.zero;
                 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
-                    .signed => return Value.Tag.int_i64.create(arena, std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed)),
-                    .unsigned => return Value.Tag.int_u64.create(arena, std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned)),
+                    .signed => return mod.intValue(ty, std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed)),
+                    .unsigned => return mod.intValue(ty, std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned)),
                 } else { // Slow path, we have to construct a big-int
                     const Limb = std.math.big.Limb;
                     const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
@@ -1390,7 +1306,7 @@ pub const Value = struct {
 
                     var bigint = BigIntMutable.init(limbs_buffer, 0);
                     bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
-                    return fromBigInt(arena, bigint.toConst());
+                    return mod.intValue_big(ty, bigint.toConst());
                 }
             },
             .Float => switch (ty.floatBits(target)) {
@@ -1444,32 +1360,29 @@ pub const Value = struct {
     }
 
     /// Asserts that the value is a float or an integer.
-    pub fn toFloat(val: Value, comptime T: type) T {
-        return switch (val.tag()) {
-            .float_16 => @floatCast(T, val.castTag(.float_16).?.data),
-            .float_32 => @floatCast(T, val.castTag(.float_32).?.data),
-            .float_64 => @floatCast(T, val.castTag(.float_64).?.data),
-            .float_80 => @floatCast(T, val.castTag(.float_80).?.data),
-            .float_128 => @floatCast(T, val.castTag(.float_128).?.data),
-
-            .zero => 0,
-            .one => 1,
-            .int_u64 => {
-                if (T == f80) {
-                    @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
-                }
-                return @intToFloat(T, val.castTag(.int_u64).?.data);
+    pub fn toFloat(val: Value, comptime T: type, mod: *const Module) T {
+        return switch (val.ip_index) {
+            .none => switch (val.tag()) {
+                .float_16 => @floatCast(T, val.castTag(.float_16).?.data),
+                .float_32 => @floatCast(T, val.castTag(.float_32).?.data),
+                .float_64 => @floatCast(T, val.castTag(.float_64).?.data),
+                .float_80 => @floatCast(T, val.castTag(.float_80).?.data),
+                .float_128 => @floatCast(T, val.castTag(.float_128).?.data),
+
+                else => unreachable,
             },
-            .int_i64 => {
-                if (T == f80) {
-                    @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
-                }
-                return @intToFloat(T, val.castTag(.int_i64).?.data);
+            else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                .int => |int| switch (int.storage) {
+                    .big_int => |big_int| @floatCast(T, bigIntToFloat(big_int.limbs, big_int.positive)),
+                    inline .u64, .i64 => |x| {
+                        if (T == f80) {
+                            @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
+                        }
+                        return @intToFloat(T, x);
+                    },
+                },
+                else => unreachable,
             },
-
-            .int_big_positive => @floatCast(T, bigIntToFloat(val.castTag(.int_big_positive).?.data, true)),
-            .int_big_negative => @floatCast(T, bigIntToFloat(val.castTag(.int_big_negative).?.data, false)),
-            else => unreachable,
         };
     }
 
@@ -1498,24 +1411,6 @@ pub const Value = struct {
             .bool_false => ty_bits,
             .bool_true => ty_bits - 1,
             .none => switch (val.tag()) {
-                .zero => ty_bits,
-                .one => ty_bits - 1,
-
-                .int_u64 => {
-                    const big = @clz(val.castTag(.int_u64).?.data);
-                    return big + ty_bits - 64;
-                },
-                .int_i64 => {
-                    @panic("TODO implement i64 Value clz");
-                },
-                .int_big_positive => {
-                    const bigint = val.castTag(.int_big_positive).?.asBigInt();
-                    return bigint.clz(ty_bits);
-                },
-                .int_big_negative => {
-                    @panic("TODO implement int_big_negative Value clz");
-                },
-
                 .the_only_possible_value => {
                     assert(ty_bits == 0);
                     return ty_bits;
@@ -1546,24 +1441,6 @@ pub const Value = struct {
             .bool_false => ty_bits,
             .bool_true => 0,
             .none => switch (val.tag()) {
-                .zero => ty_bits,
-                .one => 0,
-
-                .int_u64 => {
-                    const big = @ctz(val.castTag(.int_u64).?.data);
-                    return if (big == 64) ty_bits else big;
-                },
-                .int_i64 => {
-                    @panic("TODO implement i64 Value ctz");
-                },
-                .int_big_positive => {
-                    const bigint = val.castTag(.int_big_positive).?.asBigInt();
-                    return bigint.ctz();
-                },
-                .int_big_negative => {
-                    @panic("TODO implement int_big_negative Value ctz");
-                },
-
                 .the_only_possible_value => {
                     assert(ty_bits == 0);
                     return ty_bits;
@@ -1596,20 +1473,7 @@ pub const Value = struct {
         switch (val.ip_index) {
             .bool_false => return 0,
             .bool_true => return 1,
-            .none => switch (val.tag()) {
-                .zero => return 0,
-                .one => return 1,
-
-                .int_u64 => return @popCount(val.castTag(.int_u64).?.data),
-
-                else => {
-                    const info = ty.intInfo(mod);
-
-                    var buffer: Value.BigIntSpace = undefined;
-                    const int = val.toBigInt(&buffer, mod);
-                    return @intCast(u64, int.popCount(info.bits));
-                },
-            },
+            .none => unreachable,
             else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
                 .int => |int| {
                     const info = ty.intInfo(mod);
@@ -1622,7 +1486,7 @@ pub const Value = struct {
         }
     }
 
-    pub fn bitReverse(val: Value, ty: Type, mod: *const Module, arena: Allocator) !Value {
+    pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
         assert(!val.isUndef());
 
         const info = ty.intInfo(mod);
@@ -1637,10 +1501,10 @@ pub const Value = struct {
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
 
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
-    pub fn byteSwap(val: Value, ty: Type, mod: *const Module, arena: Allocator) !Value {
+    pub fn byteSwap(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
         assert(!val.isUndef());
 
         const info = ty.intInfo(mod);
@@ -1658,7 +1522,7 @@ pub const Value = struct {
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8);
 
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     /// Asserts the value is an integer and not undefined.
@@ -1669,19 +1533,7 @@ pub const Value = struct {
             .bool_false => 0,
             .bool_true => 1,
             .none => switch (self.tag()) {
-                .zero,
-                .the_only_possible_value,
-                => 0,
-
-                .one => 1,
-
-                .int_u64 => {
-                    const x = self.castTag(.int_u64).?.data;
-                    if (x == 0) return 0;
-                    return @intCast(usize, std.math.log2(x) + 1);
-                },
-                .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(),
-                .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),
+                .the_only_possible_value => 0,
 
                 .decl_ref_mut,
                 .comptime_field_ptr,
@@ -1715,13 +1567,14 @@ pub const Value = struct {
 
     /// Converts an integer or a float to a float. May result in a loss of information.
     /// Caller can find out by equality checking the result against the operand.
-    pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type, target: Target) !Value {
+    pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type, mod: *const Module) !Value {
+        const target = mod.getTarget();
         switch (dest_ty.floatBits(target)) {
-            16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)),
-            32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)),
-            64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)),
-            80 => return Value.Tag.float_80.create(arena, self.toFloat(f80)),
-            128 => return Value.Tag.float_128.create(arena, self.toFloat(f128)),
+            16 => return Value.Tag.float_16.create(arena, self.toFloat(f16, mod)),
+            32 => return Value.Tag.float_32.create(arena, self.toFloat(f32, mod)),
+            64 => return Value.Tag.float_64.create(arena, self.toFloat(f64, mod)),
+            80 => return Value.Tag.float_80.create(arena, self.toFloat(f80, mod)),
+            128 => return Value.Tag.float_128.create(arena, self.toFloat(f128, mod)),
             else => unreachable,
         }
     }
@@ -1729,10 +1582,6 @@ pub const Value = struct {
     /// Asserts the value is a float
     pub fn floatHasFraction(self: Value) bool {
         return switch (self.tag()) {
-            .zero,
-            .one,
-            => false,
-
             .float_16 => @rem(self.castTag(.float_16).?.data, 1) != 0,
             .float_32 => @rem(self.castTag(.float_32).?.data, 1) != 0,
             .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0,
@@ -1757,11 +1606,8 @@ pub const Value = struct {
             .bool_false => return .eq,
             .bool_true => return .gt,
             .none => return switch (lhs.tag()) {
-                .zero,
-                .the_only_possible_value,
-                => .eq,
+                .the_only_possible_value => .eq,
 
-                .one,
                 .decl_ref,
                 .decl_ref_mut,
                 .comptime_field_ptr,
@@ -1777,10 +1623,6 @@ pub const Value = struct {
                     const val = lhs.castTag(.runtime_value).?.data;
                     return val.orderAgainstZeroAdvanced(mod, opt_sema);
                 },
-                .int_u64 => std.math.order(lhs.castTag(.int_u64).?.data, 0),
-                .int_i64 => std.math.order(lhs.castTag(.int_i64).?.data, 0),
-                .int_big_positive => lhs.castTag(.int_big_positive).?.asBigInt().orderAgainstScalar(0),
-                .int_big_negative => lhs.castTag(.int_big_negative).?.asBigInt().orderAgainstScalar(0),
 
                 .lazy_align => {
                     const ty = lhs.castTag(.lazy_align).?.data;
@@ -1878,8 +1720,8 @@ pub const Value = struct {
             }
         }
         if (lhs_float or rhs_float) {
-            const lhs_f128 = lhs.toFloat(f128);
-            const rhs_f128 = rhs.toFloat(f128);
+            const lhs_f128 = lhs.toFloat(f128, mod);
+            const rhs_f128 = rhs.toFloat(f128, mod);
             return std.math.order(lhs_f128, rhs_f128);
         }
 
@@ -1929,15 +1771,13 @@ pub const Value = struct {
 
     /// Asserts the values are comparable. Both operands have type `ty`.
     /// For vectors, returns true if comparison is true for ALL elements.
-    pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) bool {
+    pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) !bool {
         if (ty.zigTypeTag(mod) == .Vector) {
-            var i: usize = 0;
-            while (i < ty.vectorLen(mod)) : (i += 1) {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                if (!compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod), mod)) {
+            const scalar_ty = ty.scalarType(mod);
+            for (0..ty.vectorLen(mod)) |i| {
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, mod)) {
                     return false;
                 }
             }
@@ -2203,10 +2043,8 @@ pub const Value = struct {
                 return a_type.eql(b_type, mod);
             },
             .Enum => {
-                var buf_a: Payload.U64 = undefined;
-                var buf_b: Payload.U64 = undefined;
-                const a_val = a.enumToInt(ty, &buf_a);
-                const b_val = b.enumToInt(ty, &buf_b);
+                const a_val = try a.enumToInt(ty, mod);
+                const b_val = try b.enumToInt(ty, mod);
                 const int_ty = ty.intTagType();
                 return eqlAdvanced(a_val, int_ty, b_val, int_ty, mod, opt_sema);
             },
@@ -2214,11 +2052,9 @@ pub const Value = struct {
                 const len = ty.arrayLen(mod);
                 const elem_ty = ty.childType(mod);
                 var i: usize = 0;
-                var a_buf: ElemValueBuffer = undefined;
-                var b_buf: ElemValueBuffer = undefined;
                 while (i < len) : (i += 1) {
-                    const a_elem = elemValueBuffer(a, mod, i, &a_buf);
-                    const b_elem = elemValueBuffer(b, mod, i, &b_buf);
+                    const a_elem = try elemValue(a, mod, i);
+                    const b_elem = try elemValue(b, mod, i);
                     if (!(try eqlAdvanced(a_elem, elem_ty, b_elem, elem_ty, mod, opt_sema))) {
                         return false;
                     }
@@ -2282,17 +2118,17 @@ pub const Value = struct {
             },
             .Float => {
                 switch (ty.floatBits(target)) {
-                    16 => return @bitCast(u16, a.toFloat(f16)) == @bitCast(u16, b.toFloat(f16)),
-                    32 => return @bitCast(u32, a.toFloat(f32)) == @bitCast(u32, b.toFloat(f32)),
-                    64 => return @bitCast(u64, a.toFloat(f64)) == @bitCast(u64, b.toFloat(f64)),
-                    80 => return @bitCast(u80, a.toFloat(f80)) == @bitCast(u80, b.toFloat(f80)),
-                    128 => return @bitCast(u128, a.toFloat(f128)) == @bitCast(u128, b.toFloat(f128)),
+                    16 => return @bitCast(u16, a.toFloat(f16, mod)) == @bitCast(u16, b.toFloat(f16, mod)),
+                    32 => return @bitCast(u32, a.toFloat(f32, mod)) == @bitCast(u32, b.toFloat(f32, mod)),
+                    64 => return @bitCast(u64, a.toFloat(f64, mod)) == @bitCast(u64, b.toFloat(f64, mod)),
+                    80 => return @bitCast(u80, a.toFloat(f80, mod)) == @bitCast(u80, b.toFloat(f80, mod)),
+                    128 => return @bitCast(u128, a.toFloat(f128, mod)) == @bitCast(u128, b.toFloat(f128, mod)),
                     else => unreachable,
                 }
             },
             .ComptimeFloat => {
-                const a_float = a.toFloat(f128);
-                const b_float = b.toFloat(f128);
+                const a_float = a.toFloat(f128, mod);
+                const b_float = b.toFloat(f128, mod);
 
                 const a_nan = std.math.isNan(a_float);
                 const b_nan = std.math.isNan(b_float);
@@ -2354,16 +2190,16 @@ pub const Value = struct {
             .Float => {
                 // For hash/eql purposes, we treat floats as their IEEE integer representation.
                 switch (ty.floatBits(mod.getTarget())) {
-                    16 => std.hash.autoHash(hasher, @bitCast(u16, val.toFloat(f16))),
-                    32 => std.hash.autoHash(hasher, @bitCast(u32, val.toFloat(f32))),
-                    64 => std.hash.autoHash(hasher, @bitCast(u64, val.toFloat(f64))),
-                    80 => std.hash.autoHash(hasher, @bitCast(u80, val.toFloat(f80))),
-                    128 => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128))),
+                    16 => std.hash.autoHash(hasher, @bitCast(u16, val.toFloat(f16, mod))),
+                    32 => std.hash.autoHash(hasher, @bitCast(u32, val.toFloat(f32, mod))),
+                    64 => std.hash.autoHash(hasher, @bitCast(u64, val.toFloat(f64, mod))),
+                    80 => std.hash.autoHash(hasher, @bitCast(u80, val.toFloat(f80, mod))),
+                    128 => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128, mod))),
                     else => unreachable,
                 }
             },
             .ComptimeFloat => {
-                const float = val.toFloat(f128);
+                const float = val.toFloat(f128, mod);
                 const is_nan = std.math.isNan(float);
                 std.hash.autoHash(hasher, is_nan);
                 if (!is_nan) {
@@ -2387,9 +2223,11 @@ pub const Value = struct {
                 const len = ty.arrayLen(mod);
                 const elem_ty = ty.childType(mod);
                 var index: usize = 0;
-                var elem_value_buf: ElemValueBuffer = undefined;
                 while (index < len) : (index += 1) {
-                    const elem_val = val.elemValueBuffer(mod, index, &elem_value_buf);
+                    const elem_val = val.elemValue(mod, index) catch |err| switch (err) {
+                        // Will be solved when arrays and vectors get migrated to the intern pool.
+                        error.OutOfMemory => @panic("OOM"),
+                    };
                     elem_val.hash(elem_ty, hasher, mod);
                 }
             },
@@ -2438,8 +2276,8 @@ pub const Value = struct {
                 hasher.update(val.getError().?);
             },
             .Enum => {
-                var enum_space: Payload.U64 = undefined;
-                const int_val = val.enumToInt(ty, &enum_space);
+                // This panic will go away when enum values move to be stored in the intern pool.
+                const int_val = val.enumToInt(ty, mod) catch @panic("OOM");
                 hashInt(int_val, hasher, mod);
             },
             .Union => {
@@ -2494,7 +2332,7 @@ pub const Value = struct {
             .Type => {
                 val.toType().hashWithHasher(hasher, mod);
             },
-            .Float, .ComptimeFloat => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128))),
+            .Float, .ComptimeFloat => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128, mod))),
             .Bool, .Int, .ComptimeInt, .Pointer, .Fn => switch (val.tag()) {
                 .slice => {
                     const slice = val.castTag(.slice).?.data;
@@ -2508,9 +2346,11 @@ pub const Value = struct {
                 const len = ty.arrayLen(mod);
                 const elem_ty = ty.childType(mod);
                 var index: usize = 0;
-                var elem_value_buf: ElemValueBuffer = undefined;
                 while (index < len) : (index += 1) {
-                    const elem_val = val.elemValueBuffer(mod, index, &elem_value_buf);
+                    const elem_val = val.elemValue(mod, index) catch |err| switch (err) {
+                        // Will be solved when arrays and vectors get migrated to the intern pool.
+                        error.OutOfMemory => @panic("OOM"),
+                    };
                     elem_val.hashUncoerced(elem_ty, hasher, mod);
                 }
             },
@@ -2661,12 +2501,6 @@ pub const Value = struct {
                 hashPtr(opt_ptr.container_ptr, hasher, mod);
             },
 
-            .zero,
-            .one,
-            .int_u64,
-            .int_i64,
-            .int_big_positive,
-            .int_big_negative,
             .the_only_possible_value,
             .lazy_align,
             .lazy_size,
@@ -2720,23 +2554,7 @@ pub const Value = struct {
 
     /// Asserts the value is a single-item pointer to an array, or an array,
     /// or an unknown-length pointer, and returns the element value at the index.
-    pub fn elemValue(val: Value, mod: *Module, arena: Allocator, index: usize) !Value {
-        return elemValueAdvanced(val, mod, index, arena, undefined);
-    }
-
-    pub const ElemValueBuffer = Payload.U64;
-
-    pub fn elemValueBuffer(val: Value, mod: *Module, index: usize, buffer: *ElemValueBuffer) Value {
-        return elemValueAdvanced(val, mod, index, null, buffer) catch unreachable;
-    }
-
-    pub fn elemValueAdvanced(
-        val: Value,
-        mod: *Module,
-        index: usize,
-        arena: ?Allocator,
-        buffer: *ElemValueBuffer,
-    ) error{OutOfMemory}!Value {
+    pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
         switch (val.ip_index) {
             .undef => return Value.undef,
             .none => switch (val.tag()) {
@@ -2751,43 +2569,27 @@ pub const Value = struct {
 
                 .bytes => {
                     const byte = val.castTag(.bytes).?.data[index];
-                    if (arena) |a| {
-                        return Tag.int_u64.create(a, byte);
-                    } else {
-                        buffer.* = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = byte,
-                        };
-                        return initPayload(&buffer.base);
-                    }
+                    return mod.intValue(Type.u8, byte);
                 },
                 .str_lit => {
                     const str_lit = val.castTag(.str_lit).?.data;
                     const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
                     const byte = bytes[index];
-                    if (arena) |a| {
-                        return Tag.int_u64.create(a, byte);
-                    } else {
-                        buffer.* = .{
-                            .base = .{ .tag = .int_u64 },
-                            .data = byte,
-                        };
-                        return initPayload(&buffer.base);
-                    }
+                    return mod.intValue(Type.u8, byte);
                 },
 
                 // No matter the index; all the elements are the same!
                 .repeated => return val.castTag(.repeated).?.data,
 
                 .aggregate => return val.castTag(.aggregate).?.data[index],
-                .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(mod, index, arena, buffer),
+                .slice => return val.castTag(.slice).?.data.ptr.elemValue(mod, index),
 
-                .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer),
-                .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer),
-                .comptime_field_ptr => return val.castTag(.comptime_field_ptr).?.data.field_val.elemValueAdvanced(mod, index, arena, buffer),
+                .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValue(mod, index),
+                .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValue(mod, index),
+                .comptime_field_ptr => return val.castTag(.comptime_field_ptr).?.data.field_val.elemValue(mod, index),
                 .elem_ptr => {
                     const data = val.castTag(.elem_ptr).?.data;
-                    return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer);
+                    return data.array_ptr.elemValue(mod, index + data.index);
                 },
                 .field_ptr => {
                     const data = val.castTag(.field_ptr).?.data;
@@ -2795,7 +2597,7 @@ pub const Value = struct {
                         const container_decl = mod.declPtr(decl_index);
                         const field_type = data.container_ty.structFieldType(data.field_index);
                         const field_val = container_decl.val.fieldValue(field_type, mod, data.field_index);
-                        return field_val.elemValueAdvanced(mod, index, arena, buffer);
+                        return field_val.elemValue(mod, index);
                     } else unreachable;
                 },
 
@@ -2803,11 +2605,11 @@ pub const Value = struct {
                 // to have only one possible value itself.
                 .the_only_possible_value => return val,
 
-                .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.container_ptr.elemValueAdvanced(mod, index, arena, buffer),
-                .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.container_ptr.elemValueAdvanced(mod, index, arena, buffer),
+                .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.container_ptr.elemValue(mod, index),
+                .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.container_ptr.elemValue(mod, index),
 
-                .opt_payload => return val.castTag(.opt_payload).?.data.elemValueAdvanced(mod, index, arena, buffer),
-                .eu_payload => return val.castTag(.eu_payload).?.data.elemValueAdvanced(mod, index, arena, buffer),
+                .opt_payload => return val.castTag(.opt_payload).?.data.elemValue(mod, index),
+                .eu_payload => return val.castTag(.eu_payload).?.data.elemValue(mod, index),
 
                 else => unreachable,
             },
@@ -3004,7 +2806,7 @@ pub const Value = struct {
     /// TODO: check for cases such as array that is not marked undef but all the element
     /// values are marked undef, or struct that is not marked undef but all fields are marked
     /// undef, etc.
-    pub fn anyUndef(self: Value, mod: *Module) bool {
+    pub fn anyUndef(self: Value, mod: *Module) !bool {
         switch (self.ip_index) {
             .undef => return true,
             .none => switch (self.tag()) {
@@ -3012,18 +2814,16 @@ pub const Value = struct {
                     const payload = self.castTag(.slice).?;
                     const len = payload.data.len.toUnsignedInt(mod);
 
-                    var elem_value_buf: ElemValueBuffer = undefined;
-                    var i: usize = 0;
-                    while (i < len) : (i += 1) {
-                        const elem_val = payload.data.ptr.elemValueBuffer(mod, i, &elem_value_buf);
-                        if (elem_val.anyUndef(mod)) return true;
+                    for (0..len) |i| {
+                        const elem_val = try payload.data.ptr.elemValue(mod, i);
+                        if (try elem_val.anyUndef(mod)) return true;
                     }
                 },
 
                 .aggregate => {
                     const payload = self.castTag(.aggregate).?;
                     for (payload.data) |val| {
-                        if (val.anyUndef(mod)) return true;
+                        if (try val.anyUndef(mod)) return true;
                     }
                 },
                 else => {},
@@ -3036,35 +2836,37 @@ pub const Value = struct {
 
     /// Asserts the value is not undefined and not unreachable.
     /// Integer value 0 is considered null because of C pointers.
-    pub fn isNull(self: Value, mod: *const Module) bool {
-        return switch (self.ip_index) {
+    pub fn isNull(val: Value, mod: *const Module) bool {
+        return switch (val.ip_index) {
             .undef => unreachable,
             .unreachable_value => unreachable,
-            .null_value => true,
-            .none => switch (self.tag()) {
+
+            .null_value,
+            .zero,
+            .zero_usize,
+            .zero_u8,
+            => true,
+
+            .none => switch (val.tag()) {
                 .opt_payload => false,
 
                 // If it's not one of those two tags then it must be a C pointer value,
                 // in which case the value 0 is null and other values are non-null.
 
-                .zero,
-                .the_only_possible_value,
-                => true,
-
-                .one => false,
-
-                .int_u64,
-                .int_i64,
-                .int_big_positive,
-                .int_big_negative,
-                => self.orderAgainstZero(mod).compare(.eq),
+                .the_only_possible_value => true,
 
                 .inferred_alloc => unreachable,
                 .inferred_alloc_comptime => unreachable,
 
                 else => false,
             },
-            else => false,
+            else => return switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                .int => |int| switch (int.storage) {
+                    .big_int => |big_int| big_int.eqZero(),
+                    inline .u64, .i64 => |x| x == 0,
+                },
+                else => unreachable,
+            },
         };
     }
 
@@ -3078,17 +2880,13 @@ pub const Value = struct {
             .unreachable_value => unreachable,
             .none => switch (self.tag()) {
                 .@"error" => self.castTag(.@"error").?.data.name,
-                .int_u64 => @panic("TODO"),
-                .int_i64 => @panic("TODO"),
-                .int_big_positive => @panic("TODO"),
-                .int_big_negative => @panic("TODO"),
-                .one => @panic("TODO"),
+                .eu_payload => null,
+
                 .inferred_alloc => unreachable,
                 .inferred_alloc_comptime => unreachable,
-
-                else => null,
+                else => unreachable,
             },
-            else => null,
+            else => unreachable,
         };
     }
 
@@ -3147,10 +2945,10 @@ pub const Value = struct {
     pub fn intToFloatAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
         if (int_ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, int_ty.vectorLen(mod));
+            const scalar_ty = float_ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try intToFloatScalar(elem_val, arena, float_ty.scalarType(mod), mod, opt_sema);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try intToFloatScalar(elem_val, arena, scalar_ty, mod, opt_sema);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
@@ -3162,24 +2960,7 @@ pub const Value = struct {
         switch (val.ip_index) {
             .undef => return val,
             .none => switch (val.tag()) {
-                .zero, .one => return val,
-                .the_only_possible_value => return Value.initTag(.zero), // for i0, u0
-                .int_u64 => {
-                    return intToFloatInner(val.castTag(.int_u64).?.data, arena, float_ty, target);
-                },
-                .int_i64 => {
-                    return intToFloatInner(val.castTag(.int_i64).?.data, arena, float_ty, target);
-                },
-                .int_big_positive => {
-                    const limbs = val.castTag(.int_big_positive).?.data;
-                    const float = bigIntToFloat(limbs, true);
-                    return floatToValue(float, arena, float_ty, target);
-                },
-                .int_big_negative => {
-                    const limbs = val.castTag(.int_big_negative).?.data;
-                    const float = bigIntToFloat(limbs, false);
-                    return floatToValue(float, arena, float_ty, target);
-                },
+                .the_only_possible_value => return Value.zero, // for i0, u0
                 .lazy_align => {
                     const ty = val.castTag(.lazy_align).?.data;
                     if (opt_sema) |sema| {
@@ -3198,7 +2979,16 @@ pub const Value = struct {
                 },
                 else => unreachable,
             },
-            else => unreachable,
+            else => return switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                .int => |int| switch (int.storage) {
+                    .big_int => |big_int| {
+                        const float = bigIntToFloat(big_int.limbs, big_int.positive);
+                        return floatToValue(float, arena, float_ty, target);
+                    },
+                    inline .u64, .i64 => |x| intToFloatInner(x, arena, float_ty, target),
+                },
+                else => unreachable,
+            },
         }
     }
 
@@ -3238,22 +3028,6 @@ pub const Value = struct {
         wrapped_result: Value,
     };
 
-    pub fn fromBigInt(arena: Allocator, big_int: BigIntConst) !Value {
-        if (big_int.positive) {
-            if (big_int.to(u64)) |x| {
-                return Value.Tag.int_u64.create(arena, x);
-            } else |_| {
-                return Value.Tag.int_big_positive.create(arena, big_int.limbs);
-            }
-        } else {
-            if (big_int.to(i64)) |x| {
-                return Value.Tag.int_i64.create(arena, x);
-            } else |_| {
-                return Value.Tag.int_big_negative.create(arena, big_int.limbs);
-            }
-        }
-    }
-
     /// Supports (vectors of) integers only; asserts neither operand is undefined.
     pub fn intAddSat(
         lhs: Value,
@@ -3264,12 +3038,11 @@ pub const Value = struct {
     ) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try intAddSatScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try intAddSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
@@ -3299,7 +3072,7 @@ pub const Value = struct {
         );
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     /// Supports (vectors of) integers only; asserts neither operand is undefined.
@@ -3312,12 +3085,11 @@ pub const Value = struct {
     ) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try intSubSatScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try intSubSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
@@ -3347,7 +3119,7 @@ pub const Value = struct {
         );
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn intMulWithOverflow(
@@ -3360,12 +3132,11 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const overflowed_data = try arena.alloc(Value, ty.vectorLen(mod));
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                const of_math_result = try intMulWithOverflowScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                const of_math_result = try intMulWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
                 overflowed_data[i] = of_math_result.overflow_bit;
                 scalar.* = of_math_result.wrapped_result;
             }
@@ -3408,7 +3179,7 @@ pub const Value = struct {
 
         return OverflowArithmeticResult{
             .overflow_bit = boolToInt(overflowed),
-            .wrapped_result = try fromBigInt(arena, result_bigint.toConst()),
+            .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
         };
     }
 
@@ -3423,10 +3194,8 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
                 scalar.* = try numberMulWrapScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
@@ -3467,10 +3236,8 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
                 scalar.* = try intMulSatScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
@@ -3510,7 +3277,7 @@ pub const Value = struct {
         );
         result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
         result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     /// Supports both floats and ints; handles undefined.
@@ -3542,8 +3309,7 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
+                const elem_val = try val.elemValue(mod, i);
                 scalar.* = try bitwiseNotScalar(elem_val, ty.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
@@ -3572,7 +3338,7 @@ pub const Value = struct {
 
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     /// operands must be (vectors of) integers; handles undefined scalars.
@@ -3580,19 +3346,17 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try bitwiseAndScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try bitwiseAndScalar(lhs_elem, rhs_elem, ty.scalarType(mod), allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return bitwiseAndScalar(lhs, rhs, allocator, mod);
+        return bitwiseAndScalar(lhs, rhs, ty, allocator, mod);
     }
 
     /// operands must be integers; handles undefined.
-    pub fn bitwiseAndScalar(lhs: Value, rhs: Value, arena: Allocator, mod: *Module) !Value {
+    pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
         if (lhs.isUndef() or rhs.isUndef()) return Value.undef;
 
         // TODO is this a performance issue? maybe we should try the operation without
@@ -3608,7 +3372,7 @@ pub const Value = struct {
         );
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.bitAnd(lhs_bigint, rhs_bigint);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     /// operands must be (vectors of) integers; handles undefined scalars.
@@ -3616,10 +3380,8 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
                 scalar.* = try bitwiseNandScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
@@ -3632,12 +3394,7 @@ pub const Value = struct {
         if (lhs.isUndef() or rhs.isUndef()) return Value.undef;
 
         const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
-
-        const all_ones = if (ty.isSignedInt(mod))
-            try Value.Tag.int_i64.create(arena, -1)
-        else
-            try ty.maxInt(arena, mod);
-
+        const all_ones = if (ty.isSignedInt(mod)) Value.negative_one else try ty.maxIntScalar(mod);
         return bitwiseXor(anded, all_ones, ty, arena, mod);
     }
 
@@ -3646,19 +3403,17 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try bitwiseOrScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try bitwiseOrScalar(lhs_elem, rhs_elem, ty.scalarType(mod), allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return bitwiseOrScalar(lhs, rhs, allocator, mod);
+        return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);
     }
 
     /// operands must be integers; handles undefined.
-    pub fn bitwiseOrScalar(lhs: Value, rhs: Value, arena: Allocator, mod: *Module) !Value {
+    pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
         if (lhs.isUndef() or rhs.isUndef()) return Value.undef;
 
         // TODO is this a performance issue? maybe we should try the operation without
@@ -3673,27 +3428,26 @@ pub const Value = struct {
         );
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.bitOr(lhs_bigint, rhs_bigint);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     /// operands must be (vectors of) integers; handles undefined scalars.
     pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try bitwiseXorScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try bitwiseXorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return bitwiseXorScalar(lhs, rhs, allocator, mod);
+        return bitwiseXorScalar(lhs, rhs, ty, allocator, mod);
     }
 
     /// operands must be integers; handles undefined.
-    pub fn bitwiseXorScalar(lhs: Value, rhs: Value, arena: Allocator, mod: *Module) !Value {
+    pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
         if (lhs.isUndef() or rhs.isUndef()) return Value.undef;
 
         // TODO is this a performance issue? maybe we should try the operation without
@@ -3709,25 +3463,24 @@ pub const Value = struct {
         );
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
         result_bigint.bitXor(lhs_bigint, rhs_bigint);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try intDivScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return intDivScalar(lhs, rhs, allocator, mod);
+        return intDivScalar(lhs, rhs, ty, allocator, mod);
     }
 
-    pub fn intDivScalar(lhs: Value, rhs: Value, allocator: Allocator, mod: *Module) !Value {
+    pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
         var lhs_space: Value.BigIntSpace = undefined;
@@ -3749,25 +3502,24 @@ pub const Value = struct {
         var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
         var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
         result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
-        return fromBigInt(allocator, result_q.toConst());
+        return mod.intValue_big(ty, result_q.toConst());
     }
 
     pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try intDivFloorScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return intDivFloorScalar(lhs, rhs, allocator, mod);
+        return intDivFloorScalar(lhs, rhs, ty, allocator, mod);
     }
 
-    pub fn intDivFloorScalar(lhs: Value, rhs: Value, allocator: Allocator, mod: *Module) !Value {
+    pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
         var lhs_space: Value.BigIntSpace = undefined;
@@ -3789,25 +3541,24 @@ pub const Value = struct {
         var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
         var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
         result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
-        return fromBigInt(allocator, result_q.toConst());
+        return mod.intValue_big(ty, result_q.toConst());
     }
 
     pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try intModScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try intModScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return intModScalar(lhs, rhs, allocator, mod);
+        return intModScalar(lhs, rhs, ty, allocator, mod);
     }
 
-    pub fn intModScalar(lhs: Value, rhs: Value, allocator: Allocator, mod: *Module) !Value {
+    pub fn intModScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
         var lhs_space: Value.BigIntSpace = undefined;
@@ -3829,7 +3580,7 @@ pub const Value = struct {
         var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
         var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
         result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
-        return fromBigInt(allocator, result_r.toConst());
+        return mod.intValue_big(ty, result_r.toConst());
     }
 
     /// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
@@ -3877,46 +3628,44 @@ pub const Value = struct {
     }
 
     pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try floatRemScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, target);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try floatRemScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatRemScalar(lhs, rhs, float_type, arena, target);
+        return floatRemScalar(lhs, rhs, float_type, arena, mod);
     }
 
-    pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value {
+    pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *const Module) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const lhs_val = lhs.toFloat(f16);
-                const rhs_val = rhs.toFloat(f16);
+                const lhs_val = lhs.toFloat(f16, mod);
+                const rhs_val = rhs.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @rem(lhs_val, rhs_val));
             },
             32 => {
-                const lhs_val = lhs.toFloat(f32);
-                const rhs_val = rhs.toFloat(f32);
+                const lhs_val = lhs.toFloat(f32, mod);
+                const rhs_val = rhs.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @rem(lhs_val, rhs_val));
             },
             64 => {
-                const lhs_val = lhs.toFloat(f64);
-                const rhs_val = rhs.toFloat(f64);
+                const lhs_val = lhs.toFloat(f64, mod);
+                const rhs_val = rhs.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val));
             },
             80 => {
-                const lhs_val = lhs.toFloat(f80);
-                const rhs_val = rhs.toFloat(f80);
+                const lhs_val = lhs.toFloat(f80, mod);
+                const rhs_val = rhs.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val));
             },
             128 => {
-                const lhs_val = lhs.toFloat(f128);
-                const rhs_val = rhs.toFloat(f128);
+                const lhs_val = lhs.toFloat(f128, mod);
+                const rhs_val = rhs.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @rem(lhs_val, rhs_val));
             },
             else => unreachable,
@@ -3924,46 +3673,44 @@ pub const Value = struct {
     }
 
     pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try floatModScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, target);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try floatModScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatModScalar(lhs, rhs, float_type, arena, target);
+        return floatModScalar(lhs, rhs, float_type, arena, mod);
     }
 
-    pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value {
+    pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *const Module) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const lhs_val = lhs.toFloat(f16);
-                const rhs_val = rhs.toFloat(f16);
+                const lhs_val = lhs.toFloat(f16, mod);
+                const rhs_val = rhs.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @mod(lhs_val, rhs_val));
             },
             32 => {
-                const lhs_val = lhs.toFloat(f32);
-                const rhs_val = rhs.toFloat(f32);
+                const lhs_val = lhs.toFloat(f32, mod);
+                const rhs_val = rhs.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @mod(lhs_val, rhs_val));
             },
             64 => {
-                const lhs_val = lhs.toFloat(f64);
-                const rhs_val = rhs.toFloat(f64);
+                const lhs_val = lhs.toFloat(f64, mod);
+                const rhs_val = rhs.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val));
             },
             80 => {
-                const lhs_val = lhs.toFloat(f80);
-                const rhs_val = rhs.toFloat(f80);
+                const lhs_val = lhs.toFloat(f80, mod);
+                const rhs_val = rhs.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val));
             },
             128 => {
-                const lhs_val = lhs.toFloat(f128);
-                const rhs_val = rhs.toFloat(f128);
+                const lhs_val = lhs.toFloat(f128, mod);
+                const rhs_val = rhs.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @mod(lhs_val, rhs_val));
             },
             else => unreachable,
@@ -3973,19 +3720,18 @@ pub const Value = struct {
     pub fn intMul(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try intMulScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return intMulScalar(lhs, rhs, allocator, mod);
+        return intMulScalar(lhs, rhs, ty, allocator, mod);
     }
 
-    pub fn intMulScalar(lhs: Value, rhs: Value, allocator: Allocator, mod: *Module) !Value {
+    pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
         var lhs_space: Value.BigIntSpace = undefined;
@@ -4003,20 +3749,20 @@ pub const Value = struct {
         );
         defer allocator.free(limbs_buffer);
         result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
-        return fromBigInt(allocator, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try intTruncScalar(elem_val, allocator, signedness, bits, mod);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try intTruncScalar(elem_val, scalar_ty, allocator, signedness, bits, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return intTruncScalar(val, allocator, signedness, bits, mod);
+        return intTruncScalar(val, ty, allocator, signedness, bits, mod);
     }
 
     /// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`.
@@ -4030,19 +3776,25 @@ pub const Value = struct {
     ) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                var bits_buf: Value.ElemValueBuffer = undefined;
-                const bits_elem = bits.elemValueBuffer(mod, i, &bits_buf);
-                scalar.* = try intTruncScalar(elem_val, allocator, signedness, @intCast(u16, bits_elem.toUnsignedInt(mod)), mod);
+                const elem_val = try val.elemValue(mod, i);
+                const bits_elem = try bits.elemValue(mod, i);
+                scalar.* = try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @intCast(u16, bits_elem.toUnsignedInt(mod)), mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return intTruncScalar(val, allocator, signedness, @intCast(u16, bits.toUnsignedInt(mod)), mod);
+        return intTruncScalar(val, ty, allocator, signedness, @intCast(u16, bits.toUnsignedInt(mod)), mod);
     }
 
-    pub fn intTruncScalar(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {
+    pub fn intTruncScalar(
+        val: Value,
+        ty: Type,
+        allocator: Allocator,
+        signedness: std.builtin.Signedness,
+        bits: u16,
+        mod: *Module,
+    ) !Value {
         if (bits == 0) return Value.zero;
 
         var val_space: Value.BigIntSpace = undefined;
@@ -4055,25 +3807,24 @@ pub const Value = struct {
         var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
 
         result_bigint.truncate(val_bigint, signedness, bits);
-        return fromBigInt(allocator, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try shlScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try shlScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return shlScalar(lhs, rhs, allocator, mod);
+        return shlScalar(lhs, rhs, ty, allocator, mod);
     }
 
-    pub fn shlScalar(lhs: Value, rhs: Value, allocator: Allocator, mod: *Module) !Value {
+    pub fn shlScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
         var lhs_space: Value.BigIntSpace = undefined;
@@ -4089,7 +3840,7 @@ pub const Value = struct {
             .len = undefined,
         };
         result_bigint.shiftLeft(lhs_bigint, shift);
-        return fromBigInt(allocator, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn shlWithOverflow(
@@ -4103,10 +3854,8 @@ pub const Value = struct {
             const overflowed_data = try allocator.alloc(Value, ty.vectorLen(mod));
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
                 const of_math_result = try shlWithOverflowScalar(lhs_elem, rhs_elem, ty.scalarType(mod), allocator, mod);
                 overflowed_data[i] = of_math_result.overflow_bit;
                 scalar.* = of_math_result.wrapped_result;
@@ -4146,7 +3895,7 @@ pub const Value = struct {
         }
         return OverflowArithmeticResult{
             .overflow_bit = boolToInt(overflowed),
-            .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()),
+            .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
         };
     }
 
@@ -4160,10 +3909,8 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
                 scalar.* = try shlSatScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
@@ -4195,7 +3942,7 @@ pub const Value = struct {
             .len = undefined,
         };
         result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
-        return fromBigInt(arena, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn shlTrunc(
@@ -4208,10 +3955,8 @@ pub const Value = struct {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, ty.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
                 scalar.* = try shlTruncScalar(lhs_elem, rhs_elem, ty.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
@@ -4235,19 +3980,18 @@ pub const Value = struct {
     pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         if (ty.zigTypeTag(mod) == .Vector) {
             const result_data = try allocator.alloc(Value, ty.vectorLen(mod));
+            const scalar_ty = ty.scalarType(mod);
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try shrScalar(lhs_elem, rhs_elem, allocator, mod);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try shrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
             }
             return Value.Tag.aggregate.create(allocator, result_data);
         }
-        return shrScalar(lhs, rhs, allocator, mod);
+        return shrScalar(lhs, rhs, ty, allocator, mod);
     }
 
-    pub fn shrScalar(lhs: Value, rhs: Value, allocator: Allocator, mod: *Module) !Value {
+    pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
         var lhs_space: Value.BigIntSpace = undefined;
@@ -4275,7 +4019,7 @@ pub const Value = struct {
             .len = undefined,
         };
         result_bigint.shiftRight(lhs_bigint, shift);
-        return fromBigInt(allocator, result_bigint.toConst());
+        return mod.intValue_big(ty, result_bigint.toConst());
     }
 
     pub fn floatNeg(
@@ -4284,31 +4028,30 @@ pub const Value = struct {
         arena: Allocator,
         mod: *Module,
     ) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try floatNegScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try floatNegScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatNegScalar(val, float_type, arena, target);
+        return floatNegScalar(val, float_type, arena, mod);
     }
 
     pub fn floatNegScalar(
         val: Value,
         float_type: Type,
         arena: Allocator,
-        target: Target,
+        mod: *const Module,
     ) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
-            16 => return Value.Tag.float_16.create(arena, -val.toFloat(f16)),
-            32 => return Value.Tag.float_32.create(arena, -val.toFloat(f32)),
-            64 => return Value.Tag.float_64.create(arena, -val.toFloat(f64)),
-            80 => return Value.Tag.float_80.create(arena, -val.toFloat(f80)),
-            128 => return Value.Tag.float_128.create(arena, -val.toFloat(f128)),
+            16 => return Value.Tag.float_16.create(arena, -val.toFloat(f16, mod)),
+            32 => return Value.Tag.float_32.create(arena, -val.toFloat(f32, mod)),
+            64 => return Value.Tag.float_64.create(arena, -val.toFloat(f64, mod)),
+            80 => return Value.Tag.float_80.create(arena, -val.toFloat(f80, mod)),
+            128 => return Value.Tag.float_128.create(arena, -val.toFloat(f128, mod)),
             else => unreachable,
         }
     }
@@ -4320,19 +4063,16 @@ pub const Value = struct {
         arena: Allocator,
         mod: *Module,
     ) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try floatDivScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, target);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try floatDivScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatDivScalar(lhs, rhs, float_type, arena, target);
+        return floatDivScalar(lhs, rhs, float_type, arena, mod);
     }
 
     pub fn floatDivScalar(
@@ -4340,32 +4080,33 @@ pub const Value = struct {
         rhs: Value,
         float_type: Type,
         arena: Allocator,
-        target: Target,
+        mod: *const Module,
     ) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const lhs_val = lhs.toFloat(f16);
-                const rhs_val = rhs.toFloat(f16);
+                const lhs_val = lhs.toFloat(f16, mod);
+                const rhs_val = rhs.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
             },
             32 => {
-                const lhs_val = lhs.toFloat(f32);
-                const rhs_val = rhs.toFloat(f32);
+                const lhs_val = lhs.toFloat(f32, mod);
+                const rhs_val = rhs.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
             },
             64 => {
-                const lhs_val = lhs.toFloat(f64);
-                const rhs_val = rhs.toFloat(f64);
+                const lhs_val = lhs.toFloat(f64, mod);
+                const rhs_val = rhs.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
             },
             80 => {
-                const lhs_val = lhs.toFloat(f80);
-                const rhs_val = rhs.toFloat(f80);
+                const lhs_val = lhs.toFloat(f80, mod);
+                const rhs_val = rhs.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, lhs_val / rhs_val);
             },
             128 => {
-                const lhs_val = lhs.toFloat(f128);
-                const rhs_val = rhs.toFloat(f128);
+                const lhs_val = lhs.toFloat(f128, mod);
+                const rhs_val = rhs.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
             },
             else => unreachable,
@@ -4379,19 +4120,16 @@ pub const Value = struct {
         arena: Allocator,
         mod: *Module,
     ) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try floatDivFloorScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, target);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try floatDivFloorScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatDivFloorScalar(lhs, rhs, float_type, arena, target);
+        return floatDivFloorScalar(lhs, rhs, float_type, arena, mod);
     }
 
     pub fn floatDivFloorScalar(
@@ -4399,32 +4137,33 @@ pub const Value = struct {
         rhs: Value,
         float_type: Type,
         arena: Allocator,
-        target: Target,
+        mod: *const Module,
     ) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const lhs_val = lhs.toFloat(f16);
-                const rhs_val = rhs.toFloat(f16);
+                const lhs_val = lhs.toFloat(f16, mod);
+                const rhs_val = rhs.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val));
             },
             32 => {
-                const lhs_val = lhs.toFloat(f32);
-                const rhs_val = rhs.toFloat(f32);
+                const lhs_val = lhs.toFloat(f32, mod);
+                const rhs_val = rhs.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val));
             },
             64 => {
-                const lhs_val = lhs.toFloat(f64);
-                const rhs_val = rhs.toFloat(f64);
+                const lhs_val = lhs.toFloat(f64, mod);
+                const rhs_val = rhs.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val));
             },
             80 => {
-                const lhs_val = lhs.toFloat(f80);
-                const rhs_val = rhs.toFloat(f80);
+                const lhs_val = lhs.toFloat(f80, mod);
+                const rhs_val = rhs.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val));
             },
             128 => {
-                const lhs_val = lhs.toFloat(f128);
-                const rhs_val = rhs.toFloat(f128);
+                const lhs_val = lhs.toFloat(f128, mod);
+                const rhs_val = rhs.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val));
             },
             else => unreachable,
@@ -4438,19 +4177,16 @@ pub const Value = struct {
         arena: Allocator,
         mod: *Module,
     ) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try floatDivTruncScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, target);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try floatDivTruncScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatDivTruncScalar(lhs, rhs, float_type, arena, target);
+        return floatDivTruncScalar(lhs, rhs, float_type, arena, mod);
     }
 
     pub fn floatDivTruncScalar(
@@ -4458,32 +4194,33 @@ pub const Value = struct {
         rhs: Value,
         float_type: Type,
         arena: Allocator,
-        target: Target,
+        mod: *const Module,
     ) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const lhs_val = lhs.toFloat(f16);
-                const rhs_val = rhs.toFloat(f16);
+                const lhs_val = lhs.toFloat(f16, mod);
+                const rhs_val = rhs.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val));
             },
             32 => {
-                const lhs_val = lhs.toFloat(f32);
-                const rhs_val = rhs.toFloat(f32);
+                const lhs_val = lhs.toFloat(f32, mod);
+                const rhs_val = rhs.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val));
             },
             64 => {
-                const lhs_val = lhs.toFloat(f64);
-                const rhs_val = rhs.toFloat(f64);
+                const lhs_val = lhs.toFloat(f64, mod);
+                const rhs_val = rhs.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val));
             },
             80 => {
-                const lhs_val = lhs.toFloat(f80);
-                const rhs_val = rhs.toFloat(f80);
+                const lhs_val = lhs.toFloat(f80, mod);
+                const rhs_val = rhs.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val));
             },
             128 => {
-                const lhs_val = lhs.toFloat(f128);
-                const rhs_val = rhs.toFloat(f128);
+                const lhs_val = lhs.toFloat(f128, mod);
+                const rhs_val = rhs.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val));
             },
             else => unreachable,
@@ -4497,19 +4234,16 @@ pub const Value = struct {
         arena: Allocator,
         mod: *Module,
     ) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var lhs_buf: Value.ElemValueBuffer = undefined;
-                var rhs_buf: Value.ElemValueBuffer = undefined;
-                const lhs_elem = lhs.elemValueBuffer(mod, i, &lhs_buf);
-                const rhs_elem = rhs.elemValueBuffer(mod, i, &rhs_buf);
-                scalar.* = try floatMulScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, target);
+                const lhs_elem = try lhs.elemValue(mod, i);
+                const rhs_elem = try rhs.elemValue(mod, i);
+                scalar.* = try floatMulScalar(lhs_elem, rhs_elem, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floatMulScalar(lhs, rhs, float_type, arena, target);
+        return floatMulScalar(lhs, rhs, float_type, arena, mod);
     }
 
     pub fn floatMulScalar(
@@ -4517,32 +4251,33 @@ pub const Value = struct {
         rhs: Value,
         float_type: Type,
         arena: Allocator,
-        target: Target,
+        mod: *const Module,
     ) !Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const lhs_val = lhs.toFloat(f16);
-                const rhs_val = rhs.toFloat(f16);
+                const lhs_val = lhs.toFloat(f16, mod);
+                const rhs_val = rhs.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
             },
             32 => {
-                const lhs_val = lhs.toFloat(f32);
-                const rhs_val = rhs.toFloat(f32);
+                const lhs_val = lhs.toFloat(f32, mod);
+                const rhs_val = rhs.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
             },
             64 => {
-                const lhs_val = lhs.toFloat(f64);
-                const rhs_val = rhs.toFloat(f64);
+                const lhs_val = lhs.toFloat(f64, mod);
+                const rhs_val = rhs.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
             },
             80 => {
-                const lhs_val = lhs.toFloat(f80);
-                const rhs_val = rhs.toFloat(f80);
+                const lhs_val = lhs.toFloat(f80, mod);
+                const rhs_val = rhs.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, lhs_val * rhs_val);
             },
             128 => {
-                const lhs_val = lhs.toFloat(f128);
-                const rhs_val = rhs.toFloat(f128);
+                const lhs_val = lhs.toFloat(f128, mod);
+                const rhs_val = rhs.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
             },
             else => unreachable,
@@ -4550,39 +4285,38 @@ pub const Value = struct {
     }
 
     pub fn sqrt(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try sqrtScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try sqrtScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return sqrtScalar(val, float_type, arena, target);
+        return sqrtScalar(val, float_type, arena, mod);
     }
 
-    pub fn sqrtScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn sqrtScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @sqrt(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @sqrt(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @sqrt(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @sqrt(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @sqrt(f));
             },
             else => unreachable,
@@ -4590,39 +4324,38 @@ pub const Value = struct {
     }
 
     pub fn sin(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try sinScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try sinScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return sinScalar(val, float_type, arena, target);
+        return sinScalar(val, float_type, arena, mod);
     }
 
-    pub fn sinScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn sinScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @sin(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @sin(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @sin(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @sin(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @sin(f));
             },
             else => unreachable,
@@ -4630,39 +4363,38 @@ pub const Value = struct {
     }
 
     pub fn cos(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try cosScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try cosScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return cosScalar(val, float_type, arena, target);
+        return cosScalar(val, float_type, arena, mod);
     }
 
-    pub fn cosScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn cosScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @cos(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @cos(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @cos(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @cos(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @cos(f));
             },
             else => unreachable,
@@ -4670,39 +4402,38 @@ pub const Value = struct {
     }
 
     pub fn tan(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try tanScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try tanScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return tanScalar(val, float_type, arena, target);
+        return tanScalar(val, float_type, arena, mod);
     }
 
-    pub fn tanScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn tanScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @tan(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @tan(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @tan(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @tan(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @tan(f));
             },
             else => unreachable,
@@ -4710,39 +4441,38 @@ pub const Value = struct {
     }
 
     pub fn exp(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try expScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try expScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return expScalar(val, float_type, arena, target);
+        return expScalar(val, float_type, arena, mod);
     }
 
-    pub fn expScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn expScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @exp(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @exp(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @exp(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @exp(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @exp(f));
             },
             else => unreachable,
@@ -4750,39 +4480,38 @@ pub const Value = struct {
     }
 
     pub fn exp2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try exp2Scalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try exp2Scalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return exp2Scalar(val, float_type, arena, target);
+        return exp2Scalar(val, float_type, arena, mod);
     }
 
-    pub fn exp2Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn exp2Scalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @exp2(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @exp2(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @exp2(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @exp2(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @exp2(f));
             },
             else => unreachable,
@@ -4790,39 +4519,38 @@ pub const Value = struct {
     }
 
     pub fn log(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try logScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try logScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return logScalar(val, float_type, arena, target);
+        return logScalar(val, float_type, arena, mod);
     }
 
-    pub fn logScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn logScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @log(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @log(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @log(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @log(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @log(f));
             },
             else => unreachable,
@@ -4830,39 +4558,38 @@ pub const Value = struct {
     }
 
     pub fn log2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try log2Scalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try log2Scalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return log2Scalar(val, float_type, arena, target);
+        return log2Scalar(val, float_type, arena, mod);
     }
 
-    pub fn log2Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn log2Scalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @log2(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @log2(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @log2(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @log2(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @log2(f));
             },
             else => unreachable,
@@ -4870,39 +4597,38 @@ pub const Value = struct {
     }
 
     pub fn log10(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try log10Scalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try log10Scalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return log10Scalar(val, float_type, arena, target);
+        return log10Scalar(val, float_type, arena, mod);
     }
 
-    pub fn log10Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn log10Scalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @log10(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @log10(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @log10(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @log10(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @log10(f));
             },
             else => unreachable,
@@ -4910,39 +4636,38 @@ pub const Value = struct {
     }
 
     pub fn fabs(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try fabsScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try fabsScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return fabsScalar(val, float_type, arena, target);
+        return fabsScalar(val, float_type, arena, mod);
     }
 
-    pub fn fabsScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn fabsScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @fabs(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @fabs(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @fabs(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @fabs(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @fabs(f));
             },
             else => unreachable,
@@ -4950,39 +4675,38 @@ pub const Value = struct {
     }
 
     pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try floorScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try floorScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return floorScalar(val, float_type, arena, target);
+        return floorScalar(val, float_type, arena, mod);
     }
 
-    pub fn floorScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn floorScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @floor(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @floor(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @floor(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @floor(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @floor(f));
             },
             else => unreachable,
@@ -4990,39 +4714,38 @@ pub const Value = struct {
     }
 
     pub fn ceil(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try ceilScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try ceilScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return ceilScalar(val, float_type, arena, target);
+        return ceilScalar(val, float_type, arena, mod);
     }
 
-    pub fn ceilScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn ceilScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @ceil(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @ceil(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @ceil(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @ceil(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @ceil(f));
             },
             else => unreachable,
@@ -5030,39 +4753,38 @@ pub const Value = struct {
     }
 
     pub fn round(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try roundScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try roundScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return roundScalar(val, float_type, arena, target);
+        return roundScalar(val, float_type, arena, mod);
     }
 
-    pub fn roundScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn roundScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @round(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @round(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @round(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @round(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @round(f));
             },
             else => unreachable,
@@ -5070,39 +4792,38 @@ pub const Value = struct {
     }
 
     pub fn trunc(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var buf: Value.ElemValueBuffer = undefined;
-                const elem_val = val.elemValueBuffer(mod, i, &buf);
-                scalar.* = try truncScalar(elem_val, float_type.scalarType(mod), arena, target);
+                const elem_val = try val.elemValue(mod, i);
+                scalar.* = try truncScalar(elem_val, float_type.scalarType(mod), arena, mod);
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return truncScalar(val, float_type, arena, target);
+        return truncScalar(val, float_type, arena, mod);
     }
 
-    pub fn truncScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
+    pub fn truncScalar(val: Value, float_type: Type, arena: Allocator, mod: *const Module) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const f = val.toFloat(f16);
+                const f = val.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @trunc(f));
             },
             32 => {
-                const f = val.toFloat(f32);
+                const f = val.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @trunc(f));
             },
             64 => {
-                const f = val.toFloat(f64);
+                const f = val.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @trunc(f));
             },
             80 => {
-                const f = val.toFloat(f80);
+                const f = val.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @trunc(f));
             },
             128 => {
-                const f = val.toFloat(f128);
+                const f = val.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @trunc(f));
             },
             else => unreachable,
@@ -5117,28 +4838,24 @@ pub const Value = struct {
         arena: Allocator,
         mod: *Module,
     ) !Value {
-        const target = mod.getTarget();
         if (float_type.zigTypeTag(mod) == .Vector) {
             const result_data = try arena.alloc(Value, float_type.vectorLen(mod));
             for (result_data, 0..) |*scalar, i| {
-                var mulend1_buf: Value.ElemValueBuffer = undefined;
-                const mulend1_elem = mulend1.elemValueBuffer(mod, i, &mulend1_buf);
-                var mulend2_buf: Value.ElemValueBuffer = undefined;
-                const mulend2_elem = mulend2.elemValueBuffer(mod, i, &mulend2_buf);
-                var addend_buf: Value.ElemValueBuffer = undefined;
-                const addend_elem = addend.elemValueBuffer(mod, i, &addend_buf);
+                const mulend1_elem = try mulend1.elemValue(mod, i);
+                const mulend2_elem = try mulend2.elemValue(mod, i);
+                const addend_elem = try addend.elemValue(mod, i);
                 scalar.* = try mulAddScalar(
                     float_type.scalarType(mod),
                     mulend1_elem,
                     mulend2_elem,
                     addend_elem,
                     arena,
-                    target,
+                    mod,
                 );
             }
             return Value.Tag.aggregate.create(arena, result_data);
         }
-        return mulAddScalar(float_type, mulend1, mulend2, addend, arena, target);
+        return mulAddScalar(float_type, mulend1, mulend2, addend, arena, mod);
     }
 
     pub fn mulAddScalar(
@@ -5147,37 +4864,38 @@ pub const Value = struct {
         mulend2: Value,
         addend: Value,
         arena: Allocator,
-        target: Target,
+        mod: *const Module,
     ) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (float_type.floatBits(target)) {
             16 => {
-                const m1 = mulend1.toFloat(f16);
-                const m2 = mulend2.toFloat(f16);
-                const a = addend.toFloat(f16);
+                const m1 = mulend1.toFloat(f16, mod);
+                const m2 = mulend2.toFloat(f16, mod);
+                const a = addend.toFloat(f16, mod);
                 return Value.Tag.float_16.create(arena, @mulAdd(f16, m1, m2, a));
             },
             32 => {
-                const m1 = mulend1.toFloat(f32);
-                const m2 = mulend2.toFloat(f32);
-                const a = addend.toFloat(f32);
+                const m1 = mulend1.toFloat(f32, mod);
+                const m2 = mulend2.toFloat(f32, mod);
+                const a = addend.toFloat(f32, mod);
                 return Value.Tag.float_32.create(arena, @mulAdd(f32, m1, m2, a));
             },
             64 => {
-                const m1 = mulend1.toFloat(f64);
-                const m2 = mulend2.toFloat(f64);
-                const a = addend.toFloat(f64);
+                const m1 = mulend1.toFloat(f64, mod);
+                const m2 = mulend2.toFloat(f64, mod);
+                const a = addend.toFloat(f64, mod);
                 return Value.Tag.float_64.create(arena, @mulAdd(f64, m1, m2, a));
             },
             80 => {
-                const m1 = mulend1.toFloat(f80);
-                const m2 = mulend2.toFloat(f80);
-                const a = addend.toFloat(f80);
+                const m1 = mulend1.toFloat(f80, mod);
+                const m2 = mulend2.toFloat(f80, mod);
+                const a = addend.toFloat(f80, mod);
                 return Value.Tag.float_80.create(arena, @mulAdd(f80, m1, m2, a));
             },
             128 => {
-                const m1 = mulend1.toFloat(f128);
-                const m2 = mulend2.toFloat(f128);
-                const a = addend.toFloat(f128);
+                const m1 = mulend1.toFloat(f128, mod);
+                const m2 = mulend2.toFloat(f128, mod);
+                const a = addend.toFloat(f128, mod);
                 return Value.Tag.float_128.create(arena, @mulAdd(f128, m1, m2, a));
             },
             else => unreachable,
@@ -5186,13 +4904,14 @@ pub const Value = struct {
 
     /// If the value is represented in-memory as a series of bytes that all
     /// have the same value, return that byte value, otherwise null.
-    pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module, value_buffer: *Payload.U64) !?Value {
+    pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module) !?Value {
         const abi_size = std.math.cast(usize, ty.abiSize(mod)) orelse return null;
         assert(abi_size >= 1);
         const byte_buffer = try mod.gpa.alloc(u8, abi_size);
         defer mod.gpa.free(byte_buffer);
 
         writeToMemory(val, ty, mod, byte_buffer) catch |err| switch (err) {
+            error.OutOfMemory => return error.OutOfMemory,
             error.ReinterpretDeclRef => return null,
             // TODO: The writeToMemory function was originally created for the purpose
             // of comptime pointer casting. However, it is now additionally being used
@@ -5206,11 +4925,7 @@ pub const Value = struct {
         for (byte_buffer[1..]) |byte| {
             if (byte != first_byte) return null;
         }
-        value_buffer.* = .{
-            .base = .{ .tag = .int_u64 },
-            .data = first_byte,
-        };
-        return initPayload(&value_buffer.base);
+        return try mod.intValue(Type.u8, first_byte);
     }
 
     pub fn isGenericPoison(val: Value) bool {
@@ -5226,30 +4941,6 @@ pub const Value = struct {
             data: u32,
         };
 
-        pub const U64 = struct {
-            base: Payload,
-            data: u64,
-        };
-
-        pub const I64 = struct {
-            base: Payload,
-            data: i64,
-        };
-
-        pub const BigInt = struct {
-            base: Payload,
-            data: []const std.math.big.Limb,
-
-            pub fn asBigInt(self: BigInt) BigIntConst {
-                const positive = switch (self.base.tag) {
-                    .int_big_positive => true,
-                    .int_big_negative => false,
-                    else => unreachable,
-                };
-                return BigIntConst{ .limbs = self.data, .positive = positive };
-            }
-        };
-
         pub const Function = struct {
             base: Payload,
             data: *Module.Fn,
@@ -5452,12 +5143,9 @@ pub const Value = struct {
 
     pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
 
-    pub const zero = initTag(.zero);
-    pub const one = initTag(.one);
-    pub const negative_one: Value = .{
-        .ip_index = .none,
-        .legacy = .{ .ptr_otherwise = &negative_one_payload.base },
-    };
+    pub const zero: Value = .{ .ip_index = .zero, .legacy = undefined };
+    pub const one: Value = .{ .ip_index = .one, .legacy = undefined };
+    pub const negative_one: Value = .{ .ip_index = .negative_one, .legacy = undefined };
     pub const undef: Value = .{ .ip_index = .undef, .legacy = undefined };
     pub const @"void": Value = .{ .ip_index = .void_value, .legacy = undefined };
     pub const @"null": Value = .{ .ip_index = .null_value, .legacy = undefined };
@@ -5515,8 +5203,3 @@ pub const Value = struct {
         }
     }
 };
-
-var negative_one_payload: Value.Payload.I64 = .{
-    .base = .{ .tag = .int_i64 },
-    .data = -1,
-};
src/Zir.zig
@@ -2120,6 +2120,7 @@ pub const Inst = struct {
         zero_u8 = @enumToInt(InternPool.Index.zero_u8),
         one = @enumToInt(InternPool.Index.one),
         one_usize = @enumToInt(InternPool.Index.one_usize),
+        negative_one = @enumToInt(InternPool.Index.negative_one),
         calling_convention_c = @enumToInt(InternPool.Index.calling_convention_c),
         calling_convention_inline = @enumToInt(InternPool.Index.calling_convention_inline),
         void_value = @enumToInt(InternPool.Index.void_value),