1const build_options = @import("build_options");
2const builtin = @import("builtin");
3
4const std = @import("std");
5const Io = std.Io;
6const assert = std.debug.assert;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
9const Target = std.Target;
10const Allocator = std.mem.Allocator;
11
12const Type = @import("Type.zig");
13const Zcu = @import("Zcu.zig");
14const Sema = @import("Sema.zig");
15const InternPool = @import("InternPool.zig");
16const print_value = @import("print_value.zig");
17const Value = @This();
18
19ip_index: InternPool.Index,
20
21pub fn format(val: Value, writer: *std.Io.Writer) !void {
22 _ = val;
23 _ = writer;
24 @compileError("do not use format values directly; use either fmtDebug or fmtValue");
25}
26
27/// This is a debug function. In order to print values in a meaningful way
28/// we also need access to the type.
29pub fn dump(start_val: Value, w: *std.Io.Writer) std.Io.Writer.Error!void {
30 try w.print("(interned: {})", .{start_val.toIntern()});
31}
32
33pub fn fmtDebug(val: Value) std.fmt.Alt(Value, dump) {
34 return .{ .data = val };
35}
36
37pub fn fmtValue(val: Value, pt: Zcu.PerThread) std.fmt.Alt(print_value.FormatContext, print_value.format) {
38 return .{ .data = .{
39 .val = val,
40 .pt = pt,
41 .opt_sema = null,
42 .depth = 3,
43 } };
44}
45
46pub fn fmtValueSema(val: Value, pt: Zcu.PerThread, sema: *Sema) std.fmt.Alt(print_value.FormatContext, print_value.formatSema) {
47 return .{ .data = .{
48 .val = val,
49 .pt = pt,
50 .opt_sema = sema,
51 .depth = 3,
52 } };
53}
54
55pub fn fmtValueSemaFull(ctx: print_value.FormatContext) std.fmt.Alt(print_value.FormatContext, print_value.formatSema) {
56 return .{ .data = ctx };
57}
58
59/// Converts `val` to a null-terminated string stored in the InternPool.
60/// Asserts `val` is an array of `u8`
61pub fn toIpString(val: Value, ty: Type, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
62 const zcu = pt.zcu;
63 const comp = zcu.comp;
64 const gpa = comp.gpa;
65 const io = comp.io;
66 const ip = &zcu.intern_pool;
67 assert(ty.zigTypeTag(zcu) == .array);
68 assert(ty.childType(zcu).toIntern() == .u8_type);
69 switch (zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage) {
70 .bytes => |bytes| return bytes.toNullTerminatedString(ty.arrayLen(zcu), ip),
71 .elems => return arrayToIpString(val, ty.arrayLen(zcu), pt),
72 .repeated_elem => |elem| {
73 const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(zcu));
74 const len: u32 = @intCast(ty.arrayLen(zcu));
75 const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa, io);
76 try string_bytes.appendNTimes(.{byte}, len);
77 return ip.getOrPutTrailingString(gpa, io, pt.tid, len, .no_embedded_nulls);
78 },
79 }
80}
81
82/// Asserts that the value is representable as an array of bytes.
83/// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
84pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, pt: Zcu.PerThread) ![]u8 {
85 const zcu = pt.zcu;
86 const ip = &zcu.intern_pool;
87 return switch (ip.indexToKey(val.toIntern())) {
88 .enum_literal => |enum_literal| allocator.dupe(u8, enum_literal.toSlice(ip)),
89 .slice => |slice| try arrayToAllocatedBytes(val, Value.fromInterned(slice.len).toUnsignedInt(zcu), allocator, pt),
90 .aggregate => |aggregate| switch (aggregate.storage) {
91 .bytes => |bytes| try allocator.dupe(u8, bytes.toSlice(ty.arrayLenIncludingSentinel(zcu), ip)),
92 .elems => try arrayToAllocatedBytes(val, ty.arrayLen(zcu), allocator, pt),
93 .repeated_elem => |elem| {
94 const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(zcu));
95 const result = try allocator.alloc(u8, @intCast(ty.arrayLen(zcu)));
96 @memset(result, byte);
97 return result;
98 },
99 },
100 else => unreachable,
101 };
102}
103
104fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, pt: Zcu.PerThread) ![]u8 {
105 const result = try allocator.alloc(u8, @intCast(len));
106 for (result, 0..) |*elem, i| {
107 const elem_val = try val.elemValue(pt, i);
108 elem.* = @intCast(elem_val.toUnsignedInt(pt.zcu));
109 }
110 return result;
111}
112
113fn arrayToIpString(val: Value, len_u64: u64, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
114 const zcu = pt.zcu;
115 const comp = zcu.comp;
116 const gpa = comp.gpa;
117 const io = comp.io;
118 const ip = &zcu.intern_pool;
119 const len: u32 = @intCast(len_u64);
120 const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa, io);
121 try string_bytes.ensureUnusedCapacity(len);
122 for (0..len) |i| {
123 // I don't think elemValue has the possibility to affect ip.string_bytes. Let's
124 // assert just to be sure.
125 const prev_len = string_bytes.mutate.len;
126 const elem_val = try val.elemValue(pt, i);
127 assert(string_bytes.mutate.len == prev_len);
128 const byte: u8 = @intCast(elem_val.toUnsignedInt(zcu));
129 string_bytes.appendAssumeCapacity(.{byte});
130 }
131 return ip.getOrPutTrailingString(gpa, io, pt.tid, len, .no_embedded_nulls);
132}
133
134pub fn fromInterned(i: InternPool.Index) Value {
135 assert(i != .none);
136 return .{ .ip_index = i };
137}
138
139pub fn toIntern(val: Value) InternPool.Index {
140 assert(val.ip_index != .none);
141 return val.ip_index;
142}
143
144/// Asserts that the value is representable as a type.
145pub fn toType(self: Value) Type {
146 return Type.fromInterned(self.toIntern());
147}
148
149/// Asserts that value is defined and of enum or bitpack type.
150pub fn backingInt(val: Value, zcu: *const Zcu) Value {
151 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
152 .enum_tag => |enum_tag| .fromInterned(enum_tag.int),
153 .bitpack => |bitpack| .fromInterned(bitpack.backing_int_val),
154 else => unreachable,
155 };
156}
157
158/// Asserts that `val` is an integer.
159pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *const Zcu) BigIntConst {
160 if (val.getUnsignedInt(zcu)) |x| {
161 return BigIntMutable.init(&space.limbs, x).toConst();
162 }
163 const ip = &zcu.intern_pool;
164 const int_key = switch (ip.indexToKey(val.toIntern())) {
165 .enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int,
166 .bitpack => |bitpack| ip.indexToKey(bitpack.backing_int_val).int,
167 .int => |int| int,
168 else => unreachable,
169 };
170 return int_key.storage.toBigInt(space);
171}
172
173pub fn isFuncBody(val: Value, zcu: *Zcu) bool {
174 return zcu.intern_pool.isFuncBody(val.toIntern());
175}
176
177pub fn getFunction(val: Value, zcu: *Zcu) ?InternPool.Key.Func {
178 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
179 .func => |x| x,
180 else => null,
181 };
182}
183
184/// Asserts the value is a (defined) integer and it fits in a u64.
185pub fn toUnsignedInt(val: Value, zcu: *const Zcu) u64 {
186 return getUnsignedInt(val, zcu).?;
187}
188
189/// If the value fits in a u64, return it, otherwise null.
190/// Asserts not undefined.
191pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
192 return switch (val.toIntern()) {
193 .undef => unreachable,
194 .null_value => 0,
195 .bool_false => 0,
196 .bool_true => 1,
197 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
198 .undef => unreachable,
199 .int => |int| switch (int.storage) {
200 .big_int => |big_int| big_int.toInt(u64) catch null,
201 .u64 => |x| x,
202 .i64 => |x| std.math.cast(u64, x),
203 },
204 .ptr => |ptr| switch (ptr.base_addr) {
205 .int => ptr.byte_offset,
206 .field => |field| {
207 const base_addr = Value.fromInterned(field.base).getUnsignedInt(zcu) orelse return null;
208 const struct_ty = Value.fromInterned(field.base).typeOf(zcu).childType(zcu);
209 return base_addr + struct_ty.structFieldOffset(@intCast(field.index), zcu) + ptr.byte_offset;
210 },
211 else => null,
212 },
213 .opt => |opt| switch (opt.val) {
214 .none => 0,
215 else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu),
216 },
217 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu),
218 .bitpack => |bitpack| Value.fromInterned(bitpack.backing_int_val).getUnsignedInt(zcu),
219 .err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?,
220 else => null,
221 },
222 };
223}
224
225/// Asserts the value is an integer and it fits in a i64
226pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 {
227 return switch (val.toIntern()) {
228 .bool_false => 0,
229 .bool_true => 1,
230 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
231 .int => |int| switch (int.storage) {
232 .big_int => |big_int| big_int.toInt(i64) catch unreachable,
233 .i64 => |x| x,
234 .u64 => |x| @intCast(x),
235 },
236 else => unreachable,
237 },
238 };
239}
240
241pub fn toBool(val: Value) bool {
242 return switch (val.toIntern()) {
243 .bool_true => true,
244 .bool_false => false,
245 else => unreachable,
246 };
247}
248
249/// Write a Value's contents to `buffer`.
250///
251/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
252/// the end of the value in memory.
253pub fn writeToMemory(val: Value, zcu: *const Zcu, buffer: []u8) error{
254 ReinterpretDeclRef,
255 IllDefinedMemoryLayout,
256 OutOfMemory,
257}!void {
258 const target = zcu.getTarget();
259 const endian = target.cpu.arch.endian();
260 const ip = &zcu.intern_pool;
261 const ty = val.typeOf(zcu);
262 if (val.isUndef(zcu)) {
263 const size: usize = @intCast(ty.abiSize(zcu));
264 @memset(buffer[0..size], 0xAA);
265 return;
266 }
267 tag: switch (ty.zigTypeTag(zcu)) {
268 .type => return error.IllDefinedMemoryLayout,
269 .comptime_float => return error.IllDefinedMemoryLayout,
270 .comptime_int => return error.IllDefinedMemoryLayout,
271 .undefined => return error.IllDefinedMemoryLayout,
272 .null => return error.IllDefinedMemoryLayout,
273 .error_union => return error.IllDefinedMemoryLayout,
274 .enum_literal => return error.IllDefinedMemoryLayout,
275 .@"fn" => return error.IllDefinedMemoryLayout,
276 .spirv => return error.IllDefinedMemoryLayout,
277 .@"opaque" => unreachable,
278 .frame => unreachable,
279 .@"anyframe" => unreachable,
280 .noreturn => unreachable,
281 .void => {},
282 .bool => {
283 buffer[0] = @intFromBool(val.toBool());
284 },
285 .pointer => {
286 if (ty.isSlice(zcu)) return error.IllDefinedMemoryLayout;
287 if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
288 continue :tag .int;
289 },
290 .int, .@"enum", .error_set => {
291 var bigint_buffer: BigIntSpace = undefined;
292 const bigint = val.toBigInt(&bigint_buffer, zcu);
293 bigint.writeTwosComplement(buffer[0..@intCast(ty.abiSize(zcu))], endian);
294 },
295 .float => {
296 const float_bits = ty.floatBits(target);
297 switch (float_bits) {
298 16 => std.mem.writeInt(u16, buffer[0..2], @bitCast(val.toFloat(f16, zcu)), endian),
299 32 => std.mem.writeInt(u32, buffer[0..4], @bitCast(val.toFloat(f32, zcu)), endian),
300 64 => std.mem.writeInt(u64, buffer[0..8], @bitCast(val.toFloat(f64, zcu)), endian),
301 80 => std.mem.writeInt(u80, buffer[0..10], @bitCast(val.toFloat(f80, zcu)), endian),
302 128 => std.mem.writeInt(u128, buffer[0..16], @bitCast(val.toFloat(f128, zcu)), endian),
303 else => unreachable,
304 }
305 const float_bytes = @divExact(float_bits, 8);
306 const total_bytes: usize = @intCast(ty.abiSize(zcu));
307 @memset(buffer[float_bytes..total_bytes], 0); // padding
308 },
309 .array => {
310 const aggregate = ip.indexToKey(val.toIntern()).aggregate;
311 const len = ty.arrayLen(zcu);
312 const elem_ty = ty.childType(zcu);
313 const elem_size: usize = @intCast(elem_ty.abiSize(zcu));
314 var elem_i: usize = 0;
315 var buf_off: usize = 0;
316 while (elem_i < len) : (elem_i += 1) {
317 switch (aggregate.storage) {
318 .bytes => |bytes| buffer[buf_off] = bytes.at(elem_i, ip),
319 .elems => |elems| try Value.fromInterned(elems[elem_i]).writeToMemory(zcu, buffer[buf_off..]),
320 .repeated_elem => |elem| try Value.fromInterned(elem).writeToMemory(zcu, buffer[buf_off..]),
321 }
322 buf_off += elem_size;
323 }
324 if (ty.sentinel(zcu)) |sentinel_val| {
325 try sentinel_val.writeToMemory(zcu, buffer[buf_off..]);
326 }
327 },
328 .vector => return error.IllDefinedMemoryLayout,
329 .@"struct" => {
330 const struct_type = zcu.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;
331 switch (struct_type.layout) {
332 .auto => return error.IllDefinedMemoryLayout,
333 .@"extern" => {
334 var last_off: usize = 0;
335 for (struct_type.field_types.get(ip), 0..) |field_ty_ip, field_index| {
336 const off: usize = @intCast(ty.structFieldOffset(field_index, zcu));
337 @memset(buffer[last_off..off], 0xAA);
338 const field_val = Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
339 .bytes => |bytes| {
340 buffer[off] = bytes.at(field_index, ip);
341 continue;
342 },
343 .elems => |elems| elems[field_index],
344 .repeated_elem => |elem| elem,
345 });
346 try writeToMemory(field_val, zcu, buffer[off..]);
347 last_off = @intCast(off + Type.fromInterned(field_ty_ip).abiSize(zcu));
348 }
349 const struct_size: usize = @intCast(ty.abiSize(zcu));
350 @memset(buffer[last_off..struct_size], 0xAA);
351 },
352 .@"packed" => {
353 const int_index = ip.indexToKey(val.toIntern()).bitpack.backing_int_val;
354 return Value.fromInterned(int_index).writeToMemory(zcu, buffer);
355 },
356 }
357 },
358 .@"union" => switch (ty.containerLayout(zcu)) {
359 .auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
360 .@"extern" => {
361 const payload_val = val.unionPayload(zcu);
362 const payload_size: usize = @intCast(payload_val.typeOf(zcu).abiSize(zcu));
363 const union_size: usize = @intCast(ty.abiSize(zcu));
364 @memset(buffer[payload_size..union_size], 0xAA);
365 return writeToMemory(payload_val, zcu, buffer);
366 },
367 .@"packed" => {
368 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
369 return writeToMemory(int_val, zcu, buffer);
370 },
371 },
372 .optional => {
373 if (!ty.isPtrLikeOptional(zcu)) return error.IllDefinedMemoryLayout;
374 const opt_val = val.optionalValue(zcu);
375 if (opt_val) |some| {
376 return some.writeToMemory(zcu, buffer);
377 } else {
378 const byte_count = Type.usize.abiSize(zcu);
379 @memset(buffer[0..@intCast(byte_count)], 0); // null pointer
380 }
381 },
382 }
383}
384
385/// Write a Value's contents to `buffer`.
386///
387/// Both the start and the end of the provided buffer must be tight, since
388/// big-endian packed memory layouts start at the end of the buffer.
389///
390/// Supports arrays and vectors, for which the value is written in logical bit
391/// order, i.e. with the first element at bit offset 0.
392pub fn writeToPackedMemory(
393 val: Value,
394 zcu: *const Zcu,
395 buffer: []u8,
396 bit_offset: usize,
397) void {
398 const ip = &zcu.intern_pool;
399 const target = zcu.getTarget();
400 const endian = target.cpu.arch.endian();
401 const ty = val.typeOf(zcu);
402 if (val.isUndef(zcu)) {
403 const bit_size: usize = @intCast(ty.bitSize(zcu));
404 if (bit_size != 0) {
405 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian);
406 }
407 return;
408 }
409 switch (ty.zigTypeTag(zcu)) {
410 .void => {},
411 .bool => {
412 const byte_index = switch (endian) {
413 .little => bit_offset / 8,
414 .big => buffer.len - bit_offset / 8 - 1,
415 };
416 if (val.toBool()) {
417 buffer[byte_index] |= (@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
418 } else {
419 buffer[byte_index] &= ~(@as(u8, 1) << @as(u3, @intCast(bit_offset % 8)));
420 }
421 },
422 .@"enum" => {
423 const int_val = val.backingInt(zcu);
424 int_val.writeToPackedMemory(zcu, buffer, bit_offset);
425 },
426 .int => {
427 const bits = ty.intInfo(zcu).bits;
428 if (bits == 0 or buffer.len == 0) return;
429 switch (ip.indexToKey(val.toIntern()).int.storage) {
430 inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
431 .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
432 }
433 },
434 .float => switch (ty.floatBits(target)) {
435 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @bitCast(val.toFloat(f16, zcu)), endian),
436 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @bitCast(val.toFloat(f32, zcu)), endian),
437 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @bitCast(val.toFloat(f64, zcu)), endian),
438 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @bitCast(val.toFloat(f80, zcu)), endian),
439 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @bitCast(val.toFloat(f128, zcu)), endian),
440 else => unreachable,
441 },
442 .@"struct", .@"union" => {
443 assert(ty.containerLayout(zcu) == .@"packed");
444 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
445 int_val.writeToPackedMemory(zcu, buffer, bit_offset);
446 },
447 .array, .vector => {
448 const elem_bits: usize = @intCast(ty.childType(zcu).bitSize(zcu));
449 const len: usize = @intCast(ty.arrayLen(zcu));
450 var elem_bit_off: usize = bit_offset;
451 switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
452 .repeated_elem => |elem_val_ip| {
453 const elem_val: Value = .fromInterned(elem_val_ip);
454 for (0..len) |_| {
455 elem_val.writeToPackedMemory(zcu, buffer, elem_bit_off);
456 elem_bit_off += elem_bits;
457 }
458 },
459 .elems => |elems| for (elems[0..len]) |elem_val_ip| {
460 const elem_val: Value = .fromInterned(elem_val_ip);
461 elem_val.writeToPackedMemory(zcu, buffer, elem_bit_off);
462 elem_bit_off += elem_bits;
463 },
464 .bytes => |bytes| for (bytes.toSlice(len, ip)) |raw_byte| {
465 std.mem.writeVarPackedInt(buffer, elem_bit_off, elem_bits, raw_byte, endian);
466 elem_bit_off += elem_bits;
467 },
468 }
469 if (ty.sentinel(zcu)) |sentinel_val| {
470 sentinel_val.writeToPackedMemory(zcu, buffer, elem_bit_off);
471 }
472 },
473 else => unreachable,
474 }
475}
476
477/// Load a Value from the contents of `buffer`, where `ty` is any integer type.
478///
479/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
480/// the end of the value in memory.
481pub fn readIntFromMemory(
482 ty: Type,
483 pt: Zcu.PerThread,
484 buffer: []const u8,
485 arena: Allocator,
486) Allocator.Error!Value {
487 const zcu = pt.zcu;
488 const endian = zcu.getTarget().cpu.arch.endian();
489
490 const int = ty.intInfo(zcu);
491 const abi_size: usize = @intCast(ty.abiSize(zcu));
492 const exact_buf = buffer[0..abi_size];
493
494 if (abi_size <= 8) {
495 const shift: u6 = @intCast(64 - int.bits);
496 switch (int.signedness) {
497 .unsigned => {
498 const x = std.mem.readVarInt(u64, exact_buf, endian);
499 return pt.intValue(ty, (x << shift) >> shift);
500 },
501 .signed => {
502 const x = std.mem.readVarInt(i64, exact_buf, endian);
503 return pt.intValue(ty, (x << shift) >> shift);
504 },
505 }
506 } else {
507 const limb_count = std.math.big.int.calcTwosCompLimbCount(int.bits);
508 const limbs_buffer = try arena.alloc(std.math.big.Limb, limb_count);
509
510 var bigint: BigIntMutable = .init(limbs_buffer, 0);
511 bigint.readTwosComplement(exact_buf, int.bits, endian, int.signedness);
512 return pt.intValue_big(ty, bigint.toConst());
513 }
514}
515
516/// Load a Value from the contents of `buffer`.
517///
518/// Both the start and the end of the provided buffer must be tight, since
519/// big-endian packed memory layouts start at the end of the buffer.
520///
521/// Supports arrays and vectors, for which the value is read in logical bit
522/// order, i.e. with the first element at bit offset 0.
523pub fn readFromPackedMemory(
524 ty: Type,
525 pt: Zcu.PerThread,
526 buffer: []const u8,
527 bit_offset: usize,
528) Allocator.Error!Value {
529 const zcu = pt.zcu;
530 const gpa = zcu.comp.gpa;
531 const target = zcu.getTarget();
532 const endian = target.cpu.arch.endian();
533 switch (ty.zigTypeTag(zcu)) {
534 .void => return Value.void,
535 .bool => {
536 const byte = switch (endian) {
537 .big => buffer[buffer.len - bit_offset / 8 - 1],
538 .little => buffer[bit_offset / 8],
539 };
540 if (((byte >> @as(u3, @intCast(bit_offset % 8))) & 1) == 0) {
541 return Value.false;
542 } else {
543 return Value.true;
544 }
545 },
546 .int => {
547 if (buffer.len == 0) return pt.intValue(ty, 0);
548 if (ty.toIntern() == .u0_type) return pt.intValue(ty, 0);
549 const int_info = ty.intInfo(zcu);
550 const bits = int_info.bits;
551
552 // Fast path for integers <= u64
553 if (bits <= 64) switch (int_info.signedness) {
554 // Use different backing types for unsigned vs signed to avoid the need to go via
555 // a larger type like `i128`.
556 .unsigned => return pt.intValue(ty, std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned)),
557 .signed => return pt.intValue(ty, std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed)),
558 };
559
560 // Slow path, we have to construct a big-int
561 const abi_size: usize = @intCast(ty.abiSize(zcu));
562 const Limb = std.math.big.Limb;
563 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
564 const limbs_buffer = try gpa.alloc(Limb, limb_count);
565 defer gpa.free(limbs_buffer);
566
567 var bigint = BigIntMutable.init(limbs_buffer, 0);
568 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
569 return pt.intValue_big(ty, bigint.toConst());
570 },
571 .@"enum" => {
572 const int_ty = ty.backingIntType(zcu);
573 const int_val: Value = try .readFromPackedMemory(int_ty, pt, buffer, bit_offset);
574 return pt.getCoerced(int_val, ty);
575 },
576 .float => return Value.fromInterned(try pt.intern(.{ .float = .{
577 .ty = ty.toIntern(),
578 .storage = switch (ty.floatBits(target)) {
579 16 => .{ .f16 = @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian)) },
580 32 => .{ .f32 = @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian)) },
581 64 => .{ .f64 = @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian)) },
582 80 => .{ .f80 = @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian)) },
583 128 => .{ .f128 = @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian)) },
584 else => unreachable,
585 },
586 } })),
587 .@"struct", .@"union" => {
588 assert(ty.containerLayout(zcu) == .@"packed");
589 const int_val: Value = try .readFromPackedMemory(ty.backingIntType(zcu), pt, buffer, bit_offset);
590 return pt.bitpackValue(ty, int_val);
591 },
592 .array, .vector => {
593 const elem_ty = ty.childType(zcu);
594 const elem_bits: usize = @intCast(elem_ty.bitSize(zcu));
595 const elems_buf = try gpa.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu)));
596 defer gpa.free(elems_buf);
597 var elem_bit_off: usize = bit_offset;
598 for (elems_buf) |*elem| {
599 const elem_val = try readFromPackedMemory(elem_ty, pt, buffer, elem_bit_off);
600 elem.* = elem_val.toIntern();
601 elem_bit_off += elem_bits;
602 }
603 return pt.aggregateValue(ty, elems_buf);
604 },
605 else => unreachable,
606 }
607}
608
609/// Asserts that the value is a float or an integer.
610pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T {
611 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
612 .int => |int| switch (int.storage) {
613 .big_int => |big_int| big_int.toFloat(T, .nearest_even)[0],
614 inline .u64, .i64 => |x| @floatFromInt(x),
615 },
616 .float => |float| switch (float.storage) {
617 inline else => |x| @floatCast(x),
618 },
619 else => unreachable,
620 };
621}
622
623pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 {
624 var bigint_buf: BigIntSpace = undefined;
625 const bigint = val.toBigInt(&bigint_buf, zcu);
626 return bigint.clz(ty.intInfo(zcu).bits);
627}
628
629pub fn ctz(val: Value, ty: Type, zcu: *Zcu) u64 {
630 var bigint_buf: BigIntSpace = undefined;
631 const bigint = val.toBigInt(&bigint_buf, zcu);
632 return bigint.ctz(ty.intInfo(zcu).bits);
633}
634
635pub fn popCount(val: Value, ty: Type, zcu: *Zcu) u64 {
636 var bigint_buf: BigIntSpace = undefined;
637 const bigint = val.toBigInt(&bigint_buf, zcu);
638 return @intCast(bigint.popCount(ty.intInfo(zcu).bits));
639}
640
641/// Asserts the value is an integer and not undefined.
642/// Returns the number of bits the value requires to represent stored in twos complement form.
643pub fn intBitCountTwosComp(self: Value, zcu: *Zcu) usize {
644 var buffer: BigIntSpace = undefined;
645 const big_int = self.toBigInt(&buffer, zcu);
646 return big_int.bitCountTwosComp();
647}
648
649/// Converts an integer or a float to a float. May result in a loss of information.
650/// Caller can find out by equality checking the result against the operand.
651pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value {
652 const zcu = pt.zcu;
653 const target = zcu.getTarget();
654 if (val.isUndef(zcu)) return pt.undefValue(dest_ty);
655 return Value.fromInterned(try pt.intern(.{ .float = .{
656 .ty = dest_ty.toIntern(),
657 .storage = switch (dest_ty.floatBits(target)) {
658 16 => .{ .f16 = val.toFloat(f16, zcu) },
659 32 => .{ .f32 = val.toFloat(f32, zcu) },
660 64 => .{ .f64 = val.toFloat(f64, zcu) },
661 80 => .{ .f80 = val.toFloat(f80, zcu) },
662 128 => .{ .f128 = val.toFloat(f128, zcu) },
663 else => unreachable,
664 },
665 } }));
666}
667
668/// Asserts the value is comparable. Supports comparisons between heterogeneous types.
669pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *const Zcu) bool {
670 if (lhs.pointerNav(zcu)) |lhs_nav| {
671 if (rhs.pointerNav(zcu)) |rhs_nav| {
672 switch (op) {
673 .eq => return lhs_nav == rhs_nav,
674 .neq => return lhs_nav != rhs_nav,
675 else => {},
676 }
677 } else {
678 switch (op) {
679 .eq => return false,
680 .neq => return true,
681 else => {},
682 }
683 }
684 } else if (rhs.pointerNav(zcu)) |_| {
685 switch (op) {
686 .eq => return false,
687 .neq => return true,
688 else => {},
689 }
690 }
691 if (lhs.isNan(zcu) or rhs.isNan(zcu)) return op == .neq;
692 return order(lhs, rhs, zcu).compare(op);
693}
694
695pub fn order(lhs: Value, rhs: Value, zcu: *const Zcu) std.math.Order {
696 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {
697 const lhs_f128 = lhs.toFloat(f128, zcu);
698 const rhs_f128 = rhs.toFloat(f128, zcu);
699 return std.math.order(lhs_f128, rhs_f128);
700 }
701 var lhs_bigint_space: BigIntSpace = undefined;
702 var rhs_bigint_space: BigIntSpace = undefined;
703 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, zcu);
704 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu);
705 return lhs_bigint.order(rhs_bigint);
706}
707
708/// Asserts the values are comparable. Both operands have type `ty`.
709/// For vectors, returns true if comparison is true for ALL elements.
710pub fn compareAll(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, pt: Zcu.PerThread) !bool {
711 const zcu = pt.zcu;
712 if (ty.zigTypeTag(zcu) == .vector) {
713 const scalar_ty = ty.scalarType(zcu);
714 for (0..ty.vectorLen(zcu)) |i| {
715 const lhs_elem = try lhs.elemValue(pt, i);
716 const rhs_elem = try rhs.elemValue(pt, i);
717 if (!compareScalar(lhs_elem, op, rhs_elem, scalar_ty, zcu)) {
718 return false;
719 }
720 }
721 return true;
722 }
723 return compareScalar(lhs, op, rhs, ty, zcu);
724}
725
726/// Asserts the values are comparable. Both operands have type `ty`.
727pub fn compareScalar(
728 lhs: Value,
729 op: std.math.CompareOperator,
730 rhs: Value,
731 ty: Type,
732 zcu: *Zcu,
733) bool {
734 return switch (op) {
735 .eq => lhs.eql(rhs, ty, zcu),
736 .neq => !lhs.eql(rhs, ty, zcu),
737 else => compareHetero(lhs, op, rhs, zcu),
738 };
739}
740
741/// Asserts the value is comparable.
742/// For vectors, returns true if comparison is true for ALL elements.
743/// Returns `false` if the value or any vector element is undefined.
744///
745/// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
746pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, zcu: *Zcu) bool {
747 return switch (zcu.intern_pool.indexToKey(lhs.toIntern())) {
748 .float => |float| switch (float.storage) {
749 inline else => |x| std.math.compare(x, op, 0),
750 },
751 .aggregate => |aggregate| switch (aggregate.storage) {
752 .bytes => |bytes| for (bytes.toSlice(
753 lhs.typeOf(zcu).arrayLenIncludingSentinel(zcu),
754 &zcu.intern_pool,
755 )) |byte| {
756 if (!std.math.compare(byte, op, 0)) break false;
757 } else true,
758 .elems => |elems| for (elems) |elem| {
759 if (!Value.fromInterned(elem).compareAllWithZero(op, zcu)) break false;
760 } else true,
761 .repeated_elem => |elem| Value.fromInterned(elem).compareAllWithZero(op, zcu),
762 },
763 .undef => false,
764 else => order(lhs, .zero_comptime_int, zcu).compare(op),
765 };
766}
767
768pub fn eql(a: Value, b: Value, ty: Type, zcu: *Zcu) bool {
769 assert(a.typeOf(zcu).toIntern() == ty.toIntern());
770 assert(b.typeOf(zcu).toIntern() == ty.toIntern());
771 return a.toIntern() == b.toIntern();
772}
773
774pub fn canMutateComptimeVarState(val: Value, zcu: *Zcu) bool {
775 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
776 .error_union => |error_union| switch (error_union.val) {
777 .err_name => false,
778 .payload => |payload| Value.fromInterned(payload).canMutateComptimeVarState(zcu),
779 },
780 .ptr => |ptr| switch (ptr.base_addr) {
781 .nav => false, // The value of a Nav can never reference a comptime alloc.
782 .int => false,
783 .comptime_alloc => true, // A comptime alloc is either mutable or references comptime-mutable memory.
784 .comptime_field => true, // Comptime field pointers are comptime-mutable, albeit only to the "correct" value.
785 .eu_payload, .opt_payload => |base| Value.fromInterned(base).canMutateComptimeVarState(zcu),
786 .uav => |uav| Value.fromInterned(uav.val).canMutateComptimeVarState(zcu),
787 .arr_elem, .field => |base_index| Value.fromInterned(base_index.base).canMutateComptimeVarState(zcu),
788 },
789 .slice => |slice| return Value.fromInterned(slice.ptr).canMutateComptimeVarState(zcu),
790 .opt => |opt| switch (opt.val) {
791 .none => false,
792 else => |payload| Value.fromInterned(payload).canMutateComptimeVarState(zcu),
793 },
794 .aggregate => |aggregate| for (aggregate.storage.values()) |elem| {
795 if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) break true;
796 } else false,
797 .un => |un| Value.fromInterned(un.val).canMutateComptimeVarState(zcu),
798 else => false,
799 };
800}
801
802/// Gets the `Nav` referenced by this pointer. If the pointer does not point
803/// to a `Nav`, or if it points to some part of one (like a field or element),
804/// returns null.
805pub fn pointerNav(val: Value, zcu: *const Zcu) ?InternPool.Nav.Index {
806 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
807 // TODO: these 3 cases are weird; these aren't pointer values!
808 .@"extern" => |e| e.owner_nav,
809 .func => |func| func.owner_nav,
810 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
811 .nav => |nav| nav,
812 else => null,
813 } else null,
814 else => null,
815 };
816}
817
818pub const slice_ptr_index = 0;
819pub const slice_len_index = 1;
820
821pub fn sliceLen(val: Value, zcu: *Zcu) u64 {
822 return Value.fromInterned(zcu.intern_pool.sliceLen(val.toIntern())).toUnsignedInt(zcu);
823}
824pub fn slicePtr(val: Value, zcu: *Zcu) Value {
825 return Value.fromInterned(zcu.intern_pool.slicePtr(val.toIntern()));
826}
827
828/// Asserts the value is an aggregate, and returns the element value at the given index.
829pub fn elemValue(val: Value, pt: Zcu.PerThread, index: usize) Allocator.Error!Value {
830 const zcu = pt.zcu;
831 const ip = &zcu.intern_pool;
832 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
833 .undef => |ty| {
834 return Value.fromInterned(try pt.intern(.{ .undef = Type.fromInterned(ty).childType(zcu).toIntern() }));
835 },
836 .aggregate => |aggregate| {
837 const len = ip.aggregateTypeLen(aggregate.ty);
838 if (index < len) return Value.fromInterned(switch (aggregate.storage) {
839 .bytes => |bytes| try pt.intern(.{ .int = .{
840 .ty = .u8_type,
841 .storage = .{ .u64 = bytes.at(index, ip) },
842 } }),
843 .elems => |elems| elems[index],
844 .repeated_elem => |elem| elem,
845 });
846 assert(index == len);
847 return Type.fromInterned(aggregate.ty).sentinel(zcu).?;
848 },
849 else => unreachable,
850 }
851}
852
853pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value {
854 const zcu = pt.zcu;
855 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
856 .undef => |ty| Value.fromInterned(try pt.intern(.{
857 .undef = Type.fromInterned(ty).fieldType(index, zcu).toIntern(),
858 })),
859 .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) {
860 .bytes => |bytes| try pt.intern(.{ .int = .{
861 .ty = .u8_type,
862 .storage = .{ .u64 = bytes.at(index, &zcu.intern_pool) },
863 } }),
864 .elems => |elems| elems[index],
865 .repeated_elem => |elem| elem,
866 }),
867 .un => |un| {
868 switch (Type.fromInterned(un.ty).containerLayout(zcu)) {
869 .auto, .@"extern" => {}, // TODO assert the tag is correct
870 .@"packed" => unreachable,
871 }
872 return .fromInterned(un.val);
873 },
874 .bitpack => |bitpack| {
875 const ty: Type = .fromInterned(bitpack.ty);
876 assert(ty.containerLayout(zcu) == .@"packed");
877 const int_val: Value = .fromInterned(bitpack.backing_int_val);
878 assert(!int_val.isUndef(zcu));
879 const field_ty = ty.fieldType(index, zcu);
880 const field_bit_offset: u16 = switch (ty.zigTypeTag(zcu)) {
881 .@"union" => 0,
882 .@"struct" => off: {
883 var off: u16 = 0;
884 for (0..index) |preceding_field_index| {
885 off += @intCast(ty.fieldType(preceding_field_index, zcu).bitSize(zcu));
886 }
887 break :off off;
888 },
889 else => unreachable,
890 };
891 // Avoid hitting gpa for accesses to small packed structs
892 var bfa_buf: [128]u8 = undefined;
893 var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, zcu.comp.gpa);
894 const bfa = bfa_state.allocator();
895 const buf = try bfa.alloc(u8, @intCast((ty.bitSize(zcu) + 7) / 8));
896 defer bfa.free(buf);
897 @memset(buf, 0);
898 int_val.writeToPackedMemory(zcu, buf, 0);
899 return .readFromPackedMemory(field_ty, pt, buf, field_bit_offset);
900 },
901 else => unreachable,
902 };
903}
904
905pub fn unionTag(val: Value, zcu: *const Zcu) ?Value {
906 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
907 .undef, .enum_tag => val,
908 .un => |un| if (un.tag != .none) Value.fromInterned(un.tag) else return null,
909 else => unreachable,
910 };
911}
912
913pub fn unionPayload(val: Value, zcu: *const Zcu) Value {
914 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
915 .un => |un| Value.fromInterned(un.val),
916 else => unreachable,
917 };
918}
919
920pub fn isUndef(val: Value, zcu: *const Zcu) bool {
921 return zcu.intern_pool.isUndef(val.toIntern());
922}
923
924/// `val` must have a numeric or vector type.
925/// Returns whether `val` is undefined or contains any undefined elements.
926/// Returns the index of the first undefined element it encounters
927/// or `null` if no element is undefined.
928pub fn anyScalarIsUndef(val: Value, zcu: *const Zcu) bool {
929 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
930 .undef => return true,
931 .int, .float => return false,
932 .aggregate => |agg| {
933 assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector);
934 for (agg.storage.values()) |elem_val| {
935 if (Value.fromInterned(elem_val).isUndef(zcu)) return true;
936 }
937 return false;
938 },
939 else => unreachable,
940 }
941}
942
943/// `val` must have a numeric or vector type.
944/// Returns whether `val` contains any elements equal to zero.
945/// Asserts that `val` is not `undefined`, nor a vector containing any `undefined` elements.
946pub fn anyScalarIsZero(val: Value, zcu: *Zcu) bool {
947 assert(!val.anyScalarIsUndef(zcu));
948
949 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
950 .int, .float => return val.eqlScalarNum(.zero_comptime_int, zcu),
951 .aggregate => |agg| {
952 assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector);
953 switch (agg.storage) {
954 .bytes => |str| {
955 const len = Type.fromInterned(agg.ty).vectorLen(zcu);
956 const slice = str.toSlice(len, &zcu.intern_pool);
957 return std.mem.findScalar(u8, slice, 0) != null;
958 },
959 .elems => |elems| {
960 for (elems) |elem| {
961 if (Value.fromInterned(elem).isUndef(zcu)) return true;
962 }
963 return false;
964 },
965 .repeated_elem => |elem| return Value.fromInterned(elem).isUndef(zcu),
966 }
967 },
968 else => unreachable,
969 }
970}
971
972/// Asserts the value is not undefined and not unreachable.
973/// C pointers with an integer value of 0 are also considered null.
974pub fn isNull(val: Value, zcu: *Zcu) bool {
975 return switch (val.toIntern()) {
976 .undef => unreachable,
977 .unreachable_value => unreachable,
978 .null_value => true,
979 else => return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
980 .undef => unreachable,
981 .ptr => |ptr| switch (ptr.base_addr) {
982 .int => ptr.byte_offset == 0,
983 else => false,
984 },
985 .opt => |opt| opt.val == .none,
986 else => false,
987 },
988 };
989}
990
991/// Valid only for error (union) types. Asserts the value is not undefined and not unreachable.
992pub fn getErrorName(val: Value, zcu: *const Zcu) InternPool.OptionalNullTerminatedString {
993 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
994 .err => |err| err.name.toOptional(),
995 .error_union => |error_union| switch (error_union.val) {
996 .err_name => |err_name| err_name.toOptional(),
997 .payload => .none,
998 },
999 else => unreachable,
1000 };
1001}
1002
1003pub fn getErrorInt(val: Value, zcu: *Zcu) Zcu.ErrorInt {
1004 return if (getErrorName(val, zcu).unwrap()) |err_name|
1005 zcu.intern_pool.getErrorValueIfExists(err_name).?
1006 else
1007 0;
1008}
1009
1010/// Assumes the type is an error union. Returns true if and only if the value is
1011/// the error union payload, not an error.
1012pub fn errorUnionIsPayload(val: Value, zcu: *const Zcu) bool {
1013 return zcu.intern_pool.indexToKey(val.toIntern()).error_union.val == .payload;
1014}
1015
1016/// Value of the optional, null if optional has no payload.
1017pub fn optionalValue(val: Value, zcu: *const Zcu) ?Value {
1018 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
1019 .opt => |opt| switch (opt.val) {
1020 .none => null,
1021 else => |payload| Value.fromInterned(payload),
1022 },
1023 .ptr => val,
1024 else => unreachable,
1025 };
1026}
1027
1028/// Valid for all types. Asserts the value is not undefined.
1029pub fn isFloat(self: Value, zcu: *const Zcu) bool {
1030 return switch (self.toIntern()) {
1031 .undef => unreachable,
1032 else => switch (zcu.intern_pool.indexToKey(self.toIntern())) {
1033 .undef => unreachable,
1034 .float => true,
1035 else => false,
1036 },
1037 };
1038}
1039
1040fn calcLimbLenFloat(scalar: anytype) usize {
1041 if (scalar == 0) {
1042 return 1;
1043 }
1044
1045 const w_value = @abs(scalar);
1046 return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).int.bits) + 1;
1047}
1048
1049pub const OverflowArithmeticResult = struct {
1050 overflow_bit: Value,
1051 wrapped_result: Value,
1052};
1053
1054pub const OverflowArithmeticResultInt = struct {
1055 overflow: bool,
1056 wrapped_result: Value,
1057};
1058
1059/// Supports both floats and ints; handles undefined.
1060pub fn numberMax(lhs: Value, rhs: Value, zcu: *Zcu) Value {
1061 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef;
1062 if (lhs.isNan(zcu)) return rhs;
1063 if (rhs.isNan(zcu)) return lhs;
1064 if (compareHetero(lhs, .gt, rhs, zcu)) {
1065 return lhs;
1066 } else {
1067 return rhs;
1068 }
1069}
1070
1071/// Supports both floats and ints; handles undefined.
1072pub fn numberMin(lhs: Value, rhs: Value, zcu: *Zcu) Value {
1073 if (lhs.isUndef(zcu) or rhs.isUndef(zcu)) return undef;
1074 if (lhs.isNan(zcu)) return rhs;
1075 if (rhs.isNan(zcu)) return lhs;
1076 if (compareHetero(lhs, .lt, rhs, zcu)) {
1077 return lhs;
1078 } else {
1079 return rhs;
1080 }
1081}
1082
1083/// Returns true if the value is a floating point type and is NaN. Returns false otherwise.
1084pub fn isNan(val: Value, zcu: *const Zcu) bool {
1085 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
1086 .float => |float| switch (float.storage) {
1087 inline else => |x| std.math.isNan(x),
1088 },
1089 else => false,
1090 };
1091}
1092
1093/// Returns true if the value is a floating point type and is infinite. Returns false otherwise.
1094pub fn isInf(val: Value, zcu: *const Zcu) bool {
1095 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
1096 .float => |float| switch (float.storage) {
1097 inline else => |x| std.math.isInf(x),
1098 },
1099 else => false,
1100 };
1101}
1102
1103/// Returns true if the value is a floating point type and is negative infinite. Returns false otherwise.
1104pub fn isNegativeInf(val: Value, zcu: *const Zcu) bool {
1105 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
1106 .float => |float| switch (float.storage) {
1107 inline else => |x| std.math.isNegativeInf(x),
1108 },
1109 else => false,
1110 };
1111}
1112
1113pub fn sqrt(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1114 if (float_type.zigTypeTag(pt.zcu) == .vector) {
1115 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(pt.zcu));
1116 const scalar_ty = float_type.scalarType(pt.zcu);
1117 for (result_data, 0..) |*scalar, i| {
1118 const elem_val = try val.elemValue(pt, i);
1119 scalar.* = (try sqrtScalar(elem_val, scalar_ty, pt)).toIntern();
1120 }
1121 return pt.aggregateValue(float_type, result_data);
1122 }
1123 return sqrtScalar(val, float_type, pt);
1124}
1125
1126pub fn sqrtScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1127 const zcu = pt.zcu;
1128 const target = zcu.getTarget();
1129 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1130 16 => .{ .f16 = @sqrt(val.toFloat(f16, zcu)) },
1131 32 => .{ .f32 = @sqrt(val.toFloat(f32, zcu)) },
1132 64 => .{ .f64 = @sqrt(val.toFloat(f64, zcu)) },
1133 80 => .{ .f80 = @sqrt(val.toFloat(f80, zcu)) },
1134 128 => .{ .f128 = @sqrt(val.toFloat(f128, zcu)) },
1135 else => unreachable,
1136 };
1137 return Value.fromInterned(try pt.intern(.{ .float = .{
1138 .ty = float_type.toIntern(),
1139 .storage = storage,
1140 } }));
1141}
1142
1143pub fn sin(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1144 const zcu = pt.zcu;
1145 if (float_type.zigTypeTag(zcu) == .vector) {
1146 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1147 const scalar_ty = float_type.scalarType(zcu);
1148 for (result_data, 0..) |*scalar, i| {
1149 const elem_val = try val.elemValue(pt, i);
1150 scalar.* = (try sinScalar(elem_val, scalar_ty, pt)).toIntern();
1151 }
1152 return pt.aggregateValue(float_type, result_data);
1153 }
1154 return sinScalar(val, float_type, pt);
1155}
1156
1157pub fn sinScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1158 const zcu = pt.zcu;
1159 const target = zcu.getTarget();
1160 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1161 16 => .{ .f16 = @sin(val.toFloat(f16, zcu)) },
1162 32 => .{ .f32 = @sin(val.toFloat(f32, zcu)) },
1163 64 => .{ .f64 = @sin(val.toFloat(f64, zcu)) },
1164 80 => .{ .f80 = @sin(val.toFloat(f80, zcu)) },
1165 128 => .{ .f128 = @sin(val.toFloat(f128, zcu)) },
1166 else => unreachable,
1167 };
1168 return Value.fromInterned(try pt.intern(.{ .float = .{
1169 .ty = float_type.toIntern(),
1170 .storage = storage,
1171 } }));
1172}
1173
1174pub fn cos(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1175 const zcu = pt.zcu;
1176 if (float_type.zigTypeTag(zcu) == .vector) {
1177 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1178 const scalar_ty = float_type.scalarType(zcu);
1179 for (result_data, 0..) |*scalar, i| {
1180 const elem_val = try val.elemValue(pt, i);
1181 scalar.* = (try cosScalar(elem_val, scalar_ty, pt)).toIntern();
1182 }
1183 return pt.aggregateValue(float_type, result_data);
1184 }
1185 return cosScalar(val, float_type, pt);
1186}
1187
1188pub fn cosScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1189 const zcu = pt.zcu;
1190 const target = zcu.getTarget();
1191 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1192 16 => .{ .f16 = @cos(val.toFloat(f16, zcu)) },
1193 32 => .{ .f32 = @cos(val.toFloat(f32, zcu)) },
1194 64 => .{ .f64 = @cos(val.toFloat(f64, zcu)) },
1195 80 => .{ .f80 = @cos(val.toFloat(f80, zcu)) },
1196 128 => .{ .f128 = @cos(val.toFloat(f128, zcu)) },
1197 else => unreachable,
1198 };
1199 return Value.fromInterned(try pt.intern(.{ .float = .{
1200 .ty = float_type.toIntern(),
1201 .storage = storage,
1202 } }));
1203}
1204
1205pub fn tan(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1206 const zcu = pt.zcu;
1207 if (float_type.zigTypeTag(zcu) == .vector) {
1208 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1209 const scalar_ty = float_type.scalarType(zcu);
1210 for (result_data, 0..) |*scalar, i| {
1211 const elem_val = try val.elemValue(pt, i);
1212 scalar.* = (try tanScalar(elem_val, scalar_ty, pt)).toIntern();
1213 }
1214 return pt.aggregateValue(float_type, result_data);
1215 }
1216 return tanScalar(val, float_type, pt);
1217}
1218
1219pub fn tanScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1220 const zcu = pt.zcu;
1221 const target = zcu.getTarget();
1222 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1223 16 => .{ .f16 = @tan(val.toFloat(f16, zcu)) },
1224 32 => .{ .f32 = @tan(val.toFloat(f32, zcu)) },
1225 64 => .{ .f64 = @tan(val.toFloat(f64, zcu)) },
1226 80 => .{ .f80 = @tan(val.toFloat(f80, zcu)) },
1227 128 => .{ .f128 = @tan(val.toFloat(f128, zcu)) },
1228 else => unreachable,
1229 };
1230 return Value.fromInterned(try pt.intern(.{ .float = .{
1231 .ty = float_type.toIntern(),
1232 .storage = storage,
1233 } }));
1234}
1235
1236pub fn exp(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1237 const zcu = pt.zcu;
1238 if (float_type.zigTypeTag(zcu) == .vector) {
1239 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1240 const scalar_ty = float_type.scalarType(zcu);
1241 for (result_data, 0..) |*scalar, i| {
1242 const elem_val = try val.elemValue(pt, i);
1243 scalar.* = (try expScalar(elem_val, scalar_ty, pt)).toIntern();
1244 }
1245 return pt.aggregateValue(float_type, result_data);
1246 }
1247 return expScalar(val, float_type, pt);
1248}
1249
1250pub fn expScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1251 const zcu = pt.zcu;
1252 const target = zcu.getTarget();
1253 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1254 16 => .{ .f16 = @exp(val.toFloat(f16, zcu)) },
1255 32 => .{ .f32 = @exp(val.toFloat(f32, zcu)) },
1256 64 => .{ .f64 = @exp(val.toFloat(f64, zcu)) },
1257 80 => .{ .f80 = @exp(val.toFloat(f80, zcu)) },
1258 128 => .{ .f128 = @exp(val.toFloat(f128, zcu)) },
1259 else => unreachable,
1260 };
1261 return Value.fromInterned(try pt.intern(.{ .float = .{
1262 .ty = float_type.toIntern(),
1263 .storage = storage,
1264 } }));
1265}
1266
1267pub fn exp2(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1268 const zcu = pt.zcu;
1269 if (float_type.zigTypeTag(zcu) == .vector) {
1270 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1271 const scalar_ty = float_type.scalarType(zcu);
1272 for (result_data, 0..) |*scalar, i| {
1273 const elem_val = try val.elemValue(pt, i);
1274 scalar.* = (try exp2Scalar(elem_val, scalar_ty, pt)).toIntern();
1275 }
1276 return pt.aggregateValue(float_type, result_data);
1277 }
1278 return exp2Scalar(val, float_type, pt);
1279}
1280
1281pub fn exp2Scalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1282 const zcu = pt.zcu;
1283 const target = zcu.getTarget();
1284 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1285 16 => .{ .f16 = @exp2(val.toFloat(f16, zcu)) },
1286 32 => .{ .f32 = @exp2(val.toFloat(f32, zcu)) },
1287 64 => .{ .f64 = @exp2(val.toFloat(f64, zcu)) },
1288 80 => .{ .f80 = @exp2(val.toFloat(f80, zcu)) },
1289 128 => .{ .f128 = @exp2(val.toFloat(f128, zcu)) },
1290 else => unreachable,
1291 };
1292 return Value.fromInterned(try pt.intern(.{ .float = .{
1293 .ty = float_type.toIntern(),
1294 .storage = storage,
1295 } }));
1296}
1297
1298pub fn log(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1299 const zcu = pt.zcu;
1300 if (float_type.zigTypeTag(zcu) == .vector) {
1301 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1302 const scalar_ty = float_type.scalarType(zcu);
1303 for (result_data, 0..) |*scalar, i| {
1304 const elem_val = try val.elemValue(pt, i);
1305 scalar.* = (try logScalar(elem_val, scalar_ty, pt)).toIntern();
1306 }
1307 return pt.aggregateValue(float_type, result_data);
1308 }
1309 return logScalar(val, float_type, pt);
1310}
1311
1312pub fn logScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1313 const zcu = pt.zcu;
1314 const target = zcu.getTarget();
1315 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1316 16 => .{ .f16 = @log(val.toFloat(f16, zcu)) },
1317 32 => .{ .f32 = @log(val.toFloat(f32, zcu)) },
1318 64 => .{ .f64 = @log(val.toFloat(f64, zcu)) },
1319 80 => .{ .f80 = @log(val.toFloat(f80, zcu)) },
1320 128 => .{ .f128 = @log(val.toFloat(f128, zcu)) },
1321 else => unreachable,
1322 };
1323 return Value.fromInterned(try pt.intern(.{ .float = .{
1324 .ty = float_type.toIntern(),
1325 .storage = storage,
1326 } }));
1327}
1328
1329pub fn log2(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1330 const zcu = pt.zcu;
1331 if (float_type.zigTypeTag(zcu) == .vector) {
1332 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1333 const scalar_ty = float_type.scalarType(zcu);
1334 for (result_data, 0..) |*scalar, i| {
1335 const elem_val = try val.elemValue(pt, i);
1336 scalar.* = (try log2Scalar(elem_val, scalar_ty, pt)).toIntern();
1337 }
1338 return pt.aggregateValue(float_type, result_data);
1339 }
1340 return log2Scalar(val, float_type, pt);
1341}
1342
1343pub fn log2Scalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1344 const zcu = pt.zcu;
1345 const target = zcu.getTarget();
1346 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1347 16 => .{ .f16 = @log2(val.toFloat(f16, zcu)) },
1348 32 => .{ .f32 = @log2(val.toFloat(f32, zcu)) },
1349 64 => .{ .f64 = @log2(val.toFloat(f64, zcu)) },
1350 80 => .{ .f80 = @log2(val.toFloat(f80, zcu)) },
1351 128 => .{ .f128 = @log2(val.toFloat(f128, zcu)) },
1352 else => unreachable,
1353 };
1354 return Value.fromInterned(try pt.intern(.{ .float = .{
1355 .ty = float_type.toIntern(),
1356 .storage = storage,
1357 } }));
1358}
1359
1360pub fn log10(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1361 const zcu = pt.zcu;
1362 if (float_type.zigTypeTag(zcu) == .vector) {
1363 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1364 const scalar_ty = float_type.scalarType(zcu);
1365 for (result_data, 0..) |*scalar, i| {
1366 const elem_val = try val.elemValue(pt, i);
1367 scalar.* = (try log10Scalar(elem_val, scalar_ty, pt)).toIntern();
1368 }
1369 return pt.aggregateValue(float_type, result_data);
1370 }
1371 return log10Scalar(val, float_type, pt);
1372}
1373
1374pub fn log10Scalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1375 const zcu = pt.zcu;
1376 const target = zcu.getTarget();
1377 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1378 16 => .{ .f16 = @log10(val.toFloat(f16, zcu)) },
1379 32 => .{ .f32 = @log10(val.toFloat(f32, zcu)) },
1380 64 => .{ .f64 = @log10(val.toFloat(f64, zcu)) },
1381 80 => .{ .f80 = @log10(val.toFloat(f80, zcu)) },
1382 128 => .{ .f128 = @log10(val.toFloat(f128, zcu)) },
1383 else => unreachable,
1384 };
1385 return Value.fromInterned(try pt.intern(.{ .float = .{
1386 .ty = float_type.toIntern(),
1387 .storage = storage,
1388 } }));
1389}
1390
1391pub fn abs(val: Value, ty: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1392 const zcu = pt.zcu;
1393 if (ty.zigTypeTag(zcu) == .vector) {
1394 const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(zcu));
1395 const scalar_ty = ty.scalarType(zcu);
1396 for (result_data, 0..) |*scalar, i| {
1397 const elem_val = try val.elemValue(pt, i);
1398 scalar.* = (try absScalar(elem_val, scalar_ty, pt, arena)).toIntern();
1399 }
1400 return pt.aggregateValue(ty, result_data);
1401 }
1402 return absScalar(val, ty, pt, arena);
1403}
1404
1405pub fn absScalar(val: Value, ty: Type, pt: Zcu.PerThread, arena: Allocator) Allocator.Error!Value {
1406 const zcu = pt.zcu;
1407 switch (ty.zigTypeTag(zcu)) {
1408 .int => {
1409 var buffer: Value.BigIntSpace = undefined;
1410 var operand_bigint = try val.toBigInt(&buffer, zcu).toManaged(arena);
1411 operand_bigint.abs();
1412
1413 return pt.intValue_big(try ty.toUnsigned(pt), operand_bigint.toConst());
1414 },
1415 .comptime_int => {
1416 var buffer: Value.BigIntSpace = undefined;
1417 var operand_bigint = try val.toBigInt(&buffer, zcu).toManaged(arena);
1418 operand_bigint.abs();
1419
1420 return pt.intValue_big(ty, operand_bigint.toConst());
1421 },
1422 .comptime_float, .float => {
1423 const target = zcu.getTarget();
1424 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) {
1425 16 => .{ .f16 = @abs(val.toFloat(f16, zcu)) },
1426 32 => .{ .f32 = @abs(val.toFloat(f32, zcu)) },
1427 64 => .{ .f64 = @abs(val.toFloat(f64, zcu)) },
1428 80 => .{ .f80 = @abs(val.toFloat(f80, zcu)) },
1429 128 => .{ .f128 = @abs(val.toFloat(f128, zcu)) },
1430 else => unreachable,
1431 };
1432 return Value.fromInterned(try pt.intern(.{ .float = .{
1433 .ty = ty.toIntern(),
1434 .storage = storage,
1435 } }));
1436 },
1437 else => unreachable,
1438 }
1439}
1440
1441pub fn floor(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1442 const zcu = pt.zcu;
1443 if (float_type.zigTypeTag(zcu) == .vector) {
1444 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1445 const scalar_ty = float_type.scalarType(zcu);
1446 for (result_data, 0..) |*scalar, i| {
1447 const elem_val = try val.elemValue(pt, i);
1448 scalar.* = (try floorScalar(elem_val, scalar_ty, pt)).toIntern();
1449 }
1450 return pt.aggregateValue(float_type, result_data);
1451 }
1452 return floorScalar(val, float_type, pt);
1453}
1454
1455pub fn floorScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1456 const zcu = pt.zcu;
1457 const target = zcu.getTarget();
1458 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1459 16 => .{ .f16 = @floor(val.toFloat(f16, zcu)) },
1460 32 => .{ .f32 = @floor(val.toFloat(f32, zcu)) },
1461 64 => .{ .f64 = @floor(val.toFloat(f64, zcu)) },
1462 80 => .{ .f80 = @floor(val.toFloat(f80, zcu)) },
1463 128 => .{ .f128 = @floor(val.toFloat(f128, zcu)) },
1464 else => unreachable,
1465 };
1466 return Value.fromInterned(try pt.intern(.{ .float = .{
1467 .ty = float_type.toIntern(),
1468 .storage = storage,
1469 } }));
1470}
1471
1472pub fn ceil(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1473 const zcu = pt.zcu;
1474 if (float_type.zigTypeTag(zcu) == .vector) {
1475 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1476 const scalar_ty = float_type.scalarType(zcu);
1477 for (result_data, 0..) |*scalar, i| {
1478 const elem_val = try val.elemValue(pt, i);
1479 scalar.* = (try ceilScalar(elem_val, scalar_ty, pt)).toIntern();
1480 }
1481 return pt.aggregateValue(float_type, result_data);
1482 }
1483 return ceilScalar(val, float_type, pt);
1484}
1485
1486pub fn ceilScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1487 const zcu = pt.zcu;
1488 const target = zcu.getTarget();
1489 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1490 16 => .{ .f16 = @ceil(val.toFloat(f16, zcu)) },
1491 32 => .{ .f32 = @ceil(val.toFloat(f32, zcu)) },
1492 64 => .{ .f64 = @ceil(val.toFloat(f64, zcu)) },
1493 80 => .{ .f80 = @ceil(val.toFloat(f80, zcu)) },
1494 128 => .{ .f128 = @ceil(val.toFloat(f128, zcu)) },
1495 else => unreachable,
1496 };
1497 return Value.fromInterned(try pt.intern(.{ .float = .{
1498 .ty = float_type.toIntern(),
1499 .storage = storage,
1500 } }));
1501}
1502
1503pub fn round(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1504 const zcu = pt.zcu;
1505 if (float_type.zigTypeTag(zcu) == .vector) {
1506 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1507 const scalar_ty = float_type.scalarType(zcu);
1508 for (result_data, 0..) |*scalar, i| {
1509 const elem_val = try val.elemValue(pt, i);
1510 scalar.* = (try roundScalar(elem_val, scalar_ty, pt)).toIntern();
1511 }
1512 return pt.aggregateValue(float_type, result_data);
1513 }
1514 return roundScalar(val, float_type, pt);
1515}
1516
1517pub fn roundScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1518 const zcu = pt.zcu;
1519 const target = zcu.getTarget();
1520 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1521 16 => .{ .f16 = @round(val.toFloat(f16, zcu)) },
1522 32 => .{ .f32 = @round(val.toFloat(f32, zcu)) },
1523 64 => .{ .f64 = @round(val.toFloat(f64, zcu)) },
1524 80 => .{ .f80 = @round(val.toFloat(f80, zcu)) },
1525 128 => .{ .f128 = @round(val.toFloat(f128, zcu)) },
1526 else => unreachable,
1527 };
1528 return Value.fromInterned(try pt.intern(.{ .float = .{
1529 .ty = float_type.toIntern(),
1530 .storage = storage,
1531 } }));
1532}
1533
1534pub fn trunc(val: Value, float_type: Type, arena: Allocator, pt: Zcu.PerThread) !Value {
1535 const zcu = pt.zcu;
1536 if (float_type.zigTypeTag(zcu) == .vector) {
1537 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1538 const scalar_ty = float_type.scalarType(zcu);
1539 for (result_data, 0..) |*scalar, i| {
1540 const elem_val = try val.elemValue(pt, i);
1541 scalar.* = (try truncScalar(elem_val, scalar_ty, pt)).toIntern();
1542 }
1543 return pt.aggregateValue(float_type, result_data);
1544 }
1545 return truncScalar(val, float_type, pt);
1546}
1547
1548pub fn truncScalar(val: Value, float_type: Type, pt: Zcu.PerThread) Allocator.Error!Value {
1549 const zcu = pt.zcu;
1550 const target = zcu.getTarget();
1551 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1552 16 => .{ .f16 = @trunc(val.toFloat(f16, zcu)) },
1553 32 => .{ .f32 = @trunc(val.toFloat(f32, zcu)) },
1554 64 => .{ .f64 = @trunc(val.toFloat(f64, zcu)) },
1555 80 => .{ .f80 = @trunc(val.toFloat(f80, zcu)) },
1556 128 => .{ .f128 = @trunc(val.toFloat(f128, zcu)) },
1557 else => unreachable,
1558 };
1559 return Value.fromInterned(try pt.intern(.{ .float = .{
1560 .ty = float_type.toIntern(),
1561 .storage = storage,
1562 } }));
1563}
1564
1565pub fn mulAdd(
1566 float_type: Type,
1567 mulend1: Value,
1568 mulend2: Value,
1569 addend: Value,
1570 arena: Allocator,
1571 pt: Zcu.PerThread,
1572) !Value {
1573 const zcu = pt.zcu;
1574 if (float_type.zigTypeTag(zcu) == .vector) {
1575 const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(zcu));
1576 const scalar_ty = float_type.scalarType(zcu);
1577 for (result_data, 0..) |*scalar, i| {
1578 const mulend1_elem = try mulend1.elemValue(pt, i);
1579 const mulend2_elem = try mulend2.elemValue(pt, i);
1580 const addend_elem = try addend.elemValue(pt, i);
1581 scalar.* = (try mulAddScalar(scalar_ty, mulend1_elem, mulend2_elem, addend_elem, pt)).toIntern();
1582 }
1583 return pt.aggregateValue(float_type, result_data);
1584 }
1585 return mulAddScalar(float_type, mulend1, mulend2, addend, pt);
1586}
1587
1588pub fn mulAddScalar(
1589 float_type: Type,
1590 mulend1: Value,
1591 mulend2: Value,
1592 addend: Value,
1593 pt: Zcu.PerThread,
1594) Allocator.Error!Value {
1595 const zcu = pt.zcu;
1596 const target = zcu.getTarget();
1597 const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) {
1598 16 => .{ .f16 = @mulAdd(f16, mulend1.toFloat(f16, zcu), mulend2.toFloat(f16, zcu), addend.toFloat(f16, zcu)) },
1599 32 => .{ .f32 = @mulAdd(f32, mulend1.toFloat(f32, zcu), mulend2.toFloat(f32, zcu), addend.toFloat(f32, zcu)) },
1600 64 => .{ .f64 = @mulAdd(f64, mulend1.toFloat(f64, zcu), mulend2.toFloat(f64, zcu), addend.toFloat(f64, zcu)) },
1601 80 => .{ .f80 = @mulAdd(f80, mulend1.toFloat(f80, zcu), mulend2.toFloat(f80, zcu), addend.toFloat(f80, zcu)) },
1602 128 => .{ .f128 = @mulAdd(f128, mulend1.toFloat(f128, zcu), mulend2.toFloat(f128, zcu), addend.toFloat(f128, zcu)) },
1603 else => unreachable,
1604 };
1605 return Value.fromInterned(try pt.intern(.{ .float = .{
1606 .ty = float_type.toIntern(),
1607 .storage = storage,
1608 } }));
1609}
1610
1611/// If the value is represented in-memory as a series of bytes that all
1612/// have the same value, return that byte value, otherwise null.
1613pub fn hasRepeatedByteRepr(val: Value, zcu: *const Zcu) !?u8 {
1614 const ty = val.typeOf(zcu);
1615 const abi_size = std.math.cast(usize, ty.abiSize(zcu)) orelse return null;
1616 assert(abi_size >= 1);
1617 const byte_buffer = try zcu.gpa.alloc(u8, abi_size);
1618 defer zcu.gpa.free(byte_buffer);
1619
1620 writeToMemory(val, zcu, byte_buffer) catch |err| switch (err) {
1621 error.OutOfMemory => |e| return e,
1622 error.ReinterpretDeclRef => return null,
1623 // TODO: The writeToMemory function was originally created for the purpose
1624 // of comptime pointer casting. However, it is now additionally being used
1625 // for checking the actual memory layout that will be generated by machine
1626 // code late in compilation. So, this error handling is too aggressive and
1627 // causes some false negatives, causing less-than-ideal code generation.
1628 error.IllDefinedMemoryLayout => return null,
1629 };
1630 const first_byte = byte_buffer[0];
1631 for (byte_buffer[1..]) |byte| {
1632 if (byte != first_byte) return null;
1633 }
1634 return first_byte;
1635}
1636
1637pub fn typeOf(val: Value, zcu: *const Zcu) Type {
1638 return Type.fromInterned(zcu.intern_pool.typeOf(val.toIntern()));
1639}
1640
1641/// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value.
1642/// If `val` is not undef, the bounds are both `val`.
1643/// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type.
1644/// If `val` is undef and is a `comptime_int`, returns null.
1645pub fn intValueBounds(val: Value, pt: Zcu.PerThread) !?[2]Value {
1646 if (!val.isUndef(pt.zcu)) return .{ val, val };
1647 const ty = pt.zcu.intern_pool.typeOf(val.toIntern());
1648 if (ty == .comptime_int_type) return null;
1649 return .{
1650 try Type.fromInterned(ty).minInt(pt, Type.fromInterned(ty)),
1651 try Type.fromInterned(ty).maxInt(pt, Type.fromInterned(ty)),
1652 };
1653}
1654
1655pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
1656
1657pub const undef: Value = .{ .ip_index = .undef };
1658pub const undef_bool: Value = .{ .ip_index = .undef_bool };
1659pub const undef_usize: Value = .{ .ip_index = .undef_usize };
1660pub const undef_u1: Value = .{ .ip_index = .undef_u1 };
1661pub const zero_comptime_int: Value = .{ .ip_index = .zero };
1662pub const zero_usize: Value = .{ .ip_index = .zero_usize };
1663pub const zero_u1: Value = .{ .ip_index = .zero_u1 };
1664pub const zero_u8: Value = .{ .ip_index = .zero_u8 };
1665pub const one_comptime_int: Value = .{ .ip_index = .one };
1666pub const one_usize: Value = .{ .ip_index = .one_usize };
1667pub const one_u1: Value = .{ .ip_index = .one_u1 };
1668pub const one_u8: Value = .{ .ip_index = .one_u8 };
1669pub const four_u8: Value = .{ .ip_index = .four_u8 };
1670pub const negative_one_comptime_int: Value = .{ .ip_index = .negative_one };
1671pub const @"void": Value = .{ .ip_index = .void_value };
1672pub const @"unreachable": Value = .{ .ip_index = .unreachable_value };
1673pub const @"null": Value = .{ .ip_index = .null_value };
1674pub const @"true": Value = .{ .ip_index = .bool_true };
1675pub const @"false": Value = .{ .ip_index = .bool_false };
1676pub const empty_tuple: Value = .{ .ip_index = .empty_tuple };
1677
1678pub fn makeBool(x: bool) Value {
1679 return if (x) .true else .false;
1680}
1681
1682/// `parent_ptr` must be a single-pointer or C pointer to some optional.
1683///
1684/// Returns a pointer to the payload of the optional.
1685pub fn ptrOptPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
1686 const zcu = pt.zcu;
1687 const parent_ptr_ty = parent_ptr.typeOf(zcu);
1688 const opt_ty = parent_ptr_ty.childType(zcu);
1689 const ptr_size = parent_ptr_ty.ptrSize(zcu);
1690
1691 assert(ptr_size == .one or ptr_size == .c);
1692 assert(opt_ty.zigTypeTag(zcu) == .optional);
1693
1694 const result_ty = try pt.ptrType(info: {
1695 var new = parent_ptr_ty.ptrInfo(zcu);
1696 // We can correctly preserve alignment `.none`, since an optional has the same
1697 // natural alignment as its child type.
1698 new.child = opt_ty.childType(zcu).toIntern();
1699 break :info new;
1700 });
1701
1702 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
1703
1704 if (opt_ty.isPtrLikeOptional(zcu)) {
1705 // Just reinterpret the pointer, since the layout is well-defined
1706 return pt.getCoerced(parent_ptr, result_ty);
1707 }
1708
1709 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, opt_ty, pt);
1710 return .fromInterned(try pt.intern(.{ .ptr = .{
1711 .ty = result_ty.toIntern(),
1712 .base_addr = .{ .opt_payload = base_ptr.toIntern() },
1713 .byte_offset = 0,
1714 } }));
1715}
1716
1717/// `parent_ptr` must be a single-pointer to some error union.
1718/// Returns a pointer to the payload of the error union.
1719pub fn ptrEuPayload(parent_ptr: Value, pt: Zcu.PerThread) !Value {
1720 const zcu = pt.zcu;
1721 const parent_ptr_ty = parent_ptr.typeOf(zcu);
1722 const eu_ty = parent_ptr_ty.childType(zcu);
1723
1724 assert(parent_ptr_ty.ptrSize(zcu) == .one);
1725 assert(eu_ty.zigTypeTag(zcu) == .error_union);
1726
1727 const result_ty = try pt.ptrType(info: {
1728 var new = parent_ptr_ty.ptrInfo(zcu);
1729 // We can correctly preserve alignment `.none`, since an error union has a
1730 // natural alignment greater than or equal to that of its payload type.
1731 new.child = eu_ty.errorUnionPayload(zcu).toIntern();
1732 break :info new;
1733 });
1734
1735 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
1736
1737 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, eu_ty, pt);
1738 return .fromInterned(try pt.intern(.{ .ptr = .{
1739 .ty = result_ty.toIntern(),
1740 .base_addr = .{ .eu_payload = base_ptr.toIntern() },
1741 .byte_offset = 0,
1742 } }));
1743}
1744
1745/// `parent_ptr` must be a single-item pointer or C pointer to a struct, union, or slice.
1746///
1747/// Returns a pointer to the aggregate field at the specified index.
1748///
1749/// For slices, uses `slice_ptr_index` and `slice_len_index`.
1750///
1751/// Asserts that the layout of the aggregate type is resolved.
1752pub fn ptrField(parent_ptr: Value, field_idx: u32, pt: Zcu.PerThread) !Value {
1753 const zcu = pt.zcu;
1754 const parent_ptr_ty = parent_ptr.typeOf(zcu);
1755 const aggregate_ty = parent_ptr_ty.childType(zcu);
1756 aggregate_ty.assertHasLayout(zcu);
1757
1758 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
1759 assert(parent_ptr_info.flags.size == .one or parent_ptr_info.flags.size == .c);
1760
1761 const field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_idx, pt);
1762
1763 switch (aggregate_ty.zigTypeTag(zcu)) {
1764 .pointer => assert(aggregate_ty.isSlice(zcu)),
1765 .@"struct" => switch (aggregate_ty.containerLayout(zcu)) {
1766 .auto => {},
1767 .@"extern" => return parent_ptr.getOffsetPtr(
1768 aggregate_ty.structFieldOffset(field_idx, zcu),
1769 field_ptr_ty,
1770 pt,
1771 ),
1772 .@"packed" => return pt.getCoerced(parent_ptr, field_ptr_ty),
1773 },
1774 .@"union" => switch (aggregate_ty.containerLayout(zcu)) {
1775 .auto => {},
1776 .@"packed", .@"extern" => return pt.getCoerced(parent_ptr, field_ptr_ty),
1777 },
1778 else => unreachable,
1779 }
1780
1781 // If we get here, we need to use the `.field` comptime pointer representation, because the
1782 // aggregate does not have a well-defined layout.
1783
1784 if (parent_ptr.isUndef(zcu)) return pt.undefValue(field_ptr_ty);
1785
1786 const base_ptr = try parent_ptr.canonicalizeBasePtr(.one, aggregate_ty, pt);
1787 return .fromInterned(try pt.intern(.{ .ptr = .{
1788 .ty = field_ptr_ty.toIntern(),
1789 .base_addr = .{ .field = .{
1790 .base = base_ptr.toIntern(),
1791 .index = field_idx,
1792 } },
1793 .byte_offset = 0,
1794 } }));
1795}
1796
1797/// `orig_parent_ptr` must be either a single-pointer to an array, a slice, a many-item pointer, or a C pointer.
1798/// Returns a pointer to the element at the specified index.
1799/// Asserts that the layout of the pointer element type is resolved.
1800pub fn ptrElem(orig_parent_ptr: Value, field_idx: u64, pt: Zcu.PerThread) !Value {
1801 const zcu = pt.zcu;
1802 const parent_ptr = switch (orig_parent_ptr.typeOf(zcu).ptrSize(zcu)) {
1803 .one, .many, .c => orig_parent_ptr,
1804 .slice => orig_parent_ptr.slicePtr(zcu),
1805 };
1806
1807 const parent_ptr_ty = parent_ptr.typeOf(zcu);
1808 const result_ty = try parent_ptr_ty.elemPtrType(field_idx, pt);
1809 const elem_ty = result_ty.childType(zcu);
1810 elem_ty.assertHasLayout(zcu);
1811
1812 if (parent_ptr.isUndef(zcu)) return pt.undefValue(result_ty);
1813
1814 if (!elem_ty.comptimeOnly(zcu)) {
1815 const byte_offset = field_idx * elem_ty.abiSize(zcu);
1816 return parent_ptr.getOffsetPtr(byte_offset, result_ty, pt);
1817 }
1818
1819 // Comptime-only element type.
1820
1821 if (field_idx == 0) {
1822 return pt.getCoerced(parent_ptr, result_ty);
1823 }
1824
1825 const arr_base_ty, const arr_base_len = elem_ty.arrayBase(zcu);
1826 const base_idx = arr_base_len * field_idx;
1827 const parent_info = zcu.intern_pool.indexToKey(parent_ptr.toIntern()).ptr;
1828 switch (parent_info.base_addr) {
1829 .arr_elem => |arr_elem| {
1830 if (Value.fromInterned(arr_elem.base).typeOf(zcu).childType(zcu).toIntern() == arr_base_ty.toIntern()) {
1831 // We already have a pointer to an element of an array of this type.
1832 // Just modify the index.
1833 return .fromInterned(try pt.intern(.{ .ptr = ptr: {
1834 var new = parent_info;
1835 new.base_addr.arr_elem.index += base_idx;
1836 new.ty = result_ty.toIntern();
1837 break :ptr new;
1838 } }));
1839 }
1840 },
1841 else => {},
1842 }
1843 const base_ptr = try parent_ptr.canonicalizeBasePtr(.many, arr_base_ty, pt);
1844 return .fromInterned(try pt.intern(.{ .ptr = .{
1845 .ty = result_ty.toIntern(),
1846 .base_addr = .{ .arr_elem = .{
1847 .base = base_ptr.toIntern(),
1848 .index = base_idx,
1849 } },
1850 .byte_offset = 0,
1851 } }));
1852}
1853
1854fn canonicalizeBasePtr(base_ptr: Value, want_size: std.lang.Type.Pointer.Size, want_child: Type, pt: Zcu.PerThread) !Value {
1855 const ptr_ty = base_ptr.typeOf(pt.zcu);
1856 const ptr_info = ptr_ty.ptrInfo(pt.zcu);
1857
1858 if (ptr_info.flags.size == want_size and
1859 ptr_info.child == want_child.toIntern() and
1860 !ptr_info.flags.is_const and
1861 !ptr_info.flags.is_volatile and
1862 !ptr_info.flags.is_allowzero and
1863 ptr_info.sentinel == .none and
1864 ptr_info.flags.alignment == .none)
1865 {
1866 // Already canonical!
1867 return base_ptr;
1868 }
1869
1870 const new_ty = try pt.ptrType(.{
1871 .child = want_child.toIntern(),
1872 .sentinel = .none,
1873 .flags = .{
1874 .size = want_size,
1875 .alignment = .none,
1876 .is_const = false,
1877 .is_volatile = false,
1878 .is_allowzero = false,
1879 .address_space = ptr_info.flags.address_space,
1880 },
1881 });
1882 return pt.getCoerced(base_ptr, new_ty);
1883}
1884
1885pub fn getOffsetPtr(ptr_val: Value, byte_off: u64, new_ty: Type, pt: Zcu.PerThread) !Value {
1886 if (ptr_val.isUndef(pt.zcu)) return ptr_val;
1887 var ptr = pt.zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
1888 ptr.ty = new_ty.toIntern();
1889 ptr.byte_offset += byte_off;
1890 return Value.fromInterned(try pt.intern(.{ .ptr = ptr }));
1891}
1892
1893pub const PointerDeriveStep = union(enum) {
1894 int: struct {
1895 addr: u64,
1896 ptr_ty: Type,
1897 },
1898 nav_ptr: InternPool.Nav.Index,
1899 uav_ptr: InternPool.Key.Ptr.BaseAddr.Uav,
1900 comptime_alloc_ptr: struct {
1901 idx: InternPool.ComptimeAllocIndex,
1902 val: Value,
1903 ptr_ty: Type,
1904 },
1905 comptime_field_ptr: Value,
1906 eu_payload_ptr: struct {
1907 parent: *PointerDeriveStep,
1908 /// This type will never be cast: it is provided for convenience.
1909 result_ptr_ty: Type,
1910 },
1911 opt_payload_ptr: struct {
1912 parent: *PointerDeriveStep,
1913 /// This type will never be cast: it is provided for convenience.
1914 result_ptr_ty: Type,
1915 },
1916 field_ptr: struct {
1917 parent: *PointerDeriveStep,
1918 field_idx: u32,
1919 /// This type will never be cast: it is provided for convenience.
1920 result_ptr_ty: Type,
1921 },
1922 elem_ptr: struct {
1923 parent: *PointerDeriveStep,
1924 elem_idx: u64,
1925 /// This type will never be cast: it is provided for convenience.
1926 result_ptr_ty: Type,
1927 },
1928 offset_and_cast: struct {
1929 parent: *PointerDeriveStep,
1930 byte_offset: u64,
1931 new_ptr_ty: Type,
1932 },
1933
1934 pub fn ptrType(step: PointerDeriveStep, pt: Zcu.PerThread) !Type {
1935 return switch (step) {
1936 .int => |int| int.ptr_ty,
1937 .nav_ptr => |nav| try pt.navPtrType(nav),
1938 .uav_ptr => |uav| Type.fromInterned(uav.orig_ty),
1939 .comptime_alloc_ptr => |info| info.ptr_ty,
1940 .comptime_field_ptr => |val| try pt.singleConstPtrType(val.typeOf(pt.zcu)),
1941 .offset_and_cast => |oac| oac.new_ptr_ty,
1942 inline .eu_payload_ptr, .opt_payload_ptr, .field_ptr, .elem_ptr => |x| x.result_ptr_ty,
1943 };
1944 }
1945};
1946
1947/// Given a pointer value, get the sequence of steps to derive it, ideally by taking
1948/// only field and element pointers with no casts. This can be used by codegen backends
1949/// which prefer field/elem accesses when lowering constant pointer values.
1950/// It is also used by the Value printing logic for pointers.
1951pub fn pointerDerivation(ptr_val: Value, arena: Allocator, pt: Zcu.PerThread, opt_sema: ?*Sema) Allocator.Error!PointerDeriveStep {
1952 const zcu = pt.zcu;
1953 const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
1954 const base_derive: PointerDeriveStep = switch (ptr.base_addr) {
1955 .int => return .{ .int = .{
1956 .addr = ptr.byte_offset,
1957 .ptr_ty = Type.fromInterned(ptr.ty),
1958 } },
1959 .nav => |nav| .{ .nav_ptr = nav },
1960 .uav => |uav| base: {
1961 // A slight tweak: `orig_ty` here is sometimes not `const`, but it ought to be.
1962 // TODO: fix this in the sites interning anon decls!
1963 const const_ty = try pt.ptrType(info: {
1964 var info = Type.fromInterned(uav.orig_ty).ptrInfo(zcu);
1965 info.flags.is_const = true;
1966 break :info info;
1967 });
1968 break :base .{ .uav_ptr = .{
1969 .val = uav.val,
1970 .orig_ty = const_ty.toIntern(),
1971 } };
1972 },
1973 .comptime_alloc => |idx| base: {
1974 const sema = opt_sema.?;
1975 const alloc = sema.getComptimeAlloc(idx);
1976 const val = try alloc.val.intern(pt, arena);
1977 const ty = val.typeOf(zcu);
1978 break :base .{ .comptime_alloc_ptr = .{
1979 .idx = idx,
1980 .val = val,
1981 .ptr_ty = try pt.ptrType(.{
1982 .child = ty.toIntern(),
1983 .flags = .{
1984 .alignment = alloc.alignment,
1985 },
1986 }),
1987 } };
1988 },
1989 .comptime_field => |val| .{ .comptime_field_ptr = Value.fromInterned(val) },
1990 .eu_payload => |eu_ptr| base: {
1991 const base_ptr = Value.fromInterned(eu_ptr);
1992 const base_ptr_ty = base_ptr.typeOf(zcu);
1993 const parent_step = try arena.create(PointerDeriveStep);
1994 parent_step.* = try pointerDerivation(.fromInterned(eu_ptr), arena, pt, opt_sema);
1995 break :base .{ .eu_payload_ptr = .{
1996 .parent = parent_step,
1997 .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).errorUnionPayload(zcu)),
1998 } };
1999 },
2000 .opt_payload => |opt_ptr| base: {
2001 const base_ptr = Value.fromInterned(opt_ptr);
2002 const base_ptr_ty = base_ptr.typeOf(zcu);
2003 const parent_step = try arena.create(PointerDeriveStep);
2004 parent_step.* = try pointerDerivation(.fromInterned(opt_ptr), arena, pt, opt_sema);
2005 break :base .{ .opt_payload_ptr = .{
2006 .parent = parent_step,
2007 .result_ptr_ty = try pt.adjustPtrTypeChild(base_ptr_ty, base_ptr_ty.childType(zcu).optionalChild(zcu)),
2008 } };
2009 },
2010 .field => |field| base: {
2011 const base_ptr = Value.fromInterned(field.base);
2012 const base_ptr_ty = try pt.ptrType(info: {
2013 var info = base_ptr.typeOf(zcu).ptrInfo(zcu);
2014 info.flags.size = .one;
2015 break :info info;
2016 });
2017 const parent_step = try arena.create(PointerDeriveStep);
2018 parent_step.* = try pointerDerivation(base_ptr, arena, pt, opt_sema);
2019 break :base .{ .field_ptr = .{
2020 .parent = parent_step,
2021 .field_idx = @intCast(field.index),
2022 .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field.index), pt),
2023 } };
2024 },
2025 .arr_elem => |arr_elem| base: {
2026 const parent_step = try arena.create(PointerDeriveStep);
2027 parent_step.* = try pointerDerivation(.fromInterned(arr_elem.base), arena, pt, opt_sema);
2028 const parent_ptr_info = (try parent_step.ptrType(pt)).ptrInfo(zcu);
2029 const result_ptr_ty = try pt.ptrType(.{
2030 .child = parent_ptr_info.child,
2031 .flags = flags: {
2032 var flags = parent_ptr_info.flags;
2033 flags.size = .one;
2034 if (flags.alignment != .none) flags.alignment = .minStrict(
2035 flags.alignment,
2036 Type.fromInterned(parent_ptr_info.child).abiAlignment(zcu),
2037 );
2038 break :flags flags;
2039 },
2040 });
2041 break :base .{ .elem_ptr = .{
2042 .parent = parent_step,
2043 .elem_idx = arr_elem.index,
2044 .result_ptr_ty = result_ptr_ty,
2045 } };
2046 },
2047 };
2048
2049 if (ptr.byte_offset == 0 and ptr.ty == (try base_derive.ptrType(pt)).toIntern()) {
2050 return base_derive;
2051 }
2052
2053 const ptr_ty_info = Type.fromInterned(ptr.ty).ptrInfo(zcu);
2054 const need_child: Type = .fromInterned(ptr_ty_info.child);
2055 if (need_child.comptimeOnly(zcu) or
2056 need_child.zigTypeTag(zcu) == .@"opaque" or
2057 need_child.isSpirvRuntimeArray(zcu))
2058 {
2059 // No refinement can happen - this pointer is presumably invalid.
2060 // Just offset it.
2061 const parent = try arena.create(PointerDeriveStep);
2062 parent.* = base_derive;
2063 return .{ .offset_and_cast = .{
2064 .parent = parent,
2065 .byte_offset = ptr.byte_offset,
2066 .new_ptr_ty = Type.fromInterned(ptr.ty),
2067 } };
2068 }
2069 const need_bytes = need_child.abiSize(zcu);
2070
2071 var cur_derive = base_derive;
2072 var cur_offset = ptr.byte_offset;
2073
2074 // Refine through fields and array elements as much as possible.
2075
2076 if (need_bytes > 0) while (true) {
2077 const cur_ty = (try cur_derive.ptrType(pt)).childType(zcu);
2078 if (cur_ty.toIntern() == need_child.toIntern() and cur_offset == 0) {
2079 break;
2080 }
2081 switch (cur_ty.zigTypeTag(zcu)) {
2082 .noreturn,
2083 .type,
2084 .comptime_int,
2085 .comptime_float,
2086 .null,
2087 .undefined,
2088 .enum_literal,
2089 .@"opaque",
2090 .spirv,
2091 .@"fn",
2092 .error_union,
2093 .int,
2094 .float,
2095 .bool,
2096 .void,
2097 .pointer,
2098 .error_set,
2099 .@"anyframe",
2100 .frame,
2101 .@"enum",
2102 .vector,
2103 .@"union",
2104 => break,
2105
2106 .optional => {
2107 ptr_opt: {
2108 if (!cur_ty.isPtrLikeOptional(zcu)) break :ptr_opt;
2109 if (need_child.zigTypeTag(zcu) != .pointer) break :ptr_opt;
2110 switch (need_child.ptrSize(zcu)) {
2111 .one, .many => {},
2112 .slice, .c => break :ptr_opt,
2113 }
2114 const parent = try arena.create(PointerDeriveStep);
2115 parent.* = cur_derive;
2116 cur_derive = .{ .opt_payload_ptr = .{
2117 .parent = parent,
2118 .result_ptr_ty = try pt.adjustPtrTypeChild(try parent.ptrType(pt), cur_ty.optionalChild(zcu)),
2119 } };
2120 continue;
2121 }
2122 break;
2123 },
2124
2125 .array => {
2126 const elem_ty = cur_ty.childType(zcu);
2127 const elem_size = elem_ty.abiSize(zcu);
2128 const start_idx = cur_offset / elem_size;
2129 const end_idx = (cur_offset + need_bytes + elem_size - 1) / elem_size;
2130 if (end_idx == start_idx + 1 and ptr_ty_info.flags.size == .one) {
2131 const parent = try arena.create(PointerDeriveStep);
2132 parent.* = cur_derive;
2133 cur_derive = .{ .elem_ptr = .{
2134 .parent = parent,
2135 .elem_idx = start_idx,
2136 .result_ptr_ty = try pt.adjustPtrTypeChild(try parent.ptrType(pt), elem_ty),
2137 } };
2138 cur_offset -= start_idx * elem_size;
2139 } else {
2140 // Go into the first element if needed, but don't go any deeper.
2141 if (start_idx > 0) {
2142 const parent = try arena.create(PointerDeriveStep);
2143 parent.* = cur_derive;
2144 cur_derive = .{ .elem_ptr = .{
2145 .parent = parent,
2146 .elem_idx = start_idx,
2147 .result_ptr_ty = try pt.adjustPtrTypeChild(try parent.ptrType(pt), elem_ty),
2148 } };
2149 cur_offset -= start_idx * elem_size;
2150 }
2151 break;
2152 }
2153 },
2154 .@"struct" => switch (cur_ty.containerLayout(zcu)) {
2155 .auto, .@"packed" => break,
2156 .@"extern" => for (0..cur_ty.structFieldCount(zcu)) |field_idx| {
2157 const field_ty = cur_ty.fieldType(field_idx, zcu);
2158 const start_off = cur_ty.structFieldOffset(field_idx, zcu);
2159 const end_off = start_off + field_ty.abiSize(zcu);
2160 if (cur_offset >= start_off and cur_offset + need_bytes <= end_off) {
2161 const base_ptr_ty = try pt.ptrType(info: {
2162 var info = (try cur_derive.ptrType(pt)).ptrInfo(zcu);
2163 info.flags.size = .one;
2164 break :info info;
2165 });
2166 const parent = try arena.create(PointerDeriveStep);
2167 parent.* = cur_derive;
2168 cur_derive = .{ .field_ptr = .{
2169 .parent = parent,
2170 .field_idx = @intCast(field_idx),
2171 .result_ptr_ty = try base_ptr_ty.fieldPtrType(@intCast(field_idx), pt),
2172 } };
2173 cur_offset -= start_off;
2174 break;
2175 }
2176 } else break, // pointer spans multiple fields
2177 },
2178 }
2179 };
2180
2181 if (cur_offset == 0) compatible: {
2182 const src_ptr_ty_info = (try cur_derive.ptrType(pt)).ptrInfo(zcu);
2183 // We allow silently doing some "coercible" pointer things.
2184 // In particular, we only give up if cv qualifiers are *removed*.
2185 if (src_ptr_ty_info.flags.is_const and !ptr_ty_info.flags.is_const) break :compatible;
2186 if (src_ptr_ty_info.flags.is_volatile and !ptr_ty_info.flags.is_volatile) break :compatible;
2187 if (src_ptr_ty_info.flags.is_allowzero and !ptr_ty_info.flags.is_allowzero) break :compatible;
2188 // Everything else has to match exactly.
2189 if (src_ptr_ty_info.child != ptr_ty_info.child) break :compatible;
2190 if (src_ptr_ty_info.sentinel != ptr_ty_info.sentinel) break :compatible;
2191 if (src_ptr_ty_info.packed_offset != ptr_ty_info.packed_offset) break :compatible;
2192 if (src_ptr_ty_info.flags.size != ptr_ty_info.flags.size) break :compatible;
2193 if (src_ptr_ty_info.flags.alignment != ptr_ty_info.flags.alignment) break :compatible;
2194 if (src_ptr_ty_info.flags.address_space != ptr_ty_info.flags.address_space) break :compatible;
2195 if (src_ptr_ty_info.flags.vector_index != ptr_ty_info.flags.vector_index) break :compatible;
2196
2197 return cur_derive;
2198 }
2199
2200 const parent = try arena.create(PointerDeriveStep);
2201 parent.* = cur_derive;
2202 return .{ .offset_and_cast = .{
2203 .parent = parent,
2204 .byte_offset = cur_offset,
2205 .new_ptr_ty = Type.fromInterned(ptr.ty),
2206 } };
2207}
2208
2209const InterpretMode = enum {
2210 /// In this mode, types are assumed to match what the compiler was built with in terms of field
2211 /// order, field types, etc. This improves compiler performance. However, it means that certain
2212 /// modifications to `std.lang` will result in compiler crashes.
2213 direct,
2214 /// In this mode, various details of the type are allowed to differ from what the compiler was built
2215 /// with. Fields are matched by name rather than index; added struct fields are ignored, and removed
2216 /// struct fields use their default value if one exists. This is slower than `.direct`, but permits
2217 /// making certain changes to `std.lang` (in particular reordering/adding/removing fields), so it is
2218 /// useful when applying breaking changes.
2219 by_name,
2220};
2221const interpret_mode: InterpretMode = @field(InterpretMode, @tagName(build_options.value_interpret_mode));
2222
2223/// Given a `Value` representing a comptime-known value of type `T`, unwrap it into an actual `T` known to the compiler.
2224/// This is useful for accessing `std.lang` structures received from comptime logic.
2225pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMemory, UndefinedValue, TypeMismatch }!T {
2226 const zcu = pt.zcu;
2227 const io = zcu.comp.io;
2228 const ip = &zcu.intern_pool;
2229 const ty = val.typeOf(zcu);
2230 if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
2231 if (val.isUndef(zcu)) return error.UndefinedValue;
2232
2233 return switch (@typeInfo(T)) {
2234 .type,
2235 .noreturn,
2236 .comptime_float,
2237 .comptime_int,
2238 .undefined,
2239 .null,
2240 .@"fn",
2241 .@"opaque",
2242 .spirv,
2243 .enum_literal,
2244 => comptime unreachable, // comptime-only or otherwise impossible
2245
2246 .pointer,
2247 .array,
2248 .error_union,
2249 .error_set,
2250 .frame,
2251 .@"anyframe",
2252 .vector,
2253 => comptime unreachable, // unsupported
2254
2255 .void => {},
2256
2257 .bool => switch (val.toIntern()) {
2258 .bool_false => false,
2259 .bool_true => true,
2260 else => unreachable,
2261 },
2262
2263 .int => switch (ip.indexToKey(val.toIntern()).int.storage) {
2264 inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch,
2265 .big_int => |big| big.toInt(T) catch return error.TypeMismatch,
2266 },
2267
2268 .float => val.toFloat(T, zcu),
2269
2270 .optional => |opt| if (val.optionalValue(zcu)) |unwrapped|
2271 try unwrapped.interpret(opt.child, pt)
2272 else
2273 null,
2274
2275 .@"enum" => switch (interpret_mode) {
2276 .direct => {
2277 const int = val.getUnsignedInt(zcu) orelse return error.TypeMismatch;
2278 return std.enums.fromInt(T, int) orelse error.TypeMismatch;
2279 },
2280 .by_name => {
2281 const field_index = ty.enumTagFieldIndex(val, zcu) orelse return error.TypeMismatch;
2282 const field_name = ty.enumFieldName(field_index, zcu);
2283 return std.meta.stringToEnum(T, field_name.toSlice(ip)) orelse error.TypeMismatch;
2284 },
2285 },
2286
2287 .@"union" => |@"union"| {
2288 // No need to handle `interpret_mode`, because the `.@"enum"` handling already deals with it.
2289 const tag_val = val.unionTag(zcu) orelse return error.TypeMismatch;
2290 const tag = try tag_val.interpret(@"union".tag_type.?, pt);
2291 return switch (tag) {
2292 inline else => |tag_comptime| @unionInit(
2293 T,
2294 @tagName(tag_comptime),
2295 try val.unionPayload(zcu).interpret(@FieldType(T, @tagName(tag_comptime)), pt),
2296 ),
2297 };
2298 },
2299
2300 .@"struct" => |@"struct"| switch (interpret_mode) {
2301 .direct => {
2302 if (ty.structFieldCount(zcu) != @"struct".field_names.len) return error.TypeMismatch;
2303 var result: T = undefined;
2304 inline for (@"struct".field_names, @"struct".field_types, 0..) |field_name, field_type, field_idx| {
2305 const field_val = try val.fieldValue(pt, field_idx);
2306 @field(result, field_name) = try field_val.interpret(field_type, pt);
2307 }
2308 return result;
2309 },
2310 .by_name => {
2311 const struct_obj = zcu.typeToStruct(ty) orelse return error.TypeMismatch;
2312 var result: T = undefined;
2313 inline for (@"struct".field_names, @"struct".field_types, @"struct".field_attrs) |field_name, field_type, field_attr| {
2314 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, field_name, .no_embedded_nulls);
2315 @field(result, field_name) = if (struct_obj.nameIndex(ip, field_name_ip)) |field_idx| f: {
2316 const field_val = try val.fieldValue(pt, field_idx);
2317 break :f try field_val.interpret(field_type, pt);
2318 } else (field_attr.defaultValue(field_type) orelse return error.TypeMismatch);
2319 }
2320 return result;
2321 },
2322 },
2323 };
2324}
2325
2326/// Given any `val` and a `Type` corresponding `@TypeOf(val)`, construct a `Value` representing it which can be used
2327/// within the compilation. This is useful for passing `std.lang` structures in the compiler back to the compilation.
2328/// This is the inverse of `interpret`.
2329pub fn uninterpret(val: anytype, ty: Type, pt: Zcu.PerThread) error{ OutOfMemory, TypeMismatch }!Value {
2330 const T = @TypeOf(val);
2331
2332 const zcu = pt.zcu;
2333 const io = zcu.comp.io;
2334 const ip = &zcu.intern_pool;
2335 if (ty.zigTypeTag(zcu) != @typeInfo(T)) return error.TypeMismatch;
2336
2337 return switch (@typeInfo(T)) {
2338 .type,
2339 .noreturn,
2340 .comptime_float,
2341 .comptime_int,
2342 .undefined,
2343 .null,
2344 .@"fn",
2345 .@"opaque",
2346 .spirv,
2347 .enum_literal,
2348 => comptime unreachable, // comptime-only or otherwise impossible
2349
2350 .pointer,
2351 .array,
2352 .error_union,
2353 .error_set,
2354 .frame,
2355 .@"anyframe",
2356 .vector,
2357 => comptime unreachable, // unsupported
2358
2359 .void => .void,
2360
2361 .bool => if (val) .true else .false,
2362
2363 .int => try pt.intValue(ty, val),
2364
2365 .float => try pt.floatValue(ty, val),
2366
2367 .optional => if (val) |some|
2368 .fromInterned(try pt.intern(.{ .opt = .{
2369 .ty = ty.toIntern(),
2370 .val = (try uninterpret(some, ty.optionalChild(zcu), pt)).toIntern(),
2371 } }))
2372 else
2373 try pt.nullValue(ty),
2374
2375 .@"enum" => switch (interpret_mode) {
2376 .direct => try pt.enumValue(ty, try uninterpret(@backingInt(val), ty.backingIntType(zcu), pt)),
2377 .by_name => {
2378 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, @tagName(val), .no_embedded_nulls);
2379 const field_idx = ty.enumFieldIndex(field_name_ip, zcu) orelse return error.TypeMismatch;
2380 return pt.enumValueFieldIndex(ty, field_idx);
2381 },
2382 },
2383
2384 .@"union" => |@"union"| {
2385 // No need to handle `interpret_mode`, because the `.@"enum"` handling already deals with it.
2386 const tag: @"union".tag_type.? = val;
2387 const tag_val = try uninterpret(tag, ty.unionTagType(zcu).?, pt);
2388 const field_ty = ty.unionFieldType(tag_val, zcu) orelse return error.TypeMismatch;
2389 return switch (val) {
2390 inline else => |payload| try pt.unionValue(
2391 ty,
2392 tag_val,
2393 try uninterpret(payload, field_ty, pt),
2394 ),
2395 };
2396 },
2397
2398 .@"struct" => |@"struct"| switch (interpret_mode) {
2399 .direct => {
2400 if (ty.structFieldCount(zcu) != @"struct".field_names.len) return error.TypeMismatch;
2401 var field_vals: [@"struct".field_names.len]InternPool.Index = undefined;
2402 inline for (&field_vals, @"struct".field_names, 0..) |*field_val, field_name, field_idx| {
2403 const field_ty = ty.fieldType(field_idx, zcu);
2404 field_val.* = (try uninterpret(@field(val, field_name), field_ty, pt)).toIntern();
2405 }
2406 return pt.aggregateValue(ty, &field_vals);
2407 },
2408 .by_name => {
2409 const struct_obj = zcu.typeToStruct(ty) orelse return error.TypeMismatch;
2410 const want_fields_len = struct_obj.field_types.len;
2411 const field_vals = try zcu.gpa.alloc(InternPool.Index, want_fields_len);
2412 defer zcu.gpa.free(field_vals);
2413 @memset(field_vals, .none);
2414 inline for (@"struct".field_names) |field_name| {
2415 const field_name_ip = try ip.getOrPutString(zcu.gpa, io, pt.tid, field_name, .no_embedded_nulls);
2416 if (struct_obj.nameIndex(ip, field_name_ip)) |field_idx| {
2417 const field_ty = ty.fieldType(field_idx, zcu);
2418 field_vals[field_idx] = (try uninterpret(@field(val, field_name), field_ty, pt)).toIntern();
2419 }
2420 }
2421 for (field_vals, 0..) |*field_val, field_idx| {
2422 if (field_val.* == .none) {
2423 const default_init = struct_obj.field_defaults.get(ip)[field_idx];
2424 if (default_init == .none) return error.TypeMismatch;
2425 field_val.* = default_init;
2426 }
2427 }
2428 return pt.aggregateValue(ty, field_vals);
2429 },
2430 },
2431 };
2432}
2433
2434/// Returns whether `ptr_val_a[0..elem_count]` and `ptr_val_b[0..elem_count]` overlap.
2435/// `ptr_val_a` and `ptr_val_b` are indexable pointers (not slices) whose element types are in-memory coercible.
2436pub fn doPointersOverlap(ptr_val_a: Value, ptr_val_b: Value, elem_count: u64, zcu: *const Zcu) bool {
2437 const ip = &zcu.intern_pool;
2438
2439 const a_elem_ty = ptr_val_a.typeOf(zcu).indexableElem(zcu);
2440 const b_elem_ty = ptr_val_b.typeOf(zcu).indexableElem(zcu);
2441
2442 const a_ptr = ip.indexToKey(ptr_val_a.toIntern()).ptr;
2443 const b_ptr = ip.indexToKey(ptr_val_b.toIntern()).ptr;
2444
2445 // If `a_elem_ty` is not comptime-only, then overlapping pointers have identical
2446 // `base_addr`, and we just need to look at the byte offset. If it *is* comptime-only,
2447 // then `base_addr` may be an `arr_elem`, and we'll have to consider the element index.
2448 if (a_elem_ty.comptimeOnly(zcu)) {
2449 assert(a_elem_ty.toIntern() == b_elem_ty.toIntern()); // IMC comptime-only types are equivalent
2450
2451 const a_base_addr: InternPool.Key.Ptr.BaseAddr, const a_idx: u64 = switch (a_ptr.base_addr) {
2452 else => .{ a_ptr.base_addr, 0 },
2453 .arr_elem => |arr_elem| a: {
2454 const base_ptr = Value.fromInterned(arr_elem.base);
2455 const base_child_ty = base_ptr.typeOf(zcu).childType(zcu);
2456 if (base_child_ty.toIntern() == a_elem_ty.toIntern()) {
2457 // This `arr_elem` is indexing into the element type we want.
2458 const base_ptr_info = ip.indexToKey(base_ptr.toIntern()).ptr;
2459 if (base_ptr_info.byte_offset != 0) {
2460 return false; // this pointer is invalid, just let the access fail
2461 }
2462 break :a .{ base_ptr_info.base_addr, arr_elem.index };
2463 }
2464 break :a .{ a_ptr.base_addr, 0 };
2465 },
2466 };
2467 const b_base_addr: InternPool.Key.Ptr.BaseAddr, const b_idx: u64 = switch (a_ptr.base_addr) {
2468 else => .{ b_ptr.base_addr, 0 },
2469 .arr_elem => |arr_elem| b: {
2470 const base_ptr = Value.fromInterned(arr_elem.base);
2471 const base_child_ty = base_ptr.typeOf(zcu).childType(zcu);
2472 if (base_child_ty.toIntern() == b_elem_ty.toIntern()) {
2473 // This `arr_elem` is indexing into the element type we want.
2474 const base_ptr_info = ip.indexToKey(base_ptr.toIntern()).ptr;
2475 if (base_ptr_info.byte_offset != 0) {
2476 return false; // this pointer is invalid, just let the access fail
2477 }
2478 break :b .{ base_ptr_info.base_addr, arr_elem.index };
2479 }
2480 break :b .{ b_ptr.base_addr, 0 };
2481 },
2482 };
2483 if (!std.meta.eql(a_base_addr, b_base_addr)) return false;
2484 const diff = if (a_idx >= b_idx) a_idx - b_idx else b_idx - a_idx;
2485 return diff < elem_count;
2486 } else {
2487 assert(a_elem_ty.abiSize(zcu) == b_elem_ty.abiSize(zcu));
2488
2489 if (!std.meta.eql(a_ptr.base_addr, b_ptr.base_addr)) return false;
2490
2491 const bytes_diff = if (a_ptr.byte_offset >= b_ptr.byte_offset)
2492 a_ptr.byte_offset - b_ptr.byte_offset
2493 else
2494 b_ptr.byte_offset - a_ptr.byte_offset;
2495
2496 const need_bytes_diff = elem_count * a_elem_ty.abiSize(zcu);
2497 return bytes_diff < need_bytes_diff;
2498 }
2499}
2500
2501/// `lhs` and `rhs` are both scalar numeric values (int or float).
2502/// Supports comparisons between heterogeneous types.
2503/// If `lhs` or `rhs` is undef, returns `false`.
2504pub fn eqlScalarNum(lhs: Value, rhs: Value, zcu: *Zcu) bool {
2505 if (lhs.isUndef(zcu)) return false;
2506 if (rhs.isUndef(zcu)) return false;
2507
2508 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {
2509 const lhs_f128 = lhs.toFloat(f128, zcu);
2510 const rhs_f128 = rhs.toFloat(f128, zcu);
2511 return lhs_f128 == rhs_f128;
2512 }
2513
2514 if (lhs.getUnsignedInt(zcu)) |lhs_u64| {
2515 if (rhs.getUnsignedInt(zcu)) |rhs_u64| {
2516 return lhs_u64 == rhs_u64;
2517 }
2518 }
2519
2520 var lhs_bigint_space: BigIntSpace = undefined;
2521 var rhs_bigint_space: BigIntSpace = undefined;
2522 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, zcu);
2523 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, zcu);
2524 return lhs_bigint.eql(rhs_bigint);
2525}
2526
2527/// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
2528/// Vectors are also accepted. Vector results are reduced with AND.
2529///
2530/// If provided, `vector_index` reports the first element that failed the range check.
2531pub fn intFitsInType(
2532 val: Value,
2533 ty: Type,
2534 vector_index: ?*usize,
2535 zcu: *const Zcu,
2536) bool {
2537 if (ty.toIntern() == .comptime_int_type) return true;
2538 const info = ty.intInfo(zcu);
2539 switch (val.toIntern()) {
2540 .zero_usize, .zero_u8 => return true,
2541 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
2542 .undef => return true,
2543 .@"extern", .func, .ptr => {
2544 const target = zcu.getTarget();
2545 const ptr_bits = target.ptrBitWidth();
2546 return switch (info.signedness) {
2547 .signed => info.bits > ptr_bits,
2548 .unsigned => info.bits >= ptr_bits,
2549 };
2550 },
2551 .int => |int| {
2552 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
2553 const big_int = int.storage.toBigInt(&buffer);
2554 return big_int.fitsInTwosComp(info.signedness, info.bits);
2555 },
2556 .aggregate => |aggregate| {
2557 assert(ty.zigTypeTag(zcu) == .vector);
2558 return switch (aggregate.storage) {
2559 .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| {
2560 if (byte == 0) continue;
2561 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed);
2562 if (info.bits >= actual_needed_bits) continue;
2563 if (vector_index) |vi| vi.* = i;
2564 break false;
2565 } else true,
2566 .elems, .repeated_elem => for (switch (aggregate.storage) {
2567 .bytes => unreachable,
2568 .elems => |elems| elems,
2569 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem),
2570 }, 0..) |elem, i| {
2571 if (Value.fromInterned(elem).intFitsInType(ty.scalarType(zcu), null, zcu)) continue;
2572 if (vector_index) |vi| vi.* = i;
2573 break false;
2574 } else true,
2575 };
2576 },
2577 else => unreachable,
2578 },
2579 }
2580}