authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-05 18:13:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-05 18:13:07-07:00
log78f15bc7147da4407f1b896ad76f4c19851bd7b9
treed91dad60e70a3a55b635291292c2e7110aaf0ae1
parent2de5e31721e2dc952219d08a417d7dc57efb3cba

compiler: rename value.zig to Value.zig

This commit only does the file rename to be friendlier to version control conflicts.

30 files changed, 4103 insertions(+), 4103 deletions(-)

CMakeLists.txt+1-1
...@@ -529,6 +529,7 @@ set(ZIG_STAGE2_SOURCES...@@ -529,6 +529,7 @@ set(ZIG_STAGE2_SOURCES
529 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"529 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
530 "${CMAKE_SOURCE_DIR}/src/Sema.zig"530 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
531 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"531 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
532 "${CMAKE_SOURCE_DIR}/src/Value.zig"
532 "${CMAKE_SOURCE_DIR}/src/Zir.zig"533 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
533 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"534 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"
534 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"535 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"
...@@ -644,7 +645,6 @@ set(ZIG_STAGE2_SOURCES...@@ -644,7 +645,6 @@ set(ZIG_STAGE2_SOURCES
644 "${CMAKE_SOURCE_DIR}/src/translate_c.zig"645 "${CMAKE_SOURCE_DIR}/src/translate_c.zig"
645 "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"646 "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
646 "${CMAKE_SOURCE_DIR}/src/type.zig"647 "${CMAKE_SOURCE_DIR}/src/type.zig"
647 "${CMAKE_SOURCE_DIR}/src/value.zig"
648 "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"648 "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
649 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"649 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
650 "${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig"650 "${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig"
src/Air.zig+1-1
...@@ -8,7 +8,7 @@ const builtin = @import("builtin");...@@ -8,7 +8,7 @@ const builtin = @import("builtin");
8const assert = std.debug.assert;8const assert = std.debug.assert;
99
10const Air = @This();10const Air = @This();
11const Value = @import("value.zig").Value;11const Value = @import("Value.zig");
12const Type = @import("type.zig").Type;12const Type = @import("type.zig").Type;
13const InternPool = @import("InternPool.zig");13const InternPool = @import("InternPool.zig");
14const Module = @import("Module.zig");14const Module = @import("Module.zig");
src/Compilation.zig+1-1
...@@ -11,7 +11,7 @@ const ThreadPool = std.Thread.Pool;...@@ -11,7 +11,7 @@ const ThreadPool = std.Thread.Pool;
11const WaitGroup = std.Thread.WaitGroup;11const WaitGroup = std.Thread.WaitGroup;
12const ErrorBundle = std.zig.ErrorBundle;12const ErrorBundle = std.zig.ErrorBundle;
1313
14const Value = @import("value.zig").Value;14const Value = @import("Value.zig");
15const Type = @import("type.zig").Type;15const Type = @import("type.zig").Type;
16const target_util = @import("target.zig");16const target_util = @import("target.zig");
17const Package = @import("Package.zig");17const Package = @import("Package.zig");
src/InternPool.zig+1-1
...@@ -6147,7 +6147,7 @@ fn finishFuncInstance(...@@ -6147,7 +6147,7 @@ fn finishFuncInstance(
6147 .has_tv = true,6147 .has_tv = true,
6148 .owns_tv = true,6148 .owns_tv = true,
6149 .ty = @import("type.zig").Type.fromInterned(func_ty),6149 .ty = @import("type.zig").Type.fromInterned(func_ty),
6150 .val = @import("value.zig").Value.fromInterned(func_index),6150 .val = @import("Value.zig").fromInterned(func_index),
6151 .alignment = .none,6151 .alignment = .none,
6152 .@"linksection" = section,6152 .@"linksection" = section,
6153 .@"addrspace" = fn_owner_decl.@"addrspace",6153 .@"addrspace" = fn_owner_decl.@"addrspace",
src/Module.zig+1-1
...@@ -19,7 +19,7 @@ const Module = Zcu;...@@ -19,7 +19,7 @@ const Module = Zcu;
19const Zcu = @This();19const Zcu = @This();
20const Compilation = @import("Compilation.zig");20const Compilation = @import("Compilation.zig");
21const Cache = std.Build.Cache;21const Cache = std.Build.Cache;
22const Value = @import("value.zig").Value;22const Value = @import("Value.zig");
23const Type = @import("type.zig").Type;23const Type = @import("type.zig").Type;
24const TypedValue = @import("TypedValue.zig");24const TypedValue = @import("TypedValue.zig");
25const Package = @import("Package.zig");25const Package = @import("Package.zig");
src/RangeSet.zig+1-1
...@@ -4,7 +4,7 @@ const Order = std.math.Order;...@@ -4,7 +4,7 @@ const Order = std.math.Order;
44
5const InternPool = @import("InternPool.zig");5const InternPool = @import("InternPool.zig");
6const Type = @import("type.zig").Type;6const Type = @import("type.zig").Type;
7const Value = @import("value.zig").Value;7const Value = @import("Value.zig");
8const Module = @import("Module.zig");8const Module = @import("Module.zig");
9const RangeSet = @This();9const RangeSet = @This();
10const SwitchProngSrc = @import("Module.zig").SwitchProngSrc;10const SwitchProngSrc = @import("Module.zig").SwitchProngSrc;
src/Sema.zig+1-1
...@@ -149,7 +149,7 @@ const assert = std.debug.assert;...@@ -149,7 +149,7 @@ const assert = std.debug.assert;
149const log = std.log.scoped(.sema);149const log = std.log.scoped(.sema);
150150
151const Sema = @This();151const Sema = @This();
152const Value = @import("value.zig").Value;152const Value = @import("Value.zig");
153const Type = @import("type.zig").Type;153const Type = @import("type.zig").Type;
154const TypedValue = @import("TypedValue.zig");154const TypedValue = @import("TypedValue.zig");
155const Air = @import("Air.zig");155const Air = @import("Air.zig");
src/TypedValue.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const Type = @import("type.zig").Type;2const Type = @import("type.zig").Type;
3const Value = @import("value.zig").Value;3const Value = @import("Value.zig");
4const Module = @import("Module.zig");4const Module = @import("Module.zig");
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const TypedValue = @This();6const TypedValue = @This();
src/Value.zig created+4075
...@@ -0,0 +1,4075 @@
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");
13pub const Value = @This();
14
15/// We are migrating towards using this for every Value object. However, many
16/// values are still represented the legacy way. This is indicated by using
17/// InternPool.Index.none.
18ip_index: InternPool.Index,
19
20/// This is the raw data, with no bookkeeping, no memory awareness,
21/// no de-duplication, and no type system awareness.
22/// This union takes advantage of the fact that the first page of memory
23/// is unmapped, giving us 4096 possible enum tags that have no payload.
24legacy: extern union {
25 ptr_otherwise: *Payload,
26},
27
28// Keep in sync with tools/stage2_pretty_printers_common.py
29pub const Tag = enum(usize) {
30 // The first section of this enum are tags that require no payload.
31 // After this, the tag requires a payload.
32
33 /// When the type is error union:
34 /// * If the tag is `.@"error"`, the error union is an error.
35 /// * If the tag is `.eu_payload`, the error union is a payload.
36 /// * A nested error such as `anyerror!(anyerror!T)` in which the the outer error union
37 /// is non-error, but the inner error union is an error, is represented as
38 /// a tag of `.eu_payload`, with a sub-tag of `.@"error"`.
39 eu_payload,
40 /// When the type is optional:
41 /// * If the tag is `.null_value`, the optional is null.
42 /// * If the tag is `.opt_payload`, the optional is a payload.
43 /// * A nested optional such as `??T` in which the the outer optional
44 /// is non-null, but the inner optional is null, is represented as
45 /// a tag of `.opt_payload`, with a sub-tag of `.null_value`.
46 opt_payload,
47 /// Pointer and length as sub `Value` objects.
48 slice,
49 /// A slice of u8 whose memory is managed externally.
50 bytes,
51 /// This value is repeated some number of times. The amount of times to repeat
52 /// is stored externally.
53 repeated,
54 /// An instance of a struct, array, or vector.
55 /// Each element/field stored as a `Value`.
56 /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
57 /// so the slice length will be one more than the type's array length.
58 aggregate,
59 /// An instance of a union.
60 @"union",
61
62 pub fn Type(comptime t: Tag) type {
63 return switch (t) {
64 .eu_payload,
65 .opt_payload,
66 .repeated,
67 => Payload.SubValue,
68 .slice => Payload.Slice,
69 .bytes => Payload.Bytes,
70 .aggregate => Payload.Aggregate,
71 .@"union" => Payload.Union,
72 };
73 }
74
75 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Value {
76 const ptr = try ally.create(t.Type());
77 ptr.* = .{
78 .base = .{ .tag = t },
79 .data = data,
80 };
81 return Value{
82 .ip_index = .none,
83 .legacy = .{ .ptr_otherwise = &ptr.base },
84 };
85 }
86
87 pub fn Data(comptime t: Tag) type {
88 return std.meta.fieldInfo(t.Type(), .data).type;
89 }
90};
91
92pub fn initPayload(payload: *Payload) Value {
93 return Value{
94 .ip_index = .none,
95 .legacy = .{ .ptr_otherwise = payload },
96 };
97}
98
99pub fn tag(self: Value) Tag {
100 assert(self.ip_index == .none);
101 return self.legacy.ptr_otherwise.tag;
102}
103
104/// Prefer `castTag` to this.
105pub fn cast(self: Value, comptime T: type) ?*T {
106 if (self.ip_index != .none) {
107 return null;
108 }
109 if (@hasField(T, "base_tag")) {
110 return self.castTag(T.base_tag);
111 }
112 inline for (@typeInfo(Tag).Enum.fields) |field| {
113 const t = @as(Tag, @enumFromInt(field.value));
114 if (self.legacy.ptr_otherwise.tag == t) {
115 if (T == t.Type()) {
116 return @fieldParentPtr(T, "base", self.legacy.ptr_otherwise);
117 }
118 return null;
119 }
120 }
121 unreachable;
122}
123
124pub fn castTag(self: Value, comptime t: Tag) ?*t.Type() {
125 if (self.ip_index != .none) return null;
126
127 if (self.legacy.ptr_otherwise.tag == t)
128 return @fieldParentPtr(t.Type(), "base", self.legacy.ptr_otherwise);
129
130 return null;
131}
132
133pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
134 _ = val;
135 _ = fmt;
136 _ = options;
137 _ = writer;
138 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
139}
140
141/// This is a debug function. In order to print values in a meaningful way
142/// we also need access to the type.
143pub fn dump(
144 start_val: Value,
145 comptime fmt: []const u8,
146 _: std.fmt.FormatOptions,
147 out_stream: anytype,
148) !void {
149 comptime assert(fmt.len == 0);
150 if (start_val.ip_index != .none) {
151 try out_stream.print("(interned: {})", .{start_val.toIntern()});
152 return;
153 }
154 var val = start_val;
155 while (true) switch (val.tag()) {
156 .aggregate => {
157 return out_stream.writeAll("(aggregate)");
158 },
159 .@"union" => {
160 return out_stream.writeAll("(union value)");
161 },
162 .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
163 .repeated => {
164 try out_stream.writeAll("(repeated) ");
165 val = val.castTag(.repeated).?.data;
166 },
167 .eu_payload => {
168 try out_stream.writeAll("(eu_payload) ");
169 val = val.castTag(.repeated).?.data;
170 },
171 .opt_payload => {
172 try out_stream.writeAll("(opt_payload) ");
173 val = val.castTag(.repeated).?.data;
174 },
175 .slice => return out_stream.writeAll("(slice)"),
176 };
177}
178
179pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) {
180 return .{ .data = val };
181}
182
183pub fn fmtValue(val: Value, ty: Type, mod: *Module) std.fmt.Formatter(TypedValue.format) {
184 return .{ .data = .{
185 .tv = .{ .ty = ty, .val = val },
186 .mod = mod,
187 } };
188}
189
190/// Asserts that the value is representable as an array of bytes.
191/// Returns the value as a null-terminated string stored in the InternPool.
192pub fn toIpString(val: Value, ty: Type, mod: *Module) !InternPool.NullTerminatedString {
193 const ip = &mod.intern_pool;
194 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
195 .enum_literal => |enum_literal| enum_literal,
196 .slice => |slice| try arrayToIpString(val, Value.fromInterned(slice.len).toUnsignedInt(mod), mod),
197 .aggregate => |aggregate| switch (aggregate.storage) {
198 .bytes => |bytes| try ip.getOrPutString(mod.gpa, bytes),
199 .elems => try arrayToIpString(val, ty.arrayLen(mod), mod),
200 .repeated_elem => |elem| {
201 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
202 const len = @as(usize, @intCast(ty.arrayLen(mod)));
203 try ip.string_bytes.appendNTimes(mod.gpa, byte, len);
204 return ip.getOrPutTrailingString(mod.gpa, len);
205 },
206 },
207 else => unreachable,
208 };
209}
210
211/// Asserts that the value is representable as an array of bytes.
212/// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
213pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 {
214 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
215 .enum_literal => |enum_literal| allocator.dupe(u8, mod.intern_pool.stringToSlice(enum_literal)),
216 .slice => |slice| try arrayToAllocatedBytes(val, Value.fromInterned(slice.len).toUnsignedInt(mod), allocator, mod),
217 .aggregate => |aggregate| switch (aggregate.storage) {
218 .bytes => |bytes| try allocator.dupe(u8, bytes),
219 .elems => try arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod),
220 .repeated_elem => |elem| {
221 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
222 const result = try allocator.alloc(u8, @as(usize, @intCast(ty.arrayLen(mod))));
223 @memset(result, byte);
224 return result;
225 },
226 },
227 else => unreachable,
228 };
229}
230
231fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 {
232 const result = try allocator.alloc(u8, @as(usize, @intCast(len)));
233 for (result, 0..) |*elem, i| {
234 const elem_val = try val.elemValue(mod, i);
235 elem.* = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
236 }
237 return result;
238}
239
240fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTerminatedString {
241 const gpa = mod.gpa;
242 const ip = &mod.intern_pool;
243 const len = @as(usize, @intCast(len_u64));
244 try ip.string_bytes.ensureUnusedCapacity(gpa, len);
245 for (0..len) |i| {
246 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's
247 // assert just to be sure.
248 const prev = ip.string_bytes.items.len;
249 const elem_val = try val.elemValue(mod, i);
250 assert(ip.string_bytes.items.len == prev);
251 const byte = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
252 ip.string_bytes.appendAssumeCapacity(byte);
253 }
254 return ip.getOrPutTrailingString(gpa, len);
255}
256
257pub fn intern2(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
258 if (val.ip_index != .none) return val.ip_index;
259 return intern(val, ty, mod);
260}
261
262pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
263 if (val.ip_index != .none) return (try mod.getCoerced(val, ty)).toIntern();
264 const ip = &mod.intern_pool;
265 switch (val.tag()) {
266 .eu_payload => {
267 const pl = val.castTag(.eu_payload).?.data;
268 return mod.intern(.{ .error_union = .{
269 .ty = ty.toIntern(),
270 .val = .{ .payload = try pl.intern(ty.errorUnionPayload(mod), mod) },
271 } });
272 },
273 .opt_payload => {
274 const pl = val.castTag(.opt_payload).?.data;
275 return mod.intern(.{ .opt = .{
276 .ty = ty.toIntern(),
277 .val = try pl.intern(ty.optionalChild(mod), mod),
278 } });
279 },
280 .slice => {
281 const pl = val.castTag(.slice).?.data;
282 return mod.intern(.{ .slice = .{
283 .ty = ty.toIntern(),
284 .len = try pl.len.intern(Type.usize, mod),
285 .ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod),
286 } });
287 },
288 .bytes => {
289 const pl = val.castTag(.bytes).?.data;
290 return mod.intern(.{ .aggregate = .{
291 .ty = ty.toIntern(),
292 .storage = .{ .bytes = pl },
293 } });
294 },
295 .repeated => {
296 const pl = val.castTag(.repeated).?.data;
297 return mod.intern(.{ .aggregate = .{
298 .ty = ty.toIntern(),
299 .storage = .{ .repeated_elem = try pl.intern(ty.childType(mod), mod) },
300 } });
301 },
302 .aggregate => {
303 const len = @as(usize, @intCast(ty.arrayLen(mod)));
304 const old_elems = val.castTag(.aggregate).?.data[0..len];
305 const new_elems = try mod.gpa.alloc(InternPool.Index, old_elems.len);
306 defer mod.gpa.free(new_elems);
307 const ty_key = ip.indexToKey(ty.toIntern());
308 for (new_elems, old_elems, 0..) |*new_elem, old_elem, field_i|
309 new_elem.* = try old_elem.intern(switch (ty_key) {
310 .struct_type => ty.structFieldType(field_i, mod),
311 .anon_struct_type => |info| Type.fromInterned(info.types.get(ip)[field_i]),
312 inline .array_type, .vector_type => |info| Type.fromInterned(info.child),
313 else => unreachable,
314 }, mod);
315 return mod.intern(.{ .aggregate = .{
316 .ty = ty.toIntern(),
317 .storage = .{ .elems = new_elems },
318 } });
319 },
320 .@"union" => {
321 const pl = val.castTag(.@"union").?.data;
322 if (pl.tag) |pl_tag| {
323 return mod.intern(.{ .un = .{
324 .ty = ty.toIntern(),
325 .tag = try pl_tag.intern(ty.unionTagTypeHypothetical(mod), mod),
326 .val = try pl.val.intern(ty.unionFieldType(pl_tag, mod).?, mod),
327 } });
328 } else {
329 return mod.intern(.{ .un = .{
330 .ty = ty.toIntern(),
331 .tag = .none,
332 .val = try pl.val.intern(try ty.unionBackingType(mod), mod),
333 } });
334 }
335 },
336 }
337}
338
339pub fn unintern(val: Value, arena: Allocator, mod: *Module) Allocator.Error!Value {
340 return if (val.ip_index == .none) val else switch (mod.intern_pool.indexToKey(val.toIntern())) {
341 .int_type,
342 .ptr_type,
343 .array_type,
344 .vector_type,
345 .opt_type,
346 .anyframe_type,
347 .error_union_type,
348 .simple_type,
349 .struct_type,
350 .anon_struct_type,
351 .union_type,
352 .opaque_type,
353 .enum_type,
354 .func_type,
355 .error_set_type,
356 .inferred_error_set_type,
357
358 .undef,
359 .simple_value,
360 .variable,
361 .extern_func,
362 .func,
363 .int,
364 .err,
365 .enum_literal,
366 .enum_tag,
367 .empty_enum_value,
368 .float,
369 .ptr,
370 => val,
371
372 .error_union => |error_union| switch (error_union.val) {
373 .err_name => val,
374 .payload => |payload| Tag.eu_payload.create(arena, Value.fromInterned(payload)),
375 },
376
377 .slice => |slice| Tag.slice.create(arena, .{
378 .ptr = Value.fromInterned(slice.ptr),
379 .len = Value.fromInterned(slice.len),
380 }),
381
382 .opt => |opt| switch (opt.val) {
383 .none => val,
384 else => |payload| Tag.opt_payload.create(arena, Value.fromInterned(payload)),
385 },
386
387 .aggregate => |aggregate| switch (aggregate.storage) {
388 .bytes => |bytes| Tag.bytes.create(arena, try arena.dupe(u8, bytes)),
389 .elems => |old_elems| {
390 const new_elems = try arena.alloc(Value, old_elems.len);
391 for (new_elems, old_elems) |*new_elem, old_elem| new_elem.* = Value.fromInterned(old_elem);
392 return Tag.aggregate.create(arena, new_elems);
393 },
394 .repeated_elem => |elem| Tag.repeated.create(arena, Value.fromInterned(elem)),
395 },
396
397 .un => |un| Tag.@"union".create(arena, .{
398 // toValue asserts that the value cannot be .none which is valid on unions.
399 .tag = if (un.tag == .none) null else Value.fromInterned(un.tag),
400 .val = Value.fromInterned(un.val),
401 }),
402
403 .memoized_call => unreachable,
404 };
405}
406
407pub fn fromInterned(i: InternPool.Index) Value {
408 assert(i != .none);
409 return .{
410 .ip_index = i,
411 .legacy = undefined,
412 };
413}
414
415pub fn toIntern(val: Value) InternPool.Index {
416 assert(val.ip_index != .none);
417 return val.ip_index;
418}
419
420/// Asserts that the value is representable as a type.
421pub fn toType(self: Value) Type {
422 return Type.fromInterned(self.toIntern());
423}
424
425pub fn intFromEnum(val: Value, ty: Type, mod: *Module) Allocator.Error!Value {
426 const ip = &mod.intern_pool;
427 return switch (ip.indexToKey(ip.typeOf(val.toIntern()))) {
428 // Assume it is already an integer and return it directly.
429 .simple_type, .int_type => val,
430 .enum_literal => |enum_literal| {
431 const field_index = ty.enumFieldIndex(enum_literal, mod).?;
432 return switch (ip.indexToKey(ty.toIntern())) {
433 // Assume it is already an integer and return it directly.
434 .simple_type, .int_type => val,
435 .enum_type => |enum_type| if (enum_type.values.len != 0)
436 Value.fromInterned(enum_type.values.get(ip)[field_index])
437 else // Field index and integer values are the same.
438 mod.intValue(Type.fromInterned(enum_type.tag_ty), field_index),
439 else => unreachable,
440 };
441 },
442 .enum_type => |enum_type| try mod.getCoerced(val, Type.fromInterned(enum_type.tag_ty)),
443 else => unreachable,
444 };
445}
446
447/// Asserts the value is an integer.
448pub fn toBigInt(val: Value, space: *BigIntSpace, mod: *Module) BigIntConst {
449 return val.toBigIntAdvanced(space, mod, null) catch unreachable;
450}
451
452/// Asserts the value is an integer.
453pub fn toBigIntAdvanced(
454 val: Value,
455 space: *BigIntSpace,
456 mod: *Module,
457 opt_sema: ?*Sema,
458) Module.CompileError!BigIntConst {
459 return switch (val.toIntern()) {
460 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
461 .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
462 .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
463 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
464 .int => |int| switch (int.storage) {
465 .u64, .i64, .big_int => int.storage.toBigInt(space),
466 .lazy_align, .lazy_size => |ty| {
467 if (opt_sema) |sema| try sema.resolveTypeLayout(Type.fromInterned(ty));
468 const x = switch (int.storage) {
469 else => unreachable,
470 .lazy_align => Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
471 .lazy_size => Type.fromInterned(ty).abiSize(mod),
472 };
473 return BigIntMutable.init(&space.limbs, x).toConst();
474 },
475 },
476 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, mod, opt_sema),
477 .opt, .ptr => BigIntMutable.init(
478 &space.limbs,
479 (try val.getUnsignedIntAdvanced(mod, opt_sema)).?,
480 ).toConst(),
481 else => unreachable,
482 },
483 };
484}
485
486pub fn isFuncBody(val: Value, mod: *Module) bool {
487 return mod.intern_pool.isFuncBody(val.toIntern());
488}
489
490pub fn getFunction(val: Value, mod: *Module) ?InternPool.Key.Func {
491 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
492 .func => |x| x,
493 else => null,
494 } else null;
495}
496
497pub fn getExternFunc(val: Value, mod: *Module) ?InternPool.Key.ExternFunc {
498 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
499 .extern_func => |extern_func| extern_func,
500 else => null,
501 } else null;
502}
503
504pub fn getVariable(val: Value, mod: *Module) ?InternPool.Key.Variable {
505 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
506 .variable => |variable| variable,
507 else => null,
508 } else null;
509}
510
511/// If the value fits in a u64, return it, otherwise null.
512/// Asserts not undefined.
513pub fn getUnsignedInt(val: Value, mod: *Module) ?u64 {
514 return getUnsignedIntAdvanced(val, mod, null) catch unreachable;
515}
516
517/// If the value fits in a u64, return it, otherwise null.
518/// Asserts not undefined.
519pub fn getUnsignedIntAdvanced(val: Value, mod: *Module, opt_sema: ?*Sema) !?u64 {
520 return switch (val.toIntern()) {
521 .undef => unreachable,
522 .bool_false => 0,
523 .bool_true => 1,
524 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
525 .undef => unreachable,
526 .int => |int| switch (int.storage) {
527 .big_int => |big_int| big_int.to(u64) catch null,
528 .u64 => |x| x,
529 .i64 => |x| std.math.cast(u64, x),
530 .lazy_align => |ty| if (opt_sema) |sema|
531 (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0)
532 else
533 Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
534 .lazy_size => |ty| if (opt_sema) |sema|
535 (try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar
536 else
537 Type.fromInterned(ty).abiSize(mod),
538 },
539 .ptr => |ptr| switch (ptr.addr) {
540 .int => |int| Value.fromInterned(int).getUnsignedIntAdvanced(mod, opt_sema),
541 .elem => |elem| {
542 const base_addr = (try Value.fromInterned(elem.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
543 const elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod);
544 return base_addr + elem.index * elem_ty.abiSize(mod);
545 },
546 .field => |field| {
547 const base_addr = (try Value.fromInterned(field.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
548 const struct_ty = Type.fromInterned(mod.intern_pool.typeOf(field.base)).childType(mod);
549 if (opt_sema) |sema| try sema.resolveTypeLayout(struct_ty);
550 return base_addr + struct_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod);
551 },
552 else => null,
553 },
554 .opt => |opt| switch (opt.val) {
555 .none => 0,
556 else => |payload| Value.fromInterned(payload).getUnsignedIntAdvanced(mod, opt_sema),
557 },
558 else => null,
559 },
560 };
561}
562
563/// Asserts the value is an integer and it fits in a u64
564pub fn toUnsignedInt(val: Value, mod: *Module) u64 {
565 return getUnsignedInt(val, mod).?;
566}
567
568/// Asserts the value is an integer and it fits in a u64
569pub fn toUnsignedIntAdvanced(val: Value, sema: *Sema) !u64 {
570 return (try getUnsignedIntAdvanced(val, sema.mod, sema)).?;
571}
572
573/// Asserts the value is an integer and it fits in a i64
574pub fn toSignedInt(val: Value, mod: *Module) i64 {
575 return switch (val.toIntern()) {
576 .bool_false => 0,
577 .bool_true => 1,
578 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
579 .int => |int| switch (int.storage) {
580 .big_int => |big_int| big_int.to(i64) catch unreachable,
581 .i64 => |x| x,
582 .u64 => |x| @intCast(x),
583 .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
584 .lazy_size => |ty| @intCast(Type.fromInterned(ty).abiSize(mod)),
585 },
586 else => unreachable,
587 },
588 };
589}
590
591pub fn toBool(val: Value) bool {
592 return switch (val.toIntern()) {
593 .bool_true => true,
594 .bool_false => false,
595 else => unreachable,
596 };
597}
598
599fn isDeclRef(val: Value, mod: *Module) bool {
600 var check = val;
601 while (true) switch (mod.intern_pool.indexToKey(check.toIntern())) {
602 .ptr => |ptr| switch (ptr.addr) {
603 .decl, .mut_decl, .comptime_field, .anon_decl => return true,
604 .eu_payload, .opt_payload => |base| check = Value.fromInterned(base),
605 .elem, .field => |base_index| check = Value.fromInterned(base_index.base),
606 .int => return false,
607 },
608 else => return false,
609 };
610}
611
612/// Write a Value's contents to `buffer`.
613///
614/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
615/// the end of the value in memory.
616pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{
617 ReinterpretDeclRef,
618 IllDefinedMemoryLayout,
619 Unimplemented,
620 OutOfMemory,
621}!void {
622 const target = mod.getTarget();
623 const endian = target.cpu.arch.endian();
624 if (val.isUndef(mod)) {
625 const size: usize = @intCast(ty.abiSize(mod));
626 @memset(buffer[0..size], 0xaa);
627 return;
628 }
629 const ip = &mod.intern_pool;
630 switch (ty.zigTypeTag(mod)) {
631 .Void => {},
632 .Bool => {
633 buffer[0] = @intFromBool(val.toBool());
634 },
635 .Int, .Enum => {
636 const int_info = ty.intInfo(mod);
637 const bits = int_info.bits;
638 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
639
640 var bigint_buffer: BigIntSpace = undefined;
641 const bigint = val.toBigInt(&bigint_buffer, mod);
642 bigint.writeTwosComplement(buffer[0..byte_count], endian);
643 },
644 .Float => switch (ty.floatBits(target)) {
645 16 => std.mem.writeInt(u16, buffer[0..2], @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
646 32 => std.mem.writeInt(u32, buffer[0..4], @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
647 64 => std.mem.writeInt(u64, buffer[0..8], @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
648 80 => std.mem.writeInt(u80, buffer[0..10], @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
649 128 => std.mem.writeInt(u128, buffer[0..16], @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
650 else => unreachable,
651 },
652 .Array => {
653 const len = ty.arrayLen(mod);
654 const elem_ty = ty.childType(mod);
655 const elem_size = @as(usize, @intCast(elem_ty.abiSize(mod)));
656 var elem_i: usize = 0;
657 var buf_off: usize = 0;
658 while (elem_i < len) : (elem_i += 1) {
659 const elem_val = try val.elemValue(mod, elem_i);
660 try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]);
661 buf_off += elem_size;
662 }
663 },
664 .Vector => {
665 // We use byte_count instead of abi_size here, so that any padding bytes
666 // follow the data bytes, on both big- and little-endian systems.
667 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
668 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
669 },
670 .Struct => {
671 const struct_type = mod.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;
672 switch (struct_type.layout) {
673 .Auto => return error.IllDefinedMemoryLayout,
674 .Extern => for (0..struct_type.field_types.len) |i| {
675 const off: usize = @intCast(ty.structFieldOffset(i, mod));
676 const field_val = switch (val.ip_index) {
677 .none => switch (val.tag()) {
678 .bytes => {
679 buffer[off] = val.castTag(.bytes).?.data[i];
680 continue;
681 },
682 .aggregate => val.castTag(.aggregate).?.data[i],
683 .repeated => val.castTag(.repeated).?.data,
684 else => unreachable,
685 },
686 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
687 .bytes => |bytes| {
688 buffer[off] = bytes[i];
689 continue;
690 },
691 .elems => |elems| elems[i],
692 .repeated_elem => |elem| elem,
693 }),
694 };
695 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
696 try writeToMemory(field_val, field_ty, mod, buffer[off..]);
697 },
698 .Packed => {
699 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
700 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
701 },
702 }
703 },
704 .ErrorSet => {
705 const bits = mod.errorSetBits();
706 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
707
708 const name = switch (ip.indexToKey(val.toIntern())) {
709 .err => |err| err.name,
710 .error_union => |error_union| error_union.val.err_name,
711 else => unreachable,
712 };
713 var bigint_buffer: BigIntSpace = undefined;
714 const bigint = BigIntMutable.init(
715 &bigint_buffer.limbs,
716 mod.global_error_set.getIndex(name).?,
717 ).toConst();
718 bigint.writeTwosComplement(buffer[0..byte_count], endian);
719 },
720 .Union => switch (ty.containerLayout(mod)) {
721 .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
722 .Extern => {
723 if (val.unionTag(mod)) |union_tag| {
724 const union_obj = mod.typeToUnion(ty).?;
725 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
726 const field_type = Type.fromInterned(union_obj.field_types.get(&mod.intern_pool)[field_index]);
727 const field_val = try val.fieldValue(mod, field_index);
728 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
729 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
730 } else {
731 const backing_ty = try ty.unionBackingType(mod);
732 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
733 return writeToMemory(val.unionValue(mod), backing_ty, mod, buffer[0..byte_count]);
734 }
735 },
736 .Packed => {
737 const backing_ty = try ty.unionBackingType(mod);
738 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
739 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
740 },
741 },
742 .Pointer => {
743 if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout;
744 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
745 return val.writeToMemory(Type.usize, mod, buffer);
746 },
747 .Optional => {
748 if (!ty.isPtrLikeOptional(mod)) return error.IllDefinedMemoryLayout;
749 const child = ty.optionalChild(mod);
750 const opt_val = val.optionalValue(mod);
751 if (opt_val) |some| {
752 return some.writeToMemory(child, mod, buffer);
753 } else {
754 return writeToMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer);
755 }
756 },
757 else => return error.Unimplemented,
758 }
759}
760
761/// Write a Value's contents to `buffer`.
762///
763/// Both the start and the end of the provided buffer must be tight, since
764/// big-endian packed memory layouts start at the end of the buffer.
765pub fn writeToPackedMemory(
766 val: Value,
767 ty: Type,
768 mod: *Module,
769 buffer: []u8,
770 bit_offset: usize,
771) error{ ReinterpretDeclRef, OutOfMemory }!void {
772 const ip = &mod.intern_pool;
773 const target = mod.getTarget();
774 const endian = target.cpu.arch.endian();
775 if (val.isUndef(mod)) {
776 const bit_size = @as(usize, @intCast(ty.bitSize(mod)));
777 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian);
778 return;
779 }
780 switch (ty.zigTypeTag(mod)) {
781 .Void => {},
782 .Bool => {
783 const byte_index = switch (endian) {
784 .little => bit_offset / 8,
785 .big => buffer.len - bit_offset / 8 - 1,
786 };
787 if (val.toBool()) {
788 buffer[byte_index] |= (@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
789 } else {
790 buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
791 }
792 },
793 .Int, .Enum => {
794 if (buffer.len == 0) return;
795 const bits = ty.intInfo(mod).bits;
796 if (bits == 0) return;
797
798 switch (ip.indexToKey((try val.intFromEnum(ty, mod)).toIntern()).int.storage) {
799 inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
800 .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
801 .lazy_align => |lazy_align| {
802 const num = Type.fromInterned(lazy_align).abiAlignment(mod).toByteUnits(0);
803 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
804 },
805 .lazy_size => |lazy_size| {
806 const num = Type.fromInterned(lazy_size).abiSize(mod);
807 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
808 },
809 }
810 },
811 .Float => switch (ty.floatBits(target)) {
812 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
813 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
814 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
815 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
816 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
817 else => unreachable,
818 },
819 .Vector => {
820 const elem_ty = ty.childType(mod);
821 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
822 const len = @as(usize, @intCast(ty.arrayLen(mod)));
823
824 var bits: u16 = 0;
825 var elem_i: usize = 0;
826 while (elem_i < len) : (elem_i += 1) {
827 // On big-endian systems, LLVM reverses the element order of vectors by default
828 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
829 const elem_val = try val.elemValue(mod, tgt_elem_i);
830 try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
831 bits += elem_bit_size;
832 }
833 },
834 .Struct => {
835 const struct_type = ip.indexToKey(ty.toIntern()).struct_type;
836 // Sema is supposed to have emitted a compile error already in the case of Auto,
837 // and Extern is handled in non-packed writeToMemory.
838 assert(struct_type.layout == .Packed);
839 var bits: u16 = 0;
840 for (0..struct_type.field_types.len) |i| {
841 const field_val = switch (val.ip_index) {
842 .none => switch (val.tag()) {
843 .bytes => unreachable,
844 .aggregate => val.castTag(.aggregate).?.data[i],
845 .repeated => val.castTag(.repeated).?.data,
846 else => unreachable,
847 },
848 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
849 .bytes => unreachable,
850 .elems => |elems| elems[i],
851 .repeated_elem => |elem| elem,
852 }),
853 };
854 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
855 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
856 try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
857 bits += field_bits;
858 }
859 },
860 .Union => {
861 const union_obj = mod.typeToUnion(ty).?;
862 switch (union_obj.getLayout(ip)) {
863 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
864 .Packed => {
865 if (val.unionTag(mod)) |union_tag| {
866 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
867 const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
868 const field_val = try val.fieldValue(mod, field_index);
869 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
870 } else {
871 const backing_ty = try ty.unionBackingType(mod);
872 return val.unionValue(mod).writeToPackedMemory(backing_ty, mod, buffer, bit_offset);
873 }
874 },
875 }
876 },
877 .Pointer => {
878 assert(!ty.isSlice(mod)); // No well defined layout.
879 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
880 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);
881 },
882 .Optional => {
883 assert(ty.isPtrLikeOptional(mod));
884 const child = ty.optionalChild(mod);
885 const opt_val = val.optionalValue(mod);
886 if (opt_val) |some| {
887 return some.writeToPackedMemory(child, mod, buffer, bit_offset);
888 } else {
889 return writeToPackedMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer, bit_offset);
890 }
891 },
892 else => @panic("TODO implement writeToPackedMemory for more types"),
893 }
894}
895
896/// Load a Value from the contents of `buffer`.
897///
898/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
899/// the end of the value in memory.
900pub fn readFromMemory(
901 ty: Type,
902 mod: *Module,
903 buffer: []const u8,
904 arena: Allocator,
905) error{
906 IllDefinedMemoryLayout,
907 Unimplemented,
908 OutOfMemory,
909}!Value {
910 const ip = &mod.intern_pool;
911 const target = mod.getTarget();
912 const endian = target.cpu.arch.endian();
913 switch (ty.zigTypeTag(mod)) {
914 .Void => return Value.void,
915 .Bool => {
916 if (buffer[0] == 0) {
917 return Value.false;
918 } else {
919 return Value.true;
920 }
921 },
922 .Int, .Enum => |ty_tag| {
923 const int_ty = switch (ty_tag) {
924 .Int => ty,
925 .Enum => ty.intTagType(mod),
926 else => unreachable,
927 };
928 const int_info = int_ty.intInfo(mod);
929 const bits = int_info.bits;
930 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
931 if (bits == 0 or buffer.len == 0) return mod.getCoerced(try mod.intValue(int_ty, 0), ty);
932
933 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
934 .signed => {
935 const val = std.mem.readVarInt(i64, buffer[0..byte_count], endian);
936 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
937 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
938 },
939 .unsigned => {
940 const val = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
941 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
942 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
943 },
944 } else { // Slow path, we have to construct a big-int
945 const Limb = std.math.big.Limb;
946 const limb_count = (byte_count + @sizeOf(Limb) - 1) / @sizeOf(Limb);
947 const limbs_buffer = try arena.alloc(Limb, limb_count);
948
949 var bigint = BigIntMutable.init(limbs_buffer, 0);
950 bigint.readTwosComplement(buffer[0..byte_count], bits, endian, int_info.signedness);
951 return mod.getCoerced(try mod.intValue_big(int_ty, bigint.toConst()), ty);
952 }
953 },
954 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
955 .ty = ty.toIntern(),
956 .storage = switch (ty.floatBits(target)) {
957 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readInt(u16, buffer[0..2], endian))) },
958 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readInt(u32, buffer[0..4], endian))) },
959 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readInt(u64, buffer[0..8], endian))) },
960 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readInt(u80, buffer[0..10], endian))) },
961 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readInt(u128, buffer[0..16], endian))) },
962 else => unreachable,
963 },
964 } }))),
965 .Array => {
966 const elem_ty = ty.childType(mod);
967 const elem_size = elem_ty.abiSize(mod);
968 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
969 var offset: usize = 0;
970 for (elems) |*elem| {
971 elem.* = try (try readFromMemory(elem_ty, mod, buffer[offset..], arena)).intern(elem_ty, mod);
972 offset += @as(usize, @intCast(elem_size));
973 }
974 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
975 .ty = ty.toIntern(),
976 .storage = .{ .elems = elems },
977 } })));
978 },
979 .Vector => {
980 // We use byte_count instead of abi_size here, so that any padding bytes
981 // follow the data bytes, on both big- and little-endian systems.
982 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
983 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
984 },
985 .Struct => {
986 const struct_type = mod.typeToStruct(ty).?;
987 switch (struct_type.layout) {
988 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
989 .Extern => {
990 const field_types = struct_type.field_types;
991 const field_vals = try arena.alloc(InternPool.Index, field_types.len);
992 for (field_vals, 0..) |*field_val, i| {
993 const field_ty = Type.fromInterned(field_types.get(ip)[i]);
994 const off: usize = @intCast(ty.structFieldOffset(i, mod));
995 const sz: usize = @intCast(field_ty.abiSize(mod));
996 field_val.* = try (try readFromMemory(field_ty, mod, buffer[off..(off + sz)], arena)).intern(field_ty, mod);
997 }
998 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
999 .ty = ty.toIntern(),
1000 .storage = .{ .elems = field_vals },
1001 } })));
1002 },
1003 .Packed => {
1004 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1005 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1006 },
1007 }
1008 },
1009 .ErrorSet => {
1010 const bits = mod.errorSetBits();
1011 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
1012 const int = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
1013 const index = (int << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
1014 const name = mod.global_error_set.keys()[@intCast(index)];
1015
1016 return Value.fromInterned((try mod.intern(.{ .err = .{
1017 .ty = ty.toIntern(),
1018 .name = name,
1019 } })));
1020 },
1021 .Union => switch (ty.containerLayout(mod)) {
1022 .Auto => return error.IllDefinedMemoryLayout,
1023 .Extern => {
1024 const union_size = ty.abiSize(mod);
1025 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
1026 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
1027 return Value.fromInterned((try mod.intern(.{ .un = .{
1028 .ty = ty.toIntern(),
1029 .tag = .none,
1030 .val = val,
1031 } })));
1032 },
1033 .Packed => {
1034 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1035 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1036 },
1037 },
1038 .Pointer => {
1039 assert(!ty.isSlice(mod)); // No well defined layout.
1040 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
1041 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1042 .ty = ty.toIntern(),
1043 .addr = .{ .int = int_val.toIntern() },
1044 } })));
1045 },
1046 .Optional => {
1047 assert(ty.isPtrLikeOptional(mod));
1048 const child_ty = ty.optionalChild(mod);
1049 const child_val = try readFromMemory(child_ty, mod, buffer, arena);
1050 return Value.fromInterned((try mod.intern(.{ .opt = .{
1051 .ty = ty.toIntern(),
1052 .val = switch (child_val.orderAgainstZero(mod)) {
1053 .lt => unreachable,
1054 .eq => .none,
1055 .gt => child_val.toIntern(),
1056 },
1057 } })));
1058 },
1059 else => return error.Unimplemented,
1060 }
1061}
1062
1063/// Load a Value from the contents of `buffer`.
1064///
1065/// Both the start and the end of the provided buffer must be tight, since
1066/// big-endian packed memory layouts start at the end of the buffer.
1067pub fn readFromPackedMemory(
1068 ty: Type,
1069 mod: *Module,
1070 buffer: []const u8,
1071 bit_offset: usize,
1072 arena: Allocator,
1073) error{
1074 IllDefinedMemoryLayout,
1075 OutOfMemory,
1076}!Value {
1077 const ip = &mod.intern_pool;
1078 const target = mod.getTarget();
1079 const endian = target.cpu.arch.endian();
1080 switch (ty.zigTypeTag(mod)) {
1081 .Void => return Value.void,
1082 .Bool => {
1083 const byte = switch (endian) {
1084 .big => buffer[buffer.len - bit_offset / 8 - 1],
1085 .little => buffer[bit_offset / 8],
1086 };
1087 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) {
1088 return Value.false;
1089 } else {
1090 return Value.true;
1091 }
1092 },
1093 .Int, .Enum => |ty_tag| {
1094 if (buffer.len == 0) return mod.intValue(ty, 0);
1095 const int_info = ty.intInfo(mod);
1096 const bits = int_info.bits;
1097 if (bits == 0) return mod.intValue(ty, 0);
1098
1099 // Fast path for integers <= u64
1100 if (bits <= 64) {
1101 const int_ty = switch (ty_tag) {
1102 .Int => ty,
1103 .Enum => ty.intTagType(mod),
1104 else => unreachable,
1105 };
1106 return mod.getCoerced(switch (int_info.signedness) {
1107 .signed => return mod.intValue(
1108 int_ty,
1109 std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed),
1110 ),
1111 .unsigned => return mod.intValue(
1112 int_ty,
1113 std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned),
1114 ),
1115 }, ty);
1116 }
1117
1118 // Slow path, we have to construct a big-int
1119 const abi_size = @as(usize, @intCast(ty.abiSize(mod)));
1120 const Limb = std.math.big.Limb;
1121 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1122 const limbs_buffer = try arena.alloc(Limb, limb_count);
1123
1124 var bigint = BigIntMutable.init(limbs_buffer, 0);
1125 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
1126 return mod.intValue_big(ty, bigint.toConst());
1127 },
1128 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
1129 .ty = ty.toIntern(),
1130 .storage = switch (ty.floatBits(target)) {
1131 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian))) },
1132 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian))) },
1133 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian))) },
1134 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian))) },
1135 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian))) },
1136 else => unreachable,
1137 },
1138 } }))),
1139 .Vector => {
1140 const elem_ty = ty.childType(mod);
1141 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
1142
1143 var bits: u16 = 0;
1144 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
1145 for (elems, 0..) |_, i| {
1146 // On big-endian systems, LLVM reverses the element order of vectors by default
1147 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;
1148 elems[tgt_elem_i] = try (try readFromPackedMemory(elem_ty, mod, buffer, bit_offset + bits, arena)).intern(elem_ty, mod);
1149 bits += elem_bit_size;
1150 }
1151 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1152 .ty = ty.toIntern(),
1153 .storage = .{ .elems = elems },
1154 } })));
1155 },
1156 .Struct => {
1157 // Sema is supposed to have emitted a compile error already for Auto layout structs,
1158 // and Extern is handled by non-packed readFromMemory.
1159 const struct_type = mod.typeToPackedStruct(ty).?;
1160 var bits: u16 = 0;
1161 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
1162 for (field_vals, 0..) |*field_val, i| {
1163 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
1164 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
1165 field_val.* = try (try readFromPackedMemory(field_ty, mod, buffer, bit_offset + bits, arena)).intern(field_ty, mod);
1166 bits += field_bits;
1167 }
1168 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1169 .ty = ty.toIntern(),
1170 .storage = .{ .elems = field_vals },
1171 } })));
1172 },
1173 .Union => switch (ty.containerLayout(mod)) {
1174 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
1175 .Packed => {
1176 const backing_ty = try ty.unionBackingType(mod);
1177 const val = (try readFromPackedMemory(backing_ty, mod, buffer, bit_offset, arena)).toIntern();
1178 return Value.fromInterned((try mod.intern(.{ .un = .{
1179 .ty = ty.toIntern(),
1180 .tag = .none,
1181 .val = val,
1182 } })));
1183 },
1184 },
1185 .Pointer => {
1186 assert(!ty.isSlice(mod)); // No well defined layout.
1187 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
1188 },
1189 .Optional => {
1190 assert(ty.isPtrLikeOptional(mod));
1191 const child = ty.optionalChild(mod);
1192 return readFromPackedMemory(child, mod, buffer, bit_offset, arena);
1193 },
1194 else => @panic("TODO implement readFromPackedMemory for more types"),
1195 }
1196}
1197
1198/// Asserts that the value is a float or an integer.
1199pub fn toFloat(val: Value, comptime T: type, mod: *Module) T {
1200 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1201 .int => |int| switch (int.storage) {
1202 .big_int => |big_int| @floatCast(bigIntToFloat(big_int.limbs, big_int.positive)),
1203 inline .u64, .i64 => |x| {
1204 if (T == f80) {
1205 @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
1206 }
1207 return @floatFromInt(x);
1208 },
1209 .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
1210 .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(mod)),
1211 },
1212 .float => |float| switch (float.storage) {
1213 inline else => |x| @floatCast(x),
1214 },
1215 else => unreachable,
1216 };
1217}
1218
1219/// TODO move this to std lib big int code
1220fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
1221 if (limbs.len == 0) return 0;
1222
1223 const base = std.math.maxInt(std.math.big.Limb) + 1;
1224 var result: f128 = 0;
1225 var i: usize = limbs.len;
1226 while (i != 0) {
1227 i -= 1;
1228 const limb: f128 = @as(f128, @floatFromInt(limbs[i]));
1229 result = @mulAdd(f128, base, result, limb);
1230 }
1231 if (positive) {
1232 return result;
1233 } else {
1234 return -result;
1235 }
1236}
1237
1238pub fn clz(val: Value, ty: Type, mod: *Module) u64 {
1239 var bigint_buf: BigIntSpace = undefined;
1240 const bigint = val.toBigInt(&bigint_buf, mod);
1241 return bigint.clz(ty.intInfo(mod).bits);
1242}
1243
1244pub fn ctz(val: Value, ty: Type, mod: *Module) u64 {
1245 var bigint_buf: BigIntSpace = undefined;
1246 const bigint = val.toBigInt(&bigint_buf, mod);
1247 return bigint.ctz(ty.intInfo(mod).bits);
1248}
1249
1250pub fn popCount(val: Value, ty: Type, mod: *Module) u64 {
1251 var bigint_buf: BigIntSpace = undefined;
1252 const bigint = val.toBigInt(&bigint_buf, mod);
1253 return @as(u64, @intCast(bigint.popCount(ty.intInfo(mod).bits)));
1254}
1255
1256pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1257 const info = ty.intInfo(mod);
1258
1259 var buffer: Value.BigIntSpace = undefined;
1260 const operand_bigint = val.toBigInt(&buffer, mod);
1261
1262 const limbs = try arena.alloc(
1263 std.math.big.Limb,
1264 std.math.big.int.calcTwosCompLimbCount(info.bits),
1265 );
1266 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1267 result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
1268
1269 return mod.intValue_big(ty, result_bigint.toConst());
1270}
1271
1272pub fn byteSwap(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1273 const info = ty.intInfo(mod);
1274
1275 // Bit count must be evenly divisible by 8
1276 assert(info.bits % 8 == 0);
1277
1278 var buffer: Value.BigIntSpace = undefined;
1279 const operand_bigint = val.toBigInt(&buffer, mod);
1280
1281 const limbs = try arena.alloc(
1282 std.math.big.Limb,
1283 std.math.big.int.calcTwosCompLimbCount(info.bits),
1284 );
1285 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1286 result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8);
1287
1288 return mod.intValue_big(ty, result_bigint.toConst());
1289}
1290
1291/// Asserts the value is an integer and not undefined.
1292/// Returns the number of bits the value requires to represent stored in twos complement form.
1293pub fn intBitCountTwosComp(self: Value, mod: *Module) usize {
1294 var buffer: BigIntSpace = undefined;
1295 const big_int = self.toBigInt(&buffer, mod);
1296 return big_int.bitCountTwosComp();
1297}
1298
1299/// Converts an integer or a float to a float. May result in a loss of information.
1300/// Caller can find out by equality checking the result against the operand.
1301pub fn floatCast(self: Value, dest_ty: Type, mod: *Module) !Value {
1302 const target = mod.getTarget();
1303 return Value.fromInterned((try mod.intern(.{ .float = .{
1304 .ty = dest_ty.toIntern(),
1305 .storage = switch (dest_ty.floatBits(target)) {
1306 16 => .{ .f16 = self.toFloat(f16, mod) },
1307 32 => .{ .f32 = self.toFloat(f32, mod) },
1308 64 => .{ .f64 = self.toFloat(f64, mod) },
1309 80 => .{ .f80 = self.toFloat(f80, mod) },
1310 128 => .{ .f128 = self.toFloat(f128, mod) },
1311 else => unreachable,
1312 },
1313 } })));
1314}
1315
1316/// Asserts the value is a float
1317pub fn floatHasFraction(self: Value, mod: *const Module) bool {
1318 return switch (mod.intern_pool.indexToKey(self.toIntern())) {
1319 .float => |float| switch (float.storage) {
1320 inline else => |x| @rem(x, 1) != 0,
1321 },
1322 else => unreachable,
1323 };
1324}
1325
1326pub fn orderAgainstZero(lhs: Value, mod: *Module) std.math.Order {
1327 return orderAgainstZeroAdvanced(lhs, mod, null) catch unreachable;
1328}
1329
1330pub fn orderAgainstZeroAdvanced(
1331 lhs: Value,
1332 mod: *Module,
1333 opt_sema: ?*Sema,
1334) Module.CompileError!std.math.Order {
1335 return switch (lhs.toIntern()) {
1336 .bool_false => .eq,
1337 .bool_true => .gt,
1338 else => switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1339 .ptr => |ptr| switch (ptr.addr) {
1340 .decl, .mut_decl, .comptime_field => .gt,
1341 .int => |int| Value.fromInterned(int).orderAgainstZeroAdvanced(mod, opt_sema),
1342 .elem => |elem| switch (try Value.fromInterned(elem.base).orderAgainstZeroAdvanced(mod, opt_sema)) {
1343 .lt => unreachable,
1344 .gt => .gt,
1345 .eq => if (elem.index == 0) .eq else .gt,
1346 },
1347 else => unreachable,
1348 },
1349 .int => |int| switch (int.storage) {
1350 .big_int => |big_int| big_int.orderAgainstScalar(0),
1351 inline .u64, .i64 => |x| std.math.order(x, 0),
1352 .lazy_align => .gt, // alignment is never 0
1353 .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsAdvanced(
1354 mod,
1355 false,
1356 if (opt_sema) |sema| .{ .sema = sema } else .eager,
1357 ) catch |err| switch (err) {
1358 error.NeedLazy => unreachable,
1359 else => |e| return e,
1360 }) .gt else .eq,
1361 },
1362 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroAdvanced(mod, opt_sema),
1363 .float => |float| switch (float.storage) {
1364 inline else => |x| std.math.order(x, 0),
1365 },
1366 else => unreachable,
1367 },
1368 };
1369}
1370
1371/// Asserts the value is comparable.
1372pub fn order(lhs: Value, rhs: Value, mod: *Module) std.math.Order {
1373 return orderAdvanced(lhs, rhs, mod, null) catch unreachable;
1374}
1375
1376/// Asserts the value is comparable.
1377/// If opt_sema is null then this function asserts things are resolved and cannot fail.
1378pub fn orderAdvanced(lhs: Value, rhs: Value, mod: *Module, opt_sema: ?*Sema) !std.math.Order {
1379 const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(mod, opt_sema);
1380 const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(mod, opt_sema);
1381 switch (lhs_against_zero) {
1382 .lt => if (rhs_against_zero != .lt) return .lt,
1383 .eq => return rhs_against_zero.invert(),
1384 .gt => {},
1385 }
1386 switch (rhs_against_zero) {
1387 .lt => if (lhs_against_zero != .lt) return .gt,
1388 .eq => return lhs_against_zero,
1389 .gt => {},
1390 }
1391
1392 if (lhs.isFloat(mod) or rhs.isFloat(mod)) {
1393 const lhs_f128 = lhs.toFloat(f128, mod);
1394 const rhs_f128 = rhs.toFloat(f128, mod);
1395 return std.math.order(lhs_f128, rhs_f128);
1396 }
1397
1398 var lhs_bigint_space: BigIntSpace = undefined;
1399 var rhs_bigint_space: BigIntSpace = undefined;
1400 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, mod, opt_sema);
1401 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, mod, opt_sema);
1402 return lhs_bigint.order(rhs_bigint);
1403}
1404
1405/// Asserts the value is comparable. Does not take a type parameter because it supports
1406/// comparisons between heterogeneous types.
1407pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, mod: *Module) bool {
1408 return compareHeteroAdvanced(lhs, op, rhs, mod, null) catch unreachable;
1409}
1410
1411pub fn compareHeteroAdvanced(
1412 lhs: Value,
1413 op: std.math.CompareOperator,
1414 rhs: Value,
1415 mod: *Module,
1416 opt_sema: ?*Sema,
1417) !bool {
1418 if (lhs.pointerDecl(mod)) |lhs_decl| {
1419 if (rhs.pointerDecl(mod)) |rhs_decl| {
1420 switch (op) {
1421 .eq => return lhs_decl == rhs_decl,
1422 .neq => return lhs_decl != rhs_decl,
1423 else => {},
1424 }
1425 } else {
1426 switch (op) {
1427 .eq => return false,
1428 .neq => return true,
1429 else => {},
1430 }
1431 }
1432 } else if (rhs.pointerDecl(mod)) |_| {
1433 switch (op) {
1434 .eq => return false,
1435 .neq => return true,
1436 else => {},
1437 }
1438 }
1439 return (try orderAdvanced(lhs, rhs, mod, opt_sema)).compare(op);
1440}
1441
1442/// Asserts the values are comparable. Both operands have type `ty`.
1443/// For vectors, returns true if comparison is true for ALL elements.
1444pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) !bool {
1445 if (ty.zigTypeTag(mod) == .Vector) {
1446 const scalar_ty = ty.scalarType(mod);
1447 for (0..ty.vectorLen(mod)) |i| {
1448 const lhs_elem = try lhs.elemValue(mod, i);
1449 const rhs_elem = try rhs.elemValue(mod, i);
1450 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, mod)) {
1451 return false;
1452 }
1453 }
1454 return true;
1455 }
1456 return compareScalar(lhs, op, rhs, ty, mod);
1457}
1458
1459/// Asserts the values are comparable. Both operands have type `ty`.
1460pub fn compareScalar(
1461 lhs: Value,
1462 op: std.math.CompareOperator,
1463 rhs: Value,
1464 ty: Type,
1465 mod: *Module,
1466) bool {
1467 return switch (op) {
1468 .eq => lhs.eql(rhs, ty, mod),
1469 .neq => !lhs.eql(rhs, ty, mod),
1470 else => compareHetero(lhs, op, rhs, mod),
1471 };
1472}
1473
1474/// Asserts the value is comparable.
1475/// For vectors, returns true if comparison is true for ALL elements.
1476///
1477/// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
1478pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, mod: *Module) bool {
1479 return compareAllWithZeroAdvancedExtra(lhs, op, mod, null) catch unreachable;
1480}
1481
1482pub fn compareAllWithZeroAdvanced(
1483 lhs: Value,
1484 op: std.math.CompareOperator,
1485 sema: *Sema,
1486) Module.CompileError!bool {
1487 return compareAllWithZeroAdvancedExtra(lhs, op, sema.mod, sema);
1488}
1489
1490pub fn compareAllWithZeroAdvancedExtra(
1491 lhs: Value,
1492 op: std.math.CompareOperator,
1493 mod: *Module,
1494 opt_sema: ?*Sema,
1495) Module.CompileError!bool {
1496 if (lhs.isInf(mod)) {
1497 switch (op) {
1498 .neq => return true,
1499 .eq => return false,
1500 .gt, .gte => return !lhs.isNegativeInf(mod),
1501 .lt, .lte => return lhs.isNegativeInf(mod),
1502 }
1503 }
1504
1505 switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1506 .float => |float| switch (float.storage) {
1507 inline else => |x| if (std.math.isNan(x)) return op == .neq,
1508 },
1509 .aggregate => |aggregate| return switch (aggregate.storage) {
1510 .bytes => |bytes| for (bytes) |byte| {
1511 if (!std.math.order(byte, 0).compare(op)) break false;
1512 } else true,
1513 .elems => |elems| for (elems) |elem| {
1514 if (!try Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema)) break false;
1515 } else true,
1516 .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema),
1517 },
1518 else => {},
1519 }
1520 return (try orderAgainstZeroAdvanced(lhs, mod, opt_sema)).compare(op);
1521}
1522
1523pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
1524 assert(mod.intern_pool.typeOf(a.toIntern()) == ty.toIntern());
1525 assert(mod.intern_pool.typeOf(b.toIntern()) == ty.toIntern());
1526 return a.toIntern() == b.toIntern();
1527}
1528
1529pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
1530 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1531 .slice => |slice| return Value.fromInterned(slice.ptr).isComptimeMutablePtr(mod),
1532 .ptr => |ptr| switch (ptr.addr) {
1533 .mut_decl, .comptime_field => true,
1534 .eu_payload, .opt_payload => |base_ptr| Value.fromInterned(base_ptr).isComptimeMutablePtr(mod),
1535 .elem, .field => |base_index| Value.fromInterned(base_index.base).isComptimeMutablePtr(mod),
1536 else => false,
1537 },
1538 else => false,
1539 };
1540}
1541
1542pub fn canMutateComptimeVarState(val: Value, mod: *Module) bool {
1543 return val.isComptimeMutablePtr(mod) or switch (val.toIntern()) {
1544 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1545 .error_union => |error_union| switch (error_union.val) {
1546 .err_name => false,
1547 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1548 },
1549 .ptr => |ptr| switch (ptr.addr) {
1550 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(mod),
1551 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).canMutateComptimeVarState(mod),
1552 .elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(mod),
1553 else => false,
1554 },
1555 .opt => |opt| switch (opt.val) {
1556 .none => false,
1557 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1558 },
1559 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| {
1560 if (Value.fromInterned(elem).canMutateComptimeVarState(mod)) break true;
1561 } else false,
1562 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(mod),
1563 else => false,
1564 },
1565 };
1566}
1567
1568/// Gets the decl referenced by this pointer. If the pointer does not point
1569/// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),
1570/// this function returns null.
1571pub fn pointerDecl(val: Value, mod: *Module) ?InternPool.DeclIndex {
1572 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1573 .variable => |variable| variable.decl,
1574 .extern_func => |extern_func| extern_func.decl,
1575 .func => |func| func.owner_decl,
1576 .ptr => |ptr| switch (ptr.addr) {
1577 .decl => |decl| decl,
1578 .mut_decl => |mut_decl| mut_decl.decl,
1579 else => null,
1580 },
1581 else => null,
1582 };
1583}
1584
1585pub const slice_ptr_index = 0;
1586pub const slice_len_index = 1;
1587
1588pub fn slicePtr(val: Value, mod: *Module) Value {
1589 return Value.fromInterned(mod.intern_pool.slicePtr(val.toIntern()));
1590}
1591
1592pub fn sliceLen(val: Value, mod: *Module) u64 {
1593 const ip = &mod.intern_pool;
1594 return switch (ip.indexToKey(val.toIntern())) {
1595 .ptr => |ptr| switch (ip.indexToKey(switch (ptr.addr) {
1596 .decl => |decl| mod.declPtr(decl).ty.toIntern(),
1597 .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(),
1598 .anon_decl => |anon_decl| ip.typeOf(anon_decl.val),
1599 .comptime_field => |comptime_field| ip.typeOf(comptime_field),
1600 else => unreachable,
1601 })) {
1602 .array_type => |array_type| array_type.len,
1603 else => 1,
1604 },
1605 .slice => |slice| Value.fromInterned(slice.len).toUnsignedInt(mod),
1606 else => unreachable,
1607 };
1608}
1609
1610/// Asserts the value is a single-item pointer to an array, or an array,
1611/// or an unknown-length pointer, and returns the element value at the index.
1612pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
1613 return (try val.maybeElemValue(mod, index)).?;
1614}
1615
1616/// Like `elemValue`, but returns `null` instead of asserting on failure.
1617pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value {
1618 return switch (val.ip_index) {
1619 .none => switch (val.tag()) {
1620 .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]),
1621 .repeated => val.castTag(.repeated).?.data,
1622 .aggregate => val.castTag(.aggregate).?.data[index],
1623 .slice => val.castTag(.slice).?.data.ptr.maybeElemValue(mod, index),
1624 else => null,
1625 },
1626 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1627 .undef => |ty| Value.fromInterned((try mod.intern(.{
1628 .undef = Type.fromInterned(ty).elemType2(mod).toIntern(),
1629 }))),
1630 .slice => |slice| return Value.fromInterned(slice.ptr).maybeElemValue(mod, index),
1631 .ptr => |ptr| switch (ptr.addr) {
1632 .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index),
1633 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValue(mod, index),
1634 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod))).maybeElemValue(mod, index),
1635 .int, .eu_payload => null,
1636 .opt_payload => |base| Value.fromInterned(base).maybeElemValue(mod, index),
1637 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValue(mod, index),
1638 .elem => |elem| Value.fromInterned(elem.base).maybeElemValue(mod, index + @as(usize, @intCast(elem.index))),
1639 .field => |field| if (Value.fromInterned(field.base).pointerDecl(mod)) |decl_index| {
1640 const base_decl = mod.declPtr(decl_index);
1641 const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index)));
1642 return field_val.maybeElemValue(mod, index);
1643 } else null,
1644 },
1645 .opt => |opt| Value.fromInterned(opt.val).maybeElemValue(mod, index),
1646 .aggregate => |aggregate| {
1647 const len = mod.intern_pool.aggregateTypeLen(aggregate.ty);
1648 if (index < len) return Value.fromInterned(switch (aggregate.storage) {
1649 .bytes => |bytes| try mod.intern(.{ .int = .{
1650 .ty = .u8_type,
1651 .storage = .{ .u64 = bytes[index] },
1652 } }),
1653 .elems => |elems| elems[index],
1654 .repeated_elem => |elem| elem,
1655 });
1656 assert(index == len);
1657 return Value.fromInterned(mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel);
1658 },
1659 else => null,
1660 },
1661 };
1662}
1663
1664pub fn isLazyAlign(val: Value, mod: *Module) bool {
1665 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1666 .int => |int| int.storage == .lazy_align,
1667 else => false,
1668 };
1669}
1670
1671pub fn isLazySize(val: Value, mod: *Module) bool {
1672 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1673 .int => |int| int.storage == .lazy_size,
1674 else => false,
1675 };
1676}
1677
1678pub fn isPtrToThreadLocal(val: Value, mod: *Module) bool {
1679 const backing_decl = mod.intern_pool.getBackingDecl(val.toIntern()).unwrap() orelse return false;
1680 const variable = mod.declPtr(backing_decl).getOwnedVariable(mod) orelse return false;
1681 return variable.is_threadlocal;
1682}
1683
1684// Asserts that the provided start/end are in-bounds.
1685pub fn sliceArray(
1686 val: Value,
1687 mod: *Module,
1688 arena: Allocator,
1689 start: usize,
1690 end: usize,
1691) error{OutOfMemory}!Value {
1692 // TODO: write something like getCoercedInts to avoid needing to dupe
1693 return switch (val.ip_index) {
1694 .none => switch (val.tag()) {
1695 .slice => val.castTag(.slice).?.data.ptr.sliceArray(mod, arena, start, end),
1696 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
1697 .repeated => val,
1698 .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),
1699 else => unreachable,
1700 },
1701 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1702 .ptr => |ptr| switch (ptr.addr) {
1703 .decl => |decl| try mod.declPtr(decl).val.sliceArray(mod, arena, start, end),
1704 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod)))
1705 .sliceArray(mod, arena, start, end),
1706 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
1707 .sliceArray(mod, arena, start, end),
1708 .elem => |elem| Value.fromInterned(elem.base)
1709 .sliceArray(mod, arena, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))),
1710 else => unreachable,
1711 },
1712 .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{
1713 .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) {
1714 .array_type => |array_type| try mod.arrayType(.{
1715 .len = @as(u32, @intCast(end - start)),
1716 .child = array_type.child,
1717 .sentinel = if (end == array_type.len) array_type.sentinel else .none,
1718 }),
1719 .vector_type => |vector_type| try mod.vectorType(.{
1720 .len = @as(u32, @intCast(end - start)),
1721 .child = vector_type.child,
1722 }),
1723 else => unreachable,
1724 }.toIntern(),
1725 .storage = switch (aggregate.storage) {
1726 .bytes => .{ .bytes = try arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) },
1727 .elems => .{ .elems = try arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) },
1728 .repeated_elem => |elem| .{ .repeated_elem = elem },
1729 },
1730 } }))),
1731 else => unreachable,
1732 },
1733 };
1734}
1735
1736pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value {
1737 return switch (val.ip_index) {
1738 .none => switch (val.tag()) {
1739 .aggregate => {
1740 const field_values = val.castTag(.aggregate).?.data;
1741 return field_values[index];
1742 },
1743 .@"union" => {
1744 const payload = val.castTag(.@"union").?.data;
1745 // TODO assert the tag is correct
1746 return payload.val;
1747 },
1748 else => unreachable,
1749 },
1750 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1751 .undef => |ty| Value.fromInterned((try mod.intern(.{
1752 .undef = Type.fromInterned(ty).structFieldType(index, mod).toIntern(),
1753 }))),
1754 .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) {
1755 .bytes => |bytes| try mod.intern(.{ .int = .{
1756 .ty = .u8_type,
1757 .storage = .{ .u64 = bytes[index] },
1758 } }),
1759 .elems => |elems| elems[index],
1760 .repeated_elem => |elem| elem,
1761 }),
1762 // TODO assert the tag is correct
1763 .un => |un| Value.fromInterned(un.val),
1764 else => unreachable,
1765 },
1766 };
1767}
1768
1769pub fn unionTag(val: Value, mod: *Module) ?Value {
1770 if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
1771 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1772 .undef, .enum_tag => val,
1773 .un => |un| if (un.tag != .none) Value.fromInterned(un.tag) else return null,
1774 else => unreachable,
1775 };
1776}
1777
1778pub fn unionValue(val: Value, mod: *Module) Value {
1779 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1780 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1781 .un => |un| Value.fromInterned(un.val),
1782 else => unreachable,
1783 };
1784}
1785
1786/// Returns a pointer to the element value at the index.
1787pub fn elemPtr(
1788 val: Value,
1789 elem_ptr_ty: Type,
1790 index: usize,
1791 mod: *Module,
1792) Allocator.Error!Value {
1793 const elem_ty = elem_ptr_ty.childType(mod);
1794 const ptr_val = switch (mod.intern_pool.indexToKey(val.toIntern())) {
1795 .slice => |slice| Value.fromInterned(slice.ptr),
1796 else => val,
1797 };
1798 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
1799 .ptr => |ptr| switch (ptr.addr) {
1800 .elem => |elem| if (Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).eql(elem_ty, mod))
1801 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1802 .ty = elem_ptr_ty.toIntern(),
1803 .addr = .{ .elem = .{
1804 .base = elem.base,
1805 .index = elem.index + index,
1806 } },
1807 } }))),
1808 else => {},
1809 },
1810 else => {},
1811 }
1812 var ptr_ty_key = mod.intern_pool.indexToKey(elem_ptr_ty.toIntern()).ptr_type;
1813 assert(ptr_ty_key.flags.size != .Slice);
1814 ptr_ty_key.flags.size = .Many;
1815 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1816 .ty = elem_ptr_ty.toIntern(),
1817 .addr = .{ .elem = .{
1818 .base = (try mod.getCoerced(ptr_val, try mod.ptrType(ptr_ty_key))).toIntern(),
1819 .index = index,
1820 } },
1821 } })));
1822}
1823
1824pub fn isUndef(val: Value, mod: *Module) bool {
1825 return val.ip_index != .none and mod.intern_pool.isUndef(val.toIntern());
1826}
1827
1828/// TODO: check for cases such as array that is not marked undef but all the element
1829/// values are marked undef, or struct that is not marked undef but all fields are marked
1830/// undef, etc.
1831pub fn isUndefDeep(val: Value, mod: *Module) bool {
1832 return val.isUndef(mod);
1833}
1834
1835/// Returns true if any value contained in `self` is undefined.
1836pub fn anyUndef(val: Value, mod: *Module) !bool {
1837 if (val.ip_index == .none) return false;
1838 return switch (val.toIntern()) {
1839 .undef => true,
1840 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1841 .undef => true,
1842 .simple_value => |v| v == .undefined,
1843 .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| {
1844 if (try (try val.elemValue(mod, idx)).anyUndef(mod)) break true;
1845 } else false,
1846 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
1847 const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
1848 if (try anyUndef(Value.fromInterned(elem), mod)) break true;
1849 } else false,
1850 else => false,
1851 },
1852 };
1853}
1854
1855/// Asserts the value is not undefined and not unreachable.
1856/// C pointers with an integer value of 0 are also considered null.
1857pub fn isNull(val: Value, mod: *Module) bool {
1858 return switch (val.toIntern()) {
1859 .undef => unreachable,
1860 .unreachable_value => unreachable,
1861 .null_value => true,
1862 else => return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1863 .undef => unreachable,
1864 .ptr => |ptr| switch (ptr.addr) {
1865 .int => {
1866 var buf: BigIntSpace = undefined;
1867 return val.toBigInt(&buf, mod).eqlZero();
1868 },
1869 else => false,
1870 },
1871 .opt => |opt| opt.val == .none,
1872 else => false,
1873 },
1874 };
1875}
1876
1877/// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.
1878pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTerminatedString {
1879 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1880 .err => |err| err.name.toOptional(),
1881 .error_union => |error_union| switch (error_union.val) {
1882 .err_name => |err_name| err_name.toOptional(),
1883 .payload => .none,
1884 },
1885 else => unreachable,
1886 };
1887}
1888
1889pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt {
1890 return if (getErrorName(val, mod).unwrap()) |err_name|
1891 @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?))
1892 else
1893 0;
1894}
1895
1896/// Assumes the type is an error union. Returns true if and only if the value is
1897/// the error union payload, not an error.
1898pub fn errorUnionIsPayload(val: Value, mod: *const Module) bool {
1899 return mod.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;
1900}
1901
1902/// Value of the optional, null if optional has no payload.
1903pub fn optionalValue(val: Value, mod: *const Module) ?Value {
1904 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1905 .opt => |opt| switch (opt.val) {
1906 .none => null,
1907 else => |payload| Value.fromInterned(payload),
1908 },
1909 .ptr => val,
1910 else => unreachable,
1911 };
1912}
1913
1914/// Valid for all types. Asserts the value is not undefined.
1915pub fn isFloat(self: Value, mod: *const Module) bool {
1916 return switch (self.toIntern()) {
1917 .undef => unreachable,
1918 else => switch (mod.intern_pool.indexToKey(self.toIntern())) {
1919 .undef => unreachable,
1920 .float => true,
1921 else => false,
1922 },
1923 };
1924}
1925
1926pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module) !Value {
1927 return floatFromIntAdvanced(val, arena, int_ty, float_ty, mod, null) catch |err| switch (err) {
1928 error.OutOfMemory => return error.OutOfMemory,
1929 else => unreachable,
1930 };
1931}
1932
1933pub fn floatFromIntAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1934 if (int_ty.zigTypeTag(mod) == .Vector) {
1935 const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(mod));
1936 const scalar_ty = float_ty.scalarType(mod);
1937 for (result_data, 0..) |*scalar, i| {
1938 const elem_val = try val.elemValue(mod, i);
1939 scalar.* = try (try floatFromIntScalar(elem_val, scalar_ty, mod, opt_sema)).intern(scalar_ty, mod);
1940 }
1941 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1942 .ty = float_ty.toIntern(),
1943 .storage = .{ .elems = result_data },
1944 } })));
1945 }
1946 return floatFromIntScalar(val, float_ty, mod, opt_sema);
1947}
1948
1949pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1950 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1951 .undef => try mod.undefValue(float_ty),
1952 .int => |int| switch (int.storage) {
1953 .big_int => |big_int| {
1954 const float = bigIntToFloat(big_int.limbs, big_int.positive);
1955 return mod.floatValue(float_ty, float);
1956 },
1957 inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, mod),
1958 .lazy_align => |ty| if (opt_sema) |sema| {
1959 return floatFromIntInner((try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0), float_ty, mod);
1960 } else {
1961 return floatFromIntInner(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0), float_ty, mod);
1962 },
1963 .lazy_size => |ty| if (opt_sema) |sema| {
1964 return floatFromIntInner((try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar, float_ty, mod);
1965 } else {
1966 return floatFromIntInner(Type.fromInterned(ty).abiSize(mod), float_ty, mod);
1967 },
1968 },
1969 else => unreachable,
1970 };
1971}
1972
1973fn floatFromIntInner(x: anytype, dest_ty: Type, mod: *Module) !Value {
1974 const target = mod.getTarget();
1975 const storage: InternPool.Key.Float.Storage = switch (dest_ty.floatBits(target)) {
1976 16 => .{ .f16 = @floatFromInt(x) },
1977 32 => .{ .f32 = @floatFromInt(x) },
1978 64 => .{ .f64 = @floatFromInt(x) },
1979 80 => .{ .f80 = @floatFromInt(x) },
1980 128 => .{ .f128 = @floatFromInt(x) },
1981 else => unreachable,
1982 };
1983 return Value.fromInterned((try mod.intern(.{ .float = .{
1984 .ty = dest_ty.toIntern(),
1985 .storage = storage,
1986 } })));
1987}
1988
1989fn calcLimbLenFloat(scalar: anytype) usize {
1990 if (scalar == 0) {
1991 return 1;
1992 }
1993
1994 const w_value = @abs(scalar);
1995 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).Int.bits) + 1;
1996}
1997
1998pub const OverflowArithmeticResult = struct {
1999 overflow_bit: Value,
2000 wrapped_result: Value,
2001};
2002
2003/// Supports (vectors of) integers only; asserts neither operand is undefined.
2004pub fn intAddSat(
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 intAddSatScalar(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 intAddSatScalar(lhs, rhs, ty, arena, mod);
2025}
2026
2027/// Supports integers only; asserts neither operand is undefined.
2028pub fn intAddSatScalar(
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 std.math.big.int.calcTwosCompLimbCount(info.bits),
2047 );
2048 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2049 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2050 return mod.intValue_big(ty, result_bigint.toConst());
2051}
2052
2053/// Supports (vectors of) integers only; asserts neither operand is undefined.
2054pub fn intSubSat(
2055 lhs: Value,
2056 rhs: Value,
2057 ty: Type,
2058 arena: Allocator,
2059 mod: *Module,
2060) !Value {
2061 if (ty.zigTypeTag(mod) == .Vector) {
2062 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2063 const scalar_ty = ty.scalarType(mod);
2064 for (result_data, 0..) |*scalar, i| {
2065 const lhs_elem = try lhs.elemValue(mod, i);
2066 const rhs_elem = try rhs.elemValue(mod, i);
2067 scalar.* = try (try intSubSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2068 }
2069 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2070 .ty = ty.toIntern(),
2071 .storage = .{ .elems = result_data },
2072 } })));
2073 }
2074 return intSubSatScalar(lhs, rhs, ty, arena, mod);
2075}
2076
2077/// Supports integers only; asserts neither operand is undefined.
2078pub fn intSubSatScalar(
2079 lhs: Value,
2080 rhs: Value,
2081 ty: Type,
2082 arena: Allocator,
2083 mod: *Module,
2084) !Value {
2085 assert(!lhs.isUndef(mod));
2086 assert(!rhs.isUndef(mod));
2087
2088 const info = ty.intInfo(mod);
2089
2090 var lhs_space: Value.BigIntSpace = undefined;
2091 var rhs_space: Value.BigIntSpace = undefined;
2092 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2093 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2094 const limbs = try arena.alloc(
2095 std.math.big.Limb,
2096 std.math.big.int.calcTwosCompLimbCount(info.bits),
2097 );
2098 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2099 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2100 return mod.intValue_big(ty, result_bigint.toConst());
2101}
2102
2103pub fn intMulWithOverflow(
2104 lhs: Value,
2105 rhs: Value,
2106 ty: Type,
2107 arena: Allocator,
2108 mod: *Module,
2109) !OverflowArithmeticResult {
2110 if (ty.zigTypeTag(mod) == .Vector) {
2111 const vec_len = ty.vectorLen(mod);
2112 const overflowed_data = try arena.alloc(InternPool.Index, vec_len);
2113 const result_data = try arena.alloc(InternPool.Index, vec_len);
2114 const scalar_ty = ty.scalarType(mod);
2115 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2116 const lhs_elem = try lhs.elemValue(mod, i);
2117 const rhs_elem = try rhs.elemValue(mod, i);
2118 const of_math_result = try intMulWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
2119 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2120 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2121 }
2122 return OverflowArithmeticResult{
2123 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2124 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2125 .storage = .{ .elems = overflowed_data },
2126 } }))),
2127 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2128 .ty = ty.toIntern(),
2129 .storage = .{ .elems = result_data },
2130 } }))),
2131 };
2132 }
2133 return intMulWithOverflowScalar(lhs, rhs, ty, arena, mod);
2134}
2135
2136pub fn intMulWithOverflowScalar(
2137 lhs: Value,
2138 rhs: Value,
2139 ty: Type,
2140 arena: Allocator,
2141 mod: *Module,
2142) !OverflowArithmeticResult {
2143 const info = ty.intInfo(mod);
2144
2145 var lhs_space: Value.BigIntSpace = undefined;
2146 var rhs_space: Value.BigIntSpace = undefined;
2147 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2148 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2149 const limbs = try arena.alloc(
2150 std.math.big.Limb,
2151 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2152 );
2153 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2154 const limbs_buffer = try arena.alloc(
2155 std.math.big.Limb,
2156 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2157 );
2158 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2159
2160 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2161 if (overflowed) {
2162 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2163 }
2164
2165 return OverflowArithmeticResult{
2166 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2167 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2168 };
2169}
2170
2171/// Supports both (vectors of) floats and ints; handles undefined scalars.
2172pub fn numberMulWrap(
2173 lhs: Value,
2174 rhs: Value,
2175 ty: Type,
2176 arena: Allocator,
2177 mod: *Module,
2178) !Value {
2179 if (ty.zigTypeTag(mod) == .Vector) {
2180 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2181 const scalar_ty = ty.scalarType(mod);
2182 for (result_data, 0..) |*scalar, i| {
2183 const lhs_elem = try lhs.elemValue(mod, i);
2184 const rhs_elem = try rhs.elemValue(mod, i);
2185 scalar.* = try (try numberMulWrapScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2186 }
2187 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2188 .ty = ty.toIntern(),
2189 .storage = .{ .elems = result_data },
2190 } })));
2191 }
2192 return numberMulWrapScalar(lhs, rhs, ty, arena, mod);
2193}
2194
2195/// Supports both floats and ints; handles undefined.
2196pub fn numberMulWrapScalar(
2197 lhs: Value,
2198 rhs: Value,
2199 ty: Type,
2200 arena: Allocator,
2201 mod: *Module,
2202) !Value {
2203 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
2204
2205 if (ty.zigTypeTag(mod) == .ComptimeInt) {
2206 return intMul(lhs, rhs, ty, undefined, arena, mod);
2207 }
2208
2209 if (ty.isAnyFloat()) {
2210 return floatMul(lhs, rhs, ty, arena, mod);
2211 }
2212
2213 const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, mod);
2214 return overflow_result.wrapped_result;
2215}
2216
2217/// Supports (vectors of) integers only; asserts neither operand is undefined.
2218pub fn intMulSat(
2219 lhs: Value,
2220 rhs: Value,
2221 ty: Type,
2222 arena: Allocator,
2223 mod: *Module,
2224) !Value {
2225 if (ty.zigTypeTag(mod) == .Vector) {
2226 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2227 const scalar_ty = ty.scalarType(mod);
2228 for (result_data, 0..) |*scalar, i| {
2229 const lhs_elem = try lhs.elemValue(mod, i);
2230 const rhs_elem = try rhs.elemValue(mod, i);
2231 scalar.* = try (try intMulSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2232 }
2233 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2234 .ty = ty.toIntern(),
2235 .storage = .{ .elems = result_data },
2236 } })));
2237 }
2238 return intMulSatScalar(lhs, rhs, ty, arena, mod);
2239}
2240
2241/// Supports (vectors of) integers only; asserts neither operand is undefined.
2242pub fn intMulSatScalar(
2243 lhs: Value,
2244 rhs: Value,
2245 ty: Type,
2246 arena: Allocator,
2247 mod: *Module,
2248) !Value {
2249 assert(!lhs.isUndef(mod));
2250 assert(!rhs.isUndef(mod));
2251
2252 const info = ty.intInfo(mod);
2253
2254 var lhs_space: Value.BigIntSpace = undefined;
2255 var rhs_space: Value.BigIntSpace = undefined;
2256 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2257 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2258 const limbs = try arena.alloc(
2259 std.math.big.Limb,
2260 @max(
2261 // For the saturate
2262 std.math.big.int.calcTwosCompLimbCount(info.bits),
2263 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2264 ),
2265 );
2266 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2267 const limbs_buffer = try arena.alloc(
2268 std.math.big.Limb,
2269 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2270 );
2271 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2272 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
2273 return mod.intValue_big(ty, result_bigint.toConst());
2274}
2275
2276/// Supports both floats and ints; handles undefined.
2277pub fn numberMax(lhs: Value, rhs: Value, mod: *Module) Value {
2278 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2279 if (lhs.isNan(mod)) return rhs;
2280 if (rhs.isNan(mod)) return lhs;
2281
2282 return switch (order(lhs, rhs, mod)) {
2283 .lt => rhs,
2284 .gt, .eq => lhs,
2285 };
2286}
2287
2288/// Supports both floats and ints; handles undefined.
2289pub fn numberMin(lhs: Value, rhs: Value, mod: *Module) Value {
2290 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2291 if (lhs.isNan(mod)) return rhs;
2292 if (rhs.isNan(mod)) return lhs;
2293
2294 return switch (order(lhs, rhs, mod)) {
2295 .lt => lhs,
2296 .gt, .eq => rhs,
2297 };
2298}
2299
2300/// operands must be (vectors of) integers; handles undefined scalars.
2301pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2302 if (ty.zigTypeTag(mod) == .Vector) {
2303 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2304 const scalar_ty = ty.scalarType(mod);
2305 for (result_data, 0..) |*scalar, i| {
2306 const elem_val = try val.elemValue(mod, i);
2307 scalar.* = try (try bitwiseNotScalar(elem_val, scalar_ty, arena, mod)).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 bitwiseNotScalar(val, ty, arena, mod);
2315}
2316
2317/// operands must be integers; handles undefined.
2318pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2319 if (val.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2320 if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());
2321
2322 const info = ty.intInfo(mod);
2323
2324 if (info.bits == 0) {
2325 return val;
2326 }
2327
2328 // TODO is this a performance issue? maybe we should try the operation without
2329 // resorting to BigInt first.
2330 var val_space: Value.BigIntSpace = undefined;
2331 const val_bigint = val.toBigInt(&val_space, mod);
2332 const limbs = try arena.alloc(
2333 std.math.big.Limb,
2334 std.math.big.int.calcTwosCompLimbCount(info.bits),
2335 );
2336
2337 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2338 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2339 return mod.intValue_big(ty, result_bigint.toConst());
2340}
2341
2342/// operands must be (vectors of) integers; handles undefined scalars.
2343pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2344 if (ty.zigTypeTag(mod) == .Vector) {
2345 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2346 const scalar_ty = ty.scalarType(mod);
2347 for (result_data, 0..) |*scalar, i| {
2348 const lhs_elem = try lhs.elemValue(mod, i);
2349 const rhs_elem = try rhs.elemValue(mod, i);
2350 scalar.* = try (try bitwiseAndScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2351 }
2352 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2353 .ty = ty.toIntern(),
2354 .storage = .{ .elems = result_data },
2355 } })));
2356 }
2357 return bitwiseAndScalar(lhs, rhs, ty, allocator, mod);
2358}
2359
2360/// operands must be integers; handles undefined.
2361pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2362 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2363 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
2364
2365 // TODO is this a performance issue? maybe we should try the operation without
2366 // resorting to BigInt first.
2367 var lhs_space: Value.BigIntSpace = undefined;
2368 var rhs_space: Value.BigIntSpace = undefined;
2369 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2370 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2371 const limbs = try arena.alloc(
2372 std.math.big.Limb,
2373 // + 1 for negatives
2374 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2375 );
2376 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2377 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2378 return mod.intValue_big(ty, result_bigint.toConst());
2379}
2380
2381/// operands must be (vectors of) integers; handles undefined scalars.
2382pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2383 if (ty.zigTypeTag(mod) == .Vector) {
2384 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2385 const scalar_ty = ty.scalarType(mod);
2386 for (result_data, 0..) |*scalar, i| {
2387 const lhs_elem = try lhs.elemValue(mod, i);
2388 const rhs_elem = try rhs.elemValue(mod, i);
2389 scalar.* = try (try bitwiseNandScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2390 }
2391 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2392 .ty = ty.toIntern(),
2393 .storage = .{ .elems = result_data },
2394 } })));
2395 }
2396 return bitwiseNandScalar(lhs, rhs, ty, arena, mod);
2397}
2398
2399/// operands must be integers; handles undefined.
2400pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2401 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2402 if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
2403
2404 const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
2405 const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);
2406 return bitwiseXor(anded, all_ones, ty, arena, mod);
2407}
2408
2409/// operands must be (vectors of) integers; handles undefined scalars.
2410pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2411 if (ty.zigTypeTag(mod) == .Vector) {
2412 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2413 const scalar_ty = ty.scalarType(mod);
2414 for (result_data, 0..) |*scalar, i| {
2415 const lhs_elem = try lhs.elemValue(mod, i);
2416 const rhs_elem = try rhs.elemValue(mod, i);
2417 scalar.* = try (try bitwiseOrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2418 }
2419 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2420 .ty = ty.toIntern(),
2421 .storage = .{ .elems = result_data },
2422 } })));
2423 }
2424 return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);
2425}
2426
2427/// operands must be integers; handles undefined.
2428pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2429 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2430 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
2431
2432 // TODO is this a performance issue? maybe we should try the operation without
2433 // resorting to BigInt first.
2434 var lhs_space: Value.BigIntSpace = undefined;
2435 var rhs_space: Value.BigIntSpace = undefined;
2436 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2437 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2438 const limbs = try arena.alloc(
2439 std.math.big.Limb,
2440 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2441 );
2442 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2443 result_bigint.bitOr(lhs_bigint, rhs_bigint);
2444 return mod.intValue_big(ty, result_bigint.toConst());
2445}
2446
2447/// operands must be (vectors of) integers; handles undefined scalars.
2448pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2449 if (ty.zigTypeTag(mod) == .Vector) {
2450 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2451 const scalar_ty = ty.scalarType(mod);
2452 for (result_data, 0..) |*scalar, i| {
2453 const lhs_elem = try lhs.elemValue(mod, i);
2454 const rhs_elem = try rhs.elemValue(mod, i);
2455 scalar.* = try (try bitwiseXorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2456 }
2457 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2458 .ty = ty.toIntern(),
2459 .storage = .{ .elems = result_data },
2460 } })));
2461 }
2462 return bitwiseXorScalar(lhs, rhs, ty, allocator, mod);
2463}
2464
2465/// operands must be integers; handles undefined.
2466pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2467 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2468 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() != rhs.toBool());
2469
2470 // TODO is this a performance issue? maybe we should try the operation without
2471 // resorting to BigInt first.
2472 var lhs_space: Value.BigIntSpace = undefined;
2473 var rhs_space: Value.BigIntSpace = undefined;
2474 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2475 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2476 const limbs = try arena.alloc(
2477 std.math.big.Limb,
2478 // + 1 for negatives
2479 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2480 );
2481 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2482 result_bigint.bitXor(lhs_bigint, rhs_bigint);
2483 return mod.intValue_big(ty, result_bigint.toConst());
2484}
2485
2486/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2487/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2488pub fn intDiv(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2489 var overflow: usize = undefined;
2490 return intDivInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2491 error.Overflow => {
2492 const is_vec = ty.isVector(mod);
2493 overflow_idx.* = if (is_vec) overflow else 0;
2494 const safe_ty = if (is_vec) try mod.vectorType(.{
2495 .len = ty.vectorLen(mod),
2496 .child = .comptime_int_type,
2497 }) else Type.comptime_int;
2498 return intDivInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2499 error.Overflow => unreachable,
2500 else => |e| return e,
2501 };
2502 },
2503 else => |e| return e,
2504 };
2505}
2506
2507fn intDivInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2508 if (ty.zigTypeTag(mod) == .Vector) {
2509 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2510 const scalar_ty = ty.scalarType(mod);
2511 for (result_data, 0..) |*scalar, i| {
2512 const lhs_elem = try lhs.elemValue(mod, i);
2513 const rhs_elem = try rhs.elemValue(mod, i);
2514 const val = intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2515 error.Overflow => {
2516 overflow_idx.* = i;
2517 return error.Overflow;
2518 },
2519 else => |e| return e,
2520 };
2521 scalar.* = try val.intern(scalar_ty, mod);
2522 }
2523 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2524 .ty = ty.toIntern(),
2525 .storage = .{ .elems = result_data },
2526 } })));
2527 }
2528 return intDivScalar(lhs, rhs, ty, allocator, mod);
2529}
2530
2531pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2532 // TODO is this a performance issue? maybe we should try the operation without
2533 // resorting to BigInt first.
2534 var lhs_space: Value.BigIntSpace = undefined;
2535 var rhs_space: Value.BigIntSpace = undefined;
2536 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2537 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2538 const limbs_q = try allocator.alloc(
2539 std.math.big.Limb,
2540 lhs_bigint.limbs.len,
2541 );
2542 const limbs_r = try allocator.alloc(
2543 std.math.big.Limb,
2544 rhs_bigint.limbs.len,
2545 );
2546 const limbs_buffer = try allocator.alloc(
2547 std.math.big.Limb,
2548 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2549 );
2550 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2551 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2552 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2553 if (ty.toIntern() != .comptime_int_type) {
2554 const info = ty.intInfo(mod);
2555 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
2556 return error.Overflow;
2557 }
2558 }
2559 return mod.intValue_big(ty, result_q.toConst());
2560}
2561
2562pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2563 if (ty.zigTypeTag(mod) == .Vector) {
2564 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2565 const scalar_ty = ty.scalarType(mod);
2566 for (result_data, 0..) |*scalar, i| {
2567 const lhs_elem = try lhs.elemValue(mod, i);
2568 const rhs_elem = try rhs.elemValue(mod, i);
2569 scalar.* = try (try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2570 }
2571 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2572 .ty = ty.toIntern(),
2573 .storage = .{ .elems = result_data },
2574 } })));
2575 }
2576 return intDivFloorScalar(lhs, rhs, ty, allocator, mod);
2577}
2578
2579pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2580 // TODO is this a performance issue? maybe we should try the operation without
2581 // resorting to BigInt first.
2582 var lhs_space: Value.BigIntSpace = undefined;
2583 var rhs_space: Value.BigIntSpace = undefined;
2584 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2585 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2586 const limbs_q = try allocator.alloc(
2587 std.math.big.Limb,
2588 lhs_bigint.limbs.len,
2589 );
2590 const limbs_r = try allocator.alloc(
2591 std.math.big.Limb,
2592 rhs_bigint.limbs.len,
2593 );
2594 const limbs_buffer = try allocator.alloc(
2595 std.math.big.Limb,
2596 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2597 );
2598 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2599 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2600 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2601 return mod.intValue_big(ty, result_q.toConst());
2602}
2603
2604pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2605 if (ty.zigTypeTag(mod) == .Vector) {
2606 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2607 const scalar_ty = ty.scalarType(mod);
2608 for (result_data, 0..) |*scalar, i| {
2609 const lhs_elem = try lhs.elemValue(mod, i);
2610 const rhs_elem = try rhs.elemValue(mod, i);
2611 scalar.* = try (try intModScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2612 }
2613 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2614 .ty = ty.toIntern(),
2615 .storage = .{ .elems = result_data },
2616 } })));
2617 }
2618 return intModScalar(lhs, rhs, ty, allocator, mod);
2619}
2620
2621pub fn intModScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2622 // TODO is this a performance issue? maybe we should try the operation without
2623 // resorting to BigInt first.
2624 var lhs_space: Value.BigIntSpace = undefined;
2625 var rhs_space: Value.BigIntSpace = undefined;
2626 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2627 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2628 const limbs_q = try allocator.alloc(
2629 std.math.big.Limb,
2630 lhs_bigint.limbs.len,
2631 );
2632 const limbs_r = try allocator.alloc(
2633 std.math.big.Limb,
2634 rhs_bigint.limbs.len,
2635 );
2636 const limbs_buffer = try allocator.alloc(
2637 std.math.big.Limb,
2638 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2639 );
2640 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2641 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2642 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2643 return mod.intValue_big(ty, result_r.toConst());
2644}
2645
2646/// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
2647pub fn isNan(val: Value, mod: *const Module) bool {
2648 if (val.ip_index == .none) return false;
2649 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2650 .float => |float| switch (float.storage) {
2651 inline else => |x| std.math.isNan(x),
2652 },
2653 else => false,
2654 };
2655}
2656
2657/// Returns true if the value is a floating point type and is infinite. Returns false otherwise.
2658pub fn isInf(val: Value, mod: *const Module) bool {
2659 if (val.ip_index == .none) return false;
2660 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2661 .float => |float| switch (float.storage) {
2662 inline else => |x| std.math.isInf(x),
2663 },
2664 else => false,
2665 };
2666}
2667
2668pub fn isNegativeInf(val: Value, mod: *const Module) bool {
2669 if (val.ip_index == .none) return false;
2670 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2671 .float => |float| switch (float.storage) {
2672 inline else => |x| std.math.isNegativeInf(x),
2673 },
2674 else => false,
2675 };
2676}
2677
2678pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2679 if (float_type.zigTypeTag(mod) == .Vector) {
2680 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2681 const scalar_ty = float_type.scalarType(mod);
2682 for (result_data, 0..) |*scalar, i| {
2683 const lhs_elem = try lhs.elemValue(mod, i);
2684 const rhs_elem = try rhs.elemValue(mod, i);
2685 scalar.* = try (try floatRemScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2686 }
2687 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2688 .ty = float_type.toIntern(),
2689 .storage = .{ .elems = result_data },
2690 } })));
2691 }
2692 return floatRemScalar(lhs, rhs, float_type, mod);
2693}
2694
2695pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2696 const target = mod.getTarget();
2697 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2698 16 => .{ .f16 = @rem(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2699 32 => .{ .f32 = @rem(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2700 64 => .{ .f64 = @rem(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2701 80 => .{ .f80 = @rem(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2702 128 => .{ .f128 = @rem(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2703 else => unreachable,
2704 };
2705 return Value.fromInterned((try mod.intern(.{ .float = .{
2706 .ty = float_type.toIntern(),
2707 .storage = storage,
2708 } })));
2709}
2710
2711pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2712 if (float_type.zigTypeTag(mod) == .Vector) {
2713 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2714 const scalar_ty = float_type.scalarType(mod);
2715 for (result_data, 0..) |*scalar, i| {
2716 const lhs_elem = try lhs.elemValue(mod, i);
2717 const rhs_elem = try rhs.elemValue(mod, i);
2718 scalar.* = try (try floatModScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2719 }
2720 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2721 .ty = float_type.toIntern(),
2722 .storage = .{ .elems = result_data },
2723 } })));
2724 }
2725 return floatModScalar(lhs, rhs, float_type, mod);
2726}
2727
2728pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2729 const target = mod.getTarget();
2730 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2731 16 => .{ .f16 = @mod(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2732 32 => .{ .f32 = @mod(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2733 64 => .{ .f64 = @mod(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2734 80 => .{ .f80 = @mod(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2735 128 => .{ .f128 = @mod(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2736 else => unreachable,
2737 };
2738 return Value.fromInterned((try mod.intern(.{ .float = .{
2739 .ty = float_type.toIntern(),
2740 .storage = storage,
2741 } })));
2742}
2743
2744/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2745/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2746pub fn intMul(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2747 var overflow: usize = undefined;
2748 return intMulInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2749 error.Overflow => {
2750 const is_vec = ty.isVector(mod);
2751 overflow_idx.* = if (is_vec) overflow else 0;
2752 const safe_ty = if (is_vec) try mod.vectorType(.{
2753 .len = ty.vectorLen(mod),
2754 .child = .comptime_int_type,
2755 }) else Type.comptime_int;
2756 return intMulInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2757 error.Overflow => unreachable,
2758 else => |e| return e,
2759 };
2760 },
2761 else => |e| return e,
2762 };
2763}
2764
2765fn intMulInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2766 if (ty.zigTypeTag(mod) == .Vector) {
2767 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2768 const scalar_ty = ty.scalarType(mod);
2769 for (result_data, 0..) |*scalar, i| {
2770 const lhs_elem = try lhs.elemValue(mod, i);
2771 const rhs_elem = try rhs.elemValue(mod, i);
2772 const val = intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2773 error.Overflow => {
2774 overflow_idx.* = i;
2775 return error.Overflow;
2776 },
2777 else => |e| return e,
2778 };
2779 scalar.* = try val.intern(scalar_ty, mod);
2780 }
2781 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2782 .ty = ty.toIntern(),
2783 .storage = .{ .elems = result_data },
2784 } })));
2785 }
2786 return intMulScalar(lhs, rhs, ty, allocator, mod);
2787}
2788
2789pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2790 if (ty.toIntern() != .comptime_int_type) {
2791 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2792 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
2793 return res.wrapped_result;
2794 }
2795 // TODO is this a performance issue? maybe we should try the operation without
2796 // resorting to BigInt first.
2797 var lhs_space: Value.BigIntSpace = undefined;
2798 var rhs_space: Value.BigIntSpace = undefined;
2799 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2800 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2801 const limbs = try allocator.alloc(
2802 std.math.big.Limb,
2803 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2804 );
2805 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2806 const limbs_buffer = try allocator.alloc(
2807 std.math.big.Limb,
2808 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2809 );
2810 defer allocator.free(limbs_buffer);
2811 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
2812 return mod.intValue_big(ty, result_bigint.toConst());
2813}
2814
2815pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {
2816 if (ty.zigTypeTag(mod) == .Vector) {
2817 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2818 const scalar_ty = ty.scalarType(mod);
2819 for (result_data, 0..) |*scalar, i| {
2820 const elem_val = try val.elemValue(mod, i);
2821 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, bits, mod)).intern(scalar_ty, mod);
2822 }
2823 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2824 .ty = ty.toIntern(),
2825 .storage = .{ .elems = result_data },
2826 } })));
2827 }
2828 return intTruncScalar(val, ty, allocator, signedness, bits, mod);
2829}
2830
2831/// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`.
2832pub fn intTruncBitsAsValue(
2833 val: Value,
2834 ty: Type,
2835 allocator: Allocator,
2836 signedness: std.builtin.Signedness,
2837 bits: Value,
2838 mod: *Module,
2839) !Value {
2840 if (ty.zigTypeTag(mod) == .Vector) {
2841 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2842 const scalar_ty = ty.scalarType(mod);
2843 for (result_data, 0..) |*scalar, i| {
2844 const elem_val = try val.elemValue(mod, i);
2845 const bits_elem = try bits.elemValue(mod, i);
2846 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @as(u16, @intCast(bits_elem.toUnsignedInt(mod))), mod)).intern(scalar_ty, mod);
2847 }
2848 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2849 .ty = ty.toIntern(),
2850 .storage = .{ .elems = result_data },
2851 } })));
2852 }
2853 return intTruncScalar(val, ty, allocator, signedness, @as(u16, @intCast(bits.toUnsignedInt(mod))), mod);
2854}
2855
2856pub fn intTruncScalar(
2857 val: Value,
2858 ty: Type,
2859 allocator: Allocator,
2860 signedness: std.builtin.Signedness,
2861 bits: u16,
2862 mod: *Module,
2863) !Value {
2864 if (bits == 0) return mod.intValue(ty, 0);
2865
2866 var val_space: Value.BigIntSpace = undefined;
2867 const val_bigint = val.toBigInt(&val_space, mod);
2868
2869 const limbs = try allocator.alloc(
2870 std.math.big.Limb,
2871 std.math.big.int.calcTwosCompLimbCount(bits),
2872 );
2873 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2874
2875 result_bigint.truncate(val_bigint, signedness, bits);
2876 return mod.intValue_big(ty, result_bigint.toConst());
2877}
2878
2879pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2880 if (ty.zigTypeTag(mod) == .Vector) {
2881 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2882 const scalar_ty = ty.scalarType(mod);
2883 for (result_data, 0..) |*scalar, i| {
2884 const lhs_elem = try lhs.elemValue(mod, i);
2885 const rhs_elem = try rhs.elemValue(mod, i);
2886 scalar.* = try (try shlScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2887 }
2888 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2889 .ty = ty.toIntern(),
2890 .storage = .{ .elems = result_data },
2891 } })));
2892 }
2893 return shlScalar(lhs, rhs, ty, allocator, mod);
2894}
2895
2896pub fn shlScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2897 // TODO is this a performance issue? maybe we should try the operation without
2898 // resorting to BigInt first.
2899 var lhs_space: Value.BigIntSpace = undefined;
2900 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2901 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2902 const limbs = try allocator.alloc(
2903 std.math.big.Limb,
2904 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2905 );
2906 var result_bigint = BigIntMutable{
2907 .limbs = limbs,
2908 .positive = undefined,
2909 .len = undefined,
2910 };
2911 result_bigint.shiftLeft(lhs_bigint, shift);
2912 if (ty.toIntern() != .comptime_int_type) {
2913 const int_info = ty.intInfo(mod);
2914 result_bigint.truncate(result_bigint.toConst(), int_info.signedness, int_info.bits);
2915 }
2916
2917 return mod.intValue_big(ty, result_bigint.toConst());
2918}
2919
2920pub fn shlWithOverflow(
2921 lhs: Value,
2922 rhs: Value,
2923 ty: Type,
2924 allocator: Allocator,
2925 mod: *Module,
2926) !OverflowArithmeticResult {
2927 if (ty.zigTypeTag(mod) == .Vector) {
2928 const vec_len = ty.vectorLen(mod);
2929 const overflowed_data = try allocator.alloc(InternPool.Index, vec_len);
2930 const result_data = try allocator.alloc(InternPool.Index, vec_len);
2931 const scalar_ty = ty.scalarType(mod);
2932 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2933 const lhs_elem = try lhs.elemValue(mod, i);
2934 const rhs_elem = try rhs.elemValue(mod, i);
2935 const of_math_result = try shlWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
2936 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2937 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2938 }
2939 return OverflowArithmeticResult{
2940 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2941 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2942 .storage = .{ .elems = overflowed_data },
2943 } }))),
2944 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2945 .ty = ty.toIntern(),
2946 .storage = .{ .elems = result_data },
2947 } }))),
2948 };
2949 }
2950 return shlWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2951}
2952
2953pub fn shlWithOverflowScalar(
2954 lhs: Value,
2955 rhs: Value,
2956 ty: Type,
2957 allocator: Allocator,
2958 mod: *Module,
2959) !OverflowArithmeticResult {
2960 const info = ty.intInfo(mod);
2961 var lhs_space: Value.BigIntSpace = undefined;
2962 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2963 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2964 const limbs = try allocator.alloc(
2965 std.math.big.Limb,
2966 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2967 );
2968 var result_bigint = BigIntMutable{
2969 .limbs = limbs,
2970 .positive = undefined,
2971 .len = undefined,
2972 };
2973 result_bigint.shiftLeft(lhs_bigint, shift);
2974 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2975 if (overflowed) {
2976 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2977 }
2978 return OverflowArithmeticResult{
2979 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2980 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2981 };
2982}
2983
2984pub fn shlSat(
2985 lhs: Value,
2986 rhs: Value,
2987 ty: Type,
2988 arena: Allocator,
2989 mod: *Module,
2990) !Value {
2991 if (ty.zigTypeTag(mod) == .Vector) {
2992 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2993 const scalar_ty = ty.scalarType(mod);
2994 for (result_data, 0..) |*scalar, i| {
2995 const lhs_elem = try lhs.elemValue(mod, i);
2996 const rhs_elem = try rhs.elemValue(mod, i);
2997 scalar.* = try (try shlSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2998 }
2999 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3000 .ty = ty.toIntern(),
3001 .storage = .{ .elems = result_data },
3002 } })));
3003 }
3004 return shlSatScalar(lhs, rhs, ty, arena, mod);
3005}
3006
3007pub fn shlSatScalar(
3008 lhs: Value,
3009 rhs: Value,
3010 ty: Type,
3011 arena: Allocator,
3012 mod: *Module,
3013) !Value {
3014 // TODO is this a performance issue? maybe we should try the operation without
3015 // resorting to BigInt first.
3016 const info = ty.intInfo(mod);
3017
3018 var lhs_space: Value.BigIntSpace = undefined;
3019 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3020 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3021 const limbs = try arena.alloc(
3022 std.math.big.Limb,
3023 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,
3024 );
3025 var result_bigint = BigIntMutable{
3026 .limbs = limbs,
3027 .positive = undefined,
3028 .len = undefined,
3029 };
3030 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
3031 return mod.intValue_big(ty, result_bigint.toConst());
3032}
3033
3034pub fn shlTrunc(
3035 lhs: Value,
3036 rhs: Value,
3037 ty: Type,
3038 arena: Allocator,
3039 mod: *Module,
3040) !Value {
3041 if (ty.zigTypeTag(mod) == .Vector) {
3042 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3043 const scalar_ty = ty.scalarType(mod);
3044 for (result_data, 0..) |*scalar, i| {
3045 const lhs_elem = try lhs.elemValue(mod, i);
3046 const rhs_elem = try rhs.elemValue(mod, i);
3047 scalar.* = try (try shlTruncScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
3048 }
3049 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3050 .ty = ty.toIntern(),
3051 .storage = .{ .elems = result_data },
3052 } })));
3053 }
3054 return shlTruncScalar(lhs, rhs, ty, arena, mod);
3055}
3056
3057pub fn shlTruncScalar(
3058 lhs: Value,
3059 rhs: Value,
3060 ty: Type,
3061 arena: Allocator,
3062 mod: *Module,
3063) !Value {
3064 const shifted = try lhs.shl(rhs, ty, arena, mod);
3065 const int_info = ty.intInfo(mod);
3066 const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits, mod);
3067 return truncated;
3068}
3069
3070pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3071 if (ty.zigTypeTag(mod) == .Vector) {
3072 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
3073 const scalar_ty = ty.scalarType(mod);
3074 for (result_data, 0..) |*scalar, i| {
3075 const lhs_elem = try lhs.elemValue(mod, i);
3076 const rhs_elem = try rhs.elemValue(mod, i);
3077 scalar.* = try (try shrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
3078 }
3079 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3080 .ty = ty.toIntern(),
3081 .storage = .{ .elems = result_data },
3082 } })));
3083 }
3084 return shrScalar(lhs, rhs, ty, allocator, mod);
3085}
3086
3087pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3088 // TODO is this a performance issue? maybe we should try the operation without
3089 // resorting to BigInt first.
3090 var lhs_space: Value.BigIntSpace = undefined;
3091 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3092 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3093
3094 const result_limbs = lhs_bigint.limbs.len -| (shift / (@sizeOf(std.math.big.Limb) * 8));
3095 if (result_limbs == 0) {
3096 // The shift is enough to remove all the bits from the number, which means the
3097 // result is 0 or -1 depending on the sign.
3098 if (lhs_bigint.positive) {
3099 return mod.intValue(ty, 0);
3100 } else {
3101 return mod.intValue(ty, -1);
3102 }
3103 }
3104
3105 const limbs = try allocator.alloc(
3106 std.math.big.Limb,
3107 result_limbs,
3108 );
3109 var result_bigint = BigIntMutable{
3110 .limbs = limbs,
3111 .positive = undefined,
3112 .len = undefined,
3113 };
3114 result_bigint.shiftRight(lhs_bigint, shift);
3115 return mod.intValue_big(ty, result_bigint.toConst());
3116}
3117
3118pub fn floatNeg(
3119 val: Value,
3120 float_type: Type,
3121 arena: Allocator,
3122 mod: *Module,
3123) !Value {
3124 if (float_type.zigTypeTag(mod) == .Vector) {
3125 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3126 const scalar_ty = float_type.scalarType(mod);
3127 for (result_data, 0..) |*scalar, i| {
3128 const elem_val = try val.elemValue(mod, i);
3129 scalar.* = try (try floatNegScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3130 }
3131 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3132 .ty = float_type.toIntern(),
3133 .storage = .{ .elems = result_data },
3134 } })));
3135 }
3136 return floatNegScalar(val, float_type, mod);
3137}
3138
3139pub fn floatNegScalar(
3140 val: Value,
3141 float_type: Type,
3142 mod: *Module,
3143) !Value {
3144 const target = mod.getTarget();
3145 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3146 16 => .{ .f16 = -val.toFloat(f16, mod) },
3147 32 => .{ .f32 = -val.toFloat(f32, mod) },
3148 64 => .{ .f64 = -val.toFloat(f64, mod) },
3149 80 => .{ .f80 = -val.toFloat(f80, mod) },
3150 128 => .{ .f128 = -val.toFloat(f128, mod) },
3151 else => unreachable,
3152 };
3153 return Value.fromInterned((try mod.intern(.{ .float = .{
3154 .ty = float_type.toIntern(),
3155 .storage = storage,
3156 } })));
3157}
3158
3159pub fn floatAdd(
3160 lhs: Value,
3161 rhs: Value,
3162 float_type: Type,
3163 arena: Allocator,
3164 mod: *Module,
3165) !Value {
3166 if (float_type.zigTypeTag(mod) == .Vector) {
3167 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3168 const scalar_ty = float_type.scalarType(mod);
3169 for (result_data, 0..) |*scalar, i| {
3170 const lhs_elem = try lhs.elemValue(mod, i);
3171 const rhs_elem = try rhs.elemValue(mod, i);
3172 scalar.* = try (try floatAddScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3173 }
3174 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3175 .ty = float_type.toIntern(),
3176 .storage = .{ .elems = result_data },
3177 } })));
3178 }
3179 return floatAddScalar(lhs, rhs, float_type, mod);
3180}
3181
3182pub fn floatAddScalar(
3183 lhs: Value,
3184 rhs: Value,
3185 float_type: Type,
3186 mod: *Module,
3187) !Value {
3188 const target = mod.getTarget();
3189 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3190 16 => .{ .f16 = lhs.toFloat(f16, mod) + rhs.toFloat(f16, mod) },
3191 32 => .{ .f32 = lhs.toFloat(f32, mod) + rhs.toFloat(f32, mod) },
3192 64 => .{ .f64 = lhs.toFloat(f64, mod) + rhs.toFloat(f64, mod) },
3193 80 => .{ .f80 = lhs.toFloat(f80, mod) + rhs.toFloat(f80, mod) },
3194 128 => .{ .f128 = lhs.toFloat(f128, mod) + rhs.toFloat(f128, mod) },
3195 else => unreachable,
3196 };
3197 return Value.fromInterned((try mod.intern(.{ .float = .{
3198 .ty = float_type.toIntern(),
3199 .storage = storage,
3200 } })));
3201}
3202
3203pub fn floatSub(
3204 lhs: Value,
3205 rhs: Value,
3206 float_type: Type,
3207 arena: Allocator,
3208 mod: *Module,
3209) !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 lhs_elem = try lhs.elemValue(mod, i);
3215 const rhs_elem = try rhs.elemValue(mod, i);
3216 scalar.* = try (try floatSubScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3217 }
3218 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3219 .ty = float_type.toIntern(),
3220 .storage = .{ .elems = result_data },
3221 } })));
3222 }
3223 return floatSubScalar(lhs, rhs, float_type, mod);
3224}
3225
3226pub fn floatSubScalar(
3227 lhs: Value,
3228 rhs: Value,
3229 float_type: Type,
3230 mod: *Module,
3231) !Value {
3232 const target = mod.getTarget();
3233 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3234 16 => .{ .f16 = lhs.toFloat(f16, mod) - rhs.toFloat(f16, mod) },
3235 32 => .{ .f32 = lhs.toFloat(f32, mod) - rhs.toFloat(f32, mod) },
3236 64 => .{ .f64 = lhs.toFloat(f64, mod) - rhs.toFloat(f64, mod) },
3237 80 => .{ .f80 = lhs.toFloat(f80, mod) - rhs.toFloat(f80, mod) },
3238 128 => .{ .f128 = lhs.toFloat(f128, mod) - rhs.toFloat(f128, mod) },
3239 else => unreachable,
3240 };
3241 return Value.fromInterned((try mod.intern(.{ .float = .{
3242 .ty = float_type.toIntern(),
3243 .storage = storage,
3244 } })));
3245}
3246
3247pub fn floatDiv(
3248 lhs: Value,
3249 rhs: Value,
3250 float_type: Type,
3251 arena: Allocator,
3252 mod: *Module,
3253) !Value {
3254 if (float_type.zigTypeTag(mod) == .Vector) {
3255 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3256 const scalar_ty = float_type.scalarType(mod);
3257 for (result_data, 0..) |*scalar, i| {
3258 const lhs_elem = try lhs.elemValue(mod, i);
3259 const rhs_elem = try rhs.elemValue(mod, i);
3260 scalar.* = try (try floatDivScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3261 }
3262 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3263 .ty = float_type.toIntern(),
3264 .storage = .{ .elems = result_data },
3265 } })));
3266 }
3267 return floatDivScalar(lhs, rhs, float_type, mod);
3268}
3269
3270pub fn floatDivScalar(
3271 lhs: Value,
3272 rhs: Value,
3273 float_type: Type,
3274 mod: *Module,
3275) !Value {
3276 const target = mod.getTarget();
3277 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3278 16 => .{ .f16 = lhs.toFloat(f16, mod) / rhs.toFloat(f16, mod) },
3279 32 => .{ .f32 = lhs.toFloat(f32, mod) / rhs.toFloat(f32, mod) },
3280 64 => .{ .f64 = lhs.toFloat(f64, mod) / rhs.toFloat(f64, mod) },
3281 80 => .{ .f80 = lhs.toFloat(f80, mod) / rhs.toFloat(f80, mod) },
3282 128 => .{ .f128 = lhs.toFloat(f128, mod) / rhs.toFloat(f128, mod) },
3283 else => unreachable,
3284 };
3285 return Value.fromInterned((try mod.intern(.{ .float = .{
3286 .ty = float_type.toIntern(),
3287 .storage = storage,
3288 } })));
3289}
3290
3291pub fn floatDivFloor(
3292 lhs: Value,
3293 rhs: Value,
3294 float_type: Type,
3295 arena: Allocator,
3296 mod: *Module,
3297) !Value {
3298 if (float_type.zigTypeTag(mod) == .Vector) {
3299 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3300 const scalar_ty = float_type.scalarType(mod);
3301 for (result_data, 0..) |*scalar, i| {
3302 const lhs_elem = try lhs.elemValue(mod, i);
3303 const rhs_elem = try rhs.elemValue(mod, i);
3304 scalar.* = try (try floatDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3305 }
3306 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3307 .ty = float_type.toIntern(),
3308 .storage = .{ .elems = result_data },
3309 } })));
3310 }
3311 return floatDivFloorScalar(lhs, rhs, float_type, mod);
3312}
3313
3314pub fn floatDivFloorScalar(
3315 lhs: Value,
3316 rhs: Value,
3317 float_type: Type,
3318 mod: *Module,
3319) !Value {
3320 const target = mod.getTarget();
3321 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3322 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3323 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3324 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3325 80 => .{ .f80 = @divFloor(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3326 128 => .{ .f128 = @divFloor(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3327 else => unreachable,
3328 };
3329 return Value.fromInterned((try mod.intern(.{ .float = .{
3330 .ty = float_type.toIntern(),
3331 .storage = storage,
3332 } })));
3333}
3334
3335pub fn floatDivTrunc(
3336 lhs: Value,
3337 rhs: Value,
3338 float_type: Type,
3339 arena: Allocator,
3340 mod: *Module,
3341) !Value {
3342 if (float_type.zigTypeTag(mod) == .Vector) {
3343 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3344 const scalar_ty = float_type.scalarType(mod);
3345 for (result_data, 0..) |*scalar, i| {
3346 const lhs_elem = try lhs.elemValue(mod, i);
3347 const rhs_elem = try rhs.elemValue(mod, i);
3348 scalar.* = try (try floatDivTruncScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3349 }
3350 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3351 .ty = float_type.toIntern(),
3352 .storage = .{ .elems = result_data },
3353 } })));
3354 }
3355 return floatDivTruncScalar(lhs, rhs, float_type, mod);
3356}
3357
3358pub fn floatDivTruncScalar(
3359 lhs: Value,
3360 rhs: Value,
3361 float_type: Type,
3362 mod: *Module,
3363) !Value {
3364 const target = mod.getTarget();
3365 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3366 16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3367 32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3368 64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3369 80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3370 128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3371 else => unreachable,
3372 };
3373 return Value.fromInterned((try mod.intern(.{ .float = .{
3374 .ty = float_type.toIntern(),
3375 .storage = storage,
3376 } })));
3377}
3378
3379pub fn floatMul(
3380 lhs: Value,
3381 rhs: Value,
3382 float_type: Type,
3383 arena: Allocator,
3384 mod: *Module,
3385) !Value {
3386 if (float_type.zigTypeTag(mod) == .Vector) {
3387 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3388 const scalar_ty = float_type.scalarType(mod);
3389 for (result_data, 0..) |*scalar, i| {
3390 const lhs_elem = try lhs.elemValue(mod, i);
3391 const rhs_elem = try rhs.elemValue(mod, i);
3392 scalar.* = try (try floatMulScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3393 }
3394 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3395 .ty = float_type.toIntern(),
3396 .storage = .{ .elems = result_data },
3397 } })));
3398 }
3399 return floatMulScalar(lhs, rhs, float_type, mod);
3400}
3401
3402pub fn floatMulScalar(
3403 lhs: Value,
3404 rhs: Value,
3405 float_type: Type,
3406 mod: *Module,
3407) !Value {
3408 const target = mod.getTarget();
3409 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3410 16 => .{ .f16 = lhs.toFloat(f16, mod) * rhs.toFloat(f16, mod) },
3411 32 => .{ .f32 = lhs.toFloat(f32, mod) * rhs.toFloat(f32, mod) },
3412 64 => .{ .f64 = lhs.toFloat(f64, mod) * rhs.toFloat(f64, mod) },
3413 80 => .{ .f80 = lhs.toFloat(f80, mod) * rhs.toFloat(f80, mod) },
3414 128 => .{ .f128 = lhs.toFloat(f128, mod) * rhs.toFloat(f128, mod) },
3415 else => unreachable,
3416 };
3417 return Value.fromInterned((try mod.intern(.{ .float = .{
3418 .ty = float_type.toIntern(),
3419 .storage = storage,
3420 } })));
3421}
3422
3423pub fn sqrt(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3424 if (float_type.zigTypeTag(mod) == .Vector) {
3425 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3426 const scalar_ty = float_type.scalarType(mod);
3427 for (result_data, 0..) |*scalar, i| {
3428 const elem_val = try val.elemValue(mod, i);
3429 scalar.* = try (try sqrtScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3430 }
3431 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3432 .ty = float_type.toIntern(),
3433 .storage = .{ .elems = result_data },
3434 } })));
3435 }
3436 return sqrtScalar(val, float_type, mod);
3437}
3438
3439pub fn sqrtScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3440 const target = mod.getTarget();
3441 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3442 16 => .{ .f16 = @sqrt(val.toFloat(f16, mod)) },
3443 32 => .{ .f32 = @sqrt(val.toFloat(f32, mod)) },
3444 64 => .{ .f64 = @sqrt(val.toFloat(f64, mod)) },
3445 80 => .{ .f80 = @sqrt(val.toFloat(f80, mod)) },
3446 128 => .{ .f128 = @sqrt(val.toFloat(f128, mod)) },
3447 else => unreachable,
3448 };
3449 return Value.fromInterned((try mod.intern(.{ .float = .{
3450 .ty = float_type.toIntern(),
3451 .storage = storage,
3452 } })));
3453}
3454
3455pub fn sin(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3456 if (float_type.zigTypeTag(mod) == .Vector) {
3457 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3458 const scalar_ty = float_type.scalarType(mod);
3459 for (result_data, 0..) |*scalar, i| {
3460 const elem_val = try val.elemValue(mod, i);
3461 scalar.* = try (try sinScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3462 }
3463 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3464 .ty = float_type.toIntern(),
3465 .storage = .{ .elems = result_data },
3466 } })));
3467 }
3468 return sinScalar(val, float_type, mod);
3469}
3470
3471pub fn sinScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3472 const target = mod.getTarget();
3473 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3474 16 => .{ .f16 = @sin(val.toFloat(f16, mod)) },
3475 32 => .{ .f32 = @sin(val.toFloat(f32, mod)) },
3476 64 => .{ .f64 = @sin(val.toFloat(f64, mod)) },
3477 80 => .{ .f80 = @sin(val.toFloat(f80, mod)) },
3478 128 => .{ .f128 = @sin(val.toFloat(f128, mod)) },
3479 else => unreachable,
3480 };
3481 return Value.fromInterned((try mod.intern(.{ .float = .{
3482 .ty = float_type.toIntern(),
3483 .storage = storage,
3484 } })));
3485}
3486
3487pub fn cos(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3488 if (float_type.zigTypeTag(mod) == .Vector) {
3489 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3490 const scalar_ty = float_type.scalarType(mod);
3491 for (result_data, 0..) |*scalar, i| {
3492 const elem_val = try val.elemValue(mod, i);
3493 scalar.* = try (try cosScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3494 }
3495 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3496 .ty = float_type.toIntern(),
3497 .storage = .{ .elems = result_data },
3498 } })));
3499 }
3500 return cosScalar(val, float_type, mod);
3501}
3502
3503pub fn cosScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3504 const target = mod.getTarget();
3505 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3506 16 => .{ .f16 = @cos(val.toFloat(f16, mod)) },
3507 32 => .{ .f32 = @cos(val.toFloat(f32, mod)) },
3508 64 => .{ .f64 = @cos(val.toFloat(f64, mod)) },
3509 80 => .{ .f80 = @cos(val.toFloat(f80, mod)) },
3510 128 => .{ .f128 = @cos(val.toFloat(f128, mod)) },
3511 else => unreachable,
3512 };
3513 return Value.fromInterned((try mod.intern(.{ .float = .{
3514 .ty = float_type.toIntern(),
3515 .storage = storage,
3516 } })));
3517}
3518
3519pub fn tan(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3520 if (float_type.zigTypeTag(mod) == .Vector) {
3521 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3522 const scalar_ty = float_type.scalarType(mod);
3523 for (result_data, 0..) |*scalar, i| {
3524 const elem_val = try val.elemValue(mod, i);
3525 scalar.* = try (try tanScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3526 }
3527 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3528 .ty = float_type.toIntern(),
3529 .storage = .{ .elems = result_data },
3530 } })));
3531 }
3532 return tanScalar(val, float_type, mod);
3533}
3534
3535pub fn tanScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3536 const target = mod.getTarget();
3537 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3538 16 => .{ .f16 = @tan(val.toFloat(f16, mod)) },
3539 32 => .{ .f32 = @tan(val.toFloat(f32, mod)) },
3540 64 => .{ .f64 = @tan(val.toFloat(f64, mod)) },
3541 80 => .{ .f80 = @tan(val.toFloat(f80, mod)) },
3542 128 => .{ .f128 = @tan(val.toFloat(f128, mod)) },
3543 else => unreachable,
3544 };
3545 return Value.fromInterned((try mod.intern(.{ .float = .{
3546 .ty = float_type.toIntern(),
3547 .storage = storage,
3548 } })));
3549}
3550
3551pub fn exp(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3552 if (float_type.zigTypeTag(mod) == .Vector) {
3553 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3554 const scalar_ty = float_type.scalarType(mod);
3555 for (result_data, 0..) |*scalar, i| {
3556 const elem_val = try val.elemValue(mod, i);
3557 scalar.* = try (try expScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3558 }
3559 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3560 .ty = float_type.toIntern(),
3561 .storage = .{ .elems = result_data },
3562 } })));
3563 }
3564 return expScalar(val, float_type, mod);
3565}
3566
3567pub fn expScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3568 const target = mod.getTarget();
3569 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3570 16 => .{ .f16 = @exp(val.toFloat(f16, mod)) },
3571 32 => .{ .f32 = @exp(val.toFloat(f32, mod)) },
3572 64 => .{ .f64 = @exp(val.toFloat(f64, mod)) },
3573 80 => .{ .f80 = @exp(val.toFloat(f80, mod)) },
3574 128 => .{ .f128 = @exp(val.toFloat(f128, mod)) },
3575 else => unreachable,
3576 };
3577 return Value.fromInterned((try mod.intern(.{ .float = .{
3578 .ty = float_type.toIntern(),
3579 .storage = storage,
3580 } })));
3581}
3582
3583pub fn exp2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3584 if (float_type.zigTypeTag(mod) == .Vector) {
3585 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3586 const scalar_ty = float_type.scalarType(mod);
3587 for (result_data, 0..) |*scalar, i| {
3588 const elem_val = try val.elemValue(mod, i);
3589 scalar.* = try (try exp2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3590 }
3591 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3592 .ty = float_type.toIntern(),
3593 .storage = .{ .elems = result_data },
3594 } })));
3595 }
3596 return exp2Scalar(val, float_type, mod);
3597}
3598
3599pub fn exp2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3600 const target = mod.getTarget();
3601 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3602 16 => .{ .f16 = @exp2(val.toFloat(f16, mod)) },
3603 32 => .{ .f32 = @exp2(val.toFloat(f32, mod)) },
3604 64 => .{ .f64 = @exp2(val.toFloat(f64, mod)) },
3605 80 => .{ .f80 = @exp2(val.toFloat(f80, mod)) },
3606 128 => .{ .f128 = @exp2(val.toFloat(f128, mod)) },
3607 else => unreachable,
3608 };
3609 return Value.fromInterned((try mod.intern(.{ .float = .{
3610 .ty = float_type.toIntern(),
3611 .storage = storage,
3612 } })));
3613}
3614
3615pub fn log(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3616 if (float_type.zigTypeTag(mod) == .Vector) {
3617 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3618 const scalar_ty = float_type.scalarType(mod);
3619 for (result_data, 0..) |*scalar, i| {
3620 const elem_val = try val.elemValue(mod, i);
3621 scalar.* = try (try logScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3622 }
3623 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3624 .ty = float_type.toIntern(),
3625 .storage = .{ .elems = result_data },
3626 } })));
3627 }
3628 return logScalar(val, float_type, mod);
3629}
3630
3631pub fn logScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3632 const target = mod.getTarget();
3633 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3634 16 => .{ .f16 = @log(val.toFloat(f16, mod)) },
3635 32 => .{ .f32 = @log(val.toFloat(f32, mod)) },
3636 64 => .{ .f64 = @log(val.toFloat(f64, mod)) },
3637 80 => .{ .f80 = @log(val.toFloat(f80, mod)) },
3638 128 => .{ .f128 = @log(val.toFloat(f128, mod)) },
3639 else => unreachable,
3640 };
3641 return Value.fromInterned((try mod.intern(.{ .float = .{
3642 .ty = float_type.toIntern(),
3643 .storage = storage,
3644 } })));
3645}
3646
3647pub fn log2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3648 if (float_type.zigTypeTag(mod) == .Vector) {
3649 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3650 const scalar_ty = float_type.scalarType(mod);
3651 for (result_data, 0..) |*scalar, i| {
3652 const elem_val = try val.elemValue(mod, i);
3653 scalar.* = try (try log2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3654 }
3655 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3656 .ty = float_type.toIntern(),
3657 .storage = .{ .elems = result_data },
3658 } })));
3659 }
3660 return log2Scalar(val, float_type, mod);
3661}
3662
3663pub fn log2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3664 const target = mod.getTarget();
3665 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3666 16 => .{ .f16 = @log2(val.toFloat(f16, mod)) },
3667 32 => .{ .f32 = @log2(val.toFloat(f32, mod)) },
3668 64 => .{ .f64 = @log2(val.toFloat(f64, mod)) },
3669 80 => .{ .f80 = @log2(val.toFloat(f80, mod)) },
3670 128 => .{ .f128 = @log2(val.toFloat(f128, mod)) },
3671 else => unreachable,
3672 };
3673 return Value.fromInterned((try mod.intern(.{ .float = .{
3674 .ty = float_type.toIntern(),
3675 .storage = storage,
3676 } })));
3677}
3678
3679pub fn log10(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3680 if (float_type.zigTypeTag(mod) == .Vector) {
3681 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3682 const scalar_ty = float_type.scalarType(mod);
3683 for (result_data, 0..) |*scalar, i| {
3684 const elem_val = try val.elemValue(mod, i);
3685 scalar.* = try (try log10Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3686 }
3687 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3688 .ty = float_type.toIntern(),
3689 .storage = .{ .elems = result_data },
3690 } })));
3691 }
3692 return log10Scalar(val, float_type, mod);
3693}
3694
3695pub fn log10Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3696 const target = mod.getTarget();
3697 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3698 16 => .{ .f16 = @log10(val.toFloat(f16, mod)) },
3699 32 => .{ .f32 = @log10(val.toFloat(f32, mod)) },
3700 64 => .{ .f64 = @log10(val.toFloat(f64, mod)) },
3701 80 => .{ .f80 = @log10(val.toFloat(f80, mod)) },
3702 128 => .{ .f128 = @log10(val.toFloat(f128, mod)) },
3703 else => unreachable,
3704 };
3705 return Value.fromInterned((try mod.intern(.{ .float = .{
3706 .ty = float_type.toIntern(),
3707 .storage = storage,
3708 } })));
3709}
3710
3711pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
3712 if (ty.zigTypeTag(mod) == .Vector) {
3713 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3714 const scalar_ty = ty.scalarType(mod);
3715 for (result_data, 0..) |*scalar, i| {
3716 const elem_val = try val.elemValue(mod, i);
3717 scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod);
3718 }
3719 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3720 .ty = ty.toIntern(),
3721 .storage = .{ .elems = result_data },
3722 } })));
3723 }
3724 return absScalar(val, ty, mod, arena);
3725}
3726
3727pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value {
3728 switch (ty.zigTypeTag(mod)) {
3729 .Int => {
3730 var buffer: Value.BigIntSpace = undefined;
3731 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3732 operand_bigint.abs();
3733
3734 return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst());
3735 },
3736 .ComptimeInt => {
3737 var buffer: Value.BigIntSpace = undefined;
3738 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3739 operand_bigint.abs();
3740
3741 return mod.intValue_big(ty, operand_bigint.toConst());
3742 },
3743 .ComptimeFloat, .Float => {
3744 const target = mod.getTarget();
3745 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
3746 16 => .{ .f16 = @abs(val.toFloat(f16, mod)) },
3747 32 => .{ .f32 = @abs(val.toFloat(f32, mod)) },
3748 64 => .{ .f64 = @abs(val.toFloat(f64, mod)) },
3749 80 => .{ .f80 = @abs(val.toFloat(f80, mod)) },
3750 128 => .{ .f128 = @abs(val.toFloat(f128, mod)) },
3751 else => unreachable,
3752 };
3753 return Value.fromInterned((try mod.intern(.{ .float = .{
3754 .ty = ty.toIntern(),
3755 .storage = storage,
3756 } })));
3757 },
3758 else => unreachable,
3759 }
3760}
3761
3762pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3763 if (float_type.zigTypeTag(mod) == .Vector) {
3764 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3765 const scalar_ty = float_type.scalarType(mod);
3766 for (result_data, 0..) |*scalar, i| {
3767 const elem_val = try val.elemValue(mod, i);
3768 scalar.* = try (try floorScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3769 }
3770 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3771 .ty = float_type.toIntern(),
3772 .storage = .{ .elems = result_data },
3773 } })));
3774 }
3775 return floorScalar(val, float_type, mod);
3776}
3777
3778pub fn floorScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3779 const target = mod.getTarget();
3780 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3781 16 => .{ .f16 = @floor(val.toFloat(f16, mod)) },
3782 32 => .{ .f32 = @floor(val.toFloat(f32, mod)) },
3783 64 => .{ .f64 = @floor(val.toFloat(f64, mod)) },
3784 80 => .{ .f80 = @floor(val.toFloat(f80, mod)) },
3785 128 => .{ .f128 = @floor(val.toFloat(f128, mod)) },
3786 else => unreachable,
3787 };
3788 return Value.fromInterned((try mod.intern(.{ .float = .{
3789 .ty = float_type.toIntern(),
3790 .storage = storage,
3791 } })));
3792}
3793
3794pub fn ceil(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3795 if (float_type.zigTypeTag(mod) == .Vector) {
3796 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3797 const scalar_ty = float_type.scalarType(mod);
3798 for (result_data, 0..) |*scalar, i| {
3799 const elem_val = try val.elemValue(mod, i);
3800 scalar.* = try (try ceilScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3801 }
3802 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3803 .ty = float_type.toIntern(),
3804 .storage = .{ .elems = result_data },
3805 } })));
3806 }
3807 return ceilScalar(val, float_type, mod);
3808}
3809
3810pub fn ceilScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3811 const target = mod.getTarget();
3812 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3813 16 => .{ .f16 = @ceil(val.toFloat(f16, mod)) },
3814 32 => .{ .f32 = @ceil(val.toFloat(f32, mod)) },
3815 64 => .{ .f64 = @ceil(val.toFloat(f64, mod)) },
3816 80 => .{ .f80 = @ceil(val.toFloat(f80, mod)) },
3817 128 => .{ .f128 = @ceil(val.toFloat(f128, mod)) },
3818 else => unreachable,
3819 };
3820 return Value.fromInterned((try mod.intern(.{ .float = .{
3821 .ty = float_type.toIntern(),
3822 .storage = storage,
3823 } })));
3824}
3825
3826pub fn round(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3827 if (float_type.zigTypeTag(mod) == .Vector) {
3828 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3829 const scalar_ty = float_type.scalarType(mod);
3830 for (result_data, 0..) |*scalar, i| {
3831 const elem_val = try val.elemValue(mod, i);
3832 scalar.* = try (try roundScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3833 }
3834 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3835 .ty = float_type.toIntern(),
3836 .storage = .{ .elems = result_data },
3837 } })));
3838 }
3839 return roundScalar(val, float_type, mod);
3840}
3841
3842pub fn roundScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3843 const target = mod.getTarget();
3844 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3845 16 => .{ .f16 = @round(val.toFloat(f16, mod)) },
3846 32 => .{ .f32 = @round(val.toFloat(f32, mod)) },
3847 64 => .{ .f64 = @round(val.toFloat(f64, mod)) },
3848 80 => .{ .f80 = @round(val.toFloat(f80, mod)) },
3849 128 => .{ .f128 = @round(val.toFloat(f128, mod)) },
3850 else => unreachable,
3851 };
3852 return Value.fromInterned((try mod.intern(.{ .float = .{
3853 .ty = float_type.toIntern(),
3854 .storage = storage,
3855 } })));
3856}
3857
3858pub fn trunc(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3859 if (float_type.zigTypeTag(mod) == .Vector) {
3860 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3861 const scalar_ty = float_type.scalarType(mod);
3862 for (result_data, 0..) |*scalar, i| {
3863 const elem_val = try val.elemValue(mod, i);
3864 scalar.* = try (try truncScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3865 }
3866 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3867 .ty = float_type.toIntern(),
3868 .storage = .{ .elems = result_data },
3869 } })));
3870 }
3871 return truncScalar(val, float_type, mod);
3872}
3873
3874pub fn truncScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3875 const target = mod.getTarget();
3876 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3877 16 => .{ .f16 = @trunc(val.toFloat(f16, mod)) },
3878 32 => .{ .f32 = @trunc(val.toFloat(f32, mod)) },
3879 64 => .{ .f64 = @trunc(val.toFloat(f64, mod)) },
3880 80 => .{ .f80 = @trunc(val.toFloat(f80, mod)) },
3881 128 => .{ .f128 = @trunc(val.toFloat(f128, mod)) },
3882 else => unreachable,
3883 };
3884 return Value.fromInterned((try mod.intern(.{ .float = .{
3885 .ty = float_type.toIntern(),
3886 .storage = storage,
3887 } })));
3888}
3889
3890pub fn mulAdd(
3891 float_type: Type,
3892 mulend1: Value,
3893 mulend2: Value,
3894 addend: Value,
3895 arena: Allocator,
3896 mod: *Module,
3897) !Value {
3898 if (float_type.zigTypeTag(mod) == .Vector) {
3899 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3900 const scalar_ty = float_type.scalarType(mod);
3901 for (result_data, 0..) |*scalar, i| {
3902 const mulend1_elem = try mulend1.elemValue(mod, i);
3903 const mulend2_elem = try mulend2.elemValue(mod, i);
3904 const addend_elem = try addend.elemValue(mod, i);
3905 scalar.* = try (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, mod)).intern(scalar_ty, mod);
3906 }
3907 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3908 .ty = float_type.toIntern(),
3909 .storage = .{ .elems = result_data },
3910 } })));
3911 }
3912 return mulAddScalar(float_type, mulend1, mulend2, addend, mod);
3913}
3914
3915pub fn mulAddScalar(
3916 float_type: Type,
3917 mulend1: Value,
3918 mulend2: Value,
3919 addend: Value,
3920 mod: *Module,
3921) Allocator.Error!Value {
3922 const target = mod.getTarget();
3923 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3924 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, mod), mulend2.toFloat(f16, mod), addend.toFloat(f16, mod)) },
3925 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, mod), mulend2.toFloat(f32, mod), addend.toFloat(f32, mod)) },
3926 64 => .{ .f64 = @mulAdd(f64, mulend1.toFloat(f64, mod), mulend2.toFloat(f64, mod), addend.toFloat(f64, mod)) },
3927 80 => .{ .f80 = @mulAdd(f80, mulend1.toFloat(f80, mod), mulend2.toFloat(f80, mod), addend.toFloat(f80, mod)) },
3928 128 => .{ .f128 = @mulAdd(f128, mulend1.toFloat(f128, mod), mulend2.toFloat(f128, mod), addend.toFloat(f128, mod)) },
3929 else => unreachable,
3930 };
3931 return Value.fromInterned((try mod.intern(.{ .float = .{
3932 .ty = float_type.toIntern(),
3933 .storage = storage,
3934 } })));
3935}
3936
3937/// If the value is represented in-memory as a series of bytes that all
3938/// have the same value, return that byte value, otherwise null.
3939pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module) !?u8 {
3940 const abi_size = std.math.cast(usize, ty.abiSize(mod)) orelse return null;
3941 assert(abi_size >= 1);
3942 const byte_buffer = try mod.gpa.alloc(u8, abi_size);
3943 defer mod.gpa.free(byte_buffer);
3944
3945 writeToMemory(val, ty, mod, byte_buffer) catch |err| switch (err) {
3946 error.OutOfMemory => return error.OutOfMemory,
3947 error.ReinterpretDeclRef => return null,
3948 // TODO: The writeToMemory function was originally created for the purpose
3949 // of comptime pointer casting. However, it is now additionally being used
3950 // for checking the actual memory layout that will be generated by machine
3951 // code late in compilation. So, this error handling is too aggressive and
3952 // causes some false negatives, causing less-than-ideal code generation.
3953 error.IllDefinedMemoryLayout => return null,
3954 error.Unimplemented => return null,
3955 };
3956 const first_byte = byte_buffer[0];
3957 for (byte_buffer[1..]) |byte| {
3958 if (byte != first_byte) return null;
3959 }
3960 return first_byte;
3961}
3962
3963pub fn isGenericPoison(val: Value) bool {
3964 return val.toIntern() == .generic_poison;
3965}
3966
3967/// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value.
3968/// If `val` is not undef, the bounds are both `val`.
3969/// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type.
3970/// If `val` is undef and is a `comptime_int`, returns null.
3971pub fn intValueBounds(val: Value, mod: *Module) !?[2]Value {
3972 if (!val.isUndef(mod)) return .{ val, val };
3973 const ty = mod.intern_pool.typeOf(val.toIntern());
3974 if (ty == .comptime_int_type) return null;
3975 return .{
3976 try Type.fromInterned(ty).minInt(mod, Type.fromInterned(ty)),
3977 try Type.fromInterned(ty).maxInt(mod, Type.fromInterned(ty)),
3978 };
3979}
3980
3981/// This type is not copyable since it may contain pointers to its inner data.
3982pub const Payload = struct {
3983 tag: Tag,
3984
3985 pub const Slice = struct {
3986 base: Payload,
3987 data: struct {
3988 ptr: Value,
3989 len: Value,
3990 },
3991 };
3992
3993 pub const Bytes = struct {
3994 base: Payload,
3995 /// Includes the sentinel, if any.
3996 data: []const u8,
3997 };
3998
3999 pub const SubValue = struct {
4000 base: Payload,
4001 data: Value,
4002 };
4003
4004 pub const Aggregate = struct {
4005 base: Payload,
4006 /// Field values. The types are according to the struct or array type.
4007 /// The length is provided here so that copying a Value does not depend on the Type.
4008 data: []Value,
4009 };
4010
4011 pub const Union = struct {
4012 pub const base_tag = Tag.@"union";
4013
4014 base: Payload = .{ .tag = base_tag },
4015 data: Data,
4016
4017 pub const Data = struct {
4018 tag: ?Value,
4019 val: Value,
4020 };
4021 };
4022};
4023
4024pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
4025
4026pub const zero_usize: Value = .{ .ip_index = .zero_usize, .legacy = undefined };
4027pub const zero_u8: Value = .{ .ip_index = .zero_u8, .legacy = undefined };
4028pub const zero_comptime_int: Value = .{ .ip_index = .zero, .legacy = undefined };
4029pub const one_comptime_int: Value = .{ .ip_index = .one, .legacy = undefined };
4030pub const negative_one_comptime_int: Value = .{ .ip_index = .negative_one, .legacy = undefined };
4031pub const undef: Value = .{ .ip_index = .undef, .legacy = undefined };
4032pub const @"void": Value = .{ .ip_index = .void_value, .legacy = undefined };
4033pub const @"null": Value = .{ .ip_index = .null_value, .legacy = undefined };
4034pub const @"false": Value = .{ .ip_index = .bool_false, .legacy = undefined };
4035pub const @"true": Value = .{ .ip_index = .bool_true, .legacy = undefined };
4036pub const @"unreachable": Value = .{ .ip_index = .unreachable_value, .legacy = undefined };
4037
4038pub const generic_poison: Value = .{ .ip_index = .generic_poison, .legacy = undefined };
4039pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type, .legacy = undefined };
4040pub const empty_struct: Value = .{ .ip_index = .empty_struct, .legacy = undefined };
4041
4042pub fn makeBool(x: bool) Value {
4043 return if (x) Value.true else Value.false;
4044}
4045
4046pub const RuntimeIndex = InternPool.RuntimeIndex;
4047
4048/// This function is used in the debugger pretty formatters in tools/ to fetch the
4049/// Tag to Payload mapping to facilitate fancy debug printing for this type.
4050fn dbHelper(self: *Value, tag_to_payload_map: *map: {
4051 const tags = @typeInfo(Tag).Enum.fields;
4052 var fields: [tags.len]std.builtin.Type.StructField = undefined;
4053 for (&fields, tags) |*field, t| field.* = .{
4054 .name = t.name ++ "",
4055 .type = *@field(Tag, t.name).Type(),
4056 .default_value = null,
4057 .is_comptime = false,
4058 .alignment = 0,
4059 };
4060 break :map @Type(.{ .Struct = .{
4061 .layout = .Extern,
4062 .fields = &fields,
4063 .decls = &.{},
4064 .is_tuple = false,
4065 } });
4066}) void {
4067 _ = self;
4068 _ = tag_to_payload_map;
4069}
4070
4071comptime {
4072 if (builtin.mode == .Debug) {
4073 _ = &dbHelper;
4074 }
4075}
src/arch/aarch64/CodeGen.zig+1-1
...@@ -9,7 +9,7 @@ const Mir = @import("Mir.zig");...@@ -9,7 +9,7 @@ const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");9const Emit = @import("Emit.zig");
10const Liveness = @import("../../Liveness.zig");10const Liveness = @import("../../Liveness.zig");
11const Type = @import("../../type.zig").Type;11const Type = @import("../../type.zig").Type;
12const Value = @import("../../value.zig").Value;12const Value = @import("../../Value.zig");
13const TypedValue = @import("../../TypedValue.zig");13const TypedValue = @import("../../TypedValue.zig");
14const link = @import("../../link.zig");14const link = @import("../../link.zig");
15const Module = @import("../../Module.zig");15const Module = @import("../../Module.zig");
src/arch/arm/CodeGen.zig+1-1
...@@ -9,7 +9,7 @@ const Mir = @import("Mir.zig");...@@ -9,7 +9,7 @@ const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");9const Emit = @import("Emit.zig");
10const Liveness = @import("../../Liveness.zig");10const Liveness = @import("../../Liveness.zig");
11const Type = @import("../../type.zig").Type;11const Type = @import("../../type.zig").Type;
12const Value = @import("../../value.zig").Value;12const Value = @import("../../Value.zig");
13const TypedValue = @import("../../TypedValue.zig");13const TypedValue = @import("../../TypedValue.zig");
14const link = @import("../../link.zig");14const link = @import("../../link.zig");
15const Module = @import("../../Module.zig");15const Module = @import("../../Module.zig");
src/arch/riscv64/CodeGen.zig+1-1
...@@ -8,7 +8,7 @@ const Mir = @import("Mir.zig");...@@ -8,7 +8,7 @@ const Mir = @import("Mir.zig");
8const Emit = @import("Emit.zig");8const Emit = @import("Emit.zig");
9const Liveness = @import("../../Liveness.zig");9const Liveness = @import("../../Liveness.zig");
10const Type = @import("../../type.zig").Type;10const Type = @import("../../type.zig").Type;
11const Value = @import("../../value.zig").Value;11const Value = @import("../../Value.zig");
12const TypedValue = @import("../../TypedValue.zig");12const TypedValue = @import("../../TypedValue.zig");
13const link = @import("../../link.zig");13const link = @import("../../link.zig");
14const Module = @import("../../Module.zig");14const Module = @import("../../Module.zig");
src/arch/wasm/CodeGen.zig+1-1
...@@ -14,7 +14,7 @@ const Module = @import("../../Module.zig");...@@ -14,7 +14,7 @@ const Module = @import("../../Module.zig");
14const InternPool = @import("../../InternPool.zig");14const InternPool = @import("../../InternPool.zig");
15const Decl = Module.Decl;15const Decl = Module.Decl;
16const Type = @import("../../type.zig").Type;16const Type = @import("../../type.zig").Type;
17const Value = @import("../../value.zig").Value;17const Value = @import("../../Value.zig");
18const Compilation = @import("../../Compilation.zig");18const Compilation = @import("../../Compilation.zig");
19const LazySrcLoc = Module.LazySrcLoc;19const LazySrcLoc = Module.LazySrcLoc;
20const link = @import("../../link.zig");20const link = @import("../../link.zig");
src/arch/x86_64/CodeGen.zig+1-1
...@@ -33,7 +33,7 @@ const Alignment = InternPool.Alignment;...@@ -33,7 +33,7 @@ const Alignment = InternPool.Alignment;
33const Target = std.Target;33const Target = std.Target;
34const Type = @import("../../type.zig").Type;34const Type = @import("../../type.zig").Type;
35const TypedValue = @import("../../TypedValue.zig");35const TypedValue = @import("../../TypedValue.zig");
36const Value = @import("../../value.zig").Value;36const Value = @import("../../Value.zig");
37const Instruction = @import("encoder.zig").Instruction;37const Instruction = @import("encoder.zig").Instruction;
3838
39const abi = @import("abi.zig");39const abi = @import("abi.zig");
src/arch/x86_64/abi.zig+1-1
...@@ -570,4 +570,4 @@ const Module = @import("../../Module.zig");...@@ -570,4 +570,4 @@ const Module = @import("../../Module.zig");
570const Register = @import("bits.zig").Register;570const Register = @import("bits.zig").Register;
571const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;571const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
572const Type = @import("../../type.zig").Type;572const Type = @import("../../type.zig").Type;
573const Value = @import("../../value.zig").Value;573const Value = @import("../../Value.zig");
src/codegen.zig+1-1
...@@ -20,7 +20,7 @@ const Module = @import("Module.zig");...@@ -20,7 +20,7 @@ const Module = @import("Module.zig");
20const Target = std.Target;20const Target = std.Target;
21const Type = @import("type.zig").Type;21const Type = @import("type.zig").Type;
22const TypedValue = @import("TypedValue.zig");22const TypedValue = @import("TypedValue.zig");
23const Value = @import("value.zig").Value;23const Value = @import("Value.zig");
24const Zir = @import("Zir.zig");24const Zir = @import("Zir.zig");
25const Alignment = InternPool.Alignment;25const Alignment = InternPool.Alignment;
2626
src/codegen/c.zig+1-1
...@@ -7,7 +7,7 @@ const log = std.log.scoped(.c);...@@ -7,7 +7,7 @@ const log = std.log.scoped(.c);
7const link = @import("../link.zig");7const link = @import("../link.zig");
8const Module = @import("../Module.zig");8const Module = @import("../Module.zig");
9const Compilation = @import("../Compilation.zig");9const Compilation = @import("../Compilation.zig");
10const Value = @import("../value.zig").Value;10const Value = @import("../Value.zig");
11const Type = @import("../type.zig").Type;11const Type = @import("../type.zig").Type;
12const TypedValue = @import("../TypedValue.zig");12const TypedValue = @import("../TypedValue.zig");
13const C = link.File.C;13const C = link.File.C;
src/codegen/llvm.zig+1-1
...@@ -21,7 +21,7 @@ const Package = @import("../Package.zig");...@@ -21,7 +21,7 @@ const Package = @import("../Package.zig");
21const TypedValue = @import("../TypedValue.zig");21const TypedValue = @import("../TypedValue.zig");
22const Air = @import("../Air.zig");22const Air = @import("../Air.zig");
23const Liveness = @import("../Liveness.zig");23const Liveness = @import("../Liveness.zig");
24const Value = @import("../value.zig").Value;24const Value = @import("../Value.zig");
25const Type = @import("../type.zig").Type;25const Type = @import("../type.zig").Type;
26const LazySrcLoc = Module.LazySrcLoc;26const LazySrcLoc = Module.LazySrcLoc;
27const x86_64_abi = @import("../arch/x86_64/abi.zig");27const x86_64_abi = @import("../arch/x86_64/abi.zig");
src/codegen/spirv.zig+1-1
...@@ -7,7 +7,7 @@ const assert = std.debug.assert;...@@ -7,7 +7,7 @@ const assert = std.debug.assert;
7const Module = @import("../Module.zig");7const Module = @import("../Module.zig");
8const Decl = Module.Decl;8const Decl = Module.Decl;
9const Type = @import("../type.zig").Type;9const Type = @import("../type.zig").Type;
10const Value = @import("../value.zig").Value;10const Value = @import("../Value.zig");
11const LazySrcLoc = Module.LazySrcLoc;11const LazySrcLoc = Module.LazySrcLoc;
12const Air = @import("../Air.zig");12const Air = @import("../Air.zig");
13const Zir = @import("../Zir.zig");13const Zir = @import("../Zir.zig");
src/link/C.zig+1-1
...@@ -14,7 +14,7 @@ const codegen = @import("../codegen/c.zig");...@@ -14,7 +14,7 @@ const codegen = @import("../codegen/c.zig");
14const link = @import("../link.zig");14const link = @import("../link.zig");
15const trace = @import("../tracy.zig").trace;15const trace = @import("../tracy.zig").trace;
16const Type = @import("../type.zig").Type;16const Type = @import("../type.zig").Type;
17const Value = @import("../value.zig").Value;17const Value = @import("../Value.zig");
18const Air = @import("../Air.zig");18const Air = @import("../Air.zig");
19const Liveness = @import("../Liveness.zig");19const Liveness = @import("../Liveness.zig");
2020
src/link/Coff.zig+1-1
...@@ -2753,7 +2753,7 @@ const Relocation = @import("Coff/Relocation.zig");...@@ -2753,7 +2753,7 @@ const Relocation = @import("Coff/Relocation.zig");
2753const TableSection = @import("table_section.zig").TableSection;2753const TableSection = @import("table_section.zig").TableSection;
2754const StringTable = @import("StringTable.zig");2754const StringTable = @import("StringTable.zig");
2755const Type = @import("../type.zig").Type;2755const Type = @import("../type.zig").Type;
2756const Value = @import("../value.zig").Value;2756const Value = @import("../Value.zig");
2757const TypedValue = @import("../TypedValue.zig");2757const TypedValue = @import("../TypedValue.zig");
27582758
2759pub const base_tag: link.File.Tag = .coff;2759pub const base_tag: link.File.Tag = .coff;
src/link/Dwarf.zig+1-1
...@@ -2847,4 +2847,4 @@ const Module = @import("../Module.zig");...@@ -2847,4 +2847,4 @@ const Module = @import("../Module.zig");
2847const InternPool = @import("../InternPool.zig");2847const InternPool = @import("../InternPool.zig");
2848const StringTable = @import("StringTable.zig");2848const StringTable = @import("StringTable.zig");
2849const Type = @import("../type.zig").Type;2849const 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");...@@ -1667,6 +1667,6 @@ const Object = @import("Object.zig");
1667const Symbol = @import("Symbol.zig");1667const Symbol = @import("Symbol.zig");
1668const StringTable = @import("../StringTable.zig");1668const StringTable = @import("../StringTable.zig");
1669const Type = @import("../../type.zig").Type;1669const Type = @import("../../type.zig").Type;
1670const Value = @import("../../value.zig").Value;1670const Value = @import("../../Value.zig");
1671const TypedValue = @import("../../TypedValue.zig");1671const TypedValue = @import("../../TypedValue.zig");
1672const ZigObject = @This();1672const ZigObject = @This();
src/link/MachO/ZigObject.zig+1-1
...@@ -1462,6 +1462,6 @@ const Relocation = @import("Relocation.zig");...@@ -1462,6 +1462,6 @@ const Relocation = @import("Relocation.zig");
1462const Symbol = @import("Symbol.zig");1462const Symbol = @import("Symbol.zig");
1463const StringTable = @import("../StringTable.zig");1463const StringTable = @import("../StringTable.zig");
1464const Type = @import("../../type.zig").Type;1464const Type = @import("../../type.zig").Type;
1465const Value = @import("../../value.zig").Value;1465const Value = @import("../../Value.zig");
1466const TypedValue = @import("../../TypedValue.zig");1466const TypedValue = @import("../../TypedValue.zig");
1467const ZigObject = @This();1467const ZigObject = @This();
src/link/Plan9.zig+1-1
...@@ -14,7 +14,7 @@ const build_options = @import("build_options");...@@ -14,7 +14,7 @@ const build_options = @import("build_options");
14const Air = @import("../Air.zig");14const Air = @import("../Air.zig");
15const Liveness = @import("../Liveness.zig");15const Liveness = @import("../Liveness.zig");
16const Type = @import("../type.zig").Type;16const Type = @import("../type.zig").Type;
17const Value = @import("../value.zig").Value;17const Value = @import("../Value.zig");
18const TypedValue = @import("../TypedValue.zig");18const TypedValue = @import("../TypedValue.zig");
1919
20const std = @import("std");20const std = @import("std");
src/link/SpirV.zig+1-1
...@@ -36,7 +36,7 @@ const trace = @import("../tracy.zig").trace;...@@ -36,7 +36,7 @@ const trace = @import("../tracy.zig").trace;
36const build_options = @import("build_options");36const build_options = @import("build_options");
37const Air = @import("../Air.zig");37const Air = @import("../Air.zig");
38const Liveness = @import("../Liveness.zig");38const Liveness = @import("../Liveness.zig");
39const Value = @import("../value.zig").Value;39const Value = @import("../Value.zig");
4040
41const SpvModule = @import("../codegen/spirv/Module.zig");41const SpvModule = @import("../codegen/spirv/Module.zig");
42const spec = @import("../codegen/spirv/spec.zig");42const spec = @import("../codegen/spirv/spec.zig");
src/link/Wasm.zig+1-1
...@@ -23,7 +23,7 @@ const build_options = @import("build_options");...@@ -23,7 +23,7 @@ const build_options = @import("build_options");
23const wasi_libc = @import("../wasi_libc.zig");23const wasi_libc = @import("../wasi_libc.zig");
24const Cache = std.Build.Cache;24const Cache = std.Build.Cache;
25const Type = @import("../type.zig").Type;25const Type = @import("../type.zig").Type;
26const Value = @import("../value.zig").Value;26const Value = @import("../Value.zig");
27const TypedValue = @import("../TypedValue.zig");27const TypedValue = @import("../TypedValue.zig");
28const LlvmObject = @import("../codegen/llvm.zig").Object;28const LlvmObject = @import("../codegen/llvm.zig").Object;
29const Air = @import("../Air.zig");29const Air = @import("../Air.zig");
src/print_air.zig+1-1
...@@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator;...@@ -3,7 +3,7 @@ const Allocator = std.mem.Allocator;
3const fmtIntSizeBin = std.fmt.fmtIntSizeBin;3const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
44
5const Module = @import("Module.zig");5const Module = @import("Module.zig");
6const Value = @import("value.zig").Value;6const Value = @import("Value.zig");
7const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
8const Air = @import("Air.zig");8const Air = @import("Air.zig");
9const Liveness = @import("Liveness.zig");9const Liveness = @import("Liveness.zig");
src/type.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Value = @import("value.zig").Value;3const Value = @import("Value.zig");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const Target = std.Target;5const Target = std.Target;
6const Module = @import("Module.zig");6const Module = @import("Module.zig");
src/value.zig deleted-4075
...@@ -1,4075 +0,0 @@
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");
13pub const Value = @This();
14
15/// We are migrating towards using this for every Value object. However, many
16/// values are still represented the legacy way. This is indicated by using
17/// InternPool.Index.none.
18ip_index: InternPool.Index,
19
20/// This is the raw data, with no bookkeeping, no memory awareness,
21/// no de-duplication, and no type system awareness.
22/// This union takes advantage of the fact that the first page of memory
23/// is unmapped, giving us 4096 possible enum tags that have no payload.
24legacy: extern union {
25 ptr_otherwise: *Payload,
26},
27
28// Keep in sync with tools/stage2_pretty_printers_common.py
29pub const Tag = enum(usize) {
30 // The first section of this enum are tags that require no payload.
31 // After this, the tag requires a payload.
32
33 /// When the type is error union:
34 /// * If the tag is `.@"error"`, the error union is an error.
35 /// * If the tag is `.eu_payload`, the error union is a payload.
36 /// * A nested error such as `anyerror!(anyerror!T)` in which the the outer error union
37 /// is non-error, but the inner error union is an error, is represented as
38 /// a tag of `.eu_payload`, with a sub-tag of `.@"error"`.
39 eu_payload,
40 /// When the type is optional:
41 /// * If the tag is `.null_value`, the optional is null.
42 /// * If the tag is `.opt_payload`, the optional is a payload.
43 /// * A nested optional such as `??T` in which the the outer optional
44 /// is non-null, but the inner optional is null, is represented as
45 /// a tag of `.opt_payload`, with a sub-tag of `.null_value`.
46 opt_payload,
47 /// Pointer and length as sub `Value` objects.
48 slice,
49 /// A slice of u8 whose memory is managed externally.
50 bytes,
51 /// This value is repeated some number of times. The amount of times to repeat
52 /// is stored externally.
53 repeated,
54 /// An instance of a struct, array, or vector.
55 /// Each element/field stored as a `Value`.
56 /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
57 /// so the slice length will be one more than the type's array length.
58 aggregate,
59 /// An instance of a union.
60 @"union",
61
62 pub fn Type(comptime t: Tag) type {
63 return switch (t) {
64 .eu_payload,
65 .opt_payload,
66 .repeated,
67 => Payload.SubValue,
68 .slice => Payload.Slice,
69 .bytes => Payload.Bytes,
70 .aggregate => Payload.Aggregate,
71 .@"union" => Payload.Union,
72 };
73 }
74
75 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Value {
76 const ptr = try ally.create(t.Type());
77 ptr.* = .{
78 .base = .{ .tag = t },
79 .data = data,
80 };
81 return Value{
82 .ip_index = .none,
83 .legacy = .{ .ptr_otherwise = &ptr.base },
84 };
85 }
86
87 pub fn Data(comptime t: Tag) type {
88 return std.meta.fieldInfo(t.Type(), .data).type;
89 }
90};
91
92pub fn initPayload(payload: *Payload) Value {
93 return Value{
94 .ip_index = .none,
95 .legacy = .{ .ptr_otherwise = payload },
96 };
97}
98
99pub fn tag(self: Value) Tag {
100 assert(self.ip_index == .none);
101 return self.legacy.ptr_otherwise.tag;
102}
103
104/// Prefer `castTag` to this.
105pub fn cast(self: Value, comptime T: type) ?*T {
106 if (self.ip_index != .none) {
107 return null;
108 }
109 if (@hasField(T, "base_tag")) {
110 return self.castTag(T.base_tag);
111 }
112 inline for (@typeInfo(Tag).Enum.fields) |field| {
113 const t = @as(Tag, @enumFromInt(field.value));
114 if (self.legacy.ptr_otherwise.tag == t) {
115 if (T == t.Type()) {
116 return @fieldParentPtr(T, "base", self.legacy.ptr_otherwise);
117 }
118 return null;
119 }
120 }
121 unreachable;
122}
123
124pub fn castTag(self: Value, comptime t: Tag) ?*t.Type() {
125 if (self.ip_index != .none) return null;
126
127 if (self.legacy.ptr_otherwise.tag == t)
128 return @fieldParentPtr(t.Type(), "base", self.legacy.ptr_otherwise);
129
130 return null;
131}
132
133pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
134 _ = val;
135 _ = fmt;
136 _ = options;
137 _ = writer;
138 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
139}
140
141/// This is a debug function. In order to print values in a meaningful way
142/// we also need access to the type.
143pub fn dump(
144 start_val: Value,
145 comptime fmt: []const u8,
146 _: std.fmt.FormatOptions,
147 out_stream: anytype,
148) !void {
149 comptime assert(fmt.len == 0);
150 if (start_val.ip_index != .none) {
151 try out_stream.print("(interned: {})", .{start_val.toIntern()});
152 return;
153 }
154 var val = start_val;
155 while (true) switch (val.tag()) {
156 .aggregate => {
157 return out_stream.writeAll("(aggregate)");
158 },
159 .@"union" => {
160 return out_stream.writeAll("(union value)");
161 },
162 .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
163 .repeated => {
164 try out_stream.writeAll("(repeated) ");
165 val = val.castTag(.repeated).?.data;
166 },
167 .eu_payload => {
168 try out_stream.writeAll("(eu_payload) ");
169 val = val.castTag(.repeated).?.data;
170 },
171 .opt_payload => {
172 try out_stream.writeAll("(opt_payload) ");
173 val = val.castTag(.repeated).?.data;
174 },
175 .slice => return out_stream.writeAll("(slice)"),
176 };
177}
178
179pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) {
180 return .{ .data = val };
181}
182
183pub fn fmtValue(val: Value, ty: Type, mod: *Module) std.fmt.Formatter(TypedValue.format) {
184 return .{ .data = .{
185 .tv = .{ .ty = ty, .val = val },
186 .mod = mod,
187 } };
188}
189
190/// Asserts that the value is representable as an array of bytes.
191/// Returns the value as a null-terminated string stored in the InternPool.
192pub fn toIpString(val: Value, ty: Type, mod: *Module) !InternPool.NullTerminatedString {
193 const ip = &mod.intern_pool;
194 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
195 .enum_literal => |enum_literal| enum_literal,
196 .slice => |slice| try arrayToIpString(val, Value.fromInterned(slice.len).toUnsignedInt(mod), mod),
197 .aggregate => |aggregate| switch (aggregate.storage) {
198 .bytes => |bytes| try ip.getOrPutString(mod.gpa, bytes),
199 .elems => try arrayToIpString(val, ty.arrayLen(mod), mod),
200 .repeated_elem => |elem| {
201 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
202 const len = @as(usize, @intCast(ty.arrayLen(mod)));
203 try ip.string_bytes.appendNTimes(mod.gpa, byte, len);
204 return ip.getOrPutTrailingString(mod.gpa, len);
205 },
206 },
207 else => unreachable,
208 };
209}
210
211/// Asserts that the value is representable as an array of bytes.
212/// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
213pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 {
214 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
215 .enum_literal => |enum_literal| allocator.dupe(u8, mod.intern_pool.stringToSlice(enum_literal)),
216 .slice => |slice| try arrayToAllocatedBytes(val, Value.fromInterned(slice.len).toUnsignedInt(mod), allocator, mod),
217 .aggregate => |aggregate| switch (aggregate.storage) {
218 .bytes => |bytes| try allocator.dupe(u8, bytes),
219 .elems => try arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod),
220 .repeated_elem => |elem| {
221 const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod)));
222 const result = try allocator.alloc(u8, @as(usize, @intCast(ty.arrayLen(mod))));
223 @memset(result, byte);
224 return result;
225 },
226 },
227 else => unreachable,
228 };
229}
230
231fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 {
232 const result = try allocator.alloc(u8, @as(usize, @intCast(len)));
233 for (result, 0..) |*elem, i| {
234 const elem_val = try val.elemValue(mod, i);
235 elem.* = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
236 }
237 return result;
238}
239
240fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTerminatedString {
241 const gpa = mod.gpa;
242 const ip = &mod.intern_pool;
243 const len = @as(usize, @intCast(len_u64));
244 try ip.string_bytes.ensureUnusedCapacity(gpa, len);
245 for (0..len) |i| {
246 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's
247 // assert just to be sure.
248 const prev = ip.string_bytes.items.len;
249 const elem_val = try val.elemValue(mod, i);
250 assert(ip.string_bytes.items.len == prev);
251 const byte = @as(u8, @intCast(elem_val.toUnsignedInt(mod)));
252 ip.string_bytes.appendAssumeCapacity(byte);
253 }
254 return ip.getOrPutTrailingString(gpa, len);
255}
256
257pub fn intern2(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
258 if (val.ip_index != .none) return val.ip_index;
259 return intern(val, ty, mod);
260}
261
262pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
263 if (val.ip_index != .none) return (try mod.getCoerced(val, ty)).toIntern();
264 const ip = &mod.intern_pool;
265 switch (val.tag()) {
266 .eu_payload => {
267 const pl = val.castTag(.eu_payload).?.data;
268 return mod.intern(.{ .error_union = .{
269 .ty = ty.toIntern(),
270 .val = .{ .payload = try pl.intern(ty.errorUnionPayload(mod), mod) },
271 } });
272 },
273 .opt_payload => {
274 const pl = val.castTag(.opt_payload).?.data;
275 return mod.intern(.{ .opt = .{
276 .ty = ty.toIntern(),
277 .val = try pl.intern(ty.optionalChild(mod), mod),
278 } });
279 },
280 .slice => {
281 const pl = val.castTag(.slice).?.data;
282 return mod.intern(.{ .slice = .{
283 .ty = ty.toIntern(),
284 .len = try pl.len.intern(Type.usize, mod),
285 .ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod),
286 } });
287 },
288 .bytes => {
289 const pl = val.castTag(.bytes).?.data;
290 return mod.intern(.{ .aggregate = .{
291 .ty = ty.toIntern(),
292 .storage = .{ .bytes = pl },
293 } });
294 },
295 .repeated => {
296 const pl = val.castTag(.repeated).?.data;
297 return mod.intern(.{ .aggregate = .{
298 .ty = ty.toIntern(),
299 .storage = .{ .repeated_elem = try pl.intern(ty.childType(mod), mod) },
300 } });
301 },
302 .aggregate => {
303 const len = @as(usize, @intCast(ty.arrayLen(mod)));
304 const old_elems = val.castTag(.aggregate).?.data[0..len];
305 const new_elems = try mod.gpa.alloc(InternPool.Index, old_elems.len);
306 defer mod.gpa.free(new_elems);
307 const ty_key = ip.indexToKey(ty.toIntern());
308 for (new_elems, old_elems, 0..) |*new_elem, old_elem, field_i|
309 new_elem.* = try old_elem.intern(switch (ty_key) {
310 .struct_type => ty.structFieldType(field_i, mod),
311 .anon_struct_type => |info| Type.fromInterned(info.types.get(ip)[field_i]),
312 inline .array_type, .vector_type => |info| Type.fromInterned(info.child),
313 else => unreachable,
314 }, mod);
315 return mod.intern(.{ .aggregate = .{
316 .ty = ty.toIntern(),
317 .storage = .{ .elems = new_elems },
318 } });
319 },
320 .@"union" => {
321 const pl = val.castTag(.@"union").?.data;
322 if (pl.tag) |pl_tag| {
323 return mod.intern(.{ .un = .{
324 .ty = ty.toIntern(),
325 .tag = try pl_tag.intern(ty.unionTagTypeHypothetical(mod), mod),
326 .val = try pl.val.intern(ty.unionFieldType(pl_tag, mod).?, mod),
327 } });
328 } else {
329 return mod.intern(.{ .un = .{
330 .ty = ty.toIntern(),
331 .tag = .none,
332 .val = try pl.val.intern(try ty.unionBackingType(mod), mod),
333 } });
334 }
335 },
336 }
337}
338
339pub fn unintern(val: Value, arena: Allocator, mod: *Module) Allocator.Error!Value {
340 return if (val.ip_index == .none) val else switch (mod.intern_pool.indexToKey(val.toIntern())) {
341 .int_type,
342 .ptr_type,
343 .array_type,
344 .vector_type,
345 .opt_type,
346 .anyframe_type,
347 .error_union_type,
348 .simple_type,
349 .struct_type,
350 .anon_struct_type,
351 .union_type,
352 .opaque_type,
353 .enum_type,
354 .func_type,
355 .error_set_type,
356 .inferred_error_set_type,
357
358 .undef,
359 .simple_value,
360 .variable,
361 .extern_func,
362 .func,
363 .int,
364 .err,
365 .enum_literal,
366 .enum_tag,
367 .empty_enum_value,
368 .float,
369 .ptr,
370 => val,
371
372 .error_union => |error_union| switch (error_union.val) {
373 .err_name => val,
374 .payload => |payload| Tag.eu_payload.create(arena, Value.fromInterned(payload)),
375 },
376
377 .slice => |slice| Tag.slice.create(arena, .{
378 .ptr = Value.fromInterned(slice.ptr),
379 .len = Value.fromInterned(slice.len),
380 }),
381
382 .opt => |opt| switch (opt.val) {
383 .none => val,
384 else => |payload| Tag.opt_payload.create(arena, Value.fromInterned(payload)),
385 },
386
387 .aggregate => |aggregate| switch (aggregate.storage) {
388 .bytes => |bytes| Tag.bytes.create(arena, try arena.dupe(u8, bytes)),
389 .elems => |old_elems| {
390 const new_elems = try arena.alloc(Value, old_elems.len);
391 for (new_elems, old_elems) |*new_elem, old_elem| new_elem.* = Value.fromInterned(old_elem);
392 return Tag.aggregate.create(arena, new_elems);
393 },
394 .repeated_elem => |elem| Tag.repeated.create(arena, Value.fromInterned(elem)),
395 },
396
397 .un => |un| Tag.@"union".create(arena, .{
398 // toValue asserts that the value cannot be .none which is valid on unions.
399 .tag = if (un.tag == .none) null else Value.fromInterned(un.tag),
400 .val = Value.fromInterned(un.val),
401 }),
402
403 .memoized_call => unreachable,
404 };
405}
406
407pub fn fromInterned(i: InternPool.Index) Value {
408 assert(i != .none);
409 return .{
410 .ip_index = i,
411 .legacy = undefined,
412 };
413}
414
415pub fn toIntern(val: Value) InternPool.Index {
416 assert(val.ip_index != .none);
417 return val.ip_index;
418}
419
420/// Asserts that the value is representable as a type.
421pub fn toType(self: Value) Type {
422 return Type.fromInterned(self.toIntern());
423}
424
425pub fn intFromEnum(val: Value, ty: Type, mod: *Module) Allocator.Error!Value {
426 const ip = &mod.intern_pool;
427 return switch (ip.indexToKey(ip.typeOf(val.toIntern()))) {
428 // Assume it is already an integer and return it directly.
429 .simple_type, .int_type => val,
430 .enum_literal => |enum_literal| {
431 const field_index = ty.enumFieldIndex(enum_literal, mod).?;
432 return switch (ip.indexToKey(ty.toIntern())) {
433 // Assume it is already an integer and return it directly.
434 .simple_type, .int_type => val,
435 .enum_type => |enum_type| if (enum_type.values.len != 0)
436 Value.fromInterned(enum_type.values.get(ip)[field_index])
437 else // Field index and integer values are the same.
438 mod.intValue(Type.fromInterned(enum_type.tag_ty), field_index),
439 else => unreachable,
440 };
441 },
442 .enum_type => |enum_type| try mod.getCoerced(val, Type.fromInterned(enum_type.tag_ty)),
443 else => unreachable,
444 };
445}
446
447/// Asserts the value is an integer.
448pub fn toBigInt(val: Value, space: *BigIntSpace, mod: *Module) BigIntConst {
449 return val.toBigIntAdvanced(space, mod, null) catch unreachable;
450}
451
452/// Asserts the value is an integer.
453pub fn toBigIntAdvanced(
454 val: Value,
455 space: *BigIntSpace,
456 mod: *Module,
457 opt_sema: ?*Sema,
458) Module.CompileError!BigIntConst {
459 return switch (val.toIntern()) {
460 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
461 .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
462 .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
463 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
464 .int => |int| switch (int.storage) {
465 .u64, .i64, .big_int => int.storage.toBigInt(space),
466 .lazy_align, .lazy_size => |ty| {
467 if (opt_sema) |sema| try sema.resolveTypeLayout(Type.fromInterned(ty));
468 const x = switch (int.storage) {
469 else => unreachable,
470 .lazy_align => Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
471 .lazy_size => Type.fromInterned(ty).abiSize(mod),
472 };
473 return BigIntMutable.init(&space.limbs, x).toConst();
474 },
475 },
476 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, mod, opt_sema),
477 .opt, .ptr => BigIntMutable.init(
478 &space.limbs,
479 (try val.getUnsignedIntAdvanced(mod, opt_sema)).?,
480 ).toConst(),
481 else => unreachable,
482 },
483 };
484}
485
486pub fn isFuncBody(val: Value, mod: *Module) bool {
487 return mod.intern_pool.isFuncBody(val.toIntern());
488}
489
490pub fn getFunction(val: Value, mod: *Module) ?InternPool.Key.Func {
491 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
492 .func => |x| x,
493 else => null,
494 } else null;
495}
496
497pub fn getExternFunc(val: Value, mod: *Module) ?InternPool.Key.ExternFunc {
498 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
499 .extern_func => |extern_func| extern_func,
500 else => null,
501 } else null;
502}
503
504pub fn getVariable(val: Value, mod: *Module) ?InternPool.Key.Variable {
505 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
506 .variable => |variable| variable,
507 else => null,
508 } else null;
509}
510
511/// If the value fits in a u64, return it, otherwise null.
512/// Asserts not undefined.
513pub fn getUnsignedInt(val: Value, mod: *Module) ?u64 {
514 return getUnsignedIntAdvanced(val, mod, null) catch unreachable;
515}
516
517/// If the value fits in a u64, return it, otherwise null.
518/// Asserts not undefined.
519pub fn getUnsignedIntAdvanced(val: Value, mod: *Module, opt_sema: ?*Sema) !?u64 {
520 return switch (val.toIntern()) {
521 .undef => unreachable,
522 .bool_false => 0,
523 .bool_true => 1,
524 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
525 .undef => unreachable,
526 .int => |int| switch (int.storage) {
527 .big_int => |big_int| big_int.to(u64) catch null,
528 .u64 => |x| x,
529 .i64 => |x| std.math.cast(u64, x),
530 .lazy_align => |ty| if (opt_sema) |sema|
531 (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0)
532 else
533 Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
534 .lazy_size => |ty| if (opt_sema) |sema|
535 (try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar
536 else
537 Type.fromInterned(ty).abiSize(mod),
538 },
539 .ptr => |ptr| switch (ptr.addr) {
540 .int => |int| Value.fromInterned(int).getUnsignedIntAdvanced(mod, opt_sema),
541 .elem => |elem| {
542 const base_addr = (try Value.fromInterned(elem.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
543 const elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod);
544 return base_addr + elem.index * elem_ty.abiSize(mod);
545 },
546 .field => |field| {
547 const base_addr = (try Value.fromInterned(field.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
548 const struct_ty = Type.fromInterned(mod.intern_pool.typeOf(field.base)).childType(mod);
549 if (opt_sema) |sema| try sema.resolveTypeLayout(struct_ty);
550 return base_addr + struct_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod);
551 },
552 else => null,
553 },
554 .opt => |opt| switch (opt.val) {
555 .none => 0,
556 else => |payload| Value.fromInterned(payload).getUnsignedIntAdvanced(mod, opt_sema),
557 },
558 else => null,
559 },
560 };
561}
562
563/// Asserts the value is an integer and it fits in a u64
564pub fn toUnsignedInt(val: Value, mod: *Module) u64 {
565 return getUnsignedInt(val, mod).?;
566}
567
568/// Asserts the value is an integer and it fits in a u64
569pub fn toUnsignedIntAdvanced(val: Value, sema: *Sema) !u64 {
570 return (try getUnsignedIntAdvanced(val, sema.mod, sema)).?;
571}
572
573/// Asserts the value is an integer and it fits in a i64
574pub fn toSignedInt(val: Value, mod: *Module) i64 {
575 return switch (val.toIntern()) {
576 .bool_false => 0,
577 .bool_true => 1,
578 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
579 .int => |int| switch (int.storage) {
580 .big_int => |big_int| big_int.to(i64) catch unreachable,
581 .i64 => |x| x,
582 .u64 => |x| @intCast(x),
583 .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
584 .lazy_size => |ty| @intCast(Type.fromInterned(ty).abiSize(mod)),
585 },
586 else => unreachable,
587 },
588 };
589}
590
591pub fn toBool(val: Value) bool {
592 return switch (val.toIntern()) {
593 .bool_true => true,
594 .bool_false => false,
595 else => unreachable,
596 };
597}
598
599fn isDeclRef(val: Value, mod: *Module) bool {
600 var check = val;
601 while (true) switch (mod.intern_pool.indexToKey(check.toIntern())) {
602 .ptr => |ptr| switch (ptr.addr) {
603 .decl, .mut_decl, .comptime_field, .anon_decl => return true,
604 .eu_payload, .opt_payload => |base| check = Value.fromInterned(base),
605 .elem, .field => |base_index| check = Value.fromInterned(base_index.base),
606 .int => return false,
607 },
608 else => return false,
609 };
610}
611
612/// Write a Value's contents to `buffer`.
613///
614/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
615/// the end of the value in memory.
616pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{
617 ReinterpretDeclRef,
618 IllDefinedMemoryLayout,
619 Unimplemented,
620 OutOfMemory,
621}!void {
622 const target = mod.getTarget();
623 const endian = target.cpu.arch.endian();
624 if (val.isUndef(mod)) {
625 const size: usize = @intCast(ty.abiSize(mod));
626 @memset(buffer[0..size], 0xaa);
627 return;
628 }
629 const ip = &mod.intern_pool;
630 switch (ty.zigTypeTag(mod)) {
631 .Void => {},
632 .Bool => {
633 buffer[0] = @intFromBool(val.toBool());
634 },
635 .Int, .Enum => {
636 const int_info = ty.intInfo(mod);
637 const bits = int_info.bits;
638 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
639
640 var bigint_buffer: BigIntSpace = undefined;
641 const bigint = val.toBigInt(&bigint_buffer, mod);
642 bigint.writeTwosComplement(buffer[0..byte_count], endian);
643 },
644 .Float => switch (ty.floatBits(target)) {
645 16 => std.mem.writeInt(u16, buffer[0..2], @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
646 32 => std.mem.writeInt(u32, buffer[0..4], @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
647 64 => std.mem.writeInt(u64, buffer[0..8], @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
648 80 => std.mem.writeInt(u80, buffer[0..10], @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
649 128 => std.mem.writeInt(u128, buffer[0..16], @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
650 else => unreachable,
651 },
652 .Array => {
653 const len = ty.arrayLen(mod);
654 const elem_ty = ty.childType(mod);
655 const elem_size = @as(usize, @intCast(elem_ty.abiSize(mod)));
656 var elem_i: usize = 0;
657 var buf_off: usize = 0;
658 while (elem_i < len) : (elem_i += 1) {
659 const elem_val = try val.elemValue(mod, elem_i);
660 try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]);
661 buf_off += elem_size;
662 }
663 },
664 .Vector => {
665 // We use byte_count instead of abi_size here, so that any padding bytes
666 // follow the data bytes, on both big- and little-endian systems.
667 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
668 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
669 },
670 .Struct => {
671 const struct_type = mod.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;
672 switch (struct_type.layout) {
673 .Auto => return error.IllDefinedMemoryLayout,
674 .Extern => for (0..struct_type.field_types.len) |i| {
675 const off: usize = @intCast(ty.structFieldOffset(i, mod));
676 const field_val = switch (val.ip_index) {
677 .none => switch (val.tag()) {
678 .bytes => {
679 buffer[off] = val.castTag(.bytes).?.data[i];
680 continue;
681 },
682 .aggregate => val.castTag(.aggregate).?.data[i],
683 .repeated => val.castTag(.repeated).?.data,
684 else => unreachable,
685 },
686 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
687 .bytes => |bytes| {
688 buffer[off] = bytes[i];
689 continue;
690 },
691 .elems => |elems| elems[i],
692 .repeated_elem => |elem| elem,
693 }),
694 };
695 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
696 try writeToMemory(field_val, field_ty, mod, buffer[off..]);
697 },
698 .Packed => {
699 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
700 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
701 },
702 }
703 },
704 .ErrorSet => {
705 const bits = mod.errorSetBits();
706 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
707
708 const name = switch (ip.indexToKey(val.toIntern())) {
709 .err => |err| err.name,
710 .error_union => |error_union| error_union.val.err_name,
711 else => unreachable,
712 };
713 var bigint_buffer: BigIntSpace = undefined;
714 const bigint = BigIntMutable.init(
715 &bigint_buffer.limbs,
716 mod.global_error_set.getIndex(name).?,
717 ).toConst();
718 bigint.writeTwosComplement(buffer[0..byte_count], endian);
719 },
720 .Union => switch (ty.containerLayout(mod)) {
721 .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
722 .Extern => {
723 if (val.unionTag(mod)) |union_tag| {
724 const union_obj = mod.typeToUnion(ty).?;
725 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
726 const field_type = Type.fromInterned(union_obj.field_types.get(&mod.intern_pool)[field_index]);
727 const field_val = try val.fieldValue(mod, field_index);
728 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
729 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
730 } else {
731 const backing_ty = try ty.unionBackingType(mod);
732 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
733 return writeToMemory(val.unionValue(mod), backing_ty, mod, buffer[0..byte_count]);
734 }
735 },
736 .Packed => {
737 const backing_ty = try ty.unionBackingType(mod);
738 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
739 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
740 },
741 },
742 .Pointer => {
743 if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout;
744 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
745 return val.writeToMemory(Type.usize, mod, buffer);
746 },
747 .Optional => {
748 if (!ty.isPtrLikeOptional(mod)) return error.IllDefinedMemoryLayout;
749 const child = ty.optionalChild(mod);
750 const opt_val = val.optionalValue(mod);
751 if (opt_val) |some| {
752 return some.writeToMemory(child, mod, buffer);
753 } else {
754 return writeToMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer);
755 }
756 },
757 else => return error.Unimplemented,
758 }
759}
760
761/// Write a Value's contents to `buffer`.
762///
763/// Both the start and the end of the provided buffer must be tight, since
764/// big-endian packed memory layouts start at the end of the buffer.
765pub fn writeToPackedMemory(
766 val: Value,
767 ty: Type,
768 mod: *Module,
769 buffer: []u8,
770 bit_offset: usize,
771) error{ ReinterpretDeclRef, OutOfMemory }!void {
772 const ip = &mod.intern_pool;
773 const target = mod.getTarget();
774 const endian = target.cpu.arch.endian();
775 if (val.isUndef(mod)) {
776 const bit_size = @as(usize, @intCast(ty.bitSize(mod)));
777 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian);
778 return;
779 }
780 switch (ty.zigTypeTag(mod)) {
781 .Void => {},
782 .Bool => {
783 const byte_index = switch (endian) {
784 .little => bit_offset / 8,
785 .big => buffer.len - bit_offset / 8 - 1,
786 };
787 if (val.toBool()) {
788 buffer[byte_index] |= (@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
789 } else {
790 buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
791 }
792 },
793 .Int, .Enum => {
794 if (buffer.len == 0) return;
795 const bits = ty.intInfo(mod).bits;
796 if (bits == 0) return;
797
798 switch (ip.indexToKey((try val.intFromEnum(ty, mod)).toIntern()).int.storage) {
799 inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
800 .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
801 .lazy_align => |lazy_align| {
802 const num = Type.fromInterned(lazy_align).abiAlignment(mod).toByteUnits(0);
803 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
804 },
805 .lazy_size => |lazy_size| {
806 const num = Type.fromInterned(lazy_size).abiSize(mod);
807 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
808 },
809 }
810 },
811 .Float => switch (ty.floatBits(target)) {
812 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
813 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
814 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
815 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
816 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
817 else => unreachable,
818 },
819 .Vector => {
820 const elem_ty = ty.childType(mod);
821 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
822 const len = @as(usize, @intCast(ty.arrayLen(mod)));
823
824 var bits: u16 = 0;
825 var elem_i: usize = 0;
826 while (elem_i < len) : (elem_i += 1) {
827 // On big-endian systems, LLVM reverses the element order of vectors by default
828 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
829 const elem_val = try val.elemValue(mod, tgt_elem_i);
830 try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
831 bits += elem_bit_size;
832 }
833 },
834 .Struct => {
835 const struct_type = ip.indexToKey(ty.toIntern()).struct_type;
836 // Sema is supposed to have emitted a compile error already in the case of Auto,
837 // and Extern is handled in non-packed writeToMemory.
838 assert(struct_type.layout == .Packed);
839 var bits: u16 = 0;
840 for (0..struct_type.field_types.len) |i| {
841 const field_val = switch (val.ip_index) {
842 .none => switch (val.tag()) {
843 .bytes => unreachable,
844 .aggregate => val.castTag(.aggregate).?.data[i],
845 .repeated => val.castTag(.repeated).?.data,
846 else => unreachable,
847 },
848 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
849 .bytes => unreachable,
850 .elems => |elems| elems[i],
851 .repeated_elem => |elem| elem,
852 }),
853 };
854 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
855 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
856 try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
857 bits += field_bits;
858 }
859 },
860 .Union => {
861 const union_obj = mod.typeToUnion(ty).?;
862 switch (union_obj.getLayout(ip)) {
863 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
864 .Packed => {
865 if (val.unionTag(mod)) |union_tag| {
866 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
867 const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
868 const field_val = try val.fieldValue(mod, field_index);
869 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
870 } else {
871 const backing_ty = try ty.unionBackingType(mod);
872 return val.unionValue(mod).writeToPackedMemory(backing_ty, mod, buffer, bit_offset);
873 }
874 },
875 }
876 },
877 .Pointer => {
878 assert(!ty.isSlice(mod)); // No well defined layout.
879 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
880 return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset);
881 },
882 .Optional => {
883 assert(ty.isPtrLikeOptional(mod));
884 const child = ty.optionalChild(mod);
885 const opt_val = val.optionalValue(mod);
886 if (opt_val) |some| {
887 return some.writeToPackedMemory(child, mod, buffer, bit_offset);
888 } else {
889 return writeToPackedMemory(try mod.intValue(Type.usize, 0), Type.usize, mod, buffer, bit_offset);
890 }
891 },
892 else => @panic("TODO implement writeToPackedMemory for more types"),
893 }
894}
895
896/// Load a Value from the contents of `buffer`.
897///
898/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
899/// the end of the value in memory.
900pub fn readFromMemory(
901 ty: Type,
902 mod: *Module,
903 buffer: []const u8,
904 arena: Allocator,
905) error{
906 IllDefinedMemoryLayout,
907 Unimplemented,
908 OutOfMemory,
909}!Value {
910 const ip = &mod.intern_pool;
911 const target = mod.getTarget();
912 const endian = target.cpu.arch.endian();
913 switch (ty.zigTypeTag(mod)) {
914 .Void => return Value.void,
915 .Bool => {
916 if (buffer[0] == 0) {
917 return Value.false;
918 } else {
919 return Value.true;
920 }
921 },
922 .Int, .Enum => |ty_tag| {
923 const int_ty = switch (ty_tag) {
924 .Int => ty,
925 .Enum => ty.intTagType(mod),
926 else => unreachable,
927 };
928 const int_info = int_ty.intInfo(mod);
929 const bits = int_info.bits;
930 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
931 if (bits == 0 or buffer.len == 0) return mod.getCoerced(try mod.intValue(int_ty, 0), ty);
932
933 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
934 .signed => {
935 const val = std.mem.readVarInt(i64, buffer[0..byte_count], endian);
936 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
937 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
938 },
939 .unsigned => {
940 const val = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
941 const result = (val << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
942 return mod.getCoerced(try mod.intValue(int_ty, result), ty);
943 },
944 } else { // Slow path, we have to construct a big-int
945 const Limb = std.math.big.Limb;
946 const limb_count = (byte_count + @sizeOf(Limb) - 1) / @sizeOf(Limb);
947 const limbs_buffer = try arena.alloc(Limb, limb_count);
948
949 var bigint = BigIntMutable.init(limbs_buffer, 0);
950 bigint.readTwosComplement(buffer[0..byte_count], bits, endian, int_info.signedness);
951 return mod.getCoerced(try mod.intValue_big(int_ty, bigint.toConst()), ty);
952 }
953 },
954 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
955 .ty = ty.toIntern(),
956 .storage = switch (ty.floatBits(target)) {
957 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readInt(u16, buffer[0..2], endian))) },
958 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readInt(u32, buffer[0..4], endian))) },
959 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readInt(u64, buffer[0..8], endian))) },
960 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readInt(u80, buffer[0..10], endian))) },
961 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readInt(u128, buffer[0..16], endian))) },
962 else => unreachable,
963 },
964 } }))),
965 .Array => {
966 const elem_ty = ty.childType(mod);
967 const elem_size = elem_ty.abiSize(mod);
968 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
969 var offset: usize = 0;
970 for (elems) |*elem| {
971 elem.* = try (try readFromMemory(elem_ty, mod, buffer[offset..], arena)).intern(elem_ty, mod);
972 offset += @as(usize, @intCast(elem_size));
973 }
974 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
975 .ty = ty.toIntern(),
976 .storage = .{ .elems = elems },
977 } })));
978 },
979 .Vector => {
980 // We use byte_count instead of abi_size here, so that any padding bytes
981 // follow the data bytes, on both big- and little-endian systems.
982 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
983 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
984 },
985 .Struct => {
986 const struct_type = mod.typeToStruct(ty).?;
987 switch (struct_type.layout) {
988 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
989 .Extern => {
990 const field_types = struct_type.field_types;
991 const field_vals = try arena.alloc(InternPool.Index, field_types.len);
992 for (field_vals, 0..) |*field_val, i| {
993 const field_ty = Type.fromInterned(field_types.get(ip)[i]);
994 const off: usize = @intCast(ty.structFieldOffset(i, mod));
995 const sz: usize = @intCast(field_ty.abiSize(mod));
996 field_val.* = try (try readFromMemory(field_ty, mod, buffer[off..(off + sz)], arena)).intern(field_ty, mod);
997 }
998 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
999 .ty = ty.toIntern(),
1000 .storage = .{ .elems = field_vals },
1001 } })));
1002 },
1003 .Packed => {
1004 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1005 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1006 },
1007 }
1008 },
1009 .ErrorSet => {
1010 const bits = mod.errorSetBits();
1011 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
1012 const int = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
1013 const index = (int << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));
1014 const name = mod.global_error_set.keys()[@intCast(index)];
1015
1016 return Value.fromInterned((try mod.intern(.{ .err = .{
1017 .ty = ty.toIntern(),
1018 .name = name,
1019 } })));
1020 },
1021 .Union => switch (ty.containerLayout(mod)) {
1022 .Auto => return error.IllDefinedMemoryLayout,
1023 .Extern => {
1024 const union_size = ty.abiSize(mod);
1025 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
1026 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
1027 return Value.fromInterned((try mod.intern(.{ .un = .{
1028 .ty = ty.toIntern(),
1029 .tag = .none,
1030 .val = val,
1031 } })));
1032 },
1033 .Packed => {
1034 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1035 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1036 },
1037 },
1038 .Pointer => {
1039 assert(!ty.isSlice(mod)); // No well defined layout.
1040 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
1041 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1042 .ty = ty.toIntern(),
1043 .addr = .{ .int = int_val.toIntern() },
1044 } })));
1045 },
1046 .Optional => {
1047 assert(ty.isPtrLikeOptional(mod));
1048 const child_ty = ty.optionalChild(mod);
1049 const child_val = try readFromMemory(child_ty, mod, buffer, arena);
1050 return Value.fromInterned((try mod.intern(.{ .opt = .{
1051 .ty = ty.toIntern(),
1052 .val = switch (child_val.orderAgainstZero(mod)) {
1053 .lt => unreachable,
1054 .eq => .none,
1055 .gt => child_val.toIntern(),
1056 },
1057 } })));
1058 },
1059 else => return error.Unimplemented,
1060 }
1061}
1062
1063/// Load a Value from the contents of `buffer`.
1064///
1065/// Both the start and the end of the provided buffer must be tight, since
1066/// big-endian packed memory layouts start at the end of the buffer.
1067pub fn readFromPackedMemory(
1068 ty: Type,
1069 mod: *Module,
1070 buffer: []const u8,
1071 bit_offset: usize,
1072 arena: Allocator,
1073) error{
1074 IllDefinedMemoryLayout,
1075 OutOfMemory,
1076}!Value {
1077 const ip = &mod.intern_pool;
1078 const target = mod.getTarget();
1079 const endian = target.cpu.arch.endian();
1080 switch (ty.zigTypeTag(mod)) {
1081 .Void => return Value.void,
1082 .Bool => {
1083 const byte = switch (endian) {
1084 .big => buffer[buffer.len - bit_offset / 8 - 1],
1085 .little => buffer[bit_offset / 8],
1086 };
1087 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) {
1088 return Value.false;
1089 } else {
1090 return Value.true;
1091 }
1092 },
1093 .Int, .Enum => |ty_tag| {
1094 if (buffer.len == 0) return mod.intValue(ty, 0);
1095 const int_info = ty.intInfo(mod);
1096 const bits = int_info.bits;
1097 if (bits == 0) return mod.intValue(ty, 0);
1098
1099 // Fast path for integers <= u64
1100 if (bits <= 64) {
1101 const int_ty = switch (ty_tag) {
1102 .Int => ty,
1103 .Enum => ty.intTagType(mod),
1104 else => unreachable,
1105 };
1106 return mod.getCoerced(switch (int_info.signedness) {
1107 .signed => return mod.intValue(
1108 int_ty,
1109 std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed),
1110 ),
1111 .unsigned => return mod.intValue(
1112 int_ty,
1113 std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned),
1114 ),
1115 }, ty);
1116 }
1117
1118 // Slow path, we have to construct a big-int
1119 const abi_size = @as(usize, @intCast(ty.abiSize(mod)));
1120 const Limb = std.math.big.Limb;
1121 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1122 const limbs_buffer = try arena.alloc(Limb, limb_count);
1123
1124 var bigint = BigIntMutable.init(limbs_buffer, 0);
1125 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
1126 return mod.intValue_big(ty, bigint.toConst());
1127 },
1128 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{
1129 .ty = ty.toIntern(),
1130 .storage = switch (ty.floatBits(target)) {
1131 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian))) },
1132 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian))) },
1133 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian))) },
1134 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian))) },
1135 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian))) },
1136 else => unreachable,
1137 },
1138 } }))),
1139 .Vector => {
1140 const elem_ty = ty.childType(mod);
1141 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));
1142
1143 var bits: u16 = 0;
1144 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
1145 for (elems, 0..) |_, i| {
1146 // On big-endian systems, LLVM reverses the element order of vectors by default
1147 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;
1148 elems[tgt_elem_i] = try (try readFromPackedMemory(elem_ty, mod, buffer, bit_offset + bits, arena)).intern(elem_ty, mod);
1149 bits += elem_bit_size;
1150 }
1151 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1152 .ty = ty.toIntern(),
1153 .storage = .{ .elems = elems },
1154 } })));
1155 },
1156 .Struct => {
1157 // Sema is supposed to have emitted a compile error already for Auto layout structs,
1158 // and Extern is handled by non-packed readFromMemory.
1159 const struct_type = mod.typeToPackedStruct(ty).?;
1160 var bits: u16 = 0;
1161 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
1162 for (field_vals, 0..) |*field_val, i| {
1163 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
1164 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
1165 field_val.* = try (try readFromPackedMemory(field_ty, mod, buffer, bit_offset + bits, arena)).intern(field_ty, mod);
1166 bits += field_bits;
1167 }
1168 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1169 .ty = ty.toIntern(),
1170 .storage = .{ .elems = field_vals },
1171 } })));
1172 },
1173 .Union => switch (ty.containerLayout(mod)) {
1174 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
1175 .Packed => {
1176 const backing_ty = try ty.unionBackingType(mod);
1177 const val = (try readFromPackedMemory(backing_ty, mod, buffer, bit_offset, arena)).toIntern();
1178 return Value.fromInterned((try mod.intern(.{ .un = .{
1179 .ty = ty.toIntern(),
1180 .tag = .none,
1181 .val = val,
1182 } })));
1183 },
1184 },
1185 .Pointer => {
1186 assert(!ty.isSlice(mod)); // No well defined layout.
1187 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
1188 },
1189 .Optional => {
1190 assert(ty.isPtrLikeOptional(mod));
1191 const child = ty.optionalChild(mod);
1192 return readFromPackedMemory(child, mod, buffer, bit_offset, arena);
1193 },
1194 else => @panic("TODO implement readFromPackedMemory for more types"),
1195 }
1196}
1197
1198/// Asserts that the value is a float or an integer.
1199pub fn toFloat(val: Value, comptime T: type, mod: *Module) T {
1200 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1201 .int => |int| switch (int.storage) {
1202 .big_int => |big_int| @floatCast(bigIntToFloat(big_int.limbs, big_int.positive)),
1203 inline .u64, .i64 => |x| {
1204 if (T == f80) {
1205 @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
1206 }
1207 return @floatFromInt(x);
1208 },
1209 .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
1210 .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(mod)),
1211 },
1212 .float => |float| switch (float.storage) {
1213 inline else => |x| @floatCast(x),
1214 },
1215 else => unreachable,
1216 };
1217}
1218
1219/// TODO move this to std lib big int code
1220fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
1221 if (limbs.len == 0) return 0;
1222
1223 const base = std.math.maxInt(std.math.big.Limb) + 1;
1224 var result: f128 = 0;
1225 var i: usize = limbs.len;
1226 while (i != 0) {
1227 i -= 1;
1228 const limb: f128 = @as(f128, @floatFromInt(limbs[i]));
1229 result = @mulAdd(f128, base, result, limb);
1230 }
1231 if (positive) {
1232 return result;
1233 } else {
1234 return -result;
1235 }
1236}
1237
1238pub fn clz(val: Value, ty: Type, mod: *Module) u64 {
1239 var bigint_buf: BigIntSpace = undefined;
1240 const bigint = val.toBigInt(&bigint_buf, mod);
1241 return bigint.clz(ty.intInfo(mod).bits);
1242}
1243
1244pub fn ctz(val: Value, ty: Type, mod: *Module) u64 {
1245 var bigint_buf: BigIntSpace = undefined;
1246 const bigint = val.toBigInt(&bigint_buf, mod);
1247 return bigint.ctz(ty.intInfo(mod).bits);
1248}
1249
1250pub fn popCount(val: Value, ty: Type, mod: *Module) u64 {
1251 var bigint_buf: BigIntSpace = undefined;
1252 const bigint = val.toBigInt(&bigint_buf, mod);
1253 return @as(u64, @intCast(bigint.popCount(ty.intInfo(mod).bits)));
1254}
1255
1256pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1257 const info = ty.intInfo(mod);
1258
1259 var buffer: Value.BigIntSpace = undefined;
1260 const operand_bigint = val.toBigInt(&buffer, mod);
1261
1262 const limbs = try arena.alloc(
1263 std.math.big.Limb,
1264 std.math.big.int.calcTwosCompLimbCount(info.bits),
1265 );
1266 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1267 result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
1268
1269 return mod.intValue_big(ty, result_bigint.toConst());
1270}
1271
1272pub fn byteSwap(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1273 const info = ty.intInfo(mod);
1274
1275 // Bit count must be evenly divisible by 8
1276 assert(info.bits % 8 == 0);
1277
1278 var buffer: Value.BigIntSpace = undefined;
1279 const operand_bigint = val.toBigInt(&buffer, mod);
1280
1281 const limbs = try arena.alloc(
1282 std.math.big.Limb,
1283 std.math.big.int.calcTwosCompLimbCount(info.bits),
1284 );
1285 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1286 result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8);
1287
1288 return mod.intValue_big(ty, result_bigint.toConst());
1289}
1290
1291/// Asserts the value is an integer and not undefined.
1292/// Returns the number of bits the value requires to represent stored in twos complement form.
1293pub fn intBitCountTwosComp(self: Value, mod: *Module) usize {
1294 var buffer: BigIntSpace = undefined;
1295 const big_int = self.toBigInt(&buffer, mod);
1296 return big_int.bitCountTwosComp();
1297}
1298
1299/// Converts an integer or a float to a float. May result in a loss of information.
1300/// Caller can find out by equality checking the result against the operand.
1301pub fn floatCast(self: Value, dest_ty: Type, mod: *Module) !Value {
1302 const target = mod.getTarget();
1303 return Value.fromInterned((try mod.intern(.{ .float = .{
1304 .ty = dest_ty.toIntern(),
1305 .storage = switch (dest_ty.floatBits(target)) {
1306 16 => .{ .f16 = self.toFloat(f16, mod) },
1307 32 => .{ .f32 = self.toFloat(f32, mod) },
1308 64 => .{ .f64 = self.toFloat(f64, mod) },
1309 80 => .{ .f80 = self.toFloat(f80, mod) },
1310 128 => .{ .f128 = self.toFloat(f128, mod) },
1311 else => unreachable,
1312 },
1313 } })));
1314}
1315
1316/// Asserts the value is a float
1317pub fn floatHasFraction(self: Value, mod: *const Module) bool {
1318 return switch (mod.intern_pool.indexToKey(self.toIntern())) {
1319 .float => |float| switch (float.storage) {
1320 inline else => |x| @rem(x, 1) != 0,
1321 },
1322 else => unreachable,
1323 };
1324}
1325
1326pub fn orderAgainstZero(lhs: Value, mod: *Module) std.math.Order {
1327 return orderAgainstZeroAdvanced(lhs, mod, null) catch unreachable;
1328}
1329
1330pub fn orderAgainstZeroAdvanced(
1331 lhs: Value,
1332 mod: *Module,
1333 opt_sema: ?*Sema,
1334) Module.CompileError!std.math.Order {
1335 return switch (lhs.toIntern()) {
1336 .bool_false => .eq,
1337 .bool_true => .gt,
1338 else => switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1339 .ptr => |ptr| switch (ptr.addr) {
1340 .decl, .mut_decl, .comptime_field => .gt,
1341 .int => |int| Value.fromInterned(int).orderAgainstZeroAdvanced(mod, opt_sema),
1342 .elem => |elem| switch (try Value.fromInterned(elem.base).orderAgainstZeroAdvanced(mod, opt_sema)) {
1343 .lt => unreachable,
1344 .gt => .gt,
1345 .eq => if (elem.index == 0) .eq else .gt,
1346 },
1347 else => unreachable,
1348 },
1349 .int => |int| switch (int.storage) {
1350 .big_int => |big_int| big_int.orderAgainstScalar(0),
1351 inline .u64, .i64 => |x| std.math.order(x, 0),
1352 .lazy_align => .gt, // alignment is never 0
1353 .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsAdvanced(
1354 mod,
1355 false,
1356 if (opt_sema) |sema| .{ .sema = sema } else .eager,
1357 ) catch |err| switch (err) {
1358 error.NeedLazy => unreachable,
1359 else => |e| return e,
1360 }) .gt else .eq,
1361 },
1362 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroAdvanced(mod, opt_sema),
1363 .float => |float| switch (float.storage) {
1364 inline else => |x| std.math.order(x, 0),
1365 },
1366 else => unreachable,
1367 },
1368 };
1369}
1370
1371/// Asserts the value is comparable.
1372pub fn order(lhs: Value, rhs: Value, mod: *Module) std.math.Order {
1373 return orderAdvanced(lhs, rhs, mod, null) catch unreachable;
1374}
1375
1376/// Asserts the value is comparable.
1377/// If opt_sema is null then this function asserts things are resolved and cannot fail.
1378pub fn orderAdvanced(lhs: Value, rhs: Value, mod: *Module, opt_sema: ?*Sema) !std.math.Order {
1379 const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(mod, opt_sema);
1380 const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(mod, opt_sema);
1381 switch (lhs_against_zero) {
1382 .lt => if (rhs_against_zero != .lt) return .lt,
1383 .eq => return rhs_against_zero.invert(),
1384 .gt => {},
1385 }
1386 switch (rhs_against_zero) {
1387 .lt => if (lhs_against_zero != .lt) return .gt,
1388 .eq => return lhs_against_zero,
1389 .gt => {},
1390 }
1391
1392 if (lhs.isFloat(mod) or rhs.isFloat(mod)) {
1393 const lhs_f128 = lhs.toFloat(f128, mod);
1394 const rhs_f128 = rhs.toFloat(f128, mod);
1395 return std.math.order(lhs_f128, rhs_f128);
1396 }
1397
1398 var lhs_bigint_space: BigIntSpace = undefined;
1399 var rhs_bigint_space: BigIntSpace = undefined;
1400 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, mod, opt_sema);
1401 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, mod, opt_sema);
1402 return lhs_bigint.order(rhs_bigint);
1403}
1404
1405/// Asserts the value is comparable. Does not take a type parameter because it supports
1406/// comparisons between heterogeneous types.
1407pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, mod: *Module) bool {
1408 return compareHeteroAdvanced(lhs, op, rhs, mod, null) catch unreachable;
1409}
1410
1411pub fn compareHeteroAdvanced(
1412 lhs: Value,
1413 op: std.math.CompareOperator,
1414 rhs: Value,
1415 mod: *Module,
1416 opt_sema: ?*Sema,
1417) !bool {
1418 if (lhs.pointerDecl(mod)) |lhs_decl| {
1419 if (rhs.pointerDecl(mod)) |rhs_decl| {
1420 switch (op) {
1421 .eq => return lhs_decl == rhs_decl,
1422 .neq => return lhs_decl != rhs_decl,
1423 else => {},
1424 }
1425 } else {
1426 switch (op) {
1427 .eq => return false,
1428 .neq => return true,
1429 else => {},
1430 }
1431 }
1432 } else if (rhs.pointerDecl(mod)) |_| {
1433 switch (op) {
1434 .eq => return false,
1435 .neq => return true,
1436 else => {},
1437 }
1438 }
1439 return (try orderAdvanced(lhs, rhs, mod, opt_sema)).compare(op);
1440}
1441
1442/// Asserts the values are comparable. Both operands have type `ty`.
1443/// For vectors, returns true if comparison is true for ALL elements.
1444pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) !bool {
1445 if (ty.zigTypeTag(mod) == .Vector) {
1446 const scalar_ty = ty.scalarType(mod);
1447 for (0..ty.vectorLen(mod)) |i| {
1448 const lhs_elem = try lhs.elemValue(mod, i);
1449 const rhs_elem = try rhs.elemValue(mod, i);
1450 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, mod)) {
1451 return false;
1452 }
1453 }
1454 return true;
1455 }
1456 return compareScalar(lhs, op, rhs, ty, mod);
1457}
1458
1459/// Asserts the values are comparable. Both operands have type `ty`.
1460pub fn compareScalar(
1461 lhs: Value,
1462 op: std.math.CompareOperator,
1463 rhs: Value,
1464 ty: Type,
1465 mod: *Module,
1466) bool {
1467 return switch (op) {
1468 .eq => lhs.eql(rhs, ty, mod),
1469 .neq => !lhs.eql(rhs, ty, mod),
1470 else => compareHetero(lhs, op, rhs, mod),
1471 };
1472}
1473
1474/// Asserts the value is comparable.
1475/// For vectors, returns true if comparison is true for ALL elements.
1476///
1477/// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
1478pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, mod: *Module) bool {
1479 return compareAllWithZeroAdvancedExtra(lhs, op, mod, null) catch unreachable;
1480}
1481
1482pub fn compareAllWithZeroAdvanced(
1483 lhs: Value,
1484 op: std.math.CompareOperator,
1485 sema: *Sema,
1486) Module.CompileError!bool {
1487 return compareAllWithZeroAdvancedExtra(lhs, op, sema.mod, sema);
1488}
1489
1490pub fn compareAllWithZeroAdvancedExtra(
1491 lhs: Value,
1492 op: std.math.CompareOperator,
1493 mod: *Module,
1494 opt_sema: ?*Sema,
1495) Module.CompileError!bool {
1496 if (lhs.isInf(mod)) {
1497 switch (op) {
1498 .neq => return true,
1499 .eq => return false,
1500 .gt, .gte => return !lhs.isNegativeInf(mod),
1501 .lt, .lte => return lhs.isNegativeInf(mod),
1502 }
1503 }
1504
1505 switch (mod.intern_pool.indexToKey(lhs.toIntern())) {
1506 .float => |float| switch (float.storage) {
1507 inline else => |x| if (std.math.isNan(x)) return op == .neq,
1508 },
1509 .aggregate => |aggregate| return switch (aggregate.storage) {
1510 .bytes => |bytes| for (bytes) |byte| {
1511 if (!std.math.order(byte, 0).compare(op)) break false;
1512 } else true,
1513 .elems => |elems| for (elems) |elem| {
1514 if (!try Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema)) break false;
1515 } else true,
1516 .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZeroAdvancedExtra(op, mod, opt_sema),
1517 },
1518 else => {},
1519 }
1520 return (try orderAgainstZeroAdvanced(lhs, mod, opt_sema)).compare(op);
1521}
1522
1523pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
1524 assert(mod.intern_pool.typeOf(a.toIntern()) == ty.toIntern());
1525 assert(mod.intern_pool.typeOf(b.toIntern()) == ty.toIntern());
1526 return a.toIntern() == b.toIntern();
1527}
1528
1529pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
1530 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1531 .slice => |slice| return Value.fromInterned(slice.ptr).isComptimeMutablePtr(mod),
1532 .ptr => |ptr| switch (ptr.addr) {
1533 .mut_decl, .comptime_field => true,
1534 .eu_payload, .opt_payload => |base_ptr| Value.fromInterned(base_ptr).isComptimeMutablePtr(mod),
1535 .elem, .field => |base_index| Value.fromInterned(base_index.base).isComptimeMutablePtr(mod),
1536 else => false,
1537 },
1538 else => false,
1539 };
1540}
1541
1542pub fn canMutateComptimeVarState(val: Value, mod: *Module) bool {
1543 return val.isComptimeMutablePtr(mod) or switch (val.toIntern()) {
1544 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1545 .error_union => |error_union| switch (error_union.val) {
1546 .err_name => false,
1547 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1548 },
1549 .ptr => |ptr| switch (ptr.addr) {
1550 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(mod),
1551 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).canMutateComptimeVarState(mod),
1552 .elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(mod),
1553 else => false,
1554 },
1555 .opt => |opt| switch (opt.val) {
1556 .none => false,
1557 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
1558 },
1559 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| {
1560 if (Value.fromInterned(elem).canMutateComptimeVarState(mod)) break true;
1561 } else false,
1562 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(mod),
1563 else => false,
1564 },
1565 };
1566}
1567
1568/// Gets the decl referenced by this pointer. If the pointer does not point
1569/// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),
1570/// this function returns null.
1571pub fn pointerDecl(val: Value, mod: *Module) ?InternPool.DeclIndex {
1572 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1573 .variable => |variable| variable.decl,
1574 .extern_func => |extern_func| extern_func.decl,
1575 .func => |func| func.owner_decl,
1576 .ptr => |ptr| switch (ptr.addr) {
1577 .decl => |decl| decl,
1578 .mut_decl => |mut_decl| mut_decl.decl,
1579 else => null,
1580 },
1581 else => null,
1582 };
1583}
1584
1585pub const slice_ptr_index = 0;
1586pub const slice_len_index = 1;
1587
1588pub fn slicePtr(val: Value, mod: *Module) Value {
1589 return Value.fromInterned(mod.intern_pool.slicePtr(val.toIntern()));
1590}
1591
1592pub fn sliceLen(val: Value, mod: *Module) u64 {
1593 const ip = &mod.intern_pool;
1594 return switch (ip.indexToKey(val.toIntern())) {
1595 .ptr => |ptr| switch (ip.indexToKey(switch (ptr.addr) {
1596 .decl => |decl| mod.declPtr(decl).ty.toIntern(),
1597 .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(),
1598 .anon_decl => |anon_decl| ip.typeOf(anon_decl.val),
1599 .comptime_field => |comptime_field| ip.typeOf(comptime_field),
1600 else => unreachable,
1601 })) {
1602 .array_type => |array_type| array_type.len,
1603 else => 1,
1604 },
1605 .slice => |slice| Value.fromInterned(slice.len).toUnsignedInt(mod),
1606 else => unreachable,
1607 };
1608}
1609
1610/// Asserts the value is a single-item pointer to an array, or an array,
1611/// or an unknown-length pointer, and returns the element value at the index.
1612pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
1613 return (try val.maybeElemValue(mod, index)).?;
1614}
1615
1616/// Like `elemValue`, but returns `null` instead of asserting on failure.
1617pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value {
1618 return switch (val.ip_index) {
1619 .none => switch (val.tag()) {
1620 .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]),
1621 .repeated => val.castTag(.repeated).?.data,
1622 .aggregate => val.castTag(.aggregate).?.data[index],
1623 .slice => val.castTag(.slice).?.data.ptr.maybeElemValue(mod, index),
1624 else => null,
1625 },
1626 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1627 .undef => |ty| Value.fromInterned((try mod.intern(.{
1628 .undef = Type.fromInterned(ty).elemType2(mod).toIntern(),
1629 }))),
1630 .slice => |slice| return Value.fromInterned(slice.ptr).maybeElemValue(mod, index),
1631 .ptr => |ptr| switch (ptr.addr) {
1632 .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index),
1633 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValue(mod, index),
1634 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod))).maybeElemValue(mod, index),
1635 .int, .eu_payload => null,
1636 .opt_payload => |base| Value.fromInterned(base).maybeElemValue(mod, index),
1637 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValue(mod, index),
1638 .elem => |elem| Value.fromInterned(elem.base).maybeElemValue(mod, index + @as(usize, @intCast(elem.index))),
1639 .field => |field| if (Value.fromInterned(field.base).pointerDecl(mod)) |decl_index| {
1640 const base_decl = mod.declPtr(decl_index);
1641 const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index)));
1642 return field_val.maybeElemValue(mod, index);
1643 } else null,
1644 },
1645 .opt => |opt| Value.fromInterned(opt.val).maybeElemValue(mod, index),
1646 .aggregate => |aggregate| {
1647 const len = mod.intern_pool.aggregateTypeLen(aggregate.ty);
1648 if (index < len) return Value.fromInterned(switch (aggregate.storage) {
1649 .bytes => |bytes| try mod.intern(.{ .int = .{
1650 .ty = .u8_type,
1651 .storage = .{ .u64 = bytes[index] },
1652 } }),
1653 .elems => |elems| elems[index],
1654 .repeated_elem => |elem| elem,
1655 });
1656 assert(index == len);
1657 return Value.fromInterned(mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel);
1658 },
1659 else => null,
1660 },
1661 };
1662}
1663
1664pub fn isLazyAlign(val: Value, mod: *Module) bool {
1665 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1666 .int => |int| int.storage == .lazy_align,
1667 else => false,
1668 };
1669}
1670
1671pub fn isLazySize(val: Value, mod: *Module) bool {
1672 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1673 .int => |int| int.storage == .lazy_size,
1674 else => false,
1675 };
1676}
1677
1678pub fn isPtrToThreadLocal(val: Value, mod: *Module) bool {
1679 const backing_decl = mod.intern_pool.getBackingDecl(val.toIntern()).unwrap() orelse return false;
1680 const variable = mod.declPtr(backing_decl).getOwnedVariable(mod) orelse return false;
1681 return variable.is_threadlocal;
1682}
1683
1684// Asserts that the provided start/end are in-bounds.
1685pub fn sliceArray(
1686 val: Value,
1687 mod: *Module,
1688 arena: Allocator,
1689 start: usize,
1690 end: usize,
1691) error{OutOfMemory}!Value {
1692 // TODO: write something like getCoercedInts to avoid needing to dupe
1693 return switch (val.ip_index) {
1694 .none => switch (val.tag()) {
1695 .slice => val.castTag(.slice).?.data.ptr.sliceArray(mod, arena, start, end),
1696 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
1697 .repeated => val,
1698 .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),
1699 else => unreachable,
1700 },
1701 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1702 .ptr => |ptr| switch (ptr.addr) {
1703 .decl => |decl| try mod.declPtr(decl).val.sliceArray(mod, arena, start, end),
1704 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod)))
1705 .sliceArray(mod, arena, start, end),
1706 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
1707 .sliceArray(mod, arena, start, end),
1708 .elem => |elem| Value.fromInterned(elem.base)
1709 .sliceArray(mod, arena, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))),
1710 else => unreachable,
1711 },
1712 .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{
1713 .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) {
1714 .array_type => |array_type| try mod.arrayType(.{
1715 .len = @as(u32, @intCast(end - start)),
1716 .child = array_type.child,
1717 .sentinel = if (end == array_type.len) array_type.sentinel else .none,
1718 }),
1719 .vector_type => |vector_type| try mod.vectorType(.{
1720 .len = @as(u32, @intCast(end - start)),
1721 .child = vector_type.child,
1722 }),
1723 else => unreachable,
1724 }.toIntern(),
1725 .storage = switch (aggregate.storage) {
1726 .bytes => .{ .bytes = try arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) },
1727 .elems => .{ .elems = try arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) },
1728 .repeated_elem => |elem| .{ .repeated_elem = elem },
1729 },
1730 } }))),
1731 else => unreachable,
1732 },
1733 };
1734}
1735
1736pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value {
1737 return switch (val.ip_index) {
1738 .none => switch (val.tag()) {
1739 .aggregate => {
1740 const field_values = val.castTag(.aggregate).?.data;
1741 return field_values[index];
1742 },
1743 .@"union" => {
1744 const payload = val.castTag(.@"union").?.data;
1745 // TODO assert the tag is correct
1746 return payload.val;
1747 },
1748 else => unreachable,
1749 },
1750 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1751 .undef => |ty| Value.fromInterned((try mod.intern(.{
1752 .undef = Type.fromInterned(ty).structFieldType(index, mod).toIntern(),
1753 }))),
1754 .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) {
1755 .bytes => |bytes| try mod.intern(.{ .int = .{
1756 .ty = .u8_type,
1757 .storage = .{ .u64 = bytes[index] },
1758 } }),
1759 .elems => |elems| elems[index],
1760 .repeated_elem => |elem| elem,
1761 }),
1762 // TODO assert the tag is correct
1763 .un => |un| Value.fromInterned(un.val),
1764 else => unreachable,
1765 },
1766 };
1767}
1768
1769pub fn unionTag(val: Value, mod: *Module) ?Value {
1770 if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
1771 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1772 .undef, .enum_tag => val,
1773 .un => |un| if (un.tag != .none) Value.fromInterned(un.tag) else return null,
1774 else => unreachable,
1775 };
1776}
1777
1778pub fn unionValue(val: Value, mod: *Module) Value {
1779 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1780 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1781 .un => |un| Value.fromInterned(un.val),
1782 else => unreachable,
1783 };
1784}
1785
1786/// Returns a pointer to the element value at the index.
1787pub fn elemPtr(
1788 val: Value,
1789 elem_ptr_ty: Type,
1790 index: usize,
1791 mod: *Module,
1792) Allocator.Error!Value {
1793 const elem_ty = elem_ptr_ty.childType(mod);
1794 const ptr_val = switch (mod.intern_pool.indexToKey(val.toIntern())) {
1795 .slice => |slice| Value.fromInterned(slice.ptr),
1796 else => val,
1797 };
1798 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
1799 .ptr => |ptr| switch (ptr.addr) {
1800 .elem => |elem| if (Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).eql(elem_ty, mod))
1801 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1802 .ty = elem_ptr_ty.toIntern(),
1803 .addr = .{ .elem = .{
1804 .base = elem.base,
1805 .index = elem.index + index,
1806 } },
1807 } }))),
1808 else => {},
1809 },
1810 else => {},
1811 }
1812 var ptr_ty_key = mod.intern_pool.indexToKey(elem_ptr_ty.toIntern()).ptr_type;
1813 assert(ptr_ty_key.flags.size != .Slice);
1814 ptr_ty_key.flags.size = .Many;
1815 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1816 .ty = elem_ptr_ty.toIntern(),
1817 .addr = .{ .elem = .{
1818 .base = (try mod.getCoerced(ptr_val, try mod.ptrType(ptr_ty_key))).toIntern(),
1819 .index = index,
1820 } },
1821 } })));
1822}
1823
1824pub fn isUndef(val: Value, mod: *Module) bool {
1825 return val.ip_index != .none and mod.intern_pool.isUndef(val.toIntern());
1826}
1827
1828/// TODO: check for cases such as array that is not marked undef but all the element
1829/// values are marked undef, or struct that is not marked undef but all fields are marked
1830/// undef, etc.
1831pub fn isUndefDeep(val: Value, mod: *Module) bool {
1832 return val.isUndef(mod);
1833}
1834
1835/// Returns true if any value contained in `self` is undefined.
1836pub fn anyUndef(val: Value, mod: *Module) !bool {
1837 if (val.ip_index == .none) return false;
1838 return switch (val.toIntern()) {
1839 .undef => true,
1840 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1841 .undef => true,
1842 .simple_value => |v| v == .undefined,
1843 .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| {
1844 if (try (try val.elemValue(mod, idx)).anyUndef(mod)) break true;
1845 } else false,
1846 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
1847 const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
1848 if (try anyUndef(Value.fromInterned(elem), mod)) break true;
1849 } else false,
1850 else => false,
1851 },
1852 };
1853}
1854
1855/// Asserts the value is not undefined and not unreachable.
1856/// C pointers with an integer value of 0 are also considered null.
1857pub fn isNull(val: Value, mod: *Module) bool {
1858 return switch (val.toIntern()) {
1859 .undef => unreachable,
1860 .unreachable_value => unreachable,
1861 .null_value => true,
1862 else => return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1863 .undef => unreachable,
1864 .ptr => |ptr| switch (ptr.addr) {
1865 .int => {
1866 var buf: BigIntSpace = undefined;
1867 return val.toBigInt(&buf, mod).eqlZero();
1868 },
1869 else => false,
1870 },
1871 .opt => |opt| opt.val == .none,
1872 else => false,
1873 },
1874 };
1875}
1876
1877/// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.
1878pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTerminatedString {
1879 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1880 .err => |err| err.name.toOptional(),
1881 .error_union => |error_union| switch (error_union.val) {
1882 .err_name => |err_name| err_name.toOptional(),
1883 .payload => .none,
1884 },
1885 else => unreachable,
1886 };
1887}
1888
1889pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt {
1890 return if (getErrorName(val, mod).unwrap()) |err_name|
1891 @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?))
1892 else
1893 0;
1894}
1895
1896/// Assumes the type is an error union. Returns true if and only if the value is
1897/// the error union payload, not an error.
1898pub fn errorUnionIsPayload(val: Value, mod: *const Module) bool {
1899 return mod.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;
1900}
1901
1902/// Value of the optional, null if optional has no payload.
1903pub fn optionalValue(val: Value, mod: *const Module) ?Value {
1904 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1905 .opt => |opt| switch (opt.val) {
1906 .none => null,
1907 else => |payload| Value.fromInterned(payload),
1908 },
1909 .ptr => val,
1910 else => unreachable,
1911 };
1912}
1913
1914/// Valid for all types. Asserts the value is not undefined.
1915pub fn isFloat(self: Value, mod: *const Module) bool {
1916 return switch (self.toIntern()) {
1917 .undef => unreachable,
1918 else => switch (mod.intern_pool.indexToKey(self.toIntern())) {
1919 .undef => unreachable,
1920 .float => true,
1921 else => false,
1922 },
1923 };
1924}
1925
1926pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module) !Value {
1927 return floatFromIntAdvanced(val, arena, int_ty, float_ty, mod, null) catch |err| switch (err) {
1928 error.OutOfMemory => return error.OutOfMemory,
1929 else => unreachable,
1930 };
1931}
1932
1933pub fn floatFromIntAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1934 if (int_ty.zigTypeTag(mod) == .Vector) {
1935 const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(mod));
1936 const scalar_ty = float_ty.scalarType(mod);
1937 for (result_data, 0..) |*scalar, i| {
1938 const elem_val = try val.elemValue(mod, i);
1939 scalar.* = try (try floatFromIntScalar(elem_val, scalar_ty, mod, opt_sema)).intern(scalar_ty, mod);
1940 }
1941 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1942 .ty = float_ty.toIntern(),
1943 .storage = .{ .elems = result_data },
1944 } })));
1945 }
1946 return floatFromIntScalar(val, float_ty, mod, opt_sema);
1947}
1948
1949pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
1950 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1951 .undef => try mod.undefValue(float_ty),
1952 .int => |int| switch (int.storage) {
1953 .big_int => |big_int| {
1954 const float = bigIntToFloat(big_int.limbs, big_int.positive);
1955 return mod.floatValue(float_ty, float);
1956 },
1957 inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, mod),
1958 .lazy_align => |ty| if (opt_sema) |sema| {
1959 return floatFromIntInner((try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0), float_ty, mod);
1960 } else {
1961 return floatFromIntInner(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0), float_ty, mod);
1962 },
1963 .lazy_size => |ty| if (opt_sema) |sema| {
1964 return floatFromIntInner((try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar, float_ty, mod);
1965 } else {
1966 return floatFromIntInner(Type.fromInterned(ty).abiSize(mod), float_ty, mod);
1967 },
1968 },
1969 else => unreachable,
1970 };
1971}
1972
1973fn floatFromIntInner(x: anytype, dest_ty: Type, mod: *Module) !Value {
1974 const target = mod.getTarget();
1975 const storage: InternPool.Key.Float.Storage = switch (dest_ty.floatBits(target)) {
1976 16 => .{ .f16 = @floatFromInt(x) },
1977 32 => .{ .f32 = @floatFromInt(x) },
1978 64 => .{ .f64 = @floatFromInt(x) },
1979 80 => .{ .f80 = @floatFromInt(x) },
1980 128 => .{ .f128 = @floatFromInt(x) },
1981 else => unreachable,
1982 };
1983 return Value.fromInterned((try mod.intern(.{ .float = .{
1984 .ty = dest_ty.toIntern(),
1985 .storage = storage,
1986 } })));
1987}
1988
1989fn calcLimbLenFloat(scalar: anytype) usize {
1990 if (scalar == 0) {
1991 return 1;
1992 }
1993
1994 const w_value = @abs(scalar);
1995 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).Int.bits) + 1;
1996}
1997
1998pub const OverflowArithmeticResult = struct {
1999 overflow_bit: Value,
2000 wrapped_result: Value,
2001};
2002
2003/// Supports (vectors of) integers only; asserts neither operand is undefined.
2004pub fn intAddSat(
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 intAddSatScalar(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 intAddSatScalar(lhs, rhs, ty, arena, mod);
2025}
2026
2027/// Supports integers only; asserts neither operand is undefined.
2028pub fn intAddSatScalar(
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 std.math.big.int.calcTwosCompLimbCount(info.bits),
2047 );
2048 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2049 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2050 return mod.intValue_big(ty, result_bigint.toConst());
2051}
2052
2053/// Supports (vectors of) integers only; asserts neither operand is undefined.
2054pub fn intSubSat(
2055 lhs: Value,
2056 rhs: Value,
2057 ty: Type,
2058 arena: Allocator,
2059 mod: *Module,
2060) !Value {
2061 if (ty.zigTypeTag(mod) == .Vector) {
2062 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2063 const scalar_ty = ty.scalarType(mod);
2064 for (result_data, 0..) |*scalar, i| {
2065 const lhs_elem = try lhs.elemValue(mod, i);
2066 const rhs_elem = try rhs.elemValue(mod, i);
2067 scalar.* = try (try intSubSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2068 }
2069 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2070 .ty = ty.toIntern(),
2071 .storage = .{ .elems = result_data },
2072 } })));
2073 }
2074 return intSubSatScalar(lhs, rhs, ty, arena, mod);
2075}
2076
2077/// Supports integers only; asserts neither operand is undefined.
2078pub fn intSubSatScalar(
2079 lhs: Value,
2080 rhs: Value,
2081 ty: Type,
2082 arena: Allocator,
2083 mod: *Module,
2084) !Value {
2085 assert(!lhs.isUndef(mod));
2086 assert(!rhs.isUndef(mod));
2087
2088 const info = ty.intInfo(mod);
2089
2090 var lhs_space: Value.BigIntSpace = undefined;
2091 var rhs_space: Value.BigIntSpace = undefined;
2092 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2093 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2094 const limbs = try arena.alloc(
2095 std.math.big.Limb,
2096 std.math.big.int.calcTwosCompLimbCount(info.bits),
2097 );
2098 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2099 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2100 return mod.intValue_big(ty, result_bigint.toConst());
2101}
2102
2103pub fn intMulWithOverflow(
2104 lhs: Value,
2105 rhs: Value,
2106 ty: Type,
2107 arena: Allocator,
2108 mod: *Module,
2109) !OverflowArithmeticResult {
2110 if (ty.zigTypeTag(mod) == .Vector) {
2111 const vec_len = ty.vectorLen(mod);
2112 const overflowed_data = try arena.alloc(InternPool.Index, vec_len);
2113 const result_data = try arena.alloc(InternPool.Index, vec_len);
2114 const scalar_ty = ty.scalarType(mod);
2115 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2116 const lhs_elem = try lhs.elemValue(mod, i);
2117 const rhs_elem = try rhs.elemValue(mod, i);
2118 const of_math_result = try intMulWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod);
2119 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2120 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2121 }
2122 return OverflowArithmeticResult{
2123 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2124 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2125 .storage = .{ .elems = overflowed_data },
2126 } }))),
2127 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2128 .ty = ty.toIntern(),
2129 .storage = .{ .elems = result_data },
2130 } }))),
2131 };
2132 }
2133 return intMulWithOverflowScalar(lhs, rhs, ty, arena, mod);
2134}
2135
2136pub fn intMulWithOverflowScalar(
2137 lhs: Value,
2138 rhs: Value,
2139 ty: Type,
2140 arena: Allocator,
2141 mod: *Module,
2142) !OverflowArithmeticResult {
2143 const info = ty.intInfo(mod);
2144
2145 var lhs_space: Value.BigIntSpace = undefined;
2146 var rhs_space: Value.BigIntSpace = undefined;
2147 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2148 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2149 const limbs = try arena.alloc(
2150 std.math.big.Limb,
2151 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2152 );
2153 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2154 const limbs_buffer = try arena.alloc(
2155 std.math.big.Limb,
2156 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2157 );
2158 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2159
2160 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2161 if (overflowed) {
2162 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2163 }
2164
2165 return OverflowArithmeticResult{
2166 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2167 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2168 };
2169}
2170
2171/// Supports both (vectors of) floats and ints; handles undefined scalars.
2172pub fn numberMulWrap(
2173 lhs: Value,
2174 rhs: Value,
2175 ty: Type,
2176 arena: Allocator,
2177 mod: *Module,
2178) !Value {
2179 if (ty.zigTypeTag(mod) == .Vector) {
2180 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2181 const scalar_ty = ty.scalarType(mod);
2182 for (result_data, 0..) |*scalar, i| {
2183 const lhs_elem = try lhs.elemValue(mod, i);
2184 const rhs_elem = try rhs.elemValue(mod, i);
2185 scalar.* = try (try numberMulWrapScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2186 }
2187 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2188 .ty = ty.toIntern(),
2189 .storage = .{ .elems = result_data },
2190 } })));
2191 }
2192 return numberMulWrapScalar(lhs, rhs, ty, arena, mod);
2193}
2194
2195/// Supports both floats and ints; handles undefined.
2196pub fn numberMulWrapScalar(
2197 lhs: Value,
2198 rhs: Value,
2199 ty: Type,
2200 arena: Allocator,
2201 mod: *Module,
2202) !Value {
2203 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
2204
2205 if (ty.zigTypeTag(mod) == .ComptimeInt) {
2206 return intMul(lhs, rhs, ty, undefined, arena, mod);
2207 }
2208
2209 if (ty.isAnyFloat()) {
2210 return floatMul(lhs, rhs, ty, arena, mod);
2211 }
2212
2213 const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, mod);
2214 return overflow_result.wrapped_result;
2215}
2216
2217/// Supports (vectors of) integers only; asserts neither operand is undefined.
2218pub fn intMulSat(
2219 lhs: Value,
2220 rhs: Value,
2221 ty: Type,
2222 arena: Allocator,
2223 mod: *Module,
2224) !Value {
2225 if (ty.zigTypeTag(mod) == .Vector) {
2226 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2227 const scalar_ty = ty.scalarType(mod);
2228 for (result_data, 0..) |*scalar, i| {
2229 const lhs_elem = try lhs.elemValue(mod, i);
2230 const rhs_elem = try rhs.elemValue(mod, i);
2231 scalar.* = try (try intMulSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2232 }
2233 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2234 .ty = ty.toIntern(),
2235 .storage = .{ .elems = result_data },
2236 } })));
2237 }
2238 return intMulSatScalar(lhs, rhs, ty, arena, mod);
2239}
2240
2241/// Supports (vectors of) integers only; asserts neither operand is undefined.
2242pub fn intMulSatScalar(
2243 lhs: Value,
2244 rhs: Value,
2245 ty: Type,
2246 arena: Allocator,
2247 mod: *Module,
2248) !Value {
2249 assert(!lhs.isUndef(mod));
2250 assert(!rhs.isUndef(mod));
2251
2252 const info = ty.intInfo(mod);
2253
2254 var lhs_space: Value.BigIntSpace = undefined;
2255 var rhs_space: Value.BigIntSpace = undefined;
2256 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2257 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2258 const limbs = try arena.alloc(
2259 std.math.big.Limb,
2260 @max(
2261 // For the saturate
2262 std.math.big.int.calcTwosCompLimbCount(info.bits),
2263 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2264 ),
2265 );
2266 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2267 const limbs_buffer = try arena.alloc(
2268 std.math.big.Limb,
2269 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2270 );
2271 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2272 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
2273 return mod.intValue_big(ty, result_bigint.toConst());
2274}
2275
2276/// Supports both floats and ints; handles undefined.
2277pub fn numberMax(lhs: Value, rhs: Value, mod: *Module) Value {
2278 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2279 if (lhs.isNan(mod)) return rhs;
2280 if (rhs.isNan(mod)) return lhs;
2281
2282 return switch (order(lhs, rhs, mod)) {
2283 .lt => rhs,
2284 .gt, .eq => lhs,
2285 };
2286}
2287
2288/// Supports both floats and ints; handles undefined.
2289pub fn numberMin(lhs: Value, rhs: Value, mod: *Module) Value {
2290 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2291 if (lhs.isNan(mod)) return rhs;
2292 if (rhs.isNan(mod)) return lhs;
2293
2294 return switch (order(lhs, rhs, mod)) {
2295 .lt => lhs,
2296 .gt, .eq => rhs,
2297 };
2298}
2299
2300/// operands must be (vectors of) integers; handles undefined scalars.
2301pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2302 if (ty.zigTypeTag(mod) == .Vector) {
2303 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2304 const scalar_ty = ty.scalarType(mod);
2305 for (result_data, 0..) |*scalar, i| {
2306 const elem_val = try val.elemValue(mod, i);
2307 scalar.* = try (try bitwiseNotScalar(elem_val, scalar_ty, arena, mod)).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 bitwiseNotScalar(val, ty, arena, mod);
2315}
2316
2317/// operands must be integers; handles undefined.
2318pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2319 if (val.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2320 if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());
2321
2322 const info = ty.intInfo(mod);
2323
2324 if (info.bits == 0) {
2325 return val;
2326 }
2327
2328 // TODO is this a performance issue? maybe we should try the operation without
2329 // resorting to BigInt first.
2330 var val_space: Value.BigIntSpace = undefined;
2331 const val_bigint = val.toBigInt(&val_space, mod);
2332 const limbs = try arena.alloc(
2333 std.math.big.Limb,
2334 std.math.big.int.calcTwosCompLimbCount(info.bits),
2335 );
2336
2337 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2338 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2339 return mod.intValue_big(ty, result_bigint.toConst());
2340}
2341
2342/// operands must be (vectors of) integers; handles undefined scalars.
2343pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2344 if (ty.zigTypeTag(mod) == .Vector) {
2345 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2346 const scalar_ty = ty.scalarType(mod);
2347 for (result_data, 0..) |*scalar, i| {
2348 const lhs_elem = try lhs.elemValue(mod, i);
2349 const rhs_elem = try rhs.elemValue(mod, i);
2350 scalar.* = try (try bitwiseAndScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2351 }
2352 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2353 .ty = ty.toIntern(),
2354 .storage = .{ .elems = result_data },
2355 } })));
2356 }
2357 return bitwiseAndScalar(lhs, rhs, ty, allocator, mod);
2358}
2359
2360/// operands must be integers; handles undefined.
2361pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2362 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2363 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
2364
2365 // TODO is this a performance issue? maybe we should try the operation without
2366 // resorting to BigInt first.
2367 var lhs_space: Value.BigIntSpace = undefined;
2368 var rhs_space: Value.BigIntSpace = undefined;
2369 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2370 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2371 const limbs = try arena.alloc(
2372 std.math.big.Limb,
2373 // + 1 for negatives
2374 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2375 );
2376 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2377 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2378 return mod.intValue_big(ty, result_bigint.toConst());
2379}
2380
2381/// operands must be (vectors of) integers; handles undefined scalars.
2382pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2383 if (ty.zigTypeTag(mod) == .Vector) {
2384 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2385 const scalar_ty = ty.scalarType(mod);
2386 for (result_data, 0..) |*scalar, i| {
2387 const lhs_elem = try lhs.elemValue(mod, i);
2388 const rhs_elem = try rhs.elemValue(mod, i);
2389 scalar.* = try (try bitwiseNandScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2390 }
2391 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2392 .ty = ty.toIntern(),
2393 .storage = .{ .elems = result_data },
2394 } })));
2395 }
2396 return bitwiseNandScalar(lhs, rhs, ty, arena, mod);
2397}
2398
2399/// operands must be integers; handles undefined.
2400pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2401 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2402 if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
2403
2404 const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
2405 const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);
2406 return bitwiseXor(anded, all_ones, ty, arena, mod);
2407}
2408
2409/// operands must be (vectors of) integers; handles undefined scalars.
2410pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2411 if (ty.zigTypeTag(mod) == .Vector) {
2412 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2413 const scalar_ty = ty.scalarType(mod);
2414 for (result_data, 0..) |*scalar, i| {
2415 const lhs_elem = try lhs.elemValue(mod, i);
2416 const rhs_elem = try rhs.elemValue(mod, i);
2417 scalar.* = try (try bitwiseOrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2418 }
2419 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2420 .ty = ty.toIntern(),
2421 .storage = .{ .elems = result_data },
2422 } })));
2423 }
2424 return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);
2425}
2426
2427/// operands must be integers; handles undefined.
2428pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2429 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2430 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
2431
2432 // TODO is this a performance issue? maybe we should try the operation without
2433 // resorting to BigInt first.
2434 var lhs_space: Value.BigIntSpace = undefined;
2435 var rhs_space: Value.BigIntSpace = undefined;
2436 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2437 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2438 const limbs = try arena.alloc(
2439 std.math.big.Limb,
2440 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2441 );
2442 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2443 result_bigint.bitOr(lhs_bigint, rhs_bigint);
2444 return mod.intValue_big(ty, result_bigint.toConst());
2445}
2446
2447/// operands must be (vectors of) integers; handles undefined scalars.
2448pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2449 if (ty.zigTypeTag(mod) == .Vector) {
2450 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2451 const scalar_ty = ty.scalarType(mod);
2452 for (result_data, 0..) |*scalar, i| {
2453 const lhs_elem = try lhs.elemValue(mod, i);
2454 const rhs_elem = try rhs.elemValue(mod, i);
2455 scalar.* = try (try bitwiseXorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2456 }
2457 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2458 .ty = ty.toIntern(),
2459 .storage = .{ .elems = result_data },
2460 } })));
2461 }
2462 return bitwiseXorScalar(lhs, rhs, ty, allocator, mod);
2463}
2464
2465/// operands must be integers; handles undefined.
2466pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2467 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2468 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() != rhs.toBool());
2469
2470 // TODO is this a performance issue? maybe we should try the operation without
2471 // resorting to BigInt first.
2472 var lhs_space: Value.BigIntSpace = undefined;
2473 var rhs_space: Value.BigIntSpace = undefined;
2474 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2475 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2476 const limbs = try arena.alloc(
2477 std.math.big.Limb,
2478 // + 1 for negatives
2479 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2480 );
2481 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2482 result_bigint.bitXor(lhs_bigint, rhs_bigint);
2483 return mod.intValue_big(ty, result_bigint.toConst());
2484}
2485
2486/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2487/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2488pub fn intDiv(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2489 var overflow: usize = undefined;
2490 return intDivInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2491 error.Overflow => {
2492 const is_vec = ty.isVector(mod);
2493 overflow_idx.* = if (is_vec) overflow else 0;
2494 const safe_ty = if (is_vec) try mod.vectorType(.{
2495 .len = ty.vectorLen(mod),
2496 .child = .comptime_int_type,
2497 }) else Type.comptime_int;
2498 return intDivInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2499 error.Overflow => unreachable,
2500 else => |e| return e,
2501 };
2502 },
2503 else => |e| return e,
2504 };
2505}
2506
2507fn intDivInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2508 if (ty.zigTypeTag(mod) == .Vector) {
2509 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2510 const scalar_ty = ty.scalarType(mod);
2511 for (result_data, 0..) |*scalar, i| {
2512 const lhs_elem = try lhs.elemValue(mod, i);
2513 const rhs_elem = try rhs.elemValue(mod, i);
2514 const val = intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2515 error.Overflow => {
2516 overflow_idx.* = i;
2517 return error.Overflow;
2518 },
2519 else => |e| return e,
2520 };
2521 scalar.* = try val.intern(scalar_ty, mod);
2522 }
2523 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2524 .ty = ty.toIntern(),
2525 .storage = .{ .elems = result_data },
2526 } })));
2527 }
2528 return intDivScalar(lhs, rhs, ty, allocator, mod);
2529}
2530
2531pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2532 // TODO is this a performance issue? maybe we should try the operation without
2533 // resorting to BigInt first.
2534 var lhs_space: Value.BigIntSpace = undefined;
2535 var rhs_space: Value.BigIntSpace = undefined;
2536 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2537 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2538 const limbs_q = try allocator.alloc(
2539 std.math.big.Limb,
2540 lhs_bigint.limbs.len,
2541 );
2542 const limbs_r = try allocator.alloc(
2543 std.math.big.Limb,
2544 rhs_bigint.limbs.len,
2545 );
2546 const limbs_buffer = try allocator.alloc(
2547 std.math.big.Limb,
2548 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2549 );
2550 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2551 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2552 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2553 if (ty.toIntern() != .comptime_int_type) {
2554 const info = ty.intInfo(mod);
2555 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
2556 return error.Overflow;
2557 }
2558 }
2559 return mod.intValue_big(ty, result_q.toConst());
2560}
2561
2562pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2563 if (ty.zigTypeTag(mod) == .Vector) {
2564 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2565 const scalar_ty = ty.scalarType(mod);
2566 for (result_data, 0..) |*scalar, i| {
2567 const lhs_elem = try lhs.elemValue(mod, i);
2568 const rhs_elem = try rhs.elemValue(mod, i);
2569 scalar.* = try (try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2570 }
2571 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2572 .ty = ty.toIntern(),
2573 .storage = .{ .elems = result_data },
2574 } })));
2575 }
2576 return intDivFloorScalar(lhs, rhs, ty, allocator, mod);
2577}
2578
2579pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2580 // TODO is this a performance issue? maybe we should try the operation without
2581 // resorting to BigInt first.
2582 var lhs_space: Value.BigIntSpace = undefined;
2583 var rhs_space: Value.BigIntSpace = undefined;
2584 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2585 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2586 const limbs_q = try allocator.alloc(
2587 std.math.big.Limb,
2588 lhs_bigint.limbs.len,
2589 );
2590 const limbs_r = try allocator.alloc(
2591 std.math.big.Limb,
2592 rhs_bigint.limbs.len,
2593 );
2594 const limbs_buffer = try allocator.alloc(
2595 std.math.big.Limb,
2596 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2597 );
2598 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2599 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2600 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2601 return mod.intValue_big(ty, result_q.toConst());
2602}
2603
2604pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2605 if (ty.zigTypeTag(mod) == .Vector) {
2606 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2607 const scalar_ty = ty.scalarType(mod);
2608 for (result_data, 0..) |*scalar, i| {
2609 const lhs_elem = try lhs.elemValue(mod, i);
2610 const rhs_elem = try rhs.elemValue(mod, i);
2611 scalar.* = try (try intModScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2612 }
2613 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2614 .ty = ty.toIntern(),
2615 .storage = .{ .elems = result_data },
2616 } })));
2617 }
2618 return intModScalar(lhs, rhs, ty, allocator, mod);
2619}
2620
2621pub fn intModScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2622 // TODO is this a performance issue? maybe we should try the operation without
2623 // resorting to BigInt first.
2624 var lhs_space: Value.BigIntSpace = undefined;
2625 var rhs_space: Value.BigIntSpace = undefined;
2626 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2627 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2628 const limbs_q = try allocator.alloc(
2629 std.math.big.Limb,
2630 lhs_bigint.limbs.len,
2631 );
2632 const limbs_r = try allocator.alloc(
2633 std.math.big.Limb,
2634 rhs_bigint.limbs.len,
2635 );
2636 const limbs_buffer = try allocator.alloc(
2637 std.math.big.Limb,
2638 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2639 );
2640 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2641 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2642 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2643 return mod.intValue_big(ty, result_r.toConst());
2644}
2645
2646/// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
2647pub fn isNan(val: Value, mod: *const Module) bool {
2648 if (val.ip_index == .none) return false;
2649 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2650 .float => |float| switch (float.storage) {
2651 inline else => |x| std.math.isNan(x),
2652 },
2653 else => false,
2654 };
2655}
2656
2657/// Returns true if the value is a floating point type and is infinite. Returns false otherwise.
2658pub fn isInf(val: Value, mod: *const Module) bool {
2659 if (val.ip_index == .none) return false;
2660 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2661 .float => |float| switch (float.storage) {
2662 inline else => |x| std.math.isInf(x),
2663 },
2664 else => false,
2665 };
2666}
2667
2668pub fn isNegativeInf(val: Value, mod: *const Module) bool {
2669 if (val.ip_index == .none) return false;
2670 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2671 .float => |float| switch (float.storage) {
2672 inline else => |x| std.math.isNegativeInf(x),
2673 },
2674 else => false,
2675 };
2676}
2677
2678pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2679 if (float_type.zigTypeTag(mod) == .Vector) {
2680 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2681 const scalar_ty = float_type.scalarType(mod);
2682 for (result_data, 0..) |*scalar, i| {
2683 const lhs_elem = try lhs.elemValue(mod, i);
2684 const rhs_elem = try rhs.elemValue(mod, i);
2685 scalar.* = try (try floatRemScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2686 }
2687 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2688 .ty = float_type.toIntern(),
2689 .storage = .{ .elems = result_data },
2690 } })));
2691 }
2692 return floatRemScalar(lhs, rhs, float_type, mod);
2693}
2694
2695pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2696 const target = mod.getTarget();
2697 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2698 16 => .{ .f16 = @rem(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2699 32 => .{ .f32 = @rem(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2700 64 => .{ .f64 = @rem(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2701 80 => .{ .f80 = @rem(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2702 128 => .{ .f128 = @rem(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2703 else => unreachable,
2704 };
2705 return Value.fromInterned((try mod.intern(.{ .float = .{
2706 .ty = float_type.toIntern(),
2707 .storage = storage,
2708 } })));
2709}
2710
2711pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
2712 if (float_type.zigTypeTag(mod) == .Vector) {
2713 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
2714 const scalar_ty = float_type.scalarType(mod);
2715 for (result_data, 0..) |*scalar, i| {
2716 const lhs_elem = try lhs.elemValue(mod, i);
2717 const rhs_elem = try rhs.elemValue(mod, i);
2718 scalar.* = try (try floatModScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
2719 }
2720 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2721 .ty = float_type.toIntern(),
2722 .storage = .{ .elems = result_data },
2723 } })));
2724 }
2725 return floatModScalar(lhs, rhs, float_type, mod);
2726}
2727
2728pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {
2729 const target = mod.getTarget();
2730 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
2731 16 => .{ .f16 = @mod(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
2732 32 => .{ .f32 = @mod(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
2733 64 => .{ .f64 = @mod(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
2734 80 => .{ .f80 = @mod(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
2735 128 => .{ .f128 = @mod(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
2736 else => unreachable,
2737 };
2738 return Value.fromInterned((try mod.intern(.{ .float = .{
2739 .ty = float_type.toIntern(),
2740 .storage = storage,
2741 } })));
2742}
2743
2744/// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting
2745/// overflow_idx to the vector index the overflow was at (or 0 for a scalar).
2746pub fn intMul(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {
2747 var overflow: usize = undefined;
2748 return intMulInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {
2749 error.Overflow => {
2750 const is_vec = ty.isVector(mod);
2751 overflow_idx.* = if (is_vec) overflow else 0;
2752 const safe_ty = if (is_vec) try mod.vectorType(.{
2753 .len = ty.vectorLen(mod),
2754 .child = .comptime_int_type,
2755 }) else Type.comptime_int;
2756 return intMulInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {
2757 error.Overflow => unreachable,
2758 else => |e| return e,
2759 };
2760 },
2761 else => |e| return e,
2762 };
2763}
2764
2765fn intMulInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {
2766 if (ty.zigTypeTag(mod) == .Vector) {
2767 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2768 const scalar_ty = ty.scalarType(mod);
2769 for (result_data, 0..) |*scalar, i| {
2770 const lhs_elem = try lhs.elemValue(mod, i);
2771 const rhs_elem = try rhs.elemValue(mod, i);
2772 const val = intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {
2773 error.Overflow => {
2774 overflow_idx.* = i;
2775 return error.Overflow;
2776 },
2777 else => |e| return e,
2778 };
2779 scalar.* = try val.intern(scalar_ty, mod);
2780 }
2781 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2782 .ty = ty.toIntern(),
2783 .storage = .{ .elems = result_data },
2784 } })));
2785 }
2786 return intMulScalar(lhs, rhs, ty, allocator, mod);
2787}
2788
2789pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2790 if (ty.toIntern() != .comptime_int_type) {
2791 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2792 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
2793 return res.wrapped_result;
2794 }
2795 // TODO is this a performance issue? maybe we should try the operation without
2796 // resorting to BigInt first.
2797 var lhs_space: Value.BigIntSpace = undefined;
2798 var rhs_space: Value.BigIntSpace = undefined;
2799 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2800 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2801 const limbs = try allocator.alloc(
2802 std.math.big.Limb,
2803 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2804 );
2805 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2806 const limbs_buffer = try allocator.alloc(
2807 std.math.big.Limb,
2808 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2809 );
2810 defer allocator.free(limbs_buffer);
2811 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
2812 return mod.intValue_big(ty, result_bigint.toConst());
2813}
2814
2815pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {
2816 if (ty.zigTypeTag(mod) == .Vector) {
2817 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2818 const scalar_ty = ty.scalarType(mod);
2819 for (result_data, 0..) |*scalar, i| {
2820 const elem_val = try val.elemValue(mod, i);
2821 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, bits, mod)).intern(scalar_ty, mod);
2822 }
2823 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2824 .ty = ty.toIntern(),
2825 .storage = .{ .elems = result_data },
2826 } })));
2827 }
2828 return intTruncScalar(val, ty, allocator, signedness, bits, mod);
2829}
2830
2831/// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`.
2832pub fn intTruncBitsAsValue(
2833 val: Value,
2834 ty: Type,
2835 allocator: Allocator,
2836 signedness: std.builtin.Signedness,
2837 bits: Value,
2838 mod: *Module,
2839) !Value {
2840 if (ty.zigTypeTag(mod) == .Vector) {
2841 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2842 const scalar_ty = ty.scalarType(mod);
2843 for (result_data, 0..) |*scalar, i| {
2844 const elem_val = try val.elemValue(mod, i);
2845 const bits_elem = try bits.elemValue(mod, i);
2846 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @as(u16, @intCast(bits_elem.toUnsignedInt(mod))), mod)).intern(scalar_ty, mod);
2847 }
2848 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2849 .ty = ty.toIntern(),
2850 .storage = .{ .elems = result_data },
2851 } })));
2852 }
2853 return intTruncScalar(val, ty, allocator, signedness, @as(u16, @intCast(bits.toUnsignedInt(mod))), mod);
2854}
2855
2856pub fn intTruncScalar(
2857 val: Value,
2858 ty: Type,
2859 allocator: Allocator,
2860 signedness: std.builtin.Signedness,
2861 bits: u16,
2862 mod: *Module,
2863) !Value {
2864 if (bits == 0) return mod.intValue(ty, 0);
2865
2866 var val_space: Value.BigIntSpace = undefined;
2867 const val_bigint = val.toBigInt(&val_space, mod);
2868
2869 const limbs = try allocator.alloc(
2870 std.math.big.Limb,
2871 std.math.big.int.calcTwosCompLimbCount(bits),
2872 );
2873 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2874
2875 result_bigint.truncate(val_bigint, signedness, bits);
2876 return mod.intValue_big(ty, result_bigint.toConst());
2877}
2878
2879pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2880 if (ty.zigTypeTag(mod) == .Vector) {
2881 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2882 const scalar_ty = ty.scalarType(mod);
2883 for (result_data, 0..) |*scalar, i| {
2884 const lhs_elem = try lhs.elemValue(mod, i);
2885 const rhs_elem = try rhs.elemValue(mod, i);
2886 scalar.* = try (try shlScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
2887 }
2888 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2889 .ty = ty.toIntern(),
2890 .storage = .{ .elems = result_data },
2891 } })));
2892 }
2893 return shlScalar(lhs, rhs, ty, allocator, mod);
2894}
2895
2896pub fn shlScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2897 // TODO is this a performance issue? maybe we should try the operation without
2898 // resorting to BigInt first.
2899 var lhs_space: Value.BigIntSpace = undefined;
2900 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2901 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2902 const limbs = try allocator.alloc(
2903 std.math.big.Limb,
2904 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2905 );
2906 var result_bigint = BigIntMutable{
2907 .limbs = limbs,
2908 .positive = undefined,
2909 .len = undefined,
2910 };
2911 result_bigint.shiftLeft(lhs_bigint, shift);
2912 if (ty.toIntern() != .comptime_int_type) {
2913 const int_info = ty.intInfo(mod);
2914 result_bigint.truncate(result_bigint.toConst(), int_info.signedness, int_info.bits);
2915 }
2916
2917 return mod.intValue_big(ty, result_bigint.toConst());
2918}
2919
2920pub fn shlWithOverflow(
2921 lhs: Value,
2922 rhs: Value,
2923 ty: Type,
2924 allocator: Allocator,
2925 mod: *Module,
2926) !OverflowArithmeticResult {
2927 if (ty.zigTypeTag(mod) == .Vector) {
2928 const vec_len = ty.vectorLen(mod);
2929 const overflowed_data = try allocator.alloc(InternPool.Index, vec_len);
2930 const result_data = try allocator.alloc(InternPool.Index, vec_len);
2931 const scalar_ty = ty.scalarType(mod);
2932 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
2933 const lhs_elem = try lhs.elemValue(mod, i);
2934 const rhs_elem = try rhs.elemValue(mod, i);
2935 const of_math_result = try shlWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
2936 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
2937 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
2938 }
2939 return OverflowArithmeticResult{
2940 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2941 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2942 .storage = .{ .elems = overflowed_data },
2943 } }))),
2944 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
2945 .ty = ty.toIntern(),
2946 .storage = .{ .elems = result_data },
2947 } }))),
2948 };
2949 }
2950 return shlWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2951}
2952
2953pub fn shlWithOverflowScalar(
2954 lhs: Value,
2955 rhs: Value,
2956 ty: Type,
2957 allocator: Allocator,
2958 mod: *Module,
2959) !OverflowArithmeticResult {
2960 const info = ty.intInfo(mod);
2961 var lhs_space: Value.BigIntSpace = undefined;
2962 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2963 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
2964 const limbs = try allocator.alloc(
2965 std.math.big.Limb,
2966 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2967 );
2968 var result_bigint = BigIntMutable{
2969 .limbs = limbs,
2970 .positive = undefined,
2971 .len = undefined,
2972 };
2973 result_bigint.shiftLeft(lhs_bigint, shift);
2974 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2975 if (overflowed) {
2976 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2977 }
2978 return OverflowArithmeticResult{
2979 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),
2980 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
2981 };
2982}
2983
2984pub fn shlSat(
2985 lhs: Value,
2986 rhs: Value,
2987 ty: Type,
2988 arena: Allocator,
2989 mod: *Module,
2990) !Value {
2991 if (ty.zigTypeTag(mod) == .Vector) {
2992 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2993 const scalar_ty = ty.scalarType(mod);
2994 for (result_data, 0..) |*scalar, i| {
2995 const lhs_elem = try lhs.elemValue(mod, i);
2996 const rhs_elem = try rhs.elemValue(mod, i);
2997 scalar.* = try (try shlSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2998 }
2999 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3000 .ty = ty.toIntern(),
3001 .storage = .{ .elems = result_data },
3002 } })));
3003 }
3004 return shlSatScalar(lhs, rhs, ty, arena, mod);
3005}
3006
3007pub fn shlSatScalar(
3008 lhs: Value,
3009 rhs: Value,
3010 ty: Type,
3011 arena: Allocator,
3012 mod: *Module,
3013) !Value {
3014 // TODO is this a performance issue? maybe we should try the operation without
3015 // resorting to BigInt first.
3016 const info = ty.intInfo(mod);
3017
3018 var lhs_space: Value.BigIntSpace = undefined;
3019 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3020 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3021 const limbs = try arena.alloc(
3022 std.math.big.Limb,
3023 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,
3024 );
3025 var result_bigint = BigIntMutable{
3026 .limbs = limbs,
3027 .positive = undefined,
3028 .len = undefined,
3029 };
3030 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
3031 return mod.intValue_big(ty, result_bigint.toConst());
3032}
3033
3034pub fn shlTrunc(
3035 lhs: Value,
3036 rhs: Value,
3037 ty: Type,
3038 arena: Allocator,
3039 mod: *Module,
3040) !Value {
3041 if (ty.zigTypeTag(mod) == .Vector) {
3042 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3043 const scalar_ty = ty.scalarType(mod);
3044 for (result_data, 0..) |*scalar, i| {
3045 const lhs_elem = try lhs.elemValue(mod, i);
3046 const rhs_elem = try rhs.elemValue(mod, i);
3047 scalar.* = try (try shlTruncScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
3048 }
3049 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3050 .ty = ty.toIntern(),
3051 .storage = .{ .elems = result_data },
3052 } })));
3053 }
3054 return shlTruncScalar(lhs, rhs, ty, arena, mod);
3055}
3056
3057pub fn shlTruncScalar(
3058 lhs: Value,
3059 rhs: Value,
3060 ty: Type,
3061 arena: Allocator,
3062 mod: *Module,
3063) !Value {
3064 const shifted = try lhs.shl(rhs, ty, arena, mod);
3065 const int_info = ty.intInfo(mod);
3066 const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits, mod);
3067 return truncated;
3068}
3069
3070pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3071 if (ty.zigTypeTag(mod) == .Vector) {
3072 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
3073 const scalar_ty = ty.scalarType(mod);
3074 for (result_data, 0..) |*scalar, i| {
3075 const lhs_elem = try lhs.elemValue(mod, i);
3076 const rhs_elem = try rhs.elemValue(mod, i);
3077 scalar.* = try (try shrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
3078 }
3079 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3080 .ty = ty.toIntern(),
3081 .storage = .{ .elems = result_data },
3082 } })));
3083 }
3084 return shrScalar(lhs, rhs, ty, allocator, mod);
3085}
3086
3087pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
3088 // TODO is this a performance issue? maybe we should try the operation without
3089 // resorting to BigInt first.
3090 var lhs_space: Value.BigIntSpace = undefined;
3091 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3092 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3093
3094 const result_limbs = lhs_bigint.limbs.len -| (shift / (@sizeOf(std.math.big.Limb) * 8));
3095 if (result_limbs == 0) {
3096 // The shift is enough to remove all the bits from the number, which means the
3097 // result is 0 or -1 depending on the sign.
3098 if (lhs_bigint.positive) {
3099 return mod.intValue(ty, 0);
3100 } else {
3101 return mod.intValue(ty, -1);
3102 }
3103 }
3104
3105 const limbs = try allocator.alloc(
3106 std.math.big.Limb,
3107 result_limbs,
3108 );
3109 var result_bigint = BigIntMutable{
3110 .limbs = limbs,
3111 .positive = undefined,
3112 .len = undefined,
3113 };
3114 result_bigint.shiftRight(lhs_bigint, shift);
3115 return mod.intValue_big(ty, result_bigint.toConst());
3116}
3117
3118pub fn floatNeg(
3119 val: Value,
3120 float_type: Type,
3121 arena: Allocator,
3122 mod: *Module,
3123) !Value {
3124 if (float_type.zigTypeTag(mod) == .Vector) {
3125 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3126 const scalar_ty = float_type.scalarType(mod);
3127 for (result_data, 0..) |*scalar, i| {
3128 const elem_val = try val.elemValue(mod, i);
3129 scalar.* = try (try floatNegScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3130 }
3131 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3132 .ty = float_type.toIntern(),
3133 .storage = .{ .elems = result_data },
3134 } })));
3135 }
3136 return floatNegScalar(val, float_type, mod);
3137}
3138
3139pub fn floatNegScalar(
3140 val: Value,
3141 float_type: Type,
3142 mod: *Module,
3143) !Value {
3144 const target = mod.getTarget();
3145 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3146 16 => .{ .f16 = -val.toFloat(f16, mod) },
3147 32 => .{ .f32 = -val.toFloat(f32, mod) },
3148 64 => .{ .f64 = -val.toFloat(f64, mod) },
3149 80 => .{ .f80 = -val.toFloat(f80, mod) },
3150 128 => .{ .f128 = -val.toFloat(f128, mod) },
3151 else => unreachable,
3152 };
3153 return Value.fromInterned((try mod.intern(.{ .float = .{
3154 .ty = float_type.toIntern(),
3155 .storage = storage,
3156 } })));
3157}
3158
3159pub fn floatAdd(
3160 lhs: Value,
3161 rhs: Value,
3162 float_type: Type,
3163 arena: Allocator,
3164 mod: *Module,
3165) !Value {
3166 if (float_type.zigTypeTag(mod) == .Vector) {
3167 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3168 const scalar_ty = float_type.scalarType(mod);
3169 for (result_data, 0..) |*scalar, i| {
3170 const lhs_elem = try lhs.elemValue(mod, i);
3171 const rhs_elem = try rhs.elemValue(mod, i);
3172 scalar.* = try (try floatAddScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3173 }
3174 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3175 .ty = float_type.toIntern(),
3176 .storage = .{ .elems = result_data },
3177 } })));
3178 }
3179 return floatAddScalar(lhs, rhs, float_type, mod);
3180}
3181
3182pub fn floatAddScalar(
3183 lhs: Value,
3184 rhs: Value,
3185 float_type: Type,
3186 mod: *Module,
3187) !Value {
3188 const target = mod.getTarget();
3189 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3190 16 => .{ .f16 = lhs.toFloat(f16, mod) + rhs.toFloat(f16, mod) },
3191 32 => .{ .f32 = lhs.toFloat(f32, mod) + rhs.toFloat(f32, mod) },
3192 64 => .{ .f64 = lhs.toFloat(f64, mod) + rhs.toFloat(f64, mod) },
3193 80 => .{ .f80 = lhs.toFloat(f80, mod) + rhs.toFloat(f80, mod) },
3194 128 => .{ .f128 = lhs.toFloat(f128, mod) + rhs.toFloat(f128, mod) },
3195 else => unreachable,
3196 };
3197 return Value.fromInterned((try mod.intern(.{ .float = .{
3198 .ty = float_type.toIntern(),
3199 .storage = storage,
3200 } })));
3201}
3202
3203pub fn floatSub(
3204 lhs: Value,
3205 rhs: Value,
3206 float_type: Type,
3207 arena: Allocator,
3208 mod: *Module,
3209) !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 lhs_elem = try lhs.elemValue(mod, i);
3215 const rhs_elem = try rhs.elemValue(mod, i);
3216 scalar.* = try (try floatSubScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3217 }
3218 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3219 .ty = float_type.toIntern(),
3220 .storage = .{ .elems = result_data },
3221 } })));
3222 }
3223 return floatSubScalar(lhs, rhs, float_type, mod);
3224}
3225
3226pub fn floatSubScalar(
3227 lhs: Value,
3228 rhs: Value,
3229 float_type: Type,
3230 mod: *Module,
3231) !Value {
3232 const target = mod.getTarget();
3233 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3234 16 => .{ .f16 = lhs.toFloat(f16, mod) - rhs.toFloat(f16, mod) },
3235 32 => .{ .f32 = lhs.toFloat(f32, mod) - rhs.toFloat(f32, mod) },
3236 64 => .{ .f64 = lhs.toFloat(f64, mod) - rhs.toFloat(f64, mod) },
3237 80 => .{ .f80 = lhs.toFloat(f80, mod) - rhs.toFloat(f80, mod) },
3238 128 => .{ .f128 = lhs.toFloat(f128, mod) - rhs.toFloat(f128, mod) },
3239 else => unreachable,
3240 };
3241 return Value.fromInterned((try mod.intern(.{ .float = .{
3242 .ty = float_type.toIntern(),
3243 .storage = storage,
3244 } })));
3245}
3246
3247pub fn floatDiv(
3248 lhs: Value,
3249 rhs: Value,
3250 float_type: Type,
3251 arena: Allocator,
3252 mod: *Module,
3253) !Value {
3254 if (float_type.zigTypeTag(mod) == .Vector) {
3255 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3256 const scalar_ty = float_type.scalarType(mod);
3257 for (result_data, 0..) |*scalar, i| {
3258 const lhs_elem = try lhs.elemValue(mod, i);
3259 const rhs_elem = try rhs.elemValue(mod, i);
3260 scalar.* = try (try floatDivScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3261 }
3262 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3263 .ty = float_type.toIntern(),
3264 .storage = .{ .elems = result_data },
3265 } })));
3266 }
3267 return floatDivScalar(lhs, rhs, float_type, mod);
3268}
3269
3270pub fn floatDivScalar(
3271 lhs: Value,
3272 rhs: Value,
3273 float_type: Type,
3274 mod: *Module,
3275) !Value {
3276 const target = mod.getTarget();
3277 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3278 16 => .{ .f16 = lhs.toFloat(f16, mod) / rhs.toFloat(f16, mod) },
3279 32 => .{ .f32 = lhs.toFloat(f32, mod) / rhs.toFloat(f32, mod) },
3280 64 => .{ .f64 = lhs.toFloat(f64, mod) / rhs.toFloat(f64, mod) },
3281 80 => .{ .f80 = lhs.toFloat(f80, mod) / rhs.toFloat(f80, mod) },
3282 128 => .{ .f128 = lhs.toFloat(f128, mod) / rhs.toFloat(f128, mod) },
3283 else => unreachable,
3284 };
3285 return Value.fromInterned((try mod.intern(.{ .float = .{
3286 .ty = float_type.toIntern(),
3287 .storage = storage,
3288 } })));
3289}
3290
3291pub fn floatDivFloor(
3292 lhs: Value,
3293 rhs: Value,
3294 float_type: Type,
3295 arena: Allocator,
3296 mod: *Module,
3297) !Value {
3298 if (float_type.zigTypeTag(mod) == .Vector) {
3299 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3300 const scalar_ty = float_type.scalarType(mod);
3301 for (result_data, 0..) |*scalar, i| {
3302 const lhs_elem = try lhs.elemValue(mod, i);
3303 const rhs_elem = try rhs.elemValue(mod, i);
3304 scalar.* = try (try floatDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3305 }
3306 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3307 .ty = float_type.toIntern(),
3308 .storage = .{ .elems = result_data },
3309 } })));
3310 }
3311 return floatDivFloorScalar(lhs, rhs, float_type, mod);
3312}
3313
3314pub fn floatDivFloorScalar(
3315 lhs: Value,
3316 rhs: Value,
3317 float_type: Type,
3318 mod: *Module,
3319) !Value {
3320 const target = mod.getTarget();
3321 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3322 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3323 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3324 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3325 80 => .{ .f80 = @divFloor(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3326 128 => .{ .f128 = @divFloor(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3327 else => unreachable,
3328 };
3329 return Value.fromInterned((try mod.intern(.{ .float = .{
3330 .ty = float_type.toIntern(),
3331 .storage = storage,
3332 } })));
3333}
3334
3335pub fn floatDivTrunc(
3336 lhs: Value,
3337 rhs: Value,
3338 float_type: Type,
3339 arena: Allocator,
3340 mod: *Module,
3341) !Value {
3342 if (float_type.zigTypeTag(mod) == .Vector) {
3343 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3344 const scalar_ty = float_type.scalarType(mod);
3345 for (result_data, 0..) |*scalar, i| {
3346 const lhs_elem = try lhs.elemValue(mod, i);
3347 const rhs_elem = try rhs.elemValue(mod, i);
3348 scalar.* = try (try floatDivTruncScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3349 }
3350 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3351 .ty = float_type.toIntern(),
3352 .storage = .{ .elems = result_data },
3353 } })));
3354 }
3355 return floatDivTruncScalar(lhs, rhs, float_type, mod);
3356}
3357
3358pub fn floatDivTruncScalar(
3359 lhs: Value,
3360 rhs: Value,
3361 float_type: Type,
3362 mod: *Module,
3363) !Value {
3364 const target = mod.getTarget();
3365 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3366 16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3367 32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3368 64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3369 80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },
3370 128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },
3371 else => unreachable,
3372 };
3373 return Value.fromInterned((try mod.intern(.{ .float = .{
3374 .ty = float_type.toIntern(),
3375 .storage = storage,
3376 } })));
3377}
3378
3379pub fn floatMul(
3380 lhs: Value,
3381 rhs: Value,
3382 float_type: Type,
3383 arena: Allocator,
3384 mod: *Module,
3385) !Value {
3386 if (float_type.zigTypeTag(mod) == .Vector) {
3387 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3388 const scalar_ty = float_type.scalarType(mod);
3389 for (result_data, 0..) |*scalar, i| {
3390 const lhs_elem = try lhs.elemValue(mod, i);
3391 const rhs_elem = try rhs.elemValue(mod, i);
3392 scalar.* = try (try floatMulScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3393 }
3394 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3395 .ty = float_type.toIntern(),
3396 .storage = .{ .elems = result_data },
3397 } })));
3398 }
3399 return floatMulScalar(lhs, rhs, float_type, mod);
3400}
3401
3402pub fn floatMulScalar(
3403 lhs: Value,
3404 rhs: Value,
3405 float_type: Type,
3406 mod: *Module,
3407) !Value {
3408 const target = mod.getTarget();
3409 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3410 16 => .{ .f16 = lhs.toFloat(f16, mod) * rhs.toFloat(f16, mod) },
3411 32 => .{ .f32 = lhs.toFloat(f32, mod) * rhs.toFloat(f32, mod) },
3412 64 => .{ .f64 = lhs.toFloat(f64, mod) * rhs.toFloat(f64, mod) },
3413 80 => .{ .f80 = lhs.toFloat(f80, mod) * rhs.toFloat(f80, mod) },
3414 128 => .{ .f128 = lhs.toFloat(f128, mod) * rhs.toFloat(f128, mod) },
3415 else => unreachable,
3416 };
3417 return Value.fromInterned((try mod.intern(.{ .float = .{
3418 .ty = float_type.toIntern(),
3419 .storage = storage,
3420 } })));
3421}
3422
3423pub fn sqrt(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3424 if (float_type.zigTypeTag(mod) == .Vector) {
3425 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3426 const scalar_ty = float_type.scalarType(mod);
3427 for (result_data, 0..) |*scalar, i| {
3428 const elem_val = try val.elemValue(mod, i);
3429 scalar.* = try (try sqrtScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3430 }
3431 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3432 .ty = float_type.toIntern(),
3433 .storage = .{ .elems = result_data },
3434 } })));
3435 }
3436 return sqrtScalar(val, float_type, mod);
3437}
3438
3439pub fn sqrtScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3440 const target = mod.getTarget();
3441 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3442 16 => .{ .f16 = @sqrt(val.toFloat(f16, mod)) },
3443 32 => .{ .f32 = @sqrt(val.toFloat(f32, mod)) },
3444 64 => .{ .f64 = @sqrt(val.toFloat(f64, mod)) },
3445 80 => .{ .f80 = @sqrt(val.toFloat(f80, mod)) },
3446 128 => .{ .f128 = @sqrt(val.toFloat(f128, mod)) },
3447 else => unreachable,
3448 };
3449 return Value.fromInterned((try mod.intern(.{ .float = .{
3450 .ty = float_type.toIntern(),
3451 .storage = storage,
3452 } })));
3453}
3454
3455pub fn sin(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3456 if (float_type.zigTypeTag(mod) == .Vector) {
3457 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3458 const scalar_ty = float_type.scalarType(mod);
3459 for (result_data, 0..) |*scalar, i| {
3460 const elem_val = try val.elemValue(mod, i);
3461 scalar.* = try (try sinScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3462 }
3463 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3464 .ty = float_type.toIntern(),
3465 .storage = .{ .elems = result_data },
3466 } })));
3467 }
3468 return sinScalar(val, float_type, mod);
3469}
3470
3471pub fn sinScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3472 const target = mod.getTarget();
3473 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3474 16 => .{ .f16 = @sin(val.toFloat(f16, mod)) },
3475 32 => .{ .f32 = @sin(val.toFloat(f32, mod)) },
3476 64 => .{ .f64 = @sin(val.toFloat(f64, mod)) },
3477 80 => .{ .f80 = @sin(val.toFloat(f80, mod)) },
3478 128 => .{ .f128 = @sin(val.toFloat(f128, mod)) },
3479 else => unreachable,
3480 };
3481 return Value.fromInterned((try mod.intern(.{ .float = .{
3482 .ty = float_type.toIntern(),
3483 .storage = storage,
3484 } })));
3485}
3486
3487pub fn cos(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3488 if (float_type.zigTypeTag(mod) == .Vector) {
3489 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3490 const scalar_ty = float_type.scalarType(mod);
3491 for (result_data, 0..) |*scalar, i| {
3492 const elem_val = try val.elemValue(mod, i);
3493 scalar.* = try (try cosScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3494 }
3495 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3496 .ty = float_type.toIntern(),
3497 .storage = .{ .elems = result_data },
3498 } })));
3499 }
3500 return cosScalar(val, float_type, mod);
3501}
3502
3503pub fn cosScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3504 const target = mod.getTarget();
3505 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3506 16 => .{ .f16 = @cos(val.toFloat(f16, mod)) },
3507 32 => .{ .f32 = @cos(val.toFloat(f32, mod)) },
3508 64 => .{ .f64 = @cos(val.toFloat(f64, mod)) },
3509 80 => .{ .f80 = @cos(val.toFloat(f80, mod)) },
3510 128 => .{ .f128 = @cos(val.toFloat(f128, mod)) },
3511 else => unreachable,
3512 };
3513 return Value.fromInterned((try mod.intern(.{ .float = .{
3514 .ty = float_type.toIntern(),
3515 .storage = storage,
3516 } })));
3517}
3518
3519pub fn tan(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3520 if (float_type.zigTypeTag(mod) == .Vector) {
3521 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3522 const scalar_ty = float_type.scalarType(mod);
3523 for (result_data, 0..) |*scalar, i| {
3524 const elem_val = try val.elemValue(mod, i);
3525 scalar.* = try (try tanScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3526 }
3527 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3528 .ty = float_type.toIntern(),
3529 .storage = .{ .elems = result_data },
3530 } })));
3531 }
3532 return tanScalar(val, float_type, mod);
3533}
3534
3535pub fn tanScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3536 const target = mod.getTarget();
3537 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3538 16 => .{ .f16 = @tan(val.toFloat(f16, mod)) },
3539 32 => .{ .f32 = @tan(val.toFloat(f32, mod)) },
3540 64 => .{ .f64 = @tan(val.toFloat(f64, mod)) },
3541 80 => .{ .f80 = @tan(val.toFloat(f80, mod)) },
3542 128 => .{ .f128 = @tan(val.toFloat(f128, mod)) },
3543 else => unreachable,
3544 };
3545 return Value.fromInterned((try mod.intern(.{ .float = .{
3546 .ty = float_type.toIntern(),
3547 .storage = storage,
3548 } })));
3549}
3550
3551pub fn exp(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3552 if (float_type.zigTypeTag(mod) == .Vector) {
3553 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3554 const scalar_ty = float_type.scalarType(mod);
3555 for (result_data, 0..) |*scalar, i| {
3556 const elem_val = try val.elemValue(mod, i);
3557 scalar.* = try (try expScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3558 }
3559 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3560 .ty = float_type.toIntern(),
3561 .storage = .{ .elems = result_data },
3562 } })));
3563 }
3564 return expScalar(val, float_type, mod);
3565}
3566
3567pub fn expScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3568 const target = mod.getTarget();
3569 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3570 16 => .{ .f16 = @exp(val.toFloat(f16, mod)) },
3571 32 => .{ .f32 = @exp(val.toFloat(f32, mod)) },
3572 64 => .{ .f64 = @exp(val.toFloat(f64, mod)) },
3573 80 => .{ .f80 = @exp(val.toFloat(f80, mod)) },
3574 128 => .{ .f128 = @exp(val.toFloat(f128, mod)) },
3575 else => unreachable,
3576 };
3577 return Value.fromInterned((try mod.intern(.{ .float = .{
3578 .ty = float_type.toIntern(),
3579 .storage = storage,
3580 } })));
3581}
3582
3583pub fn exp2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3584 if (float_type.zigTypeTag(mod) == .Vector) {
3585 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3586 const scalar_ty = float_type.scalarType(mod);
3587 for (result_data, 0..) |*scalar, i| {
3588 const elem_val = try val.elemValue(mod, i);
3589 scalar.* = try (try exp2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3590 }
3591 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3592 .ty = float_type.toIntern(),
3593 .storage = .{ .elems = result_data },
3594 } })));
3595 }
3596 return exp2Scalar(val, float_type, mod);
3597}
3598
3599pub fn exp2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3600 const target = mod.getTarget();
3601 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3602 16 => .{ .f16 = @exp2(val.toFloat(f16, mod)) },
3603 32 => .{ .f32 = @exp2(val.toFloat(f32, mod)) },
3604 64 => .{ .f64 = @exp2(val.toFloat(f64, mod)) },
3605 80 => .{ .f80 = @exp2(val.toFloat(f80, mod)) },
3606 128 => .{ .f128 = @exp2(val.toFloat(f128, mod)) },
3607 else => unreachable,
3608 };
3609 return Value.fromInterned((try mod.intern(.{ .float = .{
3610 .ty = float_type.toIntern(),
3611 .storage = storage,
3612 } })));
3613}
3614
3615pub fn log(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3616 if (float_type.zigTypeTag(mod) == .Vector) {
3617 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3618 const scalar_ty = float_type.scalarType(mod);
3619 for (result_data, 0..) |*scalar, i| {
3620 const elem_val = try val.elemValue(mod, i);
3621 scalar.* = try (try logScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3622 }
3623 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3624 .ty = float_type.toIntern(),
3625 .storage = .{ .elems = result_data },
3626 } })));
3627 }
3628 return logScalar(val, float_type, mod);
3629}
3630
3631pub fn logScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3632 const target = mod.getTarget();
3633 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3634 16 => .{ .f16 = @log(val.toFloat(f16, mod)) },
3635 32 => .{ .f32 = @log(val.toFloat(f32, mod)) },
3636 64 => .{ .f64 = @log(val.toFloat(f64, mod)) },
3637 80 => .{ .f80 = @log(val.toFloat(f80, mod)) },
3638 128 => .{ .f128 = @log(val.toFloat(f128, mod)) },
3639 else => unreachable,
3640 };
3641 return Value.fromInterned((try mod.intern(.{ .float = .{
3642 .ty = float_type.toIntern(),
3643 .storage = storage,
3644 } })));
3645}
3646
3647pub fn log2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3648 if (float_type.zigTypeTag(mod) == .Vector) {
3649 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3650 const scalar_ty = float_type.scalarType(mod);
3651 for (result_data, 0..) |*scalar, i| {
3652 const elem_val = try val.elemValue(mod, i);
3653 scalar.* = try (try log2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3654 }
3655 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3656 .ty = float_type.toIntern(),
3657 .storage = .{ .elems = result_data },
3658 } })));
3659 }
3660 return log2Scalar(val, float_type, mod);
3661}
3662
3663pub fn log2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3664 const target = mod.getTarget();
3665 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3666 16 => .{ .f16 = @log2(val.toFloat(f16, mod)) },
3667 32 => .{ .f32 = @log2(val.toFloat(f32, mod)) },
3668 64 => .{ .f64 = @log2(val.toFloat(f64, mod)) },
3669 80 => .{ .f80 = @log2(val.toFloat(f80, mod)) },
3670 128 => .{ .f128 = @log2(val.toFloat(f128, mod)) },
3671 else => unreachable,
3672 };
3673 return Value.fromInterned((try mod.intern(.{ .float = .{
3674 .ty = float_type.toIntern(),
3675 .storage = storage,
3676 } })));
3677}
3678
3679pub fn log10(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3680 if (float_type.zigTypeTag(mod) == .Vector) {
3681 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3682 const scalar_ty = float_type.scalarType(mod);
3683 for (result_data, 0..) |*scalar, i| {
3684 const elem_val = try val.elemValue(mod, i);
3685 scalar.* = try (try log10Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3686 }
3687 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3688 .ty = float_type.toIntern(),
3689 .storage = .{ .elems = result_data },
3690 } })));
3691 }
3692 return log10Scalar(val, float_type, mod);
3693}
3694
3695pub fn log10Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3696 const target = mod.getTarget();
3697 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3698 16 => .{ .f16 = @log10(val.toFloat(f16, mod)) },
3699 32 => .{ .f32 = @log10(val.toFloat(f32, mod)) },
3700 64 => .{ .f64 = @log10(val.toFloat(f64, mod)) },
3701 80 => .{ .f80 = @log10(val.toFloat(f80, mod)) },
3702 128 => .{ .f128 = @log10(val.toFloat(f128, mod)) },
3703 else => unreachable,
3704 };
3705 return Value.fromInterned((try mod.intern(.{ .float = .{
3706 .ty = float_type.toIntern(),
3707 .storage = storage,
3708 } })));
3709}
3710
3711pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
3712 if (ty.zigTypeTag(mod) == .Vector) {
3713 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3714 const scalar_ty = ty.scalarType(mod);
3715 for (result_data, 0..) |*scalar, i| {
3716 const elem_val = try val.elemValue(mod, i);
3717 scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod);
3718 }
3719 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3720 .ty = ty.toIntern(),
3721 .storage = .{ .elems = result_data },
3722 } })));
3723 }
3724 return absScalar(val, ty, mod, arena);
3725}
3726
3727pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value {
3728 switch (ty.zigTypeTag(mod)) {
3729 .Int => {
3730 var buffer: Value.BigIntSpace = undefined;
3731 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3732 operand_bigint.abs();
3733
3734 return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst());
3735 },
3736 .ComptimeInt => {
3737 var buffer: Value.BigIntSpace = undefined;
3738 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3739 operand_bigint.abs();
3740
3741 return mod.intValue_big(ty, operand_bigint.toConst());
3742 },
3743 .ComptimeFloat, .Float => {
3744 const target = mod.getTarget();
3745 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
3746 16 => .{ .f16 = @abs(val.toFloat(f16, mod)) },
3747 32 => .{ .f32 = @abs(val.toFloat(f32, mod)) },
3748 64 => .{ .f64 = @abs(val.toFloat(f64, mod)) },
3749 80 => .{ .f80 = @abs(val.toFloat(f80, mod)) },
3750 128 => .{ .f128 = @abs(val.toFloat(f128, mod)) },
3751 else => unreachable,
3752 };
3753 return Value.fromInterned((try mod.intern(.{ .float = .{
3754 .ty = ty.toIntern(),
3755 .storage = storage,
3756 } })));
3757 },
3758 else => unreachable,
3759 }
3760}
3761
3762pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3763 if (float_type.zigTypeTag(mod) == .Vector) {
3764 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3765 const scalar_ty = float_type.scalarType(mod);
3766 for (result_data, 0..) |*scalar, i| {
3767 const elem_val = try val.elemValue(mod, i);
3768 scalar.* = try (try floorScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3769 }
3770 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3771 .ty = float_type.toIntern(),
3772 .storage = .{ .elems = result_data },
3773 } })));
3774 }
3775 return floorScalar(val, float_type, mod);
3776}
3777
3778pub fn floorScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3779 const target = mod.getTarget();
3780 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3781 16 => .{ .f16 = @floor(val.toFloat(f16, mod)) },
3782 32 => .{ .f32 = @floor(val.toFloat(f32, mod)) },
3783 64 => .{ .f64 = @floor(val.toFloat(f64, mod)) },
3784 80 => .{ .f80 = @floor(val.toFloat(f80, mod)) },
3785 128 => .{ .f128 = @floor(val.toFloat(f128, mod)) },
3786 else => unreachable,
3787 };
3788 return Value.fromInterned((try mod.intern(.{ .float = .{
3789 .ty = float_type.toIntern(),
3790 .storage = storage,
3791 } })));
3792}
3793
3794pub fn ceil(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3795 if (float_type.zigTypeTag(mod) == .Vector) {
3796 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3797 const scalar_ty = float_type.scalarType(mod);
3798 for (result_data, 0..) |*scalar, i| {
3799 const elem_val = try val.elemValue(mod, i);
3800 scalar.* = try (try ceilScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3801 }
3802 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3803 .ty = float_type.toIntern(),
3804 .storage = .{ .elems = result_data },
3805 } })));
3806 }
3807 return ceilScalar(val, float_type, mod);
3808}
3809
3810pub fn ceilScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3811 const target = mod.getTarget();
3812 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3813 16 => .{ .f16 = @ceil(val.toFloat(f16, mod)) },
3814 32 => .{ .f32 = @ceil(val.toFloat(f32, mod)) },
3815 64 => .{ .f64 = @ceil(val.toFloat(f64, mod)) },
3816 80 => .{ .f80 = @ceil(val.toFloat(f80, mod)) },
3817 128 => .{ .f128 = @ceil(val.toFloat(f128, mod)) },
3818 else => unreachable,
3819 };
3820 return Value.fromInterned((try mod.intern(.{ .float = .{
3821 .ty = float_type.toIntern(),
3822 .storage = storage,
3823 } })));
3824}
3825
3826pub fn round(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3827 if (float_type.zigTypeTag(mod) == .Vector) {
3828 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3829 const scalar_ty = float_type.scalarType(mod);
3830 for (result_data, 0..) |*scalar, i| {
3831 const elem_val = try val.elemValue(mod, i);
3832 scalar.* = try (try roundScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3833 }
3834 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3835 .ty = float_type.toIntern(),
3836 .storage = .{ .elems = result_data },
3837 } })));
3838 }
3839 return roundScalar(val, float_type, mod);
3840}
3841
3842pub fn roundScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3843 const target = mod.getTarget();
3844 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3845 16 => .{ .f16 = @round(val.toFloat(f16, mod)) },
3846 32 => .{ .f32 = @round(val.toFloat(f32, mod)) },
3847 64 => .{ .f64 = @round(val.toFloat(f64, mod)) },
3848 80 => .{ .f80 = @round(val.toFloat(f80, mod)) },
3849 128 => .{ .f128 = @round(val.toFloat(f128, mod)) },
3850 else => unreachable,
3851 };
3852 return Value.fromInterned((try mod.intern(.{ .float = .{
3853 .ty = float_type.toIntern(),
3854 .storage = storage,
3855 } })));
3856}
3857
3858pub fn trunc(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3859 if (float_type.zigTypeTag(mod) == .Vector) {
3860 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3861 const scalar_ty = float_type.scalarType(mod);
3862 for (result_data, 0..) |*scalar, i| {
3863 const elem_val = try val.elemValue(mod, i);
3864 scalar.* = try (try truncScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3865 }
3866 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3867 .ty = float_type.toIntern(),
3868 .storage = .{ .elems = result_data },
3869 } })));
3870 }
3871 return truncScalar(val, float_type, mod);
3872}
3873
3874pub fn truncScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3875 const target = mod.getTarget();
3876 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3877 16 => .{ .f16 = @trunc(val.toFloat(f16, mod)) },
3878 32 => .{ .f32 = @trunc(val.toFloat(f32, mod)) },
3879 64 => .{ .f64 = @trunc(val.toFloat(f64, mod)) },
3880 80 => .{ .f80 = @trunc(val.toFloat(f80, mod)) },
3881 128 => .{ .f128 = @trunc(val.toFloat(f128, mod)) },
3882 else => unreachable,
3883 };
3884 return Value.fromInterned((try mod.intern(.{ .float = .{
3885 .ty = float_type.toIntern(),
3886 .storage = storage,
3887 } })));
3888}
3889
3890pub fn mulAdd(
3891 float_type: Type,
3892 mulend1: Value,
3893 mulend2: Value,
3894 addend: Value,
3895 arena: Allocator,
3896 mod: *Module,
3897) !Value {
3898 if (float_type.zigTypeTag(mod) == .Vector) {
3899 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3900 const scalar_ty = float_type.scalarType(mod);
3901 for (result_data, 0..) |*scalar, i| {
3902 const mulend1_elem = try mulend1.elemValue(mod, i);
3903 const mulend2_elem = try mulend2.elemValue(mod, i);
3904 const addend_elem = try addend.elemValue(mod, i);
3905 scalar.* = try (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, mod)).intern(scalar_ty, mod);
3906 }
3907 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3908 .ty = float_type.toIntern(),
3909 .storage = .{ .elems = result_data },
3910 } })));
3911 }
3912 return mulAddScalar(float_type, mulend1, mulend2, addend, mod);
3913}
3914
3915pub fn mulAddScalar(
3916 float_type: Type,
3917 mulend1: Value,
3918 mulend2: Value,
3919 addend: Value,
3920 mod: *Module,
3921) Allocator.Error!Value {
3922 const target = mod.getTarget();
3923 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3924 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, mod), mulend2.toFloat(f16, mod), addend.toFloat(f16, mod)) },
3925 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, mod), mulend2.toFloat(f32, mod), addend.toFloat(f32, mod)) },
3926 64 => .{ .f64 = @mulAdd(f64, mulend1.toFloat(f64, mod), mulend2.toFloat(f64, mod), addend.toFloat(f64, mod)) },
3927 80 => .{ .f80 = @mulAdd(f80, mulend1.toFloat(f80, mod), mulend2.toFloat(f80, mod), addend.toFloat(f80, mod)) },
3928 128 => .{ .f128 = @mulAdd(f128, mulend1.toFloat(f128, mod), mulend2.toFloat(f128, mod), addend.toFloat(f128, mod)) },
3929 else => unreachable,
3930 };
3931 return Value.fromInterned((try mod.intern(.{ .float = .{
3932 .ty = float_type.toIntern(),
3933 .storage = storage,
3934 } })));
3935}
3936
3937/// If the value is represented in-memory as a series of bytes that all
3938/// have the same value, return that byte value, otherwise null.
3939pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module) !?u8 {
3940 const abi_size = std.math.cast(usize, ty.abiSize(mod)) orelse return null;
3941 assert(abi_size >= 1);
3942 const byte_buffer = try mod.gpa.alloc(u8, abi_size);
3943 defer mod.gpa.free(byte_buffer);
3944
3945 writeToMemory(val, ty, mod, byte_buffer) catch |err| switch (err) {
3946 error.OutOfMemory => return error.OutOfMemory,
3947 error.ReinterpretDeclRef => return null,
3948 // TODO: The writeToMemory function was originally created for the purpose
3949 // of comptime pointer casting. However, it is now additionally being used
3950 // for checking the actual memory layout that will be generated by machine
3951 // code late in compilation. So, this error handling is too aggressive and
3952 // causes some false negatives, causing less-than-ideal code generation.
3953 error.IllDefinedMemoryLayout => return null,
3954 error.Unimplemented => return null,
3955 };
3956 const first_byte = byte_buffer[0];
3957 for (byte_buffer[1..]) |byte| {
3958 if (byte != first_byte) return null;
3959 }
3960 return first_byte;
3961}
3962
3963pub fn isGenericPoison(val: Value) bool {
3964 return val.toIntern() == .generic_poison;
3965}
3966
3967/// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value.
3968/// If `val` is not undef, the bounds are both `val`.
3969/// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type.
3970/// If `val` is undef and is a `comptime_int`, returns null.
3971pub fn intValueBounds(val: Value, mod: *Module) !?[2]Value {
3972 if (!val.isUndef(mod)) return .{ val, val };
3973 const ty = mod.intern_pool.typeOf(val.toIntern());
3974 if (ty == .comptime_int_type) return null;
3975 return .{
3976 try Type.fromInterned(ty).minInt(mod, Type.fromInterned(ty)),
3977 try Type.fromInterned(ty).maxInt(mod, Type.fromInterned(ty)),
3978 };
3979}
3980
3981/// This type is not copyable since it may contain pointers to its inner data.
3982pub const Payload = struct {
3983 tag: Tag,
3984
3985 pub const Slice = struct {
3986 base: Payload,
3987 data: struct {
3988 ptr: Value,
3989 len: Value,
3990 },
3991 };
3992
3993 pub const Bytes = struct {
3994 base: Payload,
3995 /// Includes the sentinel, if any.
3996 data: []const u8,
3997 };
3998
3999 pub const SubValue = struct {
4000 base: Payload,
4001 data: Value,
4002 };
4003
4004 pub const Aggregate = struct {
4005 base: Payload,
4006 /// Field values. The types are according to the struct or array type.
4007 /// The length is provided here so that copying a Value does not depend on the Type.
4008 data: []Value,
4009 };
4010
4011 pub const Union = struct {
4012 pub const base_tag = Tag.@"union";
4013
4014 base: Payload = .{ .tag = base_tag },
4015 data: Data,
4016
4017 pub const Data = struct {
4018 tag: ?Value,
4019 val: Value,
4020 };
4021 };
4022};
4023
4024pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
4025
4026pub const zero_usize: Value = .{ .ip_index = .zero_usize, .legacy = undefined };
4027pub const zero_u8: Value = .{ .ip_index = .zero_u8, .legacy = undefined };
4028pub const zero_comptime_int: Value = .{ .ip_index = .zero, .legacy = undefined };
4029pub const one_comptime_int: Value = .{ .ip_index = .one, .legacy = undefined };
4030pub const negative_one_comptime_int: Value = .{ .ip_index = .negative_one, .legacy = undefined };
4031pub const undef: Value = .{ .ip_index = .undef, .legacy = undefined };
4032pub const @"void": Value = .{ .ip_index = .void_value, .legacy = undefined };
4033pub const @"null": Value = .{ .ip_index = .null_value, .legacy = undefined };
4034pub const @"false": Value = .{ .ip_index = .bool_false, .legacy = undefined };
4035pub const @"true": Value = .{ .ip_index = .bool_true, .legacy = undefined };
4036pub const @"unreachable": Value = .{ .ip_index = .unreachable_value, .legacy = undefined };
4037
4038pub const generic_poison: Value = .{ .ip_index = .generic_poison, .legacy = undefined };
4039pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type, .legacy = undefined };
4040pub const empty_struct: Value = .{ .ip_index = .empty_struct, .legacy = undefined };
4041
4042pub fn makeBool(x: bool) Value {
4043 return if (x) Value.true else Value.false;
4044}
4045
4046pub const RuntimeIndex = InternPool.RuntimeIndex;
4047
4048/// This function is used in the debugger pretty formatters in tools/ to fetch the
4049/// Tag to Payload mapping to facilitate fancy debug printing for this type.
4050fn dbHelper(self: *Value, tag_to_payload_map: *map: {
4051 const tags = @typeInfo(Tag).Enum.fields;
4052 var fields: [tags.len]std.builtin.Type.StructField = undefined;
4053 for (&fields, tags) |*field, t| field.* = .{
4054 .name = t.name ++ "",
4055 .type = *@field(Tag, t.name).Type(),
4056 .default_value = null,
4057 .is_comptime = false,
4058 .alignment = 0,
4059 };
4060 break :map @Type(.{ .Struct = .{
4061 .layout = .Extern,
4062 .fields = &fields,
4063 .decls = &.{},
4064 .is_tuple = false,
4065 } });
4066}) void {
4067 _ = self;
4068 _ = tag_to_payload_map;
4069}
4070
4071comptime {
4072 if (builtin.mode == .Debug) {
4073 _ = &dbHelper;
4074 }
4075}