authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-05 18:07:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-05 18:07:56-07:00
log2de5e31721e2dc952219d08a417d7dc57efb3cba
tree63d878f0eae8474e4f67035c81a3b9b830830c4d
parent39ec3d311673716e145957d6d81f9d4ec7848471

compiler: flatten Value struct

This commit is almost entirely whitespace.

1 files changed, 3807 insertions(+), 3809 deletions(-)

src/value.zig+3807-3809
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Type = @import("type.zig").Type;3const Type = @import("type.zig").Type;
4const log2 = std.math.log2;
5const assert = std.debug.assert;4const assert = std.debug.assert;
6const BigIntConst = std.math.big.int.Const;5const BigIntConst = std.math.big.int.Const;
7const BigIntMutable = std.math.big.int.Mutable;6const BigIntMutable = std.math.big.int.Mutable;
...@@ -11,4067 +10,4066 @@ const Module = @import("Module.zig");...@@ -11,4067 +10,4066 @@ const Module = @import("Module.zig");
11const TypedValue = @import("TypedValue.zig");10const TypedValue = @import("TypedValue.zig");
12const Sema = @import("Sema.zig");11const Sema = @import("Sema.zig");
13const InternPool = @import("InternPool.zig");12const InternPool = @import("InternPool.zig");
1413pub const Value = @This();
15pub const Value = struct {14
16 /// We are migrating towards using this for every Value object. However, many15/// We are migrating towards using this for every Value object. However, many
17 /// values are still represented the legacy way. This is indicated by using16/// values are still represented the legacy way. This is indicated by using
18 /// InternPool.Index.none.17/// InternPool.Index.none.
19 ip_index: InternPool.Index,18ip_index: InternPool.Index,
2019
21 /// This is the raw data, with no bookkeeping, no memory awareness,20/// This is the raw data, with no bookkeeping, no memory awareness,
22 /// no de-duplication, and no type system awareness.21/// no de-duplication, and no type system awareness.
23 /// This union takes advantage of the fact that the first page of memory22/// 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.23/// is unmapped, giving us 4096 possible enum tags that have no payload.
25 legacy: extern union {24legacy: extern union {
26 ptr_otherwise: *Payload,25 ptr_otherwise: *Payload,
27 },26},
2827
29 // Keep in sync with tools/stage2_pretty_printers_common.py28// Keep in sync with tools/stage2_pretty_printers_common.py
30 pub const Tag = enum(usize) {29pub const Tag = enum(usize) {
31 // The first section of this enum are tags that require no payload.30 // The first section of this enum are tags that require no payload.
32 // After this, the tag requires a payload.31 // After this, the tag requires a payload.
3332
34 /// When the type is error union:33 /// When the type is error union:
35 /// * If the tag is `.@"error"`, the error union is an error.34 /// * If the tag is `.@"error"`, the error union is an error.
36 /// * If the tag is `.eu_payload`, the error union is a payload.35 /// * 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 union36 /// * 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 as37 /// 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"`.38 /// a tag of `.eu_payload`, with a sub-tag of `.@"error"`.
40 eu_payload,39 eu_payload,
41 /// When the type is optional:40 /// When the type is optional:
42 /// * If the tag is `.null_value`, the optional is null.41 /// * If the tag is `.null_value`, the optional is null.
43 /// * If the tag is `.opt_payload`, the optional is a payload.42 /// * If the tag is `.opt_payload`, the optional is a payload.
44 /// * A nested optional such as `??T` in which the the outer optional43 /// * A nested optional such as `??T` in which the the outer optional
45 /// is non-null, but the inner optional is null, is represented as44 /// 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`.45 /// a tag of `.opt_payload`, with a sub-tag of `.null_value`.
47 opt_payload,46 opt_payload,
48 /// Pointer and length as sub `Value` objects.47 /// Pointer and length as sub `Value` objects.
49 slice,48 slice,
50 /// A slice of u8 whose memory is managed externally.49 /// A slice of u8 whose memory is managed externally.
51 bytes,50 bytes,
52 /// This value is repeated some number of times. The amount of times to repeat51 /// This value is repeated some number of times. The amount of times to repeat
53 /// is stored externally.52 /// is stored externally.
54 repeated,53 repeated,
55 /// An instance of a struct, array, or vector.54 /// An instance of a struct, array, or vector.
56 /// Each element/field stored as a `Value`.55 /// Each element/field stored as a `Value`.
57 /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,56 /// 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.57 /// so the slice length will be one more than the type's array length.
59 aggregate,58 aggregate,
60 /// An instance of a union.59 /// An instance of a union.
61 @"union",60 @"union",
6261
63 pub fn Type(comptime t: Tag) type {62 pub fn Type(comptime t: Tag) type {
64 return switch (t) {63 return switch (t) {
65 .eu_payload,64 .eu_payload,
66 .opt_payload,65 .opt_payload,
67 .repeated,66 .repeated,
68 => Payload.SubValue,67 => Payload.SubValue,
69 .slice => Payload.Slice,68 .slice => Payload.Slice,
70 .bytes => Payload.Bytes,69 .bytes => Payload.Bytes,
71 .aggregate => Payload.Aggregate,70 .aggregate => Payload.Aggregate,
72 .@"union" => Payload.Union,71 .@"union" => Payload.Union,
73 };72 };
74 }73 }
7574
76 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Value {75 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Value {
77 const ptr = try ally.create(t.Type());76 const ptr = try ally.create(t.Type());
78 ptr.* = .{77 ptr.* = .{
79 .base = .{ .tag = t },78 .base = .{ .tag = t },
80 .data = data,79 .data = data,
81 };80 };
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{81 return Value{
95 .ip_index = .none,82 .ip_index = .none,
96 .legacy = .{ .ptr_otherwise = payload },83 .legacy = .{ .ptr_otherwise = &ptr.base },
97 };84 };
98 }85 }
9986
100 pub fn tag(self: Value) Tag {87 pub fn Data(comptime t: Tag) type {
101 assert(self.ip_index == .none);88 return std.meta.fieldInfo(t.Type(), .data).type;
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 }89 }
90};
12491
125 pub fn castTag(self: Value, comptime t: Tag) ?*t.Type() {92pub fn initPayload(payload: *Payload) Value {
126 if (self.ip_index != .none) return null;93 return Value{
94 .ip_index = .none,
95 .legacy = .{ .ptr_otherwise = payload },
96 };
97}
12798
128 if (self.legacy.ptr_otherwise.tag == t)99pub fn tag(self: Value) Tag {
129 return @fieldParentPtr(t.Type(), "base", self.legacy.ptr_otherwise);100 assert(self.ip_index == .none);
101 return self.legacy.ptr_otherwise.tag;
102}
130103
104/// Prefer `castTag` to this.
105pub fn cast(self: Value, comptime T: type) ?*T {
106 if (self.ip_index != .none) {
131 return null;107 return null;
132 }108 }
133109 if (@hasField(T, "base_tag")) {
134 pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {110 return self.castTag(T.base_tag);
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 }111 }
240112 inline for (@typeInfo(Tag).Enum.fields) |field| {
241 fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTerminatedString {113 const t = @as(Tag, @enumFromInt(field.value));
242 const gpa = mod.gpa;114 if (self.legacy.ptr_otherwise.tag == t) {
243 const ip = &mod.intern_pool;115 if (T == t.Type()) {
244 const len = @as(usize, @intCast(len_u64));116 return @fieldParentPtr(T, "base", self.legacy.ptr_otherwise);
245 try ip.string_bytes.ensureUnusedCapacity(gpa, len);117 }
246 for (0..len) |i| {118 return null;
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 }119 }
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 }120 }
262121 unreachable;
263 pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {122}
264 if (val.ip_index != .none) return (try mod.getCoerced(val, ty)).toIntern();123
265 const ip = &mod.intern_pool;124pub fn castTag(self: Value, comptime t: Tag) ?*t.Type() {
266 switch (val.tag()) {125 if (self.ip_index != .none) return null;
267 .eu_payload => {126
268 const pl = val.castTag(.eu_payload).?.data;127 if (self.legacy.ptr_otherwise.tag == t)
269 return mod.intern(.{ .error_union = .{128 return @fieldParentPtr(t.Type(), "base", self.legacy.ptr_otherwise);
270 .ty = ty.toIntern(),129
271 .val = .{ .payload = try pl.intern(ty.errorUnionPayload(mod), mod) },130 return null;
272 } });131}
273 },132
274 .opt_payload => {133pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
275 const pl = val.castTag(.opt_payload).?.data;134 _ = val;
276 return mod.intern(.{ .opt = .{135 _ = fmt;
277 .ty = ty.toIntern(),136 _ = options;
278 .val = try pl.intern(ty.optionalChild(mod), mod),137 _ = writer;
279 } });138 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
280 },139}
281 .slice => {140
282 const pl = val.castTag(.slice).?.data;141/// This is a debug function. In order to print values in a meaningful way
283 return mod.intern(.{ .slice = .{142/// we also need access to the type.
284 .ty = ty.toIntern(),143pub fn dump(
285 .len = try pl.len.intern(Type.usize, mod),144 start_val: Value,
286 .ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod),145 comptime fmt: []const u8,
287 } });146 _: std.fmt.FormatOptions,
288 },147 out_stream: anytype,
289 .bytes => {148) !void {
290 const pl = val.castTag(.bytes).?.data;149 comptime assert(fmt.len == 0);
291 return mod.intern(.{ .aggregate = .{150 if (start_val.ip_index != .none) {
292 .ty = ty.toIntern(),151 try out_stream.print("(interned: {})", .{start_val.toIntern()});
293 .storage = .{ .bytes = pl },152 return;
294 } });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);
295 },205 },
296 .repeated => {206 },
297 const pl = val.castTag(.repeated).?.data;207 else => unreachable,
298 return mod.intern(.{ .aggregate = .{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 = .{
299 .ty = ty.toIntern(),324 .ty = ty.toIntern(),
300 .storage = .{ .repeated_elem = try pl.intern(ty.childType(mod), mod) },325 .tag = try pl_tag.intern(ty.unionTagTypeHypothetical(mod), mod),
326 .val = try pl.val.intern(ty.unionFieldType(pl_tag, mod).?, mod),
301 } });327 } });
302 },328 } else {
303 .aggregate => {329 return mod.intern(.{ .un = .{
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(),330 .ty = ty.toIntern(),
318 .storage = .{ .elems = new_elems },331 .tag = .none,
332 .val = try pl.val.intern(try ty.unionBackingType(mod), mod),
319 } });333 } });
320 },334 }
321 .@"union" => {335 },
322 const pl = val.castTag(.@"union").?.data;336 }
323 if (pl.tag) |pl_tag| {337}
324 return mod.intern(.{ .un = .{338
325 .ty = ty.toIntern(),339pub fn unintern(val: Value, arena: Allocator, mod: *Module) Allocator.Error!Value {
326 .tag = try pl_tag.intern(ty.unionTagTypeHypothetical(mod), mod),340 return if (val.ip_index == .none) val else switch (mod.intern_pool.indexToKey(val.toIntern())) {
327 .val = try pl.val.intern(ty.unionFieldType(pl_tag, mod).?, mod),341 .int_type,
328 } });342 .ptr_type,
329 } else {343 .array_type,
330 return mod.intern(.{ .un = .{344 .vector_type,
331 .ty = ty.toIntern(),345 .opt_type,
332 .tag = .none,346 .anyframe_type,
333 .val = try pl.val.intern(try ty.unionBackingType(mod), mod),347 .error_union_type,
334 } });348 .simple_type,
335 }349 .struct_type,
336 },350 .anon_struct_type,
337 }351 .union_type,
338 }352 .opaque_type,
339353 .enum_type,
340 pub fn unintern(val: Value, arena: Allocator, mod: *Module) Allocator.Error!Value {354 .func_type,
341 return if (val.ip_index == .none) val else switch (mod.intern_pool.indexToKey(val.toIntern())) {355 .error_set_type,
342 .int_type,356 .inferred_error_set_type,
343 .ptr_type,357
344 .array_type,358 .undef,
345 .vector_type,359 .simple_value,
346 .opt_type,360 .variable,
347 .anyframe_type,361 .extern_func,
348 .error_union_type,362 .func,
349 .simple_type,363 .int,
350 .struct_type,364 .err,
351 .anon_struct_type,365 .enum_literal,
352 .union_type,366 .enum_tag,
353 .opaque_type,367 .empty_enum_value,
354 .enum_type,368 .float,
355 .func_type,369 .ptr,
356 .error_set_type,370 => val,
357 .inferred_error_set_type,371
358372 .error_union => |error_union| switch (error_union.val) {
359 .undef,373 .err_name => val,
360 .simple_value,374 .payload => |payload| Tag.eu_payload.create(arena, Value.fromInterned(payload)),
361 .variable,375 },
362 .extern_func,376
363 .func,377 .slice => |slice| Tag.slice.create(arena, .{
364 .int,378 .ptr = Value.fromInterned(slice.ptr),
365 .err,379 .len = Value.fromInterned(slice.len),
366 .enum_literal,380 }),
367 .enum_tag,381
368 .empty_enum_value,382 .opt => |opt| switch (opt.val) {
369 .float,383 .none => val,
370 .ptr,384 else => |payload| Tag.opt_payload.create(arena, Value.fromInterned(payload)),
371 => val,385 },
372386
373 .error_union => |error_union| switch (error_union.val) {387 .aggregate => |aggregate| switch (aggregate.storage) {
374 .err_name => val,388 .bytes => |bytes| Tag.bytes.create(arena, try arena.dupe(u8, bytes)),
375 .payload => |payload| Tag.eu_payload.create(arena, Value.fromInterned(payload)),389 .elems => |old_elems| {
376 },390 const new_elems = try arena.alloc(Value, old_elems.len);
377391 for (new_elems, old_elems) |*new_elem, old_elem| new_elem.* = Value.fromInterned(old_elem);
378 .slice => |slice| Tag.slice.create(arena, .{392 return Tag.aggregate.create(arena, new_elems);
379 .ptr = Value.fromInterned(slice.ptr),393 },
380 .len = Value.fromInterned(slice.len),394 .repeated_elem => |elem| Tag.repeated.create(arena, Value.fromInterned(elem)),
381 }),395 },
382396
383 .opt => |opt| switch (opt.val) {397 .un => |un| Tag.@"union".create(arena, .{
384 .none => val,398 // toValue asserts that the value cannot be .none which is valid on unions.
385 else => |payload| Tag.opt_payload.create(arena, Value.fromInterned(payload)),399 .tag = if (un.tag == .none) null else Value.fromInterned(un.tag),
386 },400 .val = Value.fromInterned(un.val),
401 }),
402
403 .memoized_call => unreachable,
404 };
405}
387406
388 .aggregate => |aggregate| switch (aggregate.storage) {407pub fn fromInterned(i: InternPool.Index) Value {
389 .bytes => |bytes| Tag.bytes.create(arena, try arena.dupe(u8, bytes)),408 assert(i != .none);
390 .elems => |old_elems| {409 return .{
391 const new_elems = try arena.alloc(Value, old_elems.len);410 .ip_index = i,
392 for (new_elems, old_elems) |*new_elem, old_elem| new_elem.* = Value.fromInterned(old_elem);411 .legacy = undefined,
393 return Tag.aggregate.create(arena, new_elems);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();
394 },474 },
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 },475 },
443 .enum_type => |enum_type| try mod.getCoerced(val, Type.fromInterned(enum_type.tag_ty)),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(),
444 else => unreachable,481 else => unreachable,
445 };482 },
446 }483 };
447484}
448 /// Asserts the value is an integer.485
449 pub fn toBigInt(val: Value, space: *BigIntSpace, mod: *Module) BigIntConst {486pub fn isFuncBody(val: Value, mod: *Module) bool {
450 return val.toBigIntAdvanced(space, mod, null) catch unreachable;487 return mod.intern_pool.isFuncBody(val.toIntern());
451 }488}
452489
453 /// Asserts the value is an integer.490pub fn getFunction(val: Value, mod: *Module) ?InternPool.Key.Func {
454 pub fn toBigIntAdvanced(491 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
455 val: Value,492 .func => |x| x,
456 space: *BigIntSpace,493 else => null,
457 mod: *Module,494 } else null;
458 opt_sema: ?*Sema,495}
459 ) Module.CompileError!BigIntConst {496
460 return switch (val.toIntern()) {497pub fn getExternFunc(val: Value, mod: *Module) ?InternPool.Key.ExternFunc {
461 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),498 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
462 .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),499 .extern_func => |extern_func| extern_func,
463 .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),500 else => null,
464 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {501 } else null;
465 .int => |int| switch (int.storage) {502}
466 .u64, .i64, .big_int => int.storage.toBigInt(space),503
467 .lazy_align, .lazy_size => |ty| {504pub fn getVariable(val: Value, mod: *Module) ?InternPool.Key.Variable {
468 if (opt_sema) |sema| try sema.resolveTypeLayout(Type.fromInterned(ty));505 return if (val.ip_index != .none) switch (mod.intern_pool.indexToKey(val.toIntern())) {
469 const x = switch (int.storage) {506 .variable => |variable| variable,
470 else => unreachable,507 else => null,
471 .lazy_align => Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),508 } else null;
472 .lazy_size => Type.fromInterned(ty).abiSize(mod),509}
473 };510
474 return BigIntMutable.init(&space.limbs, x).toConst();511/// If the value fits in a u64, return it, otherwise null.
475 },512/// Asserts not undefined.
476 },513pub fn getUnsignedInt(val: Value, mod: *Module) ?u64 {
477 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).toBigIntAdvanced(space, mod, opt_sema),514 return getUnsignedIntAdvanced(val, mod, null) catch unreachable;
478 .opt, .ptr => BigIntMutable.init(515}
479 &space.limbs,516
480 (try val.getUnsignedIntAdvanced(mod, opt_sema)).?,517/// If the value fits in a u64, return it, otherwise null.
481 ).toConst(),518/// Asserts not undefined.
482 else => unreachable,519pub fn getUnsignedIntAdvanced(val: Value, mod: *Module, opt_sema: ?*Sema) !?u64 {
483 },520 return switch (val.toIntern()) {
484 };521 .undef => unreachable,
485 }522 .bool_false => 0,
486523 .bool_true => 1,
487 pub fn isFuncBody(val: Value, mod: *Module) bool {524 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
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,525 .undef => unreachable,
523 .bool_false => 0,526 .int => |int| switch (int.storage) {
524 .bool_true => 1,527 .big_int => |big_int| big_int.to(u64) catch null,
525 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {528 .u64 => |x| x,
526 .undef => unreachable,529 .i64 => |x| std.math.cast(u64, x),
527 .int => |int| switch (int.storage) {530 .lazy_align => |ty| if (opt_sema) |sema|
528 .big_int => |big_int| big_int.to(u64) catch null,531 (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0)
529 .u64 => |x| x,532 else
530 .i64 => |x| std.math.cast(u64, x),533 Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),
531 .lazy_align => |ty| if (opt_sema) |sema|534 .lazy_size => |ty| if (opt_sema) |sema|
532 (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar.toByteUnits(0)535 (try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar
533 else536 else
534 Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0),537 Type.fromInterned(ty).abiSize(mod),
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 },538 },
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) {539 .ptr => |ptr| switch (ptr.addr) {
604 .decl, .mut_decl, .comptime_field, .anon_decl => return true,540 .int => |int| Value.fromInterned(int).getUnsignedIntAdvanced(mod, opt_sema),
605 .eu_payload, .opt_payload => |base| check = Value.fromInterned(base),541 .elem => |elem| {
606 .elem, .field => |base_index| check = Value.fromInterned(base_index.base),542 const base_addr = (try Value.fromInterned(elem.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
607 .int => return false,543 const elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod);
608 },544 return base_addr + elem.index * elem_ty.abiSize(mod);
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 },545 },
737 .Packed => {546 .field => |field| {
738 const backing_ty = try ty.unionBackingType(mod);547 const base_addr = (try Value.fromInterned(field.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null;
739 const byte_count: usize = @intCast(backing_ty.abiSize(mod));548 const struct_ty = Type.fromInterned(mod.intern_pool.typeOf(field.base)).childType(mod);
740 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);549 if (opt_sema) |sema| try sema.resolveTypeLayout(struct_ty);
550 return base_addr + struct_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod);
741 },551 },
552 else => null,
742 },553 },
743 .Pointer => {554 .opt => |opt| switch (opt.val) {
744 if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout;555 .none => 0,
745 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;556 else => |payload| Value.fromInterned(payload).getUnsignedIntAdvanced(mod, opt_sema),
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 },557 },
812 .Float => switch (ty.floatBits(target)) {558 else => null,
813 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian),559 },
814 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian),560 };
815 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian),561}
816 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian),562
817 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian),563/// Asserts the value is an integer and it fits in a u64
818 else => unreachable,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)),
819 },585 },
820 .Vector => {586 else => unreachable,
821 const elem_ty = ty.childType(mod);587 },
822 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));588 };
823 const len = @as(usize, @intCast(ty.arrayLen(mod)));589}
824590
825 var bits: u16 = 0;591pub fn toBool(val: Value) bool {
826 var elem_i: usize = 0;592 return switch (val.toIntern()) {
827 while (elem_i < len) : (elem_i += 1) {593 .bool_true => true,
828 // On big-endian systems, LLVM reverses the element order of vectors by default594 .bool_false => false,
829 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;595 else => unreachable,
830 const elem_val = try val.elemValue(mod, tgt_elem_i);596 };
831 try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);597}
832 bits += elem_bit_size;598
833 }599fn isDeclRef(val: Value, mod: *Module) bool {
834 },600 var check = val;
835 .Struct => {601 while (true) switch (mod.intern_pool.indexToKey(check.toIntern())) {
836 const struct_type = ip.indexToKey(ty.toIntern()).struct_type;602 .ptr => |ptr| switch (ptr.addr) {
837 // Sema is supposed to have emitted a compile error already in the case of Auto,603 .decl, .mut_decl, .comptime_field, .anon_decl => return true,
838 // and Extern is handled in non-packed writeToMemory.604 .eu_payload, .opt_payload => |base| check = Value.fromInterned(base),
839 assert(struct_type.layout == .Packed);605 .elem, .field => |base_index| check = Value.fromInterned(base_index.base),
840 var bits: u16 = 0;606 .int => return false,
841 for (0..struct_type.field_types.len) |i| {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));
842 const field_val = switch (val.ip_index) {676 const field_val = switch (val.ip_index) {
843 .none => switch (val.tag()) {677 .none => switch (val.tag()) {
844 .bytes => unreachable,678 .bytes => {
679 buffer[off] = val.castTag(.bytes).?.data[i];
680 continue;
681 },
845 .aggregate => val.castTag(.aggregate).?.data[i],682 .aggregate => val.castTag(.aggregate).?.data[i],
846 .repeated => val.castTag(.repeated).?.data,683 .repeated => val.castTag(.repeated).?.data,
847 else => unreachable,684 else => unreachable,
848 },685 },
849 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {686 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
850 .bytes => unreachable,687 .bytes => |bytes| {
688 buffer[off] = bytes[i];
689 continue;
690 },
851 .elems => |elems| elems[i],691 .elems => |elems| elems[i],
852 .repeated_elem => |elem| elem,692 .repeated_elem => |elem| elem,
853 }),693 }),
854 };694 };
855 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);695 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
856 const field_bits: u16 = @intCast(field_ty.bitSize(mod));696 try writeToMemory(field_val, field_ty, mod, buffer[off..]);
857 try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);697 },
858 bits += field_bits;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]);
859 }734 }
860 },735 },
861 .Union => {736 .Packed => {
862 const union_obj = mod.typeToUnion(ty).?;737 const backing_ty = try ty.unionBackingType(mod);
863 switch (union_obj.getLayout(ip)) {738 const byte_count: usize = @intCast(backing_ty.abiSize(mod));
864 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory739 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
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 },740 },
955 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{741 },
956 .ty = ty.toIntern(),742 .Pointer => {
957 .storage = switch (ty.floatBits(target)) {743 if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout;
958 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readInt(u16, buffer[0..2], endian))) },744 if (val.isDeclRef(mod)) return error.ReinterpretDeclRef;
959 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readInt(u32, buffer[0..4], endian))) },745 return val.writeToMemory(Type.usize, mod, buffer);
960 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readInt(u64, buffer[0..8], endian))) },746 },
961 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readInt(u80, buffer[0..10], endian))) },747 .Optional => {
962 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readInt(u128, buffer[0..16], endian))) },748 if (!ty.isPtrLikeOptional(mod)) return error.IllDefinedMemoryLayout;
963 else => unreachable,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);
964 },804 },
965 } }))),805 .lazy_size => |lazy_size| {
966 .Array => {806 const num = Type.fromInterned(lazy_size).abiSize(mod);
967 const elem_ty = ty.childType(mod);807 std.mem.writeVarPackedInt(buffer, bit_offset, bits, num, endian);
968 const elem_size = elem_ty.abiSize(mod);808 },
969 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));809 }
970 var offset: usize = 0;810 },
971 for (elems) |*elem| {811 .Float => switch (ty.floatBits(target)) {
972 elem.* = try (try readFromMemory(elem_ty, mod, buffer[offset..], arena)).intern(elem_ty, mod);812 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian),
973 offset += @as(usize, @intCast(elem_size));813 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian),
974 }814 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian),
975 return Value.fromInterned((try mod.intern(.{ .aggregate = .{815 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian),
976 .ty = ty.toIntern(),816 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian),
977 .storage = .{ .elems = elems },817 else => unreachable,
978 } })));818 },
979 },819 .Vector => {
980 .Vector => {820 const elem_ty = ty.childType(mod);
981 // We use byte_count instead of abi_size here, so that any padding bytes821 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));
982 // follow the data bytes, on both big- and little-endian systems.822 const len = @as(usize, @intCast(ty.arrayLen(mod)));
983 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;823
984 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);824 var bits: u16 = 0;
985 },825 var elem_i: usize = 0;
986 .Struct => {826 while (elem_i < len) : (elem_i += 1) {
987 const struct_type = mod.typeToStruct(ty).?;827 // On big-endian systems, LLVM reverses the element order of vectors by default
988 switch (struct_type.layout) {828 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
989 .Auto => unreachable, // Sema is supposed to have emitted a compile error already829 const elem_val = try val.elemValue(mod, tgt_elem_i);
990 .Extern => {830 try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
991 const field_types = struct_type.field_types;831 bits += elem_bit_size;
992 const field_vals = try arena.alloc(InternPool.Index, field_types.len);832 }
993 for (field_vals, 0..) |*field_val, i| {833 },
994 const field_ty = Type.fromInterned(field_types.get(ip)[i]);834 .Struct => {
995 const off: usize = @intCast(ty.structFieldOffset(i, mod));835 const struct_type = ip.indexToKey(ty.toIntern()).struct_type;
996 const sz: usize = @intCast(field_ty.abiSize(mod));836 // Sema is supposed to have emitted a compile error already in the case of Auto,
997 field_val.* = try (try readFromMemory(field_ty, mod, buffer[off..(off + sz)], arena)).intern(field_ty, mod);837 // and Extern is handled in non-packed writeToMemory.
998 }838 assert(struct_type.layout == .Packed);
999 return Value.fromInterned((try mod.intern(.{ .aggregate = .{839 var bits: u16 = 0;
1000 .ty = ty.toIntern(),840 for (0..struct_type.field_types.len) |i| {
1001 .storage = .{ .elems = field_vals },841 const field_val = switch (val.ip_index) {
1002 } })));842 .none => switch (val.tag()) {
1003 },843 .bytes => unreachable,
1004 .Packed => {844 .aggregate => val.castTag(.aggregate).?.data[i],
1005 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;845 .repeated => val.castTag(.repeated).?.data,
1006 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);846 else => unreachable,
1007 },847 },
1008 }848 else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
1009 },849 .bytes => unreachable,
1010 .ErrorSet => {850 .elems => |elems| elems[i],
1011 const bits = mod.errorSetBits();851 .repeated_elem => |elem| elem,
1012 const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);852 }),
1013 const int = std.mem.readVarInt(u64, buffer[0..byte_count], endian);853 };
1014 const index = (int << @as(u6, @intCast(64 - bits))) >> @as(u6, @intCast(64 - bits));854 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
1015 const name = mod.global_error_set.keys()[@intCast(index)];855 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
1016856 try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
1017 return Value.fromInterned((try mod.intern(.{ .err = .{857 bits += field_bits;
1018 .ty = ty.toIntern(),858 }
1019 .name = name,859 },
1020 } })));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,
1021 },963 },
1022 .Union => switch (ty.containerLayout(mod)) {964 } }))),
1023 .Auto => return error.IllDefinedMemoryLayout,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
1024 .Extern => {989 .Extern => {
1025 const union_size = ty.abiSize(mod);990 const field_types = struct_type.field_types;
1026 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });991 const field_vals = try arena.alloc(InternPool.Index, field_types.len);
1027 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);992 for (field_vals, 0..) |*field_val, i| {
1028 return Value.fromInterned((try mod.intern(.{ .un = .{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 = .{
1029 .ty = ty.toIntern(),999 .ty = ty.toIntern(),
1030 .tag = .none,1000 .storage = .{ .elems = field_vals },
1031 .val = val,
1032 } })));1001 } })));
1033 },1002 },
1034 .Packed => {1003 .Packed => {
1035 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;1004 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1036 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);1005 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1037 },1006 },
1038 },1007 }
1039 .Pointer => {1008 },
1040 assert(!ty.isSlice(mod)); // No well defined layout.1009 .ErrorSet => {
1041 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);1010 const bits = mod.errorSetBits();
1042 return Value.fromInterned((try mod.intern(.{ .ptr = .{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 = .{
1043 .ty = ty.toIntern(),1028 .ty = ty.toIntern(),
1044 .addr = .{ .int = int_val.toIntern() },1029 .tag = .none,
1030 .val = val,
1045 } })));1031 } })));
1046 },1032 },
1047 .Optional => {1033 .Packed => {
1048 assert(ty.isPtrLikeOptional(mod));1034 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1049 const child_ty = ty.optionalChild(mod);1035 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
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 },1036 },
1060 else => return error.Unimplemented,1037 },
1061 }1038 .Pointer => {
1062 }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);
10631098
1064 /// Load a Value from the contents of `buffer`.1099 // Fast path for integers <= u64
1065 ///1100 if (bits <= 64) {
1066 /// Both the start and the end of the provided buffer must be tight, since1101 const int_ty = switch (ty_tag) {
1067 /// big-endian packed memory layouts start at the end of the buffer.1102 .Int => ty,
1068 pub fn readFromPackedMemory(1103 .Enum => ty.intTagType(mod),
1069 ty: Type,1104 else => unreachable,
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 };1105 };
1088 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) {1106 return mod.getCoerced(switch (int_info.signedness) {
1089 return Value.false;1107 .signed => return mod.intValue(
1090 } else {1108 int_ty,
1091 return Value.true;1109 std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed),
1092 }1110 ),
1093 },1111 .unsigned => return mod.intValue(
1094 .Int, .Enum => |ty_tag| {1112 int_ty,
1095 if (buffer.len == 0) return mod.intValue(ty, 0);1113 std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned),
1096 const int_info = ty.intInfo(mod);1114 ),
1097 const bits = int_info.bits;1115 }, ty);
1098 if (bits == 0) return mod.intValue(ty, 0);1116 }
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);
11241117
1125 var bigint = BigIntMutable.init(limbs_buffer, 0);1118 // Slow path, we have to construct a big-int
1126 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);1119 const abi_size = @as(usize, @intCast(ty.abiSize(mod)));
1127 return mod.intValue_big(ty, bigint.toConst());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,
1128 },1137 },
1129 .Float => return Value.fromInterned((try mod.intern(.{ .float = .{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 = .{
1130 .ty = ty.toIntern(),1152 .ty = ty.toIntern(),
1131 .storage = switch (ty.floatBits(target)) {1153 .storage = .{ .elems = elems },
1132 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian))) },1154 } })));
1133 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian))) },1155 },
1134 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian))) },1156 .Struct => {
1135 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian))) },1157 // Sema is supposed to have emitted a compile error already for Auto layout structs,
1136 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian))) },1158 // and Extern is handled by non-packed readFromMemory.
1137 else => unreachable,1159 const struct_type = mod.typeToPackedStruct(ty).?;
1138 },1160 var bits: u16 = 0;
1139 } }))),1161 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
1140 .Vector => {1162 for (field_vals, 0..) |*field_val, i| {
1141 const elem_ty = ty.childType(mod);1163 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
1142 const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod))));1164 const field_bits: u16 = @intCast(field_ty.bitSize(mod));
11431165 field_val.* = try (try readFromPackedMemory(field_ty, mod, buffer, bit_offset + bits, arena)).intern(field_ty, mod);
1144 var bits: u16 = 0;1166 bits += field_bits;
1145 const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod)));1167 }
1146 for (elems, 0..) |_, i| {1168 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1147 // On big-endian systems, LLVM reverses the element order of vectors by default1169 .ty = ty.toIntern(),
1148 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;1170 .storage = .{ .elems = field_vals },
1149 elems[tgt_elem_i] = try (try readFromPackedMemory(elem_ty, mod, buffer, bit_offset + bits, arena)).intern(elem_ty, mod);1171 } })));
1150 bits += elem_bit_size;1172 },
1151 }1173 .Union => switch (ty.containerLayout(mod)) {
1152 return Value.fromInterned((try mod.intern(.{ .aggregate = .{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 = .{
1153 .ty = ty.toIntern(),1179 .ty = ty.toIntern(),
1154 .storage = .{ .elems = elems },1180 .tag = .none,
1181 .val = val,
1155 } })));1182 } })));
1156 },1183 },
1157 .Struct => {1184 },
1158 // Sema is supposed to have emitted a compile error already for Auto layout structs,1185 .Pointer => {
1159 // and Extern is handled by non-packed readFromMemory.1186 assert(!ty.isSlice(mod)); // No well defined layout.
1160 const struct_type = mod.typeToPackedStruct(ty).?;1187 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
1161 var bits: u16 = 0;1188 },
1162 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);1189 .Optional => {
1163 for (field_vals, 0..) |*field_val, i| {1190 assert(ty.isPtrLikeOptional(mod));
1164 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);1191 const child = ty.optionalChild(mod);
1165 const field_bits: u16 = @intCast(field_ty.bitSize(mod));1192 return readFromPackedMemory(child, mod, buffer, bit_offset, arena);
1166 field_val.* = try (try readFromPackedMemory(field_ty, mod, buffer, bit_offset + bits, arena)).intern(field_ty, mod);1193 },
1167 bits += field_bits;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");
1168 }1206 }
1169 return Value.fromInterned((try mod.intern(.{ .aggregate = .{1207 return @floatFromInt(x);
1170 .ty = ty.toIntern(),1208 },
1171 .storage = .{ .elems = field_vals },1209 .lazy_align => |ty| @floatFromInt(Type.fromInterned(ty).abiAlignment(mod).toByteUnits(0)),
1172 } })));1210 .lazy_size => |ty| @floatFromInt(Type.fromInterned(ty).abiSize(mod)),
1173 },1211 },
1174 .Union => switch (ty.containerLayout(mod)) {1212 .float => |float| switch (float.storage) {
1175 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory1213 inline else => |x| @floatCast(x),
1176 .Packed => {1214 },
1177 const backing_ty = try ty.unionBackingType(mod);1215 else => unreachable,
1178 const val = (try readFromPackedMemory(backing_ty, mod, buffer, bit_offset, arena)).toIntern();1216 };
1179 return Value.fromInterned((try mod.intern(.{ .un = .{1217}
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);
12621218
1263 const limbs = try arena.alloc(1219/// TODO move this to std lib big int code
1264 std.math.big.Limb,1220fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
1265 std.math.big.int.calcTwosCompLimbCount(info.bits),1221 if (limbs.len == 0) return 0;
1266 );
1267 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1268 result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
12691222
1270 return mod.intValue_big(ty, result_bigint.toConst());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);
1271 }1230 }
12721231 if (positive) {
1273 pub fn byteSwap(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {1232 return result;
1274 const info = ty.intInfo(mod);1233 } else {
12751234 return -result;
1276 // Bit count must be evenly divisible by 81235 }
1277 assert(info.bits % 8 == 0);1236}
12781237
1279 var buffer: Value.BigIntSpace = undefined;1238pub fn clz(val: Value, ty: Type, mod: *Module) u64 {
1280 const operand_bigint = val.toBigInt(&buffer, mod);1239 var bigint_buf: BigIntSpace = undefined;
12811240 const bigint = val.toBigInt(&bigint_buf, mod);
1282 const limbs = try arena.alloc(1241 return bigint.clz(ty.intInfo(mod).bits);
1283 std.math.big.Limb,1242}
1284 std.math.big.int.calcTwosCompLimbCount(info.bits),1243
1285 );1244pub fn ctz(val: Value, ty: Type, mod: *Module) u64 {
1286 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };1245 var bigint_buf: BigIntSpace = undefined;
1287 result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8);1246 const bigint = val.toBigInt(&bigint_buf, mod);
12881247 return bigint.ctz(ty.intInfo(mod).bits);
1289 return mod.intValue_big(ty, result_bigint.toConst());1248}
1290 }1249
12911250pub fn popCount(val: Value, ty: Type, mod: *Module) u64 {
1292 /// Asserts the value is an integer and not undefined.1251 var bigint_buf: BigIntSpace = undefined;
1293 /// Returns the number of bits the value requires to represent stored in twos complement form.1252 const bigint = val.toBigInt(&bigint_buf, mod);
1294 pub fn intBitCountTwosComp(self: Value, mod: *Module) usize {1253 return @as(u64, @intCast(bigint.popCount(ty.intInfo(mod).bits)));
1295 var buffer: BigIntSpace = undefined;1254}
1296 const big_int = self.toBigInt(&buffer, mod);1255
1297 return big_int.bitCountTwosComp();1256pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value {
1298 }1257 const info = ty.intInfo(mod);
12991258
1300 /// Converts an integer or a float to a float. May result in a loss of information.1259 var buffer: Value.BigIntSpace = undefined;
1301 /// Caller can find out by equality checking the result against the operand.1260 const operand_bigint = val.toBigInt(&buffer, mod);
1302 pub fn floatCast(self: Value, dest_ty: Type, mod: *Module) !Value {1261
1303 const target = mod.getTarget();1262 const limbs = try arena.alloc(
1304 return Value.fromInterned((try mod.intern(.{ .float = .{1263 std.math.big.Limb,
1305 .ty = dest_ty.toIntern(),1264 std.math.big.int.calcTwosCompLimbCount(info.bits),
1306 .storage = switch (dest_ty.floatBits(target)) {1265 );
1307 16 => .{ .f16 = self.toFloat(f16, mod) },1266 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1308 32 => .{ .f32 = self.toFloat(f32, mod) },1267 result_bigint.bitReverse(operand_bigint, info.signedness, info.bits);
1309 64 => .{ .f64 = self.toFloat(f64, mod) },1268
1310 80 => .{ .f80 = self.toFloat(f80, mod) },1269 return mod.intValue_big(ty, result_bigint.toConst());
1311 128 => .{ .f128 = self.toFloat(f128, mod) },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 },
1312 else => unreachable,1347 else => unreachable,
1313 },1348 },
1314 } })));1349 .int => |int| switch (int.storage) {
1315 }1350 .big_int => |big_int| big_int.orderAgainstScalar(0),
13161351 inline .u64, .i64 => |x| std.math.order(x, 0),
1317 /// Asserts the value is a float1352 .lazy_align => .gt, // alignment is never 0
1318 pub fn floatHasFraction(self: Value, mod: *const Module) bool {1353 .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsAdvanced(
1319 return switch (mod.intern_pool.indexToKey(self.toIntern())) {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),
1320 .float => |float| switch (float.storage) {1363 .float => |float| switch (float.storage) {
1321 inline else => |x| @rem(x, 1) != 0,1364 inline else => |x| std.math.order(x, 0),
1322 },1365 },
1323 else => unreachable,1366 else => unreachable,
1324 };1367 },
1325 }1368 };
13261369}
1327 pub fn orderAgainstZero(lhs: Value, mod: *Module) std.math.Order {1370
1328 return orderAgainstZeroAdvanced(lhs, mod, null) catch unreachable;1371/// Asserts the value is comparable.
1329 }1372pub fn order(lhs: Value, rhs: Value, mod: *Module) std.math.Order {
13301373 return orderAdvanced(lhs, rhs, mod, null) catch unreachable;
1331 pub fn orderAgainstZeroAdvanced(1374}
1332 lhs: Value,1375
1333 mod: *Module,1376/// Asserts the value is comparable.
1334 opt_sema: ?*Sema,1377/// If opt_sema is null then this function asserts things are resolved and cannot fail.
1335 ) Module.CompileError!std.math.Order {1378pub fn orderAdvanced(lhs: Value, rhs: Value, mod: *Module, opt_sema: ?*Sema) !std.math.Order {
1336 return switch (lhs.toIntern()) {1379 const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(mod, opt_sema);
1337 .bool_false => .eq,1380 const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(mod, opt_sema);
1338 .bool_true => .gt,1381 switch (lhs_against_zero) {
1339 else => switch (mod.intern_pool.indexToKey(lhs.toIntern())) {1382 .lt => if (rhs_against_zero != .lt) return .lt,
1340 .ptr => |ptr| switch (ptr.addr) {1383 .eq => return rhs_against_zero.invert(),
1341 .decl, .mut_decl, .comptime_field => .gt,1384 .gt => {},
1342 .int => |int| Value.fromInterned(int).orderAgainstZeroAdvanced(mod, opt_sema),1385 }
1343 .elem => |elem| switch (try Value.fromInterned(elem.base).orderAgainstZeroAdvanced(mod, opt_sema)) {1386 switch (rhs_against_zero) {
1344 .lt => unreachable,1387 .lt => if (lhs_against_zero != .lt) return .gt,
1345 .gt => .gt,1388 .eq => return lhs_against_zero,
1346 .eq => if (elem.index == 0) .eq else .gt,1389 .gt => {},
1347 },1390 }
1348 else => unreachable,1391
1349 },1392 if (lhs.isFloat(mod) or rhs.isFloat(mod)) {
1350 .int => |int| switch (int.storage) {1393 const lhs_f128 = lhs.toFloat(f128, mod);
1351 .big_int => |big_int| big_int.orderAgainstScalar(0),1394 const rhs_f128 = rhs.toFloat(f128, mod);
1352 inline .u64, .i64 => |x| std.math.order(x, 0),1395 return std.math.order(lhs_f128, rhs_f128);
1353 .lazy_align => .gt, // alignment is never 01396 }
1354 .lazy_size => |ty| return if (Type.fromInterned(ty).hasRuntimeBitsAdvanced(1397
1355 mod,1398 var lhs_bigint_space: BigIntSpace = undefined;
1356 false,1399 var rhs_bigint_space: BigIntSpace = undefined;
1357 if (opt_sema) |sema| .{ .sema = sema } else .eager,1400 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, mod, opt_sema);
1358 ) catch |err| switch (err) {1401 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, mod, opt_sema);
1359 error.NeedLazy => unreachable,1402 return lhs_bigint.order(rhs_bigint);
1360 else => |e| return e,1403}
1361 }) .gt else .eq,1404
1362 },1405/// Asserts the value is comparable. Does not take a type parameter because it supports
1363 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).orderAgainstZeroAdvanced(mod, opt_sema),1406/// comparisons between heterogeneous types.
1364 .float => |float| switch (float.storage) {1407pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, mod: *Module) bool {
1365 inline else => |x| std.math.order(x, 0),1408 return compareHeteroAdvanced(lhs, op, rhs, mod, null) catch unreachable;
1366 },1409}
1367 else => unreachable,1410
1368 },1411pub fn compareHeteroAdvanced(
1369 };1412 lhs: Value,
1370 }1413 op: std.math.CompareOperator,
13711414 rhs: Value,
1372 /// Asserts the value is comparable.1415 mod: *Module,
1373 pub fn order(lhs: Value, rhs: Value, mod: *Module) std.math.Order {1416 opt_sema: ?*Sema,
1374 return orderAdvanced(lhs, rhs, mod, null) catch unreachable;1417) !bool {
1375 }1418 if (lhs.pointerDecl(mod)) |lhs_decl| {
13761419 if (rhs.pointerDecl(mod)) |rhs_decl| {
1377 /// Asserts the value is comparable.1420 switch (op) {
1378 /// If opt_sema is null then this function asserts things are resolved and cannot fail.1421 .eq => return lhs_decl == rhs_decl,
1379 pub fn orderAdvanced(lhs: Value, rhs: Value, mod: *Module, opt_sema: ?*Sema) !std.math.Order {1422 .neq => return lhs_decl != rhs_decl,
1380 const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(mod, opt_sema);1423 else => {},
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 }1424 }
1433 } else if (rhs.pointerDecl(mod)) |_| {1425 } else {
1434 switch (op) {1426 switch (op) {
1435 .eq => return false,1427 .eq => return false,
1436 .neq => return true,1428 .neq => return true,
1437 else => {},1429 else => {},
1438 }1430 }
1439 }1431 }
1440 return (try orderAdvanced(lhs, rhs, mod, opt_sema)).compare(op);1432 } else if (rhs.pointerDecl(mod)) |_| {
1441 }1433 switch (op) {
14421434 .eq => return false,
1443 /// Asserts the values are comparable. Both operands have type `ty`.1435 .neq => return true,
1444 /// For vectors, returns true if comparison is true for ALL elements.1436 else => {},
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 }1437 }
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 }1438 }
1439 return (try orderAdvanced(lhs, rhs, mod, opt_sema)).compare(op);
1440}
14821441
1483 pub fn compareAllWithZeroAdvanced(1442/// Asserts the values are comparable. Both operands have type `ty`.
1484 lhs: Value,1443/// For vectors, returns true if comparison is true for ALL elements.
1485 op: std.math.CompareOperator,1444pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) !bool {
1486 sema: *Sema,1445 if (ty.zigTypeTag(mod) == .Vector) {
1487 ) Module.CompileError!bool {1446 const scalar_ty = ty.scalarType(mod);
1488 return compareAllWithZeroAdvancedExtra(lhs, op, sema.mod, sema);1447 for (0..ty.vectorLen(mod)) |i| {
1489 }1448 const lhs_elem = try lhs.elemValue(mod, i);
14901449 const rhs_elem = try rhs.elemValue(mod, i);
1491 pub fn compareAllWithZeroAdvancedExtra(1450 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, mod)) {
1492 lhs: Value,1451 return false;
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 }1452 }
1504 }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}
15051541
1506 switch (mod.intern_pool.indexToKey(lhs.toIntern())) {1542pub fn canMutateComptimeVarState(val: Value, mod: *Module) bool {
1507 .float => |float| switch (float.storage) {1543 return val.isComptimeMutablePtr(mod) or switch (val.toIntern()) {
1508 inline else => |x| if (std.math.isNan(x)) return op == .neq,1544 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1509 },1545 .error_union => |error_union| switch (error_union.val) {
1510 .aggregate => |aggregate| return switch (aggregate.storage) {1546 .err_name => false,
1511 .bytes => |bytes| for (bytes) |byte| {1547 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
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 },1548 },
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) {1549 .ptr => |ptr| switch (ptr.addr) {
1534 .mut_decl, .comptime_field => true,1550 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(mod),
1535 .eu_payload, .opt_payload => |base_ptr| Value.fromInterned(base_ptr).isComptimeMutablePtr(mod),1551 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).canMutateComptimeVarState(mod),
1536 .elem, .field => |base_index| Value.fromInterned(base_index.base).isComptimeMutablePtr(mod),1552 .elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(mod),
1537 else => false,1553 else => false,
1538 },1554 },
1539 else => false,1555 .opt => |opt| switch (opt.val) {
1540 };1556 .none => false,
1541 }1557 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(mod),
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 },1558 },
1566 };1559 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| {
1567 }1560 if (Value.fromInterned(elem).canMutateComptimeVarState(mod)) break true;
15681561 } else false,
1569 /// Gets the decl referenced by this pointer. If the pointer does not point1562 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(mod),
1570 /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),1563 else => false,
1571 /// this function returns null.1564 },
1572 pub fn pointerDecl(val: Value, mod: *Module) ?InternPool.DeclIndex {1565 };
1573 return switch (mod.intern_pool.indexToKey(val.toIntern())) {1566}
1574 .variable => |variable| variable.decl,1567
1575 .extern_func => |extern_func| extern_func.decl,1568/// Gets the decl referenced by this pointer. If the pointer does not point
1576 .func => |func| func.owner_decl,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),
1577 .ptr => |ptr| switch (ptr.addr) {1631 .ptr => |ptr| switch (ptr.addr) {
1578 .decl => |decl| decl,1632 .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index),
1579 .mut_decl => |mut_decl| mut_decl.decl,1633 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValue(mod, index),
1580 else => null,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);
1581 },1658 },
1582 else => null,1659 else => null,
1583 };1660 },
1584 }1661 };
15851662}
1586 pub const slice_ptr_index = 0;
1587 pub const slice_len_index = 1;
15881663
1589 pub fn slicePtr(val: Value, mod: *Module) Value {1664pub fn isLazyAlign(val: Value, mod: *Module) bool {
1590 return Value.fromInterned(mod.intern_pool.slicePtr(val.toIntern()));1665 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1591 }1666 .int => |int| int.storage == .lazy_align,
1667 else => false,
1668 };
1669}
15921670
1593 pub fn sliceLen(val: Value, mod: *Module) u64 {1671pub fn isLazySize(val: Value, mod: *Module) bool {
1594 const ip = &mod.intern_pool;1672 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1595 return switch (ip.indexToKey(val.toIntern())) {1673 .int => |int| int.storage == .lazy_size,
1596 .ptr => |ptr| switch (ip.indexToKey(switch (ptr.addr) {1674 else => false,
1597 .decl => |decl| mod.declPtr(decl).ty.toIntern(),1675 };
1598 .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(),1676}
1599 .anon_decl => |anon_decl| ip.typeOf(anon_decl.val),1677
1600 .comptime_field => |comptime_field| ip.typeOf(comptime_field),1678pub fn isPtrToThreadLocal(val: Value, mod: *Module) bool {
1601 else => unreachable,1679 const backing_decl = mod.intern_pool.getBackingDecl(val.toIntern()).unwrap() orelse return false;
1602 })) {1680 const variable = mod.declPtr(backing_decl).getOwnedVariable(mod) orelse return false;
1603 .array_type => |array_type| array_type.len,1681 return variable.is_threadlocal;
1604 else => 1,1682}
1605 },1683
1606 .slice => |slice| Value.fromInterned(slice.len).toUnsignedInt(mod),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]),
1607 else => unreachable,1699 else => unreachable,
1608 };1700 },
1609 }1701 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
16101702 .ptr => |ptr| switch (ptr.addr) {
1611 /// Asserts the value is a single-item pointer to an array, or an array,1703 .decl => |decl| try mod.declPtr(decl).val.sliceArray(mod, arena, start, end),
1612 /// or an unknown-length pointer, and returns the element value at the index.1704 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod)))
1613 pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {1705 .sliceArray(mod, arena, start, end),
1614 return (try val.maybeElemValue(mod, index)).?;1706 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
1615 }1707 .sliceArray(mod, arena, start, end),
16161708 .elem => |elem| Value.fromInterned(elem.base)
1617 /// Like `elemValue`, but returns `null` instead of asserting on failure.1709 .sliceArray(mod, arena, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))),
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,1710 else => unreachable,
1701 },1711 },
1702 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {1712 .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{
1703 .ptr => |ptr| switch (ptr.addr) {1713 .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) {
1704 .decl => |decl| try mod.declPtr(decl).val.sliceArray(mod, arena, start, end),1714 .array_type => |array_type| try mod.arrayType(.{
1705 .mut_decl => |mut_decl| Value.fromInterned((try mod.declPtr(mut_decl.decl).internValue(mod)))1715 .len = @as(u32, @intCast(end - start)),
1706 .sliceArray(mod, arena, start, end),1716 .child = array_type.child,
1707 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)1717 .sentinel = if (end == array_type.len) array_type.sentinel else .none,
1708 .sliceArray(mod, arena, start, end),1718 }),
1709 .elem => |elem| Value.fromInterned(elem.base)1719 .vector_type => |vector_type| try mod.vectorType(.{
1710 .sliceArray(mod, arena, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))),1720 .len = @as(u32, @intCast(end - start)),
1721 .child = vector_type.child,
1722 }),
1711 else => unreachable,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 },
1712 },1729 },
1713 .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{1730 } }))),
1714 .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) {1731 else => unreachable,
1715 .array_type => |array_type| try mod.arrayType(.{1732 },
1716 .len = @as(u32, @intCast(end - start)),1733 };
1717 .child = array_type.child,1734}
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 }
17361735
1737 pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value {1736pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value {
1738 return switch (val.ip_index) {1737 return switch (val.ip_index) {
1739 .none => switch (val.tag()) {1738 .none => switch (val.tag()) {
1740 .aggregate => {1739 .aggregate => {
1741 const field_values = val.castTag(.aggregate).?.data;1740 const field_values = val.castTag(.aggregate).?.data;
1742 return field_values[index];1741 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 },1742 },
1751 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {1743 .@"union" => {
1752 .undef => |ty| Value.fromInterned((try mod.intern(.{1744 const payload = val.castTag(.@"union").?.data;
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 correct1745 // TODO assert the tag is correct
1764 .un => |un| Value.fromInterned(un.val),1746 return payload.val;
1765 else => unreachable,
1766 },1747 },
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,1748 else => unreachable,
1776 };1749 },
1777 }1750 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
17781751 .undef => |ty| Value.fromInterned((try mod.intern(.{
1779 pub fn unionValue(val: Value, mod: *Module) Value {1752 .undef = Type.fromInterned(ty).structFieldType(index, mod).toIntern(),
1780 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;1753 }))),
1781 return switch (mod.intern_pool.indexToKey(val.toIntern())) {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
1782 .un => |un| Value.fromInterned(un.val),1763 .un => |un| Value.fromInterned(un.val),
1783 else => unreachable,1764 else => unreachable,
1784 };1765 },
1785 }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}
17861777
1787 /// Returns a pointer to the element value at the index.1778pub fn unionValue(val: Value, mod: *Module) Value {
1788 pub fn elemPtr(1779 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1789 val: Value,1780 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1790 elem_ptr_ty: Type,1781 .un => |un| Value.fromInterned(un.val),
1791 index: usize,1782 else => unreachable,
1792 mod: *Module,1783 };
1793 ) Allocator.Error!Value {1784}
1794 const elem_ty = elem_ptr_ty.childType(mod);1785
1795 const ptr_val = switch (mod.intern_pool.indexToKey(val.toIntern())) {1786/// Returns a pointer to the element value at the index.
1796 .slice => |slice| Value.fromInterned(slice.ptr),1787pub fn elemPtr(
1797 else => val,1788 val: Value,
1798 };1789 elem_ptr_ty: Type,
1799 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {1790 index: usize,
1800 .ptr => |ptr| switch (ptr.addr) {1791 mod: *Module,
1801 .elem => |elem| if (Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).eql(elem_ty, mod))1792) Allocator.Error!Value {
1802 return Value.fromInterned((try mod.intern(.{ .ptr = .{1793 const elem_ty = elem_ptr_ty.childType(mod);
1803 .ty = elem_ptr_ty.toIntern(),1794 const ptr_val = switch (mod.intern_pool.indexToKey(val.toIntern())) {
1804 .addr = .{ .elem = .{1795 .slice => |slice| Value.fromInterned(slice.ptr),
1805 .base = elem.base,1796 else => val,
1806 .index = elem.index + index,1797 };
1807 } },1798 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) {
1808 } }))),1799 .ptr => |ptr| switch (ptr.addr) {
1809 else => {},1800 .elem => |elem| if (Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).eql(elem_ty, mod))
1810 },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 } }))),
1811 else => {},1808 else => {},
1812 }1809 },
1813 var ptr_ty_key = mod.intern_pool.indexToKey(elem_ptr_ty.toIntern()).ptr_type;1810 else => {},
1814 assert(ptr_ty_key.flags.size != .Slice);1811 }
1815 ptr_ty_key.flags.size = .Many;1812 var ptr_ty_key = mod.intern_pool.indexToKey(elem_ptr_ty.toIntern()).ptr_type;
1816 return Value.fromInterned((try mod.intern(.{ .ptr = .{1813 assert(ptr_ty_key.flags.size != .Slice);
1817 .ty = elem_ptr_ty.toIntern(),1814 ptr_ty_key.flags.size = .Many;
1818 .addr = .{ .elem = .{1815 return Value.fromInterned((try mod.intern(.{ .ptr = .{
1819 .base = (try mod.getCoerced(ptr_val, try mod.ptrType(ptr_ty_key))).toIntern(),1816 .ty = elem_ptr_ty.toIntern(),
1820 .index = index,1817 .addr = .{ .elem = .{
1821 } },1818 .base = (try mod.getCoerced(ptr_val, try mod.ptrType(ptr_ty_key))).toIntern(),
1822 } })));1819 .index = index,
1823 }1820 } },
18241821 } })));
1825 pub fn isUndef(val: Value, mod: *Module) bool {1822}
1826 return val.ip_index != .none and mod.intern_pool.isUndef(val.toIntern());1823
1827 }1824pub fn isUndef(val: Value, mod: *Module) bool {
18281825 return val.ip_index != .none and mod.intern_pool.isUndef(val.toIntern());
1829 /// TODO: check for cases such as array that is not marked undef but all the element1826}
1830 /// values are marked undef, or struct that is not marked undef but all fields are marked1827
1831 /// undef, etc.1828/// TODO: check for cases such as array that is not marked undef but all the element
1832 pub fn isUndefDeep(val: Value, mod: *Module) bool {1829/// values are marked undef, or struct that is not marked undef but all fields are marked
1833 return val.isUndef(mod);1830/// undef, etc.
1834 }1831pub fn isUndefDeep(val: Value, mod: *Module) bool {
18351832 return val.isUndef(mod);
1836 /// Returns true if any value contained in `self` is undefined.1833}
1837 pub fn anyUndef(val: Value, mod: *Module) !bool {1834
1838 if (val.ip_index == .none) return false;1835/// Returns true if any value contained in `self` is undefined.
1839 return switch (val.toIntern()) {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())) {
1840 .undef => true,1841 .undef => true,
1841 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {1842 .simple_value => |v| v == .undefined,
1842 .undef => true,1843 .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| {
1843 .simple_value => |v| v == .undefined,1844 if (try (try val.elemValue(mod, idx)).anyUndef(mod)) break true;
1844 .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| {1845 } else false,
1845 if (try (try val.elemValue(mod, idx)).anyUndef(mod)) break true;1846 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
1846 } else false,1847 const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
1847 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {1848 if (try anyUndef(Value.fromInterned(elem), mod)) break true;
1848 const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];1849 } else false,
1849 if (try anyUndef(Value.fromInterned(elem), mod)) break true;1850 else => false,
1850 } else false,1851 },
1851 else => false,1852 };
1852 },1853}
1853 };1854
1854 }1855/// Asserts the value is not undefined and not unreachable.
18551856/// C pointers with an integer value of 0 are also considered null.
1856 /// Asserts the value is not undefined and not unreachable.1857pub fn isNull(val: Value, mod: *Module) bool {
1857 /// C pointers with an integer value of 0 are also considered null.1858 return switch (val.toIntern()) {
1858 pub fn isNull(val: Value, mod: *Module) bool {1859 .undef => unreachable,
1859 return switch (val.toIntern()) {1860 .unreachable_value => unreachable,
1861 .null_value => true,
1862 else => return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1860 .undef => unreachable,1863 .undef => unreachable,
1861 .unreachable_value => unreachable,1864 .ptr => |ptr| switch (ptr.addr) {
1862 .null_value => true,1865 .int => {
1863 else => return switch (mod.intern_pool.indexToKey(val.toIntern())) {1866 var buf: BigIntSpace = undefined;
1864 .undef => unreachable,1867 return val.toBigInt(&buf, mod).eqlZero();
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 },1868 },
1872 .opt => |opt| opt.val == .none,
1873 else => false,1869 else => false,
1874 },1870 },
1875 };1871 .opt => |opt| opt.val == .none,
1876 }1872 else => false,
18771873 },
1878 /// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.1874 };
1879 pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTerminatedString {1875}
1880 return switch (mod.intern_pool.indexToKey(val.toIntern())) {1876
1881 .err => |err| err.name.toOptional(),1877/// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.
1882 .error_union => |error_union| switch (error_union.val) {1878pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTerminatedString {
1883 .err_name => |err_name| err_name.toOptional(),1879 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1884 .payload => .none,1880 .err => |err| err.name.toOptional(),
1885 },1881 .error_union => |error_union| switch (error_union.val) {
1886 else => unreachable,1882 .err_name => |err_name| err_name.toOptional(),
1887 };1883 .payload => .none,
1888 }1884 },
18891885 else => unreachable,
1890 pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt {1886 };
1891 return if (getErrorName(val, mod).unwrap()) |err_name|1887}
1892 @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?))1888
1893 else1889pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt {
1894 0;1890 return if (getErrorName(val, mod).unwrap()) |err_name|
1895 }1891 @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?))
18961892 else
1897 /// Assumes the type is an error union. Returns true if and only if the value is1893 0;
1898 /// the error union payload, not an error.1894}
1899 pub fn errorUnionIsPayload(val: Value, mod: *const Module) bool {1895
1900 return mod.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;1896/// Assumes the type is an error union. Returns true if and only if the value is
1901 }1897/// the error union payload, not an error.
19021898pub fn errorUnionIsPayload(val: Value, mod: *const Module) bool {
1903 /// Value of the optional, null if optional has no payload.1899 return mod.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;
1904 pub fn optionalValue(val: Value, mod: *const Module) ?Value {1900}
1905 return switch (mod.intern_pool.indexToKey(val.toIntern())) {1901
1906 .opt => |opt| switch (opt.val) {1902/// Value of the optional, null if optional has no payload.
1907 .none => null,1903pub fn optionalValue(val: Value, mod: *const Module) ?Value {
1908 else => |payload| Value.fromInterned(payload),1904 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1909 },1905 .opt => |opt| switch (opt.val) {
1910 .ptr => val,1906 .none => null,
1911 else => unreachable,1907 else => |payload| Value.fromInterned(payload),
1912 };1908 },
1913 }1909 .ptr => val,
19141910 else => unreachable,
1915 /// Valid for all types. Asserts the value is not undefined.1911 };
1916 pub fn isFloat(self: Value, mod: *const Module) bool {1912}
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 }
22881913
2289 /// Supports both floats and ints; handles undefined.1914/// Valid for all types. Asserts the value is not undefined.
2290 pub fn numberMin(lhs: Value, rhs: Value, mod: *Module) Value {1915pub fn isFloat(self: Value, mod: *const Module) bool {
2291 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;1916 return switch (self.toIntern()) {
2292 if (lhs.isNan(mod)) return rhs;1917 .undef => unreachable,
2293 if (rhs.isNan(mod)) return lhs;1918 else => switch (mod.intern_pool.indexToKey(self.toIntern())) {
1919 .undef => unreachable,
1920 .float => true,
1921 else => false,
1922 },
1923 };
1924}
22941925
2295 return switch (order(lhs, rhs, mod)) {1926pub fn floatFromInt(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module) !Value {
2296 .lt => lhs,1927 return floatFromIntAdvanced(val, arena, int_ty, float_ty, mod, null) catch |err| switch (err) {
2297 .gt, .eq => rhs,1928 error.OutOfMemory => return error.OutOfMemory,
2298 };1929 else => unreachable,
2299 }1930 };
1931}
23001932
2301 /// operands must be (vectors of) integers; handles undefined scalars.1933pub fn floatFromIntAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
2302 pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {1934 if (int_ty.zigTypeTag(mod) == .Vector) {
2303 if (ty.zigTypeTag(mod) == .Vector) {1935 const result_data = try arena.alloc(InternPool.Index, int_ty.vectorLen(mod));
2304 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));1936 const scalar_ty = float_ty.scalarType(mod);
2305 const scalar_ty = ty.scalarType(mod);1937 for (result_data, 0..) |*scalar, i| {
2306 for (result_data, 0..) |*scalar, i| {1938 const elem_val = try val.elemValue(mod, i);
2307 const elem_val = try val.elemValue(mod, i);1939 scalar.* = try (try floatFromIntScalar(elem_val, scalar_ty, mod, opt_sema)).intern(scalar_ty, mod);
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 }1940 }
2315 return bitwiseNotScalar(val, ty, arena, mod);1941 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
1942 .ty = float_ty.toIntern(),
1943 .storage = .{ .elems = result_data },
1944 } })));
2316 }1945 }
1946 return floatFromIntScalar(val, float_ty, mod, opt_sema);
1947}
23171948
2318 /// operands must be integers; handles undefined.1949pub fn floatFromIntScalar(val: Value, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value {
2319 pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {1950 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
2320 if (val.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));1951 .undef => try mod.undefValue(float_ty),
2321 if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());1952 .int => |int| switch (int.storage) {
23221953 .big_int => |big_int| {
2323 const info = ty.intInfo(mod);1954 const float = bigIntToFloat(big_int.limbs, big_int.positive);
23241955 return mod.floatValue(float_ty, float);
2325 if (info.bits == 0) {1956 },
2326 return val;1957 inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, mod),
2327 }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}
23281988
2329 // TODO is this a performance issue? maybe we should try the operation without1989fn calcLimbLenFloat(scalar: anytype) usize {
2330 // resorting to BigInt first.1990 if (scalar == 0) {
2331 var val_space: Value.BigIntSpace = undefined;1991 return 1;
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 }1992 }
23991993
2400 /// operands must be integers; handles undefined.1994 const w_value = @abs(scalar);
2401 pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {1995 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).Int.bits) + 1;
2402 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));1996}
2403 if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
24041997
2405 const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);1998pub const OverflowArithmeticResult = struct {
2406 const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);1999 overflow_bit: Value,
2407 return bitwiseXor(anded, all_ones, ty, arena, mod);2000 wrapped_result: Value,
2408 }2001};
24092002
2410 /// operands must be (vectors of) integers; handles undefined scalars.2003/// Supports (vectors of) integers only; asserts neither operand is undefined.
2411 pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2004pub fn intAddSat(
2412 if (ty.zigTypeTag(mod) == .Vector) {2005 lhs: Value,
2413 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2006 rhs: Value,
2414 const scalar_ty = ty.scalarType(mod);2007 ty: Type,
2415 for (result_data, 0..) |*scalar, i| {2008 arena: Allocator,
2416 const lhs_elem = try lhs.elemValue(mod, i);2009 mod: *Module,
2417 const rhs_elem = try rhs.elemValue(mod, i);2010) !Value {
2418 scalar.* = try (try bitwiseOrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);2011 if (ty.zigTypeTag(mod) == .Vector) {
2419 }2012 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2420 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2013 const scalar_ty = ty.scalarType(mod);
2421 .ty = ty.toIntern(),2014 for (result_data, 0..) |*scalar, i| {
2422 .storage = .{ .elems = result_data },2015 const lhs_elem = try lhs.elemValue(mod, i);
2423 } })));2016 const rhs_elem = try rhs.elemValue(mod, i);
2424 }2017 scalar.* = try (try intAddSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2425 return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);2018 }
2426 }2019 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
24272020 .ty = ty.toIntern(),
2428 /// operands must be integers; handles undefined.2021 .storage = .{ .elems = result_data },
2429 pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {2022 } })));
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 }2023 }
25072024 return intAddSatScalar(lhs, rhs, ty, arena, mod);
2508 fn intDivInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {2025}
2509 if (ty.zigTypeTag(mod) == .Vector) {2026
2510 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2027/// Supports integers only; asserts neither operand is undefined.
2511 const scalar_ty = ty.scalarType(mod);2028pub fn intAddSatScalar(
2512 for (result_data, 0..) |*scalar, i| {2029 lhs: Value,
2513 const lhs_elem = try lhs.elemValue(mod, i);2030 rhs: Value,
2514 const rhs_elem = try rhs.elemValue(mod, i);2031 ty: Type,
2515 const val = intDivScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {2032 arena: Allocator,
2516 error.Overflow => {2033 mod: *Module,
2517 overflow_idx.* = i;2034) !Value {
2518 return error.Overflow;2035 assert(!lhs.isUndef(mod));
2519 },2036 assert(!rhs.isUndef(mod));
2520 else => |e| return e,2037
2521 };2038 const info = ty.intInfo(mod);
2522 scalar.* = try val.intern(scalar_ty, mod);2039
2523 }2040 var lhs_space: Value.BigIntSpace = undefined;
2524 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2041 var rhs_space: Value.BigIntSpace = undefined;
2525 .ty = ty.toIntern(),2042 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2526 .storage = .{ .elems = result_data },2043 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2527 } })));2044 const limbs = try arena.alloc(
2528 }2045 std.math.big.Limb,
2529 return intDivScalar(lhs, rhs, ty, allocator, mod);2046 std.math.big.int.calcTwosCompLimbCount(info.bits),
2530 }2047 );
25312048 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2532 pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2049 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2533 // TODO is this a performance issue? maybe we should try the operation without2050 return mod.intValue_big(ty, result_bigint.toConst());
2534 // resorting to BigInt first.2051}
2535 var lhs_space: Value.BigIntSpace = undefined;2052
2536 var rhs_space: Value.BigIntSpace = undefined;2053/// Supports (vectors of) integers only; asserts neither operand is undefined.
2537 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);2054pub fn intSubSat(
2538 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);2055 lhs: Value,
2539 const limbs_q = try allocator.alloc(2056 rhs: Value,
2540 std.math.big.Limb,2057 ty: Type,
2541 lhs_bigint.limbs.len,2058 arena: Allocator,
2542 );2059 mod: *Module,
2543 const limbs_r = try allocator.alloc(2060) !Value {
2544 std.math.big.Limb,2061 if (ty.zigTypeTag(mod) == .Vector) {
2545 rhs_bigint.limbs.len,2062 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
2546 );2063 const scalar_ty = ty.scalarType(mod);
2547 const limbs_buffer = try allocator.alloc(2064 for (result_data, 0..) |*scalar, i| {
2548 std.math.big.Limb,2065 const lhs_elem = try lhs.elemValue(mod, i);
2549 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),2066 const rhs_elem = try rhs.elemValue(mod, i);
2550 );2067 scalar.* = try (try intSubSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);
2551 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2068 }
2552 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2069 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2553 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);2070 .ty = ty.toIntern(),
2554 if (ty.toIntern() != .comptime_int_type) {2071 .storage = .{ .elems = result_data },
2555 const info = ty.intInfo(mod);2072 } })));
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 }2073 }
25622074 return intSubSatScalar(lhs, rhs, ty, arena, mod);
2563 pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2075}
2564 if (ty.zigTypeTag(mod) == .Vector) {2076
2565 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2077/// Supports integers only; asserts neither operand is undefined.
2566 const scalar_ty = ty.scalarType(mod);2078pub fn intSubSatScalar(
2567 for (result_data, 0..) |*scalar, i| {2079 lhs: Value,
2568 const lhs_elem = try lhs.elemValue(mod, i);2080 rhs: Value,
2569 const rhs_elem = try rhs.elemValue(mod, i);2081 ty: Type,
2570 scalar.* = try (try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);2082 arena: Allocator,
2571 }2083 mod: *Module,
2572 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2084) !Value {
2573 .ty = ty.toIntern(),2085 assert(!lhs.isUndef(mod));
2574 .storage = .{ .elems = result_data },2086 assert(!rhs.isUndef(mod));
2575 } })));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);
2576 }2121 }
2577 return intDivFloorScalar(lhs, rhs, ty, allocator, mod);2122 return OverflowArithmeticResult{
2578 }2123 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
25792124 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
2580 pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2125 .storage = .{ .elems = overflowed_data },
2581 // TODO is this a performance issue? maybe we should try the operation without2126 } }))),
2582 // resorting to BigInt first.2127 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),2128 .ty = ty.toIntern(),
2616 .storage = .{ .elems = result_data },2129 .storage = .{ .elems = result_data },
2617 } })));2130 } }))),
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 };2131 };
2656 }2132 }
26572133 return intMulWithOverflowScalar(lhs, rhs, ty, arena, mod);
2658 /// Returns true if the value is a floating point type and is infinite. Returns false otherwise.2134}
2659 pub fn isInf(val: Value, mod: *const Module) bool {2135
2660 if (val.ip_index == .none) return false;2136pub fn intMulWithOverflowScalar(
2661 return switch (mod.intern_pool.indexToKey(val.toIntern())) {2137 lhs: Value,
2662 .float => |float| switch (float.storage) {2138 rhs: Value,
2663 inline else => |x| std.math.isInf(x),2139 ty: Type,
2664 },2140 arena: Allocator,
2665 else => false,2141 mod: *Module,
2666 };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 } })));
2667 }2191 }
26682192 return numberMulWrapScalar(lhs, rhs, ty, arena, mod);
2669 pub fn isNegativeInf(val: Value, mod: *const Module) bool {2193}
2670 if (val.ip_index == .none) return false;2194
2671 return switch (mod.intern_pool.indexToKey(val.toIntern())) {2195/// Supports both floats and ints; handles undefined.
2672 .float => |float| switch (float.storage) {2196pub fn numberMulWrapScalar(
2673 inline else => |x| std.math.isNegativeInf(x),2197 lhs: Value,
2674 },2198 rhs: Value,
2675 else => false,2199 ty: Type,
2676 };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 } })));
2677 }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}
26782287
2679 pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {2288/// Supports both floats and ints; handles undefined.
2680 if (float_type.zigTypeTag(mod) == .Vector) {2289pub fn numberMin(lhs: Value, rhs: Value, mod: *Module) Value {
2681 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));2290 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return undef;
2682 const scalar_ty = float_type.scalarType(mod);2291 if (lhs.isNan(mod)) return rhs;
2683 for (result_data, 0..) |*scalar, i| {2292 if (rhs.isNan(mod)) return lhs;
2684 const lhs_elem = try lhs.elemValue(mod, i);2293
2685 const rhs_elem = try rhs.elemValue(mod, i);2294 return switch (order(lhs, rhs, mod)) {
2686 scalar.* = try (try floatRemScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);2295 .lt => lhs,
2687 }2296 .gt, .eq => rhs,
2688 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2297 };
2689 .ty = float_type.toIntern(),2298}
2690 .storage = .{ .elems = result_data },2299
2691 } })));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);
2692 }2308 }
2693 return floatRemScalar(lhs, rhs, float_type, mod);2309 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2310 .ty = ty.toIntern(),
2311 .storage = .{ .elems = result_data },
2312 } })));
2694 }2313 }
26952314 return bitwiseNotScalar(val, ty, arena, mod);
2696 pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {2315}
2697 const target = mod.getTarget();2316
2698 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {2317/// operands must be integers; handles undefined.
2699 16 => .{ .f16 = @rem(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },2318pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2700 32 => .{ .f32 = @rem(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },2319 if (val.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2701 64 => .{ .f64 = @rem(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },2320 if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());
2702 80 => .{ .f80 = @rem(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },2321
2703 128 => .{ .f128 = @rem(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },2322 const info = ty.intInfo(mod);
2704 else => unreachable,2323
2705 };2324 if (info.bits == 0) {
2706 return Value.fromInterned((try mod.intern(.{ .float = .{2325 return val;
2707 .ty = float_type.toIntern(),2326 }
2708 .storage = storage,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 },
2709 } })));2355 } })));
2710 }2356 }
27112357 return bitwiseAndScalar(lhs, rhs, ty, allocator, mod);
2712 pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {2358}
2713 if (float_type.zigTypeTag(mod) == .Vector) {2359
2714 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));2360/// operands must be integers; handles undefined.
2715 const scalar_ty = float_type.scalarType(mod);2361pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2716 for (result_data, 0..) |*scalar, i| {2362 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2717 const lhs_elem = try lhs.elemValue(mod, i);2363 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
2718 const rhs_elem = try rhs.elemValue(mod, i);2364
2719 scalar.* = try (try floatModScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);2365 // TODO is this a performance issue? maybe we should try the operation without
2720 }2366 // resorting to BigInt first.
2721 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2367 var lhs_space: Value.BigIntSpace = undefined;
2722 .ty = float_type.toIntern(),2368 var rhs_space: Value.BigIntSpace = undefined;
2723 .storage = .{ .elems = result_data },2369 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2724 } })));2370 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2725 }2371 const limbs = try arena.alloc(
2726 return floatModScalar(lhs, rhs, float_type, mod);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 } })));
2727 }2395 }
27282396 return bitwiseNandScalar(lhs, rhs, ty, arena, mod);
2729 pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, mod: *Module) !Value {2397}
2730 const target = mod.getTarget();2398
2731 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {2399/// operands must be integers; handles undefined.
2732 16 => .{ .f16 = @mod(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },2400pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2733 32 => .{ .f32 = @mod(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },2401 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2734 64 => .{ .f64 = @mod(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },2402 if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
2735 80 => .{ .f80 = @mod(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },2403
2736 128 => .{ .f128 = @mod(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },2404 const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
2737 else => unreachable,2405 const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);
2738 };2406 return bitwiseXor(anded, all_ones, ty, arena, mod);
2739 return Value.fromInterned((try mod.intern(.{ .float = .{2407}
2740 .ty = float_type.toIntern(),2408
2741 .storage = storage,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 },
2742 } })));2422 } })));
2743 }2423 }
27442424 return bitwiseOrScalar(lhs, rhs, ty, allocator, mod);
2745 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting2425}
2746 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar).2426
2747 pub fn intMul(lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize, allocator: Allocator, mod: *Module) !Value {2427/// operands must be integers; handles undefined.
2748 var overflow: usize = undefined;2428pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2749 return intMulInner(lhs, rhs, ty, &overflow, allocator, mod) catch |err| switch (err) {2429 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2750 error.Overflow => {2430 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
2751 const is_vec = ty.isVector(mod);2431
2752 overflow_idx.* = if (is_vec) overflow else 0;2432 // TODO is this a performance issue? maybe we should try the operation without
2753 const safe_ty = if (is_vec) try mod.vectorType(.{2433 // resorting to BigInt first.
2754 .len = ty.vectorLen(mod),2434 var lhs_space: Value.BigIntSpace = undefined;
2755 .child = .comptime_int_type,2435 var rhs_space: Value.BigIntSpace = undefined;
2756 }) else Type.comptime_int;2436 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2757 return intMulInner(lhs, rhs, safe_ty, undefined, allocator, mod) catch |err1| switch (err1) {2437 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2758 error.Overflow => unreachable,2438 const limbs = try arena.alloc(
2759 else => |e| return e,2439 std.math.big.Limb,
2760 };2440 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2761 },2441 );
2762 else => |e| return e,2442 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2763 };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 } })));
2764 }2461 }
27652462 return bitwiseXorScalar(lhs, rhs, ty, allocator, mod);
2766 fn intMulInner(lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize, allocator: Allocator, mod: *Module) !Value {2463}
2767 if (ty.zigTypeTag(mod) == .Vector) {2464
2768 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2465/// operands must be integers; handles undefined.
2769 const scalar_ty = ty.scalarType(mod);2466pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
2770 for (result_data, 0..) |*scalar, i| {2467 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
2771 const lhs_elem = try lhs.elemValue(mod, i);2468 if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() != rhs.toBool());
2772 const rhs_elem = try rhs.elemValue(mod, i);2469
2773 const val = intMulScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod) catch |err| switch (err) {2470 // TODO is this a performance issue? maybe we should try the operation without
2774 error.Overflow => {2471 // resorting to BigInt first.
2775 overflow_idx.* = i;2472 var lhs_space: Value.BigIntSpace = undefined;
2776 return error.Overflow;2473 var rhs_space: Value.BigIntSpace = undefined;
2777 },2474 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2778 else => |e| return e,2475 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2779 };2476 const limbs = try arena.alloc(
2780 scalar.* = try val.intern(scalar_ty, mod);2477 std.math.big.Limb,
2781 }2478 // + 1 for negatives
2782 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2479 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
2783 .ty = ty.toIntern(),2480 );
2784 .storage = .{ .elems = result_data },2481 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2785 } })));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);
2786 }2522 }
2787 return intMulScalar(lhs, rhs, ty, allocator, mod);2523 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2524 .ty = ty.toIntern(),
2525 .storage = .{ .elems = result_data },
2526 } })));
2788 }2527 }
27892528 return intDivScalar(lhs, rhs, ty, allocator, mod);
2790 pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2529}
2791 if (ty.toIntern() != .comptime_int_type) {2530
2792 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);2531pub fn intDivScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2793 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;2532 // TODO is this a performance issue? maybe we should try the operation without
2794 return res.wrapped_result;2533 // resorting to BigInt first.
2795 }2534 var lhs_space: Value.BigIntSpace = undefined;
2796 // TODO is this a performance issue? maybe we should try the operation without2535 var rhs_space: Value.BigIntSpace = undefined;
2797 // resorting to BigInt first.2536 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2798 var lhs_space: Value.BigIntSpace = undefined;2537 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2799 var rhs_space: Value.BigIntSpace = undefined;2538 const limbs_q = try allocator.alloc(
2800 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);2539 std.math.big.Limb,
2801 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);2540 lhs_bigint.limbs.len,
2802 const limbs = try allocator.alloc(2541 );
2803 std.math.big.Limb,2542 const limbs_r = try allocator.alloc(
2804 lhs_bigint.limbs.len + rhs_bigint.limbs.len,2543 std.math.big.Limb,
2805 );2544 rhs_bigint.limbs.len,
2806 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2545 );
2807 const limbs_buffer = try allocator.alloc(2546 const limbs_buffer = try allocator.alloc(
2808 std.math.big.Limb,2547 std.math.big.Limb,
2809 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),2548 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2810 );2549 );
2811 defer allocator.free(limbs_buffer);2550 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2812 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);2551 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2813 return mod.intValue_big(ty, result_bigint.toConst());2552 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2814 }2553 if (ty.toIntern() != .comptime_int_type) {
28152554 const info = ty.intInfo(mod);
2816 pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, mod: *Module) !Value {2555 if (!result_q.toConst().fitsInTwosComp(info.signedness, info.bits)) {
2817 if (ty.zigTypeTag(mod) == .Vector) {2556 return error.Overflow;
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 }2557 }
2558 }
2559 return mod.intValue_big(ty, result_q.toConst());
2560}
29172561
2918 return mod.intValue_big(ty, result_bigint.toConst());2562pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2919 }2563 if (ty.zigTypeTag(mod) == .Vector) {
29202564 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
2921 pub fn shlWithOverflow(2565 const scalar_ty = ty.scalarType(mod);
2922 lhs: Value,2566 for (result_data, 0..) |*scalar, i| {
2923 rhs: Value,2567 const lhs_elem = try lhs.elemValue(mod, i);
2924 ty: Type,2568 const rhs_elem = try rhs.elemValue(mod, i);
2925 allocator: Allocator,2569 scalar.* = try (try intDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);
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 }2570 }
2951 return shlWithOverflowScalar(lhs, rhs, ty, allocator, mod);2571 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2572 .ty = ty.toIntern(),
2573 .storage = .{ .elems = result_data },
2574 } })));
2952 }2575 }
29532576 return intDivFloorScalar(lhs, rhs, ty, allocator, mod);
2954 pub fn shlWithOverflowScalar(2577}
2955 lhs: Value,2578
2956 rhs: Value,2579pub fn intDivFloorScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2957 ty: Type,2580 // TODO is this a performance issue? maybe we should try the operation without
2958 allocator: Allocator,2581 // resorting to BigInt first.
2959 mod: *Module,2582 var lhs_space: Value.BigIntSpace = undefined;
2960 ) !OverflowArithmeticResult {2583 var rhs_space: Value.BigIntSpace = undefined;
2961 const info = ty.intInfo(mod);2584 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2962 var lhs_space: Value.BigIntSpace = undefined;2585 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2963 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);2586 const limbs_q = try allocator.alloc(
2964 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));2587 std.math.big.Limb,
2965 const limbs = try allocator.alloc(2588 lhs_bigint.limbs.len,
2966 std.math.big.Limb,2589 );
2967 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,2590 const limbs_r = try allocator.alloc(
2968 );2591 std.math.big.Limb,
2969 var result_bigint = BigIntMutable{2592 rhs_bigint.limbs.len,
2970 .limbs = limbs,2593 );
2971 .positive = undefined,2594 const limbs_buffer = try allocator.alloc(
2972 .len = undefined,2595 std.math.big.Limb,
2973 };2596 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2974 result_bigint.shiftLeft(lhs_bigint, shift);2597 );
2975 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);2598 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2976 if (overflowed) {2599 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2977 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);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);
2978 }2780 }
2979 return OverflowArithmeticResult{2781 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2980 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)),2782 .ty = ty.toIntern(),
2981 .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),2783 .storage = .{ .elems = result_data },
2982 };2784 } })));
2983 }2785 }
29842786 return intMulScalar(lhs, rhs, ty, allocator, mod);
2985 pub fn shlSat(2787}
2986 lhs: Value,2788
2987 rhs: Value,2789pub fn intMulScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {
2988 ty: Type,2790 if (ty.toIntern() != .comptime_int_type) {
2989 arena: Allocator,2791 const res = try intMulWithOverflowScalar(lhs, rhs, ty, allocator, mod);
2990 mod: *Module,2792 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow;
2991 ) !Value {2793 return res.wrapped_result;
2992 if (ty.zigTypeTag(mod) == .Vector) {2794 }
2993 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));2795 // TODO is this a performance issue? maybe we should try the operation without
2994 const scalar_ty = ty.scalarType(mod);2796 // resorting to BigInt first.
2995 for (result_data, 0..) |*scalar, i| {2797 var lhs_space: Value.BigIntSpace = undefined;
2996 const lhs_elem = try lhs.elemValue(mod, i);2798 var rhs_space: Value.BigIntSpace = undefined;
2997 const rhs_elem = try rhs.elemValue(mod, i);2799 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
2998 scalar.* = try (try shlSatScalar(lhs_elem, rhs_elem, scalar_ty, arena, mod)).intern(scalar_ty, mod);2800 const rhs_bigint = rhs.toBigInt(&rhs_space, mod);
2999 }2801 const limbs = try allocator.alloc(
3000 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2802 std.math.big.Limb,
3001 .ty = ty.toIntern(),2803 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
3002 .storage = .{ .elems = result_data },2804 );
3003 } })));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);
3004 }2822 }
3005 return shlSatScalar(lhs, rhs, ty, arena, mod);2823 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3006 }2824 .ty = ty.toIntern(),
30072825 .storage = .{ .elems = result_data },
3008 pub fn shlSatScalar(2826 } })));
3009 lhs: Value,2827 }
3010 rhs: Value,2828 return intTruncScalar(val, ty, allocator, signedness, bits, mod);
3011 ty: Type,2829}
3012 arena: Allocator,2830
3013 mod: *Module,2831/// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`.
3014 ) !Value {2832pub fn intTruncBitsAsValue(
3015 // TODO is this a performance issue? maybe we should try the operation without2833 val: Value,
3016 // resorting to BigInt first.2834 ty: Type,
3017 const info = ty.intInfo(mod);2835 allocator: Allocator,
30182836 signedness: std.builtin.Signedness,
3019 var lhs_space: Value.BigIntSpace = undefined;2837 bits: Value,
3020 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);2838 mod: *Module,
3021 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));2839) !Value {
3022 const limbs = try arena.alloc(2840 if (ty.zigTypeTag(mod) == .Vector) {
3023 std.math.big.Limb,2841 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));
3024 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,2842 const scalar_ty = ty.scalarType(mod);
3025 );2843 for (result_data, 0..) |*scalar, i| {
3026 var result_bigint = BigIntMutable{2844 const elem_val = try val.elemValue(mod, i);
3027 .limbs = limbs,2845 const bits_elem = try bits.elemValue(mod, i);
3028 .positive = undefined,2846 scalar.* = try (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @as(u16, @intCast(bits_elem.toUnsignedInt(mod))), mod)).intern(scalar_ty, mod);
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 }2847 }
3055 return shlTruncScalar(lhs, rhs, ty, arena, mod);2848 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
2849 .ty = ty.toIntern(),
2850 .storage = .{ .elems = result_data },
2851 } })));
3056 }2852 }
30572853 return intTruncScalar(val, ty, allocator, signedness, @as(u16, @intCast(bits.toUnsignedInt(mod))), mod);
3058 pub fn shlTruncScalar(2854}
3059 lhs: Value,2855
3060 rhs: Value,2856pub fn intTruncScalar(
3061 ty: Type,2857 val: Value,
3062 arena: Allocator,2858 ty: Type,
3063 mod: *Module,2859 allocator: Allocator,
3064 ) !Value {2860 signedness: std.builtin.Signedness,
3065 const shifted = try lhs.shl(rhs, ty, arena, mod);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) {
3066 const int_info = ty.intInfo(mod);2913 const int_info = ty.intInfo(mod);
3067 const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits, mod);2914 result_bigint.truncate(result_bigint.toConst(), int_info.signedness, int_info.bits);
3068 return truncated;2915 }
3069 }2916
30702917 return mod.intValue_big(ty, result_bigint.toConst());
3071 pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2918}
3072 if (ty.zigTypeTag(mod) == .Vector) {2919
3073 const result_data = try allocator.alloc(InternPool.Index, ty.vectorLen(mod));2920pub fn shlWithOverflow(
3074 const scalar_ty = ty.scalarType(mod);2921 lhs: Value,
3075 for (result_data, 0..) |*scalar, i| {2922 rhs: Value,
3076 const lhs_elem = try lhs.elemValue(mod, i);2923 ty: Type,
3077 const rhs_elem = try rhs.elemValue(mod, i);2924 allocator: Allocator,
3078 scalar.* = try (try shrScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod)).intern(scalar_ty, mod);2925 mod: *Module,
3079 }2926) !OverflowArithmeticResult {
3080 return Value.fromInterned((try mod.intern(.{ .aggregate = .{2927 if (ty.zigTypeTag(mod) == .Vector) {
3081 .ty = ty.toIntern(),2928 const vec_len = ty.vectorLen(mod);
3082 .storage = .{ .elems = result_data },2929 const overflowed_data = try allocator.alloc(InternPool.Index, vec_len);
3083 } })));2930 const result_data = try allocator.alloc(InternPool.Index, vec_len);
3084 }2931 const scalar_ty = ty.scalarType(mod);
3085 return shrScalar(lhs, rhs, ty, allocator, mod);2932 for (overflowed_data, result_data, 0..) |*of, *scalar, i| {
3086 }2933 const lhs_elem = try lhs.elemValue(mod, i);
30872934 const rhs_elem = try rhs.elemValue(mod, i);
3088 pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *Module) !Value {2935 const of_math_result = try shlWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty, allocator, mod);
3089 // TODO is this a performance issue? maybe we should try the operation without2936 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
3090 // resorting to BigInt first.2937 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
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 }2938 }
31052939 return OverflowArithmeticResult{
3106 const limbs = try allocator.alloc(2940 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{
3107 std.math.big.Limb,2941 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
3108 result_limbs,2942 .storage = .{ .elems = overflowed_data },
3109 );2943 } }))),
3110 var result_bigint = BigIntMutable{2944 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{
3111 .limbs = limbs,2945 .ty = ty.toIntern(),
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 },2946 .storage = .{ .elems = result_data },
3135 } })));2947 } }))),
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 };2948 };
3154 return Value.fromInterned((try mod.intern(.{ .float = .{2949 }
3155 .ty = float_type.toIntern(),2950 return shlWithOverflowScalar(lhs, rhs, ty, allocator, mod);
3156 .storage = storage,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 },
3157 } })));3002 } })));
3158 }3003 }
31593004 return shlSatScalar(lhs, rhs, ty, arena, mod);
3160 pub fn floatAdd(3005}
3161 lhs: Value,3006
3162 rhs: Value,3007pub fn shlSatScalar(
3163 float_type: Type,3008 lhs: Value,
3164 arena: Allocator,3009 rhs: Value,
3165 mod: *Module,3010 ty: Type,
3166 ) !Value {3011 arena: Allocator,
3167 if (float_type.zigTypeTag(mod) == .Vector) {3012 mod: *Module,
3168 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3013) !Value {
3169 const scalar_ty = float_type.scalarType(mod);3014 // TODO is this a performance issue? maybe we should try the operation without
3170 for (result_data, 0..) |*scalar, i| {3015 // resorting to BigInt first.
3171 const lhs_elem = try lhs.elemValue(mod, i);3016 const info = ty.intInfo(mod);
3172 const rhs_elem = try rhs.elemValue(mod, i);3017
3173 scalar.* = try (try floatAddScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);3018 var lhs_space: Value.BigIntSpace = undefined;
3174 }3019 const lhs_bigint = lhs.toBigInt(&lhs_space, mod);
3175 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3020 const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod)));
3176 .ty = float_type.toIntern(),3021 const limbs = try arena.alloc(
3177 .storage = .{ .elems = result_data },3022 std.math.big.Limb,
3178 } })));3023 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,
3179 }3024 );
3180 return floatAddScalar(lhs, rhs, float_type, mod);3025 var result_bigint = BigIntMutable{
3181 }3026 .limbs = limbs,
31823027 .positive = undefined,
3183 pub fn floatAddScalar(3028 .len = undefined,
3184 lhs: Value,3029 };
3185 rhs: Value,3030 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
3186 float_type: Type,3031 return mod.intValue_big(ty, result_bigint.toConst());
3187 mod: *Module,3032}
3188 ) !Value {3033
3189 const target = mod.getTarget();3034pub fn shlTrunc(
3190 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3035 lhs: Value,
3191 16 => .{ .f16 = lhs.toFloat(f16, mod) + rhs.toFloat(f16, mod) },3036 rhs: Value,
3192 32 => .{ .f32 = lhs.toFloat(f32, mod) + rhs.toFloat(f32, mod) },3037 ty: Type,
3193 64 => .{ .f64 = lhs.toFloat(f64, mod) + rhs.toFloat(f64, mod) },3038 arena: Allocator,
3194 80 => .{ .f80 = lhs.toFloat(f80, mod) + rhs.toFloat(f80, mod) },3039 mod: *Module,
3195 128 => .{ .f128 = lhs.toFloat(f128, mod) + rhs.toFloat(f128, mod) },3040) !Value {
3196 else => unreachable,3041 if (ty.zigTypeTag(mod) == .Vector) {
3197 };3042 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3198 return Value.fromInterned((try mod.intern(.{ .float = .{3043 const scalar_ty = ty.scalarType(mod);
3199 .ty = float_type.toIntern(),3044 for (result_data, 0..) |*scalar, i| {
3200 .storage = storage,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 },
3201 } })));3082 } })));
3202 }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 }
32033104
3204 pub fn floatSub(3105 const limbs = try allocator.alloc(
3205 lhs: Value,3106 std.math.big.Limb,
3206 rhs: Value,3107 result_limbs,
3207 float_type: Type,3108 );
3208 arena: Allocator,3109 var result_bigint = BigIntMutable{
3209 mod: *Module,3110 .limbs = limbs,
3210 ) !Value {3111 .positive = undefined,
3211 if (float_type.zigTypeTag(mod) == .Vector) {3112 .len = undefined,
3212 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3113 };
3213 const scalar_ty = float_type.scalarType(mod);3114 result_bigint.shiftRight(lhs_bigint, shift);
3214 for (result_data, 0..) |*scalar, i| {3115 return mod.intValue_big(ty, result_bigint.toConst());
3215 const lhs_elem = try lhs.elemValue(mod, i);3116}
3216 const rhs_elem = try rhs.elemValue(mod, i);3117
3217 scalar.* = try (try floatSubScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);3118pub fn floatNeg(
3218 }3119 val: Value,
3219 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3120 float_type: Type,
3220 .ty = float_type.toIntern(),3121 arena: Allocator,
3221 .storage = .{ .elems = result_data },3122 mod: *Module,
3222 } })));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);
3223 }3130 }
3224 return floatSubScalar(lhs, rhs, float_type, mod);3131 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3132 .ty = float_type.toIntern(),
3244 .storage = storage,3133 .storage = .{ .elems = result_data },
3245 } })));3134 } })));
3246 }3135 }
32473136 return floatNegScalar(val, float_type, mod);
3248 pub fn floatDiv(3137}
3249 lhs: Value,3138
3250 rhs: Value,3139pub fn floatNegScalar(
3251 float_type: Type,3140 val: Value,
3252 arena: Allocator,3141 float_type: Type,
3253 mod: *Module,3142 mod: *Module,
3254 ) !Value {3143) !Value {
3255 if (float_type.zigTypeTag(mod) == .Vector) {3144 const target = mod.getTarget();
3256 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3145 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3257 const scalar_ty = float_type.scalarType(mod);3146 16 => .{ .f16 = -val.toFloat(f16, mod) },
3258 for (result_data, 0..) |*scalar, i| {3147 32 => .{ .f32 = -val.toFloat(f32, mod) },
3259 const lhs_elem = try lhs.elemValue(mod, i);3148 64 => .{ .f64 = -val.toFloat(f64, mod) },
3260 const rhs_elem = try rhs.elemValue(mod, i);3149 80 => .{ .f80 = -val.toFloat(f80, mod) },
3261 scalar.* = try (try floatDivScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);3150 128 => .{ .f128 = -val.toFloat(f128, mod) },
3262 }3151 else => unreachable,
3263 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3152 };
3264 .ty = float_type.toIntern(),3153 return Value.fromInterned((try mod.intern(.{ .float = .{
3265 .storage = .{ .elems = result_data },3154 .ty = float_type.toIntern(),
3266 } })));3155 .storage = storage,
3267 }3156 } })));
3268 return floatDivScalar(lhs, rhs, float_type, mod);3157}
3269 }3158
32703159pub fn floatAdd(
3271 pub fn floatDivScalar(3160 lhs: Value,
3272 lhs: Value,3161 rhs: Value,
3273 rhs: Value,3162 float_type: Type,
3274 float_type: Type,3163 arena: Allocator,
3275 mod: *Module,3164 mod: *Module,
3276 ) !Value {3165) !Value {
3277 const target = mod.getTarget();3166 if (float_type.zigTypeTag(mod) == .Vector) {
3278 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3167 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3279 16 => .{ .f16 = lhs.toFloat(f16, mod) / rhs.toFloat(f16, mod) },3168 const scalar_ty = float_type.scalarType(mod);
3280 32 => .{ .f32 = lhs.toFloat(f32, mod) / rhs.toFloat(f32, mod) },3169 for (result_data, 0..) |*scalar, i| {
3281 64 => .{ .f64 = lhs.toFloat(f64, mod) / rhs.toFloat(f64, mod) },3170 const lhs_elem = try lhs.elemValue(mod, i);
3282 80 => .{ .f80 = lhs.toFloat(f80, mod) / rhs.toFloat(f80, mod) },3171 const rhs_elem = try rhs.elemValue(mod, i);
3283 128 => .{ .f128 = lhs.toFloat(f128, mod) / rhs.toFloat(f128, mod) },3172 scalar.* = try (try floatAddScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3284 else => unreachable,3173 }
3285 };3174 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3286 return Value.fromInterned((try mod.intern(.{ .float = .{
3287 .ty = float_type.toIntern(),3175 .ty = float_type.toIntern(),
3288 .storage = storage,3176 .storage = .{ .elems = result_data },
3289 } })));3177 } })));
3290 }3178 }
32913179 return floatAddScalar(lhs, rhs, float_type, mod);
3292 pub fn floatDivFloor(3180}
3293 lhs: Value,3181
3294 rhs: Value,3182pub fn floatAddScalar(
3295 float_type: Type,3183 lhs: Value,
3296 arena: Allocator,3184 rhs: Value,
3297 mod: *Module,3185 float_type: Type,
3298 ) !Value {3186 mod: *Module,
3299 if (float_type.zigTypeTag(mod) == .Vector) {3187) !Value {
3300 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3188 const target = mod.getTarget();
3301 const scalar_ty = float_type.scalarType(mod);3189 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3302 for (result_data, 0..) |*scalar, i| {3190 16 => .{ .f16 = lhs.toFloat(f16, mod) + rhs.toFloat(f16, mod) },
3303 const lhs_elem = try lhs.elemValue(mod, i);3191 32 => .{ .f32 = lhs.toFloat(f32, mod) + rhs.toFloat(f32, mod) },
3304 const rhs_elem = try rhs.elemValue(mod, i);3192 64 => .{ .f64 = lhs.toFloat(f64, mod) + rhs.toFloat(f64, mod) },
3305 scalar.* = try (try floatDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);3193 80 => .{ .f80 = lhs.toFloat(f80, mod) + rhs.toFloat(f80, mod) },
3306 }3194 128 => .{ .f128 = lhs.toFloat(f128, mod) + rhs.toFloat(f128, mod) },
3307 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3195 else => unreachable,
3308 .ty = float_type.toIntern(),3196 };
3309 .storage = .{ .elems = result_data },3197 return Value.fromInterned((try mod.intern(.{ .float = .{
3310 } })));3198 .ty = float_type.toIntern(),
3311 }3199 .storage = storage,
3312 return floatDivFloorScalar(lhs, rhs, float_type, mod);3200 } })));
3313 }3201}
33143202
3315 pub fn floatDivFloorScalar(3203pub fn floatSub(
3316 lhs: Value,3204 lhs: Value,
3317 rhs: Value,3205 rhs: Value,
3318 float_type: Type,3206 float_type: Type,
3319 mod: *Module,3207 arena: Allocator,
3320 ) !Value {3208 mod: *Module,
3321 const target = mod.getTarget();3209) !Value {
3322 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3210 if (float_type.zigTypeTag(mod) == .Vector) {
3323 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },3211 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3324 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },3212 const scalar_ty = float_type.scalarType(mod);
3325 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },3213 for (result_data, 0..) |*scalar, i| {
3326 80 => .{ .f80 = @divFloor(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },3214 const lhs_elem = try lhs.elemValue(mod, i);
3327 128 => .{ .f128 = @divFloor(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },3215 const rhs_elem = try rhs.elemValue(mod, i);
3328 else => unreachable,3216 scalar.* = try (try floatSubScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3329 };3217 }
3330 return Value.fromInterned((try mod.intern(.{ .float = .{3218 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3331 .ty = float_type.toIntern(),3219 .ty = float_type.toIntern(),
3332 .storage = storage,3220 .storage = .{ .elems = result_data },
3333 } })));3221 } })));
3334 }3222 }
33353223 return floatSubScalar(lhs, rhs, float_type, mod);
3336 pub fn floatDivTrunc(3224}
3337 lhs: Value,3225
3338 rhs: Value,3226pub fn floatSubScalar(
3339 float_type: Type,3227 lhs: Value,
3340 arena: Allocator,3228 rhs: Value,
3341 mod: *Module,3229 float_type: Type,
3342 ) !Value {3230 mod: *Module,
3343 if (float_type.zigTypeTag(mod) == .Vector) {3231) !Value {
3344 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3232 const target = mod.getTarget();
3345 const scalar_ty = float_type.scalarType(mod);3233 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3346 for (result_data, 0..) |*scalar, i| {3234 16 => .{ .f16 = lhs.toFloat(f16, mod) - rhs.toFloat(f16, mod) },
3347 const lhs_elem = try lhs.elemValue(mod, i);3235 32 => .{ .f32 = lhs.toFloat(f32, mod) - rhs.toFloat(f32, mod) },
3348 const rhs_elem = try rhs.elemValue(mod, i);3236 64 => .{ .f64 = lhs.toFloat(f64, mod) - rhs.toFloat(f64, mod) },
3349 scalar.* = try (try floatDivTruncScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);3237 80 => .{ .f80 = lhs.toFloat(f80, mod) - rhs.toFloat(f80, mod) },
3350 }3238 128 => .{ .f128 = lhs.toFloat(f128, mod) - rhs.toFloat(f128, mod) },
3351 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3239 else => unreachable,
3352 .ty = float_type.toIntern(),3240 };
3353 .storage = .{ .elems = result_data },3241 return Value.fromInterned((try mod.intern(.{ .float = .{
3354 } })));3242 .ty = float_type.toIntern(),
3355 }3243 .storage = storage,
3356 return floatDivTruncScalar(lhs, rhs, float_type, mod);3244 } })));
3357 }3245}
33583246
3359 pub fn floatDivTruncScalar(3247pub fn floatDiv(
3360 lhs: Value,3248 lhs: Value,
3361 rhs: Value,3249 rhs: Value,
3362 float_type: Type,3250 float_type: Type,
3363 mod: *Module,3251 arena: Allocator,
3364 ) !Value {3252 mod: *Module,
3365 const target = mod.getTarget();3253) !Value {
3366 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3254 if (float_type.zigTypeTag(mod) == .Vector) {
3367 16 => .{ .f16 = @divTrunc(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },3255 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3368 32 => .{ .f32 = @divTrunc(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },3256 const scalar_ty = float_type.scalarType(mod);
3369 64 => .{ .f64 = @divTrunc(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },3257 for (result_data, 0..) |*scalar, i| {
3370 80 => .{ .f80 = @divTrunc(lhs.toFloat(f80, mod), rhs.toFloat(f80, mod)) },3258 const lhs_elem = try lhs.elemValue(mod, i);
3371 128 => .{ .f128 = @divTrunc(lhs.toFloat(f128, mod), rhs.toFloat(f128, mod)) },3259 const rhs_elem = try rhs.elemValue(mod, i);
3372 else => unreachable,3260 scalar.* = try (try floatDivScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3373 };3261 }
3374 return Value.fromInterned((try mod.intern(.{ .float = .{3262 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3375 .ty = float_type.toIntern(),3263 .ty = float_type.toIntern(),
3376 .storage = storage,3264 .storage = .{ .elems = result_data },
3377 } })));3265 } })));
3378 }3266 }
33793267 return floatDivScalar(lhs, rhs, float_type, mod);
3380 pub fn floatMul(3268}
3381 lhs: Value,3269
3382 rhs: Value,3270pub fn floatDivScalar(
3383 float_type: Type,3271 lhs: Value,
3384 arena: Allocator,3272 rhs: Value,
3385 mod: *Module,3273 float_type: Type,
3386 ) !Value {3274 mod: *Module,
3387 if (float_type.zigTypeTag(mod) == .Vector) {3275) !Value {
3388 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3276 const target = mod.getTarget();
3389 const scalar_ty = float_type.scalarType(mod);3277 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3390 for (result_data, 0..) |*scalar, i| {3278 16 => .{ .f16 = lhs.toFloat(f16, mod) / rhs.toFloat(f16, mod) },
3391 const lhs_elem = try lhs.elemValue(mod, i);3279 32 => .{ .f32 = lhs.toFloat(f32, mod) / rhs.toFloat(f32, mod) },
3392 const rhs_elem = try rhs.elemValue(mod, i);3280 64 => .{ .f64 = lhs.toFloat(f64, mod) / rhs.toFloat(f64, mod) },
3393 scalar.* = try (try floatMulScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);3281 80 => .{ .f80 = lhs.toFloat(f80, mod) / rhs.toFloat(f80, mod) },
3394 }3282 128 => .{ .f128 = lhs.toFloat(f128, mod) / rhs.toFloat(f128, mod) },
3395 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3283 else => unreachable,
3396 .ty = float_type.toIntern(),3284 };
3397 .storage = .{ .elems = result_data },3285 return Value.fromInterned((try mod.intern(.{ .float = .{
3398 } })));3286 .ty = float_type.toIntern(),
3399 }3287 .storage = storage,
3400 return floatMulScalar(lhs, rhs, float_type, mod);3288 } })));
3401 }3289}
34023290
3403 pub fn floatMulScalar(3291pub fn floatDivFloor(
3404 lhs: Value,3292 lhs: Value,
3405 rhs: Value,3293 rhs: Value,
3406 float_type: Type,3294 float_type: Type,
3407 mod: *Module,3295 arena: Allocator,
3408 ) !Value {3296 mod: *Module,
3409 const target = mod.getTarget();3297) !Value {
3410 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3298 if (float_type.zigTypeTag(mod) == .Vector) {
3411 16 => .{ .f16 = lhs.toFloat(f16, mod) * rhs.toFloat(f16, mod) },3299 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3412 32 => .{ .f32 = lhs.toFloat(f32, mod) * rhs.toFloat(f32, mod) },3300 const scalar_ty = float_type.scalarType(mod);
3413 64 => .{ .f64 = lhs.toFloat(f64, mod) * rhs.toFloat(f64, mod) },3301 for (result_data, 0..) |*scalar, i| {
3414 80 => .{ .f80 = lhs.toFloat(f80, mod) * rhs.toFloat(f80, mod) },3302 const lhs_elem = try lhs.elemValue(mod, i);
3415 128 => .{ .f128 = lhs.toFloat(f128, mod) * rhs.toFloat(f128, mod) },3303 const rhs_elem = try rhs.elemValue(mod, i);
3416 else => unreachable,3304 scalar.* = try (try floatDivFloorScalar(lhs_elem, rhs_elem, scalar_ty, mod)).intern(scalar_ty, mod);
3417 };3305 }
3418 return Value.fromInterned((try mod.intern(.{ .float = .{3306 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3419 .ty = float_type.toIntern(),3307 .ty = float_type.toIntern(),
3420 .storage = storage,3308 .storage = .{ .elems = result_data },
3421 } })));3309 } })));
3422 }3310 }
34233311 return floatDivFloorScalar(lhs, rhs, float_type, mod);
3424 pub fn sqrt(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3312}
3425 if (float_type.zigTypeTag(mod) == .Vector) {3313
3426 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3314pub fn floatDivFloorScalar(
3427 const scalar_ty = float_type.scalarType(mod);3315 lhs: Value,
3428 for (result_data, 0..) |*scalar, i| {3316 rhs: Value,
3429 const elem_val = try val.elemValue(mod, i);3317 float_type: Type,
3430 scalar.* = try (try sqrtScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3318 mod: *Module,
3431 }3319) !Value {
3432 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3320 const target = mod.getTarget();
3433 .ty = float_type.toIntern(),3321 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3434 .storage = .{ .elems = result_data },3322 16 => .{ .f16 = @divFloor(lhs.toFloat(f16, mod), rhs.toFloat(f16, mod)) },
3435 } })));3323 32 => .{ .f32 = @divFloor(lhs.toFloat(f32, mod), rhs.toFloat(f32, mod)) },
3436 }3324 64 => .{ .f64 = @divFloor(lhs.toFloat(f64, mod), rhs.toFloat(f64, mod)) },
3437 return sqrtScalar(val, float_type, 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 } })));
3438 }3354 }
34393355 return floatDivTruncScalar(lhs, rhs, float_type, mod);
3440 pub fn sqrtScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {3356}
3441 const target = mod.getTarget();3357
3442 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3358pub fn floatDivTruncScalar(
3443 16 => .{ .f16 = @sqrt(val.toFloat(f16, mod)) },3359 lhs: Value,
3444 32 => .{ .f32 = @sqrt(val.toFloat(f32, mod)) },3360 rhs: Value,
3445 64 => .{ .f64 = @sqrt(val.toFloat(f64, mod)) },3361 float_type: Type,
3446 80 => .{ .f80 = @sqrt(val.toFloat(f80, mod)) },3362 mod: *Module,
3447 128 => .{ .f128 = @sqrt(val.toFloat(f128, mod)) },3363) !Value {
3448 else => unreachable,3364 const target = mod.getTarget();
3449 };3365 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3450 return Value.fromInterned((try mod.intern(.{ .float = .{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 = .{
3451 .ty = float_type.toIntern(),3395 .ty = float_type.toIntern(),
3452 .storage = storage,3396 .storage = .{ .elems = result_data },
3453 } })));3397 } })));
3454 }3398 }
34553399 return floatMulScalar(lhs, rhs, float_type, mod);
3456 pub fn sin(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3400}
3457 if (float_type.zigTypeTag(mod) == .Vector) {3401
3458 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3402pub fn floatMulScalar(
3459 const scalar_ty = float_type.scalarType(mod);3403 lhs: Value,
3460 for (result_data, 0..) |*scalar, i| {3404 rhs: Value,
3461 const elem_val = try val.elemValue(mod, i);3405 float_type: Type,
3462 scalar.* = try (try sinScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3406 mod: *Module,
3463 }3407) !Value {
3464 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3408 const target = mod.getTarget();
3465 .ty = float_type.toIntern(),3409 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3466 .storage = .{ .elems = result_data },3410 16 => .{ .f16 = lhs.toFloat(f16, mod) * rhs.toFloat(f16, mod) },
3467 } })));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);
3468 }3430 }
3469 return sinScalar(val, float_type, mod);3431 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3432 .ty = float_type.toIntern(),
3484 .storage = storage,3433 .storage = .{ .elems = result_data },
3485 } })));3434 } })));
3486 }3435 }
34873436 return sqrtScalar(val, float_type, mod);
3488 pub fn cos(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3437}
3489 if (float_type.zigTypeTag(mod) == .Vector) {3438
3490 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3439pub fn sqrtScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3491 const scalar_ty = float_type.scalarType(mod);3440 const target = mod.getTarget();
3492 for (result_data, 0..) |*scalar, i| {3441 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3493 const elem_val = try val.elemValue(mod, i);3442 16 => .{ .f16 = @sqrt(val.toFloat(f16, mod)) },
3494 scalar.* = try (try cosScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3443 32 => .{ .f32 = @sqrt(val.toFloat(f32, mod)) },
3495 }3444 64 => .{ .f64 = @sqrt(val.toFloat(f64, mod)) },
3496 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3445 80 => .{ .f80 = @sqrt(val.toFloat(f80, mod)) },
3497 .ty = float_type.toIntern(),3446 128 => .{ .f128 = @sqrt(val.toFloat(f128, mod)) },
3498 .storage = .{ .elems = result_data },3447 else => unreachable,
3499 } })));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);
3500 }3462 }
3501 return cosScalar(val, float_type, mod);3463 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3464 .ty = float_type.toIntern(),
3516 .storage = storage,3465 .storage = .{ .elems = result_data },
3517 } })));3466 } })));
3518 }3467 }
35193468 return sinScalar(val, float_type, mod);
3520 pub fn tan(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3469}
3521 if (float_type.zigTypeTag(mod) == .Vector) {3470
3522 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3471pub fn sinScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3523 const scalar_ty = float_type.scalarType(mod);3472 const target = mod.getTarget();
3524 for (result_data, 0..) |*scalar, i| {3473 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3525 const elem_val = try val.elemValue(mod, i);3474 16 => .{ .f16 = @sin(val.toFloat(f16, mod)) },
3526 scalar.* = try (try tanScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3475 32 => .{ .f32 = @sin(val.toFloat(f32, mod)) },
3527 }3476 64 => .{ .f64 = @sin(val.toFloat(f64, mod)) },
3528 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3477 80 => .{ .f80 = @sin(val.toFloat(f80, mod)) },
3529 .ty = float_type.toIntern(),3478 128 => .{ .f128 = @sin(val.toFloat(f128, mod)) },
3530 .storage = .{ .elems = result_data },3479 else => unreachable,
3531 } })));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);
3532 }3494 }
3533 return tanScalar(val, float_type, mod);3495 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3496 .ty = float_type.toIntern(),
3548 .storage = storage,3497 .storage = .{ .elems = result_data },
3549 } })));3498 } })));
3550 }3499 }
35513500 return cosScalar(val, float_type, mod);
3552 pub fn exp(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3501}
3553 if (float_type.zigTypeTag(mod) == .Vector) {3502
3554 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3503pub fn cosScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3555 const scalar_ty = float_type.scalarType(mod);3504 const target = mod.getTarget();
3556 for (result_data, 0..) |*scalar, i| {3505 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3557 const elem_val = try val.elemValue(mod, i);3506 16 => .{ .f16 = @cos(val.toFloat(f16, mod)) },
3558 scalar.* = try (try expScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3507 32 => .{ .f32 = @cos(val.toFloat(f32, mod)) },
3559 }3508 64 => .{ .f64 = @cos(val.toFloat(f64, mod)) },
3560 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3509 80 => .{ .f80 = @cos(val.toFloat(f80, mod)) },
3561 .ty = float_type.toIntern(),3510 128 => .{ .f128 = @cos(val.toFloat(f128, mod)) },
3562 .storage = .{ .elems = result_data },3511 else => unreachable,
3563 } })));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);
3564 }3526 }
3565 return expScalar(val, float_type, mod);3527 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3528 .ty = float_type.toIntern(),
3580 .storage = storage,3529 .storage = .{ .elems = result_data },
3581 } })));3530 } })));
3582 }3531 }
35833532 return tanScalar(val, float_type, mod);
3584 pub fn exp2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3533}
3585 if (float_type.zigTypeTag(mod) == .Vector) {3534
3586 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3535pub fn tanScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3587 const scalar_ty = float_type.scalarType(mod);3536 const target = mod.getTarget();
3588 for (result_data, 0..) |*scalar, i| {3537 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3589 const elem_val = try val.elemValue(mod, i);3538 16 => .{ .f16 = @tan(val.toFloat(f16, mod)) },
3590 scalar.* = try (try exp2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3539 32 => .{ .f32 = @tan(val.toFloat(f32, mod)) },
3591 }3540 64 => .{ .f64 = @tan(val.toFloat(f64, mod)) },
3592 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3541 80 => .{ .f80 = @tan(val.toFloat(f80, mod)) },
3593 .ty = float_type.toIntern(),3542 128 => .{ .f128 = @tan(val.toFloat(f128, mod)) },
3594 .storage = .{ .elems = result_data },3543 else => unreachable,
3595 } })));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);
3596 }3558 }
3597 return exp2Scalar(val, float_type, mod);3559 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3560 .ty = float_type.toIntern(),
3612 .storage = storage,3561 .storage = .{ .elems = result_data },
3613 } })));3562 } })));
3614 }3563 }
36153564 return expScalar(val, float_type, mod);
3616 pub fn log(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3565}
3617 if (float_type.zigTypeTag(mod) == .Vector) {3566
3618 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3567pub fn expScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3619 const scalar_ty = float_type.scalarType(mod);3568 const target = mod.getTarget();
3620 for (result_data, 0..) |*scalar, i| {3569 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3621 const elem_val = try val.elemValue(mod, i);3570 16 => .{ .f16 = @exp(val.toFloat(f16, mod)) },
3622 scalar.* = try (try logScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3571 32 => .{ .f32 = @exp(val.toFloat(f32, mod)) },
3623 }3572 64 => .{ .f64 = @exp(val.toFloat(f64, mod)) },
3624 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3573 80 => .{ .f80 = @exp(val.toFloat(f80, mod)) },
3625 .ty = float_type.toIntern(),3574 128 => .{ .f128 = @exp(val.toFloat(f128, mod)) },
3626 .storage = .{ .elems = result_data },3575 else => unreachable,
3627 } })));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);
3628 }3590 }
3629 return logScalar(val, float_type, mod);3591 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3592 .ty = float_type.toIntern(),
3644 .storage = storage,3593 .storage = .{ .elems = result_data },
3645 } })));3594 } })));
3646 }3595 }
36473596 return exp2Scalar(val, float_type, mod);
3648 pub fn log2(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3597}
3649 if (float_type.zigTypeTag(mod) == .Vector) {3598
3650 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3599pub fn exp2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3651 const scalar_ty = float_type.scalarType(mod);3600 const target = mod.getTarget();
3652 for (result_data, 0..) |*scalar, i| {3601 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3653 const elem_val = try val.elemValue(mod, i);3602 16 => .{ .f16 = @exp2(val.toFloat(f16, mod)) },
3654 scalar.* = try (try log2Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3603 32 => .{ .f32 = @exp2(val.toFloat(f32, mod)) },
3655 }3604 64 => .{ .f64 = @exp2(val.toFloat(f64, mod)) },
3656 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3605 80 => .{ .f80 = @exp2(val.toFloat(f80, mod)) },
3657 .ty = float_type.toIntern(),3606 128 => .{ .f128 = @exp2(val.toFloat(f128, mod)) },
3658 .storage = .{ .elems = result_data },3607 else => unreachable,
3659 } })));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);
3660 }3622 }
3661 return log2Scalar(val, float_type, mod);3623 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3624 .ty = float_type.toIntern(),
3676 .storage = storage,3625 .storage = .{ .elems = result_data },
3677 } })));3626 } })));
3678 }3627 }
36793628 return logScalar(val, float_type, mod);
3680 pub fn log10(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3629}
3681 if (float_type.zigTypeTag(mod) == .Vector) {3630
3682 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3631pub fn logScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3683 const scalar_ty = float_type.scalarType(mod);3632 const target = mod.getTarget();
3684 for (result_data, 0..) |*scalar, i| {3633 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3685 const elem_val = try val.elemValue(mod, i);3634 16 => .{ .f16 = @log(val.toFloat(f16, mod)) },
3686 scalar.* = try (try log10Scalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3635 32 => .{ .f32 = @log(val.toFloat(f32, mod)) },
3687 }3636 64 => .{ .f64 = @log(val.toFloat(f64, mod)) },
3688 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3637 80 => .{ .f80 = @log(val.toFloat(f80, mod)) },
3689 .ty = float_type.toIntern(),3638 128 => .{ .f128 = @log(val.toFloat(f128, mod)) },
3690 .storage = .{ .elems = result_data },3639 else => unreachable,
3691 } })));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);
3692 }3654 }
3693 return log10Scalar(val, float_type, mod);3655 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3656 .ty = float_type.toIntern(),
3708 .storage = storage,3657 .storage = .{ .elems = result_data },
3709 } })));3658 } })));
3710 }3659 }
37113660 return log2Scalar(val, float_type, mod);
3712 pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {3661}
3713 if (ty.zigTypeTag(mod) == .Vector) {3662
3714 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));3663pub fn log2Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3715 const scalar_ty = ty.scalarType(mod);3664 const target = mod.getTarget();
3716 for (result_data, 0..) |*scalar, i| {3665 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3717 const elem_val = try val.elemValue(mod, i);3666 16 => .{ .f16 = @log2(val.toFloat(f16, mod)) },
3718 scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod);3667 32 => .{ .f32 = @log2(val.toFloat(f32, mod)) },
3719 }3668 64 => .{ .f64 = @log2(val.toFloat(f64, mod)) },
3720 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3669 80 => .{ .f80 = @log2(val.toFloat(f80, mod)) },
3721 .ty = ty.toIntern(),3670 128 => .{ .f128 = @log2(val.toFloat(f128, mod)) },
3722 .storage = .{ .elems = result_data },3671 else => unreachable,
3723 } })));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);
3724 }3686 }
3725 return absScalar(val, ty, mod, arena);3687 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3688 .ty = float_type.toIntern(),
3689 .storage = .{ .elems = result_data },
3690 } })));
3726 }3691 }
37273692 return log10Scalar(val, float_type, mod);
3728 pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value {3693}
3729 switch (ty.zigTypeTag(mod)) {3694
3730 .Int => {3695pub fn log10Scalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3731 var buffer: Value.BigIntSpace = undefined;3696 const target = mod.getTarget();
3732 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);3697 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3733 operand_bigint.abs();3698 16 => .{ .f16 = @log10(val.toFloat(f16, mod)) },
37343699 32 => .{ .f32 = @log10(val.toFloat(f32, mod)) },
3735 return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst());3700 64 => .{ .f64 = @log10(val.toFloat(f64, mod)) },
3736 },3701 80 => .{ .f80 = @log10(val.toFloat(f80, mod)) },
3737 .ComptimeInt => {3702 128 => .{ .f128 = @log10(val.toFloat(f128, mod)) },
3738 var buffer: Value.BigIntSpace = undefined;3703 else => unreachable,
3739 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);3704 };
3740 operand_bigint.abs();3705 return Value.fromInterned((try mod.intern(.{ .float = .{
37413706 .ty = float_type.toIntern(),
3742 return mod.intValue_big(ty, operand_bigint.toConst());3707 .storage = storage,
3743 },3708 } })));
3744 .ComptimeFloat, .Float => {3709}
3745 const target = mod.getTarget();3710
3746 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {3711pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
3747 16 => .{ .f16 = @abs(val.toFloat(f16, mod)) },3712 if (ty.zigTypeTag(mod) == .Vector) {
3748 32 => .{ .f32 = @abs(val.toFloat(f32, mod)) },3713 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod));
3749 64 => .{ .f64 = @abs(val.toFloat(f64, mod)) },3714 const scalar_ty = ty.scalarType(mod);
3750 80 => .{ .f80 = @abs(val.toFloat(f80, mod)) },3715 for (result_data, 0..) |*scalar, i| {
3751 128 => .{ .f128 = @abs(val.toFloat(f128, mod)) },3716 const elem_val = try val.elemValue(mod, i);
3752 else => unreachable,3717 scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod);
3753 };
3754 return Value.fromInterned((try mod.intern(.{ .float = .{
3755 .ty = ty.toIntern(),
3756 .storage = storage,
3757 } })));
3758 },
3759 else => unreachable,
3760 }3718 }
3719 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3720 .ty = ty.toIntern(),
3721 .storage = .{ .elems = result_data },
3722 } })));
3761 }3723 }
37623724 return absScalar(val, ty, mod, arena);
3763 pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3725}
3764 if (float_type.zigTypeTag(mod) == .Vector) {3726
3765 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3727pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value {
3766 const scalar_ty = float_type.scalarType(mod);3728 switch (ty.zigTypeTag(mod)) {
3767 for (result_data, 0..) |*scalar, i| {3729 .Int => {
3768 const elem_val = try val.elemValue(mod, i);3730 var buffer: Value.BigIntSpace = undefined;
3769 scalar.* = try (try floorScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3731 var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena);
3770 }3732 operand_bigint.abs();
3771 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3733
3772 .ty = float_type.toIntern(),3734 return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst());
3773 .storage = .{ .elems = result_data },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,
3774 } })));3756 } })));
3775 }3757 },
3776 return floorScalar(val, float_type, mod);3758 else => unreachable,
3777 }3759 }
3760}
37783761
3779 pub fn floorScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {3762pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {
3780 const target = mod.getTarget();3763 if (float_type.zigTypeTag(mod) == .Vector) {
3781 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3764 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3782 16 => .{ .f16 = @floor(val.toFloat(f16, mod)) },3765 const scalar_ty = float_type.scalarType(mod);
3783 32 => .{ .f32 = @floor(val.toFloat(f32, mod)) },3766 for (result_data, 0..) |*scalar, i| {
3784 64 => .{ .f64 = @floor(val.toFloat(f64, mod)) },3767 const elem_val = try val.elemValue(mod, i);
3785 80 => .{ .f80 = @floor(val.toFloat(f80, mod)) },3768 scalar.* = try (try floorScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);
3786 128 => .{ .f128 = @floor(val.toFloat(f128, mod)) },3769 }
3787 else => unreachable,3770 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
3788 };
3789 return Value.fromInterned((try mod.intern(.{ .float = .{
3790 .ty = float_type.toIntern(),3771 .ty = float_type.toIntern(),
3791 .storage = storage,3772 .storage = .{ .elems = result_data },
3792 } })));3773 } })));
3793 }3774 }
37943775 return floorScalar(val, float_type, mod);
3795 pub fn ceil(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3776}
3796 if (float_type.zigTypeTag(mod) == .Vector) {3777
3797 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3778pub fn floorScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3798 const scalar_ty = float_type.scalarType(mod);3779 const target = mod.getTarget();
3799 for (result_data, 0..) |*scalar, i| {3780 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3800 const elem_val = try val.elemValue(mod, i);3781 16 => .{ .f16 = @floor(val.toFloat(f16, mod)) },
3801 scalar.* = try (try ceilScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3782 32 => .{ .f32 = @floor(val.toFloat(f32, mod)) },
3802 }3783 64 => .{ .f64 = @floor(val.toFloat(f64, mod)) },
3803 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3784 80 => .{ .f80 = @floor(val.toFloat(f80, mod)) },
3804 .ty = float_type.toIntern(),3785 128 => .{ .f128 = @floor(val.toFloat(f128, mod)) },
3805 .storage = .{ .elems = result_data },3786 else => unreachable,
3806 } })));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);
3807 }3801 }
3808 return ceilScalar(val, float_type, mod);3802 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3803 .ty = float_type.toIntern(),
3823 .storage = storage,3804 .storage = .{ .elems = result_data },
3824 } })));3805 } })));
3825 }3806 }
38263807 return ceilScalar(val, float_type, mod);
3827 pub fn round(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3808}
3828 if (float_type.zigTypeTag(mod) == .Vector) {3809
3829 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3810pub fn ceilScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3830 const scalar_ty = float_type.scalarType(mod);3811 const target = mod.getTarget();
3831 for (result_data, 0..) |*scalar, i| {3812 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3832 const elem_val = try val.elemValue(mod, i);3813 16 => .{ .f16 = @ceil(val.toFloat(f16, mod)) },
3833 scalar.* = try (try roundScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3814 32 => .{ .f32 = @ceil(val.toFloat(f32, mod)) },
3834 }3815 64 => .{ .f64 = @ceil(val.toFloat(f64, mod)) },
3835 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3816 80 => .{ .f80 = @ceil(val.toFloat(f80, mod)) },
3836 .ty = float_type.toIntern(),3817 128 => .{ .f128 = @ceil(val.toFloat(f128, mod)) },
3837 .storage = .{ .elems = result_data },3818 else => unreachable,
3838 } })));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);
3839 }3833 }
3840 return roundScalar(val, float_type, mod);3834 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3835 .ty = float_type.toIntern(),
3855 .storage = storage,3836 .storage = .{ .elems = result_data },
3856 } })));3837 } })));
3857 }3838 }
38583839 return roundScalar(val, float_type, mod);
3859 pub fn trunc(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value {3840}
3860 if (float_type.zigTypeTag(mod) == .Vector) {3841
3861 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3842pub fn roundScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3862 const scalar_ty = float_type.scalarType(mod);3843 const target = mod.getTarget();
3863 for (result_data, 0..) |*scalar, i| {3844 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3864 const elem_val = try val.elemValue(mod, i);3845 16 => .{ .f16 = @round(val.toFloat(f16, mod)) },
3865 scalar.* = try (try truncScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod);3846 32 => .{ .f32 = @round(val.toFloat(f32, mod)) },
3866 }3847 64 => .{ .f64 = @round(val.toFloat(f64, mod)) },
3867 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3848 80 => .{ .f80 = @round(val.toFloat(f80, mod)) },
3868 .ty = float_type.toIntern(),3849 128 => .{ .f128 = @round(val.toFloat(f128, mod)) },
3869 .storage = .{ .elems = result_data },3850 else => unreachable,
3870 } })));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);
3871 }3865 }
3872 return truncScalar(val, float_type, mod);3866 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3867 .ty = float_type.toIntern(),
3887 .storage = storage,3868 .storage = .{ .elems = result_data },
3888 } })));3869 } })));
3889 }3870 }
38903871 return truncScalar(val, float_type, mod);
3891 pub fn mulAdd(3872}
3892 float_type: Type,3873
3893 mulend1: Value,3874pub fn truncScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value {
3894 mulend2: Value,3875 const target = mod.getTarget();
3895 addend: Value,3876 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
3896 arena: Allocator,3877 16 => .{ .f16 = @trunc(val.toFloat(f16, mod)) },
3897 mod: *Module,3878 32 => .{ .f32 = @trunc(val.toFloat(f32, mod)) },
3898 ) !Value {3879 64 => .{ .f64 = @trunc(val.toFloat(f64, mod)) },
3899 if (float_type.zigTypeTag(mod) == .Vector) {3880 80 => .{ .f80 = @trunc(val.toFloat(f80, mod)) },
3900 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));3881 128 => .{ .f128 = @trunc(val.toFloat(f128, mod)) },
3901 const scalar_ty = float_type.scalarType(mod);3882 else => unreachable,
3902 for (result_data, 0..) |*scalar, i| {3883 };
3903 const mulend1_elem = try mulend1.elemValue(mod, i);3884 return Value.fromInterned((try mod.intern(.{ .float = .{
3904 const mulend2_elem = try mulend2.elemValue(mod, i);3885 .ty = float_type.toIntern(),
3905 const addend_elem = try addend.elemValue(mod, i);3886 .storage = storage,
3906 scalar.* = try (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, mod)).intern(scalar_ty, mod);3887 } })));
3907 }3888}
3908 return Value.fromInterned((try mod.intern(.{ .aggregate = .{3889
3909 .ty = float_type.toIntern(),3890pub fn mulAdd(
3910 .storage = .{ .elems = result_data },3891 float_type: Type,
3911 } })));3892 mulend1: Value,
3912 }3893 mulend2: Value,
3913 return mulAddScalar(float_type, mulend1, mulend2, addend, mod);3894 addend: Value,
3914 }3895 arena: Allocator,
39153896 mod: *Module,
3916 pub fn mulAddScalar(3897) !Value {
3917 float_type: Type,3898 if (float_type.zigTypeTag(mod) == .Vector) {
3918 mulend1: Value,3899 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod));
3919 mulend2: Value,3900 const scalar_ty = float_type.scalarType(mod);
3920 addend: Value,3901 for (result_data, 0..) |*scalar, i| {
3921 mod: *Module,3902 const mulend1_elem = try mulend1.elemValue(mod, i);
3922 ) Allocator.Error!Value {3903 const mulend2_elem = try mulend2.elemValue(mod, i);
3923 const target = mod.getTarget();3904 const addend_elem = try addend.elemValue(mod, i);
3924 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {3905 scalar.* = try (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, mod)).intern(scalar_ty, mod);
3925 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, mod), mulend2.toFloat(f16, mod), addend.toFloat(f16, mod)) },3906 }
3926 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, mod), mulend2.toFloat(f32, mod), addend.toFloat(f32, mod)) },3907 return Value.fromInterned((try mod.intern(.{ .aggregate = .{
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(),3908 .ty = float_type.toIntern(),
3934 .storage = storage,3909 .storage = .{ .elems = result_data },
3935 } })));3910 } })));
3936 }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 };
39373992
3938 /// If the value is represented in-memory as a series of bytes that all3993 pub const Bytes = struct {
3939 /// have the same value, return that byte value, otherwise null.3994 base: Payload,
3940 pub fn hasRepeatedByteRepr(val: Value, ty: Type, mod: *Module) !?u8 {3995 /// Includes the sentinel, if any.
3941 const abi_size = std.math.cast(usize, ty.abiSize(mod)) orelse return null;3996 data: []const u8,
3942 assert(abi_size >= 1);3997 };
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 };
39993998
4000 pub const SubValue = struct {3999 pub const SubValue = struct {
4001 base: Payload,4000 base: Payload,
4002 data: Value,4001 data: Value,
4003 };4002 };
40044003
4005 pub const Aggregate = struct {4004 pub const Aggregate = struct {
4006 base: Payload,4005 base: Payload,
4007 /// Field values. The types are according to the struct or array type.4006 /// 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.4007 /// The length is provided here so that copying a Value does not depend on the Type.
4009 data: []Value,4008 data: []Value,
4010 };4009 };
40114010
4012 pub const Union = struct {4011 pub const Union = struct {
4013 pub const base_tag = Tag.@"union";4012 pub const base_tag = Tag.@"union";
40144013
4015 base: Payload = .{ .tag = base_tag },4014 base: Payload = .{ .tag = base_tag },
4016 data: Data,4015 data: Data,
40174016
4018 pub const Data = struct {4017 pub const Data = struct {
4019 tag: ?Value,4018 tag: ?Value,
4020 val: Value,4019 val: Value,
4021 };
4022 };4020 };
4023 };4021 };
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};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}