authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-05 23:32:22-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-05 23:32:22-08:00
log648b492ef1d962cabd7d2f017ef47aef73c0c3aa
tree844d80b99d08bc7f9c3b5ff8902fbb15a587b6cc
parent5cf138e512a06dca65b737f27e4493cdfb3b7ddb
parentdd54804d8623a6d022dcac63359636b990b926f9
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18831 from ziglang/flatten-value

flatten value.zig into Value.zig (refactor only)

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

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