authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-30 22:16:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-05 18:04:58-07:00
logf34deec4e23281ea51a27056ffba7955697e2141
tree625630b3f9e0137e4ce8f99cc324438ab79411dc
parent0266017b597e8fc74f41ec4eb78b1466751021c9

sema: rework comptime mutation

Split Value into an immutable value that always has an InternPool.Index representation, and a mutable value that is part of a new ComptimeMemory abstraction. Mainly, this deletes WipAnonDecl, deletes InternPool.Key.Ptr.Addr.MutDecl, and then updates all comptime-mutable allocations to happen via ComptimeMemory. This makes no comptime mutable memory escape from the Sema instance in which the comptime memory is allocated, reducing the amount of garbage in the global InternPool, and forces comptime memory mutations to be completely reworked in the compiler. This has sweeping implications to the codebase, and will require quite a few follow up changes to unbreak things

32 files changed, 4048 insertions(+), 4919 deletions(-)

src/Air.zig+8-2
......@@ -8,7 +8,7 @@ const builtin = @import("builtin");
88const assert = std.debug.assert;
99
1010const Air = @This();
11const Value = @import("value.zig").Value;
11const Value = @import("Value.zig");
1212const Type = @import("type.zig").Type;
1313const InternPool = @import("InternPool.zig");
1414const Module = @import("Module.zig");
......@@ -986,6 +986,12 @@ pub const Inst = struct {
986986 empty_struct = @intFromEnum(InternPool.Index.empty_struct),
987987 generic_poison = @intFromEnum(InternPool.Index.generic_poison),
988988
989 /// This Ref does not correspond to any AIR instruction.
990 /// It is a special value recognized only by Sema.
991 /// It indicates the value is mutable comptime memory, and represented
992 /// via the comptime_memory field of Sema. This value never occurs
993 /// in AIR which is emitted to backends.
994 mutable_comptime = @intFromEnum(InternPool.Index.mutable_comptime),
989995 /// This Ref does not correspond to any AIR instruction or constant
990996 /// value. It is used to handle argument types of var args functions.
991997 var_args_param_type = @intFromEnum(InternPool.Index.var_args_param_type),
......@@ -1095,7 +1101,7 @@ pub const Inst = struct {
10951101 inferred_alloc: InferredAlloc,
10961102
10971103 pub const InferredAllocComptime = struct {
1098 decl_index: InternPool.DeclIndex,
1104 comptime_memory_value_index: @import("Sema/ComptimeMemory.zig").Value.Index,
10991105 alignment: InternPool.Alignment,
11001106 is_const: bool,
11011107 };
src/Compilation.zig+1-1
......@@ -11,7 +11,7 @@ const ThreadPool = std.Thread.Pool;
1111const WaitGroup = std.Thread.WaitGroup;
1212const ErrorBundle = std.zig.ErrorBundle;
1313
14const Value = @import("value.zig").Value;
14const Value = @import("Value.zig");
1515const Type = @import("type.zig").Type;
1616const target_util = @import("target.zig");
1717const Package = @import("Package.zig");
src/InternPool.zig+5-61
......@@ -98,6 +98,7 @@ const InternPool = @This();
9898const Module = @import("Module.zig");
9999const Zcu = Module;
100100const Zir = @import("Zir.zig");
101const Air = @import("Air.zig");
101102
102103const KeyAdapter = struct {
103104 intern_pool: *const InternPool,
......@@ -132,16 +133,6 @@ pub const MapIndex = enum(u32) {
132133 }
133134};
134135
135pub const RuntimeIndex = enum(u32) {
136 zero = 0,
137 comptime_field_ptr = std.math.maxInt(u32),
138 _,
139
140 pub fn increment(ri: *RuntimeIndex) void {
141 ri.* = @as(RuntimeIndex, @enumFromInt(@intFromEnum(ri.*) + 1));
142 }
143};
144
145136pub const DeclIndex = enum(u32) {
146137 _,
147138
......@@ -1203,7 +1194,6 @@ pub const Key = union(enum) {
12031194 const Tag = @typeInfo(Addr).Union.tag_type.?;
12041195
12051196 decl: DeclIndex,
1206 mut_decl: MutDecl,
12071197 anon_decl: AnonDecl,
12081198 comptime_field: Index,
12091199 int: Index,
......@@ -1212,10 +1202,6 @@ pub const Key = union(enum) {
12121202 elem: BaseIndex,
12131203 field: BaseIndex,
12141204
1215 pub const MutDecl = struct {
1216 decl: DeclIndex,
1217 runtime_index: RuntimeIndex,
1218 };
12191205 pub const BaseIndex = struct {
12201206 base: Index,
12211207 index: u64,
......@@ -1373,11 +1359,6 @@ pub const Key = union(enum) {
13731359 return switch (ptr.addr) {
13741360 .decl => |x| Hash.hash(seed2, common ++ asBytes(&x)),
13751361
1376 .mut_decl => |x| Hash.hash(
1377 seed2,
1378 common ++ asBytes(&x.decl) ++ asBytes(&x.runtime_index),
1379 ),
1380
13811362 .anon_decl => |x| Hash.hash(seed2, common ++ asBytes(&x)),
13821363
13831364 .int,
......@@ -1651,7 +1632,6 @@ pub const Key = union(enum) {
16511632
16521633 return switch (a_info.addr) {
16531634 .decl => |a_decl| a_decl == b_info.addr.decl,
1654 .mut_decl => |a_mut_decl| std.meta.eql(a_mut_decl, b_info.addr.mut_decl),
16551635 .anon_decl => |ad| ad.val == b_info.addr.anon_decl.val and
16561636 ad.orig_ty == b_info.addr.anon_decl.orig_ty,
16571637 .int => |a_int| a_int == b_info.addr.int,
......@@ -2171,6 +2151,7 @@ pub const Index = enum(u32) {
21712151 generic_poison,
21722152
21732153 /// Used by Air/Sema only.
2154 mutable_comptime = std.math.maxInt(u32) - 2,
21742155 var_args_param_type = std.math.maxInt(u32) - 1,
21752156 none = std.math.maxInt(u32),
21762157
......@@ -2280,7 +2261,6 @@ pub const Index = enum(u32) {
22802261 undef: DataIsIndex,
22812262 simple_value: struct { data: SimpleValue },
22822263 ptr_decl: struct { data: *PtrDecl },
2283 ptr_mut_decl: struct { data: *PtrMutDecl },
22842264 ptr_anon_decl: struct { data: *PtrAnonDecl },
22852265 ptr_anon_decl_aligned: struct { data: *PtrAnonDeclAligned },
22862266 ptr_comptime_field: struct { data: *PtrComptimeField },
......@@ -2732,9 +2712,6 @@ pub const Tag = enum(u8) {
27322712 /// A pointer to a decl.
27332713 /// data is extra index of `PtrDecl`, which contains the type and address.
27342714 ptr_decl,
2735 /// A pointer to a decl that can be mutated at comptime.
2736 /// data is extra index of `PtrMutDecl`, which contains the type and address.
2737 ptr_mut_decl,
27382715 /// A pointer to an anonymous decl.
27392716 /// data is extra index of `PtrAnonDecl`, which contains the pointer type and decl value.
27402717 /// The alignment of the anonymous decl is communicated via the pointer type.
......@@ -2939,7 +2916,6 @@ pub const Tag = enum(u8) {
29392916 .undef => unreachable,
29402917 .simple_value => unreachable,
29412918 .ptr_decl => PtrDecl,
2942 .ptr_mut_decl => PtrMutDecl,
29432919 .ptr_anon_decl => PtrAnonDecl,
29442920 .ptr_anon_decl_aligned => PtrAnonDeclAligned,
29452921 .ptr_comptime_field => PtrComptimeField,
......@@ -3570,12 +3546,6 @@ pub const PtrAnonDeclAligned = struct {
35703546 orig_ty: Index,
35713547};
35723548
3573pub const PtrMutDecl = struct {
3574 ty: Index,
3575 decl: DeclIndex,
3576 runtime_index: RuntimeIndex,
3577};
3578
35793549pub const PtrComptimeField = struct {
35803550 ty: Index,
35813551 field_val: Index,
......@@ -3910,16 +3880,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
39103880 .addr = .{ .decl = info.decl },
39113881 } };
39123882 },
3913 .ptr_mut_decl => {
3914 const info = ip.extraData(PtrMutDecl, data);
3915 return .{ .ptr = .{
3916 .ty = info.ty,
3917 .addr = .{ .mut_decl = .{
3918 .decl = info.decl,
3919 .runtime_index = info.runtime_index,
3920 } },
3921 } };
3922 },
39233883 .ptr_anon_decl => {
39243884 const info = ip.extraData(PtrAnonDecl, data);
39253885 return .{ .ptr = .{
......@@ -4712,14 +4672,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
47124672 .decl = decl,
47134673 }),
47144674 }),
4715 .mut_decl => |mut_decl| ip.items.appendAssumeCapacity(.{
4716 .tag = .ptr_mut_decl,
4717 .data = try ip.addExtra(gpa, PtrMutDecl{
4718 .ty = ptr.ty,
4719 .decl = mut_decl.decl,
4720 .runtime_index = mut_decl.runtime_index,
4721 }),
4722 }),
47234675 .anon_decl => |anon_decl| ip.items.appendAssumeCapacity(
47244676 if (ptrsHaveSameAlignment(ip, ptr.ty, ptr_type, anon_decl.orig_ty)) .{
47254677 .tag = .ptr_anon_decl,
......@@ -6147,7 +6099,7 @@ fn finishFuncInstance(
61476099 .has_tv = true,
61486100 .owns_tv = true,
61496101 .ty = @import("type.zig").Type.fromInterned(func_ty),
6150 .val = @import("value.zig").Value.fromInterned(func_index),
6102 .val = @import("Value.zig").fromInterned(func_index),
61516103 .alignment = .none,
61526104 .@"linksection" = section,
61536105 .@"addrspace" = fn_owner_decl.@"addrspace",
......@@ -6501,7 +6453,6 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
65016453 OptionalNamespaceIndex,
65026454 MapIndex,
65036455 OptionalMapIndex,
6504 RuntimeIndex,
65056456 String,
65066457 NullTerminatedString,
65076458 OptionalNullTerminatedString,
......@@ -6577,7 +6528,6 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
65776528 OptionalNamespaceIndex,
65786529 MapIndex,
65796530 OptionalMapIndex,
6580 RuntimeIndex,
65816531 String,
65826532 NullTerminatedString,
65836533 OptionalNullTerminatedString,
......@@ -7344,7 +7294,6 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
73447294 .simple_type => 0,
73457295 .simple_value => 0,
73467296 .ptr_decl => @sizeOf(PtrDecl),
7347 .ptr_mut_decl => @sizeOf(PtrMutDecl),
73487297 .ptr_anon_decl => @sizeOf(PtrAnonDecl),
73497298 .ptr_anon_decl_aligned => @sizeOf(PtrAnonDeclAligned),
73507299 .ptr_comptime_field => @sizeOf(PtrComptimeField),
......@@ -7474,7 +7423,6 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {
74747423 .type_function,
74757424 .undef,
74767425 .ptr_decl,
7477 .ptr_mut_decl,
74787426 .ptr_anon_decl,
74797427 .ptr_anon_decl_aligned,
74807428 .ptr_comptime_field,
......@@ -7887,7 +7835,6 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
78877835 .simple_value => unreachable, // handled via Index above
78887836
78897837 inline .ptr_decl,
7890 .ptr_mut_decl,
78917838 .ptr_anon_decl,
78927839 .ptr_anon_decl_aligned,
78937840 .ptr_comptime_field,
......@@ -7951,6 +7898,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
79517898 .memoized_call => unreachable,
79527899 },
79537900
7901 .mutable_comptime => unreachable,
79547902 .var_args_param_type => unreachable,
79557903 .none => unreachable,
79567904 };
......@@ -8019,9 +7967,7 @@ pub fn getBackingDecl(ip: *const InternPool, val: Index) OptionalDeclIndex {
80197967 var base = @intFromEnum(val);
80207968 while (true) {
80217969 switch (ip.items.items(.tag)[base]) {
8022 inline .ptr_decl,
8023 .ptr_mut_decl,
8024 => |tag| return @enumFromInt(ip.extra.items[
7970 .ptr_decl => |tag| return @enumFromInt(ip.extra.items[
80257971 ip.items.items(.data)[base] + std.meta.fieldIndex(tag.Payload(), "decl").?
80267972 ]),
80277973 inline .ptr_eu_payload,
......@@ -8044,7 +7990,6 @@ pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.Addr.Tag {
80447990 while (true) {
80457991 switch (ip.items.items(.tag)[base]) {
80467992 .ptr_decl => return .decl,
8047 .ptr_mut_decl => return .mut_decl,
80487993 .ptr_anon_decl, .ptr_anon_decl_aligned => return .anon_decl,
80497994 .ptr_comptime_field => return .comptime_field,
80507995 .ptr_int => return .int,
......@@ -8219,7 +8164,6 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois
82198164 .undef,
82208165 .simple_value,
82218166 .ptr_decl,
8222 .ptr_mut_decl,
82238167 .ptr_anon_decl,
82248168 .ptr_anon_decl_aligned,
82258169 .ptr_comptime_field,
src/Module.zig+11-24
......@@ -19,7 +19,7 @@ const Module = Zcu;
1919const Zcu = @This();
2020const Compilation = @import("Compilation.zig");
2121const Cache = std.Build.Cache;
22const Value = @import("value.zig").Value;
22const Value = @import("Value.zig");
2323const Type = @import("type.zig").Type;
2424const TypedValue = @import("TypedValue.zig");
2525const Package = @import("Package.zig");
......@@ -3416,8 +3416,8 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
34163416 defer sema_arena.deinit();
34173417 const sema_arena_allocator = sema_arena.allocator();
34183418
3419 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
3420 defer comptime_mutable_decls.deinit();
3419 var comptime_memory: Sema.ComptimeMemory = .{};
3420 defer comptime_memory.deinit(gpa);
34213421
34223422 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa);
34233423 defer comptime_err_ret_trace.deinit();
......@@ -3434,7 +3434,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
34343434 .fn_ret_ty = Type.void,
34353435 .fn_ret_ty_ies = null,
34363436 .owner_func_index = .none,
3437 .comptime_mutable_decls = &comptime_mutable_decls,
3437 .comptime_memory = &comptime_memory,
34383438 .comptime_err_ret_trace = &comptime_err_ret_trace,
34393439 };
34403440 defer sema.deinit();
......@@ -3448,10 +3448,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
34483448 };
34493449 // TODO: figure out InternPool removals for incremental compilation
34503450 //errdefer ip.remove(struct_ty);
3451 for (comptime_mutable_decls.items) |decl_index| {
3452 const decl = mod.declPtr(decl_index);
3453 _ = try decl.internValue(mod);
3454 }
34553451
34563452 new_namespace.ty = Type.fromInterned(struct_ty);
34573453 new_decl.val = Value.fromInterned(struct_ty);
......@@ -3540,8 +3536,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
35403536 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
35413537 defer analysis_arena.deinit();
35423538
3543 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
3544 defer comptime_mutable_decls.deinit();
3539 var comptime_memory: Sema.ComptimeMemory = .{};
3540 defer comptime_memory.deinit(gpa);
35453541
35463542 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa);
35473543 defer comptime_err_ret_trace.deinit();
......@@ -3558,7 +3554,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
35583554 .fn_ret_ty = Type.void,
35593555 .fn_ret_ty_ies = null,
35603556 .owner_func_index = .none,
3561 .comptime_mutable_decls = &comptime_mutable_decls,
3557 .comptime_memory = &comptime_memory,
35623558 .comptime_err_ret_trace = &comptime_err_ret_trace,
35633559 .builtin_type_target_index = builtin_type_target_index,
35643560 };
......@@ -3584,10 +3580,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
35843580 // We'll do some other bits with the Sema. Clear the type target index just
35853581 // in case they analyze any type.
35863582 sema.builtin_type_target_index = .none;
3587 for (comptime_mutable_decls.items) |ct_decl_index| {
3588 const ct_decl = mod.declPtr(ct_decl_index);
3589 _ = try ct_decl.internValue(mod);
3590 }
35913583 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = 0 };
35923584 const section_src: LazySrcLoc = .{ .node_offset_var_decl_section = 0 };
35933585 const address_space_src: LazySrcLoc = .{ .node_offset_var_decl_addrspace = 0 };
......@@ -4362,8 +4354,8 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
43624354 const decl_index = func.owner_decl;
43634355 const decl = mod.declPtr(decl_index);
43644356
4365 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
4366 defer comptime_mutable_decls.deinit();
4357 var comptime_memory: Sema.ComptimeMemory = .{};
4358 defer comptime_memory.deinit(gpa);
43674359
43684360 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa);
43694361 defer comptime_err_ret_trace.deinit();
......@@ -4389,7 +4381,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
43894381 .fn_ret_ty_ies = null,
43904382 .owner_func_index = func_index,
43914383 .branch_quota = @max(func.branchQuota(ip).*, Sema.default_branch_quota),
4392 .comptime_mutable_decls = &comptime_mutable_decls,
4384 .comptime_memory = &comptime_memory,
43934385 .comptime_err_ret_trace = &comptime_err_ret_trace,
43944386 };
43954387 defer sema.deinit();
......@@ -4522,11 +4514,6 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
45224514 };
45234515 }
45244516
4525 for (comptime_mutable_decls.items) |ct_decl_index| {
4526 const ct_decl = mod.declPtr(ct_decl_index);
4527 _ = try ct_decl.internValue(mod);
4528 }
4529
45304517 // Copy the block into place and mark that as the main block.
45314518 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
45324519 inner_block.instructions.items.len);
......@@ -5213,6 +5200,7 @@ pub fn populateTestFunctions(
52135200 mod: *Module,
52145201 main_progress_node: *std.Progress.Node,
52155202) !void {
5203 if (true) @panic("TODO implement populateTestFunctions");
52165204 const gpa = mod.gpa;
52175205 const ip = &mod.intern_pool;
52185206 const builtin_mod = mod.root_mod.getBuiltinDependency();
......@@ -5436,7 +5424,6 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {
54365424 .ptr => |ptr| switch (ptr.addr) {
54375425 .decl => |decl| try mod.markDeclIndexAlive(decl),
54385426 .anon_decl => {},
5439 .mut_decl => |mut_decl| try mod.markDeclIndexAlive(mut_decl.decl),
54405427 .int, .comptime_field => {},
54415428 .eu_payload, .opt_payload => |parent| try mod.markReferencedDeclsAlive(Value.fromInterned(parent)),
54425429 .elem, .field => |base_index| try mod.markReferencedDeclsAlive(Value.fromInterned(base_index.base)),
src/RangeSet.zig+1-1
......@@ -4,7 +4,7 @@ const Order = std.math.Order;
44
55const InternPool = @import("InternPool.zig");
66const Type = @import("type.zig").Type;
7const Value = @import("value.zig").Value;
7const Value = @import("Value.zig");
88const Module = @import("Module.zig");
99const RangeSet = @This();
1010const SwitchProngSrc = @import("Module.zig").SwitchProngSrc;
src/Sema.zig+80-712
......@@ -16,6 +16,8 @@ air_instructions: std.MultiArrayList(Air.Inst) = .{},
1616air_extra: std.ArrayListUnmanaged(u32) = .{},
1717/// Maps ZIR to AIR.
1818inst_map: InstMap = .{},
19/// Comptime-mutable memory. This is inherited by child Sema instances.
20comptime_memory: *ComptimeMemory,
1921/// When analyzing an inline function call, owner_decl is the Decl of the caller
2022/// and `src_decl` of `Block` is the `Decl` of the callee.
2123/// This `Decl` owns the arena memory of this `Sema`.
......@@ -96,14 +98,6 @@ no_partial_func_ty: bool = false,
9698/// here so the values can be dropped without any cleanup.
9799unresolved_inferred_allocs: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .{},
98100
99/// Indices of comptime-mutable decls created by this Sema. These decls' values
100/// should be interned after analysis completes, as they may refer to memory in
101/// the Sema arena.
102/// TODO: this is a workaround for memory bugs triggered by the removal of
103/// Decl.value_arena. A better solution needs to be found. Probably this will
104/// involve transitioning comptime-mutable memory away from using Decls at all.
105comptime_mutable_decls: *std.ArrayList(InternPool.DeclIndex),
106
107101/// This is populated when `@setAlignStack` occurs so that if there is a duplicate
108102/// one encountered, the conflicting source location can be shown.
109103prev_stack_alignment_src: ?LazySrcLoc = null,
......@@ -128,9 +122,11 @@ base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .{},
128122/// Backed by gpa.
129123maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .{},
130124
125const ComptimeMemory = @import("Sema/ComptimeMemory.zig");
126
131127const MaybeComptimeAlloc = struct {
132128 /// The runtime index of the `alloc` instruction.
133 runtime_index: Value.RuntimeIndex,
129 runtime_index: ComptimeMemory.RuntimeIndex,
134130 /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to
135131 /// RLS, a single comptime-known allocation may have arbitrarily many stores.
136132 /// This may also contain `set_union_tag` instructions.
......@@ -149,7 +145,8 @@ const assert = std.debug.assert;
149145const log = std.log.scoped(.sema);
150146
151147const Sema = @This();
152const Value = @import("value.zig").Value;
148const ConstValue = @import("Value.zig");
149const MutValue = ComptimeMemory.Value;
153150const Type = @import("type.zig").Type;
154151const TypedValue = @import("TypedValue.zig");
155152const Air = @import("Air.zig");
......@@ -349,7 +346,7 @@ pub const Block = struct {
349346 src_decl: InternPool.DeclIndex,
350347 /// Non zero if a non-inline loop or a runtime conditional have been encountered.
351348 /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index.
352 runtime_index: Value.RuntimeIndex = .zero,
349 runtime_index: ComptimeMemory.RuntimeIndex = .zero,
353350 inline_block: Zir.Inst.OptionalIndex = .none,
354351
355352 comptime_reason: ?*const ComptimeReason = null,
......@@ -784,45 +781,6 @@ pub const Block = struct {
784781 _ = try block.addNoOp(.unreach);
785782 }
786783 }
787
788 pub fn ownerModule(block: Block) *Package.Module {
789 const zcu = block.sema.mod;
790 return zcu.namespacePtr(block.namespace).file_scope.mod;
791 }
792
793 pub fn startAnonDecl(block: *Block) !WipAnonDecl {
794 return WipAnonDecl{
795 .block = block,
796 .finished = false,
797 };
798 }
799
800 pub const WipAnonDecl = struct {
801 block: *Block,
802 finished: bool,
803
804 pub fn deinit(wad: *WipAnonDecl) void {
805 wad.* = undefined;
806 }
807
808 /// `alignment` value of 0 means to use ABI alignment.
809 pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: Alignment) !InternPool.DeclIndex {
810 const sema = wad.block.sema;
811 // Do this ahead of time because `createAnonymousDecl` depends on calling
812 // `type.hasRuntimeBits()`.
813 _ = try sema.typeHasRuntimeBits(ty);
814 const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{
815 .ty = ty,
816 .val = val,
817 });
818 const new_decl = sema.mod.declPtr(new_decl_index);
819 new_decl.alignment = alignment;
820 errdefer sema.mod.abortAnonDecl(new_decl_index);
821 wad.finished = true;
822 try sema.mod.finalizeAnonDecl(new_decl_index);
823 return new_decl_index;
824 }
825 };
826784};
827785
828786const LabeledBlock = struct {
......@@ -2117,7 +2075,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
21172075/// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
21182076/// InternPool key `variable` is considered a runtime value.
21192077/// Generic poison causes `error.GenericPoison` to be returned.
2120fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
2078fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?MutValue {
21212079 const val = (try sema.resolveValueAllowVariables(inst)) orelse return null;
21222080 if (val.isGenericPoison()) return error.GenericPoison;
21232081 if (sema.mod.intern_pool.isVariable(val.toIntern())) return null;
......@@ -2176,7 +2134,7 @@ fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value
21762134fn resolveValueIntable(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
21772135 const val = (try sema.resolveValue(inst)) orelse return null;
21782136 if (sema.mod.intern_pool.getBackingAddrTag(val.toIntern())) |addr| switch (addr) {
2179 .decl, .anon_decl, .mut_decl, .comptime_field => return null,
2137 .decl, .anon_decl, .comptime_field => return null,
21802138 .int => {},
21812139 .eu_payload, .opt_payload, .elem, .field => unreachable,
21822140 };
......@@ -3699,6 +3657,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
36993657/// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`.
37003658fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index {
37013659 const mod = sema.mod;
3660 const gpa = sema.gpa;
37023661
37033662 const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
37043663 const ptr_info = alloc_ty.ptrInfo(mod);
......@@ -3734,23 +3693,12 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re
37343693
37353694 // The simple strategy failed: we must create a mutable comptime alloc and
37363695 // perform all of the runtime store operations at comptime.
3737
3738 var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl
3739 defer anon_decl.deinit();
3740 const decl_index = try anon_decl.finish(elem_ty, try mod.undefValue(elem_ty), ptr_info.flags.alignment);
3741
3742 const decl_ptr = try mod.intern(.{ .ptr = .{
3743 .ty = alloc_ty.toIntern(),
3744 .addr = .{ .mut_decl = .{
3745 .decl = decl_index,
3746 .runtime_index = block.runtime_index,
3747 } },
3748 } });
3696 const comptime_ptr = try sema.comptime_memory.allocate(gpa, alloc_ty, block.runtime_index);
37493697
37503698 // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the mut decl.
3751 var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena);
3699 var ptr_mapping = std.AutoHashMap(Air.Inst.Index, ComptimeMemory.Value).init(sema.arena);
37523700 try ptr_mapping.ensureTotalCapacity(@intCast(stores.len));
3753 ptr_mapping.putAssumeCapacity(alloc_inst, decl_ptr);
3701 ptr_mapping.putAssumeCapacity(alloc_inst, comptime_ptr);
37543702
37553703 var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len);
37563704 for (stores) |store_inst| {
......@@ -3888,7 +3836,7 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re
38883836 }
38893837
38903838 // The value is finalized - load it!
3891 const val = (try sema.pointerDeref(block, .unneeded, Value.fromInterned(decl_ptr), alloc_ty)).?.toIntern();
3839 const val = (try sema.pointerDeref(block, .unneeded, comptime_ptr.toValue(), alloc_ty)).?.toIntern();
38923840 return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value);
38933841}
38943842
......@@ -5446,10 +5394,18 @@ fn storeToInferredAllocComptime(
54465394 // There will be only one store_to_inferred_ptr because we are running at comptime.
54475395 // The alloc will turn into a Decl.
54485396 if (try sema.resolveValue(operand)) |operand_val| {
5449 var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl
5450 defer anon_decl.deinit();
5451 iac.decl_index = try anon_decl.finish(operand_ty, operand_val, iac.alignment);
5452 try sema.comptime_mutable_decls.append(iac.decl_index);
5397 const gpa = sema.gpa;
5398 const ptr_ty = try sema.ptrType(.{
5399 .child = operand_ty.toIntern(),
5400 .flags = .{
5401 .alignment = iac.alignment,
5402 .is_const = iac.is_const,
5403 .address_space = .generic,
5404 },
5405 });
5406 const comptime_ptr = try sema.comptime_memory.allocate(gpa, ptr_ty, block.runtime_index);
5407 iac.comptime_memory_value_index = try sema.comptime_memory.addValue(gpa, comptime_ptr);
5408 sema.comptime_memory.store(comptime_ptr, operand_val);
54535409 return;
54545410 }
54555411
......@@ -7938,7 +7894,7 @@ fn instantiateGenericCall(
79387894 .generic_call_decl = block.src_decl.toOptional(),
79397895 .branch_quota = sema.branch_quota,
79407896 .branch_count = sema.branch_count,
7941 .comptime_mutable_decls = sema.comptime_mutable_decls,
7897 .comptime_memory = sema.comptime_memory,
79427898 .comptime_err_ret_trace = sema.comptime_err_ret_trace,
79437899 };
79447900 defer child_sema.deinit();
......@@ -30465,7 +30421,6 @@ fn storePtrVal(
3046530421}
3046630422
3046730423const ComptimePtrMutationKit = struct {
30468 mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
3046930424 pointee: union(enum) {
3047030425 opv,
3047130426 /// The pointer type matches the actual comptime Value so a direct
......@@ -30500,586 +30455,17 @@ fn beginComptimePtrMutation(
3050030455 ptr_val: Value,
3050130456 ptr_elem_ty: Type,
3050230457) CompileError!ComptimePtrMutationKit {
30503 const mod = sema.mod;
30504 const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
30505 switch (ptr.addr) {
30506 .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already
30507 .mut_decl => |mut_decl| {
30508 const decl = mod.declPtr(mut_decl.decl);
30509 return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl);
30510 },
30511 .comptime_field => |comptime_field| {
30512 const duped = try sema.arena.create(Value);
30513 duped.* = Value.fromInterned(comptime_field);
30514 return sema.beginComptimePtrMutationInner(block, src, Type.fromInterned(mod.intern_pool.typeOf(comptime_field)), duped, ptr_elem_ty, .{
30515 .decl = undefined,
30516 .runtime_index = .comptime_field_ptr,
30517 });
30518 },
30519 .eu_payload => |eu_ptr| {
30520 const eu_ty = Type.fromInterned(mod.intern_pool.typeOf(eu_ptr)).childType(mod);
30521 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(eu_ptr), eu_ty);
30522 switch (parent.pointee) {
30523 .opv => unreachable,
30524 .direct => |val_ptr| {
30525 const payload_ty = parent.ty.errorUnionPayload(mod);
30526 if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) {
30527 return ComptimePtrMutationKit{
30528 .mut_decl = parent.mut_decl,
30529 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
30530 .ty = payload_ty,
30531 };
30532 } else {
30533 // An error union has been initialized to undefined at comptime and now we
30534 // are for the first time setting the payload. We must change the
30535 // representation of the error union from `undef` to `opt_payload`.
30536
30537 const payload = try sema.arena.create(Value.Payload.SubValue);
30538 payload.* = .{
30539 .base = .{ .tag = .eu_payload },
30540 .data = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),
30541 };
30542
30543 val_ptr.* = Value.initPayload(&payload.base);
30544
30545 return ComptimePtrMutationKit{
30546 .mut_decl = parent.mut_decl,
30547 .pointee = .{ .direct = &payload.data },
30548 .ty = payload_ty,
30549 };
30550 }
30551 },
30552 .bad_decl_ty, .bad_ptr_ty => return parent,
30553 // Even though the parent value type has well-defined memory layout, our
30554 // pointer type does not.
30555 .reinterpret => return ComptimePtrMutationKit{
30556 .mut_decl = parent.mut_decl,
30557 .pointee = .bad_ptr_ty,
30558 .ty = eu_ty,
30559 },
30560 }
30561 },
30562 .opt_payload => |opt_ptr| {
30563 const opt_ty = Type.fromInterned(mod.intern_pool.typeOf(opt_ptr)).childType(mod);
30564 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(opt_ptr), opt_ty);
30565 switch (parent.pointee) {
30566 .opv => unreachable,
30567 .direct => |val_ptr| {
30568 const payload_ty = parent.ty.optionalChild(mod);
30569 switch (val_ptr.ip_index) {
30570 .none => return ComptimePtrMutationKit{
30571 .mut_decl = parent.mut_decl,
30572 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
30573 .ty = payload_ty,
30574 },
30575 else => {
30576 const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) {
30577 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }),
30578 .opt => |opt| switch (opt.val) {
30579 .none => try mod.intern(.{ .undef = payload_ty.toIntern() }),
30580 else => |payload| payload,
30581 },
30582 else => unreachable,
30583 };
30584
30585 // An optional has been initialized to undefined at comptime and now we
30586 // are for the first time setting the payload. We must change the
30587 // representation of the optional from `undef` to `opt_payload`.
30588
30589 const payload = try sema.arena.create(Value.Payload.SubValue);
30590 payload.* = .{
30591 .base = .{ .tag = .opt_payload },
30592 .data = Value.fromInterned(payload_val),
30593 };
30594
30595 val_ptr.* = Value.initPayload(&payload.base);
30596
30597 return ComptimePtrMutationKit{
30598 .mut_decl = parent.mut_decl,
30599 .pointee = .{ .direct = &payload.data },
30600 .ty = payload_ty,
30601 };
30602 },
30603 }
30604 },
30605 .bad_decl_ty, .bad_ptr_ty => return parent,
30606 // Even though the parent value type has well-defined memory layout, our
30607 // pointer type does not.
30608 .reinterpret => return ComptimePtrMutationKit{
30609 .mut_decl = parent.mut_decl,
30610 .pointee = .bad_ptr_ty,
30611 .ty = opt_ty,
30612 },
30613 }
30614 },
30615 .elem => |elem_ptr| {
30616 const base_elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem_ptr.base)).elemType2(mod);
30617 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(elem_ptr.base), base_elem_ty);
30618
30619 switch (parent.pointee) {
30620 .opv => unreachable,
30621 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) {
30622 .Array, .Vector => {
30623 const elem_ty = parent.ty.childType(mod);
30624 const check_len = parent.ty.arrayLenIncludingSentinel(mod);
30625 if ((try sema.typeHasOnePossibleValue(ptr_elem_ty)) != null) {
30626 if (elem_ptr.index > check_len) {
30627 // TODO have the parent include the decl so we can say "declared here"
30628 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
30629 elem_ptr.index, check_len,
30630 });
30631 }
30632 return .{
30633 .mut_decl = parent.mut_decl,
30634 .pointee = .opv,
30635 .ty = elem_ty,
30636 };
30637 }
30638 if (elem_ptr.index >= check_len) {
30639 // TODO have the parent include the decl so we can say "declared here"
30640 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
30641 elem_ptr.index, check_len,
30642 });
30643 }
30644
30645 // We might have a pointer to multiple elements of the array (e.g. a pointer
30646 // to a sub-array). In this case, we just have to reinterpret the relevant
30647 // bytes of the whole array rather than any single element.
30648 reinterp_multi_elem: {
30649 if (try sema.typeRequiresComptime(base_elem_ty)) break :reinterp_multi_elem;
30650 if (try sema.typeRequiresComptime(ptr_elem_ty)) break :reinterp_multi_elem;
30651
30652 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
30653 if (elem_abi_size_u64 >= try sema.typeAbiSize(ptr_elem_ty)) break :reinterp_multi_elem;
30654
30655 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
30656 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
30657 return .{
30658 .mut_decl = parent.mut_decl,
30659 .pointee = .{ .reinterpret = .{
30660 .val_ptr = val_ptr,
30661 .byte_offset = elem_abi_size * elem_idx,
30662 } },
30663 .ty = parent.ty,
30664 };
30665 }
30666
30667 switch (val_ptr.ip_index) {
30668 .none => switch (val_ptr.tag()) {
30669 .bytes => {
30670 // An array is memory-optimized to store a slice of bytes, but we are about
30671 // to modify an individual field and the representation has to change.
30672 // If we wanted to avoid this, there would need to be special detection
30673 // elsewhere to identify when writing a value to an array element that is stored
30674 // using the `bytes` tag, and handle it without making a call to this function.
30675 const arena = mod.tmp_hack_arena.allocator();
30676
30677 const bytes = val_ptr.castTag(.bytes).?.data;
30678 const dest_len = parent.ty.arrayLenIncludingSentinel(mod);
30679 // bytes.len may be one greater than dest_len because of the case when
30680 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
30681 assert(bytes.len >= dest_len);
30682 const elems = try arena.alloc(Value, @intCast(dest_len));
30683 for (elems, 0..) |*elem, i| {
30684 elem.* = try mod.intValue(elem_ty, bytes[i]);
30685 }
30686
30687 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
30688
30689 return beginComptimePtrMutationInner(
30690 sema,
30691 block,
30692 src,
30693 elem_ty,
30694 &elems[@intCast(elem_ptr.index)],
30695 ptr_elem_ty,
30696 parent.mut_decl,
30697 );
30698 },
30699 .repeated => {
30700 // An array is memory-optimized to store only a single element value, and
30701 // that value is understood to be the same for the entire length of the array.
30702 // However, now we want to modify an individual field and so the
30703 // representation has to change. If we wanted to avoid this, there would
30704 // need to be special detection elsewhere to identify when writing a value to an
30705 // array element that is stored using the `repeated` tag, and handle it
30706 // without making a call to this function.
30707 const arena = mod.tmp_hack_arena.allocator();
30708
30709 const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod);
30710 const array_len_including_sentinel =
30711 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
30712 const elems = try arena.alloc(Value, array_len_including_sentinel);
30713 @memset(elems, Value.fromInterned(repeated_val));
30714
30715 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
30716
30717 return beginComptimePtrMutationInner(
30718 sema,
30719 block,
30720 src,
30721 elem_ty,
30722 &elems[@intCast(elem_ptr.index)],
30723 ptr_elem_ty,
30724 parent.mut_decl,
30725 );
30726 },
30727
30728 .aggregate => return beginComptimePtrMutationInner(
30729 sema,
30730 block,
30731 src,
30732 elem_ty,
30733 &val_ptr.castTag(.aggregate).?.data[@intCast(elem_ptr.index)],
30734 ptr_elem_ty,
30735 parent.mut_decl,
30736 ),
30737
30738 else => unreachable,
30739 },
30740 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
30741 .undef => {
30742 // An array has been initialized to undefined at comptime and now we
30743 // are for the first time setting an element. We must change the representation
30744 // of the array from `undef` to `array`.
30745 const arena = mod.tmp_hack_arena.allocator();
30746
30747 const array_len_including_sentinel =
30748 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
30749 const elems = try arena.alloc(Value, array_len_including_sentinel);
30750 @memset(elems, Value.fromInterned((try mod.intern(.{ .undef = elem_ty.toIntern() }))));
30751
30752 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
30753
30754 return beginComptimePtrMutationInner(
30755 sema,
30756 block,
30757 src,
30758 elem_ty,
30759 &elems[@intCast(elem_ptr.index)],
30760 ptr_elem_ty,
30761 parent.mut_decl,
30762 );
30763 },
30764 else => unreachable,
30765 },
30766 }
30767 },
30768 else => {
30769 if (elem_ptr.index != 0) {
30770 // TODO include a "declared here" note for the decl
30771 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
30772 elem_ptr.index,
30773 });
30774 }
30775 return beginComptimePtrMutationInner(
30776 sema,
30777 block,
30778 src,
30779 parent.ty,
30780 val_ptr,
30781 ptr_elem_ty,
30782 parent.mut_decl,
30783 );
30784 },
30785 },
30786 .reinterpret => |reinterpret| {
30787 if (!base_elem_ty.hasWellDefinedLayout(mod)) {
30788 // Even though the parent value type has well-defined memory layout, our
30789 // pointer type does not.
30790 return ComptimePtrMutationKit{
30791 .mut_decl = parent.mut_decl,
30792 .pointee = .bad_ptr_ty,
30793 .ty = base_elem_ty,
30794 };
30795 }
30796
30797 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
30798 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
30799 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
30800 return ComptimePtrMutationKit{
30801 .mut_decl = parent.mut_decl,
30802 .pointee = .{ .reinterpret = .{
30803 .val_ptr = reinterpret.val_ptr,
30804 .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx,
30805 } },
30806 .ty = parent.ty,
30807 };
30808 },
30809 .bad_decl_ty, .bad_ptr_ty => return parent,
30810 }
30811 },
30812 .field => |field_ptr| {
30813 const base_child_ty = Type.fromInterned(mod.intern_pool.typeOf(field_ptr.base)).childType(mod);
30814 const field_index: u32 = @intCast(field_ptr.index);
30815
30816 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(field_ptr.base), base_child_ty);
30817 switch (parent.pointee) {
30818 .opv => unreachable,
30819 .direct => |val_ptr| switch (val_ptr.ip_index) {
30820 .empty_struct => {
30821 const duped = try sema.arena.create(Value);
30822 duped.* = val_ptr.*;
30823 return beginComptimePtrMutationInner(
30824 sema,
30825 block,
30826 src,
30827 parent.ty.structFieldType(field_index, mod),
30828 duped,
30829 ptr_elem_ty,
30830 parent.mut_decl,
30831 );
30832 },
30833 .none => switch (val_ptr.tag()) {
30834 .aggregate => return beginComptimePtrMutationInner(
30835 sema,
30836 block,
30837 src,
30838 parent.ty.structFieldType(field_index, mod),
30839 &val_ptr.castTag(.aggregate).?.data[field_index],
30840 ptr_elem_ty,
30841 parent.mut_decl,
30842 ),
30843 .repeated => {
30844 const arena = mod.tmp_hack_arena.allocator();
30845
30846 const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod));
30847 @memset(elems, val_ptr.castTag(.repeated).?.data);
30848 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
30849
30850 return beginComptimePtrMutationInner(
30851 sema,
30852 block,
30853 src,
30854 parent.ty.structFieldType(field_index, mod),
30855 &elems[field_index],
30856 ptr_elem_ty,
30857 parent.mut_decl,
30858 );
30859 },
30860 .@"union" => {
30861 const payload = &val_ptr.castTag(.@"union").?.data;
30862 const layout = base_child_ty.containerLayout(mod);
30863
30864 const tag_type = base_child_ty.unionTagTypeHypothetical(mod);
30865 const hypothetical_tag = try mod.enumValueFieldIndex(tag_type, field_index);
30866 if (layout == .Auto or (payload.tag != null and hypothetical_tag.eql(payload.tag.?, tag_type, mod))) {
30867 // We need to set the active field of the union.
30868 payload.tag = hypothetical_tag;
30869
30870 const field_ty = parent.ty.structFieldType(field_index, mod);
30871 return beginComptimePtrMutationInner(
30872 sema,
30873 block,
30874 src,
30875 field_ty,
30876 &payload.val,
30877 ptr_elem_ty,
30878 parent.mut_decl,
30879 );
30880 } else {
30881 // Writing to a different field (a different or unknown tag is active) requires reinterpreting
30882 // memory of the entire union, which requires knowing its abiSize.
30883 try sema.resolveTypeLayout(parent.ty);
30884
30885 // This union value no longer has a well-defined tag type.
30886 // The reinterpretation will read it back out as .none.
30887 payload.val = try payload.val.unintern(sema.arena, mod);
30888 return ComptimePtrMutationKit{
30889 .mut_decl = parent.mut_decl,
30890 .pointee = .{ .reinterpret = .{
30891 .val_ptr = val_ptr,
30892 .byte_offset = 0,
30893 .write_packed = layout == .Packed,
30894 } },
30895 .ty = parent.ty,
30896 };
30897 }
30898 },
30899 .slice => switch (field_index) {
30900 Value.slice_ptr_index => return beginComptimePtrMutationInner(
30901 sema,
30902 block,
30903 src,
30904 parent.ty.slicePtrFieldType(mod),
30905 &val_ptr.castTag(.slice).?.data.ptr,
30906 ptr_elem_ty,
30907 parent.mut_decl,
30908 ),
30909
30910 Value.slice_len_index => return beginComptimePtrMutationInner(
30911 sema,
30912 block,
30913 src,
30914 Type.usize,
30915 &val_ptr.castTag(.slice).?.data.len,
30916 ptr_elem_ty,
30917 parent.mut_decl,
30918 ),
30919
30920 else => unreachable,
30921 },
30922 else => unreachable,
30923 },
30924 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
30925 .undef => {
30926 // A struct or union has been initialized to undefined at comptime and now we
30927 // are for the first time setting a field. We must change the representation
30928 // of the struct/union from `undef` to `struct`/`union`.
30929 const arena = mod.tmp_hack_arena.allocator();
30930
30931 switch (parent.ty.zigTypeTag(mod)) {
30932 .Struct => {
30933 const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
30934 for (fields, 0..) |*field, i| field.* = Value.fromInterned((try mod.intern(.{
30935 .undef = parent.ty.structFieldType(i, mod).toIntern(),
30936 })));
30937
30938 val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
30939
30940 return beginComptimePtrMutationInner(
30941 sema,
30942 block,
30943 src,
30944 parent.ty.structFieldType(field_index, mod),
30945 &fields[field_index],
30946 ptr_elem_ty,
30947 parent.mut_decl,
30948 );
30949 },
30950 .Union => {
30951 const payload = try arena.create(Value.Payload.Union);
30952 const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
30953 const payload_ty = parent.ty.structFieldType(field_index, mod);
30954 payload.* = .{ .data = .{
30955 .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
30956 .val = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),
30957 } };
30958
30959 val_ptr.* = Value.initPayload(&payload.base);
30960
30961 return beginComptimePtrMutationInner(
30962 sema,
30963 block,
30964 src,
30965 payload_ty,
30966 &payload.data.val,
30967 ptr_elem_ty,
30968 parent.mut_decl,
30969 );
30970 },
30971 .Pointer => {
30972 assert(parent.ty.isSlice(mod));
30973 const ptr_ty = parent.ty.slicePtrFieldType(mod);
30974 val_ptr.* = try Value.Tag.slice.create(arena, .{
30975 .ptr = Value.fromInterned((try mod.intern(.{ .undef = ptr_ty.toIntern() }))),
30976 .len = Value.fromInterned((try mod.intern(.{ .undef = .usize_type }))),
30977 });
30978
30979 switch (field_index) {
30980 Value.slice_ptr_index => return beginComptimePtrMutationInner(
30981 sema,
30982 block,
30983 src,
30984 ptr_ty,
30985 &val_ptr.castTag(.slice).?.data.ptr,
30986 ptr_elem_ty,
30987 parent.mut_decl,
30988 ),
30989 Value.slice_len_index => return beginComptimePtrMutationInner(
30990 sema,
30991 block,
30992 src,
30993 Type.usize,
30994 &val_ptr.castTag(.slice).?.data.len,
30995 ptr_elem_ty,
30996 parent.mut_decl,
30997 ),
30998
30999 else => unreachable,
31000 }
31001 },
31002 else => unreachable,
31003 }
31004 },
31005 else => unreachable,
31006 },
31007 },
31008 .reinterpret => |reinterpret| {
31009 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
31010 const field_offset = try sema.usizeCast(block, src, field_offset_u64);
31011 return ComptimePtrMutationKit{
31012 .mut_decl = parent.mut_decl,
31013 .pointee = .{ .reinterpret = .{
31014 .val_ptr = reinterpret.val_ptr,
31015 .byte_offset = reinterpret.byte_offset + field_offset,
31016 } },
31017 .ty = parent.ty,
31018 };
31019 },
31020 .bad_decl_ty, .bad_ptr_ty => return parent,
31021 }
31022 },
31023 }
31024}
31025
31026fn beginComptimePtrMutationInner(
31027 sema: *Sema,
31028 block: *Block,
31029 src: LazySrcLoc,
31030 decl_ty: Type,
31031 decl_val: *Value,
31032 ptr_elem_ty: Type,
31033 mut_decl: InternPool.Key.Ptr.Addr.MutDecl,
31034) CompileError!ComptimePtrMutationKit {
31035 const mod = sema.mod;
31036 const target = mod.getTarget();
31037 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
31038
31039 decl_val.* = try decl_val.unintern(sema.arena, mod);
31040
31041 if (coerce_ok) {
31042 return ComptimePtrMutationKit{
31043 .mut_decl = mut_decl,
31044 .pointee = .{ .direct = decl_val },
31045 .ty = decl_ty,
31046 };
31047 }
31048
31049 // Handle the case that the decl is an array and we're actually trying to point to an element.
31050 if (decl_ty.isArrayOrVector(mod)) {
31051 const decl_elem_ty = decl_ty.childType(mod);
31052 if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
31053 return ComptimePtrMutationKit{
31054 .mut_decl = mut_decl,
31055 .pointee = .{ .direct = decl_val },
31056 .ty = decl_ty,
31057 };
31058 }
31059 }
31060
31061 if (!decl_ty.hasWellDefinedLayout(mod)) {
31062 return ComptimePtrMutationKit{
31063 .mut_decl = mut_decl,
31064 .pointee = .bad_decl_ty,
31065 .ty = decl_ty,
31066 };
31067 }
31068 if (!ptr_elem_ty.hasWellDefinedLayout(mod)) {
31069 return ComptimePtrMutationKit{
31070 .mut_decl = mut_decl,
31071 .pointee = .bad_ptr_ty,
31072 .ty = ptr_elem_ty,
31073 };
30458 if (true) {
30459 // The previous implementation operated on the InternPool pointer value representation,
30460 // which is an immutable data structure. Instead, the new implementation needs to
30461 // operate on ComptimeMemory, which is a mutable data structure.
30462 _ = sema;
30463 _ = block;
30464 _ = src;
30465 _ = ptr_val;
30466 _ = ptr_elem_ty;
30467 @panic("TODO implement beginComptimePtrMutation");
3107430468 }
31075 return ComptimePtrMutationKit{
31076 .mut_decl = mut_decl,
31077 .pointee = .{ .reinterpret = .{
31078 .val_ptr = decl_val,
31079 .byte_offset = 0,
31080 } },
31081 .ty = decl_ty,
31082 };
3108330469}
3108430470
3108530471const TypedValueAndOffset = struct {
......@@ -31121,13 +30507,11 @@ fn beginComptimePtrLoad(
3112130507
3112230508 var deref: ComptimePtrLoadKit = switch (ip.indexToKey(ptr_val.toIntern())) {
3112330509 .ptr => |ptr| switch (ptr.addr) {
31124 .decl, .mut_decl => blk: {
30510 .decl => blk: {
3112530511 const decl_index = switch (ptr.addr) {
3112630512 .decl => |decl| decl,
31127 .mut_decl => |mut_decl| mut_decl.decl,
3112830513 else => unreachable,
3112930514 };
31130 const is_mutable = ptr.addr == .mut_decl;
3113130515 const decl = mod.declPtr(decl_index);
3113230516 const decl_tv = try decl.typedValue();
3113330517 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
......@@ -31136,7 +30520,7 @@ fn beginComptimePtrLoad(
3113630520 break :blk ComptimePtrLoadKit{
3113730521 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
3113830522 .pointee = decl_tv,
31139 .is_mutable = is_mutable,
30523 .is_mutable = false,
3114030524 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
3114130525 };
3114230526 },
......@@ -35280,7 +34664,7 @@ fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
3528034664 },
3528134665 .ptr => |ptr| {
3528234666 switch (ptr.addr) {
35283 .decl, .mut_decl, .anon_decl => return val,
34667 .decl, .anon_decl => return val,
3528434668 .comptime_field => |field_val| {
3528534669 const resolved_field_val =
3528634670 (try sema.resolveLazyValue(Value.fromInterned(field_val))).toIntern();
......@@ -35635,8 +35019,8 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
3563535019 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
3563635020 defer analysis_arena.deinit();
3563735021
35638 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
35639 defer comptime_mutable_decls.deinit();
35022 var comptime_memory: ComptimeMemory = .{};
35023 defer comptime_memory.deinit(gpa);
3564035024
3564135025 var comptime_err_ret_trace = std.ArrayList(Module.SrcLoc).init(gpa);
3564235026 defer comptime_err_ret_trace.deinit();
......@@ -35653,7 +35037,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
3565335037 .fn_ret_ty = Type.void,
3565435038 .fn_ret_ty_ies = null,
3565535039 .owner_func_index = .none,
35656 .comptime_mutable_decls = &comptime_mutable_decls,
35040 .comptime_memory = &comptime_memory,
3565735041 .comptime_err_ret_trace = &comptime_err_ret_trace,
3565835042 };
3565935043 defer sema.deinit();
......@@ -35714,11 +35098,6 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
3571435098 const backing_int_ty = try mod.intType(.unsigned, @intCast(fields_bit_sum));
3571535099 struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
3571635100 }
35717
35718 for (comptime_mutable_decls.items) |ct_decl_index| {
35719 const ct_decl = mod.declPtr(ct_decl_index);
35720 _ = try ct_decl.internValue(mod);
35721 }
3572235101}
3572335102
3572435103fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
......@@ -36460,8 +35839,8 @@ fn semaStructFields(
3646035839 },
3646135840 };
3646235841
36463 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
36464 defer comptime_mutable_decls.deinit();
35842 var comptime_memory: ComptimeMemory = .{};
35843 defer comptime_memory.deinit(gpa);
3646535844
3646635845 var comptime_err_ret_trace = std.ArrayList(Module.SrcLoc).init(gpa);
3646735846 defer comptime_err_ret_trace.deinit();
......@@ -36478,7 +35857,7 @@ fn semaStructFields(
3647835857 .fn_ret_ty = Type.void,
3647935858 .fn_ret_ty_ies = null,
3648035859 .owner_func_index = .none,
36481 .comptime_mutable_decls = &comptime_mutable_decls,
35860 .comptime_memory = &comptime_memory,
3648235861 .comptime_err_ret_trace = &comptime_err_ret_trace,
3648335862 };
3648435863 defer sema.deinit();
......@@ -36693,11 +36072,6 @@ fn semaStructFields(
3669336072
3669436073 struct_type.clearTypesWip(ip);
3669536074 if (!any_inits) struct_type.setHaveFieldInits(ip);
36696
36697 for (comptime_mutable_decls.items) |ct_decl_index| {
36698 const ct_decl = mod.declPtr(ct_decl_index);
36699 _ = try ct_decl.internValue(mod);
36700 }
3670136075}
3670236076
3670336077// This logic must be kept in sync with `semaStructFields`
......@@ -36718,8 +36092,8 @@ fn semaStructFieldInits(
3671836092 const zir_index = struct_type.zir_index.resolve(ip);
3671936093 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
3672036094
36721 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
36722 defer comptime_mutable_decls.deinit();
36095 var comptime_memory: ComptimeMemory = .{};
36096 defer comptime_memory.deinit(gpa);
3672336097
3672436098 var comptime_err_ret_trace = std.ArrayList(Module.SrcLoc).init(gpa);
3672536099 defer comptime_err_ret_trace.deinit();
......@@ -36736,7 +36110,7 @@ fn semaStructFieldInits(
3673636110 .fn_ret_ty = Type.void,
3673736111 .fn_ret_ty_ies = null,
3673836112 .owner_func_index = .none,
36739 .comptime_mutable_decls = &comptime_mutable_decls,
36113 .comptime_memory = &comptime_memory,
3674036114 .comptime_err_ret_trace = &comptime_err_ret_trace,
3674136115 };
3674236116 defer sema.deinit();
......@@ -36849,11 +36223,6 @@ fn semaStructFieldInits(
3684936223 struct_type.field_inits.get(ip)[field_i] = field_init;
3685036224 }
3685136225 }
36852
36853 for (comptime_mutable_decls.items) |ct_decl_index| {
36854 const ct_decl = mod.declPtr(ct_decl_index);
36855 _ = try ct_decl.internValue(mod);
36856 }
3685736226}
3685836227
3685936228fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.UnionType) CompileError!void {
......@@ -36905,8 +36274,8 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
3690536274
3690636275 const decl = mod.declPtr(decl_index);
3690736276
36908 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
36909 defer comptime_mutable_decls.deinit();
36277 var comptime_memory: ComptimeMemory = .{};
36278 defer comptime_memory.deinit(gpa);
3691036279
3691136280 var comptime_err_ret_trace = std.ArrayList(Module.SrcLoc).init(gpa);
3691236281 defer comptime_err_ret_trace.deinit();
......@@ -36923,7 +36292,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
3692336292 .fn_ret_ty = Type.void,
3692436293 .fn_ret_ty_ies = null,
3692536294 .owner_func_index = .none,
36926 .comptime_mutable_decls = &comptime_mutable_decls,
36295 .comptime_memory = &comptime_memory,
3692736296 .comptime_err_ret_trace = &comptime_err_ret_trace,
3692836297 };
3692936298 defer sema.deinit();
......@@ -36944,11 +36313,6 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
3694436313 try sema.analyzeBody(&block_scope, body);
3694536314 }
3694636315
36947 for (comptime_mutable_decls.items) |ct_decl_index| {
36948 const ct_decl = mod.declPtr(ct_decl_index);
36949 _ = try ct_decl.internValue(mod);
36950 }
36951
3695236316 var int_tag_ty: Type = undefined;
3695336317 var enum_field_names: []InternPool.NullTerminatedString = &.{};
3695436318 var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
......@@ -37567,7 +36931,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
3756736931 .ptr_decl,
3756836932 .ptr_anon_decl,
3756936933 .ptr_anon_decl_aligned,
37570 .ptr_mut_decl,
3757136934 .ptr_comptime_field,
3757236935 .ptr_int,
3757336936 .ptr_eu_payload,
......@@ -37831,10 +37194,12 @@ fn isComptimeKnown(
3783137194fn analyzeComptimeAlloc(
3783237195 sema: *Sema,
3783337196 block: *Block,
37197 inst: Zir.Inst.Index,
3783437198 var_type: Type,
3783537199 alignment: Alignment,
3783637200) CompileError!Air.Inst.Ref {
3783737201 const mod = sema.mod;
37202 const gpa = sema.gpa;
3783837203
3783937204 // Needed to make an anon decl with type `var_type` (the `finish()` call below).
3784037205 _ = try sema.typeHasOnePossibleValue(var_type);
......@@ -37847,28 +37212,10 @@ fn analyzeComptimeAlloc(
3784737212 },
3784837213 });
3784937214
37850 var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl
37851 defer anon_decl.deinit();
37852
37853 const decl_index = try anon_decl.finish(
37854 var_type,
37855 // There will be stores before the first load, but they may be to sub-elements or
37856 // sub-fields. So we need to initialize with undef to allow the mechanism to expand
37857 // into fields/elements and have those overridden with stored values.
37858 Value.fromInterned((try mod.intern(.{ .undef = var_type.toIntern() }))),
37859 alignment,
37860 );
37861 const decl = mod.declPtr(decl_index);
37862 decl.alignment = alignment;
37863
37864 try sema.comptime_mutable_decls.append(decl_index);
37865 return Air.internedToRef((try mod.intern(.{ .ptr = .{
37866 .ty = ptr_type.toIntern(),
37867 .addr = .{ .mut_decl = .{
37868 .decl = decl_index,
37869 .runtime_index = block.runtime_index,
37870 } },
37871 } })));
37215 const comptime_ptr = try sema.comptime_memory.allocate(gpa, ptr_type, block.runtime_index);
37216 try sema.value_map_values.append(gpa, comptime_ptr);
37217 try sema.comptime_memory.value_map.put(gpa, inst, {});
37218 return .mutable_comptime;
3787237219}
3787337220
3787437221/// The places where a user can specify an address space attribute
......@@ -38871,3 +38218,24 @@ fn ptrType(sema: *Sema, info: InternPool.Key.PtrType) CompileError!Type {
3887138218 }
3887238219 return sema.mod.ptrType(info);
3887338220}
38221
38222fn fieldValue(sema: *Sema, val: MutValue, index: usize) !Value {
38223 const zcu = sema.mod;
38224 const cm = sema.comptime_memory;
38225 return switch (val.tag) {
38226 .interned => return ConstValue.fromInterned(val.repr.ip_index).fieldValue(zcu, index),
38227 .aggregate => {
38228 const agg = cm.aggregate_list.items[val.repr.aggregate];
38229 assert(index < agg.start + agg.len);
38230 return cm.value_list.get(agg.start + index);
38231 },
38232 .@"union" => {
38233 const un = cm.union_list.items[val.repr.@"union"];
38234 // TODO assert the tag is correct
38235 return cm.value_list.get(un.val);
38236 },
38237 else => unreachable,
38238 };
38239}
38240
38241
src/Sema/ComptimeMemory.zig created+63
......@@ -0,0 +1,63 @@
1/// The index points into `value_map_values`.
2value_map: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, void) = .{},
3value_map_values: std.MultiArrayList(Value) = .{},
4
5// The following fields are used by the untagged union of Value:
6
7/// Corresponds to `Value.Index`
8value_list: std.MultiArrayList(Value) = .{},
9/// Corresponds to `Slice.Index`
10slice_list: std.ArrayListUnmanaged(Slice) = .{},
11/// Corresponds to `Bytes.Index`
12bytes_list: std.ArrayListUnmanaged(Bytes) = .{},
13/// Corresponds to `Aggregate.Index`
14aggregate_list: std.ArrayListUnmanaged(Aggregate) = .{},
15/// Corresponds to `Union.Index`
16union_list: std.ArrayListUnmanaged(Union) = .{},
17
18pub const Value = @import("ComptimeMemory/Value.zig");
19
20pub const Bytes = struct {
21 /// The full slice of data owned by the allocation backing this value.
22 memory_island: []u8,
23 start: usize,
24 /// Includes the sentinel, if any.
25 len: usize,
26
27 pub const Index = enum(u32) { _ };
28};
29
30pub const Slice = struct {
31 ptr: Value,
32 len: Value,
33
34 pub const Index = enum(u32) { _ };
35};
36
37pub const Aggregate = struct {
38 start: Value.Index,
39 len: u32,
40
41 pub const Index = enum(u32) { _ };
42};
43
44pub const Union = struct {
45 /// none means undefined tag.
46 tag: Value.OptionalIndex,
47 val: Value,
48
49 pub const Index = enum(u32) { _ };
50};
51
52pub const RuntimeIndex = enum(u32) {
53 zero = 0,
54 comptime_field_ptr = std.math.maxInt(u32),
55 _,
56
57 pub fn increment(ri: *RuntimeIndex) void {
58 ri.* = @enumFromInt(@intFromEnum(ri.*) + 1);
59 }
60};
61
62const std = @import("std");
63const Zir = @import("../Zir.zig");
src/Sema/ComptimeMemory/Value.zig created+66
......@@ -0,0 +1,66 @@
1ty: InternPool.Index,
2tag: Tag,
3repr: Repr,
4
5comptime {
6 switch (builtin.mode) {
7 .ReleaseFast, .ReleaseSmall => {
8 assert(@sizeOf(InternPool.Index) == 4);
9 assert(@sizeOf(Repr) == 4);
10 assert(@sizeOf(Tag) == 1);
11 },
12 .Debug, .ReleaseSafe => {},
13 }
14}
15
16pub const Tag = enum(u8) {
17 /// Represents an value stored in `InternPool`.
18 interned,
19 /// Represents an error union value that is not an error.
20 /// The value is the payload value.
21 eu_payload,
22 /// Represents an optional value that is not null.
23 /// The value is the payload value.
24 opt_payload,
25 /// The type must be an array, vector, or tuple. The element is this sub
26 /// value repeated according to the length provided by the type.
27 repeated,
28 /// The type must be a slice pointer type.
29 slice,
30 /// The value is index into ComptimeMemory buffers array.
31 bytes,
32 /// An instance of a struct, array, or vector.
33 /// Each element/field stored as a `Value`.
34 /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
35 /// so the slice length will be one more than the type's array length.
36 aggregate,
37 /// An instance of a union.
38 @"union",
39};
40
41pub const Repr = union {
42 ip_index: InternPool.Index,
43 eu_payload: Index,
44 opt_payload: Index,
45 repeated: Index,
46 slice: ComptimeMemory.Slice.Index,
47 bytes: ComptimeMemory.Bytes.Index,
48 aggregate: ComptimeMemory.Aggregate.Index,
49 @"union": ComptimeMemory.Union.Index,
50};
51
52pub const Index = enum(u32) { _ };
53
54pub const OptionalIndex = enum(u32) {
55 none = std.math.maxInt(u32),
56 _,
57};
58
59const builtin = @import("builtin");
60const std = @import("std");
61const assert = std.debug.assert;
62const Value = @This();
63const ConstValue = @import("../../Value.zig");
64
65const InternPool = @import("../../InternPool.zig");
66const ComptimeMemory = @import("../ComptimeMemory.zig");
src/TypedValue.zig+1-9
......@@ -1,6 +1,6 @@
11const std = @import("std");
22const Type = @import("type.zig").Type;
3const Value = @import("value.zig").Value;
3const Value = @import("Value.zig");
44const Module = @import("Module.zig");
55const Allocator = std.mem.Allocator;
66const TypedValue = @This();
......@@ -329,14 +329,6 @@ pub fn print(
329329 .val = Value.fromInterned(decl_val),
330330 }, writer, level - 1, mod);
331331 },
332 .mut_decl => |mut_decl| {
333 const decl = mod.declPtr(mut_decl.decl);
334 if (level == 0) return writer.print("(mut decl '{}')", .{decl.name.fmt(ip)});
335 return print(.{
336 .ty = decl.ty,
337 .val = decl.val,
338 }, writer, level - 1, mod);
339 },
340332 .comptime_field => |field_val_ip| {
341333 return print(.{
342334 .ty = Type.fromInterned(ip.typeOf(field_val_ip)),
src/Value.zig created+3787
......@@ -0,0 +1,3787 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Type = @import("type.zig").Type;
4const assert = std.debug.assert;
5const BigIntConst = std.math.big.int.Const;
6const BigIntMutable = std.math.big.int.Mutable;
7const Target = std.Target;
8const Allocator = std.mem.Allocator;
9const Module = @import("Module.zig");
10const TypedValue = @import("TypedValue.zig");
11const Sema = @import("Sema.zig");
12const InternPool = @import("InternPool.zig");
13const Value = @This();
14
15ip_index: InternPool.Index,
16
17pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
18 _ = val;
19 _ = fmt;
20 _ = options;
21 _ = writer;
22 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
23}
24
25/// This is a debug function. In order to print values in a meaningful way
26/// we also need access to the type.
27pub fn dump(
28 start_val: Value,
29 comptime fmt: []const u8,
30 _: std.fmt.FormatOptions,
31 out_stream: anytype,
32) !void {
33 comptime assert(fmt.len == 0);
34 if (start_val.ip_index != .none) {
35 try out_stream.print("(interned: {})", .{start_val.toIntern()});
36 return;
37 }
38 var val = start_val;
39 while (true) switch (val.tag()) {
40 .aggregate => {
41 return out_stream.writeAll("(aggregate)");
42 },
43 .@"union" => {
44 return out_stream.writeAll("(union value)");
45 },
46 .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
47 .repeated => {
48 try out_stream.writeAll("(repeated) ");
49 val = val.castTag(.repeated).?.data;
50 },
51 .eu_payload => {
52 try out_stream.writeAll("(eu_payload) ");
53 val = val.castTag(.repeated).?.data;
54 },
55 .opt_payload => {
56 try out_stream.writeAll("(opt_payload) ");
57 val = val.castTag(.repeated).?.data;
58 },
59 .slice => return out_stream.writeAll("(slice)"),
60 };
61}
62
63pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) {
64 return .{ .data = val };
65}
66
67pub fn fmtValue(val: Value, ty: Type, mod: *Module) std.fmt.Formatter(TypedValue.format) {
68 return .{ .data = .{
69 .tv = .{ .ty = ty, .val = val },
70 .mod = mod,
71 } };
72}
73
74/// Asserts that the value is representable as an array of bytes.
75/// Returns the value as a null-terminated string stored in the InternPool.
76pub fn toIpString(val: Value, ty: Type, mod: *Module) !InternPool.NullTerminatedString {
77 const ip = &mod.intern_pool;
78 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
79 .enum_literal => |enum_literal| enum_literal,
80 .slice => |slice| try arrayToIpString(val, Value.fromInterned(slice.len).toUnsignedInt(mod), mod),
81 .aggregate => |aggregate| switch (aggregate.storage) {
82 .bytes => |bytes| try ip.getOrPutString(mod.gpa, bytes),
83 .elems => try arrayToIpString(val, ty.arrayLen(mod), mod),
84 .repeated_elem => |elem| {
85 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
86 const len = @as(usize, @intCast(ty.arrayLen(mod)));
87 try ip.string_bytes.appendNTimes(mod.gpa, byte, len);
88 return ip.getOrPutTrailingString(mod.gpa, len);
89 },
90 },
91 else => unreachable,
92 };
93}
94
95/// Asserts that the value is representable as an array of bytes.
96/// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
97pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 {
98 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
99 .enum_literal => |enum_literal| allocator.dupe(u8, mod.intern_pool.stringToSlice(enum_literal)),
100 .slice => |slice| try arrayToAllocatedBytes(val, Value.fromInterned(slice.len).toUnsignedInt(mod), allocator, mod),
101 .aggregate => |aggregate| switch (aggregate.storage) {
102 .bytes => |bytes| try allocator.dupe(u8, bytes),
103 .elems => try arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod),
104 .repeated_elem => |elem| {
105 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
106 const result = try allocator.alloc(u8, @as(usize, @intCast(ty.arrayLen(mod))));
107 @memset(result, byte);
108 return result;
109 },
110 },
111 else => unreachable,
112 };
113}
114
115fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 {
116 const result = try allocator.alloc(u8, @as(usize, @intCast(len)));
117 for (result, 0..) |*elem, i| {
118 const elem_val = try val.elemValue(mod, i);
119 elem.* = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
120 }
121 return result;
122}
123
124fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTerminatedString {
125 const gpa = mod.gpa;
126 const ip = &mod.intern_pool;
127 const len = @as(usize, @intCast(len_u64));
128 try ip.string_bytes.ensureUnusedCapacity(gpa, len);
129 for (0..len) |i| {
130 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's
131 // assert just to be sure.
132 const prev = ip.string_bytes.items.len;
133 const elem_val = try val.elemValue(mod, i);
134 assert(ip.string_bytes.items.len == prev);
135 const byte = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
136 ip.string_bytes.appendAssumeCapacity(byte);
137 }
138 return ip.getOrPutTrailingString(gpa, len);
139}
140
141pub fn intern2(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
142 if (val.ip_index != .none) return val.ip_index;
143 return intern(val, ty, mod);
144}
145
146pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
147 if (val.ip_index != .none) return (try mod.getCoerced(val, ty)).toIntern();
148 const ip = &mod.intern_pool;
149 switch (val.tag()) {
150 .eu_payload => {
151 const pl = val.castTag(.eu_payload).?.data;
152 return mod.intern(.{ .error_union = .{
153 .ty = ty.toIntern(),
154 .val = .{ .payload = try pl.intern(ty.errorUnionPayload(mod), mod) },
155 } });
156 },
157 .opt_payload => {
158 const pl = val.castTag(.opt_payload).?.data;
159 return mod.intern(.{ .opt = .{
160 .ty = ty.toIntern(),
161 .val = try pl.intern(ty.optionalChild(mod), mod),
162 } });
163 },
164 .slice => {
165 const pl = val.castTag(.slice).?.data;
166 return mod.intern(.{ .slice = .{
167 .ty = ty.toIntern(),
168 .len = try pl.len.intern(Type.usize, mod),
169 .ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod),
170 } });
171 },
172 .bytes => {
173 const pl = val.castTag(.bytes).?.data;
174 return mod.intern(.{ .aggregate = .{
175 .ty = ty.toIntern(),
176 .storage = .{ .bytes = pl },
177 } });
178 },
179 .repeated => {
180 const pl = val.castTag(.repeated).?.data;
181 return mod.intern(.{ .aggregate = .{
182 .ty = ty.toIntern(),
183 .storage = .{ .repeated_elem = try pl.intern(ty.childType(mod), mod) },
184 } });
185 },
186 .aggregate => {
187 const len = @as(usize, @intCast(ty.arrayLen(mod)));
188 const old_elems = val.castTag(.aggregate).?.data[0..len];
189 const new_elems = try mod.gpa.alloc(InternPool.Index, old_elems.len);
190 defer mod.gpa.free(new_elems);
191 const ty_key = ip.indexToKey(ty.toIntern());
192 for (new_elems, old_elems, 0..) |*new_elem, old_elem, field_i|
193 new_elem.* = try old_elem.intern(switch (ty_key) {
194 .struct_type => ty.structFieldType(field_i, mod),
195 .anon_struct_type => |info| Type.fromInterned(info.types.get(ip)[field_i]),
196 inline .array_type, .vector_type => |info| Type.fromInterned(info.child),
197 else => unreachable,
198 }, mod);
199 return mod.intern(.{ .aggregate = .{
200 .ty = ty.toIntern(),
201 .storage = .{ .elems = new_elems },
202 } });
203 },
204 .@"union" => {
205 const pl = val.castTag(.@"union").?.data;
206 if (pl.tag) |pl_tag| {
207 return mod.intern(.{ .un = .{
208 .ty = ty.toIntern(),
209 .tag = try pl_tag.intern(ty.unionTagTypeHypothetical(mod), mod),
210 .val = try pl.val.intern(ty.unionFieldType(pl_tag, mod).?, mod),
211 } });
212 } else {
213 return mod.intern(.{ .un = .{
214 .ty = ty.toIntern(),
215 .tag = .none,
216 .val = try pl.val.intern(try ty.unionBackingType(mod), mod),
217 } });
218 }
219 },
220 }
221}
222
223pub fn fromInterned(i: InternPool.Index) Value {
224 assert(i != .none);
225 return .{ .ip_index = i };
226}
227
228pub fn toIntern(val: Value) InternPool.Index {
229 assert(val.ip_index != .none);
230 return val.ip_index;
231}
232
233/// Asserts that the value is representable as a type.
234pub fn toType(self: Value) Type {
235 return Type.fromInterned(self.toIntern());
236}
237
238pub fn intFromEnum(val: Value, ty: Type, mod: *Module) Allocator.Error!Value {
239 const ip = &mod.intern_pool;
240 return switch (ip.indexToKey(ip.typeOf(val.toIntern()))) {
241 // Assume it is already an integer and return it directly.
242 .simple_type, .int_type => val,
243 .enum_literal => |enum_literal| {
244 const field_index = ty.enumFieldIndex(enum_literal, mod).?;
245 return switch (ip.indexToKey(ty.toIntern())) {
246 // Assume it is already an integer and return it directly.
247 .simple_type, .int_type => val,
248 .enum_type => |enum_type| if (enum_type.values.len != 0)
249 Value.fromInterned(enum_type.values.get(ip)[field_index])
250 else // Field index and integer values are the same.
251 mod.intValue(Type.fromInterned(enum_type.tag_ty), field_index),
252 else => unreachable,
253 };
254 },
255 .enum_type => |enum_type| try mod.getCoerced(val, Type.fromInterned(enum_type.tag_ty)),
256 else => unreachable,
257 };
258}
259
260/// Asserts the value is an integer.
261pub fn toBigInt(val: Value, space: *BigIntSpace, mod: *Module) BigIntConst {
262 return val.toBigIntAdvanced(space, mod, null) catch unreachable;
263}
264
265/// Asserts the value is an integer.
266pub fn toBigIntAdvanced(
267 val: Value,
268 space: *BigIntSpace,
269 mod: *Module,
270 opt_sema: ?*Sema,
271) Module.CompileError!BigIntConst {
272 return switch (val.toIntern()) {
273 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
274 .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
275 .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
276 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
277 .int => |int| switch (int.storage) {
278 .u64, .i64, .big_int => int.storage.toBigInt(space),
279 .lazy_align, .lazy_size => |ty| {
280 if (opt_sema) |sema| try sema.resolveTypeLayout(Type.fromInterned(ty));
281 const x = switch (int.storage) {
282 else => unreachable,
283 .lazy_align => Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
284 .lazy_size => Type.fromInterned(ty).abiSize(mod),
285 };
286 return BigIntMutable.init(&space.limbs, x).toConst();
287 },
288 },
289 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, mod, opt_sema),
290 .opt, .ptr => BigIntMutable.init(
291 &space.limbs,
292 (try val.getUnsignedIntAdvanced(mod, opt_sema)).?,
293 ).toConst(),
294 else => unreachable,
295 },
296 };
297}
298
299pub fn isFuncBody(val: Value, mod: *Module) bool {
300 return mod.intern_pool.isFuncBody(val.toIntern());
301}
302
303pub fn getFunction(val: Value, mod: *Module) ?InternPool.Key.Func {
304 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
305 .func => |x| x,
306 else => null,
307 } else null;
308}
309
310pub fn getExternFunc(val: Value, mod: *Module) ?InternPool.Key.ExternFunc {
311 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
312 .extern_func => |extern_func| extern_func,
313 else => null,
314 } else null;
315}
316
317pub fn getVariable(val: Value, mod: *Module) ?InternPool.Key.Variable {
318 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
319 .variable => |variable| variable,
320 else => null,
321 } else null;
322}
323
324/// If the value fits in a u64, return it, otherwise null.
325/// Asserts not undefined.
326pub fn getUnsignedInt(val: Value, mod: *Module) ?u64 {
327 return getUnsignedIntAdvanced(val, mod, null) catch unreachable;
328}
329
330/// If the value fits in a u64, return it, otherwise null.
331/// Asserts not undefined.
332pub fn getUnsignedIntAdvanced(val: Value, mod: *Module, opt_sema: ?*Sema) !?u64 {
333 return switch (val.toIntern()) {
334 .undef => unreachable,
335 .bool_false => 0,
336 .bool_true => 1,
337 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
338 .undef => unreachable,
339 .int => |int| switch (int.storage) {
340 .big_int => |big_int| big_int.to(u64) catch null,
341 .u64 => |x| x,
342 .i64 => |x| std.math.cast(u64, x),
343 .lazy_align => |ty| if (opt_sema) |sema|
344 (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0)
345 else
346 Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
347 .lazy_size => |ty| if (opt_sema) |sema|
348 (try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar
349 else
350 Type.fromInterned(ty).abiSize(mod),
351 },
352 .ptr => |ptr| switch (ptr.addr) {
353 .int => |int| Value.fromInterned(int).getUnsignedIntAdvanced(mod, opt_sema),
354 .elem => |elem| {
355 const base_addr = (try Value.fromInterned(elem.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
356 const elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod);
357 return base_addr + elem.index * elem_ty.abiSize(mod);
358 },
359 .field => |field| {
360 const base_addr = (try Value.fromInterned(field.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
361 const struct_ty = Type.fromInterned(mod.intern_pool.typeOf(field.base)).childType(mod);
362 if (opt_sema) |sema| try sema.resolveTypeLayout(struct_ty);
363 return base_addr + struct_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod);
364 },
365 else => null,
366 },
367 .opt => |opt| switch (opt.val) {
368 .none => 0,
369 else => |payload| Value.fromInterned(payload).getUnsignedIntAdvanced(mod, opt_sema),
370 },
371 else => null,
372 },
373 };
374}
375
376/// Asserts the value is an integer and it fits in a u64
377pub fn toUnsignedInt(val: Value, mod: *Module) u64 {
378 return getUnsignedInt(val, mod).?;
379}
380
381/// Asserts the value is an integer and it fits in a u64
382pub fn toUnsignedIntAdvanced(val: Value, sema: *Sema) !u64 {
383 return (try getUnsignedIntAdvanced(val, sema.mod, sema)).?;
384}
385
386/// Asserts the value is an integer and it fits in a i64
387pub fn toSignedInt(val: Value, mod: *Module) i64 {
388 return switch (val.toIntern()) {
389 .bool_false => 0,
390 .bool_true => 1,
391 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
392 .int => |int| switch (int.storage) {
393 .big_int => |big_int| big_int.to(i64) catch unreachable,
394 .i64 => |x| x,
395 .u64 => |x| @intCast(x),
396 .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
397 .lazy_size => |ty| @intCast(Type.fromInterned(ty).abiSize(mod)),
398 },
399 else => unreachable,
400 },
401 };
402}
403
404pub fn toBool(val: Value) bool {
405 return switch (val.toIntern()) {
406 .bool_true => true,
407 .bool_false => false,
408 else => unreachable,
409 };
410}
411
412fn isDeclRef(val: Value, mod: *Module) bool {
413 var check = val;
414 while (true) switch (mod.intern_pool.indexToKey(check.toIntern())) {
415 .ptr => |ptr| switch (ptr.addr) {
416 .decl, .comptime_field, .anon_decl => return true,
417 .eu_payload, .opt_payload => |base| check = Value.fromInterned(base),
418 .elem, .field => |base_index| check = Value.fromInterned(base_index.base),
419 .int => return false,
420 },
421 else => return false,
422 };
423}
424
425/// Write a Value's contents to `buffer`.
426///
427/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
428/// the end of the value in memory.
429pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{
430 ReinterpretDeclRef,
431 IllDefinedMemoryLayout,
432 Unimplemented,
433 OutOfMemory,
434}!void {
435 const target = mod.getTarget();
436 const endian = target.cpu.arch.endian();
437 if (val.isUndef(mod)) {
438 const size: usize = @intCast(ty.abiSize(mod));
439 @memset(buffer[0..size], 0xaa);
440 return;
441 }
442 const ip = &mod.intern_pool;
443 switch (ty.zigTypeTag(mod)) {
444 .Void => {},
445 .Bool => {
446 buffer[0] = @intFromBool(val.toBool());
447 },
448 .Int, .Enum => {
449 const int_info = ty.intInfo(mod);
450 const bits = int_info.bits;
451 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
452
453 var bigint_buffer: BigIntSpace = undefined;
454 const bigint = val.toBigInt(&bigint_buffer, mod);
455 bigint.writeTwosComplement(buffer[0..byte_count], endian);
456 },
457 .Float => switch (ty.floatBits(target)) {
458 16 => std.mem.writeInt(u16, buffer[0..2], @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
459 32 => std.mem.writeInt(u32, buffer[0..4], @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
460 64 => std.mem.writeInt(u64, buffer[0..8], @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
461 80 => std.mem.writeInt(u80, buffer[0..10], @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
462 128 => std.mem.writeInt(u128, buffer[0..16], @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
463 else => unreachable,
464 },
465 .Array => {
466 const len = ty.arrayLen(mod);
467 const elem_ty = ty.childType(mod);
468 const elem_size = @as(usize, @intCast(elem_ty.abiSize(mod)));
469 var elem_i: usize = 0;
470 var buf_off: usize = 0;
471 while (elem_i < len) : (elem_i += 1) {
472 const elem_val = try val.elemValue(mod, elem_i);
473 try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]);
474 buf_off += elem_size;
475 }
476 },
477 .Vector => {
478 // We use byte_count instead of abi_size here, so that any padding bytes
479 // follow the data bytes, on both big- and little-endian systems.
480 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
481 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
482 },
483 .Struct => {
484 const struct_type = mod.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;
485 switch (struct_type.layout) {
486 .Auto => return error.IllDefinedMemoryLayout,
487 .Extern => for (0..struct_type.field_types.len) |i| {
488 const off: usize = @intCast(ty.structFieldOffset(i, mod));
489 const field_val = switch (val.ip_index) {
490 .none => switch (val.tag()) {
491 .bytes => {
492 buffer[off] = val.castTag(.bytes).?.data[i];
493 continue;
494 },
495 .aggregate => val.castTag(.aggregate).?.data[i],
496 .repeated => val.castTag(.repeated).?.data,
497 else => unreachable,
498 },
499 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
500 .bytes => |bytes| {
501 buffer[off] = bytes[i];
502 continue;
503 },
504 .elems => |elems| elems[i],
505 .repeated_elem => |elem| elem,
506 }),
507 };
508 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
509 try writeToMemory(field_val, field_ty, mod, buffer[off..]);
510 },
511 .Packed => {
512 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
513 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
514 },
515 }
516 },
517 .ErrorSet => {
518 const bits = mod.errorSetBits();
519 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
520
521 const name = switch (ip.indexToKey(val.toIntern())) {
522 .err => |err| err.name,
523 .error_union => |error_union| error_union.val.err_name,
524 else => unreachable,
525 };
526 var bigint_buffer: BigIntSpace = undefined;
527 const bigint = BigIntMutable.init(
528 &bigint_buffer.limbs,
529 mod.global_error_set.getIndex(name).?,
530 ).toConst();
531 bigint.writeTwosComplement(buffer[0..byte_count], endian);
532 },
533 .Union => switch (ty.containerLayout(mod)) {
534 .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
535 .Extern => {
536 if (val.unionTag(mod)) |union_tag| {
537 const union_obj = mod.typeToUnion(ty).?;
538 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
539 const field_type = Type.fromInterned(union_obj.field_types.get(&mod.intern_pool)[field_index]);
540 const field_val = try val.fieldValue(mod, field_index);
541 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
542 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
543 } else {
544 const backing_ty = try ty.unionBackingType(mod);
545 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
546 return writeToMemory(val.unionValue(mod), backing_ty, mod, buffer[0..byte_count]);
547 }
548 },
549 .Packed => {
550 const backing_ty = try ty.unionBackingType(mod);
551 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
552 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
553 },
554 },
555 .Pointer => {
556 if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout;
557 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
558 return val.writeToMemory(Type.usize, mod, buffer);
559 },
560 .Optional => {
561 if (!ty.isPtrLikeOptional(mod)) return error.IllDefinedMemoryLayout;
562 const child = ty.optionalChild(mod);
563 const opt_val = val.optionalValue(mod);
564 if (opt_val) |some| {
565 return some.writeToMemory(child, mod, buffer);
566 } else {
567 return writeToMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer);
568 }
569 },
570 else => return error.Unimplemented,
571 }
572}
573
574/// Write a Value's contents to `buffer`.
575///
576/// Both the start and the end of the provided buffer must be tight, since
577/// big-endian packed memory layouts start at the end of the buffer.
578pub fn writeToPackedMemory(
579 val: Value,
580 ty: Type,
581 mod: *Module,
582 buffer: []u8,
583 bit_offset: usize,
584) error{ ReinterpretDeclRef, OutOfMemory }!void {
585 const ip = &mod.intern_pool;
586 const target = mod.getTarget();
587 const endian = target.cpu.arch.endian();
588 if (val.isUndef(mod)) {
589 const bit_size = @as(usize, @intCast(ty.bitSize(mod)));
590 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian);
591 return;
592 }
593 switch (ty.zigTypeTag(mod)) {
594 .Void => {},
595 .Bool => {
596 const byte_index = switch (endian) {
597 .little => bit_offset / 8,
598 .big => buffer.len - bit_offset / 8 - 1,
599 };
600 if (val.toBool()) {
601 buffer[byte_index] |= (@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
602 } else {
603 buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
604 }
605 },
606 .Int, .Enum => {
607 if (buffer.len == 0) return;
608 const bits = ty.intInfo(mod).bits;
609 if (bits == 0) return;
610
611 switch (ip.indexToKey((try val.intFromEnum(ty, mod)).toIntern()).int.storage) {
612 inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
613 .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
614 .lazy_align => |lazy_align| {
615 const num = Type.fromInterned(lazy_align).abiAlignment(mod).toByteUnits(0);
616 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
617 },
618 .lazy_size => |lazy_size| {
619 const num = Type.fromInterned(lazy_size).abiSize(mod);
620 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
621 },
622 }
623 },
624 .Float => switch (ty.floatBits(target)) {
625 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
626 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
627 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
628 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
629 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
630 else => unreachable,
631 },
632 .Vector => {
633 const elem_ty = ty.childType(mod);
634 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
635 const len = @as(usize, @intCast(ty.arrayLen(mod)));
636
637 var bits: u16 = 0;
638 var elem_i: usize = 0;
639 while (elem_i < len) : (elem_i += 1) {
640 // On big-endian systems, LLVM reverses the element order of vectors by default
641 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
642 const elem_val = try val.elemValue(mod, tgt_elem_i);
643 try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
644 bits += elem_bit_size;
645 }
646 },
647 .Struct => {
648 const struct_type = ip.indexToKey(ty.toIntern()).struct_type;
649 // Sema is supposed to have emitted a compile error already in the case of Auto,
650 // and Extern is handled in non-packed writeToMemory.
651 assert(struct_type.layout == .Packed);
652 var bits: u16 = 0;
653 for (0..struct_type.field_types.len) |i| {
654 const field_val = switch (val.ip_index) {
655 .none => switch (val.tag()) {
656 .bytes => unreachable,
657 .aggregate => val.castTag(.aggregate).?.data[i],
658 .repeated => val.castTag(.repeated).?.data,
659 else => unreachable,
660 },
661 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
662 .bytes => unreachable,
663 .elems => |elems| elems[i],
664 .repeated_elem => |elem| elem,
665 }),
666 };
667 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
668 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
669 try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
670 bits += field_bits;
671 }
672 },
673 .Union => {
674 const union_obj = mod.typeToUnion(ty).?;
675 switch (union_obj.getLayout(ip)) {
676 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
677 .Packed => {
678 if (val.unionTag(mod)) |union_tag| {
679 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
680 const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
681 const field_val = try val.fieldValue(mod, field_index);
682 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
683 } else {
684 const backing_ty = try ty.unionBackingType(mod);
685 return val.unionValue(mod).writeToPackedMemory(backing_ty, mod, buffer, bit_offset);
686 }
687 },
688 }
689 },
690 .Pointer => {
691 assert(!ty.isSlice(mod)); // No well defined layout.
692 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
693 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);
694 },
695 .Optional => {
696 assert(ty.isPtrLikeOptional(mod));
697 const child = ty.optionalChild(mod);
698 const opt_val = val.optionalValue(mod);
699 if (opt_val) |some| {
700 return some.writeToPackedMemory(child, mod, buffer, bit_offset);
701 } else {
702 return writeToPackedMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer, bit_offset);
703 }
704 },
705 else => @panic("TODO implement writeToPackedMemory for more types"),
706 }
707}
708
709/// Load a Value from the contents of `buffer`.
710///
711/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
712/// the end of the value in memory.
713pub fn readFromMemory(
714 ty: Type,
715 mod: *Module,
716 buffer: []const u8,
717 arena: Allocator,
718) error{
719 IllDefinedMemoryLayout,
720 Unimplemented,
721 OutOfMemory,
722}!Value {
723 const ip = &mod.intern_pool;
724 const target = mod.getTarget();
725 const endian = target.cpu.arch.endian();
726 switch (ty.zigTypeTag(mod)) {
727 .Void => return Value.void,
728 .Bool => {
729 if (buffer[0] == 0) {
730 return Value.false;
731 } else {
732 return Value.true;
733 }
734 },
735 .Int, .Enum => |ty_tag| {
736 const int_ty = switch (ty_tag) {
737 .Int => ty,
738 .Enum => ty.intTagType(mod),
739 else => unreachable,
740 };
741 const int_info = int_ty.intInfo(mod);
742 const bits = int_info.bits;
743 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
744 if (bits == 0 or buffer.len == 0) return mod.getCoerced(try mod.intValue(int_ty, 0), ty);
745
746 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
747 .signed => {
748 const val = std.mem.readVarInt(i64, buffer[0..byte_count], endian);
749 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
750 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
751 },
752 .unsigned => {
753 const val = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
754 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
755 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
756 },
757 } else { // Slow path, we have to construct a big-int
758 const Limb = std.math.big.Limb;
759 const limb_count = (byte_count + @sizeOf(Limb) - 1) / @sizeOf(Limb);
760 const limbs_buffer = try arena.alloc(Limb, limb_count);
761
762 var bigint = BigIntMutable.init(limbs_buffer, 0);
763 bigint.readTwosComplement(buffer[0..byte_count], bits, endian, int_info.signedness);
764 return mod.getCoerced(try mod.intValue_big(int_ty, bigint.toConst()), ty);
765 }
766 },
767 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
768 .ty = ty.toIntern(),
769 .storage = switch (ty.floatBits(target)) {
770 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readInt(u16, buffer[0..2], endian))) },
771 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readInt(u32, buffer[0..4], endian))) },
772 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readInt(u64, buffer[0..8], endian))) },
773 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readInt(u80, buffer[0..10], endian))) },
774 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readInt(u128, buffer[0..16], endian))) },
775 else => unreachable,
776 },
777 } }))),
778 .Array => {
779 const elem_ty = ty.childType(mod);
780 const elem_size = elem_ty.abiSize(mod);
781 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
782 var offset: usize = 0;
783 for (elems) |*elem| {
784 elem.* = try (try readFromMemory(elem_ty, mod, buffer[offset..], arena)).intern(elem_ty, mod);
785 offset += @as(usize, @intCast(elem_size));
786 }
787 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
788 .ty = ty.toIntern(),
789 .storage = .{ .elems = elems },
790 } })));
791 },
792 .Vector => {
793 // We use byte_count instead of abi_size here, so that any padding bytes
794 // follow the data bytes, on both big- and little-endian systems.
795 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
796 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
797 },
798 .Struct => {
799 const struct_type = mod.typeToStruct(ty).?;
800 switch (struct_type.layout) {
801 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
802 .Extern => {
803 const field_types = struct_type.field_types;
804 const field_vals = try arena.alloc(InternPool.Index, field_types.len);
805 for (field_vals, 0..) |*field_val, i| {
806 const field_ty = Type.fromInterned(field_types.get(ip)[i]);
807 const off: usize = @intCast(ty.structFieldOffset(i, mod));
808 const sz: usize = @intCast(field_ty.abiSize(mod));
809 field_val.* = try (try readFromMemory(field_ty, mod, buffer[off..(off + sz)], arena)).intern(field_ty, mod);
810 }
811 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
812 .ty = ty.toIntern(),
813 .storage = .{ .elems = field_vals },
814 } })));
815 },
816 .Packed => {
817 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
818 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
819 },
820 }
821 },
822 .ErrorSet => {
823 const bits = mod.errorSetBits();
824 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
825 const int = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
826 const index = (int << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
827 const name = mod.global_error_set.keys()[@intCast(index)];
828
829 return Value.fromInterned((try mod.intern(.{ .err = .{
830 .ty = ty.toIntern(),
831 .name = name,
832 } })));
833 },
834 .Union => switch (ty.containerLayout(mod)) {
835 .Auto => return error.IllDefinedMemoryLayout,
836 .Extern => {
837 const union_size = ty.abiSize(mod);
838 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
839 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
840 return Value.fromInterned((try mod.intern(.{ .un = .{
841 .ty = ty.toIntern(),
842 .tag = .none,
843 .val = val,
844 } })));
845 },
846 .Packed => {
847 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
848 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
849 },
850 },
851 .Pointer => {
852 assert(!ty.isSlice(mod)); // No well defined layout.
853 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
854 return Value.fromInterned((try mod.intern(.{ .ptr = .{
855 .ty = ty.toIntern(),
856 .addr = .{ .int = int_val.toIntern() },
857 } })));
858 },
859 .Optional => {
860 assert(ty.isPtrLikeOptional(mod));
861 const child_ty = ty.optionalChild(mod);
862 const child_val = try readFromMemory(child_ty, mod, buffer, arena);
863 return Value.fromInterned((try mod.intern(.{ .opt = .{
864 .ty = ty.toIntern(),
865 .val = switch (child_val.orderAgainstZero(mod)) {
866 .lt => unreachable,
867 .eq => .none,
868 .gt => child_val.toIntern(),
869 },
870 } })));
871 },
872 else => return error.Unimplemented,
873 }
874}
875
876/// Load a Value from the contents of `buffer`.
877///
878/// Both the start and the end of the provided buffer must be tight, since
879/// big-endian packed memory layouts start at the end of the buffer.
880pub fn readFromPackedMemory(
881 ty: Type,
882 mod: *Module,
883 buffer: []const u8,
884 bit_offset: usize,
885 arena: Allocator,
886) error{
887 IllDefinedMemoryLayout,
888 OutOfMemory,
889}!Value {
890 const ip = &mod.intern_pool;
891 const target = mod.getTarget();
892 const endian = target.cpu.arch.endian();
893 switch (ty.zigTypeTag(mod)) {
894 .Void => return Value.void,
895 .Bool => {
896 const byte = switch (endian) {
897 .big => buffer[buffer.len - bit_offset / 8 - 1],
898 .little => buffer[bit_offset / 8],
899 };
900 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) {
901 return Value.false;
902 } else {
903 return Value.true;
904 }
905 },
906 .Int, .Enum => |ty_tag| {
907 if (buffer.len == 0) return mod.intValue(ty, 0);
908 const int_info = ty.intInfo(mod);
909 const bits = int_info.bits;
910 if (bits == 0) return mod.intValue(ty, 0);
911
912 // Fast path for integers <= u64
913 if (bits <= 64) {
914 const int_ty = switch (ty_tag) {
915 .Int => ty,
916 .Enum => ty.intTagType(mod),
917 else => unreachable,
918 };
919 return mod.getCoerced(switch (int_info.signedness) {
920 .signed => return mod.intValue(
921 int_ty,
922 std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed),
923 ),
924 .unsigned => return mod.intValue(
925 int_ty,
926 std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned),
927 ),
928 }, ty);
929 }
930
931 // Slow path, we have to construct a big-int
932 const abi_size = @as(usize, @intCast(ty.abiSize(mod)));
933 const Limb = std.math.big.Limb;
934 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
935 const limbs_buffer = try arena.alloc(Limb, limb_count);
936
937 var bigint = BigIntMutable.init(limbs_buffer, 0);
938 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
939 return mod.intValue_big(ty, bigint.toConst());
940 },
941 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
942 .ty = ty.toIntern(),
943 .storage = switch (ty.floatBits(target)) {
944 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian))) },
945 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian))) },
946 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian))) },
947 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian))) },
948 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian))) },
949 else => unreachable,
950 },
951 } }))),
952 .Vector => {
953 const elem_ty = ty.childType(mod);
954 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
955
956 var bits: u16 = 0;
957 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
958 for (elems, 0..) |_, i| {
959 // On big-endian systems, LLVM reverses the element order of vectors by default
960 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;
961 elems[tgt_elem_i] = try (try readFromPackedMemory(elem_ty, mod, buffer, bit_offset + bits, arena)).intern(elem_ty, mod);
962 bits += elem_bit_size;
963 }
964 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
965 .ty = ty.toIntern(),
966 .storage = .{ .elems = elems },
967 } })));
968 },
969 .Struct => {
970 // Sema is supposed to have emitted a compile error already for Auto layout structs,
971 // and Extern is handled by non-packed readFromMemory.
972 const struct_type = mod.typeToPackedStruct(ty).?;
973 var bits: u16 = 0;
974 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
975 for (field_vals, 0..) |*field_val, i| {
976 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
977 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
978 field_val.* = try (try readFromPackedMemory(field_ty, mod, buffer, bit_offset + bits, arena)).intern(field_ty, mod);
979 bits += field_bits;
980 }
981 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
982 .ty = ty.toIntern(),
983 .storage = .{ .elems = field_vals },
984 } })));
985 },
986 .Union => switch (ty.containerLayout(mod)) {
987 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
988 .Packed => {
989 const backing_ty = try ty.unionBackingType(mod);
990 const val = (try readFromPackedMemory(backing_ty, mod, buffer, bit_offset, arena)).toIntern();
991 return Value.fromInterned((try mod.intern(.{ .un = .{
992 .ty = ty.toIntern(),
993 .tag = .none,
994 .val = val,
995 } })));
996 },
997 },
998 .Pointer => {
999 assert(!ty.isSlice(mod)); // No well defined layout.
1000 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
1001 },
1002 .Optional => {
1003 assert(ty.isPtrLikeOptional(mod));
1004 const child = ty.optionalChild(mod);
1005 return readFromPackedMemory(child, mod, buffer, bit_offset, arena);
1006 },
1007 else => @panic("TODO implement readFromPackedMemory for more types"),
1008 }
1009}
1010
1011/// Asserts that the value is a float or an integer.
1012pub fn toFloat(val: Value, comptime T: type, mod: *Module) T {
1013 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1014 .int => |int| switch (int.storage) {
1015 .big_int => |big_int| @floatCast(bigIntToFloat(big_int.limbs, big_int.positive)),
1016 inline .u64, .i64 => |x| {
1017 if (T == f80) {
1018 @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
1019 }
1020 return @floatFromInt(x);
1021 },
1022 .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
1023 .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(mod)),
1024 },
1025 .float => |float| switch (float.storage) {
1026 inline else => |x| @floatCast(x),
1027 },
1028 else => unreachable,
1029 };
1030}
1031
1032/// TODO move this to std lib big int code
1033fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
1034 if (limbs.len == 0) return 0;
1035
1036 const base = std.math.maxInt(std.math.big.Limb) + 1;
1037 var result: f128 = 0;
1038 var i: usize = limbs.len;
1039 while (i != 0) {
1040 i -= 1;
1041 const limb: f128 = @as(f128, @floatFromInt(limbs[i]));
1042 result = @mulAdd(f128, base, result, limb);
1043 }
1044 if (positive) {
1045 return result;
1046 } else {
1047 return -result;
1048 }
1049}
1050
1051pub fn clz(val: Value, ty: Type, mod: *Module) u64 {
1052 var bigint_buf: BigIntSpace = undefined;
1053 const bigint = val.toBigInt(&bigint_buf, mod);
1054 return bigint.clz(ty.intInfo(mod).bits);
1055}
1056
1057pub fn ctz(val: Value, ty: Type, mod: *Module) u64 {
1058 var bigint_buf: BigIntSpace = undefined;
1059 const bigint = val.toBigInt(&bigint_buf, mod);
1060 return bigint.ctz(ty.intInfo(mod).bits);
1061}
1062
1063pub fn popCount(val: Value, ty: Type, mod: *Module) u64 {
1064 var bigint_buf: BigIntSpace = undefined;
1065 const bigint = val.toBigInt(&bigint_buf, mod);
1066 return @as(u64, @intCast(bigint.popCount(ty.intInfo(mod).bits)));
1067}
1068
1069pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1070 const info = ty.intInfo(mod);
1071
1072 var buffer: Value.BigIntSpace = undefined;
1073 const operand_bigint = val.toBigInt(&buffer, mod);
1074
1075 const limbs = try arena.alloc(
1076 std.math.big.Limb,
1077 std.math.big.int.calcTwosCompLimbCount(info.bits),
1078 );
1079 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1080 result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
1081
1082 return mod.intValue_big(ty, result_bigint.toConst());
1083}
1084
1085pub fn byteSwap(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1086 const info = ty.intInfo(mod);
1087
1088 // Bit count must be evenly divisible by 8
1089 assert(info.bits % 8 == 0);
1090
1091 var buffer: Value.BigIntSpace = undefined;
1092 const operand_bigint = val.toBigInt(&buffer, mod);
1093
1094 const limbs = try arena.alloc(
1095 std.math.big.Limb,
1096 std.math.big.int.calcTwosCompLimbCount(info.bits),
1097 );
1098 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1099 result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8);
1100
1101 return mod.intValue_big(ty, result_bigint.toConst());
1102}
1103
1104/// Asserts the value is an integer and not undefined.
1105/// Returns the number of bits the value requires to represent stored in twos complement form.
1106pub fn intBitCountTwosComp(self: Value, mod: *Module) usize {
1107 var buffer: BigIntSpace = undefined;
1108 const big_int = self.toBigInt(&buffer, mod);
1109 return big_int.bitCountTwosComp();
1110}
1111
1112/// Converts an integer or a float to a float. May result in a loss of information.
1113/// Caller can find out by equality checking the result against the operand.
1114pub fn floatCast(self: Value, dest_ty: Type, mod: *Module) !Value {
1115 const target = mod.getTarget();
1116 return Value.fromInterned((try mod.intern(.{ .float = .{
1117 .ty = dest_ty.toIntern(),
1118 .storage = switch (dest_ty.floatBits(target)) {
1119 16 => .{ .f16 = self.toFloat(f16, mod) },
1120 32 => .{ .f32 = self.toFloat(f32, mod) },
1121 64 => .{ .f64 = self.toFloat(f64, mod) },
1122 80 => .{ .f80 = self.toFloat(f80, mod) },
1123 128 => .{ .f128 = self.toFloat(f128, mod) },
1124 else => unreachable,
1125 },
1126 } })));
1127}
1128
1129/// Asserts the value is a float
1130pub fn floatHasFraction(self: Value, mod: *const Module) bool {
1131 return switch (mod.intern_pool.indexToKey(self.toIntern())) {
1132 .float => |float| switch (float.storage) {
1133 inline else => |x| @rem(x, 1) != 0,
1134 },
1135 else => unreachable,
1136 };
1137}
1138
1139pub fn orderAgainstZero(lhs: Value, mod: *Module) std.math.Order {
1140 return orderAgainstZeroAdvanced(lhs, mod, null) catch unreachable;
1141}
1142
1143pub fn orderAgainstZeroAdvanced(
1144 lhs: Value,
1145 mod: *Module,
1146 opt_sema: ?*Sema,
1147) Module.CompileError!std.math.Order {
1148 return switch (lhs.toIntern()) {
1149 .bool_false => .eq,
1150 .bool_true => .gt,
1151 else => switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1152 .ptr => |ptr| switch (ptr.addr) {
1153 .decl, .comptime_field => .gt,
1154 .int => |int| Value.fromInterned(int).orderAgainstZeroAdvanced(mod, opt_sema),
1155 .elem => |elem| switch (try Value.fromInterned(elem.base).orderAgainstZeroAdvanced(mod, opt_sema)) {
1156 .lt => unreachable,
1157 .gt => .gt,
1158 .eq => if (elem.index == 0) .eq else .gt,
1159 },
1160 else => unreachable,
1161 },
1162 .int => |int| switch (int.storage) {
1163 .big_int => |big_int| big_int.orderAgainstScalar(0),
1164 inline .u64, .i64 => |x| std.math.order(x, 0),
1165 .lazy_align => .gt, // alignment is never 0
1166 .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsAdvanced(
1167 mod,
1168 false,
1169 if (opt_sema) |sema| .{ .sema = sema } else .eager,
1170 ) catch |err| switch (err) {
1171 error.NeedLazy => unreachable,
1172 else => |e| return e,
1173 }) .gt else .eq,
1174 },
1175 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroAdvanced(mod, opt_sema),
1176 .float => |float| switch (float.storage) {
1177 inline else => |x| std.math.order(x, 0),
1178 },
1179 else => unreachable,
1180 },
1181 };
1182}
1183
1184/// Asserts the value is comparable.
1185pub fn order(lhs: Value, rhs: Value, mod: *Module) std.math.Order {
1186 return orderAdvanced(lhs, rhs, mod, null) catch unreachable;
1187}
1188
1189/// Asserts the value is comparable.
1190/// If opt_sema is null then this function asserts things are resolved and cannot fail.
1191pub fn orderAdvanced(lhs: Value, rhs: Value, mod: *Module, opt_sema: ?*Sema) !std.math.Order {
1192 const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(mod, opt_sema);
1193 const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(mod, opt_sema);
1194 switch (lhs_against_zero) {
1195 .lt => if (rhs_against_zero != .lt) return .lt,
1196 .eq => return rhs_against_zero.invert(),
1197 .gt => {},
1198 }
1199 switch (rhs_against_zero) {
1200 .lt => if (lhs_against_zero != .lt) return .gt,
1201 .eq => return lhs_against_zero,
1202 .gt => {},
1203 }
1204
1205 if (lhs.isFloat(mod) or rhs.isFloat(mod)) {
1206 const lhs_f128 = lhs.toFloat(f128, mod);
1207 const rhs_f128 = rhs.toFloat(f128, mod);
1208 return std.math.order(lhs_f128, rhs_f128);
1209 }
1210
1211 var lhs_bigint_space: BigIntSpace = undefined;
1212 var rhs_bigint_space: BigIntSpace = undefined;
1213 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, mod, opt_sema);
1214 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, mod, opt_sema);
1215 return lhs_bigint.order(rhs_bigint);
1216}
1217
1218/// Asserts the value is comparable. Does not take a type parameter because it supports
1219/// comparisons between heterogeneous types.
1220pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, mod: *Module) bool {
1221 return compareHeteroAdvanced(lhs, op, rhs, mod, null) catch unreachable;
1222}
1223
1224pub fn compareHeteroAdvanced(
1225 lhs: Value,
1226 op: std.math.CompareOperator,
1227 rhs: Value,
1228 mod: *Module,
1229 opt_sema: ?*Sema,
1230) !bool {
1231 if (lhs.pointerDecl(mod)) |lhs_decl| {
1232 if (rhs.pointerDecl(mod)) |rhs_decl| {
1233 switch (op) {
1234 .eq => return lhs_decl == rhs_decl,
1235 .neq => return lhs_decl != rhs_decl,
1236 else => {},
1237 }
1238 } else {
1239 switch (op) {
1240 .eq => return false,
1241 .neq => return true,
1242 else => {},
1243 }
1244 }
1245 } else if (rhs.pointerDecl(mod)) |_| {
1246 switch (op) {
1247 .eq => return false,
1248 .neq => return true,
1249 else => {},
1250 }
1251 }
1252 return (try orderAdvanced(lhs, rhs, mod, opt_sema)).compare(op);
1253}
1254
1255/// Asserts the values are comparable. Both operands have type `ty`.
1256/// For vectors, returns true if comparison is true for ALL elements.
1257pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) !bool {
1258 if (ty.zigTypeTag(mod) == .Vector) {
1259 const scalar_ty = ty.scalarType(mod);
1260 for (0..ty.vectorLen(mod)) |i| {
1261 const lhs_elem = try lhs.elemValue(mod, i);
1262 const rhs_elem = try rhs.elemValue(mod, i);
1263 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, mod)) {
1264 return false;
1265 }
1266 }
1267 return true;
1268 }
1269 return compareScalar(lhs, op, rhs, ty, mod);
1270}
1271
1272/// Asserts the values are comparable. Both operands have type `ty`.
1273pub fn compareScalar(
1274 lhs: Value,
1275 op: std.math.CompareOperator,
1276 rhs: Value,
1277 ty: Type,
1278 mod: *Module,
1279) bool {
1280 return switch (op) {
1281 .eq => lhs.eql(rhs, ty, mod),
1282 .neq => !lhs.eql(rhs, ty, mod),
1283 else => compareHetero(lhs, op, rhs, mod),
1284 };
1285}
1286
1287/// Asserts the value is comparable.
1288/// For vectors, returns true if comparison is true for ALL elements.
1289///
1290/// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
1291pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, mod: *Module) bool {
1292 return compareAllWithZeroAdvancedExtra(lhs, op, mod, null) catch unreachable;
1293}
1294
1295pub fn compareAllWithZeroAdvanced(
1296 lhs: Value,
1297 op: std.math.CompareOperator,
1298 sema: *Sema,
1299) Module.CompileError!bool {
1300 return compareAllWithZeroAdvancedExtra(lhs, op, sema.mod, sema);
1301}
1302
1303pub fn compareAllWithZeroAdvancedExtra(
1304 lhs: Value,
1305 op: std.math.CompareOperator,
1306 mod: *Module,
1307 opt_sema: ?*Sema,
1308) Module.CompileError!bool {
1309 if (lhs.isInf(mod)) {
1310 switch (op) {
1311 .neq => return true,
1312 .eq => return false,
1313 .gt, .gte => return !lhs.isNegativeInf(mod),
1314 .lt, .lte => return lhs.isNegativeInf(mod),
1315 }
1316 }
1317
1318 switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1319 .float => |float| switch (float.storage) {
1320 inline else => |x| if (std.math.isNan(x)) return op == .neq,
1321 },
1322 .aggregate => |aggregate| return switch (aggregate.storage) {
1323 .bytes => |bytes| for (bytes) |byte| {
1324 if (!std.math.order(byte, 0).compare(op)) break false;
1325 } else true,
1326 .elems => |elems| for (elems) |elem| {
1327 if (!try Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema)) break false;
1328 } else true,
1329 .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema),
1330 },
1331 else => {},
1332 }
1333 return (try orderAgainstZeroAdvanced(lhs, mod, opt_sema)).compare(op);
1334}
1335
1336pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
1337 assert(mod.intern_pool.typeOf(a.toIntern()) == ty.toIntern());
1338 assert(mod.intern_pool.typeOf(b.toIntern()) == ty.toIntern());
1339 return a.toIntern() == b.toIntern();
1340}
1341
1342pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
1343 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1344 .slice => |slice| return Value.fromInterned(slice.ptr).isComptimeMutablePtr(mod),
1345 .ptr => |ptr| switch (ptr.addr) {
1346 .comptime_field => true,
1347 .eu_payload, .opt_payload => |base_ptr| Value.fromInterned(base_ptr).isComptimeMutablePtr(mod),
1348 .elem, .field => |base_index| Value.fromInterned(base_index.base).isComptimeMutablePtr(mod),
1349 else => false,
1350 },
1351 else => false,
1352 };
1353}
1354
1355pub fn canMutateComptimeVarState(val: Value, mod: *Module) bool {
1356 return val.isComptimeMutablePtr(mod) or switch (val.toIntern()) {
1357 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1358 .error_union => |error_union| switch (error_union.val) {
1359 .err_name => false,
1360 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1361 },
1362 .ptr => |ptr| switch (ptr.addr) {
1363 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(mod),
1364 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).canMutateComptimeVarState(mod),
1365 .elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(mod),
1366 else => false,
1367 },
1368 .opt => |opt| switch (opt.val) {
1369 .none => false,
1370 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1371 },
1372 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| {
1373 if (Value.fromInterned(elem).canMutateComptimeVarState(mod)) break true;
1374 } else false,
1375 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(mod),
1376 else => false,
1377 },
1378 };
1379}
1380
1381/// Gets the decl referenced by this pointer. If the pointer does not point
1382/// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),
1383/// this function returns null.
1384pub fn pointerDecl(val: Value, mod: *Module) ?InternPool.DeclIndex {
1385 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1386 .variable => |variable| variable.decl,
1387 .extern_func => |extern_func| extern_func.decl,
1388 .func => |func| func.owner_decl,
1389 .ptr => |ptr| switch (ptr.addr) {
1390 .decl => |decl| decl,
1391 else => null,
1392 },
1393 else => null,
1394 };
1395}
1396
1397pub const slice_ptr_index = 0;
1398pub const slice_len_index = 1;
1399
1400pub fn slicePtr(val: Value, mod: *Module) Value {
1401 return Value.fromInterned(mod.intern_pool.slicePtr(val.toIntern()));
1402}
1403
1404pub fn sliceLen(val: Value, mod: *Module) u64 {
1405 const ip = &mod.intern_pool;
1406 return switch (ip.indexToKey(val.toIntern())) {
1407 .ptr => |ptr| switch (ip.indexToKey(switch (ptr.addr) {
1408 .decl => |decl| mod.declPtr(decl).ty.toIntern(),
1409 .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(),
1410 .anon_decl => |anon_decl| ip.typeOf(anon_decl.val),
1411 .comptime_field => |comptime_field| ip.typeOf(comptime_field),
1412 else => unreachable,
1413 })) {
1414 .array_type => |array_type| array_type.len,
1415 else => 1,
1416 },
1417 .slice => |slice| Value.fromInterned(slice.len).toUnsignedInt(mod),
1418 else => unreachable,
1419 };
1420}
1421
1422/// Asserts the value is a single-item pointer to an array, or an array,
1423/// or an unknown-length pointer, and returns the element value at the index.
1424pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
1425 return (try val.maybeElemValue(mod, index)).?;
1426}
1427
1428/// Like `elemValue`, but returns `null` instead of asserting on failure.
1429pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value {
1430 return switch (val.ip_index) {
1431 .none => switch (val.tag()) {
1432 .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]),
1433 .repeated => val.castTag(.repeated).?.data,
1434 .aggregate => val.castTag(.aggregate).?.data[index],
1435 .slice => val.castTag(.slice).?.data.ptr.maybeElemValue(mod, index),
1436 else => null,
1437 },
1438 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1439 .undef => |ty| Value.fromInterned((try mod.intern(.{
1440 .undef = Type.fromInterned(ty).elemType2(mod).toIntern(),
1441 }))),
1442 .slice => |slice| return Value.fromInterned(slice.ptr).maybeElemValue(mod, index),
1443 .ptr => |ptr| switch (ptr.addr) {
1444 .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index),
1445 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValue(mod, index),
1446 .int, .eu_payload => null,
1447 .opt_payload => |base| Value.fromInterned(base).maybeElemValue(mod, index),
1448 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValue(mod, index),
1449 .elem => |elem| Value.fromInterned(elem.base).maybeElemValue(mod, index + @as(usize, @intCast(elem.index))),
1450 .field => |field| if (Value.fromInterned(field.base).pointerDecl(mod)) |decl_index| {
1451 const base_decl = mod.declPtr(decl_index);
1452 const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index)));
1453 return field_val.maybeElemValue(mod, index);
1454 } else null,
1455 },
1456 .opt => |opt| Value.fromInterned(opt.val).maybeElemValue(mod, index),
1457 .aggregate => |aggregate| {
1458 const len = mod.intern_pool.aggregateTypeLen(aggregate.ty);
1459 if (index < len) return Value.fromInterned(switch (aggregate.storage) {
1460 .bytes => |bytes| try mod.intern(.{ .int = .{
1461 .ty = .u8_type,
1462 .storage = .{ .u64 = bytes[index] },
1463 } }),
1464 .elems => |elems| elems[index],
1465 .repeated_elem => |elem| elem,
1466 });
1467 assert(index == len);
1468 return Value.fromInterned(mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel);
1469 },
1470 else => null,
1471 },
1472 };
1473}
1474
1475pub fn isLazyAlign(val: Value, mod: *Module) bool {
1476 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1477 .int => |int| int.storage == .lazy_align,
1478 else => false,
1479 };
1480}
1481
1482pub fn isLazySize(val: Value, mod: *Module) bool {
1483 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1484 .int => |int| int.storage == .lazy_size,
1485 else => false,
1486 };
1487}
1488
1489pub fn isPtrToThreadLocal(val: Value, mod: *Module) bool {
1490 const backing_decl = mod.intern_pool.getBackingDecl(val.toIntern()).unwrap() orelse return false;
1491 const variable = mod.declPtr(backing_decl).getOwnedVariable(mod) orelse return false;
1492 return variable.is_threadlocal;
1493}
1494
1495// Asserts that the provided start/end are in-bounds.
1496pub fn sliceArray(
1497 val: Value,
1498 mod: *Module,
1499 arena: Allocator,
1500 start: usize,
1501 end: usize,
1502) error{OutOfMemory}!Value {
1503 // TODO: write something like getCoercedInts to avoid needing to dupe
1504 assert(val.ip_index != .none);
1505 switch (mod.intern_pool.indexToKey(val.toIntern())) {
1506 .ptr => |ptr| switch (ptr.addr) {
1507 .decl => |decl| try mod.declPtr(decl).val.sliceArray(mod, arena, start, end),
1508 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
1509 .sliceArray(mod, arena, start, end),
1510 .elem => |elem| Value.fromInterned(elem.base)
1511 .sliceArray(mod, arena, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))),
1512 else => unreachable,
1513 },
1514 .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{
1515 .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) {
1516 .array_type => |array_type| try mod.arrayType(.{
1517 .len = @as(u32, @intCast(end - start)),
1518 .child = array_type.child,
1519 .sentinel = if (end == array_type.len) array_type.sentinel else .none,
1520 }),
1521 .vector_type => |vector_type| try mod.vectorType(.{
1522 .len = @as(u32, @intCast(end - start)),
1523 .child = vector_type.child,
1524 }),
1525 else => unreachable,
1526 }.toIntern(),
1527 .storage = switch (aggregate.storage) {
1528 .bytes => .{ .bytes = try arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) },
1529 .elems => .{ .elems = try arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) },
1530 .repeated_elem => |elem| .{ .repeated_elem = elem },
1531 },
1532 } }))),
1533 else => unreachable,
1534 }
1535}
1536
1537pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value {
1538 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1539 .undef => |ty| Value.fromInterned((try mod.intern(.{
1540 .undef = Type.fromInterned(ty).structFieldType(index, mod).toIntern(),
1541 }))),
1542 .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) {
1543 .bytes => |bytes| try mod.intern(.{ .int = .{
1544 .ty = .u8_type,
1545 .storage = .{ .u64 = bytes[index] },
1546 } }),
1547 .elems => |elems| elems[index],
1548 .repeated_elem => |elem| elem,
1549 }),
1550 // TODO assert the tag is correct
1551 .un => |un| Value.fromInterned(un.val),
1552 else => unreachable,
1553 };
1554}
1555
1556pub fn unionTag(val: Value, mod: *Module) ?Value {
1557 if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
1558 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1559 .undef, .enum_tag => val,
1560 .un => |un| if (un.tag != .none) Value.fromInterned(un.tag) else return null,
1561 else => unreachable,
1562 };
1563}
1564
1565pub fn unionValue(val: Value, mod: *Module) Value {
1566 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1567 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1568 .un => |un| Value.fromInterned(un.val),
1569 else => unreachable,
1570 };
1571}
1572
1573/// Returns a pointer to the element value at the index.
1574pub fn elemPtr(
1575 val: Value,
1576 elem_ptr_ty: Type,
1577 index: usize,
1578 mod: *Module,
1579) Allocator.Error!Value {
1580 const elem_ty = elem_ptr_ty.childType(mod);
1581 const ptr_val = switch (mod.intern_pool.indexToKey(val.toIntern())) {
1582 .slice => |slice| Value.fromInterned(slice.ptr),
1583 else => val,
1584 };
1585 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
1586 .ptr => |ptr| switch (ptr.addr) {
1587 .elem => |elem| if (Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).eql(elem_ty, mod))
1588 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1589 .ty = elem_ptr_ty.toIntern(),
1590 .addr = .{ .elem = .{
1591 .base = elem.base,
1592 .index = elem.index + index,
1593 } },
1594 } }))),
1595 else => {},
1596 },
1597 else => {},
1598 }
1599 var ptr_ty_key = mod.intern_pool.indexToKey(elem_ptr_ty.toIntern()).ptr_type;
1600 assert(ptr_ty_key.flags.size != .Slice);
1601 ptr_ty_key.flags.size = .Many;
1602 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1603 .ty = elem_ptr_ty.toIntern(),
1604 .addr = .{ .elem = .{
1605 .base = (try mod.getCoerced(ptr_val, try mod.ptrType(ptr_ty_key))).toIntern(),
1606 .index = index,
1607 } },
1608 } })));
1609}
1610
1611pub fn isUndef(val: Value, mod: *Module) bool {
1612 return val.ip_index != .none and mod.intern_pool.isUndef(val.toIntern());
1613}
1614
1615/// TODO: check for cases such as array that is not marked undef but all the element
1616/// values are marked undef, or struct that is not marked undef but all fields are marked
1617/// undef, etc.
1618pub fn isUndefDeep(val: Value, mod: *Module) bool {
1619 return val.isUndef(mod);
1620}
1621
1622/// Returns true if any value contained in `self` is undefined.
1623pub fn anyUndef(val: Value, mod: *Module) !bool {
1624 if (val.ip_index == .none) return false;
1625 return switch (val.toIntern()) {
1626 .undef => true,
1627 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1628 .undef => true,
1629 .simple_value => |v| v == .undefined,
1630 .ptr => |ptr| switch (ptr.len) {
1631 .none => false,
1632 else => for (0..@as(usize, @intCast(Value.fromInterned(ptr.len).toUnsignedInt(mod)))) |index| {
1633 if (try (try val.elemValue(mod, index)).anyUndef(mod)) break true;
1634 } else false,
1635 },
1636 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
1637 const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
1638 if (try anyUndef(Value.fromInterned(elem), mod)) break true;
1639 } else false,
1640 else => false,
1641 },
1642 };
1643}
1644
1645/// Asserts the value is not undefined and not unreachable.
1646/// C pointers with an integer value of 0 are also considered null.
1647pub fn isNull(val: Value, mod: *Module) bool {
1648 return switch (val.toIntern()) {
1649 .undef => unreachable,
1650 .unreachable_value => unreachable,
1651 .null_value => true,
1652 else => return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1653 .undef => unreachable,
1654 .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| {
1655 if (try (try val.elemValue(mod, idx)).anyUndef(mod)) break true;
1656 } else false,
1657 .opt => |opt| opt.val == .none,
1658 else => false,
1659 },
1660 };
1661}
1662
1663/// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.
1664pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTerminatedString {
1665 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1666 .err => |err| err.name.toOptional(),
1667 .error_union => |error_union| switch (error_union.val) {
1668 .err_name => |err_name| err_name.toOptional(),
1669 .payload => .none,
1670 },
1671 else => unreachable,
1672 };
1673}
1674
1675pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt {
1676 return if (getErrorName(val, mod).unwrap()) |err_name|
1677 @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?))
1678 else
1679 0;
1680}
1681
1682/// Assumes the type is an error union. Returns true if and only if the value is
1683/// the error union payload, not an error.
1684pub fn errorUnionIsPayload(val: Value, mod: *const Module) bool {
1685 return mod.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;
1686}
1687
1688/// Value of the optional, null if optional has no payload.
1689pub fn optionalValue(val: Value, mod: *const Module) ?Value {
1690 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1691 .opt => |opt| switch (opt.val) {
1692 .none => null,
1693 else => |payload| Value.fromInterned(payload),
1694 },
1695 .ptr => val,
1696 else => unreachable,
1697 };
1698}
1699
1700/// Valid for all types. Asserts the value is not undefined.
1701pub fn isFloat(self: Value, mod: *const Module) bool {
1702 return switch (self.toIntern()) {
1703 .undef => unreachable,
1704 else => switch (mod.intern_pool.indexToKey(self.toIntern())) {
1705 .undef => unreachable,
1706 .float => true,
1707 else => false,
1708 },
1709 };
1710}
1711
1712pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module) !Value {
1713 return floatFromIntAdvanced(val, arena, int_ty, float_ty, mod, null) catch |err| switch (err) {
1714 error.OutOfMemory => return error.OutOfMemory,
1715 else => unreachable,
1716 };
1717}
1718
1719pub fn floatFromIntAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1720 if (int_ty.zigTypeTag(mod) == .Vector) {
1721 const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(mod));
1722 const scalar_ty = float_ty.scalarType(mod);
1723 for (result_data, 0..) |*scalar, i| {
1724 const elem_val = try val.elemValue(mod, i);
1725 scalar.* = try (try floatFromIntScalar(elem_val, scalar_ty, mod, opt_sema)).intern(scalar_ty, mod);
1726 }
1727 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1728 .ty = float_ty.toIntern(),
1729 .storage = .{ .elems = result_data },
1730 } })));
1731 }
1732 return floatFromIntScalar(val, float_ty, mod, opt_sema);
1733}
1734
1735pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1736 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1737 .undef => try mod.undefValue(float_ty),
1738 .int => |int| switch (int.storage) {
1739 .big_int => |big_int| {
1740 const float = bigIntToFloat(big_int.limbs, big_int.positive);
1741 return mod.floatValue(float_ty, float);
1742 },
1743 inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, mod),
1744 .lazy_align => |ty| if (opt_sema) |sema| {
1745 return floatFromIntInner((try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0), float_ty, mod);
1746 } else {
1747 return floatFromIntInner(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0), float_ty, mod);
1748 },
1749 .lazy_size => |ty| if (opt_sema) |sema| {
1750 return floatFromIntInner((try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar, float_ty, mod);
1751 } else {
1752 return floatFromIntInner(Type.fromInterned(ty).abiSize(mod), float_ty, mod);
1753 },
1754 },
1755 else => unreachable,
1756 };
1757}
1758
1759fn floatFromIntInner(x: anytype, dest_ty: Type, mod: *Module) !Value {
1760 const target = mod.getTarget();
1761 const storage: InternPool.Key.Float.Storage = switch (dest_ty.floatBits(target)) {
1762 16 => .{ .f16 = @floatFromInt(x) },
1763 32 => .{ .f32 = @floatFromInt(x) },
1764 64 => .{ .f64 = @floatFromInt(x) },
1765 80 => .{ .f80 = @floatFromInt(x) },
1766 128 => .{ .f128 = @floatFromInt(x) },
1767 else => unreachable,
1768 };
1769 return Value.fromInterned((try mod.intern(.{ .float = .{
1770 .ty = dest_ty.toIntern(),
1771 .storage = storage,
1772 } })));
1773}
1774
1775fn calcLimbLenFloat(scalar: anytype) usize {
1776 if (scalar == 0) {
1777 return 1;
1778 }
1779
1780 const w_value = @abs(scalar);
1781 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).Int.bits) + 1;
1782}
1783
1784pub const OverflowArithmeticResult = struct {
1785 overflow_bit: Value,
1786 wrapped_result: Value,
1787};
1788
1789/// Supports (vectors of) integers only; asserts neither operand is undefined.
1790pub fn intAddSat(
1791 lhs: Value,
1792 rhs: Value,
1793 ty: Type,
1794 arena: Allocator,
1795 mod: *Module,
1796) !Value {
1797 if (ty.zigTypeTag(mod) == .Vector) {
1798 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
1799 const scalar_ty = ty.scalarType(mod);
1800 for (result_data, 0..) |*scalar, i| {
1801 const lhs_elem = try lhs.elemValue(mod, i);
1802 const rhs_elem = try rhs.elemValue(mod, i);
1803 scalar.* = try (try intAddSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
1804 }
1805 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1806 .ty = ty.toIntern(),
1807 .storage = .{ .elems = result_data },
1808 } })));
1809 }
1810 return intAddSatScalar(lhs, rhs, ty, arena, mod);
1811}
1812
1813/// Supports integers only; asserts neither operand is undefined.
1814pub fn intAddSatScalar(
1815 lhs: Value,
1816 rhs: Value,
1817 ty: Type,
1818 arena: Allocator,
1819 mod: *Module,
1820) !Value {
1821 assert(!lhs.isUndef(mod));
1822 assert(!rhs.isUndef(mod));
1823
1824 const info = ty.intInfo(mod);
1825
1826 var lhs_space: Value.BigIntSpace = undefined;
1827 var rhs_space: Value.BigIntSpace = undefined;
1828 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
1829 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
1830 const limbs = try arena.alloc(
1831 std.math.big.Limb,
1832 std.math.big.int.calcTwosCompLimbCount(info.bits),
1833 );
1834 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1835 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1836 return mod.intValue_big(ty, result_bigint.toConst());
1837}
1838
1839/// Supports (vectors of) integers only; asserts neither operand is undefined.
1840pub fn intSubSat(
1841 lhs: Value,
1842 rhs: Value,
1843 ty: Type,
1844 arena: Allocator,
1845 mod: *Module,
1846) !Value {
1847 if (ty.zigTypeTag(mod) == .Vector) {
1848 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
1849 const scalar_ty = ty.scalarType(mod);
1850 for (result_data, 0..) |*scalar, i| {
1851 const lhs_elem = try lhs.elemValue(mod, i);
1852 const rhs_elem = try rhs.elemValue(mod, i);
1853 scalar.* = try (try intSubSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
1854 }
1855 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1856 .ty = ty.toIntern(),
1857 .storage = .{ .elems = result_data },
1858 } })));
1859 }
1860 return intSubSatScalar(lhs, rhs, ty, arena, mod);
1861}
1862
1863/// Supports integers only; asserts neither operand is undefined.
1864pub fn intSubSatScalar(
1865 lhs: Value,
1866 rhs: Value,
1867 ty: Type,
1868 arena: Allocator,
1869 mod: *Module,
1870) !Value {
1871 assert(!lhs.isUndef(mod));
1872 assert(!rhs.isUndef(mod));
1873
1874 const info = ty.intInfo(mod);
1875
1876 var lhs_space: Value.BigIntSpace = undefined;
1877 var rhs_space: Value.BigIntSpace = undefined;
1878 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
1879 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
1880 const limbs = try arena.alloc(
1881 std.math.big.Limb,
1882 std.math.big.int.calcTwosCompLimbCount(info.bits),
1883 );
1884 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1885 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1886 return mod.intValue_big(ty, result_bigint.toConst());
1887}
1888
1889pub fn intMulWithOverflow(
1890 lhs: Value,
1891 rhs: Value,
1892 ty: Type,
1893 arena: Allocator,
1894 mod: *Module,
1895) !OverflowArithmeticResult {
1896 if (ty.zigTypeTag(mod) == .Vector) {
1897 const vec_len = ty.vectorLen(mod);
1898 const overflowed_data = try arena.alloc(InternPool.Index, vec_len);
1899 const result_data = try arena.alloc(InternPool.Index, vec_len);
1900 const scalar_ty = ty.scalarType(mod);
1901 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
1902 const lhs_elem = try lhs.elemValue(mod, i);
1903 const rhs_elem = try rhs.elemValue(mod, i);
1904 const of_math_result = try intMulWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
1905 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
1906 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
1907 }
1908 return OverflowArithmeticResult{
1909 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
1910 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
1911 .storage = .{ .elems = overflowed_data },
1912 } }))),
1913 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
1914 .ty = ty.toIntern(),
1915 .storage = .{ .elems = result_data },
1916 } }))),
1917 };
1918 }
1919 return intMulWithOverflowScalar(lhs, rhs, ty, arena, mod);
1920}
1921
1922pub fn intMulWithOverflowScalar(
1923 lhs: Value,
1924 rhs: Value,
1925 ty: Type,
1926 arena: Allocator,
1927 mod: *Module,
1928) !OverflowArithmeticResult {
1929 const info = ty.intInfo(mod);
1930
1931 var lhs_space: Value.BigIntSpace = undefined;
1932 var rhs_space: Value.BigIntSpace = undefined;
1933 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
1934 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
1935 const limbs = try arena.alloc(
1936 std.math.big.Limb,
1937 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
1938 );
1939 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1940 const limbs_buffer = try arena.alloc(
1941 std.math.big.Limb,
1942 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
1943 );
1944 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
1945
1946 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
1947 if (overflowed) {
1948 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
1949 }
1950
1951 return OverflowArithmeticResult{
1952 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
1953 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
1954 };
1955}
1956
1957/// Supports both (vectors of) floats and ints; handles undefined scalars.
1958pub fn numberMulWrap(
1959 lhs: Value,
1960 rhs: Value,
1961 ty: Type,
1962 arena: Allocator,
1963 mod: *Module,
1964) !Value {
1965 if (ty.zigTypeTag(mod) == .Vector) {
1966 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
1967 const scalar_ty = ty.scalarType(mod);
1968 for (result_data, 0..) |*scalar, i| {
1969 const lhs_elem = try lhs.elemValue(mod, i);
1970 const rhs_elem = try rhs.elemValue(mod, i);
1971 scalar.* = try (try numberMulWrapScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
1972 }
1973 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1974 .ty = ty.toIntern(),
1975 .storage = .{ .elems = result_data },
1976 } })));
1977 }
1978 return numberMulWrapScalar(lhs, rhs, ty, arena, mod);
1979}
1980
1981/// Supports both floats and ints; handles undefined.
1982pub fn numberMulWrapScalar(
1983 lhs: Value,
1984 rhs: Value,
1985 ty: Type,
1986 arena: Allocator,
1987 mod: *Module,
1988) !Value {
1989 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
1990
1991 if (ty.zigTypeTag(mod) == .ComptimeInt) {
1992 return intMul(lhs, rhs, ty, undefined, arena, mod);
1993 }
1994
1995 if (ty.isAnyFloat()) {
1996 return floatMul(lhs, rhs, ty, arena, mod);
1997 }
1998
1999 const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, mod);
2000 return overflow_result.wrapped_result;
2001}
2002
2003/// Supports (vectors of) integers only; asserts neither operand is undefined.
2004pub fn intMulSat(
2005 lhs: Value,
2006 rhs: Value,
2007 ty: Type,
2008 arena: Allocator,
2009 mod: *Module,
2010) !Value {
2011 if (ty.zigTypeTag(mod) == .Vector) {
2012 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2013 const scalar_ty = ty.scalarType(mod);
2014 for (result_data, 0..) |*scalar, i| {
2015 const lhs_elem = try lhs.elemValue(mod, i);
2016 const rhs_elem = try rhs.elemValue(mod, i);
2017 scalar.* = try (try intMulSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2018 }
2019 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2020 .ty = ty.toIntern(),
2021 .storage = .{ .elems = result_data },
2022 } })));
2023 }
2024 return intMulSatScalar(lhs, rhs, ty, arena, mod);
2025}
2026
2027/// Supports (vectors of) integers only; asserts neither operand is undefined.
2028pub fn intMulSatScalar(
2029 lhs: Value,
2030 rhs: Value,
2031 ty: Type,
2032 arena: Allocator,
2033 mod: *Module,
2034) !Value {
2035 assert(!lhs.isUndef(mod));
2036 assert(!rhs.isUndef(mod));
2037
2038 const info = ty.intInfo(mod);
2039
2040 var lhs_space: Value.BigIntSpace = undefined;
2041 var rhs_space: Value.BigIntSpace = undefined;
2042 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2043 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2044 const limbs = try arena.alloc(
2045 std.math.big.Limb,
2046 @max(
2047 // For the saturate
2048 std.math.big.int.calcTwosCompLimbCount(info.bits),
2049 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2050 ),
2051 );
2052 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2053 const limbs_buffer = try arena.alloc(
2054 std.math.big.Limb,
2055 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2056 );
2057 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2058 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
2059 return mod.intValue_big(ty, result_bigint.toConst());
2060}
2061
2062/// Supports both floats and ints; handles undefined.
2063pub fn numberMax(lhs: Value, rhs: Value, mod: *Module) Value {
2064 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2065 if (lhs.isNan(mod)) return rhs;
2066 if (rhs.isNan(mod)) return lhs;
2067
2068 return switch (order(lhs, rhs, mod)) {
2069 .lt => rhs,
2070 .gt, .eq => lhs,
2071 };
2072}
2073
2074/// Supports both floats and ints; handles undefined.
2075pub fn numberMin(lhs: Value, rhs: Value, mod: *Module) Value {
2076 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2077 if (lhs.isNan(mod)) return rhs;
2078 if (rhs.isNan(mod)) return lhs;
2079
2080 return switch (order(lhs, rhs, mod)) {
2081 .lt => lhs,
2082 .gt, .eq => rhs,
2083 };
2084}
2085
2086/// operands must be (vectors of) integers; handles undefined scalars.
2087pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2088 if (ty.zigTypeTag(mod) == .Vector) {
2089 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2090 const scalar_ty = ty.scalarType(mod);
2091 for (result_data, 0..) |*scalar, i| {
2092 const elem_val = try val.elemValue(mod, i);
2093 scalar.* = try (try bitwiseNotScalar(elem_val, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2094 }
2095 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2096 .ty = ty.toIntern(),
2097 .storage = .{ .elems = result_data },
2098 } })));
2099 }
2100 return bitwiseNotScalar(val, ty, arena, mod);
2101}
2102
2103/// operands must be integers; handles undefined.
2104pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2105 if (val.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2106 if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());
2107
2108 const info = ty.intInfo(mod);
2109
2110 if (info.bits == 0) {
2111 return val;
2112 }
2113
2114 // TODO is this a performance issue? maybe we should try the operation without
2115 // resorting to BigInt first.
2116 var val_space: Value.BigIntSpace = undefined;
2117 const val_bigint = val.toBigInt(&val_space, mod);
2118 const limbs = try arena.alloc(
2119 std.math.big.Limb,
2120 std.math.big.int.calcTwosCompLimbCount(info.bits),
2121 );
2122
2123 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2124 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2125 return mod.intValue_big(ty, result_bigint.toConst());
2126}
2127
2128/// operands must be (vectors of) integers; handles undefined scalars.
2129pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2130 if (ty.zigTypeTag(mod) == .Vector) {
2131 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2132 const scalar_ty = ty.scalarType(mod);
2133 for (result_data, 0..) |*scalar, i| {
2134 const lhs_elem = try lhs.elemValue(mod, i);
2135 const rhs_elem = try rhs.elemValue(mod, i);
2136 scalar.* = try (try bitwiseAndScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2137 }
2138 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2139 .ty = ty.toIntern(),
2140 .storage = .{ .elems = result_data },
2141 } })));
2142 }
2143 return bitwiseAndScalar(lhs, rhs, ty, allocator, mod);
2144}
2145
2146/// operands must be integers; handles undefined.
2147pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2148 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2149 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
2150
2151 // TODO is this a performance issue? maybe we should try the operation without
2152 // resorting to BigInt first.
2153 var lhs_space: Value.BigIntSpace = undefined;
2154 var rhs_space: Value.BigIntSpace = undefined;
2155 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2156 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2157 const limbs = try arena.alloc(
2158 std.math.big.Limb,
2159 // + 1 for negatives
2160 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2161 );
2162 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2163 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2164 return mod.intValue_big(ty, result_bigint.toConst());
2165}
2166
2167/// operands must be (vectors of) integers; handles undefined scalars.
2168pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2169 if (ty.zigTypeTag(mod) == .Vector) {
2170 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2171 const scalar_ty = ty.scalarType(mod);
2172 for (result_data, 0..) |*scalar, i| {
2173 const lhs_elem = try lhs.elemValue(mod, i);
2174 const rhs_elem = try rhs.elemValue(mod, i);
2175 scalar.* = try (try bitwiseNandScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2176 }
2177 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2178 .ty = ty.toIntern(),
2179 .storage = .{ .elems = result_data },
2180 } })));
2181 }
2182 return bitwiseNandScalar(lhs, rhs, ty, arena, mod);
2183}
2184
2185/// operands must be integers; handles undefined.
2186pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2187 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2188 if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
2189
2190 const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
2191 const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);
2192 return bitwiseXor(anded, all_ones, ty, arena, mod);
2193}
2194
2195/// operands must be (vectors of) integers; handles undefined scalars.
2196pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2197 if (ty.zigTypeTag(mod) == .Vector) {
2198 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2199 const scalar_ty = ty.scalarType(mod);
2200 for (result_data, 0..) |*scalar, i| {
2201 const lhs_elem = try lhs.elemValue(mod, i);
2202 const rhs_elem = try rhs.elemValue(mod, i);
2203 scalar.* = try (try bitwiseOrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2204 }
2205 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2206 .ty = ty.toIntern(),
2207 .storage = .{ .elems = result_data },
2208 } })));
2209 }
2210 return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);
2211}
2212
2213/// operands must be integers; handles undefined.
2214pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2215 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2216 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
2217
2218 // TODO is this a performance issue? maybe we should try the operation without
2219 // resorting to BigInt first.
2220 var lhs_space: Value.BigIntSpace = undefined;
2221 var rhs_space: Value.BigIntSpace = undefined;
2222 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2223 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2224 const limbs = try arena.alloc(
2225 std.math.big.Limb,
2226 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2227 );
2228 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2229 result_bigint.bitOr(lhs_bigint, rhs_bigint);
2230 return mod.intValue_big(ty, result_bigint.toConst());
2231}
2232
2233/// operands must be (vectors of) integers; handles undefined scalars.
2234pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2235 if (ty.zigTypeTag(mod) == .Vector) {
2236 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2237 const scalar_ty = ty.scalarType(mod);
2238 for (result_data, 0..) |*scalar, i| {
2239 const lhs_elem = try lhs.elemValue(mod, i);
2240 const rhs_elem = try rhs.elemValue(mod, i);
2241 scalar.* = try (try bitwiseXorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2242 }
2243 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2244 .ty = ty.toIntern(),
2245 .storage = .{ .elems = result_data },
2246 } })));
2247 }
2248 return bitwiseXorScalar(lhs, rhs, ty, allocator, mod);
2249}
2250
2251/// operands must be integers; handles undefined.
2252pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2253 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2254 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() != rhs.toBool());
2255
2256 // TODO is this a performance issue? maybe we should try the operation without
2257 // resorting to BigInt first.
2258 var lhs_space: Value.BigIntSpace = undefined;
2259 var rhs_space: Value.BigIntSpace = undefined;
2260 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2261 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2262 const limbs = try arena.alloc(
2263 std.math.big.Limb,
2264 // + 1 for negatives
2265 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2266 );
2267 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2268 result_bigint.bitXor(lhs_bigint, rhs_bigint);
2269 return mod.intValue_big(ty, result_bigint.toConst());
2270}
2271
2272/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2273/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2274pub fn intDiv(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2275 var overflow: usize = undefined;
2276 return intDivInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2277 error.Overflow => {
2278 const is_vec = ty.isVector(mod);
2279 overflow_idx.* = if (is_vec) overflow else 0;
2280 const safe_ty = if (is_vec) try mod.vectorType(.{
2281 .len = ty.vectorLen(mod),
2282 .child = .comptime_int_type,
2283 }) else Type.comptime_int;
2284 return intDivInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2285 error.Overflow => unreachable,
2286 else => |e| return e,
2287 };
2288 },
2289 else => |e| return e,
2290 };
2291}
2292
2293fn intDivInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2294 if (ty.zigTypeTag(mod) == .Vector) {
2295 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2296 const scalar_ty = ty.scalarType(mod);
2297 for (result_data, 0..) |*scalar, i| {
2298 const lhs_elem = try lhs.elemValue(mod, i);
2299 const rhs_elem = try rhs.elemValue(mod, i);
2300 const val = intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2301 error.Overflow => {
2302 overflow_idx.* = i;
2303 return error.Overflow;
2304 },
2305 else => |e| return e,
2306 };
2307 scalar.* = try val.intern(scalar_ty, mod);
2308 }
2309 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2310 .ty = ty.toIntern(),
2311 .storage = .{ .elems = result_data },
2312 } })));
2313 }
2314 return intDivScalar(lhs, rhs, ty, allocator, mod);
2315}
2316
2317pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2318 // TODO is this a performance issue? maybe we should try the operation without
2319 // resorting to BigInt first.
2320 var lhs_space: Value.BigIntSpace = undefined;
2321 var rhs_space: Value.BigIntSpace = undefined;
2322 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2323 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2324 const limbs_q = try allocator.alloc(
2325 std.math.big.Limb,
2326 lhs_bigint.limbs.len,
2327 );
2328 const limbs_r = try allocator.alloc(
2329 std.math.big.Limb,
2330 rhs_bigint.limbs.len,
2331 );
2332 const limbs_buffer = try allocator.alloc(
2333 std.math.big.Limb,
2334 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2335 );
2336 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2337 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2338 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2339 if (ty.toIntern() != .comptime_int_type) {
2340 const info = ty.intInfo(mod);
2341 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
2342 return error.Overflow;
2343 }
2344 }
2345 return mod.intValue_big(ty, result_q.toConst());
2346}
2347
2348pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2349 if (ty.zigTypeTag(mod) == .Vector) {
2350 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2351 const scalar_ty = ty.scalarType(mod);
2352 for (result_data, 0..) |*scalar, i| {
2353 const lhs_elem = try lhs.elemValue(mod, i);
2354 const rhs_elem = try rhs.elemValue(mod, i);
2355 scalar.* = try (try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2356 }
2357 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2358 .ty = ty.toIntern(),
2359 .storage = .{ .elems = result_data },
2360 } })));
2361 }
2362 return intDivFloorScalar(lhs, rhs, ty, allocator, mod);
2363}
2364
2365pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2366 // TODO is this a performance issue? maybe we should try the operation without
2367 // resorting to BigInt first.
2368 var lhs_space: Value.BigIntSpace = undefined;
2369 var rhs_space: Value.BigIntSpace = undefined;
2370 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2371 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2372 const limbs_q = try allocator.alloc(
2373 std.math.big.Limb,
2374 lhs_bigint.limbs.len,
2375 );
2376 const limbs_r = try allocator.alloc(
2377 std.math.big.Limb,
2378 rhs_bigint.limbs.len,
2379 );
2380 const limbs_buffer = try allocator.alloc(
2381 std.math.big.Limb,
2382 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2383 );
2384 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2385 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2386 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2387 return mod.intValue_big(ty, result_q.toConst());
2388}
2389
2390pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2391 if (ty.zigTypeTag(mod) == .Vector) {
2392 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2393 const scalar_ty = ty.scalarType(mod);
2394 for (result_data, 0..) |*scalar, i| {
2395 const lhs_elem = try lhs.elemValue(mod, i);
2396 const rhs_elem = try rhs.elemValue(mod, i);
2397 scalar.* = try (try intModScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2398 }
2399 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2400 .ty = ty.toIntern(),
2401 .storage = .{ .elems = result_data },
2402 } })));
2403 }
2404 return intModScalar(lhs, rhs, ty, allocator, mod);
2405}
2406
2407pub fn intModScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2408 // TODO is this a performance issue? maybe we should try the operation without
2409 // resorting to BigInt first.
2410 var lhs_space: Value.BigIntSpace = undefined;
2411 var rhs_space: Value.BigIntSpace = undefined;
2412 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2413 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2414 const limbs_q = try allocator.alloc(
2415 std.math.big.Limb,
2416 lhs_bigint.limbs.len,
2417 );
2418 const limbs_r = try allocator.alloc(
2419 std.math.big.Limb,
2420 rhs_bigint.limbs.len,
2421 );
2422 const limbs_buffer = try allocator.alloc(
2423 std.math.big.Limb,
2424 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2425 );
2426 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2427 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2428 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2429 return mod.intValue_big(ty, result_r.toConst());
2430}
2431
2432/// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
2433pub fn isNan(val: Value, mod: *const Module) bool {
2434 if (val.ip_index == .none) return false;
2435 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2436 .float => |float| switch (float.storage) {
2437 inline else => |x| std.math.isNan(x),
2438 },
2439 else => false,
2440 };
2441}
2442
2443/// Returns true if the value is a floating point type and is infinite. Returns false otherwise.
2444pub fn isInf(val: Value, mod: *const Module) bool {
2445 if (val.ip_index == .none) return false;
2446 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2447 .float => |float| switch (float.storage) {
2448 inline else => |x| std.math.isInf(x),
2449 },
2450 else => false,
2451 };
2452}
2453
2454pub fn isNegativeInf(val: Value, mod: *const Module) bool {
2455 if (val.ip_index == .none) return false;
2456 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2457 .float => |float| switch (float.storage) {
2458 inline else => |x| std.math.isNegativeInf(x),
2459 },
2460 else => false,
2461 };
2462}
2463
2464pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2465 if (float_type.zigTypeTag(mod) == .Vector) {
2466 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2467 const scalar_ty = float_type.scalarType(mod);
2468 for (result_data, 0..) |*scalar, i| {
2469 const lhs_elem = try lhs.elemValue(mod, i);
2470 const rhs_elem = try rhs.elemValue(mod, i);
2471 scalar.* = try (try floatRemScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2472 }
2473 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2474 .ty = float_type.toIntern(),
2475 .storage = .{ .elems = result_data },
2476 } })));
2477 }
2478 return floatRemScalar(lhs, rhs, float_type, mod);
2479}
2480
2481pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2482 const target = mod.getTarget();
2483 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2484 16 => .{ .f16 = @rem(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2485 32 => .{ .f32 = @rem(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2486 64 => .{ .f64 = @rem(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2487 80 => .{ .f80 = @rem(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2488 128 => .{ .f128 = @rem(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2489 else => unreachable,
2490 };
2491 return Value.fromInterned((try mod.intern(.{ .float = .{
2492 .ty = float_type.toIntern(),
2493 .storage = storage,
2494 } })));
2495}
2496
2497pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2498 if (float_type.zigTypeTag(mod) == .Vector) {
2499 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2500 const scalar_ty = float_type.scalarType(mod);
2501 for (result_data, 0..) |*scalar, i| {
2502 const lhs_elem = try lhs.elemValue(mod, i);
2503 const rhs_elem = try rhs.elemValue(mod, i);
2504 scalar.* = try (try floatModScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2505 }
2506 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2507 .ty = float_type.toIntern(),
2508 .storage = .{ .elems = result_data },
2509 } })));
2510 }
2511 return floatModScalar(lhs, rhs, float_type, mod);
2512}
2513
2514pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2515 const target = mod.getTarget();
2516 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2517 16 => .{ .f16 = @mod(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2518 32 => .{ .f32 = @mod(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2519 64 => .{ .f64 = @mod(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2520 80 => .{ .f80 = @mod(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2521 128 => .{ .f128 = @mod(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2522 else => unreachable,
2523 };
2524 return Value.fromInterned((try mod.intern(.{ .float = .{
2525 .ty = float_type.toIntern(),
2526 .storage = storage,
2527 } })));
2528}
2529
2530/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2531/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2532pub fn intMul(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2533 var overflow: usize = undefined;
2534 return intMulInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2535 error.Overflow => {
2536 const is_vec = ty.isVector(mod);
2537 overflow_idx.* = if (is_vec) overflow else 0;
2538 const safe_ty = if (is_vec) try mod.vectorType(.{
2539 .len = ty.vectorLen(mod),
2540 .child = .comptime_int_type,
2541 }) else Type.comptime_int;
2542 return intMulInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2543 error.Overflow => unreachable,
2544 else => |e| return e,
2545 };
2546 },
2547 else => |e| return e,
2548 };
2549}
2550
2551fn intMulInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2552 if (ty.zigTypeTag(mod) == .Vector) {
2553 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2554 const scalar_ty = ty.scalarType(mod);
2555 for (result_data, 0..) |*scalar, i| {
2556 const lhs_elem = try lhs.elemValue(mod, i);
2557 const rhs_elem = try rhs.elemValue(mod, i);
2558 const val = intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2559 error.Overflow => {
2560 overflow_idx.* = i;
2561 return error.Overflow;
2562 },
2563 else => |e| return e,
2564 };
2565 scalar.* = try val.intern(scalar_ty, mod);
2566 }
2567 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2568 .ty = ty.toIntern(),
2569 .storage = .{ .elems = result_data },
2570 } })));
2571 }
2572 return intMulScalar(lhs, rhs, ty, allocator, mod);
2573}
2574
2575pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2576 if (ty.toIntern() != .comptime_int_type) {
2577 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2578 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
2579 return res.wrapped_result;
2580 }
2581 // TODO is this a performance issue? maybe we should try the operation without
2582 // resorting to BigInt first.
2583 var lhs_space: Value.BigIntSpace = undefined;
2584 var rhs_space: Value.BigIntSpace = undefined;
2585 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2586 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2587 const limbs = try allocator.alloc(
2588 std.math.big.Limb,
2589 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2590 );
2591 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2592 const limbs_buffer = try allocator.alloc(
2593 std.math.big.Limb,
2594 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2595 );
2596 defer allocator.free(limbs_buffer);
2597 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
2598 return mod.intValue_big(ty, result_bigint.toConst());
2599}
2600
2601pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {
2602 if (ty.zigTypeTag(mod) == .Vector) {
2603 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2604 const scalar_ty = ty.scalarType(mod);
2605 for (result_data, 0..) |*scalar, i| {
2606 const elem_val = try val.elemValue(mod, i);
2607 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, bits, mod)).intern(scalar_ty, mod);
2608 }
2609 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2610 .ty = ty.toIntern(),
2611 .storage = .{ .elems = result_data },
2612 } })));
2613 }
2614 return intTruncScalar(val, ty, allocator, signedness, bits, mod);
2615}
2616
2617/// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`.
2618pub fn intTruncBitsAsValue(
2619 val: Value,
2620 ty: Type,
2621 allocator: Allocator,
2622 signedness: std.builtin.Signedness,
2623 bits: Value,
2624 mod: *Module,
2625) !Value {
2626 if (ty.zigTypeTag(mod) == .Vector) {
2627 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2628 const scalar_ty = ty.scalarType(mod);
2629 for (result_data, 0..) |*scalar, i| {
2630 const elem_val = try val.elemValue(mod, i);
2631 const bits_elem = try bits.elemValue(mod, i);
2632 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @as(u16, @intCast(bits_elem.toUnsignedInt(mod))), mod)).intern(scalar_ty, mod);
2633 }
2634 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2635 .ty = ty.toIntern(),
2636 .storage = .{ .elems = result_data },
2637 } })));
2638 }
2639 return intTruncScalar(val, ty, allocator, signedness, @as(u16, @intCast(bits.toUnsignedInt(mod))), mod);
2640}
2641
2642pub fn intTruncScalar(
2643 val: Value,
2644 ty: Type,
2645 allocator: Allocator,
2646 signedness: std.builtin.Signedness,
2647 bits: u16,
2648 mod: *Module,
2649) !Value {
2650 if (bits == 0) return mod.intValue(ty, 0);
2651
2652 var val_space: Value.BigIntSpace = undefined;
2653 const val_bigint = val.toBigInt(&val_space, mod);
2654
2655 const limbs = try allocator.alloc(
2656 std.math.big.Limb,
2657 std.math.big.int.calcTwosCompLimbCount(bits),
2658 );
2659 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2660
2661 result_bigint.truncate(val_bigint, signedness, bits);
2662 return mod.intValue_big(ty, result_bigint.toConst());
2663}
2664
2665pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2666 if (ty.zigTypeTag(mod) == .Vector) {
2667 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2668 const scalar_ty = ty.scalarType(mod);
2669 for (result_data, 0..) |*scalar, i| {
2670 const lhs_elem = try lhs.elemValue(mod, i);
2671 const rhs_elem = try rhs.elemValue(mod, i);
2672 scalar.* = try (try shlScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2673 }
2674 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2675 .ty = ty.toIntern(),
2676 .storage = .{ .elems = result_data },
2677 } })));
2678 }
2679 return shlScalar(lhs, rhs, ty, allocator, mod);
2680}
2681
2682pub fn shlScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2683 // TODO is this a performance issue? maybe we should try the operation without
2684 // resorting to BigInt first.
2685 var lhs_space: Value.BigIntSpace = undefined;
2686 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2687 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2688 const limbs = try allocator.alloc(
2689 std.math.big.Limb,
2690 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2691 );
2692 var result_bigint = BigIntMutable{
2693 .limbs = limbs,
2694 .positive = undefined,
2695 .len = undefined,
2696 };
2697 result_bigint.shiftLeft(lhs_bigint, shift);
2698 if (ty.toIntern() != .comptime_int_type) {
2699 const int_info = ty.intInfo(mod);
2700 result_bigint.truncate(result_bigint.toConst(), int_info.signedness, int_info.bits);
2701 }
2702
2703 return mod.intValue_big(ty, result_bigint.toConst());
2704}
2705
2706pub fn shlWithOverflow(
2707 lhs: Value,
2708 rhs: Value,
2709 ty: Type,
2710 allocator: Allocator,
2711 mod: *Module,
2712) !OverflowArithmeticResult {
2713 if (ty.zigTypeTag(mod) == .Vector) {
2714 const vec_len = ty.vectorLen(mod);
2715 const overflowed_data = try allocator.alloc(InternPool.Index, vec_len);
2716 const result_data = try allocator.alloc(InternPool.Index, vec_len);
2717 const scalar_ty = ty.scalarType(mod);
2718 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2719 const lhs_elem = try lhs.elemValue(mod, i);
2720 const rhs_elem = try rhs.elemValue(mod, i);
2721 const of_math_result = try shlWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
2722 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2723 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2724 }
2725 return OverflowArithmeticResult{
2726 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2727 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2728 .storage = .{ .elems = overflowed_data },
2729 } }))),
2730 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2731 .ty = ty.toIntern(),
2732 .storage = .{ .elems = result_data },
2733 } }))),
2734 };
2735 }
2736 return shlWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2737}
2738
2739pub fn shlWithOverflowScalar(
2740 lhs: Value,
2741 rhs: Value,
2742 ty: Type,
2743 allocator: Allocator,
2744 mod: *Module,
2745) !OverflowArithmeticResult {
2746 const info = ty.intInfo(mod);
2747 var lhs_space: Value.BigIntSpace = undefined;
2748 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2749 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2750 const limbs = try allocator.alloc(
2751 std.math.big.Limb,
2752 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2753 );
2754 var result_bigint = BigIntMutable{
2755 .limbs = limbs,
2756 .positive = undefined,
2757 .len = undefined,
2758 };
2759 result_bigint.shiftLeft(lhs_bigint, shift);
2760 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2761 if (overflowed) {
2762 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2763 }
2764 return OverflowArithmeticResult{
2765 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2766 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2767 };
2768}
2769
2770pub fn shlSat(
2771 lhs: Value,
2772 rhs: Value,
2773 ty: Type,
2774 arena: Allocator,
2775 mod: *Module,
2776) !Value {
2777 if (ty.zigTypeTag(mod) == .Vector) {
2778 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2779 const scalar_ty = ty.scalarType(mod);
2780 for (result_data, 0..) |*scalar, i| {
2781 const lhs_elem = try lhs.elemValue(mod, i);
2782 const rhs_elem = try rhs.elemValue(mod, i);
2783 scalar.* = try (try shlSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2784 }
2785 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2786 .ty = ty.toIntern(),
2787 .storage = .{ .elems = result_data },
2788 } })));
2789 }
2790 return shlSatScalar(lhs, rhs, ty, arena, mod);
2791}
2792
2793pub fn shlSatScalar(
2794 lhs: Value,
2795 rhs: Value,
2796 ty: Type,
2797 arena: Allocator,
2798 mod: *Module,
2799) !Value {
2800 // TODO is this a performance issue? maybe we should try the operation without
2801 // resorting to BigInt first.
2802 const info = ty.intInfo(mod);
2803
2804 var lhs_space: Value.BigIntSpace = undefined;
2805 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2806 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2807 const limbs = try arena.alloc(
2808 std.math.big.Limb,
2809 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,
2810 );
2811 var result_bigint = BigIntMutable{
2812 .limbs = limbs,
2813 .positive = undefined,
2814 .len = undefined,
2815 };
2816 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
2817 return mod.intValue_big(ty, result_bigint.toConst());
2818}
2819
2820pub fn shlTrunc(
2821 lhs: Value,
2822 rhs: Value,
2823 ty: Type,
2824 arena: Allocator,
2825 mod: *Module,
2826) !Value {
2827 if (ty.zigTypeTag(mod) == .Vector) {
2828 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2829 const scalar_ty = ty.scalarType(mod);
2830 for (result_data, 0..) |*scalar, i| {
2831 const lhs_elem = try lhs.elemValue(mod, i);
2832 const rhs_elem = try rhs.elemValue(mod, i);
2833 scalar.* = try (try shlTruncScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2834 }
2835 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2836 .ty = ty.toIntern(),
2837 .storage = .{ .elems = result_data },
2838 } })));
2839 }
2840 return shlTruncScalar(lhs, rhs, ty, arena, mod);
2841}
2842
2843pub fn shlTruncScalar(
2844 lhs: Value,
2845 rhs: Value,
2846 ty: Type,
2847 arena: Allocator,
2848 mod: *Module,
2849) !Value {
2850 const shifted = try lhs.shl(rhs, ty, arena, mod);
2851 const int_info = ty.intInfo(mod);
2852 const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits, mod);
2853 return truncated;
2854}
2855
2856pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2857 if (ty.zigTypeTag(mod) == .Vector) {
2858 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2859 const scalar_ty = ty.scalarType(mod);
2860 for (result_data, 0..) |*scalar, i| {
2861 const lhs_elem = try lhs.elemValue(mod, i);
2862 const rhs_elem = try rhs.elemValue(mod, i);
2863 scalar.* = try (try shrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2864 }
2865 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2866 .ty = ty.toIntern(),
2867 .storage = .{ .elems = result_data },
2868 } })));
2869 }
2870 return shrScalar(lhs, rhs, ty, allocator, mod);
2871}
2872
2873pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2874 // TODO is this a performance issue? maybe we should try the operation without
2875 // resorting to BigInt first.
2876 var lhs_space: Value.BigIntSpace = undefined;
2877 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2878 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2879
2880 const result_limbs = lhs_bigint.limbs.len -| (shift / (@sizeOf(std.math.big.Limb) * 8));
2881 if (result_limbs == 0) {
2882 // The shift is enough to remove all the bits from the number, which means the
2883 // result is 0 or -1 depending on the sign.
2884 if (lhs_bigint.positive) {
2885 return mod.intValue(ty, 0);
2886 } else {
2887 return mod.intValue(ty, -1);
2888 }
2889 }
2890
2891 const limbs = try allocator.alloc(
2892 std.math.big.Limb,
2893 result_limbs,
2894 );
2895 var result_bigint = BigIntMutable{
2896 .limbs = limbs,
2897 .positive = undefined,
2898 .len = undefined,
2899 };
2900 result_bigint.shiftRight(lhs_bigint, shift);
2901 return mod.intValue_big(ty, result_bigint.toConst());
2902}
2903
2904pub fn floatNeg(
2905 val: Value,
2906 float_type: Type,
2907 arena: Allocator,
2908 mod: *Module,
2909) !Value {
2910 if (float_type.zigTypeTag(mod) == .Vector) {
2911 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2912 const scalar_ty = float_type.scalarType(mod);
2913 for (result_data, 0..) |*scalar, i| {
2914 const elem_val = try val.elemValue(mod, i);
2915 scalar.* = try (try floatNegScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
2916 }
2917 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2918 .ty = float_type.toIntern(),
2919 .storage = .{ .elems = result_data },
2920 } })));
2921 }
2922 return floatNegScalar(val, float_type, mod);
2923}
2924
2925pub fn floatNegScalar(
2926 val: Value,
2927 float_type: Type,
2928 mod: *Module,
2929) !Value {
2930 const target = mod.getTarget();
2931 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2932 16 => .{ .f16 = -val.toFloat(f16, mod) },
2933 32 => .{ .f32 = -val.toFloat(f32, mod) },
2934 64 => .{ .f64 = -val.toFloat(f64, mod) },
2935 80 => .{ .f80 = -val.toFloat(f80, mod) },
2936 128 => .{ .f128 = -val.toFloat(f128, mod) },
2937 else => unreachable,
2938 };
2939 return Value.fromInterned((try mod.intern(.{ .float = .{
2940 .ty = float_type.toIntern(),
2941 .storage = storage,
2942 } })));
2943}
2944
2945pub fn floatAdd(
2946 lhs: Value,
2947 rhs: Value,
2948 float_type: Type,
2949 arena: Allocator,
2950 mod: *Module,
2951) !Value {
2952 if (float_type.zigTypeTag(mod) == .Vector) {
2953 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2954 const scalar_ty = float_type.scalarType(mod);
2955 for (result_data, 0..) |*scalar, i| {
2956 const lhs_elem = try lhs.elemValue(mod, i);
2957 const rhs_elem = try rhs.elemValue(mod, i);
2958 scalar.* = try (try floatAddScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2959 }
2960 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2961 .ty = float_type.toIntern(),
2962 .storage = .{ .elems = result_data },
2963 } })));
2964 }
2965 return floatAddScalar(lhs, rhs, float_type, mod);
2966}
2967
2968pub fn floatAddScalar(
2969 lhs: Value,
2970 rhs: Value,
2971 float_type: Type,
2972 mod: *Module,
2973) !Value {
2974 const target = mod.getTarget();
2975 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2976 16 => .{ .f16 = lhs.toFloat(f16, mod) + rhs.toFloat(f16, mod) },
2977 32 => .{ .f32 = lhs.toFloat(f32, mod) + rhs.toFloat(f32, mod) },
2978 64 => .{ .f64 = lhs.toFloat(f64, mod) + rhs.toFloat(f64, mod) },
2979 80 => .{ .f80 = lhs.toFloat(f80, mod) + rhs.toFloat(f80, mod) },
2980 128 => .{ .f128 = lhs.toFloat(f128, mod) + rhs.toFloat(f128, mod) },
2981 else => unreachable,
2982 };
2983 return Value.fromInterned((try mod.intern(.{ .float = .{
2984 .ty = float_type.toIntern(),
2985 .storage = storage,
2986 } })));
2987}
2988
2989pub fn floatSub(
2990 lhs: Value,
2991 rhs: Value,
2992 float_type: Type,
2993 arena: Allocator,
2994 mod: *Module,
2995) !Value {
2996 if (float_type.zigTypeTag(mod) == .Vector) {
2997 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2998 const scalar_ty = float_type.scalarType(mod);
2999 for (result_data, 0..) |*scalar, i| {
3000 const lhs_elem = try lhs.elemValue(mod, i);
3001 const rhs_elem = try rhs.elemValue(mod, i);
3002 scalar.* = try (try floatSubScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3003 }
3004 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3005 .ty = float_type.toIntern(),
3006 .storage = .{ .elems = result_data },
3007 } })));
3008 }
3009 return floatSubScalar(lhs, rhs, float_type, mod);
3010}
3011
3012pub fn floatSubScalar(
3013 lhs: Value,
3014 rhs: Value,
3015 float_type: Type,
3016 mod: *Module,
3017) !Value {
3018 const target = mod.getTarget();
3019 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3020 16 => .{ .f16 = lhs.toFloat(f16, mod) - rhs.toFloat(f16, mod) },
3021 32 => .{ .f32 = lhs.toFloat(f32, mod) - rhs.toFloat(f32, mod) },
3022 64 => .{ .f64 = lhs.toFloat(f64, mod) - rhs.toFloat(f64, mod) },
3023 80 => .{ .f80 = lhs.toFloat(f80, mod) - rhs.toFloat(f80, mod) },
3024 128 => .{ .f128 = lhs.toFloat(f128, mod) - rhs.toFloat(f128, mod) },
3025 else => unreachable,
3026 };
3027 return Value.fromInterned((try mod.intern(.{ .float = .{
3028 .ty = float_type.toIntern(),
3029 .storage = storage,
3030 } })));
3031}
3032
3033pub fn floatDiv(
3034 lhs: Value,
3035 rhs: Value,
3036 float_type: Type,
3037 arena: Allocator,
3038 mod: *Module,
3039) !Value {
3040 if (float_type.zigTypeTag(mod) == .Vector) {
3041 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3042 const scalar_ty = float_type.scalarType(mod);
3043 for (result_data, 0..) |*scalar, i| {
3044 const lhs_elem = try lhs.elemValue(mod, i);
3045 const rhs_elem = try rhs.elemValue(mod, i);
3046 scalar.* = try (try floatDivScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3047 }
3048 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3049 .ty = float_type.toIntern(),
3050 .storage = .{ .elems = result_data },
3051 } })));
3052 }
3053 return floatDivScalar(lhs, rhs, float_type, mod);
3054}
3055
3056pub fn floatDivScalar(
3057 lhs: Value,
3058 rhs: Value,
3059 float_type: Type,
3060 mod: *Module,
3061) !Value {
3062 const target = mod.getTarget();
3063 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3064 16 => .{ .f16 = lhs.toFloat(f16, mod) / rhs.toFloat(f16, mod) },
3065 32 => .{ .f32 = lhs.toFloat(f32, mod) / rhs.toFloat(f32, mod) },
3066 64 => .{ .f64 = lhs.toFloat(f64, mod) / rhs.toFloat(f64, mod) },
3067 80 => .{ .f80 = lhs.toFloat(f80, mod) / rhs.toFloat(f80, mod) },
3068 128 => .{ .f128 = lhs.toFloat(f128, mod) / rhs.toFloat(f128, mod) },
3069 else => unreachable,
3070 };
3071 return Value.fromInterned((try mod.intern(.{ .float = .{
3072 .ty = float_type.toIntern(),
3073 .storage = storage,
3074 } })));
3075}
3076
3077pub fn floatDivFloor(
3078 lhs: Value,
3079 rhs: Value,
3080 float_type: Type,
3081 arena: Allocator,
3082 mod: *Module,
3083) !Value {
3084 if (float_type.zigTypeTag(mod) == .Vector) {
3085 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3086 const scalar_ty = float_type.scalarType(mod);
3087 for (result_data, 0..) |*scalar, i| {
3088 const lhs_elem = try lhs.elemValue(mod, i);
3089 const rhs_elem = try rhs.elemValue(mod, i);
3090 scalar.* = try (try floatDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3091 }
3092 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3093 .ty = float_type.toIntern(),
3094 .storage = .{ .elems = result_data },
3095 } })));
3096 }
3097 return floatDivFloorScalar(lhs, rhs, float_type, mod);
3098}
3099
3100pub fn floatDivFloorScalar(
3101 lhs: Value,
3102 rhs: Value,
3103 float_type: Type,
3104 mod: *Module,
3105) !Value {
3106 const target = mod.getTarget();
3107 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3108 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3109 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3110 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3111 80 => .{ .f80 = @divFloor(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3112 128 => .{ .f128 = @divFloor(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3113 else => unreachable,
3114 };
3115 return Value.fromInterned((try mod.intern(.{ .float = .{
3116 .ty = float_type.toIntern(),
3117 .storage = storage,
3118 } })));
3119}
3120
3121pub fn floatDivTrunc(
3122 lhs: Value,
3123 rhs: Value,
3124 float_type: Type,
3125 arena: Allocator,
3126 mod: *Module,
3127) !Value {
3128 if (float_type.zigTypeTag(mod) == .Vector) {
3129 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3130 const scalar_ty = float_type.scalarType(mod);
3131 for (result_data, 0..) |*scalar, i| {
3132 const lhs_elem = try lhs.elemValue(mod, i);
3133 const rhs_elem = try rhs.elemValue(mod, i);
3134 scalar.* = try (try floatDivTruncScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3135 }
3136 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3137 .ty = float_type.toIntern(),
3138 .storage = .{ .elems = result_data },
3139 } })));
3140 }
3141 return floatDivTruncScalar(lhs, rhs, float_type, mod);
3142}
3143
3144pub fn floatDivTruncScalar(
3145 lhs: Value,
3146 rhs: Value,
3147 float_type: Type,
3148 mod: *Module,
3149) !Value {
3150 const target = mod.getTarget();
3151 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3152 16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3153 32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3154 64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3155 80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3156 128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3157 else => unreachable,
3158 };
3159 return Value.fromInterned((try mod.intern(.{ .float = .{
3160 .ty = float_type.toIntern(),
3161 .storage = storage,
3162 } })));
3163}
3164
3165pub fn floatMul(
3166 lhs: Value,
3167 rhs: Value,
3168 float_type: Type,
3169 arena: Allocator,
3170 mod: *Module,
3171) !Value {
3172 if (float_type.zigTypeTag(mod) == .Vector) {
3173 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3174 const scalar_ty = float_type.scalarType(mod);
3175 for (result_data, 0..) |*scalar, i| {
3176 const lhs_elem = try lhs.elemValue(mod, i);
3177 const rhs_elem = try rhs.elemValue(mod, i);
3178 scalar.* = try (try floatMulScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3179 }
3180 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3181 .ty = float_type.toIntern(),
3182 .storage = .{ .elems = result_data },
3183 } })));
3184 }
3185 return floatMulScalar(lhs, rhs, float_type, mod);
3186}
3187
3188pub fn floatMulScalar(
3189 lhs: Value,
3190 rhs: Value,
3191 float_type: Type,
3192 mod: *Module,
3193) !Value {
3194 const target = mod.getTarget();
3195 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3196 16 => .{ .f16 = lhs.toFloat(f16, mod) * rhs.toFloat(f16, mod) },
3197 32 => .{ .f32 = lhs.toFloat(f32, mod) * rhs.toFloat(f32, mod) },
3198 64 => .{ .f64 = lhs.toFloat(f64, mod) * rhs.toFloat(f64, mod) },
3199 80 => .{ .f80 = lhs.toFloat(f80, mod) * rhs.toFloat(f80, mod) },
3200 128 => .{ .f128 = lhs.toFloat(f128, mod) * rhs.toFloat(f128, mod) },
3201 else => unreachable,
3202 };
3203 return Value.fromInterned((try mod.intern(.{ .float = .{
3204 .ty = float_type.toIntern(),
3205 .storage = storage,
3206 } })));
3207}
3208
3209pub fn sqrt(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3210 if (float_type.zigTypeTag(mod) == .Vector) {
3211 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3212 const scalar_ty = float_type.scalarType(mod);
3213 for (result_data, 0..) |*scalar, i| {
3214 const elem_val = try val.elemValue(mod, i);
3215 scalar.* = try (try sqrtScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3216 }
3217 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3218 .ty = float_type.toIntern(),
3219 .storage = .{ .elems = result_data },
3220 } })));
3221 }
3222 return sqrtScalar(val, float_type, mod);
3223}
3224
3225pub fn sqrtScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3226 const target = mod.getTarget();
3227 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3228 16 => .{ .f16 = @sqrt(val.toFloat(f16, mod)) },
3229 32 => .{ .f32 = @sqrt(val.toFloat(f32, mod)) },
3230 64 => .{ .f64 = @sqrt(val.toFloat(f64, mod)) },
3231 80 => .{ .f80 = @sqrt(val.toFloat(f80, mod)) },
3232 128 => .{ .f128 = @sqrt(val.toFloat(f128, mod)) },
3233 else => unreachable,
3234 };
3235 return Value.fromInterned((try mod.intern(.{ .float = .{
3236 .ty = float_type.toIntern(),
3237 .storage = storage,
3238 } })));
3239}
3240
3241pub fn sin(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3242 if (float_type.zigTypeTag(mod) == .Vector) {
3243 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3244 const scalar_ty = float_type.scalarType(mod);
3245 for (result_data, 0..) |*scalar, i| {
3246 const elem_val = try val.elemValue(mod, i);
3247 scalar.* = try (try sinScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3248 }
3249 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3250 .ty = float_type.toIntern(),
3251 .storage = .{ .elems = result_data },
3252 } })));
3253 }
3254 return sinScalar(val, float_type, mod);
3255}
3256
3257pub fn sinScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3258 const target = mod.getTarget();
3259 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3260 16 => .{ .f16 = @sin(val.toFloat(f16, mod)) },
3261 32 => .{ .f32 = @sin(val.toFloat(f32, mod)) },
3262 64 => .{ .f64 = @sin(val.toFloat(f64, mod)) },
3263 80 => .{ .f80 = @sin(val.toFloat(f80, mod)) },
3264 128 => .{ .f128 = @sin(val.toFloat(f128, mod)) },
3265 else => unreachable,
3266 };
3267 return Value.fromInterned((try mod.intern(.{ .float = .{
3268 .ty = float_type.toIntern(),
3269 .storage = storage,
3270 } })));
3271}
3272
3273pub fn cos(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3274 if (float_type.zigTypeTag(mod) == .Vector) {
3275 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3276 const scalar_ty = float_type.scalarType(mod);
3277 for (result_data, 0..) |*scalar, i| {
3278 const elem_val = try val.elemValue(mod, i);
3279 scalar.* = try (try cosScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3280 }
3281 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3282 .ty = float_type.toIntern(),
3283 .storage = .{ .elems = result_data },
3284 } })));
3285 }
3286 return cosScalar(val, float_type, mod);
3287}
3288
3289pub fn cosScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3290 const target = mod.getTarget();
3291 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3292 16 => .{ .f16 = @cos(val.toFloat(f16, mod)) },
3293 32 => .{ .f32 = @cos(val.toFloat(f32, mod)) },
3294 64 => .{ .f64 = @cos(val.toFloat(f64, mod)) },
3295 80 => .{ .f80 = @cos(val.toFloat(f80, mod)) },
3296 128 => .{ .f128 = @cos(val.toFloat(f128, mod)) },
3297 else => unreachable,
3298 };
3299 return Value.fromInterned((try mod.intern(.{ .float = .{
3300 .ty = float_type.toIntern(),
3301 .storage = storage,
3302 } })));
3303}
3304
3305pub fn tan(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3306 if (float_type.zigTypeTag(mod) == .Vector) {
3307 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3308 const scalar_ty = float_type.scalarType(mod);
3309 for (result_data, 0..) |*scalar, i| {
3310 const elem_val = try val.elemValue(mod, i);
3311 scalar.* = try (try tanScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3312 }
3313 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3314 .ty = float_type.toIntern(),
3315 .storage = .{ .elems = result_data },
3316 } })));
3317 }
3318 return tanScalar(val, float_type, mod);
3319}
3320
3321pub fn tanScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3322 const target = mod.getTarget();
3323 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3324 16 => .{ .f16 = @tan(val.toFloat(f16, mod)) },
3325 32 => .{ .f32 = @tan(val.toFloat(f32, mod)) },
3326 64 => .{ .f64 = @tan(val.toFloat(f64, mod)) },
3327 80 => .{ .f80 = @tan(val.toFloat(f80, mod)) },
3328 128 => .{ .f128 = @tan(val.toFloat(f128, mod)) },
3329 else => unreachable,
3330 };
3331 return Value.fromInterned((try mod.intern(.{ .float = .{
3332 .ty = float_type.toIntern(),
3333 .storage = storage,
3334 } })));
3335}
3336
3337pub fn exp(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3338 if (float_type.zigTypeTag(mod) == .Vector) {
3339 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3340 const scalar_ty = float_type.scalarType(mod);
3341 for (result_data, 0..) |*scalar, i| {
3342 const elem_val = try val.elemValue(mod, i);
3343 scalar.* = try (try expScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3344 }
3345 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3346 .ty = float_type.toIntern(),
3347 .storage = .{ .elems = result_data },
3348 } })));
3349 }
3350 return expScalar(val, float_type, mod);
3351}
3352
3353pub fn expScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3354 const target = mod.getTarget();
3355 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3356 16 => .{ .f16 = @exp(val.toFloat(f16, mod)) },
3357 32 => .{ .f32 = @exp(val.toFloat(f32, mod)) },
3358 64 => .{ .f64 = @exp(val.toFloat(f64, mod)) },
3359 80 => .{ .f80 = @exp(val.toFloat(f80, mod)) },
3360 128 => .{ .f128 = @exp(val.toFloat(f128, mod)) },
3361 else => unreachable,
3362 };
3363 return Value.fromInterned((try mod.intern(.{ .float = .{
3364 .ty = float_type.toIntern(),
3365 .storage = storage,
3366 } })));
3367}
3368
3369pub fn exp2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3370 if (float_type.zigTypeTag(mod) == .Vector) {
3371 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3372 const scalar_ty = float_type.scalarType(mod);
3373 for (result_data, 0..) |*scalar, i| {
3374 const elem_val = try val.elemValue(mod, i);
3375 scalar.* = try (try exp2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3376 }
3377 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3378 .ty = float_type.toIntern(),
3379 .storage = .{ .elems = result_data },
3380 } })));
3381 }
3382 return exp2Scalar(val, float_type, mod);
3383}
3384
3385pub fn exp2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3386 const target = mod.getTarget();
3387 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3388 16 => .{ .f16 = @exp2(val.toFloat(f16, mod)) },
3389 32 => .{ .f32 = @exp2(val.toFloat(f32, mod)) },
3390 64 => .{ .f64 = @exp2(val.toFloat(f64, mod)) },
3391 80 => .{ .f80 = @exp2(val.toFloat(f80, mod)) },
3392 128 => .{ .f128 = @exp2(val.toFloat(f128, mod)) },
3393 else => unreachable,
3394 };
3395 return Value.fromInterned((try mod.intern(.{ .float = .{
3396 .ty = float_type.toIntern(),
3397 .storage = storage,
3398 } })));
3399}
3400
3401pub fn log(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3402 if (float_type.zigTypeTag(mod) == .Vector) {
3403 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3404 const scalar_ty = float_type.scalarType(mod);
3405 for (result_data, 0..) |*scalar, i| {
3406 const elem_val = try val.elemValue(mod, i);
3407 scalar.* = try (try logScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3408 }
3409 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3410 .ty = float_type.toIntern(),
3411 .storage = .{ .elems = result_data },
3412 } })));
3413 }
3414 return logScalar(val, float_type, mod);
3415}
3416
3417pub fn logScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3418 const target = mod.getTarget();
3419 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3420 16 => .{ .f16 = @log(val.toFloat(f16, mod)) },
3421 32 => .{ .f32 = @log(val.toFloat(f32, mod)) },
3422 64 => .{ .f64 = @log(val.toFloat(f64, mod)) },
3423 80 => .{ .f80 = @log(val.toFloat(f80, mod)) },
3424 128 => .{ .f128 = @log(val.toFloat(f128, mod)) },
3425 else => unreachable,
3426 };
3427 return Value.fromInterned((try mod.intern(.{ .float = .{
3428 .ty = float_type.toIntern(),
3429 .storage = storage,
3430 } })));
3431}
3432
3433pub fn log2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3434 if (float_type.zigTypeTag(mod) == .Vector) {
3435 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3436 const scalar_ty = float_type.scalarType(mod);
3437 for (result_data, 0..) |*scalar, i| {
3438 const elem_val = try val.elemValue(mod, i);
3439 scalar.* = try (try log2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3440 }
3441 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3442 .ty = float_type.toIntern(),
3443 .storage = .{ .elems = result_data },
3444 } })));
3445 }
3446 return log2Scalar(val, float_type, mod);
3447}
3448
3449pub fn log2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3450 const target = mod.getTarget();
3451 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3452 16 => .{ .f16 = @log2(val.toFloat(f16, mod)) },
3453 32 => .{ .f32 = @log2(val.toFloat(f32, mod)) },
3454 64 => .{ .f64 = @log2(val.toFloat(f64, mod)) },
3455 80 => .{ .f80 = @log2(val.toFloat(f80, mod)) },
3456 128 => .{ .f128 = @log2(val.toFloat(f128, mod)) },
3457 else => unreachable,
3458 };
3459 return Value.fromInterned((try mod.intern(.{ .float = .{
3460 .ty = float_type.toIntern(),
3461 .storage = storage,
3462 } })));
3463}
3464
3465pub fn log10(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3466 if (float_type.zigTypeTag(mod) == .Vector) {
3467 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3468 const scalar_ty = float_type.scalarType(mod);
3469 for (result_data, 0..) |*scalar, i| {
3470 const elem_val = try val.elemValue(mod, i);
3471 scalar.* = try (try log10Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3472 }
3473 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3474 .ty = float_type.toIntern(),
3475 .storage = .{ .elems = result_data },
3476 } })));
3477 }
3478 return log10Scalar(val, float_type, mod);
3479}
3480
3481pub fn log10Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3482 const target = mod.getTarget();
3483 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3484 16 => .{ .f16 = @log10(val.toFloat(f16, mod)) },
3485 32 => .{ .f32 = @log10(val.toFloat(f32, mod)) },
3486 64 => .{ .f64 = @log10(val.toFloat(f64, mod)) },
3487 80 => .{ .f80 = @log10(val.toFloat(f80, mod)) },
3488 128 => .{ .f128 = @log10(val.toFloat(f128, mod)) },
3489 else => unreachable,
3490 };
3491 return Value.fromInterned((try mod.intern(.{ .float = .{
3492 .ty = float_type.toIntern(),
3493 .storage = storage,
3494 } })));
3495}
3496
3497pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
3498 if (ty.zigTypeTag(mod) == .Vector) {
3499 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3500 const scalar_ty = ty.scalarType(mod);
3501 for (result_data, 0..) |*scalar, i| {
3502 const elem_val = try val.elemValue(mod, i);
3503 scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod);
3504 }
3505 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3506 .ty = ty.toIntern(),
3507 .storage = .{ .elems = result_data },
3508 } })));
3509 }
3510 return absScalar(val, ty, mod, arena);
3511}
3512
3513pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value {
3514 switch (ty.zigTypeTag(mod)) {
3515 .Int => {
3516 var buffer: Value.BigIntSpace = undefined;
3517 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3518 operand_bigint.abs();
3519
3520 return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst());
3521 },
3522 .ComptimeInt => {
3523 var buffer: Value.BigIntSpace = undefined;
3524 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3525 operand_bigint.abs();
3526
3527 return mod.intValue_big(ty, operand_bigint.toConst());
3528 },
3529 .ComptimeFloat, .Float => {
3530 const target = mod.getTarget();
3531 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
3532 16 => .{ .f16 = @abs(val.toFloat(f16, mod)) },
3533 32 => .{ .f32 = @abs(val.toFloat(f32, mod)) },
3534 64 => .{ .f64 = @abs(val.toFloat(f64, mod)) },
3535 80 => .{ .f80 = @abs(val.toFloat(f80, mod)) },
3536 128 => .{ .f128 = @abs(val.toFloat(f128, mod)) },
3537 else => unreachable,
3538 };
3539 return Value.fromInterned((try mod.intern(.{ .float = .{
3540 .ty = ty.toIntern(),
3541 .storage = storage,
3542 } })));
3543 },
3544 else => unreachable,
3545 }
3546}
3547
3548pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3549 if (float_type.zigTypeTag(mod) == .Vector) {
3550 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3551 const scalar_ty = float_type.scalarType(mod);
3552 for (result_data, 0..) |*scalar, i| {
3553 const elem_val = try val.elemValue(mod, i);
3554 scalar.* = try (try floorScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3555 }
3556 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3557 .ty = float_type.toIntern(),
3558 .storage = .{ .elems = result_data },
3559 } })));
3560 }
3561 return floorScalar(val, float_type, mod);
3562}
3563
3564pub fn floorScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3565 const target = mod.getTarget();
3566 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3567 16 => .{ .f16 = @floor(val.toFloat(f16, mod)) },
3568 32 => .{ .f32 = @floor(val.toFloat(f32, mod)) },
3569 64 => .{ .f64 = @floor(val.toFloat(f64, mod)) },
3570 80 => .{ .f80 = @floor(val.toFloat(f80, mod)) },
3571 128 => .{ .f128 = @floor(val.toFloat(f128, mod)) },
3572 else => unreachable,
3573 };
3574 return Value.fromInterned((try mod.intern(.{ .float = .{
3575 .ty = float_type.toIntern(),
3576 .storage = storage,
3577 } })));
3578}
3579
3580pub fn ceil(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3581 if (float_type.zigTypeTag(mod) == .Vector) {
3582 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3583 const scalar_ty = float_type.scalarType(mod);
3584 for (result_data, 0..) |*scalar, i| {
3585 const elem_val = try val.elemValue(mod, i);
3586 scalar.* = try (try ceilScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3587 }
3588 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3589 .ty = float_type.toIntern(),
3590 .storage = .{ .elems = result_data },
3591 } })));
3592 }
3593 return ceilScalar(val, float_type, mod);
3594}
3595
3596pub fn ceilScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3597 const target = mod.getTarget();
3598 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3599 16 => .{ .f16 = @ceil(val.toFloat(f16, mod)) },
3600 32 => .{ .f32 = @ceil(val.toFloat(f32, mod)) },
3601 64 => .{ .f64 = @ceil(val.toFloat(f64, mod)) },
3602 80 => .{ .f80 = @ceil(val.toFloat(f80, mod)) },
3603 128 => .{ .f128 = @ceil(val.toFloat(f128, mod)) },
3604 else => unreachable,
3605 };
3606 return Value.fromInterned((try mod.intern(.{ .float = .{
3607 .ty = float_type.toIntern(),
3608 .storage = storage,
3609 } })));
3610}
3611
3612pub fn round(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3613 if (float_type.zigTypeTag(mod) == .Vector) {
3614 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3615 const scalar_ty = float_type.scalarType(mod);
3616 for (result_data, 0..) |*scalar, i| {
3617 const elem_val = try val.elemValue(mod, i);
3618 scalar.* = try (try roundScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3619 }
3620 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3621 .ty = float_type.toIntern(),
3622 .storage = .{ .elems = result_data },
3623 } })));
3624 }
3625 return roundScalar(val, float_type, mod);
3626}
3627
3628pub fn roundScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3629 const target = mod.getTarget();
3630 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3631 16 => .{ .f16 = @round(val.toFloat(f16, mod)) },
3632 32 => .{ .f32 = @round(val.toFloat(f32, mod)) },
3633 64 => .{ .f64 = @round(val.toFloat(f64, mod)) },
3634 80 => .{ .f80 = @round(val.toFloat(f80, mod)) },
3635 128 => .{ .f128 = @round(val.toFloat(f128, mod)) },
3636 else => unreachable,
3637 };
3638 return Value.fromInterned((try mod.intern(.{ .float = .{
3639 .ty = float_type.toIntern(),
3640 .storage = storage,
3641 } })));
3642}
3643
3644pub fn trunc(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3645 if (float_type.zigTypeTag(mod) == .Vector) {
3646 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3647 const scalar_ty = float_type.scalarType(mod);
3648 for (result_data, 0..) |*scalar, i| {
3649 const elem_val = try val.elemValue(mod, i);
3650 scalar.* = try (try truncScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3651 }
3652 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3653 .ty = float_type.toIntern(),
3654 .storage = .{ .elems = result_data },
3655 } })));
3656 }
3657 return truncScalar(val, float_type, mod);
3658}
3659
3660pub fn truncScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3661 const target = mod.getTarget();
3662 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3663 16 => .{ .f16 = @trunc(val.toFloat(f16, mod)) },
3664 32 => .{ .f32 = @trunc(val.toFloat(f32, mod)) },
3665 64 => .{ .f64 = @trunc(val.toFloat(f64, mod)) },
3666 80 => .{ .f80 = @trunc(val.toFloat(f80, mod)) },
3667 128 => .{ .f128 = @trunc(val.toFloat(f128, mod)) },
3668 else => unreachable,
3669 };
3670 return Value.fromInterned((try mod.intern(.{ .float = .{
3671 .ty = float_type.toIntern(),
3672 .storage = storage,
3673 } })));
3674}
3675
3676pub fn mulAdd(
3677 float_type: Type,
3678 mulend1: Value,
3679 mulend2: Value,
3680 addend: Value,
3681 arena: Allocator,
3682 mod: *Module,
3683) !Value {
3684 if (float_type.zigTypeTag(mod) == .Vector) {
3685 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3686 const scalar_ty = float_type.scalarType(mod);
3687 for (result_data, 0..) |*scalar, i| {
3688 const mulend1_elem = try mulend1.elemValue(mod, i);
3689 const mulend2_elem = try mulend2.elemValue(mod, i);
3690 const addend_elem = try addend.elemValue(mod, i);
3691 scalar.* = try (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, mod)).intern(scalar_ty, mod);
3692 }
3693 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3694 .ty = float_type.toIntern(),
3695 .storage = .{ .elems = result_data },
3696 } })));
3697 }
3698 return mulAddScalar(float_type, mulend1, mulend2, addend, mod);
3699}
3700
3701pub fn mulAddScalar(
3702 float_type: Type,
3703 mulend1: Value,
3704 mulend2: Value,
3705 addend: Value,
3706 mod: *Module,
3707) Allocator.Error!Value {
3708 const target = mod.getTarget();
3709 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3710 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, mod), mulend2.toFloat(f16, mod), addend.toFloat(f16, mod)) },
3711 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, mod), mulend2.toFloat(f32, mod), addend.toFloat(f32, mod)) },
3712 64 => .{ .f64 = @mulAdd(f64, mulend1.toFloat(f64, mod), mulend2.toFloat(f64, mod), addend.toFloat(f64, mod)) },
3713 80 => .{ .f80 = @mulAdd(f80, mulend1.toFloat(f80, mod), mulend2.toFloat(f80, mod), addend.toFloat(f80, mod)) },
3714 128 => .{ .f128 = @mulAdd(f128, mulend1.toFloat(f128, mod), mulend2.toFloat(f128, mod), addend.toFloat(f128, mod)) },
3715 else => unreachable,
3716 };
3717 return Value.fromInterned((try mod.intern(.{ .float = .{
3718 .ty = float_type.toIntern(),
3719 .storage = storage,
3720 } })));
3721}
3722
3723/// If the value is represented in-memory as a series of bytes that all
3724/// have the same value, return that byte value, otherwise null.
3725pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module) !?u8 {
3726 const abi_size = std.math.cast(usize, ty.abiSize(mod)) orelse return null;
3727 assert(abi_size >= 1);
3728 const byte_buffer = try mod.gpa.alloc(u8, abi_size);
3729 defer mod.gpa.free(byte_buffer);
3730
3731 writeToMemory(val, ty, mod, byte_buffer) catch |err| switch (err) {
3732 error.OutOfMemory => return error.OutOfMemory,
3733 error.ReinterpretDeclRef => return null,
3734 // TODO: The writeToMemory function was originally created for the purpose
3735 // of comptime pointer casting. However, it is now additionally being used
3736 // for checking the actual memory layout that will be generated by machine
3737 // code late in compilation. So, this error handling is too aggressive and
3738 // causes some false negatives, causing less-than-ideal code generation.
3739 error.IllDefinedMemoryLayout => return null,
3740 error.Unimplemented => return null,
3741 };
3742 const first_byte = byte_buffer[0];
3743 for (byte_buffer[1..]) |byte| {
3744 if (byte != first_byte) return null;
3745 }
3746 return first_byte;
3747}
3748
3749pub fn isGenericPoison(val: Value) bool {
3750 return val.toIntern() == .generic_poison;
3751}
3752
3753/// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value.
3754/// If `val` is not undef, the bounds are both `val`.
3755/// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type.
3756/// If `val` is undef and is a `comptime_int`, returns null.
3757pub fn intValueBounds(val: Value, mod: *Module) !?[2]Value {
3758 if (!val.isUndef(mod)) return .{ val, val };
3759 const ty = mod.intern_pool.typeOf(val.toIntern());
3760 if (ty == .comptime_int_type) return null;
3761 return .{
3762 try Type.fromInterned(ty).minInt(mod, Type.fromInterned(ty)),
3763 try Type.fromInterned(ty).maxInt(mod, Type.fromInterned(ty)),
3764 };
3765}
3766
3767pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
3768
3769pub const zero_usize: Value = .{ .ip_index = .zero_usize };
3770pub const zero_u8: Value = .{ .ip_index = .zero_u8 };
3771pub const zero_comptime_int: Value = .{ .ip_index = .zero };
3772pub const one_comptime_int: Value = .{ .ip_index = .one };
3773pub const negative_one_comptime_int: Value = .{ .ip_index = .negative_one };
3774pub const undef: Value = .{ .ip_index = .undef };
3775pub const @"void": Value = .{ .ip_index = .void_value };
3776pub const @"null": Value = .{ .ip_index = .null_value };
3777pub const @"false": Value = .{ .ip_index = .bool_false };
3778pub const @"true": Value = .{ .ip_index = .bool_true };
3779pub const @"unreachable": Value = .{ .ip_index = .unreachable_value };
3780
3781pub const generic_poison: Value = .{ .ip_index = .generic_poison };
3782pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type };
3783pub const empty_struct: Value = .{ .ip_index = .empty_struct };
3784
3785pub fn makeBool(x: bool) Value {
3786 return if (x) Value.true else Value.false;
3787}
src/Zir.zig+5
......@@ -2211,6 +2211,11 @@ pub const Inst = struct {
22112211 empty_struct = @intFromEnum(InternPool.Index.empty_struct),
22122212 generic_poison = @intFromEnum(InternPool.Index.generic_poison),
22132213
2214 /// This Ref does not correspond to any ZIR instruction.
2215 /// It is a special value recognized only by Sema.
2216 /// It indicates the value is mutable comptime memory, and represented
2217 /// via the comptime_memory field of Sema. This value never occurs in ZIR.
2218 mutable_comptime = @intFromEnum(InternPool.Index.mutable_comptime),
22142219 /// This tag is here to match Air and InternPool, however it is unused
22152220 /// for ZIR purposes.
22162221 var_args_param_type = @intFromEnum(InternPool.Index.var_args_param_type),
src/arch/aarch64/CodeGen.zig+1-1
......@@ -9,7 +9,7 @@ const Mir = @import("Mir.zig");
99const Emit = @import("Emit.zig");
1010const Liveness = @import("../../Liveness.zig");
1111const Type = @import("../../type.zig").Type;
12const Value = @import("../../value.zig").Value;
12const Value = @import("../../Value.zig");
1313const TypedValue = @import("../../TypedValue.zig");
1414const link = @import("../../link.zig");
1515const Module = @import("../../Module.zig");
src/arch/arm/CodeGen.zig+1-1
......@@ -9,7 +9,7 @@ const Mir = @import("Mir.zig");
99const Emit = @import("Emit.zig");
1010const Liveness = @import("../../Liveness.zig");
1111const Type = @import("../../type.zig").Type;
12const Value = @import("../../value.zig").Value;
12const Value = @import("../../Value.zig");
1313const TypedValue = @import("../../TypedValue.zig");
1414const link = @import("../../link.zig");
1515const Module = @import("../../Module.zig");
src/arch/riscv64/CodeGen.zig+1-1
......@@ -8,7 +8,7 @@ const Mir = @import("Mir.zig");
88const Emit = @import("Emit.zig");
99const Liveness = @import("../../Liveness.zig");
1010const Type = @import("../../type.zig").Type;
11const Value = @import("../../value.zig").Value;
11const Value = @import("../../Value.zig");
1212const TypedValue = @import("../../TypedValue.zig");
1313const link = @import("../../link.zig");
1414const Module = @import("../../Module.zig");
src/arch/wasm/CodeGen.zig+1-6
......@@ -14,7 +14,7 @@ const Module = @import("../../Module.zig");
1414const InternPool = @import("../../InternPool.zig");
1515const Decl = Module.Decl;
1616const Type = @import("../../type.zig").Type;
17const Value = @import("../../value.zig").Value;
17const Value = @import("../../Value.zig");
1818const Compilation = @import("../../Compilation.zig");
1919const LazySrcLoc = Module.LazySrcLoc;
2020const link = @import("../../link.zig");
......@@ -3082,10 +3082,6 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue
30823082 return func.lowerParentPtrDecl(ptr_val, decl_index, offset);
30833083 },
30843084 .anon_decl => |ad| return func.lowerAnonDeclRef(ad, offset),
3085 .mut_decl => |mut_decl| {
3086 const decl_index = mut_decl.decl;
3087 return func.lowerParentPtrDecl(ptr_val, decl_index, offset);
3088 },
30893085 .eu_payload => |tag| return func.fail("TODO: Implement lowerParentPtr for {}", .{tag}),
30903086 .int => |base| return func.lowerConstant(Value.fromInterned(base), Type.usize),
30913087 .opt_payload => |base_ptr| return func.lowerParentPtr(Value.fromInterned(base_ptr), offset),
......@@ -3346,7 +3342,6 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
33463342 },
33473343 .ptr => |ptr| switch (ptr.addr) {
33483344 .decl => |decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl, 0),
3349 .mut_decl => |mut_decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, mut_decl.decl, 0),
33503345 .int => |int| return func.lowerConstant(Value.fromInterned(int), Type.fromInterned(ip.typeOf(int))),
33513346 .opt_payload, .elem, .field => return func.lowerParentPtr(val, 0),
33523347 .anon_decl => |ad| return func.lowerAnonDeclRef(ad, 0),
src/arch/x86_64/CodeGen.zig+1-1
......@@ -33,7 +33,7 @@ const Alignment = InternPool.Alignment;
3333const Target = std.Target;
3434const Type = @import("../../type.zig").Type;
3535const TypedValue = @import("../../TypedValue.zig");
36const Value = @import("../../value.zig").Value;
36const Value = @import("../../Value.zig");
3737const Instruction = @import("encoder.zig").Instruction;
3838
3939const abi = @import("abi.zig");
src/arch/x86_64/abi.zig+1-1
......@@ -570,4 +570,4 @@ const Module = @import("../../Module.zig");
570570const Register = @import("bits.zig").Register;
571571const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
572572const Type = @import("../../type.zig").Type;
573const Value = @import("../../value.zig").Value;
573const Value = @import("../../Value.zig");
src/codegen.zig+1-3
......@@ -20,7 +20,7 @@ const Module = @import("Module.zig");
2020const Target = std.Target;
2121const Type = @import("type.zig").Type;
2222const TypedValue = @import("TypedValue.zig");
23const Value = @import("value.zig").Value;
23const Value = @import("Value.zig");
2424const Zir = @import("Zir.zig");
2525const Alignment = InternPool.Alignment;
2626
......@@ -678,7 +678,6 @@ fn lowerParentPtr(
678678 const ptr = mod.intern_pool.indexToKey(parent_ptr).ptr;
679679 return switch (ptr.addr) {
680680 .decl => |decl| try lowerDeclRef(bin_file, src_loc, decl, code, debug_output, reloc_info),
681 .mut_decl => |md| try lowerDeclRef(bin_file, src_loc, md.decl, code, debug_output, reloc_info),
682681 .anon_decl => |ad| try lowerAnonDeclRef(bin_file, src_loc, ad, code, debug_output, reloc_info),
683682 .int => |int| try generateSymbol(bin_file, src_loc, .{
684683 .ty = Type.usize,
......@@ -1087,7 +1086,6 @@ pub fn genTypedValue(
10871086 if (!typed_value.ty.isSlice(zcu)) switch (zcu.intern_pool.indexToKey(typed_value.val.toIntern())) {
10881087 .ptr => |ptr| switch (ptr.addr) {
10891088 .decl => |decl| return genDeclRef(lf, src_loc, typed_value, decl),
1090 .mut_decl => |mut_decl| return genDeclRef(lf, src_loc, typed_value, mut_decl.decl),
10911089 else => {},
10921090 },
10931091 else => {},
src/codegen/c.zig+1-3
......@@ -7,7 +7,7 @@ const log = std.log.scoped(.c);
77const link = @import("../link.zig");
88const Module = @import("../Module.zig");
99const Compilation = @import("../Compilation.zig");
10const Value = @import("../value.zig").Value;
10const Value = @import("../Value.zig");
1111const Type = @import("../type.zig").Type;
1212const TypedValue = @import("../TypedValue.zig");
1313const C = link.File.C;
......@@ -691,7 +691,6 @@ pub const DeclGen = struct {
691691 const ptr = mod.intern_pool.indexToKey(ptr_val).ptr;
692692 switch (ptr.addr) {
693693 .decl => |d| try dg.renderDeclValue(writer, ptr_ty, Value.fromInterned(ptr_val), d, location),
694 .mut_decl => |md| try dg.renderDeclValue(writer, ptr_ty, Value.fromInterned(ptr_val), md.decl, location),
695694 .anon_decl => |anon_decl| try dg.renderAnonDeclValue(writer, ptr_ty, Value.fromInterned(ptr_val), anon_decl, location),
696695 .int => |int| {
697696 try writer.writeByte('(');
......@@ -1221,7 +1220,6 @@ pub const DeclGen = struct {
12211220 },
12221221 .ptr => |ptr| switch (ptr.addr) {
12231222 .decl => |d| try dg.renderDeclValue(writer, ty, val, d, location),
1224 .mut_decl => |md| try dg.renderDeclValue(writer, ty, val, md.decl, location),
12251223 .anon_decl => |decl_val| try dg.renderAnonDeclValue(writer, ty, val, decl_val, location),
12261224 .int => |int| {
12271225 try writer.writeAll("((");
src/codegen/llvm.zig+1-3
......@@ -21,7 +21,7 @@ const Package = @import("../Package.zig");
2121const TypedValue = @import("../TypedValue.zig");
2222const Air = @import("../Air.zig");
2323const Liveness = @import("../Liveness.zig");
24const Value = @import("../value.zig").Value;
24const Value = @import("../Value.zig");
2525const Type = @import("../type.zig").Type;
2626const LazySrcLoc = Module.LazySrcLoc;
2727const x86_64_abi = @import("../arch/x86_64/abi.zig");
......@@ -3875,7 +3875,6 @@ pub const Object = struct {
38753875 },
38763876 .ptr => |ptr| return switch (ptr.addr) {
38773877 .decl => |decl| try o.lowerDeclRefValue(ty, decl),
3878 .mut_decl => |mut_decl| try o.lowerDeclRefValue(ty, mut_decl.decl),
38793878 .anon_decl => |anon_decl| try o.lowerAnonDeclRef(ty, anon_decl),
38803879 .int => |int| try o.lowerIntAsPtr(int),
38813880 .eu_payload,
......@@ -4340,7 +4339,6 @@ pub const Object = struct {
43404339 const ptr = ip.indexToKey(ptr_val.toIntern()).ptr;
43414340 return switch (ptr.addr) {
43424341 .decl => |decl| try o.lowerParentPtrDecl(decl),
4343 .mut_decl => |mut_decl| try o.lowerParentPtrDecl(mut_decl.decl),
43444342 .anon_decl => |ad| try o.lowerAnonDeclRef(Type.fromInterned(ad.orig_ty), ad),
43454343 .int => |int| try o.lowerIntAsPtr(int),
43464344 .eu_payload => |eu_ptr| {
src/codegen/spirv.zig+1-2
......@@ -7,7 +7,7 @@ const assert = std.debug.assert;
77const Module = @import("../Module.zig");
88const Decl = Module.Decl;
99const Type = @import("../type.zig").Type;
10const Value = @import("../value.zig").Value;
10const Value = @import("../Value.zig");
1111const LazySrcLoc = Module.LazySrcLoc;
1212const Air = @import("../Air.zig");
1313const Zir = @import("../Zir.zig");
......@@ -992,7 +992,6 @@ const DeclGen = struct {
992992 const mod = self.module;
993993 switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
994994 .decl => |decl| return try self.constantDeclRef(ptr_ty, decl),
995 .mut_decl => |decl_mut| return try self.constantDeclRef(ptr_ty, decl_mut.decl),
996995 .anon_decl => |anon_decl| return try self.constantAnonDeclRef(ptr_ty, anon_decl),
997996 .int => |int| {
998997 const ptr_id = self.spv.allocId();
src/link/C.zig+1-1
......@@ -14,7 +14,7 @@ const codegen = @import("../codegen/c.zig");
1414const link = @import("../link.zig");
1515const trace = @import("../tracy.zig").trace;
1616const Type = @import("../type.zig").Type;
17const Value = @import("../value.zig").Value;
17const Value = @import("../Value.zig");
1818const Air = @import("../Air.zig");
1919const Liveness = @import("../Liveness.zig");
2020
src/link/Coff.zig+1-1
......@@ -2753,7 +2753,7 @@ const Relocation = @import("Coff/Relocation.zig");
27532753const TableSection = @import("table_section.zig").TableSection;
27542754const StringTable = @import("StringTable.zig");
27552755const Type = @import("../type.zig").Type;
2756const Value = @import("../value.zig").Value;
2756const Value = @import("../Value.zig");
27572757const TypedValue = @import("../TypedValue.zig");
27582758
27592759pub const base_tag: link.File.Tag = .coff;
src/link/Dwarf.zig+1-1
......@@ -2847,4 +2847,4 @@ const Module = @import("../Module.zig");
28472847const InternPool = @import("../InternPool.zig");
28482848const StringTable = @import("StringTable.zig");
28492849const Type = @import("../type.zig").Type;
2850const Value = @import("../value.zig").Value;
2850const Value = @import("../Value.zig");
src/link/Elf/ZigObject.zig+1-1
......@@ -1667,6 +1667,6 @@ const Object = @import("Object.zig");
16671667const Symbol = @import("Symbol.zig");
16681668const StringTable = @import("../StringTable.zig");
16691669const Type = @import("../../type.zig").Type;
1670const Value = @import("../../value.zig").Value;
1670const Value = @import("../../Value.zig");
16711671const TypedValue = @import("../../TypedValue.zig");
16721672const ZigObject = @This();
src/link/MachO/ZigObject.zig+1-1
......@@ -1462,6 +1462,6 @@ const Relocation = @import("Relocation.zig");
14621462const Symbol = @import("Symbol.zig");
14631463const StringTable = @import("../StringTable.zig");
14641464const Type = @import("../../type.zig").Type;
1465const Value = @import("../../value.zig").Value;
1465const Value = @import("../../Value.zig");
14661466const TypedValue = @import("../../TypedValue.zig");
14671467const ZigObject = @This();
src/link/Plan9.zig+1-1
......@@ -14,7 +14,7 @@ const build_options = @import("build_options");
1414const Air = @import("../Air.zig");
1515const Liveness = @import("../Liveness.zig");
1616const Type = @import("../type.zig").Type;
17const Value = @import("../value.zig").Value;
17const Value = @import("../Value.zig");
1818const TypedValue = @import("../TypedValue.zig");
1919
2020const std = @import("std");
src/link/SpirV.zig+1-1
......@@ -36,7 +36,7 @@ const trace = @import("../tracy.zig").trace;
3636const build_options = @import("build_options");
3737const Air = @import("../Air.zig");
3838const Liveness = @import("../Liveness.zig");
39const Value = @import("../value.zig").Value;
39const Value = @import("../Value.zig");
4040
4141const SpvModule = @import("../codegen/spirv/Module.zig");
4242const spec = @import("../codegen/spirv/spec.zig");
src/link/Wasm.zig+1-1
......@@ -23,7 +23,7 @@ const build_options = @import("build_options");
2323const wasi_libc = @import("../wasi_libc.zig");
2424const Cache = std.Build.Cache;
2525const Type = @import("../type.zig").Type;
26const Value = @import("../value.zig").Value;
26const Value = @import("../Value.zig");
2727const TypedValue = @import("../TypedValue.zig");
2828const LlvmObject = @import("../codegen/llvm.zig").Object;
2929const Air = @import("../Air.zig");
src/print_air.zig+1-1
......@@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator;
33const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
44
55const Module = @import("Module.zig");
6const Value = @import("value.zig").Value;
6const Value = @import("Value.zig");
77const Type = @import("type.zig").Type;
88const Air = @import("Air.zig");
99const Liveness = @import("Liveness.zig");
src/type.zig+1-1
......@@ -1,6 +1,6 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Value = @import("value.zig").Value;
3const Value = @import("Value.zig");
44const assert = std.debug.assert;
55const Target = std.Target;
66const Module = @import("Module.zig");
src/value.zig deleted-4077
......@@ -1,4077 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Type = @import("type.zig").Type;
4const log2 = std.math.log2;
5const assert = std.debug.assert;
6const BigIntConst = std.math.big.int.Const;
7const BigIntMutable = std.math.big.int.Mutable;
8const Target = std.Target;
9const Allocator = std.mem.Allocator;
10const Module = @import("Module.zig");
11const TypedValue = @import("TypedValue.zig");
12const Sema = @import("Sema.zig");
13const InternPool = @import("InternPool.zig");
14
15pub const Value = struct {
16 /// We are migrating towards using this for every Value object. However, many
17 /// values are still represented the legacy way. This is indicated by using
18 /// InternPool.Index.none.
19 ip_index: InternPool.Index,
20
21 /// This is the raw data, with no bookkeeping, no memory awareness,
22 /// no de-duplication, and no type system awareness.
23 /// This union takes advantage of the fact that the first page of memory
24 /// is unmapped, giving us 4096 possible enum tags that have no payload.
25 legacy: extern union {
26 ptr_otherwise: *Payload,
27 },
28
29 // Keep in sync with tools/stage2_pretty_printers_common.py
30 pub const Tag = enum(usize) {
31 // The first section of this enum are tags that require no payload.
32 // After this, the tag requires a payload.
33
34 /// When the type is error union:
35 /// * If the tag is `.@"error"`, the error union is an error.
36 /// * If the tag is `.eu_payload`, the error union is a payload.
37 /// * A nested error such as `anyerror!(anyerror!T)` in which the the outer error union
38 /// is non-error, but the inner error union is an error, is represented as
39 /// a tag of `.eu_payload`, with a sub-tag of `.@"error"`.
40 eu_payload,
41 /// When the type is optional:
42 /// * If the tag is `.null_value`, the optional is null.
43 /// * If the tag is `.opt_payload`, the optional is a payload.
44 /// * A nested optional such as `??T` in which the the outer optional
45 /// is non-null, but the inner optional is null, is represented as
46 /// a tag of `.opt_payload`, with a sub-tag of `.null_value`.
47 opt_payload,
48 /// Pointer and length as sub `Value` objects.
49 slice,
50 /// A slice of u8 whose memory is managed externally.
51 bytes,
52 /// This value is repeated some number of times. The amount of times to repeat
53 /// is stored externally.
54 repeated,
55 /// An instance of a struct, array, or vector.
56 /// Each element/field stored as a `Value`.
57 /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
58 /// so the slice length will be one more than the type's array length.
59 aggregate,
60 /// An instance of a union.
61 @"union",
62
63 pub fn Type(comptime t: Tag) type {
64 return switch (t) {
65 .eu_payload,
66 .opt_payload,
67 .repeated,
68 => Payload.SubValue,
69 .slice => Payload.Slice,
70 .bytes => Payload.Bytes,
71 .aggregate => Payload.Aggregate,
72 .@"union" => Payload.Union,
73 };
74 }
75
76 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Value {
77 const ptr = try ally.create(t.Type());
78 ptr.* = .{
79 .base = .{ .tag = t },
80 .data = data,
81 };
82 return Value{
83 .ip_index = .none,
84 .legacy = .{ .ptr_otherwise = &ptr.base },
85 };
86 }
87
88 pub fn Data(comptime t: Tag) type {
89 return std.meta.fieldInfo(t.Type(), .data).type;
90 }
91 };
92
93 pub fn initPayload(payload: *Payload) Value {
94 return Value{
95 .ip_index = .none,
96 .legacy = .{ .ptr_otherwise = payload },
97 };
98 }
99
100 pub fn tag(self: Value) Tag {
101 assert(self.ip_index == .none);
102 return self.legacy.ptr_otherwise.tag;
103 }
104
105 /// Prefer `castTag` to this.
106 pub fn cast(self: Value, comptime T: type) ?*T {
107 if (self.ip_index != .none) {
108 return null;
109 }
110 if (@hasField(T, "base_tag")) {
111 return self.castTag(T.base_tag);
112 }
113 inline for (@typeInfo(Tag).Enum.fields) |field| {
114 const t = @as(Tag, @enumFromInt(field.value));
115 if (self.legacy.ptr_otherwise.tag == t) {
116 if (T == t.Type()) {
117 return @fieldParentPtr(T, "base", self.legacy.ptr_otherwise);
118 }
119 return null;
120 }
121 }
122 unreachable;
123 }
124
125 pub fn castTag(self: Value, comptime t: Tag) ?*t.Type() {
126 if (self.ip_index != .none) return null;
127
128 if (self.legacy.ptr_otherwise.tag == t)
129 return @fieldParentPtr(t.Type(), "base", self.legacy.ptr_otherwise);
130
131 return null;
132 }
133
134 pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
135 _ = val;
136 _ = fmt;
137 _ = options;
138 _ = writer;
139 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
140 }
141
142 /// This is a debug function. In order to print values in a meaningful way
143 /// we also need access to the type.
144 pub fn dump(
145 start_val: Value,
146 comptime fmt: []const u8,
147 _: std.fmt.FormatOptions,
148 out_stream: anytype,
149 ) !void {
150 comptime assert(fmt.len == 0);
151 if (start_val.ip_index != .none) {
152 try out_stream.print("(interned: {})", .{start_val.toIntern()});
153 return;
154 }
155 var val = start_val;
156 while (true) switch (val.tag()) {
157 .aggregate => {
158 return out_stream.writeAll("(aggregate)");
159 },
160 .@"union" => {
161 return out_stream.writeAll("(union value)");
162 },
163 .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
164 .repeated => {
165 try out_stream.writeAll("(repeated) ");
166 val = val.castTag(.repeated).?.data;
167 },
168 .eu_payload => {
169 try out_stream.writeAll("(eu_payload) ");
170 val = val.castTag(.repeated).?.data;
171 },
172 .opt_payload => {
173 try out_stream.writeAll("(opt_payload) ");
174 val = val.castTag(.repeated).?.data;
175 },
176 .slice => return out_stream.writeAll("(slice)"),
177 };
178 }
179
180 pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) {
181 return .{ .data = val };
182 }
183
184 pub fn fmtValue(val: Value, ty: Type, mod: *Module) std.fmt.Formatter(TypedValue.format) {
185 return .{ .data = .{
186 .tv = .{ .ty = ty, .val = val },
187 .mod = mod,
188 } };
189 }
190
191 /// Asserts that the value is representable as an array of bytes.
192 /// Returns the value as a null-terminated string stored in the InternPool.
193 pub fn toIpString(val: Value, ty: Type, mod: *Module) !InternPool.NullTerminatedString {
194 const ip = &mod.intern_pool;
195 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
196 .enum_literal => |enum_literal| enum_literal,
197 .slice => |slice| try arrayToIpString(val, Value.fromInterned(slice.len).toUnsignedInt(mod), mod),
198 .aggregate => |aggregate| switch (aggregate.storage) {
199 .bytes => |bytes| try ip.getOrPutString(mod.gpa, bytes),
200 .elems => try arrayToIpString(val, ty.arrayLen(mod), mod),
201 .repeated_elem => |elem| {
202 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
203 const len = @as(usize, @intCast(ty.arrayLen(mod)));
204 try ip.string_bytes.appendNTimes(mod.gpa, byte, len);
205 return ip.getOrPutTrailingString(mod.gpa, len);
206 },
207 },
208 else => unreachable,
209 };
210 }
211
212 /// Asserts that the value is representable as an array of bytes.
213 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
214 pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 {
215 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
216 .enum_literal => |enum_literal| allocator.dupe(u8, mod.intern_pool.stringToSlice(enum_literal)),
217 .slice => |slice| try arrayToAllocatedBytes(val, Value.fromInterned(slice.len).toUnsignedInt(mod), allocator, mod),
218 .aggregate => |aggregate| switch (aggregate.storage) {
219 .bytes => |bytes| try allocator.dupe(u8, bytes),
220 .elems => try arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod),
221 .repeated_elem => |elem| {
222 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
223 const result = try allocator.alloc(u8, @as(usize, @intCast(ty.arrayLen(mod))));
224 @memset(result, byte);
225 return result;
226 },
227 },
228 else => unreachable,
229 };
230 }
231
232 fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 {
233 const result = try allocator.alloc(u8, @as(usize, @intCast(len)));
234 for (result, 0..) |*elem, i| {
235 const elem_val = try val.elemValue(mod, i);
236 elem.* = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
237 }
238 return result;
239 }
240
241 fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTerminatedString {
242 const gpa = mod.gpa;
243 const ip = &mod.intern_pool;
244 const len = @as(usize, @intCast(len_u64));
245 try ip.string_bytes.ensureUnusedCapacity(gpa, len);
246 for (0..len) |i| {
247 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's
248 // assert just to be sure.
249 const prev = ip.string_bytes.items.len;
250 const elem_val = try val.elemValue(mod, i);
251 assert(ip.string_bytes.items.len == prev);
252 const byte = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
253 ip.string_bytes.appendAssumeCapacity(byte);
254 }
255 return ip.getOrPutTrailingString(gpa, len);
256 }
257
258 pub fn intern2(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
259 if (val.ip_index != .none) return val.ip_index;
260 return intern(val, ty, mod);
261 }
262
263 pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
264 if (val.ip_index != .none) return (try mod.getCoerced(val, ty)).toIntern();
265 const ip = &mod.intern_pool;
266 switch (val.tag()) {
267 .eu_payload => {
268 const pl = val.castTag(.eu_payload).?.data;
269 return mod.intern(.{ .error_union = .{
270 .ty = ty.toIntern(),
271 .val = .{ .payload = try pl.intern(ty.errorUnionPayload(mod), mod) },
272 } });
273 },
274 .opt_payload => {
275 const pl = val.castTag(.opt_payload).?.data;
276 return mod.intern(.{ .opt = .{
277 .ty = ty.toIntern(),
278 .val = try pl.intern(ty.optionalChild(mod), mod),
279 } });
280 },
281 .slice => {
282 const pl = val.castTag(.slice).?.data;
283 return mod.intern(.{ .slice = .{
284 .ty = ty.toIntern(),
285 .len = try pl.len.intern(Type.usize, mod),
286 .ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod),
287 } });
288 },
289 .bytes => {
290 const pl = val.castTag(.bytes).?.data;
291 return mod.intern(.{ .aggregate = .{
292 .ty = ty.toIntern(),
293 .storage = .{ .bytes = pl },
294 } });
295 },
296 .repeated => {
297 const pl = val.castTag(.repeated).?.data;
298 return mod.intern(.{ .aggregate = .{
299 .ty = ty.toIntern(),
300 .storage = .{ .repeated_elem = try pl.intern(ty.childType(mod), mod) },
301 } });
302 },
303 .aggregate => {
304 const len = @as(usize, @intCast(ty.arrayLen(mod)));
305 const old_elems = val.castTag(.aggregate).?.data[0..len];
306 const new_elems = try mod.gpa.alloc(InternPool.Index, old_elems.len);
307 defer mod.gpa.free(new_elems);
308 const ty_key = ip.indexToKey(ty.toIntern());
309 for (new_elems, old_elems, 0..) |*new_elem, old_elem, field_i|
310 new_elem.* = try old_elem.intern(switch (ty_key) {
311 .struct_type => ty.structFieldType(field_i, mod),
312 .anon_struct_type => |info| Type.fromInterned(info.types.get(ip)[field_i]),
313 inline .array_type, .vector_type => |info| Type.fromInterned(info.child),
314 else => unreachable,
315 }, mod);
316 return mod.intern(.{ .aggregate = .{
317 .ty = ty.toIntern(),
318 .storage = .{ .elems = new_elems },
319 } });
320 },
321 .@"union" => {
322 const pl = val.castTag(.@"union").?.data;
323 if (pl.tag) |pl_tag| {
324 return mod.intern(.{ .un = .{
325 .ty = ty.toIntern(),
326 .tag = try pl_tag.intern(ty.unionTagTypeHypothetical(mod), mod),
327 .val = try pl.val.intern(ty.unionFieldType(pl_tag, mod).?, mod),
328 } });
329 } else {
330 return mod.intern(.{ .un = .{
331 .ty = ty.toIntern(),
332 .tag = .none,
333 .val = try pl.val.intern(try ty.unionBackingType(mod), mod),
334 } });
335 }
336 },
337 }
338 }
339
340 pub fn unintern(val: Value, arena: Allocator, mod: *Module) Allocator.Error!Value {
341 return if (val.ip_index == .none) val else switch (mod.intern_pool.indexToKey(val.toIntern())) {
342 .int_type,
343 .ptr_type,
344 .array_type,
345 .vector_type,
346 .opt_type,
347 .anyframe_type,
348 .error_union_type,
349 .simple_type,
350 .struct_type,
351 .anon_struct_type,
352 .union_type,
353 .opaque_type,
354 .enum_type,
355 .func_type,
356 .error_set_type,
357 .inferred_error_set_type,
358
359 .undef,
360 .simple_value,
361 .variable,
362 .extern_func,
363 .func,
364 .int,
365 .err,
366 .enum_literal,
367 .enum_tag,
368 .empty_enum_value,
369 .float,
370 .ptr,
371 => val,
372
373 .error_union => |error_union| switch (error_union.val) {
374 .err_name => val,
375 .payload => |payload| Tag.eu_payload.create(arena, Value.fromInterned(payload)),
376 },
377
378 .slice => |slice| Tag.slice.create(arena, .{
379 .ptr = Value.fromInterned(slice.ptr),
380 .len = Value.fromInterned(slice.len),
381 }),
382
383 .opt => |opt| switch (opt.val) {
384 .none => val,
385 else => |payload| Tag.opt_payload.create(arena, Value.fromInterned(payload)),
386 },
387
388 .aggregate => |aggregate| switch (aggregate.storage) {
389 .bytes => |bytes| Tag.bytes.create(arena, try arena.dupe(u8, bytes)),
390 .elems => |old_elems| {
391 const new_elems = try arena.alloc(Value, old_elems.len);
392 for (new_elems, old_elems) |*new_elem, old_elem| new_elem.* = Value.fromInterned(old_elem);
393 return Tag.aggregate.create(arena, new_elems);
394 },
395 .repeated_elem => |elem| Tag.repeated.create(arena, Value.fromInterned(elem)),
396 },
397
398 .un => |un| Tag.@"union".create(arena, .{
399 // toValue asserts that the value cannot be .none which is valid on unions.
400 .tag = if (un.tag == .none) null else Value.fromInterned(un.tag),
401 .val = Value.fromInterned(un.val),
402 }),
403
404 .memoized_call => unreachable,
405 };
406 }
407
408 pub fn fromInterned(i: InternPool.Index) Value {
409 assert(i != .none);
410 return .{
411 .ip_index = i,
412 .legacy = undefined,
413 };
414 }
415
416 pub fn toIntern(val: Value) InternPool.Index {
417 assert(val.ip_index != .none);
418 return val.ip_index;
419 }
420
421 /// Asserts that the value is representable as a type.
422 pub fn toType(self: Value) Type {
423 return Type.fromInterned(self.toIntern());
424 }
425
426 pub fn intFromEnum(val: Value, ty: Type, mod: *Module) Allocator.Error!Value {
427 const ip = &mod.intern_pool;
428 return switch (ip.indexToKey(ip.typeOf(val.toIntern()))) {
429 // Assume it is already an integer and return it directly.
430 .simple_type, .int_type => val,
431 .enum_literal => |enum_literal| {
432 const field_index = ty.enumFieldIndex(enum_literal, mod).?;
433 return switch (ip.indexToKey(ty.toIntern())) {
434 // Assume it is already an integer and return it directly.
435 .simple_type, .int_type => val,
436 .enum_type => |enum_type| if (enum_type.values.len != 0)
437 Value.fromInterned(enum_type.values.get(ip)[field_index])
438 else // Field index and integer values are the same.
439 mod.intValue(Type.fromInterned(enum_type.tag_ty), field_index),
440 else => unreachable,
441 };
442 },
443 .enum_type => |enum_type| try mod.getCoerced(val, Type.fromInterned(enum_type.tag_ty)),
444 else => unreachable,
445 };
446 }
447
448 /// Asserts the value is an integer.
449 pub fn toBigInt(val: Value, space: *BigIntSpace, mod: *Module) BigIntConst {
450 return val.toBigIntAdvanced(space, mod, null) catch unreachable;
451 }
452
453 /// Asserts the value is an integer.
454 pub fn toBigIntAdvanced(
455 val: Value,
456 space: *BigIntSpace,
457 mod: *Module,
458 opt_sema: ?*Sema,
459 ) Module.CompileError!BigIntConst {
460 return switch (val.toIntern()) {
461 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
462 .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
463 .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
464 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
465 .int => |int| switch (int.storage) {
466 .u64, .i64, .big_int => int.storage.toBigInt(space),
467 .lazy_align, .lazy_size => |ty| {
468 if (opt_sema) |sema| try sema.resolveTypeLayout(Type.fromInterned(ty));
469 const x = switch (int.storage) {
470 else => unreachable,
471 .lazy_align => Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
472 .lazy_size => Type.fromInterned(ty).abiSize(mod),
473 };
474 return BigIntMutable.init(&space.limbs, x).toConst();
475 },
476 },
477 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, mod, opt_sema),
478 .opt, .ptr => BigIntMutable.init(
479 &space.limbs,
480 (try val.getUnsignedIntAdvanced(mod, opt_sema)).?,
481 ).toConst(),
482 else => unreachable,
483 },
484 };
485 }
486
487 pub fn isFuncBody(val: Value, mod: *Module) bool {
488 return mod.intern_pool.isFuncBody(val.toIntern());
489 }
490
491 pub fn getFunction(val: Value, mod: *Module) ?InternPool.Key.Func {
492 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
493 .func => |x| x,
494 else => null,
495 } else null;
496 }
497
498 pub fn getExternFunc(val: Value, mod: *Module) ?InternPool.Key.ExternFunc {
499 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
500 .extern_func => |extern_func| extern_func,
501 else => null,
502 } else null;
503 }
504
505 pub fn getVariable(val: Value, mod: *Module) ?InternPool.Key.Variable {
506 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
507 .variable => |variable| variable,
508 else => null,
509 } else null;
510 }
511
512 /// If the value fits in a u64, return it, otherwise null.
513 /// Asserts not undefined.
514 pub fn getUnsignedInt(val: Value, mod: *Module) ?u64 {
515 return getUnsignedIntAdvanced(val, mod, null) catch unreachable;
516 }
517
518 /// If the value fits in a u64, return it, otherwise null.
519 /// Asserts not undefined.
520 pub fn getUnsignedIntAdvanced(val: Value, mod: *Module, opt_sema: ?*Sema) !?u64 {
521 return switch (val.toIntern()) {
522 .undef => unreachable,
523 .bool_false => 0,
524 .bool_true => 1,
525 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
526 .undef => unreachable,
527 .int => |int| switch (int.storage) {
528 .big_int => |big_int| big_int.to(u64) catch null,
529 .u64 => |x| x,
530 .i64 => |x| std.math.cast(u64, x),
531 .lazy_align => |ty| if (opt_sema) |sema|
532 (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0)
533 else
534 Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
535 .lazy_size => |ty| if (opt_sema) |sema|
536 (try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar
537 else
538 Type.fromInterned(ty).abiSize(mod),
539 },
540 .ptr => |ptr| switch (ptr.addr) {
541 .int => |int| Value.fromInterned(int).getUnsignedIntAdvanced(mod, opt_sema),
542 .elem => |elem| {
543 const base_addr = (try Value.fromInterned(elem.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
544 const elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod);
545 return base_addr + elem.index * elem_ty.abiSize(mod);
546 },
547 .field => |field| {
548 const base_addr = (try Value.fromInterned(field.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
549 const struct_ty = Type.fromInterned(mod.intern_pool.typeOf(field.base)).childType(mod);
550 if (opt_sema) |sema| try sema.resolveTypeLayout(struct_ty);
551 return base_addr + struct_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod);
552 },
553 else => null,
554 },
555 .opt => |opt| switch (opt.val) {
556 .none => 0,
557 else => |payload| Value.fromInterned(payload).getUnsignedIntAdvanced(mod, opt_sema),
558 },
559 else => null,
560 },
561 };
562 }
563
564 /// Asserts the value is an integer and it fits in a u64
565 pub fn toUnsignedInt(val: Value, mod: *Module) u64 {
566 return getUnsignedInt(val, mod).?;
567 }
568
569 /// Asserts the value is an integer and it fits in a u64
570 pub fn toUnsignedIntAdvanced(val: Value, sema: *Sema) !u64 {
571 return (try getUnsignedIntAdvanced(val, sema.mod, sema)).?;
572 }
573
574 /// Asserts the value is an integer and it fits in a i64
575 pub fn toSignedInt(val: Value, mod: *Module) i64 {
576 return switch (val.toIntern()) {
577 .bool_false => 0,
578 .bool_true => 1,
579 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
580 .int => |int| switch (int.storage) {
581 .big_int => |big_int| big_int.to(i64) catch unreachable,
582 .i64 => |x| x,
583 .u64 => |x| @intCast(x),
584 .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
585 .lazy_size => |ty| @intCast(Type.fromInterned(ty).abiSize(mod)),
586 },
587 else => unreachable,
588 },
589 };
590 }
591
592 pub fn toBool(val: Value) bool {
593 return switch (val.toIntern()) {
594 .bool_true => true,
595 .bool_false => false,
596 else => unreachable,
597 };
598 }
599
600 fn isDeclRef(val: Value, mod: *Module) bool {
601 var check = val;
602 while (true) switch (mod.intern_pool.indexToKey(check.toIntern())) {
603 .ptr => |ptr| switch (ptr.addr) {
604 .decl, .mut_decl, .comptime_field, .anon_decl => return true,
605 .eu_payload, .opt_payload => |base| check = Value.fromInterned(base),
606 .elem, .field => |base_index| check = Value.fromInterned(base_index.base),
607 .int => return false,
608 },
609 else => return false,
610 };
611 }
612
613 /// Write a Value's contents to `buffer`.
614 ///
615 /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
616 /// the end of the value in memory.
617 pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{
618 ReinterpretDeclRef,
619 IllDefinedMemoryLayout,
620 Unimplemented,
621 OutOfMemory,
622 }!void {
623 const target = mod.getTarget();
624 const endian = target.cpu.arch.endian();
625 if (val.isUndef(mod)) {
626 const size: usize = @intCast(ty.abiSize(mod));
627 @memset(buffer[0..size], 0xaa);
628 return;
629 }
630 const ip = &mod.intern_pool;
631 switch (ty.zigTypeTag(mod)) {
632 .Void => {},
633 .Bool => {
634 buffer[0] = @intFromBool(val.toBool());
635 },
636 .Int, .Enum => {
637 const int_info = ty.intInfo(mod);
638 const bits = int_info.bits;
639 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
640
641 var bigint_buffer: BigIntSpace = undefined;
642 const bigint = val.toBigInt(&bigint_buffer, mod);
643 bigint.writeTwosComplement(buffer[0..byte_count], endian);
644 },
645 .Float => switch (ty.floatBits(target)) {
646 16 => std.mem.writeInt(u16, buffer[0..2], @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
647 32 => std.mem.writeInt(u32, buffer[0..4], @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
648 64 => std.mem.writeInt(u64, buffer[0..8], @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
649 80 => std.mem.writeInt(u80, buffer[0..10], @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
650 128 => std.mem.writeInt(u128, buffer[0..16], @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
651 else => unreachable,
652 },
653 .Array => {
654 const len = ty.arrayLen(mod);
655 const elem_ty = ty.childType(mod);
656 const elem_size = @as(usize, @intCast(elem_ty.abiSize(mod)));
657 var elem_i: usize = 0;
658 var buf_off: usize = 0;
659 while (elem_i < len) : (elem_i += 1) {
660 const elem_val = try val.elemValue(mod, elem_i);
661 try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]);
662 buf_off += elem_size;
663 }
664 },
665 .Vector => {
666 // We use byte_count instead of abi_size here, so that any padding bytes
667 // follow the data bytes, on both big- and little-endian systems.
668 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
669 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
670 },
671 .Struct => {
672 const struct_type = mod.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;
673 switch (struct_type.layout) {
674 .Auto => return error.IllDefinedMemoryLayout,
675 .Extern => for (0..struct_type.field_types.len) |i| {
676 const off: usize = @intCast(ty.structFieldOffset(i, mod));
677 const field_val = switch (val.ip_index) {
678 .none => switch (val.tag()) {
679 .bytes => {
680 buffer[off] = val.castTag(.bytes).?.data[i];
681 continue;
682 },
683 .aggregate => val.castTag(.aggregate).?.data[i],
684 .repeated => val.castTag(.repeated).?.data,
685 else => unreachable,
686 },
687 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
688 .bytes => |bytes| {
689 buffer[off] = bytes[i];
690 continue;
691 },
692 .elems => |elems| elems[i],
693 .repeated_elem => |elem| elem,
694 }),
695 };
696 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
697 try writeToMemory(field_val, field_ty, mod, buffer[off..]);
698 },
699 .Packed => {
700 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
701 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
702 },
703 }
704 },
705 .ErrorSet => {
706 const bits = mod.errorSetBits();
707 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
708
709 const name = switch (ip.indexToKey(val.toIntern())) {
710 .err => |err| err.name,
711 .error_union => |error_union| error_union.val.err_name,
712 else => unreachable,
713 };
714 var bigint_buffer: BigIntSpace = undefined;
715 const bigint = BigIntMutable.init(
716 &bigint_buffer.limbs,
717 mod.global_error_set.getIndex(name).?,
718 ).toConst();
719 bigint.writeTwosComplement(buffer[0..byte_count], endian);
720 },
721 .Union => switch (ty.containerLayout(mod)) {
722 .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
723 .Extern => {
724 if (val.unionTag(mod)) |union_tag| {
725 const union_obj = mod.typeToUnion(ty).?;
726 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
727 const field_type = Type.fromInterned(union_obj.field_types.get(&mod.intern_pool)[field_index]);
728 const field_val = try val.fieldValue(mod, field_index);
729 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
730 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
731 } else {
732 const backing_ty = try ty.unionBackingType(mod);
733 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
734 return writeToMemory(val.unionValue(mod), backing_ty, mod, buffer[0..byte_count]);
735 }
736 },
737 .Packed => {
738 const backing_ty = try ty.unionBackingType(mod);
739 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
740 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
741 },
742 },
743 .Pointer => {
744 if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout;
745 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
746 return val.writeToMemory(Type.usize, mod, buffer);
747 },
748 .Optional => {
749 if (!ty.isPtrLikeOptional(mod)) return error.IllDefinedMemoryLayout;
750 const child = ty.optionalChild(mod);
751 const opt_val = val.optionalValue(mod);
752 if (opt_val) |some| {
753 return some.writeToMemory(child, mod, buffer);
754 } else {
755 return writeToMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer);
756 }
757 },
758 else => return error.Unimplemented,
759 }
760 }
761
762 /// Write a Value's contents to `buffer`.
763 ///
764 /// Both the start and the end of the provided buffer must be tight, since
765 /// big-endian packed memory layouts start at the end of the buffer.
766 pub fn writeToPackedMemory(
767 val: Value,
768 ty: Type,
769 mod: *Module,
770 buffer: []u8,
771 bit_offset: usize,
772 ) error{ ReinterpretDeclRef, OutOfMemory }!void {
773 const ip = &mod.intern_pool;
774 const target = mod.getTarget();
775 const endian = target.cpu.arch.endian();
776 if (val.isUndef(mod)) {
777 const bit_size = @as(usize, @intCast(ty.bitSize(mod)));
778 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian);
779 return;
780 }
781 switch (ty.zigTypeTag(mod)) {
782 .Void => {},
783 .Bool => {
784 const byte_index = switch (endian) {
785 .little => bit_offset / 8,
786 .big => buffer.len - bit_offset / 8 - 1,
787 };
788 if (val.toBool()) {
789 buffer[byte_index] |= (@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
790 } else {
791 buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
792 }
793 },
794 .Int, .Enum => {
795 if (buffer.len == 0) return;
796 const bits = ty.intInfo(mod).bits;
797 if (bits == 0) return;
798
799 switch (ip.indexToKey((try val.intFromEnum(ty, mod)).toIntern()).int.storage) {
800 inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
801 .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
802 .lazy_align => |lazy_align| {
803 const num = Type.fromInterned(lazy_align).abiAlignment(mod).toByteUnits(0);
804 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
805 },
806 .lazy_size => |lazy_size| {
807 const num = Type.fromInterned(lazy_size).abiSize(mod);
808 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
809 },
810 }
811 },
812 .Float => switch (ty.floatBits(target)) {
813 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
814 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
815 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
816 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
817 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
818 else => unreachable,
819 },
820 .Vector => {
821 const elem_ty = ty.childType(mod);
822 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
823 const len = @as(usize, @intCast(ty.arrayLen(mod)));
824
825 var bits: u16 = 0;
826 var elem_i: usize = 0;
827 while (elem_i < len) : (elem_i += 1) {
828 // On big-endian systems, LLVM reverses the element order of vectors by default
829 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
830 const elem_val = try val.elemValue(mod, tgt_elem_i);
831 try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
832 bits += elem_bit_size;
833 }
834 },
835 .Struct => {
836 const struct_type = ip.indexToKey(ty.toIntern()).struct_type;
837 // Sema is supposed to have emitted a compile error already in the case of Auto,
838 // and Extern is handled in non-packed writeToMemory.
839 assert(struct_type.layout == .Packed);
840 var bits: u16 = 0;
841 for (0..struct_type.field_types.len) |i| {
842 const field_val = switch (val.ip_index) {
843 .none => switch (val.tag()) {
844 .bytes => unreachable,
845 .aggregate => val.castTag(.aggregate).?.data[i],
846 .repeated => val.castTag(.repeated).?.data,
847 else => unreachable,
848 },
849 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
850 .bytes => unreachable,
851 .elems => |elems| elems[i],
852 .repeated_elem => |elem| elem,
853 }),
854 };
855 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
856 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
857 try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
858 bits += field_bits;
859 }
860 },
861 .Union => {
862 const union_obj = mod.typeToUnion(ty).?;
863 switch (union_obj.getLayout(ip)) {
864 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
865 .Packed => {
866 if (val.unionTag(mod)) |union_tag| {
867 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
868 const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
869 const field_val = try val.fieldValue(mod, field_index);
870 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
871 } else {
872 const backing_ty = try ty.unionBackingType(mod);
873 return val.unionValue(mod).writeToPackedMemory(backing_ty, mod, buffer, bit_offset);
874 }
875 },
876 }
877 },
878 .Pointer => {
879 assert(!ty.isSlice(mod)); // No well defined layout.
880 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
881 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);
882 },
883 .Optional => {
884 assert(ty.isPtrLikeOptional(mod));
885 const child = ty.optionalChild(mod);
886 const opt_val = val.optionalValue(mod);
887 if (opt_val) |some| {
888 return some.writeToPackedMemory(child, mod, buffer, bit_offset);
889 } else {
890 return writeToPackedMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer, bit_offset);
891 }
892 },
893 else => @panic("TODO implement writeToPackedMemory for more types"),
894 }
895 }
896
897 /// Load a Value from the contents of `buffer`.
898 ///
899 /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
900 /// the end of the value in memory.
901 pub fn readFromMemory(
902 ty: Type,
903 mod: *Module,
904 buffer: []const u8,
905 arena: Allocator,
906 ) error{
907 IllDefinedMemoryLayout,
908 Unimplemented,
909 OutOfMemory,
910 }!Value {
911 const ip = &mod.intern_pool;
912 const target = mod.getTarget();
913 const endian = target.cpu.arch.endian();
914 switch (ty.zigTypeTag(mod)) {
915 .Void => return Value.void,
916 .Bool => {
917 if (buffer[0] == 0) {
918 return Value.false;
919 } else {
920 return Value.true;
921 }
922 },
923 .Int, .Enum => |ty_tag| {
924 const int_ty = switch (ty_tag) {
925 .Int => ty,
926 .Enum => ty.intTagType(mod),
927 else => unreachable,
928 };
929 const int_info = int_ty.intInfo(mod);
930 const bits = int_info.bits;
931 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
932 if (bits == 0 or buffer.len == 0) return mod.getCoerced(try mod.intValue(int_ty, 0), ty);
933
934 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
935 .signed => {
936 const val = std.mem.readVarInt(i64, buffer[0..byte_count], endian);
937 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
938 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
939 },
940 .unsigned => {
941 const val = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
942 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
943 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
944 },
945 } else { // Slow path, we have to construct a big-int
946 const Limb = std.math.big.Limb;
947 const limb_count = (byte_count + @sizeOf(Limb) - 1) / @sizeOf(Limb);
948 const limbs_buffer = try arena.alloc(Limb, limb_count);
949
950 var bigint = BigIntMutable.init(limbs_buffer, 0);
951 bigint.readTwosComplement(buffer[0..byte_count], bits, endian, int_info.signedness);
952 return mod.getCoerced(try mod.intValue_big(int_ty, bigint.toConst()), ty);
953 }
954 },
955 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
956 .ty = ty.toIntern(),
957 .storage = switch (ty.floatBits(target)) {
958 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readInt(u16, buffer[0..2], endian))) },
959 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readInt(u32, buffer[0..4], endian))) },
960 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readInt(u64, buffer[0..8], endian))) },
961 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readInt(u80, buffer[0..10], endian))) },
962 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readInt(u128, buffer[0..16], endian))) },
963 else => unreachable,
964 },
965 } }))),
966 .Array => {
967 const elem_ty = ty.childType(mod);
968 const elem_size = elem_ty.abiSize(mod);
969 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
970 var offset: usize = 0;
971 for (elems) |*elem| {
972 elem.* = try (try readFromMemory(elem_ty, mod, buffer[offset..], arena)).intern(elem_ty, mod);
973 offset += @as(usize, @intCast(elem_size));
974 }
975 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
976 .ty = ty.toIntern(),
977 .storage = .{ .elems = elems },
978 } })));
979 },
980 .Vector => {
981 // We use byte_count instead of abi_size here, so that any padding bytes
982 // follow the data bytes, on both big- and little-endian systems.
983 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
984 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
985 },
986 .Struct => {
987 const struct_type = mod.typeToStruct(ty).?;
988 switch (struct_type.layout) {
989 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
990 .Extern => {
991 const field_types = struct_type.field_types;
992 const field_vals = try arena.alloc(InternPool.Index, field_types.len);
993 for (field_vals, 0..) |*field_val, i| {
994 const field_ty = Type.fromInterned(field_types.get(ip)[i]);
995 const off: usize = @intCast(ty.structFieldOffset(i, mod));
996 const sz: usize = @intCast(field_ty.abiSize(mod));
997 field_val.* = try (try readFromMemory(field_ty, mod, buffer[off..(off + sz)], arena)).intern(field_ty, mod);
998 }
999 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1000 .ty = ty.toIntern(),
1001 .storage = .{ .elems = field_vals },
1002 } })));
1003 },
1004 .Packed => {
1005 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1006 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1007 },
1008 }
1009 },
1010 .ErrorSet => {
1011 const bits = mod.errorSetBits();
1012 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
1013 const int = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
1014 const index = (int << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
1015 const name = mod.global_error_set.keys()[@intCast(index)];
1016
1017 return Value.fromInterned((try mod.intern(.{ .err = .{
1018 .ty = ty.toIntern(),
1019 .name = name,
1020 } })));
1021 },
1022 .Union => switch (ty.containerLayout(mod)) {
1023 .Auto => return error.IllDefinedMemoryLayout,
1024 .Extern => {
1025 const union_size = ty.abiSize(mod);
1026 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
1027 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
1028 return Value.fromInterned((try mod.intern(.{ .un = .{
1029 .ty = ty.toIntern(),
1030 .tag = .none,
1031 .val = val,
1032 } })));
1033 },
1034 .Packed => {
1035 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1036 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1037 },
1038 },
1039 .Pointer => {
1040 assert(!ty.isSlice(mod)); // No well defined layout.
1041 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
1042 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1043 .ty = ty.toIntern(),
1044 .addr = .{ .int = int_val.toIntern() },
1045 } })));
1046 },
1047 .Optional => {
1048 assert(ty.isPtrLikeOptional(mod));
1049 const child_ty = ty.optionalChild(mod);
1050 const child_val = try readFromMemory(child_ty, mod, buffer, arena);
1051 return Value.fromInterned((try mod.intern(.{ .opt = .{
1052 .ty = ty.toIntern(),
1053 .val = switch (child_val.orderAgainstZero(mod)) {
1054 .lt => unreachable,
1055 .eq => .none,
1056 .gt => child_val.toIntern(),
1057 },
1058 } })));
1059 },
1060 else => return error.Unimplemented,
1061 }
1062 }
1063
1064 /// Load a Value from the contents of `buffer`.
1065 ///
1066 /// Both the start and the end of the provided buffer must be tight, since
1067 /// big-endian packed memory layouts start at the end of the buffer.
1068 pub fn readFromPackedMemory(
1069 ty: Type,
1070 mod: *Module,
1071 buffer: []const u8,
1072 bit_offset: usize,
1073 arena: Allocator,
1074 ) error{
1075 IllDefinedMemoryLayout,
1076 OutOfMemory,
1077 }!Value {
1078 const ip = &mod.intern_pool;
1079 const target = mod.getTarget();
1080 const endian = target.cpu.arch.endian();
1081 switch (ty.zigTypeTag(mod)) {
1082 .Void => return Value.void,
1083 .Bool => {
1084 const byte = switch (endian) {
1085 .big => buffer[buffer.len - bit_offset / 8 - 1],
1086 .little => buffer[bit_offset / 8],
1087 };
1088 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) {
1089 return Value.false;
1090 } else {
1091 return Value.true;
1092 }
1093 },
1094 .Int, .Enum => |ty_tag| {
1095 if (buffer.len == 0) return mod.intValue(ty, 0);
1096 const int_info = ty.intInfo(mod);
1097 const bits = int_info.bits;
1098 if (bits == 0) return mod.intValue(ty, 0);
1099
1100 // Fast path for integers <= u64
1101 if (bits <= 64) {
1102 const int_ty = switch (ty_tag) {
1103 .Int => ty,
1104 .Enum => ty.intTagType(mod),
1105 else => unreachable,
1106 };
1107 return mod.getCoerced(switch (int_info.signedness) {
1108 .signed => return mod.intValue(
1109 int_ty,
1110 std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed),
1111 ),
1112 .unsigned => return mod.intValue(
1113 int_ty,
1114 std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned),
1115 ),
1116 }, ty);
1117 }
1118
1119 // Slow path, we have to construct a big-int
1120 const abi_size = @as(usize, @intCast(ty.abiSize(mod)));
1121 const Limb = std.math.big.Limb;
1122 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1123 const limbs_buffer = try arena.alloc(Limb, limb_count);
1124
1125 var bigint = BigIntMutable.init(limbs_buffer, 0);
1126 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
1127 return mod.intValue_big(ty, bigint.toConst());
1128 },
1129 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
1130 .ty = ty.toIntern(),
1131 .storage = switch (ty.floatBits(target)) {
1132 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian))) },
1133 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian))) },
1134 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian))) },
1135 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian))) },
1136 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian))) },
1137 else => unreachable,
1138 },
1139 } }))),
1140 .Vector => {
1141 const elem_ty = ty.childType(mod);
1142 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
1143
1144 var bits: u16 = 0;
1145 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
1146 for (elems, 0..) |_, i| {
1147 // On big-endian systems, LLVM reverses the element order of vectors by default
1148 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;
1149 elems[tgt_elem_i] = try (try readFromPackedMemory(elem_ty, mod, buffer, bit_offset + bits, arena)).intern(elem_ty, mod);
1150 bits += elem_bit_size;
1151 }
1152 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1153 .ty = ty.toIntern(),
1154 .storage = .{ .elems = elems },
1155 } })));
1156 },
1157 .Struct => {
1158 // Sema is supposed to have emitted a compile error already for Auto layout structs,
1159 // and Extern is handled by non-packed readFromMemory.
1160 const struct_type = mod.typeToPackedStruct(ty).?;
1161 var bits: u16 = 0;
1162 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
1163 for (field_vals, 0..) |*field_val, i| {
1164 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
1165 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
1166 field_val.* = try (try readFromPackedMemory(field_ty, mod, buffer, bit_offset + bits, arena)).intern(field_ty, mod);
1167 bits += field_bits;
1168 }
1169 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1170 .ty = ty.toIntern(),
1171 .storage = .{ .elems = field_vals },
1172 } })));
1173 },
1174 .Union => switch (ty.containerLayout(mod)) {
1175 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
1176 .Packed => {
1177 const backing_ty = try ty.unionBackingType(mod);
1178 const val = (try readFromPackedMemory(backing_ty, mod, buffer, bit_offset, arena)).toIntern();
1179 return Value.fromInterned((try mod.intern(.{ .un = .{
1180 .ty = ty.toIntern(),
1181 .tag = .none,
1182 .val = val,
1183 } })));
1184 },
1185 },
1186 .Pointer => {
1187 assert(!ty.isSlice(mod)); // No well defined layout.
1188 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
1189 },
1190 .Optional => {
1191 assert(ty.isPtrLikeOptional(mod));
1192 const child = ty.optionalChild(mod);
1193 return readFromPackedMemory(child, mod, buffer, bit_offset, arena);
1194 },
1195 else => @panic("TODO implement readFromPackedMemory for more types"),
1196 }
1197 }
1198
1199 /// Asserts that the value is a float or an integer.
1200 pub fn toFloat(val: Value, comptime T: type, mod: *Module) T {
1201 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1202 .int => |int| switch (int.storage) {
1203 .big_int => |big_int| @floatCast(bigIntToFloat(big_int.limbs, big_int.positive)),
1204 inline .u64, .i64 => |x| {
1205 if (T == f80) {
1206 @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
1207 }
1208 return @floatFromInt(x);
1209 },
1210 .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
1211 .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(mod)),
1212 },
1213 .float => |float| switch (float.storage) {
1214 inline else => |x| @floatCast(x),
1215 },
1216 else => unreachable,
1217 };
1218 }
1219
1220 /// TODO move this to std lib big int code
1221 fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
1222 if (limbs.len == 0) return 0;
1223
1224 const base = std.math.maxInt(std.math.big.Limb) + 1;
1225 var result: f128 = 0;
1226 var i: usize = limbs.len;
1227 while (i != 0) {
1228 i -= 1;
1229 const limb: f128 = @as(f128, @floatFromInt(limbs[i]));
1230 result = @mulAdd(f128, base, result, limb);
1231 }
1232 if (positive) {
1233 return result;
1234 } else {
1235 return -result;
1236 }
1237 }
1238
1239 pub fn clz(val: Value, ty: Type, mod: *Module) u64 {
1240 var bigint_buf: BigIntSpace = undefined;
1241 const bigint = val.toBigInt(&bigint_buf, mod);
1242 return bigint.clz(ty.intInfo(mod).bits);
1243 }
1244
1245 pub fn ctz(val: Value, ty: Type, mod: *Module) u64 {
1246 var bigint_buf: BigIntSpace = undefined;
1247 const bigint = val.toBigInt(&bigint_buf, mod);
1248 return bigint.ctz(ty.intInfo(mod).bits);
1249 }
1250
1251 pub fn popCount(val: Value, ty: Type, mod: *Module) u64 {
1252 var bigint_buf: BigIntSpace = undefined;
1253 const bigint = val.toBigInt(&bigint_buf, mod);
1254 return @as(u64, @intCast(bigint.popCount(ty.intInfo(mod).bits)));
1255 }
1256
1257 pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1258 const info = ty.intInfo(mod);
1259
1260 var buffer: Value.BigIntSpace = undefined;
1261 const operand_bigint = val.toBigInt(&buffer, mod);
1262
1263 const limbs = try arena.alloc(
1264 std.math.big.Limb,
1265 std.math.big.int.calcTwosCompLimbCount(info.bits),
1266 );
1267 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1268 result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
1269
1270 return mod.intValue_big(ty, result_bigint.toConst());
1271 }
1272
1273 pub fn byteSwap(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1274 const info = ty.intInfo(mod);
1275
1276 // Bit count must be evenly divisible by 8
1277 assert(info.bits % 8 == 0);
1278
1279 var buffer: Value.BigIntSpace = undefined;
1280 const operand_bigint = val.toBigInt(&buffer, mod);
1281
1282 const limbs = try arena.alloc(
1283 std.math.big.Limb,
1284 std.math.big.int.calcTwosCompLimbCount(info.bits),
1285 );
1286 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1287 result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8);
1288
1289 return mod.intValue_big(ty, result_bigint.toConst());
1290 }
1291
1292 /// Asserts the value is an integer and not undefined.
1293 /// Returns the number of bits the value requires to represent stored in twos complement form.
1294 pub fn intBitCountTwosComp(self: Value, mod: *Module) usize {
1295 var buffer: BigIntSpace = undefined;
1296 const big_int = self.toBigInt(&buffer, mod);
1297 return big_int.bitCountTwosComp();
1298 }
1299
1300 /// Converts an integer or a float to a float. May result in a loss of information.
1301 /// Caller can find out by equality checking the result against the operand.
1302 pub fn floatCast(self: Value, dest_ty: Type, mod: *Module) !Value {
1303 const target = mod.getTarget();
1304 return Value.fromInterned((try mod.intern(.{ .float = .{
1305 .ty = dest_ty.toIntern(),
1306 .storage = switch (dest_ty.floatBits(target)) {
1307 16 => .{ .f16 = self.toFloat(f16, mod) },
1308 32 => .{ .f32 = self.toFloat(f32, mod) },
1309 64 => .{ .f64 = self.toFloat(f64, mod) },
1310 80 => .{ .f80 = self.toFloat(f80, mod) },
1311 128 => .{ .f128 = self.toFloat(f128, mod) },
1312 else => unreachable,
1313 },
1314 } })));
1315 }
1316
1317 /// Asserts the value is a float
1318 pub fn floatHasFraction(self: Value, mod: *const Module) bool {
1319 return switch (mod.intern_pool.indexToKey(self.toIntern())) {
1320 .float => |float| switch (float.storage) {
1321 inline else => |x| @rem(x, 1) != 0,
1322 },
1323 else => unreachable,
1324 };
1325 }
1326
1327 pub fn orderAgainstZero(lhs: Value, mod: *Module) std.math.Order {
1328 return orderAgainstZeroAdvanced(lhs, mod, null) catch unreachable;
1329 }
1330
1331 pub fn orderAgainstZeroAdvanced(
1332 lhs: Value,
1333 mod: *Module,
1334 opt_sema: ?*Sema,
1335 ) Module.CompileError!std.math.Order {
1336 return switch (lhs.toIntern()) {
1337 .bool_false => .eq,
1338 .bool_true => .gt,
1339 else => switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1340 .ptr => |ptr| switch (ptr.addr) {
1341 .decl, .mut_decl, .comptime_field => .gt,
1342 .int => |int| Value.fromInterned(int).orderAgainstZeroAdvanced(mod, opt_sema),
1343 .elem => |elem| switch (try Value.fromInterned(elem.base).orderAgainstZeroAdvanced(mod, opt_sema)) {
1344 .lt => unreachable,
1345 .gt => .gt,
1346 .eq => if (elem.index == 0) .eq else .gt,
1347 },
1348 else => unreachable,
1349 },
1350 .int => |int| switch (int.storage) {
1351 .big_int => |big_int| big_int.orderAgainstScalar(0),
1352 inline .u64, .i64 => |x| std.math.order(x, 0),
1353 .lazy_align => .gt, // alignment is never 0
1354 .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsAdvanced(
1355 mod,
1356 false,
1357 if (opt_sema) |sema| .{ .sema = sema } else .eager,
1358 ) catch |err| switch (err) {
1359 error.NeedLazy => unreachable,
1360 else => |e| return e,
1361 }) .gt else .eq,
1362 },
1363 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroAdvanced(mod, opt_sema),
1364 .float => |float| switch (float.storage) {
1365 inline else => |x| std.math.order(x, 0),
1366 },
1367 else => unreachable,
1368 },
1369 };
1370 }
1371
1372 /// Asserts the value is comparable.
1373 pub fn order(lhs: Value, rhs: Value, mod: *Module) std.math.Order {
1374 return orderAdvanced(lhs, rhs, mod, null) catch unreachable;
1375 }
1376
1377 /// Asserts the value is comparable.
1378 /// If opt_sema is null then this function asserts things are resolved and cannot fail.
1379 pub fn orderAdvanced(lhs: Value, rhs: Value, mod: *Module, opt_sema: ?*Sema) !std.math.Order {
1380 const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(mod, opt_sema);
1381 const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(mod, opt_sema);
1382 switch (lhs_against_zero) {
1383 .lt => if (rhs_against_zero != .lt) return .lt,
1384 .eq => return rhs_against_zero.invert(),
1385 .gt => {},
1386 }
1387 switch (rhs_against_zero) {
1388 .lt => if (lhs_against_zero != .lt) return .gt,
1389 .eq => return lhs_against_zero,
1390 .gt => {},
1391 }
1392
1393 if (lhs.isFloat(mod) or rhs.isFloat(mod)) {
1394 const lhs_f128 = lhs.toFloat(f128, mod);
1395 const rhs_f128 = rhs.toFloat(f128, mod);
1396 return std.math.order(lhs_f128, rhs_f128);
1397 }
1398
1399 var lhs_bigint_space: BigIntSpace = undefined;
1400 var rhs_bigint_space: BigIntSpace = undefined;
1401 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, mod, opt_sema);
1402 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, mod, opt_sema);
1403 return lhs_bigint.order(rhs_bigint);
1404 }
1405
1406 /// Asserts the value is comparable. Does not take a type parameter because it supports
1407 /// comparisons between heterogeneous types.
1408 pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, mod: *Module) bool {
1409 return compareHeteroAdvanced(lhs, op, rhs, mod, null) catch unreachable;
1410 }
1411
1412 pub fn compareHeteroAdvanced(
1413 lhs: Value,
1414 op: std.math.CompareOperator,
1415 rhs: Value,
1416 mod: *Module,
1417 opt_sema: ?*Sema,
1418 ) !bool {
1419 if (lhs.pointerDecl(mod)) |lhs_decl| {
1420 if (rhs.pointerDecl(mod)) |rhs_decl| {
1421 switch (op) {
1422 .eq => return lhs_decl == rhs_decl,
1423 .neq => return lhs_decl != rhs_decl,
1424 else => {},
1425 }
1426 } else {
1427 switch (op) {
1428 .eq => return false,
1429 .neq => return true,
1430 else => {},
1431 }
1432 }
1433 } else if (rhs.pointerDecl(mod)) |_| {
1434 switch (op) {
1435 .eq => return false,
1436 .neq => return true,
1437 else => {},
1438 }
1439 }
1440 return (try orderAdvanced(lhs, rhs, mod, opt_sema)).compare(op);
1441 }
1442
1443 /// Asserts the values are comparable. Both operands have type `ty`.
1444 /// For vectors, returns true if comparison is true for ALL elements.
1445 pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) !bool {
1446 if (ty.zigTypeTag(mod) == .Vector) {
1447 const scalar_ty = ty.scalarType(mod);
1448 for (0..ty.vectorLen(mod)) |i| {
1449 const lhs_elem = try lhs.elemValue(mod, i);
1450 const rhs_elem = try rhs.elemValue(mod, i);
1451 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, mod)) {
1452 return false;
1453 }
1454 }
1455 return true;
1456 }
1457 return compareScalar(lhs, op, rhs, ty, mod);
1458 }
1459
1460 /// Asserts the values are comparable. Both operands have type `ty`.
1461 pub fn compareScalar(
1462 lhs: Value,
1463 op: std.math.CompareOperator,
1464 rhs: Value,
1465 ty: Type,
1466 mod: *Module,
1467 ) bool {
1468 return switch (op) {
1469 .eq => lhs.eql(rhs, ty, mod),
1470 .neq => !lhs.eql(rhs, ty, mod),
1471 else => compareHetero(lhs, op, rhs, mod),
1472 };
1473 }
1474
1475 /// Asserts the value is comparable.
1476 /// For vectors, returns true if comparison is true for ALL elements.
1477 ///
1478 /// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
1479 pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, mod: *Module) bool {
1480 return compareAllWithZeroAdvancedExtra(lhs, op, mod, null) catch unreachable;
1481 }
1482
1483 pub fn compareAllWithZeroAdvanced(
1484 lhs: Value,
1485 op: std.math.CompareOperator,
1486 sema: *Sema,
1487 ) Module.CompileError!bool {
1488 return compareAllWithZeroAdvancedExtra(lhs, op, sema.mod, sema);
1489 }
1490
1491 pub fn compareAllWithZeroAdvancedExtra(
1492 lhs: Value,
1493 op: std.math.CompareOperator,
1494 mod: *Module,
1495 opt_sema: ?*Sema,
1496 ) Module.CompileError!bool {
1497 if (lhs.isInf(mod)) {
1498 switch (op) {
1499 .neq => return true,
1500 .eq => return false,
1501 .gt, .gte => return !lhs.isNegativeInf(mod),
1502 .lt, .lte => return lhs.isNegativeInf(mod),
1503 }
1504 }
1505
1506 switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1507 .float => |float| switch (float.storage) {
1508 inline else => |x| if (std.math.isNan(x)) return op == .neq,
1509 },
1510 .aggregate => |aggregate| return switch (aggregate.storage) {
1511 .bytes => |bytes| for (bytes) |byte| {
1512 if (!std.math.order(byte, 0).compare(op)) break false;
1513 } else true,
1514 .elems => |elems| for (elems) |elem| {
1515 if (!try Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema)) break false;
1516 } else true,
1517 .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema),
1518 },
1519 else => {},
1520 }
1521 return (try orderAgainstZeroAdvanced(lhs, mod, opt_sema)).compare(op);
1522 }
1523
1524 pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
1525 assert(mod.intern_pool.typeOf(a.toIntern()) == ty.toIntern());
1526 assert(mod.intern_pool.typeOf(b.toIntern()) == ty.toIntern());
1527 return a.toIntern() == b.toIntern();
1528 }
1529
1530 pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
1531 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1532 .slice => |slice| return Value.fromInterned(slice.ptr).isComptimeMutablePtr(mod),
1533 .ptr => |ptr| switch (ptr.addr) {
1534 .mut_decl, .comptime_field => true,
1535 .eu_payload, .opt_payload => |base_ptr| Value.fromInterned(base_ptr).isComptimeMutablePtr(mod),
1536 .elem, .field => |base_index| Value.fromInterned(base_index.base).isComptimeMutablePtr(mod),
1537 else => false,
1538 },
1539 else => false,
1540 };
1541 }
1542
1543 pub fn canMutateComptimeVarState(val: Value, mod: *Module) bool {
1544 return val.isComptimeMutablePtr(mod) or switch (val.toIntern()) {
1545 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1546 .error_union => |error_union| switch (error_union.val) {
1547 .err_name => false,
1548 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1549 },
1550 .ptr => |ptr| switch (ptr.addr) {
1551 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(mod),
1552 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).canMutateComptimeVarState(mod),
1553 .elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(mod),
1554 else => false,
1555 },
1556 .opt => |opt| switch (opt.val) {
1557 .none => false,
1558 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1559 },
1560 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| {
1561 if (Value.fromInterned(elem).canMutateComptimeVarState(mod)) break true;
1562 } else false,
1563 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(mod),
1564 else => false,
1565 },
1566 };
1567 }
1568
1569 /// Gets the decl referenced by this pointer. If the pointer does not point
1570 /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),
1571 /// this function returns null.
1572 pub fn pointerDecl(val: Value, mod: *Module) ?InternPool.DeclIndex {
1573 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1574 .variable => |variable| variable.decl,
1575 .extern_func => |extern_func| extern_func.decl,
1576 .func => |func| func.owner_decl,
1577 .ptr => |ptr| switch (ptr.addr) {
1578 .decl => |decl| decl,
1579 .mut_decl => |mut_decl| mut_decl.decl,
1580 else => null,
1581 },
1582 else => null,
1583 };
1584 }
1585
1586 pub const slice_ptr_index = 0;
1587 pub const slice_len_index = 1;
1588
1589 pub fn slicePtr(val: Value, mod: *Module) Value {
1590 return Value.fromInterned(mod.intern_pool.slicePtr(val.toIntern()));
1591 }
1592
1593 pub fn sliceLen(val: Value, mod: *Module) u64 {
1594 const ip = &mod.intern_pool;
1595 return switch (ip.indexToKey(val.toIntern())) {
1596 .ptr => |ptr| switch (ip.indexToKey(switch (ptr.addr) {
1597 .decl => |decl| mod.declPtr(decl).ty.toIntern(),
1598 .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(),
1599 .anon_decl => |anon_decl| ip.typeOf(anon_decl.val),
1600 .comptime_field => |comptime_field| ip.typeOf(comptime_field),
1601 else => unreachable,
1602 })) {
1603 .array_type => |array_type| array_type.len,
1604 else => 1,
1605 },
1606 .slice => |slice| Value.fromInterned(slice.len).toUnsignedInt(mod),
1607 else => unreachable,
1608 };
1609 }
1610
1611 /// Asserts the value is a single-item pointer to an array, or an array,
1612 /// or an unknown-length pointer, and returns the element value at the index.
1613 pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
1614 return (try val.maybeElemValue(mod, index)).?;
1615 }
1616
1617 /// Like `elemValue`, but returns `null` instead of asserting on failure.
1618 pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value {
1619 return switch (val.ip_index) {
1620 .none => switch (val.tag()) {
1621 .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]),
1622 .repeated => val.castTag(.repeated).?.data,
1623 .aggregate => val.castTag(.aggregate).?.data[index],
1624 .slice => val.castTag(.slice).?.data.ptr.maybeElemValue(mod, index),
1625 else => null,
1626 },
1627 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1628 .undef => |ty| Value.fromInterned((try mod.intern(.{
1629 .undef = Type.fromInterned(ty).elemType2(mod).toIntern(),
1630 }))),
1631 .slice => |slice| return Value.fromInterned(slice.ptr).maybeElemValue(mod, index),
1632 .ptr => |ptr| switch (ptr.addr) {
1633 .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index),
1634 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValue(mod, index),
1635 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod))).maybeElemValue(mod, index),
1636 .int, .eu_payload => null,
1637 .opt_payload => |base| Value.fromInterned(base).maybeElemValue(mod, index),
1638 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValue(mod, index),
1639 .elem => |elem| Value.fromInterned(elem.base).maybeElemValue(mod, index + @as(usize, @intCast(elem.index))),
1640 .field => |field| if (Value.fromInterned(field.base).pointerDecl(mod)) |decl_index| {
1641 const base_decl = mod.declPtr(decl_index);
1642 const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index)));
1643 return field_val.maybeElemValue(mod, index);
1644 } else null,
1645 },
1646 .opt => |opt| Value.fromInterned(opt.val).maybeElemValue(mod, index),
1647 .aggregate => |aggregate| {
1648 const len = mod.intern_pool.aggregateTypeLen(aggregate.ty);
1649 if (index < len) return Value.fromInterned(switch (aggregate.storage) {
1650 .bytes => |bytes| try mod.intern(.{ .int = .{
1651 .ty = .u8_type,
1652 .storage = .{ .u64 = bytes[index] },
1653 } }),
1654 .elems => |elems| elems[index],
1655 .repeated_elem => |elem| elem,
1656 });
1657 assert(index == len);
1658 return Value.fromInterned(mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel);
1659 },
1660 else => null,
1661 },
1662 };
1663 }
1664
1665 pub fn isLazyAlign(val: Value, mod: *Module) bool {
1666 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1667 .int => |int| int.storage == .lazy_align,
1668 else => false,
1669 };
1670 }
1671
1672 pub fn isLazySize(val: Value, mod: *Module) bool {
1673 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1674 .int => |int| int.storage == .lazy_size,
1675 else => false,
1676 };
1677 }
1678
1679 pub fn isPtrToThreadLocal(val: Value, mod: *Module) bool {
1680 const backing_decl = mod.intern_pool.getBackingDecl(val.toIntern()).unwrap() orelse return false;
1681 const variable = mod.declPtr(backing_decl).getOwnedVariable(mod) orelse return false;
1682 return variable.is_threadlocal;
1683 }
1684
1685 // Asserts that the provided start/end are in-bounds.
1686 pub fn sliceArray(
1687 val: Value,
1688 mod: *Module,
1689 arena: Allocator,
1690 start: usize,
1691 end: usize,
1692 ) error{OutOfMemory}!Value {
1693 // TODO: write something like getCoercedInts to avoid needing to dupe
1694 return switch (val.ip_index) {
1695 .none => switch (val.tag()) {
1696 .slice => val.castTag(.slice).?.data.ptr.sliceArray(mod, arena, start, end),
1697 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
1698 .repeated => val,
1699 .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),
1700 else => unreachable,
1701 },
1702 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1703 .ptr => |ptr| switch (ptr.addr) {
1704 .decl => |decl| try mod.declPtr(decl).val.sliceArray(mod, arena, start, end),
1705 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod)))
1706 .sliceArray(mod, arena, start, end),
1707 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
1708 .sliceArray(mod, arena, start, end),
1709 .elem => |elem| Value.fromInterned(elem.base)
1710 .sliceArray(mod, arena, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))),
1711 else => unreachable,
1712 },
1713 .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{
1714 .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) {
1715 .array_type => |array_type| try mod.arrayType(.{
1716 .len = @as(u32, @intCast(end - start)),
1717 .child = array_type.child,
1718 .sentinel = if (end == array_type.len) array_type.sentinel else .none,
1719 }),
1720 .vector_type => |vector_type| try mod.vectorType(.{
1721 .len = @as(u32, @intCast(end - start)),
1722 .child = vector_type.child,
1723 }),
1724 else => unreachable,
1725 }.toIntern(),
1726 .storage = switch (aggregate.storage) {
1727 .bytes => .{ .bytes = try arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) },
1728 .elems => .{ .elems = try arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) },
1729 .repeated_elem => |elem| .{ .repeated_elem = elem },
1730 },
1731 } }))),
1732 else => unreachable,
1733 },
1734 };
1735 }
1736
1737 pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value {
1738 return switch (val.ip_index) {
1739 .none => switch (val.tag()) {
1740 .aggregate => {
1741 const field_values = val.castTag(.aggregate).?.data;
1742 return field_values[index];
1743 },
1744 .@"union" => {
1745 const payload = val.castTag(.@"union").?.data;
1746 // TODO assert the tag is correct
1747 return payload.val;
1748 },
1749 else => unreachable,
1750 },
1751 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1752 .undef => |ty| Value.fromInterned((try mod.intern(.{
1753 .undef = Type.fromInterned(ty).structFieldType(index, mod).toIntern(),
1754 }))),
1755 .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) {
1756 .bytes => |bytes| try mod.intern(.{ .int = .{
1757 .ty = .u8_type,
1758 .storage = .{ .u64 = bytes[index] },
1759 } }),
1760 .elems => |elems| elems[index],
1761 .repeated_elem => |elem| elem,
1762 }),
1763 // TODO assert the tag is correct
1764 .un => |un| Value.fromInterned(un.val),
1765 else => unreachable,
1766 },
1767 };
1768 }
1769
1770 pub fn unionTag(val: Value, mod: *Module) ?Value {
1771 if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
1772 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1773 .undef, .enum_tag => val,
1774 .un => |un| if (un.tag != .none) Value.fromInterned(un.tag) else return null,
1775 else => unreachable,
1776 };
1777 }
1778
1779 pub fn unionValue(val: Value, mod: *Module) Value {
1780 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1781 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1782 .un => |un| Value.fromInterned(un.val),
1783 else => unreachable,
1784 };
1785 }
1786
1787 /// Returns a pointer to the element value at the index.
1788 pub fn elemPtr(
1789 val: Value,
1790 elem_ptr_ty: Type,
1791 index: usize,
1792 mod: *Module,
1793 ) Allocator.Error!Value {
1794 const elem_ty = elem_ptr_ty.childType(mod);
1795 const ptr_val = switch (mod.intern_pool.indexToKey(val.toIntern())) {
1796 .slice => |slice| Value.fromInterned(slice.ptr),
1797 else => val,
1798 };
1799 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
1800 .ptr => |ptr| switch (ptr.addr) {
1801 .elem => |elem| if (Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).eql(elem_ty, mod))
1802 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1803 .ty = elem_ptr_ty.toIntern(),
1804 .addr = .{ .elem = .{
1805 .base = elem.base,
1806 .index = elem.index + index,
1807 } },
1808 } }))),
1809 else => {},
1810 },
1811 else => {},
1812 }
1813 var ptr_ty_key = mod.intern_pool.indexToKey(elem_ptr_ty.toIntern()).ptr_type;
1814 assert(ptr_ty_key.flags.size != .Slice);
1815 ptr_ty_key.flags.size = .Many;
1816 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1817 .ty = elem_ptr_ty.toIntern(),
1818 .addr = .{ .elem = .{
1819 .base = (try mod.getCoerced(ptr_val, try mod.ptrType(ptr_ty_key))).toIntern(),
1820 .index = index,
1821 } },
1822 } })));
1823 }
1824
1825 pub fn isUndef(val: Value, mod: *Module) bool {
1826 return val.ip_index != .none and mod.intern_pool.isUndef(val.toIntern());
1827 }
1828
1829 /// TODO: check for cases such as array that is not marked undef but all the element
1830 /// values are marked undef, or struct that is not marked undef but all fields are marked
1831 /// undef, etc.
1832 pub fn isUndefDeep(val: Value, mod: *Module) bool {
1833 return val.isUndef(mod);
1834 }
1835
1836 /// Returns true if any value contained in `self` is undefined.
1837 pub fn anyUndef(val: Value, mod: *Module) !bool {
1838 if (val.ip_index == .none) return false;
1839 return switch (val.toIntern()) {
1840 .undef => true,
1841 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1842 .undef => true,
1843 .simple_value => |v| v == .undefined,
1844 .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| {
1845 if (try (try val.elemValue(mod, idx)).anyUndef(mod)) break true;
1846 } else false,
1847 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
1848 const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
1849 if (try anyUndef(Value.fromInterned(elem), mod)) break true;
1850 } else false,
1851 else => false,
1852 },
1853 };
1854 }
1855
1856 /// Asserts the value is not undefined and not unreachable.
1857 /// C pointers with an integer value of 0 are also considered null.
1858 pub fn isNull(val: Value, mod: *Module) bool {
1859 return switch (val.toIntern()) {
1860 .undef => unreachable,
1861 .unreachable_value => unreachable,
1862 .null_value => true,
1863 else => return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1864 .undef => unreachable,
1865 .ptr => |ptr| switch (ptr.addr) {
1866 .int => {
1867 var buf: BigIntSpace = undefined;
1868 return val.toBigInt(&buf, mod).eqlZero();
1869 },
1870 else => false,
1871 },
1872 .opt => |opt| opt.val == .none,
1873 else => false,
1874 },
1875 };
1876 }
1877
1878 /// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.
1879 pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTerminatedString {
1880 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1881 .err => |err| err.name.toOptional(),
1882 .error_union => |error_union| switch (error_union.val) {
1883 .err_name => |err_name| err_name.toOptional(),
1884 .payload => .none,
1885 },
1886 else => unreachable,
1887 };
1888 }
1889
1890 pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt {
1891 return if (getErrorName(val, mod).unwrap()) |err_name|
1892 @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?))
1893 else
1894 0;
1895 }
1896
1897 /// Assumes the type is an error union. Returns true if and only if the value is
1898 /// the error union payload, not an error.
1899 pub fn errorUnionIsPayload(val: Value, mod: *const Module) bool {
1900 return mod.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;
1901 }
1902
1903 /// Value of the optional, null if optional has no payload.
1904 pub fn optionalValue(val: Value, mod: *const Module) ?Value {
1905 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1906 .opt => |opt| switch (opt.val) {
1907 .none => null,
1908 else => |payload| Value.fromInterned(payload),
1909 },
1910 .ptr => val,
1911 else => unreachable,
1912 };
1913 }
1914
1915 /// Valid for all types. Asserts the value is not undefined.
1916 pub fn isFloat(self: Value, mod: *const Module) bool {
1917 return switch (self.toIntern()) {
1918 .undef => unreachable,
1919 else => switch (mod.intern_pool.indexToKey(self.toIntern())) {
1920 .undef => unreachable,
1921 .float => true,
1922 else => false,
1923 },
1924 };
1925 }
1926
1927 pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module) !Value {
1928 return floatFromIntAdvanced(val, arena, int_ty, float_ty, mod, null) catch |err| switch (err) {
1929 error.OutOfMemory => return error.OutOfMemory,
1930 else => unreachable,
1931 };
1932 }
1933
1934 pub fn floatFromIntAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1935 if (int_ty.zigTypeTag(mod) == .Vector) {
1936 const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(mod));
1937 const scalar_ty = float_ty.scalarType(mod);
1938 for (result_data, 0..) |*scalar, i| {
1939 const elem_val = try val.elemValue(mod, i);
1940 scalar.* = try (try floatFromIntScalar(elem_val, scalar_ty, mod, opt_sema)).intern(scalar_ty, mod);
1941 }
1942 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1943 .ty = float_ty.toIntern(),
1944 .storage = .{ .elems = result_data },
1945 } })));
1946 }
1947 return floatFromIntScalar(val, float_ty, mod, opt_sema);
1948 }
1949
1950 pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1951 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1952 .undef => try mod.undefValue(float_ty),
1953 .int => |int| switch (int.storage) {
1954 .big_int => |big_int| {
1955 const float = bigIntToFloat(big_int.limbs, big_int.positive);
1956 return mod.floatValue(float_ty, float);
1957 },
1958 inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, mod),
1959 .lazy_align => |ty| if (opt_sema) |sema| {
1960 return floatFromIntInner((try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0), float_ty, mod);
1961 } else {
1962 return floatFromIntInner(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0), float_ty, mod);
1963 },
1964 .lazy_size => |ty| if (opt_sema) |sema| {
1965 return floatFromIntInner((try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar, float_ty, mod);
1966 } else {
1967 return floatFromIntInner(Type.fromInterned(ty).abiSize(mod), float_ty, mod);
1968 },
1969 },
1970 else => unreachable,
1971 };
1972 }
1973
1974 fn floatFromIntInner(x: anytype, dest_ty: Type, mod: *Module) !Value {
1975 const target = mod.getTarget();
1976 const storage: InternPool.Key.Float.Storage = switch (dest_ty.floatBits(target)) {
1977 16 => .{ .f16 = @floatFromInt(x) },
1978 32 => .{ .f32 = @floatFromInt(x) },
1979 64 => .{ .f64 = @floatFromInt(x) },
1980 80 => .{ .f80 = @floatFromInt(x) },
1981 128 => .{ .f128 = @floatFromInt(x) },
1982 else => unreachable,
1983 };
1984 return Value.fromInterned((try mod.intern(.{ .float = .{
1985 .ty = dest_ty.toIntern(),
1986 .storage = storage,
1987 } })));
1988 }
1989
1990 fn calcLimbLenFloat(scalar: anytype) usize {
1991 if (scalar == 0) {
1992 return 1;
1993 }
1994
1995 const w_value = @abs(scalar);
1996 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).Int.bits) + 1;
1997 }
1998
1999 pub const OverflowArithmeticResult = struct {
2000 overflow_bit: Value,
2001 wrapped_result: Value,
2002 };
2003
2004 /// Supports (vectors of) integers only; asserts neither operand is undefined.
2005 pub fn intAddSat(
2006 lhs: Value,
2007 rhs: Value,
2008 ty: Type,
2009 arena: Allocator,
2010 mod: *Module,
2011 ) !Value {
2012 if (ty.zigTypeTag(mod) == .Vector) {
2013 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2014 const scalar_ty = ty.scalarType(mod);
2015 for (result_data, 0..) |*scalar, i| {
2016 const lhs_elem = try lhs.elemValue(mod, i);
2017 const rhs_elem = try rhs.elemValue(mod, i);
2018 scalar.* = try (try intAddSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2019 }
2020 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2021 .ty = ty.toIntern(),
2022 .storage = .{ .elems = result_data },
2023 } })));
2024 }
2025 return intAddSatScalar(lhs, rhs, ty, arena, mod);
2026 }
2027
2028 /// Supports integers only; asserts neither operand is undefined.
2029 pub fn intAddSatScalar(
2030 lhs: Value,
2031 rhs: Value,
2032 ty: Type,
2033 arena: Allocator,
2034 mod: *Module,
2035 ) !Value {
2036 assert(!lhs.isUndef(mod));
2037 assert(!rhs.isUndef(mod));
2038
2039 const info = ty.intInfo(mod);
2040
2041 var lhs_space: Value.BigIntSpace = undefined;
2042 var rhs_space: Value.BigIntSpace = undefined;
2043 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2044 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2045 const limbs = try arena.alloc(
2046 std.math.big.Limb,
2047 std.math.big.int.calcTwosCompLimbCount(info.bits),
2048 );
2049 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2050 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2051 return mod.intValue_big(ty, result_bigint.toConst());
2052 }
2053
2054 /// Supports (vectors of) integers only; asserts neither operand is undefined.
2055 pub fn intSubSat(
2056 lhs: Value,
2057 rhs: Value,
2058 ty: Type,
2059 arena: Allocator,
2060 mod: *Module,
2061 ) !Value {
2062 if (ty.zigTypeTag(mod) == .Vector) {
2063 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2064 const scalar_ty = ty.scalarType(mod);
2065 for (result_data, 0..) |*scalar, i| {
2066 const lhs_elem = try lhs.elemValue(mod, i);
2067 const rhs_elem = try rhs.elemValue(mod, i);
2068 scalar.* = try (try intSubSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2069 }
2070 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2071 .ty = ty.toIntern(),
2072 .storage = .{ .elems = result_data },
2073 } })));
2074 }
2075 return intSubSatScalar(lhs, rhs, ty, arena, mod);
2076 }
2077
2078 /// Supports integers only; asserts neither operand is undefined.
2079 pub fn intSubSatScalar(
2080 lhs: Value,
2081 rhs: Value,
2082 ty: Type,
2083 arena: Allocator,
2084 mod: *Module,
2085 ) !Value {
2086 assert(!lhs.isUndef(mod));
2087 assert(!rhs.isUndef(mod));
2088
2089 const info = ty.intInfo(mod);
2090
2091 var lhs_space: Value.BigIntSpace = undefined;
2092 var rhs_space: Value.BigIntSpace = undefined;
2093 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2094 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2095 const limbs = try arena.alloc(
2096 std.math.big.Limb,
2097 std.math.big.int.calcTwosCompLimbCount(info.bits),
2098 );
2099 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2100 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2101 return mod.intValue_big(ty, result_bigint.toConst());
2102 }
2103
2104 pub fn intMulWithOverflow(
2105 lhs: Value,
2106 rhs: Value,
2107 ty: Type,
2108 arena: Allocator,
2109 mod: *Module,
2110 ) !OverflowArithmeticResult {
2111 if (ty.zigTypeTag(mod) == .Vector) {
2112 const vec_len = ty.vectorLen(mod);
2113 const overflowed_data = try arena.alloc(InternPool.Index, vec_len);
2114 const result_data = try arena.alloc(InternPool.Index, vec_len);
2115 const scalar_ty = ty.scalarType(mod);
2116 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2117 const lhs_elem = try lhs.elemValue(mod, i);
2118 const rhs_elem = try rhs.elemValue(mod, i);
2119 const of_math_result = try intMulWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
2120 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2121 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2122 }
2123 return OverflowArithmeticResult{
2124 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2125 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2126 .storage = .{ .elems = overflowed_data },
2127 } }))),
2128 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2129 .ty = ty.toIntern(),
2130 .storage = .{ .elems = result_data },
2131 } }))),
2132 };
2133 }
2134 return intMulWithOverflowScalar(lhs, rhs, ty, arena, mod);
2135 }
2136
2137 pub fn intMulWithOverflowScalar(
2138 lhs: Value,
2139 rhs: Value,
2140 ty: Type,
2141 arena: Allocator,
2142 mod: *Module,
2143 ) !OverflowArithmeticResult {
2144 const info = ty.intInfo(mod);
2145
2146 var lhs_space: Value.BigIntSpace = undefined;
2147 var rhs_space: Value.BigIntSpace = undefined;
2148 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2149 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2150 const limbs = try arena.alloc(
2151 std.math.big.Limb,
2152 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2153 );
2154 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2155 const limbs_buffer = try arena.alloc(
2156 std.math.big.Limb,
2157 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2158 );
2159 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2160
2161 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2162 if (overflowed) {
2163 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2164 }
2165
2166 return OverflowArithmeticResult{
2167 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2168 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2169 };
2170 }
2171
2172 /// Supports both (vectors of) floats and ints; handles undefined scalars.
2173 pub fn numberMulWrap(
2174 lhs: Value,
2175 rhs: Value,
2176 ty: Type,
2177 arena: Allocator,
2178 mod: *Module,
2179 ) !Value {
2180 if (ty.zigTypeTag(mod) == .Vector) {
2181 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2182 const scalar_ty = ty.scalarType(mod);
2183 for (result_data, 0..) |*scalar, i| {
2184 const lhs_elem = try lhs.elemValue(mod, i);
2185 const rhs_elem = try rhs.elemValue(mod, i);
2186 scalar.* = try (try numberMulWrapScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2187 }
2188 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2189 .ty = ty.toIntern(),
2190 .storage = .{ .elems = result_data },
2191 } })));
2192 }
2193 return numberMulWrapScalar(lhs, rhs, ty, arena, mod);
2194 }
2195
2196 /// Supports both floats and ints; handles undefined.
2197 pub fn numberMulWrapScalar(
2198 lhs: Value,
2199 rhs: Value,
2200 ty: Type,
2201 arena: Allocator,
2202 mod: *Module,
2203 ) !Value {
2204 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
2205
2206 if (ty.zigTypeTag(mod) == .ComptimeInt) {
2207 return intMul(lhs, rhs, ty, undefined, arena, mod);
2208 }
2209
2210 if (ty.isAnyFloat()) {
2211 return floatMul(lhs, rhs, ty, arena, mod);
2212 }
2213
2214 const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, mod);
2215 return overflow_result.wrapped_result;
2216 }
2217
2218 /// Supports (vectors of) integers only; asserts neither operand is undefined.
2219 pub fn intMulSat(
2220 lhs: Value,
2221 rhs: Value,
2222 ty: Type,
2223 arena: Allocator,
2224 mod: *Module,
2225 ) !Value {
2226 if (ty.zigTypeTag(mod) == .Vector) {
2227 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2228 const scalar_ty = ty.scalarType(mod);
2229 for (result_data, 0..) |*scalar, i| {
2230 const lhs_elem = try lhs.elemValue(mod, i);
2231 const rhs_elem = try rhs.elemValue(mod, i);
2232 scalar.* = try (try intMulSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2233 }
2234 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2235 .ty = ty.toIntern(),
2236 .storage = .{ .elems = result_data },
2237 } })));
2238 }
2239 return intMulSatScalar(lhs, rhs, ty, arena, mod);
2240 }
2241
2242 /// Supports (vectors of) integers only; asserts neither operand is undefined.
2243 pub fn intMulSatScalar(
2244 lhs: Value,
2245 rhs: Value,
2246 ty: Type,
2247 arena: Allocator,
2248 mod: *Module,
2249 ) !Value {
2250 assert(!lhs.isUndef(mod));
2251 assert(!rhs.isUndef(mod));
2252
2253 const info = ty.intInfo(mod);
2254
2255 var lhs_space: Value.BigIntSpace = undefined;
2256 var rhs_space: Value.BigIntSpace = undefined;
2257 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2258 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2259 const limbs = try arena.alloc(
2260 std.math.big.Limb,
2261 @max(
2262 // For the saturate
2263 std.math.big.int.calcTwosCompLimbCount(info.bits),
2264 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2265 ),
2266 );
2267 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2268 const limbs_buffer = try arena.alloc(
2269 std.math.big.Limb,
2270 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2271 );
2272 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2273 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
2274 return mod.intValue_big(ty, result_bigint.toConst());
2275 }
2276
2277 /// Supports both floats and ints; handles undefined.
2278 pub fn numberMax(lhs: Value, rhs: Value, mod: *Module) Value {
2279 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2280 if (lhs.isNan(mod)) return rhs;
2281 if (rhs.isNan(mod)) return lhs;
2282
2283 return switch (order(lhs, rhs, mod)) {
2284 .lt => rhs,
2285 .gt, .eq => lhs,
2286 };
2287 }
2288
2289 /// Supports both floats and ints; handles undefined.
2290 pub fn numberMin(lhs: Value, rhs: Value, mod: *Module) Value {
2291 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2292 if (lhs.isNan(mod)) return rhs;
2293 if (rhs.isNan(mod)) return lhs;
2294
2295 return switch (order(lhs, rhs, mod)) {
2296 .lt => lhs,
2297 .gt, .eq => rhs,
2298 };
2299 }
2300
2301 /// operands must be (vectors of) integers; handles undefined scalars.
2302 pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2303 if (ty.zigTypeTag(mod) == .Vector) {
2304 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2305 const scalar_ty = ty.scalarType(mod);
2306 for (result_data, 0..) |*scalar, i| {
2307 const elem_val = try val.elemValue(mod, i);
2308 scalar.* = try (try bitwiseNotScalar(elem_val, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2309 }
2310 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2311 .ty = ty.toIntern(),
2312 .storage = .{ .elems = result_data },
2313 } })));
2314 }
2315 return bitwiseNotScalar(val, ty, arena, mod);
2316 }
2317
2318 /// operands must be integers; handles undefined.
2319 pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2320 if (val.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2321 if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());
2322
2323 const info = ty.intInfo(mod);
2324
2325 if (info.bits == 0) {
2326 return val;
2327 }
2328
2329 // TODO is this a performance issue? maybe we should try the operation without
2330 // resorting to BigInt first.
2331 var val_space: Value.BigIntSpace = undefined;
2332 const val_bigint = val.toBigInt(&val_space, mod);
2333 const limbs = try arena.alloc(
2334 std.math.big.Limb,
2335 std.math.big.int.calcTwosCompLimbCount(info.bits),
2336 );
2337
2338 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2339 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2340 return mod.intValue_big(ty, result_bigint.toConst());
2341 }
2342
2343 /// operands must be (vectors of) integers; handles undefined scalars.
2344 pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2345 if (ty.zigTypeTag(mod) == .Vector) {
2346 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2347 const scalar_ty = ty.scalarType(mod);
2348 for (result_data, 0..) |*scalar, i| {
2349 const lhs_elem = try lhs.elemValue(mod, i);
2350 const rhs_elem = try rhs.elemValue(mod, i);
2351 scalar.* = try (try bitwiseAndScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2352 }
2353 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2354 .ty = ty.toIntern(),
2355 .storage = .{ .elems = result_data },
2356 } })));
2357 }
2358 return bitwiseAndScalar(lhs, rhs, ty, allocator, mod);
2359 }
2360
2361 /// operands must be integers; handles undefined.
2362 pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2363 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2364 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
2365
2366 // TODO is this a performance issue? maybe we should try the operation without
2367 // resorting to BigInt first.
2368 var lhs_space: Value.BigIntSpace = undefined;
2369 var rhs_space: Value.BigIntSpace = undefined;
2370 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2371 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2372 const limbs = try arena.alloc(
2373 std.math.big.Limb,
2374 // + 1 for negatives
2375 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2376 );
2377 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2378 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2379 return mod.intValue_big(ty, result_bigint.toConst());
2380 }
2381
2382 /// operands must be (vectors of) integers; handles undefined scalars.
2383 pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2384 if (ty.zigTypeTag(mod) == .Vector) {
2385 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2386 const scalar_ty = ty.scalarType(mod);
2387 for (result_data, 0..) |*scalar, i| {
2388 const lhs_elem = try lhs.elemValue(mod, i);
2389 const rhs_elem = try rhs.elemValue(mod, i);
2390 scalar.* = try (try bitwiseNandScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2391 }
2392 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2393 .ty = ty.toIntern(),
2394 .storage = .{ .elems = result_data },
2395 } })));
2396 }
2397 return bitwiseNandScalar(lhs, rhs, ty, arena, mod);
2398 }
2399
2400 /// operands must be integers; handles undefined.
2401 pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2402 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2403 if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
2404
2405 const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
2406 const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);
2407 return bitwiseXor(anded, all_ones, ty, arena, mod);
2408 }
2409
2410 /// operands must be (vectors of) integers; handles undefined scalars.
2411 pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2412 if (ty.zigTypeTag(mod) == .Vector) {
2413 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2414 const scalar_ty = ty.scalarType(mod);
2415 for (result_data, 0..) |*scalar, i| {
2416 const lhs_elem = try lhs.elemValue(mod, i);
2417 const rhs_elem = try rhs.elemValue(mod, i);
2418 scalar.* = try (try bitwiseOrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2419 }
2420 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2421 .ty = ty.toIntern(),
2422 .storage = .{ .elems = result_data },
2423 } })));
2424 }
2425 return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);
2426 }
2427
2428 /// operands must be integers; handles undefined.
2429 pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2430 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2431 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
2432
2433 // TODO is this a performance issue? maybe we should try the operation without
2434 // resorting to BigInt first.
2435 var lhs_space: Value.BigIntSpace = undefined;
2436 var rhs_space: Value.BigIntSpace = undefined;
2437 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2438 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2439 const limbs = try arena.alloc(
2440 std.math.big.Limb,
2441 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2442 );
2443 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2444 result_bigint.bitOr(lhs_bigint, rhs_bigint);
2445 return mod.intValue_big(ty, result_bigint.toConst());
2446 }
2447
2448 /// operands must be (vectors of) integers; handles undefined scalars.
2449 pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2450 if (ty.zigTypeTag(mod) == .Vector) {
2451 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2452 const scalar_ty = ty.scalarType(mod);
2453 for (result_data, 0..) |*scalar, i| {
2454 const lhs_elem = try lhs.elemValue(mod, i);
2455 const rhs_elem = try rhs.elemValue(mod, i);
2456 scalar.* = try (try bitwiseXorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2457 }
2458 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2459 .ty = ty.toIntern(),
2460 .storage = .{ .elems = result_data },
2461 } })));
2462 }
2463 return bitwiseXorScalar(lhs, rhs, ty, allocator, mod);
2464 }
2465
2466 /// operands must be integers; handles undefined.
2467 pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2468 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2469 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() != rhs.toBool());
2470
2471 // TODO is this a performance issue? maybe we should try the operation without
2472 // resorting to BigInt first.
2473 var lhs_space: Value.BigIntSpace = undefined;
2474 var rhs_space: Value.BigIntSpace = undefined;
2475 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2476 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2477 const limbs = try arena.alloc(
2478 std.math.big.Limb,
2479 // + 1 for negatives
2480 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2481 );
2482 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2483 result_bigint.bitXor(lhs_bigint, rhs_bigint);
2484 return mod.intValue_big(ty, result_bigint.toConst());
2485 }
2486
2487 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2488 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2489 pub fn intDiv(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2490 var overflow: usize = undefined;
2491 return intDivInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2492 error.Overflow => {
2493 const is_vec = ty.isVector(mod);
2494 overflow_idx.* = if (is_vec) overflow else 0;
2495 const safe_ty = if (is_vec) try mod.vectorType(.{
2496 .len = ty.vectorLen(mod),
2497 .child = .comptime_int_type,
2498 }) else Type.comptime_int;
2499 return intDivInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2500 error.Overflow => unreachable,
2501 else => |e| return e,
2502 };
2503 },
2504 else => |e| return e,
2505 };
2506 }
2507
2508 fn intDivInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2509 if (ty.zigTypeTag(mod) == .Vector) {
2510 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2511 const scalar_ty = ty.scalarType(mod);
2512 for (result_data, 0..) |*scalar, i| {
2513 const lhs_elem = try lhs.elemValue(mod, i);
2514 const rhs_elem = try rhs.elemValue(mod, i);
2515 const val = intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2516 error.Overflow => {
2517 overflow_idx.* = i;
2518 return error.Overflow;
2519 },
2520 else => |e| return e,
2521 };
2522 scalar.* = try val.intern(scalar_ty, mod);
2523 }
2524 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2525 .ty = ty.toIntern(),
2526 .storage = .{ .elems = result_data },
2527 } })));
2528 }
2529 return intDivScalar(lhs, rhs, ty, allocator, mod);
2530 }
2531
2532 pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2533 // TODO is this a performance issue? maybe we should try the operation without
2534 // resorting to BigInt first.
2535 var lhs_space: Value.BigIntSpace = undefined;
2536 var rhs_space: Value.BigIntSpace = undefined;
2537 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2538 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2539 const limbs_q = try allocator.alloc(
2540 std.math.big.Limb,
2541 lhs_bigint.limbs.len,
2542 );
2543 const limbs_r = try allocator.alloc(
2544 std.math.big.Limb,
2545 rhs_bigint.limbs.len,
2546 );
2547 const limbs_buffer = try allocator.alloc(
2548 std.math.big.Limb,
2549 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2550 );
2551 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2552 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2553 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2554 if (ty.toIntern() != .comptime_int_type) {
2555 const info = ty.intInfo(mod);
2556 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
2557 return error.Overflow;
2558 }
2559 }
2560 return mod.intValue_big(ty, result_q.toConst());
2561 }
2562
2563 pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2564 if (ty.zigTypeTag(mod) == .Vector) {
2565 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2566 const scalar_ty = ty.scalarType(mod);
2567 for (result_data, 0..) |*scalar, i| {
2568 const lhs_elem = try lhs.elemValue(mod, i);
2569 const rhs_elem = try rhs.elemValue(mod, i);
2570 scalar.* = try (try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2571 }
2572 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2573 .ty = ty.toIntern(),
2574 .storage = .{ .elems = result_data },
2575 } })));
2576 }
2577 return intDivFloorScalar(lhs, rhs, ty, allocator, mod);
2578 }
2579
2580 pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2581 // TODO is this a performance issue? maybe we should try the operation without
2582 // resorting to BigInt first.
2583 var lhs_space: Value.BigIntSpace = undefined;
2584 var rhs_space: Value.BigIntSpace = undefined;
2585 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2586 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2587 const limbs_q = try allocator.alloc(
2588 std.math.big.Limb,
2589 lhs_bigint.limbs.len,
2590 );
2591 const limbs_r = try allocator.alloc(
2592 std.math.big.Limb,
2593 rhs_bigint.limbs.len,
2594 );
2595 const limbs_buffer = try allocator.alloc(
2596 std.math.big.Limb,
2597 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2598 );
2599 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2600 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2601 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2602 return mod.intValue_big(ty, result_q.toConst());
2603 }
2604
2605 pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2606 if (ty.zigTypeTag(mod) == .Vector) {
2607 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2608 const scalar_ty = ty.scalarType(mod);
2609 for (result_data, 0..) |*scalar, i| {
2610 const lhs_elem = try lhs.elemValue(mod, i);
2611 const rhs_elem = try rhs.elemValue(mod, i);
2612 scalar.* = try (try intModScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2613 }
2614 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2615 .ty = ty.toIntern(),
2616 .storage = .{ .elems = result_data },
2617 } })));
2618 }
2619 return intModScalar(lhs, rhs, ty, allocator, mod);
2620 }
2621
2622 pub fn intModScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2623 // TODO is this a performance issue? maybe we should try the operation without
2624 // resorting to BigInt first.
2625 var lhs_space: Value.BigIntSpace = undefined;
2626 var rhs_space: Value.BigIntSpace = undefined;
2627 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2628 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2629 const limbs_q = try allocator.alloc(
2630 std.math.big.Limb,
2631 lhs_bigint.limbs.len,
2632 );
2633 const limbs_r = try allocator.alloc(
2634 std.math.big.Limb,
2635 rhs_bigint.limbs.len,
2636 );
2637 const limbs_buffer = try allocator.alloc(
2638 std.math.big.Limb,
2639 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2640 );
2641 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2642 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2643 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2644 return mod.intValue_big(ty, result_r.toConst());
2645 }
2646
2647 /// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
2648 pub fn isNan(val: Value, mod: *const Module) bool {
2649 if (val.ip_index == .none) return false;
2650 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2651 .float => |float| switch (float.storage) {
2652 inline else => |x| std.math.isNan(x),
2653 },
2654 else => false,
2655 };
2656 }
2657
2658 /// Returns true if the value is a floating point type and is infinite. Returns false otherwise.
2659 pub fn isInf(val: Value, mod: *const Module) bool {
2660 if (val.ip_index == .none) return false;
2661 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2662 .float => |float| switch (float.storage) {
2663 inline else => |x| std.math.isInf(x),
2664 },
2665 else => false,
2666 };
2667 }
2668
2669 pub fn isNegativeInf(val: Value, mod: *const Module) bool {
2670 if (val.ip_index == .none) return false;
2671 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2672 .float => |float| switch (float.storage) {
2673 inline else => |x| std.math.isNegativeInf(x),
2674 },
2675 else => false,
2676 };
2677 }
2678
2679 pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2680 if (float_type.zigTypeTag(mod) == .Vector) {
2681 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2682 const scalar_ty = float_type.scalarType(mod);
2683 for (result_data, 0..) |*scalar, i| {
2684 const lhs_elem = try lhs.elemValue(mod, i);
2685 const rhs_elem = try rhs.elemValue(mod, i);
2686 scalar.* = try (try floatRemScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2687 }
2688 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2689 .ty = float_type.toIntern(),
2690 .storage = .{ .elems = result_data },
2691 } })));
2692 }
2693 return floatRemScalar(lhs, rhs, float_type, mod);
2694 }
2695
2696 pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2697 const target = mod.getTarget();
2698 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2699 16 => .{ .f16 = @rem(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2700 32 => .{ .f32 = @rem(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2701 64 => .{ .f64 = @rem(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2702 80 => .{ .f80 = @rem(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2703 128 => .{ .f128 = @rem(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2704 else => unreachable,
2705 };
2706 return Value.fromInterned((try mod.intern(.{ .float = .{
2707 .ty = float_type.toIntern(),
2708 .storage = storage,
2709 } })));
2710 }
2711
2712 pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2713 if (float_type.zigTypeTag(mod) == .Vector) {
2714 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2715 const scalar_ty = float_type.scalarType(mod);
2716 for (result_data, 0..) |*scalar, i| {
2717 const lhs_elem = try lhs.elemValue(mod, i);
2718 const rhs_elem = try rhs.elemValue(mod, i);
2719 scalar.* = try (try floatModScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2720 }
2721 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2722 .ty = float_type.toIntern(),
2723 .storage = .{ .elems = result_data },
2724 } })));
2725 }
2726 return floatModScalar(lhs, rhs, float_type, mod);
2727 }
2728
2729 pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2730 const target = mod.getTarget();
2731 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2732 16 => .{ .f16 = @mod(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2733 32 => .{ .f32 = @mod(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2734 64 => .{ .f64 = @mod(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2735 80 => .{ .f80 = @mod(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2736 128 => .{ .f128 = @mod(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2737 else => unreachable,
2738 };
2739 return Value.fromInterned((try mod.intern(.{ .float = .{
2740 .ty = float_type.toIntern(),
2741 .storage = storage,
2742 } })));
2743 }
2744
2745 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2746 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2747 pub fn intMul(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2748 var overflow: usize = undefined;
2749 return intMulInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2750 error.Overflow => {
2751 const is_vec = ty.isVector(mod);
2752 overflow_idx.* = if (is_vec) overflow else 0;
2753 const safe_ty = if (is_vec) try mod.vectorType(.{
2754 .len = ty.vectorLen(mod),
2755 .child = .comptime_int_type,
2756 }) else Type.comptime_int;
2757 return intMulInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2758 error.Overflow => unreachable,
2759 else => |e| return e,
2760 };
2761 },
2762 else => |e| return e,
2763 };
2764 }
2765
2766 fn intMulInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2767 if (ty.zigTypeTag(mod) == .Vector) {
2768 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2769 const scalar_ty = ty.scalarType(mod);
2770 for (result_data, 0..) |*scalar, i| {
2771 const lhs_elem = try lhs.elemValue(mod, i);
2772 const rhs_elem = try rhs.elemValue(mod, i);
2773 const val = intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2774 error.Overflow => {
2775 overflow_idx.* = i;
2776 return error.Overflow;
2777 },
2778 else => |e| return e,
2779 };
2780 scalar.* = try val.intern(scalar_ty, mod);
2781 }
2782 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2783 .ty = ty.toIntern(),
2784 .storage = .{ .elems = result_data },
2785 } })));
2786 }
2787 return intMulScalar(lhs, rhs, ty, allocator, mod);
2788 }
2789
2790 pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2791 if (ty.toIntern() != .comptime_int_type) {
2792 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2793 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
2794 return res.wrapped_result;
2795 }
2796 // TODO is this a performance issue? maybe we should try the operation without
2797 // resorting to BigInt first.
2798 var lhs_space: Value.BigIntSpace = undefined;
2799 var rhs_space: Value.BigIntSpace = undefined;
2800 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2801 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2802 const limbs = try allocator.alloc(
2803 std.math.big.Limb,
2804 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2805 );
2806 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2807 const limbs_buffer = try allocator.alloc(
2808 std.math.big.Limb,
2809 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2810 );
2811 defer allocator.free(limbs_buffer);
2812 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
2813 return mod.intValue_big(ty, result_bigint.toConst());
2814 }
2815
2816 pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {
2817 if (ty.zigTypeTag(mod) == .Vector) {
2818 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2819 const scalar_ty = ty.scalarType(mod);
2820 for (result_data, 0..) |*scalar, i| {
2821 const elem_val = try val.elemValue(mod, i);
2822 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, bits, mod)).intern(scalar_ty, mod);
2823 }
2824 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2825 .ty = ty.toIntern(),
2826 .storage = .{ .elems = result_data },
2827 } })));
2828 }
2829 return intTruncScalar(val, ty, allocator, signedness, bits, mod);
2830 }
2831
2832 /// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`.
2833 pub fn intTruncBitsAsValue(
2834 val: Value,
2835 ty: Type,
2836 allocator: Allocator,
2837 signedness: std.builtin.Signedness,
2838 bits: Value,
2839 mod: *Module,
2840 ) !Value {
2841 if (ty.zigTypeTag(mod) == .Vector) {
2842 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2843 const scalar_ty = ty.scalarType(mod);
2844 for (result_data, 0..) |*scalar, i| {
2845 const elem_val = try val.elemValue(mod, i);
2846 const bits_elem = try bits.elemValue(mod, i);
2847 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @as(u16, @intCast(bits_elem.toUnsignedInt(mod))), mod)).intern(scalar_ty, mod);
2848 }
2849 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2850 .ty = ty.toIntern(),
2851 .storage = .{ .elems = result_data },
2852 } })));
2853 }
2854 return intTruncScalar(val, ty, allocator, signedness, @as(u16, @intCast(bits.toUnsignedInt(mod))), mod);
2855 }
2856
2857 pub fn intTruncScalar(
2858 val: Value,
2859 ty: Type,
2860 allocator: Allocator,
2861 signedness: std.builtin.Signedness,
2862 bits: u16,
2863 mod: *Module,
2864 ) !Value {
2865 if (bits == 0) return mod.intValue(ty, 0);
2866
2867 var val_space: Value.BigIntSpace = undefined;
2868 const val_bigint = val.toBigInt(&val_space, mod);
2869
2870 const limbs = try allocator.alloc(
2871 std.math.big.Limb,
2872 std.math.big.int.calcTwosCompLimbCount(bits),
2873 );
2874 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2875
2876 result_bigint.truncate(val_bigint, signedness, bits);
2877 return mod.intValue_big(ty, result_bigint.toConst());
2878 }
2879
2880 pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2881 if (ty.zigTypeTag(mod) == .Vector) {
2882 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2883 const scalar_ty = ty.scalarType(mod);
2884 for (result_data, 0..) |*scalar, i| {
2885 const lhs_elem = try lhs.elemValue(mod, i);
2886 const rhs_elem = try rhs.elemValue(mod, i);
2887 scalar.* = try (try shlScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2888 }
2889 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2890 .ty = ty.toIntern(),
2891 .storage = .{ .elems = result_data },
2892 } })));
2893 }
2894 return shlScalar(lhs, rhs, ty, allocator, mod);
2895 }
2896
2897 pub fn shlScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2898 // TODO is this a performance issue? maybe we should try the operation without
2899 // resorting to BigInt first.
2900 var lhs_space: Value.BigIntSpace = undefined;
2901 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2902 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2903 const limbs = try allocator.alloc(
2904 std.math.big.Limb,
2905 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2906 );
2907 var result_bigint = BigIntMutable{
2908 .limbs = limbs,
2909 .positive = undefined,
2910 .len = undefined,
2911 };
2912 result_bigint.shiftLeft(lhs_bigint, shift);
2913 if (ty.toIntern() != .comptime_int_type) {
2914 const int_info = ty.intInfo(mod);
2915 result_bigint.truncate(result_bigint.toConst(), int_info.signedness, int_info.bits);
2916 }
2917
2918 return mod.intValue_big(ty, result_bigint.toConst());
2919 }
2920
2921 pub fn shlWithOverflow(
2922 lhs: Value,
2923 rhs: Value,
2924 ty: Type,
2925 allocator: Allocator,
2926 mod: *Module,
2927 ) !OverflowArithmeticResult {
2928 if (ty.zigTypeTag(mod) == .Vector) {
2929 const vec_len = ty.vectorLen(mod);
2930 const overflowed_data = try allocator.alloc(InternPool.Index, vec_len);
2931 const result_data = try allocator.alloc(InternPool.Index, vec_len);
2932 const scalar_ty = ty.scalarType(mod);
2933 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2934 const lhs_elem = try lhs.elemValue(mod, i);
2935 const rhs_elem = try rhs.elemValue(mod, i);
2936 const of_math_result = try shlWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
2937 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2938 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2939 }
2940 return OverflowArithmeticResult{
2941 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2942 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2943 .storage = .{ .elems = overflowed_data },
2944 } }))),
2945 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2946 .ty = ty.toIntern(),
2947 .storage = .{ .elems = result_data },
2948 } }))),
2949 };
2950 }
2951 return shlWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2952 }
2953
2954 pub fn shlWithOverflowScalar(
2955 lhs: Value,
2956 rhs: Value,
2957 ty: Type,
2958 allocator: Allocator,
2959 mod: *Module,
2960 ) !OverflowArithmeticResult {
2961 const info = ty.intInfo(mod);
2962 var lhs_space: Value.BigIntSpace = undefined;
2963 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2964 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2965 const limbs = try allocator.alloc(
2966 std.math.big.Limb,
2967 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2968 );
2969 var result_bigint = BigIntMutable{
2970 .limbs = limbs,
2971 .positive = undefined,
2972 .len = undefined,
2973 };
2974 result_bigint.shiftLeft(lhs_bigint, shift);
2975 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2976 if (overflowed) {
2977 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2978 }
2979 return OverflowArithmeticResult{
2980 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2981 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2982 };
2983 }
2984
2985 pub fn shlSat(
2986 lhs: Value,
2987 rhs: Value,
2988 ty: Type,
2989 arena: Allocator,
2990 mod: *Module,
2991 ) !Value {
2992 if (ty.zigTypeTag(mod) == .Vector) {
2993 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2994 const scalar_ty = ty.scalarType(mod);
2995 for (result_data, 0..) |*scalar, i| {
2996 const lhs_elem = try lhs.elemValue(mod, i);
2997 const rhs_elem = try rhs.elemValue(mod, i);
2998 scalar.* = try (try shlSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2999 }
3000 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3001 .ty = ty.toIntern(),
3002 .storage = .{ .elems = result_data },
3003 } })));
3004 }
3005 return shlSatScalar(lhs, rhs, ty, arena, mod);
3006 }
3007
3008 pub fn shlSatScalar(
3009 lhs: Value,
3010 rhs: Value,
3011 ty: Type,
3012 arena: Allocator,
3013 mod: *Module,
3014 ) !Value {
3015 // TODO is this a performance issue? maybe we should try the operation without
3016 // resorting to BigInt first.
3017 const info = ty.intInfo(mod);
3018
3019 var lhs_space: Value.BigIntSpace = undefined;
3020 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3021 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3022 const limbs = try arena.alloc(
3023 std.math.big.Limb,
3024 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,
3025 );
3026 var result_bigint = BigIntMutable{
3027 .limbs = limbs,
3028 .positive = undefined,
3029 .len = undefined,
3030 };
3031 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
3032 return mod.intValue_big(ty, result_bigint.toConst());
3033 }
3034
3035 pub fn shlTrunc(
3036 lhs: Value,
3037 rhs: Value,
3038 ty: Type,
3039 arena: Allocator,
3040 mod: *Module,
3041 ) !Value {
3042 if (ty.zigTypeTag(mod) == .Vector) {
3043 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3044 const scalar_ty = ty.scalarType(mod);
3045 for (result_data, 0..) |*scalar, i| {
3046 const lhs_elem = try lhs.elemValue(mod, i);
3047 const rhs_elem = try rhs.elemValue(mod, i);
3048 scalar.* = try (try shlTruncScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
3049 }
3050 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3051 .ty = ty.toIntern(),
3052 .storage = .{ .elems = result_data },
3053 } })));
3054 }
3055 return shlTruncScalar(lhs, rhs, ty, arena, mod);
3056 }
3057
3058 pub fn shlTruncScalar(
3059 lhs: Value,
3060 rhs: Value,
3061 ty: Type,
3062 arena: Allocator,
3063 mod: *Module,
3064 ) !Value {
3065 const shifted = try lhs.shl(rhs, ty, arena, mod);
3066 const int_info = ty.intInfo(mod);
3067 const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits, mod);
3068 return truncated;
3069 }
3070
3071 pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3072 if (ty.zigTypeTag(mod) == .Vector) {
3073 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
3074 const scalar_ty = ty.scalarType(mod);
3075 for (result_data, 0..) |*scalar, i| {
3076 const lhs_elem = try lhs.elemValue(mod, i);
3077 const rhs_elem = try rhs.elemValue(mod, i);
3078 scalar.* = try (try shrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
3079 }
3080 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3081 .ty = ty.toIntern(),
3082 .storage = .{ .elems = result_data },
3083 } })));
3084 }
3085 return shrScalar(lhs, rhs, ty, allocator, mod);
3086 }
3087
3088 pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3089 // TODO is this a performance issue? maybe we should try the operation without
3090 // resorting to BigInt first.
3091 var lhs_space: Value.BigIntSpace = undefined;
3092 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3093 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3094
3095 const result_limbs = lhs_bigint.limbs.len -| (shift / (@sizeOf(std.math.big.Limb) * 8));
3096 if (result_limbs == 0) {
3097 // The shift is enough to remove all the bits from the number, which means the
3098 // result is 0 or -1 depending on the sign.
3099 if (lhs_bigint.positive) {
3100 return mod.intValue(ty, 0);
3101 } else {
3102 return mod.intValue(ty, -1);
3103 }
3104 }
3105
3106 const limbs = try allocator.alloc(
3107 std.math.big.Limb,
3108 result_limbs,
3109 );
3110 var result_bigint = BigIntMutable{
3111 .limbs = limbs,
3112 .positive = undefined,
3113 .len = undefined,
3114 };
3115 result_bigint.shiftRight(lhs_bigint, shift);
3116 return mod.intValue_big(ty, result_bigint.toConst());
3117 }
3118
3119 pub fn floatNeg(
3120 val: Value,
3121 float_type: Type,
3122 arena: Allocator,
3123 mod: *Module,
3124 ) !Value {
3125 if (float_type.zigTypeTag(mod) == .Vector) {
3126 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3127 const scalar_ty = float_type.scalarType(mod);
3128 for (result_data, 0..) |*scalar, i| {
3129 const elem_val = try val.elemValue(mod, i);
3130 scalar.* = try (try floatNegScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3131 }
3132 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3133 .ty = float_type.toIntern(),
3134 .storage = .{ .elems = result_data },
3135 } })));
3136 }
3137 return floatNegScalar(val, float_type, mod);
3138 }
3139
3140 pub fn floatNegScalar(
3141 val: Value,
3142 float_type: Type,
3143 mod: *Module,
3144 ) !Value {
3145 const target = mod.getTarget();
3146 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3147 16 => .{ .f16 = -val.toFloat(f16, mod) },
3148 32 => .{ .f32 = -val.toFloat(f32, mod) },
3149 64 => .{ .f64 = -val.toFloat(f64, mod) },
3150 80 => .{ .f80 = -val.toFloat(f80, mod) },
3151 128 => .{ .f128 = -val.toFloat(f128, mod) },
3152 else => unreachable,
3153 };
3154 return Value.fromInterned((try mod.intern(.{ .float = .{
3155 .ty = float_type.toIntern(),
3156 .storage = storage,
3157 } })));
3158 }
3159
3160 pub fn floatAdd(
3161 lhs: Value,
3162 rhs: Value,
3163 float_type: Type,
3164 arena: Allocator,
3165 mod: *Module,
3166 ) !Value {
3167 if (float_type.zigTypeTag(mod) == .Vector) {
3168 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3169 const scalar_ty = float_type.scalarType(mod);
3170 for (result_data, 0..) |*scalar, i| {
3171 const lhs_elem = try lhs.elemValue(mod, i);
3172 const rhs_elem = try rhs.elemValue(mod, i);
3173 scalar.* = try (try floatAddScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3174 }
3175 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3176 .ty = float_type.toIntern(),
3177 .storage = .{ .elems = result_data },
3178 } })));
3179 }
3180 return floatAddScalar(lhs, rhs, float_type, mod);
3181 }
3182
3183 pub fn floatAddScalar(
3184 lhs: Value,
3185 rhs: Value,
3186 float_type: Type,
3187 mod: *Module,
3188 ) !Value {
3189 const target = mod.getTarget();
3190 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3191 16 => .{ .f16 = lhs.toFloat(f16, mod) + rhs.toFloat(f16, mod) },
3192 32 => .{ .f32 = lhs.toFloat(f32, mod) + rhs.toFloat(f32, mod) },
3193 64 => .{ .f64 = lhs.toFloat(f64, mod) + rhs.toFloat(f64, mod) },
3194 80 => .{ .f80 = lhs.toFloat(f80, mod) + rhs.toFloat(f80, mod) },
3195 128 => .{ .f128 = lhs.toFloat(f128, mod) + rhs.toFloat(f128, mod) },
3196 else => unreachable,
3197 };
3198 return Value.fromInterned((try mod.intern(.{ .float = .{
3199 .ty = float_type.toIntern(),
3200 .storage = storage,
3201 } })));
3202 }
3203
3204 pub fn floatSub(
3205 lhs: Value,
3206 rhs: Value,
3207 float_type: Type,
3208 arena: Allocator,
3209 mod: *Module,
3210 ) !Value {
3211 if (float_type.zigTypeTag(mod) == .Vector) {
3212 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3213 const scalar_ty = float_type.scalarType(mod);
3214 for (result_data, 0..) |*scalar, i| {
3215 const lhs_elem = try lhs.elemValue(mod, i);
3216 const rhs_elem = try rhs.elemValue(mod, i);
3217 scalar.* = try (try floatSubScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3218 }
3219 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3220 .ty = float_type.toIntern(),
3221 .storage = .{ .elems = result_data },
3222 } })));
3223 }
3224 return floatSubScalar(lhs, rhs, float_type, mod);
3225 }
3226
3227 pub fn floatSubScalar(
3228 lhs: Value,
3229 rhs: Value,
3230 float_type: Type,
3231 mod: *Module,
3232 ) !Value {
3233 const target = mod.getTarget();
3234 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3235 16 => .{ .f16 = lhs.toFloat(f16, mod) - rhs.toFloat(f16, mod) },
3236 32 => .{ .f32 = lhs.toFloat(f32, mod) - rhs.toFloat(f32, mod) },
3237 64 => .{ .f64 = lhs.toFloat(f64, mod) - rhs.toFloat(f64, mod) },
3238 80 => .{ .f80 = lhs.toFloat(f80, mod) - rhs.toFloat(f80, mod) },
3239 128 => .{ .f128 = lhs.toFloat(f128, mod) - rhs.toFloat(f128, mod) },
3240 else => unreachable,
3241 };
3242 return Value.fromInterned((try mod.intern(.{ .float = .{
3243 .ty = float_type.toIntern(),
3244 .storage = storage,
3245 } })));
3246 }
3247
3248 pub fn floatDiv(
3249 lhs: Value,
3250 rhs: Value,
3251 float_type: Type,
3252 arena: Allocator,
3253 mod: *Module,
3254 ) !Value {
3255 if (float_type.zigTypeTag(mod) == .Vector) {
3256 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3257 const scalar_ty = float_type.scalarType(mod);
3258 for (result_data, 0..) |*scalar, i| {
3259 const lhs_elem = try lhs.elemValue(mod, i);
3260 const rhs_elem = try rhs.elemValue(mod, i);
3261 scalar.* = try (try floatDivScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3262 }
3263 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3264 .ty = float_type.toIntern(),
3265 .storage = .{ .elems = result_data },
3266 } })));
3267 }
3268 return floatDivScalar(lhs, rhs, float_type, mod);
3269 }
3270
3271 pub fn floatDivScalar(
3272 lhs: Value,
3273 rhs: Value,
3274 float_type: Type,
3275 mod: *Module,
3276 ) !Value {
3277 const target = mod.getTarget();
3278 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3279 16 => .{ .f16 = lhs.toFloat(f16, mod) / rhs.toFloat(f16, mod) },
3280 32 => .{ .f32 = lhs.toFloat(f32, mod) / rhs.toFloat(f32, mod) },
3281 64 => .{ .f64 = lhs.toFloat(f64, mod) / rhs.toFloat(f64, mod) },
3282 80 => .{ .f80 = lhs.toFloat(f80, mod) / rhs.toFloat(f80, mod) },
3283 128 => .{ .f128 = lhs.toFloat(f128, mod) / rhs.toFloat(f128, mod) },
3284 else => unreachable,
3285 };
3286 return Value.fromInterned((try mod.intern(.{ .float = .{
3287 .ty = float_type.toIntern(),
3288 .storage = storage,
3289 } })));
3290 }
3291
3292 pub fn floatDivFloor(
3293 lhs: Value,
3294 rhs: Value,
3295 float_type: Type,
3296 arena: Allocator,
3297 mod: *Module,
3298 ) !Value {
3299 if (float_type.zigTypeTag(mod) == .Vector) {
3300 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3301 const scalar_ty = float_type.scalarType(mod);
3302 for (result_data, 0..) |*scalar, i| {
3303 const lhs_elem = try lhs.elemValue(mod, i);
3304 const rhs_elem = try rhs.elemValue(mod, i);
3305 scalar.* = try (try floatDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3306 }
3307 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3308 .ty = float_type.toIntern(),
3309 .storage = .{ .elems = result_data },
3310 } })));
3311 }
3312 return floatDivFloorScalar(lhs, rhs, float_type, mod);
3313 }
3314
3315 pub fn floatDivFloorScalar(
3316 lhs: Value,
3317 rhs: Value,
3318 float_type: Type,
3319 mod: *Module,
3320 ) !Value {
3321 const target = mod.getTarget();
3322 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3323 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3324 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3325 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3326 80 => .{ .f80 = @divFloor(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3327 128 => .{ .f128 = @divFloor(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3328 else => unreachable,
3329 };
3330 return Value.fromInterned((try mod.intern(.{ .float = .{
3331 .ty = float_type.toIntern(),
3332 .storage = storage,
3333 } })));
3334 }
3335
3336 pub fn floatDivTrunc(
3337 lhs: Value,
3338 rhs: Value,
3339 float_type: Type,
3340 arena: Allocator,
3341 mod: *Module,
3342 ) !Value {
3343 if (float_type.zigTypeTag(mod) == .Vector) {
3344 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3345 const scalar_ty = float_type.scalarType(mod);
3346 for (result_data, 0..) |*scalar, i| {
3347 const lhs_elem = try lhs.elemValue(mod, i);
3348 const rhs_elem = try rhs.elemValue(mod, i);
3349 scalar.* = try (try floatDivTruncScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3350 }
3351 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3352 .ty = float_type.toIntern(),
3353 .storage = .{ .elems = result_data },
3354 } })));
3355 }
3356 return floatDivTruncScalar(lhs, rhs, float_type, mod);
3357 }
3358
3359 pub fn floatDivTruncScalar(
3360 lhs: Value,
3361 rhs: Value,
3362 float_type: Type,
3363 mod: *Module,
3364 ) !Value {
3365 const target = mod.getTarget();
3366 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3367 16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3368 32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3369 64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3370 80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3371 128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3372 else => unreachable,
3373 };
3374 return Value.fromInterned((try mod.intern(.{ .float = .{
3375 .ty = float_type.toIntern(),
3376 .storage = storage,
3377 } })));
3378 }
3379
3380 pub fn floatMul(
3381 lhs: Value,
3382 rhs: Value,
3383 float_type: Type,
3384 arena: Allocator,
3385 mod: *Module,
3386 ) !Value {
3387 if (float_type.zigTypeTag(mod) == .Vector) {
3388 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3389 const scalar_ty = float_type.scalarType(mod);
3390 for (result_data, 0..) |*scalar, i| {
3391 const lhs_elem = try lhs.elemValue(mod, i);
3392 const rhs_elem = try rhs.elemValue(mod, i);
3393 scalar.* = try (try floatMulScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3394 }
3395 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3396 .ty = float_type.toIntern(),
3397 .storage = .{ .elems = result_data },
3398 } })));
3399 }
3400 return floatMulScalar(lhs, rhs, float_type, mod);
3401 }
3402
3403 pub fn floatMulScalar(
3404 lhs: Value,
3405 rhs: Value,
3406 float_type: Type,
3407 mod: *Module,
3408 ) !Value {
3409 const target = mod.getTarget();
3410 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3411 16 => .{ .f16 = lhs.toFloat(f16, mod) * rhs.toFloat(f16, mod) },
3412 32 => .{ .f32 = lhs.toFloat(f32, mod) * rhs.toFloat(f32, mod) },
3413 64 => .{ .f64 = lhs.toFloat(f64, mod) * rhs.toFloat(f64, mod) },
3414 80 => .{ .f80 = lhs.toFloat(f80, mod) * rhs.toFloat(f80, mod) },
3415 128 => .{ .f128 = lhs.toFloat(f128, mod) * rhs.toFloat(f128, mod) },
3416 else => unreachable,
3417 };
3418 return Value.fromInterned((try mod.intern(.{ .float = .{
3419 .ty = float_type.toIntern(),
3420 .storage = storage,
3421 } })));
3422 }
3423
3424 pub fn sqrt(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3425 if (float_type.zigTypeTag(mod) == .Vector) {
3426 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3427 const scalar_ty = float_type.scalarType(mod);
3428 for (result_data, 0..) |*scalar, i| {
3429 const elem_val = try val.elemValue(mod, i);
3430 scalar.* = try (try sqrtScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3431 }
3432 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3433 .ty = float_type.toIntern(),
3434 .storage = .{ .elems = result_data },
3435 } })));
3436 }
3437 return sqrtScalar(val, float_type, mod);
3438 }
3439
3440 pub fn sqrtScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3441 const target = mod.getTarget();
3442 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3443 16 => .{ .f16 = @sqrt(val.toFloat(f16, mod)) },
3444 32 => .{ .f32 = @sqrt(val.toFloat(f32, mod)) },
3445 64 => .{ .f64 = @sqrt(val.toFloat(f64, mod)) },
3446 80 => .{ .f80 = @sqrt(val.toFloat(f80, mod)) },
3447 128 => .{ .f128 = @sqrt(val.toFloat(f128, mod)) },
3448 else => unreachable,
3449 };
3450 return Value.fromInterned((try mod.intern(.{ .float = .{
3451 .ty = float_type.toIntern(),
3452 .storage = storage,
3453 } })));
3454 }
3455
3456 pub fn sin(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3457 if (float_type.zigTypeTag(mod) == .Vector) {
3458 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3459 const scalar_ty = float_type.scalarType(mod);
3460 for (result_data, 0..) |*scalar, i| {
3461 const elem_val = try val.elemValue(mod, i);
3462 scalar.* = try (try sinScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3463 }
3464 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3465 .ty = float_type.toIntern(),
3466 .storage = .{ .elems = result_data },
3467 } })));
3468 }
3469 return sinScalar(val, float_type, mod);
3470 }
3471
3472 pub fn sinScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3473 const target = mod.getTarget();
3474 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3475 16 => .{ .f16 = @sin(val.toFloat(f16, mod)) },
3476 32 => .{ .f32 = @sin(val.toFloat(f32, mod)) },
3477 64 => .{ .f64 = @sin(val.toFloat(f64, mod)) },
3478 80 => .{ .f80 = @sin(val.toFloat(f80, mod)) },
3479 128 => .{ .f128 = @sin(val.toFloat(f128, mod)) },
3480 else => unreachable,
3481 };
3482 return Value.fromInterned((try mod.intern(.{ .float = .{
3483 .ty = float_type.toIntern(),
3484 .storage = storage,
3485 } })));
3486 }
3487
3488 pub fn cos(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3489 if (float_type.zigTypeTag(mod) == .Vector) {
3490 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3491 const scalar_ty = float_type.scalarType(mod);
3492 for (result_data, 0..) |*scalar, i| {
3493 const elem_val = try val.elemValue(mod, i);
3494 scalar.* = try (try cosScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3495 }
3496 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3497 .ty = float_type.toIntern(),
3498 .storage = .{ .elems = result_data },
3499 } })));
3500 }
3501 return cosScalar(val, float_type, mod);
3502 }
3503
3504 pub fn cosScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3505 const target = mod.getTarget();
3506 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3507 16 => .{ .f16 = @cos(val.toFloat(f16, mod)) },
3508 32 => .{ .f32 = @cos(val.toFloat(f32, mod)) },
3509 64 => .{ .f64 = @cos(val.toFloat(f64, mod)) },
3510 80 => .{ .f80 = @cos(val.toFloat(f80, mod)) },
3511 128 => .{ .f128 = @cos(val.toFloat(f128, mod)) },
3512 else => unreachable,
3513 };
3514 return Value.fromInterned((try mod.intern(.{ .float = .{
3515 .ty = float_type.toIntern(),
3516 .storage = storage,
3517 } })));
3518 }
3519
3520 pub fn tan(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3521 if (float_type.zigTypeTag(mod) == .Vector) {
3522 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3523 const scalar_ty = float_type.scalarType(mod);
3524 for (result_data, 0..) |*scalar, i| {
3525 const elem_val = try val.elemValue(mod, i);
3526 scalar.* = try (try tanScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3527 }
3528 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3529 .ty = float_type.toIntern(),
3530 .storage = .{ .elems = result_data },
3531 } })));
3532 }
3533 return tanScalar(val, float_type, mod);
3534 }
3535
3536 pub fn tanScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3537 const target = mod.getTarget();
3538 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3539 16 => .{ .f16 = @tan(val.toFloat(f16, mod)) },
3540 32 => .{ .f32 = @tan(val.toFloat(f32, mod)) },
3541 64 => .{ .f64 = @tan(val.toFloat(f64, mod)) },
3542 80 => .{ .f80 = @tan(val.toFloat(f80, mod)) },
3543 128 => .{ .f128 = @tan(val.toFloat(f128, mod)) },
3544 else => unreachable,
3545 };
3546 return Value.fromInterned((try mod.intern(.{ .float = .{
3547 .ty = float_type.toIntern(),
3548 .storage = storage,
3549 } })));
3550 }
3551
3552 pub fn exp(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3553 if (float_type.zigTypeTag(mod) == .Vector) {
3554 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3555 const scalar_ty = float_type.scalarType(mod);
3556 for (result_data, 0..) |*scalar, i| {
3557 const elem_val = try val.elemValue(mod, i);
3558 scalar.* = try (try expScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3559 }
3560 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3561 .ty = float_type.toIntern(),
3562 .storage = .{ .elems = result_data },
3563 } })));
3564 }
3565 return expScalar(val, float_type, mod);
3566 }
3567
3568 pub fn expScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3569 const target = mod.getTarget();
3570 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3571 16 => .{ .f16 = @exp(val.toFloat(f16, mod)) },
3572 32 => .{ .f32 = @exp(val.toFloat(f32, mod)) },
3573 64 => .{ .f64 = @exp(val.toFloat(f64, mod)) },
3574 80 => .{ .f80 = @exp(val.toFloat(f80, mod)) },
3575 128 => .{ .f128 = @exp(val.toFloat(f128, mod)) },
3576 else => unreachable,
3577 };
3578 return Value.fromInterned((try mod.intern(.{ .float = .{
3579 .ty = float_type.toIntern(),
3580 .storage = storage,
3581 } })));
3582 }
3583
3584 pub fn exp2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3585 if (float_type.zigTypeTag(mod) == .Vector) {
3586 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3587 const scalar_ty = float_type.scalarType(mod);
3588 for (result_data, 0..) |*scalar, i| {
3589 const elem_val = try val.elemValue(mod, i);
3590 scalar.* = try (try exp2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3591 }
3592 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3593 .ty = float_type.toIntern(),
3594 .storage = .{ .elems = result_data },
3595 } })));
3596 }
3597 return exp2Scalar(val, float_type, mod);
3598 }
3599
3600 pub fn exp2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3601 const target = mod.getTarget();
3602 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3603 16 => .{ .f16 = @exp2(val.toFloat(f16, mod)) },
3604 32 => .{ .f32 = @exp2(val.toFloat(f32, mod)) },
3605 64 => .{ .f64 = @exp2(val.toFloat(f64, mod)) },
3606 80 => .{ .f80 = @exp2(val.toFloat(f80, mod)) },
3607 128 => .{ .f128 = @exp2(val.toFloat(f128, mod)) },
3608 else => unreachable,
3609 };
3610 return Value.fromInterned((try mod.intern(.{ .float = .{
3611 .ty = float_type.toIntern(),
3612 .storage = storage,
3613 } })));
3614 }
3615
3616 pub fn log(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3617 if (float_type.zigTypeTag(mod) == .Vector) {
3618 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3619 const scalar_ty = float_type.scalarType(mod);
3620 for (result_data, 0..) |*scalar, i| {
3621 const elem_val = try val.elemValue(mod, i);
3622 scalar.* = try (try logScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3623 }
3624 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3625 .ty = float_type.toIntern(),
3626 .storage = .{ .elems = result_data },
3627 } })));
3628 }
3629 return logScalar(val, float_type, mod);
3630 }
3631
3632 pub fn logScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3633 const target = mod.getTarget();
3634 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3635 16 => .{ .f16 = @log(val.toFloat(f16, mod)) },
3636 32 => .{ .f32 = @log(val.toFloat(f32, mod)) },
3637 64 => .{ .f64 = @log(val.toFloat(f64, mod)) },
3638 80 => .{ .f80 = @log(val.toFloat(f80, mod)) },
3639 128 => .{ .f128 = @log(val.toFloat(f128, mod)) },
3640 else => unreachable,
3641 };
3642 return Value.fromInterned((try mod.intern(.{ .float = .{
3643 .ty = float_type.toIntern(),
3644 .storage = storage,
3645 } })));
3646 }
3647
3648 pub fn log2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3649 if (float_type.zigTypeTag(mod) == .Vector) {
3650 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3651 const scalar_ty = float_type.scalarType(mod);
3652 for (result_data, 0..) |*scalar, i| {
3653 const elem_val = try val.elemValue(mod, i);
3654 scalar.* = try (try log2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3655 }
3656 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3657 .ty = float_type.toIntern(),
3658 .storage = .{ .elems = result_data },
3659 } })));
3660 }
3661 return log2Scalar(val, float_type, mod);
3662 }
3663
3664 pub fn log2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3665 const target = mod.getTarget();
3666 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3667 16 => .{ .f16 = @log2(val.toFloat(f16, mod)) },
3668 32 => .{ .f32 = @log2(val.toFloat(f32, mod)) },
3669 64 => .{ .f64 = @log2(val.toFloat(f64, mod)) },
3670 80 => .{ .f80 = @log2(val.toFloat(f80, mod)) },
3671 128 => .{ .f128 = @log2(val.toFloat(f128, mod)) },
3672 else => unreachable,
3673 };
3674 return Value.fromInterned((try mod.intern(.{ .float = .{
3675 .ty = float_type.toIntern(),
3676 .storage = storage,
3677 } })));
3678 }
3679
3680 pub fn log10(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3681 if (float_type.zigTypeTag(mod) == .Vector) {
3682 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3683 const scalar_ty = float_type.scalarType(mod);
3684 for (result_data, 0..) |*scalar, i| {
3685 const elem_val = try val.elemValue(mod, i);
3686 scalar.* = try (try log10Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3687 }
3688 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3689 .ty = float_type.toIntern(),
3690 .storage = .{ .elems = result_data },
3691 } })));
3692 }
3693 return log10Scalar(val, float_type, mod);
3694 }
3695
3696 pub fn log10Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3697 const target = mod.getTarget();
3698 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3699 16 => .{ .f16 = @log10(val.toFloat(f16, mod)) },
3700 32 => .{ .f32 = @log10(val.toFloat(f32, mod)) },
3701 64 => .{ .f64 = @log10(val.toFloat(f64, mod)) },
3702 80 => .{ .f80 = @log10(val.toFloat(f80, mod)) },
3703 128 => .{ .f128 = @log10(val.toFloat(f128, mod)) },
3704 else => unreachable,
3705 };
3706 return Value.fromInterned((try mod.intern(.{ .float = .{
3707 .ty = float_type.toIntern(),
3708 .storage = storage,
3709 } })));
3710 }
3711
3712 pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
3713 if (ty.zigTypeTag(mod) == .Vector) {
3714 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3715 const scalar_ty = ty.scalarType(mod);
3716 for (result_data, 0..) |*scalar, i| {
3717 const elem_val = try val.elemValue(mod, i);
3718 scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod);
3719 }
3720 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3721 .ty = ty.toIntern(),
3722 .storage = .{ .elems = result_data },
3723 } })));
3724 }
3725 return absScalar(val, ty, mod, arena);
3726 }
3727
3728 pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value {
3729 switch (ty.zigTypeTag(mod)) {
3730 .Int => {
3731 var buffer: Value.BigIntSpace = undefined;
3732 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3733 operand_bigint.abs();
3734
3735 return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst());
3736 },
3737 .ComptimeInt => {
3738 var buffer: Value.BigIntSpace = undefined;
3739 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3740 operand_bigint.abs();
3741
3742 return mod.intValue_big(ty, operand_bigint.toConst());
3743 },
3744 .ComptimeFloat, .Float => {
3745 const target = mod.getTarget();
3746 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
3747 16 => .{ .f16 = @abs(val.toFloat(f16, mod)) },
3748 32 => .{ .f32 = @abs(val.toFloat(f32, mod)) },
3749 64 => .{ .f64 = @abs(val.toFloat(f64, mod)) },
3750 80 => .{ .f80 = @abs(val.toFloat(f80, mod)) },
3751 128 => .{ .f128 = @abs(val.toFloat(f128, mod)) },
3752 else => unreachable,
3753 };
3754 return Value.fromInterned((try mod.intern(.{ .float = .{
3755 .ty = ty.toIntern(),
3756 .storage = storage,
3757 } })));
3758 },
3759 else => unreachable,
3760 }
3761 }
3762
3763 pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3764 if (float_type.zigTypeTag(mod) == .Vector) {
3765 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3766 const scalar_ty = float_type.scalarType(mod);
3767 for (result_data, 0..) |*scalar, i| {
3768 const elem_val = try val.elemValue(mod, i);
3769 scalar.* = try (try floorScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3770 }
3771 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3772 .ty = float_type.toIntern(),
3773 .storage = .{ .elems = result_data },
3774 } })));
3775 }
3776 return floorScalar(val, float_type, mod);
3777 }
3778
3779 pub fn floorScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3780 const target = mod.getTarget();
3781 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3782 16 => .{ .f16 = @floor(val.toFloat(f16, mod)) },
3783 32 => .{ .f32 = @floor(val.toFloat(f32, mod)) },
3784 64 => .{ .f64 = @floor(val.toFloat(f64, mod)) },
3785 80 => .{ .f80 = @floor(val.toFloat(f80, mod)) },
3786 128 => .{ .f128 = @floor(val.toFloat(f128, mod)) },
3787 else => unreachable,
3788 };
3789 return Value.fromInterned((try mod.intern(.{ .float = .{
3790 .ty = float_type.toIntern(),
3791 .storage = storage,
3792 } })));
3793 }
3794
3795 pub fn ceil(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3796 if (float_type.zigTypeTag(mod) == .Vector) {
3797 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3798 const scalar_ty = float_type.scalarType(mod);
3799 for (result_data, 0..) |*scalar, i| {
3800 const elem_val = try val.elemValue(mod, i);
3801 scalar.* = try (try ceilScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3802 }
3803 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3804 .ty = float_type.toIntern(),
3805 .storage = .{ .elems = result_data },
3806 } })));
3807 }
3808 return ceilScalar(val, float_type, mod);
3809 }
3810
3811 pub fn ceilScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3812 const target = mod.getTarget();
3813 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3814 16 => .{ .f16 = @ceil(val.toFloat(f16, mod)) },
3815 32 => .{ .f32 = @ceil(val.toFloat(f32, mod)) },
3816 64 => .{ .f64 = @ceil(val.toFloat(f64, mod)) },
3817 80 => .{ .f80 = @ceil(val.toFloat(f80, mod)) },
3818 128 => .{ .f128 = @ceil(val.toFloat(f128, mod)) },
3819 else => unreachable,
3820 };
3821 return Value.fromInterned((try mod.intern(.{ .float = .{
3822 .ty = float_type.toIntern(),
3823 .storage = storage,
3824 } })));
3825 }
3826
3827 pub fn round(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3828 if (float_type.zigTypeTag(mod) == .Vector) {
3829 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3830 const scalar_ty = float_type.scalarType(mod);
3831 for (result_data, 0..) |*scalar, i| {
3832 const elem_val = try val.elemValue(mod, i);
3833 scalar.* = try (try roundScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3834 }
3835 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3836 .ty = float_type.toIntern(),
3837 .storage = .{ .elems = result_data },
3838 } })));
3839 }
3840 return roundScalar(val, float_type, mod);
3841 }
3842
3843 pub fn roundScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3844 const target = mod.getTarget();
3845 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3846 16 => .{ .f16 = @round(val.toFloat(f16, mod)) },
3847 32 => .{ .f32 = @round(val.toFloat(f32, mod)) },
3848 64 => .{ .f64 = @round(val.toFloat(f64, mod)) },
3849 80 => .{ .f80 = @round(val.toFloat(f80, mod)) },
3850 128 => .{ .f128 = @round(val.toFloat(f128, mod)) },
3851 else => unreachable,
3852 };
3853 return Value.fromInterned((try mod.intern(.{ .float = .{
3854 .ty = float_type.toIntern(),
3855 .storage = storage,
3856 } })));
3857 }
3858
3859 pub fn trunc(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3860 if (float_type.zigTypeTag(mod) == .Vector) {
3861 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3862 const scalar_ty = float_type.scalarType(mod);
3863 for (result_data, 0..) |*scalar, i| {
3864 const elem_val = try val.elemValue(mod, i);
3865 scalar.* = try (try truncScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3866 }
3867 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3868 .ty = float_type.toIntern(),
3869 .storage = .{ .elems = result_data },
3870 } })));
3871 }
3872 return truncScalar(val, float_type, mod);
3873 }
3874
3875 pub fn truncScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3876 const target = mod.getTarget();
3877 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3878 16 => .{ .f16 = @trunc(val.toFloat(f16, mod)) },
3879 32 => .{ .f32 = @trunc(val.toFloat(f32, mod)) },
3880 64 => .{ .f64 = @trunc(val.toFloat(f64, mod)) },
3881 80 => .{ .f80 = @trunc(val.toFloat(f80, mod)) },
3882 128 => .{ .f128 = @trunc(val.toFloat(f128, mod)) },
3883 else => unreachable,
3884 };
3885 return Value.fromInterned((try mod.intern(.{ .float = .{
3886 .ty = float_type.toIntern(),
3887 .storage = storage,
3888 } })));
3889 }
3890
3891 pub fn mulAdd(
3892 float_type: Type,
3893 mulend1: Value,
3894 mulend2: Value,
3895 addend: Value,
3896 arena: Allocator,
3897 mod: *Module,
3898 ) !Value {
3899 if (float_type.zigTypeTag(mod) == .Vector) {
3900 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3901 const scalar_ty = float_type.scalarType(mod);
3902 for (result_data, 0..) |*scalar, i| {
3903 const mulend1_elem = try mulend1.elemValue(mod, i);
3904 const mulend2_elem = try mulend2.elemValue(mod, i);
3905 const addend_elem = try addend.elemValue(mod, i);
3906 scalar.* = try (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, mod)).intern(scalar_ty, mod);
3907 }
3908 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3909 .ty = float_type.toIntern(),
3910 .storage = .{ .elems = result_data },
3911 } })));
3912 }
3913 return mulAddScalar(float_type, mulend1, mulend2, addend, mod);
3914 }
3915
3916 pub fn mulAddScalar(
3917 float_type: Type,
3918 mulend1: Value,
3919 mulend2: Value,
3920 addend: Value,
3921 mod: *Module,
3922 ) Allocator.Error!Value {
3923 const target = mod.getTarget();
3924 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3925 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, mod), mulend2.toFloat(f16, mod), addend.toFloat(f16, mod)) },
3926 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, mod), mulend2.toFloat(f32, mod), addend.toFloat(f32, mod)) },
3927 64 => .{ .f64 = @mulAdd(f64, mulend1.toFloat(f64, mod), mulend2.toFloat(f64, mod), addend.toFloat(f64, mod)) },
3928 80 => .{ .f80 = @mulAdd(f80, mulend1.toFloat(f80, mod), mulend2.toFloat(f80, mod), addend.toFloat(f80, mod)) },
3929 128 => .{ .f128 = @mulAdd(f128, mulend1.toFloat(f128, mod), mulend2.toFloat(f128, mod), addend.toFloat(f128, mod)) },
3930 else => unreachable,
3931 };
3932 return Value.fromInterned((try mod.intern(.{ .float = .{
3933 .ty = float_type.toIntern(),
3934 .storage = storage,
3935 } })));
3936 }
3937
3938 /// If the value is represented in-memory as a series of bytes that all
3939 /// have the same value, return that byte value, otherwise null.
3940 pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module) !?u8 {
3941 const abi_size = std.math.cast(usize, ty.abiSize(mod)) orelse return null;
3942 assert(abi_size >= 1);
3943 const byte_buffer = try mod.gpa.alloc(u8, abi_size);
3944 defer mod.gpa.free(byte_buffer);
3945
3946 writeToMemory(val, ty, mod, byte_buffer) catch |err| switch (err) {
3947 error.OutOfMemory => return error.OutOfMemory,
3948 error.ReinterpretDeclRef => return null,
3949 // TODO: The writeToMemory function was originally created for the purpose
3950 // of comptime pointer casting. However, it is now additionally being used
3951 // for checking the actual memory layout that will be generated by machine
3952 // code late in compilation. So, this error handling is too aggressive and
3953 // causes some false negatives, causing less-than-ideal code generation.
3954 error.IllDefinedMemoryLayout => return null,
3955 error.Unimplemented => return null,
3956 };
3957 const first_byte = byte_buffer[0];
3958 for (byte_buffer[1..]) |byte| {
3959 if (byte != first_byte) return null;
3960 }
3961 return first_byte;
3962 }
3963
3964 pub fn isGenericPoison(val: Value) bool {
3965 return val.toIntern() == .generic_poison;
3966 }
3967
3968 /// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value.
3969 /// If `val` is not undef, the bounds are both `val`.
3970 /// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type.
3971 /// If `val` is undef and is a `comptime_int`, returns null.
3972 pub fn intValueBounds(val: Value, mod: *Module) !?[2]Value {
3973 if (!val.isUndef(mod)) return .{ val, val };
3974 const ty = mod.intern_pool.typeOf(val.toIntern());
3975 if (ty == .comptime_int_type) return null;
3976 return .{
3977 try Type.fromInterned(ty).minInt(mod, Type.fromInterned(ty)),
3978 try Type.fromInterned(ty).maxInt(mod, Type.fromInterned(ty)),
3979 };
3980 }
3981
3982 /// This type is not copyable since it may contain pointers to its inner data.
3983 pub const Payload = struct {
3984 tag: Tag,
3985
3986 pub const Slice = struct {
3987 base: Payload,
3988 data: struct {
3989 ptr: Value,
3990 len: Value,
3991 },
3992 };
3993
3994 pub const Bytes = struct {
3995 base: Payload,
3996 /// Includes the sentinel, if any.
3997 data: []const u8,
3998 };
3999
4000 pub const SubValue = struct {
4001 base: Payload,
4002 data: Value,
4003 };
4004
4005 pub const Aggregate = struct {
4006 base: Payload,
4007 /// Field values. The types are according to the struct or array type.
4008 /// The length is provided here so that copying a Value does not depend on the Type.
4009 data: []Value,
4010 };
4011
4012 pub const Union = struct {
4013 pub const base_tag = Tag.@"union";
4014
4015 base: Payload = .{ .tag = base_tag },
4016 data: Data,
4017
4018 pub const Data = struct {
4019 tag: ?Value,
4020 val: Value,
4021 };
4022 };
4023 };
4024
4025 pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
4026
4027 pub const zero_usize: Value = .{ .ip_index = .zero_usize, .legacy = undefined };
4028 pub const zero_u8: Value = .{ .ip_index = .zero_u8, .legacy = undefined };
4029 pub const zero_comptime_int: Value = .{ .ip_index = .zero, .legacy = undefined };
4030 pub const one_comptime_int: Value = .{ .ip_index = .one, .legacy = undefined };
4031 pub const negative_one_comptime_int: Value = .{ .ip_index = .negative_one, .legacy = undefined };
4032 pub const undef: Value = .{ .ip_index = .undef, .legacy = undefined };
4033 pub const @"void": Value = .{ .ip_index = .void_value, .legacy = undefined };
4034 pub const @"null": Value = .{ .ip_index = .null_value, .legacy = undefined };
4035 pub const @"false": Value = .{ .ip_index = .bool_false, .legacy = undefined };
4036 pub const @"true": Value = .{ .ip_index = .bool_true, .legacy = undefined };
4037 pub const @"unreachable": Value = .{ .ip_index = .unreachable_value, .legacy = undefined };
4038
4039 pub const generic_poison: Value = .{ .ip_index = .generic_poison, .legacy = undefined };
4040 pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type, .legacy = undefined };
4041 pub const empty_struct: Value = .{ .ip_index = .empty_struct, .legacy = undefined };
4042
4043 pub fn makeBool(x: bool) Value {
4044 return if (x) Value.true else Value.false;
4045 }
4046
4047 pub const RuntimeIndex = InternPool.RuntimeIndex;
4048
4049 /// This function is used in the debugger pretty formatters in tools/ to fetch the
4050 /// Tag to Payload mapping to facilitate fancy debug printing for this type.
4051 fn dbHelper(self: *Value, tag_to_payload_map: *map: {
4052 const tags = @typeInfo(Tag).Enum.fields;
4053 var fields: [tags.len]std.builtin.Type.StructField = undefined;
4054 for (&fields, tags) |*field, t| field.* = .{
4055 .name = t.name ++ "",
4056 .type = *@field(Tag, t.name).Type(),
4057 .default_value = null,
4058 .is_comptime = false,
4059 .alignment = 0,
4060 };
4061 break :map @Type(.{ .Struct = .{
4062 .layout = .Extern,
4063 .fields = &fields,
4064 .decls = &.{},
4065 .is_tuple = false,
4066 } });
4067 }) void {
4068 _ = self;
4069 _ = tag_to_payload_map;
4070 }
4071
4072 comptime {
4073 if (builtin.mode == .Debug) {
4074 _ = &dbHelper;
4075 }
4076 }
4077};