authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-22 09:34:44+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-22 09:34:44+02:00
logdef135918740846d9b206c3188563cb77333f3a9
tree2d054e8bca8b70ab2d38e384ff4eaac665b86422
parentd0edd37f690c3e6cf3f8a7fc7a27016ba9b010ce
parent8d0671157cdf8bc8b89d047138b42227420a5388

Merge remote-tracking branch 'origin/master' into zld-incremental-2


50 files changed, 7644 insertions(+), 6243 deletions(-)

CMakeLists.txt+2-2
......@@ -564,7 +564,7 @@ set(ZIG_STAGE2_SOURCES
564564 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"
565565 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
566566 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
567 "${CMAKE_SOURCE_DIR}/src/air.zig"
567 "${CMAKE_SOURCE_DIR}/src/Air.zig"
568568 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
569569 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
570570 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
......@@ -593,7 +593,7 @@ set(ZIG_STAGE2_SOURCES
593593 "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig"
594594 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
595595 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
596 "${CMAKE_SOURCE_DIR}/src/liveness.zig"
596 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
597597 "${CMAKE_SOURCE_DIR}/src/main.zig"
598598 "${CMAKE_SOURCE_DIR}/src/mingw.zig"
599599 "${CMAKE_SOURCE_DIR}/src/musl.zig"
doc/langref.html.in+2-3
......@@ -5725,9 +5725,8 @@ test "turn HashMap into a set with void" {
57255725 value is deleted, as seen above.
57265726 </p>
57275727 <p>
5728 {#syntax#}void{#endsyntax#} is distinct from {#syntax#}c_void{#endsyntax#}, which is defined like this:
5729 {#syntax#}pub const c_void = opaque {};{#endsyntax#}.
5730 {#syntax#}void{#endsyntax#} has a known size of 0 bytes, and {#syntax#}c_void{#endsyntax#} has an unknown, but non-zero, size.
5728 {#syntax#}void{#endsyntax#} is distinct from {#syntax#}c_void{#endsyntax#}.
5729 {#syntax#}void{#endsyntax#} has a known size of 0 bytes, and {#syntax#}c_void{#endsyntax#} has an unknown, but non-zero, size.
57315730 </p>
57325731 <p>
57335732 Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example:
lib/std/Progress.zig+61-74
......@@ -63,6 +63,10 @@ done: bool = true,
6363/// while it was still being accessed by the `refresh` function.
6464update_lock: std.Thread.Mutex = .{},
6565
66/// Keeps track of how many columns in the terminal have been output, so that
67/// we can move the cursor back later.
68columns_written: usize = undefined,
69
6670/// Represents one unit of progress. Each node can have children nodes, or
6771/// one can use integers with `update`.
6872pub const Node = struct {
......@@ -160,6 +164,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
160164 .unprotected_estimated_total_items = estimated_total_items,
161165 .unprotected_completed_items = 0,
162166 };
167 self.columns_written = 0;
163168 self.prev_refresh_timestamp = 0;
164169 self.timer = try std.time.Timer.start();
165170 self.done = false;
......@@ -187,15 +192,6 @@ pub fn refresh(self: *Progress) void {
187192 return self.refreshWithHeldLock();
188193}
189194
190// ED -- Clear screen
191const ED = "\x1b[J";
192// DECSC -- Save cursor position
193const DECSC = "\x1b7";
194// DECRC -- Restore cursor position
195const DECRC = "\x1b8";
196// Note that ESC7/ESC8 are used instead of CSI s/CSI u as the latter are not
197// supported by some terminals (eg. Terminal.app).
198
199195fn refreshWithHeldLock(self: *Progress) void {
200196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
201197 if (is_dumb and self.dont_print_on_dumb) return;
......@@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void {
203199 const file = self.terminal orelse return;
204200
205201 var end: usize = 0;
206 // Save the cursor position and clear the part of the screen below.
207 // Clearing only the line is not enough as the terminal may wrap the line
208 // when it becomes too long.
209 var saved_cursor_pos: windows.COORD = undefined;
210 if (self.supports_ansi_escape_codes) {
211 const seq_before = DECSC ++ ED;
212 std.mem.copy(u8, self.output_buffer[end..], seq_before);
213 end += seq_before.len;
214 } else if (std.builtin.os.tag == .windows) winapi: {
215 std.debug.assert(self.is_windows_terminal);
216
217 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
218 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
219 unreachable;
220
221 saved_cursor_pos = info.dwCursorPosition;
222
223 const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1);
224 const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1);
225 // Number of terminal cells to clear, starting from the cursor position
226 // and ending at the window bottom right corner.
227 const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: {
228 break :chars window_width * (window_height -
229 @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) -
230 @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left);
231 };
232
233 var written: windows.DWORD = undefined;
234 if (windows.kernel32.FillConsoleOutputAttribute(
235 file.handle,
236 info.wAttributes,
237 fill_chars,
238 saved_cursor_pos,
239 &written,
240 ) != windows.TRUE) {
241 // Stop trying to write to this file.
242 self.terminal = null;
243 break :winapi;
244 }
245 if (windows.kernel32.FillConsoleOutputCharacterW(
246 file.handle,
247 ' ',
248 fill_chars,
249 saved_cursor_pos,
250 &written,
251 ) != windows.TRUE) {
252 unreachable;
202 if (self.columns_written > 0) {
203 // restore the cursor position by moving the cursor
204 // `columns_written` cells to the left, then clear the rest of the
205 // line
206 if (self.supports_ansi_escape_codes) {
207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
209 } else if (std.builtin.os.tag == .windows) winapi: {
210 std.debug.assert(self.is_windows_terminal);
211
212 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
213 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
214 unreachable;
215
216 var cursor_pos = windows.COORD{
217 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
218 .Y = info.dwCursorPosition.Y,
219 };
220
221 if (cursor_pos.X < 0)
222 cursor_pos.X = 0;
223
224 const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
225
226 var written: windows.DWORD = undefined;
227 if (windows.kernel32.FillConsoleOutputAttribute(
228 file.handle,
229 info.wAttributes,
230 fill_chars,
231 cursor_pos,
232 &written,
233 ) != windows.TRUE) {
234 // Stop trying to write to this file.
235 self.terminal = null;
236 break :winapi;
237 }
238 if (windows.kernel32.FillConsoleOutputCharacterW(
239 file.handle,
240 ' ',
241 fill_chars,
242 cursor_pos,
243 &written,
244 ) != windows.TRUE) unreachable;
245
246 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
247 unreachable;
248 } else {
249 // we are in a "dumb" terminal like in acme or writing to a file
250 self.output_buffer[end] = '\n';
251 end += 1;
253252 }
253
254 self.columns_written = 0;
254255 }
255256
256257 if (!self.done) {
......@@ -285,28 +286,10 @@ fn refreshWithHeldLock(self: *Progress) void {
285286 }
286287 }
287288
288 // We're done printing the updated message, restore the cursor position.
289 if (self.supports_ansi_escape_codes) {
290 const seq_after = DECRC;
291 std.mem.copy(u8, self.output_buffer[end..], seq_after);
292 end += seq_after.len;
293 } else if (!self.is_windows_terminal) {
294 self.output_buffer[end] = '\n';
295 end += 1;
296 }
297
298289 _ = file.write(self.output_buffer[0..end]) catch {
299290 // Stop trying to write to this file once it errors.
300291 self.terminal = null;
301292 };
302
303 if (std.builtin.os.tag == .windows) {
304 if (self.is_windows_terminal) {
305 const res = windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos);
306 std.debug.assert(res == windows.TRUE);
307 }
308 }
309
310293 self.prev_refresh_timestamp = self.timer.read();
311294}
312295
......@@ -317,14 +300,17 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
317300 self.terminal = null;
318301 return;
319302 };
303 self.columns_written = 0;
320304}
321305
322306fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
323307 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
324308 const amt = written.len;
325309 end.* += amt;
310 self.columns_written += amt;
326311 } else |err| switch (err) {
327312 error.NoSpaceLeft => {
313 self.columns_written += self.output_buffer.len - end.*;
328314 end.* = self.output_buffer.len;
329315 },
330316 }
......@@ -332,6 +318,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any
332318 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
333319 if (end.* > max_end) {
334320 const suffix = "... ";
321 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
335322 std.mem.copy(u8, self.output_buffer[max_end..], suffix);
336323 end.* = max_end + suffix.len;
337324 }
lib/std/Thread.zig+43-21
......@@ -505,8 +505,8 @@ const LinuxThreadImpl = struct {
505505 /// Ported over from musl libc's pthread detached implementation:
506506 /// https://github.com/ifduyue/musl/search?q=__unmapself
507507 fn freeAndExit(self: *ThreadCompletion) noreturn {
508 const unmap_and_exit: []const u8 = switch (target.cpu.arch) {
509 .i386 => (
508 switch (target.cpu.arch) {
509 .i386 => asm volatile (
510510 \\ movl $91, %%eax
511511 \\ movl %[ptr], %%ebx
512512 \\ movl %[len], %%ecx
......@@ -514,8 +514,12 @@ const LinuxThreadImpl = struct {
514514 \\ movl $1, %%eax
515515 \\ movl $0, %%ebx
516516 \\ int $128
517 :
518 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
519 [len] "r" (self.mapped.len)
520 : "memory"
517521 ),
518 .x86_64 => (
522 .x86_64 => asm volatile (
519523 \\ movq $11, %%rax
520524 \\ movq %[ptr], %%rbx
521525 \\ movq %[len], %%rcx
......@@ -523,8 +527,12 @@ const LinuxThreadImpl = struct {
523527 \\ movq $60, %%rax
524528 \\ movq $1, %%rdi
525529 \\ syscall
530 :
531 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
532 [len] "r" (self.mapped.len)
533 : "memory"
526534 ),
527 .arm, .armeb, .thumb, .thumbeb => (
535 .arm, .armeb, .thumb, .thumbeb => asm volatile (
528536 \\ mov r7, #91
529537 \\ mov r0, %[ptr]
530538 \\ mov r1, %[len]
......@@ -532,8 +540,12 @@ const LinuxThreadImpl = struct {
532540 \\ mov r7, #1
533541 \\ mov r0, #0
534542 \\ svc 0
543 :
544 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
545 [len] "r" (self.mapped.len)
546 : "memory"
535547 ),
536 .aarch64, .aarch64_be, .aarch64_32 => (
548 .aarch64, .aarch64_be, .aarch64_32 => asm volatile (
537549 \\ mov x8, #215
538550 \\ mov x0, %[ptr]
539551 \\ mov x1, %[len]
......@@ -541,8 +553,12 @@ const LinuxThreadImpl = struct {
541553 \\ mov x8, #93
542554 \\ mov x0, #0
543555 \\ svc 0
556 :
557 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
558 [len] "r" (self.mapped.len)
559 : "memory"
544560 ),
545 .mips, .mipsel => (
561 .mips, .mipsel => asm volatile (
546562 \\ move $sp, $25
547563 \\ li $2, 4091
548564 \\ move $4, %[ptr]
......@@ -551,8 +567,12 @@ const LinuxThreadImpl = struct {
551567 \\ li $2, 4001
552568 \\ li $4, 0
553569 \\ syscall
570 :
571 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
572 [len] "r" (self.mapped.len)
573 : "memory"
554574 ),
555 .mips64, .mips64el => (
575 .mips64, .mips64el => asm volatile (
556576 \\ li $2, 4091
557577 \\ move $4, %[ptr]
558578 \\ move $5, %[len]
......@@ -560,8 +580,12 @@ const LinuxThreadImpl = struct {
560580 \\ li $2, 4001
561581 \\ li $4, 0
562582 \\ syscall
583 :
584 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
585 [len] "r" (self.mapped.len)
586 : "memory"
563587 ),
564 .powerpc, .powerpcle, .powerpc64, .powerpc64le => (
588 .powerpc, .powerpcle, .powerpc64, .powerpc64le => asm volatile (
565589 \\ li 0, 91
566590 \\ mr %[ptr], 3
567591 \\ mr %[len], 4
......@@ -570,8 +594,12 @@ const LinuxThreadImpl = struct {
570594 \\ li 3, 0
571595 \\ sc
572596 \\ blr
597 :
598 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
599 [len] "r" (self.mapped.len)
600 : "memory"
573601 ),
574 .riscv64 => (
602 .riscv64 => asm volatile (
575603 \\ li a7, 215
576604 \\ mv a0, %[ptr]
577605 \\ mv a1, %[len]
......@@ -579,19 +607,13 @@ const LinuxThreadImpl = struct {
579607 \\ li a7, 93
580608 \\ mv a0, zero
581609 \\ ecall
610 :
611 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
612 [len] "r" (self.mapped.len)
613 : "memory"
582614 ),
583 else => |cpu_arch| {
584 @compileLog("Unsupported linux arch ", cpu_arch);
585 },
586 };
587
588 asm volatile (unmap_and_exit
589 :
590 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
591 [len] "r" (self.mapped.len)
592 : "memory"
593 );
594
615 else => |cpu_arch| @compileError("Unsupported linux arch: " ++ @tagName(cpu_arch)),
616 }
595617 unreachable;
596618 }
597619 };
lib/std/array_list.zig+10-9
......@@ -227,10 +227,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
227227 /// Append the slice of items to the list, asserting the capacity is already
228228 /// enough to store the new items. **Does not** invalidate pointers.
229229 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
230 const oldlen = self.items.len;
231 const newlen = self.items.len + items.len;
232 self.items.len = newlen;
233 mem.copy(T, self.items[oldlen..], items);
230 const old_len = self.items.len;
231 const new_len = old_len + items.len;
232 assert(new_len <= self.capacity);
233 self.items.len = new_len;
234 mem.copy(T, self.items[old_len..], items);
234235 }
235236
236237 pub usingnamespace if (T != u8) struct {} else struct {
......@@ -570,11 +571,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
570571 /// Append the slice of items to the list, asserting the capacity is enough
571572 /// to store the new items.
572573 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
573 const oldlen = self.items.len;
574 const newlen = self.items.len + items.len;
575
576 self.items.len = newlen;
577 mem.copy(T, self.items[oldlen..], items);
574 const old_len = self.items.len;
575 const new_len = old_len + items.len;
576 assert(new_len <= self.capacity);
577 self.items.len = new_len;
578 mem.copy(T, self.items[old_len..], items);
578579 }
579580
580581 /// Append a value to the list `n` times.
lib/std/atomic.zig+21-17
......@@ -46,34 +46,38 @@ test "fence/compilerFence" {
4646
4747/// Signals to the processor that the caller is inside a busy-wait spin-loop.
4848pub inline fn spinLoopHint() void {
49 const hint_instruction = switch (target.cpu.arch) {
50 // No-op instruction that can hint to save (or share with a hardware-thread) pipelining/power resources
49 switch (target.cpu.arch) {
50 // No-op instruction that can hint to save (or share with a hardware-thread)
51 // pipelining/power resources
5152 // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html
52 .i386, .x86_64 => "pause",
53 .i386, .x86_64 => asm volatile ("pause" ::: "memory"),
5354
5455 // No-op instruction that serves as a hardware-thread resource yield hint.
5556 // https://stackoverflow.com/a/7588941
56 .powerpc64, .powerpc64le => "or 27, 27, 27",
57 .powerpc64, .powerpc64le => asm volatile ("or 27, 27, 27" ::: "memory"),
5758
58 // `isb` appears more reliable for releasing execution resources than `yield` on common aarch64 CPUs.
59 // `isb` appears more reliable for releasing execution resources than `yield`
60 // on common aarch64 CPUs.
5961 // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
6062 // https://bugs.mysql.com/bug.php?id=100664
61 .aarch64, .aarch64_be, .aarch64_32 => "isb",
63 .aarch64, .aarch64_be, .aarch64_32 => asm volatile ("isb" ::: "memory"),
6264
6365 // `yield` was introduced in v6k but is also available on v6m.
6466 // https://www.keil.com/support/man/docs/armasm/armasm_dom1361289926796.htm
65 .arm, .armeb, .thumb, .thumbeb => blk: {
66 const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{ .has_v6k, .has_v6m });
67 const instruction = if (can_yield) "yield" else "";
68 break :blk instruction;
67 .arm, .armeb, .thumb, .thumbeb => {
68 const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{
69 .has_v6k, .has_v6m,
70 });
71 if (can_yield) {
72 asm volatile ("yield" ::: "memory");
73 } else {
74 asm volatile ("" ::: "memory");
75 }
6976 },
70
71 else => "",
72 };
73
74 // Memory barrier to prevent the compiler from optimizing away the spin-loop
75 // even if no hint_instruction was provided.
76 asm volatile (hint_instruction ::: "memory");
77 // Memory barrier to prevent the compiler from optimizing away the spin-loop
78 // even if no hint_instruction was provided.
79 else => asm volatile ("" ::: "memory"),
80 }
7781}
7882
7983test "spinLoopHint" {
lib/std/atomic/Atomic.zig+70-18
......@@ -178,26 +178,78 @@ pub fn Atomic(comptime T: type) type {
178178 ) u1 {
179179 // x86 supports dedicated bitwise instructions
180180 if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) {
181 const instruction = switch (op) {
182 .Set => "lock bts",
183 .Reset => "lock btr",
184 .Toggle => "lock btc",
185 };
186
187 const suffix = switch (@sizeOf(T)) {
188 2 => "w",
189 4 => "l",
190 8 => "q",
181 const old_bit: u8 = switch (@sizeOf(T)) {
182 2 => switch (op) {
183 .Set => asm volatile ("lock btsw %[bit], %[ptr]"
184 // LLVM doesn't support u1 flag register return values
185 : [result] "={@ccc}" (-> u8)
186 : [ptr] "*p" (&self.value),
187 [bit] "X" (@as(T, bit))
188 : "cc", "memory"
189 ),
190 .Reset => asm volatile ("lock btrw %[bit], %[ptr]"
191 // LLVM doesn't support u1 flag register return values
192 : [result] "={@ccc}" (-> u8)
193 : [ptr] "*p" (&self.value),
194 [bit] "X" (@as(T, bit))
195 : "cc", "memory"
196 ),
197 .Toggle => asm volatile ("lock btcw %[bit], %[ptr]"
198 // LLVM doesn't support u1 flag register return values
199 : [result] "={@ccc}" (-> u8)
200 : [ptr] "*p" (&self.value),
201 [bit] "X" (@as(T, bit))
202 : "cc", "memory"
203 ),
204 },
205 4 => switch (op) {
206 .Set => asm volatile ("lock btsl %[bit], %[ptr]"
207 // LLVM doesn't support u1 flag register return values
208 : [result] "={@ccc}" (-> u8)
209 : [ptr] "*p" (&self.value),
210 [bit] "X" (@as(T, bit))
211 : "cc", "memory"
212 ),
213 .Reset => asm volatile ("lock btrl %[bit], %[ptr]"
214 // LLVM doesn't support u1 flag register return values
215 : [result] "={@ccc}" (-> u8)
216 : [ptr] "*p" (&self.value),
217 [bit] "X" (@as(T, bit))
218 : "cc", "memory"
219 ),
220 .Toggle => asm volatile ("lock btcl %[bit], %[ptr]"
221 // LLVM doesn't support u1 flag register return values
222 : [result] "={@ccc}" (-> u8)
223 : [ptr] "*p" (&self.value),
224 [bit] "X" (@as(T, bit))
225 : "cc", "memory"
226 ),
227 },
228 8 => switch (op) {
229 .Set => asm volatile ("lock btsq %[bit], %[ptr]"
230 // LLVM doesn't support u1 flag register return values
231 : [result] "={@ccc}" (-> u8)
232 : [ptr] "*p" (&self.value),
233 [bit] "X" (@as(T, bit))
234 : "cc", "memory"
235 ),
236 .Reset => asm volatile ("lock btrq %[bit], %[ptr]"
237 // LLVM doesn't support u1 flag register return values
238 : [result] "={@ccc}" (-> u8)
239 : [ptr] "*p" (&self.value),
240 [bit] "X" (@as(T, bit))
241 : "cc", "memory"
242 ),
243 .Toggle => asm volatile ("lock btcq %[bit], %[ptr]"
244 // LLVM doesn't support u1 flag register return values
245 : [result] "={@ccc}" (-> u8)
246 : [ptr] "*p" (&self.value),
247 [bit] "X" (@as(T, bit))
248 : "cc", "memory"
249 ),
250 },
191251 else => @compileError("Invalid atomic type " ++ @typeName(T)),
192252 };
193
194 const old_bit = asm volatile (instruction ++ suffix ++ " %[bit], %[ptr]"
195 : [result] "={@ccc}" (-> u8) // LLVM doesn't support u1 flag register return values
196 : [ptr] "*p" (&self.value),
197 [bit] "X" (@as(T, bit))
198 : "cc", "memory"
199 );
200
201253 return @intCast(u1, old_bit);
202254 }
203255
lib/std/hash/auto_hash.zig+1-4
......@@ -122,12 +122,9 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
122122 .Array => hashArray(hasher, key, strat),
123123
124124 .Vector => |info| {
125 if (std.meta.bitCount(info.child) % 8 == 0) {
126 // If there's no unused bits in the child type, we can just hash
127 // this as an array of bytes.
125 if (comptime meta.trait.hasUniqueRepresentation(Key)) {
128126 hasher.update(mem.asBytes(&key));
129127 } else {
130 // Otherwise, hash every element.
131128 comptime var i = 0;
132129 inline while (i < info.len) : (i += 1) {
133130 hash(hasher, key[i], strat);
lib/std/json.zig+4-4
......@@ -2590,7 +2590,7 @@ test "write json then parse it" {
25902590 try testing.expect(mem.eql(u8, tree.root.Object.get("str").?.String, "hello"));
25912591}
25922592
2593fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value {
2593fn testParse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value {
25942594 var p = Parser.init(arena_allocator, false);
25952595 return (try p.parse(json_str)).root;
25962596}
......@@ -2598,13 +2598,13 @@ fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value
25982598test "parsing empty string gives appropriate error" {
25992599 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
26002600 defer arena_allocator.deinit();
2601 try testing.expectError(error.UnexpectedEndOfJson, test_parse(&arena_allocator.allocator, ""));
2601 try testing.expectError(error.UnexpectedEndOfJson, testParse(&arena_allocator.allocator, ""));
26022602}
26032603
26042604test "integer after float has proper type" {
26052605 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
26062606 defer arena_allocator.deinit();
2607 const json = try test_parse(&arena_allocator.allocator,
2607 const json = try testParse(&arena_allocator.allocator,
26082608 \\{
26092609 \\ "float": 3.14,
26102610 \\ "ints": [1, 2, 3]
......@@ -2639,7 +2639,7 @@ test "escaped characters" {
26392639 \\}
26402640 ;
26412641
2642 const obj = (try test_parse(&arena_allocator.allocator, input)).Object;
2642 const obj = (try testParse(&arena_allocator.allocator, input)).Object;
26432643
26442644 try testing.expectEqualSlices(u8, obj.get("backslash").?.String, "\\");
26452645 try testing.expectEqualSlices(u8, obj.get("forwardslash").?.String, "/");
lib/std/math/hypot.zig+1-1
......@@ -14,7 +14,7 @@ const math = std.math;
1414const expect = std.testing.expect;
1515const maxInt = std.math.maxInt;
1616
17/// Returns sqrt(x * x + y * y), avoiding unncessary overflow and underflow.
17/// Returns sqrt(x * x + y * y), avoiding unnecessary overflow and underflow.
1818///
1919/// Special Cases:
2020/// - hypot(+-inf, y) = +inf
lib/std/meta/trait.zig+4-1
......@@ -582,7 +582,7 @@ pub fn hasUniqueRepresentation(comptime T: type) bool {
582582 return @sizeOf(T) == sum_size;
583583 },
584584
585 .Vector => |info| return comptime hasUniqueRepresentation(info.child),
585 .Vector => |info| return comptime hasUniqueRepresentation(info.child) and @sizeOf(T) == @sizeOf(info.child) * info.len,
586586 }
587587}
588588
......@@ -653,4 +653,7 @@ test "std.meta.trait.hasUniqueRepresentation" {
653653
654654 try testing.expect(!hasUniqueRepresentation([]u8));
655655 try testing.expect(!hasUniqueRepresentation([]const u8));
656
657 try testing.expect(hasUniqueRepresentation(@Vector(4, u16)));
658 try testing.expect(!hasUniqueRepresentation(@Vector(3, u16)));
656659}
lib/std/os.zig-1
......@@ -385,7 +385,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
385385 else => |err| return unexpectedErrno(err),
386386 }
387387 }
388 return index;
389388}
390389
391390/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
lib/std/unicode.zig+43-2
......@@ -317,9 +317,9 @@ pub const Utf16LeIterator = struct {
317317 assert(it.i <= it.bytes.len);
318318 if (it.i == it.bytes.len) return null;
319319 const c0: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]);
320 it.i += 2;
320321 if (c0 & ~@as(u21, 0x03ff) == 0xd800) {
321322 // surrogate pair
322 it.i += 2;
323323 if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
324324 const c1: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]);
325325 if (c1 & ~@as(u21, 0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
......@@ -328,7 +328,6 @@ pub const Utf16LeIterator = struct {
328328 } else if (c0 & ~@as(u21, 0x03ff) == 0xdc00) {
329329 return error.UnexpectedSecondSurrogateHalf;
330330 } else {
331 it.i += 2;
332331 return c0;
333332 }
334333 }
......@@ -769,6 +768,48 @@ fn calcUtf16LeLen(utf8: []const u8) usize {
769768 return dest_len;
770769}
771770
771/// Print the given `utf16le` string
772fn formatUtf16le(
773 utf16le: []const u16,
774 comptime fmt: []const u8,
775 options: std.fmt.FormatOptions,
776 writer: anytype,
777) !void {
778 const unknown_codepoint = 0xfffd;
779 _ = fmt;
780 _ = options;
781 var buf: [300]u8 = undefined; // just a random size I chose
782 var it = Utf16LeIterator.init(utf16le);
783 var u8len: usize = 0;
784 while (it.nextCodepoint() catch unknown_codepoint) |codepoint| {
785 u8len += utf8Encode(codepoint, buf[u8len..]) catch
786 utf8Encode(unknown_codepoint, buf[u8len..]) catch unreachable;
787 if (u8len + 3 >= buf.len) {
788 try writer.writeAll(buf[0..u8len]);
789 u8len = 0;
790 }
791 }
792 try writer.writeAll(buf[0..u8len]);
793}
794
795/// Return a Formatter for a Utf16le string
796pub fn fmtUtf16le(utf16le: []const u16) std.fmt.Formatter(formatUtf16le) {
797 return .{ .data = utf16le };
798}
799
800test "fmtUtf16le" {
801 const expectFmt = std.testing.expectFmt;
802 try expectFmt("", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral(""))});
803 try expectFmt("foo", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral("foo"))});
804 try expectFmt("𐐷", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral("𐐷"))});
805 try expectFmt("퟿", "{}", .{fmtUtf16le(&[_]u16{std.mem.readIntNative(u16, "\xff\xd7")})});
806 try expectFmt("�", "{}", .{fmtUtf16le(&[_]u16{std.mem.readIntNative(u16, "\x00\xd8")})});
807 try expectFmt("�", "{}", .{fmtUtf16le(&[_]u16{std.mem.readIntNative(u16, "\xff\xdb")})});
808 try expectFmt("�", "{}", .{fmtUtf16le(&[_]u16{std.mem.readIntNative(u16, "\x00\xdc")})});
809 try expectFmt("�", "{}", .{fmtUtf16le(&[_]u16{std.mem.readIntNative(u16, "\xff\xdf")})});
810 try expectFmt("", "{}", .{fmtUtf16le(&[_]u16{std.mem.readIntNative(u16, "\x00\xe0")})});
811}
812
772813test "utf8ToUtf16LeStringLiteral" {
773814 {
774815 const bytes = [_:0]u16{
lib/std/zig/fmt.zig+1
......@@ -23,6 +23,7 @@ pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {
2323}
2424
2525pub fn isValidId(bytes: []const u8) bool {
26 if (mem.eql(u8, bytes, "_")) return false;
2627 for (bytes) |c, i| {
2728 switch (c) {
2829 '_', 'a'...'z', 'A'...'Z' => {},
src/Air.zig created+546
......@@ -0,0 +1,546 @@
1//! Analyzed Intermediate Representation.
2//! This data is produced by Sema and consumed by codegen.
3//! Unlike ZIR where there is one instance for an entire source file, each function
4//! gets its own `Air` instance.
5
6const std = @import("std");
7const Value = @import("value.zig").Value;
8const Type = @import("type.zig").Type;
9const Module = @import("Module.zig");
10const assert = std.debug.assert;
11const Air = @This();
12
13instructions: std.MultiArrayList(Inst).Slice,
14/// The meaning of this data is determined by `Inst.Tag` value.
15/// The first few indexes are reserved. See `ExtraIndex` for the values.
16extra: []const u32,
17values: []const Value,
18variables: []const *Module.Var,
19
20pub const ExtraIndex = enum(u32) {
21 /// Payload index of the main `Block` in the `extra` array.
22 main_block,
23
24 _,
25};
26
27pub const Inst = struct {
28 tag: Tag,
29 data: Data,
30
31 pub const Tag = enum(u8) {
32 /// The first N instructions in the main block must be one arg instruction per
33 /// function parameter. This makes function parameters participate in
34 /// liveness analysis without any special handling.
35 /// Uses the `ty_str` field.
36 /// The string is the parameter name.
37 arg,
38 /// Float or integer addition. For integers, wrapping is undefined behavior.
39 /// Both operands are guaranteed to be the same type, and the result type
40 /// is the same as both operands.
41 /// Uses the `bin_op` field.
42 add,
43 /// Integer addition. Wrapping is defined to be twos complement wrapping.
44 /// Both operands are guaranteed to be the same type, and the result type
45 /// is the same as both operands.
46 /// Uses the `bin_op` field.
47 addwrap,
48 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
49 /// Both operands are guaranteed to be the same type, and the result type
50 /// is the same as both operands.
51 /// Uses the `bin_op` field.
52 sub,
53 /// Integer subtraction. Wrapping is defined to be twos complement wrapping.
54 /// Both operands are guaranteed to be the same type, and the result type
55 /// is the same as both operands.
56 /// Uses the `bin_op` field.
57 subwrap,
58 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
59 /// Both operands are guaranteed to be the same type, and the result type
60 /// is the same as both operands.
61 /// Uses the `bin_op` field.
62 mul,
63 /// Integer multiplication. Wrapping is defined to be twos complement wrapping.
64 /// Both operands are guaranteed to be the same type, and the result type
65 /// is the same as both operands.
66 /// Uses the `bin_op` field.
67 mulwrap,
68 /// Integer or float division. For integers, wrapping is undefined behavior.
69 /// Both operands are guaranteed to be the same type, and the result type
70 /// is the same as both operands.
71 /// Uses the `bin_op` field.
72 div,
73 /// Allocates stack local memory.
74 /// Uses the `ty` field.
75 alloc,
76 /// Inline assembly. Uses the `ty_pl` field. Payload is `Asm`.
77 assembly,
78 /// Bitwise AND. `&`.
79 /// Result type is the same as both operands.
80 /// Uses the `bin_op` field.
81 bit_and,
82 /// Bitwise OR. `|`.
83 /// Result type is the same as both operands.
84 /// Uses the `bin_op` field.
85 bit_or,
86 /// Bitwise XOR. `^`
87 /// Uses the `bin_op` field.
88 xor,
89 /// Boolean or binary NOT.
90 /// Uses the `ty_op` field.
91 not,
92 /// Reinterpret the memory representation of a value as a different type.
93 /// Uses the `ty_op` field.
94 bitcast,
95 /// Uses the `ty_pl` field with payload `Block`.
96 block,
97 /// A labeled block of code that loops forever. At the end of the body it is implied
98 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
99 /// Result type is always noreturn; no instructions in a block follow this one.
100 /// Uses the `ty_pl` field. Payload is `Block`.
101 loop,
102 /// Return from a block with a result.
103 /// Result type is always noreturn; no instructions in a block follow this one.
104 /// Uses the `br` field.
105 br,
106 /// Lowers to a hardware trap instruction, or the next best thing.
107 /// Result type is always void.
108 breakpoint,
109 /// Function call.
110 /// Result type is the return type of the function being called.
111 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
112 call,
113 /// `<`. Result type is always bool.
114 /// Uses the `bin_op` field.
115 cmp_lt,
116 /// `<=`. Result type is always bool.
117 /// Uses the `bin_op` field.
118 cmp_lte,
119 /// `==`. Result type is always bool.
120 /// Uses the `bin_op` field.
121 cmp_eq,
122 /// `>=`. Result type is always bool.
123 /// Uses the `bin_op` field.
124 cmp_gte,
125 /// `>`. Result type is always bool.
126 /// Uses the `bin_op` field.
127 cmp_gt,
128 /// `!=`. Result type is always bool.
129 /// Uses the `bin_op` field.
130 cmp_neq,
131 /// Conditional branch.
132 /// Result type is always noreturn; no instructions in a block follow this one.
133 /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`.
134 cond_br,
135 /// Switch branch.
136 /// Result type is always noreturn; no instructions in a block follow this one.
137 /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.
138 switch_br,
139 /// A comptime-known value. Uses the `ty_pl` field, payload is index of
140 /// `values` array.
141 constant,
142 /// A comptime-known type. Uses the `ty` field.
143 const_ty,
144 /// Notes the beginning of a source code statement and marks the line and column.
145 /// Result type is always void.
146 /// Uses the `dbg_stmt` field.
147 dbg_stmt,
148 /// ?T => bool
149 /// Result type is always bool.
150 /// Uses the `un_op` field.
151 is_null,
152 /// ?T => bool (inverted logic)
153 /// Result type is always bool.
154 /// Uses the `un_op` field.
155 is_non_null,
156 /// *?T => bool
157 /// Result type is always bool.
158 /// Uses the `un_op` field.
159 is_null_ptr,
160 /// *?T => bool (inverted logic)
161 /// Result type is always bool.
162 /// Uses the `un_op` field.
163 is_non_null_ptr,
164 /// E!T => bool
165 /// Result type is always bool.
166 /// Uses the `un_op` field.
167 is_err,
168 /// E!T => bool (inverted logic)
169 /// Result type is always bool.
170 /// Uses the `un_op` field.
171 is_non_err,
172 /// *E!T => bool
173 /// Result type is always bool.
174 /// Uses the `un_op` field.
175 is_err_ptr,
176 /// *E!T => bool (inverted logic)
177 /// Result type is always bool.
178 /// Uses the `un_op` field.
179 is_non_err_ptr,
180 /// Result type is always bool.
181 /// Uses the `bin_op` field.
182 bool_and,
183 /// Result type is always bool.
184 /// Uses the `bin_op` field.
185 bool_or,
186 /// Read a value from a pointer.
187 /// Uses the `ty_op` field.
188 load,
189 /// Converts a pointer to its address. Result type is always `usize`.
190 /// Uses the `un_op` field.
191 ptrtoint,
192 /// Stores a value onto the stack and returns a pointer to it.
193 /// TODO audit where this AIR instruction is emitted, maybe it should instead be emitting
194 /// alloca instruction and storing to the alloca.
195 /// Uses the `ty_op` field.
196 ref,
197 /// Return a value from a function.
198 /// Result type is always noreturn; no instructions in a block follow this one.
199 /// Uses the `un_op` field.
200 ret,
201 /// Returns a pointer to a global variable.
202 /// Uses the `ty_pl` field. Index is into the `variables` array.
203 /// TODO this can be modeled simply as a constant with a decl ref and then
204 /// the variables array can be removed from Air.
205 varptr,
206 /// Write a value to a pointer. LHS is pointer, RHS is value.
207 /// Result type is always void.
208 /// Uses the `bin_op` field.
209 store,
210 /// Indicates the program counter will never get to this instruction.
211 /// Result type is always noreturn; no instructions in a block follow this one.
212 unreach,
213 /// Convert from one float type to another.
214 /// Uses the `ty_op` field.
215 floatcast,
216 /// TODO audit uses of this. We should have explicit instructions for integer
217 /// widening and truncating.
218 /// Uses the `ty_op` field.
219 intcast,
220 /// ?T => T. If the value is null, undefined behavior.
221 /// Uses the `ty_op` field.
222 optional_payload,
223 /// *?T => *T. If the value is null, undefined behavior.
224 /// Uses the `ty_op` field.
225 optional_payload_ptr,
226 /// Given a payload value, wraps it in an optional type.
227 /// Uses the `ty_op` field.
228 wrap_optional,
229 /// E!T -> T. If the value is an error, undefined behavior.
230 /// Uses the `ty_op` field.
231 unwrap_errunion_payload,
232 /// E!T -> E. If the value is not an error, undefined behavior.
233 /// Uses the `ty_op` field.
234 unwrap_errunion_err,
235 /// *(E!T) -> *T. If the value is an error, undefined behavior.
236 /// Uses the `ty_op` field.
237 unwrap_errunion_payload_ptr,
238 /// *(E!T) -> E. If the value is not an error, undefined behavior.
239 /// Uses the `ty_op` field.
240 unwrap_errunion_err_ptr,
241 /// wrap from T to E!T
242 /// Uses the `ty_op` field.
243 wrap_errunion_payload,
244 /// wrap from E to E!T
245 /// Uses the `ty_op` field.
246 wrap_errunion_err,
247 /// Given a pointer to a struct and a field index, returns a pointer to the field.
248 /// Uses the `ty_pl` field, payload is `StructField`.
249 struct_field_ptr,
250
251 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
252 return switch (op) {
253 .lt => .cmp_lt,
254 .lte => .cmp_lte,
255 .eq => .cmp_eq,
256 .gte => .cmp_gte,
257 .gt => .cmp_gt,
258 .neq => .cmp_neq,
259 };
260 }
261
262 pub fn toCmpOp(tag: Tag) ?std.math.CompareOperator {
263 return switch (tag) {
264 .cmp_lt => .lt,
265 .cmp_lte => .lte,
266 .cmp_eq => .eq,
267 .cmp_gte => .gte,
268 .cmp_gt => .gt,
269 .cmp_neq => .neq,
270 else => null,
271 };
272 }
273 };
274
275 /// The position of an AIR instruction within the `Air` instructions array.
276 pub const Index = u32;
277
278 pub const Ref = @import("Zir.zig").Inst.Ref;
279
280 /// All instructions have an 8-byte payload, which is contained within
281 /// this union. `Tag` determines which union field is active, as well as
282 /// how to interpret the data within.
283 pub const Data = union {
284 no_op: void,
285 un_op: Ref,
286 bin_op: struct {
287 lhs: Ref,
288 rhs: Ref,
289 },
290 ty: Type,
291 ty_op: struct {
292 ty: Ref,
293 operand: Ref,
294 },
295 ty_pl: struct {
296 ty: Ref,
297 // Index into a different array.
298 payload: u32,
299 },
300 ty_str: struct {
301 ty: Ref,
302 // ZIR string table index.
303 str: u32,
304 },
305 br: struct {
306 block_inst: Index,
307 operand: Ref,
308 },
309 pl_op: struct {
310 operand: Ref,
311 payload: u32,
312 },
313 dbg_stmt: struct {
314 line: u32,
315 column: u32,
316 },
317
318 // Make sure we don't accidentally add a field to make this union
319 // bigger than expected. Note that in Debug builds, Zig is allowed
320 // to insert a secret field for safety checks.
321 comptime {
322 if (std.builtin.mode != .Debug) {
323 assert(@sizeOf(Data) == 8);
324 }
325 }
326 };
327};
328
329/// Trailing is a list of instruction indexes for every `body_len`.
330pub const Block = struct {
331 body_len: u32,
332};
333
334/// Trailing is a list of `Inst.Ref` for every `args_len`.
335pub const Call = struct {
336 args_len: u32,
337};
338
339/// This data is stored inside extra, with two sets of trailing `Inst.Ref`:
340/// * 0. the then body, according to `then_body_len`.
341/// * 1. the else body, according to `else_body_len`.
342pub const CondBr = struct {
343 then_body_len: u32,
344 else_body_len: u32,
345};
346
347/// Trailing:
348/// * 0. `Case` for each `cases_len`
349/// * 1. the else body, according to `else_body_len`.
350pub const SwitchBr = struct {
351 cases_len: u32,
352 else_body_len: u32,
353
354 /// Trailing:
355 /// * item: Inst.Ref // for each `items_len`.
356 /// * instruction index for each `body_len`.
357 pub const Case = struct {
358 items_len: u32,
359 body_len: u32,
360 };
361};
362
363pub const StructField = struct {
364 struct_ptr: Inst.Ref,
365 field_index: u32,
366};
367
368/// Trailing:
369/// 0. `Inst.Ref` for every outputs_len
370/// 1. `Inst.Ref` for every inputs_len
371pub const Asm = struct {
372 /// Index to the corresponding ZIR instruction.
373 /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and
374 /// clobbers are found via here.
375 zir_index: u32,
376};
377
378pub fn getMainBody(air: Air) []const Air.Inst.Index {
379 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
380 const extra = air.extraData(Block, body_index);
381 return air.extra[extra.end..][0..extra.data.body_len];
382}
383
384pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {
385 const ref_int = @enumToInt(inst);
386 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
387 return Air.Inst.Ref.typed_value_map[ref_int].ty;
388 }
389 return air.typeOfIndex(@intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len));
390}
391
392pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
393 const datas = air.instructions.items(.data);
394 switch (air.instructions.items(.tag)[inst]) {
395 .arg => return air.getRefType(datas[inst].ty_str.ty),
396
397 .add,
398 .addwrap,
399 .sub,
400 .subwrap,
401 .mul,
402 .mulwrap,
403 .div,
404 .bit_and,
405 .bit_or,
406 .xor,
407 => return air.typeOf(datas[inst].bin_op.lhs),
408
409 .cmp_lt,
410 .cmp_lte,
411 .cmp_eq,
412 .cmp_gte,
413 .cmp_gt,
414 .cmp_neq,
415 .is_null,
416 .is_non_null,
417 .is_null_ptr,
418 .is_non_null_ptr,
419 .is_err,
420 .is_non_err,
421 .is_err_ptr,
422 .is_non_err_ptr,
423 .bool_and,
424 .bool_or,
425 => return Type.initTag(.bool),
426
427 .const_ty => return Type.initTag(.type),
428
429 .alloc => return datas[inst].ty,
430
431 .assembly,
432 .block,
433 .constant,
434 .varptr,
435 .struct_field_ptr,
436 => return air.getRefType(datas[inst].ty_pl.ty),
437
438 .not,
439 .bitcast,
440 .load,
441 .ref,
442 .floatcast,
443 .intcast,
444 .optional_payload,
445 .optional_payload_ptr,
446 .wrap_optional,
447 .unwrap_errunion_payload,
448 .unwrap_errunion_err,
449 .unwrap_errunion_payload_ptr,
450 .unwrap_errunion_err_ptr,
451 .wrap_errunion_payload,
452 .wrap_errunion_err,
453 => return air.getRefType(datas[inst].ty_op.ty),
454
455 .loop,
456 .br,
457 .cond_br,
458 .switch_br,
459 .ret,
460 .unreach,
461 => return Type.initTag(.noreturn),
462
463 .breakpoint,
464 .dbg_stmt,
465 .store,
466 => return Type.initTag(.void),
467
468 .ptrtoint => return Type.initTag(.usize),
469
470 .call => {
471 const callee_ty = air.typeOf(datas[inst].pl_op.operand);
472 return callee_ty.fnReturnType();
473 },
474 }
475}
476
477pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type {
478 const ref_int = @enumToInt(ref);
479 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
480 return Air.Inst.Ref.typed_value_map[ref_int].val.toType(undefined) catch unreachable;
481 }
482 const inst_index = ref_int - Air.Inst.Ref.typed_value_map.len;
483 const air_tags = air.instructions.items(.tag);
484 const air_datas = air.instructions.items(.data);
485 assert(air_tags[inst_index] == .const_ty);
486 return air_datas[inst_index].ty;
487}
488
489/// Returns the requested data, as well as the new index which is at the start of the
490/// trailers for the object.
491pub fn extraData(air: Air, comptime T: type, index: usize) struct { data: T, end: usize } {
492 const fields = std.meta.fields(T);
493 var i: usize = index;
494 var result: T = undefined;
495 inline for (fields) |field| {
496 @field(result, field.name) = switch (field.field_type) {
497 u32 => air.extra[i],
498 Inst.Ref => @intToEnum(Inst.Ref, air.extra[i]),
499 i32 => @bitCast(i32, air.extra[i]),
500 else => @compileError("bad field type"),
501 };
502 i += 1;
503 }
504 return .{
505 .data = result,
506 .end = i,
507 };
508}
509
510pub fn deinit(air: *Air, gpa: *std.mem.Allocator) void {
511 air.instructions.deinit(gpa);
512 gpa.free(air.extra);
513 gpa.free(air.values);
514 gpa.free(air.variables);
515 air.* = undefined;
516}
517
518const ref_start_index: u32 = Air.Inst.Ref.typed_value_map.len;
519
520pub fn indexToRef(inst: Air.Inst.Index) Air.Inst.Ref {
521 return @intToEnum(Air.Inst.Ref, ref_start_index + inst);
522}
523
524pub fn refToIndex(inst: Air.Inst.Ref) ?Air.Inst.Index {
525 const ref_int = @enumToInt(inst);
526 if (ref_int >= ref_start_index) {
527 return ref_int - ref_start_index;
528 } else {
529 return null;
530 }
531}
532
533/// Returns `null` if runtime-known.
534pub fn value(air: Air, inst: Air.Inst.Ref) ?Value {
535 const ref_int = @enumToInt(inst);
536 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
537 return Air.Inst.Ref.typed_value_map[ref_int].val;
538 }
539 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
540 const air_datas = air.instructions.items(.data);
541 switch (air.instructions.items(.tag)[inst_index]) {
542 .constant => return air.values[air_datas[inst_index].ty_pl.payload],
543 .const_ty => unreachable,
544 else => return air.typeOfIndex(inst_index).onePossibleValue(),
545 }
546}
src/AstGen.zig+118-109
......@@ -36,7 +36,7 @@ compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
3636fn_block: ?*GenZir = null,
3737/// Maps string table indexes to the first `@import` ZIR instruction
3838/// that uses this string as the operand.
39imports: std.AutoArrayHashMapUnmanaged(u32, Zir.Inst.Index) = .{},
39imports: std.AutoArrayHashMapUnmanaged(u32, ast.TokenIndex) = .{},
4040
4141const InnerError = error{ OutOfMemory, AnalysisFail };
4242
......@@ -132,8 +132,7 @@ pub fn generate(gpa: *Allocator, tree: ast.Tree) Allocator.Error!Zir {
132132 if (astgen.compile_errors.items.len == 0) {
133133 astgen.extra.items[err_index] = 0;
134134 } else {
135 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
136 1 + astgen.compile_errors.items.len *
135 try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
137136 @typeInfo(Zir.Inst.CompileErrors.Item).Struct.fields.len);
138137
139138 astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
......@@ -149,13 +148,20 @@ pub fn generate(gpa: *Allocator, tree: ast.Tree) Allocator.Error!Zir {
149148 if (astgen.imports.count() == 0) {
150149 astgen.extra.items[imports_index] = 0;
151150 } else {
152 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
153 @typeInfo(Zir.Inst.Imports).Struct.fields.len + astgen.imports.count());
151 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).Struct.fields.len +
152 astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).Struct.fields.len);
154153
155154 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
156155 .imports_len = @intCast(u32, astgen.imports.count()),
157156 });
158 astgen.extra.appendSliceAssumeCapacity(astgen.imports.values());
157
158 var it = astgen.imports.iterator();
159 while (it.next()) |entry| {
160 _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
161 .name = entry.key_ptr.*,
162 .token = entry.value_ptr.*,
163 });
164 }
159165 }
160166
161167 return Zir{
......@@ -983,7 +989,7 @@ fn suspendExpr(
983989 }
984990 try suspend_scope.setBlockBody(suspend_inst);
985991
986 return gz.indexToRef(suspend_inst);
992 return indexToRef(suspend_inst);
987993}
988994
989995fn awaitExpr(
......@@ -1294,7 +1300,7 @@ fn arrayInitExprRlPtr(
12941300 .lhs = result_ptr,
12951301 .rhs = index_inst,
12961302 });
1297 elem_ptr_list[i] = gz.refToIndex(elem_ptr).?;
1303 elem_ptr_list[i] = refToIndex(elem_ptr).?;
12981304 _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
12991305 }
13001306 _ = try gz.addPlNode(.validate_array_init_ptr, node, Zir.Inst.Block{
......@@ -1449,7 +1455,7 @@ fn structInitExprRlPtr(
14491455 .lhs = result_ptr,
14501456 .field_name_start = str_index,
14511457 });
1452 field_ptr_list[i] = gz.refToIndex(field_ptr).?;
1458 field_ptr_list[i] = refToIndex(field_ptr).?;
14531459 _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
14541460 }
14551461 _ = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{
......@@ -1483,7 +1489,7 @@ fn structInitExprRlTy(
14831489 .name_start = str_index,
14841490 });
14851491 fields_list[i] = .{
1486 .field_type = gz.refToIndex(field_ty_inst).?,
1492 .field_type = refToIndex(field_ty_inst).?,
14871493 .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init),
14881494 };
14891495 }
......@@ -1780,7 +1786,7 @@ fn labeledBlockExpr(
17801786 }
17811787 try block_scope.setBlockBody(block_inst);
17821788
1783 return gz.indexToRef(block_inst);
1789 return indexToRef(block_inst);
17841790 },
17851791 .break_operand => {
17861792 // All break operands are values that did not use the result location pointer.
......@@ -1794,7 +1800,7 @@ fn labeledBlockExpr(
17941800 } else {
17951801 try block_scope.setBlockBody(block_inst);
17961802 }
1797 const block_ref = gz.indexToRef(block_inst);
1803 const block_ref = indexToRef(block_inst);
17981804 switch (rl) {
17991805 .ref => return block_ref,
18001806 else => return rvalue(gz, rl, block_ref, block_node),
......@@ -1872,7 +1878,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
18721878 // we want to avoid adding the ZIR instruction if possible for performance.
18731879 const maybe_unused_result = try expr(gz, scope, .none, statement);
18741880 var noreturn_src_node: ast.Node.Index = 0;
1875 const elide_check = if (gz.refToIndex(maybe_unused_result)) |inst| b: {
1881 const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
18761882 // Note that this array becomes invalid after appending more items to it
18771883 // in the above while loop.
18781884 const zir_tags = gz.astgen.instructions.items(.tag);
......@@ -1916,8 +1922,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
19161922 .bool_br_and,
19171923 .bool_br_or,
19181924 .bool_not,
1919 .bool_and,
1920 .bool_or,
19211925 .call_compile_time,
19221926 .call_nosuspend,
19231927 .call_async,
......@@ -2434,7 +2438,7 @@ fn varDecl(
24342438 // the alloc instruction and the store_to_block_ptr instruction.
24352439 try parent_zir.ensureUnusedCapacity(gpa, init_scope.instructions.items.len);
24362440 for (init_scope.instructions.items) |src_inst| {
2437 if (gz.indexToRef(src_inst) == init_scope.rl_ptr) continue;
2441 if (indexToRef(src_inst) == init_scope.rl_ptr) continue;
24382442 if (zir_tags[src_inst] == .store_to_block_ptr) {
24392443 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
24402444 }
......@@ -2737,7 +2741,7 @@ fn ptrType(
27372741 }
27382742
27392743 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
2740 const result = gz.indexToRef(new_index);
2744 const result = indexToRef(new_index);
27412745 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
27422746 .ptr_type = .{
27432747 .flags = .{
......@@ -3467,7 +3471,7 @@ fn structDeclInner(
34673471 .body_len = 0,
34683472 .decls_len = 0,
34693473 });
3470 return gz.indexToRef(decl_inst);
3474 return indexToRef(decl_inst);
34713475 }
34723476
34733477 const astgen = gz.astgen;
......@@ -3486,7 +3490,6 @@ fn structDeclInner(
34863490 .astgen = astgen,
34873491 .force_comptime = true,
34883492 .in_defer = false,
3489 .ref_start_index = gz.ref_start_index,
34903493 };
34913494 defer block_scope.instructions.deinit(gpa);
34923495
......@@ -3724,7 +3727,7 @@ fn structDeclInner(
37243727 }
37253728 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
37263729
3727 return gz.indexToRef(decl_inst);
3730 return indexToRef(decl_inst);
37283731}
37293732
37303733fn unionDeclInner(
......@@ -3752,7 +3755,6 @@ fn unionDeclInner(
37523755 .astgen = astgen,
37533756 .force_comptime = true,
37543757 .in_defer = false,
3755 .ref_start_index = gz.ref_start_index,
37563758 };
37573759 defer block_scope.instructions.deinit(gpa);
37583760
......@@ -4000,7 +4002,7 @@ fn unionDeclInner(
40004002 astgen.extra.appendAssumeCapacity(cur_bit_bag);
40014003 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
40024004
4003 return gz.indexToRef(decl_inst);
4005 return indexToRef(decl_inst);
40044006}
40054007
40064008fn containerDecl(
......@@ -4164,7 +4166,6 @@ fn containerDecl(
41644166 .astgen = astgen,
41654167 .force_comptime = true,
41664168 .in_defer = false,
4167 .ref_start_index = gz.ref_start_index,
41684169 };
41694170 defer block_scope.instructions.deinit(gpa);
41704171
......@@ -4392,7 +4393,7 @@ fn containerDecl(
43924393 astgen.extra.appendAssumeCapacity(cur_bit_bag);
43934394 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
43944395
4395 return rvalue(gz, rl, gz.indexToRef(decl_inst), node);
4396 return rvalue(gz, rl, indexToRef(decl_inst), node);
43964397 },
43974398 .keyword_opaque => {
43984399 var namespace: Scope.Namespace = .{ .parent = scope };
......@@ -4553,7 +4554,7 @@ fn containerDecl(
45534554 }
45544555 astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items);
45554556
4556 return rvalue(gz, rl, gz.indexToRef(decl_inst), node);
4557 return rvalue(gz, rl, indexToRef(decl_inst), node);
45574558 },
45584559 else => unreachable,
45594560 }
......@@ -4791,7 +4792,7 @@ fn finishThenElseBlock(
47914792 }
47924793 assert(!strat.elide_store_to_block_ptr_instructions);
47934794 try setCondBrPayload(condbr, cond, then_scope, else_scope);
4794 return parent_gz.indexToRef(main_block);
4795 return indexToRef(main_block);
47954796 },
47964797 .break_operand => {
47974798 if (!parent_gz.refIsNoReturn(then_result)) {
......@@ -4809,7 +4810,7 @@ fn finishThenElseBlock(
48094810 } else {
48104811 try setCondBrPayload(condbr, cond, then_scope, else_scope);
48114812 }
4812 const block_ref = parent_gz.indexToRef(main_block);
4813 const block_ref = indexToRef(main_block);
48134814 switch (rl) {
48144815 .ref => return block_ref,
48154816 else => return rvalue(parent_gz, rl, block_ref, node),
......@@ -4931,7 +4932,7 @@ fn boolBinOp(
49314932 }
49324933 try rhs_scope.setBoolBrBody(bool_br);
49334934
4934 const block_ref = gz.indexToRef(bool_br);
4935 const block_ref = indexToRef(bool_br);
49354936 return rvalue(gz, rl, block_ref, node);
49364937}
49374938
......@@ -5953,7 +5954,7 @@ fn switchExpr(
59535954 if (!strat.elide_store_to_block_ptr_instructions) {
59545955 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
59555956 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
5956 return parent_gz.indexToRef(switch_block);
5957 return indexToRef(switch_block);
59575958 }
59585959
59595960 // There will necessarily be a store_to_block_ptr for
......@@ -5997,7 +5998,7 @@ fn switchExpr(
59975998 .lhs = block_scope.rl_ty_inst,
59985999 .rhs = zir_datas[break_inst].@"break".operand,
59996000 };
6000 zir_datas[break_inst].@"break".operand = parent_gz.indexToRef(store_inst);
6001 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
60016002 } else {
60026003 scalar_cases_payload.items[body_len_index] -= 1;
60036004 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
......@@ -6039,7 +6040,7 @@ fn switchExpr(
60396040 .lhs = block_scope.rl_ty_inst,
60406041 .rhs = zir_datas[break_inst].@"break".operand,
60416042 };
6042 zir_datas[break_inst].@"break".operand = parent_gz.indexToRef(store_inst);
6043 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
60436044 } else {
60446045 scalar_cases_payload.items[body_len_index] -= 1;
60456046 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
......@@ -6085,7 +6086,7 @@ fn switchExpr(
60856086 .lhs = block_scope.rl_ty_inst,
60866087 .rhs = zir_datas[break_inst].@"break".operand,
60876088 };
6088 zir_datas[break_inst].@"break".operand = parent_gz.indexToRef(store_inst);
6089 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
60896090 } else {
60906091 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
60916092 multi_cases_payload.items[body_len_index] -= 1;
......@@ -6096,7 +6097,7 @@ fn switchExpr(
60966097 }
60976098 }
60986099
6099 const block_ref = parent_gz.indexToRef(switch_block);
6100 const block_ref = indexToRef(switch_block);
61006101 switch (rl) {
61016102 .ref => return block_ref,
61026103 else => return rvalue(parent_gz, rl, block_ref, switch_node),
......@@ -6156,7 +6157,7 @@ fn switchExpr(
61566157 }
61576158 }
61586159
6159 return parent_gz.indexToRef(switch_block);
6160 return indexToRef(switch_block);
61606161 },
61616162 }
61626163}
......@@ -6411,37 +6412,12 @@ fn multilineStringLiteral(
64116412 node: ast.Node.Index,
64126413) InnerError!Zir.Inst.Ref {
64136414 const astgen = gz.astgen;
6414 const tree = astgen.tree;
6415 const node_datas = tree.nodes.items(.data);
6416
6417 const start = node_datas[node].lhs;
6418 const end = node_datas[node].rhs;
6419
6420 const gpa = gz.astgen.gpa;
6421 const string_bytes = &gz.astgen.string_bytes;
6422 const str_index = string_bytes.items.len;
6423
6424 // First line: do not append a newline.
6425 var tok_i = start;
6426 {
6427 const slice = tree.tokenSlice(tok_i);
6428 const line_bytes = slice[2 .. slice.len - 1];
6429 try string_bytes.appendSlice(gpa, line_bytes);
6430 tok_i += 1;
6431 }
6432 // Following lines: each line prepends a newline.
6433 while (tok_i <= end) : (tok_i += 1) {
6434 const slice = tree.tokenSlice(tok_i);
6435 const line_bytes = slice[2 .. slice.len - 1];
6436 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
6437 string_bytes.appendAssumeCapacity('\n');
6438 string_bytes.appendSliceAssumeCapacity(line_bytes);
6439 }
6415 const str = try astgen.strLitNodeAsString(node);
64406416 const result = try gz.add(.{
64416417 .tag = .str,
64426418 .data = .{ .str = .{
6443 .start = @intCast(u32, str_index),
6444 .len = @intCast(u32, string_bytes.items.len - str_index),
6419 .start = str.index,
6420 .len = str.len,
64456421 } },
64466422 });
64476423 return rvalue(gz, rl, result, node);
......@@ -6588,12 +6564,12 @@ fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir
65886564 } else std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
65896565 error.InvalidCharacter => unreachable, // validated by tokenizer
65906566 };
6591 // If the value fits into a f32 without losing any precision, store it that way.
6567 // If the value fits into a f64 without losing any precision, store it that way.
65926568 @setFloatMode(.Strict);
6593 const smaller_float = @floatCast(f32, float_number);
6569 const smaller_float = @floatCast(f64, float_number);
65946570 const bigger_again: f128 = smaller_float;
65956571 if (bigger_again == float_number) {
6596 const result = try gz.addFloat(smaller_float, node);
6572 const result = try gz.addFloat(smaller_float);
65976573 return rvalue(gz, rl, result, node);
65986574 }
65996575 // We need to use 128 bits. Break the float into 4 u32 values so we can
......@@ -6619,9 +6595,14 @@ fn asmExpr(
66196595 const tree = astgen.tree;
66206596 const main_tokens = tree.nodes.items(.main_token);
66216597 const node_datas = tree.nodes.items(.data);
6598 const node_tags = tree.nodes.items(.tag);
66226599 const token_tags = tree.tokens.items(.tag);
66236600
6624 const asm_source = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
6601 const asm_source = switch (node_tags[full.ast.template]) {
6602 .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]),
6603 .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template),
6604 else => return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}),
6605 };
66256606
66266607 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
66276608 // possible inline assembly improvements. Until then here is status quo AstGen
......@@ -6751,7 +6732,7 @@ fn asmExpr(
67516732
67526733 const result = try gz.addAsm(.{
67536734 .node = node,
6754 .asm_source = asm_source,
6735 .asm_source = asm_source.index,
67556736 .is_volatile = full.volatile_token != null,
67566737 .output_type_bits = output_type_bits,
67576738 .outputs = outputs,
......@@ -6855,7 +6836,7 @@ fn asRlPtr(
68556836 const zir_datas = astgen.instructions.items(.data);
68566837 try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len);
68576838 for (as_scope.instructions.items) |src_inst| {
6858 if (parent_gz.indexToRef(src_inst) == as_scope.rl_ptr) continue;
6839 if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
68596840 if (zir_tags[src_inst] == .store_to_block_ptr) {
68606841 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
68616842 }
......@@ -6986,11 +6967,9 @@ fn builtinCall(
69866967 const str_lit_token = main_tokens[operand_node];
69876968 const str = try astgen.strLitAsString(str_lit_token);
69886969 const result = try gz.addStrTok(.import, str.index, str_lit_token);
6989 if (gz.refToIndex(result)) |import_inst_index| {
6990 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
6991 if (!gop.found_existing) {
6992 gop.value_ptr.* = import_inst_index;
6993 }
6970 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
6971 if (!gop.found_existing) {
6972 gop.value_ptr.* = str_lit_token;
69946973 }
69956974 return rvalue(gz, rl, result, node);
69966975 },
......@@ -8580,6 +8559,41 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !IndexSlice {
85808559 }
85818560}
85828561
8562fn strLitNodeAsString(astgen: *AstGen, node: ast.Node.Index) !IndexSlice {
8563 const tree = astgen.tree;
8564 const node_datas = tree.nodes.items(.data);
8565
8566 const start = node_datas[node].lhs;
8567 const end = node_datas[node].rhs;
8568
8569 const gpa = astgen.gpa;
8570 const string_bytes = &astgen.string_bytes;
8571 const str_index = string_bytes.items.len;
8572
8573 // First line: do not append a newline.
8574 var tok_i = start;
8575 {
8576 const slice = tree.tokenSlice(tok_i);
8577 const line_bytes = slice[2 .. slice.len - 1];
8578 try string_bytes.appendSlice(gpa, line_bytes);
8579 tok_i += 1;
8580 }
8581 // Following lines: each line prepends a newline.
8582 while (tok_i <= end) : (tok_i += 1) {
8583 const slice = tree.tokenSlice(tok_i);
8584 const line_bytes = slice[2 .. slice.len - 1];
8585 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
8586 string_bytes.appendAssumeCapacity('\n');
8587 string_bytes.appendSliceAssumeCapacity(line_bytes);
8588 }
8589 const len = string_bytes.items.len - str_index;
8590 try string_bytes.append(gpa, 0);
8591 return IndexSlice{
8592 .index = @intCast(u32, str_index),
8593 .len = @intCast(u32, len),
8594 };
8595}
8596
85838597fn testNameString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !u32 {
85848598 const gpa = astgen.gpa;
85858599 const string_bytes = &astgen.string_bytes;
......@@ -8701,9 +8715,6 @@ const GenZir = struct {
87018715 in_defer: bool,
87028716 /// How decls created in this scope should be named.
87038717 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
8704 /// The end of special indexes. `Zir.Inst.Ref` subtracts against this number to convert
8705 /// to `Zir.Inst.Index`. The default here is correct if there are 0 parameters.
8706 ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len,
87078718 /// The containing decl AST node.
87088719 decl_node_index: ast.Node.Index,
87098720 /// The containing decl line index, absolute.
......@@ -8747,7 +8758,6 @@ const GenZir = struct {
87478758 return .{
87488759 .force_comptime = gz.force_comptime,
87498760 .in_defer = gz.in_defer,
8750 .ref_start_index = gz.ref_start_index,
87518761 .decl_node_index = gz.decl_node_index,
87528762 .decl_line = gz.decl_line,
87538763 .parent = scope,
......@@ -8765,7 +8775,7 @@ const GenZir = struct {
87658775
87668776 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
87678777 if (inst_ref == .unreachable_value) return true;
8768 if (gz.refToIndex(inst_ref)) |inst_index| {
8778 if (refToIndex(inst_ref)) |inst_index| {
87698779 return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();
87708780 }
87718781 return false;
......@@ -8803,19 +8813,6 @@ const GenZir = struct {
88038813 return gz.astgen.tree.firstToken(gz.decl_node_index);
88048814 }
88058815
8806 fn indexToRef(gz: GenZir, inst: Zir.Inst.Index) Zir.Inst.Ref {
8807 return @intToEnum(Zir.Inst.Ref, gz.ref_start_index + inst);
8808 }
8809
8810 fn refToIndex(gz: GenZir, inst: Zir.Inst.Ref) ?Zir.Inst.Index {
8811 const ref_int = @enumToInt(inst);
8812 if (ref_int >= gz.ref_start_index) {
8813 return ref_int - gz.ref_start_index;
8814 } else {
8815 return null;
8816 }
8817 }
8818
88198816 fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
88208817 // Depending on whether the result location is a pointer or value, different
88218818 // ZIR needs to be generated. In the former case we rely on storing to the
......@@ -8994,7 +8991,7 @@ const GenZir = struct {
89948991 } },
89958992 });
89968993 gz.instructions.appendAssumeCapacity(new_index);
8997 return gz.indexToRef(new_index);
8994 return indexToRef(new_index);
89988995 } else {
89998996 try gz.astgen.extra.ensureUnusedCapacity(
90008997 gpa,
......@@ -9021,7 +9018,7 @@ const GenZir = struct {
90219018 } },
90229019 });
90239020 gz.instructions.appendAssumeCapacity(new_index);
9024 return gz.indexToRef(new_index);
9021 return indexToRef(new_index);
90259022 }
90269023 }
90279024
......@@ -9075,7 +9072,7 @@ const GenZir = struct {
90759072 } },
90769073 });
90779074 gz.instructions.appendAssumeCapacity(new_index);
9078 return gz.indexToRef(new_index);
9075 return indexToRef(new_index);
90799076 }
90809077
90819078 fn addCall(
......@@ -9109,7 +9106,7 @@ const GenZir = struct {
91099106 } },
91109107 });
91119108 gz.instructions.appendAssumeCapacity(new_index);
9112 return gz.indexToRef(new_index);
9109 return indexToRef(new_index);
91139110 }
91149111
91159112 /// Note that this returns a `Zir.Inst.Index` not a ref.
......@@ -9160,16 +9157,13 @@ const GenZir = struct {
91609157 });
91619158 gz.instructions.appendAssumeCapacity(new_index);
91629159 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
9163 return gz.indexToRef(new_index);
9160 return indexToRef(new_index);
91649161 }
91659162
9166 fn addFloat(gz: *GenZir, number: f32, src_node: ast.Node.Index) !Zir.Inst.Ref {
9163 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
91679164 return gz.add(.{
91689165 .tag = .float,
9169 .data = .{ .float = .{
9170 .src_node = gz.nodeIndexToRelative(src_node),
9171 .number = number,
9172 } },
9166 .data = .{ .float = number },
91739167 });
91749168 }
91759169
......@@ -9211,7 +9205,7 @@ const GenZir = struct {
92119205 } },
92129206 });
92139207 gz.instructions.appendAssumeCapacity(new_index);
9214 return gz.indexToRef(new_index);
9208 return indexToRef(new_index);
92159209 }
92169210
92179211 fn addExtendedPayload(
......@@ -9235,7 +9229,7 @@ const GenZir = struct {
92359229 } },
92369230 });
92379231 gz.instructions.appendAssumeCapacity(new_index);
9238 return gz.indexToRef(new_index);
9232 return indexToRef(new_index);
92399233 }
92409234
92419235 fn addExtendedMultiOp(
......@@ -9268,7 +9262,7 @@ const GenZir = struct {
92689262 });
92699263 gz.instructions.appendAssumeCapacity(new_index);
92709264 astgen.appendRefsAssumeCapacity(operands);
9271 return gz.indexToRef(new_index);
9265 return indexToRef(new_index);
92729266 }
92739267
92749268 fn addArrayTypeSentinel(
......@@ -9294,7 +9288,7 @@ const GenZir = struct {
92949288 } },
92959289 });
92969290 gz.instructions.appendAssumeCapacity(new_index);
9297 return gz.indexToRef(new_index);
9291 return indexToRef(new_index);
92989292 }
92999293
93009294 fn addUnTok(
......@@ -9453,7 +9447,7 @@ const GenZir = struct {
94539447 } },
94549448 });
94559449 gz.instructions.appendAssumeCapacity(new_index);
9456 return gz.indexToRef(new_index);
9450 return indexToRef(new_index);
94579451 }
94589452
94599453 fn addAsm(
......@@ -9461,7 +9455,7 @@ const GenZir = struct {
94619455 args: struct {
94629456 /// Absolute node index. This function does the conversion to offset from Decl.
94639457 node: ast.Node.Index,
9464 asm_source: Zir.Inst.Ref,
9458 asm_source: u32,
94659459 output_type_bits: u32,
94669460 is_volatile: bool,
94679461 outputs: []const Zir.Inst.Asm.Output,
......@@ -9511,7 +9505,7 @@ const GenZir = struct {
95119505 } },
95129506 });
95139507 gz.instructions.appendAssumeCapacity(new_index);
9514 return gz.indexToRef(new_index);
9508 return indexToRef(new_index);
95159509 }
95169510
95179511 /// Note that this returns a `Zir.Inst.Index` not a ref.
......@@ -9689,7 +9683,7 @@ const GenZir = struct {
96899683 }
96909684
96919685 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
9692 return gz.indexToRef(try gz.addAsIndex(inst));
9686 return indexToRef(try gz.addAsIndex(inst));
96939687 }
96949688
96959689 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
......@@ -9836,3 +9830,18 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
98369830 astgen.source_line = line;
98379831 astgen.source_column = column;
98389832}
9833
9834const ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len;
9835
9836fn indexToRef(inst: Zir.Inst.Index) Zir.Inst.Ref {
9837 return @intToEnum(Zir.Inst.Ref, ref_start_index + inst);
9838}
9839
9840fn refToIndex(inst: Zir.Inst.Ref) ?Zir.Inst.Index {
9841 const ref_int = @enumToInt(inst);
9842 if (ref_int >= ref_start_index) {
9843 return ref_int - ref_start_index;
9844 } else {
9845 return null;
9846 }
9847}
src/Compilation.zig+95-49
......@@ -1,6 +1,7 @@
11const Compilation = @This();
22
33const std = @import("std");
4const builtin = @import("builtin");
45const mem = std.mem;
56const Allocator = std.mem.Allocator;
67const assert = std.debug.assert;
......@@ -13,7 +14,7 @@ const target_util = @import("target.zig");
1314const Package = @import("Package.zig");
1415const link = @import("link.zig");
1516const trace = @import("tracy.zig").trace;
16const liveness = @import("liveness.zig");
17const Liveness = @import("Liveness.zig");
1718const build_options = @import("build_options");
1819const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1920const glibc = @import("glibc.zig");
......@@ -148,7 +149,7 @@ emit_docs: ?EmitLoc,
148149work_queue_wait_group: WaitGroup,
149150astgen_wait_group: WaitGroup,
150151
151pub const InnerError = Module.InnerError;
152pub const SemaError = Module.SemaError;
152153
153154pub const CRTFile = struct {
154155 lock: Cache.Lock,
......@@ -168,8 +169,10 @@ pub const CSourceFile = struct {
168169};
169170
170171const Job = union(enum) {
171 /// Write the machine code for a Decl to the output file.
172 /// Write the constant value for a Decl to the output file.
172173 codegen_decl: *Module.Decl,
174 /// Write the machine code for a function to the output file.
175 codegen_func: *Module.Fn,
173176 /// Render the .h file snippet for the Decl.
174177 emit_h_decl: *Module.Decl,
175178 /// The Decl needs to be analyzed and possibly export itself.
......@@ -1927,6 +1930,7 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
19271930}
19281931
19291932pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
1933 const gpa = self.gpa;
19301934 // If the terminal is dumb, we dont want to show the user all the
19311935 // output.
19321936 var progress: std.Progress = .{ .dont_print_on_dumb = true };
......@@ -2008,33 +2012,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20082012 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
20092013 const module = self.bin_file.options.module.?;
20102014 assert(decl.has_tv);
2011 if (decl.val.castTag(.function)) |payload| {
2012 const func = payload.data;
2013 switch (func.state) {
2014 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
2015 error.AnalysisFail => {
2016 assert(func.state != .in_progress);
2017 continue;
2018 },
2019 error.OutOfMemory => return error.OutOfMemory,
2020 },
2021 .in_progress => unreachable,
2022 .inline_only => unreachable, // don't queue work for this
2023 .sema_failure, .dependency_failure => continue,
2024 .success => {},
2025 }
2026 // Here we tack on additional allocations to the Decl's arena. The allocations
2027 // are lifetime annotations in the ZIR.
2028 var decl_arena = decl.value_arena.?.promote(module.gpa);
2029 defer decl.value_arena.?.* = decl_arena.state;
2030 log.debug("analyze liveness of {s}", .{decl.name});
2031 try liveness.analyze(module.gpa, &decl_arena.allocator, func.body);
2032
2033 if (std.builtin.mode == .Debug and self.verbose_air) {
2034 func.dump(module.*);
2035 }
2036 }
2037
20382015 assert(decl.ty.hasCodeGenBits());
20392016
20402017 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
......@@ -2044,9 +2021,74 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20442021 continue;
20452022 },
20462023 else => {
2047 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.count() + 1);
2024 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2025 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2026 gpa,
2027 decl.srcLoc(),
2028 "unable to codegen: {s}",
2029 .{@errorName(err)},
2030 ));
2031 decl.analysis = .codegen_failure_retryable;
2032 continue;
2033 },
2034 };
2035 },
2036 },
2037 .codegen_func => |func| switch (func.owner_decl.analysis) {
2038 .unreferenced => unreachable,
2039 .in_progress => unreachable,
2040 .outdated => unreachable,
2041
2042 .file_failure,
2043 .sema_failure,
2044 .codegen_failure,
2045 .dependency_failure,
2046 .sema_failure_retryable,
2047 => continue,
2048
2049 .complete, .codegen_failure_retryable => {
2050 if (build_options.omit_stage2)
2051 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2052 switch (func.state) {
2053 .sema_failure, .dependency_failure => continue,
2054 .queued => {},
2055 .in_progress => unreachable,
2056 .inline_only => unreachable, // don't queue work for this
2057 .success => unreachable, // don't queue it twice
2058 }
2059
2060 const module = self.bin_file.options.module.?;
2061 const decl = func.owner_decl;
2062
2063 var air = module.analyzeFnBody(decl, func) catch |err| switch (err) {
2064 error.AnalysisFail => {
2065 assert(func.state != .in_progress);
2066 continue;
2067 },
2068 error.OutOfMemory => return error.OutOfMemory,
2069 };
2070 defer air.deinit(gpa);
2071
2072 log.debug("analyze liveness of {s}", .{decl.name});
2073 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
2074 defer liveness.deinit(gpa);
2075
2076 if (builtin.mode == .Debug and self.verbose_air) {
2077 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2078 @import("print_air.zig").dump(gpa, air, decl.namespace.file_scope.zir, liveness);
2079 std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
2080 }
2081
2082 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2083 error.OutOfMemory => return error.OutOfMemory,
2084 error.AnalysisFail => {
2085 decl.analysis = .codegen_failure;
2086 continue;
2087 },
2088 else => {
2089 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
20482090 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2049 module.gpa,
2091 gpa,
20502092 decl.srcLoc(),
20512093 "unable to codegen: {s}",
20522094 .{@errorName(err)},
......@@ -2055,6 +2097,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20552097 continue;
20562098 },
20572099 };
2100 continue;
20582101 },
20592102 },
20602103 .emit_h_decl => |decl| switch (decl.analysis) {
......@@ -2075,7 +2118,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20752118 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
20762119 const module = self.bin_file.options.module.?;
20772120 const emit_h = module.emit_h.?;
2078 _ = try emit_h.decl_table.getOrPut(module.gpa, decl);
2121 _ = try emit_h.decl_table.getOrPut(gpa, decl);
20792122 const decl_emit_h = decl.getEmitH(module);
20802123 const fwd_decl = &decl_emit_h.fwd_decl;
20812124 fwd_decl.shrinkRetainingCapacity(0);
......@@ -2084,7 +2127,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20842127 .module = module,
20852128 .error_msg = null,
20862129 .decl = decl,
2087 .fwd_decl = fwd_decl.toManaged(module.gpa),
2130 .fwd_decl = fwd_decl.toManaged(gpa),
20882131 // we don't want to emit optionals and error unions to headers since they have no ABI
20892132 .typedefs = undefined,
20902133 };
......@@ -2092,14 +2135,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20922135
20932136 c_codegen.genHeader(&dg) catch |err| switch (err) {
20942137 error.AnalysisFail => {
2095 try emit_h.failed_decls.put(module.gpa, decl, dg.error_msg.?);
2138 try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?);
20962139 continue;
20972140 },
20982141 else => |e| return e,
20992142 };
21002143
21012144 fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
2102 fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len);
2145 fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len);
21032146 },
21042147 },
21052148 .analyze_decl => |decl| {
......@@ -2116,9 +2159,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21162159 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
21172160 const module = self.bin_file.options.module.?;
21182161 self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
2119 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.count() + 1);
2162 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
21202163 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2121 module.gpa,
2164 gpa,
21222165 decl.srcLoc(),
21232166 "unable to update line number: {s}",
21242167 .{@errorName(err)},
......@@ -2320,7 +2363,7 @@ const AstGenSrc = union(enum) {
23202363 root,
23212364 import: struct {
23222365 importing_file: *Module.Scope.File,
2323 import_inst: Zir.Inst.Index,
2366 import_tok: std.zig.ast.TokenIndex,
23242367 },
23252368};
23262369
......@@ -2357,11 +2400,15 @@ fn workerAstGenFile(
23572400 assert(file.zir_loaded);
23582401 const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)];
23592402 if (imports_index != 0) {
2360 const imports_len = file.zir.extra[imports_index];
2403 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
2404 var import_i: u32 = 0;
2405 var extra_index = extra.end;
23612406
2362 for (file.zir.extra[imports_index + 1 ..][0..imports_len]) |import_inst| {
2363 const inst_data = file.zir.instructions.items(.data)[import_inst].str_tok;
2364 const import_path = inst_data.get(file.zir);
2407 while (import_i < extra.data.imports_len) : (import_i += 1) {
2408 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
2409 extra_index = item.end;
2410
2411 const import_path = file.zir.nullTerminatedString(item.data.name);
23652412
23662413 const import_result = blk: {
23672414 const lock = comp.mutex.acquire();
......@@ -2375,7 +2422,7 @@ fn workerAstGenFile(
23752422 });
23762423 const sub_src: AstGenSrc = .{ .import = .{
23772424 .importing_file = file,
2378 .import_inst = import_inst,
2425 .import_tok = item.data.token,
23792426 } };
23802427 wg.start();
23812428 comp.thread_pool.spawn(workerAstGenFile, .{
......@@ -2607,12 +2654,11 @@ fn reportRetryableAstGenError(
26072654 },
26082655 .import => |info| blk: {
26092656 const importing_file = info.importing_file;
2610 const import_inst = info.import_inst;
2611 const inst_data = importing_file.zir.instructions.items(.data)[import_inst].str_tok;
2657
26122658 break :blk .{
26132659 .file_scope = importing_file,
26142660 .parent_decl_node = 0,
2615 .lazy = .{ .token_offset = inst_data.src_tok },
2661 .lazy = .{ .token_abs = info.import_tok },
26162662 };
26172663 },
26182664 };
......@@ -3149,7 +3195,7 @@ pub fn addCCArgs(
31493195 try argv.appendSlice(comp.clang_argv);
31503196}
31513197
3152fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
3198fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) SemaError {
31533199 @setCold(true);
31543200 const err_msg = blk: {
31553201 const msg = try std.fmt.allocPrint(comp.gpa, format, args);
......@@ -3170,7 +3216,7 @@ fn failCObjWithOwnedErrorMsg(
31703216 comp: *Compilation,
31713217 c_object: *CObject,
31723218 err_msg: *CObject.ErrorMsg,
3173) InnerError {
3219) SemaError {
31743220 @setCold(true);
31753221 {
31763222 const lock = comp.mutex.acquire();
src/Liveness.zig created+602
......@@ -0,0 +1,602 @@
1//! For each AIR instruction, we want to know:
2//! * Is the instruction unreferenced (e.g. dies immediately)?
3//! * For each of its operands, does the operand die with this instruction (e.g. is
4//! this the last reference to it)?
5//! Some instructions are special, such as:
6//! * Conditional Branches
7//! * Switch Branches
8const Liveness = @This();
9const std = @import("std");
10const trace = @import("tracy.zig").trace;
11const log = std.log.scoped(.liveness);
12const assert = std.debug.assert;
13const Allocator = std.mem.Allocator;
14const Air = @import("Air.zig");
15const Zir = @import("Zir.zig");
16const Log2Int = std.math.Log2Int;
17
18/// This array is split into sets of 4 bits per AIR instruction.
19/// The MSB (0bX000) is whether the instruction is unreferenced.
20/// The LSB (0b000X) is the first operand, and so on, up to 3 operands. A set bit means the
21/// operand dies after this instruction.
22/// Instructions which need more data to track liveness have special handling via the
23/// `special` table.
24tomb_bits: []usize,
25/// Sparse table of specially handled instructions. The value is an index into the `extra`
26/// array. The meaning of the data depends on the AIR tag.
27/// * `cond_br` - points to a `CondBr` in `extra` at this index.
28/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.
29/// * `asm`, `call` - the value is a set of bits which are the extra tomb bits of operands.
30/// The main tomb bits are still used and the extra ones are starting with the lsb of the
31/// value here.
32special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
33/// Auxilliary data. The way this data is interpreted is determined contextually.
34extra: []const u32,
35
36/// Trailing is the set of instructions whose lifetimes end at the start of the then branch,
37/// followed by the set of instructions whose lifetimes end at the start of the else branch.
38pub const CondBr = struct {
39 then_death_count: u32,
40 else_death_count: u32,
41};
42
43/// Trailing is:
44/// * For each case in the same order as in the AIR:
45/// - case_death_count: u32
46/// - Air.Inst.Index for each `case_death_count`: set of instructions whose lifetimes
47/// end at the start of this case.
48/// * Air.Inst.Index for each `else_death_count`: set of instructions whose lifetimes
49/// end at the start of the else case.
50pub const SwitchBr = struct {
51 else_death_count: u32,
52};
53
54pub fn analyze(gpa: *Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
55 const tracy = trace(@src());
56 defer tracy.end();
57
58 var a: Analysis = .{
59 .gpa = gpa,
60 .air = air,
61 .table = .{},
62 .tomb_bits = try gpa.alloc(
63 usize,
64 (air.instructions.len * bpi + @bitSizeOf(usize) - 1) / @bitSizeOf(usize),
65 ),
66 .extra = .{},
67 .special = .{},
68 .zir = &zir,
69 };
70 errdefer gpa.free(a.tomb_bits);
71 errdefer a.special.deinit(gpa);
72 defer a.extra.deinit(gpa);
73 defer a.table.deinit(gpa);
74
75 std.mem.set(usize, a.tomb_bits, 0);
76
77 const main_body = air.getMainBody();
78 try a.table.ensureTotalCapacity(gpa, @intCast(u32, main_body.len));
79 try analyzeWithContext(&a, null, main_body);
80 return Liveness{
81 .tomb_bits = a.tomb_bits,
82 .special = a.special,
83 .extra = a.extra.toOwnedSlice(gpa),
84 };
85}
86
87pub fn getTombBits(l: Liveness, inst: Air.Inst.Index) Bpi {
88 const usize_index = (inst * bpi) / @bitSizeOf(usize);
89 return @truncate(Bpi, l.tomb_bits[usize_index] >>
90 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi));
91}
92
93pub fn isUnused(l: Liveness, inst: Air.Inst.Index) bool {
94 const usize_index = (inst * bpi) / @bitSizeOf(usize);
95 const mask = @as(usize, 1) <<
96 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi + (bpi - 1));
97 return (l.tomb_bits[usize_index] & mask) != 0;
98}
99
100pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool {
101 assert(operand < bpi - 1);
102 const usize_index = (inst * bpi) / @bitSizeOf(usize);
103 const mask = @as(usize, 1) <<
104 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi + operand);
105 return (l.tomb_bits[usize_index] & mask) != 0;
106}
107
108pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) void {
109 assert(operand < bpi - 1);
110 const usize_index = (inst * bpi) / @bitSizeOf(usize);
111 const mask = @as(usize, 1) <<
112 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi + operand);
113 l.tomb_bits[usize_index] &= ~mask;
114}
115
116/// Higher level API.
117pub const CondBrSlices = struct {
118 then_deaths: []const Air.Inst.Index,
119 else_deaths: []const Air.Inst.Index,
120};
121
122pub fn getCondBr(l: Liveness, inst: Air.Inst.Index) CondBrSlices {
123 var index: usize = l.special.get(inst) orelse return .{
124 .then_deaths = &.{},
125 .else_deaths = &.{},
126 };
127 const then_death_count = l.extra[index];
128 index += 1;
129 const else_death_count = l.extra[index];
130 index += 1;
131 const then_deaths = l.extra[index..][0..then_death_count];
132 index += then_death_count;
133 return .{
134 .then_deaths = then_deaths,
135 .else_deaths = l.extra[index..][0..else_death_count],
136 };
137}
138
139pub fn deinit(l: *Liveness, gpa: *Allocator) void {
140 gpa.free(l.tomb_bits);
141 gpa.free(l.extra);
142 l.special.deinit(gpa);
143 l.* = undefined;
144}
145
146/// How many tomb bits per AIR instruction.
147pub const bpi = 4;
148pub const Bpi = std.meta.Int(.unsigned, bpi);
149pub const OperandInt = std.math.Log2Int(Bpi);
150
151/// In-progress data; on successful analysis converted into `Liveness`.
152const Analysis = struct {
153 gpa: *Allocator,
154 air: Air,
155 table: std.AutoHashMapUnmanaged(Air.Inst.Index, void),
156 tomb_bits: []usize,
157 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
158 extra: std.ArrayListUnmanaged(u32),
159 zir: *const Zir,
160
161 fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {
162 const usize_index = (inst * bpi) / @bitSizeOf(usize);
163 a.tomb_bits[usize_index] |= @as(usize, tomb_bits) <<
164 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi);
165 }
166
167 fn addExtra(a: *Analysis, extra: anytype) Allocator.Error!u32 {
168 const fields = std.meta.fields(@TypeOf(extra));
169 try a.extra.ensureUnusedCapacity(a.gpa, fields.len);
170 return addExtraAssumeCapacity(a, extra);
171 }
172
173 fn addExtraAssumeCapacity(a: *Analysis, extra: anytype) u32 {
174 const fields = std.meta.fields(@TypeOf(extra));
175 const result = @intCast(u32, a.extra.items.len);
176 inline for (fields) |field| {
177 a.extra.appendAssumeCapacity(switch (field.field_type) {
178 u32 => @field(extra, field.name),
179 else => @compileError("bad field type"),
180 });
181 }
182 return result;
183 }
184};
185
186fn analyzeWithContext(
187 a: *Analysis,
188 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
189 body: []const Air.Inst.Index,
190) Allocator.Error!void {
191 var i: usize = body.len;
192
193 if (new_set) |ns| {
194 // We are only interested in doing this for instructions which are born
195 // before a conditional branch, so after obtaining the new set for
196 // each branch we prune the instructions which were born within.
197 while (i != 0) {
198 i -= 1;
199 const inst = body[i];
200 _ = ns.remove(inst);
201 try analyzeInst(a, new_set, inst);
202 }
203 } else {
204 while (i != 0) {
205 i -= 1;
206 const inst = body[i];
207 try analyzeInst(a, new_set, inst);
208 }
209 }
210}
211
212fn analyzeInst(
213 a: *Analysis,
214 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
215 inst: Air.Inst.Index,
216) Allocator.Error!void {
217 const gpa = a.gpa;
218 const table = &a.table;
219 const inst_tags = a.air.instructions.items(.tag);
220 const inst_datas = a.air.instructions.items(.data);
221
222 // No tombstone for this instruction means it is never referenced,
223 // and its birth marks its own death. Very metal 🤘
224 const main_tomb = !table.contains(inst);
225
226 switch (inst_tags[inst]) {
227 .add,
228 .addwrap,
229 .sub,
230 .subwrap,
231 .mul,
232 .mulwrap,
233 .div,
234 .bit_and,
235 .bit_or,
236 .xor,
237 .cmp_lt,
238 .cmp_lte,
239 .cmp_eq,
240 .cmp_gte,
241 .cmp_gt,
242 .cmp_neq,
243 .bool_and,
244 .bool_or,
245 .store,
246 => {
247 const o = inst_datas[inst].bin_op;
248 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
249 },
250
251 .arg,
252 .alloc,
253 .constant,
254 .const_ty,
255 .breakpoint,
256 .dbg_stmt,
257 .varptr,
258 .unreach,
259 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
260
261 .not,
262 .bitcast,
263 .load,
264 .ref,
265 .floatcast,
266 .intcast,
267 .optional_payload,
268 .optional_payload_ptr,
269 .wrap_optional,
270 .unwrap_errunion_payload,
271 .unwrap_errunion_err,
272 .unwrap_errunion_payload_ptr,
273 .unwrap_errunion_err_ptr,
274 .wrap_errunion_payload,
275 .wrap_errunion_err,
276 => {
277 const o = inst_datas[inst].ty_op;
278 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
279 },
280
281 .is_null,
282 .is_non_null,
283 .is_null_ptr,
284 .is_non_null_ptr,
285 .is_err,
286 .is_non_err,
287 .is_err_ptr,
288 .is_non_err_ptr,
289 .ptrtoint,
290 .ret,
291 => {
292 const operand = inst_datas[inst].un_op;
293 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
294 },
295
296 .call => {
297 const inst_data = inst_datas[inst].pl_op;
298 const callee = inst_data.operand;
299 const extra = a.air.extraData(Air.Call, inst_data.payload);
300 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]);
301 if (args.len <= bpi - 2) {
302 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
303 buf[0] = callee;
304 std.mem.copy(Air.Inst.Ref, buf[1..], args);
305 return trackOperands(a, new_set, inst, main_tomb, buf);
306 }
307 var extra_tombs: ExtraTombs = .{
308 .analysis = a,
309 .new_set = new_set,
310 .inst = inst,
311 .main_tomb = main_tomb,
312 };
313 try extra_tombs.feed(callee);
314 for (args) |arg| {
315 try extra_tombs.feed(arg);
316 }
317 return extra_tombs.finish();
318 },
319 .struct_field_ptr => {
320 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
321 return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_ptr, .none, .none });
322 },
323 .br => {
324 const br = inst_datas[inst].br;
325 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
326 },
327 .assembly => {
328 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
329 const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;
330 const outputs_len = @truncate(u5, extended.small);
331 const inputs_len = @truncate(u5, extended.small >> 5);
332 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);
333 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);
334 if (outputs.len + args.len <= bpi - 1) {
335 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
336 std.mem.copy(Air.Inst.Ref, &buf, outputs);
337 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
338 return trackOperands(a, new_set, inst, main_tomb, buf);
339 }
340 var extra_tombs: ExtraTombs = .{
341 .analysis = a,
342 .new_set = new_set,
343 .inst = inst,
344 .main_tomb = main_tomb,
345 };
346 for (outputs) |output| {
347 try extra_tombs.feed(output);
348 }
349 for (args) |arg| {
350 try extra_tombs.feed(arg);
351 }
352 return extra_tombs.finish();
353 },
354 .block => {
355 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
356 const body = a.air.extra[extra.end..][0..extra.data.body_len];
357 try analyzeWithContext(a, new_set, body);
358 return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none });
359 },
360 .loop => {
361 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
362 const body = a.air.extra[extra.end..][0..extra.data.body_len];
363 try analyzeWithContext(a, new_set, body);
364 return; // Loop has no operands and it is always unreferenced.
365 },
366 .cond_br => {
367 // Each death that occurs inside one branch, but not the other, needs
368 // to be added as a death immediately upon entering the other branch.
369 const inst_data = inst_datas[inst].pl_op;
370 const condition = inst_data.operand;
371 const extra = a.air.extraData(Air.CondBr, inst_data.payload);
372 const then_body = a.air.extra[extra.end..][0..extra.data.then_body_len];
373 const else_body = a.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
374
375 var then_table: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};
376 defer then_table.deinit(gpa);
377 try analyzeWithContext(a, &then_table, then_body);
378
379 // Reset the table back to its state from before the branch.
380 {
381 var it = then_table.keyIterator();
382 while (it.next()) |key| {
383 assert(table.remove(key.*));
384 }
385 }
386
387 var else_table: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{};
388 defer else_table.deinit(gpa);
389 try analyzeWithContext(a, &else_table, else_body);
390
391 var then_entry_deaths = std.ArrayList(Air.Inst.Index).init(gpa);
392 defer then_entry_deaths.deinit();
393 var else_entry_deaths = std.ArrayList(Air.Inst.Index).init(gpa);
394 defer else_entry_deaths.deinit();
395
396 {
397 var it = else_table.keyIterator();
398 while (it.next()) |key| {
399 const else_death = key.*;
400 if (!then_table.contains(else_death)) {
401 try then_entry_deaths.append(else_death);
402 }
403 }
404 }
405 // This loop is the same, except it's for the then branch, and it additionally
406 // has to put its items back into the table to undo the reset.
407 {
408 var it = then_table.keyIterator();
409 while (it.next()) |key| {
410 const then_death = key.*;
411 if (!else_table.contains(then_death)) {
412 try else_entry_deaths.append(then_death);
413 }
414 try table.put(gpa, then_death, {});
415 }
416 }
417 // Now we have to correctly populate new_set.
418 if (new_set) |ns| {
419 try ns.ensureCapacity(gpa, @intCast(u32, ns.count() + then_table.count() + else_table.count()));
420 var it = then_table.keyIterator();
421 while (it.next()) |key| {
422 _ = ns.putAssumeCapacity(key.*, {});
423 }
424 it = else_table.keyIterator();
425 while (it.next()) |key| {
426 _ = ns.putAssumeCapacity(key.*, {});
427 }
428 }
429 const then_death_count = @intCast(u32, then_entry_deaths.items.len);
430 const else_death_count = @intCast(u32, else_entry_deaths.items.len);
431
432 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(Air.CondBr).len +
433 then_death_count + else_death_count);
434 const extra_index = a.addExtraAssumeCapacity(CondBr{
435 .then_death_count = then_death_count,
436 .else_death_count = else_death_count,
437 });
438 a.extra.appendSliceAssumeCapacity(then_entry_deaths.items);
439 a.extra.appendSliceAssumeCapacity(else_entry_deaths.items);
440 try a.special.put(gpa, inst, extra_index);
441
442 // Continue on with the instruction analysis. The following code will find the condition
443 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
444 // condition's lifetime ends immediately before entering any branch.
445 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });
446 },
447 .switch_br => {
448 const pl_op = inst_datas[inst].pl_op;
449 const condition = pl_op.operand;
450 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);
451
452 const Table = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
453 const case_tables = try gpa.alloc(Table, switch_br.data.cases_len + 1); // +1 for else
454 defer gpa.free(case_tables);
455
456 std.mem.set(Table, case_tables, .{});
457 defer for (case_tables) |*ct| ct.deinit(gpa);
458
459 var air_extra_index: usize = switch_br.end;
460 for (case_tables[0..switch_br.data.cases_len]) |*case_table| {
461 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
462 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
463 air_extra_index = case.end + case.data.items_len + case_body.len;
464 try analyzeWithContext(a, case_table, case_body);
465
466 // Reset the table back to its state from before the case.
467 var it = case_table.keyIterator();
468 while (it.next()) |key| {
469 assert(table.remove(key.*));
470 }
471 }
472 { // else
473 const else_table = &case_tables[case_tables.len - 1];
474 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];
475 try analyzeWithContext(a, else_table, else_body);
476
477 // Reset the table back to its state from before the case.
478 var it = else_table.keyIterator();
479 while (it.next()) |key| {
480 assert(table.remove(key.*));
481 }
482 }
483
484 const List = std.ArrayListUnmanaged(Air.Inst.Index);
485 const case_deaths = try gpa.alloc(List, case_tables.len); // includes else
486 defer gpa.free(case_deaths);
487
488 std.mem.set(List, case_deaths, .{});
489 defer for (case_deaths) |*cd| cd.deinit(gpa);
490
491 var total_deaths: u32 = 0;
492 for (case_tables) |*ct, i| {
493 total_deaths += ct.count();
494 var it = ct.keyIterator();
495 while (it.next()) |key| {
496 const case_death = key.*;
497 for (case_tables) |*ct_inner, j| {
498 if (i == j) continue;
499 if (!ct_inner.contains(case_death)) {
500 // instruction is not referenced in this case
501 try case_deaths[j].append(gpa, case_death);
502 }
503 }
504 // undo resetting the table
505 try table.put(gpa, case_death, {});
506 }
507 }
508
509 // Now we have to correctly populate new_set.
510 if (new_set) |ns| {
511 try ns.ensureUnusedCapacity(gpa, total_deaths);
512 for (case_tables) |*ct| {
513 var it = ct.keyIterator();
514 while (it.next()) |key| {
515 _ = ns.putAssumeCapacity(key.*, {});
516 }
517 }
518 }
519
520 const else_death_count = @intCast(u32, case_deaths[case_deaths.len - 1].items.len);
521 const extra_index = try a.addExtra(SwitchBr{
522 .else_death_count = else_death_count,
523 });
524 for (case_deaths[0 .. case_deaths.len - 1]) |*cd| {
525 const case_death_count = @intCast(u32, cd.items.len);
526 try a.extra.ensureUnusedCapacity(gpa, 1 + case_death_count + else_death_count);
527 a.extra.appendAssumeCapacity(case_death_count);
528 a.extra.appendSliceAssumeCapacity(cd.items);
529 }
530 a.extra.appendSliceAssumeCapacity(case_deaths[case_deaths.len - 1].items);
531 try a.special.put(gpa, inst, extra_index);
532
533 return trackOperands(a, new_set, inst, main_tomb, .{ condition, .none, .none });
534 },
535 }
536}
537
538fn trackOperands(
539 a: *Analysis,
540 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
541 inst: Air.Inst.Index,
542 main_tomb: bool,
543 operands: [bpi - 1]Air.Inst.Ref,
544) Allocator.Error!void {
545 const table = &a.table;
546 const gpa = a.gpa;
547
548 var tomb_bits: Bpi = @boolToInt(main_tomb);
549 var i = operands.len;
550
551 while (i > 0) {
552 i -= 1;
553 tomb_bits <<= 1;
554 const op_int = @enumToInt(operands[i]);
555 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
556 const operand: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
557 const prev = try table.fetchPut(gpa, operand, {});
558 if (prev == null) {
559 // Death.
560 tomb_bits |= 1;
561 if (new_set) |ns| try ns.putNoClobber(gpa, operand, {});
562 }
563 }
564 a.storeTombBits(inst, tomb_bits);
565}
566
567const ExtraTombs = struct {
568 analysis: *Analysis,
569 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
570 inst: Air.Inst.Index,
571 main_tomb: bool,
572 bit_index: usize = 0,
573 tomb_bits: Bpi = 0,
574 big_tomb_bits: u32 = 0,
575
576 fn feed(et: *ExtraTombs, op_ref: Air.Inst.Ref) !void {
577 const this_bit_index = et.bit_index;
578 assert(this_bit_index < 32); // TODO mechanism for when there are greater than 32 operands
579 et.bit_index += 1;
580 const gpa = et.analysis.gpa;
581 const op_int = @enumToInt(op_ref);
582 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
583 const op_index: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
584 const prev = try et.analysis.table.fetchPut(gpa, op_index, {});
585 if (prev == null) {
586 // Death.
587 if (et.new_set) |ns| try ns.putNoClobber(gpa, op_index, {});
588 if (this_bit_index < bpi - 1) {
589 et.tomb_bits |= @as(Bpi, 1) << @intCast(OperandInt, this_bit_index);
590 } else {
591 const big_bit_index = this_bit_index - (bpi - 1);
592 et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, big_bit_index);
593 }
594 }
595 }
596
597 fn finish(et: *ExtraTombs) !void {
598 et.tomb_bits |= @as(Bpi, @boolToInt(et.main_tomb)) << (bpi - 1);
599 et.analysis.storeTombBits(et.inst, et.tomb_bits);
600 try et.analysis.special.put(et.analysis.gpa, et.inst, et.big_tomb_bits);
601 }
602};
src/Module.zig+230-421
......@@ -21,7 +21,7 @@ const Type = @import("type.zig").Type;
2121const TypedValue = @import("TypedValue.zig");
2222const Package = @import("Package.zig");
2323const link = @import("link.zig");
24const ir = @import("air.zig");
24const Air = @import("Air.zig");
2525const Zir = @import("Zir.zig");
2626const trace = @import("tracy.zig").trace;
2727const AstGen = @import("AstGen.zig");
......@@ -739,8 +739,6 @@ pub const Union = struct {
739739pub const Fn = struct {
740740 /// The Decl that corresponds to the function itself.
741741 owner_decl: *Decl,
742 /// undefined unless analysis state is `success`.
743 body: ir.Body,
744742 /// The ZIR instruction that is a function instruction. Use this to find
745743 /// the body. We store this rather than the body directly so that when ZIR
746744 /// is regenerated on update(), we can map this to the new corresponding
......@@ -771,11 +769,6 @@ pub const Fn = struct {
771769 success,
772770 };
773771
774 /// For debugging purposes.
775 pub fn dump(func: *Fn, mod: Module) void {
776 ir.dumpFn(mod, func);
777 }
778
779772 pub fn deinit(func: *Fn, gpa: *Allocator) void {
780773 if (func.getInferredErrorSet()) |map| {
781774 map.deinit(gpa);
......@@ -1157,7 +1150,7 @@ pub const Scope = struct {
11571150 /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
11581151 /// for the one that will be the same for all Block instances.
11591152 src_decl: *Decl,
1160 instructions: ArrayListUnmanaged(*ir.Inst),
1153 instructions: ArrayListUnmanaged(Air.Inst.Index),
11611154 label: ?*Label = null,
11621155 inlining: ?*Inlining,
11631156 /// If runtime_index is not 0 then one of these is guaranteed to be non null.
......@@ -1189,14 +1182,14 @@ pub const Scope = struct {
11891182 };
11901183
11911184 pub const Merges = struct {
1192 block_inst: *ir.Inst.Block,
1185 block_inst: Air.Inst.Index,
11931186 /// Separate array list from break_inst_list so that it can be passed directly
11941187 /// to resolvePeerTypes.
1195 results: ArrayListUnmanaged(*ir.Inst),
1188 results: ArrayListUnmanaged(Air.Inst.Ref),
11961189 /// Keeps track of the break instructions so that the operand can be replaced
11971190 /// if we need to add type coercion at the end of block analysis.
11981191 /// Same indexes, capacity, length as `results`.
1199 br_list: ArrayListUnmanaged(*ir.Inst.Br),
1192 br_list: ArrayListUnmanaged(Air.Inst.Index),
12001193 };
12011194
12021195 /// For debugging purposes.
......@@ -1233,185 +1226,94 @@ pub const Scope = struct {
12331226 return block.src_decl.namespace.file_scope;
12341227 }
12351228
1236 pub fn addNoOp(
1237 block: *Scope.Block,
1238 src: LazySrcLoc,
1229 pub fn addTy(
1230 block: *Block,
1231 tag: Air.Inst.Tag,
12391232 ty: Type,
1240 comptime tag: ir.Inst.Tag,
1241 ) !*ir.Inst {
1242 const inst = try block.sema.arena.create(tag.Type());
1243 inst.* = .{
1244 .base = .{
1245 .tag = tag,
1246 .ty = ty,
1247 .src = src,
1248 },
1249 };
1250 try block.instructions.append(block.sema.gpa, &inst.base);
1251 return &inst.base;
1233 ) error{OutOfMemory}!Air.Inst.Ref {
1234 return block.addInst(.{
1235 .tag = tag,
1236 .data = .{ .ty = ty },
1237 });
12521238 }
12531239
1254 pub fn addUnOp(
1255 block: *Scope.Block,
1256 src: LazySrcLoc,
1240 pub fn addTyOp(
1241 block: *Block,
1242 tag: Air.Inst.Tag,
12571243 ty: Type,
1258 tag: ir.Inst.Tag,
1259 operand: *ir.Inst,
1260 ) !*ir.Inst {
1261 const inst = try block.sema.arena.create(ir.Inst.UnOp);
1262 inst.* = .{
1263 .base = .{
1264 .tag = tag,
1265 .ty = ty,
1266 .src = src,
1267 },
1268 .operand = operand,
1269 };
1270 try block.instructions.append(block.sema.gpa, &inst.base);
1271 return &inst.base;
1244 operand: Air.Inst.Ref,
1245 ) error{OutOfMemory}!Air.Inst.Ref {
1246 return block.addInst(.{
1247 .tag = tag,
1248 .data = .{ .ty_op = .{
1249 .ty = try block.sema.addType(ty),
1250 .operand = operand,
1251 } },
1252 });
12721253 }
12731254
1274 pub fn addBinOp(
1275 block: *Scope.Block,
1276 src: LazySrcLoc,
1277 ty: Type,
1278 tag: ir.Inst.Tag,
1279 lhs: *ir.Inst,
1280 rhs: *ir.Inst,
1281 ) !*ir.Inst {
1282 const inst = try block.sema.arena.create(ir.Inst.BinOp);
1283 inst.* = .{
1284 .base = .{
1285 .tag = tag,
1286 .ty = ty,
1287 .src = src,
1288 },
1289 .lhs = lhs,
1290 .rhs = rhs,
1291 };
1292 try block.instructions.append(block.sema.gpa, &inst.base);
1293 return &inst.base;
1255 pub fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
1256 return block.addInst(.{
1257 .tag = tag,
1258 .data = .{ .no_op = {} },
1259 });
12941260 }
12951261
1296 pub fn addBr(
1297 scope_block: *Scope.Block,
1298 src: LazySrcLoc,
1299 target_block: *ir.Inst.Block,
1300 operand: *ir.Inst,
1301 ) !*ir.Inst.Br {
1302 const inst = try scope_block.sema.arena.create(ir.Inst.Br);
1303 inst.* = .{
1304 .base = .{
1305 .tag = .br,
1306 .ty = Type.initTag(.noreturn),
1307 .src = src,
1308 },
1309 .operand = operand,
1310 .block = target_block,
1311 };
1312 try scope_block.instructions.append(scope_block.sema.gpa, &inst.base);
1313 return inst;
1262 pub fn addUnOp(
1263 block: *Block,
1264 tag: Air.Inst.Tag,
1265 operand: Air.Inst.Ref,
1266 ) error{OutOfMemory}!Air.Inst.Ref {
1267 return block.addInst(.{
1268 .tag = tag,
1269 .data = .{ .un_op = operand },
1270 });
13141271 }
13151272
1316 pub fn addCondBr(
1317 block: *Scope.Block,
1318 src: LazySrcLoc,
1319 condition: *ir.Inst,
1320 then_body: ir.Body,
1321 else_body: ir.Body,
1322 ) !*ir.Inst {
1323 const inst = try block.sema.arena.create(ir.Inst.CondBr);
1324 inst.* = .{
1325 .base = .{
1326 .tag = .condbr,
1327 .ty = Type.initTag(.noreturn),
1328 .src = src,
1329 },
1330 .condition = condition,
1331 .then_body = then_body,
1332 .else_body = else_body,
1333 };
1334 try block.instructions.append(block.sema.gpa, &inst.base);
1335 return &inst.base;
1273 pub fn addBr(
1274 block: *Block,
1275 target_block: Air.Inst.Index,
1276 operand: Air.Inst.Ref,
1277 ) error{OutOfMemory}!Air.Inst.Ref {
1278 return block.addInst(.{
1279 .tag = .br,
1280 .data = .{ .br = .{
1281 .block_inst = target_block,
1282 .operand = operand,
1283 } },
1284 });
13361285 }
13371286
1338 pub fn addCall(
1339 block: *Scope.Block,
1340 src: LazySrcLoc,
1341 ty: Type,
1342 func: *ir.Inst,
1343 args: []const *ir.Inst,
1344 ) !*ir.Inst {
1345 const inst = try block.sema.arena.create(ir.Inst.Call);
1346 inst.* = .{
1347 .base = .{
1348 .tag = .call,
1349 .ty = ty,
1350 .src = src,
1351 },
1352 .func = func,
1353 .args = args,
1354 };
1355 try block.instructions.append(block.sema.gpa, &inst.base);
1356 return &inst.base;
1287 pub fn addBinOp(
1288 block: *Block,
1289 tag: Air.Inst.Tag,
1290 lhs: Air.Inst.Ref,
1291 rhs: Air.Inst.Ref,
1292 ) error{OutOfMemory}!Air.Inst.Ref {
1293 return block.addInst(.{
1294 .tag = tag,
1295 .data = .{ .bin_op = .{
1296 .lhs = lhs,
1297 .rhs = rhs,
1298 } },
1299 });
13571300 }
13581301
1359 pub fn addSwitchBr(
1360 block: *Scope.Block,
1361 src: LazySrcLoc,
1362 operand: *ir.Inst,
1363 cases: []ir.Inst.SwitchBr.Case,
1364 else_body: ir.Body,
1365 ) !*ir.Inst {
1366 const inst = try block.sema.arena.create(ir.Inst.SwitchBr);
1367 inst.* = .{
1368 .base = .{
1369 .tag = .switchbr,
1370 .ty = Type.initTag(.noreturn),
1371 .src = src,
1372 },
1373 .target = operand,
1374 .cases = cases,
1375 .else_body = else_body,
1376 };
1377 try block.instructions.append(block.sema.gpa, &inst.base);
1378 return &inst.base;
1302 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
1303 return Air.indexToRef(try block.addInstAsIndex(inst));
13791304 }
13801305
1381 pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, line: u32, column: u32) !*ir.Inst {
1382 const inst = try block.sema.arena.create(ir.Inst.DbgStmt);
1383 inst.* = .{
1384 .base = .{
1385 .tag = .dbg_stmt,
1386 .ty = Type.initTag(.void),
1387 .src = src,
1388 },
1389 .line = line,
1390 .column = column,
1391 };
1392 try block.instructions.append(block.sema.gpa, &inst.base);
1393 return &inst.base;
1394 }
1306 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
1307 const sema = block.sema;
1308 const gpa = sema.gpa;
13951309
1396 pub fn addStructFieldPtr(
1397 block: *Scope.Block,
1398 src: LazySrcLoc,
1399 ty: Type,
1400 struct_ptr: *ir.Inst,
1401 field_index: u32,
1402 ) !*ir.Inst {
1403 const inst = try block.sema.arena.create(ir.Inst.StructFieldPtr);
1404 inst.* = .{
1405 .base = .{
1406 .tag = .struct_field_ptr,
1407 .ty = ty,
1408 .src = src,
1409 },
1410 .struct_ptr = struct_ptr,
1411 .field_index = field_index,
1412 };
1413 try block.instructions.append(block.sema.gpa, &inst.base);
1414 return &inst.base;
1310 try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
1311 try block.instructions.ensureUnusedCapacity(gpa, 1);
1312
1313 const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
1314 sema.air_instructions.appendAssumeCapacity(inst);
1315 block.instructions.appendAssumeCapacity(result_index);
1316 return result_index;
14151317 }
14161318 };
14171319};
......@@ -2130,7 +2032,8 @@ pub const LazySrcLoc = union(enum) {
21302032 }
21312033};
21322034
2133pub const InnerError = error{ OutOfMemory, AnalysisFail };
2035pub const SemaError = error{ OutOfMemory, AnalysisFail };
2036pub const CompileError = error{ OutOfMemory, AnalysisFail, NeededSourceLocation };
21342037
21352038pub fn deinit(mod: *Module) void {
21362039 const gpa = mod.gpa;
......@@ -2769,7 +2672,7 @@ pub fn mapOldZirToNew(
27692672 }
27702673}
27712674
2772pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
2675pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
27732676 const tracy = trace(@src());
27742677 defer tracy.end();
27752678
......@@ -2869,7 +2772,7 @@ pub fn semaPkg(mod: *Module, pkg: *Package) !void {
28692772
28702773/// Regardless of the file status, will create a `Decl` so that we
28712774/// can track dependencies and re-analyze when the file becomes outdated.
2872pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
2775pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
28732776 const tracy = trace(@src());
28742777 defer tracy.end();
28752778
......@@ -2999,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29992902 decl.generation = mod.generation;
30002903 return false;
30012904 }
2905 log.debug("semaDecl {*} ({s})", .{ decl, decl.name });
30022906
30032907 var block_scope: Scope.Block = .{
30042908 .parent = null,
......@@ -3035,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
30352939 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
30362940
30372941 if (decl_tv.val.castTag(.function)) |fn_payload| {
3038 var prev_type_has_bits = false;
3039 var prev_is_inline = false;
3040 var type_changed = true;
3041
3042 if (decl.has_tv) {
3043 prev_type_has_bits = decl.ty.hasCodeGenBits();
3044 type_changed = !decl.ty.eql(decl_tv.ty);
3045 if (decl.getFunction()) |prev_func| {
3046 prev_is_inline = prev_func.state == .inline_only;
2942 const func = fn_payload.data;
2943 const owns_tv = func.owner_decl == decl;
2944 if (owns_tv) {
2945 var prev_type_has_bits = false;
2946 var prev_is_inline = false;
2947 var type_changed = true;
2948
2949 if (decl.has_tv) {
2950 prev_type_has_bits = decl.ty.hasCodeGenBits();
2951 type_changed = !decl.ty.eql(decl_tv.ty);
2952 if (decl.getFunction()) |prev_func| {
2953 prev_is_inline = prev_func.state == .inline_only;
2954 }
2955 decl.clearValues(gpa);
30472956 }
3048 decl.clearValues(gpa);
3049 }
3050
3051 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3052 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3053 decl.align_val = try align_val.copy(&decl_arena.allocator);
3054 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3055 decl.has_tv = true;
3056 decl.owns_tv = fn_payload.data.owner_decl == decl;
3057 decl_arena_state.* = decl_arena.state;
3058 decl.value_arena = decl_arena_state;
3059 decl.analysis = .complete;
3060 decl.generation = mod.generation;
30612957
3062 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
3063 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
3064 // We don't fully codegen the decl until later, but we do need to reserve a global
3065 // offset table index for it. This allows us to codegen decls out of dependency order,
3066 // increasing how many computations can be done in parallel.
3067 try mod.comp.bin_file.allocateDeclIndexes(decl);
3068 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
3069 if (type_changed and mod.emit_h != null) {
3070 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
2958 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
2959 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
2960 decl.align_val = try align_val.copy(&decl_arena.allocator);
2961 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
2962 decl.has_tv = true;
2963 decl.owns_tv = owns_tv;
2964 decl_arena_state.* = decl_arena.state;
2965 decl.value_arena = decl_arena_state;
2966 decl.analysis = .complete;
2967 decl.generation = mod.generation;
2968
2969 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
2970 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
2971 // We don't fully codegen the decl until later, but we do need to reserve a global
2972 // offset table index for it. This allows us to codegen decls out of dependency order,
2973 // increasing how many computations can be done in parallel.
2974 try mod.comp.bin_file.allocateDeclIndexes(decl);
2975 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
2976 if (type_changed and mod.emit_h != null) {
2977 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
2978 }
2979 } else if (!prev_is_inline and prev_type_has_bits) {
2980 mod.comp.bin_file.freeDecl(decl);
30712981 }
3072 } else if (!prev_is_inline and prev_type_has_bits) {
3073 mod.comp.bin_file.freeDecl(decl);
3074 }
30752982
3076 if (decl.is_exported) {
3077 const export_src = src; // TODO make this point at `export` token
3078 if (is_inline) {
3079 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
2983 if (decl.is_exported) {
2984 const export_src = src; // TODO make this point at `export` token
2985 if (is_inline) {
2986 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
2987 }
2988 // The scope needs to have the decl in it.
2989 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
30802990 }
3081 // The scope needs to have the decl in it.
3082 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3083 }
3084 return type_changed or is_inline != prev_is_inline;
3085 } else {
3086 var type_changed = true;
3087 if (decl.has_tv) {
3088 type_changed = !decl.ty.eql(decl_tv.ty);
3089 decl.clearValues(gpa);
2991 return type_changed or is_inline != prev_is_inline;
30902992 }
2993 }
2994 var type_changed = true;
2995 if (decl.has_tv) {
2996 type_changed = !decl.ty.eql(decl_tv.ty);
2997 decl.clearValues(gpa);
2998 }
30912999
3092 decl.owns_tv = false;
3093 var queue_linker_work = false;
3094 if (decl_tv.val.castTag(.variable)) |payload| {
3095 const variable = payload.data;
3096 if (variable.owner_decl == decl) {
3097 decl.owns_tv = true;
3098 queue_linker_work = true;
3000 decl.owns_tv = false;
3001 var queue_linker_work = false;
3002 if (decl_tv.val.castTag(.variable)) |payload| {
3003 const variable = payload.data;
3004 if (variable.owner_decl == decl) {
3005 decl.owns_tv = true;
3006 queue_linker_work = true;
30993007
3100 const copied_init = try variable.init.copy(&decl_arena.allocator);
3101 variable.init = copied_init;
3102 }
3103 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
3104 const owner_decl = payload.data;
3105 if (decl == owner_decl) {
3106 decl.owns_tv = true;
3107 queue_linker_work = true;
3108 }
3008 const copied_init = try variable.init.copy(&decl_arena.allocator);
3009 variable.init = copied_init;
31093010 }
3011 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
3012 const owner_decl = payload.data;
3013 if (decl == owner_decl) {
3014 decl.owns_tv = true;
3015 queue_linker_work = true;
3016 }
3017 }
31103018
3111 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3112 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3113 decl.align_val = try align_val.copy(&decl_arena.allocator);
3114 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3115 decl.has_tv = true;
3116 decl_arena_state.* = decl_arena.state;
3117 decl.value_arena = decl_arena_state;
3118 decl.analysis = .complete;
3119 decl.generation = mod.generation;
3120
3121 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3122 try mod.comp.bin_file.allocateDeclIndexes(decl);
3123 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
3019 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3020 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3021 decl.align_val = try align_val.copy(&decl_arena.allocator);
3022 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3023 decl.has_tv = true;
3024 decl_arena_state.* = decl_arena.state;
3025 decl.value_arena = decl_arena_state;
3026 decl.analysis = .complete;
3027 decl.generation = mod.generation;
31243028
3125 if (type_changed and mod.emit_h != null) {
3126 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
3127 }
3128 }
3029 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3030 try mod.comp.bin_file.allocateDeclIndexes(decl);
3031 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
31293032
3130 if (decl.is_exported) {
3131 const export_src = src; // TODO point to the export token
3132 // The scope needs to have the decl in it.
3133 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3033 if (type_changed and mod.emit_h != null) {
3034 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
31343035 }
3036 }
31353037
3136 return type_changed;
3038 if (decl.is_exported) {
3039 const export_src = src; // TODO point to the export token
3040 // The scope needs to have the decl in it.
3041 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
31373042 }
3043
3044 return type_changed;
31383045}
31393046
31403047/// Returns the depender's index of the dependee.
......@@ -3284,7 +3191,7 @@ pub fn scanNamespace(
32843191 extra_start: usize,
32853192 decls_len: u32,
32863193 parent_decl: *Decl,
3287) InnerError!usize {
3194) SemaError!usize {
32883195 const tracy = trace(@src());
32893196 defer tracy.end();
32903197
......@@ -3331,7 +3238,7 @@ const ScanDeclIter = struct {
33313238 unnamed_test_index: usize = 0,
33323239};
33333240
3334fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!void {
3241fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!void {
33353242 const tracy = trace(@src());
33363243 defer tracy.end();
33373244
......@@ -3585,39 +3492,25 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
35853492 mod.gpa.free(kv.value);
35863493}
35873494
3588pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
3495pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
35893496 const tracy = trace(@src());
35903497 defer tracy.end();
35913498
3499 const gpa = mod.gpa;
3500
35923501 // Use the Decl's arena for function memory.
3593 var arena = decl.value_arena.?.promote(mod.gpa);
3502 var arena = decl.value_arena.?.promote(gpa);
35943503 defer decl.value_arena.?.* = arena.state;
35953504
35963505 const fn_ty = decl.ty;
3597 const param_inst_list = try mod.gpa.alloc(*ir.Inst, fn_ty.fnParamLen());
3598 defer mod.gpa.free(param_inst_list);
3599
3600 for (param_inst_list) |*param_inst, param_index| {
3601 const param_type = fn_ty.fnParamType(param_index);
3602 const arg_inst = try arena.allocator.create(ir.Inst.Arg);
3603 arg_inst.* = .{
3604 .base = .{
3605 .tag = .arg,
3606 .ty = param_type,
3607 .src = .unneeded,
3608 },
3609 .name = undefined, // Set in the semantic analysis of the arg instruction.
3610 };
3611 param_inst.* = &arg_inst.base;
3612 }
3613
3614 const zir = decl.namespace.file_scope.zir;
3506 const param_inst_list = try gpa.alloc(Air.Inst.Ref, fn_ty.fnParamLen());
3507 defer gpa.free(param_inst_list);
36153508
36163509 var sema: Sema = .{
36173510 .mod = mod,
3618 .gpa = mod.gpa,
3511 .gpa = gpa,
36193512 .arena = &arena.allocator,
3620 .code = zir,
3513 .code = decl.namespace.file_scope.zir,
36213514 .owner_decl = decl,
36223515 .namespace = decl.namespace,
36233516 .func = func,
......@@ -3626,6 +3519,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
36263519 };
36273520 defer sema.deinit();
36283521
3522 // First few indexes of extra are reserved and set at the end.
3523 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
3524 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
3525 sema.air_extra.items.len += reserved_count;
3526
36293527 var inner_block: Scope.Block = .{
36303528 .parent = null,
36313529 .sema = &sema,
......@@ -3634,20 +3532,50 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
36343532 .inlining = null,
36353533 .is_comptime = false,
36363534 };
3637 defer inner_block.instructions.deinit(mod.gpa);
3535 defer inner_block.instructions.deinit(gpa);
36383536
3639 // AIR currently requires the arg parameters to be the first N instructions
3640 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);
3537 // AIR requires the arg parameters to be the first N instructions.
3538 try inner_block.instructions.ensureTotalCapacity(gpa, param_inst_list.len);
3539 for (param_inst_list) |*param_inst, param_index| {
3540 const param_type = fn_ty.fnParamType(param_index);
3541 const ty_ref = try sema.addType(param_type);
3542 const arg_index = @intCast(u32, sema.air_instructions.len);
3543 inner_block.instructions.appendAssumeCapacity(arg_index);
3544 param_inst.* = Air.indexToRef(arg_index);
3545 try sema.air_instructions.append(gpa, .{
3546 .tag = .arg,
3547 .data = .{
3548 .ty_str = .{
3549 .ty = ty_ref,
3550 .str = undefined, // Set in the semantic analysis of the arg instruction.
3551 },
3552 },
3553 });
3554 }
36413555
36423556 func.state = .in_progress;
36433557 log.debug("set {s} to in_progress", .{decl.name});
36443558
36453559 try sema.analyzeFnBody(&inner_block, func.zir_body_inst);
36463560
3647 const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items);
3561 // Copy the block into place and mark that as the main block.
3562 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
3563 inner_block.instructions.items.len);
3564 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
3565 .body_len = @intCast(u32, inner_block.instructions.items.len),
3566 });
3567 sema.air_extra.appendSliceAssumeCapacity(inner_block.instructions.items);
3568 sema.air_extra.items[@enumToInt(Air.ExtraIndex.main_block)] = main_block_index;
3569
36483570 func.state = .success;
3649 func.body = .{ .instructions = instructions };
36503571 log.debug("set {s} to success", .{decl.name});
3572
3573 return Air{
3574 .instructions = sema.air_instructions.toOwnedSlice(),
3575 .extra = sema.air_extra.toOwnedSlice(gpa),
3576 .values = sema.air_values.toOwnedSlice(gpa),
3577 .variables = sema.air_variables.toOwnedSlice(gpa),
3578 };
36513579}
36523580
36533581fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
......@@ -3801,94 +3729,6 @@ pub fn analyzeExport(
38013729 de_gop.value_ptr.*[de_gop.value_ptr.len - 1] = new_export;
38023730 errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
38033731}
3804pub fn constInst(mod: *Module, arena: *Allocator, src: LazySrcLoc, typed_value: TypedValue) !*ir.Inst {
3805 _ = mod;
3806 const const_inst = try arena.create(ir.Inst.Constant);
3807 const_inst.* = .{
3808 .base = .{
3809 .tag = ir.Inst.Constant.base_tag,
3810 .ty = typed_value.ty,
3811 .src = src,
3812 },
3813 .val = typed_value.val,
3814 };
3815 return &const_inst.base;
3816}
3817
3818pub fn constType(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst {
3819 return mod.constInst(arena, src, .{
3820 .ty = Type.initTag(.type),
3821 .val = try ty.toValue(arena),
3822 });
3823}
3824
3825pub fn constVoid(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst {
3826 return mod.constInst(arena, src, .{
3827 .ty = Type.initTag(.void),
3828 .val = Value.initTag(.void_value),
3829 });
3830}
3831
3832pub fn constNoReturn(mod: *Module, arena: *Allocator, src: LazySrcLoc) !*ir.Inst {
3833 return mod.constInst(arena, src, .{
3834 .ty = Type.initTag(.noreturn),
3835 .val = Value.initTag(.unreachable_value),
3836 });
3837}
3838
3839pub fn constUndef(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type) !*ir.Inst {
3840 return mod.constInst(arena, src, .{
3841 .ty = ty,
3842 .val = Value.initTag(.undef),
3843 });
3844}
3845
3846pub fn constBool(mod: *Module, arena: *Allocator, src: LazySrcLoc, v: bool) !*ir.Inst {
3847 return mod.constInst(arena, src, .{
3848 .ty = Type.initTag(.bool),
3849 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
3850 });
3851}
3852
3853pub fn constIntUnsigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: u64) !*ir.Inst {
3854 return mod.constInst(arena, src, .{
3855 .ty = ty,
3856 .val = try Value.Tag.int_u64.create(arena, int),
3857 });
3858}
3859
3860pub fn constIntSigned(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, int: i64) !*ir.Inst {
3861 return mod.constInst(arena, src, .{
3862 .ty = ty,
3863 .val = try Value.Tag.int_i64.create(arena, int),
3864 });
3865}
3866
3867pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, big_int: BigIntConst) !*ir.Inst {
3868 if (big_int.positive) {
3869 if (big_int.to(u64)) |x| {
3870 return mod.constIntUnsigned(arena, src, ty, x);
3871 } else |err| switch (err) {
3872 error.NegativeIntoUnsigned => unreachable,
3873 error.TargetTooSmall => {}, // handled below
3874 }
3875 return mod.constInst(arena, src, .{
3876 .ty = ty,
3877 .val = try Value.Tag.int_big_positive.create(arena, big_int.limbs),
3878 });
3879 } else {
3880 if (big_int.to(i64)) |x| {
3881 return mod.constIntSigned(arena, src, ty, x);
3882 } else |err| switch (err) {
3883 error.NegativeIntoUnsigned => unreachable,
3884 error.TargetTooSmall => {}, // handled below
3885 }
3886 return mod.constInst(arena, src, .{
3887 .ty = ty,
3888 .val = try Value.Tag.int_big_negative.create(arena, big_int.limbs),
3889 });
3890 }
3891}
38923732
38933733pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
38943734 const scope_decl = scope.ownerDecl().?;
......@@ -4006,7 +3846,7 @@ pub fn fail(
40063846 src: LazySrcLoc,
40073847 comptime format: []const u8,
40083848 args: anytype,
4009) InnerError {
3849) CompileError {
40103850 const err_msg = try mod.errMsg(scope, src, format, args);
40113851 return mod.failWithOwnedErrorMsg(scope, err_msg);
40123852}
......@@ -4019,7 +3859,7 @@ pub fn failTok(
40193859 token_index: ast.TokenIndex,
40203860 comptime format: []const u8,
40213861 args: anytype,
4022) InnerError {
3862) CompileError {
40233863 const src = scope.srcDecl().?.tokSrcLoc(token_index);
40243864 return mod.fail(scope, src, format, args);
40253865}
......@@ -4032,18 +3872,21 @@ pub fn failNode(
40323872 node_index: ast.Node.Index,
40333873 comptime format: []const u8,
40343874 args: anytype,
4035) InnerError {
3875) CompileError {
40363876 const src = scope.srcDecl().?.nodeSrcLoc(node_index);
40373877 return mod.fail(scope, src, format, args);
40383878}
40393879
4040pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {
3880pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) CompileError {
40413881 @setCold(true);
40423882
40433883 {
40443884 errdefer err_msg.destroy(mod.gpa);
4045 try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.count() + 1);
4046 try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.count() + 1);
3885 if (err_msg.src_loc.lazy == .unneeded) {
3886 return error.NeededSourceLocation;
3887 }
3888 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
3889 try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1);
40473890 }
40483891 switch (scope.tag) {
40493892 .block => {
......@@ -4301,13 +4144,11 @@ pub fn floatMul(
43014144}
43024145
43034146pub fn simplePtrType(
4304 mod: *Module,
43054147 arena: *Allocator,
43064148 elem_ty: Type,
43074149 mutable: bool,
43084150 size: std.builtin.TypeInfo.Pointer.Size,
43094151) Allocator.Error!Type {
4310 _ = mod;
43114152 if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
43124153 return Type.initTag(.const_slice_u8);
43134154 }
......@@ -4424,38 +4265,6 @@ pub fn errorUnionType(
44244265 });
44254266}
44264267
4427pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void {
4428 const zir_module = scope.namespace();
4429 const source = zir_module.getSource(mod) catch @panic("dumpInst failed to get source");
4430 const loc = std.zig.findLineColumn(source, inst.src);
4431 if (inst.tag == .constant) {
4432 std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{
4433 inst.ty,
4434 inst.castTag(.constant).?.val,
4435 zir_module.subFilePath(),
4436 loc.line + 1,
4437 loc.column + 1,
4438 });
4439 } else if (inst.deaths == 0) {
4440 std.debug.print("{s} ty={} src={s}:{d}:{d}\n", .{
4441 @tagName(inst.tag),
4442 inst.ty,
4443 zir_module.subFilePath(),
4444 loc.line + 1,
4445 loc.column + 1,
4446 });
4447 } else {
4448 std.debug.print("{s} ty={} deaths={b} src={s}:{d}:{d}\n", .{
4449 @tagName(inst.tag),
4450 inst.ty,
4451 inst.deaths,
4452 zir_module.subFilePath(),
4453 loc.line + 1,
4454 loc.column + 1,
4455 });
4456 }
4457}
4458
44594268pub fn getTarget(mod: Module) Target {
44604269 return mod.comp.bin_file.options.target;
44614270}
......@@ -4576,7 +4385,7 @@ pub const SwitchProngSrc = union(enum) {
45764385 }
45774386};
45784387
4579pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {
4388pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void {
45804389 const tracy = trace(@src());
45814390 defer tracy.end();
45824391
......@@ -4726,7 +4535,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {
47264535 }
47274536}
47284537
4729pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void {
4538pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
47304539 const tracy = trace(@src());
47314540 defer tracy.end();
47324541
src/Sema.zig+1755-1461
......@@ -1,6 +1,6 @@
11//! Semantic analysis of ZIR instructions.
22//! Shared to every Block. Stored on the stack.
3//! State used for compiling a `Zir` into AIR.
3//! State used for compiling a ZIR into AIR.
44//! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions.
55//! Does type checking, comptime control flow, and safety-check generation.
66//! This is the the heart of the Zig compiler.
......@@ -11,6 +11,10 @@ gpa: *Allocator,
1111/// Points to the arena allocator of the Decl.
1212arena: *Allocator,
1313code: Zir,
14air_instructions: std.MultiArrayList(Air.Inst) = .{},
15air_extra: std.ArrayListUnmanaged(u32) = .{},
16air_values: std.ArrayListUnmanaged(Value) = .{},
17air_variables: std.ArrayListUnmanaged(*Module.Var) = .{},
1418/// Maps ZIR to AIR.
1519inst_map: InstMap = .{},
1620/// When analyzing an inline function call, owner_decl is the Decl of the caller
......@@ -32,7 +36,7 @@ func: ?*Module.Fn,
3236/// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function,
3337/// > otherwise it is the number of parameters of the function.
3438/// > param_count: u32
35param_inst_list: []const *ir.Inst,
39param_inst_list: []const Air.Inst.Ref,
3640branch_quota: u32 = 1000,
3741branch_count: u32 = 0,
3842/// This field is updated when a new source location becomes active, so that
......@@ -41,6 +45,7 @@ branch_count: u32 = 0,
4145/// contain a mapped source location.
4246src: LazySrcLoc = .{ .token_offset = 0 },
4347next_arg_index: usize = 0,
48decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},
4449
4550const std = @import("std");
4651const mem = std.mem;
......@@ -52,23 +57,28 @@ const Sema = @This();
5257const Value = @import("value.zig").Value;
5358const Type = @import("type.zig").Type;
5459const TypedValue = @import("TypedValue.zig");
55const ir = @import("air.zig");
60const Air = @import("Air.zig");
5661const Zir = @import("Zir.zig");
5762const Module = @import("Module.zig");
58const Inst = ir.Inst;
59const Body = ir.Body;
6063const trace = @import("tracy.zig").trace;
6164const Scope = Module.Scope;
62const InnerError = Module.InnerError;
65const CompileError = Module.CompileError;
66const SemaError = Module.SemaError;
6367const Decl = Module.Decl;
6468const LazySrcLoc = Module.LazySrcLoc;
6569const RangeSet = @import("RangeSet.zig");
6670const target_util = @import("target.zig");
6771
68pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, *ir.Inst);
72pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref);
6973
7074pub fn deinit(sema: *Sema) void {
71 sema.inst_map.deinit(sema.gpa);
75 const gpa = sema.gpa;
76 sema.air_instructions.deinit(gpa);
77 sema.air_extra.deinit(gpa);
78 sema.air_values.deinit(gpa);
79 sema.air_variables.deinit(gpa);
80 sema.inst_map.deinit(gpa);
81 sema.decl_val_table.deinit(gpa);
7282 sema.* = undefined;
7383}
7484
......@@ -76,7 +86,7 @@ pub fn analyzeFnBody(
7686 sema: *Sema,
7787 block: *Scope.Block,
7888 fn_body_inst: Zir.Inst.Index,
79) InnerError!void {
89) SemaError!void {
8090 const tags = sema.code.instructions.items(.tag);
8191 const datas = sema.code.instructions.items(.data);
8292 const body: []const Zir.Inst.Index = switch (tags[fn_body_inst]) {
......@@ -102,13 +112,16 @@ pub fn analyzeFnBody(
102112 },
103113 else => unreachable,
104114 };
105 _ = try sema.analyzeBody(block, body);
115 _ = sema.analyzeBody(block, body) catch |err| switch (err) {
116 error.NeededSourceLocation => unreachable,
117 else => |e| return e,
118 };
106119}
107120
108121/// Returns only the result from the body that is specified.
109122/// Only appropriate to call when it is determined at comptime that this body
110123/// has no peers.
111fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const Zir.Inst.Index) InnerError!*Inst {
124fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const Zir.Inst.Index) CompileError!Air.Inst.Ref {
112125 const break_inst = try sema.analyzeBody(block, body);
113126 const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;
114127 return sema.resolveInst(operand_ref);
......@@ -118,7 +131,7 @@ fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const Zir.Inst.Index) I
118131/// return type of `analyzeBody` so that we can tail call them.
119132/// Only appropriate to return when the instruction is known to be NoReturn
120133/// solely based on the ZIR tag.
121const always_noreturn: InnerError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined);
134const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined);
122135
123136/// This function is the main loop of `Sema` and it can be used in two different ways:
124137/// * The traditional way where there are N breaks out of the block and peer type
......@@ -133,7 +146,7 @@ pub fn analyzeBody(
133146 sema: *Sema,
134147 block: *Scope.Block,
135148 body: []const Zir.Inst.Index,
136) InnerError!Zir.Inst.Index {
149) CompileError!Zir.Inst.Index {
137150 // No tracy calls here, to avoid interfering with the tail call mechanism.
138151
139152 const map = &block.sema.inst_map;
......@@ -149,7 +162,7 @@ pub fn analyzeBody(
149162 var i: usize = 0;
150163 while (true) {
151164 const inst = body[i];
152 const air_inst = switch (tags[inst]) {
165 const air_inst: Air.Inst.Ref = switch (tags[inst]) {
153166 // zig fmt: off
154167 .arg => try sema.zirArg(block, inst),
155168 .alloc => try sema.zirAlloc(block, inst),
......@@ -174,8 +187,6 @@ pub fn analyzeBody(
174187 .block => try sema.zirBlock(block, inst),
175188 .suspend_block => try sema.zirSuspendBlock(block, inst),
176189 .bool_not => try sema.zirBoolNot(block, inst),
177 .bool_and => try sema.zirBoolOp(block, inst, false),
178 .bool_or => try sema.zirBoolOp(block, inst, true),
179190 .bool_br_and => try sema.zirBoolBr(block, inst, false),
180191 .bool_br_or => try sema.zirBoolBr(block, inst, true),
181192 .c_import => try sema.zirCImport(block, inst),
......@@ -504,7 +515,7 @@ pub fn analyzeBody(
504515 const break_inst = try sema.analyzeBody(block, inline_body);
505516 const break_data = datas[break_inst].@"break";
506517 if (inst == break_data.block_inst) {
507 break :blk try sema.resolveInst(break_data.operand);
518 break :blk sema.resolveInst(break_data.operand);
508519 } else {
509520 return break_inst;
510521 }
......@@ -520,20 +531,20 @@ pub fn analyzeBody(
520531 const break_inst = try sema.analyzeBody(block, inline_body);
521532 const break_data = datas[break_inst].@"break";
522533 if (inst == break_data.block_inst) {
523 break :blk try sema.resolveInst(break_data.operand);
534 break :blk sema.resolveInst(break_data.operand);
524535 } else {
525536 return break_inst;
526537 }
527538 },
528539 };
529 if (air_inst.ty.isNoReturn())
540 if (sema.typeOf(air_inst).isNoReturn())
530541 return always_noreturn;
531542 try map.put(sema.gpa, inst, air_inst);
532543 i += 1;
533544 }
534545}
535546
536fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
547fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
537548 const extended = sema.code.instructions.items(.data)[inst].extended;
538549 switch (extended.opcode) {
539550 // zig fmt: off
......@@ -552,7 +563,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
552563 .frame_address => return sema.zirFrameAddress( block, extended),
553564 .alloc => return sema.zirAllocExtended( block, extended),
554565 .builtin_extern => return sema.zirBuiltinExtern( block, extended),
555 .@"asm" => return sema.zirAsm( block, extended),
566 .@"asm" => return sema.zirAsm( block, extended, inst),
556567 .typeof_peer => return sema.zirTypeofPeer( block, extended),
557568 .compile_log => return sema.zirCompileLog( block, extended),
558569 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended),
......@@ -568,18 +579,13 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
568579 }
569580}
570581
571/// TODO when we rework AIR memory layout, this function will no longer have a possible error.
572pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.Inst {
582pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref {
573583 var i: usize = @enumToInt(zir_ref);
574584
575585 // First section of indexes correspond to a set number of constant values.
576586 if (i < Zir.Inst.Ref.typed_value_map.len) {
577 // TODO when we rework AIR memory layout, this function can be as simple as:
578 // if (zir_ref < Zir.const_inst_list.len + sema.param_count)
579 // return zir_ref;
580 // Until then we allocate memory for a new, mutable `ir.Inst` to match what
581 // AIR expects.
582 return sema.mod.constInst(sema.arena, .unneeded, Zir.Inst.Ref.typed_value_map[i]);
587 // We intentionally map the same indexes to the same values between ZIR and AIR.
588 return zir_ref;
583589 }
584590 i -= Zir.Inst.Ref.typed_value_map.len;
585591
......@@ -593,7 +599,7 @@ fn resolveConstBool(
593599 src: LazySrcLoc,
594600 zir_ref: Zir.Inst.Ref,
595601) !bool {
596 const air_inst = try sema.resolveInst(zir_ref);
602 const air_inst = sema.resolveInst(zir_ref);
597603 const wanted_type = Type.initTag(.bool);
598604 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
599605 const val = try sema.resolveConstValue(block, src, coerced_inst);
......@@ -606,7 +612,7 @@ fn resolveConstString(
606612 src: LazySrcLoc,
607613 zir_ref: Zir.Inst.Ref,
608614) ![]u8 {
609 const air_inst = try sema.resolveInst(zir_ref);
615 const air_inst = sema.resolveInst(zir_ref);
610616 const wanted_type = Type.initTag(.const_slice_u8);
611617 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
612618 const val = try sema.resolveConstValue(block, src, coerced_inst);
......@@ -614,24 +620,39 @@ fn resolveConstString(
614620}
615621
616622pub fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
617 const air_inst = try sema.resolveInst(zir_ref);
618 return sema.resolveAirAsType(block, src, air_inst);
623 const air_inst = sema.resolveInst(zir_ref);
624 return sema.analyzeAsType(block, src, air_inst);
619625}
620626
621fn resolveAirAsType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, air_inst: *ir.Inst) !Type {
627fn analyzeAsType(
628 sema: *Sema,
629 block: *Scope.Block,
630 src: LazySrcLoc,
631 air_inst: Air.Inst.Ref,
632) !Type {
622633 const wanted_type = Type.initTag(.@"type");
623634 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
624635 const val = try sema.resolveConstValue(block, src, coerced_inst);
625636 return val.toType(sema.arena);
626637}
627638
628fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value {
629 return (try sema.resolveDefinedValue(block, src, base)) orelse
639fn resolveConstValue(
640 sema: *Sema,
641 block: *Scope.Block,
642 src: LazySrcLoc,
643 air_ref: Air.Inst.Ref,
644) CompileError!Value {
645 return (try sema.resolveDefinedValue(block, src, air_ref)) orelse
630646 return sema.failWithNeededComptime(block, src);
631647}
632648
633fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value {
634 if (try sema.resolvePossiblyUndefinedValue(block, src, base)) |val| {
649fn resolveDefinedValue(
650 sema: *Sema,
651 block: *Scope.Block,
652 src: LazySrcLoc,
653 air_ref: Air.Inst.Ref,
654) CompileError!?Value {
655 if (try sema.resolvePossiblyUndefinedValue(block, src, air_ref)) |val| {
635656 if (val.isUndef()) {
636657 return sema.failWithUseOfUndef(block, src);
637658 }
......@@ -644,20 +665,36 @@ fn resolvePossiblyUndefinedValue(
644665 sema: *Sema,
645666 block: *Scope.Block,
646667 src: LazySrcLoc,
647 base: *ir.Inst,
648) !?Value {
649 if (try sema.typeHasOnePossibleValue(block, src, base.ty)) |opv| {
668 inst: Air.Inst.Ref,
669) CompileError!?Value {
670 // First section of indexes correspond to a set number of constant values.
671 var i: usize = @enumToInt(inst);
672 if (i < Air.Inst.Ref.typed_value_map.len) {
673 return Air.Inst.Ref.typed_value_map[i].val;
674 }
675 i -= Air.Inst.Ref.typed_value_map.len;
676
677 if (try sema.typeHasOnePossibleValue(block, src, sema.typeOf(inst))) |opv| {
650678 return opv;
651679 }
652 const inst = base.castTag(.constant) orelse return null;
653 return inst.val;
680
681 switch (sema.air_instructions.items(.tag)[i]) {
682 .constant => {
683 const ty_pl = sema.air_instructions.items(.data)[i].ty_pl;
684 return sema.air_values.items[ty_pl.payload];
685 },
686 .const_ty => {
687 return try sema.air_instructions.items(.data)[i].ty.toValue(sema.arena);
688 },
689 else => return null,
690 }
654691}
655692
656fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
693fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError {
657694 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
658695}
659696
660fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
697fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError {
661698 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
662699}
663700
......@@ -672,7 +709,7 @@ fn resolveAlreadyCoercedInt(
672709 comptime Int: type,
673710) !Int {
674711 comptime assert(@typeInfo(Int).Int.bits <= 64);
675 const air_inst = try sema.resolveInst(zir_ref);
712 const air_inst = sema.resolveInst(zir_ref);
676713 const val = try sema.resolveConstValue(block, src, air_inst);
677714 switch (@typeInfo(Int).Int.signedness) {
678715 .signed => return @intCast(Int, val.toSignedInt()),
......@@ -687,7 +724,7 @@ fn resolveInt(
687724 zir_ref: Zir.Inst.Ref,
688725 dest_type: Type,
689726) !u64 {
690 const air_inst = try sema.resolveInst(zir_ref);
727 const air_inst = sema.resolveInst(zir_ref);
691728 const coerced = try sema.coerce(block, dest_type, air_inst, src);
692729 const val = try sema.resolveConstValue(block, src, coerced);
693730
......@@ -699,22 +736,22 @@ pub fn resolveInstConst(
699736 block: *Scope.Block,
700737 src: LazySrcLoc,
701738 zir_ref: Zir.Inst.Ref,
702) InnerError!TypedValue {
703 const air_inst = try sema.resolveInst(zir_ref);
704 const val = try sema.resolveConstValue(block, src, air_inst);
739) CompileError!TypedValue {
740 const air_ref = sema.resolveInst(zir_ref);
741 const val = try sema.resolveConstValue(block, src, air_ref);
705742 return TypedValue{
706 .ty = air_inst.ty,
743 .ty = sema.typeOf(air_ref),
707744 .val = val,
708745 };
709746}
710747
711fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
748fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
712749 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
713750 const src = inst_data.src();
714751 return sema.mod.fail(&block.base, src, "TODO implement zir_sema.zirBitcastResultPtr", .{});
715752}
716753
717fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
754fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
718755 _ = inst;
719756 const tracy = trace(@src());
720757 defer tracy.end();
......@@ -726,7 +763,7 @@ pub fn analyzeStructDecl(
726763 new_decl: *Decl,
727764 inst: Zir.Inst.Index,
728765 struct_obj: *Module.Struct,
729) InnerError!void {
766) SemaError!void {
730767 const extended = sema.code.instructions.items(.data)[inst].extended;
731768 assert(extended.opcode == .struct_decl);
732769 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
......@@ -749,7 +786,7 @@ fn zirStructDecl(
749786 block: *Scope.Block,
750787 extended: Zir.Inst.Extended.InstData,
751788 inst: Zir.Inst.Index,
752) InnerError!*Inst {
789) CompileError!Air.Inst.Ref {
753790 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
754791 const src: LazySrcLoc = if (small.has_src_node) blk: {
755792 const node_offset = @bitCast(i32, sema.code.extra[extended.operand]);
......@@ -820,7 +857,7 @@ fn zirEnumDecl(
820857 sema: *Sema,
821858 block: *Scope.Block,
822859 extended: Zir.Inst.Extended.InstData,
823) InnerError!*Inst {
860) CompileError!Air.Inst.Ref {
824861 const tracy = trace(@src());
825862 defer tracy.end();
826863
......@@ -1017,7 +1054,7 @@ fn zirUnionDecl(
10171054 block: *Scope.Block,
10181055 extended: Zir.Inst.Extended.InstData,
10191056 inst: Zir.Inst.Index,
1020) InnerError!*Inst {
1057) CompileError!Air.Inst.Ref {
10211058 const tracy = trace(@src());
10221059 defer tracy.end();
10231060
......@@ -1081,7 +1118,7 @@ fn zirOpaqueDecl(
10811118 block: *Scope.Block,
10821119 inst: Zir.Inst.Index,
10831120 name_strategy: Zir.Inst.NameStrategy,
1084) InnerError!*Inst {
1121) CompileError!Air.Inst.Ref {
10851122 const tracy = trace(@src());
10861123 defer tracy.end();
10871124
......@@ -1101,7 +1138,7 @@ fn zirErrorSetDecl(
11011138 block: *Scope.Block,
11021139 inst: Zir.Inst.Index,
11031140 name_strategy: Zir.Inst.NameStrategy,
1104) InnerError!*Inst {
1141) CompileError!Air.Inst.Ref {
11051142 const tracy = trace(@src());
11061143 defer tracy.end();
11071144
......@@ -1141,7 +1178,7 @@ fn zirRetPtr(
11411178 sema: *Sema,
11421179 block: *Scope.Block,
11431180 extended: Zir.Inst.Extended.InstData,
1144) InnerError!*Inst {
1181) CompileError!Air.Inst.Ref {
11451182 const tracy = trace(@src());
11461183 defer tracy.end();
11471184
......@@ -1149,16 +1186,16 @@ fn zirRetPtr(
11491186 try sema.requireFunctionBlock(block, src);
11501187 const fn_ty = sema.func.?.owner_decl.ty;
11511188 const ret_type = fn_ty.fnReturnType();
1152 const ptr_type = try sema.mod.simplePtrType(sema.arena, ret_type, true, .One);
1153 return block.addNoOp(src, ptr_type, .alloc);
1189 const ptr_type = try Module.simplePtrType(sema.arena, ret_type, true, .One);
1190 return block.addTy(.alloc, ptr_type);
11541191}
11551192
1156fn zirRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1193fn zirRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11571194 const tracy = trace(@src());
11581195 defer tracy.end();
11591196
11601197 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
1161 const operand = try sema.resolveInst(inst_data.operand);
1198 const operand = sema.resolveInst(inst_data.operand);
11621199 return sema.analyzeRef(block, inst_data.src(), operand);
11631200}
11641201
......@@ -1166,7 +1203,7 @@ fn zirRetType(
11661203 sema: *Sema,
11671204 block: *Scope.Block,
11681205 extended: Zir.Inst.Extended.InstData,
1169) InnerError!*Inst {
1206) CompileError!Air.Inst.Ref {
11701207 const tracy = trace(@src());
11711208 defer tracy.end();
11721209
......@@ -1174,15 +1211,15 @@ fn zirRetType(
11741211 try sema.requireFunctionBlock(block, src);
11751212 const fn_ty = sema.func.?.owner_decl.ty;
11761213 const ret_type = fn_ty.fnReturnType();
1177 return sema.mod.constType(sema.arena, src, ret_type);
1214 return sema.addType(ret_type);
11781215}
11791216
1180fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1217fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
11811218 const tracy = trace(@src());
11821219 defer tracy.end();
11831220
11841221 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1185 const operand = try sema.resolveInst(inst_data.operand);
1222 const operand = sema.resolveInst(inst_data.operand);
11861223 const src = inst_data.src();
11871224
11881225 return sema.ensureResultUsed(block, operand, src);
......@@ -1191,37 +1228,39 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) I
11911228fn ensureResultUsed(
11921229 sema: *Sema,
11931230 block: *Scope.Block,
1194 operand: *Inst,
1231 operand: Air.Inst.Ref,
11951232 src: LazySrcLoc,
1196) InnerError!void {
1197 switch (operand.ty.zigTypeTag()) {
1233) CompileError!void {
1234 const operand_ty = sema.typeOf(operand);
1235 switch (operand_ty.zigTypeTag()) {
11981236 .Void, .NoReturn => return,
11991237 else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}),
12001238 }
12011239}
12021240
1203fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1241fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
12041242 const tracy = trace(@src());
12051243 defer tracy.end();
12061244
12071245 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1208 const operand = try sema.resolveInst(inst_data.operand);
1246 const operand = sema.resolveInst(inst_data.operand);
12091247 const src = inst_data.src();
1210 switch (operand.ty.zigTypeTag()) {
1248 const operand_ty = sema.typeOf(operand);
1249 switch (operand_ty.zigTypeTag()) {
12111250 .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}),
12121251 else => return,
12131252 }
12141253}
12151254
1216fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1255fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12171256 const tracy = trace(@src());
12181257 defer tracy.end();
12191258
12201259 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
12211260 const src = inst_data.src();
1222 const array_ptr = try sema.resolveInst(inst_data.operand);
1261 const array_ptr = sema.resolveInst(inst_data.operand);
12231262
1224 const elem_ty = array_ptr.ty.elemType();
1263 const elem_ty = sema.typeOf(array_ptr).elemType();
12251264 if (!elem_ty.isIndexable()) {
12261265 const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node };
12271266 const msg = msg: {
......@@ -1244,46 +1283,47 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
12441283 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
12451284 }
12461285 const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src);
1247 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
1286 const result_ptr_src = src;
1287 return sema.analyzeLoad(block, src, result_ptr, result_ptr_src);
12481288}
12491289
1250fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1290fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12511291 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
12521292 const arg_name = inst_data.get(sema.code);
12531293 const arg_index = sema.next_arg_index;
12541294 sema.next_arg_index += 1;
12551295
12561296 // TODO check if arg_name shadows a Decl
1297 _ = arg_name;
12571298
12581299 if (block.inlining) |_| {
12591300 return sema.param_inst_list[arg_index];
12601301 }
12611302
1262 // Need to set the name of the Air.Arg instruction.
1263 const air_arg = sema.param_inst_list[arg_index].castTag(.arg).?;
1264 air_arg.name = arg_name;
1265 return &air_arg.base;
1303 // Set the name of the Air.Arg instruction for use by codegen debug info.
1304 const air_arg = sema.param_inst_list[arg_index];
1305 sema.air_instructions.items(.data)[Air.refToIndex(air_arg).?].ty_str.str = inst_data.start;
1306 return air_arg;
12661307}
12671308
12681309fn zirAllocExtended(
12691310 sema: *Sema,
12701311 block: *Scope.Block,
12711312 extended: Zir.Inst.Extended.InstData,
1272) InnerError!*Inst {
1313) CompileError!Air.Inst.Ref {
12731314 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
12741315 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
12751316 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended", .{});
12761317}
12771318
1278fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1319fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12791320 const tracy = trace(@src());
12801321 defer tracy.end();
12811322
12821323 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1283 const src = inst_data.src();
12841324 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
12851325 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
1286 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
1326 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
12871327
12881328 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);
12891329 val_payload.* = .{
......@@ -1292,19 +1332,16 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
12921332 .val = undefined, // astgen guarantees there will be a store before the first load
12931333 },
12941334 };
1295 return sema.mod.constInst(sema.arena, src, .{
1296 .ty = ptr_type,
1297 .val = Value.initPayload(&val_payload.base),
1298 });
1335 return sema.addConstant(ptr_type, Value.initPayload(&val_payload.base));
12991336}
13001337
1301fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1338fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13021339 const src_node = sema.code.instructions.items(.data)[inst].node;
13031340 const src: LazySrcLoc = .{ .node_offset = src_node };
13041341 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocInferredComptime", .{});
13051342}
13061343
1307fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1344fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13081345 const tracy = trace(@src());
13091346 defer tracy.end();
13101347
......@@ -1312,12 +1349,12 @@ fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*
13121349 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
13131350 const var_decl_src = inst_data.src();
13141351 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
1315 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
1352 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
13161353 try sema.requireRuntimeBlock(block, var_decl_src);
1317 return block.addNoOp(var_decl_src, ptr_type, .alloc);
1354 return block.addTy(.alloc, ptr_type);
13181355}
13191356
1320fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1357fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13211358 const tracy = trace(@src());
13221359 defer tracy.end();
13231360
......@@ -1326,9 +1363,9 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
13261363 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
13271364 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
13281365 try sema.validateVarType(block, ty_src, var_type);
1329 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
1366 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
13301367 try sema.requireRuntimeBlock(block, var_decl_src);
1331 return block.addNoOp(var_decl_src, ptr_type, .alloc);
1368 return block.addTy(.alloc, ptr_type);
13321369}
13331370
13341371fn zirAllocInferred(
......@@ -1336,7 +1373,7 @@ fn zirAllocInferred(
13361373 block: *Scope.Block,
13371374 inst: Zir.Inst.Index,
13381375 inferred_alloc_ty: Type,
1339) InnerError!*Inst {
1376) CompileError!Air.Inst.Ref {
13401377 const tracy = trace(@src());
13411378 defer tracy.end();
13421379
......@@ -1351,27 +1388,27 @@ fn zirAllocInferred(
13511388 // not needed in the case of constant values. However here, we plan to "downgrade"
13521389 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
13531390 // to the block even though it is currently a `.constant`.
1354 const result = try sema.mod.constInst(sema.arena, src, .{
1355 .ty = inferred_alloc_ty,
1356 .val = Value.initPayload(&val_payload.base),
1357 });
1391 const result = try sema.addConstant(inferred_alloc_ty, Value.initPayload(&val_payload.base));
13581392 try sema.requireFunctionBlock(block, src);
1359 try block.instructions.append(sema.gpa, result);
1393 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
13601394 return result;
13611395}
13621396
1363fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1397fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
13641398 const tracy = trace(@src());
13651399 defer tracy.end();
13661400
13671401 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
13681402 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1369 const ptr = try sema.resolveInst(inst_data.operand);
1370 const ptr_val = ptr.castTag(.constant).?.val;
1403 const ptr = sema.resolveInst(inst_data.operand);
1404 const ptr_inst = Air.refToIndex(ptr).?;
1405 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
1406 const air_datas = sema.air_instructions.items(.data);
1407 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
13711408 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
13721409 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
13731410 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);
1374 const var_is_mut = switch (ptr.ty.tag()) {
1411 const var_is_mut = switch (sema.typeOf(ptr).tag()) {
13751412 .inferred_alloc_const => false,
13761413 .inferred_alloc_mut => true,
13771414 else => unreachable,
......@@ -1379,14 +1416,16 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
13791416 if (var_is_mut) {
13801417 try sema.validateVarType(block, ty_src, final_elem_ty);
13811418 }
1382 const final_ptr_ty = try sema.mod.simplePtrType(sema.arena, final_elem_ty, true, .One);
1419 const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
13831420
13841421 // Change it to a normal alloc.
1385 ptr.ty = final_ptr_ty;
1386 ptr.tag = .alloc;
1422 sema.air_instructions.set(ptr_inst, .{
1423 .tag = .alloc,
1424 .data = .{ .ty = final_ptr_ty },
1425 });
13871426}
13881427
1389fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1428fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
13901429 const tracy = trace(@src());
13911430 defer tracy.end();
13921431
......@@ -1400,8 +1439,8 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
14001439 const struct_obj: *Module.Struct = s: {
14011440 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
14021441 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
1403 const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
1404 break :s object_ptr.ty.elemType().castTag(.@"struct").?.data;
1442 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
1443 break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data;
14051444 };
14061445
14071446 // Maps field index to field_ptr index of where it was already initialized.
......@@ -1459,7 +1498,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
14591498 }
14601499}
14611500
1462fn zirValidateArrayInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1501fn zirValidateArrayInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
14631502 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
14641503 const src = inst_data.src();
14651504 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirValidateArrayInitPtr", .{});
......@@ -1471,7 +1510,7 @@ fn failWithBadFieldAccess(
14711510 struct_obj: *Module.Struct,
14721511 field_src: LazySrcLoc,
14731512 field_name: []const u8,
1474) InnerError {
1513) CompileError {
14751514 const mod = sema.mod;
14761515 const gpa = sema.gpa;
14771516
......@@ -1498,7 +1537,7 @@ fn failWithBadUnionFieldAccess(
14981537 union_obj: *Module.Union,
14991538 field_src: LazySrcLoc,
15001539 field_name: []const u8,
1501) InnerError {
1540) CompileError {
15021541 const mod = sema.mod;
15031542 const gpa = sema.gpa;
15041543
......@@ -1519,7 +1558,7 @@ fn failWithBadUnionFieldAccess(
15191558 return mod.failWithOwnedErrorMsg(&block.base, msg);
15201559}
15211560
1522fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1561fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
15231562 const tracy = trace(@src());
15241563 defer tracy.end();
15251564
......@@ -1529,37 +1568,41 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
15291568 // to omit it.
15301569 return;
15311570 }
1532 const ptr = try sema.resolveInst(bin_inst.lhs);
1533 const value = try sema.resolveInst(bin_inst.rhs);
1534 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);
1571 const ptr = sema.resolveInst(bin_inst.lhs);
1572 const value = sema.resolveInst(bin_inst.rhs);
1573 const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One);
15351574 // TODO detect when this store should be done at compile-time. For example,
15361575 // if expressions should force it when the condition is compile-time known.
15371576 const src: LazySrcLoc = .unneeded;
15381577 try sema.requireRuntimeBlock(block, src);
1539 const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr);
1578 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
15401579 return sema.storePtr(block, src, bitcasted_ptr, value);
15411580}
15421581
1543fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1582fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
15441583 const tracy = trace(@src());
15451584 defer tracy.end();
15461585
15471586 const src: LazySrcLoc = .unneeded;
15481587 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1549 const ptr = try sema.resolveInst(bin_inst.lhs);
1550 const value = try sema.resolveInst(bin_inst.rhs);
1551 const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;
1588 const ptr = sema.resolveInst(bin_inst.lhs);
1589 const value = sema.resolveInst(bin_inst.rhs);
1590 const ptr_inst = Air.refToIndex(ptr).?;
1591 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
1592 const air_datas = sema.air_instructions.items(.data);
1593 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
1594 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
15521595 // Add the stored instruction to the set we will use to resolve peer types
15531596 // for the inferred allocation.
15541597 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);
15551598 // Create a runtime bitcast instruction with exactly the type the pointer wants.
1556 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);
1599 const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One);
15571600 try sema.requireRuntimeBlock(block, src);
1558 const bitcasted_ptr = try block.addUnOp(src, ptr_ty, .bitcast, ptr);
1601 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
15591602 return sema.storePtr(block, src, bitcasted_ptr, value);
15601603}
15611604
1562fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1605fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
15631606 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
15641607 const src = inst_data.src();
15651608 const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32);
......@@ -1567,51 +1610,54 @@ fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
15671610 sema.branch_quota = quota;
15681611}
15691612
1570fn zirStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1613fn zirStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
15711614 const tracy = trace(@src());
15721615 defer tracy.end();
15731616
15741617 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1575 const ptr = try sema.resolveInst(bin_inst.lhs);
1576 const value = try sema.resolveInst(bin_inst.rhs);
1618 const ptr = sema.resolveInst(bin_inst.lhs);
1619 const value = sema.resolveInst(bin_inst.rhs);
15771620 return sema.storePtr(block, sema.src, ptr, value);
15781621}
15791622
1580fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
1623fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
15811624 const tracy = trace(@src());
15821625 defer tracy.end();
15831626
15841627 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
15851628 const src = inst_data.src();
15861629 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
1587 const ptr = try sema.resolveInst(extra.lhs);
1588 const value = try sema.resolveInst(extra.rhs);
1630 const ptr = sema.resolveInst(extra.lhs);
1631 const value = sema.resolveInst(extra.rhs);
15891632 return sema.storePtr(block, src, ptr, value);
15901633}
15911634
1592fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1635fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
15931636 const tracy = trace(@src());
15941637 defer tracy.end();
15951638
1596 const src: LazySrcLoc = .unneeded;
1639 const src = sema.src;
1640 const fn_inst_src = sema.src;
1641
15971642 const inst_data = sema.code.instructions.items(.data)[inst].param_type;
1598 const fn_inst = try sema.resolveInst(inst_data.callee);
1643 const fn_inst = sema.resolveInst(inst_data.callee);
1644 const fn_inst_ty = sema.typeOf(fn_inst);
15991645 const param_index = inst_data.param_index;
16001646
1601 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
1602 .Fn => fn_inst.ty,
1647 const fn_ty: Type = switch (fn_inst_ty.zigTypeTag()) {
1648 .Fn => fn_inst_ty,
16031649 .BoundFn => {
1604 return sema.mod.fail(&block.base, fn_inst.src, "TODO implement zirParamType for method call syntax", .{});
1650 return sema.mod.fail(&block.base, fn_inst_src, "TODO implement zirParamType for method call syntax", .{});
16051651 },
16061652 else => {
1607 return sema.mod.fail(&block.base, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
1653 return sema.mod.fail(&block.base, fn_inst_src, "expected function, found '{}'", .{fn_inst_ty});
16081654 },
16091655 };
16101656
16111657 const param_count = fn_ty.fnParamLen();
16121658 if (param_index >= param_count) {
16131659 if (fn_ty.fnIsVarArgs()) {
1614 return sema.mod.constType(sema.arena, src, Type.initTag(.var_args_param));
1660 return sema.addType(Type.initTag(.var_args_param));
16151661 }
16161662 return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{
16171663 param_index,
......@@ -1622,10 +1668,10 @@ fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
16221668
16231669 // TODO support generic functions
16241670 const param_type = fn_ty.fnParamType(param_index);
1625 return sema.mod.constType(sema.arena, src, param_type);
1671 return sema.addType(param_type);
16261672}
16271673
1628fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1674fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
16291675 const tracy = trace(@src());
16301676 defer tracy.end();
16311677
......@@ -1653,16 +1699,16 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
16531699 return sema.analyzeDeclRef(block, .unneeded, new_decl);
16541700}
16551701
1656fn zirInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1702fn zirInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
16571703 _ = block;
16581704 const tracy = trace(@src());
16591705 defer tracy.end();
16601706
16611707 const int = sema.code.instructions.items(.data)[inst].int;
1662 return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
1708 return sema.addIntUnsigned(Type.initTag(.comptime_int), int);
16631709}
16641710
1665fn zirIntBig(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1711fn zirIntBig(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
16661712 _ = block;
16671713 const tracy = trace(@src());
16681714 defer tracy.end();
......@@ -1674,40 +1720,35 @@ fn zirIntBig(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
16741720 const limbs = try arena.alloc(std.math.big.Limb, int.len);
16751721 mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes);
16761722
1677 return sema.mod.constInst(arena, .unneeded, .{
1678 .ty = Type.initTag(.comptime_int),
1679 .val = try Value.Tag.int_big_positive.create(arena, limbs),
1680 });
1723 return sema.addConstant(
1724 Type.initTag(.comptime_int),
1725 try Value.Tag.int_big_positive.create(arena, limbs),
1726 );
16811727}
16821728
1683fn zirFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1729fn zirFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
16841730 _ = block;
16851731 const arena = sema.arena;
1686 const inst_data = sema.code.instructions.items(.data)[inst].float;
1687 const src = inst_data.src();
1688 const number = inst_data.number;
1689
1690 return sema.mod.constInst(arena, src, .{
1691 .ty = Type.initTag(.comptime_float),
1692 .val = try Value.Tag.float_32.create(arena, number),
1693 });
1732 const number = sema.code.instructions.items(.data)[inst].float;
1733 return sema.addConstant(
1734 Type.initTag(.comptime_float),
1735 try Value.Tag.float_64.create(arena, number),
1736 );
16941737}
16951738
1696fn zirFloat128(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1739fn zirFloat128(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
16971740 _ = block;
16981741 const arena = sema.arena;
16991742 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
17001743 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
1701 const src = inst_data.src();
17021744 const number = extra.get();
1703
1704 return sema.mod.constInst(arena, src, .{
1705 .ty = Type.initTag(.comptime_float),
1706 .val = try Value.Tag.float_128.create(arena, number),
1707 });
1745 return sema.addConstant(
1746 Type.initTag(.comptime_float),
1747 try Value.Tag.float_128.create(arena, number),
1748 );
17081749}
17091750
1710fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
1751fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
17111752 const tracy = trace(@src());
17121753 defer tracy.end();
17131754
......@@ -1722,7 +1763,7 @@ fn zirCompileLog(
17221763 sema: *Sema,
17231764 block: *Scope.Block,
17241765 extended: Zir.Inst.Extended.InstData,
1725) InnerError!*Inst {
1766) CompileError!Air.Inst.Ref {
17261767 var managed = sema.mod.compile_log_text.toManaged(sema.gpa);
17271768 defer sema.mod.compile_log_text = managed.moveToUnmanaged();
17281769 const writer = managed.writer();
......@@ -1735,11 +1776,12 @@ fn zirCompileLog(
17351776 for (args) |arg_ref, i| {
17361777 if (i != 0) try writer.print(", ", .{});
17371778
1738 const arg = try sema.resolveInst(arg_ref);
1779 const arg = sema.resolveInst(arg_ref);
1780 const arg_ty = sema.typeOf(arg);
17391781 if (try sema.resolvePossiblyUndefinedValue(block, src, arg)) |val| {
1740 try writer.print("@as({}, {})", .{ arg.ty, val });
1782 try writer.print("@as({}, {})", .{ arg_ty, val });
17411783 } else {
1742 try writer.print("@as({}, [runtime value])", .{arg.ty});
1784 try writer.print("@as({}, [runtime value])", .{arg_ty});
17431785 }
17441786 }
17451787 try writer.print("\n", .{});
......@@ -1748,13 +1790,10 @@ fn zirCompileLog(
17481790 if (!gop.found_existing) {
17491791 gop.value_ptr.* = src_node;
17501792 }
1751 return sema.mod.constInst(sema.arena, src, .{
1752 .ty = Type.initTag(.void),
1753 .val = Value.initTag(.void_value),
1754 });
1793 return Air.Inst.Ref.void_value;
17551794}
17561795
1757fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
1796fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
17581797 const tracy = trace(@src());
17591798 defer tracy.end();
17601799
......@@ -1764,15 +1803,15 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
17641803 return always_noreturn;
17651804}
17661805
1767fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
1806fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
17681807 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
17691808 const src: LazySrcLoc = inst_data.src();
1770 const msg_inst = try sema.resolveInst(inst_data.operand);
1809 const msg_inst = sema.resolveInst(inst_data.operand);
17711810
17721811 return sema.panicWithMsg(block, src, msg_inst);
17731812}
17741813
1775fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1814fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17761815 const tracy = trace(@src());
17771816 defer tracy.end();
17781817
......@@ -1780,18 +1819,26 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
17801819 const src = inst_data.src();
17811820 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
17821821 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1822 const gpa = sema.gpa;
17831823
17841824 // AIR expects a block outside the loop block too.
1785 const block_inst = try sema.arena.create(Inst.Block);
1786 block_inst.* = .{
1787 .base = .{
1788 .tag = Inst.Block.base_tag,
1789 .ty = undefined,
1790 .src = src,
1791 },
1792 .body = undefined,
1793 };
1794
1825 // Reserve space for a Loop instruction so that generated Break instructions can
1826 // point to it, even if it doesn't end up getting used because the code ends up being
1827 // comptime evaluated.
1828 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
1829 const loop_inst = block_inst + 1;
1830 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
1831 sema.air_instructions.appendAssumeCapacity(.{
1832 .tag = .block,
1833 .data = undefined,
1834 });
1835 sema.air_instructions.appendAssumeCapacity(.{
1836 .tag = .loop,
1837 .data = .{ .ty_pl = .{
1838 .ty = .noreturn_type,
1839 .payload = undefined,
1840 } },
1841 });
17951842 var label: Scope.Block.Label = .{
17961843 .zir_block = inst,
17971844 .merges = .{
......@@ -1807,37 +1854,28 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
18071854 child_block.runtime_index += 1;
18081855 const merges = &child_block.label.?.merges;
18091856
1810 defer child_block.instructions.deinit(sema.gpa);
1811 defer merges.results.deinit(sema.gpa);
1812 defer merges.br_list.deinit(sema.gpa);
1813
1814 // Reserve space for a Loop instruction so that generated Break instructions can
1815 // point to it, even if it doesn't end up getting used because the code ends up being
1816 // comptime evaluated.
1817 const loop_inst = try sema.arena.create(Inst.Loop);
1818 loop_inst.* = .{
1819 .base = .{
1820 .tag = Inst.Loop.base_tag,
1821 .ty = Type.initTag(.noreturn),
1822 .src = src,
1823 },
1824 .body = undefined,
1825 };
1857 defer child_block.instructions.deinit(gpa);
1858 defer merges.results.deinit(gpa);
1859 defer merges.br_list.deinit(gpa);
18261860
18271861 var loop_block = child_block.makeSubBlock();
1828 defer loop_block.instructions.deinit(sema.gpa);
1862 defer loop_block.instructions.deinit(gpa);
18291863
18301864 _ = try sema.analyzeBody(&loop_block, body);
18311865
18321866 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
1867 try child_block.instructions.append(gpa, loop_inst);
18331868
1834 try child_block.instructions.append(sema.gpa, &loop_inst.base);
1835 loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) };
1836
1869 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
1870 loop_block.instructions.items.len);
1871 sema.air_instructions.items(.data)[loop_inst].ty_pl.payload = sema.addExtraAssumeCapacity(
1872 Air.Block{ .body_len = @intCast(u32, loop_block.instructions.items.len) },
1873 );
1874 sema.air_extra.appendSliceAssumeCapacity(loop_block.instructions.items);
18371875 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
18381876}
18391877
1840fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1878fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
18411879 const tracy = trace(@src());
18421880 defer tracy.end();
18431881
......@@ -1847,33 +1885,34 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inn
18471885 return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirCImport", .{});
18481886}
18491887
1850fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1888fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
18511889 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
18521890 const src = inst_data.src();
18531891 return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirSuspendBlock", .{});
18541892}
18551893
1856fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1894fn zirBlock(
1895 sema: *Sema,
1896 parent_block: *Scope.Block,
1897 inst: Zir.Inst.Index,
1898) CompileError!Air.Inst.Ref {
18571899 const tracy = trace(@src());
18581900 defer tracy.end();
18591901
1860 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1861 const src = inst_data.src();
1862 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1902 const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
1903 const src = pl_node.src();
1904 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
18631905 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1906 const gpa = sema.gpa;
18641907
18651908 // Reserve space for a Block instruction so that generated Break instructions can
18661909 // point to it, even if it doesn't end up getting used because the code ends up being
18671910 // comptime evaluated.
1868 const block_inst = try sema.arena.create(Inst.Block);
1869 block_inst.* = .{
1870 .base = .{
1871 .tag = Inst.Block.base_tag,
1872 .ty = undefined, // Set after analysis.
1873 .src = src,
1874 },
1875 .body = undefined,
1876 };
1911 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
1912 try sema.air_instructions.append(gpa, .{
1913 .tag = .block,
1914 .data = undefined,
1915 });
18771916
18781917 var label: Scope.Block.Label = .{
18791918 .zir_block = inst,
......@@ -1895,9 +1934,9 @@ fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inner
18951934 };
18961935 const merges = &child_block.label.?.merges;
18971936
1898 defer child_block.instructions.deinit(sema.gpa);
1899 defer merges.results.deinit(sema.gpa);
1900 defer merges.br_list.deinit(sema.gpa);
1937 defer child_block.instructions.deinit(gpa);
1938 defer merges.results.deinit(gpa);
1939 defer merges.br_list.deinit(gpa);
19011940
19021941 _ = try sema.analyzeBody(&child_block, body);
19031942
......@@ -1911,7 +1950,7 @@ fn resolveBlockBody(
19111950 child_block: *Scope.Block,
19121951 body: []const Zir.Inst.Index,
19131952 merges: *Scope.Block.Merges,
1914) InnerError!*Inst {
1953) CompileError!Air.Inst.Ref {
19151954 _ = try sema.analyzeBody(child_block, body);
19161955 return sema.analyzeBlockBody(parent_block, src, child_block, merges);
19171956}
......@@ -1922,30 +1961,31 @@ fn analyzeBlockBody(
19221961 src: LazySrcLoc,
19231962 child_block: *Scope.Block,
19241963 merges: *Scope.Block.Merges,
1925) InnerError!*Inst {
1964) CompileError!Air.Inst.Ref {
19261965 const tracy = trace(@src());
19271966 defer tracy.end();
19281967
1968 const gpa = sema.gpa;
1969
19291970 // Blocks must terminate with noreturn instruction.
19301971 assert(child_block.instructions.items.len != 0);
1931 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
1972 assert(sema.typeOf(Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn());
19321973
19331974 if (merges.results.items.len == 0) {
19341975 // No need for a block instruction. We can put the new instructions
19351976 // directly into the parent block.
1936 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items);
1937 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
1938 return copied_instructions[copied_instructions.len - 1];
1977 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
1978 return Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]);
19391979 }
19401980 if (merges.results.items.len == 1) {
19411981 const last_inst_index = child_block.instructions.items.len - 1;
19421982 const last_inst = child_block.instructions.items[last_inst_index];
1943 if (last_inst.breakBlock()) |br_block| {
1983 if (sema.getBreakBlock(last_inst)) |br_block| {
19441984 if (br_block == merges.block_inst) {
19451985 // No need for a block instruction. We can put the new instructions directly
19461986 // into the parent block. Here we omit the break instruction.
1947 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
1948 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
1987 const without_break = child_block.instructions.items[0..last_inst_index];
1988 try parent_block.instructions.appendSlice(gpa, without_break);
19491989 return merges.results.items[0];
19501990 }
19511991 }
......@@ -1955,50 +1995,70 @@ fn analyzeBlockBody(
19551995
19561996 // Need to set the type and emit the Block instruction. This allows machine code generation
19571997 // to emit a jump instruction to after the block when it encounters the break.
1958 try parent_block.instructions.append(sema.gpa, &merges.block_inst.base);
1998 try parent_block.instructions.append(gpa, merges.block_inst);
19591999 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items);
1960 merges.block_inst.base.ty = resolved_ty;
1961 merges.block_inst.body = .{
1962 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
1963 };
2000 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
2001 child_block.instructions.items.len);
2002 sema.air_instructions.items(.data)[merges.block_inst] = .{ .ty_pl = .{
2003 .ty = try sema.addType(resolved_ty),
2004 .payload = sema.addExtraAssumeCapacity(Air.Block{
2005 .body_len = @intCast(u32, child_block.instructions.items.len),
2006 }),
2007 } };
2008 sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items);
19642009 // Now that the block has its type resolved, we need to go back into all the break
19652010 // instructions, and insert type coercion on the operands.
19662011 for (merges.br_list.items) |br| {
1967 if (br.operand.ty.eql(resolved_ty)) {
2012 const br_operand = sema.air_instructions.items(.data)[br].br.operand;
2013 const br_operand_src = src;
2014 const br_operand_ty = sema.typeOf(br_operand);
2015 if (br_operand_ty.eql(resolved_ty)) {
19682016 // No type coercion needed.
19692017 continue;
19702018 }
19712019 var coerce_block = parent_block.makeSubBlock();
1972 defer coerce_block.instructions.deinit(sema.gpa);
1973 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src);
2020 defer coerce_block.instructions.deinit(gpa);
2021 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
19742022 // If no instructions were produced, such as in the case of a coercion of a
19752023 // constant value to a new type, we can simply point the br operand to it.
19762024 if (coerce_block.instructions.items.len == 0) {
1977 br.operand = coerced_operand;
2025 sema.air_instructions.items(.data)[br].br.operand = coerced_operand;
19782026 continue;
19792027 }
1980 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
1981 // Here we depend on the br instruction having been over-allocated (if necessary)
1982 // inside zirBreak so that it can be converted into a br_block_flat instruction.
1983 const br_src = br.base.src;
1984 const br_ty = br.base.ty;
1985 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
1986 br_block_flat.* = .{
1987 .base = .{
1988 .src = br_src,
1989 .ty = br_ty,
1990 .tag = .br_block_flat,
1991 },
1992 .block = merges.block_inst,
1993 .body = .{
1994 .instructions = try sema.arena.dupe(*Inst, coerce_block.instructions.items),
1995 },
1996 };
2028 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==
2029 Air.refToIndex(coerced_operand).?);
2030
2031 // Convert the br operand to a block.
2032 const br_operand_ty_ref = try sema.addType(br_operand_ty);
2033 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
2034 coerce_block.instructions.items.len);
2035 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
2036 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
2037 const sub_br_inst = sub_block_inst + 1;
2038 sema.air_instructions.items(.data)[br].br.operand = Air.indexToRef(sub_block_inst);
2039 sema.air_instructions.appendAssumeCapacity(.{
2040 .tag = .block,
2041 .data = .{ .ty_pl = .{
2042 .ty = br_operand_ty_ref,
2043 .payload = sema.addExtraAssumeCapacity(Air.Block{
2044 .body_len = @intCast(u32, coerce_block.instructions.items.len),
2045 }),
2046 } },
2047 });
2048 sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items);
2049 sema.air_extra.appendAssumeCapacity(sub_br_inst);
2050 sema.air_instructions.appendAssumeCapacity(.{
2051 .tag = .br,
2052 .data = .{ .br = .{
2053 .block_inst = sub_block_inst,
2054 .operand = coerced_operand,
2055 } },
2056 });
19972057 }
1998 return &merges.block_inst.base;
2058 return Air.indexToRef(merges.block_inst);
19992059}
20002060
2001fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2061fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20022062 const tracy = trace(@src());
20032063 defer tracy.end();
20042064
......@@ -2034,13 +2094,13 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
20342094 try sema.mod.analyzeExport(&block.base, src, export_name, decl);
20352095}
20362096
2037fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2097fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20382098 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
20392099 const src: LazySrcLoc = inst_data.src();
20402100 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetAlignStack", .{});
20412101}
20422102
2043fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2103fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20442104 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
20452105 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
20462106 const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand);
......@@ -2048,67 +2108,49 @@ fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
20482108 func.is_cold = is_cold;
20492109}
20502110
2051fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2111fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20522112 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
20532113 const src: LazySrcLoc = inst_data.src();
20542114 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetFloatMode", .{});
20552115}
20562116
2057fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2117fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20582118 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
20592119 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
20602120 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
20612121}
20622122
2063fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2123fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20642124 const tracy = trace(@src());
20652125 defer tracy.end();
20662126
20672127 const src_node = sema.code.instructions.items(.data)[inst].node;
20682128 const src: LazySrcLoc = .{ .node_offset = src_node };
20692129 try sema.requireRuntimeBlock(block, src);
2070 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
2130 _ = try block.addNoOp(.breakpoint);
20712131}
20722132
2073fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2133fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
20742134 const src_node = sema.code.instructions.items(.data)[inst].node;
20752135 const src: LazySrcLoc = .{ .node_offset = src_node };
20762136 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{});
20772137}
20782138
2079fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
2139fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
20802140 const tracy = trace(@src());
20812141 defer tracy.end();
20822142
20832143 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
2084 const src = sema.src;
2085 const operand = try sema.resolveInst(inst_data.operand);
2144 const operand = sema.resolveInst(inst_data.operand);
20862145 const zir_block = inst_data.block_inst;
20872146
20882147 var block = start_block;
20892148 while (true) {
20902149 if (block.label) |label| {
20912150 if (label.zir_block == zir_block) {
2092 // Here we add a br instruction, but we over-allocate a little bit
2093 // (if necessary) to make it possible to convert the instruction into
2094 // a br_block_flat instruction later.
2095 const br = @ptrCast(*Inst.Br, try sema.arena.alignedAlloc(
2096 u8,
2097 Inst.convertable_br_align,
2098 Inst.convertable_br_size,
2099 ));
2100 br.* = .{
2101 .base = .{
2102 .tag = .br,
2103 .ty = Type.initTag(.noreturn),
2104 .src = src,
2105 },
2106 .operand = operand,
2107 .block = label.merges.block_inst,
2108 };
2109 try start_block.instructions.append(sema.gpa, &br.base);
2151 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
21102152 try label.merges.results.append(sema.gpa, operand);
2111 try label.merges.br_list.append(sema.gpa, br);
2153 try label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
21122154 return inst;
21132155 }
21142156 }
......@@ -2116,7 +2158,7 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
21162158 }
21172159}
21182160
2119fn zirDbgStmt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
2161fn zirDbgStmt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
21202162 const tracy = trace(@src());
21212163 defer tracy.end();
21222164
......@@ -2127,10 +2169,16 @@ fn zirDbgStmt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
21272169 if (block.is_comptime) return;
21282170
21292171 const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;
2130 _ = try block.addDbgStmt(.unneeded, inst_data.line, inst_data.column);
2172 _ = try block.addInst(.{
2173 .tag = .dbg_stmt,
2174 .data = .{ .dbg_stmt = .{
2175 .line = inst_data.line,
2176 .column = inst_data.column,
2177 } },
2178 });
21312179}
21322180
2133fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2181fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21342182 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
21352183 const src = inst_data.src();
21362184 const decl_name = inst_data.get(sema.code);
......@@ -2138,7 +2186,7 @@ fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
21382186 return sema.analyzeDeclRef(block, src, decl);
21392187}
21402188
2141fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2189fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21422190 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
21432191 const src = inst_data.src();
21442192 const decl_name = inst_data.get(sema.code);
......@@ -2164,7 +2212,7 @@ fn lookupInNamespace(
21642212 sema: *Sema,
21652213 namespace: *Scope.Namespace,
21662214 ident_name: []const u8,
2167) InnerError!?*Decl {
2215) CompileError!?*Decl {
21682216 const namespace_decl = namespace.getDecl();
21692217 if (namespace_decl.analysis == .file_failure) {
21702218 try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);
......@@ -2192,7 +2240,7 @@ fn zirCall(
21922240 inst: Zir.Inst.Index,
21932241 modifier: std.builtin.CallOptions.Modifier,
21942242 ensure_result_used: bool,
2195) InnerError!*Inst {
2243) CompileError!Air.Inst.Ref {
21962244 const tracy = trace(@src());
21972245 defer tracy.end();
21982246
......@@ -2202,12 +2250,12 @@ fn zirCall(
22022250 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);
22032251 const args = sema.code.refSlice(extra.end, extra.data.args_len);
22042252
2205 const func = try sema.resolveInst(extra.data.callee);
2253 const func = sema.resolveInst(extra.data.callee);
22062254 // TODO handle function calls of generic functions
2207 const resolved_args = try sema.arena.alloc(*Inst, args.len);
2255 const resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);
22082256 for (args) |zir_arg, i| {
22092257 // the args are already casted to the result of a param type instruction.
2210 resolved_args[i] = try sema.resolveInst(zir_arg);
2258 resolved_args[i] = sema.resolveInst(zir_arg);
22112259 }
22122260
22132261 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
......@@ -2216,17 +2264,18 @@ fn zirCall(
22162264fn analyzeCall(
22172265 sema: *Sema,
22182266 block: *Scope.Block,
2219 func: *ir.Inst,
2267 func: Air.Inst.Ref,
22202268 func_src: LazySrcLoc,
22212269 call_src: LazySrcLoc,
22222270 modifier: std.builtin.CallOptions.Modifier,
22232271 ensure_result_used: bool,
2224 args: []const *ir.Inst,
2225) InnerError!*ir.Inst {
2226 if (func.ty.zigTypeTag() != .Fn)
2227 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});
2272 args: []const Air.Inst.Ref,
2273) CompileError!Air.Inst.Ref {
2274 const func_ty = sema.typeOf(func);
2275 if (func_ty.zigTypeTag() != .Fn)
2276 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func_ty});
22282277
2229 const cc = func.ty.fnCallingConvention();
2278 const cc = func_ty.fnCallingConvention();
22302279 if (cc == .Naked) {
22312280 // TODO add error note: declared here
22322281 return sema.mod.fail(
......@@ -2236,8 +2285,8 @@ fn analyzeCall(
22362285 .{},
22372286 );
22382287 }
2239 const fn_params_len = func.ty.fnParamLen();
2240 if (func.ty.fnIsVarArgs()) {
2288 const fn_params_len = func_ty.fnParamLen();
2289 if (func_ty.fnIsVarArgs()) {
22412290 assert(cc == .C);
22422291 if (args.len < fn_params_len) {
22432292 // TODO add error note: declared here
......@@ -2274,12 +2323,12 @@ fn analyzeCall(
22742323 }),
22752324 }
22762325
2277 const ret_type = func.ty.fnReturnType();
2326 const gpa = sema.gpa;
22782327
22792328 const is_comptime_call = block.is_comptime or modifier == .compile_time;
22802329 const is_inline_call = is_comptime_call or modifier == .always_inline or
2281 func.ty.fnCallingConvention() == .Inline;
2282 const result: *Inst = if (is_inline_call) res: {
2330 func_ty.fnCallingConvention() == .Inline;
2331 const result: Air.Inst.Ref = if (is_inline_call) res: {
22832332 const func_val = try sema.resolveConstValue(block, func_src, func);
22842333 const module_fn = switch (func_val.tag()) {
22852334 .function => func_val.castTag(.function).?.data,
......@@ -2294,15 +2343,11 @@ fn analyzeCall(
22942343 // set to in the `Scope.Block`.
22952344 // This block instruction will be used to capture the return value from the
22962345 // inlined function.
2297 const block_inst = try sema.arena.create(Inst.Block);
2298 block_inst.* = .{
2299 .base = .{
2300 .tag = Inst.Block.base_tag,
2301 .ty = ret_type,
2302 .src = call_src,
2303 },
2304 .body = undefined,
2305 };
2346 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
2347 try sema.air_instructions.append(gpa, .{
2348 .tag = .block,
2349 .data = undefined,
2350 });
23062351 // This one is shared among sub-blocks within the same callee, but not
23072352 // shared among the entire inline/comptime call stack.
23082353 var inlining: Scope.Block.Inlining = .{
......@@ -2321,7 +2366,7 @@ fn analyzeCall(
23212366 const parent_inst_map = sema.inst_map;
23222367 sema.inst_map = .{};
23232368 defer {
2324 sema.inst_map.deinit(sema.gpa);
2369 sema.inst_map.deinit(gpa);
23252370 sema.inst_map = parent_inst_map;
23262371 }
23272372
......@@ -2353,9 +2398,9 @@ fn analyzeCall(
23532398
23542399 const merges = &child_block.inlining.?.merges;
23552400
2356 defer child_block.instructions.deinit(sema.gpa);
2357 defer merges.results.deinit(sema.gpa);
2358 defer merges.br_list.deinit(sema.gpa);
2401 defer child_block.instructions.deinit(gpa);
2402 defer merges.results.deinit(gpa);
2403 defer merges.br_list.deinit(gpa);
23592404
23602405 try sema.emitBackwardBranch(&child_block, call_src);
23612406
......@@ -2368,7 +2413,19 @@ fn analyzeCall(
23682413 break :res result;
23692414 } else res: {
23702415 try sema.requireRuntimeBlock(block, call_src);
2371 break :res try block.addCall(call_src, ret_type, func, args);
2416 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
2417 args.len);
2418 const func_inst = try block.addInst(.{
2419 .tag = .call,
2420 .data = .{ .pl_op = .{
2421 .operand = func,
2422 .payload = sema.addExtraAssumeCapacity(Air.Call{
2423 .args_len = @intCast(u32, args.len),
2424 }),
2425 } },
2426 });
2427 sema.appendRefsAssumeCapacity(args);
2428 break :res func_inst;
23722429 };
23732430
23742431 if (ensure_result_used) {
......@@ -2377,19 +2434,18 @@ fn analyzeCall(
23772434 return result;
23782435}
23792436
2380fn zirIntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2437fn zirIntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23812438 _ = block;
23822439 const tracy = trace(@src());
23832440 defer tracy.end();
23842441
23852442 const int_type = sema.code.instructions.items(.data)[inst].int_type;
2386 const src = int_type.src();
23872443 const ty = try Module.makeIntType(sema.arena, int_type.signedness, int_type.bit_count);
23882444
2389 return sema.mod.constType(sema.arena, src, ty);
2445 return sema.addType(ty);
23902446}
23912447
2392fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2448fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23932449 const tracy = trace(@src());
23942450 defer tracy.end();
23952451
......@@ -2398,20 +2454,19 @@ fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
23982454 const child_type = try sema.resolveType(block, src, inst_data.operand);
23992455 const opt_type = try sema.mod.optionalType(sema.arena, child_type);
24002456
2401 return sema.mod.constType(sema.arena, src, opt_type);
2457 return sema.addType(opt_type);
24022458}
24032459
2404fn zirElemType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2460fn zirElemType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24052461 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
24062462 const src = inst_data.src();
24072463 const array_type = try sema.resolveType(block, src, inst_data.operand);
24082464 const elem_type = array_type.elemType();
2409 return sema.mod.constType(sema.arena, src, elem_type);
2465 return sema.addType(elem_type);
24102466}
24112467
2412fn zirVectorType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2468fn zirVectorType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24132469 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2414 const src = inst_data.src();
24152470 const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
24162471 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
24172472 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
......@@ -2421,10 +2476,10 @@ fn zirVectorType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
24212476 .len = len,
24222477 .elem_type = elem_type,
24232478 });
2424 return sema.mod.constType(sema.arena, src, vector_type);
2479 return sema.addType(vector_type);
24252480}
24262481
2427fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2482fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24282483 const tracy = trace(@src());
24292484 defer tracy.end();
24302485
......@@ -2434,10 +2489,10 @@ fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
24342489 const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);
24352490 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type);
24362491
2437 return sema.mod.constType(sema.arena, .unneeded, array_ty);
2492 return sema.addType(array_ty);
24382493}
24392494
2440fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2495fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24412496 const tracy = trace(@src());
24422497 defer tracy.end();
24432498
......@@ -2449,29 +2504,27 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
24492504 const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type);
24502505 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type);
24512506
2452 return sema.mod.constType(sema.arena, .unneeded, array_ty);
2507 return sema.addType(array_ty);
24532508}
24542509
2455fn zirAnyframeType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2510fn zirAnyframeType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24562511 const tracy = trace(@src());
24572512 defer tracy.end();
24582513
24592514 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2460 const src = inst_data.src();
24612515 const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node };
24622516 const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
24632517 const anyframe_type = try Type.Tag.anyframe_T.create(sema.arena, return_type);
24642518
2465 return sema.mod.constType(sema.arena, src, anyframe_type);
2519 return sema.addType(anyframe_type);
24662520}
24672521
2468fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2522fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24692523 const tracy = trace(@src());
24702524 defer tracy.end();
24712525
24722526 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
24732527 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
2474 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
24752528 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
24762529 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
24772530 const error_union = try sema.resolveType(block, lhs_src, extra.lhs);
......@@ -2483,59 +2536,55 @@ fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
24832536 });
24842537 }
24852538 const err_union_ty = try sema.mod.errorUnionType(sema.arena, error_union, payload);
2486 return sema.mod.constType(sema.arena, src, err_union_ty);
2539 return sema.addType(err_union_ty);
24872540}
24882541
2489fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2542fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24902543 _ = block;
24912544 const tracy = trace(@src());
24922545 defer tracy.end();
24932546
24942547 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
2495 const src = inst_data.src();
24962548
24972549 // Create an anonymous error set type with only this error value, and return the value.
24982550 const kv = try sema.mod.getErrorValue(inst_data.get(sema.code));
24992551 const result_type = try Type.Tag.error_set_single.create(sema.arena, kv.key);
2500 return sema.mod.constInst(sema.arena, src, .{
2501 .ty = result_type,
2502 .val = try Value.Tag.@"error".create(sema.arena, .{
2552 return sema.addConstant(
2553 result_type,
2554 try Value.Tag.@"error".create(sema.arena, .{
25032555 .name = kv.key,
25042556 }),
2505 });
2557 );
25062558}
25072559
2508fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2560fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
25092561 const tracy = trace(@src());
25102562 defer tracy.end();
25112563
25122564 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
25132565 const src = inst_data.src();
25142566 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2515 const op = try sema.resolveInst(inst_data.operand);
2567 const op = sema.resolveInst(inst_data.operand);
25162568 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
25172569 const result_ty = Type.initTag(.u16);
25182570
25192571 if (try sema.resolvePossiblyUndefinedValue(block, src, op_coerced)) |val| {
25202572 if (val.isUndef()) {
2521 return sema.mod.constUndef(sema.arena, src, result_ty);
2573 return sema.addConstUndef(result_ty);
25222574 }
25232575 const payload = try sema.arena.create(Value.Payload.U64);
25242576 payload.* = .{
25252577 .base = .{ .tag = .int_u64 },
25262578 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
25272579 };
2528 return sema.mod.constInst(sema.arena, src, .{
2529 .ty = result_ty,
2530 .val = Value.initPayload(&payload.base),
2531 });
2580 return sema.addConstant(result_ty, Value.initPayload(&payload.base));
25322581 }
25332582
25342583 try sema.requireRuntimeBlock(block, src);
2535 return block.addUnOp(src, result_ty, .bitcast, op_coerced);
2584 return block.addTyOp(.bitcast, result_ty, op_coerced);
25362585}
25372586
2538fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2587fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
25392588 const tracy = trace(@src());
25402589 defer tracy.end();
25412590
......@@ -2543,7 +2592,7 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
25432592 const src = inst_data.src();
25442593 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
25452594
2546 const op = try sema.resolveInst(inst_data.operand);
2595 const op = sema.resolveInst(inst_data.operand);
25472596
25482597 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
25492598 const int = value.toUnsignedInt();
......@@ -2554,10 +2603,7 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
25542603 .base = .{ .tag = .@"error" },
25552604 .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },
25562605 };
2557 return sema.mod.constInst(sema.arena, src, .{
2558 .ty = Type.initTag(.anyerror),
2559 .val = Value.initPayload(&payload.base),
2560 });
2606 return sema.addConstant(Type.initTag(.anyerror), Value.initPayload(&payload.base));
25612607 }
25622608 try sema.requireRuntimeBlock(block, src);
25632609 if (block.wantSafety()) {
......@@ -2565,10 +2611,10 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
25652611 // const is_gt_max = @panic("TODO get max errors in compilation");
25662612 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
25672613 }
2568 return block.addUnOp(src, Type.initTag(.anyerror), .bitcast, op);
2614 return block.addTyOp(.bitcast, Type.initTag(.anyerror), op);
25692615}
25702616
2571fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2617fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
25722618 const tracy = trace(@src());
25732619 defer tracy.end();
25742620
......@@ -2577,9 +2623,9 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
25772623 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
25782624 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
25792625 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
2580 const lhs = try sema.resolveInst(extra.lhs);
2581 const rhs = try sema.resolveInst(extra.rhs);
2582 if (rhs.ty.zigTypeTag() == .Bool and lhs.ty.zigTypeTag() == .Bool) {
2626 const lhs = sema.resolveInst(extra.lhs);
2627 const rhs = sema.resolveInst(extra.rhs);
2628 if (sema.typeOf(lhs).zigTypeTag() == .Bool and sema.typeOf(rhs).zigTypeTag() == .Bool) {
25832629 const msg = msg: {
25842630 const msg = try sema.mod.errMsg(&block.base, lhs_src, "expected error set type, found 'bool'", .{});
25852631 errdefer msg.destroy(sema.gpa);
......@@ -2588,19 +2634,16 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
25882634 };
25892635 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
25902636 }
2591 const rhs_ty = try sema.resolveAirAsType(block, rhs_src, rhs);
2592 const lhs_ty = try sema.resolveAirAsType(block, lhs_src, lhs);
2593 if (rhs_ty.zigTypeTag() != .ErrorSet)
2594 return sema.mod.fail(&block.base, rhs_src, "expected error set type, found {}", .{rhs_ty});
2637 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
2638 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
25952639 if (lhs_ty.zigTypeTag() != .ErrorSet)
25962640 return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{lhs_ty});
2641 if (rhs_ty.zigTypeTag() != .ErrorSet)
2642 return sema.mod.fail(&block.base, rhs_src, "expected error set type, found {}", .{rhs_ty});
25972643
25982644 // Anything merged with anyerror is anyerror.
25992645 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
2600 return sema.mod.constInst(sema.arena, src, .{
2601 .ty = Type.initTag(.type),
2602 .val = Value.initTag(.anyerror_type),
2603 });
2646 return Air.Inst.Ref.anyerror_type;
26042647 }
26052648 // When we support inferred error sets, we'll want to use a data structure that can
26062649 // represent a merged set of errors without forcing them to be resolved here. Until then
......@@ -2652,38 +2695,35 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
26522695 .names_len = @intCast(u32, new_names.len),
26532696 };
26542697 const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);
2655 return sema.mod.constInst(sema.arena, src, .{
2656 .ty = Type.initTag(.type),
2657 .val = try Value.Tag.ty.create(sema.arena, error_set_ty),
2658 });
2698 return sema.addConstant(Type.initTag(.type), try Value.Tag.ty.create(sema.arena, error_set_ty));
26592699}
26602700
2661fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2701fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
26622702 _ = block;
26632703 const tracy = trace(@src());
26642704 defer tracy.end();
26652705
26662706 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
2667 const src = inst_data.src();
26682707 const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code));
2669 return sema.mod.constInst(sema.arena, src, .{
2670 .ty = Type.initTag(.enum_literal),
2671 .val = try Value.Tag.enum_literal.create(sema.arena, duped_name),
2672 });
2708 return sema.addConstant(
2709 Type.initTag(.enum_literal),
2710 try Value.Tag.enum_literal.create(sema.arena, duped_name),
2711 );
26732712}
26742713
2675fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2714fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
26762715 const mod = sema.mod;
26772716 const arena = sema.arena;
26782717 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
26792718 const src = inst_data.src();
26802719 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2681 const operand = try sema.resolveInst(inst_data.operand);
2720 const operand = sema.resolveInst(inst_data.operand);
2721 const operand_ty = sema.typeOf(operand);
26822722
2683 const enum_tag: *Inst = switch (operand.ty.zigTypeTag()) {
2723 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) {
26842724 .Enum => operand,
26852725 .Union => {
2686 //if (!operand.ty.unionHasTag()) {
2726 //if (!operand_ty.unionHasTag()) {
26872727 // return mod.fail(
26882728 // &block.base,
26892729 // operand_src,
......@@ -2695,91 +2735,73 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
26952735 },
26962736 else => {
26972737 return mod.fail(&block.base, operand_src, "expected enum or tagged union, found {}", .{
2698 operand.ty,
2738 operand_ty,
26992739 });
27002740 },
27012741 };
2742 const enum_tag_ty = sema.typeOf(enum_tag);
27022743
27032744 var int_tag_type_buffer: Type.Payload.Bits = undefined;
2704 const int_tag_ty = try enum_tag.ty.intTagType(&int_tag_type_buffer).copy(arena);
2745 const int_tag_ty = try enum_tag_ty.intTagType(&int_tag_type_buffer).copy(arena);
27052746
2706 if (try sema.typeHasOnePossibleValue(block, src, enum_tag.ty)) |opv| {
2707 return mod.constInst(arena, src, .{
2708 .ty = int_tag_ty,
2709 .val = opv,
2710 });
2747 if (try sema.typeHasOnePossibleValue(block, src, enum_tag_ty)) |opv| {
2748 return sema.addConstant(int_tag_ty, opv);
27112749 }
27122750
2713 if (enum_tag.value()) |enum_tag_val| {
2751 if (try sema.resolvePossiblyUndefinedValue(block, operand_src, enum_tag)) |enum_tag_val| {
27142752 if (enum_tag_val.castTag(.enum_field_index)) |enum_field_payload| {
27152753 const field_index = enum_field_payload.data;
2716 switch (enum_tag.ty.tag()) {
2754 switch (enum_tag_ty.tag()) {
27172755 .enum_full => {
2718 const enum_full = enum_tag.ty.castTag(.enum_full).?.data;
2756 const enum_full = enum_tag_ty.castTag(.enum_full).?.data;
27192757 if (enum_full.values.count() != 0) {
27202758 const val = enum_full.values.keys()[field_index];
2721 return mod.constInst(arena, src, .{
2722 .ty = int_tag_ty,
2723 .val = val,
2724 });
2759 return sema.addConstant(int_tag_ty, val);
27252760 } else {
27262761 // Field index and integer values are the same.
27272762 const val = try Value.Tag.int_u64.create(arena, field_index);
2728 return mod.constInst(arena, src, .{
2729 .ty = int_tag_ty,
2730 .val = val,
2731 });
2763 return sema.addConstant(int_tag_ty, val);
27322764 }
27332765 },
27342766 .enum_simple => {
27352767 // Field index and integer values are the same.
27362768 const val = try Value.Tag.int_u64.create(arena, field_index);
2737 return mod.constInst(arena, src, .{
2738 .ty = int_tag_ty,
2739 .val = val,
2740 });
2769 return sema.addConstant(int_tag_ty, val);
27412770 },
27422771 else => unreachable,
27432772 }
27442773 } else {
27452774 // Assume it is already an integer and return it directly.
2746 return mod.constInst(arena, src, .{
2747 .ty = int_tag_ty,
2748 .val = enum_tag_val,
2749 });
2775 return sema.addConstant(int_tag_ty, enum_tag_val);
27502776 }
27512777 }
27522778
27532779 try sema.requireRuntimeBlock(block, src);
2754 return block.addUnOp(src, int_tag_ty, .bitcast, enum_tag);
2780 return block.addTyOp(.bitcast, int_tag_ty, enum_tag);
27552781}
27562782
2757fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2783fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
27582784 const mod = sema.mod;
27592785 const target = mod.getTarget();
2760 const arena = sema.arena;
27612786 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
27622787 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
27632788 const src = inst_data.src();
27642789 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
27652790 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
27662791 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
2767 const operand = try sema.resolveInst(extra.rhs);
2792 const operand = sema.resolveInst(extra.rhs);
27682793
27692794 if (dest_ty.zigTypeTag() != .Enum) {
27702795 return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty});
27712796 }
27722797
2773 if (dest_ty.isNonexhaustiveEnum()) {
2774 if (operand.value()) |int_val| {
2775 return mod.constInst(arena, src, .{
2776 .ty = dest_ty,
2777 .val = int_val,
2778 });
2798 if (try sema.resolvePossiblyUndefinedValue(block, operand_src, operand)) |int_val| {
2799 if (dest_ty.isNonexhaustiveEnum()) {
2800 return sema.addConstant(dest_ty, int_val);
2801 }
2802 if (int_val.isUndef()) {
2803 return sema.failWithUseOfUndef(block, operand_src);
27792804 }
2780 }
2781
2782 if (try sema.resolveDefinedValue(block, operand_src, operand)) |int_val| {
27832805 if (!dest_ty.enumHasInt(int_val, target)) {
27842806 const msg = msg: {
27852807 const msg = try mod.errMsg(
......@@ -2799,14 +2821,11 @@ fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
27992821 };
28002822 return mod.failWithOwnedErrorMsg(&block.base, msg);
28012823 }
2802 return mod.constInst(arena, src, .{
2803 .ty = dest_ty,
2804 .val = int_val,
2805 });
2824 return sema.addConstant(dest_ty, int_val);
28062825 }
28072826
28082827 try sema.requireRuntimeBlock(block, src);
2809 return block.addUnOp(src, dest_ty, .bitcast, operand);
2828 return block.addTyOp(.bitcast, dest_ty, operand);
28102829}
28112830
28122831/// Pointer in, pointer out.
......@@ -2815,41 +2834,39 @@ fn zirOptionalPayloadPtr(
28152834 block: *Scope.Block,
28162835 inst: Zir.Inst.Index,
28172836 safety_check: bool,
2818) InnerError!*Inst {
2837) CompileError!Air.Inst.Ref {
28192838 const tracy = trace(@src());
28202839 defer tracy.end();
28212840
28222841 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2823 const optional_ptr = try sema.resolveInst(inst_data.operand);
2824 assert(optional_ptr.ty.zigTypeTag() == .Pointer);
2842 const optional_ptr = sema.resolveInst(inst_data.operand);
2843 const optional_ptr_ty = sema.typeOf(optional_ptr);
2844 assert(optional_ptr_ty.zigTypeTag() == .Pointer);
28252845 const src = inst_data.src();
28262846
2827 const opt_type = optional_ptr.ty.elemType();
2847 const opt_type = optional_ptr_ty.elemType();
28282848 if (opt_type.zigTypeTag() != .Optional) {
28292849 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
28302850 }
28312851
28322852 const child_type = try opt_type.optionalChildAlloc(sema.arena);
2833 const child_pointer = try sema.mod.simplePtrType(sema.arena, child_type, !optional_ptr.ty.isConstPtr(), .One);
2853 const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One);
28342854
2835 if (optional_ptr.value()) |pointer_val| {
2855 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
28362856 const val = try pointer_val.pointerDeref(sema.arena);
28372857 if (val.isNull()) {
28382858 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
28392859 }
28402860 // The same Value represents the pointer to the optional and the payload.
2841 return sema.mod.constInst(sema.arena, src, .{
2842 .ty = child_pointer,
2843 .val = pointer_val,
2844 });
2861 return sema.addConstant(child_pointer, pointer_val);
28452862 }
28462863
28472864 try sema.requireRuntimeBlock(block, src);
28482865 if (safety_check and block.wantSafety()) {
2849 const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr);
2866 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
28502867 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
28512868 }
2852 return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr);
2869 return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
28532870}
28542871
28552872/// Value in, value out.
......@@ -2858,36 +2875,34 @@ fn zirOptionalPayload(
28582875 block: *Scope.Block,
28592876 inst: Zir.Inst.Index,
28602877 safety_check: bool,
2861) InnerError!*Inst {
2878) CompileError!Air.Inst.Ref {
28622879 const tracy = trace(@src());
28632880 defer tracy.end();
28642881
28652882 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
28662883 const src = inst_data.src();
2867 const operand = try sema.resolveInst(inst_data.operand);
2868 const opt_type = operand.ty;
2884 const operand = sema.resolveInst(inst_data.operand);
2885 const operand_ty = sema.typeOf(operand);
2886 const opt_type = operand_ty;
28692887 if (opt_type.zigTypeTag() != .Optional) {
28702888 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
28712889 }
28722890
28732891 const child_type = try opt_type.optionalChildAlloc(sema.arena);
28742892
2875 if (operand.value()) |val| {
2893 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
28762894 if (val.isNull()) {
28772895 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
28782896 }
2879 return sema.mod.constInst(sema.arena, src, .{
2880 .ty = child_type,
2881 .val = val,
2882 });
2897 return sema.addConstant(child_type, val);
28832898 }
28842899
28852900 try sema.requireRuntimeBlock(block, src);
28862901 if (safety_check and block.wantSafety()) {
2887 const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null, operand);
2902 const is_non_null = try block.addUnOp(.is_non_null, operand);
28882903 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
28892904 }
2890 return block.addUnOp(src, child_type, .optional_payload, operand);
2905 return block.addTyOp(.optional_payload, child_type, operand);
28912906}
28922907
28932908/// Value in, value out
......@@ -2896,32 +2911,35 @@ fn zirErrUnionPayload(
28962911 block: *Scope.Block,
28972912 inst: Zir.Inst.Index,
28982913 safety_check: bool,
2899) InnerError!*Inst {
2914) CompileError!Air.Inst.Ref {
29002915 const tracy = trace(@src());
29012916 defer tracy.end();
29022917
29032918 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
29042919 const src = inst_data.src();
2905 const operand = try sema.resolveInst(inst_data.operand);
2906 if (operand.ty.zigTypeTag() != .ErrorUnion)
2907 return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty});
2920 const operand = sema.resolveInst(inst_data.operand);
2921 const operand_src = src;
2922 const operand_ty = sema.typeOf(operand);
2923 if (operand_ty.zigTypeTag() != .ErrorUnion)
2924 return sema.mod.fail(&block.base, operand_src, "expected error union type, found '{}'", .{operand_ty});
29082925
2909 if (operand.value()) |val| {
2926 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
29102927 if (val.getError()) |name| {
29112928 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
29122929 }
29132930 const data = val.castTag(.error_union).?.data;
2914 return sema.mod.constInst(sema.arena, src, .{
2915 .ty = operand.ty.castTag(.error_union).?.data.payload,
2916 .val = data,
2917 });
2931 return sema.addConstant(
2932 operand_ty.castTag(.error_union).?.data.payload,
2933 data,
2934 );
29182935 }
29192936 try sema.requireRuntimeBlock(block, src);
29202937 if (safety_check and block.wantSafety()) {
2921 const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
2938 const is_non_err = try block.addUnOp(.is_err, operand);
29222939 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
29232940 }
2924 return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand);
2941 const result_ty = operand_ty.castTag(.error_union).?.data.payload;
2942 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
29252943}
29262944
29272945/// Pointer in, pointer out.
......@@ -2930,109 +2948,107 @@ fn zirErrUnionPayloadPtr(
29302948 block: *Scope.Block,
29312949 inst: Zir.Inst.Index,
29322950 safety_check: bool,
2933) InnerError!*Inst {
2951) CompileError!Air.Inst.Ref {
29342952 const tracy = trace(@src());
29352953 defer tracy.end();
29362954
29372955 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
29382956 const src = inst_data.src();
2939 const operand = try sema.resolveInst(inst_data.operand);
2940 assert(operand.ty.zigTypeTag() == .Pointer);
2957 const operand = sema.resolveInst(inst_data.operand);
2958 const operand_ty = sema.typeOf(operand);
2959 assert(operand_ty.zigTypeTag() == .Pointer);
29412960
2942 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
2943 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
2961 if (operand_ty.elemType().zigTypeTag() != .ErrorUnion)
2962 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});
29442963
2945 const operand_pointer_ty = try sema.mod.simplePtrType(sema.arena, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One);
2964 const operand_pointer_ty = try Module.simplePtrType(sema.arena, operand_ty.elemType().castTag(.error_union).?.data.payload, !operand_ty.isConstPtr(), .One);
29462965
2947 if (operand.value()) |pointer_val| {
2966 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
29482967 const val = try pointer_val.pointerDeref(sema.arena);
29492968 if (val.getError()) |name| {
29502969 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
29512970 }
29522971 const data = val.castTag(.error_union).?.data;
29532972 // The same Value represents the pointer to the error union and the payload.
2954 return sema.mod.constInst(sema.arena, src, .{
2955 .ty = operand_pointer_ty,
2956 .val = try Value.Tag.ref_val.create(
2973 return sema.addConstant(
2974 operand_pointer_ty,
2975 try Value.Tag.ref_val.create(
29572976 sema.arena,
29582977 data,
29592978 ),
2960 });
2979 );
29612980 }
29622981
29632982 try sema.requireRuntimeBlock(block, src);
29642983 if (safety_check and block.wantSafety()) {
2965 const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
2984 const is_non_err = try block.addUnOp(.is_err, operand);
29662985 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
29672986 }
2968 return block.addUnOp(src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand);
2987 return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
29692988}
29702989
29712990/// Value in, value out
2972fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
2991fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
29732992 const tracy = trace(@src());
29742993 defer tracy.end();
29752994
29762995 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
29772996 const src = inst_data.src();
2978 const operand = try sema.resolveInst(inst_data.operand);
2979 if (operand.ty.zigTypeTag() != .ErrorUnion)
2980 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
2997 const operand = sema.resolveInst(inst_data.operand);
2998 const operand_ty = sema.typeOf(operand);
2999 if (operand_ty.zigTypeTag() != .ErrorUnion)
3000 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand_ty});
29813001
2982 const result_ty = operand.ty.castTag(.error_union).?.data.error_set;
3002 const result_ty = operand_ty.castTag(.error_union).?.data.error_set;
29833003
2984 if (operand.value()) |val| {
3004 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
29853005 assert(val.getError() != null);
29863006 const data = val.castTag(.error_union).?.data;
2987 return sema.mod.constInst(sema.arena, src, .{
2988 .ty = result_ty,
2989 .val = data,
2990 });
3007 return sema.addConstant(result_ty, data);
29913008 }
29923009
29933010 try sema.requireRuntimeBlock(block, src);
2994 return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand);
3011 return block.addTyOp(.unwrap_errunion_err, result_ty, operand);
29953012}
29963013
29973014/// Pointer in, value out
2998fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3015fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
29993016 const tracy = trace(@src());
30003017 defer tracy.end();
30013018
30023019 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
30033020 const src = inst_data.src();
3004 const operand = try sema.resolveInst(inst_data.operand);
3005 assert(operand.ty.zigTypeTag() == .Pointer);
3021 const operand = sema.resolveInst(inst_data.operand);
3022 const operand_ty = sema.typeOf(operand);
3023 assert(operand_ty.zigTypeTag() == .Pointer);
30063024
3007 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
3008 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
3025 if (operand_ty.elemType().zigTypeTag() != .ErrorUnion)
3026 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});
30093027
3010 const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set;
3028 const result_ty = operand_ty.elemType().castTag(.error_union).?.data.error_set;
30113029
3012 if (operand.value()) |pointer_val| {
3030 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
30133031 const val = try pointer_val.pointerDeref(sema.arena);
30143032 assert(val.getError() != null);
30153033 const data = val.castTag(.error_union).?.data;
3016 return sema.mod.constInst(sema.arena, src, .{
3017 .ty = result_ty,
3018 .val = data,
3019 });
3034 return sema.addConstant(result_ty, data);
30203035 }
30213036
30223037 try sema.requireRuntimeBlock(block, src);
3023 return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand);
3038 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
30243039}
30253040
3026fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
3041fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
30273042 const tracy = trace(@src());
30283043 defer tracy.end();
30293044
30303045 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
30313046 const src = inst_data.src();
3032 const operand = try sema.resolveInst(inst_data.operand);
3033 if (operand.ty.zigTypeTag() != .ErrorUnion)
3034 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
3035 if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
3047 const operand = sema.resolveInst(inst_data.operand);
3048 const operand_ty = sema.typeOf(operand);
3049 if (operand_ty.zigTypeTag() != .ErrorUnion)
3050 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand_ty});
3051 if (operand_ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
30363052 return sema.mod.fail(&block.base, src, "expression value is ignored", .{});
30373053 }
30383054}
......@@ -3042,7 +3058,7 @@ fn zirFunc(
30423058 block: *Scope.Block,
30433059 inst: Zir.Inst.Index,
30443060 inferred_error_set: bool,
3045) InnerError!*Inst {
3061) CompileError!Air.Inst.Ref {
30463062 const tracy = trace(@src());
30473063 defer tracy.end();
30483064
......@@ -3093,7 +3109,7 @@ fn funcCommon(
30933109 is_extern: bool,
30943110 src_locs: Zir.Inst.Func.SrcLocs,
30953111 opt_lib_name: ?[]const u8,
3096) InnerError!*Inst {
3112) CompileError!Air.Inst.Ref {
30973113 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
30983114 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
30993115 const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
......@@ -3199,14 +3215,14 @@ fn funcCommon(
31993215 }
32003216
32013217 if (is_extern) {
3202 return sema.mod.constInst(sema.arena, src, .{
3203 .ty = fn_ty,
3204 .val = try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl),
3205 });
3218 return sema.addConstant(
3219 fn_ty,
3220 try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl),
3221 );
32063222 }
32073223
32083224 if (body_inst == 0) {
3209 return mod.constType(sema.arena, src, fn_ty);
3225 return sema.addType(fn_ty);
32103226 }
32113227
32123228 const is_inline = fn_ty.fnCallingConvention() == .Inline;
......@@ -3217,7 +3233,6 @@ fn funcCommon(
32173233 .state = anal_state,
32183234 .zir_body_inst = body_inst,
32193235 .owner_decl = sema.owner_decl,
3220 .body = undefined,
32213236 .lbrace_line = src_locs.lbrace_line,
32223237 .rbrace_line = src_locs.rbrace_line,
32233238 .lbrace_column = @truncate(u16, src_locs.columns),
......@@ -3227,14 +3242,10 @@ fn funcCommon(
32273242 .base = .{ .tag = .function },
32283243 .data = new_func,
32293244 };
3230 const result = try sema.mod.constInst(sema.arena, src, .{
3231 .ty = fn_ty,
3232 .val = Value.initPayload(&fn_payload.base),
3233 });
3234 return result;
3245 return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base));
32353246}
32363247
3237fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3248fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
32383249 const tracy = trace(@src());
32393250 defer tracy.end();
32403251
......@@ -3242,7 +3253,7 @@ fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Ins
32423253 return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);
32433254}
32443255
3245fn zirAsNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3256fn zirAsNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
32463257 const tracy = trace(@src());
32473258 defer tracy.end();
32483259
......@@ -3258,30 +3269,30 @@ fn analyzeAs(
32583269 src: LazySrcLoc,
32593270 zir_dest_type: Zir.Inst.Ref,
32603271 zir_operand: Zir.Inst.Ref,
3261) InnerError!*Inst {
3272) CompileError!Air.Inst.Ref {
32623273 const dest_type = try sema.resolveType(block, src, zir_dest_type);
3263 const operand = try sema.resolveInst(zir_operand);
3274 const operand = sema.resolveInst(zir_operand);
32643275 return sema.coerce(block, dest_type, operand, src);
32653276}
32663277
3267fn zirPtrToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3278fn zirPtrToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
32683279 const tracy = trace(@src());
32693280 defer tracy.end();
32703281
32713282 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3272 const ptr = try sema.resolveInst(inst_data.operand);
3273 if (ptr.ty.zigTypeTag() != .Pointer) {
3283 const ptr = sema.resolveInst(inst_data.operand);
3284 const ptr_ty = sema.typeOf(ptr);
3285 if (ptr_ty.zigTypeTag() != .Pointer) {
32743286 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
3275 return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty});
3287 return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty});
32763288 }
32773289 // TODO handle known-pointer-address
32783290 const src = inst_data.src();
32793291 try sema.requireRuntimeBlock(block, src);
3280 const ty = Type.initTag(.usize);
3281 return block.addUnOp(src, ty, .ptrtoint, ptr);
3292 return block.addUnOp(.ptrtoint, ptr);
32823293}
32833294
3284fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3295fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
32853296 const tracy = trace(@src());
32863297 defer tracy.end();
32873298
......@@ -3290,16 +3301,17 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
32903301 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
32913302 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
32923303 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
3293 const object = try sema.resolveInst(extra.lhs);
3294 const object_ptr = if (object.ty.zigTypeTag() == .Pointer)
3304 const object = sema.resolveInst(extra.lhs);
3305 const object_ptr = if (sema.typeOf(object).zigTypeTag() == .Pointer)
32953306 object
32963307 else
32973308 try sema.analyzeRef(block, src, object);
32983309 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
3299 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
3310 const result_ptr_src = src;
3311 return sema.analyzeLoad(block, src, result_ptr, result_ptr_src);
33003312}
33013313
3302fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3314fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
33033315 const tracy = trace(@src());
33043316 defer tracy.end();
33053317
......@@ -3308,11 +3320,11 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
33083320 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
33093321 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
33103322 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
3311 const object_ptr = try sema.resolveInst(extra.lhs);
3323 const object_ptr = sema.resolveInst(extra.lhs);
33123324 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
33133325}
33143326
3315fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3327fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
33163328 const tracy = trace(@src());
33173329 defer tracy.end();
33183330
......@@ -3320,14 +3332,14 @@ fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
33203332 const src = inst_data.src();
33213333 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
33223334 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
3323 const object = try sema.resolveInst(extra.lhs);
3335 const object = sema.resolveInst(extra.lhs);
33243336 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
33253337 const object_ptr = try sema.analyzeRef(block, src, object);
33263338 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
33273339 return sema.analyzeLoad(block, src, result_ptr, src);
33283340}
33293341
3330fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3342fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
33313343 const tracy = trace(@src());
33323344 defer tracy.end();
33333345
......@@ -3335,12 +3347,12 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
33353347 const src = inst_data.src();
33363348 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
33373349 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
3338 const object_ptr = try sema.resolveInst(extra.lhs);
3350 const object_ptr = sema.resolveInst(extra.lhs);
33393351 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
33403352 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
33413353}
33423354
3343fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3355fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
33443356 const tracy = trace(@src());
33453357 defer tracy.end();
33463358
......@@ -3351,7 +3363,7 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
33513363 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
33523364
33533365 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
3354 const operand = try sema.resolveInst(extra.rhs);
3366 const operand = sema.resolveInst(extra.rhs);
33553367
33563368 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
33573369 .ComptimeInt => true,
......@@ -3364,17 +3376,18 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
33643376 ),
33653377 };
33663378
3367 switch (operand.ty.zigTypeTag()) {
3379 const operand_ty = sema.typeOf(operand);
3380 switch (operand_ty.zigTypeTag()) {
33683381 .ComptimeInt, .Int => {},
33693382 else => return sema.mod.fail(
33703383 &block.base,
33713384 operand_src,
33723385 "expected integer type, found '{}'",
3373 .{operand.ty},
3386 .{operand_ty},
33743387 ),
33753388 }
33763389
3377 if (operand.value() != null) {
3390 if (try sema.isComptimeKnown(block, operand_src, operand)) {
33783391 return sema.coerce(block, dest_type, operand, operand_src);
33793392 } else if (dest_is_comptime_int) {
33803393 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});
......@@ -3383,20 +3396,21 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
33833396 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{});
33843397}
33853398
3386fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3399fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
33873400 const tracy = trace(@src());
33883401 defer tracy.end();
33893402
33903403 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
33913404 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
3405 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
33923406 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
33933407
33943408 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
3395 const operand = try sema.resolveInst(extra.rhs);
3396 return sema.bitcast(block, dest_type, operand);
3409 const operand = sema.resolveInst(extra.rhs);
3410 return sema.bitcast(block, dest_type, operand, operand_src);
33973411}
33983412
3399fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3413fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
34003414 const tracy = trace(@src());
34013415 defer tracy.end();
34023416
......@@ -3407,7 +3421,7 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
34073421 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
34083422
34093423 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
3410 const operand = try sema.resolveInst(extra.rhs);
3424 const operand = sema.resolveInst(extra.rhs);
34113425
34123426 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
34133427 .ComptimeFloat => true,
......@@ -3420,17 +3434,18 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
34203434 ),
34213435 };
34223436
3423 switch (operand.ty.zigTypeTag()) {
3437 const operand_ty = sema.typeOf(operand);
3438 switch (operand_ty.zigTypeTag()) {
34243439 .ComptimeFloat, .Float, .ComptimeInt => {},
34253440 else => return sema.mod.fail(
34263441 &block.base,
34273442 operand_src,
34283443 "expected float type, found '{}'",
3429 .{operand.ty},
3444 .{operand_ty},
34303445 ),
34313446 }
34323447
3433 if (operand.value() != null) {
3448 if (try sema.isComptimeKnown(block, operand_src, operand)) {
34343449 return sema.coerce(block, dest_type, operand, operand_src);
34353450 } else if (dest_is_comptime_float) {
34363451 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});
......@@ -3439,22 +3454,23 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
34393454 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});
34403455}
34413456
3442fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3457fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
34433458 const tracy = trace(@src());
34443459 defer tracy.end();
34453460
34463461 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3447 const array = try sema.resolveInst(bin_inst.lhs);
3448 const array_ptr = if (array.ty.zigTypeTag() == .Pointer)
3462 const array = sema.resolveInst(bin_inst.lhs);
3463 const array_ty = sema.typeOf(array);
3464 const array_ptr = if (array_ty.zigTypeTag() == .Pointer)
34493465 array
34503466 else
34513467 try sema.analyzeRef(block, sema.src, array);
3452 const elem_index = try sema.resolveInst(bin_inst.rhs);
3468 const elem_index = sema.resolveInst(bin_inst.rhs);
34533469 const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
34543470 return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);
34553471}
34563472
3457fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3473fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
34583474 const tracy = trace(@src());
34593475 defer tracy.end();
34603476
......@@ -3462,27 +3478,28 @@ fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
34623478 const src = inst_data.src();
34633479 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
34643480 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
3465 const array = try sema.resolveInst(extra.lhs);
3466 const array_ptr = if (array.ty.zigTypeTag() == .Pointer)
3481 const array = sema.resolveInst(extra.lhs);
3482 const array_ty = sema.typeOf(array);
3483 const array_ptr = if (array_ty.zigTypeTag() == .Pointer)
34673484 array
34683485 else
34693486 try sema.analyzeRef(block, src, array);
3470 const elem_index = try sema.resolveInst(extra.rhs);
3487 const elem_index = sema.resolveInst(extra.rhs);
34713488 const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
34723489 return sema.analyzeLoad(block, src, result_ptr, src);
34733490}
34743491
3475fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3492fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
34763493 const tracy = trace(@src());
34773494 defer tracy.end();
34783495
34793496 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3480 const array_ptr = try sema.resolveInst(bin_inst.lhs);
3481 const elem_index = try sema.resolveInst(bin_inst.rhs);
3497 const array_ptr = sema.resolveInst(bin_inst.lhs);
3498 const elem_index = sema.resolveInst(bin_inst.rhs);
34823499 return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
34833500}
34843501
3485fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3502fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
34863503 const tracy = trace(@src());
34873504 defer tracy.end();
34883505
......@@ -3490,39 +3507,39 @@ fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
34903507 const src = inst_data.src();
34913508 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
34923509 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
3493 const array_ptr = try sema.resolveInst(extra.lhs);
3494 const elem_index = try sema.resolveInst(extra.rhs);
3510 const array_ptr = sema.resolveInst(extra.lhs);
3511 const elem_index = sema.resolveInst(extra.rhs);
34953512 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
34963513}
34973514
3498fn zirSliceStart(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3515fn zirSliceStart(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
34993516 const tracy = trace(@src());
35003517 defer tracy.end();
35013518
35023519 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
35033520 const src = inst_data.src();
35043521 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
3505 const array_ptr = try sema.resolveInst(extra.lhs);
3506 const start = try sema.resolveInst(extra.start);
3522 const array_ptr = sema.resolveInst(extra.lhs);
3523 const start = sema.resolveInst(extra.start);
35073524
3508 return sema.analyzeSlice(block, src, array_ptr, start, null, null, .unneeded);
3525 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded);
35093526}
35103527
3511fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3528fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
35123529 const tracy = trace(@src());
35133530 defer tracy.end();
35143531
35153532 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
35163533 const src = inst_data.src();
35173534 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
3518 const array_ptr = try sema.resolveInst(extra.lhs);
3519 const start = try sema.resolveInst(extra.start);
3520 const end = try sema.resolveInst(extra.end);
3535 const array_ptr = sema.resolveInst(extra.lhs);
3536 const start = sema.resolveInst(extra.start);
3537 const end = sema.resolveInst(extra.end);
35213538
3522 return sema.analyzeSlice(block, src, array_ptr, start, end, null, .unneeded);
3539 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded);
35233540}
35243541
3525fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
3542fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
35263543 const tracy = trace(@src());
35273544 defer tracy.end();
35283545
......@@ -3530,10 +3547,10 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
35303547 const src = inst_data.src();
35313548 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
35323549 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
3533 const array_ptr = try sema.resolveInst(extra.lhs);
3534 const start = try sema.resolveInst(extra.start);
3535 const end = try sema.resolveInst(extra.end);
3536 const sentinel = try sema.resolveInst(extra.sentinel);
3550 const array_ptr = sema.resolveInst(extra.lhs);
3551 const start = sema.resolveInst(extra.start);
3552 const end = sema.resolveInst(extra.end);
3553 const sentinel = sema.resolveInst(extra.sentinel);
35373554
35383555 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
35393556}
......@@ -3544,7 +3561,7 @@ fn zirSwitchCapture(
35443561 inst: Zir.Inst.Index,
35453562 is_multi: bool,
35463563 is_ref: bool,
3547) InnerError!*Inst {
3564) CompileError!Air.Inst.Ref {
35483565 const tracy = trace(@src());
35493566 defer tracy.end();
35503567
......@@ -3563,7 +3580,7 @@ fn zirSwitchCaptureElse(
35633580 block: *Scope.Block,
35643581 inst: Zir.Inst.Index,
35653582 is_ref: bool,
3566) InnerError!*Inst {
3583) CompileError!Air.Inst.Ref {
35673584 const tracy = trace(@src());
35683585 defer tracy.end();
35693586
......@@ -3582,7 +3599,7 @@ fn zirSwitchBlock(
35823599 inst: Zir.Inst.Index,
35833600 is_ref: bool,
35843601 special_prong: Zir.SpecialProng,
3585) InnerError!*Inst {
3602) CompileError!Air.Inst.Ref {
35863603 const tracy = trace(@src());
35873604 defer tracy.end();
35883605
......@@ -3591,7 +3608,7 @@ fn zirSwitchBlock(
35913608 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
35923609 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
35933610
3594 const operand_ptr = try sema.resolveInst(extra.data.operand);
3611 const operand_ptr = sema.resolveInst(extra.data.operand);
35953612 const operand = if (is_ref)
35963613 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
35973614 else
......@@ -3615,7 +3632,7 @@ fn zirSwitchBlockMulti(
36153632 inst: Zir.Inst.Index,
36163633 is_ref: bool,
36173634 special_prong: Zir.SpecialProng,
3618) InnerError!*Inst {
3635) CompileError!Air.Inst.Ref {
36193636 const tracy = trace(@src());
36203637 defer tracy.end();
36213638
......@@ -3624,7 +3641,7 @@ fn zirSwitchBlockMulti(
36243641 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
36253642 const extra = sema.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
36263643
3627 const operand_ptr = try sema.resolveInst(extra.data.operand);
3644 const operand_ptr = sema.resolveInst(extra.data.operand);
36283645 const operand = if (is_ref)
36293646 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
36303647 else
......@@ -3645,14 +3662,14 @@ fn zirSwitchBlockMulti(
36453662fn analyzeSwitch(
36463663 sema: *Sema,
36473664 block: *Scope.Block,
3648 operand: *Inst,
3665 operand: Air.Inst.Ref,
36493666 extra_end: usize,
36503667 special_prong: Zir.SpecialProng,
36513668 scalar_cases_len: usize,
36523669 multi_cases_len: usize,
36533670 switch_inst: Zir.Inst.Index,
36543671 src_node_offset: i32,
3655) InnerError!*Inst {
3672) CompileError!Air.Inst.Ref {
36563673 const gpa = sema.gpa;
36573674 const mod = sema.mod;
36583675
......@@ -3671,9 +3688,10 @@ fn analyzeSwitch(
36713688 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
36723689 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
36733690 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
3691 const operand_ty = sema.typeOf(operand);
36743692
36753693 // Validate usage of '_' prongs.
3676 if (special_prong == .under and !operand.ty.isNonexhaustiveEnum()) {
3694 if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) {
36773695 const msg = msg: {
36783696 const msg = try mod.errMsg(
36793697 &block.base,
......@@ -3695,9 +3713,9 @@ fn analyzeSwitch(
36953713 }
36963714
36973715 // Validate for duplicate items, missing else prong, and invalid range.
3698 switch (operand.ty.zigTypeTag()) {
3716 switch (operand_ty.zigTypeTag()) {
36993717 .Enum => {
3700 var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand.ty.enumFieldCount());
3718 var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
37013719 defer gpa.free(seen_fields);
37023720
37033721 mem.set(?Module.SwitchProngSrc, seen_fields, null);
......@@ -3743,7 +3761,7 @@ fn analyzeSwitch(
37433761 );
37443762 }
37453763
3746 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
3764 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
37473765 }
37483766 }
37493767 const all_tags_handled = for (seen_fields) |seen_src| {
......@@ -3764,7 +3782,7 @@ fn analyzeSwitch(
37643782 for (seen_fields) |seen_src, i| {
37653783 if (seen_src != null) continue;
37663784
3767 const field_name = operand.ty.enumFieldName(i);
3785 const field_name = operand_ty.enumFieldName(i);
37683786
37693787 // TODO have this point to the tag decl instead of here
37703788 try mod.errNote(
......@@ -3776,10 +3794,10 @@ fn analyzeSwitch(
37763794 );
37773795 }
37783796 try mod.errNoteNonLazy(
3779 operand.ty.declSrcLoc(),
3797 operand_ty.declSrcLoc(),
37803798 msg,
37813799 "enum '{}' declared here",
3782 .{operand.ty},
3800 .{operand_ty},
37833801 );
37843802 break :msg msg;
37853803 };
......@@ -3874,12 +3892,12 @@ fn analyzeSwitch(
38743892 }
38753893
38763894 check_range: {
3877 if (operand.ty.zigTypeTag() == .Int) {
3895 if (operand_ty.zigTypeTag() == .Int) {
38783896 var arena = std.heap.ArenaAllocator.init(gpa);
38793897 defer arena.deinit();
38803898
3881 const min_int = try operand.ty.minInt(&arena, mod.getTarget());
3882 const max_int = try operand.ty.maxInt(&arena, mod.getTarget());
3899 const min_int = try operand_ty.minInt(&arena, mod.getTarget());
3900 const max_int = try operand_ty.maxInt(&arena, mod.getTarget());
38833901 if (try range_set.spans(min_int, max_int)) {
38843902 if (special_prong == .@"else") {
38853903 return mod.fail(
......@@ -3949,7 +3967,7 @@ fn analyzeSwitch(
39493967 );
39503968 }
39513969
3952 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
3970 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
39533971 }
39543972 }
39553973 switch (special_prong) {
......@@ -3981,7 +3999,7 @@ fn analyzeSwitch(
39813999 &block.base,
39824000 src,
39834001 "else prong required when switching on type '{}'",
3984 .{operand.ty},
4002 .{operand_ty},
39854003 );
39864004 }
39874005
......@@ -4029,7 +4047,7 @@ fn analyzeSwitch(
40294047 );
40304048 }
40314049
4032 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
4050 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
40334051 }
40344052 }
40354053 },
......@@ -4049,20 +4067,15 @@ fn analyzeSwitch(
40494067 .ComptimeFloat,
40504068 .Float,
40514069 => return mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
4052 operand.ty,
4070 operand_ty,
40534071 }),
40544072 }
40554073
4056 const block_inst = try sema.arena.create(Inst.Block);
4057 block_inst.* = .{
4058 .base = .{
4059 .tag = Inst.Block.base_tag,
4060 .ty = undefined, // Set after analysis.
4061 .src = src,
4062 },
4063 .body = undefined,
4064 };
4065
4074 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
4075 try sema.air_instructions.append(gpa, .{
4076 .tag = .block,
4077 .data = undefined,
4078 });
40664079 var label: Scope.Block.Label = .{
40674080 .zir_block = switch_inst,
40684081 .merges = .{
......@@ -4098,8 +4111,8 @@ fn analyzeSwitch(
40984111 const body = sema.code.extra[extra_index..][0..body_len];
40994112 extra_index += body_len;
41004113
4114 const item = sema.resolveInst(item_ref);
41014115 // Validation above ensured these will succeed.
4102 const item = sema.resolveInst(item_ref) catch unreachable;
41034116 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
41044117 if (operand_val.eql(item_val)) {
41054118 return sema.resolveBlockBody(block, src, &child_block, body, merges);
......@@ -4120,9 +4133,9 @@ fn analyzeSwitch(
41204133 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
41214134
41224135 for (items) |item_ref| {
4136 const item = sema.resolveInst(item_ref);
41234137 // Validation above ensured these will succeed.
4124 const item = sema.resolveInst(item_ref) catch unreachable;
4125 const item_val = sema.resolveConstValue(&child_block, item.src, item) catch unreachable;
4138 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
41264139 if (operand_val.eql(item_val)) {
41274140 return sema.resolveBlockBody(block, src, &child_block, body, merges);
41284141 }
......@@ -4157,13 +4170,15 @@ fn analyzeSwitch(
41574170
41584171 try sema.requireRuntimeBlock(block, src);
41594172
4160 // TODO when reworking AIR memory layout make multi cases get generated as cases,
4161 // not as part of the "else" block.
4162 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
4173 var cases_extra: std.ArrayListUnmanaged(u32) = .{};
4174 defer cases_extra.deinit(gpa);
4175
4176 try cases_extra.ensureTotalCapacity(gpa, (scalar_cases_len + multi_cases_len) *
4177 @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2);
41634178
41644179 var case_block = child_block.makeSubBlock();
41654180 case_block.runtime_loop = null;
4166 case_block.runtime_cond = operand.src;
4181 case_block.runtime_cond = operand_src;
41674182 case_block.runtime_index += 1;
41684183 defer case_block.instructions.deinit(gpa);
41694184
......@@ -4179,21 +4194,26 @@ fn analyzeSwitch(
41794194 extra_index += body_len;
41804195
41814196 case_block.instructions.shrinkRetainingCapacity(0);
4182 // We validate these above; these two calls are guaranteed to succeed.
4183 const item = sema.resolveInst(item_ref) catch unreachable;
4184 const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
4197 const item = sema.resolveInst(item_ref);
4198 // `item` is already guaranteed to be constant known.
41854199
41864200 _ = try sema.analyzeBody(&case_block, body);
41874201
4188 cases[scalar_i] = .{
4189 .item = item_val,
4190 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },
4191 };
4202 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
4203 cases_extra.appendAssumeCapacity(1); // items_len
4204 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
4205 cases_extra.appendAssumeCapacity(@enumToInt(item));
4206 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
41924207 }
41934208
4194 var first_else_body: Body = undefined;
4195 var prev_condbr: ?*Inst.CondBr = null;
4209 var is_first = true;
4210 var prev_cond_br: Air.Inst.Index = undefined;
4211 var first_else_body: []const Air.Inst.Index = &.{};
4212 defer gpa.free(first_else_body);
4213 var prev_then_body: []const Air.Inst.Index = &.{};
4214 defer gpa.free(prev_then_body);
41964215
4216 var cases_len = scalar_cases_len;
41974217 var multi_i: usize = 0;
41984218 while (multi_i < multi_cases_len) : (multi_i += 1) {
41994219 const items_len = sema.code.extra[extra_index];
......@@ -4207,116 +4227,146 @@ fn analyzeSwitch(
42074227
42084228 case_block.instructions.shrinkRetainingCapacity(0);
42094229
4210 var any_ok: ?*Inst = null;
4211 const bool_ty = comptime Type.initTag(.bool);
4230 var any_ok: Air.Inst.Ref = .none;
42124231
4213 for (items) |item_ref| {
4214 const item = try sema.resolveInst(item_ref);
4215 _ = try sema.resolveConstValue(&child_block, item.src, item);
4232 // If there are any ranges, we have to put all the items into the
4233 // else prong. Otherwise, we can take advantage of multiple items
4234 // mapping to the same body.
4235 if (ranges_len == 0) {
4236 cases_len += 1;
42164237
4217 const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
4218 if (any_ok) |some| {
4219 any_ok = try case_block.addBinOp(item.src, bool_ty, .bool_or, some, cmp_ok);
4220 } else {
4221 any_ok = cmp_ok;
4238 const body = sema.code.extra[extra_index..][0..body_len];
4239 extra_index += body_len;
4240 _ = try sema.analyzeBody(&case_block, body);
4241
4242 try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
4243 case_block.instructions.items.len);
4244
4245 cases_extra.appendAssumeCapacity(@intCast(u32, items.len));
4246 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
4247
4248 for (items) |item_ref| {
4249 const item = sema.resolveInst(item_ref);
4250 cases_extra.appendAssumeCapacity(@enumToInt(item));
42224251 }
4223 }
42244252
4225 var range_i: usize = 0;
4226 while (range_i < ranges_len) : (range_i += 1) {
4227 const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4228 extra_index += 1;
4229 const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4230 extra_index += 1;
4253 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
4254 } else {
4255 for (items) |item_ref| {
4256 const item = sema.resolveInst(item_ref);
4257 const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
4258 if (any_ok != .none) {
4259 any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);
4260 } else {
4261 any_ok = cmp_ok;
4262 }
4263 }
42314264
4232 const item_first = try sema.resolveInst(first_ref);
4233 const item_last = try sema.resolveInst(last_ref);
4265 var range_i: usize = 0;
4266 while (range_i < ranges_len) : (range_i += 1) {
4267 const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4268 extra_index += 1;
4269 const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4270 extra_index += 1;
42344271
4235 _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
4236 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
4272 const item_first = sema.resolveInst(first_ref);
4273 const item_last = sema.resolveInst(last_ref);
42374274
4238 const range_src = item_first.src;
4275 // operand >= first and operand <= last
4276 const range_first_ok = try case_block.addBinOp(
4277 .cmp_gte,
4278 operand,
4279 item_first,
4280 );
4281 const range_last_ok = try case_block.addBinOp(
4282 .cmp_lte,
4283 operand,
4284 item_last,
4285 );
4286 const range_ok = try case_block.addBinOp(
4287 .bool_and,
4288 range_first_ok,
4289 range_last_ok,
4290 );
4291 if (any_ok != .none) {
4292 any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok);
4293 } else {
4294 any_ok = range_ok;
4295 }
4296 }
42394297
4240 // operand >= first and operand <= last
4241 const range_first_ok = try case_block.addBinOp(
4242 item_first.src,
4243 bool_ty,
4244 .cmp_gte,
4245 operand,
4246 item_first,
4247 );
4248 const range_last_ok = try case_block.addBinOp(
4249 item_last.src,
4250 bool_ty,
4251 .cmp_lte,
4252 operand,
4253 item_last,
4254 );
4255 const range_ok = try case_block.addBinOp(
4256 range_src,
4257 bool_ty,
4258 .bool_and,
4259 range_first_ok,
4260 range_last_ok,
4261 );
4262 if (any_ok) |some| {
4263 any_ok = try case_block.addBinOp(range_src, bool_ty, .bool_or, some, range_ok);
4298 const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{
4299 .pl_op = .{
4300 .operand = any_ok,
4301 .payload = undefined,
4302 },
4303 } });
4304 var cond_body = case_block.instructions.toOwnedSlice(gpa);
4305 defer gpa.free(cond_body);
4306
4307 case_block.instructions.shrinkRetainingCapacity(0);
4308 const body = sema.code.extra[extra_index..][0..body_len];
4309 extra_index += body_len;
4310 _ = try sema.analyzeBody(&case_block, body);
4311
4312 if (is_first) {
4313 is_first = false;
4314 first_else_body = cond_body;
4315 cond_body = &.{};
42644316 } else {
4265 any_ok = range_ok;
4317 try sema.air_extra.ensureUnusedCapacity(
4318 gpa,
4319 @typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len,
4320 );
4321
4322 sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload =
4323 sema.addExtraAssumeCapacity(Air.CondBr{
4324 .then_body_len = @intCast(u32, prev_then_body.len),
4325 .else_body_len = @intCast(u32, cond_body.len),
4326 });
4327 sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
4328 sema.air_extra.appendSliceAssumeCapacity(cond_body);
42664329 }
4330 prev_then_body = case_block.instructions.toOwnedSlice(gpa);
4331 prev_cond_br = new_cond_br;
42674332 }
4333 }
42684334
4269 const new_condbr = try sema.arena.create(Inst.CondBr);
4270 new_condbr.* = .{
4271 .base = .{
4272 .tag = .condbr,
4273 .ty = Type.initTag(.noreturn),
4274 .src = src,
4275 },
4276 .condition = any_ok.?,
4277 .then_body = undefined,
4278 .else_body = undefined,
4279 };
4280 try case_block.instructions.append(gpa, &new_condbr.base);
4281
4282 const cond_body: Body = .{
4283 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
4284 };
4285
4335 var final_else_body: []const Air.Inst.Index = &.{};
4336 if (special.body.len != 0) {
42864337 case_block.instructions.shrinkRetainingCapacity(0);
4287 const body = sema.code.extra[extra_index..][0..body_len];
4288 extra_index += body_len;
4289 _ = try sema.analyzeBody(&case_block, body);
4290 new_condbr.then_body = .{
4291 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
4292 };
4293 if (prev_condbr) |condbr| {
4294 condbr.else_body = cond_body;
4338 _ = try sema.analyzeBody(&case_block, special.body);
4339
4340 if (is_first) {
4341 final_else_body = case_block.instructions.items;
42954342 } else {
4296 first_else_body = cond_body;
4343 try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len +
4344 @typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len);
4345
4346 sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload =
4347 sema.addExtraAssumeCapacity(Air.CondBr{
4348 .then_body_len = @intCast(u32, prev_then_body.len),
4349 .else_body_len = @intCast(u32, case_block.instructions.items.len),
4350 });
4351 sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
4352 sema.air_extra.appendSliceAssumeCapacity(case_block.instructions.items);
4353 final_else_body = first_else_body;
42974354 }
4298 prev_condbr = new_condbr;
42994355 }
43004356
4301 const final_else_body: Body = blk: {
4302 if (special.body.len != 0) {
4303 case_block.instructions.shrinkRetainingCapacity(0);
4304 _ = try sema.analyzeBody(&case_block, special.body);
4305 const else_body: Body = .{
4306 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
4307 };
4308 if (prev_condbr) |condbr| {
4309 condbr.else_body = else_body;
4310 break :blk first_else_body;
4311 } else {
4312 break :blk else_body;
4313 }
4314 } else {
4315 break :blk .{ .instructions = &.{} };
4316 }
4317 };
4357 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
4358 cases_extra.items.len + final_else_body.len);
4359
4360 _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{
4361 .operand = operand,
4362 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
4363 .cases_len = @intCast(u32, cases_len),
4364 .else_body_len = @intCast(u32, final_else_body.len),
4365 }),
4366 } } });
4367 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
4368 sema.air_extra.appendSliceAssumeCapacity(final_else_body);
43184369
4319 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
43204370 return sema.analyzeBlockBody(block, src, &child_block, merges);
43214371}
43224372
......@@ -4327,20 +4377,24 @@ fn resolveSwitchItemVal(
43274377 switch_node_offset: i32,
43284378 switch_prong_src: Module.SwitchProngSrc,
43294379 range_expand: Module.SwitchProngSrc.RangeExpand,
4330) InnerError!TypedValue {
4331 const item = try sema.resolveInst(item_ref);
4332 // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc
4333 // because we only have the switch AST node. Only if we know for sure we need to report
4334 // a compile error do we resolve the full source locations.
4335 if (item.value()) |val| {
4336 if (val.isUndef()) {
4380) CompileError!TypedValue {
4381 const item = sema.resolveInst(item_ref);
4382 const item_ty = sema.typeOf(item);
4383 // Constructing a LazySrcLoc is costly because we only have the switch AST node.
4384 // Only if we know for sure we need to report a compile error do we resolve the
4385 // full source locations.
4386 if (sema.resolveConstValue(block, .unneeded, item)) |val| {
4387 return TypedValue{ .ty = item_ty, .val = val };
4388 } else |err| switch (err) {
4389 error.NeededSourceLocation => {
43374390 const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
4338 return sema.failWithUseOfUndef(block, src);
4339 }
4340 return TypedValue{ .ty = item.ty, .val = val };
4391 return TypedValue{
4392 .ty = item_ty,
4393 .val = try sema.resolveConstValue(block, src, item),
4394 };
4395 },
4396 else => |e| return e,
43414397 }
4342 const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
4343 return sema.failWithNeededComptime(block, src);
43444398}
43454399
43464400fn validateSwitchRange(
......@@ -4351,7 +4405,7 @@ fn validateSwitchRange(
43514405 last_ref: Zir.Inst.Ref,
43524406 src_node_offset: i32,
43534407 switch_prong_src: Module.SwitchProngSrc,
4354) InnerError!void {
4408) CompileError!void {
43554409 const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
43564410 const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
43574411 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);
......@@ -4365,7 +4419,7 @@ fn validateSwitchItem(
43654419 item_ref: Zir.Inst.Ref,
43664420 src_node_offset: i32,
43674421 switch_prong_src: Module.SwitchProngSrc,
4368) InnerError!void {
4422) CompileError!void {
43694423 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
43704424 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);
43714425 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
......@@ -4378,7 +4432,7 @@ fn validateSwitchItemEnum(
43784432 item_ref: Zir.Inst.Ref,
43794433 src_node_offset: i32,
43804434 switch_prong_src: Module.SwitchProngSrc,
4381) InnerError!void {
4435) CompileError!void {
43824436 const mod = sema.mod;
43834437 const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
43844438 const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse {
......@@ -4412,7 +4466,7 @@ fn validateSwitchDupe(
44124466 maybe_prev_src: ?Module.SwitchProngSrc,
44134467 switch_prong_src: Module.SwitchProngSrc,
44144468 src_node_offset: i32,
4415) InnerError!void {
4469) CompileError!void {
44164470 const prev_prong_src = maybe_prev_src orelse return;
44174471 const mod = sema.mod;
44184472 const gpa = sema.gpa;
......@@ -4446,7 +4500,7 @@ fn validateSwitchItemBool(
44464500 item_ref: Zir.Inst.Ref,
44474501 src_node_offset: i32,
44484502 switch_prong_src: Module.SwitchProngSrc,
4449) InnerError!void {
4503) CompileError!void {
44504504 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
44514505 if (item_val.toBool()) {
44524506 true_count.* += 1;
......@@ -4468,7 +4522,7 @@ fn validateSwitchItemSparse(
44684522 item_ref: Zir.Inst.Ref,
44694523 src_node_offset: i32,
44704524 switch_prong_src: Module.SwitchProngSrc,
4471) InnerError!void {
4525) CompileError!void {
44724526 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
44734527 const kv = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
44744528 return sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
......@@ -4480,7 +4534,7 @@ fn validateSwitchNoRange(
44804534 ranges_len: u32,
44814535 operand_ty: Type,
44824536 src_node_offset: i32,
4483) InnerError!void {
4537) CompileError!void {
44844538 if (ranges_len == 0)
44854539 return;
44864540
......@@ -4507,7 +4561,7 @@ fn validateSwitchNoRange(
45074561 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
45084562}
45094563
4510fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4564fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45114565 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
45124566 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
45134567 _ = extra;
......@@ -4516,16 +4570,14 @@ fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
45164570 return sema.mod.fail(&block.base, src, "TODO implement zirHasField", .{});
45174571}
45184572
4519fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4573fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45204574 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
45214575 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4522 const src = inst_data.src();
45234576 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
45244577 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
45254578 const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
45264579 const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs);
45274580 const mod = sema.mod;
4528 const arena = sema.arena;
45294581
45304582 const namespace = container_type.getNamespace() orelse return mod.fail(
45314583 &block.base,
......@@ -4535,13 +4587,13 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
45354587 );
45364588 if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
45374589 if (decl.is_pub or decl.namespace.file_scope == block.base.namespace().file_scope) {
4538 return mod.constBool(arena, src, true);
4590 return Air.Inst.Ref.bool_true;
45394591 }
45404592 }
4541 return mod.constBool(arena, src, false);
4593 return Air.Inst.Ref.bool_false;
45424594}
45434595
4544fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4596fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45454597 const tracy = trace(@src());
45464598 defer tracy.end();
45474599
......@@ -4563,16 +4615,16 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
45634615 try mod.semaFile(result.file);
45644616 const file_root_decl = result.file.root_decl.?;
45654617 try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl);
4566 return mod.constType(sema.arena, src, file_root_decl.ty);
4618 return sema.addType(file_root_decl.ty);
45674619}
45684620
4569fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4621fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45704622 _ = block;
45714623 _ = inst;
45724624 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
45734625}
45744626
4575fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4627fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45764628 const tracy = trace(@src());
45774629 defer tracy.end();
45784630
......@@ -4581,7 +4633,7 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
45814633 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{});
45824634}
45834635
4584fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4636fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45854637 const tracy = trace(@src());
45864638 defer tracy.end();
45874639
......@@ -4593,8 +4645,8 @@ fn zirBitwise(
45934645 sema: *Sema,
45944646 block: *Scope.Block,
45954647 inst: Zir.Inst.Index,
4596 ir_tag: ir.Inst.Tag,
4597) InnerError!*Inst {
4648 air_tag: Air.Inst.Tag,
4649) CompileError!Air.Inst.Ref {
45984650 const tracy = trace(@src());
45994651 defer tracy.end();
46004652
......@@ -4603,10 +4655,12 @@ fn zirBitwise(
46034655 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
46044656 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
46054657 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4606 const lhs = try sema.resolveInst(extra.lhs);
4607 const rhs = try sema.resolveInst(extra.rhs);
4658 const lhs = sema.resolveInst(extra.lhs);
4659 const rhs = sema.resolveInst(extra.rhs);
4660 const lhs_ty = sema.typeOf(lhs);
4661 const rhs_ty = sema.typeOf(rhs);
46084662
4609 const instructions = &[_]*Inst{ lhs, rhs };
4663 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
46104664 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
46114665 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
46124666 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
......@@ -4618,41 +4672,41 @@ fn zirBitwise(
46184672
46194673 const scalar_tag = scalar_type.zigTypeTag();
46204674
4621 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
4622 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
4675 if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) {
4676 if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
46234677 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
4624 lhs.ty.arrayLen(),
4625 rhs.ty.arrayLen(),
4678 lhs_ty.arrayLen(),
4679 rhs_ty.arrayLen(),
46264680 });
46274681 }
46284682 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBitwise", .{});
4629 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
4683 } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) {
46304684 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
4631 lhs.ty,
4632 rhs.ty,
4685 lhs_ty,
4686 rhs_ty,
46334687 });
46344688 }
46354689
46364690 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
46374691
46384692 if (!is_int) {
4639 return sema.mod.fail(&block.base, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
4693 return sema.mod.fail(&block.base, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) });
46404694 }
46414695
4642 if (casted_lhs.value()) |lhs_val| {
4643 if (casted_rhs.value()) |rhs_val| {
4696 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| {
4697 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
46444698 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4645 return sema.mod.constUndef(sema.arena, src, resolved_type);
4699 return sema.addConstUndef(resolved_type);
46464700 }
46474701 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
46484702 }
46494703 }
46504704
46514705 try sema.requireRuntimeBlock(block, src);
4652 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
4706 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
46534707}
46544708
4655fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4709fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
46564710 const tracy = trace(@src());
46574711 defer tracy.end();
46584712
......@@ -4660,7 +4714,7 @@ fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
46604714 return sema.mod.fail(&block.base, sema.src, "TODO implement zirBitNot", .{});
46614715}
46624716
4663fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4717fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
46644718 const tracy = trace(@src());
46654719 defer tracy.end();
46664720
......@@ -4668,7 +4722,7 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
46684722 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});
46694723}
46704724
4671fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4725fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
46724726 const tracy = trace(@src());
46734727 defer tracy.end();
46744728
......@@ -4681,7 +4735,7 @@ fn zirNegate(
46814735 block: *Scope.Block,
46824736 inst: Zir.Inst.Index,
46834737 tag_override: Zir.Inst.Tag,
4684) InnerError!*Inst {
4738) CompileError!Air.Inst.Ref {
46854739 const tracy = trace(@src());
46864740 defer tracy.end();
46874741
......@@ -4689,13 +4743,13 @@ fn zirNegate(
46894743 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
46904744 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
46914745 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4692 const lhs = try sema.resolveInst(.zero);
4693 const rhs = try sema.resolveInst(inst_data.operand);
4746 const lhs = sema.resolveInst(.zero);
4747 const rhs = sema.resolveInst(inst_data.operand);
46944748
46954749 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
46964750}
46974751
4698fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4752fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
46994753 const tracy = trace(@src());
47004754 defer tracy.end();
47014755
......@@ -4705,8 +4759,8 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
47054759 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
47064760 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
47074761 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4708 const lhs = try sema.resolveInst(extra.lhs);
4709 const rhs = try sema.resolveInst(extra.rhs);
4762 const lhs = sema.resolveInst(extra.lhs);
4763 const rhs = sema.resolveInst(extra.rhs);
47104764
47114765 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);
47124766}
......@@ -4715,7 +4769,7 @@ fn zirOverflowArithmetic(
47154769 sema: *Sema,
47164770 block: *Scope.Block,
47174771 extended: Zir.Inst.Extended.InstData,
4718) InnerError!*Inst {
4772) CompileError!Air.Inst.Ref {
47194773 const tracy = trace(@src());
47204774 defer tracy.end();
47214775
......@@ -4729,13 +4783,30 @@ fn analyzeArithmetic(
47294783 sema: *Sema,
47304784 block: *Scope.Block,
47314785 zir_tag: Zir.Inst.Tag,
4732 lhs: *Inst,
4733 rhs: *Inst,
4786 lhs: Air.Inst.Ref,
4787 rhs: Air.Inst.Ref,
47344788 src: LazySrcLoc,
47354789 lhs_src: LazySrcLoc,
47364790 rhs_src: LazySrcLoc,
4737) InnerError!*Inst {
4738 const instructions = &[_]*Inst{ lhs, rhs };
4791) CompileError!Air.Inst.Ref {
4792 const lhs_ty = sema.typeOf(lhs);
4793 const rhs_ty = sema.typeOf(rhs);
4794 if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) {
4795 if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
4796 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
4797 lhs_ty.arrayLen(),
4798 rhs_ty.arrayLen(),
4799 });
4800 }
4801 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{});
4802 } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) {
4803 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
4804 lhs_ty,
4805 rhs_ty,
4806 });
4807 }
4808
4809 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
47394810 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
47404811 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
47414812 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
......@@ -4747,42 +4818,24 @@ fn analyzeArithmetic(
47474818
47484819 const scalar_tag = scalar_type.zigTypeTag();
47494820
4750 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
4751 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
4752 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
4753 lhs.ty.arrayLen(),
4754 rhs.ty.arrayLen(),
4755 });
4756 }
4757 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{});
4758 } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
4759 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
4760 lhs.ty,
4761 rhs.ty,
4762 });
4763 }
4764
47654821 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
47664822 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
47674823
47684824 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
4769 return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
4825 return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) });
47704826 }
47714827
4772 if (casted_lhs.value()) |lhs_val| {
4773 if (casted_rhs.value()) |rhs_val| {
4828 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| {
4829 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
47744830 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4775 return sema.mod.constUndef(sema.arena, src, resolved_type);
4831 return sema.addConstUndef(resolved_type);
47764832 }
47774833 // incase rhs is 0, simply return lhs without doing any calculations
47784834 // TODO Once division is implemented we should throw an error when dividing by 0.
47794835 if (rhs_val.compareWithZero(.eq)) {
47804836 switch (zir_tag) {
47814837 .add, .addwrap, .sub, .subwrap => {
4782 return sema.mod.constInst(sema.arena, src, .{
4783 .ty = scalar_type,
4784 .val = lhs_val,
4785 });
4838 return sema.addConstant(scalar_type, lhs_val);
47864839 },
47874840 else => {},
47884841 }
......@@ -4822,15 +4875,12 @@ fn analyzeArithmetic(
48224875
48234876 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
48244877
4825 return sema.mod.constInst(sema.arena, src, .{
4826 .ty = scalar_type,
4827 .val = value,
4828 });
4878 return sema.addConstant(scalar_type, value);
48294879 }
48304880 }
48314881
48324882 try sema.requireRuntimeBlock(block, src);
4833 const ir_tag: Inst.Tag = switch (zir_tag) {
4883 const air_tag: Air.Inst.Tag = switch (zir_tag) {
48344884 .add => .add,
48354885 .addwrap => .addwrap,
48364886 .sub => .sub,
......@@ -4841,17 +4891,17 @@ fn analyzeArithmetic(
48414891 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}),
48424892 };
48434893
4844 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
4894 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
48454895}
48464896
4847fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4897fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
48484898 const tracy = trace(@src());
48494899 defer tracy.end();
48504900
48514901 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
48524902 const src = inst_data.src();
48534903 const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
4854 const ptr = try sema.resolveInst(inst_data.operand);
4904 const ptr = sema.resolveInst(inst_data.operand);
48554905 return sema.analyzeLoad(block, src, ptr, ptr_src);
48564906}
48574907
......@@ -4859,19 +4909,17 @@ fn zirAsm(
48594909 sema: *Sema,
48604910 block: *Scope.Block,
48614911 extended: Zir.Inst.Extended.InstData,
4862) InnerError!*Inst {
4912 inst: Zir.Inst.Index,
4913) CompileError!Air.Inst.Ref {
48634914 const tracy = trace(@src());
48644915 defer tracy.end();
48654916
48664917 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
48674918 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
4868 const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = extra.data.src_node };
48694919 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };
4870 const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);
48714920 const outputs_len = @truncate(u5, extended.small);
48724921 const inputs_len = @truncate(u5, extended.small >> 5);
48734922 const clobbers_len = @truncate(u5, extended.small >> 10);
4874 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
48754923
48764924 if (outputs_len > 1) {
48774925 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{});
......@@ -4899,7 +4947,7 @@ fn zirAsm(
48994947 };
49004948 };
49014949
4902 const args = try sema.arena.alloc(*Inst, inputs_len);
4950 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
49034951 const inputs = try sema.arena.alloc([]const u8, inputs_len);
49044952
49054953 for (args) |*arg, arg_i| {
......@@ -4909,7 +4957,7 @@ fn zirAsm(
49094957 const name = sema.code.nullTerminatedString(input.data.name);
49104958 _ = name; // TODO: use the name
49114959
4912 arg.* = try sema.resolveInst(input.data.operand);
4960 arg.* = sema.resolveInst(input.data.operand);
49134961 inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);
49144962 }
49154963
......@@ -4920,22 +4968,19 @@ fn zirAsm(
49204968 }
49214969
49224970 try sema.requireRuntimeBlock(block, src);
4923 const asm_air = try sema.arena.create(Inst.Assembly);
4924 asm_air.* = .{
4925 .base = .{
4926 .tag = .assembly,
4927 .ty = if (output) |o| o.ty else Type.initTag(.void),
4928 .src = src,
4929 },
4930 .asm_source = asm_source,
4931 .is_volatile = is_volatile,
4932 .output_constraint = if (output) |o| o.constraint else null,
4933 .inputs = inputs,
4934 .clobbers = clobbers,
4935 .args = args,
4936 };
4937 try block.instructions.append(sema.gpa, &asm_air.base);
4938 return &asm_air.base;
4971 const gpa = sema.gpa;
4972 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len);
4973 const asm_air = try block.addInst(.{
4974 .tag = .assembly,
4975 .data = .{ .ty_pl = .{
4976 .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
4977 .payload = sema.addExtraAssumeCapacity(Air.Asm{
4978 .zir_index = inst,
4979 }),
4980 } },
4981 });
4982 sema.appendRefsAssumeCapacity(args);
4983 return asm_air;
49394984}
49404985
49414986fn zirCmp(
......@@ -4943,7 +4988,7 @@ fn zirCmp(
49434988 block: *Scope.Block,
49444989 inst: Zir.Inst.Index,
49454990 op: std.math.CompareOperator,
4946) InnerError!*Inst {
4991) CompileError!Air.Inst.Ref {
49474992 const tracy = trace(@src());
49484993 defer tracy.end();
49494994
......@@ -4954,18 +4999,24 @@ fn zirCmp(
49544999 const src: LazySrcLoc = inst_data.src();
49555000 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
49565001 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4957 const lhs = try sema.resolveInst(extra.lhs);
4958 const rhs = try sema.resolveInst(extra.rhs);
5002 const lhs = sema.resolveInst(extra.lhs);
5003 const rhs = sema.resolveInst(extra.rhs);
49595004
49605005 const is_equality_cmp = switch (op) {
49615006 .eq, .neq => true,
49625007 else => false,
49635008 };
4964 const lhs_ty_tag = lhs.ty.zigTypeTag();
4965 const rhs_ty_tag = rhs.ty.zigTypeTag();
5009 const lhs_ty = sema.typeOf(lhs);
5010 const rhs_ty = sema.typeOf(rhs);
5011 const lhs_ty_tag = lhs_ty.zigTypeTag();
5012 const rhs_ty_tag = rhs_ty.zigTypeTag();
49665013 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
49675014 // null == null, null != null
4968 return mod.constBool(sema.arena, src, op == .eq);
5015 if (op == .eq) {
5016 return Air.Inst.Ref.bool_true;
5017 } else {
5018 return Air.Inst.Ref.bool_false;
5019 }
49695020 } else if (is_equality_cmp and
49705021 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
49715022 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
......@@ -4974,11 +5025,11 @@ fn zirCmp(
49745025 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
49755026 return sema.analyzeIsNull(block, src, opt_operand, op == .neq);
49765027 } else if (is_equality_cmp and
4977 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
5028 ((lhs_ty_tag == .Null and rhs_ty.isCPtr()) or (rhs_ty_tag == .Null and lhs_ty.isCPtr())))
49785029 {
49795030 return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});
49805031 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
4981 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
5032 const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
49825033 return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
49835034 } else if (is_equality_cmp and
49845035 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
......@@ -4989,27 +5040,45 @@ fn zirCmp(
49895040 if (!is_equality_cmp) {
49905041 return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});
49915042 }
4992 if (rhs.value()) |rval| {
4993 if (lhs.value()) |lval| {
4994 // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster
4995 return mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));
5043 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lval| {
5044 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rval| {
5045 if (lval.isUndef() or rval.isUndef()) {
5046 return sema.addConstUndef(Type.initTag(.bool));
5047 }
5048 // TODO optimisation opportunity: evaluate if mem.eql is faster with the names,
5049 // or calling to Module.getErrorValue to get the values and then compare them is
5050 // faster.
5051 const lhs_name = lval.castTag(.@"error").?.data.name;
5052 const rhs_name = rval.castTag(.@"error").?.data.name;
5053 if (mem.eql(u8, lhs_name, rhs_name) == (op == .eq)) {
5054 return Air.Inst.Ref.bool_true;
5055 } else {
5056 return Air.Inst.Ref.bool_false;
5057 }
49965058 }
49975059 }
49985060 try sema.requireRuntimeBlock(block, src);
4999 return block.addBinOp(src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs);
5000 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
5061 const tag: Air.Inst.Tag = if (op == .eq) .cmp_eq else .cmp_neq;
5062 return block.addBinOp(tag, lhs, rhs);
5063 } else if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) {
50015064 // This operation allows any combination of integer and float types, regardless of the
50025065 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
50035066 // numeric types.
5004 return sema.cmpNumeric(block, src, lhs, rhs, op);
5067 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
50055068 } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
50065069 if (!is_equality_cmp) {
50075070 return mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});
50085071 }
5009 return mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq));
5072 const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
5073 const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
5074 if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) {
5075 return Air.Inst.Ref.bool_true;
5076 } else {
5077 return Air.Inst.Ref.bool_false;
5078 }
50105079 }
50115080
5012 const instructions = &[_]*Inst{ lhs, rhs };
5081 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
50135082 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
50145083 if (!resolved_type.isSelfComparable(is_equality_cmp)) {
50155084 return mod.fail(&block.base, src, "operator not allowed for type '{}'", .{resolved_type});
......@@ -5018,18 +5087,21 @@ fn zirCmp(
50185087 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
50195088 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
50205089
5021 if (casted_lhs.value()) |lhs_val| {
5022 if (casted_rhs.value()) |rhs_val| {
5090 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| {
5091 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
50235092 if (lhs_val.isUndef() or rhs_val.isUndef()) {
5024 return sema.mod.constUndef(sema.arena, src, resolved_type);
5093 return sema.addConstUndef(resolved_type);
5094 }
5095 if (lhs_val.compare(op, rhs_val)) {
5096 return Air.Inst.Ref.bool_true;
5097 } else {
5098 return Air.Inst.Ref.bool_false;
50255099 }
5026 const result = lhs_val.compare(op, rhs_val);
5027 return sema.mod.constBool(sema.arena, src, result);
50285100 }
50295101 }
50305102
50315103 try sema.requireRuntimeBlock(block, src);
5032 const tag: Inst.Tag = switch (op) {
5104 const tag: Air.Inst.Tag = switch (op) {
50335105 .lt => .cmp_lt,
50345106 .lte => .cmp_lte,
50355107 .eq => .cmp_eq,
......@@ -5037,35 +5109,33 @@ fn zirCmp(
50375109 .gt => .cmp_gt,
50385110 .neq => .cmp_neq,
50395111 };
5040 const bool_type = Type.initTag(.bool); // TODO handle vectors
5041 return block.addBinOp(src, bool_type, tag, casted_lhs, casted_rhs);
5112 // TODO handle vectors
5113 return block.addBinOp(tag, casted_lhs, casted_rhs);
50425114}
50435115
5044fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5116fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
50455117 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5046 const src = inst_data.src();
50475118 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
50485119 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
50495120 const target = sema.mod.getTarget();
50505121 const abi_size = operand_ty.abiSize(target);
5051 return sema.mod.constIntUnsigned(sema.arena, src, Type.initTag(.comptime_int), abi_size);
5122 return sema.addIntUnsigned(Type.initTag(.comptime_int), abi_size);
50525123}
50535124
5054fn zirBitSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5125fn zirBitSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
50555126 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5056 const src = inst_data.src();
50575127 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
50585128 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
50595129 const target = sema.mod.getTarget();
50605130 const bit_size = operand_ty.bitSize(target);
5061 return sema.mod.constIntUnsigned(sema.arena, src, Type.initTag(.comptime_int), bit_size);
5131 return sema.addIntUnsigned(Type.initTag(.comptime_int), bit_size);
50625132}
50635133
50645134fn zirThis(
50655135 sema: *Sema,
50665136 block: *Scope.Block,
50675137 extended: Zir.Inst.Extended.InstData,
5068) InnerError!*Inst {
5138) CompileError!Air.Inst.Ref {
50695139 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
50705140 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirThis", .{});
50715141}
......@@ -5074,7 +5144,7 @@ fn zirRetAddr(
50745144 sema: *Sema,
50755145 block: *Scope.Block,
50765146 extended: Zir.Inst.Extended.InstData,
5077) InnerError!*Inst {
5147) CompileError!Air.Inst.Ref {
50785148 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
50795149 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirRetAddr", .{});
50805150}
......@@ -5083,12 +5153,12 @@ fn zirBuiltinSrc(
50835153 sema: *Sema,
50845154 block: *Scope.Block,
50855155 extended: Zir.Inst.Extended.InstData,
5086) InnerError!*Inst {
5156) CompileError!Air.Inst.Ref {
50875157 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
50885158 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinSrc", .{});
50895159}
50905160
5091fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5161fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
50925162 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
50935163 const src = inst_data.src();
50945164 const ty = try sema.resolveType(block, src, inst_data.operand);
......@@ -5114,16 +5184,16 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
51145184 // args: []const FnArg,
51155185 field_values[5] = Value.initTag(.null_value); // TODO
51165186
5117 return sema.mod.constInst(sema.arena, src, .{
5118 .ty = type_info_ty,
5119 .val = try Value.Tag.@"union".create(sema.arena, .{
5187 return sema.addConstant(
5188 type_info_ty,
5189 try Value.Tag.@"union".create(sema.arena, .{
51205190 .tag = try Value.Tag.enum_field_index.create(
51215191 sema.arena,
51225192 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),
51235193 ),
51245194 .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),
51255195 }),
5126 });
5196 );
51275197 },
51285198 else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{
51295199 @tagName(t),
......@@ -5131,31 +5201,30 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
51315201 }
51325202}
51335203
5134fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5204fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
51355205 _ = block;
51365206 const zir_datas = sema.code.instructions.items(.data);
51375207 const inst_data = zir_datas[inst].un_node;
5138 const src = inst_data.src();
5139 const operand = try sema.resolveInst(inst_data.operand);
5140 return sema.mod.constType(sema.arena, src, operand.ty);
5208 const operand = sema.resolveInst(inst_data.operand);
5209 const operand_ty = sema.typeOf(operand);
5210 return sema.addType(operand_ty);
51415211}
51425212
5143fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5213fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
51445214 _ = block;
51455215 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5146 const src = inst_data.src();
5147 const operand_ptr = try sema.resolveInst(inst_data.operand);
5148 const elem_ty = operand_ptr.ty.elemType();
5149 return sema.mod.constType(sema.arena, src, elem_ty);
5216 const operand_ptr = sema.resolveInst(inst_data.operand);
5217 const elem_ty = sema.typeOf(operand_ptr).elemType();
5218 return sema.addType(elem_ty);
51505219}
51515220
5152fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5221fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
51535222 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
51545223 const src = inst_data.src();
51555224 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeofLog2IntType", .{});
51565225}
51575226
5158fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5227fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
51595228 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
51605229 const src = inst_data.src();
51615230 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});
......@@ -5165,7 +5234,7 @@ fn zirTypeofPeer(
51655234 sema: *Sema,
51665235 block: *Scope.Block,
51675236 extended: Zir.Inst.Extended.InstData,
5168) InnerError!*Inst {
5237) CompileError!Air.Inst.Ref {
51695238 const tracy = trace(@src());
51705239 defer tracy.end();
51715240
......@@ -5173,63 +5242,37 @@ fn zirTypeofPeer(
51735242 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
51745243 const args = sema.code.refSlice(extra.end, extended.small);
51755244
5176 const inst_list = try sema.gpa.alloc(*ir.Inst, args.len);
5245 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
51775246 defer sema.gpa.free(inst_list);
51785247
51795248 for (args) |arg_ref, i| {
5180 inst_list[i] = try sema.resolveInst(arg_ref);
5249 inst_list[i] = sema.resolveInst(arg_ref);
51815250 }
51825251
51835252 const result_type = try sema.resolvePeerTypes(block, src, inst_list);
5184 return sema.mod.constType(sema.arena, src, result_type);
5253 return sema.addType(result_type);
51855254}
51865255
5187fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5256fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
51885257 const tracy = trace(@src());
51895258 defer tracy.end();
51905259
51915260 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
51925261 const src = inst_data.src();
5193 const uncasted_operand = try sema.resolveInst(inst_data.operand);
5262 const operand_src = src; // TODO put this on the operand, not the `!`
5263 const uncasted_operand = sema.resolveInst(inst_data.operand);
51945264
51955265 const bool_type = Type.initTag(.bool);
5196 const operand = try sema.coerce(block, bool_type, uncasted_operand, uncasted_operand.src);
5197 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
5198 return sema.mod.constBool(sema.arena, src, !val.toBool());
5199 }
5200 try sema.requireRuntimeBlock(block, src);
5201 return block.addUnOp(src, bool_type, .not, operand);
5202}
5203
5204fn zirBoolOp(
5205 sema: *Sema,
5206 block: *Scope.Block,
5207 inst: Zir.Inst.Index,
5208 comptime is_bool_or: bool,
5209) InnerError!*Inst {
5210 const tracy = trace(@src());
5211 defer tracy.end();
5212
5213 const src: LazySrcLoc = .unneeded;
5214 const bool_type = Type.initTag(.bool);
5215 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
5216 const uncasted_lhs = try sema.resolveInst(bin_inst.lhs);
5217 const lhs = try sema.coerce(block, bool_type, uncasted_lhs, uncasted_lhs.src);
5218 const uncasted_rhs = try sema.resolveInst(bin_inst.rhs);
5219 const rhs = try sema.coerce(block, bool_type, uncasted_rhs, uncasted_rhs.src);
5220
5221 if (lhs.value()) |lhs_val| {
5222 if (rhs.value()) |rhs_val| {
5223 if (is_bool_or) {
5224 return sema.mod.constBool(sema.arena, src, lhs_val.toBool() or rhs_val.toBool());
5225 } else {
5226 return sema.mod.constBool(sema.arena, src, lhs_val.toBool() and rhs_val.toBool());
5227 }
5266 const operand = try sema.coerce(block, bool_type, uncasted_operand, operand_src);
5267 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
5268 if (val.toBool()) {
5269 return Air.Inst.Ref.bool_false;
5270 } else {
5271 return Air.Inst.Ref.bool_true;
52285272 }
52295273 }
52305274 try sema.requireRuntimeBlock(block, src);
5231 const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and;
5232 return block.addBinOp(src, bool_type, tag, lhs, rhs);
5275 return block.addTyOp(.not, bool_type, operand);
52335276}
52345277
52355278fn zirBoolBr(
......@@ -5237,20 +5280,25 @@ fn zirBoolBr(
52375280 parent_block: *Scope.Block,
52385281 inst: Zir.Inst.Index,
52395282 is_bool_or: bool,
5240) InnerError!*Inst {
5283) CompileError!Air.Inst.Ref {
52415284 const tracy = trace(@src());
52425285 defer tracy.end();
52435286
52445287 const datas = sema.code.instructions.items(.data);
52455288 const inst_data = datas[inst].bool_br;
5246 const src: LazySrcLoc = .unneeded;
5247 const lhs = try sema.resolveInst(inst_data.lhs);
5289 const lhs = sema.resolveInst(inst_data.lhs);
5290 const lhs_src = sema.src;
52485291 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
52495292 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
5293 const gpa = sema.gpa;
52505294
5251 if (try sema.resolveDefinedValue(parent_block, src, lhs)) |lhs_val| {
5295 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
52525296 if (lhs_val.toBool() == is_bool_or) {
5253 return sema.mod.constBool(sema.arena, src, is_bool_or);
5297 if (is_bool_or) {
5298 return Air.Inst.Ref.bool_true;
5299 } else {
5300 return Air.Inst.Ref.bool_false;
5301 }
52545302 }
52555303 // comptime-known left-hand side. No need for a block here; the result
52565304 // is simply the rhs expression. Here we rely on there only being 1
......@@ -5258,62 +5306,72 @@ fn zirBoolBr(
52585306 return sema.resolveBody(parent_block, body);
52595307 }
52605308
5261 const block_inst = try sema.arena.create(Inst.Block);
5262 block_inst.* = .{
5263 .base = .{
5264 .tag = Inst.Block.base_tag,
5265 .ty = Type.initTag(.bool),
5266 .src = src,
5267 },
5268 .body = undefined,
5269 };
5309 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
5310 try sema.air_instructions.append(gpa, .{
5311 .tag = .block,
5312 .data = .{ .ty_pl = .{
5313 .ty = .bool_type,
5314 .payload = undefined,
5315 } },
5316 });
52705317
52715318 var child_block = parent_block.makeSubBlock();
52725319 child_block.runtime_loop = null;
5273 child_block.runtime_cond = lhs.src;
5320 child_block.runtime_cond = lhs_src;
52745321 child_block.runtime_index += 1;
5275 defer child_block.instructions.deinit(sema.gpa);
5322 defer child_block.instructions.deinit(gpa);
52765323
52775324 var then_block = child_block.makeSubBlock();
5278 defer then_block.instructions.deinit(sema.gpa);
5325 defer then_block.instructions.deinit(gpa);
52795326
52805327 var else_block = child_block.makeSubBlock();
5281 defer else_block.instructions.deinit(sema.gpa);
5328 defer else_block.instructions.deinit(gpa);
52825329
52835330 const lhs_block = if (is_bool_or) &then_block else &else_block;
52845331 const rhs_block = if (is_bool_or) &else_block else &then_block;
52855332
5286 const lhs_result = try sema.mod.constInst(sema.arena, src, .{
5287 .ty = Type.initTag(.bool),
5288 .val = if (is_bool_or) Value.initTag(.bool_true) else Value.initTag(.bool_false),
5289 });
5290 _ = try lhs_block.addBr(src, block_inst, lhs_result);
5333 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
5334 _ = try lhs_block.addBr(block_inst, lhs_result);
52915335
52925336 const rhs_result = try sema.resolveBody(rhs_block, body);
5293 _ = try rhs_block.addBr(src, block_inst, rhs_result);
5337 _ = try rhs_block.addBr(block_inst, rhs_result);
52945338
5295 const air_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
5296 const air_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, else_block.instructions.items) };
5297 _ = try child_block.addCondBr(src, lhs, air_then_body, air_else_body);
5339 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
5340 then_block.instructions.items.len + else_block.instructions.items.len +
5341 @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len);
52985342
5299 block_inst.body = .{
5300 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
5301 };
5302 try parent_block.instructions.append(sema.gpa, &block_inst.base);
5303 return &block_inst.base;
5343 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
5344 .then_body_len = @intCast(u32, then_block.instructions.items.len),
5345 .else_body_len = @intCast(u32, else_block.instructions.items.len),
5346 });
5347 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
5348 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
5349
5350 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
5351 .operand = lhs,
5352 .payload = cond_br_payload,
5353 } } });
5354
5355 sema.air_instructions.items(.data)[block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(
5356 Air.Block{ .body_len = @intCast(u32, child_block.instructions.items.len) },
5357 );
5358 sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items);
5359
5360 try parent_block.instructions.append(gpa, block_inst);
5361 return Air.indexToRef(block_inst);
53045362}
53055363
53065364fn zirIsNonNull(
53075365 sema: *Sema,
53085366 block: *Scope.Block,
53095367 inst: Zir.Inst.Index,
5310) InnerError!*Inst {
5368) CompileError!Air.Inst.Ref {
53115369 const tracy = trace(@src());
53125370 defer tracy.end();
53135371
53145372 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
53155373 const src = inst_data.src();
5316 const operand = try sema.resolveInst(inst_data.operand);
5374 const operand = sema.resolveInst(inst_data.operand);
53175375 return sema.analyzeIsNull(block, src, operand, true);
53185376}
53195377
......@@ -5321,33 +5379,33 @@ fn zirIsNonNullPtr(
53215379 sema: *Sema,
53225380 block: *Scope.Block,
53235381 inst: Zir.Inst.Index,
5324) InnerError!*Inst {
5382) CompileError!Air.Inst.Ref {
53255383 const tracy = trace(@src());
53265384 defer tracy.end();
53275385
53285386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
53295387 const src = inst_data.src();
5330 const ptr = try sema.resolveInst(inst_data.operand);
5388 const ptr = sema.resolveInst(inst_data.operand);
53315389 const loaded = try sema.analyzeLoad(block, src, ptr, src);
53325390 return sema.analyzeIsNull(block, src, loaded, true);
53335391}
53345392
5335fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5393fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
53365394 const tracy = trace(@src());
53375395 defer tracy.end();
53385396
53395397 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5340 const operand = try sema.resolveInst(inst_data.operand);
5398 const operand = sema.resolveInst(inst_data.operand);
53415399 return sema.analyzeIsNonErr(block, inst_data.src(), operand);
53425400}
53435401
5344fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5402fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
53455403 const tracy = trace(@src());
53465404 defer tracy.end();
53475405
53485406 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
53495407 const src = inst_data.src();
5350 const ptr = try sema.resolveInst(inst_data.operand);
5408 const ptr = sema.resolveInst(inst_data.operand);
53515409 const loaded = try sema.analyzeLoad(block, src, ptr, src);
53525410 return sema.analyzeIsNonErr(block, src, loaded);
53535411}
......@@ -5356,7 +5414,7 @@ fn zirCondbr(
53565414 sema: *Sema,
53575415 parent_block: *Scope.Block,
53585416 inst: Zir.Inst.Index,
5359) InnerError!Zir.Inst.Index {
5417) CompileError!Zir.Inst.Index {
53605418 const tracy = trace(@src());
53615419 defer tracy.end();
53625420
......@@ -5368,7 +5426,7 @@ fn zirCondbr(
53685426 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
53695427 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
53705428
5371 const uncasted_cond = try sema.resolveInst(extra.data.condition);
5429 const uncasted_cond = sema.resolveInst(extra.data.condition);
53725430 const cond = try sema.coerce(parent_block, Type.initTag(.bool), uncasted_cond, cond_src);
53735431
53745432 if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
......@@ -5377,29 +5435,39 @@ fn zirCondbr(
53775435 return always_noreturn;
53785436 }
53795437
5438 const gpa = sema.gpa;
5439
5440 // We'll re-use the sub block to save on memory bandwidth, and yank out the
5441 // instructions array in between using it for the then block and else block.
53805442 var sub_block = parent_block.makeSubBlock();
53815443 sub_block.runtime_loop = null;
5382 sub_block.runtime_cond = cond.src;
5444 sub_block.runtime_cond = cond_src;
53835445 sub_block.runtime_index += 1;
5384 defer sub_block.instructions.deinit(sema.gpa);
5446 defer sub_block.instructions.deinit(gpa);
53855447
53865448 _ = try sema.analyzeBody(&sub_block, then_body);
5387 const air_then_body: ir.Body = .{
5388 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
5389 };
5390
5391 sub_block.instructions.shrinkRetainingCapacity(0);
5449 const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
5450 defer gpa.free(true_instructions);
53925451
53935452 _ = try sema.analyzeBody(&sub_block, else_body);
5394 const air_else_body: ir.Body = .{
5395 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),
5396 };
5397
5398 _ = try parent_block.addCondBr(src, cond, air_then_body, air_else_body);
5453 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
5454 true_instructions.len + sub_block.instructions.items.len);
5455 _ = try parent_block.addInst(.{
5456 .tag = .cond_br,
5457 .data = .{ .pl_op = .{
5458 .operand = cond,
5459 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
5460 .then_body_len = @intCast(u32, true_instructions.len),
5461 .else_body_len = @intCast(u32, sub_block.instructions.items.len),
5462 }),
5463 } },
5464 });
5465 sema.air_extra.appendSliceAssumeCapacity(true_instructions);
5466 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
53995467 return always_noreturn;
54005468}
54015469
5402fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
5470fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
54035471 const tracy = trace(@src());
54045472 defer tracy.end();
54055473
......@@ -5411,7 +5479,7 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
54115479 if (safety_check and block.wantSafety()) {
54125480 return sema.safetyPanic(block, src, .unreach);
54135481 } else {
5414 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
5482 _ = try block.addNoOp(.unreach);
54155483 return always_noreturn;
54165484 }
54175485}
......@@ -5420,7 +5488,7 @@ fn zirRetErrValue(
54205488 sema: *Sema,
54215489 block: *Scope.Block,
54225490 inst: Zir.Inst.Index,
5423) InnerError!Zir.Inst.Index {
5491) CompileError!Zir.Inst.Index {
54245492 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
54255493 const err_name = inst_data.get(sema.code);
54265494 const src = inst_data.src();
......@@ -5433,10 +5501,10 @@ fn zirRetErrValue(
54335501 }
54345502 // Return the error code from the function.
54355503 const kv = try sema.mod.getErrorValue(err_name);
5436 const result_inst = try sema.mod.constInst(sema.arena, src, .{
5437 .ty = try Type.Tag.error_set_single.create(sema.arena, kv.key),
5438 .val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
5439 });
5504 const result_inst = try sema.addConstant(
5505 try Type.Tag.error_set_single.create(sema.arena, kv.key),
5506 try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
5507 );
54405508 return sema.analyzeRet(block, result_inst, src, true);
54415509}
54425510
......@@ -5445,23 +5513,23 @@ fn zirRetCoerce(
54455513 block: *Scope.Block,
54465514 inst: Zir.Inst.Index,
54475515 need_coercion: bool,
5448) InnerError!Zir.Inst.Index {
5516) CompileError!Zir.Inst.Index {
54495517 const tracy = trace(@src());
54505518 defer tracy.end();
54515519
54525520 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
5453 const operand = try sema.resolveInst(inst_data.operand);
5521 const operand = sema.resolveInst(inst_data.operand);
54545522 const src = inst_data.src();
54555523
54565524 return sema.analyzeRet(block, operand, src, need_coercion);
54575525}
54585526
5459fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
5527fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
54605528 const tracy = trace(@src());
54615529 defer tracy.end();
54625530
54635531 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5464 const operand = try sema.resolveInst(inst_data.operand);
5532 const operand = sema.resolveInst(inst_data.operand);
54655533 const src = inst_data.src();
54665534
54675535 return sema.analyzeRet(block, operand, src, false);
......@@ -5470,14 +5538,14 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
54705538fn analyzeRet(
54715539 sema: *Sema,
54725540 block: *Scope.Block,
5473 operand: *Inst,
5541 operand: Air.Inst.Ref,
54745542 src: LazySrcLoc,
54755543 need_coercion: bool,
5476) InnerError!Zir.Inst.Index {
5544) CompileError!Zir.Inst.Index {
54775545 if (block.inlining) |inlining| {
54785546 // We are inlining a function call; rewrite the `ret` as a `break`.
54795547 try inlining.merges.results.append(sema.gpa, operand);
5480 _ = try block.addBr(src, inlining.merges.block_inst, operand);
5548 _ = try block.addBr(inlining.merges.block_inst, operand);
54815549 return always_noreturn;
54825550 }
54835551
......@@ -5486,14 +5554,11 @@ fn analyzeRet(
54865554 const fn_ty = func.owner_decl.ty;
54875555 const fn_ret_ty = fn_ty.fnReturnType();
54885556 const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src);
5489 if (fn_ret_ty.zigTypeTag() == .Void)
5490 _ = try block.addNoOp(src, Type.initTag(.noreturn), .retvoid)
5491 else
5492 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
5557 _ = try block.addUnOp(.ret, casted_operand);
54935558 return always_noreturn;
54945559 }
54955560 }
5496 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);
5561 _ = try block.addUnOp(.ret, operand);
54975562 return always_noreturn;
54985563}
54995564
......@@ -5505,7 +5570,7 @@ fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
55055570 };
55065571}
55075572
5508fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5573fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
55095574 const tracy = trace(@src());
55105575 defer tracy.end();
55115576
......@@ -5523,10 +5588,10 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
55235588 inst_data.is_volatile,
55245589 inst_data.size,
55255590 );
5526 return sema.mod.constType(sema.arena, .unneeded, ty);
5591 return sema.addType(ty);
55275592}
55285593
5529fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5594fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
55305595 const tracy = trace(@src());
55315596 defer tracy.end();
55325597
......@@ -5577,10 +5642,10 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
55775642 inst_data.flags.is_volatile,
55785643 inst_data.size,
55795644 );
5580 return sema.mod.constType(sema.arena, src, ty);
5645 return sema.addType(ty);
55815646}
55825647
5583fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5648fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
55845649 const tracy = trace(@src());
55855650 defer tracy.end();
55865651
......@@ -5588,19 +5653,16 @@ fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
55885653 const src = inst_data.src();
55895654 const struct_type = try sema.resolveType(block, src, inst_data.operand);
55905655
5591 return sema.mod.constInst(sema.arena, src, .{
5592 .ty = struct_type,
5593 .val = Value.initTag(.empty_struct_value),
5594 });
5656 return sema.addConstant(struct_type, Value.initTag(.empty_struct_value));
55955657}
55965658
5597fn zirUnionInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5659fn zirUnionInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
55985660 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
55995661 const src = inst_data.src();
56005662 return sema.mod.fail(&block.base, src, "TODO: Sema.zirUnionInitPtr", .{});
56015663}
56025664
5603fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
5665fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
56045666 const mod = sema.mod;
56055667 const gpa = sema.gpa;
56065668 const zir_datas = sema.code.instructions.items(.data);
......@@ -5622,7 +5684,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
56225684 mem.set(Zir.Inst.Index, found_fields, 0);
56235685
56245686 // The init values to use for the struct instance.
5625 const field_inits = try gpa.alloc(*ir.Inst, struct_obj.fields.count());
5687 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
56265688 defer gpa.free(field_inits);
56275689
56285690 var field_i: u32 = 0;
......@@ -5651,7 +5713,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
56515713 return mod.failWithOwnedErrorMsg(&block.base, msg);
56525714 }
56535715 found_fields[field_index] = item.data.field_type;
5654 field_inits[field_index] = try sema.resolveInst(item.data.init);
5716 field_inits[field_index] = sema.resolveInst(item.data.init);
56555717 }
56565718
56575719 var root_msg: ?*Module.ErrorMsg = null;
......@@ -5671,10 +5733,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
56715733 root_msg = try mod.errMsg(&block.base, src, template, args);
56725734 }
56735735 } else {
5674 field_inits[i] = try mod.constInst(sema.arena, src, .{
5675 .ty = field.ty,
5676 .val = field.default_val,
5677 });
5736 field_inits[i] = try sema.addConstant(field.ty, field.default_val);
56785737 }
56795738 }
56805739 if (root_msg) |msg| {
......@@ -5694,7 +5753,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
56945753 }
56955754
56965755 const is_comptime = for (field_inits) |field_init| {
5697 if (field_init.value() == null) {
5756 if (!(try sema.isComptimeKnown(block, src, field_init))) {
56985757 break false;
56995758 }
57005759 } else true;
......@@ -5702,18 +5761,15 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
57025761 if (is_comptime) {
57035762 const values = try sema.arena.alloc(Value, field_inits.len);
57045763 for (field_inits) |field_init, i| {
5705 values[i] = field_init.value().?;
5764 values[i] = (sema.resolvePossiblyUndefinedValue(block, src, field_init) catch unreachable).?;
57065765 }
5707 return mod.constInst(sema.arena, src, .{
5708 .ty = struct_ty,
5709 .val = try Value.Tag.@"struct".create(sema.arena, values.ptr),
5710 });
5766 return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr));
57115767 }
57125768
57135769 return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
57145770}
57155771
5716fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
5772fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
57175773 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
57185774 const src = inst_data.src();
57195775
......@@ -5721,7 +5777,7 @@ fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_
57215777 return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInitAnon", .{});
57225778}
57235779
5724fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
5780fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
57255781 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
57265782 const src = inst_data.src();
57275783
......@@ -5729,7 +5785,7 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
57295785 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{});
57305786}
57315787
5732fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
5788fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
57335789 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
57345790 const src = inst_data.src();
57355791
......@@ -5737,13 +5793,13 @@ fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_r
57375793 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInitAnon", .{});
57385794}
57395795
5740fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5796fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
57415797 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
57425798 const src = inst_data.src();
57435799 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldTypeRef", .{});
57445800}
57455801
5746fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5802fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
57475803 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
57485804 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
57495805 const src = inst_data.src();
......@@ -5758,14 +5814,14 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
57585814 const struct_obj = struct_ty.castTag(.@"struct").?.data;
57595815 const field = struct_obj.fields.get(field_name) orelse
57605816 return sema.failWithBadFieldAccess(block, struct_obj, src, field_name);
5761 return sema.mod.constType(sema.arena, src, field.ty);
5817 return sema.addType(field.ty);
57625818}
57635819
57645820fn zirErrorReturnTrace(
57655821 sema: *Sema,
57665822 block: *Scope.Block,
57675823 extended: Zir.Inst.Extended.InstData,
5768) InnerError!*Inst {
5824) CompileError!Air.Inst.Ref {
57695825 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
57705826 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorReturnTrace", .{});
57715827}
......@@ -5774,7 +5830,7 @@ fn zirFrame(
57745830 sema: *Sema,
57755831 block: *Scope.Block,
57765832 extended: Zir.Inst.Extended.InstData,
5777) InnerError!*Inst {
5833) CompileError!Air.Inst.Ref {
57785834 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
57795835 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrame", .{});
57805836}
......@@ -5783,91 +5839,91 @@ fn zirFrameAddress(
57835839 sema: *Sema,
57845840 block: *Scope.Block,
57855841 extended: Zir.Inst.Extended.InstData,
5786) InnerError!*Inst {
5842) CompileError!Air.Inst.Ref {
57875843 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
57885844 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameAddress", .{});
57895845}
57905846
5791fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5847fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
57925848 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
57935849 const src = inst_data.src();
57945850 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignOf", .{});
57955851}
57965852
5797fn zirBoolToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5853fn zirBoolToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
57985854 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
57995855 const src = inst_data.src();
58005856 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBoolToInt", .{});
58015857}
58025858
5803fn zirEmbedFile(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5859fn zirEmbedFile(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58045860 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58055861 const src = inst_data.src();
58065862 return sema.mod.fail(&block.base, src, "TODO: Sema.zirEmbedFile", .{});
58075863}
58085864
5809fn zirErrorName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5865fn zirErrorName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58105866 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58115867 const src = inst_data.src();
58125868 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorName", .{});
58135869}
58145870
5815fn zirUnaryMath(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5871fn zirUnaryMath(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58165872 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58175873 const src = inst_data.src();
58185874 return sema.mod.fail(&block.base, src, "TODO: Sema.zirUnaryMath", .{});
58195875}
58205876
5821fn zirTagName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5877fn zirTagName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58225878 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58235879 const src = inst_data.src();
58245880 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTagName", .{});
58255881}
58265882
5827fn zirReify(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5883fn zirReify(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58285884 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58295885 const src = inst_data.src();
58305886 return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify", .{});
58315887}
58325888
5833fn zirTypeName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5889fn zirTypeName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58345890 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58355891 const src = inst_data.src();
58365892 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTypeName", .{});
58375893}
58385894
5839fn zirFrameType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5895fn zirFrameType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58405896 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58415897 const src = inst_data.src();
58425898 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameType", .{});
58435899}
58445900
5845fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5901fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58465902 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58475903 const src = inst_data.src();
58485904 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameSize", .{});
58495905}
58505906
5851fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5907fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58525908 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
58535909 const src = inst_data.src();
58545910 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
58555911}
58565912
5857fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5913fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58585914 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
58595915 const src = inst_data.src();
58605916 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});
58615917}
58625918
5863fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5919fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
58645920 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
58655921 const src = inst_data.src();
58665922
58675923 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
58685924
58695925 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
5870 const operand_res = try sema.resolveInst(extra.rhs);
5926 const operand_res = sema.resolveInst(extra.rhs);
58715927 const operand_coerced = try sema.coerce(block, Type.initTag(.usize), operand_res, operand_src);
58725928
58735929 const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
......@@ -5888,20 +5944,13 @@ fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
58885944 .base = .{ .tag = .int_u64 },
58895945 .data = addr,
58905946 };
5891 return sema.mod.constInst(sema.arena, src, .{
5892 .ty = type_res,
5893 .val = Value.initPayload(&val_payload.base),
5894 });
5947 return sema.addConstant(type_res, Value.initPayload(&val_payload.base));
58955948 }
58965949
58975950 try sema.requireRuntimeBlock(block, src);
58985951 if (block.wantSafety()) {
5899 const zero = try sema.mod.constInst(sema.arena, src, .{
5900 .ty = Type.initTag(.u64),
5901 .val = Value.initTag(.zero),
5902 });
59035952 if (!type_res.isAllowzeroPtr()) {
5904 const is_non_zero = try block.addBinOp(src, Type.initTag(.bool), .cmp_neq, operand_coerced, zero);
5953 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
59055954 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
59065955 }
59075956
......@@ -5911,211 +5960,211 @@ fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
59115960 .base = .{ .tag = .int_u64 },
59125961 .data = ptr_align - 1,
59135962 };
5914 const align_minus_1 = try sema.mod.constInst(sema.arena, src, .{
5915 .ty = Type.initTag(.u64),
5916 .val = Value.initPayload(&val_payload.base),
5917 });
5918 const remainder = try block.addBinOp(src, Type.initTag(.u64), .bit_and, operand_coerced, align_minus_1);
5919 const is_aligned = try block.addBinOp(src, Type.initTag(.bool), .cmp_eq, remainder, zero);
5963 const align_minus_1 = try sema.addConstant(
5964 Type.initTag(.usize),
5965 Value.initPayload(&val_payload.base),
5966 );
5967 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
5968 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
59205969 try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
59215970 }
59225971 }
5923 return block.addUnOp(src, type_res, .bitcast, operand_coerced);
5972 return block.addTyOp(.bitcast, type_res, operand_coerced);
59245973}
59255974
5926fn zirErrSetCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5975fn zirErrSetCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59275976 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59285977 const src = inst_data.src();
59295978 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrSetCast", .{});
59305979}
59315980
5932fn zirPtrCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5981fn zirPtrCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59335982 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59345983 const src = inst_data.src();
59355984 return sema.mod.fail(&block.base, src, "TODO: Sema.zirPtrCast", .{});
59365985}
59375986
5938fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5987fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59395988 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59405989 const src = inst_data.src();
59415990 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTruncate", .{});
59425991}
59435992
5944fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5993fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59455994 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59465995 const src = inst_data.src();
59475996 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignCast", .{});
59485997}
59495998
5950fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5999fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59516000 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
59526001 const src = inst_data.src();
59536002 return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});
59546003}
59556004
5956fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6005fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59576006 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
59586007 const src = inst_data.src();
59596008 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});
59606009}
59616010
5962fn zirPopCount(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6011fn zirPopCount(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59636012 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
59646013 const src = inst_data.src();
59656014 return sema.mod.fail(&block.base, src, "TODO: Sema.zirPopCount", .{});
59666015}
59676016
5968fn zirByteSwap(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6017fn zirByteSwap(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59696018 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
59706019 const src = inst_data.src();
59716020 return sema.mod.fail(&block.base, src, "TODO: Sema.zirByteSwap", .{});
59726021}
59736022
5974fn zirBitReverse(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6023fn zirBitReverse(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59756024 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
59766025 const src = inst_data.src();
59776026 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBitReverse", .{});
59786027}
59796028
5980fn zirDivExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6029fn zirDivExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59816030 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59826031 const src = inst_data.src();
59836032 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivExact", .{});
59846033}
59856034
5986fn zirDivFloor(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6035fn zirDivFloor(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59876036 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59886037 const src = inst_data.src();
59896038 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{});
59906039}
59916040
5992fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6041fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59936042 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
59946043 const src = inst_data.src();
59956044 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});
59966045}
59976046
5998fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6047fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
59996048 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60006049 const src = inst_data.src();
60016050 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMod", .{});
60026051}
60036052
6004fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6053fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60056054 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60066055 const src = inst_data.src();
60076056 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRem", .{});
60086057}
60096058
6010fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6059fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60116060 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60126061 const src = inst_data.src();
60136062 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShlExact", .{});
60146063}
60156064
6016fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6065fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60176066 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60186067 const src = inst_data.src();
60196068 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShrExact", .{});
60206069}
60216070
6022fn zirBitOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6071fn zirBitOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60236072 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60246073 const src = inst_data.src();
60256074 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBitOffsetOf", .{});
60266075}
60276076
6028fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6077fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60296078 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60306079 const src = inst_data.src();
60316080 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
60326081}
60336082
6034fn zirCmpxchg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6083fn zirCmpxchg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60356084 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60366085 const src = inst_data.src();
60376086 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCmpxchg", .{});
60386087}
60396088
6040fn zirSplat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6089fn zirSplat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60416090 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60426091 const src = inst_data.src();
60436092 return sema.mod.fail(&block.base, src, "TODO: Sema.zirSplat", .{});
60446093}
60456094
6046fn zirReduce(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6095fn zirReduce(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60476096 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60486097 const src = inst_data.src();
60496098 return sema.mod.fail(&block.base, src, "TODO: Sema.zirReduce", .{});
60506099}
60516100
6052fn zirShuffle(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6101fn zirShuffle(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60536102 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60546103 const src = inst_data.src();
60556104 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShuffle", .{});
60566105}
60576106
6058fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6107fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60596108 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60606109 const src = inst_data.src();
60616110 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicLoad", .{});
60626111}
60636112
6064fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6113fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60656114 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60666115 const src = inst_data.src();
60676116 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicRmw", .{});
60686117}
60696118
6070fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6119fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60716120 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60726121 const src = inst_data.src();
60736122 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicStore", .{});
60746123}
60756124
6076fn zirMulAdd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6125fn zirMulAdd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60776126 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60786127 const src = inst_data.src();
60796128 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMulAdd", .{});
60806129}
60816130
6082fn zirBuiltinCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6131fn zirBuiltinCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60836132 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60846133 const src = inst_data.src();
60856134 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinCall", .{});
60866135}
60876136
6088fn zirFieldPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6137fn zirFieldPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60896138 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60906139 const src = inst_data.src();
60916140 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldPtrType", .{});
60926141}
60936142
6094fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6143fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
60956144 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
60966145 const src = inst_data.src();
60976146 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});
60986147}
60996148
6100fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6149fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61016150 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61026151 const src = inst_data.src();
61036152 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{});
61046153}
61056154
6106fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6155fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61076156 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61086157 const src = inst_data.src();
61096158 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
61106159}
61116160
6112fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6161fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61136162 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61146163 const src = inst_data.src();
61156164 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinAsyncCall", .{});
61166165}
61176166
6118fn zirResume(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
6167fn zirResume(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61196168 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
61206169 const src = inst_data.src();
61216170 return sema.mod.fail(&block.base, src, "TODO: Sema.zirResume", .{});
......@@ -6126,7 +6175,7 @@ fn zirAwait(
61266175 block: *Scope.Block,
61276176 inst: Zir.Inst.Index,
61286177 is_nosuspend: bool,
6129) InnerError!*Inst {
6178) CompileError!Air.Inst.Ref {
61306179 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
61316180 const src = inst_data.src();
61326181
......@@ -6138,7 +6187,7 @@ fn zirVarExtended(
61386187 sema: *Sema,
61396188 block: *Scope.Block,
61406189 extended: Zir.Inst.Extended.InstData,
6141) InnerError!*Inst {
6190) CompileError!Air.Inst.Ref {
61426191 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
61436192 const src = sema.src;
61446193 const ty_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at type
......@@ -6192,10 +6241,10 @@ fn zirVarExtended(
61926241 .is_mutable = true, // TODO get rid of this unused field
61936242 .is_threadlocal = small.is_threadlocal,
61946243 };
6195 const result = try sema.mod.constInst(sema.arena, src, .{
6196 .ty = var_ty,
6197 .val = try Value.Tag.variable.create(sema.arena, new_var),
6198 });
6244 const result = try sema.addConstant(
6245 var_ty,
6246 try Value.Tag.variable.create(sema.arena, new_var),
6247 );
61996248 return result;
62006249}
62016250
......@@ -6204,7 +6253,7 @@ fn zirFuncExtended(
62046253 block: *Scope.Block,
62056254 extended: Zir.Inst.Extended.InstData,
62066255 inst: Zir.Inst.Index,
6207) InnerError!*Inst {
6256) CompileError!Air.Inst.Ref {
62086257 const tracy = trace(@src());
62096258 defer tracy.end();
62106259
......@@ -6271,7 +6320,7 @@ fn zirCUndef(
62716320 sema: *Sema,
62726321 block: *Scope.Block,
62736322 extended: Zir.Inst.Extended.InstData,
6274) InnerError!*Inst {
6323) CompileError!Air.Inst.Ref {
62756324 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
62766325 const src: LazySrcLoc = .{ .node_offset = extra.node };
62776326 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCUndef", .{});
......@@ -6281,7 +6330,7 @@ fn zirCInclude(
62816330 sema: *Sema,
62826331 block: *Scope.Block,
62836332 extended: Zir.Inst.Extended.InstData,
6284) InnerError!*Inst {
6333) CompileError!Air.Inst.Ref {
62856334 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
62866335 const src: LazySrcLoc = .{ .node_offset = extra.node };
62876336 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCInclude", .{});
......@@ -6291,7 +6340,7 @@ fn zirCDefine(
62916340 sema: *Sema,
62926341 block: *Scope.Block,
62936342 extended: Zir.Inst.Extended.InstData,
6294) InnerError!*Inst {
6343) CompileError!Air.Inst.Ref {
62956344 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
62966345 const src: LazySrcLoc = .{ .node_offset = extra.node };
62976346 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCDefine", .{});
......@@ -6301,7 +6350,7 @@ fn zirWasmMemorySize(
63016350 sema: *Sema,
63026351 block: *Scope.Block,
63036352 extended: Zir.Inst.Extended.InstData,
6304) InnerError!*Inst {
6353) CompileError!Air.Inst.Ref {
63056354 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
63066355 const src: LazySrcLoc = .{ .node_offset = extra.node };
63076356 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirWasmMemorySize", .{});
......@@ -6311,7 +6360,7 @@ fn zirWasmMemoryGrow(
63116360 sema: *Sema,
63126361 block: *Scope.Block,
63136362 extended: Zir.Inst.Extended.InstData,
6314) InnerError!*Inst {
6363) CompileError!Air.Inst.Ref {
63156364 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
63166365 const src: LazySrcLoc = .{ .node_offset = extra.node };
63176366 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});
......@@ -6321,7 +6370,7 @@ fn zirBuiltinExtern(
63216370 sema: *Sema,
63226371 block: *Scope.Block,
63236372 extended: Zir.Inst.Extended.InstData,
6324) InnerError!*Inst {
6373) CompileError!Air.Inst.Ref {
63256374 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
63266375 const src: LazySrcLoc = .{ .node_offset = extra.node };
63276376 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinExtern", .{});
......@@ -6355,32 +6404,13 @@ pub const PanicId = enum {
63556404 invalid_error_code,
63566405};
63576406
6358fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
6359 const block_inst = try sema.arena.create(Inst.Block);
6360 block_inst.* = .{
6361 .base = .{
6362 .tag = Inst.Block.base_tag,
6363 .ty = Type.initTag(.void),
6364 .src = ok.src,
6365 },
6366 .body = .{
6367 .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the condbr.
6368 },
6369 };
6370
6371 const ok_body: ir.Body = .{
6372 .instructions = try sema.arena.alloc(*Inst, 1), // Only need space for the br_void.
6373 };
6374 const br_void = try sema.arena.create(Inst.BrVoid);
6375 br_void.* = .{
6376 .base = .{
6377 .tag = .br_void,
6378 .ty = Type.initTag(.noreturn),
6379 .src = ok.src,
6380 },
6381 .block = block_inst,
6382 };
6383 ok_body.instructions[0] = &br_void.base;
6407fn addSafetyCheck(
6408 sema: *Sema,
6409 parent_block: *Scope.Block,
6410 ok: Air.Inst.Ref,
6411 panic_id: PanicId,
6412) !void {
6413 const gpa = sema.gpa;
63846414
63856415 var fail_block: Scope.Block = .{
63866416 .parent = parent_block,
......@@ -6391,33 +6421,62 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
63916421 .is_comptime = parent_block.is_comptime,
63926422 };
63936423
6394 defer fail_block.instructions.deinit(sema.gpa);
6424 defer fail_block.instructions.deinit(gpa);
63956425
6396 _ = try sema.safetyPanic(&fail_block, ok.src, panic_id);
6426 _ = try sema.safetyPanic(&fail_block, .unneeded, panic_id);
63976427
6398 const fail_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, fail_block.instructions.items) };
6428 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
63996429
6400 const condbr = try sema.arena.create(Inst.CondBr);
6401 condbr.* = .{
6402 .base = .{
6403 .tag = .condbr,
6404 .ty = Type.initTag(.noreturn),
6405 .src = ok.src,
6406 },
6407 .condition = ok,
6408 .then_body = ok_body,
6409 .else_body = fail_body,
6410 };
6411 block_inst.body.instructions[0] = &condbr.base;
6430 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
6431 1 + // The main block only needs space for the cond_br.
6432 @typeInfo(Air.CondBr).Struct.fields.len +
6433 1 + // The ok branch of the cond_br only needs space for the br.
6434 fail_block.instructions.items.len);
64126435
6413 try parent_block.instructions.append(sema.gpa, &block_inst.base);
6436 try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
6437 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
6438 const cond_br_inst = block_inst + 1;
6439 const br_inst = cond_br_inst + 1;
6440 sema.air_instructions.appendAssumeCapacity(.{
6441 .tag = .block,
6442 .data = .{ .ty_pl = .{
6443 .ty = .void_type,
6444 .payload = sema.addExtraAssumeCapacity(Air.Block{
6445 .body_len = 1,
6446 }),
6447 } },
6448 });
6449 sema.air_extra.appendAssumeCapacity(cond_br_inst);
6450
6451 sema.air_instructions.appendAssumeCapacity(.{
6452 .tag = .cond_br,
6453 .data = .{ .pl_op = .{
6454 .operand = ok,
6455 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
6456 .then_body_len = 1,
6457 .else_body_len = @intCast(u32, fail_block.instructions.items.len),
6458 }),
6459 } },
6460 });
6461 sema.air_extra.appendAssumeCapacity(br_inst);
6462 sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items);
6463
6464 sema.air_instructions.appendAssumeCapacity(.{
6465 .tag = .br,
6466 .data = .{ .br = .{
6467 .block_inst = block_inst,
6468 .operand = .void_value,
6469 } },
6470 });
6471
6472 parent_block.instructions.appendAssumeCapacity(block_inst);
64146473}
64156474
64166475fn panicWithMsg(
64176476 sema: *Sema,
64186477 block: *Scope.Block,
64196478 src: LazySrcLoc,
6420 msg_inst: *ir.Inst,
6479 msg_inst: Air.Inst.Ref,
64216480) !Zir.Inst.Index {
64226481 const mod = sema.mod;
64236482 const arena = sema.arena;
......@@ -6426,19 +6485,19 @@ fn panicWithMsg(
64266485 mod.comp.bin_file.options.object_format == .c;
64276486 if (!this_feature_is_implemented_in_the_backend) {
64286487 // TODO implement this feature in all the backends and then delete this branch
6429 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
6430 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
6488 _ = try block.addNoOp(.breakpoint);
6489 _ = try block.addNoOp(.unreach);
64316490 return always_noreturn;
64326491 }
64336492 const panic_fn = try sema.getBuiltin(block, src, "panic");
64346493 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
64356494 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
6436 const ptr_stack_trace_ty = try mod.simplePtrType(arena, stack_trace_ty, true, .One);
6437 const null_stack_trace = try mod.constInst(arena, src, .{
6438 .ty = try mod.optionalType(arena, ptr_stack_trace_ty),
6439 .val = Value.initTag(.null_value),
6440 });
6441 const args = try arena.create([2]*ir.Inst);
6495 const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One);
6496 const null_stack_trace = try sema.addConstant(
6497 try mod.optionalType(arena, ptr_stack_trace_ty),
6498 Value.initTag(.null_value),
6499 );
6500 const args = try arena.create([2]Air.Inst.Ref);
64426501 args.* = .{ msg_inst, null_stack_trace };
64436502 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
64446503 return always_noreturn;
......@@ -6478,7 +6537,6 @@ fn safetyPanic(
64786537 };
64796538
64806539 const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);
6481
64826540 return sema.panicWithMsg(block, src, casted_msg_inst);
64836541}
64846542
......@@ -6494,27 +6552,29 @@ fn namedFieldPtr(
64946552 sema: *Sema,
64956553 block: *Scope.Block,
64966554 src: LazySrcLoc,
6497 object_ptr: *Inst,
6555 object_ptr: Air.Inst.Ref,
64986556 field_name: []const u8,
64996557 field_name_src: LazySrcLoc,
6500) InnerError!*Inst {
6558) CompileError!Air.Inst.Ref {
65016559 const mod = sema.mod;
65026560 const arena = sema.arena;
65036561
6504 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
6505 .Pointer => object_ptr.ty.elemType(),
6506 else => return mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
6562 const object_ptr_src = src; // TODO better source location
6563 const object_ptr_ty = sema.typeOf(object_ptr);
6564 const elem_ty = switch (object_ptr_ty.zigTypeTag()) {
6565 .Pointer => object_ptr_ty.elemType(),
6566 else => return mod.fail(&block.base, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}),
65076567 };
65086568 switch (elem_ty.zigTypeTag()) {
65096569 .Array => {
65106570 if (mem.eql(u8, field_name, "len")) {
6511 return mod.constInst(arena, src, .{
6512 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
6513 .val = try Value.Tag.ref_val.create(
6571 return sema.addConstant(
6572 Type.initTag(.single_const_pointer_to_comptime_int),
6573 try Value.Tag.ref_val.create(
65146574 arena,
65156575 try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),
65166576 ),
6517 });
6577 );
65186578 } else {
65196579 return mod.fail(
65206580 &block.base,
......@@ -6529,13 +6589,13 @@ fn namedFieldPtr(
65296589 switch (ptr_child.zigTypeTag()) {
65306590 .Array => {
65316591 if (mem.eql(u8, field_name, "len")) {
6532 return mod.constInst(arena, src, .{
6533 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
6534 .val = try Value.Tag.ref_val.create(
6592 return sema.addConstant(
6593 Type.initTag(.single_const_pointer_to_comptime_int),
6594 try Value.Tag.ref_val.create(
65356595 arena,
65366596 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
65376597 ),
6538 });
6598 );
65396599 } else {
65406600 return mod.fail(
65416601 &block.base,
......@@ -6549,9 +6609,9 @@ fn namedFieldPtr(
65496609 }
65506610 },
65516611 .Type => {
6552 _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
6553 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
6554 const val = result.value().?;
6612 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
6613 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
6614 const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?;
65556615 const child_type = try val.toType(arena);
65566616 switch (child_type.zigTypeTag()) {
65576617 .ErrorSet => {
......@@ -6572,15 +6632,15 @@ fn namedFieldPtr(
65726632 });
65736633 } else (try mod.getErrorValue(field_name)).key;
65746634
6575 return mod.constInst(arena, src, .{
6576 .ty = try mod.simplePtrType(arena, child_type, false, .One),
6577 .val = try Value.Tag.ref_val.create(
6635 return sema.addConstant(
6636 try Module.simplePtrType(arena, child_type, false, .One),
6637 try Value.Tag.ref_val.create(
65786638 arena,
65796639 try Value.Tag.@"error".create(arena, .{
65806640 .name = name,
65816641 }),
65826642 ),
6583 });
6643 );
65846644 },
65856645 .Struct, .Opaque, .Union => {
65866646 if (child_type.getNamespace()) |namespace| {
......@@ -6626,10 +6686,10 @@ fn namedFieldPtr(
66266686 };
66276687 const field_index_u32 = @intCast(u32, field_index);
66286688 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
6629 return mod.constInst(arena, src, .{
6630 .ty = try mod.simplePtrType(arena, child_type, false, .One),
6631 .val = try Value.Tag.ref_val.create(arena, enum_val),
6632 });
6689 return sema.addConstant(
6690 try Module.simplePtrType(arena, child_type, false, .One),
6691 try Value.Tag.ref_val.create(arena, enum_val),
6692 );
66336693 },
66346694 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
66356695 }
......@@ -6647,7 +6707,7 @@ fn analyzeNamespaceLookup(
66476707 src: LazySrcLoc,
66486708 namespace: *Scope.Namespace,
66496709 decl_name: []const u8,
6650) InnerError!?*Inst {
6710) CompileError!?Air.Inst.Ref {
66516711 const mod = sema.mod;
66526712 const gpa = sema.gpa;
66536713 if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
......@@ -6671,12 +6731,11 @@ fn analyzeStructFieldPtr(
66716731 sema: *Sema,
66726732 block: *Scope.Block,
66736733 src: LazySrcLoc,
6674 struct_ptr: *Inst,
6734 struct_ptr: Air.Inst.Ref,
66756735 field_name: []const u8,
66766736 field_name_src: LazySrcLoc,
66776737 unresolved_struct_ty: Type,
6678) InnerError!*Inst {
6679 const mod = sema.mod;
6738) CompileError!Air.Inst.Ref {
66806739 const arena = sema.arena;
66816740 assert(unresolved_struct_ty.zigTypeTag() == .Struct);
66826741
......@@ -6686,31 +6745,40 @@ fn analyzeStructFieldPtr(
66866745 const field_index = struct_obj.fields.getIndex(field_name) orelse
66876746 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
66886747 const field = struct_obj.fields.values()[field_index];
6689 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
6748 const ptr_field_ty = try Module.simplePtrType(arena, field.ty, true, .One);
66906749
66916750 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
6692 return mod.constInst(arena, src, .{
6693 .ty = ptr_field_ty,
6694 .val = try Value.Tag.field_ptr.create(arena, .{
6751 return sema.addConstant(
6752 ptr_field_ty,
6753 try Value.Tag.field_ptr.create(arena, .{
66956754 .container_ptr = struct_ptr_val,
66966755 .field_index = field_index,
66976756 }),
6698 });
6757 );
66996758 }
67006759
67016760 try sema.requireRuntimeBlock(block, src);
6702 return block.addStructFieldPtr(src, ptr_field_ty, struct_ptr, @intCast(u32, field_index));
6761 return block.addInst(.{
6762 .tag = .struct_field_ptr,
6763 .data = .{ .ty_pl = .{
6764 .ty = try sema.addType(ptr_field_ty),
6765 .payload = try sema.addExtra(Air.StructField{
6766 .struct_ptr = struct_ptr,
6767 .field_index = @intCast(u32, field_index),
6768 }),
6769 } },
6770 });
67036771}
67046772
67056773fn analyzeUnionFieldPtr(
67066774 sema: *Sema,
67076775 block: *Scope.Block,
67086776 src: LazySrcLoc,
6709 union_ptr: *Inst,
6777 union_ptr: Air.Inst.Ref,
67106778 field_name: []const u8,
67116779 field_name_src: LazySrcLoc,
67126780 unresolved_union_ty: Type,
6713) InnerError!*Inst {
6781) CompileError!Air.Inst.Ref {
67146782 const mod = sema.mod;
67156783 const arena = sema.arena;
67166784 assert(unresolved_union_ty.zigTypeTag() == .Union);
......@@ -6722,17 +6790,17 @@ fn analyzeUnionFieldPtr(
67226790 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
67236791
67246792 const field = union_obj.fields.values()[field_index];
6725 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
6793 const ptr_field_ty = try Module.simplePtrType(arena, field.ty, true, .One);
67266794
67276795 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
67286796 // TODO detect inactive union field and emit compile error
6729 return mod.constInst(arena, src, .{
6730 .ty = ptr_field_ty,
6731 .val = try Value.Tag.field_ptr.create(arena, .{
6797 return sema.addConstant(
6798 ptr_field_ty,
6799 try Value.Tag.field_ptr.create(arena, .{
67326800 .container_ptr = union_ptr_val,
67336801 .field_index = field_index,
67346802 }),
6735 });
6803 );
67366804 }
67376805
67386806 try sema.requireRuntimeBlock(block, src);
......@@ -6743,20 +6811,22 @@ fn elemPtr(
67436811 sema: *Sema,
67446812 block: *Scope.Block,
67456813 src: LazySrcLoc,
6746 array_ptr: *Inst,
6747 elem_index: *Inst,
6814 array_ptr: Air.Inst.Ref,
6815 elem_index: Air.Inst.Ref,
67486816 elem_index_src: LazySrcLoc,
6749) InnerError!*Inst {
6750 const array_ty = switch (array_ptr.ty.zigTypeTag()) {
6751 .Pointer => array_ptr.ty.elemType(),
6752 else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
6817) CompileError!Air.Inst.Ref {
6818 const array_ptr_src = src; // TODO better source location
6819 const array_ptr_ty = sema.typeOf(array_ptr);
6820 const array_ty = switch (array_ptr_ty.zigTypeTag()) {
6821 .Pointer => array_ptr_ty.elemType(),
6822 else => return sema.mod.fail(&block.base, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}),
67536823 };
67546824 if (!array_ty.isIndexable()) {
67556825 return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{array_ty});
67566826 }
67576827 if (array_ty.isSinglePointer() and array_ty.elemType().zigTypeTag() == .Array) {
67586828 // we have to deref the ptr operand to get the actual array pointer
6759 const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src);
6829 const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr_src);
67606830 return sema.elemPtrArray(block, src, array_ptr_deref, elem_index, elem_index_src);
67616831 }
67626832 if (array_ty.zigTypeTag() == .Array) {
......@@ -6770,23 +6840,23 @@ fn elemPtrArray(
67706840 sema: *Sema,
67716841 block: *Scope.Block,
67726842 src: LazySrcLoc,
6773 array_ptr: *Inst,
6774 elem_index: *Inst,
6843 array_ptr: Air.Inst.Ref,
6844 elem_index: Air.Inst.Ref,
67756845 elem_index_src: LazySrcLoc,
6776) InnerError!*Inst {
6777 if (array_ptr.value()) |array_ptr_val| {
6778 if (elem_index.value()) |index_val| {
6846) CompileError!Air.Inst.Ref {
6847 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
6848 if (try sema.resolveDefinedValue(block, src, elem_index)) |index_val| {
67796849 // Both array pointer and index are compile-time known.
67806850 const index_u64 = index_val.toUnsignedInt();
67816851 // @intCast here because it would have been impossible to construct a value that
67826852 // required a larger index.
67836853 const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));
6784 const pointee_type = array_ptr.ty.elemType().elemType();
6854 const pointee_type = sema.typeOf(array_ptr).elemType().elemType();
67856855
6786 return sema.mod.constInst(sema.arena, src, .{
6787 .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),
6788 .val = elem_ptr,
6789 });
6856 return sema.addConstant(
6857 try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),
6858 elem_ptr,
6859 );
67906860 }
67916861 }
67926862 _ = elem_index;
......@@ -6798,39 +6868,41 @@ fn coerce(
67986868 sema: *Sema,
67996869 block: *Scope.Block,
68006870 dest_type: Type,
6801 inst: *Inst,
6871 inst: Air.Inst.Ref,
68026872 inst_src: LazySrcLoc,
6803) InnerError!*Inst {
6873) CompileError!Air.Inst.Ref {
68046874 if (dest_type.tag() == .var_args_param) {
6805 return sema.coerceVarArgParam(block, inst);
6875 return sema.coerceVarArgParam(block, inst, inst_src);
68066876 }
6877
6878 const inst_ty = sema.typeOf(inst);
68076879 // If the types are the same, we can return the operand.
6808 if (dest_type.eql(inst.ty))
6880 if (dest_type.eql(inst_ty))
68096881 return inst;
68106882
6811 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
6883 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty);
68126884 if (in_memory_result == .ok) {
6813 return sema.bitcast(block, dest_type, inst);
6885 return sema.bitcast(block, dest_type, inst, inst_src);
68146886 }
68156887
68166888 const mod = sema.mod;
68176889 const arena = sema.arena;
68186890
68196891 // undefined to anything
6820 if (inst.value()) |val| {
6821 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
6822 return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = val });
6892 if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| {
6893 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
6894 return sema.addConstant(dest_type, val);
68236895 }
68246896 }
6825 assert(inst.ty.zigTypeTag() != .Undefined);
6897 assert(inst_ty.zigTypeTag() != .Undefined);
68266898
68276899 // T to E!T or E to E!T
68286900 if (dest_type.tag() == .error_union) {
6829 return try sema.wrapErrorUnion(block, dest_type, inst);
6901 return try sema.wrapErrorUnion(block, dest_type, inst, inst_src);
68306902 }
68316903
68326904 // comptime known number to other number
6833 if (try sema.coerceNum(block, dest_type, inst)) |some|
6905 if (try sema.coerceNum(block, dest_type, inst, inst_src)) |some|
68346906 return some;
68356907
68366908 const target = mod.getTarget();
......@@ -6838,28 +6910,28 @@ fn coerce(
68386910 switch (dest_type.zigTypeTag()) {
68396911 .Optional => {
68406912 // null to ?T
6841 if (inst.ty.zigTypeTag() == .Null) {
6842 return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
6913 if (inst_ty.zigTypeTag() == .Null) {
6914 return sema.addConstant(dest_type, Value.initTag(.null_value));
68436915 }
68446916
68456917 // T to ?T
68466918 var buf: Type.Payload.ElemType = undefined;
68476919 const child_type = dest_type.optionalChild(&buf);
6848 if (child_type.eql(inst.ty)) {
6849 return sema.wrapOptional(block, dest_type, inst);
6850 } else if (try sema.coerceNum(block, child_type, inst)) |some| {
6851 return sema.wrapOptional(block, dest_type, some);
6920 if (child_type.eql(inst_ty)) {
6921 return sema.wrapOptional(block, dest_type, inst, inst_src);
6922 } else if (try sema.coerceNum(block, child_type, inst, inst_src)) |some| {
6923 return sema.wrapOptional(block, dest_type, some, inst_src);
68526924 }
68536925 },
68546926 .Pointer => {
68556927 // Coercions where the source is a single pointer to an array.
68566928 src_array_ptr: {
6857 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
6858 const array_type = inst.ty.elemType();
6929 if (!inst_ty.isSinglePointer()) break :src_array_ptr;
6930 const array_type = inst_ty.elemType();
68596931 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
68606932 const array_elem_type = array_type.elemType();
6861 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
6862 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
6933 if (inst_ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
6934 if (inst_ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
68636935
68646936 const dst_elem_type = dest_type.elemType();
68656937 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
......@@ -6870,11 +6942,11 @@ fn coerce(
68706942 switch (dest_type.ptrSize()) {
68716943 .Slice => {
68726944 // *[N]T to []T
6873 return sema.coerceArrayPtrToSlice(block, dest_type, inst);
6945 return sema.coerceArrayPtrToSlice(block, dest_type, inst, inst_src);
68746946 },
68756947 .C => {
68766948 // *[N]T to [*c]T
6877 return sema.coerceArrayPtrToMany(block, dest_type, inst);
6949 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
68786950 },
68796951 .Many => {
68806952 // *[N]T to [*]T
......@@ -6882,12 +6954,12 @@ fn coerce(
68826954 const src_sentinel = array_type.sentinel();
68836955 const dst_sentinel = dest_type.sentinel();
68846956 if (src_sentinel == null and dst_sentinel == null)
6885 return sema.coerceArrayPtrToMany(block, dest_type, inst);
6957 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
68866958
68876959 if (src_sentinel) |src_s| {
68886960 if (dst_sentinel) |dst_s| {
68896961 if (src_s.eql(dst_s)) {
6890 return sema.coerceArrayPtrToMany(block, dest_type, inst);
6962 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
68916963 }
68926964 }
68936965 }
......@@ -6898,36 +6970,36 @@ fn coerce(
68986970 },
68996971 .Int => {
69006972 // integer widening
6901 if (inst.ty.zigTypeTag() == .Int) {
6902 assert(inst.value() == null); // handled above
6973 if (inst_ty.zigTypeTag() == .Int) {
6974 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
69036975
69046976 const dst_info = dest_type.intInfo(target);
6905 const src_info = inst.ty.intInfo(target);
6977 const src_info = inst_ty.intInfo(target);
69066978 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
69076979 // small enough unsigned ints can get casted to large enough signed ints
69086980 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
69096981 {
69106982 try sema.requireRuntimeBlock(block, inst_src);
6911 return block.addUnOp(inst_src, dest_type, .intcast, inst);
6983 return block.addTyOp(.intcast, dest_type, inst);
69126984 }
69136985 }
69146986 },
69156987 .Float => {
69166988 // float widening
6917 if (inst.ty.zigTypeTag() == .Float) {
6918 assert(inst.value() == null); // handled above
6989 if (inst_ty.zigTypeTag() == .Float) {
6990 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
69196991
6920 const src_bits = inst.ty.floatBits(target);
6992 const src_bits = inst_ty.floatBits(target);
69216993 const dst_bits = dest_type.floatBits(target);
69226994 if (dst_bits >= src_bits) {
69236995 try sema.requireRuntimeBlock(block, inst_src);
6924 return block.addUnOp(inst_src, dest_type, .floatcast, inst);
6996 return block.addTyOp(.floatcast, dest_type, inst);
69256997 }
69266998 }
69276999 },
69287000 .Enum => {
69297001 // enum literal to enum
6930 if (inst.ty.zigTypeTag() == .EnumLiteral) {
7002 if (inst_ty.zigTypeTag() == .EnumLiteral) {
69317003 const val = try sema.resolveConstValue(block, inst_src, inst);
69327004 const bytes = val.castTag(.enum_literal).?.data;
69337005 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);
......@@ -6950,16 +7022,16 @@ fn coerce(
69507022 };
69517023 return mod.failWithOwnedErrorMsg(&block.base, msg);
69527024 };
6953 return mod.constInst(arena, inst_src, .{
6954 .ty = resolved_dest_type,
6955 .val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
6956 });
7025 return sema.addConstant(
7026 resolved_dest_type,
7027 try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
7028 );
69577029 }
69587030 },
69597031 else => {},
69607032 }
69617033
6962 return mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
7034 return mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst_ty });
69637035}
69647036
69657037const InMemoryCoercionResult = enum {
......@@ -6976,9 +7048,16 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult
69767048 return .no_match;
69777049}
69787050
6979fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst {
6980 const val = inst.value() orelse return null;
6981 const src_zig_tag = inst.ty.zigTypeTag();
7051fn coerceNum(
7052 sema: *Sema,
7053 block: *Scope.Block,
7054 dest_type: Type,
7055 inst: Air.Inst.Ref,
7056 inst_src: LazySrcLoc,
7057) CompileError!?Air.Inst.Ref {
7058 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse return null;
7059 const inst_ty = sema.typeOf(inst);
7060 const src_zig_tag = inst_ty.zigTypeTag();
69827061 const dst_zig_tag = dest_type.zigTypeTag();
69837062
69847063 const target = sema.mod.getTarget();
......@@ -6986,37 +7065,43 @@ fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) Inn
69867065 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
69877066 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
69887067 if (val.floatHasFraction()) {
6989 return sema.mod.fail(&block.base, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
7068 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
69907069 }
6991 return sema.mod.fail(&block.base, inst.src, "TODO float to int", .{});
7070 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
69927071 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
69937072 if (!val.intFitsInType(dest_type, target)) {
6994 return sema.mod.fail(&block.base, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
7073 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ inst_ty, val });
69957074 }
6996 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
7075 return try sema.addConstant(dest_type, val);
69977076 }
69987077 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
69997078 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
70007079 const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) {
70017080 error.Overflow => return sema.mod.fail(
70027081 &block.base,
7003 inst.src,
7082 inst_src,
70047083 "cast of value {} to type '{}' loses information",
70057084 .{ val, dest_type },
70067085 ),
70077086 error.OutOfMemory => return error.OutOfMemory,
70087087 };
7009 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = res });
7088 return try sema.addConstant(dest_type, res);
70107089 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
7011 return sema.mod.fail(&block.base, inst.src, "TODO int to float", .{});
7090 return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{});
70127091 }
70137092 }
70147093 return null;
70157094}
70167095
7017fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst {
7018 switch (inst.ty.zigTypeTag()) {
7019 .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}),
7096fn coerceVarArgParam(
7097 sema: *Sema,
7098 block: *Scope.Block,
7099 inst: Air.Inst.Ref,
7100 inst_src: LazySrcLoc,
7101) !Air.Inst.Ref {
7102 const inst_ty = sema.typeOf(inst);
7103 switch (inst_ty.zigTypeTag()) {
7104 .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst_src, "integer and float literals in var args function must be casted", .{}),
70207105 else => {},
70217106 }
70227107 // TODO implement more of this function.
......@@ -7027,13 +7112,14 @@ fn storePtr(
70277112 sema: *Sema,
70287113 block: *Scope.Block,
70297114 src: LazySrcLoc,
7030 ptr: *Inst,
7031 uncasted_value: *Inst,
7115 ptr: Air.Inst.Ref,
7116 uncasted_value: Air.Inst.Ref,
70327117) !void {
7033 if (ptr.ty.isConstPtr())
7118 const ptr_ty = sema.typeOf(ptr);
7119 if (ptr_ty.isConstPtr())
70347120 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});
70357121
7036 const elem_ty = ptr.ty.elemType();
7122 const elem_ty = ptr_ty.elemType();
70377123 const value = try sema.coerce(block, elem_ty, uncasted_value, src);
70387124 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
70397125 return;
......@@ -7073,41 +7159,73 @@ fn storePtr(
70737159 // TODO handle if the element type requires comptime
70747160
70757161 try sema.requireRuntimeBlock(block, src);
7076 _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value);
7162 _ = try block.addBinOp(.store, ptr, value);
70777163}
70787164
7079fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
7080 if (inst.value()) |val| {
7165fn bitcast(
7166 sema: *Sema,
7167 block: *Scope.Block,
7168 dest_type: Type,
7169 inst: Air.Inst.Ref,
7170 inst_src: LazySrcLoc,
7171) CompileError!Air.Inst.Ref {
7172 if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| {
70817173 // Keep the comptime Value representation; take the new type.
7082 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
7174 return sema.addConstant(dest_type, val);
70837175 }
70847176 // TODO validate the type size and other compile errors
7085 try sema.requireRuntimeBlock(block, inst.src);
7086 return block.addUnOp(inst.src, dest_type, .bitcast, inst);
7177 try sema.requireRuntimeBlock(block, inst_src);
7178 return block.addTyOp(.bitcast, dest_type, inst);
70877179}
70887180
7089fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
7090 if (inst.value()) |val| {
7181fn coerceArrayPtrToSlice(
7182 sema: *Sema,
7183 block: *Scope.Block,
7184 dest_type: Type,
7185 inst: Air.Inst.Ref,
7186 inst_src: LazySrcLoc,
7187) CompileError!Air.Inst.Ref {
7188 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
70917189 // The comptime Value representation is compatible with both types.
7092 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
7190 return sema.addConstant(dest_type, val);
70937191 }
7094 return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
7192 return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
70957193}
70967194
7097fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
7098 if (inst.value()) |val| {
7195fn coerceArrayPtrToMany(
7196 sema: *Sema,
7197 block: *Scope.Block,
7198 dest_type: Type,
7199 inst: Air.Inst.Ref,
7200 inst_src: LazySrcLoc,
7201) !Air.Inst.Ref {
7202 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
70997203 // The comptime Value representation is compatible with both types.
7100 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
7204 return sema.addConstant(dest_type, val);
71017205 }
7102 return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
7206 return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
71037207}
71047208
7105fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
7209fn analyzeDeclVal(
7210 sema: *Sema,
7211 block: *Scope.Block,
7212 src: LazySrcLoc,
7213 decl: *Decl,
7214) CompileError!Air.Inst.Ref {
7215 if (sema.decl_val_table.get(decl)) |result| {
7216 return result;
7217 }
71067218 const decl_ref = try sema.analyzeDeclRef(block, src, decl);
7107 return sema.analyzeLoad(block, src, decl_ref, src);
7219 const result = try sema.analyzeLoad(block, src, decl_ref, src);
7220 if (Air.refToIndex(result)) |index| {
7221 if (sema.air_instructions.items(.tag)[index] == .constant) {
7222 sema.decl_val_table.put(sema.gpa, decl, result) catch {};
7223 }
7224 }
7225 return result;
71087226}
71097227
7110fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
7228fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) CompileError!Air.Inst.Ref {
71117229 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
71127230 sema.mod.ensureDeclAnalyzed(decl) catch |err| {
71137231 if (sema.func) |func| {
......@@ -7122,136 +7240,140 @@ fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl
71227240 if (decl_tv.val.tag() == .variable) {
71237241 return sema.analyzeVarRef(block, src, decl_tv);
71247242 }
7125 return sema.mod.constInst(sema.arena, src, .{
7126 .ty = try sema.mod.simplePtrType(sema.arena, decl_tv.ty, false, .One),
7127 .val = try Value.Tag.decl_ref.create(sema.arena, decl),
7128 });
7243 return sema.addConstant(
7244 try Module.simplePtrType(sema.arena, decl_tv.ty, false, .One),
7245 try Value.Tag.decl_ref.create(sema.arena, decl),
7246 );
71297247}
71307248
7131fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) InnerError!*Inst {
7249fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) CompileError!Air.Inst.Ref {
71327250 const variable = tv.val.castTag(.variable).?.data;
71337251
7134 const ty = try sema.mod.simplePtrType(sema.arena, tv.ty, variable.is_mutable, .One);
7252 const ty = try Module.simplePtrType(sema.arena, tv.ty, variable.is_mutable, .One);
71357253 if (!variable.is_mutable and !variable.is_extern) {
7136 return sema.mod.constInst(sema.arena, src, .{
7137 .ty = ty,
7138 .val = try Value.Tag.ref_val.create(sema.arena, variable.init),
7139 });
7254 return sema.addConstant(ty, try Value.Tag.ref_val.create(sema.arena, variable.init));
71407255 }
71417256
7257 const gpa = sema.gpa;
71427258 try sema.requireRuntimeBlock(block, src);
7143 const inst = try sema.arena.create(Inst.VarPtr);
7144 inst.* = .{
7145 .base = .{
7146 .tag = .varptr,
7147 .ty = ty,
7148 .src = src,
7149 },
7150 .variable = variable,
7151 };
7152 try block.instructions.append(sema.gpa, &inst.base);
7153 return &inst.base;
7259 try sema.air_variables.append(gpa, variable);
7260 const result_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
7261 try sema.air_instructions.append(gpa, .{
7262 .tag = .varptr,
7263 .data = .{ .ty_pl = .{
7264 .ty = try sema.addType(ty),
7265 .payload = @intCast(u32, sema.air_variables.items.len - 1),
7266 } },
7267 });
7268 try block.instructions.append(gpa, result_inst);
7269 return Air.indexToRef(result_inst);
71547270}
71557271
71567272fn analyzeRef(
71577273 sema: *Sema,
71587274 block: *Scope.Block,
71597275 src: LazySrcLoc,
7160 operand: *Inst,
7161) InnerError!*Inst {
7162 const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One);
7276 operand: Air.Inst.Ref,
7277) CompileError!Air.Inst.Ref {
7278 const operand_ty = sema.typeOf(operand);
7279 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
71637280
71647281 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |val| {
7165 return sema.mod.constInst(sema.arena, src, .{
7166 .ty = ptr_type,
7167 .val = try Value.Tag.ref_val.create(sema.arena, val),
7168 });
7282 return sema.addConstant(ptr_type, try Value.Tag.ref_val.create(sema.arena, val));
71697283 }
71707284
71717285 try sema.requireRuntimeBlock(block, src);
7172 return block.addUnOp(src, ptr_type, .ref, operand);
7286 return block.addTyOp(.ref, ptr_type, operand);
71737287}
71747288
71757289fn analyzeLoad(
71767290 sema: *Sema,
71777291 block: *Scope.Block,
71787292 src: LazySrcLoc,
7179 ptr: *Inst,
7293 ptr: Air.Inst.Ref,
71807294 ptr_src: LazySrcLoc,
7181) InnerError!*Inst {
7182 const elem_ty = switch (ptr.ty.zigTypeTag()) {
7183 .Pointer => ptr.ty.elemType(),
7184 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
7295) CompileError!Air.Inst.Ref {
7296 const ptr_ty = sema.typeOf(ptr);
7297 const elem_ty = switch (ptr_ty.zigTypeTag()) {
7298 .Pointer => ptr_ty.elemType(),
7299 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
71857300 };
71867301 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {
71877302 if (ptr_val.tag() == .int_u64)
71887303 break :blk; // do it at runtime
71897304
7190 return sema.mod.constInst(sema.arena, src, .{
7191 .ty = elem_ty,
7192 .val = try ptr_val.pointerDeref(sema.arena),
7193 });
7305 return sema.addConstant(elem_ty, try ptr_val.pointerDeref(sema.arena));
71947306 }
71957307
71967308 try sema.requireRuntimeBlock(block, src);
7197 return block.addUnOp(src, elem_ty, .load, ptr);
7309 return block.addTyOp(.load, elem_ty, ptr);
71987310}
71997311
72007312fn analyzeIsNull(
72017313 sema: *Sema,
72027314 block: *Scope.Block,
72037315 src: LazySrcLoc,
7204 operand: *Inst,
7316 operand: Air.Inst.Ref,
72057317 invert_logic: bool,
7206) InnerError!*Inst {
7318) CompileError!Air.Inst.Ref {
72077319 const result_ty = Type.initTag(.bool);
72087320 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |opt_val| {
72097321 if (opt_val.isUndef()) {
7210 return sema.mod.constUndef(sema.arena, src, result_ty);
7322 return sema.addConstUndef(result_ty);
72117323 }
72127324 const is_null = opt_val.isNull();
72137325 const bool_value = if (invert_logic) !is_null else is_null;
7214 return sema.mod.constBool(sema.arena, src, bool_value);
7326 if (bool_value) {
7327 return Air.Inst.Ref.bool_true;
7328 } else {
7329 return Air.Inst.Ref.bool_false;
7330 }
72157331 }
72167332 try sema.requireRuntimeBlock(block, src);
7217 const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;
7218 return block.addUnOp(src, result_ty, inst_tag, operand);
7333 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
7334 return block.addUnOp(air_tag, operand);
72197335}
72207336
72217337fn analyzeIsNonErr(
72227338 sema: *Sema,
72237339 block: *Scope.Block,
72247340 src: LazySrcLoc,
7225 operand: *Inst,
7226) InnerError!*Inst {
7227 const ot = operand.ty.zigTypeTag();
7228 if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true);
7229 if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false);
7341 operand: Air.Inst.Ref,
7342) CompileError!Air.Inst.Ref {
7343 const operand_ty = sema.typeOf(operand);
7344 const ot = operand_ty.zigTypeTag();
7345 if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true;
7346 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
72307347 assert(ot == .ErrorUnion);
72317348 const result_ty = Type.initTag(.bool);
72327349 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
72337350 if (err_union.isUndef()) {
7234 return sema.mod.constUndef(sema.arena, src, result_ty);
7351 return sema.addConstUndef(result_ty);
7352 }
7353 if (err_union.getError() == null) {
7354 return Air.Inst.Ref.bool_true;
7355 } else {
7356 return Air.Inst.Ref.bool_false;
72357357 }
7236 return sema.mod.constBool(sema.arena, src, err_union.getError() == null);
72377358 }
72387359 try sema.requireRuntimeBlock(block, src);
7239 return block.addUnOp(src, result_ty, .is_non_err, operand);
7360 return block.addUnOp(.is_non_err, operand);
72407361}
72417362
72427363fn analyzeSlice(
72437364 sema: *Sema,
72447365 block: *Scope.Block,
72457366 src: LazySrcLoc,
7246 array_ptr: *Inst,
7247 start: *Inst,
7248 end_opt: ?*Inst,
7249 sentinel_opt: ?*Inst,
7367 array_ptr: Air.Inst.Ref,
7368 start: Air.Inst.Ref,
7369 end_opt: Air.Inst.Ref,
7370 sentinel_opt: Air.Inst.Ref,
72507371 sentinel_src: LazySrcLoc,
7251) InnerError!*Inst {
7252 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
7253 .Pointer => array_ptr.ty.elemType(),
7254 else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}),
7372) CompileError!Air.Inst.Ref {
7373 const array_ptr_ty = sema.typeOf(array_ptr);
7374 const ptr_child = switch (array_ptr_ty.zigTypeTag()) {
7375 .Pointer => array_ptr_ty.elemType(),
7376 else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr_ty}),
72557377 };
72567378
72577379 var array_type = ptr_child;
......@@ -7271,16 +7393,16 @@ fn analyzeSlice(
72717393 else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}),
72727394 };
72737395
7274 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
7275 const casted = try sema.coerce(block, elem_type, sentinel, sentinel.src);
7396 const slice_sentinel = if (sentinel_opt != .none) blk: {
7397 const casted = try sema.coerce(block, elem_type, sentinel_opt, sentinel_src);
72767398 break :blk try sema.resolveConstValue(block, sentinel_src, casted);
72777399 } else null;
72787400
72797401 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
72807402 var return_elem_type = elem_type;
7281 if (end_opt) |end| {
7282 if (end.value()) |end_val| {
7283 if (start.value()) |start_val| {
7403 if (end_opt != .none) {
7404 if (try sema.resolveDefinedValue(block, src, end_opt)) |end_val| {
7405 if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
72847406 const start_u64 = start_val.toUnsignedInt();
72857407 const end_u64 = end_val.toUnsignedInt();
72867408 if (start_u64 > end_u64) {
......@@ -7300,7 +7422,7 @@ fn analyzeSlice(
73007422 const return_type = try sema.mod.ptrType(
73017423 sema.arena,
73027424 return_elem_type,
7303 if (end_opt == null) slice_sentinel else null,
7425 if (end_opt == .none) slice_sentinel else null,
73047426 0, // TODO alignment
73057427 0,
73067428 0,
......@@ -7319,34 +7441,46 @@ fn cmpNumeric(
73197441 sema: *Sema,
73207442 block: *Scope.Block,
73217443 src: LazySrcLoc,
7322 lhs: *Inst,
7323 rhs: *Inst,
7444 lhs: Air.Inst.Ref,
7445 rhs: Air.Inst.Ref,
73247446 op: std.math.CompareOperator,
7325) InnerError!*Inst {
7326 assert(lhs.ty.isNumeric());
7327 assert(rhs.ty.isNumeric());
7447 lhs_src: LazySrcLoc,
7448 rhs_src: LazySrcLoc,
7449) CompileError!Air.Inst.Ref {
7450 const lhs_ty = sema.typeOf(lhs);
7451 const rhs_ty = sema.typeOf(rhs);
73287452
7329 const lhs_ty_tag = lhs.ty.zigTypeTag();
7330 const rhs_ty_tag = rhs.ty.zigTypeTag();
7453 assert(lhs_ty.isNumeric());
7454 assert(rhs_ty.isNumeric());
7455
7456 const lhs_ty_tag = lhs_ty.zigTypeTag();
7457 const rhs_ty_tag = rhs_ty.zigTypeTag();
73317458
73327459 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
7333 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
7460 if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
73347461 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
7335 lhs.ty.arrayLen(),
7336 rhs.ty.arrayLen(),
7462 lhs_ty.arrayLen(),
7463 rhs_ty.arrayLen(),
73377464 });
73387465 }
73397466 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{});
73407467 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
73417468 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
7342 lhs.ty,
7343 rhs.ty,
7469 lhs_ty,
7470 rhs_ty,
73447471 });
73457472 }
73467473
7347 if (lhs.value()) |lhs_val| {
7348 if (rhs.value()) |rhs_val| {
7349 return sema.mod.constBool(sema.arena, src, Value.compare(lhs_val, op, rhs_val));
7474 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lhs_val| {
7475 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rhs_val| {
7476 if (lhs_val.isUndef() or rhs_val.isUndef()) {
7477 return sema.addConstUndef(Type.initTag(.bool));
7478 }
7479 if (Value.compare(lhs_val, op, rhs_val)) {
7480 return Air.Inst.Ref.bool_true;
7481 } else {
7482 return Air.Inst.Ref.bool_false;
7483 }
73507484 }
73517485 }
73527486
......@@ -7372,19 +7506,19 @@ fn cmpNumeric(
73727506 // Implicit cast the smaller one to the larger one.
73737507 const dest_type = x: {
73747508 if (lhs_ty_tag == .ComptimeFloat) {
7375 break :x rhs.ty;
7509 break :x rhs_ty;
73767510 } else if (rhs_ty_tag == .ComptimeFloat) {
7377 break :x lhs.ty;
7511 break :x lhs_ty;
73787512 }
7379 if (lhs.ty.floatBits(target) >= rhs.ty.floatBits(target)) {
7380 break :x lhs.ty;
7513 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
7514 break :x lhs_ty;
73817515 } else {
7382 break :x rhs.ty;
7516 break :x rhs_ty;
73837517 }
73847518 };
7385 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);
7386 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src);
7387 return block.addBinOp(src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
7519 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs_src);
7520 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs_src);
7521 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
73887522 }
73897523 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
73907524 // For mixed signed and unsigned integers, implicit cast both operands to a signed
......@@ -7392,22 +7526,22 @@ fn cmpNumeric(
73927526 // For mixed floats and integers, extract the integer part from the float, cast that to
73937527 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
73947528 // add/subtract 1.
7395 const lhs_is_signed = if (lhs.value()) |lhs_val|
7529 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
73967530 lhs_val.compareWithZero(.lt)
73977531 else
7398 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
7399 const rhs_is_signed = if (rhs.value()) |rhs_val|
7532 (lhs_ty.isFloat() or lhs_ty.isSignedInt());
7533 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
74007534 rhs_val.compareWithZero(.lt)
74017535 else
7402 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
7536 (rhs_ty.isFloat() or rhs_ty.isSignedInt());
74037537 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
74047538
74057539 var dest_float_type: ?Type = null;
74067540
74077541 var lhs_bits: usize = undefined;
7408 if (lhs.value()) |lhs_val| {
7542 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lhs_val| {
74097543 if (lhs_val.isUndef())
7410 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));
7544 return sema.addConstUndef(Type.initTag(.bool));
74117545 const is_unsigned = if (lhs_is_float) x: {
74127546 var bigint_space: Value.BigIntSpace = undefined;
74137547 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
......@@ -7415,8 +7549,8 @@ fn cmpNumeric(
74157549 const zcmp = lhs_val.orderAgainstZero();
74167550 if (lhs_val.floatHasFraction()) {
74177551 switch (op) {
7418 .eq => return sema.mod.constBool(sema.arena, src, false),
7419 .neq => return sema.mod.constBool(sema.arena, src, true),
7552 .eq => return Air.Inst.Ref.bool_false,
7553 .neq => return Air.Inst.Ref.bool_true,
74207554 else => {},
74217555 }
74227556 if (zcmp == .lt) {
......@@ -7433,16 +7567,16 @@ fn cmpNumeric(
74337567 };
74347568 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
74357569 } else if (lhs_is_float) {
7436 dest_float_type = lhs.ty;
7570 dest_float_type = lhs_ty;
74377571 } else {
7438 const int_info = lhs.ty.intInfo(target);
7572 const int_info = lhs_ty.intInfo(target);
74397573 lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
74407574 }
74417575
74427576 var rhs_bits: usize = undefined;
7443 if (rhs.value()) |rhs_val| {
7577 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rhs_val| {
74447578 if (rhs_val.isUndef())
7445 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));
7579 return sema.addConstUndef(Type.initTag(.bool));
74467580 const is_unsigned = if (rhs_is_float) x: {
74477581 var bigint_space: Value.BigIntSpace = undefined;
74487582 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
......@@ -7450,8 +7584,8 @@ fn cmpNumeric(
74507584 const zcmp = rhs_val.orderAgainstZero();
74517585 if (rhs_val.floatHasFraction()) {
74527586 switch (op) {
7453 .eq => return sema.mod.constBool(sema.arena, src, false),
7454 .neq => return sema.mod.constBool(sema.arena, src, true),
7587 .eq => return Air.Inst.Ref.bool_false,
7588 .neq => return Air.Inst.Ref.bool_true,
74557589 else => {},
74567590 }
74577591 if (zcmp == .lt) {
......@@ -7468,9 +7602,9 @@ fn cmpNumeric(
74687602 };
74697603 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
74707604 } else if (rhs_is_float) {
7471 dest_float_type = rhs.ty;
7605 dest_float_type = rhs_ty;
74727606 } else {
7473 const int_info = rhs.ty.intInfo(target);
7607 const int_info = rhs_ty.intInfo(target);
74747608 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
74757609 }
74767610
......@@ -7482,26 +7616,39 @@ fn cmpNumeric(
74827616 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
74837617 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
74847618 };
7485 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);
7486 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs.src);
7619 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs_src);
7620 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs_src);
74877621
7488 return block.addBinOp(src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
7622 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
74897623}
74907624
7491fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
7492 if (inst.value()) |val| {
7493 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });
7625fn wrapOptional(
7626 sema: *Sema,
7627 block: *Scope.Block,
7628 dest_type: Type,
7629 inst: Air.Inst.Ref,
7630 inst_src: LazySrcLoc,
7631) !Air.Inst.Ref {
7632 if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| {
7633 return sema.addConstant(dest_type, val);
74947634 }
74957635
7496 try sema.requireRuntimeBlock(block, inst.src);
7497 return block.addUnOp(inst.src, dest_type, .wrap_optional, inst);
7636 try sema.requireRuntimeBlock(block, inst_src);
7637 return block.addTyOp(.wrap_optional, dest_type, inst);
74987638}
74997639
7500fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
7640fn wrapErrorUnion(
7641 sema: *Sema,
7642 block: *Scope.Block,
7643 dest_type: Type,
7644 inst: Air.Inst.Ref,
7645 inst_src: LazySrcLoc,
7646) !Air.Inst.Ref {
7647 const inst_ty = sema.typeOf(inst);
75017648 const err_union = dest_type.castTag(.error_union).?;
7502 if (inst.value()) |val| {
7503 if (inst.ty.zigTypeTag() != .ErrorSet) {
7504 _ = try sema.coerce(block, err_union.data.payload, inst, inst.src);
7649 if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| {
7650 if (inst_ty.zigTypeTag() != .ErrorSet) {
7651 _ = try sema.coerce(block, err_union.data.payload, inst, inst_src);
75057652 } else switch (err_union.data.error_set.tag()) {
75067653 .anyerror => {},
75077654 .error_set_single => {
......@@ -7510,9 +7657,9 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
75107657 if (!mem.eql(u8, expected_name, n)) {
75117658 return sema.mod.fail(
75127659 &block.base,
7513 inst.src,
7660 inst_src,
75147661 "expected type '{}', found type '{}'",
7515 .{ err_union.data.error_set, inst.ty },
7662 .{ err_union.data.error_set, inst_ty },
75167663 );
75177664 }
75187665 },
......@@ -7528,9 +7675,9 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
75287675 if (!found) {
75297676 return sema.mod.fail(
75307677 &block.base,
7531 inst.src,
7678 inst_src,
75327679 "expected type '{}', found type '{}'",
7533 .{ err_union.data.error_set, inst.ty },
7680 .{ err_union.data.error_set, inst_ty },
75347681 );
75357682 }
75367683 },
......@@ -7540,109 +7687,113 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
75407687 if (!map.contains(expected_name)) {
75417688 return sema.mod.fail(
75427689 &block.base,
7543 inst.src,
7690 inst_src,
75447691 "expected type '{}', found type '{}'",
7545 .{ err_union.data.error_set, inst.ty },
7692 .{ err_union.data.error_set, inst_ty },
75467693 );
75477694 }
75487695 },
75497696 else => unreachable,
75507697 }
75517698
7552 return sema.mod.constInst(sema.arena, inst.src, .{
7553 .ty = dest_type,
7554 // creating a SubValue for the error_union payload
7555 .val = try Value.Tag.error_union.create(sema.arena, val),
7556 });
7699 // Create a SubValue for the error_union payload.
7700 return sema.addConstant(dest_type, try Value.Tag.error_union.create(sema.arena, val));
75577701 }
75587702
7559 try sema.requireRuntimeBlock(block, inst.src);
7703 try sema.requireRuntimeBlock(block, inst_src);
75607704
75617705 // we are coercing from E to E!T
7562 if (inst.ty.zigTypeTag() == .ErrorSet) {
7563 var coerced = try sema.coerce(block, err_union.data.error_set, inst, inst.src);
7564 return block.addUnOp(inst.src, dest_type, .wrap_errunion_err, coerced);
7706 if (inst_ty.zigTypeTag() == .ErrorSet) {
7707 var coerced = try sema.coerce(block, err_union.data.error_set, inst, inst_src);
7708 return block.addTyOp(.wrap_errunion_err, dest_type, coerced);
75657709 } else {
7566 var coerced = try sema.coerce(block, err_union.data.payload, inst, inst.src);
7567 return block.addUnOp(inst.src, dest_type, .wrap_errunion_payload, coerced);
7710 var coerced = try sema.coerce(block, err_union.data.payload, inst, inst_src);
7711 return block.addTyOp(.wrap_errunion_payload, dest_type, coerced);
75687712 }
75697713}
75707714
7571fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructions: []*Inst) !Type {
7715fn resolvePeerTypes(
7716 sema: *Sema,
7717 block: *Scope.Block,
7718 src: LazySrcLoc,
7719 instructions: []Air.Inst.Ref,
7720) !Type {
75727721 if (instructions.len == 0)
75737722 return Type.initTag(.noreturn);
75747723
75757724 if (instructions.len == 1)
7576 return instructions[0].ty;
7725 return sema.typeOf(instructions[0]);
75777726
75787727 const target = sema.mod.getTarget();
75797728
75807729 var chosen = instructions[0];
75817730 for (instructions[1..]) |candidate| {
7582 if (candidate.ty.eql(chosen.ty))
7731 const candidate_ty = sema.typeOf(candidate);
7732 const chosen_ty = sema.typeOf(chosen);
7733 if (candidate_ty.eql(chosen_ty))
75837734 continue;
7584 if (candidate.ty.zigTypeTag() == .NoReturn)
7735 if (candidate_ty.zigTypeTag() == .NoReturn)
75857736 continue;
7586 if (chosen.ty.zigTypeTag() == .NoReturn) {
7737 if (chosen_ty.zigTypeTag() == .NoReturn) {
75877738 chosen = candidate;
75887739 continue;
75897740 }
7590 if (candidate.ty.zigTypeTag() == .Undefined)
7741 if (candidate_ty.zigTypeTag() == .Undefined)
75917742 continue;
7592 if (chosen.ty.zigTypeTag() == .Undefined) {
7743 if (chosen_ty.zigTypeTag() == .Undefined) {
75937744 chosen = candidate;
75947745 continue;
75957746 }
7596 if (chosen.ty.isInt() and
7597 candidate.ty.isInt() and
7598 chosen.ty.isSignedInt() == candidate.ty.isSignedInt())
7747 if (chosen_ty.isInt() and
7748 candidate_ty.isInt() and
7749 chosen_ty.isSignedInt() == candidate_ty.isSignedInt())
75997750 {
7600 if (chosen.ty.intInfo(target).bits < candidate.ty.intInfo(target).bits) {
7751 if (chosen_ty.intInfo(target).bits < candidate_ty.intInfo(target).bits) {
76017752 chosen = candidate;
76027753 }
76037754 continue;
76047755 }
7605 if (chosen.ty.isFloat() and candidate.ty.isFloat()) {
7606 if (chosen.ty.floatBits(target) < candidate.ty.floatBits(target)) {
7756 if (chosen_ty.isFloat() and candidate_ty.isFloat()) {
7757 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
76077758 chosen = candidate;
76087759 }
76097760 continue;
76107761 }
76117762
7612 if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) {
7763 if (chosen_ty.zigTypeTag() == .ComptimeInt and candidate_ty.isInt()) {
76137764 chosen = candidate;
76147765 continue;
76157766 }
76167767
7617 if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) {
7768 if (chosen_ty.isInt() and candidate_ty.zigTypeTag() == .ComptimeInt) {
76187769 continue;
76197770 }
76207771
7621 if (chosen.ty.zigTypeTag() == .ComptimeFloat and candidate.ty.isFloat()) {
7772 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {
76227773 chosen = candidate;
76237774 continue;
76247775 }
76257776
7626 if (chosen.ty.isFloat() and candidate.ty.zigTypeTag() == .ComptimeFloat) {
7777 if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
76277778 continue;
76287779 }
76297780
7630 if (chosen.ty.zigTypeTag() == .Enum and candidate.ty.zigTypeTag() == .EnumLiteral) {
7781 if (chosen_ty.zigTypeTag() == .Enum and candidate_ty.zigTypeTag() == .EnumLiteral) {
76317782 continue;
76327783 }
7633 if (chosen.ty.zigTypeTag() == .EnumLiteral and candidate.ty.zigTypeTag() == .Enum) {
7784 if (chosen_ty.zigTypeTag() == .EnumLiteral and candidate_ty.zigTypeTag() == .Enum) {
76347785 chosen = candidate;
76357786 continue;
76367787 }
76377788
76387789 // TODO error notes pointing out each type
7639 return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty });
7790 return sema.mod.fail(&block.base, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty });
76407791 }
76417792
7642 return chosen.ty;
7793 return sema.typeOf(chosen);
76437794}
76447795
7645fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) InnerError!Type {
7796fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {
76467797 switch (ty.tag()) {
76477798 .@"struct" => {
76487799 const struct_obj = ty.castTag(.@"struct").?.data;
......@@ -7694,7 +7845,7 @@ fn resolveBuiltinTypeFields(
76947845 block: *Scope.Block,
76957846 src: LazySrcLoc,
76967847 name: []const u8,
7697) InnerError!Type {
7848) CompileError!Type {
76987849 const resolved_ty = try sema.getBuiltinType(block, src, name);
76997850 return sema.resolveTypeFields(block, src, resolved_ty);
77007851}
......@@ -7704,7 +7855,7 @@ fn getBuiltin(
77047855 block: *Scope.Block,
77057856 src: LazySrcLoc,
77067857 name: []const u8,
7707) InnerError!*ir.Inst {
7858) CompileError!Air.Inst.Ref {
77087859 const mod = sema.mod;
77097860 const std_pkg = mod.root_pkg.table.get("std").?;
77107861 const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
......@@ -7715,7 +7866,7 @@ fn getBuiltin(
77157866 "builtin",
77167867 );
77177868 const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);
7718 const builtin_ty = try sema.resolveAirAsType(block, src, builtin_inst);
7869 const builtin_ty = try sema.analyzeAsType(block, src, builtin_inst);
77197870 const opt_ty_inst = try sema.analyzeNamespaceLookup(
77207871 block,
77217872 src,
......@@ -7730,9 +7881,9 @@ fn getBuiltinType(
77307881 block: *Scope.Block,
77317882 src: LazySrcLoc,
77327883 name: []const u8,
7733) InnerError!Type {
7884) CompileError!Type {
77347885 const ty_inst = try sema.getBuiltin(block, src, name);
7735 return sema.resolveAirAsType(block, src, ty_inst);
7886 return sema.analyzeAsType(block, src, ty_inst);
77367887}
77377888
77387889/// There is another implementation of this in `Type.onePossibleValue`. This one
......@@ -7744,7 +7895,7 @@ fn typeHasOnePossibleValue(
77447895 block: *Scope.Block,
77457896 src: LazySrcLoc,
77467897 starting_type: Type,
7747) InnerError!?Value {
7898) CompileError!?Value {
77487899 var ty = starting_type;
77497900 while (true) switch (ty.tag()) {
77507901 .f16,
......@@ -7882,7 +8033,7 @@ fn typeHasOnePossibleValue(
78828033 };
78838034}
78848035
7885fn getAstTree(sema: *Sema, block: *Scope.Block) InnerError!*const std.zig.ast.Tree {
8036fn getAstTree(sema: *Sema, block: *Scope.Block) CompileError!*const std.zig.ast.Tree {
78868037 return block.src_decl.namespace.file_scope.getTree(sema.gpa) catch |err| {
78878038 log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
78888039 return error.AnalysisFail;
......@@ -7931,3 +8082,146 @@ fn enumFieldSrcLoc(
79318082 }
79328083 } else unreachable;
79338084}
8085
8086/// Returns the type of the AIR instruction.
8087fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
8088 return sema.getTmpAir().typeOf(inst);
8089}
8090
8091fn getTmpAir(sema: Sema) Air {
8092 return .{
8093 .instructions = sema.air_instructions.slice(),
8094 .extra = sema.air_extra.items,
8095 .values = sema.air_values.items,
8096 .variables = sema.air_variables.items,
8097 };
8098}
8099
8100pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
8101 switch (ty.tag()) {
8102 .u8 => return .u8_type,
8103 .i8 => return .i8_type,
8104 .u16 => return .u16_type,
8105 .i16 => return .i16_type,
8106 .u32 => return .u32_type,
8107 .i32 => return .i32_type,
8108 .u64 => return .u64_type,
8109 .i64 => return .i64_type,
8110 .u128 => return .u128_type,
8111 .i128 => return .i128_type,
8112 .usize => return .usize_type,
8113 .isize => return .isize_type,
8114 .c_short => return .c_short_type,
8115 .c_ushort => return .c_ushort_type,
8116 .c_int => return .c_int_type,
8117 .c_uint => return .c_uint_type,
8118 .c_long => return .c_long_type,
8119 .c_ulong => return .c_ulong_type,
8120 .c_longlong => return .c_longlong_type,
8121 .c_ulonglong => return .c_ulonglong_type,
8122 .c_longdouble => return .c_longdouble_type,
8123 .f16 => return .f16_type,
8124 .f32 => return .f32_type,
8125 .f64 => return .f64_type,
8126 .f128 => return .f128_type,
8127 .c_void => return .c_void_type,
8128 .bool => return .bool_type,
8129 .void => return .void_type,
8130 .type => return .type_type,
8131 .anyerror => return .anyerror_type,
8132 .comptime_int => return .comptime_int_type,
8133 .comptime_float => return .comptime_float_type,
8134 .noreturn => return .noreturn_type,
8135 .@"anyframe" => return .anyframe_type,
8136 .@"null" => return .null_type,
8137 .@"undefined" => return .undefined_type,
8138 .enum_literal => return .enum_literal_type,
8139 .atomic_ordering => return .atomic_ordering_type,
8140 .atomic_rmw_op => return .atomic_rmw_op_type,
8141 .calling_convention => return .calling_convention_type,
8142 .float_mode => return .float_mode_type,
8143 .reduce_op => return .reduce_op_type,
8144 .call_options => return .call_options_type,
8145 .export_options => return .export_options_type,
8146 .extern_options => return .extern_options_type,
8147 .manyptr_u8 => return .manyptr_u8_type,
8148 .manyptr_const_u8 => return .manyptr_const_u8_type,
8149 .fn_noreturn_no_args => return .fn_noreturn_no_args_type,
8150 .fn_void_no_args => return .fn_void_no_args_type,
8151 .fn_naked_noreturn_no_args => return .fn_naked_noreturn_no_args_type,
8152 .fn_ccc_void_no_args => return .fn_ccc_void_no_args_type,
8153 .single_const_pointer_to_comptime_int => return .single_const_pointer_to_comptime_int_type,
8154 .const_slice_u8 => return .const_slice_u8_type,
8155 else => {},
8156 }
8157 try sema.air_instructions.append(sema.gpa, .{
8158 .tag = .const_ty,
8159 .data = .{ .ty = ty },
8160 });
8161 return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
8162}
8163
8164fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
8165 return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
8166}
8167
8168fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
8169 return sema.addConstant(ty, Value.initTag(.undef));
8170}
8171
8172fn addConstant(sema: *Sema, ty: Type, val: Value) CompileError!Air.Inst.Ref {
8173 const gpa = sema.gpa;
8174 const ty_inst = try sema.addType(ty);
8175 try sema.air_values.append(gpa, val);
8176 try sema.air_instructions.append(gpa, .{
8177 .tag = .constant,
8178 .data = .{ .ty_pl = .{
8179 .ty = ty_inst,
8180 .payload = @intCast(u32, sema.air_values.items.len - 1),
8181 } },
8182 });
8183 return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
8184}
8185
8186pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
8187 const fields = std.meta.fields(@TypeOf(extra));
8188 try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len);
8189 return addExtraAssumeCapacity(sema, extra);
8190}
8191
8192pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
8193 const fields = std.meta.fields(@TypeOf(extra));
8194 const result = @intCast(u32, sema.air_extra.items.len);
8195 inline for (fields) |field| {
8196 sema.air_extra.appendAssumeCapacity(switch (field.field_type) {
8197 u32 => @field(extra, field.name),
8198 Air.Inst.Ref => @enumToInt(@field(extra, field.name)),
8199 i32 => @bitCast(u32, @field(extra, field.name)),
8200 else => @compileError("bad field type"),
8201 });
8202 }
8203 return result;
8204}
8205
8206fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
8207 const coerced = @bitCast([]const u32, refs);
8208 sema.air_extra.appendSliceAssumeCapacity(coerced);
8209}
8210
8211fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
8212 const air_datas = sema.air_instructions.items(.data);
8213 const air_tags = sema.air_instructions.items(.tag);
8214 switch (air_tags[inst_index]) {
8215 .br => return air_datas[inst_index].br.block_inst,
8216 else => return null,
8217 }
8218}
8219
8220fn isComptimeKnown(
8221 sema: *Sema,
8222 block: *Scope.Block,
8223 src: LazySrcLoc,
8224 inst: Air.Inst.Ref,
8225) !bool {
8226 return (try sema.resolvePossiblyUndefinedValue(block, src, inst)) != null;
8227}
src/Zir.zig+28-38
......@@ -22,7 +22,6 @@ const Zir = @This();
2222const Type = @import("type.zig").Type;
2323const Value = @import("value.zig").Value;
2424const TypedValue = @import("TypedValue.zig");
25const ir = @import("air.zig");
2625const Module = @import("Module.zig");
2726const LazySrcLoc = Module.LazySrcLoc;
2827
......@@ -138,11 +137,17 @@ pub fn renderAsTextToFile(
138137 const imports_index = scope_file.zir.extra[@enumToInt(ExtraIndex.imports)];
139138 if (imports_index != 0) {
140139 try fs_file.writeAll("Imports:\n");
141 const imports_len = scope_file.zir.extra[imports_index];
142 for (scope_file.zir.extra[imports_index + 1 ..][0..imports_len]) |import_inst| {
143 const inst_data = writer.code.instructions.items(.data)[import_inst].str_tok;
144 const src = inst_data.src();
145 const import_path = inst_data.get(writer.code);
140
141 const extra = scope_file.zir.extraData(Inst.Imports, imports_index);
142 var import_i: u32 = 0;
143 var extra_index = extra.end;
144
145 while (import_i < extra.data.imports_len) : (import_i += 1) {
146 const item = scope_file.zir.extraData(Inst.Imports.Item, extra_index);
147 extra_index = item.end;
148
149 const src: LazySrcLoc = .{ .token_abs = item.data.token };
150 const import_path = scope_file.zir.nullTerminatedString(item.data.name);
146151 try fs_file.writer().print(" @import(\"{}\") ", .{
147152 std.zig.fmtEscapes(import_path),
148153 });
......@@ -208,7 +213,7 @@ pub const Inst = struct {
208213 as_node,
209214 /// Bitwise AND. `&`
210215 bit_and,
211 /// Bitcast a value to a different type.
216 /// Reinterpret the memory representation of a value as a different type.
212217 /// Uses the pl_node field with payload `Bin`.
213218 bitcast,
214219 /// A typed result location pointer is bitcasted to a new result location pointer.
......@@ -231,15 +236,9 @@ pub const Inst = struct {
231236 /// Implements `suspend {...}`.
232237 /// Uses the `pl_node` union field. Payload is `Block`.
233238 suspend_block,
234 /// Boolean AND. See also `bit_and`.
235 /// Uses the `pl_node` union field. Payload is `Bin`.
236 bool_and,
237239 /// Boolean NOT. See also `bit_not`.
238240 /// Uses the `un_node` field.
239241 bool_not,
240 /// Boolean OR. See also `bit_or`.
241 /// Uses the `pl_node` union field. Payload is `Bin`.
242 bool_or,
243242 /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
244243 /// is a block, which is evaluated if `lhs` is `true`.
245244 /// Uses the `bool_br` union field.
......@@ -387,7 +386,7 @@ pub const Inst = struct {
387386 int,
388387 /// Arbitrary sized integer literal. Uses the `str` union field.
389388 int_big,
390 /// A float literal that fits in a f32. Uses the float union value.
389 /// A float literal that fits in a f64. Uses the float union value.
391390 float,
392391 /// A float literal that fits in a f128. Uses the `pl_node` union value.
393392 /// Payload is `Float128`.
......@@ -993,8 +992,6 @@ pub const Inst = struct {
993992 .bool_br_and,
994993 .bool_br_or,
995994 .bool_not,
996 .bool_and,
997 .bool_or,
998995 .breakpoint,
999996 .fence,
1000997 .call,
......@@ -1243,9 +1240,7 @@ pub const Inst = struct {
12431240 .block = .pl_node,
12441241 .block_inline = .pl_node,
12451242 .suspend_block = .pl_node,
1246 .bool_and = .pl_node,
12471243 .bool_not = .un_node,
1248 .bool_or = .pl_node,
12491244 .bool_br_and = .bool_br,
12501245 .bool_br_or = .bool_br,
12511246 .@"break" = .@"break",
......@@ -2063,16 +2058,7 @@ pub const Inst = struct {
20632058 /// Offset from Decl AST node index.
20642059 node: i32,
20652060 int: u64,
2066 float: struct {
2067 /// Offset from Decl AST node index.
2068 /// `Tag` determines which kind of AST node this points to.
2069 src_node: i32,
2070 number: f32,
2071
2072 pub fn src(self: @This()) LazySrcLoc {
2073 return .{ .node_offset = self.src_node };
2074 }
2075 },
2061 float: f64,
20762062 array_type_sentinel: struct {
20772063 len: Ref,
20782064 /// index into extra, points to an `ArrayTypeSentinel`
......@@ -2190,7 +2176,8 @@ pub const Inst = struct {
21902176 /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
21912177 pub const Asm = struct {
21922178 src_node: i32,
2193 asm_source: Ref,
2179 // null-terminated string index
2180 asm_source: u32,
21942181 /// 1 bit for each outputs_len: whether it uses `-> T` or not.
21952182 /// 0b0 - operand is a pointer to where to store the output.
21962183 /// 0b1 - operand is a type; asm expression has the output as the result.
......@@ -2780,10 +2767,16 @@ pub const Inst = struct {
27802767 };
27812768 };
27822769
2783 /// Trailing: for each `imports_len` there is an instruction index
2784 /// to an import instruction.
2770 /// Trailing: for each `imports_len` there is an Item
27852771 pub const Imports = struct {
27862772 imports_len: Zir.Inst.Index,
2773
2774 pub const Item = struct {
2775 /// null terminated string index
2776 name: u32,
2777 /// points to the import name
2778 token: ast.TokenIndex,
2779 };
27872780 };
27882781};
27892782
......@@ -2970,8 +2963,6 @@ const Writer = struct {
29702963 .mulwrap,
29712964 .sub,
29722965 .subwrap,
2973 .bool_and,
2974 .bool_or,
29752966 .cmp_lt,
29762967 .cmp_lte,
29772968 .cmp_eq,
......@@ -3257,10 +3248,8 @@ const Writer = struct {
32573248 }
32583249
32593250 fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void {
3260 const inst_data = self.code.instructions.items(.data)[inst].float;
3261 const src = inst_data.src();
3262 try stream.print("{d}) ", .{inst_data.number});
3263 try self.writeSrc(stream, src);
3251 const number = self.code.instructions.items(.data)[inst].float;
3252 try stream.print("{d})", .{number});
32643253 }
32653254
32663255 fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void {
......@@ -3395,9 +3384,10 @@ const Writer = struct {
33953384 const inputs_len = @truncate(u5, extended.small >> 5);
33963385 const clobbers_len = @truncate(u5, extended.small >> 10);
33973386 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3387 const asm_source = self.code.nullTerminatedString(extra.data.asm_source);
33983388
33993389 try self.writeFlag(stream, "volatile, ", is_volatile);
3400 try self.writeInstRef(stream, extra.data.asm_source);
3390 try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)});
34013391 try stream.writeAll(", ");
34023392
34033393 var extra_i: usize = extra.end;
src/air.zig deleted-1185
......@@ -1,1185 +0,0 @@
1const std = @import("std");
2const Value = @import("value.zig").Value;
3const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");
5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");
7const ast = std.zig.ast;
8
9/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
10/// of instructions that correspond to the ZIR text format.
11/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
12/// so are the `Value` and `Type`. The value of a constant must be copied into
13/// a memory location for the value to survive after a const instruction.
14pub const Inst = struct {
15 tag: Tag,
16 /// Each bit represents the index of an `Inst` parameter in the `args` field.
17 /// If a bit is set, it marks the end of the lifetime of the corresponding
18 /// instruction parameter. For example, 0b101 means that the first and
19 /// third `Inst` parameters' lifetimes end after this instruction, and will
20 /// not have any more following references.
21 /// The most significant bit being set means that the instruction itself is
22 /// never referenced, in other words its lifetime ends as soon as it finishes.
23 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
24 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
25 /// lifetimes of operands are encoded elsewhere.
26 deaths: DeathsInt = undefined,
27 ty: Type,
28 src: Module.LazySrcLoc,
29
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
35 pub fn isUnused(self: Inst) bool {
36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
37 }
38
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths >> index) != 0;
42 }
43
44 pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
45 assert(index < deaths_bits);
46 self.deaths &= ~(@as(DeathsInt, 1) << index);
47 }
48
49 pub fn specialOperandDeaths(self: Inst) bool {
50 return (self.deaths & (1 << deaths_bits)) != 0;
51 }
52
53 pub const Tag = enum {
54 add,
55 addwrap,
56 alloc,
57 arg,
58 assembly,
59 bit_and,
60 bitcast,
61 bit_or,
62 block,
63 br,
64 /// Same as `br` except the operand is a list of instructions to be treated as
65 /// a flat block; that is there is only 1 break instruction from the block, and
66 /// it is implied to be after the last instruction, and the last instruction is
67 /// the break operand.
68 /// This instruction exists for late-stage semantic analysis patch ups, to
69 /// replace one br operand with multiple instructions, without moving anything else around.
70 br_block_flat,
71 breakpoint,
72 br_void,
73 call,
74 cmp_lt,
75 cmp_lte,
76 cmp_eq,
77 cmp_gte,
78 cmp_gt,
79 cmp_neq,
80 condbr,
81 constant,
82 dbg_stmt,
83 /// ?T => bool
84 is_null,
85 /// ?T => bool (inverted logic)
86 is_non_null,
87 /// *?T => bool
88 is_null_ptr,
89 /// *?T => bool (inverted logic)
90 is_non_null_ptr,
91 /// E!T => bool
92 is_err,
93 /// E!T => bool (inverted logic)
94 is_non_err,
95 /// *E!T => bool
96 is_err_ptr,
97 /// *E!T => bool (inverted logic)
98 is_non_err_ptr,
99 bool_and,
100 bool_or,
101 /// Read a value from a pointer.
102 load,
103 /// A labeled block of code that loops forever. At the end of the body it is implied
104 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
105 loop,
106 ptrtoint,
107 ref,
108 ret,
109 retvoid,
110 varptr,
111 /// Write a value to a pointer. LHS is pointer, RHS is value.
112 store,
113 sub,
114 subwrap,
115 unreach,
116 mul,
117 mulwrap,
118 div,
119 not,
120 floatcast,
121 intcast,
122 /// ?T => T
123 optional_payload,
124 /// *?T => *T
125 optional_payload_ptr,
126 wrap_optional,
127 /// E!T -> T
128 unwrap_errunion_payload,
129 /// E!T -> E
130 unwrap_errunion_err,
131 /// *(E!T) -> *T
132 unwrap_errunion_payload_ptr,
133 /// *(E!T) -> E
134 unwrap_errunion_err_ptr,
135 /// wrap from T to E!T
136 wrap_errunion_payload,
137 /// wrap from E to E!T
138 wrap_errunion_err,
139 xor,
140 switchbr,
141 /// Given a pointer to a struct and a field index, returns a pointer to the field.
142 struct_field_ptr,
143
144 pub fn Type(tag: Tag) type {
145 return switch (tag) {
146 .alloc,
147 .retvoid,
148 .unreach,
149 .breakpoint,
150 => NoOp,
151
152 .ref,
153 .ret,
154 .bitcast,
155 .not,
156 .is_non_null,
157 .is_non_null_ptr,
158 .is_null,
159 .is_null_ptr,
160 .is_err,
161 .is_non_err,
162 .is_err_ptr,
163 .is_non_err_ptr,
164 .ptrtoint,
165 .floatcast,
166 .intcast,
167 .load,
168 .optional_payload,
169 .optional_payload_ptr,
170 .wrap_optional,
171 .unwrap_errunion_payload,
172 .unwrap_errunion_err,
173 .unwrap_errunion_payload_ptr,
174 .unwrap_errunion_err_ptr,
175 .wrap_errunion_payload,
176 .wrap_errunion_err,
177 => UnOp,
178
179 .add,
180 .addwrap,
181 .sub,
182 .subwrap,
183 .mul,
184 .mulwrap,
185 .div,
186 .cmp_lt,
187 .cmp_lte,
188 .cmp_eq,
189 .cmp_gte,
190 .cmp_gt,
191 .cmp_neq,
192 .store,
193 .bool_and,
194 .bool_or,
195 .bit_and,
196 .bit_or,
197 .xor,
198 => BinOp,
199
200 .arg => Arg,
201 .assembly => Assembly,
202 .block => Block,
203 .br => Br,
204 .br_block_flat => BrBlockFlat,
205 .br_void => BrVoid,
206 .call => Call,
207 .condbr => CondBr,
208 .constant => Constant,
209 .loop => Loop,
210 .varptr => VarPtr,
211 .struct_field_ptr => StructFieldPtr,
212 .switchbr => SwitchBr,
213 .dbg_stmt => DbgStmt,
214 };
215 }
216
217 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
218 return switch (op) {
219 .lt => .cmp_lt,
220 .lte => .cmp_lte,
221 .eq => .cmp_eq,
222 .gte => .cmp_gte,
223 .gt => .cmp_gt,
224 .neq => .cmp_neq,
225 };
226 }
227 };
228
229 /// Prefer `castTag` to this.
230 pub fn cast(base: *Inst, comptime T: type) ?*T {
231 if (@hasField(T, "base_tag")) {
232 return base.castTag(T.base_tag);
233 }
234 inline for (@typeInfo(Tag).Enum.fields) |field| {
235 const tag = @intToEnum(Tag, field.value);
236 if (base.tag == tag) {
237 if (T == tag.Type()) {
238 return @fieldParentPtr(T, "base", base);
239 }
240 return null;
241 }
242 }
243 unreachable;
244 }
245
246 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
247 if (base.tag == tag) {
248 return @fieldParentPtr(tag.Type(), "base", base);
249 }
250 return null;
251 }
252
253 pub fn Args(comptime T: type) type {
254 return std.meta.fieldInfo(T, .args).field_type;
255 }
256
257 /// Returns `null` if runtime-known.
258 /// Should be called by codegen, not by Sema. Sema functions should call
259 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
260 /// TODO audit Sema code for violations to the above guidance.
261 pub fn value(base: *Inst) ?Value {
262 if (base.ty.onePossibleValue()) |opv| return opv;
263
264 const inst = base.castTag(.constant) orelse return null;
265 return inst.val;
266 }
267
268 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
269 return switch (base.tag) {
270 .cmp_lt => .lt,
271 .cmp_lte => .lte,
272 .cmp_eq => .eq,
273 .cmp_gte => .gte,
274 .cmp_gt => .gt,
275 .cmp_neq => .neq,
276 else => null,
277 };
278 }
279
280 pub fn operandCount(base: *Inst) usize {
281 inline for (@typeInfo(Tag).Enum.fields) |field| {
282 const tag = @intToEnum(Tag, field.value);
283 if (tag == base.tag) {
284 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
285 }
286 }
287 unreachable;
288 }
289
290 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
291 inline for (@typeInfo(Tag).Enum.fields) |field| {
292 const tag = @intToEnum(Tag, field.value);
293 if (tag == base.tag) {
294 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
295 }
296 }
297 unreachable;
298 }
299
300 pub fn breakBlock(base: *Inst) ?*Block {
301 return switch (base.tag) {
302 .br => base.castTag(.br).?.block,
303 .br_void => base.castTag(.br_void).?.block,
304 .br_block_flat => base.castTag(.br_block_flat).?.block,
305 else => null,
306 };
307 }
308
309 pub const NoOp = struct {
310 base: Inst,
311
312 pub fn operandCount(self: *const NoOp) usize {
313 _ = self;
314 return 0;
315 }
316 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
317 _ = self;
318 _ = index;
319 return null;
320 }
321 };
322
323 pub const UnOp = struct {
324 base: Inst,
325 operand: *Inst,
326
327 pub fn operandCount(self: *const UnOp) usize {
328 _ = self;
329 return 1;
330 }
331 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
332 if (index == 0)
333 return self.operand;
334 return null;
335 }
336 };
337
338 pub const BinOp = struct {
339 base: Inst,
340 lhs: *Inst,
341 rhs: *Inst,
342
343 pub fn operandCount(self: *const BinOp) usize {
344 _ = self;
345 return 2;
346 }
347 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
348 var i = index;
349
350 if (i < 1)
351 return self.lhs;
352 i -= 1;
353
354 if (i < 1)
355 return self.rhs;
356 i -= 1;
357
358 return null;
359 }
360 };
361
362 pub const Arg = struct {
363 pub const base_tag = Tag.arg;
364
365 base: Inst,
366 /// This exists to be emitted into debug info.
367 name: [*:0]const u8,
368
369 pub fn operandCount(self: *const Arg) usize {
370 _ = self;
371 return 0;
372 }
373 pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
374 _ = self;
375 _ = index;
376 return null;
377 }
378 };
379
380 pub const Assembly = struct {
381 pub const base_tag = Tag.assembly;
382
383 base: Inst,
384 asm_source: []const u8,
385 is_volatile: bool,
386 output_constraint: ?[]const u8,
387 inputs: []const []const u8,
388 clobbers: []const []const u8,
389 args: []const *Inst,
390
391 pub fn operandCount(self: *const Assembly) usize {
392 return self.args.len;
393 }
394 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
395 if (index < self.args.len)
396 return self.args[index];
397 return null;
398 }
399 };
400
401 pub const Block = struct {
402 pub const base_tag = Tag.block;
403
404 base: Inst,
405 body: Body,
406
407 pub fn operandCount(self: *const Block) usize {
408 _ = self;
409 return 0;
410 }
411 pub fn getOperand(self: *const Block, index: usize) ?*Inst {
412 _ = self;
413 _ = index;
414 return null;
415 }
416 };
417
418 pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br));
419 pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br));
420 comptime {
421 assert(@offsetOf(BrBlockFlat, "base") == @offsetOf(Br, "base"));
422 }
423
424 pub const BrBlockFlat = struct {
425 pub const base_tag = Tag.br_block_flat;
426
427 base: Inst,
428 block: *Block,
429 body: Body,
430
431 pub fn operandCount(self: *const BrBlockFlat) usize {
432 _ = self;
433 return 0;
434 }
435 pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst {
436 _ = self;
437 _ = index;
438 return null;
439 }
440 };
441
442 pub const Br = struct {
443 pub const base_tag = Tag.br;
444
445 base: Inst,
446 block: *Block,
447 operand: *Inst,
448
449 pub fn operandCount(self: *const Br) usize {
450 _ = self;
451 return 1;
452 }
453 pub fn getOperand(self: *const Br, index: usize) ?*Inst {
454 _ = self;
455 if (index == 0)
456 return self.operand;
457 return null;
458 }
459 };
460
461 pub const BrVoid = struct {
462 pub const base_tag = Tag.br_void;
463
464 base: Inst,
465 block: *Block,
466
467 pub fn operandCount(self: *const BrVoid) usize {
468 _ = self;
469 return 0;
470 }
471 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
472 _ = self;
473 _ = index;
474 return null;
475 }
476 };
477
478 pub const Call = struct {
479 pub const base_tag = Tag.call;
480
481 base: Inst,
482 func: *Inst,
483 args: []const *Inst,
484
485 pub fn operandCount(self: *const Call) usize {
486 return self.args.len + 1;
487 }
488 pub fn getOperand(self: *const Call, index: usize) ?*Inst {
489 var i = index;
490
491 if (i < 1)
492 return self.func;
493 i -= 1;
494
495 if (i < self.args.len)
496 return self.args[i];
497 i -= self.args.len;
498
499 return null;
500 }
501 };
502
503 pub const CondBr = struct {
504 pub const base_tag = Tag.condbr;
505
506 base: Inst,
507 condition: *Inst,
508 then_body: Body,
509 else_body: Body,
510 /// Set of instructions whose lifetimes end at the start of one of the branches.
511 /// The `then` branch is first: `deaths[0..then_death_count]`.
512 /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
513 deaths: [*]*Inst = undefined,
514 then_death_count: u32 = 0,
515 else_death_count: u32 = 0,
516
517 pub fn operandCount(self: *const CondBr) usize {
518 _ = self;
519 return 1;
520 }
521 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
522 var i = index;
523
524 if (i < 1)
525 return self.condition;
526 i -= 1;
527
528 return null;
529 }
530 pub fn thenDeaths(self: *const CondBr) []*Inst {
531 return self.deaths[0..self.then_death_count];
532 }
533 pub fn elseDeaths(self: *const CondBr) []*Inst {
534 return (self.deaths + self.then_death_count)[0..self.else_death_count];
535 }
536 };
537
538 pub const Constant = struct {
539 pub const base_tag = Tag.constant;
540
541 base: Inst,
542 val: Value,
543
544 pub fn operandCount(self: *const Constant) usize {
545 _ = self;
546 return 0;
547 }
548 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
549 _ = self;
550 _ = index;
551 return null;
552 }
553 };
554
555 pub const Loop = struct {
556 pub const base_tag = Tag.loop;
557
558 base: Inst,
559 body: Body,
560
561 pub fn operandCount(self: *const Loop) usize {
562 _ = self;
563 return 0;
564 }
565 pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
566 _ = self;
567 _ = index;
568 return null;
569 }
570 };
571
572 pub const VarPtr = struct {
573 pub const base_tag = Tag.varptr;
574
575 base: Inst,
576 variable: *Module.Var,
577
578 pub fn operandCount(self: *const VarPtr) usize {
579 _ = self;
580 return 0;
581 }
582 pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
583 _ = self;
584 _ = index;
585 return null;
586 }
587 };
588
589 pub const StructFieldPtr = struct {
590 pub const base_tag = Tag.struct_field_ptr;
591
592 base: Inst,
593 struct_ptr: *Inst,
594 field_index: usize,
595
596 pub fn operandCount(self: *const StructFieldPtr) usize {
597 _ = self;
598 return 1;
599 }
600 pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst {
601 _ = self;
602 _ = index;
603 var i = index;
604
605 if (i < 1)
606 return self.struct_ptr;
607 i -= 1;
608
609 return null;
610 }
611 };
612
613 pub const SwitchBr = struct {
614 pub const base_tag = Tag.switchbr;
615
616 base: Inst,
617 target: *Inst,
618 cases: []Case,
619 /// Set of instructions whose lifetimes end at the start of one of the cases.
620 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
621 deaths: [*]*Inst = undefined,
622 else_index: u32 = 0,
623 else_deaths: u32 = 0,
624 else_body: Body,
625
626 pub const Case = struct {
627 item: Value,
628 body: Body,
629 index: u32 = 0,
630 deaths: u32 = 0,
631 };
632
633 pub fn operandCount(self: *const SwitchBr) usize {
634 _ = self;
635 return 1;
636 }
637 pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst {
638 var i = index;
639
640 if (i < 1)
641 return self.target;
642 i -= 1;
643
644 return null;
645 }
646 pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst {
647 const case = self.cases[case_index];
648 return (self.deaths + case.index)[0..case.deaths];
649 }
650 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
651 return (self.deaths + self.else_index)[0..self.else_deaths];
652 }
653 };
654
655 pub const DbgStmt = struct {
656 pub const base_tag = Tag.dbg_stmt;
657
658 base: Inst,
659 line: u32,
660 column: u32,
661
662 pub fn operandCount(self: *const DbgStmt) usize {
663 _ = self;
664 return 0;
665 }
666 pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst {
667 _ = self;
668 _ = index;
669 return null;
670 }
671 };
672};
673
674pub const Body = struct {
675 instructions: []*Inst,
676};
677
678/// For debugging purposes, prints a function representation to stderr.
679pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
680 const allocator = old_module.gpa;
681 var ctx: DumpAir = .{
682 .allocator = allocator,
683 .arena = std.heap.ArenaAllocator.init(allocator),
684 .old_module = &old_module,
685 .module_fn = module_fn,
686 .indent = 2,
687 .inst_table = DumpAir.InstTable.init(allocator),
688 .partial_inst_table = DumpAir.InstTable.init(allocator),
689 .const_table = DumpAir.InstTable.init(allocator),
690 };
691 defer ctx.inst_table.deinit();
692 defer ctx.partial_inst_table.deinit();
693 defer ctx.const_table.deinit();
694 defer ctx.arena.deinit();
695
696 switch (module_fn.state) {
697 .queued => std.debug.print("(queued)", .{}),
698 .inline_only => std.debug.print("(inline_only)", .{}),
699 .in_progress => std.debug.print("(in_progress)", .{}),
700 .sema_failure => std.debug.print("(sema_failure)", .{}),
701 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
702 .success => {
703 const writer = std.io.getStdErr().writer();
704 ctx.dump(module_fn.body, writer) catch @panic("failed to dump AIR");
705 },
706 }
707}
708
709const DumpAir = struct {
710 allocator: *std.mem.Allocator,
711 arena: std.heap.ArenaAllocator,
712 old_module: *const Module,
713 module_fn: *Module.Fn,
714 indent: usize,
715 inst_table: InstTable,
716 partial_inst_table: InstTable,
717 const_table: InstTable,
718 next_index: usize = 0,
719 next_partial_index: usize = 0,
720 next_const_index: usize = 0,
721
722 const InstTable = std.AutoArrayHashMap(*Inst, usize);
723
724 /// TODO: Improve this code to include a stack of Body and store the instructions
725 /// in there. Now we are putting all the instructions in a function local table,
726 /// however instructions that are in a Body can be thown away when the Body ends.
727 fn dump(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) !void {
728 // First pass to pre-populate the table so that we can show even invalid references.
729 // Must iterate the same order we iterate the second time.
730 // We also look for constants and put them in the const_table.
731 try dtz.fetchInstsAndResolveConsts(body);
732
733 std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
734
735 var it = dtz.const_table.iterator();
736 while (it.next()) |entry| {
737 const constant = entry.key_ptr.*.castTag(.constant).?;
738 try writer.print(" @{d}: {} = {};\n", .{
739 entry.value_ptr.*, constant.base.ty, constant.val,
740 });
741 }
742
743 return dtz.dumpBody(body, writer);
744 }
745
746 fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void {
747 for (body.instructions) |inst| {
748 try dtz.inst_table.put(inst, dtz.next_index);
749 dtz.next_index += 1;
750 switch (inst.tag) {
751 .alloc,
752 .retvoid,
753 .unreach,
754 .breakpoint,
755 .dbg_stmt,
756 .arg,
757 => {},
758
759 .ref,
760 .ret,
761 .bitcast,
762 .not,
763 .is_non_null,
764 .is_non_null_ptr,
765 .is_null,
766 .is_null_ptr,
767 .is_err,
768 .is_non_err,
769 .is_err_ptr,
770 .is_non_err_ptr,
771 .ptrtoint,
772 .floatcast,
773 .intcast,
774 .load,
775 .optional_payload,
776 .optional_payload_ptr,
777 .wrap_optional,
778 .wrap_errunion_payload,
779 .wrap_errunion_err,
780 .unwrap_errunion_payload,
781 .unwrap_errunion_err,
782 .unwrap_errunion_payload_ptr,
783 .unwrap_errunion_err_ptr,
784 => {
785 const un_op = inst.cast(Inst.UnOp).?;
786 try dtz.findConst(un_op.operand);
787 },
788
789 .add,
790 .addwrap,
791 .sub,
792 .subwrap,
793 .mul,
794 .mulwrap,
795 .div,
796 .cmp_lt,
797 .cmp_lte,
798 .cmp_eq,
799 .cmp_gte,
800 .cmp_gt,
801 .cmp_neq,
802 .store,
803 .bool_and,
804 .bool_or,
805 .bit_and,
806 .bit_or,
807 .xor,
808 => {
809 const bin_op = inst.cast(Inst.BinOp).?;
810 try dtz.findConst(bin_op.lhs);
811 try dtz.findConst(bin_op.rhs);
812 },
813
814 .br => {
815 const br = inst.castTag(.br).?;
816 try dtz.findConst(&br.block.base);
817 try dtz.findConst(br.operand);
818 },
819
820 .br_block_flat => {
821 const br_block_flat = inst.castTag(.br_block_flat).?;
822 try dtz.findConst(&br_block_flat.block.base);
823 try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
824 },
825
826 .br_void => {
827 const br_void = inst.castTag(.br_void).?;
828 try dtz.findConst(&br_void.block.base);
829 },
830
831 .block => {
832 const block = inst.castTag(.block).?;
833 try dtz.fetchInstsAndResolveConsts(block.body);
834 },
835
836 .condbr => {
837 const condbr = inst.castTag(.condbr).?;
838 try dtz.findConst(condbr.condition);
839 try dtz.fetchInstsAndResolveConsts(condbr.then_body);
840 try dtz.fetchInstsAndResolveConsts(condbr.else_body);
841 },
842 .switchbr => {
843 const switchbr = inst.castTag(.switchbr).?;
844 try dtz.findConst(switchbr.target);
845 try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
846 for (switchbr.cases) |case| {
847 try dtz.fetchInstsAndResolveConsts(case.body);
848 }
849 },
850
851 .loop => {
852 const loop = inst.castTag(.loop).?;
853 try dtz.fetchInstsAndResolveConsts(loop.body);
854 },
855 .call => {
856 const call = inst.castTag(.call).?;
857 try dtz.findConst(call.func);
858 for (call.args) |arg| {
859 try dtz.findConst(arg);
860 }
861 },
862 .struct_field_ptr => {
863 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
864 try dtz.findConst(struct_field_ptr.struct_ptr);
865 },
866
867 // TODO fill out this debug printing
868 .assembly,
869 .constant,
870 .varptr,
871 => {},
872 }
873 }
874 }
875
876 fn dumpBody(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
877 for (body.instructions) |inst| {
878 const my_index = dtz.next_partial_index;
879 try dtz.partial_inst_table.put(inst, my_index);
880 dtz.next_partial_index += 1;
881
882 try writer.writeByteNTimes(' ', dtz.indent);
883 try writer.print("%{d}: {} = {s}(", .{
884 my_index, inst.ty, @tagName(inst.tag),
885 });
886 switch (inst.tag) {
887 .alloc,
888 .retvoid,
889 .unreach,
890 .breakpoint,
891 .dbg_stmt,
892 => try writer.writeAll(")\n"),
893
894 .ref,
895 .ret,
896 .bitcast,
897 .not,
898 .is_non_null,
899 .is_non_null_ptr,
900 .is_null,
901 .is_null_ptr,
902 .is_err,
903 .is_err_ptr,
904 .is_non_err,
905 .is_non_err_ptr,
906 .ptrtoint,
907 .floatcast,
908 .intcast,
909 .load,
910 .optional_payload,
911 .optional_payload_ptr,
912 .wrap_optional,
913 .wrap_errunion_err,
914 .wrap_errunion_payload,
915 .unwrap_errunion_err,
916 .unwrap_errunion_payload,
917 .unwrap_errunion_payload_ptr,
918 .unwrap_errunion_err_ptr,
919 => {
920 const un_op = inst.cast(Inst.UnOp).?;
921 const kinky = try dtz.writeInst(writer, un_op.operand);
922 if (kinky != null) {
923 try writer.writeAll(") // Instruction does not dominate all uses!\n");
924 } else {
925 try writer.writeAll(")\n");
926 }
927 },
928
929 .add,
930 .addwrap,
931 .sub,
932 .subwrap,
933 .mul,
934 .mulwrap,
935 .div,
936 .cmp_lt,
937 .cmp_lte,
938 .cmp_eq,
939 .cmp_gte,
940 .cmp_gt,
941 .cmp_neq,
942 .store,
943 .bool_and,
944 .bool_or,
945 .bit_and,
946 .bit_or,
947 .xor,
948 => {
949 const bin_op = inst.cast(Inst.BinOp).?;
950
951 const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
952 try writer.writeAll(", ");
953 const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
954
955 if (lhs_kinky != null or rhs_kinky != null) {
956 try writer.writeAll(") // Instruction does not dominate all uses!");
957 if (lhs_kinky) |lhs| {
958 try writer.print(" %{d}", .{lhs});
959 }
960 if (rhs_kinky) |rhs| {
961 try writer.print(" %{d}", .{rhs});
962 }
963 try writer.writeAll("\n");
964 } else {
965 try writer.writeAll(")\n");
966 }
967 },
968
969 .arg => {
970 const arg = inst.castTag(.arg).?;
971 try writer.print("{s})\n", .{arg.name});
972 },
973
974 .br => {
975 const br = inst.castTag(.br).?;
976
977 const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
978 try writer.writeAll(", ");
979 const rhs_kinky = try dtz.writeInst(writer, br.operand);
980
981 if (lhs_kinky != null or rhs_kinky != null) {
982 try writer.writeAll(") // Instruction does not dominate all uses!");
983 if (lhs_kinky) |lhs| {
984 try writer.print(" %{d}", .{lhs});
985 }
986 if (rhs_kinky) |rhs| {
987 try writer.print(" %{d}", .{rhs});
988 }
989 try writer.writeAll("\n");
990 } else {
991 try writer.writeAll(")\n");
992 }
993 },
994
995 .br_block_flat => {
996 const br_block_flat = inst.castTag(.br_block_flat).?;
997 const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
998 if (block_kinky != null) {
999 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1000 } else {
1001 try writer.writeAll(", {\n");
1002 }
1003
1004 const old_indent = dtz.indent;
1005 dtz.indent += 2;
1006 try dtz.dumpBody(br_block_flat.body, writer);
1007 dtz.indent = old_indent;
1008
1009 try writer.writeByteNTimes(' ', dtz.indent);
1010 try writer.writeAll("})\n");
1011 },
1012
1013 .br_void => {
1014 const br_void = inst.castTag(.br_void).?;
1015 const kinky = try dtz.writeInst(writer, &br_void.block.base);
1016 if (kinky) |_| {
1017 try writer.writeAll(") // Instruction does not dominate all uses!\n");
1018 } else {
1019 try writer.writeAll(")\n");
1020 }
1021 },
1022
1023 .block => {
1024 const block = inst.castTag(.block).?;
1025
1026 try writer.writeAll("{\n");
1027
1028 const old_indent = dtz.indent;
1029 dtz.indent += 2;
1030 try dtz.dumpBody(block.body, writer);
1031 dtz.indent = old_indent;
1032
1033 try writer.writeByteNTimes(' ', dtz.indent);
1034 try writer.writeAll("})\n");
1035 },
1036
1037 .condbr => {
1038 const condbr = inst.castTag(.condbr).?;
1039
1040 const condition_kinky = try dtz.writeInst(writer, condbr.condition);
1041 if (condition_kinky != null) {
1042 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1043 } else {
1044 try writer.writeAll(", {\n");
1045 }
1046
1047 const old_indent = dtz.indent;
1048 dtz.indent += 2;
1049 try dtz.dumpBody(condbr.then_body, writer);
1050
1051 try writer.writeByteNTimes(' ', old_indent);
1052 try writer.writeAll("}, {\n");
1053
1054 try dtz.dumpBody(condbr.else_body, writer);
1055 dtz.indent = old_indent;
1056
1057 try writer.writeByteNTimes(' ', old_indent);
1058 try writer.writeAll("})\n");
1059 },
1060
1061 .switchbr => {
1062 const switchbr = inst.castTag(.switchbr).?;
1063
1064 const condition_kinky = try dtz.writeInst(writer, switchbr.target);
1065 if (condition_kinky != null) {
1066 try writer.writeAll(", { // Instruction does not dominate all uses!\n");
1067 } else {
1068 try writer.writeAll(", {\n");
1069 }
1070 const old_indent = dtz.indent;
1071
1072 if (switchbr.else_body.instructions.len != 0) {
1073 dtz.indent += 2;
1074 try dtz.dumpBody(switchbr.else_body, writer);
1075
1076 try writer.writeByteNTimes(' ', old_indent);
1077 try writer.writeAll("}, {\n");
1078 dtz.indent = old_indent;
1079 }
1080 for (switchbr.cases) |case| {
1081 dtz.indent += 2;
1082 try dtz.dumpBody(case.body, writer);
1083
1084 try writer.writeByteNTimes(' ', old_indent);
1085 try writer.writeAll("}, {\n");
1086 dtz.indent = old_indent;
1087 }
1088
1089 try writer.writeByteNTimes(' ', old_indent);
1090 try writer.writeAll("})\n");
1091 },
1092
1093 .loop => {
1094 const loop = inst.castTag(.loop).?;
1095
1096 try writer.writeAll("{\n");
1097
1098 const old_indent = dtz.indent;
1099 dtz.indent += 2;
1100 try dtz.dumpBody(loop.body, writer);
1101 dtz.indent = old_indent;
1102
1103 try writer.writeByteNTimes(' ', dtz.indent);
1104 try writer.writeAll("})\n");
1105 },
1106
1107 .call => {
1108 const call = inst.castTag(.call).?;
1109
1110 const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
1111 defer dtz.allocator.free(args_kinky);
1112 std.mem.set(?usize, args_kinky, null);
1113 var any_kinky_args = false;
1114
1115 const func_kinky = try dtz.writeInst(writer, call.func);
1116
1117 for (call.args) |arg, i| {
1118 try writer.writeAll(", ");
1119
1120 args_kinky[i] = try dtz.writeInst(writer, arg);
1121 any_kinky_args = any_kinky_args or args_kinky[i] != null;
1122 }
1123
1124 if (func_kinky != null or any_kinky_args) {
1125 try writer.writeAll(") // Instruction does not dominate all uses!");
1126 if (func_kinky) |func_index| {
1127 try writer.print(" %{d}", .{func_index});
1128 }
1129 for (args_kinky) |arg_kinky| {
1130 if (arg_kinky) |arg_index| {
1131 try writer.print(" %{d}", .{arg_index});
1132 }
1133 }
1134 try writer.writeAll("\n");
1135 } else {
1136 try writer.writeAll(")\n");
1137 }
1138 },
1139
1140 .struct_field_ptr => {
1141 const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
1142 const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr);
1143 if (kinky != null) {
1144 try writer.print("{d}) // Instruction does not dominate all uses!\n", .{
1145 struct_field_ptr.field_index,
1146 });
1147 } else {
1148 try writer.print("{d})\n", .{struct_field_ptr.field_index});
1149 }
1150 },
1151
1152 // TODO fill out this debug printing
1153 .assembly,
1154 .constant,
1155 .varptr,
1156 => {
1157 try writer.writeAll("!TODO!)\n");
1158 },
1159 }
1160 }
1161 }
1162
1163 fn writeInst(dtz: *DumpAir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
1164 if (dtz.partial_inst_table.get(inst)) |operand_index| {
1165 try writer.print("%{d}", .{operand_index});
1166 return null;
1167 } else if (dtz.const_table.get(inst)) |operand_index| {
1168 try writer.print("@{d}", .{operand_index});
1169 return null;
1170 } else if (dtz.inst_table.get(inst)) |operand_index| {
1171 try writer.print("%{d}", .{operand_index});
1172 return operand_index;
1173 } else {
1174 try writer.writeAll("!BADREF!");
1175 return null;
1176 }
1177 }
1178
1179 fn findConst(dtz: *DumpAir, operand: *Inst) !void {
1180 if (operand.tag == .constant) {
1181 try dtz.const_table.put(operand, dtz.next_const_index);
1182 dtz.next_const_index += 1;
1183 }
1184 }
1185};
src/codegen.zig+1302-1010
......@@ -2,7 +2,9 @@ const std = @import("std");
22const mem = std.mem;
33const math = std.math;
44const assert = std.debug.assert;
5const ir = @import("air.zig");
5const Air = @import("Air.zig");
6const Zir = @import("Zir.zig");
7const Liveness = @import("Liveness.zig");
68const Type = @import("type.zig").Type;
79const Value = @import("value.zig").Value;
810const TypedValue = @import("TypedValue.zig");
......@@ -22,6 +24,11 @@ const RegisterManager = @import("register_manager.zig").RegisterManager;
2224
2325const X8664Encoder = @import("codegen/x86_64.zig").Encoder;
2426
27pub const FnResult = union(enum) {
28 /// The `code` parameter passed to `generateSymbol` has the value appended.
29 appended: void,
30 fail: *ErrorMsg,
31};
2532pub const Result = union(enum) {
2633 /// The `code` parameter passed to `generateSymbol` has the value appended.
2734 appended: void,
......@@ -45,6 +52,71 @@ pub const DebugInfoOutput = union(enum) {
4552 none,
4653};
4754
55pub fn generateFunction(
56 bin_file: *link.File,
57 src_loc: Module.SrcLoc,
58 func: *Module.Fn,
59 air: Air,
60 liveness: Liveness,
61 code: *std.ArrayList(u8),
62 debug_output: DebugInfoOutput,
63) GenerateSymbolError!FnResult {
64 switch (bin_file.options.target.cpu.arch) {
65 .wasm32 => unreachable, // has its own code path
66 .wasm64 => unreachable, // has its own code path
67 .arm => return Function(.arm).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
68 .armeb => return Function(.armeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
69 .aarch64 => return Function(.aarch64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
70 .aarch64_be => return Function(.aarch64_be).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
71 .aarch64_32 => return Function(.aarch64_32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
72 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
73 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
74 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
75 //.bpfeb => return Function(.bpfeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
76 //.hexagon => return Function(.hexagon).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
77 //.mips => return Function(.mips).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
78 //.mipsel => return Function(.mipsel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
79 //.mips64 => return Function(.mips64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
80 //.mips64el => return Function(.mips64el).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
81 //.msp430 => return Function(.msp430).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
82 //.powerpc => return Function(.powerpc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
83 //.powerpc64 => return Function(.powerpc64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
84 //.powerpc64le => return Function(.powerpc64le).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
85 //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
86 //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
87 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
88 .riscv64 => return Function(.riscv64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
89 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
90 //.sparcv9 => return Function(.sparcv9).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
91 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
92 //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
93 //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
94 //.tcele => return Function(.tcele).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
95 //.thumb => return Function(.thumb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
96 //.thumbeb => return Function(.thumbeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
97 //.i386 => return Function(.i386).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
98 .x86_64 => return Function(.x86_64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
99 //.xcore => return Function(.xcore).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
100 //.nvptx => return Function(.nvptx).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
101 //.nvptx64 => return Function(.nvptx64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
102 //.le32 => return Function(.le32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
103 //.le64 => return Function(.le64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
104 //.amdil => return Function(.amdil).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
105 //.amdil64 => return Function(.amdil64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
106 //.hsail => return Function(.hsail).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
107 //.hsail64 => return Function(.hsail64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
108 //.spir => return Function(.spir).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
109 //.spir64 => return Function(.spir64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
110 //.kalimba => return Function(.kalimba).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
111 //.shave => return Function(.shave).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
112 //.lanai => return Function(.lanai).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
113 //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
114 //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
115 //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
116 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
117 }
118}
119
48120pub fn generateSymbol(
49121 bin_file: *link.File,
50122 src_loc: Module.SrcLoc,
......@@ -57,60 +129,14 @@ pub fn generateSymbol(
57129
58130 switch (typed_value.ty.zigTypeTag()) {
59131 .Fn => {
60 switch (bin_file.options.target.cpu.arch) {
61 .wasm32 => unreachable, // has its own code path
62 .wasm64 => unreachable, // has its own code path
63 .arm => return Function(.arm).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
64 .armeb => return Function(.armeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
65 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
66 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
67 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
68 //.arc => return Function(.arc).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
69 //.avr => return Function(.avr).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
70 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
71 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
72 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
73 //.mips => return Function(.mips).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
74 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
75 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
76 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
77 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
78 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
79 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
80 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
81 //.r600 => return Function(.r600).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
82 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
83 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
84 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
85 //.sparc => return Function(.sparc).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
86 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
87 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
88 //.s390x => return Function(.s390x).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
89 //.tce => return Function(.tce).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
90 //.tcele => return Function(.tcele).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
91 //.thumb => return Function(.thumb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
92 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
93 //.i386 => return Function(.i386).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
94 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
95 //.xcore => return Function(.xcore).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
96 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
97 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
98 //.le32 => return Function(.le32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
99 //.le64 => return Function(.le64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
100 //.amdil => return Function(.amdil).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
101 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
102 //.hsail => return Function(.hsail).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
103 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
104 //.spir => return Function(.spir).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
105 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
106 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
107 //.shave => return Function(.shave).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
108 //.lanai => return Function(.lanai).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
109 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
110 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
111 //.ve => return Function(.ve).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),
112 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
113 }
132 return Result{
133 .fail = try ErrorMsg.create(
134 bin_file.allocator,
135 src_loc,
136 "TODO implement generateSymbol function pointers",
137 .{},
138 ),
139 };
114140 },
115141 .Array => {
116142 // TODO populate .debug_info for the array
......@@ -262,6 +288,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
262288
263289 return struct {
264290 gpa: *Allocator,
291 air: Air,
292 liveness: Liveness,
265293 bin_file: *link.File,
266294 target: *const std.Target,
267295 mod_fn: *const Module.Fn,
......@@ -297,7 +325,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
297325 /// across each runtime branch upon joining.
298326 branch_stack: *std.ArrayList(Branch),
299327
300 blocks: std.AutoHashMapUnmanaged(*ir.Inst.Block, BlockData) = .{},
328 // Key is the block instruction
329 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
301330
302331 register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
303332 /// Maps offset to what is stored there.
......@@ -309,6 +338,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
309338 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
310339 next_stack_offset: u32 = 0,
311340
341 /// Debug field, used to find bugs in the compiler.
342 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
343
344 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
345
312346 const MCValue = union(enum) {
313347 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
314348 /// TODO Look into deleting this tag and using `dead` instead, since every use
......@@ -383,7 +417,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
383417 };
384418
385419 const Branch = struct {
386 inst_table: std.AutoArrayHashMapUnmanaged(*ir.Inst, MCValue) = .{},
420 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
387421
388422 fn deinit(self: *Branch, gpa: *Allocator) void {
389423 self.inst_table.deinit(gpa);
......@@ -392,7 +426,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
392426 };
393427
394428 const StackAllocation = struct {
395 inst: *ir.Inst,
429 inst: Air.Inst.Index,
396430 /// TODO do we need size? should be determined by inst.ty.abiSize()
397431 size: u32,
398432 };
......@@ -418,21 +452,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
418452 },
419453 };
420454
455 const BigTomb = struct {
456 function: *Self,
457 inst: Air.Inst.Index,
458 tomb_bits: Liveness.Bpi,
459 big_tomb_bits: u32,
460 bit_index: usize,
461
462 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
463 const this_bit_index = bt.bit_index;
464 bt.bit_index += 1;
465
466 const op_int = @enumToInt(op_ref);
467 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
468 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
469
470 if (this_bit_index < Liveness.bpi - 1) {
471 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
472 if (!dies) return;
473 } else {
474 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
475 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
476 if (!dies) return;
477 }
478 bt.function.processDeath(op_index);
479 }
480
481 fn finishAir(bt: *BigTomb, result: MCValue) void {
482 const is_used = !bt.function.liveness.isUnused(bt.inst);
483 if (is_used) {
484 log.debug("%{d} => {}", .{ bt.inst, result });
485 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
486 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
487 }
488 bt.function.finishAirBookkeeping();
489 }
490 };
491
421492 const Self = @This();
422493
423 fn generateSymbol(
494 fn generate(
424495 bin_file: *link.File,
425496 src_loc: Module.SrcLoc,
426 typed_value: TypedValue,
497 module_fn: *Module.Fn,
498 air: Air,
499 liveness: Liveness,
427500 code: *std.ArrayList(u8),
428501 debug_output: DebugInfoOutput,
429 ) GenerateSymbolError!Result {
502 ) GenerateSymbolError!FnResult {
430503 if (build_options.skip_non_native and std.Target.current.cpu.arch != arch) {
431504 @panic("Attempted to compile for architecture that was disabled by build configuration");
432505 }
433506
434 const module_fn = typed_value.val.castTag(.function).?.data;
435
436507 assert(module_fn.owner_decl.has_tv);
437508 const fn_type = module_fn.owner_decl.ty;
438509
......@@ -446,6 +517,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
446517
447518 var function = Self{
448519 .gpa = bin_file.allocator,
520 .air = air,
521 .liveness = liveness,
449522 .target = &bin_file.options.target,
450523 .bin_file = bin_file,
451524 .mod_fn = module_fn,
......@@ -469,8 +542,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
469542 defer function.blocks.deinit(bin_file.allocator);
470543 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
471544
472 var call_info = function.resolveCallingConventionValues(src_loc.lazy, fn_type) catch |err| switch (err) {
473 error.CodegenFail => return Result{ .fail = function.err_msg.? },
545 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
546 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
474547 else => |e| return e,
475548 };
476549 defer call_info.deinit(&function);
......@@ -481,14 +554,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
481554 function.max_end_stack = call_info.stack_byte_count;
482555
483556 function.gen() catch |err| switch (err) {
484 error.CodegenFail => return Result{ .fail = function.err_msg.? },
557 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
485558 else => |e| return e,
486559 };
487560
488561 if (function.err_msg) |em| {
489 return Result{ .fail = em };
562 return FnResult{ .fail = em };
490563 } else {
491 return Result{ .appended = {} };
564 return FnResult{ .appended = {} };
492565 }
493566 }
494567
......@@ -512,7 +585,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
512585 self.code.items.len += 4;
513586
514587 try self.dbgSetPrologueEnd();
515 try self.genBody(self.mod_fn.body);
588 try self.genBody(self.air.getMainBody());
516589
517590 const stack_end = self.max_end_stack;
518591 if (stack_end > math.maxInt(i32))
......@@ -553,7 +626,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
553626 });
554627 } else {
555628 try self.dbgSetPrologueEnd();
556 try self.genBody(self.mod_fn.body);
629 try self.genBody(self.air.getMainBody());
557630 try self.dbgSetEpilogueBegin();
558631 }
559632 },
......@@ -569,7 +642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
569642
570643 try self.dbgSetPrologueEnd();
571644
572 try self.genBody(self.mod_fn.body);
645 try self.genBody(self.air.getMainBody());
573646
574647 // Backpatch push callee saved regs
575648 var saved_regs = Instruction.RegisterList{
......@@ -630,7 +703,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
630703 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32());
631704 } else {
632705 try self.dbgSetPrologueEnd();
633 try self.genBody(self.mod_fn.body);
706 try self.genBody(self.air.getMainBody());
634707 try self.dbgSetEpilogueBegin();
635708 }
636709 },
......@@ -654,7 +727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
654727
655728 try self.dbgSetPrologueEnd();
656729
657 try self.genBody(self.mod_fn.body);
730 try self.genBody(self.air.getMainBody());
658731
659732 // Backpatch stack offset
660733 const stack_end = self.max_end_stack;
......@@ -706,13 +779,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
706779 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
707780 } else {
708781 try self.dbgSetPrologueEnd();
709 try self.genBody(self.mod_fn.body);
782 try self.genBody(self.air.getMainBody());
710783 try self.dbgSetEpilogueBegin();
711784 }
712785 },
713786 else => {
714787 try self.dbgSetPrologueEnd();
715 try self.genBody(self.mod_fn.body);
788 try self.genBody(self.air.getMainBody());
716789 try self.dbgSetEpilogueBegin();
717790 },
718791 }
......@@ -720,21 +793,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
720793 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
721794 }
722795
723 fn genBody(self: *Self, body: ir.Body) InnerError!void {
724 for (body.instructions) |inst| {
725 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));
726
727 const mcv = try self.genFuncInst(inst);
728 if (!inst.isUnused()) {
729 log.debug("{*} => {}", .{ inst, mcv });
730 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
731 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
796 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
797 const air_tags = self.air.instructions.items(.tag);
798
799 for (body) |inst| {
800 const old_air_bookkeeping = self.air_bookkeeping;
801 try self.ensureProcessDeathCapacity(Liveness.bpi);
802
803 switch (air_tags[inst]) {
804 // zig fmt: off
805 .add => try self.airAdd(inst),
806 .addwrap => try self.airAddWrap(inst),
807 .sub => try self.airSub(inst),
808 .subwrap => try self.airSubWrap(inst),
809 .mul => try self.airMul(inst),
810 .mulwrap => try self.airMulWrap(inst),
811 .div => try self.airDiv(inst),
812
813 .cmp_lt => try self.airCmp(inst, .lt),
814 .cmp_lte => try self.airCmp(inst, .lte),
815 .cmp_eq => try self.airCmp(inst, .eq),
816 .cmp_gte => try self.airCmp(inst, .gte),
817 .cmp_gt => try self.airCmp(inst, .gt),
818 .cmp_neq => try self.airCmp(inst, .neq),
819
820 .bool_and => try self.airBoolOp(inst),
821 .bool_or => try self.airBoolOp(inst),
822 .bit_and => try self.airBitAnd(inst),
823 .bit_or => try self.airBitOr(inst),
824 .xor => try self.airXor(inst),
825
826 .alloc => try self.airAlloc(inst),
827 .arg => try self.airArg(inst),
828 .assembly => try self.airAsm(inst),
829 .bitcast => try self.airBitCast(inst),
830 .block => try self.airBlock(inst),
831 .br => try self.airBr(inst),
832 .breakpoint => try self.airBreakpoint(),
833 .call => try self.airCall(inst),
834 .cond_br => try self.airCondBr(inst),
835 .dbg_stmt => try self.airDbgStmt(inst),
836 .floatcast => try self.airFloatCast(inst),
837 .intcast => try self.airIntCast(inst),
838 .is_non_null => try self.airIsNonNull(inst),
839 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
840 .is_null => try self.airIsNull(inst),
841 .is_null_ptr => try self.airIsNullPtr(inst),
842 .is_non_err => try self.airIsNonErr(inst),
843 .is_non_err_ptr => try self.airIsNonErrPtr(inst),
844 .is_err => try self.airIsErr(inst),
845 .is_err_ptr => try self.airIsErrPtr(inst),
846 .load => try self.airLoad(inst),
847 .loop => try self.airLoop(inst),
848 .not => try self.airNot(inst),
849 .ptrtoint => try self.airPtrToInt(inst),
850 .ref => try self.airRef(inst),
851 .ret => try self.airRet(inst),
852 .store => try self.airStore(inst),
853 .struct_field_ptr=> try self.airStructFieldPtr(inst),
854 .switch_br => try self.airSwitch(inst),
855 .varptr => try self.airVarPtr(inst),
856
857 .constant => unreachable, // excluded from function bodies
858 .const_ty => unreachable, // excluded from function bodies
859 .unreach => self.finishAirBookkeeping(),
860
861 .optional_payload => try self.airOptionalPayload(inst),
862 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
863 .unwrap_errunion_err => try self.airUnwrapErrErr(inst),
864 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
865 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
866 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
867
868 .wrap_optional => try self.airWrapOptional(inst),
869 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
870 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
871 // zig fmt: on
732872 }
733
734 var i: ir.Inst.DeathsBitIndex = 0;
735 while (inst.getOperand(i)) |operand| : (i += 1) {
736 if (inst.operandDies(i))
737 self.processDeath(operand);
873 if (std.debug.runtime_safety) {
874 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
875 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
876 }
738877 }
739878 }
740879 }
......@@ -784,8 +923,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
784923 }
785924
786925 /// Asserts there is already capacity to insert into top branch inst_table.
787 fn processDeath(self: *Self, inst: *ir.Inst) void {
788 if (inst.tag == .constant) return; // Constants are immortal.
926 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
927 const air_tags = self.air.instructions.items(.tag);
928 if (air_tags[inst] == .constant) return; // Constants are immortal.
789929 // When editing this function, note that the logic must synchronize with `reuseOperand`.
790930 const prev_value = self.getResolvedInstValue(inst);
791931 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
......@@ -799,9 +939,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
799939 }
800940 }
801941
942 /// Called when there are no operands, and the instruction is always unreferenced.
943 fn finishAirBookkeeping(self: *Self) void {
944 if (std.debug.runtime_safety) {
945 self.air_bookkeeping += 1;
946 }
947 }
948
949 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
950 var tomb_bits = self.liveness.getTombBits(inst);
951 for (operands) |op| {
952 const dies = @truncate(u1, tomb_bits) != 0;
953 tomb_bits >>= 1;
954 if (!dies) continue;
955 const op_int = @enumToInt(op);
956 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
957 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
958 self.processDeath(op_index);
959 }
960 const is_used = @truncate(u1, tomb_bits) == 0;
961 if (is_used) {
962 log.debug("%{d} => {}", .{ inst, result });
963 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
964 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
965 }
966 self.finishAirBookkeeping();
967 }
968
802969 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
803970 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
804 try table.ensureCapacity(self.gpa, table.count() + additional_count);
971 try table.ensureUnusedCapacity(self.gpa, additional_count);
805972 }
806973
807974 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
......@@ -826,74 +993,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
826993 }
827994 }
828995
829 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
830 switch (inst.tag) {
831 .add => return self.genAdd(inst.castTag(.add).?),
832 .addwrap => return self.genAddWrap(inst.castTag(.addwrap).?),
833 .alloc => return self.genAlloc(inst.castTag(.alloc).?),
834 .arg => return self.genArg(inst.castTag(.arg).?),
835 .assembly => return self.genAsm(inst.castTag(.assembly).?),
836 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
837 .bit_and => return self.genBitAnd(inst.castTag(.bit_and).?),
838 .bit_or => return self.genBitOr(inst.castTag(.bit_or).?),
839 .block => return self.genBlock(inst.castTag(.block).?),
840 .br => return self.genBr(inst.castTag(.br).?),
841 .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?),
842 .breakpoint => return self.genBreakpoint(inst.src),
843 .br_void => return self.genBrVoid(inst.castTag(.br_void).?),
844 .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?),
845 .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?),
846 .call => return self.genCall(inst.castTag(.call).?),
847 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
848 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
849 .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
850 .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
851 .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
852 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
853 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
854 .constant => unreachable, // excluded from function bodies
855 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
856 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
857 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
858 .is_non_null => return self.genIsNonNull(inst.castTag(.is_non_null).?),
859 .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
860 .is_null => return self.genIsNull(inst.castTag(.is_null).?),
861 .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
862 .is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),
863 .is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),
864 .is_err => return self.genIsErr(inst.castTag(.is_err).?),
865 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
866 .load => return self.genLoad(inst.castTag(.load).?),
867 .loop => return self.genLoop(inst.castTag(.loop).?),
868 .not => return self.genNot(inst.castTag(.not).?),
869 .mul => return self.genMul(inst.castTag(.mul).?),
870 .mulwrap => return self.genMulWrap(inst.castTag(.mulwrap).?),
871 .div => return self.genDiv(inst.castTag(.div).?),
872 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
873 .ref => return self.genRef(inst.castTag(.ref).?),
874 .ret => return self.genRet(inst.castTag(.ret).?),
875 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
876 .store => return self.genStore(inst.castTag(.store).?),
877 .struct_field_ptr => return self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
878 .sub => return self.genSub(inst.castTag(.sub).?),
879 .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?),
880 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
881 .unreach => return MCValue{ .unreach = {} },
882 .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),
883 .optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),
884 .unwrap_errunion_err => return self.genUnwrapErrErr(inst.castTag(.unwrap_errunion_err).?),
885 .unwrap_errunion_payload => return self.genUnwrapErrPayload(inst.castTag(.unwrap_errunion_payload).?),
886 .unwrap_errunion_err_ptr => return self.genUnwrapErrErrPtr(inst.castTag(.unwrap_errunion_err_ptr).?),
887 .unwrap_errunion_payload_ptr => return self.genUnwrapErrPayloadPtr(inst.castTag(.unwrap_errunion_payload_ptr).?),
888 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
889 .wrap_errunion_payload => return self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
890 .wrap_errunion_err => return self.genWrapErrUnionErr(inst.castTag(.wrap_errunion_err).?),
891 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
892 .xor => return self.genXor(inst.castTag(.xor).?),
893 }
894 }
895
896 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
996 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
897997 if (abi_align > self.stack_align)
898998 self.stack_align = abi_align;
899999 // TODO find a free slot instead of always appending
......@@ -909,20 +1009,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9091009 }
9101010
9111011 /// Use a pointer instruction as the basis for allocating stack memory.
912 fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {
913 const elem_ty = inst.ty.elemType();
1012 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
1013 const elem_ty = self.air.typeOfIndex(inst).elemType();
9141014 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
915 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
1015 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
9161016 };
9171017 // TODO swap this for inst.ty.ptrAlign
9181018 const abi_align = elem_ty.abiAlignment(self.target.*);
9191019 return self.allocMem(inst, abi_size, abi_align);
9201020 }
9211021
922 fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {
923 const elem_ty = inst.ty;
1022 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
1023 const elem_ty = self.air.typeOfIndex(inst);
9241024 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
925 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
1025 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
9261026 };
9271027 const abi_align = elem_ty.abiAlignment(self.target.*);
9281028 if (abi_align > self.stack_align)
......@@ -942,310 +1042,299 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9421042 return MCValue{ .stack_offset = stack_offset };
9431043 }
9441044
945 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void {
1045 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
9461046 const stack_mcv = try self.allocRegOrMem(inst, false);
947 log.debug("spilling {*} to stack mcv {any}", .{ inst, stack_mcv });
1047 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
9481048 const reg_mcv = self.getResolvedInstValue(inst);
9491049 assert(reg == toCanonicalReg(reg_mcv.register));
9501050 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
9511051 try branch.inst_table.put(self.gpa, inst, stack_mcv);
952 try self.genSetStack(src, inst.ty, stack_mcv.stack_offset, reg_mcv);
1052 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
9531053 }
9541054
9551055 /// Copies a value to a register without tracking the register. The register is not considered
9561056 /// allocated. A second call to `copyToTmpRegister` may return the same register.
9571057 /// This can have a side effect of spilling instructions to the stack to free up a register.
958 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {
1058 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
9591059 const reg = try self.register_manager.allocReg(null, &.{});
960 try self.genSetReg(src, ty, reg, mcv);
1060 try self.genSetReg(ty, reg, mcv);
9611061 return reg;
9621062 }
9631063
9641064 /// Allocates a new register and copies `mcv` into it.
9651065 /// `reg_owner` is the instruction that gets associated with the register in the register table.
9661066 /// This can have a side effect of spilling instructions to the stack to free up a register.
967 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
1067 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
9681068 const reg = try self.register_manager.allocReg(reg_owner, &.{});
969 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
1069 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
9701070 return MCValue{ .register = reg };
9711071 }
9721072
973 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
974 const stack_offset = try self.allocMemPtr(&inst.base);
975 return MCValue{ .ptr_stack_offset = stack_offset };
1073 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1074 const stack_offset = try self.allocMemPtr(inst);
1075 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
9761076 }
9771077
978 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
979 // No side effects, so if it's unreferenced, do nothing.
980 if (inst.base.isUnused())
981 return MCValue.dead;
982 switch (arch) {
983 else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),
984 }
1078 fn airFloatCast(self: *Self, inst: Air.Inst.Index) !void {
1079 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1080 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1081 else => return self.fail("TODO implement floatCast for {}", .{self.target.cpu.arch}),
1082 };
1083 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
9851084 }
9861085
987 fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
988 // No side effects, so if it's unreferenced, do nothing.
989 if (inst.base.isUnused())
990 return MCValue.dead;
1086 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1087 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1088 if (self.liveness.isUnused(inst))
1089 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
9911090
992 const operand = try self.resolveInst(inst.operand);
993 const info_a = inst.operand.ty.intInfo(self.target.*);
994 const info_b = inst.base.ty.intInfo(self.target.*);
1091 const operand_ty = self.air.typeOf(ty_op.operand);
1092 const operand = try self.resolveInst(ty_op.operand);
1093 const info_a = operand_ty.intInfo(self.target.*);
1094 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
9951095 if (info_a.signedness != info_b.signedness)
996 return self.fail(inst.base.src, "TODO gen intcast sign safety in semantic analysis", .{});
1096 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
9971097
9981098 if (info_a.bits == info_b.bits)
999 return operand;
1099 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
10001100
1001 switch (arch) {
1002 else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),
1003 }
1101 const result: MCValue = switch (arch) {
1102 else => return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}),
1103 };
1104 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
10041105 }
10051106
1006 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1007 // No side effects, so if it's unreferenced, do nothing.
1008 if (inst.base.isUnused())
1009 return MCValue.dead;
1010 const operand = try self.resolveInst(inst.operand);
1011 switch (operand) {
1012 .dead => unreachable,
1013 .unreach => unreachable,
1014 .compare_flags_unsigned => |op| return MCValue{
1015 .compare_flags_unsigned = switch (op) {
1016 .gte => .lt,
1017 .gt => .lte,
1018 .neq => .eq,
1019 .lt => .gte,
1020 .lte => .gt,
1021 .eq => .neq,
1107 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1108 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1109 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1110 const operand = try self.resolveInst(ty_op.operand);
1111 switch (operand) {
1112 .dead => unreachable,
1113 .unreach => unreachable,
1114 .compare_flags_unsigned => |op| {
1115 const r = MCValue{
1116 .compare_flags_unsigned = switch (op) {
1117 .gte => .lt,
1118 .gt => .lte,
1119 .neq => .eq,
1120 .lt => .gte,
1121 .lte => .gt,
1122 .eq => .neq,
1123 },
1124 };
1125 break :result r;
10221126 },
1023 },
1024 .compare_flags_signed => |op| return MCValue{
1025 .compare_flags_signed = switch (op) {
1026 .gte => .lt,
1027 .gt => .lte,
1028 .neq => .eq,
1029 .lt => .gte,
1030 .lte => .gt,
1031 .eq => .neq,
1127 .compare_flags_signed => |op| {
1128 const r = MCValue{
1129 .compare_flags_signed = switch (op) {
1130 .gte => .lt,
1131 .gt => .lte,
1132 .neq => .eq,
1133 .lt => .gte,
1134 .lte => .gt,
1135 .eq => .neq,
1136 },
1137 };
1138 break :result r;
10321139 },
1033 },
1034 else => {},
1035 }
1140 else => {},
1141 }
10361142
1037 switch (arch) {
1038 .x86_64 => {
1039 var imm = ir.Inst.Constant{
1040 .base = .{
1041 .tag = .constant,
1042 .deaths = 0,
1043 .ty = inst.operand.ty,
1044 .src = inst.operand.src,
1045 },
1046 .val = Value.initTag(.bool_true),
1047 };
1048 return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base);
1049 },
1050 .arm, .armeb => {
1051 var imm = ir.Inst.Constant{
1052 .base = .{
1053 .tag = .constant,
1054 .deaths = 0,
1055 .ty = inst.operand.ty,
1056 .src = inst.operand.src,
1057 },
1058 .val = Value.initTag(.bool_true),
1059 };
1060 return try self.genArmBinOp(&inst.base, inst.operand, &imm.base, .not);
1061 },
1062 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
1063 }
1143 switch (arch) {
1144 .x86_64 => {
1145 break :result try self.genX8664BinMath(inst, ty_op.operand, .bool_true);
1146 },
1147 .arm, .armeb => {
1148 break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
1149 },
1150 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),
1151 }
1152 };
1153 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
10641154 }
10651155
1066 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1067 // No side effects, so if it's unreferenced, do nothing.
1068 if (inst.base.isUnused())
1069 return MCValue.dead;
1070 switch (arch) {
1071 .x86_64 => {
1072 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs);
1073 },
1074 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .add),
1075 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
1076 }
1156 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
1157 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1158 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1159 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1160 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
1161 else => return self.fail("TODO implement add for {}", .{self.target.cpu.arch}),
1162 };
1163 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
10771164 }
10781165
1079 fn genAddWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1080 // No side effects, so if it's unreferenced, do nothing.
1081 if (inst.base.isUnused())
1082 return MCValue.dead;
1083 switch (arch) {
1084 else => return self.fail(inst.base.src, "TODO implement addwrap for {}", .{self.target.cpu.arch}),
1085 }
1166 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
1167 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1168 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1169 else => return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}),
1170 };
1171 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
10861172 }
10871173
1088 fn genMul(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1089 // No side effects, so if it's unreferenced, do nothing.
1090 if (inst.base.isUnused())
1091 return MCValue.dead;
1092 switch (arch) {
1093 .x86_64 => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),
1094 .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs),
1095 else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}),
1096 }
1174 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
1175 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1176 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1177 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1178 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .sub),
1179 else => return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}),
1180 };
1181 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
10971182 }
10981183
1099 fn genMulWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1100 // No side effects, so if it's unreferenced, do nothing.
1101 if (inst.base.isUnused())
1102 return MCValue.dead;
1103 switch (arch) {
1104 else => return self.fail(inst.base.src, "TODO implement mulwrap for {}", .{self.target.cpu.arch}),
1105 }
1184 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
1185 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1186 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1187 else => return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}),
1188 };
1189 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11061190 }
11071191
1108 fn genDiv(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1109 // No side effects, so if it's unreferenced, do nothing.
1110 if (inst.base.isUnused())
1111 return MCValue.dead;
1112 switch (arch) {
1113 else => return self.fail(inst.base.src, "TODO implement div for {}", .{self.target.cpu.arch}),
1114 }
1192 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
1193 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1194 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1195 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1196 .arm, .armeb => try self.genArmMul(inst, bin_op.lhs, bin_op.rhs),
1197 else => return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}),
1198 };
1199 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11151200 }
11161201
1117 fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1118 // No side effects, so if it's unreferenced, do nothing.
1119 if (inst.base.isUnused())
1120 return MCValue.dead;
1121 switch (arch) {
1122 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_and),
1123 else => return self.fail(inst.base.src, "TODO implement bitwise and for {}", .{self.target.cpu.arch}),
1124 }
1202 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
1203 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1204 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1205 else => return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}),
1206 };
1207 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11251208 }
11261209
1127 fn genBitOr(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1128 // No side effects, so if it's unreferenced, do nothing.
1129 if (inst.base.isUnused())
1130 return MCValue.dead;
1131 switch (arch) {
1132 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_or),
1133 else => return self.fail(inst.base.src, "TODO implement bitwise or for {}", .{self.target.cpu.arch}),
1134 }
1210 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1211 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1212 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1213 else => return self.fail("TODO implement div for {}", .{self.target.cpu.arch}),
1214 };
1215 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11351216 }
11361217
1137 fn genXor(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1138 // No side effects, so if it's unreferenced, do nothing.
1139 if (inst.base.isUnused())
1140 return MCValue.dead;
1141 switch (arch) {
1142 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .xor),
1143 else => return self.fail(inst.base.src, "TODO implement xor for {}", .{self.target.cpu.arch}),
1144 }
1218 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
1219 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1220 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1221 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),
1222 else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}),
1223 };
1224 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11451225 }
11461226
1147 fn genOptionalPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1148 // No side effects, so if it's unreferenced, do nothing.
1149 if (inst.base.isUnused())
1150 return MCValue.dead;
1151 switch (arch) {
1152 else => return self.fail(inst.base.src, "TODO implement .optional_payload for {}", .{self.target.cpu.arch}),
1153 }
1227 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
1228 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1229 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1230 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),
1231 else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}),
1232 };
1233 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11541234 }
11551235
1156 fn genOptionalPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1157 // No side effects, so if it's unreferenced, do nothing.
1158 if (inst.base.isUnused())
1159 return MCValue.dead;
1160 switch (arch) {
1161 else => return self.fail(inst.base.src, "TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),
1162 }
1236 fn airXor(self: *Self, inst: Air.Inst.Index) !void {
1237 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1238 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1239 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),
1240 else => return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}),
1241 };
1242 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11631243 }
11641244
1165 fn genUnwrapErrErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1166 // No side effects, so if it's unreferenced, do nothing.
1167 if (inst.base.isUnused())
1168 return MCValue.dead;
1169 switch (arch) {
1170 else => return self.fail(inst.base.src, "TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),
1171 }
1245 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1246 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1247 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1248 else => return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}),
1249 };
1250 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11721251 }
11731252
1174 fn genUnwrapErrPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1175 // No side effects, so if it's unreferenced, do nothing.
1176 if (inst.base.isUnused())
1177 return MCValue.dead;
1178 switch (arch) {
1179 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),
1180 }
1253 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1254 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1255 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1256 else => return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),
1257 };
1258 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1259 }
1260
1261 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
1262 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1263 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1264 else => return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),
1265 };
1266 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11811267 }
1268
1269 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1270 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1271 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1272 else => return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),
1273 };
1274 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1275 }
1276
11821277 // *(E!T) -> E
1183 fn genUnwrapErrErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1184 // No side effects, so if it's unreferenced, do nothing.
1185 if (inst.base.isUnused())
1186 return MCValue.dead;
1187 switch (arch) {
1188 else => return self.fail(inst.base.src, "TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
1189 }
1278 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
1279 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1280 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1281 else => return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
1282 };
1283 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11901284 }
1285
11911286 // *(E!T) -> *T
1192 fn genUnwrapErrPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1193 // No side effects, so if it's unreferenced, do nothing.
1194 if (inst.base.isUnused())
1195 return MCValue.dead;
1196 switch (arch) {
1197 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
1198 }
1287 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1288 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1289 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1290 else => return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
1291 };
1292 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11991293 }
1200 fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1201 const optional_ty = inst.base.ty;
12021294
1203 // No side effects, so if it's unreferenced, do nothing.
1204 if (inst.base.isUnused())
1205 return MCValue.dead;
1295 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1296 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1297 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1298 const optional_ty = self.air.typeOfIndex(inst);
12061299
1207 // Optional type is just a boolean true
1208 if (optional_ty.abiSize(self.target.*) == 1)
1209 return MCValue{ .immediate = 1 };
1300 // Optional with a zero-bit payload type is just a boolean true
1301 if (optional_ty.abiSize(self.target.*) == 1)
1302 break :result MCValue{ .immediate = 1 };
12101303
1211 switch (arch) {
1212 else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1213 }
1304 switch (arch) {
1305 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1306 }
1307 };
1308 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
12141309 }
12151310
12161311 /// T to E!T
1217 fn genWrapErrUnionPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1218 // No side effects, so if it's unreferenced, do nothing.
1219 if (inst.base.isUnused())
1220 return MCValue.dead;
1221
1222 switch (arch) {
1223 else => return self.fail(inst.base.src, "TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
1224 }
1312 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1313 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1314 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1315 else => return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
1316 };
1317 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
12251318 }
12261319
12271320 /// E to E!T
1228 fn genWrapErrUnionErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1229 // No side effects, so if it's unreferenced, do nothing.
1230 if (inst.base.isUnused())
1231 return MCValue.dead;
1232
1233 switch (arch) {
1234 else => return self.fail(inst.base.src, "TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
1235 }
1321 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1322 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1323 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1324 else => return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
1325 };
1326 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
12361327 }
1237 fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {
1238 // No side effects, so if it's unreferenced, do nothing.
1239 if (inst.base.isUnused())
1240 return MCValue.dead;
12411328
1242 switch (arch) {
1243 else => return self.fail(inst.base.src, "TODO implement varptr for {}", .{self.target.cpu.arch}),
1244 }
1329 fn airVarPtr(self: *Self, inst: Air.Inst.Index) !void {
1330 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1331 else => return self.fail("TODO implement varptr for {}", .{self.target.cpu.arch}),
1332 };
1333 return self.finishAir(inst, result, .{ .none, .none, .none });
12451334 }
12461335
1247 fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
1248 if (!inst.operandDies(op_index))
1336 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1337 if (!self.liveness.operandDies(inst, op_index))
12491338 return false;
12501339
12511340 switch (mcv) {
......@@ -1257,40 +1346,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12571346 self.register_manager.registers[index] = inst;
12581347 }
12591348 }
1260 log.debug("reusing {} => {*}", .{ reg, inst });
1349 log.debug("%{d} => {} (reused)", .{ inst, reg });
12611350 },
12621351 .stack_offset => |off| {
1263 log.debug("reusing stack offset {} => {*}", .{ off, inst });
1352 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
12641353 },
12651354 else => return false,
12661355 }
12671356
12681357 // Prevent the operand deaths processing code from deallocating it.
1269 inst.clearOperandDeath(op_index);
1358 self.liveness.clearOperandDeath(inst, op_index);
12701359
12711360 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
12721361 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1273 branch.inst_table.putAssumeCapacity(inst.getOperand(op_index).?, .dead);
1362 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
12741363
12751364 return true;
12761365 }
12771366
1278 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1279 const elem_ty = inst.base.ty;
1280 if (!elem_ty.hasCodeGenBits())
1281 return MCValue.none;
1282 const ptr = try self.resolveInst(inst.operand);
1283 const is_volatile = inst.operand.ty.isVolatilePtr();
1284 if (inst.base.isUnused() and !is_volatile)
1285 return MCValue.dead;
1286 const dst_mcv: MCValue = blk: {
1287 if (self.reuseOperand(&inst.base, 0, ptr)) {
1288 // The MCValue that holds the pointer can be re-used as the value.
1289 break :blk ptr;
1290 } else {
1291 break :blk try self.allocRegOrMem(&inst.base, true);
1292 }
1293 };
1367 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) !void {
1368 const elem_ty = ptr_ty.elemType();
12941369 switch (ptr) {
12951370 .none => unreachable,
12961371 .undef => unreachable,
......@@ -1298,31 +1373,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12981373 .dead => unreachable,
12991374 .compare_flags_unsigned => unreachable,
13001375 .compare_flags_signed => unreachable,
1301 .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),
1302 .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),
1376 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1377 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
13031378 .ptr_embedded_in_code => |off| {
1304 try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });
1379 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
13051380 },
13061381 .embedded_in_code => {
1307 return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});
1382 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
13081383 },
13091384 .register => {
1310 return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});
1385 return self.fail("TODO implement loading from MCValue.register", .{});
13111386 },
13121387 .memory => {
1313 return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});
1388 return self.fail("TODO implement loading from MCValue.memory", .{});
13141389 },
13151390 .stack_offset => {
1316 return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});
1391 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
13171392 },
13181393 }
1319 return dst_mcv;
13201394 }
13211395
1322 fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1323 const ptr = try self.resolveInst(inst.lhs);
1324 const value = try self.resolveInst(inst.rhs);
1325 const elem_ty = inst.rhs.ty;
1396 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1397 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1398 const elem_ty = self.air.typeOfIndex(inst);
1399 const result: MCValue = result: {
1400 if (!elem_ty.hasCodeGenBits())
1401 break :result MCValue.none;
1402
1403 const ptr = try self.resolveInst(ty_op.operand);
1404 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1405 if (self.liveness.isUnused(inst) and !is_volatile)
1406 break :result MCValue.dead;
1407
1408 const dst_mcv: MCValue = blk: {
1409 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
1410 // The MCValue that holds the pointer can be re-used as the value.
1411 break :blk ptr;
1412 } else {
1413 break :blk try self.allocRegOrMem(inst, true);
1414 }
1415 };
1416 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
1417 break :result dst_mcv;
1418 };
1419 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1420 }
1421
1422 fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1423 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1424 const ptr = try self.resolveInst(bin_op.lhs);
1425 const value = try self.resolveInst(bin_op.rhs);
1426 const elem_ty = self.air.typeOf(bin_op.rhs);
13261427 switch (ptr) {
13271428 .none => unreachable,
13281429 .undef => unreachable,
......@@ -1331,57 +1432,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13311432 .compare_flags_unsigned => unreachable,
13321433 .compare_flags_signed => unreachable,
13331434 .immediate => |imm| {
1334 try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);
1435 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
13351436 },
13361437 .ptr_stack_offset => |off| {
1337 try self.genSetStack(inst.base.src, elem_ty, off, value);
1438 try self.genSetStack(elem_ty, off, value);
13381439 },
13391440 .ptr_embedded_in_code => |off| {
1340 try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);
1441 try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value);
13411442 },
13421443 .embedded_in_code => {
1343 return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});
1444 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
13441445 },
13451446 .register => {
1346 return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});
1447 return self.fail("TODO implement storing to MCValue.register", .{});
13471448 },
13481449 .memory => {
1349 return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});
1450 return self.fail("TODO implement storing to MCValue.memory", .{});
13501451 },
13511452 .stack_offset => {
1352 return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});
1353 },
1354 }
1355 return .none;
1356 }
1357
1358 fn genStructFieldPtr(self: *Self, inst: *ir.Inst.StructFieldPtr) !MCValue {
1359 return self.fail(inst.base.src, "TODO implement codegen struct_field_ptr", .{});
1360 }
1361
1362 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1363 // No side effects, so if it's unreferenced, do nothing.
1364 if (inst.base.isUnused())
1365 return MCValue.dead;
1366 switch (arch) {
1367 .x86_64 => {
1368 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs);
1453 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
13691454 },
1370 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .sub),
1371 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
13721455 }
1456 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
13731457 }
13741458
1375 fn genSubWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
1376 // No side effects, so if it's unreferenced, do nothing.
1377 if (inst.base.isUnused())
1378 return MCValue.dead;
1379 switch (arch) {
1380 else => return self.fail(inst.base.src, "TODO implement subwrap for {}", .{self.target.cpu.arch}),
1381 }
1459 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
1460 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1461 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1462 _ = extra;
1463 return self.fail("TODO implement codegen struct_field_ptr", .{});
1464 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
13821465 }
13831466
1384 fn armOperandShouldBeRegister(self: *Self, src: LazySrcLoc, mcv: MCValue) !bool {
1467 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
13851468 return switch (mcv) {
13861469 .none => unreachable,
13871470 .undef => unreachable,
......@@ -1391,7 +1474,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13911474 .ptr_stack_offset => unreachable,
13921475 .ptr_embedded_in_code => unreachable,
13931476 .immediate => |imm| blk: {
1394 if (imm > std.math.maxInt(u32)) return self.fail(src, "TODO ARM binary arithmetic immediate larger than u32", .{});
1477 if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{});
13951478
13961479 // Load immediate into register if it doesn't fit
13971480 // in an operand
......@@ -1405,16 +1488,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14051488 };
14061489 }
14071490
1408 fn genArmBinOp(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, op: ir.Inst.Tag) !MCValue {
1491 fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue {
14091492 const lhs = try self.resolveInst(op_lhs);
14101493 const rhs = try self.resolveInst(op_rhs);
14111494
14121495 const lhs_is_register = lhs == .register;
14131496 const rhs_is_register = rhs == .register;
1414 const lhs_should_be_register = try self.armOperandShouldBeRegister(op_lhs.src, lhs);
1415 const rhs_should_be_register = try self.armOperandShouldBeRegister(op_rhs.src, rhs);
1416 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);
1417 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);
1497 const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
1498 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1499 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1500 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
14181501
14191502 // Destination must be a register
14201503 var dst_mcv: MCValue = undefined;
......@@ -1427,15 +1510,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14271510 if (reuse_lhs) {
14281511 // Allocate 0 or 1 registers
14291512 if (!rhs_is_register and rhs_should_be_register) {
1430 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };
1431 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);
1513 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1514 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
14321515 }
14331516 dst_mcv = lhs;
14341517 } else if (reuse_rhs) {
14351518 // Allocate 0 or 1 registers
14361519 if (!lhs_is_register and lhs_should_be_register) {
1437 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };
1438 branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv);
1520 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1521 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
14391522 }
14401523 dst_mcv = rhs;
14411524
......@@ -1455,12 +1538,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14551538 lhs_mcv = dst_mcv;
14561539 } else {
14571540 // Move LHS and RHS to register
1458 const regs = try self.register_manager.allocRegs(2, .{ inst, op_rhs }, &.{});
1541 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{});
14591542 lhs_mcv = MCValue{ .register = regs[0] };
14601543 rhs_mcv = MCValue{ .register = regs[1] };
14611544 dst_mcv = lhs_mcv;
14621545
1463 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);
1546 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
14641547 }
14651548 } else if (lhs_should_be_register) {
14661549 // RHS is immediate
......@@ -1485,14 +1568,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14851568
14861569 // Move the operands to the newly allocated registers
14871570 if (lhs_mcv == .register and !lhs_is_register) {
1488 try self.genSetReg(op_lhs.src, op_lhs.ty, lhs_mcv.register, lhs);
1571 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
14891572 }
14901573 if (rhs_mcv == .register and !rhs_is_register) {
1491 try self.genSetReg(op_rhs.src, op_rhs.ty, rhs_mcv.register, rhs);
1574 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
14921575 }
14931576
14941577 try self.genArmBinOpCode(
1495 inst.src,
14961578 dst_mcv.register,
14971579 lhs_mcv,
14981580 rhs_mcv,
......@@ -1504,14 +1586,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15041586
15051587 fn genArmBinOpCode(
15061588 self: *Self,
1507 src: LazySrcLoc,
15081589 dst_reg: Register,
15091590 lhs_mcv: MCValue,
15101591 rhs_mcv: MCValue,
15111592 swap_lhs_and_rhs: bool,
1512 op: ir.Inst.Tag,
1593 op: Air.Inst.Tag,
15131594 ) !void {
1514 _ = src;
15151595 assert(lhs_mcv == .register or rhs_mcv == .register);
15161596
15171597 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;
......@@ -1560,14 +1640,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15601640 }
15611641 }
15621642
1563 fn genArmMul(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst) !MCValue {
1643 fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
15641644 const lhs = try self.resolveInst(op_lhs);
15651645 const rhs = try self.resolveInst(op_rhs);
15661646
15671647 const lhs_is_register = lhs == .register;
15681648 const rhs_is_register = rhs == .register;
1569 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);
1570 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);
1649 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1650 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
15711651
15721652 // Destination must be a register
15731653 // LHS must be a register
......@@ -1581,15 +1661,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15811661 if (reuse_lhs) {
15821662 // Allocate 0 or 1 registers
15831663 if (!rhs_is_register) {
1584 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };
1585 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);
1664 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1665 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
15861666 }
15871667 dst_mcv = lhs;
15881668 } else if (reuse_rhs) {
15891669 // Allocate 0 or 1 registers
15901670 if (!lhs_is_register) {
1591 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };
1592 branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv);
1671 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1672 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
15931673 }
15941674 dst_mcv = rhs;
15951675 } else {
......@@ -1606,21 +1686,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16061686 lhs_mcv = dst_mcv;
16071687 } else {
16081688 // Move LHS and RHS to register
1609 const regs = try self.register_manager.allocRegs(2, .{ inst, op_rhs }, &.{});
1689 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{});
16101690 lhs_mcv = MCValue{ .register = regs[0] };
16111691 rhs_mcv = MCValue{ .register = regs[1] };
16121692 dst_mcv = lhs_mcv;
16131693
1614 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);
1694 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
16151695 }
16161696 }
16171697
16181698 // Move the operands to the newly allocated registers
16191699 if (!lhs_is_register) {
1620 try self.genSetReg(op_lhs.src, op_lhs.ty, lhs_mcv.register, lhs);
1700 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
16211701 }
16221702 if (!rhs_is_register) {
1623 try self.genSetReg(op_rhs.src, op_rhs.ty, rhs_mcv.register, rhs);
1703 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
16241704 }
16251705
16261706 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());
......@@ -1630,7 +1710,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16301710 /// Perform "binary" operators, excluding comparisons.
16311711 /// Currently, the following ops are supported:
16321712 /// ADD, SUB, XOR, OR, AND
1633 fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst) !MCValue {
1713 fn genX8664BinMath(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
16341714 // We'll handle these ops in two steps.
16351715 // 1) Prepare an output location (register or memory)
16361716 // This location will be the location of the operand that dies (if one exists)
......@@ -1653,8 +1733,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16531733 // as the result MCValue.
16541734 var dst_mcv: MCValue = undefined;
16551735 var src_mcv: MCValue = undefined;
1656 var src_inst: *ir.Inst = undefined;
1657 if (self.reuseOperand(inst, 0, lhs)) {
1736 var src_inst: Air.Inst.Ref = undefined;
1737 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
16581738 // LHS dies; use it as the destination.
16591739 // Both operands cannot be memory.
16601740 src_inst = op_rhs;
......@@ -1665,7 +1745,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16651745 dst_mcv = lhs;
16661746 src_mcv = rhs;
16671747 }
1668 } else if (self.reuseOperand(inst, 1, rhs)) {
1748 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {
16691749 // RHS dies; use it as the destination.
16701750 // Both operands cannot be memory.
16711751 src_inst = op_lhs;
......@@ -1695,22 +1775,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16951775 switch (src_mcv) {
16961776 .immediate => |imm| {
16971777 if (imm > math.maxInt(u31)) {
1698 src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) };
1778 src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.u64), src_mcv) };
16991779 }
17001780 },
17011781 else => {},
17021782 }
17031783
17041784 // Now for step 2, we perform the actual op
1705 switch (inst.tag) {
1785 const inst_ty = self.air.typeOfIndex(inst);
1786 const air_tags = self.air.instructions.items(.tag);
1787 switch (air_tags[inst]) {
17061788 // TODO: Generate wrapping and non-wrapping versions separately
1707 .add, .addwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 0, 0x00),
1708 .bool_or, .bit_or => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 1, 0x08),
1709 .bool_and, .bit_and => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 4, 0x20),
1710 .sub, .subwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 5, 0x28),
1711 .xor, .not => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 6, 0x30),
1789 .add, .addwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 0, 0x00),
1790 .bool_or, .bit_or => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 1, 0x08),
1791 .bool_and, .bit_and => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 4, 0x20),
1792 .sub, .subwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 5, 0x28),
1793 .xor, .not => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 6, 0x30),
17121794
1713 .mul, .mulwrap => try self.genX8664Imul(inst.src, inst.ty, dst_mcv, src_mcv),
1795 .mul, .mulwrap => try self.genX8664Imul(inst_ty, dst_mcv, src_mcv),
17141796 else => unreachable,
17151797 }
17161798
......@@ -1718,16 +1800,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17181800 }
17191801
17201802 /// Wrap over Instruction.encodeInto to translate errors
1721 fn encodeX8664Instruction(
1722 self: *Self,
1723 src: LazySrcLoc,
1724 inst: Instruction,
1725 ) !void {
1803 fn encodeX8664Instruction(self: *Self, inst: Instruction) !void {
17261804 inst.encodeInto(self.code) catch |err| {
17271805 if (err == error.OutOfMemory)
17281806 return error.OutOfMemory
17291807 else
1730 return self.fail(src, "Instruction.encodeInto failed because {s}", .{@errorName(err)});
1808 return self.fail("Instruction.encodeInto failed because {s}", .{@errorName(err)});
17311809 };
17321810 }
17331811
......@@ -1799,7 +1877,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17991877 /// d3 /opx | *r/m16/32/64*, CL (for context, CL is register 1)
18001878 fn genX8664BinMathCode(
18011879 self: *Self,
1802 src: LazySrcLoc,
18031880 dst_ty: Type,
18041881 dst_mcv: MCValue,
18051882 src_mcv: MCValue,
......@@ -1817,7 +1894,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18171894 .register => |dst_reg| {
18181895 switch (src_mcv) {
18191896 .none => unreachable,
1820 .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),
1897 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
18211898 .dead, .unreach => unreachable,
18221899 .ptr_stack_offset => unreachable,
18231900 .ptr_embedded_in_code => unreachable,
......@@ -1871,7 +1948,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18711948 }
18721949 },
18731950 .embedded_in_code, .memory => {
1874 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
1951 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
18751952 },
18761953 .stack_offset => |off| {
18771954 // register, indirect use mr + 3
......@@ -1879,7 +1956,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18791956 const abi_size = dst_ty.abiSize(self.target.*);
18801957 const adj_off = off + abi_size;
18811958 if (off > math.maxInt(i32)) {
1882 return self.fail(src, "stack offset too large", .{});
1959 return self.fail("stack offset too large", .{});
18831960 }
18841961 const encoder = try X8664Encoder.init(self.code, 7);
18851962 encoder.rex(.{
......@@ -1902,40 +1979,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19021979 }
19031980 },
19041981 .compare_flags_unsigned => {
1905 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
1982 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
19061983 },
19071984 .compare_flags_signed => {
1908 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
1985 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
19091986 },
19101987 }
19111988 },
19121989 .stack_offset => |off| {
19131990 switch (src_mcv) {
19141991 .none => unreachable,
1915 .undef => return self.genSetStack(src, dst_ty, off, .undef),
1992 .undef => return self.genSetStack(dst_ty, off, .undef),
19161993 .dead, .unreach => unreachable,
19171994 .ptr_stack_offset => unreachable,
19181995 .ptr_embedded_in_code => unreachable,
19191996 .register => |src_reg| {
1920 try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1);
1997 try self.genX8664ModRMRegToStack(dst_ty, off, src_reg, mr + 0x1);
19211998 },
19221999 .immediate => |imm| {
19232000 _ = imm;
1924 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{});
2001 return self.fail("TODO implement x86 ADD/SUB/CMP source immediate", .{});
19252002 },
19262003 .embedded_in_code, .memory, .stack_offset => {
1927 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
2004 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
19282005 },
19292006 .compare_flags_unsigned => {
1930 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
2007 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
19312008 },
19322009 .compare_flags_signed => {
1933 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
2010 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
19342011 },
19352012 }
19362013 },
19372014 .embedded_in_code, .memory => {
1938 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
2015 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});
19392016 },
19402017 }
19412018 }
......@@ -1943,7 +2020,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19432020 /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
19442021 fn genX8664Imul(
19452022 self: *Self,
1946 src: LazySrcLoc,
19472023 dst_ty: Type,
19482024 dst_mcv: MCValue,
19492025 src_mcv: MCValue,
......@@ -1959,7 +2035,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19592035 .register => |dst_reg| {
19602036 switch (src_mcv) {
19612037 .none => unreachable,
1962 .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),
2038 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
19632039 .dead, .unreach => unreachable,
19642040 .ptr_stack_offset => unreachable,
19652041 .ptr_embedded_in_code => unreachable,
......@@ -2025,31 +2101,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20252101 );
20262102 encoder.imm32(@intCast(i32, imm));
20272103 } else {
2028 const src_reg = try self.copyToTmpRegister(src, dst_ty, src_mcv);
2029 return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg });
2104 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
2105 return self.genX8664Imul(dst_ty, dst_mcv, MCValue{ .register = src_reg });
20302106 }
20312107 },
20322108 .embedded_in_code, .memory, .stack_offset => {
2033 return self.fail(src, "TODO implement x86 multiply source memory", .{});
2109 return self.fail("TODO implement x86 multiply source memory", .{});
20342110 },
20352111 .compare_flags_unsigned => {
2036 return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{});
2112 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
20372113 },
20382114 .compare_flags_signed => {
2039 return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{});
2115 return self.fail("TODO implement x86 multiply source compare flag (signed)", .{});
20402116 },
20412117 }
20422118 },
20432119 .stack_offset => |off| {
20442120 switch (src_mcv) {
20452121 .none => unreachable,
2046 .undef => return self.genSetStack(src, dst_ty, off, .undef),
2122 .undef => return self.genSetStack(dst_ty, off, .undef),
20472123 .dead, .unreach => unreachable,
20482124 .ptr_stack_offset => unreachable,
20492125 .ptr_embedded_in_code => unreachable,
20502126 .register => |src_reg| {
20512127 // copy dst to a register
2052 const dst_reg = try self.copyToTmpRegister(src, dst_ty, dst_mcv);
2128 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
20532129 // multiply into dst_reg
20542130 // register, register
20552131 // Use the following imul opcode
......@@ -2067,34 +2143,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20672143 src_reg.low_id(),
20682144 );
20692145 // copy dst_reg back out
2070 return self.genSetStack(src, dst_ty, off, MCValue{ .register = dst_reg });
2146 return self.genSetStack(dst_ty, off, MCValue{ .register = dst_reg });
20712147 },
20722148 .immediate => |imm| {
20732149 _ = imm;
2074 return self.fail(src, "TODO implement x86 multiply source immediate", .{});
2150 return self.fail("TODO implement x86 multiply source immediate", .{});
20752151 },
20762152 .embedded_in_code, .memory, .stack_offset => {
2077 return self.fail(src, "TODO implement x86 multiply source memory", .{});
2153 return self.fail("TODO implement x86 multiply source memory", .{});
20782154 },
20792155 .compare_flags_unsigned => {
2080 return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{});
2156 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
20812157 },
20822158 .compare_flags_signed => {
2083 return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{});
2159 return self.fail("TODO implement x86 multiply source compare flag (signed)", .{});
20842160 },
20852161 }
20862162 },
20872163 .embedded_in_code, .memory => {
2088 return self.fail(src, "TODO implement x86 multiply destination memory", .{});
2164 return self.fail("TODO implement x86 multiply destination memory", .{});
20892165 },
20902166 }
20912167 }
20922168
2093 fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void {
2169 fn genX8664ModRMRegToStack(self: *Self, ty: Type, off: u32, reg: Register, opcode: u8) !void {
20942170 const abi_size = ty.abiSize(self.target.*);
20952171 const adj_off = off + abi_size;
20962172 if (off > math.maxInt(i32)) {
2097 return self.fail(src, "stack offset too large", .{});
2173 return self.fail("stack offset too large", .{});
20982174 }
20992175
21002176 const i_adj_off = -@intCast(i32, adj_off);
......@@ -2121,8 +2197,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21212197 }
21222198 }
21232199
2124 fn genArgDbgInfo(self: *Self, inst: *ir.Inst.Arg, mcv: MCValue) !void {
2125 const name_with_null = inst.name[0 .. mem.lenZ(inst.name) + 1];
2200 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2201 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
2202 const zir = &self.mod_fn.owner_decl.namespace.file_scope.zir;
2203 const name = zir.nullTerminatedString(ty_str.str);
2204 const name_with_null = name.ptr[0 .. name.len + 1];
2205 const ty = self.air.getRefType(ty_str.ty);
21262206
21272207 switch (mcv) {
21282208 .register => |reg| {
......@@ -2135,7 +2215,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21352215 reg.dwarfLocOp(),
21362216 });
21372217 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);
2138 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
2218 try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
21392219 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
21402220 },
21412221 .none => {},
......@@ -2146,12 +2226,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21462226 .dwarf => |dbg_out| {
21472227 switch (arch) {
21482228 .arm, .armeb => {
2149 const ty = inst.base.ty;
21502229 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
2151 return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{ty});
2230 return self.fail("type '{}' too big to fit into stack frame", .{ty});
21522231 };
21532232 const adjusted_stack_offset = math.negateCast(offset + abi_size) catch {
2154 return self.fail(inst.base.src, "Stack offset too large for arguments", .{});
2233 return self.fail("Stack offset too large for arguments", .{});
21552234 };
21562235
21572236 try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter);
......@@ -2167,7 +2246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21672246 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);
21682247
21692248 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);
2170 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
2249 try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
21712250 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
21722251 },
21732252 else => {},
......@@ -2180,23 +2259,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21802259 }
21812260 }
21822261
2183 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
2262 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
21842263 const arg_index = self.arg_index;
21852264 self.arg_index += 1;
21862265
2266 const ty = self.air.typeOfIndex(inst);
2267
21872268 const result = self.args[arg_index];
21882269 const mcv = switch (arch) {
21892270 // TODO support stack-only arguments on all target architectures
21902271 .arm, .armeb, .aarch64, .aarch64_32, .aarch64_be => switch (result) {
21912272 // Copy registers to the stack
21922273 .register => |reg| blk: {
2193 const ty = inst.base.ty;
21942274 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
2195 return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{ty});
2275 return self.fail("type '{}' too big to fit into stack frame", .{ty});
21962276 };
21972277 const abi_align = ty.abiAlignment(self.target.*);
2198 const stack_offset = try self.allocMem(&inst.base, abi_size, abi_align);
2199 try self.genSetStack(inst.base.src, ty, stack_offset, MCValue{ .register = reg });
2278 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
2279 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
22002280
22012281 break :blk MCValue{ .stack_offset = stack_offset };
22022282 },
......@@ -2206,20 +2286,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22062286 };
22072287 try self.genArgDbgInfo(inst, mcv);
22082288
2209 if (inst.base.isUnused())
2210 return MCValue.dead;
2289 if (self.liveness.isUnused(inst))
2290 return self.finishAirBookkeeping();
22112291
22122292 switch (mcv) {
22132293 .register => |reg| {
2214 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
2294 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst);
22152295 },
22162296 else => {},
22172297 }
22182298
2219 return mcv;
2299 return self.finishAir(inst, mcv, .{ .none, .none, .none });
22202300 }
22212301
2222 fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue {
2302 fn airBreakpoint(self: *Self) !void {
22232303 switch (arch) {
22242304 .i386, .x86_64 => {
22252305 try self.code.append(0xcc); // int3
......@@ -2233,13 +2313,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22332313 .aarch64 => {
22342314 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());
22352315 },
2236 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
2316 else => return self.fail("TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
22372317 }
2238 return .none;
2318 return self.finishAirBookkeeping();
22392319 }
22402320
2241 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
2242 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
2321 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2322 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2323 const fn_ty = self.air.typeOf(pl_op.operand);
2324 const callee = pl_op.operand;
2325 const extra = self.air.extraData(Air.Call, pl_op.payload);
2326 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
2327
2328 var info = try self.resolveCallingConventionValues(fn_ty);
22432329 defer info.deinit(self);
22442330
22452331 // Due to incremental compilation, how function calls are generated depends
......@@ -2248,26 +2334,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22482334 switch (arch) {
22492335 .x86_64 => {
22502336 for (info.args) |mc_arg, arg_i| {
2251 const arg = inst.args[arg_i];
2252 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2337 const arg = args[arg_i];
2338 const arg_ty = self.air.typeOf(arg);
2339 const arg_mcv = try self.resolveInst(args[arg_i]);
22532340 // Here we do not use setRegOrMem even though the logic is similar, because
22542341 // the function call will move the stack pointer, so the offsets are different.
22552342 switch (mc_arg) {
22562343 .none => continue,
22572344 .register => |reg| {
22582345 try self.register_manager.getReg(reg, null);
2259 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2346 try self.genSetReg(arg_ty, reg, arg_mcv);
22602347 },
22612348 .stack_offset => |off| {
22622349 // Here we need to emit instructions like this:
22632350 // mov qword ptr [rsp + stack_offset], x
2264 try self.genSetStack(arg.src, arg.ty, off, arg_mcv);
2351 try self.genSetStack(arg_ty, off, arg_mcv);
22652352 },
22662353 .ptr_stack_offset => {
2267 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2354 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
22682355 },
22692356 .ptr_embedded_in_code => {
2270 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2357 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
22712358 },
22722359 .undef => unreachable,
22732360 .immediate => unreachable,
......@@ -2280,7 +2367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22802367 }
22812368 }
22822369
2283 if (inst.func.value()) |func_value| {
2370 if (self.air.value(callee)) |func_value| {
22842371 if (func_value.castTag(.function)) |func_payload| {
22852372 const func = func_payload.data;
22862373
......@@ -2299,18 +2386,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22992386 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
23002387 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
23012388 } else if (func_value.castTag(.extern_fn)) |_| {
2302 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
2389 return self.fail("TODO implement calling extern functions", .{});
23032390 } else {
2304 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
2391 return self.fail("TODO implement calling bitcasted functions", .{});
23052392 }
23062393 } else {
2307 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2394 return self.fail("TODO implement calling runtime known function pointer", .{});
23082395 }
23092396 },
23102397 .riscv64 => {
2311 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
2398 if (info.args.len > 0) return self.fail("TODO implement fn args for {}", .{self.target.cpu.arch});
23122399
2313 if (inst.func.value()) |func_value| {
2400 if (self.air.value(callee)) |func_value| {
23142401 if (func_value.castTag(.function)) |func_payload| {
23152402 const func = func_payload.data;
23162403
......@@ -2324,21 +2411,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23242411 else
23252412 unreachable;
23262413
2327 try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr });
2414 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
23282415 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
23292416 } else if (func_value.castTag(.extern_fn)) |_| {
2330 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
2417 return self.fail("TODO implement calling extern functions", .{});
23312418 } else {
2332 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
2419 return self.fail("TODO implement calling bitcasted functions", .{});
23332420 }
23342421 } else {
2335 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2422 return self.fail("TODO implement calling runtime known function pointer", .{});
23362423 }
23372424 },
23382425 .arm, .armeb => {
23392426 for (info.args) |mc_arg, arg_i| {
2340 const arg = inst.args[arg_i];
2341 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2427 const arg = args[arg_i];
2428 const arg_ty = self.air.typeOf(arg);
2429 const arg_mcv = try self.resolveInst(args[arg_i]);
23422430
23432431 switch (mc_arg) {
23442432 .none => continue,
......@@ -2352,21 +2440,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23522440 .compare_flags_unsigned => unreachable,
23532441 .register => |reg| {
23542442 try self.register_manager.getReg(reg, null);
2355 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2443 try self.genSetReg(arg_ty, reg, arg_mcv);
23562444 },
23572445 .stack_offset => {
2358 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
2446 return self.fail("TODO implement calling with parameters in memory", .{});
23592447 },
23602448 .ptr_stack_offset => {
2361 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2449 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
23622450 },
23632451 .ptr_embedded_in_code => {
2364 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2452 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
23652453 },
23662454 }
23672455 }
23682456
2369 if (inst.func.value()) |func_value| {
2457 if (self.air.value(callee)) |func_value| {
23702458 if (func_value.castTag(.function)) |func_payload| {
23712459 const func = func_payload.data;
23722460 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
......@@ -2379,7 +2467,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23792467 else
23802468 unreachable;
23812469
2382 try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr });
2470 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
23832471
23842472 // TODO: add Instruction.supportedOn
23852473 // function for ARM
......@@ -2390,18 +2478,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23902478 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
23912479 }
23922480 } else if (func_value.castTag(.extern_fn)) |_| {
2393 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
2481 return self.fail("TODO implement calling extern functions", .{});
23942482 } else {
2395 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
2483 return self.fail("TODO implement calling bitcasted functions", .{});
23962484 }
23972485 } else {
2398 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2486 return self.fail("TODO implement calling runtime known function pointer", .{});
23992487 }
24002488 },
24012489 .aarch64 => {
24022490 for (info.args) |mc_arg, arg_i| {
2403 const arg = inst.args[arg_i];
2404 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2491 const arg = args[arg_i];
2492 const arg_ty = self.air.typeOf(arg);
2493 const arg_mcv = try self.resolveInst(args[arg_i]);
24052494
24062495 switch (mc_arg) {
24072496 .none => continue,
......@@ -2415,21 +2504,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24152504 .compare_flags_unsigned => unreachable,
24162505 .register => |reg| {
24172506 try self.register_manager.getReg(reg, null);
2418 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2507 try self.genSetReg(arg_ty, reg, arg_mcv);
24192508 },
24202509 .stack_offset => {
2421 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
2510 return self.fail("TODO implement calling with parameters in memory", .{});
24222511 },
24232512 .ptr_stack_offset => {
2424 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2513 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
24252514 },
24262515 .ptr_embedded_in_code => {
2427 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2516 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
24282517 },
24292518 }
24302519 }
24312520
2432 if (inst.func.value()) |func_value| {
2521 if (self.air.value(callee)) |func_value| {
24332522 if (func_value.castTag(.function)) |func_payload| {
24342523 const func = func_payload.data;
24352524 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
......@@ -2442,24 +2531,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24422531 else
24432532 unreachable;
24442533
2445 try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr });
2534 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
24462535
24472536 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
24482537 } else if (func_value.castTag(.extern_fn)) |_| {
2449 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
2538 return self.fail("TODO implement calling extern functions", .{});
24502539 } else {
2451 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
2540 return self.fail("TODO implement calling bitcasted functions", .{});
24522541 }
24532542 } else {
2454 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2543 return self.fail("TODO implement calling runtime known function pointer", .{});
24552544 }
24562545 },
2457 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
2546 else => return self.fail("TODO implement call for {}", .{self.target.cpu.arch}),
24582547 }
24592548 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
24602549 for (info.args) |mc_arg, arg_i| {
2461 const arg = inst.args[arg_i];
2462 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2550 const arg = args[arg_i];
2551 const arg_ty = self.air.typeOf(arg);
2552 const arg_mcv = try self.resolveInst(args[arg_i]);
24632553 // Here we do not use setRegOrMem even though the logic is similar, because
24642554 // the function call will move the stack pointer, so the offsets are different.
24652555 switch (mc_arg) {
......@@ -2470,18 +2560,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24702560 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
24712561 else => unreachable,
24722562 }
2473 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2563 try self.genSetReg(arg_ty, reg, arg_mcv);
24742564 },
24752565 .stack_offset => {
24762566 // Here we need to emit instructions like this:
24772567 // mov qword ptr [rsp + stack_offset], x
2478 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
2568 return self.fail("TODO implement calling with parameters in memory", .{});
24792569 },
24802570 .ptr_stack_offset => {
2481 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2571 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
24822572 },
24832573 .ptr_embedded_in_code => {
2484 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2574 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
24852575 },
24862576 .undef => unreachable,
24872577 .immediate => unreachable,
......@@ -2494,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24942584 }
24952585 }
24962586
2497 if (inst.func.value()) |func_value| {
2587 if (self.air.value(callee)) |func_value| {
24982588 if (func_value.castTag(.function)) |func_payload| {
24992589 const func = func_payload.data;
25002590 const got_addr = blk: {
......@@ -2508,13 +2598,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25082598 };
25092599 switch (arch) {
25102600 .x86_64 => {
2511 try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .memory = got_addr });
2601 try self.genSetReg(Type.initTag(.u64), .rax, .{ .memory = got_addr });
25122602 // callq *%rax
25132603 try self.code.ensureCapacity(self.code.items.len + 2);
25142604 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
25152605 },
25162606 .aarch64 => {
2517 try self.genSetReg(inst.base.src, Type.initTag(.u64), .x30, .{ .memory = got_addr });
2607 try self.genSetReg(Type.initTag(.u64), .x30, .{ .memory = got_addr });
25182608 // blr x30
25192609 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
25202610 },
......@@ -2550,35 +2640,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25502640 } },
25512641 });
25522642 } else {
2553 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
2643 return self.fail("TODO implement calling bitcasted functions", .{});
25542644 }
25552645 } else {
2556 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2646 return self.fail("TODO implement calling runtime known function pointer", .{});
25572647 }
25582648 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
25592649 switch (arch) {
25602650 .x86_64 => {
25612651 for (info.args) |mc_arg, arg_i| {
2562 const arg = inst.args[arg_i];
2563 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2652 const arg = args[arg_i];
2653 const arg_ty = self.air.typeOf(arg);
2654 const arg_mcv = try self.resolveInst(args[arg_i]);
25642655 // Here we do not use setRegOrMem even though the logic is similar, because
25652656 // the function call will move the stack pointer, so the offsets are different.
25662657 switch (mc_arg) {
25672658 .none => continue,
25682659 .register => |reg| {
25692660 try self.register_manager.getReg(reg, null);
2570 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2661 try self.genSetReg(arg_ty, reg, arg_mcv);
25712662 },
25722663 .stack_offset => {
25732664 // Here we need to emit instructions like this:
25742665 // mov qword ptr [rsp + stack_offset], x
2575 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
2666 return self.fail("TODO implement calling with parameters in memory", .{});
25762667 },
25772668 .ptr_stack_offset => {
2578 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2669 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
25792670 },
25802671 .ptr_embedded_in_code => {
2581 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2672 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
25822673 },
25832674 .undef => unreachable,
25842675 .immediate => unreachable,
......@@ -2590,7 +2681,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25902681 .compare_flags_unsigned => unreachable,
25912682 }
25922683 }
2593 if (inst.func.value()) |func_value| {
2684 if (self.air.value(callee)) |func_value| {
25942685 if (func_value.castTag(.function)) |func_payload| {
25952686 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
25962687 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
......@@ -2601,15 +2692,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26012692 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
26022693 const fn_got_addr = got_addr + got_index * ptr_bytes;
26032694 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));
2604 } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{});
2695 } else return self.fail("TODO implement calling extern fn on plan9", .{});
26052696 } else {
2606 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2697 return self.fail("TODO implement calling runtime known function pointer", .{});
26072698 }
26082699 },
26092700 .aarch64 => {
26102701 for (info.args) |mc_arg, arg_i| {
2611 const arg = inst.args[arg_i];
2612 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
2702 const arg = args[arg_i];
2703 const arg_ty = self.air.typeOf(arg);
2704 const arg_mcv = try self.resolveInst(args[arg_i]);
26132705
26142706 switch (mc_arg) {
26152707 .none => continue,
......@@ -2623,20 +2715,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26232715 .compare_flags_unsigned => unreachable,
26242716 .register => |reg| {
26252717 try self.register_manager.getReg(reg, null);
2626 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2718 try self.genSetReg(arg_ty, reg, arg_mcv);
26272719 },
26282720 .stack_offset => {
2629 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
2721 return self.fail("TODO implement calling with parameters in memory", .{});
26302722 },
26312723 .ptr_stack_offset => {
2632 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2724 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
26332725 },
26342726 .ptr_embedded_in_code => {
2635 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2727 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
26362728 },
26372729 }
26382730 }
2639 if (inst.func.value()) |func_value| {
2731 if (self.air.value(callee)) |func_value| {
26402732 if (func_value.castTag(.function)) |func_payload| {
26412733 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
26422734 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
......@@ -2644,65 +2736,84 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26442736 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
26452737 const fn_got_addr = got_addr + got_index * ptr_bytes;
26462738
2647 try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
2739 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
26482740
26492741 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
26502742 } else if (func_value.castTag(.extern_fn)) |_| {
2651 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
2743 return self.fail("TODO implement calling extern functions", .{});
26522744 } else {
2653 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
2745 return self.fail("TODO implement calling bitcasted functions", .{});
26542746 }
26552747 } else {
2656 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
2748 return self.fail("TODO implement calling runtime known function pointer", .{});
26572749 }
26582750 },
2659 else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
2751 else => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
26602752 }
26612753 } else unreachable;
26622754
2663 switch (info.return_value) {
2664 .register => |reg| {
2665 if (Register.allocIndex(reg) == null) {
2666 // Save function return value in a callee saved register
2667 return try self.copyToNewRegister(&inst.base, info.return_value);
2668 }
2669 },
2670 else => {},
2671 }
2755 const result: MCValue = result: {
2756 switch (info.return_value) {
2757 .register => |reg| {
2758 if (Register.allocIndex(reg) == null) {
2759 // Save function return value in a callee saved register
2760 break :result try self.copyToNewRegister(inst, info.return_value);
2761 }
2762 },
2763 else => {},
2764 }
2765 break :result info.return_value;
2766 };
26722767
2673 return info.return_value;
2768 if (args.len <= Liveness.bpi - 2) {
2769 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2770 buf[0] = callee;
2771 std.mem.copy(Air.Inst.Ref, buf[1..], args);
2772 return self.finishAir(inst, result, buf);
2773 }
2774 var bt = try self.iterateBigTomb(inst, 1 + args.len);
2775 bt.feed(callee);
2776 for (args) |arg| {
2777 bt.feed(arg);
2778 }
2779 return bt.finishAir(result);
26742780 }
26752781
2676 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2677 const operand = try self.resolveInst(inst.operand);
2678 switch (operand) {
2679 .unreach => unreachable,
2680 .dead => unreachable,
2681 .none => return .none,
2682
2683 .immediate,
2684 .register,
2685 .ptr_stack_offset,
2686 .ptr_embedded_in_code,
2687 .compare_flags_unsigned,
2688 .compare_flags_signed,
2689 => {
2690 const stack_offset = try self.allocMemPtr(&inst.base);
2691 try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);
2692 return MCValue{ .ptr_stack_offset = stack_offset };
2693 },
2782 fn airRef(self: *Self, inst: Air.Inst.Index) !void {
2783 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2784 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2785 const operand_ty = self.air.typeOf(ty_op.operand);
2786 const operand = try self.resolveInst(ty_op.operand);
2787 switch (operand) {
2788 .unreach => unreachable,
2789 .dead => unreachable,
2790 .none => break :result MCValue{ .none = {} },
2791
2792 .immediate,
2793 .register,
2794 .ptr_stack_offset,
2795 .ptr_embedded_in_code,
2796 .compare_flags_unsigned,
2797 .compare_flags_signed,
2798 => {
2799 const stack_offset = try self.allocMemPtr(inst);
2800 try self.genSetStack(operand_ty, stack_offset, operand);
2801 break :result MCValue{ .ptr_stack_offset = stack_offset };
2802 },
26942803
2695 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
2696 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
2697 .memory => |vaddr| return MCValue{ .immediate = vaddr },
2804 .stack_offset => |offset| break :result MCValue{ .ptr_stack_offset = offset },
2805 .embedded_in_code => |offset| break :result MCValue{ .ptr_embedded_in_code = offset },
2806 .memory => |vaddr| break :result MCValue{ .immediate = vaddr },
26982807
2699 .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),
2700 }
2808 .undef => return self.fail("TODO implement ref on an undefined value", .{}),
2809 }
2810 };
2811 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
27012812 }
27022813
2703 fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue {
2814 fn ret(self: *Self, mcv: MCValue) !void {
27042815 const ret_ty = self.fn_type.fnReturnType();
2705 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
2816 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
27062817 switch (arch) {
27072818 .i386 => {
27082819 try self.code.append(0xc3); // ret
......@@ -2728,58 +2839,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27282839 try self.code.resize(self.code.items.len + 4);
27292840 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
27302841 },
2731 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
2842 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),
27322843 }
2733 return .unreach;
27342844 }
27352845
2736 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2737 const operand = try self.resolveInst(inst.operand);
2738 return self.ret(inst.base.src, operand);
2846 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
2847 const un_op = self.air.instructions.items(.data)[inst].un_op;
2848 const operand = try self.resolveInst(un_op);
2849 try self.ret(operand);
2850 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
27392851 }
27402852
2741 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
2742 return self.ret(inst.base.src, .none);
2743 }
2744
2745 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: math.CompareOperator) !MCValue {
2746 // No side effects, so if it's unreferenced, do nothing.
2747 if (inst.base.isUnused())
2748 return MCValue{ .dead = {} };
2749 if (inst.lhs.ty.zigTypeTag() == .ErrorSet or inst.rhs.ty.zigTypeTag() == .ErrorSet)
2750 return self.fail(inst.base.src, "TODO implement cmp for errors", .{});
2751 switch (arch) {
2752 .x86_64 => {
2853 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2854 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2855 if (self.liveness.isUnused(inst))
2856 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2857 const ty = self.air.typeOf(bin_op.lhs);
2858 assert(ty.eql(self.air.typeOf(bin_op.rhs)));
2859 if (ty.zigTypeTag() == .ErrorSet)
2860 return self.fail("TODO implement cmp for errors", .{});
2861
2862 const lhs = try self.resolveInst(bin_op.lhs);
2863 const rhs = try self.resolveInst(bin_op.rhs);
2864 const result: MCValue = switch (arch) {
2865 .x86_64 => result: {
27532866 try self.code.ensureCapacity(self.code.items.len + 8);
27542867
2755 const lhs = try self.resolveInst(inst.lhs);
2756 const rhs = try self.resolveInst(inst.rhs);
2757
27582868 // There are 2 operands, destination and source.
27592869 // Either one, but not both, can be a memory operand.
27602870 // Source operand can be an immediate, 8 bits or 32 bits.
27612871 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
2762 try self.copyToNewRegister(&inst.base, lhs)
2872 try self.copyToNewRegister(inst, lhs)
27632873 else
27642874 lhs;
27652875 // This instruction supports only signed 32-bit immediates at most.
2766 const src_mcv = try self.limitImmediateType(inst.rhs, i32);
2876 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
27672877
2768 try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);
2769 const info = inst.lhs.ty.intInfo(self.target.*);
2770 return switch (info.signedness) {
2878 try self.genX8664BinMathCode(Type.initTag(.bool), dst_mcv, src_mcv, 7, 0x38);
2879 const info = ty.intInfo(self.target.*);
2880 break :result switch (info.signedness) {
27712881 .signed => MCValue{ .compare_flags_signed = op },
27722882 .unsigned => MCValue{ .compare_flags_unsigned = op },
27732883 };
27742884 },
2775 .arm, .armeb => {
2776 const lhs = try self.resolveInst(inst.lhs);
2777 const rhs = try self.resolveInst(inst.rhs);
2778
2885 .arm, .armeb => result: {
27792886 const lhs_is_register = lhs == .register;
27802887 const rhs_is_register = rhs == .register;
27812888 // lhs should always be a register
2782 const rhs_should_be_register = try self.armOperandShouldBeRegister(inst.rhs.src, rhs);
2889 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
27832890
27842891 var lhs_mcv = lhs;
27852892 var rhs_mcv = rhs;
......@@ -2787,53 +2894,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27872894 // Allocate registers
27882895 if (rhs_should_be_register) {
27892896 if (!lhs_is_register and !rhs_is_register) {
2790 const regs = try self.register_manager.allocRegs(2, .{ inst.rhs, inst.lhs }, &.{});
2897 const regs = try self.register_manager.allocRegs(2, .{
2898 Air.refToIndex(bin_op.rhs).?, Air.refToIndex(bin_op.lhs).?,
2899 }, &.{});
27912900 lhs_mcv = MCValue{ .register = regs[0] };
27922901 rhs_mcv = MCValue{ .register = regs[1] };
27932902 } else if (!rhs_is_register) {
2794 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.rhs, &.{}) };
2903 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.rhs).?, &.{}) };
27952904 }
27962905 }
27972906 if (!lhs_is_register) {
2798 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.lhs, &.{}) };
2907 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?, &.{}) };
27992908 }
28002909
28012910 // Move the operands to the newly allocated registers
28022911 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
28032912 if (lhs_mcv == .register and !lhs_is_register) {
2804 try self.genSetReg(inst.lhs.src, inst.lhs.ty, lhs_mcv.register, lhs);
2805 branch.inst_table.putAssumeCapacity(inst.lhs, lhs);
2913 try self.genSetReg(ty, lhs_mcv.register, lhs);
2914 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs);
28062915 }
28072916 if (rhs_mcv == .register and !rhs_is_register) {
2808 try self.genSetReg(inst.rhs.src, inst.rhs.ty, rhs_mcv.register, rhs);
2809 branch.inst_table.putAssumeCapacity(inst.rhs, rhs);
2917 try self.genSetReg(ty, rhs_mcv.register, rhs);
2918 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs);
28102919 }
28112920
28122921 // The destination register is not present in the cmp instruction
2813 try self.genArmBinOpCode(inst.base.src, undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
2922 try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
28142923
2815 const info = inst.lhs.ty.intInfo(self.target.*);
2816 return switch (info.signedness) {
2924 const info = ty.intInfo(self.target.*);
2925 break :result switch (info.signedness) {
28172926 .signed => MCValue{ .compare_flags_signed = op },
28182927 .unsigned => MCValue{ .compare_flags_unsigned = op },
28192928 };
28202929 },
2821 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
2822 }
2930 else => return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}),
2931 };
2932 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
28232933 }
28242934
2825 fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue {
2826 // TODO when reworking AIR memory layout, rework source locations here as
2827 // well to be more efficient, as well as support inlined function calls correctly.
2828 // For now we convert LazySrcLoc to absolute byte offset, to match what the
2829 // existing codegen code expects.
2830 try self.dbgAdvancePCAndLine(inst.line, inst.column);
2831 assert(inst.base.isUnused());
2832 return MCValue.dead;
2935 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
2936 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
2937 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
2938 return self.finishAirBookkeeping();
28332939 }
28342940
2835 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
2836 const cond = try self.resolveInst(inst.condition);
2941 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2942 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2943 const cond = try self.resolveInst(pl_op.operand);
2944 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2945 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2946 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2947 const liveness_condbr = self.liveness.getCondBr(inst);
28372948
28382949 const reloc: Reloc = switch (arch) {
28392950 .i386, .x86_64 => reloc: {
......@@ -2882,7 +2993,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28822993 encoder.disp8(1);
28832994 break :blk 0x84;
28842995 },
2885 else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
2996 else => return self.fail("TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
28862997 };
28872998 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
28882999 const reloc = Reloc{ .rel32 = self.code.items.len };
......@@ -2908,7 +3019,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29083019 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());
29093020 break :blk .ne;
29103021 },
2911 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
3022 else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
29123023 };
29133024
29143025 const reloc = Reloc{
......@@ -2920,7 +3031,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29203031 try self.code.resize(self.code.items.len + 4);
29213032 break :reloc reloc;
29223033 },
2923 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}),
3034 else => return self.fail("TODO implement condbr {}", .{self.target.cpu.arch}),
29243035 };
29253036
29263037 // Capture the state of register and stack allocation state so that we can revert to it.
......@@ -2932,12 +3043,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29323043
29333044 try self.branch_stack.append(.{});
29343045
2935 const then_deaths = inst.thenDeaths();
2936 try self.ensureProcessDeathCapacity(then_deaths.len);
2937 for (then_deaths) |operand| {
3046 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
3047 for (liveness_condbr.then_deaths) |operand| {
29383048 self.processDeath(operand);
29393049 }
2940 try self.genBody(inst.then_body);
3050 try self.genBody(then_body);
29413051
29423052 // Revert to the previous register and stack allocation state.
29433053
......@@ -2953,16 +3063,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29533063 self.next_stack_offset = parent_next_stack_offset;
29543064 self.register_manager.free_registers = parent_free_registers;
29553065
2956 try self.performReloc(inst.base.src, reloc);
3066 try self.performReloc(reloc);
29573067 const else_branch = self.branch_stack.addOneAssumeCapacity();
29583068 else_branch.* = .{};
29593069
2960 const else_deaths = inst.elseDeaths();
2961 try self.ensureProcessDeathCapacity(else_deaths.len);
2962 for (else_deaths) |operand| {
3070 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
3071 for (liveness_condbr.else_deaths) |operand| {
29633072 self.processDeath(operand);
29643073 }
2965 try self.genBody(inst.else_body);
3074 try self.genBody(else_body);
29663075
29673076 // At this point, each branch will possibly have conflicting values for where
29683077 // each instruction is stored. They agree, however, on which instructions are alive/dead.
......@@ -2973,8 +3082,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29733082 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
29743083 // rather than assigning it.
29753084 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
2976 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +
2977 else_branch.inst_table.count());
3085 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
29783086
29793087 const else_slice = else_branch.inst_table.entries.slice();
29803088 const else_keys = else_slice.items(.key);
......@@ -3002,14 +3110,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30023110 }
30033111 }
30043112 };
3005 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });
3113 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
30063114 // TODO make sure the destination stack offset / register does not already have something
30073115 // going on there.
3008 try self.setRegOrMem(inst.base.src, else_key.ty, canon_mcv, else_value);
3116 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
30093117 // TODO track the new register / stack allocation
30103118 }
3011 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +
3012 saved_then_branch.inst_table.count());
3119 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
30133120 const then_slice = saved_then_branch.inst_table.entries.slice();
30143121 const then_keys = then_slice.items(.key);
30153122 const then_values = then_slice.items(.value);
......@@ -3030,70 +3137,175 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30303137 }
30313138 }
30323139 };
3033 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });
3140 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
30343141 // TODO make sure the destination stack offset / register does not already have something
30353142 // going on there.
3036 try self.setRegOrMem(inst.base.src, then_key.ty, parent_mcv, then_value);
3143 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
30373144 // TODO track the new register / stack allocation
30383145 }
30393146
30403147 self.branch_stack.pop().deinit(self.gpa);
30413148
3042 return MCValue.unreach;
3149 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
30433150 }
30443151
3045 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3152 fn isNull(self: *Self, operand: MCValue) !MCValue {
3153 _ = operand;
3154 // Here you can specialize this instruction if it makes sense to, otherwise the default
3155 // will call isNonNull and invert the result.
30463156 switch (arch) {
3047 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
3157 else => return self.fail("TODO call isNonNull and invert the result", .{}),
30483158 }
30493159 }
30503160
3051 fn genIsNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3052 return self.fail(inst.base.src, "TODO load the operand and call genIsNull", .{});
3053 }
3054
3055 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3161 fn isNonNull(self: *Self, operand: MCValue) !MCValue {
3162 _ = operand;
30563163 // Here you can specialize this instruction if it makes sense to, otherwise the default
3057 // will call genIsNull and invert the result.
3164 // will call isNull and invert the result.
30583165 switch (arch) {
3059 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
3166 else => return self.fail("TODO call isNull and invert the result", .{}),
30603167 }
30613168 }
30623169
3063 fn genIsNonNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3064 return self.fail(inst.base.src, "TODO load the operand and call genIsNonNull", .{});
3170 fn isErr(self: *Self, operand: MCValue) !MCValue {
3171 _ = operand;
3172 // Here you can specialize this instruction if it makes sense to, otherwise the default
3173 // will call isNonNull and invert the result.
3174 switch (arch) {
3175 else => return self.fail("TODO call isNonErr and invert the result", .{}),
3176 }
30653177 }
30663178
3067 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3179 fn isNonErr(self: *Self, operand: MCValue) !MCValue {
3180 _ = operand;
3181 // Here you can specialize this instruction if it makes sense to, otherwise the default
3182 // will call isNull and invert the result.
30683183 switch (arch) {
3069 else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),
3184 else => return self.fail("TODO call isErr and invert the result", .{}),
30703185 }
30713186 }
30723187
3073 fn genIsErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3074 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
3188 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
3189 const un_op = self.air.instructions.items(.data)[inst].un_op;
3190 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3191 const operand = try self.resolveInst(un_op);
3192 break :result try self.isNull(operand);
3193 };
3194 return self.finishAir(inst, result, .{ un_op, .none, .none });
30753195 }
30763196
3077 fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3078 switch (arch) {
3079 else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}),
3080 }
3197 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3198 const un_op = self.air.instructions.items(.data)[inst].un_op;
3199 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3200 const operand_ptr = try self.resolveInst(un_op);
3201 const operand: MCValue = blk: {
3202 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3203 // The MCValue that holds the pointer can be re-used as the value.
3204 break :blk operand_ptr;
3205 } else {
3206 break :blk try self.allocRegOrMem(inst, true);
3207 }
3208 };
3209 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3210 break :result try self.isNull(operand);
3211 };
3212 return self.finishAir(inst, result, .{ un_op, .none, .none });
3213 }
3214
3215 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
3216 const un_op = self.air.instructions.items(.data)[inst].un_op;
3217 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3218 const operand = try self.resolveInst(un_op);
3219 break :result try self.isNonNull(operand);
3220 };
3221 return self.finishAir(inst, result, .{ un_op, .none, .none });
3222 }
3223
3224 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3225 const un_op = self.air.instructions.items(.data)[inst].un_op;
3226 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3227 const operand_ptr = try self.resolveInst(un_op);
3228 const operand: MCValue = blk: {
3229 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3230 // The MCValue that holds the pointer can be re-used as the value.
3231 break :blk operand_ptr;
3232 } else {
3233 break :blk try self.allocRegOrMem(inst, true);
3234 }
3235 };
3236 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3237 break :result try self.isNonNull(operand);
3238 };
3239 return self.finishAir(inst, result, .{ un_op, .none, .none });
3240 }
3241
3242 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
3243 const un_op = self.air.instructions.items(.data)[inst].un_op;
3244 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3245 const operand = try self.resolveInst(un_op);
3246 break :result try self.isErr(operand);
3247 };
3248 return self.finishAir(inst, result, .{ un_op, .none, .none });
3249 }
3250
3251 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3252 const un_op = self.air.instructions.items(.data)[inst].un_op;
3253 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3254 const operand_ptr = try self.resolveInst(un_op);
3255 const operand: MCValue = blk: {
3256 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3257 // The MCValue that holds the pointer can be re-used as the value.
3258 break :blk operand_ptr;
3259 } else {
3260 break :blk try self.allocRegOrMem(inst, true);
3261 }
3262 };
3263 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3264 break :result try self.isErr(operand);
3265 };
3266 return self.finishAir(inst, result, .{ un_op, .none, .none });
30813267 }
30823268
3083 fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3084 return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{});
3269 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
3270 const un_op = self.air.instructions.items(.data)[inst].un_op;
3271 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3272 const operand = try self.resolveInst(un_op);
3273 break :result try self.isNonErr(operand);
3274 };
3275 return self.finishAir(inst, result, .{ un_op, .none, .none });
30853276 }
30863277
3087 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
3278 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3279 const un_op = self.air.instructions.items(.data)[inst].un_op;
3280 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3281 const operand_ptr = try self.resolveInst(un_op);
3282 const operand: MCValue = blk: {
3283 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3284 // The MCValue that holds the pointer can be re-used as the value.
3285 break :blk operand_ptr;
3286 } else {
3287 break :blk try self.allocRegOrMem(inst, true);
3288 }
3289 };
3290 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3291 break :result try self.isNonErr(operand);
3292 };
3293 return self.finishAir(inst, result, .{ un_op, .none, .none });
3294 }
3295
3296 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
30883297 // A loop is a setup to be able to jump back to the beginning.
3298 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3299 const loop = self.air.extraData(Air.Block, ty_pl.payload);
3300 const body = self.air.extra[loop.end..][0..loop.data.body_len];
30893301 const start_index = self.code.items.len;
3090 try self.genBody(inst.body);
3091 try self.jump(inst.base.src, start_index);
3092 return MCValue.unreach;
3302 try self.genBody(body);
3303 try self.jump(start_index);
3304 return self.finishAirBookkeeping();
30933305 }
30943306
30953307 /// Send control flow to the `index` of `self.code`.
3096 fn jump(self: *Self, src: LazySrcLoc, index: usize) !void {
3308 fn jump(self: *Self, index: usize) !void {
30973309 switch (arch) {
30983310 .i386, .x86_64 => {
30993311 try self.code.ensureCapacity(self.code.items.len + 5);
......@@ -3110,21 +3322,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31103322 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
31113323 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());
31123324 } else |_| {
3113 return self.fail(src, "TODO: enable larger branch offset", .{});
3325 return self.fail("TODO: enable larger branch offset", .{});
31143326 }
31153327 },
31163328 .aarch64, .aarch64_be, .aarch64_32 => {
31173329 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
31183330 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());
31193331 } else |_| {
3120 return self.fail(src, "TODO: enable larger branch offset", .{});
3332 return self.fail("TODO: enable larger branch offset", .{});
31213333 }
31223334 },
3123 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),
3335 else => return self.fail("TODO implement jump for {}", .{self.target.cpu.arch}),
31243336 }
31253337 }
31263338
3127 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
3339 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
31283340 try self.blocks.putNoClobber(self.gpa, inst, .{
31293341 // A block is a setup to be able to jump to the end.
31303342 .relocs = .{},
......@@ -3138,20 +3350,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31383350 const block_data = self.blocks.getPtr(inst).?;
31393351 defer block_data.relocs.deinit(self.gpa);
31403352
3141 try self.genBody(inst.body);
3353 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3354 const extra = self.air.extraData(Air.Block, ty_pl.payload);
3355 const body = self.air.extra[extra.end..][0..extra.data.body_len];
3356 try self.genBody(body);
31423357
3143 for (block_data.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
3358 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
31443359
3145 return @bitCast(MCValue, block_data.mcv);
3360 const result = @bitCast(MCValue, block_data.mcv);
3361 return self.finishAir(inst, result, .{ .none, .none, .none });
31463362 }
31473363
3148 fn genSwitch(self: *Self, inst: *ir.Inst.SwitchBr) !MCValue {
3364 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
3365 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3366 const condition = pl_op.operand;
31493367 switch (arch) {
3150 else => return self.fail(inst.base.src, "TODO genSwitch for {}", .{self.target.cpu.arch}),
3368 else => return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}),
31513369 }
3370 return self.finishAir(inst, .dead, .{ condition, .none, .none });
31523371 }
31533372
3154 fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void {
3373 fn performReloc(self: *Self, reloc: Reloc) !void {
31553374 switch (reloc) {
31563375 .rel32 => |pos| {
31573376 const amt = self.code.items.len - (pos + 4);
......@@ -3162,7 +3381,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31623381 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
31633382 // only have 1 break instruction.
31643383 const s32_amt = math.cast(i32, amt) catch
3165 return self.fail(src, "unable to perform relocation: jump too far", .{});
3384 return self.fail("unable to perform relocation: jump too far", .{});
31663385 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
31673386 },
31683387 .arm_branch => |info| {
......@@ -3172,7 +3391,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31723391 if (math.cast(i26, amt)) |delta| {
31733392 writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());
31743393 } else |_| {
3175 return self.fail(src, "TODO: enable larger branch offset", .{});
3394 return self.fail("TODO: enable larger branch offset", .{});
31763395 }
31773396 },
31783397 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch
......@@ -3181,56 +3400,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31813400 }
31823401 }
31833402
3184 fn genBrBlockFlat(self: *Self, inst: *ir.Inst.BrBlockFlat) !MCValue {
3185 try self.genBody(inst.body);
3186 const last = inst.body.instructions[inst.body.instructions.len - 1];
3187 return self.br(inst.base.src, inst.block, last);
3188 }
3189
3190 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
3191 return self.br(inst.base.src, inst.block, inst.operand);
3403 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
3404 const branch = self.air.instructions.items(.data)[inst].br;
3405 try self.br(branch.block_inst, branch.operand);
3406 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
31923407 }
31933408
3194 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
3195 return self.brVoid(inst.base.src, inst.block);
3196 }
3197
3198 fn genBoolOp(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
3199 if (inst.base.isUnused())
3200 return MCValue.dead;
3201 switch (arch) {
3202 .x86_64 => switch (inst.base.tag) {
3409 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
3410 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3411 const air_tags = self.air.instructions.items(.tag);
3412 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
3413 .x86_64 => switch (air_tags[inst]) {
32033414 // lhs AND rhs
3204 .bool_and => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),
3415 .bool_and => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
32053416 // lhs OR rhs
3206 .bool_or => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),
3417 .bool_or => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
32073418 else => unreachable, // Not a boolean operation
32083419 },
3209 .arm, .armeb => switch (inst.base.tag) {
3210 .bool_and => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_and),
3211 .bool_or => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_or),
3420 .arm, .armeb => switch (air_tags[inst]) {
3421 .bool_and => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),
3422 .bool_or => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),
32123423 else => unreachable, // Not a boolean operation
32133424 },
3214 else => return self.fail(inst.base.src, "TODO implement boolean operations for {}", .{self.target.cpu.arch}),
3215 }
3425 else => return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}),
3426 };
3427 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
32163428 }
32173429
3218 fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
3430 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
32193431 const block_data = self.blocks.getPtr(block).?;
32203432
3221 if (operand.ty.hasCodeGenBits()) {
3433 if (self.air.typeOf(operand).hasCodeGenBits()) {
32223434 const operand_mcv = try self.resolveInst(operand);
32233435 const block_mcv = block_data.mcv;
32243436 if (block_mcv == .none) {
32253437 block_data.mcv = operand_mcv;
32263438 } else {
3227 try self.setRegOrMem(src, block.base.ty, block_mcv, operand_mcv);
3439 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
32283440 }
32293441 }
3230 return self.brVoid(src, block);
3442 return self.brVoid(block);
32313443 }
32323444
3233 fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue {
3445 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
32343446 const block_data = self.blocks.getPtr(block).?;
32353447
32363448 // Emit a jump with a relocation. It will be patched up after the block ends.
......@@ -3254,201 +3466,265 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32543466 },
32553467 });
32563468 },
3257 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
3469 else => return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}),
32583470 }
3259 return .none;
32603471 }
32613472
3262 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
3263 if (!inst.is_volatile and inst.base.isUnused())
3264 return MCValue.dead;
3265 switch (arch) {
3266 .arm, .armeb => {
3267 for (inst.inputs) |input, i| {
3268 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3269 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});
3473 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3474 const air_datas = self.air.instructions.items(.data);
3475 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3476 const zir = self.mod_fn.owner_decl.namespace.file_scope.zir;
3477 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3478 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3479 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3480 const outputs_len = @truncate(u5, extended.small);
3481 const args_len = @truncate(u5, extended.small >> 5);
3482 const clobbers_len = @truncate(u5, extended.small >> 10);
3483 _ = clobbers_len; // TODO honor these
3484 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3485 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
3486 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
3487
3488 if (outputs_len > 1) {
3489 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3490 }
3491 var extra_i: usize = zir_extra.end;
3492 const output_constraint: ?[]const u8 = out: {
3493 var i: usize = 0;
3494 while (i < outputs_len) : (i += 1) {
3495 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3496 extra_i = output.end;
3497 break :out zir.nullTerminatedString(output.data.constraint);
3498 }
3499 break :out null;
3500 };
3501
3502 const dead = !is_volatile and self.liveness.isUnused(inst);
3503 const result: MCValue = if (dead) .dead else switch (arch) {
3504 .arm, .armeb => result: {
3505 for (args) |arg| {
3506 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3507 extra_i = input.end;
3508 const constraint = zir.nullTerminatedString(input.data.constraint);
3509
3510 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3511 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
32703512 }
3271 const reg_name = input[1 .. input.len - 1];
3513 const reg_name = constraint[1 .. constraint.len - 1];
32723514 const reg = parseRegName(reg_name) orelse
3273 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3515 return self.fail("unrecognized register: '{s}'", .{reg_name});
32743516
3275 const arg = inst.args[i];
32763517 const arg_mcv = try self.resolveInst(arg);
32773518 try self.register_manager.getReg(reg, null);
3278 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3519 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
32793520 }
32803521
3281 if (mem.eql(u8, inst.asm_source, "svc #0")) {
3522 if (mem.eql(u8, asm_source, "svc #0")) {
32823523 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
32833524 } else {
3284 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
3525 return self.fail("TODO implement support for more arm assembly instructions", .{});
32853526 }
32863527
3287 if (inst.output_constraint) |output| {
3528 if (output_constraint) |output| {
32883529 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3289 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
3530 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
32903531 }
32913532 const reg_name = output[2 .. output.len - 1];
32923533 const reg = parseRegName(reg_name) orelse
3293 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3294 return MCValue{ .register = reg };
3534 return self.fail("unrecognized register: '{s}'", .{reg_name});
3535
3536 break :result MCValue{ .register = reg };
32953537 } else {
3296 return MCValue.none;
3538 break :result MCValue{ .none = {} };
32973539 }
32983540 },
3299 .aarch64 => {
3300 for (inst.inputs) |input, i| {
3301 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3302 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});
3541 .aarch64 => result: {
3542 for (args) |arg| {
3543 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3544 extra_i = input.end;
3545 const constraint = zir.nullTerminatedString(input.data.constraint);
3546
3547 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3548 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
33033549 }
3304 const reg_name = input[1 .. input.len - 1];
3550 const reg_name = constraint[1 .. constraint.len - 1];
33053551 const reg = parseRegName(reg_name) orelse
3306 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3552 return self.fail("unrecognized register: '{s}'", .{reg_name});
33073553
3308 const arg = inst.args[i];
33093554 const arg_mcv = try self.resolveInst(arg);
33103555 try self.register_manager.getReg(reg, null);
3311 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3556 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
33123557 }
33133558
3314 if (mem.eql(u8, inst.asm_source, "svc #0")) {
3559 if (mem.eql(u8, asm_source, "svc #0")) {
33153560 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
3316 } else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {
3561 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
33173562 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
33183563 } else {
3319 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
3564 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
33203565 }
33213566
3322 if (inst.output_constraint) |output| {
3567 if (output_constraint) |output| {
33233568 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3324 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
3569 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
33253570 }
33263571 const reg_name = output[2 .. output.len - 1];
33273572 const reg = parseRegName(reg_name) orelse
3328 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3329 return MCValue{ .register = reg };
3573 return self.fail("unrecognized register: '{s}'", .{reg_name});
3574 break :result MCValue{ .register = reg };
33303575 } else {
3331 return MCValue.none;
3576 break :result MCValue{ .none = {} };
33323577 }
33333578 },
3334 .riscv64 => {
3335 for (inst.inputs) |input, i| {
3336 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3337 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});
3579 .riscv64 => result: {
3580 for (args) |arg| {
3581 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3582 extra_i = input.end;
3583 const constraint = zir.nullTerminatedString(input.data.constraint);
3584
3585 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3586 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
33383587 }
3339 const reg_name = input[1 .. input.len - 1];
3588 const reg_name = constraint[1 .. constraint.len - 1];
33403589 const reg = parseRegName(reg_name) orelse
3341 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3590 return self.fail("unrecognized register: '{s}'", .{reg_name});
33423591
3343 const arg = inst.args[i];
33443592 const arg_mcv = try self.resolveInst(arg);
33453593 try self.register_manager.getReg(reg, null);
3346 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3594 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
33473595 }
33483596
3349 if (mem.eql(u8, inst.asm_source, "ecall")) {
3597 if (mem.eql(u8, asm_source, "ecall")) {
33503598 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
33513599 } else {
3352 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
3600 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
33533601 }
33543602
3355 if (inst.output_constraint) |output| {
3603 if (output_constraint) |output| {
33563604 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3357 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
3605 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
33583606 }
33593607 const reg_name = output[2 .. output.len - 1];
33603608 const reg = parseRegName(reg_name) orelse
3361 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3362 return MCValue{ .register = reg };
3609 return self.fail("unrecognized register: '{s}'", .{reg_name});
3610 break :result MCValue{ .register = reg };
33633611 } else {
3364 return MCValue.none;
3612 break :result MCValue{ .none = {} };
33653613 }
33663614 },
3367 .x86_64, .i386 => {
3368 for (inst.inputs) |input, i| {
3369 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3370 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});
3615 .x86_64, .i386 => result: {
3616 for (args) |arg| {
3617 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3618 extra_i = input.end;
3619 const constraint = zir.nullTerminatedString(input.data.constraint);
3620
3621 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3622 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
33713623 }
3372 const reg_name = input[1 .. input.len - 1];
3624 const reg_name = constraint[1 .. constraint.len - 1];
33733625 const reg = parseRegName(reg_name) orelse
3374 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3626 return self.fail("unrecognized register: '{s}'", .{reg_name});
33753627
3376 const arg = inst.args[i];
33773628 const arg_mcv = try self.resolveInst(arg);
33783629 try self.register_manager.getReg(reg, null);
3379 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3630 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
33803631 }
33813632
33823633 {
3383 var iter = std.mem.tokenize(inst.asm_source, "\n\r");
3634 var iter = std.mem.tokenize(asm_source, "\n\r");
33843635 while (iter.next()) |ins| {
33853636 if (mem.eql(u8, ins, "syscall")) {
33863637 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
33873638 } else if (mem.indexOf(u8, ins, "push")) |_| {
33883639 const arg = ins[4..];
33893640 if (mem.indexOf(u8, arg, "$")) |l| {
3390 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail(inst.base.src, "TODO implement more inline asm int parsing", .{});
3641 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail("TODO implement more inline asm int parsing", .{});
33913642 try self.code.appendSlice(&.{ 0x6a, n });
33923643 } else if (mem.indexOf(u8, arg, "%%")) |l| {
33933644 const reg_name = ins[4 + l + 2 ..];
33943645 const reg = parseRegName(reg_name) orelse
3395 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3646 return self.fail("unrecognized register: '{s}'", .{reg_name});
33963647 const low_id: u8 = reg.low_id();
33973648 if (reg.isExtended()) {
33983649 try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });
33993650 } else {
34003651 try self.code.append(0b1010000 | low_id);
34013652 }
3402 } else return self.fail(inst.base.src, "TODO more push operands", .{});
3653 } else return self.fail("TODO more push operands", .{});
34033654 } else if (mem.indexOf(u8, ins, "pop")) |_| {
34043655 const arg = ins[3..];
34053656 if (mem.indexOf(u8, arg, "%%")) |l| {
34063657 const reg_name = ins[3 + l + 2 ..];
34073658 const reg = parseRegName(reg_name) orelse
3408 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3659 return self.fail("unrecognized register: '{s}'", .{reg_name});
34093660 const low_id: u8 = reg.low_id();
34103661 if (reg.isExtended()) {
34113662 try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });
34123663 } else {
34133664 try self.code.append(0b1011000 | low_id);
34143665 }
3415 } else return self.fail(inst.base.src, "TODO more pop operands", .{});
3666 } else return self.fail("TODO more pop operands", .{});
34163667 } else {
3417 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
3668 return self.fail("TODO implement support for more x86 assembly instructions", .{});
34183669 }
34193670 }
34203671 }
34213672
3422 if (inst.output_constraint) |output| {
3673 if (output_constraint) |output| {
34233674 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3424 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
3675 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
34253676 }
34263677 const reg_name = output[2 .. output.len - 1];
34273678 const reg = parseRegName(reg_name) orelse
3428 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
3429 return MCValue{ .register = reg };
3679 return self.fail("unrecognized register: '{s}'", .{reg_name});
3680 break :result MCValue{ .register = reg };
34303681 } else {
3431 return MCValue.none;
3682 break :result MCValue{ .none = {} };
34323683 }
34333684 },
3434 else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
3685 else => return self.fail("TODO implement inline asm support for more architectures", .{}),
3686 };
3687 if (outputs.len + args.len <= Liveness.bpi - 1) {
3688 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3689 std.mem.copy(Air.Inst.Ref, &buf, outputs);
3690 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
3691 return self.finishAir(inst, result, buf);
3692 }
3693 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
3694 for (outputs) |output| {
3695 bt.feed(output);
3696 }
3697 for (args) |arg| {
3698 bt.feed(arg);
34353699 }
3700 return bt.finishAir(result);
3701 }
3702
3703 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
3704 try self.ensureProcessDeathCapacity(operand_count + 1);
3705 return BigTomb{
3706 .function = self,
3707 .inst = inst,
3708 .tomb_bits = self.liveness.getTombBits(inst),
3709 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
3710 .bit_index = 0,
3711 };
34363712 }
34373713
34383714 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
3439 fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void {
3715 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
34403716 switch (loc) {
34413717 .none => return,
3442 .register => |reg| return self.genSetReg(src, ty, reg, val),
3443 .stack_offset => |off| return self.genSetStack(src, ty, off, val),
3718 .register => |reg| return self.genSetReg(ty, reg, val),
3719 .stack_offset => |off| return self.genSetStack(ty, off, val),
34443720 .memory => {
3445 return self.fail(src, "TODO implement setRegOrMem for memory", .{});
3721 return self.fail("TODO implement setRegOrMem for memory", .{});
34463722 },
34473723 else => unreachable,
34483724 }
34493725 }
34503726
3451 fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
3727 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
34523728 switch (arch) {
34533729 .arm, .armeb => switch (mcv) {
34543730 .dead => unreachable,
......@@ -3460,28 +3736,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
34603736 return; // The already existing value will do just fine.
34613737 // TODO Upgrade this to a memset call when we have that available.
34623738 switch (ty.abiSize(self.target.*)) {
3463 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
3464 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
3465 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3466 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3467 else => return self.fail(src, "TODO implement memset", .{}),
3739 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3740 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3741 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3742 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3743 else => return self.fail("TODO implement memset", .{}),
34683744 }
34693745 },
34703746 .compare_flags_unsigned => |op| {
34713747 _ = op;
3472 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
3748 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
34733749 },
34743750 .compare_flags_signed => |op| {
34753751 _ = op;
3476 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
3752 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
34773753 },
34783754 .immediate => {
3479 const reg = try self.copyToTmpRegister(src, ty, mcv);
3480 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3755 const reg = try self.copyToTmpRegister(ty, mcv);
3756 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
34813757 },
34823758 .embedded_in_code => |code_offset| {
34833759 _ = code_offset;
3484 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
3760 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
34853761 },
34863762 .register => |reg| {
34873763 const abi_size = ty.abiSize(self.target.*);
......@@ -3491,7 +3767,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
34913767 1, 4 => {
34923768 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
34933769 break :blk Instruction.Offset.imm(imm);
3494 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3770 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
34953771 const str = switch (abi_size) {
34963772 1 => Instruction.strb,
34973773 4 => Instruction.str,
......@@ -3506,26 +3782,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35063782 2 => {
35073783 const offset = if (adj_off <= math.maxInt(u8)) blk: {
35083784 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
3509 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));
3785 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
35103786
35113787 writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{
35123788 .offset = offset,
35133789 .positive = false,
35143790 }).toU32());
35153791 },
3516 else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),
3792 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
35173793 }
35183794 },
35193795 .memory => |vaddr| {
35203796 _ = vaddr;
3521 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
3797 return self.fail("TODO implement set stack variable from memory vaddr", .{});
35223798 },
35233799 .stack_offset => |off| {
35243800 if (stack_offset == off)
35253801 return; // Copy stack variable to itself; nothing to do.
35263802
3527 const reg = try self.copyToTmpRegister(src, ty, mcv);
3528 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3803 const reg = try self.copyToTmpRegister(ty, mcv);
3804 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
35293805 },
35303806 },
35313807 .x86_64 => switch (mcv) {
......@@ -3538,34 +3814,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35383814 return; // The already existing value will do just fine.
35393815 // TODO Upgrade this to a memset call when we have that available.
35403816 switch (ty.abiSize(self.target.*)) {
3541 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
3542 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
3543 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3544 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3545 else => return self.fail(src, "TODO implement memset", .{}),
3817 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3818 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3819 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3820 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3821 else => return self.fail("TODO implement memset", .{}),
35463822 }
35473823 },
35483824 .compare_flags_unsigned => |op| {
35493825 _ = op;
3550 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
3826 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
35513827 },
35523828 .compare_flags_signed => |op| {
35533829 _ = op;
3554 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
3830 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
35553831 },
35563832 .immediate => |x_big| {
35573833 const abi_size = ty.abiSize(self.target.*);
35583834 const adj_off = stack_offset + abi_size;
35593835 if (adj_off > 128) {
3560 return self.fail(src, "TODO implement set stack variable with large stack offset", .{});
3836 return self.fail("TODO implement set stack variable with large stack offset", .{});
35613837 }
35623838 try self.code.ensureCapacity(self.code.items.len + 8);
35633839 switch (abi_size) {
35643840 1 => {
3565 return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});
3841 return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{});
35663842 },
35673843 2 => {
3568 return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});
3844 return self.fail("TODO implement set abi_size=2 stack variable with immediate", .{});
35693845 },
35703846 4 => {
35713847 const x = @intCast(u32, x_big);
......@@ -3598,22 +3874,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35983874 self.code.appendSliceAssumeCapacity(buf[0..4]);
35993875 },
36003876 else => {
3601 return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});
3877 return self.fail("TODO implement set abi_size=large stack variable with immediate", .{});
36023878 },
36033879 }
36043880 },
36053881 .embedded_in_code => {
36063882 // TODO this and `.stack_offset` below need to get improved to support types greater than
36073883 // register size, and do general memcpy
3608 const reg = try self.copyToTmpRegister(src, ty, mcv);
3609 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3884 const reg = try self.copyToTmpRegister(ty, mcv);
3885 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
36103886 },
36113887 .register => |reg| {
3612 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
3888 try self.genX8664ModRMRegToStack(ty, stack_offset, reg, 0x89);
36133889 },
36143890 .memory => |vaddr| {
36153891 _ = vaddr;
3616 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
3892 return self.fail("TODO implement set stack variable from memory vaddr", .{});
36173893 },
36183894 .stack_offset => |off| {
36193895 // TODO this and `.embedded_in_code` above need to get improved to support types greater than
......@@ -3622,8 +3898,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36223898 if (stack_offset == off)
36233899 return; // Copy stack variable to itself; nothing to do.
36243900
3625 const reg = try self.copyToTmpRegister(src, ty, mcv);
3626 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3901 const reg = try self.copyToTmpRegister(ty, mcv);
3902 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
36273903 },
36283904 },
36293905 .aarch64, .aarch64_be, .aarch64_32 => switch (mcv) {
......@@ -3636,28 +3912,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36363912 return; // The already existing value will do just fine.
36373913 // TODO Upgrade this to a memset call when we have that available.
36383914 switch (ty.abiSize(self.target.*)) {
3639 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
3640 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
3641 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3642 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3643 else => return self.fail(src, "TODO implement memset", .{}),
3915 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3916 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3917 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3918 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3919 else => return self.fail("TODO implement memset", .{}),
36443920 }
36453921 },
36463922 .compare_flags_unsigned => |op| {
36473923 _ = op;
3648 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
3924 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
36493925 },
36503926 .compare_flags_signed => |op| {
36513927 _ = op;
3652 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
3928 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
36533929 },
36543930 .immediate => {
3655 const reg = try self.copyToTmpRegister(src, ty, mcv);
3656 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3931 const reg = try self.copyToTmpRegister(ty, mcv);
3932 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
36573933 },
36583934 .embedded_in_code => |code_offset| {
36593935 _ = code_offset;
3660 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
3936 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
36613937 },
36623938 .register => |reg| {
36633939 const abi_size = ty.abiSize(self.target.*);
......@@ -3668,7 +3944,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36683944 const offset = if (math.cast(i9, adj_off)) |imm|
36693945 Instruction.LoadStoreOffset.imm_post_index(-imm)
36703946 else |_|
3671 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
3947 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
36723948 const rn: Register = switch (arch) {
36733949 .aarch64, .aarch64_be => .x29,
36743950 .aarch64_32 => .w29,
......@@ -3685,26 +3961,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36853961 .offset = offset,
36863962 }).toU32());
36873963 },
3688 else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),
3964 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
36893965 }
36903966 },
36913967 .memory => |vaddr| {
36923968 _ = vaddr;
3693 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
3969 return self.fail("TODO implement set stack variable from memory vaddr", .{});
36943970 },
36953971 .stack_offset => |off| {
36963972 if (stack_offset == off)
36973973 return; // Copy stack variable to itself; nothing to do.
36983974
3699 const reg = try self.copyToTmpRegister(src, ty, mcv);
3700 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3975 const reg = try self.copyToTmpRegister(ty, mcv);
3976 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
37013977 },
37023978 },
3703 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
3979 else => return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch}),
37043980 }
37053981 }
37063982
3707 fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3983 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
37083984 switch (arch) {
37093985 .arm, .armeb => switch (mcv) {
37103986 .dead => unreachable,
......@@ -3715,7 +3991,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37153991 if (!self.wantSafety())
37163992 return; // The already existing value will do just fine.
37173993 // Write the debug undefined value.
3718 return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa });
3994 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa });
37193995 },
37203996 .compare_flags_unsigned,
37213997 .compare_flags_signed,
......@@ -3734,7 +4010,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37344010 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());
37354011 },
37364012 .immediate => |x| {
3737 if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{});
4013 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});
37384014
37394015 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
37404016 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());
......@@ -3780,7 +4056,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37804056 .memory => |addr| {
37814057 // The value is in memory at a hard-coded address.
37824058 // If the type is a pointer, it means the pointer address is at this memory location.
3783 try self.genSetReg(src, ty, reg, .{ .immediate = addr });
4059 try self.genSetReg(ty, reg, .{ .immediate = addr });
37844060 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
37854061 },
37864062 .stack_offset => |unadjusted_off| {
......@@ -3792,7 +4068,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37924068 1, 4 => {
37934069 const offset = if (adj_off <= math.maxInt(u12)) blk: {
37944070 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
3795 } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
4071 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
37964072 const ldr = switch (abi_size) {
37974073 1 => Instruction.ldrb,
37984074 4 => Instruction.ldr,
......@@ -3807,17 +4083,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
38074083 2 => {
38084084 const offset = if (adj_off <= math.maxInt(u8)) blk: {
38094085 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
3810 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));
4086 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
38114087
38124088 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{
38134089 .offset = offset,
38144090 .positive = false,
38154091 }).toU32());
38164092 },
3817 else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}),
4093 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
38184094 }
38194095 },
3820 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
4096 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
38214097 },
38224098 .aarch64 => switch (mcv) {
38234099 .dead => unreachable,
......@@ -3829,8 +4105,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
38294105 return; // The already existing value will do just fine.
38304106 // Write the debug undefined value.
38314107 switch (reg.size()) {
3832 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),
3833 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
4108 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
4109 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
38344110 else => unreachable, // unexpected register size
38354111 }
38364112 },
......@@ -3909,12 +4185,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39094185 .payload = .{ .page_off = .{ .kind = .got } },
39104186 });
39114187 } else {
3912 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
4188 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
39134189 }
39144190 } else {
39154191 // The value is in memory at a hard-coded address.
39164192 // If the type is a pointer, it means the pointer address is at this memory location.
3917 try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr });
4193 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
39184194 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
39194195 }
39204196 },
......@@ -3932,7 +4208,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39324208 const offset = if (math.cast(i9, adj_off)) |imm|
39334209 Instruction.LoadStoreOffset.imm_post_index(-imm)
39344210 else |_|
3935 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
4211 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
39364212
39374213 switch (abi_size) {
39384214 1, 2 => {
......@@ -3952,10 +4228,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39524228 .offset = offset,
39534229 } }).toU32());
39544230 },
3955 else => return self.fail(src, "TODO implement genSetReg other types abi_size={}", .{abi_size}),
4231 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
39564232 }
39574233 },
3958 else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}),
4234 else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}),
39594235 },
39604236 .riscv64 => switch (mcv) {
39614237 .dead => unreachable,
......@@ -3966,7 +4242,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39664242 if (!self.wantSafety())
39674243 return; // The already existing value will do just fine.
39684244 // Write the debug undefined value.
3969 return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
4245 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
39704246 },
39714247 .immediate => |unsigned_x| {
39724248 const x = @bitCast(i64, unsigned_x);
......@@ -3986,19 +4262,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39864262 }
39874263 // li rd, immediate
39884264 // "Myriad sequences"
3989 return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
4265 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
39904266 },
39914267 .memory => |addr| {
39924268 // The value is in memory at a hard-coded address.
39934269 // If the type is a pointer, it means the pointer address is at this memory location.
3994 try self.genSetReg(src, ty, reg, .{ .immediate = addr });
4270 try self.genSetReg(ty, reg, .{ .immediate = addr });
39954271
39964272 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
39974273 // LOAD imm=[i12 offset = 0], rs1 =
39984274
39994275 // return self.fail("TODO implement genSetReg memory for riscv64");
40004276 },
4001 else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),
4277 else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}),
40024278 },
40034279 .x86_64 => switch (mcv) {
40044280 .dead => unreachable,
......@@ -4010,10 +4286,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
40104286 return; // The already existing value will do just fine.
40114287 // Write the debug undefined value.
40124288 switch (reg.size()) {
4013 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }),
4014 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }),
4015 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),
4016 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
4289 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }),
4290 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }),
4291 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
4292 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
40174293 else => unreachable,
40184294 }
40194295 },
......@@ -4040,7 +4316,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
40404316 },
40414317 .compare_flags_signed => |op| {
40424318 _ = op;
4043 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
4319 return self.fail("TODO set register with compare flags value (signed)", .{});
40444320 },
40454321 .immediate => |x| {
40464322 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
......@@ -4185,7 +4461,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
41854461 .payload = .{ .load = .{ .kind = .got } },
41864462 });
41874463 } else {
4188 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
4464 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
41894465 }
41904466
41914467 // MOV reg, [reg]
......@@ -4241,7 +4517,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
42414517 assert(id3 != 4 and id3 != 5);
42424518
42434519 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
4244 try self.genSetReg(src, ty, reg, MCValue{ .immediate = x });
4520 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
42454521
42464522 // Now, the register contains the address of the value to load into it
42474523 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
......@@ -4264,7 +4540,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
42644540 const abi_size = ty.abiSize(self.target.*);
42654541 const off = unadjusted_off + abi_size;
42664542 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
4267 return self.fail(src, "stack offset too large", .{});
4543 return self.fail("stack offset too large", .{});
42684544 }
42694545 const ioff = -@intCast(i32, off);
42704546 const encoder = try X8664Encoder.init(self.code, 3);
......@@ -4284,39 +4560,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
42844560 }
42854561 },
42864562 },
4287 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),
4563 else => return self.fail("TODO implement getSetReg for {}", .{self.target.cpu.arch}),
42884564 }
42894565 }
42904566
4291 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
4292 // no-op
4293 return self.resolveInst(inst.operand);
4567 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
4568 const un_op = self.air.instructions.items(.data)[inst].un_op;
4569 const result = try self.resolveInst(un_op);
4570 return self.finishAir(inst, result, .{ un_op, .none, .none });
42944571 }
42954572
4296 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
4297 const operand = try self.resolveInst(inst.operand);
4298 return operand;
4573 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
4574 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4575 const result = try self.resolveInst(ty_op.operand);
4576 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
42994577 }
43004578
4301 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
4302 // If the type has no codegen bits, no need to store it.
4303 if (!inst.ty.hasCodeGenBits())
4304 return MCValue.none;
4305
4306 // Constants have static lifetimes, so they are always memoized in the outer most table.
4307 if (inst.castTag(.constant)) |const_inst| {
4308 const branch = &self.branch_stack.items[0];
4309 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
4310 if (!gop.found_existing) {
4311 gop.value_ptr.* = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
4579 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4580 // First section of indexes correspond to a set number of constant values.
4581 const ref_int = @enumToInt(inst);
4582 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
4583 const tv = Air.Inst.Ref.typed_value_map[ref_int];
4584 if (!tv.ty.hasCodeGenBits()) {
4585 return MCValue{ .none = {} };
43124586 }
4313 return gop.value_ptr.*;
4587 return self.genTypedValue(tv);
43144588 }
43154589
4316 return self.getResolvedInstValue(inst);
4590 // If the type has no codegen bits, no need to store it.
4591 const inst_ty = self.air.typeOf(inst);
4592 if (!inst_ty.hasCodeGenBits())
4593 return MCValue{ .none = {} };
4594
4595 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
4596 switch (self.air.instructions.items(.tag)[inst_index]) {
4597 .constant => {
4598 // Constants have static lifetimes, so they are always memoized in the outer most table.
4599 const branch = &self.branch_stack.items[0];
4600 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
4601 if (!gop.found_existing) {
4602 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
4603 gop.value_ptr.* = try self.genTypedValue(.{
4604 .ty = inst_ty,
4605 .val = self.air.values[ty_pl.payload],
4606 });
4607 }
4608 return gop.value_ptr.*;
4609 },
4610 .const_ty => unreachable,
4611 else => return self.getResolvedInstValue(inst_index),
4612 }
43174613 }
43184614
4319 fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {
4615 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
43204616 // Treat each stack item as a "layer" on top of the previous one.
43214617 var i: usize = self.branch_stack.items.len;
43224618 while (true) {
......@@ -4333,15 +4629,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43334629 /// A potential opportunity for future optimization here would be keeping track
43344630 /// of the fact that the instruction is available both as an immediate
43354631 /// and as a register.
4336 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
4337 const mcv = try self.resolveInst(inst);
4632 fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
4633 const mcv = try self.resolveInst(operand);
43384634 const ti = @typeInfo(T).Int;
43394635 switch (mcv) {
43404636 .immediate => |imm| {
43414637 // This immediate is unsigned.
43424638 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
43434639 if (imm >= math.maxInt(U)) {
4344 return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) };
4640 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
43454641 }
43464642 },
43474643 else => {},
......@@ -4349,7 +4645,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43494645 return mcv;
43504646 }
43514647
4352 fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {
4648 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
43534649 if (typed_value.val.isUndef())
43544650 return MCValue{ .undef = {} };
43554651 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
......@@ -4359,7 +4655,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43594655 .Slice => {
43604656 var buf: Type.Payload.ElemType = undefined;
43614657 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
4362 const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val });
4658 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
43634659 const slice_len = typed_value.val.sliceLen();
43644660 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
43654661 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
......@@ -4367,7 +4663,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43674663 _ = slice_len;
43684664 _ = ptr_imm;
43694665 // We need more general support for const data being stored in memory to make this work.
4370 return self.fail(src, "TODO codegen for const slices", .{});
4666 return self.fail("TODO codegen for const slices", .{});
43714667 },
43724668 else => {
43734669 if (typed_value.val.castTag(.decl_ref)) |payload| {
......@@ -4397,19 +4693,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43974693 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
43984694 return MCValue{ .memory = got_addr };
43994695 } else {
4400 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
4696 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
44014697 }
44024698 }
44034699 if (typed_value.val.tag() == .int_u64) {
44044700 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
44054701 }
4406 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
4702 return self.fail("TODO codegen more kinds of const pointers", .{});
44074703 },
44084704 },
44094705 .Int => {
44104706 const info = typed_value.ty.intInfo(self.target.*);
44114707 if (info.bits > ptr_bits or info.signedness == .signed) {
4412 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
4708 return self.fail("TODO const int bigger than ptr and signed int", .{});
44134709 }
44144710 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
44154711 },
......@@ -4424,16 +4720,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44244720 return MCValue{ .immediate = 0 };
44254721
44264722 var buf: Type.Payload.ElemType = undefined;
4427 return self.genTypedValue(src, .{
4723 return self.genTypedValue(.{
44284724 .ty = typed_value.ty.optionalChild(&buf),
44294725 .val = typed_value.val,
44304726 });
44314727 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
44324728 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
44334729 }
4434 return self.fail(src, "TODO non pointer optionals", .{});
4730 return self.fail("TODO non pointer optionals", .{});
44354731 },
4436 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
4732 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
44374733 }
44384734 }
44394735
......@@ -4450,7 +4746,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44504746 };
44514747
44524748 /// Caller must call `CallMCValues.deinit`.
4453 fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues {
4749 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
44544750 const cc = fn_ty.fnCallingConvention();
44554751 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
44564752 defer self.gpa.free(param_types);
......@@ -4519,7 +4815,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
45194815 result.stack_byte_count = next_stack_offset;
45204816 result.stack_align = 16;
45214817 },
4522 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
4818 else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}),
45234819 }
45244820 },
45254821 .arm, .armeb => {
......@@ -4546,10 +4842,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
45464842 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
45474843 ncrn += 1;
45484844 } else {
4549 return self.fail(src, "TODO MCValues with multiple registers", .{});
4845 return self.fail("TODO MCValues with multiple registers", .{});
45504846 }
45514847 } else if (ncrn < 4 and nsaa == 0) {
4552 return self.fail(src, "TODO MCValues split between registers and stack", .{});
4848 return self.fail("TODO MCValues split between registers and stack", .{});
45534849 } else {
45544850 ncrn = 4;
45554851 if (ty.abiAlignment(self.target.*) == 8)
......@@ -4563,7 +4859,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
45634859 result.stack_byte_count = nsaa;
45644860 result.stack_align = 4;
45654861 },
4566 else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),
4862 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
45674863 }
45684864 },
45694865 .aarch64 => {
......@@ -4594,10 +4890,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
45944890 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
45954891 ncrn += 1;
45964892 } else {
4597 return self.fail(src, "TODO MCValues with multiple registers", .{});
4893 return self.fail("TODO MCValues with multiple registers", .{});
45984894 }
45994895 } else if (ncrn < 8 and nsaa == 0) {
4600 return self.fail(src, "TODO MCValues split between registers and stack", .{});
4896 return self.fail("TODO MCValues split between registers and stack", .{});
46014897 } else {
46024898 ncrn = 8;
46034899 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided
......@@ -4616,11 +4912,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46164912 result.stack_byte_count = nsaa;
46174913 result.stack_align = 16;
46184914 },
4619 else => return self.fail(src, "TODO implement function parameters for {} on aarch64", .{cc}),
4915 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
46204916 }
46214917 },
46224918 else => if (param_types.len != 0)
4623 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
4919 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
46244920 }
46254921
46264922 if (ret_ty.zigTypeTag() == .NoReturn) {
......@@ -4635,7 +4931,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46354931 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
46364932 result.return_value = .{ .register = aliased_reg };
46374933 },
4638 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
4934 else => return self.fail("TODO implement function return values for {}", .{cc}),
46394935 },
46404936 .arm, .armeb => switch (cc) {
46414937 .Naked => unreachable,
......@@ -4644,10 +4940,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46444940 if (ret_ty_size <= 4) {
46454941 result.return_value = .{ .register = c_abi_int_return_regs[0] };
46464942 } else {
4647 return self.fail(src, "TODO support more return types for ARM backend", .{});
4943 return self.fail("TODO support more return types for ARM backend", .{});
46484944 }
46494945 },
4650 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
4946 else => return self.fail("TODO implement function return values for {}", .{cc}),
46514947 },
46524948 .aarch64 => switch (cc) {
46534949 .Naked => unreachable,
......@@ -4656,12 +4952,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46564952 if (ret_ty_size <= 8) {
46574953 result.return_value = .{ .register = c_abi_int_return_regs[0] };
46584954 } else {
4659 return self.fail(src, "TODO support more return types for ARM backend", .{});
4955 return self.fail("TODO support more return types for ARM backend", .{});
46604956 }
46614957 },
4662 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
4958 else => return self.fail("TODO implement function return values for {}", .{cc}),
46634959 },
4664 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
4960 else => return self.fail("TODO implement codegen return values for {}", .{self.target.cpu.arch}),
46654961 }
46664962 return result;
46674963 }
......@@ -4676,14 +4972,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
46764972 };
46774973 }
46784974
4679 fn fail(self: *Self, src: LazySrcLoc, comptime format: []const u8, args: anytype) InnerError {
4975 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
46804976 @setCold(true);
46814977 assert(self.err_msg == null);
4682 const src_loc = if (src != .unneeded)
4683 src.toSrcLocWithDecl(self.mod_fn.owner_decl)
4684 else
4685 self.src_loc;
4686 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src_loc, format, args);
4978 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
46874979 return error.CodegenFail;
46884980 }
46894981
src/codegen/c.zig+436-276
......@@ -6,8 +6,6 @@ const log = std.log.scoped(.c);
66const link = @import("../link.zig");
77const Module = @import("../Module.zig");
88const Compilation = @import("../Compilation.zig");
9const ir = @import("../air.zig");
10const Inst = ir.Inst;
119const Value = @import("../value.zig").Value;
1210const Type = @import("../type.zig").Type;
1311const TypedValue = @import("../TypedValue.zig");
......@@ -15,6 +13,9 @@ const C = link.File.C;
1513const Decl = Module.Decl;
1614const trace = @import("../tracy.zig").trace;
1715const LazySrcLoc = Module.LazySrcLoc;
16const Air = @import("../Air.zig");
17const Zir = @import("../Zir.zig");
18const Liveness = @import("../Liveness.zig");
1819
1920const Mutability = enum { Const, Mut };
2021
......@@ -25,7 +26,7 @@ pub const CValue = union(enum) {
2526 /// Index into local_names, but take the address.
2627 local_ref: usize,
2728 /// A constant instruction, to be rendered inline.
28 constant: *Inst,
29 constant: Air.Inst.Ref,
2930 /// Index into the parameters
3031 arg: usize,
3132 /// By-value
......@@ -38,7 +39,7 @@ const BlockData = struct {
3839 result: CValue,
3940};
4041
41pub const CValueMap = std.AutoHashMap(*Inst, CValue);
42pub const CValueMap = std.AutoHashMap(Air.Inst.Index, CValue);
4243pub const TypedefMap = std.ArrayHashMap(
4344 Type,
4445 struct { name: []const u8, rendered: []u8 },
......@@ -94,20 +95,23 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
9495/// It is not available when generating .h file.
9596pub const Object = struct {
9697 dg: DeclGen,
98 air: Air,
99 liveness: Liveness,
97100 gpa: *mem.Allocator,
98101 code: std.ArrayList(u8),
99102 value_map: CValueMap,
100 blocks: std.AutoHashMapUnmanaged(*ir.Inst.Block, BlockData) = .{},
103 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
101104 next_arg_index: usize = 0,
102105 next_local_index: usize = 0,
103106 next_block_index: usize = 0,
104107 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
105108
106 fn resolveInst(o: *Object, inst: *Inst) !CValue {
107 if (inst.value()) |_| {
109 fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue {
110 if (o.air.value(inst)) |_| {
108111 return CValue{ .constant = inst };
109112 }
110 return o.value_map.get(inst).?; // Instruction does not dominate all uses!
113 const index = Air.refToIndex(inst).?;
114 return o.value_map.get(index).?; // Assertion means instruction does not dominate usage.
111115 }
112116
113117 fn allocLocalValue(o: *Object) CValue {
......@@ -131,7 +135,11 @@ pub const Object = struct {
131135 .none => unreachable,
132136 .local => |i| return w.print("t{d}", .{i}),
133137 .local_ref => |i| return w.print("&t{d}", .{i}),
134 .constant => |inst| return o.dg.renderValue(w, inst.ty, inst.value().?),
138 .constant => |inst| {
139 const ty = o.air.typeOf(inst);
140 const val = o.air.value(inst).?;
141 return o.dg.renderValue(w, ty, val);
142 },
135143 .arg => |i| return w.print("a{d}", .{i}),
136144 .decl => |decl| return w.writeAll(mem.span(decl.name)),
137145 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),
......@@ -211,8 +219,9 @@ pub const DeclGen = struct {
211219 error_msg: ?*Module.ErrorMsg,
212220 typedefs: TypedefMap,
213221
214 fn fail(dg: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
222 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
215223 @setCold(true);
224 const src: LazySrcLoc = .{ .node_offset = 0 };
216225 const src_loc = src.toSrcLocWithDecl(dg.decl);
217226 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args);
218227 return error.AnalysisFail;
......@@ -228,7 +237,7 @@ pub const DeclGen = struct {
228237 // This should lower to 0xaa bytes in safe modes, and for unsafe modes should
229238 // lower to leaving variables uninitialized (that might need to be implemented
230239 // outside of this function).
231 return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement renderValue undef", .{});
240 return dg.fail("TODO: C backend: implement renderValue undef", .{});
232241 }
233242 switch (t.zigTypeTag()) {
234243 .Int => {
......@@ -438,7 +447,7 @@ pub const DeclGen = struct {
438447 },
439448 else => unreachable,
440449 },
441 else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
450 else => |e| return dg.fail("TODO: C backend: implement value {s}", .{
442451 @tagName(e),
443452 }),
444453 }
......@@ -517,14 +526,14 @@ pub const DeclGen = struct {
517526 break;
518527 }
519528 } else {
520 return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 128 bits", .{});
529 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
521530 }
522531 },
523532 else => unreachable,
524533 }
525534 },
526535
527 .Float => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Float", .{}),
536 .Float => return dg.fail("TODO: C backend: implement type Float", .{}),
528537
529538 .Pointer => {
530539 if (t.isSlice()) {
......@@ -679,7 +688,7 @@ pub const DeclGen = struct {
679688
680689 try dg.renderType(w, int_tag_ty);
681690 },
682 .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}),
691 .Union => return dg.fail("TODO: C backend: implement type Union", .{}),
683692 .Fn => {
684693 try dg.renderType(w, t.fnReturnType());
685694 try w.writeAll(" (*)(");
......@@ -702,10 +711,10 @@ pub const DeclGen = struct {
702711 }
703712 try w.writeByte(')');
704713 },
705 .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}),
706 .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}),
707 .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}),
708 .Vector => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Vector", .{}),
714 .Opaque => return dg.fail("TODO: C backend: implement type Opaque", .{}),
715 .Frame => return dg.fail("TODO: C backend: implement type Frame", .{}),
716 .AnyFrame => return dg.fail("TODO: C backend: implement type AnyFrame", .{}),
717 .Vector => return dg.fail("TODO: C backend: implement type Vector", .{}),
709718
710719 .Null,
711720 .Undefined,
......@@ -758,7 +767,8 @@ pub fn genDecl(o: *Object) !void {
758767 try o.dg.renderFunctionSignature(o.writer(), is_global);
759768
760769 try o.writer().writeByte(' ');
761 try genBody(o, func.body);
770 const main_body = o.air.getMainBody();
771 try genBody(o, main_body);
762772
763773 try o.indent_writer.insertNewline();
764774 return;
......@@ -831,9 +841,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
831841 }
832842}
833843
834pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!void {
844fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
835845 const writer = o.writer();
836 if (body.instructions.len == 0) {
846 if (body.len == 0) {
837847 try writer.writeAll("{}");
838848 return;
839849 }
......@@ -841,82 +851,92 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
841851 try writer.writeAll("{\n");
842852 o.indent_writer.pushIndent();
843853
844 for (body.instructions) |inst| {
845 const result_value = switch (inst.tag) {
854 const air_tags = o.air.instructions.items(.tag);
855
856 for (body) |inst| {
857 const result_value = switch (air_tags[inst]) {
858 // zig fmt: off
859 .constant => unreachable, // excluded from function bodies
860 .const_ty => unreachable, // excluded from function bodies
861 .arg => airArg(o),
862
863 .breakpoint => try airBreakpoint(o),
864 .unreach => try airUnreach(o),
865
846866 // TODO use a different strategy for add that communicates to the optimizer
847867 // that wrapping is UB.
848 .add => try genBinOp(o, inst.castTag(.add).?, " + "),
849 .addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"),
868 .add => try airBinOp( o, inst, " + "),
869 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
850870 // TODO use a different strategy for sub that communicates to the optimizer
851871 // that wrapping is UB.
852 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
853 .subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"),
872 .sub => try airBinOp( o, inst, " - "),
873 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
854874 // TODO use a different strategy for mul that communicates to the optimizer
855875 // that wrapping is UB.
856 .mul => try genBinOp(o, inst.castTag(.sub).?, " * "),
857 .mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"),
876 .mul => try airBinOp( o, inst, " * "),
877 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
858878 // TODO use a different strategy for div that communicates to the optimizer
859879 // that wrapping is UB.
860 .div => try genBinOp(o, inst.castTag(.div).?, " / "),
880 .div => try airBinOp( o, inst, " / "),
881
882 .cmp_eq => try airBinOp(o, inst, " == "),
883 .cmp_gt => try airBinOp(o, inst, " > "),
884 .cmp_gte => try airBinOp(o, inst, " >= "),
885 .cmp_lt => try airBinOp(o, inst, " < "),
886 .cmp_lte => try airBinOp(o, inst, " <= "),
887 .cmp_neq => try airBinOp(o, inst, " != "),
861888
862 .constant => unreachable, // excluded from function bodies
863 .alloc => try genAlloc(o, inst.castTag(.alloc).?),
864 .arg => genArg(o),
865 .assembly => try genAsm(o, inst.castTag(.assembly).?),
866 .block => try genBlock(o, inst.castTag(.block).?),
867 .bitcast => try genBitcast(o, inst.castTag(.bitcast).?),
868 .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?),
869 .call => try genCall(o, inst.castTag(.call).?),
870 .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "),
871 .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "),
872 .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "),
873 .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "),
874 .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "),
875 .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "),
876 .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?),
877 .intcast => try genIntCast(o, inst.castTag(.intcast).?),
878 .load => try genLoad(o, inst.castTag(.load).?),
879 .ret => try genRet(o, inst.castTag(.ret).?),
880 .retvoid => try genRetVoid(o),
881 .store => try genStore(o, inst.castTag(.store).?),
882 .unreach => try genUnreach(o, inst.castTag(.unreach).?),
883 .loop => try genLoop(o, inst.castTag(.loop).?),
884 .condbr => try genCondBr(o, inst.castTag(.condbr).?),
885 .br => try genBr(o, inst.castTag(.br).?),
886 .br_void => try genBrVoid(o, inst.castTag(.br_void).?.block),
887 .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?),
888889 // bool_and and bool_or are non-short-circuit operations
889 .bool_and => try genBinOp(o, inst.castTag(.bool_and).?, " & "),
890 .bool_or => try genBinOp(o, inst.castTag(.bool_or).?, " | "),
891 .bit_and => try genBinOp(o, inst.castTag(.bit_and).?, " & "),
892 .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "),
893 .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "),
894 .not => try genUnOp(o, inst.castTag(.not).?, "!"),
895 .is_null => try genIsNull(o, inst.castTag(.is_null).?),
896 .is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?),
897 .is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?),
898 .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?),
899 .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?),
900 .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?),
901 .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),
902 .ref => try genRef(o, inst.castTag(.ref).?),
903 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
904
905 .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="),
906 .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="),
907 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="),
908 .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="),
909
910 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
911 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
912 .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
913 .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),
914 .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),
915 .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
916 .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}),
917 .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}),
918 .varptr => try genVarPtr(o, inst.castTag(.varptr).?),
919 .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}),
890 .bool_and => try airBinOp(o, inst, " & "),
891 .bool_or => try airBinOp(o, inst, " | "),
892 .bit_and => try airBinOp(o, inst, " & "),
893 .bit_or => try airBinOp(o, inst, " | "),
894 .xor => try airBinOp(o, inst, " ^ "),
895
896 .not => try airNot( o, inst),
897
898 .optional_payload => try airOptionalPayload(o, inst),
899 .optional_payload_ptr => try airOptionalPayload(o, inst),
900
901 .is_err => try airIsErr(o, inst, "", ".", "!="),
902 .is_non_err => try airIsErr(o, inst, "", ".", "=="),
903 .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="),
904 .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="),
905
906 .is_null => try airIsNull(o, inst, "==", ""),
907 .is_non_null => try airIsNull(o, inst, "!=", ""),
908 .is_null_ptr => try airIsNull(o, inst, "==", "[0]"),
909 .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"),
910
911 .alloc => try airAlloc(o, inst),
912 .assembly => try airAsm(o, inst),
913 .block => try airBlock(o, inst),
914 .bitcast => try airBitcast(o, inst),
915 .call => try airCall(o, inst),
916 .dbg_stmt => try airDbgStmt(o, inst),
917 .intcast => try airIntCast(o, inst),
918 .load => try airLoad(o, inst),
919 .ret => try airRet(o, inst),
920 .store => try airStore(o, inst),
921 .loop => try airLoop(o, inst),
922 .cond_br => try airCondBr(o, inst),
923 .br => try airBr(o, inst),
924 .switch_br => try airSwitchBr(o, inst),
925 .wrap_optional => try airWrapOptional(o, inst),
926 .ref => try airRef(o, inst),
927 .struct_field_ptr => try airStructFieldPtr(o, inst),
928 .varptr => try airVarPtr(o, inst),
929
930 .unwrap_errunion_payload => try airUnwrapErrUnionPay(o, inst),
931 .unwrap_errunion_err => try airUnwrapErrUnionErr(o, inst),
932 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(o, inst),
933 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(o, inst),
934 .wrap_errunion_payload => try airWrapErrUnionPay(o, inst),
935 .wrap_errunion_err => try airWrapErrUnionErr(o, inst),
936
937 .ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}),
938 .floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}),
939 // zig fmt: on
920940 };
921941 switch (result_value) {
922942 .none => {},
......@@ -928,38 +948,40 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
928948 try writer.writeAll("}");
929949}
930950
931fn genVarPtr(o: *Object, inst: *Inst.VarPtr) !CValue {
932 _ = o;
933 return CValue{ .decl_ref = inst.variable.owner_decl };
951fn airVarPtr(o: *Object, inst: Air.Inst.Index) !CValue {
952 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
953 const variable = o.air.variables[ty_pl.payload];
954 return CValue{ .decl_ref = variable.owner_decl };
934955}
935956
936fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {
957fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue {
937958 const writer = o.writer();
959 const inst_ty = o.air.typeOfIndex(inst);
938960
939961 // First line: the variable used as data storage.
940 const elem_type = alloc.base.ty.elemType();
941 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;
962 const elem_type = inst_ty.elemType();
963 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
942964 const local = try o.allocLocal(elem_type, mutability);
943965 try writer.writeAll(";\n");
944966
945967 return CValue{ .local_ref = local.local };
946968}
947969
948fn genArg(o: *Object) CValue {
970fn airArg(o: *Object) CValue {
949971 const i = o.next_arg_index;
950972 o.next_arg_index += 1;
951973 return .{ .arg = i };
952974}
953975
954fn genRetVoid(o: *Object) !CValue {
955 try o.writer().print("return;\n", .{});
956 return CValue.none;
957}
958
959fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
960 const operand = try o.resolveInst(inst.operand);
976fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue {
977 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
978 const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr();
979 if (!is_volatile and o.liveness.isUnused(inst))
980 return CValue.none;
981 const inst_ty = o.air.typeOfIndex(inst);
982 const operand = try o.resolveInst(ty_op.operand);
961983 const writer = o.writer();
962 const local = try o.allocLocal(inst.base.ty, .Const);
984 const local = try o.allocLocal(inst_ty, .Const);
963985 switch (operand) {
964986 .local_ref => |i| {
965987 const wrapped: CValue = .{ .local = i };
......@@ -982,35 +1004,43 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
9821004 return local;
9831005}
9841006
985fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {
986 const operand = try o.resolveInst(inst.operand);
1007fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
1008 const un_op = o.air.instructions.items(.data)[inst].un_op;
9871009 const writer = o.writer();
988 try writer.writeAll("return ");
989 try o.writeCValue(writer, operand);
990 try writer.writeAll(";\n");
1010 if (o.air.typeOf(un_op).hasCodeGenBits()) {
1011 const operand = try o.resolveInst(un_op);
1012 try writer.writeAll("return ");
1013 try o.writeCValue(writer, operand);
1014 try writer.writeAll(";\n");
1015 } else {
1016 try writer.writeAll("return;\n");
1017 }
9911018 return CValue.none;
9921019}
9931020
994fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue {
995 if (inst.base.isUnused())
1021fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
1022 if (o.liveness.isUnused(inst))
9961023 return CValue.none;
9971024
998 const from = try o.resolveInst(inst.operand);
1025 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1026 const from = try o.resolveInst(ty_op.operand);
9991027
10001028 const writer = o.writer();
1001 const local = try o.allocLocal(inst.base.ty, .Const);
1029 const inst_ty = o.air.typeOfIndex(inst);
1030 const local = try o.allocLocal(inst_ty, .Const);
10021031 try writer.writeAll(" = (");
1003 try o.dg.renderType(writer, inst.base.ty);
1032 try o.dg.renderType(writer, inst_ty);
10041033 try writer.writeAll(")");
10051034 try o.writeCValue(writer, from);
10061035 try writer.writeAll(";\n");
10071036 return local;
10081037}
10091038
1010fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
1039fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
10111040 // *a = b;
1012 const dest_ptr = try o.resolveInst(inst.lhs);
1013 const src_val = try o.resolveInst(inst.rhs);
1041 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1042 const dest_ptr = try o.resolveInst(bin_op.lhs);
1043 const src_val = try o.resolveInst(bin_op.rhs);
10141044
10151045 const writer = o.writer();
10161046 switch (dest_ptr) {
......@@ -1039,11 +1069,18 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
10391069 return CValue.none;
10401070}
10411071
1042fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {
1043 if (inst.base.isUnused())
1072fn airWrapOp(
1073 o: *Object,
1074 inst: Air.Inst.Index,
1075 str_op: [*:0]const u8,
1076 fn_op: [*:0]const u8,
1077) !CValue {
1078 if (o.liveness.isUnused(inst))
10441079 return CValue.none;
10451080
1046 const int_info = inst.base.ty.intInfo(o.dg.module.getTarget());
1081 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1082 const inst_ty = o.air.typeOfIndex(inst);
1083 const int_info = inst_ty.intInfo(o.dg.module.getTarget());
10471084 const bits = int_info.bits;
10481085
10491086 // if it's an unsigned int with non-arbitrary bit size then we can just add
......@@ -1052,19 +1089,19 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
10521089 8, 16, 32, 64, 128 => true,
10531090 else => false,
10541091 };
1055 if (ok_bits or inst.base.ty.tag() != .int_unsigned) {
1056 return try genBinOp(o, inst, str_op);
1092 if (ok_bits or inst_ty.tag() != .int_unsigned) {
1093 return try airBinOp(o, inst, str_op);
10571094 }
10581095 }
10591096
10601097 if (bits > 64) {
1061 return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: genWrapOp for large integers", .{});
1098 return o.dg.fail("TODO: C backend: airWrapOp for large integers", .{});
10621099 }
10631100
10641101 var min_buf: [80]u8 = undefined;
10651102 const min = switch (int_info.signedness) {
10661103 .unsigned => "0",
1067 else => switch (inst.base.ty.tag()) {
1104 else => switch (inst_ty.tag()) {
10681105 .c_short => "SHRT_MIN",
10691106 .c_int => "INT_MIN",
10701107 .c_long => "LONG_MIN",
......@@ -1081,7 +1118,7 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
10811118 };
10821119
10831120 var max_buf: [80]u8 = undefined;
1084 const max = switch (inst.base.ty.tag()) {
1121 const max = switch (inst_ty.tag()) {
10851122 .c_short => "SHRT_MAX",
10861123 .c_ushort => "USHRT_MAX",
10871124 .c_int => "INT_MAX",
......@@ -1105,14 +1142,14 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
11051142 },
11061143 };
11071144
1108 const lhs = try o.resolveInst(inst.lhs);
1109 const rhs = try o.resolveInst(inst.rhs);
1145 const lhs = try o.resolveInst(bin_op.lhs);
1146 const rhs = try o.resolveInst(bin_op.rhs);
11101147 const w = o.writer();
11111148
1112 const ret = try o.allocLocal(inst.base.ty, .Mut);
1149 const ret = try o.allocLocal(inst_ty, .Mut);
11131150 try w.print(" = zig_{s}", .{fn_op});
11141151
1115 switch (inst.base.ty.tag()) {
1152 switch (inst_ty.tag()) {
11161153 .isize => try w.writeAll("isize"),
11171154 .c_short => try w.writeAll("short"),
11181155 .c_int => try w.writeAll("int"),
......@@ -1149,53 +1186,65 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
11491186 return ret;
11501187}
11511188
1152fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
1153 if (inst.base.isUnused())
1189fn airNot(o: *Object, inst: Air.Inst.Index) !CValue {
1190 if (o.liveness.isUnused(inst))
11541191 return CValue.none;
11551192
1156 const lhs = try o.resolveInst(inst.lhs);
1157 const rhs = try o.resolveInst(inst.rhs);
1193 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1194 const op = try o.resolveInst(ty_op.operand);
11581195
11591196 const writer = o.writer();
1160 const local = try o.allocLocal(inst.base.ty, .Const);
1197 const inst_ty = o.air.typeOfIndex(inst);
1198 const local = try o.allocLocal(inst_ty, .Const);
11611199
11621200 try writer.writeAll(" = ");
1163 try o.writeCValue(writer, lhs);
1164 try writer.print("{s}", .{operator});
1165 try o.writeCValue(writer, rhs);
1201 if (inst_ty.zigTypeTag() == .Bool)
1202 try writer.writeAll("!")
1203 else
1204 try writer.writeAll("~");
1205 try o.writeCValue(writer, op);
11661206 try writer.writeAll(";\n");
11671207
11681208 return local;
11691209}
11701210
1171fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue {
1172 if (inst.base.isUnused())
1211fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1212 if (o.liveness.isUnused(inst))
11731213 return CValue.none;
11741214
1175 const operand = try o.resolveInst(inst.operand);
1215 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1216 const lhs = try o.resolveInst(bin_op.lhs);
1217 const rhs = try o.resolveInst(bin_op.rhs);
11761218
11771219 const writer = o.writer();
1178 const local = try o.allocLocal(inst.base.ty, .Const);
1220 const inst_ty = o.air.typeOfIndex(inst);
1221 const local = try o.allocLocal(inst_ty, .Const);
11791222
1180 try writer.print(" = {s}", .{operator});
1181 try o.writeCValue(writer, operand);
1223 try writer.writeAll(" = ");
1224 try o.writeCValue(writer, lhs);
1225 try writer.print("{s}", .{operator});
1226 try o.writeCValue(writer, rhs);
11821227 try writer.writeAll(";\n");
11831228
11841229 return local;
11851230}
11861231
1187fn genCall(o: *Object, inst: *Inst.Call) !CValue {
1188 if (inst.func.castTag(.constant)) |func_inst| {
1189 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|
1232fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
1233 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1234 const extra = o.air.extraData(Air.Call, pl_op.payload);
1235 const args = @bitCast([]const Air.Inst.Ref, o.air.extra[extra.end..][0..extra.data.args_len]);
1236
1237 if (o.air.value(pl_op.operand)) |func_val| {
1238 const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|
11901239 extern_fn.data
1191 else if (func_inst.val.castTag(.function)) |func_payload|
1240 else if (func_val.castTag(.function)) |func_payload|
11921241 func_payload.data.owner_decl
11931242 else
11941243 unreachable;
11951244
11961245 const fn_ty = fn_decl.ty;
11971246 const ret_ty = fn_ty.fnReturnType();
1198 const unused_result = inst.base.isUnused();
1247 const unused_result = o.liveness.isUnused(inst);
11991248 var result_local: CValue = .none;
12001249
12011250 const writer = o.writer();
......@@ -1209,41 +1258,44 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {
12091258 }
12101259 const fn_name = mem.spanZ(fn_decl.name);
12111260 try writer.print("{s}(", .{fn_name});
1212 if (inst.args.len != 0) {
1213 for (inst.args) |arg, i| {
1214 if (i > 0) {
1215 try writer.writeAll(", ");
1216 }
1217 if (arg.value()) |val| {
1218 try o.dg.renderValue(writer, arg.ty, val);
1219 } else {
1220 const val = try o.resolveInst(arg);
1221 try o.writeCValue(writer, val);
1222 }
1261 for (args) |arg, i| {
1262 if (i != 0) {
1263 try writer.writeAll(", ");
1264 }
1265 if (o.air.value(arg)) |val| {
1266 try o.dg.renderValue(writer, o.air.typeOf(arg), val);
1267 } else {
1268 const val = try o.resolveInst(arg);
1269 try o.writeCValue(writer, val);
12231270 }
12241271 }
12251272 try writer.writeAll(");\n");
12261273 return result_local;
12271274 } else {
1228 return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement function pointers", .{});
1275 return o.dg.fail("TODO: C backend: implement function pointers", .{});
12291276 }
12301277}
12311278
1232fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue {
1233 _ = o;
1234 _ = inst;
1235 // TODO emit #line directive here with line number and filename
1279fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue {
1280 const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt;
1281 const writer = o.writer();
1282 try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
12361283 return CValue.none;
12371284}
12381285
1239fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
1286fn airBlock(o: *Object, inst: Air.Inst.Index) !CValue {
1287 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1288 const extra = o.air.extraData(Air.Block, ty_pl.payload);
1289 const body = o.air.extra[extra.end..][0..extra.data.body_len];
1290
12401291 const block_id: usize = o.next_block_index;
12411292 o.next_block_index += 1;
12421293 const writer = o.writer();
12431294
1244 const result = if (inst.base.ty.tag() != .void and !inst.base.isUnused()) blk: {
1295 const inst_ty = o.air.typeOfIndex(inst);
1296 const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: {
12451297 // allocate a location for the result
1246 const local = try o.allocLocal(inst.base.ty, .Mut);
1298 const local = try o.allocLocal(inst_ty, .Mut);
12471299 try writer.writeAll(";\n");
12481300 break :blk local;
12491301 } else CValue{ .none = {} };
......@@ -1253,42 +1305,44 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
12531305 .result = result,
12541306 });
12551307
1256 try genBody(o, inst.body);
1308 try genBody(o, body);
12571309 try o.indent_writer.insertNewline();
12581310 // label must be followed by an expression, add an empty one.
12591311 try writer.print("zig_block_{d}:;\n", .{block_id});
12601312 return result;
12611313}
12621314
1263fn genBr(o: *Object, inst: *Inst.Br) !CValue {
1264 const result = o.blocks.get(inst.block).?.result;
1315fn airBr(o: *Object, inst: Air.Inst.Index) !CValue {
1316 const branch = o.air.instructions.items(.data)[inst].br;
1317 const block = o.blocks.get(branch.block_inst).?;
1318 const result = block.result;
12651319 const writer = o.writer();
12661320
12671321 // If result is .none then the value of the block is unused.
1268 if (inst.operand.ty.tag() != .void and result != .none) {
1269 const operand = try o.resolveInst(inst.operand);
1322 if (result != .none) {
1323 const operand = try o.resolveInst(branch.operand);
12701324 try o.writeCValue(writer, result);
12711325 try writer.writeAll(" = ");
12721326 try o.writeCValue(writer, operand);
12731327 try writer.writeAll(";\n");
12741328 }
12751329
1276 return genBrVoid(o, inst.block);
1277}
1278
1279fn genBrVoid(o: *Object, block: *Inst.Block) !CValue {
1280 try o.writer().print("goto zig_block_{d};\n", .{o.blocks.get(block).?.block_id});
1330 try o.writer().print("goto zig_block_{d};\n", .{block.block_id});
12811331 return CValue.none;
12821332}
12831333
1284fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
1285 const operand = try o.resolveInst(inst.operand);
1334fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue {
1335 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1336 const operand = try o.resolveInst(ty_op.operand);
12861337
12871338 const writer = o.writer();
1288 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
1289 const local = try o.allocLocal(inst.base.ty, .Const);
1339 const inst_ty = o.air.typeOfIndex(inst);
1340 if (inst_ty.zigTypeTag() == .Pointer and
1341 o.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
1342 {
1343 const local = try o.allocLocal(inst_ty, .Const);
12901344 try writer.writeAll(" = (");
1291 try o.dg.renderType(writer, inst.base.ty);
1345 try o.dg.renderType(writer, inst_ty);
12921346
12931347 try writer.writeAll(")");
12941348 try o.writeCValue(writer, operand);
......@@ -1296,7 +1350,7 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
12961350 return local;
12971351 }
12981352
1299 const local = try o.allocLocal(inst.base.ty, .Mut);
1353 const local = try o.allocLocal(inst_ty, .Mut);
13001354 try writer.writeAll(";\n");
13011355
13021356 try writer.writeAll("memcpy(&");
......@@ -1310,60 +1364,79 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
13101364 return local;
13111365}
13121366
1313fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue {
1314 _ = inst;
1367fn airBreakpoint(o: *Object) !CValue {
13151368 try o.writer().writeAll("zig_breakpoint();\n");
13161369 return CValue.none;
13171370}
13181371
1319fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue {
1320 _ = inst;
1372fn airUnreach(o: *Object) !CValue {
13211373 try o.writer().writeAll("zig_unreachable();\n");
13221374 return CValue.none;
13231375}
13241376
1325fn genLoop(o: *Object, inst: *Inst.Loop) !CValue {
1377fn airLoop(o: *Object, inst: Air.Inst.Index) !CValue {
1378 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1379 const loop = o.air.extraData(Air.Block, ty_pl.payload);
1380 const body = o.air.extra[loop.end..][0..loop.data.body_len];
13261381 try o.writer().writeAll("while (true) ");
1327 try genBody(o, inst.body);
1382 try genBody(o, body);
13281383 try o.indent_writer.insertNewline();
13291384 return CValue.none;
13301385}
13311386
1332fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue {
1333 const cond = try o.resolveInst(inst.condition);
1387fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue {
1388 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1389 const cond = try o.resolveInst(pl_op.operand);
1390 const extra = o.air.extraData(Air.CondBr, pl_op.payload);
1391 const then_body = o.air.extra[extra.end..][0..extra.data.then_body_len];
1392 const else_body = o.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
13341393 const writer = o.writer();
13351394
13361395 try writer.writeAll("if (");
13371396 try o.writeCValue(writer, cond);
13381397 try writer.writeAll(") ");
1339 try genBody(o, inst.then_body);
1398 try genBody(o, then_body);
13401399 try writer.writeAll(" else ");
1341 try genBody(o, inst.else_body);
1400 try genBody(o, else_body);
13421401 try o.indent_writer.insertNewline();
13431402
13441403 return CValue.none;
13451404}
13461405
1347fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {
1348 const target = try o.resolveInst(inst.target);
1406fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue {
1407 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1408 const condition = try o.resolveInst(pl_op.operand);
1409 const condition_ty = o.air.typeOf(pl_op.operand);
1410 const switch_br = o.air.extraData(Air.SwitchBr, pl_op.payload);
13491411 const writer = o.writer();
13501412
13511413 try writer.writeAll("switch (");
1352 try o.writeCValue(writer, target);
1353 try writer.writeAll(") {\n");
1414 try o.writeCValue(writer, condition);
1415 try writer.writeAll(") {");
13541416 o.indent_writer.pushIndent();
13551417
1356 for (inst.cases) |case| {
1357 try writer.writeAll("case ");
1358 try o.dg.renderValue(writer, inst.target.ty, case.item);
1359 try writer.writeAll(": ");
1360 // the case body must be noreturn so we don't need to insert a break
1361 try genBody(o, case.body);
1362 try o.indent_writer.insertNewline();
1418 var extra_index: usize = switch_br.end;
1419 var case_i: u32 = 0;
1420 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
1421 const case = o.air.extraData(Air.SwitchBr.Case, extra_index);
1422 const items = @bitCast([]const Air.Inst.Ref, o.air.extra[case.end..][0..case.data.items_len]);
1423 const case_body = o.air.extra[case.end + items.len ..][0..case.data.body_len];
1424 extra_index = case.end + case.data.items_len + case_body.len;
1425
1426 for (items) |item| {
1427 try o.indent_writer.insertNewline();
1428 try writer.writeAll("case ");
1429 try o.dg.renderValue(writer, condition_ty, o.air.value(item).?);
1430 try writer.writeAll(": ");
1431 }
1432 // The case body must be noreturn so we don't need to insert a break.
1433 try genBody(o, case_body);
13631434 }
13641435
1436 const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len];
1437 try o.indent_writer.insertNewline();
13651438 try writer.writeAll("default: ");
1366 try genBody(o, inst.else_body);
1439 try genBody(o, else_body);
13671440 try o.indent_writer.insertNewline();
13681441
13691442 o.indent_writer.popIndent();
......@@ -1371,39 +1444,75 @@ fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {
13711444 return CValue.none;
13721445}
13731446
1374fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
1375 if (as.base.isUnused() and !as.is_volatile)
1447fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1448 const air_datas = o.air.instructions.items(.data);
1449 const air_extra = o.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1450 const zir = o.dg.decl.namespace.file_scope.zir;
1451 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
1452 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1453 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
1454 const outputs_len = @truncate(u5, extended.small);
1455 const args_len = @truncate(u5, extended.small >> 5);
1456 const clobbers_len = @truncate(u5, extended.small >> 10);
1457 _ = clobbers_len; // TODO honor these
1458 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1459 const outputs = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end..][0..outputs_len]);
1460 const args = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end + outputs.len ..][0..args_len]);
1461
1462 if (outputs_len > 1) {
1463 return o.dg.fail("TODO implement codegen for asm with more than 1 output", .{});
1464 }
1465
1466 if (o.liveness.isUnused(inst) and !is_volatile)
13761467 return CValue.none;
13771468
1469 var extra_i: usize = zir_extra.end;
1470 const output_constraint: ?[]const u8 = out: {
1471 var i: usize = 0;
1472 while (i < outputs_len) : (i += 1) {
1473 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
1474 extra_i = output.end;
1475 break :out zir.nullTerminatedString(output.data.constraint);
1476 }
1477 break :out null;
1478 };
1479 const args_extra_begin = extra_i;
1480
13781481 const writer = o.writer();
1379 for (as.inputs) |i, index| {
1380 if (i[0] == '{' and i[i.len - 1] == '}') {
1381 const reg = i[1 .. i.len - 1];
1382 const arg = as.args[index];
1482 for (args) |arg| {
1483 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1484 extra_i = input.end;
1485 const constraint = zir.nullTerminatedString(input.data.constraint);
1486 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
1487 const reg = constraint[1 .. constraint.len - 1];
13831488 const arg_c_value = try o.resolveInst(arg);
13841489 try writer.writeAll("register ");
1385 try o.dg.renderType(writer, arg.ty);
1490 try o.dg.renderType(writer, o.air.typeOf(arg));
13861491
13871492 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
13881493 try o.writeCValue(writer, arg_c_value);
13891494 try writer.writeAll(";\n");
13901495 } else {
1391 return o.dg.fail(.{ .node_offset = 0 }, "TODO non-explicit inline asm regs", .{});
1496 return o.dg.fail("TODO non-explicit inline asm regs", .{});
13921497 }
13931498 }
1394 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
1395 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
1396 if (as.output_constraint) |_| {
1397 return o.dg.fail(.{ .node_offset = 0 }, "TODO: CBE inline asm output", .{});
1499 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
1500 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
1501 if (output_constraint) |_| {
1502 return o.dg.fail("TODO: CBE inline asm output", .{});
13981503 }
1399 if (as.inputs.len > 0) {
1400 if (as.output_constraint == null) {
1504 if (args.len > 0) {
1505 if (output_constraint == null) {
14011506 try writer.writeAll(" :");
14021507 }
14031508 try writer.writeAll(": ");
1404 for (as.inputs) |i, index| {
1405 if (i[0] == '{' and i[i.len - 1] == '}') {
1406 const reg = i[1 .. i.len - 1];
1509 extra_i = args_extra_begin;
1510 for (args) |_, index| {
1511 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1512 extra_i = input.end;
1513 const constraint = zir.nullTerminatedString(input.data.constraint);
1514 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
1515 const reg = constraint[1 .. constraint.len - 1];
14071516 if (index > 0) {
14081517 try writer.writeAll(", ");
14091518 }
......@@ -1416,40 +1525,51 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
14161525 }
14171526 try writer.writeAll(");\n");
14181527
1419 if (as.base.isUnused())
1528 if (o.liveness.isUnused(inst))
14201529 return CValue.none;
14211530
1422 return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: inline asm expression result used", .{});
1531 return o.dg.fail("TODO: C backend: inline asm expression result used", .{});
14231532}
14241533
1425fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue {
1534fn airIsNull(
1535 o: *Object,
1536 inst: Air.Inst.Index,
1537 operator: [*:0]const u8,
1538 deref_suffix: [*:0]const u8,
1539) !CValue {
1540 if (o.liveness.isUnused(inst))
1541 return CValue.none;
1542
1543 const un_op = o.air.instructions.items(.data)[inst].un_op;
14261544 const writer = o.writer();
1427 const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr;
1428 const operator = if (invert_logic) "!=" else "==";
1429 const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else "";
1430 const operand = try o.resolveInst(inst.operand);
1545 const operand = try o.resolveInst(un_op);
14311546
14321547 const local = try o.allocLocal(Type.initTag(.bool), .Const);
14331548 try writer.writeAll(" = (");
14341549 try o.writeCValue(writer, operand);
14351550
1436 if (inst.operand.ty.isPtrLikeOptional()) {
1551 if (o.air.typeOf(un_op).isPtrLikeOptional()) {
14371552 // operand is a regular pointer, test `operand !=/== NULL`
1438 try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator });
1553 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
14391554 } else {
1440 try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator });
1555 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
14411556 }
14421557 return local;
14431558}
14441559
1445fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
1560fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
1561 if (o.liveness.isUnused(inst))
1562 return CValue.none;
1563
1564 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
14461565 const writer = o.writer();
1447 const operand = try o.resolveInst(inst.operand);
1566 const operand = try o.resolveInst(ty_op.operand);
1567 const operand_ty = o.air.typeOf(ty_op.operand);
14481568
1449 const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer)
1450 inst.operand.ty.elemType()
1569 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
1570 operand_ty.elemType()
14511571 else
1452 inst.operand.ty;
1572 operand_ty;
14531573
14541574 if (opt_ty.isPtrLikeOptional()) {
14551575 // the operand is just a regular pointer, no need to do anything special.
......@@ -1457,10 +1577,11 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
14571577 return operand;
14581578 }
14591579
1460 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
1461 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
1580 const inst_ty = o.air.typeOfIndex(inst);
1581 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
1582 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
14621583
1463 const local = try o.allocLocal(inst.base.ty, .Const);
1584 const local = try o.allocLocal(inst_ty, .Const);
14641585 try writer.print(" = {s}(", .{maybe_addrof});
14651586 try o.writeCValue(writer, operand);
14661587
......@@ -1468,24 +1589,36 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
14681589 return local;
14691590}
14701591
1471fn genRef(o: *Object, inst: *Inst.UnOp) !CValue {
1592fn airRef(o: *Object, inst: Air.Inst.Index) !CValue {
1593 if (o.liveness.isUnused(inst))
1594 return CValue.none;
1595
1596 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
14721597 const writer = o.writer();
1473 const operand = try o.resolveInst(inst.operand);
1598 const operand = try o.resolveInst(ty_op.operand);
14741599
1475 const local = try o.allocLocal(inst.base.ty, .Const);
1600 const inst_ty = o.air.typeOfIndex(inst);
1601 const local = try o.allocLocal(inst_ty, .Const);
14761602 try writer.writeAll(" = ");
14771603 try o.writeCValue(writer, operand);
14781604 try writer.writeAll(";\n");
14791605 return local;
14801606}
14811607
1482fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
1608fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue {
1609 if (o.liveness.isUnused(inst))
1610 return CValue.none;
1611
1612 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1613 const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;
14831614 const writer = o.writer();
1484 const struct_ptr = try o.resolveInst(inst.struct_ptr);
1485 const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data;
1486 const field_name = struct_obj.fields.keys()[inst.field_index];
1615 const struct_ptr = try o.resolveInst(extra.struct_ptr);
1616 const struct_ptr_ty = o.air.typeOf(extra.struct_ptr);
1617 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
1618 const field_name = struct_obj.fields.keys()[extra.field_index];
14871619
1488 const local = try o.allocLocal(inst.base.ty, .Const);
1620 const inst_ty = o.air.typeOfIndex(inst);
1621 const local = try o.allocLocal(inst_ty, .Const);
14891622 switch (struct_ptr) {
14901623 .local_ref => |i| {
14911624 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
......@@ -1500,17 +1633,20 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
15001633}
15011634
15021635// *(E!T) -> E NOT *E
1503fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1504 if (inst.base.isUnused())
1636fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1637 if (o.liveness.isUnused(inst))
15051638 return CValue.none;
15061639
1640 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1641 const inst_ty = o.air.typeOfIndex(inst);
15071642 const writer = o.writer();
1508 const operand = try o.resolveInst(inst.operand);
1643 const operand = try o.resolveInst(ty_op.operand);
1644 const operand_ty = o.air.typeOf(ty_op.operand);
15091645
1510 const payload_ty = inst.operand.ty.errorUnionChild();
1646 const payload_ty = operand_ty.errorUnionChild();
15111647 if (!payload_ty.hasCodeGenBits()) {
1512 if (inst.operand.ty.zigTypeTag() == .Pointer) {
1513 const local = try o.allocLocal(inst.base.ty, .Const);
1648 if (operand_ty.zigTypeTag() == .Pointer) {
1649 const local = try o.allocLocal(inst_ty, .Const);
15141650 try writer.writeAll(" = *");
15151651 try o.writeCValue(writer, operand);
15161652 try writer.writeAll(";\n");
......@@ -1520,9 +1656,9 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
15201656 }
15211657 }
15221658
1523 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
1659 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
15241660
1525 const local = try o.allocLocal(inst.base.ty, .Const);
1661 const local = try o.allocLocal(inst_ty, .Const);
15261662 try writer.writeAll(" = (");
15271663 try o.writeCValue(writer, operand);
15281664
......@@ -1530,22 +1666,25 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
15301666 return local;
15311667}
15321668
1533fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1534 if (inst.base.isUnused())
1669fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1670 if (o.liveness.isUnused(inst))
15351671 return CValue.none;
15361672
1673 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
15371674 const writer = o.writer();
1538 const operand = try o.resolveInst(inst.operand);
1675 const operand = try o.resolveInst(ty_op.operand);
1676 const operand_ty = o.air.typeOf(ty_op.operand);
15391677
1540 const payload_ty = inst.operand.ty.errorUnionChild();
1678 const payload_ty = operand_ty.errorUnionChild();
15411679 if (!payload_ty.hasCodeGenBits()) {
15421680 return CValue.none;
15431681 }
15441682
1545 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
1546 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
1683 const inst_ty = o.air.typeOfIndex(inst);
1684 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
1685 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
15471686
1548 const local = try o.allocLocal(inst.base.ty, .Const);
1687 const local = try o.allocLocal(inst_ty, .Const);
15491688 try writer.print(" = {s}(", .{maybe_addrof});
15501689 try o.writeCValue(writer, operand);
15511690
......@@ -1553,54 +1692,75 @@ fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
15531692 return local;
15541693}
15551694
1556fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue {
1695fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue {
1696 if (o.liveness.isUnused(inst))
1697 return CValue.none;
1698
1699 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
15571700 const writer = o.writer();
1558 const operand = try o.resolveInst(inst.operand);
1701 const operand = try o.resolveInst(ty_op.operand);
15591702
1560 if (inst.base.ty.isPtrLikeOptional()) {
1703 const inst_ty = o.air.typeOfIndex(inst);
1704 if (inst_ty.isPtrLikeOptional()) {
15611705 // the operand is just a regular pointer, no need to do anything special.
15621706 return operand;
15631707 }
15641708
15651709 // .wrap_optional is used to convert non-optionals into optionals so it can never be null.
1566 const local = try o.allocLocal(inst.base.ty, .Const);
1710 const local = try o.allocLocal(inst_ty, .Const);
15671711 try writer.writeAll(" = { .is_null = false, .payload =");
15681712 try o.writeCValue(writer, operand);
15691713 try writer.writeAll("};\n");
15701714 return local;
15711715}
1572fn genWrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1716fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1717 if (o.liveness.isUnused(inst))
1718 return CValue.none;
1719
15731720 const writer = o.writer();
1574 const operand = try o.resolveInst(inst.operand);
1721 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1722 const operand = try o.resolveInst(ty_op.operand);
15751723
1576 const local = try o.allocLocal(inst.base.ty, .Const);
1724 const inst_ty = o.air.typeOfIndex(inst);
1725 const local = try o.allocLocal(inst_ty, .Const);
15771726 try writer.writeAll(" = { .error = ");
15781727 try o.writeCValue(writer, operand);
15791728 try writer.writeAll(" };\n");
15801729 return local;
15811730}
1582fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1731
1732fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1733 if (o.liveness.isUnused(inst))
1734 return CValue.none;
1735
1736 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
15831737 const writer = o.writer();
1584 const operand = try o.resolveInst(inst.operand);
1738 const operand = try o.resolveInst(ty_op.operand);
15851739
1586 const local = try o.allocLocal(inst.base.ty, .Const);
1740 const inst_ty = o.air.typeOfIndex(inst);
1741 const local = try o.allocLocal(inst_ty, .Const);
15871742 try writer.writeAll(" = { .error = 0, .payload = ");
15881743 try o.writeCValue(writer, operand);
15891744 try writer.writeAll(" };\n");
15901745 return local;
15911746}
15921747
1593fn genIsErr(
1748fn airIsErr(
15941749 o: *Object,
1595 inst: *Inst.UnOp,
1750 inst: Air.Inst.Index,
15961751 deref_prefix: [*:0]const u8,
15971752 deref_suffix: [*:0]const u8,
15981753 op_str: [*:0]const u8,
15991754) !CValue {
1755 if (o.liveness.isUnused(inst))
1756 return CValue.none;
1757
1758 const un_op = o.air.instructions.items(.data)[inst].un_op;
16001759 const writer = o.writer();
1601 const operand = try o.resolveInst(inst.operand);
1760 const operand = try o.resolveInst(un_op);
1761 const operand_ty = o.air.typeOf(un_op);
16021762 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1603 const payload_ty = inst.operand.ty.errorUnionChild();
1763 const payload_ty = operand_ty.errorUnionChild();
16041764 if (!payload_ty.hasCodeGenBits()) {
16051765 try writer.print(" = {s}", .{deref_prefix});
16061766 try o.writeCValue(writer, operand);
src/codegen/llvm.zig+278-195
......@@ -9,8 +9,8 @@ const math = std.math;
99
1010const Module = @import("../Module.zig");
1111const TypedValue = @import("../TypedValue.zig");
12const ir = @import("../air.zig");
13const Inst = ir.Inst;
12const Air = @import("../Air.zig");
13const Liveness = @import("../Liveness.zig");
1414
1515const Value = @import("../value.zig").Value;
1616const Type = @import("../type.zig").Type;
......@@ -276,6 +276,70 @@ pub const Object = struct {
276276 }
277277 }
278278
279 pub fn updateFunc(
280 self: *Object,
281 module: *Module,
282 func: *Module.Fn,
283 air: Air,
284 liveness: Liveness,
285 ) !void {
286 var dg: DeclGen = .{
287 .object = self,
288 .module = module,
289 .decl = func.owner_decl,
290 .err_msg = null,
291 .gpa = module.gpa,
292 };
293
294 const llvm_func = try dg.resolveLLVMFunction(func.owner_decl);
295
296 // This gets the LLVM values from the function and stores them in `dg.args`.
297 const fn_param_len = func.owner_decl.ty.fnParamLen();
298 var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len);
299
300 for (args) |*arg, i| {
301 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
302 }
303
304 // We remove all the basic blocks of a function to support incremental
305 // compilation!
306 // TODO: remove all basic blocks if functions can have more than one
307 if (llvm_func.getFirstBasicBlock()) |bb| {
308 bb.deleteBasicBlock();
309 }
310
311 const builder = dg.context().createBuilder();
312
313 const entry_block = dg.context().appendBasicBlock(llvm_func, "Entry");
314 builder.positionBuilderAtEnd(entry_block);
315
316 var fg: FuncGen = .{
317 .gpa = dg.gpa,
318 .air = air,
319 .liveness = liveness,
320 .dg = &dg,
321 .builder = builder,
322 .args = args,
323 .arg_index = 0,
324 .func_inst_table = .{},
325 .entry_block = entry_block,
326 .latest_alloca_inst = null,
327 .llvm_func = llvm_func,
328 .blocks = .{},
329 };
330 defer fg.deinit();
331
332 fg.genBody(air.getMainBody()) catch |err| switch (err) {
333 error.CodegenFail => {
334 func.owner_decl.analysis = .codegen_failure;
335 try module.failed_decls.put(module.gpa, func.owner_decl, dg.err_msg.?);
336 dg.err_msg = null;
337 return;
338 },
339 else => |e| return e,
340 };
341 }
342
279343 pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void {
280344 var dg: DeclGen = .{
281345 .object = self,
......@@ -327,44 +391,8 @@ pub const DeclGen = struct {
327391 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
328392
329393 if (decl.val.castTag(.function)) |func_payload| {
330 const func = func_payload.data;
331
332 const llvm_func = try self.resolveLLVMFunction(func.owner_decl);
333
334 // This gets the LLVM values from the function and stores them in `self.args`.
335 const fn_param_len = func.owner_decl.ty.fnParamLen();
336 var args = try self.gpa.alloc(*const llvm.Value, fn_param_len);
337
338 for (args) |*arg, i| {
339 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
340 }
341
342 // We remove all the basic blocks of a function to support incremental
343 // compilation!
344 // TODO: remove all basic blocks if functions can have more than one
345 if (llvm_func.getFirstBasicBlock()) |bb| {
346 bb.deleteBasicBlock();
347 }
348
349 const builder = self.context().createBuilder();
350
351 const entry_block = self.context().appendBasicBlock(llvm_func, "Entry");
352 builder.positionBuilderAtEnd(entry_block);
353
354 var fg: FuncGen = .{
355 .dg = self,
356 .builder = builder,
357 .args = args,
358 .arg_index = 0,
359 .func_inst_table = .{},
360 .entry_block = entry_block,
361 .latest_alloca_inst = null,
362 .llvm_func = llvm_func,
363 .blocks = .{},
364 };
365 defer fg.deinit();
366
367 try fg.genBody(func.body);
394 _ = func_payload;
395 @panic("TODO llvm backend genDecl function pointer");
368396 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
369397 _ = try self.resolveLLVMFunction(extern_fn.data);
370398 } else {
......@@ -590,29 +618,31 @@ pub const DeclGen = struct {
590618};
591619
592620pub const FuncGen = struct {
621 gpa: *Allocator,
593622 dg: *DeclGen,
623 air: Air,
624 liveness: Liveness,
594625
595626 builder: *const llvm.Builder,
596627
597 /// This stores the LLVM values used in a function, such that they can be
598 /// referred to in other instructions. This table is cleared before every function is generated.
599 /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks
600 /// in here, however if a block ends, the instructions can be thrown away.
601 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value),
628 /// This stores the LLVM values used in a function, such that they can be referred to
629 /// in other instructions. This table is cleared before every function is generated.
630 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value),
602631
603 /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction.
632 /// These fields are used to refer to the LLVM value of the function paramaters
633 /// in an Arg instruction.
604634 args: []*const llvm.Value,
605635 arg_index: usize,
606636
607637 entry_block: *const llvm.BasicBlock,
608 /// This fields stores the last alloca instruction, such that we can append more alloca instructions
609 /// to the top of the function.
638 /// This fields stores the last alloca instruction, such that we can append
639 /// more alloca instructions to the top of the function.
610640 latest_alloca_inst: ?*const llvm.Value,
611641
612642 llvm_func: *const llvm.Value,
613643
614644 /// This data structure is used to implement breaking to blocks.
615 blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct {
645 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
616646 parent_bb: *const llvm.BasicBlock,
617647 break_bbs: *BreakBasicBlocks,
618648 break_vals: *BreakValues,
......@@ -623,9 +653,9 @@ pub const FuncGen = struct {
623653
624654 fn deinit(self: *FuncGen) void {
625655 self.builder.dispose();
626 self.func_inst_table.deinit(self.gpa());
627 self.gpa().free(self.args);
628 self.blocks.deinit(self.gpa());
656 self.func_inst_table.deinit(self.gpa);
657 self.gpa.free(self.args);
658 self.blocks.deinit(self.gpa);
629659 }
630660
631661 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
......@@ -641,65 +671,68 @@ pub const FuncGen = struct {
641671 return self.dg.object.context;
642672 }
643673
644 fn gpa(self: *FuncGen) *Allocator {
645 return self.dg.gpa;
646 }
647
648 fn resolveInst(self: *FuncGen, inst: *ir.Inst) !*const llvm.Value {
649 if (inst.value()) |val| {
650 return self.dg.genTypedValue(.{ .ty = inst.ty, .val = val }, self);
674 fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {
675 if (self.air.value(inst)) |val| {
676 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self);
651677 }
652 if (self.func_inst_table.get(inst)) |value| return value;
678 const inst_index = Air.refToIndex(inst).?;
679 if (self.func_inst_table.get(inst_index)) |value| return value;
653680
654681 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
655682 }
656683
657 fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void {
658 for (body.instructions) |inst| {
659 const opt_value = switch (inst.tag) {
660 .add => try self.genAdd(inst.castTag(.add).?),
661 .alloc => try self.genAlloc(inst.castTag(.alloc).?),
662 .arg => try self.genArg(inst.castTag(.arg).?),
663 .bitcast => try self.genBitCast(inst.castTag(.bitcast).?),
664 .block => try self.genBlock(inst.castTag(.block).?),
665 .br => try self.genBr(inst.castTag(.br).?),
666 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
667 .br_void => try self.genBrVoid(inst.castTag(.br_void).?),
668 .call => try self.genCall(inst.castTag(.call).?),
669 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),
670 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),
671 .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?, .gte),
672 .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?, .lt),
673 .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?, .lte),
674 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),
675 .condbr => try self.genCondBr(inst.castTag(.condbr).?),
676 .intcast => try self.genIntCast(inst.castTag(.intcast).?),
677 .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false),
678 .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true),
679 .is_null => try self.genIsNull(inst.castTag(.is_null).?, false),
680 .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true),
681 .load => try self.genLoad(inst.castTag(.load).?),
682 .loop => try self.genLoop(inst.castTag(.loop).?),
683 .not => try self.genNot(inst.castTag(.not).?),
684 .ret => try self.genRet(inst.castTag(.ret).?),
685 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
686 .store => try self.genStore(inst.castTag(.store).?),
687 .sub => try self.genSub(inst.castTag(.sub).?),
688 .unreach => self.genUnreach(inst.castTag(.unreach).?),
689 .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false),
690 .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true),
684 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
685 const air_tags = self.air.instructions.items(.tag);
686 for (body) |inst| {
687 const opt_value = switch (air_tags[inst]) {
688 .add => try self.airAdd(inst),
689 .sub => try self.airSub(inst),
690
691 .cmp_eq => try self.airCmp(inst, .eq),
692 .cmp_gt => try self.airCmp(inst, .gt),
693 .cmp_gte => try self.airCmp(inst, .gte),
694 .cmp_lt => try self.airCmp(inst, .lt),
695 .cmp_lte => try self.airCmp(inst, .lte),
696 .cmp_neq => try self.airCmp(inst, .neq),
697
698 .is_non_null => try self.airIsNonNull(inst, false),
699 .is_non_null_ptr => try self.airIsNonNull(inst, true),
700 .is_null => try self.airIsNull(inst, false),
701 .is_null_ptr => try self.airIsNull(inst, true),
702
703 .alloc => try self.airAlloc(inst),
704 .arg => try self.airArg(inst),
705 .bitcast => try self.airBitCast(inst),
706 .block => try self.airBlock(inst),
707 .br => try self.airBr(inst),
708 .breakpoint => try self.airBreakpoint(inst),
709 .call => try self.airCall(inst),
710 .cond_br => try self.airCondBr(inst),
711 .intcast => try self.airIntCast(inst),
712 .load => try self.airLoad(inst),
713 .loop => try self.airLoop(inst),
714 .not => try self.airNot(inst),
715 .ret => try self.airRet(inst),
716 .store => try self.airStore(inst),
717 .unreach => self.airUnreach(inst),
718 .optional_payload => try self.airOptionalPayload(inst, false),
719 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
691720 .dbg_stmt => blk: {
692721 // TODO: implement debug info
693722 break :blk null;
694723 },
695 else => |tag| return self.todo("implement TZIR instruction: {}", .{tag}),
724 else => |tag| return self.todo("implement AIR instruction: {}", .{tag}),
696725 };
697 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa(), inst, val);
726 if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val);
698727 }
699728 }
700729
701 fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value {
702 if (inst.func.value()) |func_value| {
730 fn airCall(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
731 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
732 const extra = self.air.extraData(Air.Call, pl_op.payload);
733 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
734
735 if (self.air.value(pl_op.operand)) |func_value| {
703736 const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|
704737 extern_fn.data
705738 else if (func_value.castTag(.function)) |func_payload|
......@@ -711,12 +744,10 @@ pub const FuncGen = struct {
711744 const zig_fn_type = fn_decl.ty;
712745 const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl);
713746
714 const num_args = inst.args.len;
747 const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, args.len);
748 defer self.gpa.free(llvm_param_vals);
715749
716 const llvm_param_vals = try self.gpa().alloc(*const llvm.Value, num_args);
717 defer self.gpa().free(llvm_param_vals);
718
719 for (inst.args) |arg, i| {
750 for (args) |arg, i| {
720751 llvm_param_vals[i] = try self.resolveInst(arg);
721752 }
722753
......@@ -724,8 +755,8 @@ pub const FuncGen = struct {
724755 // Do we need that?
725756 const call = self.builder.buildCall(
726757 llvm_fn,
727 if (num_args == 0) null else llvm_param_vals.ptr,
728 @intCast(c_uint, num_args),
758 if (args.len == 0) null else llvm_param_vals.ptr,
759 @intCast(c_uint, args.len),
729760 "",
730761 );
731762
......@@ -743,31 +774,31 @@ pub const FuncGen = struct {
743774 }
744775 }
745776
746 fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {
747 _ = inst;
748 _ = self.builder.buildRetVoid();
749 return null;
750 }
751
752 fn genRet(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
753 if (!inst.operand.ty.hasCodeGenBits()) {
754 // TODO: in astgen these instructions should turn into `retvoid` instructions.
777 fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
778 const un_op = self.air.instructions.items(.data)[inst].un_op;
779 if (!self.air.typeOf(un_op).hasCodeGenBits()) {
755780 _ = self.builder.buildRetVoid();
756781 return null;
757782 }
758 _ = self.builder.buildRet(try self.resolveInst(inst.operand));
783 const operand = try self.resolveInst(un_op);
784 _ = self.builder.buildRet(operand);
759785 return null;
760786 }
761787
762 fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value {
763 const lhs = try self.resolveInst(inst.lhs);
764 const rhs = try self.resolveInst(inst.rhs);
788 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator) !?*const llvm.Value {
789 if (self.liveness.isUnused(inst))
790 return null;
765791
766 if (!inst.base.ty.isInt())
767 if (inst.base.ty.tag() != .bool)
768 return self.todo("implement 'genCmp' for type {}", .{inst.base.ty});
792 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
793 const lhs = try self.resolveInst(bin_op.lhs);
794 const rhs = try self.resolveInst(bin_op.rhs);
795 const inst_ty = self.air.typeOfIndex(inst);
769796
770 const is_signed = inst.base.ty.isSignedInt();
797 if (!inst_ty.isInt())
798 if (inst_ty.tag() != .bool)
799 return self.todo("implement 'airCmp' for type {}", .{inst_ty});
800
801 const is_signed = inst_ty.isSignedInt();
771802 const operation = switch (op) {
772803 .eq => .EQ,
773804 .neq => .NE,
......@@ -780,32 +811,36 @@ pub const FuncGen = struct {
780811 return self.builder.buildICmp(operation, lhs, rhs, "");
781812 }
782813
783 fn genBlock(self: *FuncGen, inst: *Inst.Block) !?*const llvm.Value {
814 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
815 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
816 const extra = self.air.extraData(Air.Block, ty_pl.payload);
817 const body = self.air.extra[extra.end..][0..extra.data.body_len];
784818 const parent_bb = self.context().createBasicBlock("Block");
785819
786820 // 5 breaks to a block seems like a reasonable default.
787 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5);
788 var break_vals = try BreakValues.initCapacity(self.gpa(), 5);
789 try self.blocks.putNoClobber(self.gpa(), inst, .{
821 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5);
822 var break_vals = try BreakValues.initCapacity(self.gpa, 5);
823 try self.blocks.putNoClobber(self.gpa, inst, .{
790824 .parent_bb = parent_bb,
791825 .break_bbs = &break_bbs,
792826 .break_vals = &break_vals,
793827 });
794828 defer {
795829 assert(self.blocks.remove(inst));
796 break_bbs.deinit(self.gpa());
797 break_vals.deinit(self.gpa());
830 break_bbs.deinit(self.gpa);
831 break_vals.deinit(self.gpa);
798832 }
799833
800 try self.genBody(inst.body);
834 try self.genBody(body);
801835
802836 self.llvm_func.appendExistingBasicBlock(parent_bb);
803837 self.builder.positionBuilderAtEnd(parent_bb);
804838
805839 // If the block does not return a value, we dont have to create a phi node.
806 if (!inst.base.ty.hasCodeGenBits()) return null;
840 const inst_ty = self.air.typeOfIndex(inst);
841 if (!inst_ty.hasCodeGenBits()) return null;
807842
808 const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst.base.ty), "");
843 const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst_ty), "");
809844 phi_node.addIncoming(
810845 break_vals.items.ptr,
811846 break_bbs.items.ptr,
......@@ -814,35 +849,30 @@ pub const FuncGen = struct {
814849 return phi_node;
815850 }
816851
817 fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value {
818 var block = self.blocks.get(inst.block).?;
852 fn airBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
853 const branch = self.air.instructions.items(.data)[inst].br;
854 const block = self.blocks.get(branch.block_inst).?;
819855
820856 // If the break doesn't break a value, then we don't have to add
821857 // the values to the lists.
822 if (!inst.operand.ty.hasCodeGenBits()) {
823 // TODO: in astgen these instructions should turn into `br_void` instructions.
824 _ = self.builder.buildBr(block.parent_bb);
825 } else {
826 const val = try self.resolveInst(inst.operand);
858 if (self.air.typeOf(branch.operand).hasCodeGenBits()) {
859 const val = try self.resolveInst(branch.operand);
827860
828861 // For the phi node, we need the basic blocks and the values of the
829862 // break instructions.
830 try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock());
831 try block.break_vals.append(self.gpa(), val);
832
833 _ = self.builder.buildBr(block.parent_bb);
863 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
864 try block.break_vals.append(self.gpa, val);
834865 }
835 return null;
836 }
837
838 fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value {
839 var block = self.blocks.get(inst.block).?;
840866 _ = self.builder.buildBr(block.parent_bb);
841867 return null;
842868 }
843869
844 fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value {
845 const condition_value = try self.resolveInst(inst.condition);
870 fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
871 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
872 const cond = try self.resolveInst(pl_op.operand);
873 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
874 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
875 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
846876
847877 const then_block = self.context().appendBasicBlock(self.llvm_func, "Then");
848878 const else_block = self.context().appendBasicBlock(self.llvm_func, "Else");
......@@ -851,38 +881,51 @@ pub const FuncGen = struct {
851881 defer self.builder.positionBuilderAtEnd(prev_block);
852882
853883 self.builder.positionBuilderAtEnd(then_block);
854 try self.genBody(inst.then_body);
884 try self.genBody(then_body);
855885
856886 self.builder.positionBuilderAtEnd(else_block);
857 try self.genBody(inst.else_body);
887 try self.genBody(else_body);
858888 }
859 _ = self.builder.buildCondBr(condition_value, then_block, else_block);
889 _ = self.builder.buildCondBr(cond, then_block, else_block);
860890 return null;
861891 }
862892
863 fn genLoop(self: *FuncGen, inst: *Inst.Loop) !?*const llvm.Value {
893 fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
894 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
895 const loop = self.air.extraData(Air.Block, ty_pl.payload);
896 const body = self.air.extra[loop.end..][0..loop.data.body_len];
864897 const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop");
865898 _ = self.builder.buildBr(loop_block);
866899
867900 self.builder.positionBuilderAtEnd(loop_block);
868 try self.genBody(inst.body);
901 try self.genBody(body);
869902
870903 _ = self.builder.buildBr(loop_block);
871904 return null;
872905 }
873906
874 fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
875 return self.builder.buildNot(try self.resolveInst(inst.operand), "");
907 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
908 if (self.liveness.isUnused(inst))
909 return null;
910
911 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
912 const operand = try self.resolveInst(ty_op.operand);
913
914 return self.builder.buildNot(operand, "");
876915 }
877916
878 fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {
917 fn airUnreach(self: *FuncGen, inst: Air.Inst.Index) ?*const llvm.Value {
879918 _ = inst;
880919 _ = self.builder.buildUnreachable();
881920 return null;
882921 }
883922
884 fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
885 const operand = try self.resolveInst(inst.operand);
923 fn airIsNonNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
924 if (self.liveness.isUnused(inst))
925 return null;
926
927 const un_op = self.air.instructions.items(.data)[inst].un_op;
928 const operand = try self.resolveInst(un_op);
886929
887930 if (operand_is_ptr) {
888931 const index_type = self.context().intType(32);
......@@ -898,12 +941,23 @@ pub const FuncGen = struct {
898941 }
899942 }
900943
901 fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
902 return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");
944 fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
945 if (self.liveness.isUnused(inst))
946 return null;
947
948 return self.builder.buildNot((try self.airIsNonNull(inst, operand_is_ptr)).?, "");
903949 }
904950
905 fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
906 const operand = try self.resolveInst(inst.operand);
951 fn airOptionalPayload(
952 self: *FuncGen,
953 inst: Air.Inst.Index,
954 operand_is_ptr: bool,
955 ) !?*const llvm.Value {
956 if (self.liveness.isUnused(inst))
957 return null;
958
959 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
960 const operand = try self.resolveInst(ty_op.operand);
907961
908962 if (operand_is_ptr) {
909963 const index_type = self.context().intType(32);
......@@ -919,61 +973,83 @@ pub const FuncGen = struct {
919973 }
920974 }
921975
922 fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
923 const lhs = try self.resolveInst(inst.lhs);
924 const rhs = try self.resolveInst(inst.rhs);
976 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
977 if (self.liveness.isUnused(inst))
978 return null;
979 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
980 const lhs = try self.resolveInst(bin_op.lhs);
981 const rhs = try self.resolveInst(bin_op.rhs);
982 const inst_ty = self.air.typeOfIndex(inst);
925983
926 if (!inst.base.ty.isInt())
927 return self.todo("implement 'genAdd' for type {}", .{inst.base.ty});
984 if (!inst_ty.isInt())
985 return self.todo("implement 'airAdd' for type {}", .{inst_ty});
928986
929 return if (inst.base.ty.isSignedInt())
987 return if (inst_ty.isSignedInt())
930988 self.builder.buildNSWAdd(lhs, rhs, "")
931989 else
932990 self.builder.buildNUWAdd(lhs, rhs, "");
933991 }
934992
935 fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
936 const lhs = try self.resolveInst(inst.lhs);
937 const rhs = try self.resolveInst(inst.rhs);
993 fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
994 if (self.liveness.isUnused(inst))
995 return null;
996 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
997 const lhs = try self.resolveInst(bin_op.lhs);
998 const rhs = try self.resolveInst(bin_op.rhs);
999 const inst_ty = self.air.typeOfIndex(inst);
9381000
939 if (!inst.base.ty.isInt())
940 return self.todo("implement 'genSub' for type {}", .{inst.base.ty});
1001 if (!inst_ty.isInt())
1002 return self.todo("implement 'airSub' for type {}", .{inst_ty});
9411003
942 return if (inst.base.ty.isSignedInt())
1004 return if (inst_ty.isSignedInt())
9431005 self.builder.buildNSWSub(lhs, rhs, "")
9441006 else
9451007 self.builder.buildNUWSub(lhs, rhs, "");
9461008 }
9471009
948 fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
949 const val = try self.resolveInst(inst.operand);
1010 fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1011 if (self.liveness.isUnused(inst))
1012 return null;
1013
1014 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1015 const operand = try self.resolveInst(ty_op.operand);
1016 const inst_ty = self.air.typeOfIndex(inst);
9501017
951 const signed = inst.base.ty.isSignedInt();
1018 const signed = inst_ty.isSignedInt();
9521019 // TODO: Should we use intcast here or just a simple bitcast?
9531020 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
954 return self.builder.buildIntCast2(val, try self.dg.getLLVMType(inst.base.ty), llvm.Bool.fromBool(signed), "");
1021 return self.builder.buildIntCast2(operand, try self.dg.getLLVMType(inst_ty), llvm.Bool.fromBool(signed), "");
9551022 }
9561023
957 fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
958 const val = try self.resolveInst(inst.operand);
959 const dest_type = try self.dg.getLLVMType(inst.base.ty);
1024 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1025 if (self.liveness.isUnused(inst))
1026 return null;
1027
1028 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1029 const operand = try self.resolveInst(ty_op.operand);
1030 const inst_ty = self.air.typeOfIndex(inst);
1031 const dest_type = try self.dg.getLLVMType(inst_ty);
9601032
961 return self.builder.buildBitCast(val, dest_type, "");
1033 return self.builder.buildBitCast(operand, dest_type, "");
9621034 }
9631035
964 fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value {
1036 fn airArg(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
9651037 const arg_val = self.args[self.arg_index];
9661038 self.arg_index += 1;
9671039
968 const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst.base.ty));
1040 const inst_ty = self.air.typeOfIndex(inst);
1041 const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst_ty));
9691042 _ = self.builder.buildStore(arg_val, ptr_val);
9701043 return self.builder.buildLoad(ptr_val, "");
9711044 }
9721045
973 fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {
1046 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1047 if (self.liveness.isUnused(inst))
1048 return null;
9741049 // buildAlloca expects the pointee type, not the pointer type, so assert that
9751050 // a Payload.PointerSimple is passed to the alloc instruction.
976 const pointee_type = inst.base.ty.castPointer().?.data;
1051 const inst_ty = self.air.typeOfIndex(inst);
1052 const pointee_type = inst_ty.castPointer().?.data;
9771053
9781054 // TODO: figure out a way to get the name of the var decl.
9791055 // TODO: set alignment and volatile
......@@ -1004,19 +1080,26 @@ pub const FuncGen = struct {
10041080 return val;
10051081 }
10061082
1007 fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {
1008 const val = try self.resolveInst(inst.rhs);
1009 const ptr = try self.resolveInst(inst.lhs);
1010 _ = self.builder.buildStore(val, ptr);
1083 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1084 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1085 const dest_ptr = try self.resolveInst(bin_op.lhs);
1086 const src_operand = try self.resolveInst(bin_op.rhs);
1087 // TODO set volatile on this store properly
1088 _ = self.builder.buildStore(src_operand, dest_ptr);
10111089 return null;
10121090 }
10131091
1014 fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {
1015 const ptr_val = try self.resolveInst(inst.operand);
1016 return self.builder.buildLoad(ptr_val, "");
1092 fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1093 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1094 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1095 if (!is_volatile and self.liveness.isUnused(inst))
1096 return null;
1097 const ptr = try self.resolveInst(ty_op.operand);
1098 // TODO set volatile on this load properly
1099 return self.builder.buildLoad(ptr, "");
10171100 }
10181101
1019 fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {
1102 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
10201103 _ = inst;
10211104 const llvn_fn = self.getIntrinsic("llvm.debugtrap");
10221105 _ = self.builder.buildCall(llvn_fn, null, 0, "");
src/codegen/spirv.zig+247-245
......@@ -12,21 +12,21 @@ const Decl = Module.Decl;
1212const Type = @import("../type.zig").Type;
1313const Value = @import("../value.zig").Value;
1414const LazySrcLoc = Module.LazySrcLoc;
15const ir = @import("../air.zig");
16const Inst = ir.Inst;
15const Air = @import("../Air.zig");
16const Liveness = @import("../Liveness.zig");
1717
1818pub const Word = u32;
1919pub const ResultId = u32;
2020
2121pub const TypeMap = std.HashMap(Type, u32, Type.HashContext64, std.hash_map.default_max_load_percentage);
22pub const InstMap = std.AutoHashMap(*Inst, ResultId);
22pub const InstMap = std.AutoHashMap(Air.Inst.Index, ResultId);
2323
2424const IncomingBlock = struct {
2525 src_label_id: ResultId,
2626 break_value_id: ResultId,
2727};
2828
29pub const BlockMap = std.AutoHashMap(*Inst.Block, struct {
29pub const BlockMap = std.AutoHashMap(Air.Inst.Index, struct {
3030 label_id: ResultId,
3131 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),
3232});
......@@ -160,7 +160,11 @@ pub const DeclGen = struct {
160160 /// The SPIR-V module code should be put in.
161161 spv: *SPIRVModule,
162162
163 /// An array of function argument result-ids. Each index corresponds with the function argument of the same index.
163 air: Air,
164 liveness: Liveness,
165
166 /// An array of function argument result-ids. Each index corresponds with the
167 /// function argument of the same index.
164168 args: std.ArrayList(ResultId),
165169
166170 /// A counter to keep track of how many `arg` instructions we've seen yet.
......@@ -169,33 +173,35 @@ pub const DeclGen = struct {
169173 /// A map keeping track of which instruction generated which result-id.
170174 inst_results: InstMap,
171175
172 /// We need to keep track of result ids for block labels, as well as the 'incoming' blocks for a block.
176 /// We need to keep track of result ids for block labels, as well as the 'incoming'
177 /// blocks for a block.
173178 blocks: BlockMap,
174179
175180 /// The label of the SPIR-V block we are currently generating.
176181 current_block_label_id: ResultId,
177182
178 /// The actual instructions for this function. We need to declare all locals in the first block, and because we don't
179 /// know which locals there are going to be, we're just going to generate everything after the locals-section in this array.
180 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the initial OpLabel. These will be generated
181 /// into spv.binary.fn_decls directly.
183 /// The actual instructions for this function. We need to declare all locals in
184 /// the first block, and because we don't know which locals there are going to be,
185 /// we're just going to generate everything after the locals-section in this array.
186 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the
187 /// initial OpLabel. These will be generated into spv.binary.fn_decls directly.
182188 code: std.ArrayList(Word),
183189
184190 /// The decl we are currently generating code for.
185191 decl: *Decl,
186192
187 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message. Memory is owned by
188 /// `module.gpa`.
193 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message.
194 /// Memory is owned by `module.gpa`.
189195 error_msg: ?*Module.ErrorMsg,
190196
191197 /// Possible errors the `gen` function may return.
192198 const Error = error{ AnalysisFail, OutOfMemory };
193199
194 /// This structure is used to return information about a type typically used for arithmetic operations.
195 /// These types may either be integers, floats, or a vector of these. Most scalar operations also work on vectors,
196 /// so we can easily represent those as arithmetic types.
197 /// If the type is a scalar, 'inner type' refers to the scalar type. Otherwise, if its a vector, it refers
198 /// to the vector's element type.
200 /// This structure is used to return information about a type typically used for
201 /// arithmetic operations. These types may either be integers, floats, or a vector
202 /// of these. Most scalar operations also work on vectors, so we can easily represent
203 /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the
204 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type.
199205 const ArithmeticTypeInfo = struct {
200206 /// A classification of the inner type.
201207 const Class = enum {
......@@ -207,13 +213,14 @@ pub const DeclGen = struct {
207213 /// the relevant capability is enabled).
208214 integer,
209215
210 /// A regular float. These are all required to be natively supported. Floating points for
211 /// which the relevant capability is not enabled are not emulated.
216 /// A regular float. These are all required to be natively supported. Floating points
217 /// for which the relevant capability is not enabled are not emulated.
212218 float,
213219
214 /// An integer of a 'strange' size (which' bit size is not the same as its backing type. **Note**: this
215 /// may **also** include power-of-2 integers for which the relevant capability is not enabled), but still
216 /// within the limits of the largest natively supported integer type.
220 /// An integer of a 'strange' size (which' bit size is not the same as its backing
221 /// type. **Note**: this may **also** include power-of-2 integers for which the
222 /// relevant capability is not enabled), but still within the limits of the largest
223 /// natively supported integer type.
217224 strange_integer,
218225
219226 /// An integer with more bits than the largest natively supported integer type.
......@@ -221,7 +228,7 @@ pub const DeclGen = struct {
221228 };
222229
223230 /// The number of bits in the inner type.
224 /// Note: this is the actual number of bits of the type, not the size of the backing integer.
231 /// This is the actual number of bits of the type, not the size of the backing integer.
225232 bits: u16,
226233
227234 /// Whether the type is a vector.
......@@ -235,10 +242,13 @@ pub const DeclGen = struct {
235242 class: Class,
236243 };
237244
238 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, only set when `gen` is called.
245 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
246 /// only set when `gen` is called.
239247 pub fn init(spv: *SPIRVModule) DeclGen {
240248 return .{
241249 .spv = spv,
250 .air = undefined,
251 .liveness = undefined,
242252 .args = std.ArrayList(ResultId).init(spv.gpa),
243253 .next_arg_index = undefined,
244254 .inst_results = InstMap.init(spv.gpa),
......@@ -251,10 +261,12 @@ pub const DeclGen = struct {
251261 }
252262
253263 /// Generate the code for `decl`. If a reportable error occured during code generation,
254 /// a message is returned by this function. Callee owns the memory. If this function returns such
255 /// a reportable error, it is valid to be called again for a different decl.
256 pub fn gen(self: *DeclGen, decl: *Decl) !?*Module.ErrorMsg {
264 /// a message is returned by this function. Callee owns the memory. If this function
265 /// returns such a reportable error, it is valid to be called again for a different decl.
266 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
257267 // Reset internal resources, we don't want to re-allocate these.
268 self.air = air;
269 self.liveness = liveness;
258270 self.args.items.len = 0;
259271 self.next_arg_index = 0;
260272 self.inst_results.clearRetainingCapacity();
......@@ -280,19 +292,20 @@ pub const DeclGen = struct {
280292 return self.spv.module.getTarget();
281293 }
282294
283 fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) Error {
295 fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
284296 @setCold(true);
297 const src: LazySrcLoc = .{ .node_offset = 0 };
285298 const src_loc = src.toSrcLocWithDecl(self.decl);
286299 self.error_msg = try Module.ErrorMsg.create(self.spv.module.gpa, src_loc, format, args);
287300 return error.AnalysisFail;
288301 }
289302
290 fn resolve(self: *DeclGen, inst: *Inst) !ResultId {
291 if (inst.value()) |val| {
292 return self.genConstant(inst.src, inst.ty, val);
303 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !ResultId {
304 if (self.air.value(inst)) |val| {
305 return self.genConstant(self.air.typeOf(inst), val);
293306 }
294
295 return self.inst_results.get(inst).?; // Instruction does not dominate all uses!
307 const index = Air.refToIndex(inst).?;
308 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
296309 }
297310
298311 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {
......@@ -314,7 +327,7 @@ pub const DeclGen = struct {
314327 const target = self.getTarget();
315328
316329 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
317 std.debug.assert(bits != 0);
330 assert(bits != 0);
318331
319332 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
320333 // 32-bit integers are always supported (see spec, 2.16.1, Data rules).
......@@ -388,19 +401,19 @@ pub const DeclGen = struct {
388401 .composite_integer };
389402 },
390403 // As of yet, there is no vector support in the self-hosted compiler.
391 .Vector => self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement arithmeticTypeInfo for Vector", .{}),
404 .Vector => self.fail("TODO: SPIR-V backend: implement arithmeticTypeInfo for Vector", .{}),
392405 // TODO: For which types is this the case?
393 else => self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement arithmeticTypeInfo for {}", .{ty}),
406 else => self.fail("TODO: SPIR-V backend: implement arithmeticTypeInfo for {}", .{ty}),
394407 };
395408 }
396409
397410 /// Generate a constant representing `val`.
398411 /// TODO: Deduplication?
399 fn genConstant(self: *DeclGen, src: LazySrcLoc, ty: Type, val: Value) Error!ResultId {
412 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!ResultId {
400413 const target = self.getTarget();
401414 const code = &self.spv.binary.types_globals_constants;
402415 const result_id = self.spv.allocResultId();
403 const result_type_id = try self.genType(src, ty);
416 const result_type_id = try self.genType(ty);
404417
405418 if (val.isUndef()) {
406419 try writeInstruction(code, .OpUndef, &[_]Word{ result_type_id, result_id });
......@@ -412,13 +425,13 @@ pub const DeclGen = struct {
412425 const int_info = ty.intInfo(target);
413426 const backing_bits = self.backingIntBits(int_info.bits) orelse {
414427 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.
415 return self.fail(src, "TODO: SPIR-V backend: implement composite int constants for {}", .{ty});
428 return self.fail("TODO: SPIR-V backend: implement composite int constants for {}", .{ty});
416429 };
417430
418431 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any
419432 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this
420433 // might need to be updated.
421 std.debug.assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64));
434 assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64));
422435 var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt();
423436
424437 // Mask the low bits which make up the actual integer. This is to make sure that negative values
......@@ -470,13 +483,13 @@ pub const DeclGen = struct {
470483 }
471484 },
472485 .Void => unreachable,
473 else => return self.fail(src, "TODO: SPIR-V backend: constant generation of type {}", .{ty}),
486 else => return self.fail("TODO: SPIR-V backend: constant generation of type {}", .{ty}),
474487 }
475488
476489 return result_id;
477490 }
478491
479 fn genType(self: *DeclGen, src: LazySrcLoc, ty: Type) Error!ResultId {
492 fn genType(self: *DeclGen, ty: Type) Error!ResultId {
480493 // We can't use getOrPut here so we can recursively generate types.
481494 if (self.spv.types.get(ty)) |already_generated| {
482495 return already_generated;
......@@ -493,7 +506,7 @@ pub const DeclGen = struct {
493506 const int_info = ty.intInfo(target);
494507 const backing_bits = self.backingIntBits(int_info.bits) orelse {
495508 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.
496 return self.fail(src, "TODO: SPIR-V backend: implement composite int {}", .{ty});
509 return self.fail("TODO: SPIR-V backend: implement composite int {}", .{ty});
497510 };
498511
499512 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.
......@@ -519,7 +532,7 @@ pub const DeclGen = struct {
519532 };
520533
521534 if (!supported) {
522 return self.fail(src, "Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});
535 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});
523536 }
524537
525538 try writeInstruction(code, .OpTypeFloat, &[_]Word{ result_id, bits });
......@@ -527,19 +540,19 @@ pub const DeclGen = struct {
527540 .Fn => {
528541 // We only support zig-calling-convention functions, no varargs.
529542 if (ty.fnCallingConvention() != .Unspecified)
530 return self.fail(src, "Unsupported calling convention for SPIR-V", .{});
543 return self.fail("Unsupported calling convention for SPIR-V", .{});
531544 if (ty.fnIsVarArgs())
532 return self.fail(src, "VarArgs unsupported for SPIR-V", .{});
545 return self.fail("VarArgs unsupported for SPIR-V", .{});
533546
534547 // In order to avoid a temporary here, first generate all the required types and then simply look them up
535548 // when generating the function type.
536549 const params = ty.fnParamLen();
537550 var i: usize = 0;
538551 while (i < params) : (i += 1) {
539 _ = try self.genType(src, ty.fnParamType(i));
552 _ = try self.genType(ty.fnParamType(i));
540553 }
541554
542 const return_type_id = try self.genType(src, ty.fnReturnType());
555 const return_type_id = try self.genType(ty.fnReturnType());
543556
544557 // result id + result type id + parameter type ids.
545558 try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u16, ty.fnParamLen()));
......@@ -552,7 +565,7 @@ pub const DeclGen = struct {
552565 }
553566 },
554567 // When recursively generating a type, we cannot infer the pointer's storage class. See genPointerType.
555 .Pointer => return self.fail(src, "Cannot create pointer with unkown storage class", .{}),
568 .Pointer => return self.fail("Cannot create pointer with unkown storage class", .{}),
556569 .Vector => {
557570 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
558571 // which work on them), so simply use those.
......@@ -562,7 +575,7 @@ pub const DeclGen = struct {
562575 // is adequate at all for this.
563576
564577 // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems.
565 return self.fail(src, "TODO: SPIR-V backend: implement type Vector", .{});
578 return self.fail("TODO: SPIR-V backend: implement type Vector", .{});
566579 },
567580 .Null,
568581 .Undefined,
......@@ -574,7 +587,7 @@ pub const DeclGen = struct {
574587
575588 .BoundFn => unreachable, // this type will be deleted from the language.
576589
577 else => |tag| return self.fail(src, "TODO: SPIR-V backend: implement type {}s", .{tag}),
590 else => |tag| return self.fail("TODO: SPIR-V backend: implement type {}s", .{tag}),
578591 }
579592
580593 try self.spv.types.putNoClobber(ty, result_id);
......@@ -583,8 +596,8 @@ pub const DeclGen = struct {
583596
584597 /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that.
585598 /// TODO: The result of this needs to be cached.
586 fn genPointerType(self: *DeclGen, src: LazySrcLoc, ty: Type, storage_class: spec.StorageClass) !ResultId {
587 std.debug.assert(ty.zigTypeTag() == .Pointer);
599 fn genPointerType(self: *DeclGen, ty: Type, storage_class: spec.StorageClass) !ResultId {
600 assert(ty.zigTypeTag() == .Pointer);
588601
589602 const code = &self.spv.binary.types_globals_constants;
590603 const result_id = self.spv.allocResultId();
......@@ -592,7 +605,7 @@ pub const DeclGen = struct {
592605 // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types
593606 // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled.
594607 // These also relates to the pointer's address space.
595 const child_id = try self.genType(src, ty.elemType());
608 const child_id = try self.genType(ty.elemType());
596609
597610 try writeInstruction(code, .OpTypePointer, &[_]Word{ result_id, @enumToInt(storage_class), child_id });
598611
......@@ -603,9 +616,9 @@ pub const DeclGen = struct {
603616 const decl = self.decl;
604617 const result_id = decl.fn_link.spirv.id;
605618
606 if (decl.val.castTag(.function)) |func_payload| {
607 std.debug.assert(decl.ty.zigTypeTag() == .Fn);
608 const prototype_id = try self.genType(.{ .node_offset = 0 }, decl.ty);
619 if (decl.val.castTag(.function)) |_| {
620 assert(decl.ty.zigTypeTag() == .Fn);
621 const prototype_id = try self.genType(decl.ty);
609622 try writeInstruction(&self.spv.binary.fn_decls, .OpFunction, &[_]Word{
610623 self.spv.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.
611624 result_id,
......@@ -632,189 +645,171 @@ pub const DeclGen = struct {
632645 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{root_block_id});
633646 self.current_block_label_id = root_block_id;
634647
635 try self.genBody(func_payload.data.body);
648 const main_body = self.air.getMainBody();
649 try self.genBody(main_body);
636650
637651 // Append the actual code into the fn_decls section.
638652 try self.spv.binary.fn_decls.appendSlice(self.code.items);
639653 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});
640654 } else {
641 return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});
655 return self.fail("TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});
642656 }
643657 }
644658
645 fn genBody(self: *DeclGen, body: ir.Body) Error!void {
646 for (body.instructions) |inst| {
659 fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void {
660 for (body) |inst| {
647661 try self.genInst(inst);
648662 }
649663 }
650664
651 fn genInst(self: *DeclGen, inst: *Inst) !void {
652 const result_id = switch (inst.tag) {
653 .add, .addwrap => try self.genBinOp(inst.castTag(.add).?),
654 .sub, .subwrap => try self.genBinOp(inst.castTag(.sub).?),
655 .mul, .mulwrap => try self.genBinOp(inst.castTag(.mul).?),
656 .div => try self.genBinOp(inst.castTag(.div).?),
657 .bit_and => try self.genBinOp(inst.castTag(.bit_and).?),
658 .bit_or => try self.genBinOp(inst.castTag(.bit_or).?),
659 .xor => try self.genBinOp(inst.castTag(.xor).?),
660 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?),
661 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?),
662 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?),
663 .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?),
664 .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?),
665 .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?),
666 .bool_and => try self.genBinOp(inst.castTag(.bool_and).?),
667 .bool_or => try self.genBinOp(inst.castTag(.bool_or).?),
668 .not => try self.genUnOp(inst.castTag(.not).?),
669 .alloc => try self.genAlloc(inst.castTag(.alloc).?),
670 .arg => self.genArg(),
671 .block => (try self.genBlock(inst.castTag(.block).?)) orelse return,
672 .br => return try self.genBr(inst.castTag(.br).?),
673 .br_void => return try self.genBrVoid(inst.castTag(.br_void).?),
674 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them
675 // throughout the IR.
665 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
666 const air_tags = self.air.instructions.items(.tag);
667 const result_id = switch (air_tags[inst]) {
668 // zig fmt: off
669 .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}),
670 .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}),
671 .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}),
672 .div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}),
673
674 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),
675 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),
676 .xor => try self.airBinOpSimple(inst, .OpBitwiseXor),
677 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
678 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),
679
680 .not => try self.airNot(inst),
681
682 .cmp_eq => try self.airCmp(inst, .{.OpFOrdEqual, .OpLogicalEqual, .OpIEqual}),
683 .cmp_neq => try self.airCmp(inst, .{.OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual}),
684 .cmp_gt => try self.airCmp(inst, .{.OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan}),
685 .cmp_gte => try self.airCmp(inst, .{.OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual}),
686 .cmp_lt => try self.airCmp(inst, .{.OpFOrdLessThan, .OpSLessThan, .OpULessThan}),
687 .cmp_lte => try self.airCmp(inst, .{.OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual}),
688
689 .arg => self.airArg(),
690 .alloc => try self.airAlloc(inst),
691 .block => (try self.airBlock(inst)) orelse return,
692 .load => try self.airLoad(inst),
693
694 .br => return self.airBr(inst),
676695 .breakpoint => return,
677 .condbr => return try self.genCondBr(inst.castTag(.condbr).?),
678 .constant => unreachable,
679 .dbg_stmt => return try self.genDbgStmt(inst.castTag(.dbg_stmt).?),
680 .load => try self.genLoad(inst.castTag(.load).?),
681 .loop => return try self.genLoop(inst.castTag(.loop).?),
682 .ret => return try self.genRet(inst.castTag(.ret).?),
683 .retvoid => return try self.genRetVoid(),
684 .store => return try self.genStore(inst.castTag(.store).?),
685 .unreach => return try self.genUnreach(),
686 else => return self.fail(inst.src, "TODO: SPIR-V backend: implement inst {s}", .{@tagName(inst.tag)}),
696 .cond_br => return self.airCondBr(inst),
697 .constant => unreachable,
698 .dbg_stmt => return self.airDbgStmt(inst),
699 .loop => return self.airLoop(inst),
700 .ret => return self.airRet(inst),
701 .store => return self.airStore(inst),
702 .unreach => return self.airUnreach(),
703 // zig fmt: on
704
705 else => |tag| return self.fail("TODO: SPIR-V backend: implement AIR tag {s}", .{
706 @tagName(tag),
707 }),
687708 };
688709
689710 try self.inst_results.putNoClobber(inst, result_id);
690711 }
691712
692 fn genBinOp(self: *DeclGen, inst: *Inst.BinOp) !ResultId {
693 // TODO: Will lhs and rhs have the same type?
694 const lhs_id = try self.resolve(inst.lhs);
695 const rhs_id = try self.resolve(inst.rhs);
713 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, opcode: Opcode) !ResultId {
714 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
715 const lhs_id = try self.resolve(bin_op.lhs);
716 const rhs_id = try self.resolve(bin_op.rhs);
717 const result_id = self.spv.allocResultId();
718 const result_type_id = try self.genType(self.air.typeOfIndex(inst));
719 try writeInstruction(&self.code, opcode, &[_]Word{
720 result_type_id, result_id, lhs_id, rhs_id,
721 });
722 return result_id;
723 }
724
725 fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId {
726 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
727 // the result to be the same as the LHS and RHS, which matches SPIR-V.
728 const ty = self.air.typeOfIndex(inst);
729 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
730 const lhs_id = try self.resolve(bin_op.lhs);
731 const rhs_id = try self.resolve(bin_op.rhs);
696732
697733 const result_id = self.spv.allocResultId();
698 const result_type_id = try self.genType(inst.base.src, inst.base.ty);
699
700 // TODO: Is the result the same as the argument types?
701 // This is supposed to be the case for SPIR-V.
702 std.debug.assert(inst.rhs.ty.eql(inst.lhs.ty));
703 std.debug.assert(inst.base.ty.tag() == .bool or inst.base.ty.eql(inst.lhs.ty));
704
705 // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float
706 // versions of operations require different opcodes.
707 // For operations which produce bools, the information of inst.base.ty is not useful, so just pick either operand
708 // instead.
709 const info = try self.arithmeticTypeInfo(inst.lhs.ty);
710
711 if (info.class == .composite_integer) {
712 return self.fail(inst.base.src, "TODO: SPIR-V backend: binary operations for composite integers", .{});
713 } else if (info.class == .strange_integer) {
714 return self.fail(inst.base.src, "TODO: SPIR-V backend: binary operations for strange integers", .{});
715 }
734 const result_type_id = try self.genType(ty);
735
736 assert(self.air.typeOf(bin_op.lhs).eql(ty));
737 assert(self.air.typeOf(bin_op.rhs).eql(ty));
738
739 // Binary operations are generally applicable to both scalar and vector operations
740 // in SPIR-V, but int and float versions of operations require different opcodes.
741 const info = try self.arithmeticTypeInfo(ty);
716742
717 const is_float = info.class == .float;
718 const is_signed = info.signedness == .signed;
719 // **Note**: All these operations must be valid for vectors as well!
720 const opcode = switch (inst.base.tag) {
721 // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,
722 // we can just switch on both cases here.
723 .add, .addwrap => if (is_float) Opcode.OpFAdd else Opcode.OpIAdd,
724 .sub, .subwrap => if (is_float) Opcode.OpFSub else Opcode.OpISub,
725 .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul,
726 // TODO: Trap if divisor is 0?
727 // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful.
728 // => Those are probably for divTrunc and divFloor, though the compiler does not yet generate those.
729 // => TODO: Figure out how those work on the SPIR-V side.
730 // => TODO: Test these.
731 .div => if (is_float) Opcode.OpFDiv else if (is_signed) Opcode.OpSDiv else Opcode.OpUDiv,
732 // Only integer versions for these.
733 .bit_and => Opcode.OpBitwiseAnd,
734 .bit_or => Opcode.OpBitwiseOr,
735 .xor => Opcode.OpBitwiseXor,
736 // Bool -> bool operations.
737 .bool_and => Opcode.OpLogicalAnd,
738 .bool_or => Opcode.OpLogicalOr,
743 const opcode_index: usize = switch (info.class) {
744 .composite_integer => {
745 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});
746 },
747 .strange_integer => {
748 return self.fail("TODO: SPIR-V backend: binary operations for strange integers", .{});
749 },
750 .integer => switch (info.signedness) {
751 .signed => @as(usize, 1),
752 .unsigned => @as(usize, 2),
753 },
754 .float => 0,
739755 else => unreachable,
740756 };
741
757 const opcode = ops[opcode_index];
742758 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
743759
744760 // TODO: Trap on overflow? Probably going to be annoying.
745761 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
746762
747 if (info.class != .strange_integer)
748 return result_id;
749
750 return self.fail(inst.base.src, "TODO: SPIR-V backend: strange integer operation mask", .{});
763 return result_id;
751764 }
752765
753 fn genCmp(self: *DeclGen, inst: *Inst.BinOp) !ResultId {
754 const lhs_id = try self.resolve(inst.lhs);
755 const rhs_id = try self.resolve(inst.rhs);
756
766 fn airCmp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId {
767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
768 const lhs_id = try self.resolve(bin_op.lhs);
769 const rhs_id = try self.resolve(bin_op.rhs);
757770 const result_id = self.spv.allocResultId();
758 const result_type_id = try self.genType(inst.base.src, inst.base.ty);
759
760 // All of these operations should be 2 equal types -> bool
761 std.debug.assert(inst.rhs.ty.eql(inst.lhs.ty));
762 std.debug.assert(inst.base.ty.tag() == .bool);
763
764 // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, but int and float
765 // versions of operations require different opcodes.
766 // Since inst.base.ty is always bool and so not very useful, and because both arguments must be the same, just get the info
767 // from either of the operands.
768 const info = try self.arithmeticTypeInfo(inst.lhs.ty);
769
770 if (info.class == .composite_integer) {
771 return self.fail(inst.base.src, "TODO: SPIR-V backend: binary operations for composite integers", .{});
772 } else if (info.class == .strange_integer) {
773 return self.fail(inst.base.src, "TODO: SPIR-V backend: comparison for strange integers", .{});
774 }
771 const result_type_id = try self.genType(Type.initTag(.bool));
772 const op_ty = self.air.typeOf(bin_op.lhs);
773 assert(op_ty.eql(self.air.typeOf(bin_op.rhs)));
775774
776 const is_bool = info.class == .bool;
777 const is_float = info.class == .float;
778 const is_signed = info.signedness == .signed;
779
780 // **Note**: All these operations must be valid for vectors as well!
781 // For floating points, we generally want ordered operations (which return false if either operand is nan).
782 const opcode = switch (inst.base.tag) {
783 .cmp_eq => if (is_float) Opcode.OpFOrdEqual else if (is_bool) Opcode.OpLogicalEqual else Opcode.OpIEqual,
784 .cmp_neq => if (is_float) Opcode.OpFOrdNotEqual else if (is_bool) Opcode.OpLogicalNotEqual else Opcode.OpINotEqual,
785 // TODO: Verify that these OpFOrd type operations produce the right value.
786 // TODO: Is there a more fundamental difference between OpU and OpS operations here than just the type?
787 .cmp_gt => if (is_float) Opcode.OpFOrdGreaterThan else if (is_signed) Opcode.OpSGreaterThan else Opcode.OpUGreaterThan,
788 .cmp_gte => if (is_float) Opcode.OpFOrdGreaterThanEqual else if (is_signed) Opcode.OpSGreaterThanEqual else Opcode.OpUGreaterThanEqual,
789 .cmp_lt => if (is_float) Opcode.OpFOrdLessThan else if (is_signed) Opcode.OpSLessThan else Opcode.OpULessThan,
790 .cmp_lte => if (is_float) Opcode.OpFOrdLessThanEqual else if (is_signed) Opcode.OpSLessThanEqual else Opcode.OpULessThanEqual,
791 else => unreachable,
775 // Comparisons are generally applicable to both scalar and vector operations in SPIR-V,
776 // but int and float versions of operations require different opcodes.
777 const info = try self.arithmeticTypeInfo(op_ty);
778
779 const opcode_index: usize = switch (info.class) {
780 .composite_integer => {
781 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});
782 },
783 .strange_integer => {
784 return self.fail("TODO: SPIR-V backend: comparison for strange integers", .{});
785 },
786 .float => 0,
787 .bool => 1,
788 .integer => switch (info.signedness) {
789 .signed => @as(usize, 1),
790 .unsigned => @as(usize, 2),
791 },
792792 };
793 const opcode = ops[opcode_index];
793794
794795 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
795796 return result_id;
796797 }
797798
798 fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !ResultId {
799 const operand_id = try self.resolve(inst.operand);
800
799 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
800 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
801 const operand_id = try self.resolve(ty_op.operand);
801802 const result_id = self.spv.allocResultId();
802 const result_type_id = try self.genType(inst.base.src, inst.base.ty);
803
804 const opcode = switch (inst.base.tag) {
805 // Bool -> bool
806 .not => Opcode.OpLogicalNot,
807 else => unreachable,
808 };
809
803 const result_type_id = try self.genType(Type.initTag(.bool));
804 const opcode: Opcode = .OpLogicalNot;
810805 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, operand_id });
811
812806 return result_id;
813807 }
814808
815 fn genAlloc(self: *DeclGen, inst: *Inst.NoOp) !ResultId {
809 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
810 const ty = self.air.typeOfIndex(inst);
816811 const storage_class = spec.StorageClass.Function;
817 const result_type_id = try self.genPointerType(inst.base.src, inst.base.ty, storage_class);
812 const result_type_id = try self.genPointerType(ty, storage_class);
818813 const result_id = self.spv.allocResultId();
819814
820815 // Rather than generating into code here, we're just going to generate directly into the fn_decls section so that
......@@ -824,12 +819,12 @@ pub const DeclGen = struct {
824819 return result_id;
825820 }
826821
827 fn genArg(self: *DeclGen) ResultId {
822 fn airArg(self: *DeclGen) ResultId {
828823 defer self.next_arg_index += 1;
829824 return self.args.items[self.next_arg_index];
830825 }
831826
832 fn genBlock(self: *DeclGen, inst: *Inst.Block) !?ResultId {
827 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?ResultId {
833828 // In IR, a block doesn't really define an entry point like a block, but more like a scope that breaks can jump out of and
834829 // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up
835830 // the current block by first generating the code of the block, then a label, and then generate the rest of the current
......@@ -849,11 +844,16 @@ pub const DeclGen = struct {
849844 incoming_blocks.deinit(self.spv.gpa);
850845 }
851846
852 try self.genBody(inst.body);
847 const ty = self.air.typeOfIndex(inst);
848 const inst_datas = self.air.instructions.items(.data);
849 const extra = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
850 const body = self.air.extra[extra.end..][0..extra.data.body_len];
851
852 try self.genBody(body);
853853 try self.beginSPIRVBlock(label_id);
854854
855855 // If this block didn't produce a value, simply return here.
856 if (!inst.base.ty.hasCodeGenBits())
856 if (!ty.hasCodeGenBits())
857857 return null;
858858
859859 // Combine the result from the blocks using the Phi instruction.
......@@ -863,7 +863,7 @@ pub const DeclGen = struct {
863863 // TODO: OpPhi is limited in the types that it may produce, such as pointers. Figure out which other types
864864 // are not allowed to be created from a phi node, and throw an error for those. For now, genType already throws
865865 // an error for pointers.
866 const result_type_id = try self.genType(inst.base.src, inst.base.ty);
866 const result_type_id = try self.genType(ty);
867867 _ = result_type_id;
868868
869869 try writeOpcode(&self.code, .OpPhi, 2 + @intCast(u16, incoming_blocks.items.len * 2)); // result type + result + variable/parent...
......@@ -875,30 +875,26 @@ pub const DeclGen = struct {
875875 return result_id;
876876 }
877877
878 fn genBr(self: *DeclGen, inst: *Inst.Br) !void {
879 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
880 const target = self.blocks.get(inst.block).?;
878 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
879 const br = self.air.instructions.items(.data)[inst].br;
880 const block = self.blocks.get(br.block_inst).?;
881 const operand_ty = self.air.typeOf(br.operand);
881882
882 // TODO: For some reason, br is emitted with void parameters.
883 if (inst.operand.ty.hasCodeGenBits()) {
884 const operand_id = try self.resolve(inst.operand);
883 if (operand_ty.hasCodeGenBits()) {
884 const operand_id = try self.resolve(br.operand);
885885 // current_block_label_id should not be undefined here, lest there is a br or br_void in the function's body.
886 try target.incoming_blocks.append(self.spv.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id });
886 try block.incoming_blocks.append(self.spv.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id });
887887 }
888888
889 try writeInstruction(&self.code, .OpBranch, &[_]Word{target.label_id});
890 }
891
892 fn genBrVoid(self: *DeclGen, inst: *Inst.BrVoid) !void {
893 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
894 const target = self.blocks.get(inst.block).?;
895 // Don't need to add this to the incoming block list, as there is no value to insert in the phi node anyway.
896 try writeInstruction(&self.code, .OpBranch, &[_]Word{target.label_id});
889 try writeInstruction(&self.code, .OpBranch, &[_]Word{block.label_id});
897890 }
898891
899 fn genCondBr(self: *DeclGen, inst: *Inst.CondBr) !void {
900 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
901 const condition_id = try self.resolve(inst.condition);
892 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {
893 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
894 const cond_br = self.air.extraData(Air.CondBr, pl_op.payload);
895 const then_body = self.air.extra[cond_br.end..][0..cond_br.data.then_body_len];
896 const else_body = self.air.extra[cond_br.end + then_body.len ..][0..cond_br.data.else_body_len];
897 const condition_id = try self.resolve(pl_op.operand);
902898
903899 // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block.
904900 const then_label_id = self.spv.allocResultId();
......@@ -914,23 +910,26 @@ pub const DeclGen = struct {
914910 });
915911
916912 try self.beginSPIRVBlock(then_label_id);
917 try self.genBody(inst.then_body);
913 try self.genBody(then_body);
918914 try self.beginSPIRVBlock(else_label_id);
919 try self.genBody(inst.else_body);
915 try self.genBody(else_body);
920916 }
921917
922 fn genDbgStmt(self: *DeclGen, inst: *Inst.DbgStmt) !void {
918 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
919 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
923920 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);
924 try writeInstruction(&self.code, .OpLine, &[_]Word{ src_fname_id, inst.line, inst.column });
921 try writeInstruction(&self.code, .OpLine, &[_]Word{ src_fname_id, dbg_stmt.line, dbg_stmt.column });
925922 }
926923
927 fn genLoad(self: *DeclGen, inst: *Inst.UnOp) !ResultId {
928 const operand_id = try self.resolve(inst.operand);
924 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
925 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
926 const operand_id = try self.resolve(ty_op.operand);
927 const ty = self.air.typeOfIndex(inst);
929928
930 const result_type_id = try self.genType(inst.base.src, inst.base.ty);
929 const result_type_id = try self.genType(ty);
931930 const result_id = self.spv.allocResultId();
932931
933 const operands = if (inst.base.ty.isVolatilePtr())
932 const operands = if (ty.isVolatilePtr())
934933 &[_]Word{ result_type_id, result_id, operand_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }
935934 else
936935 &[_]Word{ result_type_id, result_id, operand_id };
......@@ -940,8 +939,10 @@ pub const DeclGen = struct {
940939 return result_id;
941940 }
942941
943 fn genLoop(self: *DeclGen, inst: *Inst.Loop) !void {
944 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
942 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {
943 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
944 const loop = self.air.extraData(Air.Block, ty_pl.payload);
945 const body = self.air.extra[loop.end..][0..loop.data.body_len];
945946 const loop_label_id = self.spv.allocResultId();
946947
947948 // Jump to the loop entry point
......@@ -950,27 +951,29 @@ pub const DeclGen = struct {
950951 // TODO: Look into OpLoopMerge.
951952
952953 try self.beginSPIRVBlock(loop_label_id);
953 try self.genBody(inst.body);
954 try self.genBody(body);
954955
955956 try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id});
956957 }
957958
958 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !void {
959 const operand_id = try self.resolve(inst.operand);
960 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
961 try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id});
962 }
963
964 fn genRetVoid(self: *DeclGen) !void {
965 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
966 try writeInstruction(&self.code, .OpReturn, &[_]Word{});
959 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
960 const operand = self.air.instructions.items(.data)[inst].un_op;
961 const operand_ty = self.air.typeOf(operand);
962 if (operand_ty.hasCodeGenBits()) {
963 const operand_id = try self.resolve(operand);
964 try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id});
965 } else {
966 try writeInstruction(&self.code, .OpReturn, &[_]Word{});
967 }
967968 }
968969
969 fn genStore(self: *DeclGen, inst: *Inst.BinOp) !void {
970 const dst_ptr_id = try self.resolve(inst.lhs);
971 const src_val_id = try self.resolve(inst.rhs);
970 fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void {
971 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
972 const dst_ptr_id = try self.resolve(bin_op.lhs);
973 const src_val_id = try self.resolve(bin_op.rhs);
974 const lhs_ty = self.air.typeOf(bin_op.lhs);
972975
973 const operands = if (inst.lhs.ty.isVolatilePtr())
976 const operands = if (lhs_ty.isVolatilePtr())
974977 &[_]Word{ dst_ptr_id, src_val_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }
975978 else
976979 &[_]Word{ dst_ptr_id, src_val_id };
......@@ -978,8 +981,7 @@ pub const DeclGen = struct {
978981 try writeInstruction(&self.code, .OpStore, operands);
979982 }
980983
981 fn genUnreach(self: *DeclGen) !void {
982 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
984 fn airUnreach(self: *DeclGen) !void {
983985 try writeInstruction(&self.code, .OpUnreachable, &[_]Word{});
984986 }
985987};
src/codegen/wasm.zig+293-242
......@@ -9,14 +9,14 @@ const wasm = std.wasm;
99
1010const Module = @import("../Module.zig");
1111const Decl = Module.Decl;
12const ir = @import("../air.zig");
13const Inst = ir.Inst;
1412const Type = @import("../type.zig").Type;
1513const Value = @import("../value.zig").Value;
1614const Compilation = @import("../Compilation.zig");
1715const LazySrcLoc = Module.LazySrcLoc;
1816const link = @import("../link.zig");
1917const TypedValue = @import("../TypedValue.zig");
18const Air = @import("../Air.zig");
19const Liveness = @import("../Liveness.zig");
2020
2121/// Wasm Value, created when generating an instruction
2222const WValue = union(enum) {
......@@ -24,8 +24,8 @@ const WValue = union(enum) {
2424 none: void,
2525 /// Index of the local variable
2626 local: u32,
27 /// Instruction holding a constant `Value`
28 constant: *Inst,
27 /// Holds a memoized typed value
28 constant: TypedValue,
2929 /// Offset position in the list of bytecode instructions
3030 code_offset: usize,
3131 /// Used for variables that create multiple locals on the stack when allocated
......@@ -483,8 +483,8 @@ pub const Result = union(enum) {
483483 externally_managed: []const u8,
484484};
485485
486/// Hashmap to store generated `WValue` for each `Inst`
487pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue);
486/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
487pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
488488
489489/// Code represents the `Code` section of wasm that
490490/// belongs to a function
......@@ -492,11 +492,13 @@ pub const Context = struct {
492492 /// Reference to the function declaration the code
493493 /// section belongs to
494494 decl: *Decl,
495 air: Air,
496 liveness: Liveness,
495497 gpa: *mem.Allocator,
496 /// Table to save `WValue`'s generated by an `Inst`
498 /// Table to save `WValue`'s generated by an `Air.Inst`
497499 values: ValueTable,
498 /// Mapping from *Inst.Block to block ids
499 blocks: std.AutoArrayHashMapUnmanaged(*Inst.Block, u32) = .{},
500 /// Mapping from Air.Inst.Index to block ids
501 blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
500502 /// `bytes` contains the wasm bytecode belonging to the 'code' section.
501503 code: ArrayList(u8),
502504 /// Contains the generated function type bytecode for the current function
......@@ -536,7 +538,8 @@ pub const Context = struct {
536538 }
537539
538540 /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
539 fn fail(self: *Context, src: LazySrcLoc, comptime fmt: []const u8, args: anytype) InnerError {
541 fn fail(self: *Context, comptime fmt: []const u8, args: anytype) InnerError {
542 const src: LazySrcLoc = .{ .node_offset = 0 };
540543 const src_loc = src.toSrcLocWithDecl(self.decl);
541544 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
542545 return error.CodegenFail;
......@@ -544,59 +547,66 @@ pub const Context = struct {
544547
545548 /// Resolves the `WValue` for the given instruction `inst`
546549 /// When the given instruction has a `Value`, it returns a constant instead
547 fn resolveInst(self: Context, inst: *Inst) WValue {
548 if (!inst.ty.hasCodeGenBits()) return .none;
550 fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
551 const inst_index = Air.refToIndex(ref) orelse {
552 const tv = Air.Inst.Ref.typed_value_map[@enumToInt(ref)];
553 if (!tv.ty.hasCodeGenBits()) {
554 return WValue.none;
555 }
556 return WValue{ .constant = tv };
557 };
558
559 const inst_type = self.air.typeOfIndex(inst_index);
560 if (!inst_type.hasCodeGenBits()) return .none;
549561
550 if (inst.value()) |_| {
551 return WValue{ .constant = inst };
562 if (self.air.instructions.items(.tag)[inst_index] == .constant) {
563 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
564 return WValue{ .constant = .{ .ty = inst_type, .val = self.air.values[ty_pl.payload] } };
552565 }
553566
554 return self.values.get(inst).?; // Instruction does not dominate all uses!
567 return self.values.get(inst_index).?; // Instruction does not dominate all uses!
555568 }
556569
557570 /// Using a given `Type`, returns the corresponding wasm Valtype
558 fn typeToValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!wasm.Valtype {
571 fn typeToValtype(self: *Context, ty: Type) InnerError!wasm.Valtype {
559572 return switch (ty.zigTypeTag()) {
560573 .Float => blk: {
561574 const bits = ty.floatBits(self.target);
562575 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
563576 if (bits == 64) break :blk wasm.Valtype.f64;
564 return self.fail(src, "Float bit size not supported by wasm: '{d}'", .{bits});
577 return self.fail("Float bit size not supported by wasm: '{d}'", .{bits});
565578 },
566579 .Int => blk: {
567580 const info = ty.intInfo(self.target);
568581 if (info.bits <= 32) break :blk wasm.Valtype.i32;
569582 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;
570 return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits});
583 return self.fail("Integer bit size not supported by wasm: '{d}'", .{info.bits});
571584 },
572585 .Enum => switch (ty.tag()) {
573586 .enum_simple => wasm.Valtype.i32,
574 else => self.typeToValtype(
575 src,
576 ty.cast(Type.Payload.EnumFull).?.data.tag_ty,
577 ),
587 else => self.typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty),
578588 },
579589 .Bool,
580590 .Pointer,
581591 .ErrorSet,
582592 => wasm.Valtype.i32,
583593 .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually.
584 else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
594 else => self.fail("TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
585595 };
586596 }
587597
588598 /// Using a given `Type`, returns the byte representation of its wasm value type
589 fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
590 return wasm.valtype(try self.typeToValtype(src, ty));
599 fn genValtype(self: *Context, ty: Type) InnerError!u8 {
600 return wasm.valtype(try self.typeToValtype(ty));
591601 }
592602
593603 /// Using a given `Type`, returns the corresponding wasm value type
594604 /// Differently from `genValtype` this also allows `void` to create a block
595605 /// with no return type
596 fn genBlockType(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
606 fn genBlockType(self: *Context, ty: Type) InnerError!u8 {
597607 return switch (ty.tag()) {
598608 .void, .noreturn => wasm.block_empty,
599 else => self.genValtype(src, ty),
609 else => self.genValtype(ty),
600610 };
601611 }
602612
......@@ -610,7 +620,7 @@ pub const Context = struct {
610620 try writer.writeByte(wasm.opcode(.local_get));
611621 try leb.writeULEB128(writer, idx);
612622 },
613 .constant => |inst| try self.emitConstant(inst.src, inst.value().?, inst.ty), // creates a new constant onto the stack
623 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
614624 }
615625 }
616626
......@@ -626,10 +636,7 @@ pub const Context = struct {
626636 const fields_len = @intCast(u32, struct_data.fields.count());
627637 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
628638 for (struct_data.fields.values()) |*value| {
629 const val_type = try self.genValtype(
630 .{ .node_offset = struct_data.node_offset },
631 value.ty,
632 );
639 const val_type = try self.genValtype(value.ty);
633640 self.locals.appendAssumeCapacity(val_type);
634641 self.local_index += 1;
635642 }
......@@ -640,7 +647,7 @@ pub const Context = struct {
640647 },
641648 .ErrorUnion => {
642649 const payload_type = ty.errorUnionChild();
643 const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type);
650 const val_type = try self.genValtype(payload_type);
644651
645652 // we emit the error value as the first local, and the payload as the following.
646653 // The first local is also used to find the index of the error and payload.
......@@ -657,7 +664,7 @@ pub const Context = struct {
657664 } };
658665 },
659666 else => {
660 const valtype = try self.genValtype(.{ .node_offset = 0 }, ty);
667 const valtype = try self.genValtype(ty);
661668 try self.locals.append(self.gpa, valtype);
662669 self.local_index += 1;
663670 return WValue{ .local = initial_index };
......@@ -680,7 +687,7 @@ pub const Context = struct {
680687 ty.fnParamTypes(params);
681688 for (params) |param_type| {
682689 // Can we maybe get the source index of each param?
683 const val_type = try self.genValtype(.{ .node_offset = 0 }, param_type);
690 const val_type = try self.genValtype(param_type);
684691 try writer.writeByte(val_type);
685692 }
686693 }
......@@ -689,13 +696,10 @@ pub const Context = struct {
689696 const return_type = ty.fnReturnType();
690697 switch (return_type.zigTypeTag()) {
691698 .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)),
692 .Struct => return self.fail(.{ .node_offset = 0 }, "TODO: Implement struct as return type for wasm", .{}),
693 .Optional => return self.fail(.{ .node_offset = 0 }, "TODO: Implement optionals as return type for wasm", .{}),
699 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
700 .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
694701 .ErrorUnion => {
695 const val_type = try self.genValtype(
696 .{ .node_offset = 0 },
697 return_type.errorUnionChild(),
698 );
702 const val_type = try self.genValtype(return_type.errorUnionChild());
699703
700704 // write down the amount of return values
701705 try leb.writeULEB128(writer, @as(u32, 2));
......@@ -705,58 +709,57 @@ pub const Context = struct {
705709 else => {
706710 try leb.writeULEB128(writer, @as(u32, 1));
707711 // Can we maybe get the source index of the return type?
708 const val_type = try self.genValtype(.{ .node_offset = 0 }, return_type);
712 const val_type = try self.genValtype(return_type);
709713 try writer.writeByte(val_type);
710714 },
711715 }
712716 }
713717
714 /// Generates the wasm bytecode for the function declaration belonging to `Context`
715 pub fn gen(self: *Context, typed_value: TypedValue) InnerError!Result {
716 switch (typed_value.ty.zigTypeTag()) {
717 .Fn => {
718 try self.genFunctype();
718 pub fn genFunc(self: *Context) InnerError!Result {
719 try self.genFunctype();
720 // TODO: check for and handle death of instructions
719721
720 // Write instructions
721 // TODO: check for and handle death of instructions
722 const mod_fn = blk: {
723 if (typed_value.val.castTag(.function)) |func| break :blk func.data;
724 if (typed_value.val.castTag(.extern_fn)) |_| return Result.appended; // don't need code body for extern functions
725 unreachable;
726 };
727
728 // Reserve space to write the size after generating the code as well as space for locals count
729 try self.code.resize(10);
730
731 try self.genBody(mod_fn.body);
732
733 // finally, write our local types at the 'offset' position
734 {
735 leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
736
737 // offset into 'code' section where we will put our locals types
738 var local_offset: usize = 10;
739
740 // emit the actual locals amount
741 for (self.locals.items) |local| {
742 var buf: [6]u8 = undefined;
743 leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
744 buf[5] = local;
745 try self.code.insertSlice(local_offset, &buf);
746 local_offset += 6;
747 }
748 }
722 // Reserve space to write the size after generating the code as well as space for locals count
723 try self.code.resize(10);
724
725 try self.genBody(self.air.getMainBody());
749726
750 const writer = self.code.writer();
751 try writer.writeByte(wasm.opcode(.end));
727 // finally, write our local types at the 'offset' position
728 {
729 leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
752730
753 // Fill in the size of the generated code to the reserved space at the
754 // beginning of the buffer.
755 const size = self.code.items.len - 5 + self.decl.fn_link.wasm.idx_refs.items.len * 5;
756 leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size));
731 // offset into 'code' section where we will put our locals types
732 var local_offset: usize = 10;
757733
758 // codegen data has been appended to `code`
759 return Result.appended;
734 // emit the actual locals amount
735 for (self.locals.items) |local| {
736 var buf: [6]u8 = undefined;
737 leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
738 buf[5] = local;
739 try self.code.insertSlice(local_offset, &buf);
740 local_offset += 6;
741 }
742 }
743
744 const writer = self.code.writer();
745 try writer.writeByte(wasm.opcode(.end));
746
747 // Fill in the size of the generated code to the reserved space at the
748 // beginning of the buffer.
749 const size = self.code.items.len - 5 + self.decl.fn_link.wasm.idx_refs.items.len * 5;
750 leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size));
751
752 // codegen data has been appended to `code`
753 return Result.appended;
754 }
755
756 /// Generates the wasm bytecode for the declaration belonging to `Context`
757 pub fn gen(self: *Context, typed_value: TypedValue) InnerError!Result {
758 switch (typed_value.ty.zigTypeTag()) {
759 .Fn => {
760 try self.genFunctype();
761 if (typed_value.val.castTag(.extern_fn)) |_| return Result.appended; // don't need code body for extern functions
762 return self.fail("TODO implement wasm codegen for function pointers", .{});
760763 },
761764 .Array => {
762765 if (typed_value.val.castTag(.bytes)) |payload| {
......@@ -775,7 +778,7 @@ pub const Context = struct {
775778 }
776779 }
777780 return Result{ .externally_managed = payload.data };
778 } else return self.fail(.{ .node_offset = 0 }, "TODO implement gen for more kinds of arrays", .{});
781 } else return self.fail("TODO implement gen for more kinds of arrays", .{});
779782 },
780783 .Int => {
781784 const info = typed_value.ty.intInfo(self.target);
......@@ -784,85 +787,91 @@ pub const Context = struct {
784787 try self.code.append(@intCast(u8, int_byte));
785788 return Result.appended;
786789 }
787 return self.fail(.{ .node_offset = 0 }, "TODO: Implement codegen for int type: '{}'", .{typed_value.ty});
790 return self.fail("TODO: Implement codegen for int type: '{}'", .{typed_value.ty});
788791 },
789 else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: Implement zig type codegen for type: '{s}'", .{tag}),
792 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
790793 }
791794 }
792795
793 fn genInst(self: *Context, inst: *Inst) InnerError!WValue {
794 return switch (inst.tag) {
795 .add => self.genBinOp(inst.castTag(.add).?, .add),
796 .alloc => self.genAlloc(inst.castTag(.alloc).?),
797 .arg => self.genArg(inst.castTag(.arg).?),
798 .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
799 .bitcast => self.genBitcast(inst.castTag(.bitcast).?),
800 .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
801 .block => self.genBlock(inst.castTag(.block).?),
802 .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
803 .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
804 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
805 .br => self.genBr(inst.castTag(.br).?),
806 .call => self.genCall(inst.castTag(.call).?),
807 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
808 .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
809 .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
810 .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),
811 .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),
812 .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),
813 .condbr => self.genCondBr(inst.castTag(.condbr).?),
796 fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
797 const air_tags = self.air.instructions.items(.tag);
798 return switch (air_tags[inst]) {
799 .add => self.airBinOp(inst, .add),
800 .sub => self.airBinOp(inst, .sub),
801 .mul => self.airBinOp(inst, .mul),
802 .div => self.airBinOp(inst, .div),
803 .bit_and => self.airBinOp(inst, .@"and"),
804 .bit_or => self.airBinOp(inst, .@"or"),
805 .bool_and => self.airBinOp(inst, .@"and"),
806 .bool_or => self.airBinOp(inst, .@"or"),
807 .xor => self.airBinOp(inst, .xor),
808
809 .cmp_eq => self.airCmp(inst, .eq),
810 .cmp_gte => self.airCmp(inst, .gte),
811 .cmp_gt => self.airCmp(inst, .gt),
812 .cmp_lte => self.airCmp(inst, .lte),
813 .cmp_lt => self.airCmp(inst, .lt),
814 .cmp_neq => self.airCmp(inst, .neq),
815
816 .alloc => self.airAlloc(inst),
817 .arg => self.airArg(inst),
818 .bitcast => self.airBitcast(inst),
819 .block => self.airBlock(inst),
820 .breakpoint => self.airBreakpoint(inst),
821 .br => self.airBr(inst),
822 .call => self.airCall(inst),
823 .cond_br => self.airCondBr(inst),
814824 .constant => unreachable,
815825 .dbg_stmt => WValue.none,
816 .div => self.genBinOp(inst.castTag(.div).?, .div),
817 .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
818 .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
819 .load => self.genLoad(inst.castTag(.load).?),
820 .loop => self.genLoop(inst.castTag(.loop).?),
821 .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
822 .not => self.genNot(inst.castTag(.not).?),
823 .ret => self.genRet(inst.castTag(.ret).?),
824 .retvoid => WValue.none,
825 .store => self.genStore(inst.castTag(.store).?),
826 .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
827 .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
828 .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?),
829 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
830 .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?),
831 .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
832 .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
833 else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}),
826 .is_err => self.airIsErr(inst, .i32_ne),
827 .is_non_err => self.airIsErr(inst, .i32_eq),
828 .load => self.airLoad(inst),
829 .loop => self.airLoop(inst),
830 .not => self.airNot(inst),
831 .ret => self.airRet(inst),
832 .store => self.airStore(inst),
833 .struct_field_ptr => self.airStructFieldPtr(inst),
834 .switch_br => self.airSwitchBr(inst),
835 .unreach => self.airUnreachable(inst),
836 .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst),
837 .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
838 else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
834839 };
835840 }
836841
837 fn genBody(self: *Context, body: ir.Body) InnerError!void {
838 for (body.instructions) |inst| {
842 fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
843 for (body) |inst| {
839844 const result = try self.genInst(inst);
840845 try self.values.putNoClobber(self.gpa, inst, result);
841846 }
842847 }
843848
844 fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
845 // TODO: Implement tail calls
846 const operand = self.resolveInst(inst.operand);
849 fn airRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
850 const un_op = self.air.instructions.items(.data)[inst].un_op;
851 const operand = self.resolveInst(un_op);
847852 try self.emitWValue(operand);
848853 try self.code.append(wasm.opcode(.@"return"));
849854 return .none;
850855 }
851856
852 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
853 const func_val = inst.func.value().?;
857 fn airCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
858 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
859 const extra = self.air.extraData(Air.Call, pl_op.payload);
860 const args = self.air.extra[extra.end..][0..extra.data.args_len];
854861
855862 const target: *Decl = blk: {
863 const func_val = self.air.value(pl_op.operand).?;
864
856865 if (func_val.castTag(.function)) |func| {
857866 break :blk func.data.owner_decl;
858867 } else if (func_val.castTag(.extern_fn)) |ext_fn| {
859868 break :blk ext_fn.data;
860869 }
861 return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()});
870 return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
862871 };
863872
864 for (inst.args) |arg| {
865 const arg_val = self.resolveInst(arg);
873 for (args) |arg| {
874 const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
866875 try self.emitWValue(arg_val);
867876 }
868877
......@@ -878,16 +887,17 @@ pub const Context = struct {
878887 return .none;
879888 }
880889
881 fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {
882 const elem_type = inst.base.ty.elemType();
890 fn airAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
891 const elem_type = self.air.typeOfIndex(inst).elemType();
883892 return self.allocLocal(elem_type);
884893 }
885894
886 fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
895 fn airStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
896 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
887897 const writer = self.code.writer();
888898
889 const lhs = self.resolveInst(inst.lhs);
890 const rhs = self.resolveInst(inst.rhs);
899 const lhs = self.resolveInst(bin_op.lhs);
900 const rhs = self.resolveInst(bin_op.rhs);
891901
892902 switch (lhs) {
893903 .multi_value => |multi_value| switch (rhs) {
......@@ -895,7 +905,7 @@ pub const Context = struct {
895905 // we simply assign the local_index to the rhs one.
896906 // This allows us to update struct fields without having to individually
897907 // set each local as each field's index will be calculated off the struct's base index
898 .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
908 .multi_value => self.values.put(self.gpa, Air.refToIndex(bin_op.lhs).?, rhs) catch unreachable, // Instruction does not dominate all uses!
899909 .constant, .none => {
900910 // emit all values onto the stack if constant
901911 try self.emitWValue(rhs);
......@@ -921,20 +931,22 @@ pub const Context = struct {
921931 return .none;
922932 }
923933
924 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
925 return self.resolveInst(inst.operand);
934 fn airLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
935 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
936 return self.resolveInst(ty_op.operand);
926937 }
927938
928 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
939 fn airArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
929940 _ = inst;
930941 // arguments share the index with locals
931942 defer self.local_index += 1;
932943 return WValue{ .local = self.local_index };
933944 }
934945
935 fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue {
936 const lhs = self.resolveInst(inst.lhs);
937 const rhs = self.resolveInst(inst.rhs);
946 fn airBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
947 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
948 const lhs = self.resolveInst(bin_op.lhs);
949 const rhs = self.resolveInst(bin_op.rhs);
938950
939951 // it's possible for both lhs and/or rhs to return an offset as well,
940952 // in which case we return the first offset occurance we find.
......@@ -947,23 +959,24 @@ pub const Context = struct {
947959 try self.emitWValue(lhs);
948960 try self.emitWValue(rhs);
949961
962 const bin_ty = self.air.typeOf(bin_op.lhs);
950963 const opcode: wasm.Opcode = buildOpcode(.{
951964 .op = op,
952 .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
953 .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,
965 .valtype1 = try self.typeToValtype(bin_ty),
966 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
954967 });
955968 try self.code.append(wasm.opcode(opcode));
956969 return WValue{ .code_offset = offset };
957970 }
958971
959 fn emitConstant(self: *Context, src: LazySrcLoc, value: Value, ty: Type) InnerError!void {
972 fn emitConstant(self: *Context, value: Value, ty: Type) InnerError!void {
960973 const writer = self.code.writer();
961974 switch (ty.zigTypeTag()) {
962975 .Int => {
963976 // write opcode
964977 const opcode: wasm.Opcode = buildOpcode(.{
965978 .op = .@"const",
966 .valtype1 = try self.typeToValtype(src, ty),
979 .valtype1 = try self.typeToValtype(ty),
967980 });
968981 try writer.writeByte(wasm.opcode(opcode));
969982 // write constant
......@@ -982,14 +995,14 @@ pub const Context = struct {
982995 // write opcode
983996 const opcode: wasm.Opcode = buildOpcode(.{
984997 .op = .@"const",
985 .valtype1 = try self.typeToValtype(src, ty),
998 .valtype1 = try self.typeToValtype(ty),
986999 });
9871000 try writer.writeByte(wasm.opcode(opcode));
9881001 // write constant
9891002 switch (ty.floatBits(self.target)) {
9901003 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),
9911004 64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))),
992 else => |bits| return self.fail(src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),
1005 else => |bits| return self.fail("Wasm TODO: emitConstant for float with {d} bits", .{bits}),
9931006 }
9941007 },
9951008 .Pointer => {
......@@ -1006,7 +1019,7 @@ pub const Context = struct {
10061019 try writer.writeByte(wasm.opcode(.i32_load));
10071020 try leb.writeULEB128(writer, @as(u32, 0));
10081021 try leb.writeULEB128(writer, @as(u32, 0));
1009 } else return self.fail(src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
1022 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
10101023 },
10111024 .Void => {},
10121025 .Enum => {
......@@ -1020,7 +1033,7 @@ pub const Context = struct {
10201033 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
10211034 if (enum_full.values.count() != 0) {
10221035 const tag_val = enum_full.values.keys()[field_index.data];
1023 try self.emitConstant(src, tag_val, enum_full.tag_ty);
1036 try self.emitConstant(tag_val, enum_full.tag_ty);
10241037 } else {
10251038 try writer.writeByte(wasm.opcode(.i32_const));
10261039 try leb.writeULEB128(writer, field_index.data);
......@@ -1031,7 +1044,7 @@ pub const Context = struct {
10311044 } else {
10321045 var int_tag_buffer: Type.Payload.Bits = undefined;
10331046 const int_tag_ty = ty.intTagType(&int_tag_buffer);
1034 try self.emitConstant(src, value, int_tag_ty);
1047 try self.emitConstant(value, int_tag_ty);
10351048 }
10361049 },
10371050 .ErrorSet => {
......@@ -1045,12 +1058,12 @@ pub const Context = struct {
10451058 const payload_type = ty.errorUnionChild();
10461059 if (value.getError()) |_| {
10471060 // write the error value
1048 try self.emitConstant(src, data, error_type);
1061 try self.emitConstant(data, error_type);
10491062
10501063 // no payload, so write a '0' const
10511064 const opcode: wasm.Opcode = buildOpcode(.{
10521065 .op = .@"const",
1053 .valtype1 = try self.typeToValtype(src, payload_type),
1066 .valtype1 = try self.typeToValtype(payload_type),
10541067 });
10551068 try writer.writeByte(wasm.opcode(opcode));
10561069 try leb.writeULEB128(writer, @as(u32, 0));
......@@ -1059,21 +1072,24 @@ pub const Context = struct {
10591072 try writer.writeByte(wasm.opcode(.i32_const));
10601073 try leb.writeULEB128(writer, @as(u32, 0));
10611074 // after the error code, we emit the payload
1062 try self.emitConstant(src, data, payload_type);
1075 try self.emitConstant(data, payload_type);
10631076 }
10641077 },
1065 else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
1078 else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
10661079 }
10671080 }
10681081
1069 fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {
1070 const block_ty = try self.genBlockType(block.base.src, block.base.ty);
1082 fn airBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1083 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1084 const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
1085 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1086 const body = self.air.extra[extra.end..][0..extra.data.body_len];
10711087
10721088 try self.startBlock(.block, block_ty, null);
10731089 // Here we set the current block idx, so breaks know the depth to jump
10741090 // to when breaking out.
1075 try self.blocks.putNoClobber(self.gpa, block, self.block_depth);
1076 try self.genBody(block.body);
1091 try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
1092 try self.genBody(body);
10771093 try self.endBlock();
10781094
10791095 return .none;
......@@ -1097,11 +1113,15 @@ pub const Context = struct {
10971113 self.block_depth -= 1;
10981114 }
10991115
1100 fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {
1101 const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);
1116 fn airLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1117 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1118 const loop = self.air.extraData(Air.Block, ty_pl.payload);
1119 const body = self.air.extra[loop.end..][0..loop.data.body_len];
11021120
1103 try self.startBlock(.loop, loop_ty, null);
1104 try self.genBody(loop.body);
1121 // result type of loop is always 'noreturn', meaning we can always
1122 // emit the wasm type 'block_empty'.
1123 try self.startBlock(.loop, wasm.block_empty, null);
1124 try self.genBody(body);
11051125
11061126 // breaking to the index of a loop block will continue the loop instead
11071127 try self.code.append(wasm.opcode(.br));
......@@ -1112,8 +1132,12 @@ pub const Context = struct {
11121132 return .none;
11131133 }
11141134
1115 fn genCondBr(self: *Context, condbr: *Inst.CondBr) InnerError!WValue {
1116 const condition = self.resolveInst(condbr.condition);
1135 fn airCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1136 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1137 const condition = self.resolveInst(pl_op.operand);
1138 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
1139 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
1140 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
11171141 const writer = self.code.writer();
11181142
11191143 // TODO: Handle death instructions for then and else body
......@@ -1128,8 +1152,9 @@ pub const Context = struct {
11281152 break :blk offset;
11291153 },
11301154 };
1131 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
1132 try self.startBlock(.block, block_ty, offset);
1155
1156 // result type is always noreturn, so use `block_empty` as type.
1157 try self.startBlock(.block, wasm.block_empty, offset);
11331158
11341159 // we inserted the block in front of the condition
11351160 // so now check if condition matches. If not, break outside this block
......@@ -1137,35 +1162,37 @@ pub const Context = struct {
11371162 try writer.writeByte(wasm.opcode(.br_if));
11381163 try leb.writeULEB128(writer, @as(u32, 0));
11391164
1140 try self.genBody(condbr.else_body);
1165 try self.genBody(else_body);
11411166 try self.endBlock();
11421167
11431168 // Outer block that matches the condition
1144 try self.genBody(condbr.then_body);
1169 try self.genBody(then_body);
11451170
11461171 return .none;
11471172 }
11481173
1149 fn genCmp(self: *Context, inst: *Inst.BinOp, op: std.math.CompareOperator) InnerError!WValue {
1174 fn airCmp(self: *Context, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
11501175 // save offset, so potential conditions can insert blocks in front of
11511176 // the comparison that we can later jump back to
11521177 const offset = self.code.items.len;
11531178
1154 const lhs = self.resolveInst(inst.lhs);
1155 const rhs = self.resolveInst(inst.rhs);
1179 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
1180 const lhs = self.resolveInst(data.bin_op.lhs);
1181 const rhs = self.resolveInst(data.bin_op.rhs);
1182 const lhs_ty = self.air.typeOf(data.bin_op.lhs);
11561183
11571184 try self.emitWValue(lhs);
11581185 try self.emitWValue(rhs);
11591186
11601187 const signedness: std.builtin.Signedness = blk: {
11611188 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1162 if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;
1189 if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
11631190
11641191 // incase of an actual integer, we emit the correct signedness
1165 break :blk inst.lhs.ty.intInfo(self.target).signedness;
1192 break :blk lhs_ty.intInfo(self.target).signedness;
11661193 };
11671194 const opcode: wasm.Opcode = buildOpcode(.{
1168 .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),
1195 .valtype1 = try self.typeToValtype(lhs_ty),
11691196 .op = switch (op) {
11701197 .lt => .lt,
11711198 .lte => .le,
......@@ -1180,16 +1207,17 @@ pub const Context = struct {
11801207 return WValue{ .code_offset = offset };
11811208 }
11821209
1183 fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
1210 fn airBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1211 const br = self.air.instructions.items(.data)[inst].br;
1212
11841213 // if operand has codegen bits we should break with a value
1185 if (br.operand.ty.hasCodeGenBits()) {
1186 const operand = self.resolveInst(br.operand);
1187 try self.emitWValue(operand);
1214 if (self.air.typeOf(br.operand).hasCodeGenBits()) {
1215 try self.emitWValue(self.resolveInst(br.operand));
11881216 }
11891217
11901218 // We map every block to its block index.
11911219 // We then determine how far we have to jump to it by substracting it from current block depth
1192 const idx: u32 = self.block_depth - self.blocks.get(br.block).?;
1220 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
11931221 const writer = self.code.writer();
11941222 try writer.writeByte(wasm.opcode(.br));
11951223 try leb.writeULEB128(writer, idx);
......@@ -1197,10 +1225,11 @@ pub const Context = struct {
11971225 return .none;
11981226 }
11991227
1200 fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue {
1228 fn airNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1229 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
12011230 const offset = self.code.items.len;
12021231
1203 const operand = self.resolveInst(not.operand);
1232 const operand = self.resolveInst(ty_op.operand);
12041233 try self.emitWValue(operand);
12051234
12061235 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
......@@ -1214,73 +1243,93 @@ pub const Context = struct {
12141243 return WValue{ .code_offset = offset };
12151244 }
12161245
1217 fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {
1246 fn airBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
12181247 _ = self;
1219 _ = breakpoint;
1248 _ = inst;
12201249 // unsupported by wasm itself. Can be implemented once we support DWARF
12211250 // for wasm
12221251 return .none;
12231252 }
12241253
1225 fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {
1226 _ = unreach;
1254 fn airUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1255 _ = inst;
12271256 try self.code.append(wasm.opcode(.@"unreachable"));
12281257 return .none;
12291258 }
12301259
1231 fn genBitcast(self: *Context, bitcast: *Inst.UnOp) InnerError!WValue {
1232 return self.resolveInst(bitcast.operand);
1260 fn airBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1261 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1262 return self.resolveInst(ty_op.operand);
12331263 }
12341264
1235 fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue {
1236 const struct_ptr = self.resolveInst(inst.struct_ptr);
1265 fn airStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1266 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1267 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
1268 const struct_ptr = self.resolveInst(extra.data.struct_ptr);
12371269
1238 return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) };
1270 return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
12391271 }
12401272
1241 fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue {
1242 const target = self.resolveInst(inst.target);
1243 const target_ty = inst.target.ty;
1244 const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty);
1245 const blocktype = try self.genBlockType(inst.base.src, inst.base.ty);
1246
1247 const signedness: std.builtin.Signedness = blk: {
1248 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1249 if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
1250
1251 // incase of an actual integer, we emit the correct signedness
1252 break :blk target_ty.intInfo(self.target).signedness;
1253 };
1254 for (inst.cases) |case| {
1255 // create a block for each case, when the condition does not match we break out of it
1256 try self.startBlock(.block, blocktype, null);
1257 try self.emitWValue(target);
1258 try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty);
1259 const opcode = buildOpcode(.{
1260 .valtype1 = valtype,
1261 .op = .ne, // not equal because we jump out the block if it does not match the condition
1262 .signedness = signedness,
1263 });
1264 try self.code.append(wasm.opcode(opcode));
1265 try self.code.append(wasm.opcode(.br_if));
1266 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
1267
1268 // emit our block code
1269 try self.genBody(case.body);
1270
1271 // end the block we created earlier
1272 try self.endBlock();
1273 }
1274
1275 // finally, emit the else case if it exists. Here we will not have to
1276 // check for a condition, so also no need to emit a block.
1277 try self.genBody(inst.else_body);
1278
1279 return .none;
1273 fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1274 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1275 const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
1276 const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
1277 const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
1278
1279 const target = self.resolveInst(pl_op.operand);
1280 const target_ty = self.air.typeOf(pl_op.operand);
1281 const valtype = try self.typeToValtype(target_ty);
1282 // result type is always 'noreturn'
1283 const blocktype = wasm.block_empty;
1284
1285 _ = valtype;
1286 _ = blocktype;
1287 _ = target;
1288 _ = else_body;
1289 return self.fail("TODO implement wasm codegen for switch", .{});
1290 //const signedness: std.builtin.Signedness = blk: {
1291 // // by default we tell the operand type is unsigned (i.e. bools and enum values)
1292 // if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
1293
1294 // // incase of an actual integer, we emit the correct signedness
1295 // break :blk target_ty.intInfo(self.target).signedness;
1296 //};
1297 //for (cases) |case_idx| {
1298 // const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
1299 // const case_body = self.air.extra[case.end..][0..case.data.body_len];
1300
1301 // // create a block for each case, when the condition does not match we break out of it
1302 // try self.startBlock(.block, blocktype, null);
1303 // try self.emitWValue(target);
1304
1305 // const val = self.air.value(case.data.item).?;
1306 // try self.emitConstant(val, target_ty);
1307 // const opcode = buildOpcode(.{
1308 // .valtype1 = valtype,
1309 // .op = .ne, // not equal because we jump out the block if it does not match the condition
1310 // .signedness = signedness,
1311 // });
1312 // try self.code.append(wasm.opcode(opcode));
1313 // try self.code.append(wasm.opcode(.br_if));
1314 // try leb.writeULEB128(self.code.writer(), @as(u32, 0));
1315
1316 // // emit our block code
1317 // try self.genBody(case_body);
1318
1319 // // end the block we created earlier
1320 // try self.endBlock();
1321 //}
1322
1323 //// finally, emit the else case if it exists. Here we will not have to
1324 //// check for a condition, so also no need to emit a block.
1325 //try self.genBody(else_body);
1326
1327 //return .none;
12801328 }
12811329
1282 fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {
1283 const operand = self.resolveInst(inst.operand);
1330 fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
1331 const un_op = self.air.instructions.items(.data)[inst].un_op;
1332 const operand = self.resolveInst(un_op);
12841333 const offset = self.code.items.len;
12851334 const writer = self.code.writer();
12861335
......@@ -1295,8 +1344,9 @@ pub const Context = struct {
12951344 return WValue{ .code_offset = offset };
12961345 }
12971346
1298 fn genUnwrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
1299 const operand = self.resolveInst(inst.operand);
1347 fn airUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1348 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1349 const operand = self.resolveInst(ty_op.operand);
13001350 // The index of multi_value contains the error code. To get the initial index of the payload we get
13011351 // the following index. Next, convert it to a `WValue.local`
13021352 //
......@@ -1304,7 +1354,8 @@ pub const Context = struct {
13041354 return WValue{ .local = operand.multi_value.index + 1 };
13051355 }
13061356
1307 fn genWrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
1308 return self.resolveInst(inst.operand);
1357 fn airWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1358 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1359 return self.resolveInst(ty_op.operand);
13091360 }
13101361};
src/link.zig+29-5
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const mem = std.mem;
34const Allocator = std.mem.Allocator;
45const fs = std.fs;
......@@ -14,8 +15,10 @@ const Cache = @import("Cache.zig");
1415const build_options = @import("build_options");
1516const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1617const wasi_libc = @import("wasi_libc.zig");
18const Air = @import("Air.zig");
19const Liveness = @import("Liveness.zig");
1720
18pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
21pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
1922
2023pub const Emit = struct {
2124 /// Where the output will go.
......@@ -313,13 +316,34 @@ pub const File = struct {
313316 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty });
314317 assert(decl.has_tv);
315318 switch (base.tag) {
316 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
317 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
319 // zig fmt: off
320 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
321 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
318322 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
319 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
320 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
323 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
324 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
321325 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
322326 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl),
327 // zig fmt: on
328 }
329 }
330
331 /// May be called before or after updateDeclExports but must be called
332 /// after allocateDeclIndexes for any given Decl.
333 pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
334 log.debug("updateFunc {*} ({s}), type={}", .{
335 func.owner_decl, func.owner_decl.name, func.owner_decl.ty,
336 });
337 switch (base.tag) {
338 // zig fmt: off
339 .coff => return @fieldParentPtr(Coff, "base", base).updateFunc(module, func, air, liveness),
340 .elf => return @fieldParentPtr(Elf, "base", base).updateFunc(module, func, air, liveness),
341 .macho => return @fieldParentPtr(MachO, "base", base).updateFunc(module, func, air, liveness),
342 .c => return @fieldParentPtr(C, "base", base).updateFunc(module, func, air, liveness),
343 .wasm => return @fieldParentPtr(Wasm, "base", base).updateFunc(module, func, air, liveness),
344 .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func, air, liveness),
345 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func, air, liveness),
346 // zig fmt: on
323347 }
324348 }
325349
src/link/C.zig+22-6
......@@ -2,14 +2,17 @@ const std = @import("std");
22const mem = std.mem;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
5const fs = std.fs;
6
7const C = @This();
58const Module = @import("../Module.zig");
69const Compilation = @import("../Compilation.zig");
7const fs = std.fs;
810const codegen = @import("../codegen/c.zig");
911const link = @import("../link.zig");
1012const trace = @import("../tracy.zig").trace;
11const C = @This();
1213const Type = @import("../type.zig").Type;
14const Air = @import("../Air.zig");
15const Liveness = @import("../Liveness.zig");
1316
1417pub const base_tag: link.File.Tag = .c;
1518pub const zig_h = @embedFile("C/zig.h");
......@@ -95,10 +98,7 @@ fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void {
9598 decl.fn_link.c.typedefs.deinit(gpa);
9699}
97100
98pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
99 const tracy = trace(@src());
100 defer tracy.end();
101
101pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, liveness: Liveness) !void {
102102 // Keep track of all decls so we can iterate over them on flush().
103103 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
104104
......@@ -126,6 +126,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
126126 .code = code.toManaged(module.gpa),
127127 .value_map = codegen.CValueMap.init(module.gpa),
128128 .indent_writer = undefined, // set later so we can get a pointer to object.code
129 .air = air,
130 .liveness = liveness,
129131 };
130132 object.indent_writer = .{ .underlying_writer = object.code.writer() };
131133 defer {
......@@ -157,6 +159,20 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
157159 code.shrinkAndFree(module.gpa, code.items.len);
158160}
159161
162pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
163 const tracy = trace(@src());
164 defer tracy.end();
165
166 return self.finishUpdateDecl(module, func.owner_decl, air, liveness);
167}
168
169pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
170 const tracy = trace(@src());
171 defer tracy.end();
172
173 return self.finishUpdateDecl(module, decl, undefined, undefined);
174}
175
160176pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
161177 // The C backend does not have the ability to fix line numbers without re-generating
162178 // the entire Decl.
src/link/Coff.zig+56-5
......@@ -1,6 +1,7 @@
11const Coff = @This();
22
33const std = @import("std");
4const builtin = @import("builtin");
45const log = std.log.scoped(.link);
56const Allocator = std.mem.Allocator;
67const assert = std.debug.assert;
......@@ -17,6 +18,8 @@ const build_options = @import("build_options");
1718const Cache = @import("../Cache.zig");
1819const mingw = @import("../mingw.zig");
1920const llvm_backend = @import("../codegen/llvm.zig");
21const Air = @import("../Air.zig");
22const Liveness = @import("../Liveness.zig");
2023
2124const allocation_padding = 4 / 3;
2225const minimum_text_block_size = 64 * allocation_padding;
......@@ -653,19 +656,63 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
653656 }
654657}
655658
656pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
657 // TODO COFF/PE debug information
658 // TODO Implement exports
659pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
660 if (build_options.skip_non_native and
661 builtin.object_format != .coff and
662 builtin.object_format != .pe)
663 {
664 @panic("Attempted to compile for object format that was disabled by build configuration");
665 }
666 if (build_options.have_llvm) {
667 if (self.llvm_object) |llvm_object| {
668 return llvm_object.updateFunc(module, func, air, liveness);
669 }
670 }
659671 const tracy = trace(@src());
660672 defer tracy.end();
661673
662 if (build_options.have_llvm)
663 if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);
674 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
675 defer code_buffer.deinit();
676
677 const decl = func.owner_decl;
678 const res = try codegen.generateFunction(
679 &self.base,
680 decl.srcLoc(),
681 func,
682 air,
683 liveness,
684 &code_buffer,
685 .none,
686 );
687 const code = switch (res) {
688 .appended => code_buffer.items,
689 .fail => |em| {
690 decl.analysis = .codegen_failure;
691 try module.failed_decls.put(module.gpa, decl, em);
692 return;
693 },
694 };
695
696 return self.finishUpdateDecl(module, func.owner_decl, code);
697}
698
699pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
700 if (build_options.skip_non_native and builtin.object_format != .coff and builtin.object_format != .pe) {
701 @panic("Attempted to compile for object format that was disabled by build configuration");
702 }
703 if (build_options.have_llvm) {
704 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
705 }
706 const tracy = trace(@src());
707 defer tracy.end();
664708
665709 if (decl.val.tag() == .extern_fn) {
666710 return; // TODO Should we do more when front-end analyzed extern decl?
667711 }
668712
713 // TODO COFF/PE debug information
714 // TODO Implement exports
715
669716 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
670717 defer code_buffer.deinit();
671718
......@@ -683,6 +730,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
683730 },
684731 };
685732
733 return self.finishUpdateDecl(module, decl, code);
734}
735
736fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []const u8) !void {
686737 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
687738 const curr_size = decl.link.coff.size;
688739 if (curr_size != 0) {
src/link/Elf.zig+310-252
......@@ -1,6 +1,7 @@
11const Elf = @This();
22
33const std = @import("std");
4const builtin = @import("builtin");
45const mem = std.mem;
56const assert = std.debug.assert;
67const Allocator = std.mem.Allocator;
......@@ -10,7 +11,6 @@ const log = std.log.scoped(.link);
1011const DW = std.dwarf;
1112const leb128 = std.leb;
1213
13const ir = @import("../air.zig");
1414const Module = @import("../Module.zig");
1515const Compilation = @import("../Compilation.zig");
1616const codegen = @import("../codegen.zig");
......@@ -26,6 +26,8 @@ const glibc = @import("../glibc.zig");
2626const musl = @import("../musl.zig");
2727const Cache = @import("../Cache.zig");
2828const llvm_backend = @import("../codegen/llvm.zig");
29const Air = @import("../Air.zig");
30const Liveness = @import("../Liveness.zig");
2931
3032const default_entry_addr = 0x8000000;
3133
......@@ -2155,138 +2157,17 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
21552157 }
21562158}
21572159
2158pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2159 const tracy = trace(@src());
2160 defer tracy.end();
2161
2162 if (build_options.have_llvm)
2163 if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);
2164
2165 if (decl.val.tag() == .extern_fn) {
2166 return; // TODO Should we do more when front-end analyzed extern decl?
2167 }
2168 if (decl.val.castTag(.variable)) |payload| {
2169 const variable = payload.data;
2170 if (variable.is_extern) {
2171 return; // TODO Should we do more when front-end analyzed extern decl?
2172 }
2173 }
2174
2175 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2176 defer code_buffer.deinit();
2177
2178 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
2179 defer dbg_line_buffer.deinit();
2180
2181 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
2182 defer dbg_info_buffer.deinit();
2183
2184 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
2185 defer {
2186 var it = dbg_info_type_relocs.valueIterator();
2187 while (it.next()) |value| {
2188 value.relocs.deinit(self.base.allocator);
2189 }
2190 dbg_info_type_relocs.deinit(self.base.allocator);
2191 }
2192
2193 const is_fn: bool = switch (decl.ty.zigTypeTag()) {
2194 .Fn => true,
2195 else => false,
2196 };
2197 if (is_fn) {
2198 // For functions we need to add a prologue to the debug line program.
2199 try dbg_line_buffer.ensureCapacity(26);
2200
2201 const func = decl.val.castTag(.function).?.data;
2202 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
2203
2204 const ptr_width_bytes = self.ptrWidthBytes();
2205 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
2206 DW.LNS_extended_op,
2207 ptr_width_bytes + 1,
2208 DW.LNE_set_address,
2209 });
2210 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
2211 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
2212 dbg_line_buffer.items.len += ptr_width_bytes;
2213
2214 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
2215 // This is the "relocatable" relative line offset from the previous function's end curly
2216 // to this function's begin curly.
2217 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
2218 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
2219 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
2220
2221 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
2222 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
2223 // Once we support more than one source file, this will have the ability to be more
2224 // than one possible value.
2225 const file_index = 1;
2226 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
2227
2228 // Emit a line for the begin curly with prologue_end=false. The codegen will
2229 // do the work of setting prologue_end=true and epilogue_begin=true.
2230 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
2231
2232 // .debug_info subprogram
2233 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
2234 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
2235
2236 const fn_ret_type = decl.ty.fnReturnType();
2237 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
2238 if (fn_ret_has_bits) {
2239 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
2240 } else {
2241 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
2242 }
2243 // These get overwritten after generating the machine code. These values are
2244 // "relocations" and have to be in this fixed place so that functions can be
2245 // moved in virtual address space.
2246 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
2247 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
2248 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
2249 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
2250 if (fn_ret_has_bits) {
2251 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
2252 if (!gop.found_existing) {
2253 gop.value_ptr.* = .{
2254 .off = undefined,
2255 .relocs = .{},
2256 };
2257 }
2258 try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
2259 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
2260 }
2261 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
2262 } else {
2263 // TODO implement .debug_info for global variables
2160fn deinitRelocs(gpa: *Allocator, table: *File.DbgInfoTypeRelocsTable) void {
2161 var it = table.valueIterator();
2162 while (it.next()) |value| {
2163 value.relocs.deinit(gpa);
22642164 }
2265 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
2266 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2267 .ty = decl.ty,
2268 .val = decl_val,
2269 }, &code_buffer, .{
2270 .dwarf = .{
2271 .dbg_line = &dbg_line_buffer,
2272 .dbg_info = &dbg_info_buffer,
2273 .dbg_info_type_relocs = &dbg_info_type_relocs,
2274 },
2275 });
2276 const code = switch (res) {
2277 .externally_managed => |x| x,
2278 .appended => code_buffer.items,
2279 .fail => |em| {
2280 decl.analysis = .codegen_failure;
2281 try module.failed_decls.put(module.gpa, decl, em);
2282 return;
2283 },
2284 };
2165 table.deinit(gpa);
2166}
22852167
2168fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
22862169 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
22872170
2288 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
2289
22902171 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
22912172 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
22922173 if (local_sym.st_size != 0) {
......@@ -2338,128 +2219,16 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
23382219 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
23392220 try self.base.file.?.pwriteAll(code, file_offset);
23402221
2341 const target_endian = self.base.options.target.cpu.arch.endian();
2342
2343 const text_block = &decl.link.elf;
2344
2345 // If the Decl is a function, we need to update the .debug_line program.
2346 if (is_fn) {
2347 // Perform the relocations based on vaddr.
2348 switch (self.ptr_width) {
2349 .p32 => {
2350 {
2351 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
2352 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2353 }
2354 {
2355 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
2356 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2357 }
2358 },
2359 .p64 => {
2360 {
2361 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
2362 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2363 }
2364 {
2365 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
2366 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2367 }
2368 },
2369 }
2370 {
2371 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
2372 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
2373 }
2374
2375 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
2376
2377 // Now we have the full contents and may allocate a region to store it.
2378
2379 // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for
2380 // `TextBlock` and the .debug_info. If you are editing this logic, you
2381 // probably need to edit that logic too.
2382
2383 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
2384 const src_fn = &decl.fn_link.elf;
2385 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
2386 if (self.dbg_line_fn_last) |last| not_first: {
2387 if (src_fn.next) |next| {
2388 // Update existing function - non-last item.
2389 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
2390 // It grew too big, so we move it to a new location.
2391 if (src_fn.prev) |prev| {
2392 self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
2393 prev.next = src_fn.next;
2394 }
2395 assert(src_fn.prev != next);
2396 next.prev = src_fn.prev;
2397 src_fn.next = null;
2398 // Populate where it used to be with NOPs.
2399 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2400 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
2401 // TODO Look at the free list before appending at the end.
2402 src_fn.prev = last;
2403 last.next = src_fn;
2404 self.dbg_line_fn_last = src_fn;
2405
2406 src_fn.off = last.off + padToIdeal(last.len);
2407 }
2408 } else if (src_fn.prev == null) {
2409 if (src_fn == last) {
2410 // Special case: there is only 1 function and it is being updated.
2411 // In this case there is nothing to do. The function's length has
2412 // already been updated, and the logic below takes care of
2413 // resizing the .debug_line section.
2414 break :not_first;
2415 }
2416 // Append new function.
2417 // TODO Look at the free list before appending at the end.
2418 src_fn.prev = last;
2419 last.next = src_fn;
2420 self.dbg_line_fn_last = src_fn;
2421
2422 src_fn.off = last.off + padToIdeal(last.len);
2423 }
2424 } else {
2425 // This is the first function of the Line Number Program.
2426 self.dbg_line_fn_first = src_fn;
2427 self.dbg_line_fn_last = src_fn;
2428
2429 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes());
2430 }
2431
2432 const last_src_fn = self.dbg_line_fn_last.?;
2433 const needed_size = last_src_fn.off + last_src_fn.len;
2434 if (needed_size != debug_line_sect.sh_size) {
2435 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
2436 const new_offset = self.findFreeSpace(needed_size, 1);
2437 const existing_size = last_src_fn.off;
2438 log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{
2439 existing_size,
2440 debug_line_sect.sh_offset,
2441 new_offset,
2442 });
2443 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2444 if (amt != existing_size) return error.InputOutput;
2445 debug_line_sect.sh_offset = new_offset;
2446 }
2447 debug_line_sect.sh_size = needed_size;
2448 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2449 self.debug_line_header_dirty = true;
2450 }
2451 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
2452 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
2453
2454 // We only have support for one compilation unit so far, so the offsets are directly
2455 // from the .debug_line section.
2456 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2457 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
2458
2459 // .debug_info - End the TAG_subprogram children.
2460 try dbg_info_buffer.append(0);
2461 }
2222 return local_sym;
2223}
24622224
2225fn finishUpdateDecl(
2226 self: *Elf,
2227 module: *Module,
2228 decl: *Module.Decl,
2229 dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable,
2230 dbg_info_buffer: *std.ArrayList(u8),
2231) !void {
24632232 // Now we emit the .debug_info types of the Decl. These will count towards the size of
24642233 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
24652234 // relocations yet.
......@@ -2467,12 +2236,15 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
24672236 var it = dbg_info_type_relocs.iterator();
24682237 while (it.next()) |entry| {
24692238 entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
2470 try self.addDbgInfoType(entry.key_ptr.*, &dbg_info_buffer);
2239 try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer);
24712240 }
24722241 }
24732242
2243 const text_block = &decl.link.elf;
24742244 try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
24752245
2246 const target_endian = self.base.options.target.cpu.arch.endian();
2247
24762248 {
24772249 // Now that we have the offset assigned we can finally perform type relocations.
24782250 var it = dbg_info_type_relocs.valueIterator();
......@@ -2495,6 +2267,292 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
24952267 return self.updateDeclExports(module, decl, decl_exports);
24962268}
24972269
2270pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
2271 if (build_options.skip_non_native and builtin.object_format != .elf) {
2272 @panic("Attempted to compile for object format that was disabled by build configuration");
2273 }
2274 if (build_options.have_llvm) {
2275 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
2276 }
2277
2278 const tracy = trace(@src());
2279 defer tracy.end();
2280
2281 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2282 defer code_buffer.deinit();
2283
2284 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
2285 defer dbg_line_buffer.deinit();
2286
2287 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
2288 defer dbg_info_buffer.deinit();
2289
2290 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
2291 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
2292
2293 // For functions we need to add a prologue to the debug line program.
2294 try dbg_line_buffer.ensureCapacity(26);
2295
2296 const decl = func.owner_decl;
2297 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
2298
2299 const ptr_width_bytes = self.ptrWidthBytes();
2300 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
2301 DW.LNS_extended_op,
2302 ptr_width_bytes + 1,
2303 DW.LNE_set_address,
2304 });
2305 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
2306 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
2307 dbg_line_buffer.items.len += ptr_width_bytes;
2308
2309 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
2310 // This is the "relocatable" relative line offset from the previous function's end curly
2311 // to this function's begin curly.
2312 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
2313 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
2314 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
2315
2316 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
2317 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
2318 // Once we support more than one source file, this will have the ability to be more
2319 // than one possible value.
2320 const file_index = 1;
2321 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
2322
2323 // Emit a line for the begin curly with prologue_end=false. The codegen will
2324 // do the work of setting prologue_end=true and epilogue_begin=true.
2325 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
2326
2327 // .debug_info subprogram
2328 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
2329 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
2330
2331 const fn_ret_type = decl.ty.fnReturnType();
2332 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
2333 if (fn_ret_has_bits) {
2334 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
2335 } else {
2336 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
2337 }
2338 // These get overwritten after generating the machine code. These values are
2339 // "relocations" and have to be in this fixed place so that functions can be
2340 // moved in virtual address space.
2341 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
2342 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
2343 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
2344 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
2345 if (fn_ret_has_bits) {
2346 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
2347 if (!gop.found_existing) {
2348 gop.value_ptr.* = .{
2349 .off = undefined,
2350 .relocs = .{},
2351 };
2352 }
2353 try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
2354 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
2355 }
2356 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
2357
2358 const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
2359 .dwarf = .{
2360 .dbg_line = &dbg_line_buffer,
2361 .dbg_info = &dbg_info_buffer,
2362 .dbg_info_type_relocs = &dbg_info_type_relocs,
2363 },
2364 });
2365 const code = switch (res) {
2366 .appended => code_buffer.items,
2367 .fail => |em| {
2368 decl.analysis = .codegen_failure;
2369 try module.failed_decls.put(module.gpa, decl, em);
2370 return;
2371 },
2372 };
2373
2374 const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);
2375
2376 const target_endian = self.base.options.target.cpu.arch.endian();
2377
2378 // Since the Decl is a function, we need to update the .debug_line program.
2379 // Perform the relocations based on vaddr.
2380 switch (self.ptr_width) {
2381 .p32 => {
2382 {
2383 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
2384 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2385 }
2386 {
2387 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
2388 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2389 }
2390 },
2391 .p64 => {
2392 {
2393 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
2394 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2395 }
2396 {
2397 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
2398 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2399 }
2400 },
2401 }
2402 {
2403 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
2404 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
2405 }
2406
2407 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
2408
2409 // Now we have the full contents and may allocate a region to store it.
2410
2411 // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for
2412 // `TextBlock` and the .debug_info. If you are editing this logic, you
2413 // probably need to edit that logic too.
2414
2415 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
2416 const src_fn = &decl.fn_link.elf;
2417 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
2418 if (self.dbg_line_fn_last) |last| not_first: {
2419 if (src_fn.next) |next| {
2420 // Update existing function - non-last item.
2421 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
2422 // It grew too big, so we move it to a new location.
2423 if (src_fn.prev) |prev| {
2424 self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
2425 prev.next = src_fn.next;
2426 }
2427 assert(src_fn.prev != next);
2428 next.prev = src_fn.prev;
2429 src_fn.next = null;
2430 // Populate where it used to be with NOPs.
2431 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2432 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
2433 // TODO Look at the free list before appending at the end.
2434 src_fn.prev = last;
2435 last.next = src_fn;
2436 self.dbg_line_fn_last = src_fn;
2437
2438 src_fn.off = last.off + padToIdeal(last.len);
2439 }
2440 } else if (src_fn.prev == null) {
2441 if (src_fn == last) {
2442 // Special case: there is only 1 function and it is being updated.
2443 // In this case there is nothing to do. The function's length has
2444 // already been updated, and the logic below takes care of
2445 // resizing the .debug_line section.
2446 break :not_first;
2447 }
2448 // Append new function.
2449 // TODO Look at the free list before appending at the end.
2450 src_fn.prev = last;
2451 last.next = src_fn;
2452 self.dbg_line_fn_last = src_fn;
2453
2454 src_fn.off = last.off + padToIdeal(last.len);
2455 }
2456 } else {
2457 // This is the first function of the Line Number Program.
2458 self.dbg_line_fn_first = src_fn;
2459 self.dbg_line_fn_last = src_fn;
2460
2461 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes());
2462 }
2463
2464 const last_src_fn = self.dbg_line_fn_last.?;
2465 const needed_size = last_src_fn.off + last_src_fn.len;
2466 if (needed_size != debug_line_sect.sh_size) {
2467 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
2468 const new_offset = self.findFreeSpace(needed_size, 1);
2469 const existing_size = last_src_fn.off;
2470 log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{
2471 existing_size,
2472 debug_line_sect.sh_offset,
2473 new_offset,
2474 });
2475 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
2476 if (amt != existing_size) return error.InputOutput;
2477 debug_line_sect.sh_offset = new_offset;
2478 }
2479 debug_line_sect.sh_size = needed_size;
2480 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2481 self.debug_line_header_dirty = true;
2482 }
2483 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
2484 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
2485
2486 // We only have support for one compilation unit so far, so the offsets are directly
2487 // from the .debug_line section.
2488 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2489 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
2490
2491 // .debug_info - End the TAG_subprogram children.
2492 try dbg_info_buffer.append(0);
2493
2494 return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);
2495}
2496
2497pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2498 if (build_options.skip_non_native and builtin.object_format != .elf) {
2499 @panic("Attempted to compile for object format that was disabled by build configuration");
2500 }
2501 if (build_options.have_llvm) {
2502 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
2503 }
2504
2505 const tracy = trace(@src());
2506 defer tracy.end();
2507
2508 if (decl.val.tag() == .extern_fn) {
2509 return; // TODO Should we do more when front-end analyzed extern decl?
2510 }
2511 if (decl.val.castTag(.variable)) |payload| {
2512 const variable = payload.data;
2513 if (variable.is_extern) {
2514 return; // TODO Should we do more when front-end analyzed extern decl?
2515 }
2516 }
2517
2518 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2519 defer code_buffer.deinit();
2520
2521 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
2522 defer dbg_line_buffer.deinit();
2523
2524 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
2525 defer dbg_info_buffer.deinit();
2526
2527 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
2528 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
2529
2530 // TODO implement .debug_info for global variables
2531 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
2532 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2533 .ty = decl.ty,
2534 .val = decl_val,
2535 }, &code_buffer, .{
2536 .dwarf = .{
2537 .dbg_line = &dbg_line_buffer,
2538 .dbg_info = &dbg_info_buffer,
2539 .dbg_info_type_relocs = &dbg_info_type_relocs,
2540 },
2541 });
2542 const code = switch (res) {
2543 .externally_managed => |x| x,
2544 .appended => code_buffer.items,
2545 .fail => |em| {
2546 decl.analysis = .codegen_failure;
2547 try module.failed_decls.put(module.gpa, decl, em);
2548 return;
2549 },
2550 };
2551
2552 _ = try self.updateDeclCode(decl, code, elf.STT_OBJECT);
2553 return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);
2554}
2555
24982556/// Asserts the type has codegen bits.
24992557fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
25002558 switch (ty.zigTypeTag()) {
......@@ -3022,7 +3080,7 @@ fn pwriteDbgLineNops(
30223080
30233081 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
30243082 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
3025 var vecs: [256]std.os.iovec_const = undefined;
3083 var vecs: [512]std.os.iovec_const = undefined;
30263084 var vec_index: usize = 0;
30273085 {
30283086 var padding_left = prev_padding_size;
src/link/MachO.zig+128-31
......@@ -2,6 +2,7 @@ const MachO = @This();
22
33const std = @import("std");
44const build_options = @import("build_options");
5const builtin = @import("builtin");
56const assert = std.debug.assert;
67const fmt = std.fmt;
78const fs = std.fs;
......@@ -16,9 +17,11 @@ const bind = @import("MachO/bind.zig");
1617const codegen = @import("../codegen.zig");
1718const commands = @import("MachO/commands.zig");
1819const link = @import("../link.zig");
20const llvm_backend = @import("../codegen/llvm.zig");
1921const target_util = @import("../target.zig");
2022const trace = @import("../tracy.zig").trace;
2123
24const Air = @import("../Air.zig");
2225const Allocator = mem.Allocator;
2326const Archive = @import("MachO/Archive.zig");
2427const Cache = @import("../Cache.zig");
......@@ -27,6 +30,7 @@ const Compilation = @import("../Compilation.zig");
2730const DebugSymbols = @import("MachO/DebugSymbols.zig");
2831const Dylib = @import("MachO/Dylib.zig");
2932const Object = @import("MachO/Object.zig");
33const Liveness = @import("../Liveness.zig");
3034const LoadCommand = commands.LoadCommand;
3135const Module = @import("../Module.zig");
3236const File = link.File;
......@@ -38,6 +42,9 @@ pub const base_tag: File.Tag = File.Tag.macho;
3842
3943base: File,
4044
45/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
46llvm_object: ?*llvm_backend.Object = null,
47
4148/// Debug symbols bundle (or dSym).
4249d_sym: ?DebugSymbols = null,
4350
......@@ -319,7 +326,13 @@ pub const SrcFn = struct {
319326pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
320327 assert(options.object_format == .macho);
321328
322 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
329 if (build_options.have_llvm and options.use_llvm) {
330 const self = try createEmpty(allocator, options);
331 errdefer self.base.destroy();
332
333 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
334 return self;
335 }
323336
324337 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
325338 .truncate = false,
......@@ -3468,7 +3481,88 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
34683481 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index);
34693482}
34703483
3484pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
3485 if (build_options.skip_non_native and builtin.object_format != .macho) {
3486 @panic("Attempted to compile for object format that was disabled by build configuration");
3487 }
3488 if (build_options.have_llvm) {
3489 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
3490 }
3491 const tracy = trace(@src());
3492 defer tracy.end();
3493
3494 const decl = func.owner_decl;
3495
3496 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3497 defer code_buffer.deinit();
3498
3499 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3500 const debug_buffers = if (self.d_sym) |*ds| blk: {
3501 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3502 break :blk &debug_buffers_buf;
3503 } else null;
3504 defer {
3505 if (debug_buffers) |dbg| {
3506 dbg.dbg_line_buffer.deinit();
3507 dbg.dbg_info_buffer.deinit();
3508 var it = dbg.dbg_info_type_relocs.valueIterator();
3509 while (it.next()) |value| {
3510 value.relocs.deinit(self.base.allocator);
3511 }
3512 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
3513 }
3514 }
3515
3516 self.active_decl = decl;
3517
3518 const res = if (debug_buffers) |dbg|
3519 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
3520 .dwarf = .{
3521 .dbg_line = &dbg.dbg_line_buffer,
3522 .dbg_info = &dbg.dbg_info_buffer,
3523 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3524 },
3525 })
3526 else
3527 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
3528 switch (res) {
3529 .appended => {
3530 decl.link.macho.code = code_buffer.toOwnedSlice();
3531 },
3532 .fail => |em| {
3533 decl.analysis = .codegen_failure;
3534 try module.failed_decls.put(module.gpa, decl, em);
3535 return;
3536 },
3537 }
3538
3539 const symbol = try self.placeDecl(decl, decl.link.macho.code.len);
3540
3541 try self.writeCode(symbol, decl.link.macho.code);
3542
3543 if (debug_buffers) |db| {
3544 try self.d_sym.?.commitDeclDebugInfo(
3545 self.base.allocator,
3546 module,
3547 decl,
3548 db,
3549 self.base.options.target,
3550 );
3551 }
3552
3553 // Since we updated the vaddr and the size, each corresponding export symbol also
3554 // needs to be updated.
3555 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3556 try self.updateDeclExports(module, decl, decl_exports);
3557}
3558
34713559pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3560 if (build_options.skip_non_native and builtin.object_format != .macho) {
3561 @panic("Attempted to compile for object format that was disabled by build configuration");
3562 }
3563 if (build_options.have_llvm) {
3564 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
3565 }
34723566 const tracy = trace(@src());
34733567 defer tracy.end();
34743568
......@@ -3479,9 +3573,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
34793573 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
34803574 defer code_buffer.deinit();
34813575
3482 var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null;
3576 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3577 const debug_buffers = if (self.d_sym) |*ds| blk: {
3578 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3579 break :blk &debug_buffers_buf;
3580 } else null;
34833581 defer {
3484 if (debug_buffers) |*dbg| {
3582 if (debug_buffers) |dbg| {
34853583 dbg.dbg_line_buffer.deinit();
34863584 dbg.dbg_info_buffer.deinit();
34873585 var it = dbg.dbg_info_type_relocs.valueIterator();
......@@ -3494,7 +3592,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
34943592
34953593 self.active_decl = decl;
34963594
3497 const res = if (debug_buffers) |*dbg|
3595 const res = if (debug_buffers) |dbg|
34983596 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
34993597 .ty = decl.ty,
35003598 .val = decl.val,
......@@ -3525,16 +3623,26 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35253623 },
35263624 }
35273625 };
3626 const symbol = try self.placeDecl(decl, code.len);
3627
3628 try self.writeCode(symbol, code);
35283629
3630 // Since we updated the vaddr and the size, each corresponding export symbol also
3631 // needs to be updated.
3632 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3633 try self.updateDeclExports(module, decl, decl_exports);
3634}
3635
3636fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
35293637 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
35303638 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
35313639 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
35323640
35333641 if (decl.link.macho.size != 0) {
35343642 const capacity = decl.link.macho.capacity(self.*);
3535 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
3643 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
35363644 if (need_realloc) {
3537 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
3645 const vaddr = try self.growTextBlock(&decl.link.macho, code_len, required_alignment);
35383646
35393647 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
35403648
......@@ -3548,10 +3656,10 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35483656 }
35493657
35503658 symbol.n_value = vaddr;
3551 } else if (code.len < decl.link.macho.size) {
3552 self.shrinkTextBlock(&decl.link.macho, code.len);
3659 } else if (code_len < decl.link.macho.size) {
3660 self.shrinkTextBlock(&decl.link.macho, code_len);
35533661 }
3554 decl.link.macho.size = code.len;
3662 decl.link.macho.size = code_len;
35553663
35563664 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
35573665 defer self.base.allocator.free(new_name);
......@@ -3569,7 +3677,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35693677 defer self.base.allocator.free(decl_name);
35703678
35713679 const name_str_index = try self.makeString(decl_name);
3572 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
3680 const addr = try self.allocateTextBlock(&decl.link.macho, code_len, required_alignment);
35733681
35743682 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
35753683
......@@ -3582,17 +3690,15 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
35823690 .n_desc = 0,
35833691 .n_value = addr,
35843692 };
3585
3586 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
3587
3588 if (self.d_sym) |*ds|
3589 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
3590
35913693 const got_index = self.got_entries_map.get(.{
35923694 .where = .local,
35933695 .where_index = decl.link.macho.local_sym_index,
35943696 }) orelse unreachable;
35953697 try self.writeGotEntry(got_index);
3698
3699 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
3700 if (self.d_sym) |*ds|
3701 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
35963702 }
35973703
35983704 // Resolve relocations
......@@ -3615,25 +3721,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
36153721 }
36163722 }
36173723
3618 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3724 return symbol;
3725}
3726
3727fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void {
3728 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
36193729 const text_section = text_segment.sections.items[self.text_section_index.?];
36203730 const section_offset = symbol.n_value - text_section.addr;
36213731 const file_offset = text_section.offset + section_offset;
3732 log.debug("writing code for symbol {s} at file offset 0x{x}", .{ self.getString(symbol.n_strx), file_offset });
36223733 try self.base.file.?.pwriteAll(code, file_offset);
3623
3624 if (debug_buffers) |*db| {
3625 try self.d_sym.?.commitDeclDebugInfo(
3626 self.base.allocator,
3627 module,
3628 decl,
3629 db,
3630 self.base.options.target,
3631 );
3632 }
3633
3634 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
3635 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
3636 try self.updateDeclExports(module, decl, decl_exports);
36373734}
36383735
36393736pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
src/link/Plan9.zig+230-155
......@@ -2,18 +2,21 @@
22//! would be to add incremental linking in a similar way as ELF does.
33
44const Plan9 = @This();
5
6const std = @import("std");
75const link = @import("../link.zig");
86const Module = @import("../Module.zig");
97const Compilation = @import("../Compilation.zig");
108const aout = @import("Plan9/aout.zig");
119const codegen = @import("../codegen.zig");
1210const trace = @import("../tracy.zig").trace;
13const mem = std.mem;
1411const File = link.File;
15const Allocator = std.mem.Allocator;
12const build_options = @import("build_options");
13const Air = @import("../Air.zig");
14const Liveness = @import("../Liveness.zig");
1615
16const std = @import("std");
17const builtin = @import("builtin");
18const mem = std.mem;
19const Allocator = std.mem.Allocator;
1720const log = std.log.scoped(.link);
1821const assert = std.debug.assert;
1922
......@@ -22,20 +25,22 @@ sixtyfour_bit: bool,
2225error_flags: File.ErrorFlags = File.ErrorFlags{},
2326bases: Bases,
2427
25decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
26/// is just casted down when 32 bit
28/// A symbol's value is just casted down when compiling
29/// for a 32 bit target.
2730syms: std.ArrayListUnmanaged(aout.Sym) = .{},
28text_buf: std.ArrayListUnmanaged(u8) = .{},
29data_buf: std.ArrayListUnmanaged(u8) = .{},
31
32fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
33data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
3034
3135hdr: aout.ExecHdr = undefined,
3236
33entry_decl: ?*Module.Decl = null,
37entry_val: ?u64 = null,
38
39got_len: u64 = 0,
3440
35got: std.ArrayListUnmanaged(u64) = .{},
3641const Bases = struct {
3742 text: u64,
38 /// the addr of the got
43 /// the Global Offset Table starts at the beginning of the data section
3944 data: u64,
4045};
4146
......@@ -46,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
4651 else => unreachable,
4752 };
4853}
49/// opposite of getAddr
50fn takeAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
51 return addr - switch (t) {
52 .T, .t, .l, .L => self.bases.text,
53 .D, .d, .B, .b => self.bases.data,
54 else => unreachable,
55 };
56}
5754
5855fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
5956 return self.getAddr(s.value, s.type);
......@@ -120,9 +117,84 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
120117 return self;
121118}
122119
120pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
121 if (build_options.skip_non_native and builtin.object_format != .plan9) {
122 @panic("Attempted to compile for object format that was disabled by build configuration");
123 }
124
125 const decl = func.owner_decl;
126 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
127
128 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
129 defer code_buffer.deinit();
130 const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .none = .{} });
131 const code = switch (res) {
132 .appended => code_buffer.toOwnedSlice(),
133 .fail => |em| {
134 decl.analysis = .codegen_failure;
135 try module.failed_decls.put(module.gpa, decl, em);
136 return;
137 },
138 };
139 try self.fn_decl_table.put(self.base.allocator, decl, code);
140 return self.updateFinish(decl);
141}
142
123143pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
124 _ = module;
125 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
144 if (decl.val.tag() == .extern_fn) {
145 return; // TODO Should we do more when front-end analyzed extern decl?
146 }
147 if (decl.val.castTag(.variable)) |payload| {
148 const variable = payload.data;
149 if (variable.is_extern) {
150 return; // TODO Should we do more when front-end analyzed extern decl?
151 }
152 }
153
154 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
155
156 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
157 defer code_buffer.deinit();
158 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
159 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
160 .ty = decl.ty,
161 .val = decl_val,
162 }, &code_buffer, .{ .none = .{} });
163 const code = switch (res) {
164 .externally_managed => |x| x,
165 .appended => code_buffer.items,
166 .fail => |em| {
167 decl.analysis = .codegen_failure;
168 try module.failed_decls.put(module.gpa, decl, em);
169 return;
170 },
171 };
172 var duped_code = try std.mem.dupe(self.base.allocator, u8, code);
173 errdefer self.base.allocator.free(duped_code);
174 try self.data_decl_table.put(self.base.allocator, decl, duped_code);
175 return self.updateFinish(decl);
176}
177/// called at the end of update{Decl,Func}
178fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
179 const is_fn = (decl.ty.zigTypeTag() == .Fn);
180 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
181 const sym_t: aout.Sym.Type = if (is_fn) .t else .d;
182 // write the internal linker metadata
183 decl.link.plan9.type = sym_t;
184 // write the symbol
185 // we already have the got index because that got allocated in allocateDeclIndexes
186 const sym: aout.Sym = .{
187 .value = undefined, // the value of stuff gets filled in in flushModule
188 .type = decl.link.plan9.type,
189 .name = mem.span(decl.name),
190 };
191
192 if (decl.link.plan9.sym_index) |s| {
193 self.syms.items[s] = sym;
194 } else {
195 try self.syms.append(self.base.allocator, sym);
196 decl.link.plan9.sym_index = self.syms.items.len - 1;
197 }
126198}
127199
128200pub fn flush(self: *Plan9, comp: *Compilation) !void {
......@@ -138,6 +210,10 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
138210}
139211
140212pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
213 if (build_options.skip_non_native and builtin.object_format != .plan9) {
214 @panic("Attempted to compile for object format that was disabled by build configuration");
215 }
216
141217 _ = comp;
142218 const tracy = trace(@src());
143219 defer tracy.end();
......@@ -146,160 +222,147 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
146222
147223 defer assert(self.hdr.entry != 0x0);
148224
149 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
225 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
150226
151 self.text_buf.items.len = 0;
152 self.data_buf.items.len = 0;
153 // ensure space to write the got later
154 assert(self.got.items.len == self.decl_table.count());
155 try self.data_buf.appendNTimes(self.base.allocator, 0x69, self.got.items.len * if (!self.sixtyfour_bit) @as(u32, 4) else 8);
156 // temporary buffer
157 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
158 defer code_buffer.deinit();
159 {
160 for (self.decl_table.keys()) |decl| {
161 if (!decl.has_tv) continue;
162 const is_fn = (decl.ty.zigTypeTag() == .Fn);
163
164 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
165 decl.link.plan9 = if (is_fn) .{
166 .offset = self.getAddr(self.text_buf.items.len, .t),
167 .type = .t,
168 .sym_index = decl.link.plan9.sym_index,
169 .got_index = decl.link.plan9.got_index,
170 } else .{
171 .offset = self.getAddr(self.data_buf.items.len, .d),
172 .type = .d,
173 .sym_index = decl.link.plan9.sym_index,
174 .got_index = decl.link.plan9.got_index,
175 };
176 self.got.items[decl.link.plan9.got_index.?] = decl.link.plan9.offset.?;
177 if (decl.link.plan9.sym_index) |s| {
178 self.syms.items[s] = .{
179 .value = decl.link.plan9.offset.?,
180 .type = decl.link.plan9.type,
181 .name = mem.span(decl.name),
182 };
183 } else {
184 try self.syms.append(self.base.allocator, .{
185 .value = decl.link.plan9.offset.?,
186 .type = decl.link.plan9.type,
187 .name = mem.span(decl.name),
188 });
189 decl.link.plan9.sym_index = self.syms.items.len - 1;
190 }
227 assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count());
228 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
229 var got_table = try self.base.allocator.alloc(u8, got_size);
230 defer self.base.allocator.free(got_table);
191231
192 if (module.decl_exports.get(decl)) |exports| {
193 for (exports) |exp| {
194 // plan9 does not support custom sections
195 if (exp.options.section) |section_name| {
196 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
197 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));
198 break;
199 }
200 }
201 if (std.mem.eql(u8, exp.options.name, "_start")) {
202 std.debug.assert(decl.link.plan9.type == .t); // we tried to link a non-function as the entry
203 self.entry_decl = decl;
204 }
205 if (exp.link.plan9) |i| {
206 self.syms.items[i] = .{
207 .value = decl.link.plan9.offset.?,
208 .type = decl.link.plan9.type.toGlobal(),
209 .name = exp.options.name,
210 };
211 } else {
212 try self.syms.append(self.base.allocator, .{
213 .value = decl.link.plan9.offset.?,
214 .type = decl.link.plan9.type.toGlobal(),
215 .name = exp.options.name,
216 });
217 exp.link.plan9 = self.syms.items.len - 1;
218 }
219 }
220 }
232 // + 2 for header, got, symbols
233 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);
234 defer self.base.allocator.free(iovecs);
221235
222 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
223 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
224 .ty = decl.ty,
225 .val = decl.val,
226 }, &code_buffer, .{ .none = {} });
227 const code = switch (res) {
228 .externally_managed => |x| x,
229 .appended => code_buffer.items,
230 .fail => |em| {
231 decl.analysis = .codegen_failure;
232 try module.failed_decls.put(module.gpa, decl, em);
233 // TODO try to do more decls
234 return;
235 },
236 };
237 if (is_fn) {
238 try self.text_buf.appendSlice(self.base.allocator, code);
239 code_buffer.items.len = 0;
236 const file = self.base.file.?;
237
238 var hdr_buf: [40]u8 = undefined;
239 // account for the fat header
240 const hdr_size = if (self.sixtyfour_bit) @as(usize, 40) else 32;
241 const hdr_slice: []u8 = hdr_buf[0..hdr_size];
242 var foff = hdr_size;
243 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
244 var iovecs_i: u64 = 1;
245 var text_i: u64 = 0;
246 // text
247 {
248 var it = self.fn_decl_table.iterator();
249 while (it.next()) |entry| {
250 const decl = entry.key_ptr.*;
251 const code = entry.value_ptr.*;
252 log.debug("write text decl {*} ({s})", .{ decl, decl.name });
253 foff += code.len;
254 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
255 iovecs_i += 1;
256 const off = self.getAddr(text_i, .t);
257 text_i += code.len;
258 decl.link.plan9.offset = off;
259 if (!self.sixtyfour_bit) {
260 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
261 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
240262 } else {
241 try self.data_buf.appendSlice(self.base.allocator, code);
242 code_buffer.items.len = 0;
263 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
264 }
265 self.syms.items[decl.link.plan9.sym_index.?].value = off;
266 if (mod.decl_exports.get(decl)) |exports| {
267 try self.addDeclExports(mod, decl, exports);
243268 }
244269 }
270 // etext symbol
271 self.syms.items[2].value = self.getAddr(text_i, .t);
245272 }
246
247 // write the got
248 if (!self.sixtyfour_bit) {
249 for (self.got.items) |p, i| {
250 mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian());
251 }
252 } else {
253 for (self.got.items) |p, i| {
254 mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian());
273 // global offset table is in data
274 iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };
275 iovecs_i += 1;
276 // data
277 var data_i: u64 = got_size;
278 {
279 var it = self.data_decl_table.iterator();
280 while (it.next()) |entry| {
281 const decl = entry.key_ptr.*;
282 const code = entry.value_ptr.*;
283 log.debug("write data decl {*} ({s})", .{ decl, decl.name });
284
285 foff += code.len;
286 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
287 iovecs_i += 1;
288 const off = self.getAddr(data_i, .d);
289 data_i += code.len;
290 decl.link.plan9.offset = off;
291 if (!self.sixtyfour_bit) {
292 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
293 } else {
294 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
295 }
296 self.syms.items[decl.link.plan9.sym_index.?].value = off;
297 if (mod.decl_exports.get(decl)) |exports| {
298 try self.addDeclExports(mod, decl, exports);
299 }
255300 }
301 // edata symbol
302 self.syms.items[0].value = self.getAddr(data_i, .b);
256303 }
257
258 self.hdr.entry = @truncate(u32, self.entry_decl.?.link.plan9.offset.?);
259
260 // edata, end, etext
261 self.syms.items[0].value = self.getAddr(0x0, .b);
304 // edata
262305 self.syms.items[1].value = self.getAddr(0x0, .b);
263 self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t);
264
265306 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
266307 defer sym_buf.deinit();
267308 try self.writeSyms(&sym_buf);
268
309 assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls
310 iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };
311 iovecs_i += 1;
269312 // generate the header
270313 self.hdr = .{
271314 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
272 .text = @intCast(u32, self.text_buf.items.len),
273 .data = @intCast(u32, self.data_buf.items.len),
315 .text = @intCast(u32, text_i),
316 .data = @intCast(u32, data_i),
274317 .syms = @intCast(u32, sym_buf.items.len),
275318 .bss = 0,
276319 .pcsz = 0,
277320 .spsz = 0,
278 .entry = self.hdr.entry,
321 .entry = @intCast(u32, self.entry_val.?),
279322 };
280
281 const file = self.base.file.?;
282
283 var hdr_buf = self.hdr.toU8s();
284 const hdr_slice: []const u8 = &hdr_buf;
285 // account for the fat header
286 const hdr_size: u8 = if (!self.sixtyfour_bit) 32 else 40;
323 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
287324 // write the fat header for 64 bit entry points
288325 if (self.sixtyfour_bit) {
289 mem.writeIntSliceBig(u64, hdr_buf[32..40], self.hdr.entry);
326 mem.writeIntSliceBig(u64, hdr_buf[32..40], self.entry_val.?);
290327 }
291328 // write it all!
292 var vectors: [4]std.os.iovec_const = .{
293 .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size },
294 .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len },
295 .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len },
296 .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len },
297 // TODO spsz, pcsz
298 };
299 try file.pwritevAll(&vectors, 0);
329 try file.pwritevAll(iovecs, 0);
330}
331fn addDeclExports(
332 self: *Plan9,
333 module: *Module,
334 decl: *Module.Decl,
335 exports: []const *Module.Export,
336) !void {
337 for (exports) |exp| {
338 // plan9 does not support custom sections
339 if (exp.options.section) |section_name| {
340 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
341 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));
342 break;
343 }
344 }
345 const sym = .{
346 .value = decl.link.plan9.offset.?,
347 .type = decl.link.plan9.type.toGlobal(),
348 .name = exp.options.name,
349 };
350
351 if (exp.link.plan9) |i| {
352 self.syms.items[i] = sym;
353 } else {
354 try self.syms.append(self.base.allocator, sym);
355 exp.link.plan9 = self.syms.items.len - 1;
356 }
357 }
300358}
359
301360pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
302 assert(self.decl_table.swapRemove(decl));
361 const is_fn = (decl.ty.zigTypeTag() == .Fn);
362 if (is_fn)
363 assert(self.fn_decl_table.swapRemove(decl))
364 else
365 assert(self.data_decl_table.swapRemove(decl));
303366}
304367
305368pub fn updateDeclExports(
......@@ -315,11 +378,17 @@ pub fn updateDeclExports(
315378 _ = exports;
316379}
317380pub fn deinit(self: *Plan9) void {
318 self.decl_table.deinit(self.base.allocator);
381 var itf = self.fn_decl_table.iterator();
382 while (itf.next()) |entry| {
383 self.base.allocator.free(entry.value_ptr.*);
384 }
385 self.fn_decl_table.deinit(self.base.allocator);
386 var itd = self.data_decl_table.iterator();
387 while (itd.next()) |entry| {
388 self.base.allocator.free(entry.value_ptr.*);
389 }
390 self.data_decl_table.deinit(self.base.allocator);
319391 self.syms.deinit(self.base.allocator);
320 self.text_buf.deinit(self.base.allocator);
321 self.data_buf.deinit(self.base.allocator);
322 self.got.deinit(self.base.allocator);
323392}
324393
325394pub const Export = ?usize;
......@@ -366,18 +435,24 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
366435pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
367436 const writer = buf.writer();
368437 for (self.syms.items) |sym| {
438 log.debug("sym.name: {s}", .{sym.name});
439 log.debug("sym.value: {x}", .{sym.value});
440 if (mem.eql(u8, sym.name, "_start"))
441 self.entry_val = sym.value;
369442 if (!self.sixtyfour_bit) {
370443 try writer.writeIntBig(u32, @intCast(u32, sym.value));
371444 } else {
372445 try writer.writeIntBig(u64, sym.value);
373446 }
374447 try writer.writeByte(@enumToInt(sym.type));
375 try writer.writeAll(std.mem.span(sym.name));
448 try writer.writeAll(sym.name);
376449 try writer.writeByte(0);
377450 }
378451}
379452
380453pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
381 try self.got.append(self.base.allocator, 0xdeadbeef);
382 decl.link.plan9.got_index = self.got.items.len - 1;
454 if (decl.link.plan9.got_index == null) {
455 self.got_len += 1;
456 decl.link.plan9.got_index = self.got_len - 1;
457 }
383458}
src/link/SpirV.zig+35-3
......@@ -36,6 +36,8 @@ const ResultId = codegen.ResultId;
3636const trace = @import("../tracy.zig").trace;
3737const build_options = @import("build_options");
3838const spec = @import("../codegen/spirv/spec.zig");
39const Air = @import("../Air.zig");
40const Liveness = @import("../Liveness.zig");
3941
4042// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?
4143pub const FnData = struct {
......@@ -49,7 +51,12 @@ base: link.File,
4951/// This linker backend does not try to incrementally link output SPIR-V code.
5052/// Instead, it tracks all declarations in this table, and iterates over it
5153/// in the flush function.
52decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
54decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{},
55
56const DeclGenContext = struct {
57 air: Air,
58 liveness: Liveness,
59};
5360
5461pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {
5562 const spirv = try gpa.create(SpirV);
......@@ -101,7 +108,23 @@ pub fn deinit(self: *SpirV) void {
101108 self.decl_table.deinit(self.base.allocator);
102109}
103110
111pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
112 if (build_options.skip_non_native) {
113 @panic("Attempted to compile for architecture that was disabled by build configuration");
114 }
115 _ = module;
116 // Keep track of all decls so we can iterate over them on flush().
117 _ = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl);
118
119 _ = air;
120 _ = liveness;
121 @panic("TODO SPIR-V needs to keep track of Air and Liveness so it can use them later");
122}
123
104124pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
125 if (build_options.skip_non_native) {
126 @panic("Attempted to compile for architecture that was disabled by build configuration");
127 }
105128 _ = module;
106129 // Keep track of all decls so we can iterate over them on flush().
107130 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
......@@ -132,6 +155,10 @@ pub fn flush(self: *SpirV, comp: *Compilation) !void {
132155}
133156
134157pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
158 if (build_options.skip_non_native) {
159 @panic("Attempted to compile for architecture that was disabled by build configuration");
160 }
161
135162 const tracy = trace(@src());
136163 defer tracy.end();
137164
......@@ -159,10 +186,15 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
159186 var decl_gen = codegen.DeclGen.init(&spv);
160187 defer decl_gen.deinit();
161188
162 for (self.decl_table.keys()) |decl| {
189 var it = self.decl_table.iterator();
190 while (it.next()) |entry| {
191 const decl = entry.key_ptr.*;
163192 if (!decl.has_tv) continue;
164193
165 if (try decl_gen.gen(decl)) |msg| {
194 const air = entry.value_ptr.air;
195 const liveness = entry.value_ptr.liveness;
196
197 if (try decl_gen.gen(decl, air, liveness)) |msg| {
166198 try module.failed_decls.put(module.gpa, decl, msg);
167199 return; // TODO: Attempt to generate more decls?
168200 }
src/link/Wasm.zig+76-8
......@@ -1,6 +1,7 @@
11const Wasm = @This();
22
33const std = @import("std");
4const builtin = @import("builtin");
45const mem = std.mem;
56const Allocator = std.mem.Allocator;
67const assert = std.debug.assert;
......@@ -18,10 +19,15 @@ const build_options = @import("build_options");
1819const wasi_libc = @import("../wasi_libc.zig");
1920const Cache = @import("../Cache.zig");
2021const TypedValue = @import("../TypedValue.zig");
22const llvm_backend = @import("../codegen/llvm.zig");
23const Air = @import("../Air.zig");
24const Liveness = @import("../Liveness.zig");
2125
2226pub const base_tag = link.File.Tag.wasm;
2327
2428base: link.File,
29/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
30llvm_object: ?*llvm_backend.Object = null,
2531/// List of all function Decls to be written to the output file. The index of
2632/// each Decl in this list at the time of writing the binary is used as the
2733/// function index. In the event where ext_funcs' size is not 0, the index of
......@@ -111,8 +117,13 @@ pub const DeclBlock = struct {
111117pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
112118 assert(options.object_format == .wasm);
113119
114 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
115 if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
120 if (build_options.have_llvm and options.use_llvm) {
121 const self = try createEmpty(allocator, options);
122 errdefer self.base.destroy();
123
124 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
125 return self;
126 }
116127
117128 // TODO: read the file and keep valid parts instead of truncating
118129 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
......@@ -186,11 +197,60 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
186197 }
187198}
188199
200pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
201 if (build_options.skip_non_native and builtin.object_format != .wasm) {
202 @panic("Attempted to compile for object format that was disabled by build configuration");
203 }
204 if (build_options.have_llvm) {
205 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
206 }
207 const decl = func.owner_decl;
208 assert(decl.link.wasm.init); // Must call allocateDeclIndexes()
209
210 const fn_data = &decl.fn_link.wasm;
211 fn_data.functype.items.len = 0;
212 fn_data.code.items.len = 0;
213 fn_data.idx_refs.items.len = 0;
214
215 var context = codegen.Context{
216 .gpa = self.base.allocator,
217 .air = air,
218 .liveness = liveness,
219 .values = .{},
220 .code = fn_data.code.toManaged(self.base.allocator),
221 .func_type_data = fn_data.functype.toManaged(self.base.allocator),
222 .decl = decl,
223 .err_msg = undefined,
224 .locals = .{},
225 .target = self.base.options.target,
226 .global_error_set = self.base.options.module.?.global_error_set,
227 };
228 defer context.deinit();
229
230 // generate the 'code' section for the function declaration
231 const result = context.genFunc() catch |err| switch (err) {
232 error.CodegenFail => {
233 decl.analysis = .codegen_failure;
234 try module.failed_decls.put(module.gpa, decl, context.err_msg);
235 return;
236 },
237 else => |e| return e,
238 };
239 return self.finishUpdateDecl(decl, result, &context);
240}
241
189242// Generate code for the Decl, storing it in memory to be later written to
190243// the file on flush().
191244pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
192 std.debug.assert(decl.link.wasm.init); // Must call allocateDeclIndexes()
245 if (build_options.skip_non_native and builtin.object_format != .wasm) {
246 @panic("Attempted to compile for object format that was disabled by build configuration");
247 }
248 if (build_options.have_llvm) {
249 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
250 }
251 assert(decl.link.wasm.init); // Must call allocateDeclIndexes()
193252
253 // TODO don't use this for non-functions
194254 const fn_data = &decl.fn_link.wasm;
195255 fn_data.functype.items.len = 0;
196256 fn_data.code.items.len = 0;
......@@ -198,6 +258,8 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
198258
199259 var context = codegen.Context{
200260 .gpa = self.base.allocator,
261 .air = undefined,
262 .liveness = undefined,
201263 .values = .{},
202264 .code = fn_data.code.toManaged(self.base.allocator),
203265 .func_type_data = fn_data.functype.toManaged(self.base.allocator),
......@@ -219,14 +281,20 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
219281 else => |e| return e,
220282 };
221283
222 const code: []const u8 = switch (result) {
223 .appended => @as([]const u8, context.code.items),
224 .externally_managed => |payload| payload,
225 };
284 return self.finishUpdateDecl(decl, result, &context);
285}
286
287fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: codegen.Result, context: *codegen.Context) !void {
288 const fn_data: *FnData = &decl.fn_link.wasm;
226289
227290 fn_data.code = context.code.toUnmanaged();
228291 fn_data.functype = context.func_type_data.toUnmanaged();
229292
293 const code: []const u8 = switch (result) {
294 .appended => @as([]const u8, fn_data.code.items),
295 .externally_managed => |payload| payload,
296 };
297
230298 const block = &decl.link.wasm;
231299 if (decl.ty.zigTypeTag() == .Fn) {
232300 // as locals are patched afterwards, the offsets of funcidx's are off,
......@@ -521,7 +589,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
521589 var data_offset = offset_table_size;
522590 while (cur) |cur_block| : (cur = cur_block.next) {
523591 if (cur_block.size == 0) continue;
524 std.debug.assert(cur_block.init);
592 assert(cur_block.init);
525593
526594 const offset = (cur_block.offset_index) * ptr_width;
527595 var buf: [4]u8 = undefined;
src/liveness.zig deleted-254
......@@ -1,254 +0,0 @@
1const std = @import("std");
2const ir = @import("air.zig");
3const trace = @import("tracy.zig").trace;
4const log = std.log.scoped(.liveness);
5const assert = std.debug.assert;
6
7/// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated.
8pub fn analyze(
9 /// Used for temporary storage during the analysis.
10 gpa: *std.mem.Allocator,
11 /// Used to tack on extra allocations in the same lifetime as the existing instructions.
12 arena: *std.mem.Allocator,
13 body: ir.Body,
14) error{OutOfMemory}!void {
15 const tracy = trace(@src());
16 defer tracy.end();
17
18 var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
19 defer table.deinit();
20 try table.ensureCapacity(@intCast(u32, body.instructions.len));
21 try analyzeWithTable(arena, &table, null, body);
22}
23
24fn analyzeWithTable(
25 arena: *std.mem.Allocator,
26 table: *std.AutoHashMap(*ir.Inst, void),
27 new_set: ?*std.AutoHashMap(*ir.Inst, void),
28 body: ir.Body,
29) error{OutOfMemory}!void {
30 var i: usize = body.instructions.len;
31
32 if (new_set) |ns| {
33 // We are only interested in doing this for instructions which are born
34 // before a conditional branch, so after obtaining the new set for
35 // each branch we prune the instructions which were born within.
36 while (i != 0) {
37 i -= 1;
38 const base = body.instructions[i];
39 _ = ns.remove(base);
40 try analyzeInst(arena, table, new_set, base);
41 }
42 } else {
43 while (i != 0) {
44 i -= 1;
45 const base = body.instructions[i];
46 try analyzeInst(arena, table, new_set, base);
47 }
48 }
49}
50
51fn analyzeInst(
52 arena: *std.mem.Allocator,
53 table: *std.AutoHashMap(*ir.Inst, void),
54 new_set: ?*std.AutoHashMap(*ir.Inst, void),
55 base: *ir.Inst,
56) error{OutOfMemory}!void {
57 if (table.contains(base)) {
58 base.deaths = 0;
59 } else {
60 // No tombstone for this instruction means it is never referenced,
61 // and its birth marks its own death. Very metal 🤘
62 base.deaths = 1 << ir.Inst.unreferenced_bit_index;
63 }
64
65 switch (base.tag) {
66 .constant => return,
67 .block => {
68 const inst = base.castTag(.block).?;
69 try analyzeWithTable(arena, table, new_set, inst.body);
70 // We let this continue so that it can possibly mark the block as
71 // unreferenced below.
72 },
73 .loop => {
74 const inst = base.castTag(.loop).?;
75 try analyzeWithTable(arena, table, new_set, inst.body);
76 return; // Loop has no operands and it is always unreferenced.
77 },
78 .condbr => {
79 const inst = base.castTag(.condbr).?;
80
81 // Each death that occurs inside one branch, but not the other, needs
82 // to be added as a death immediately upon entering the other branch.
83
84 var then_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
85 defer then_table.deinit();
86 try analyzeWithTable(arena, table, &then_table, inst.then_body);
87
88 // Reset the table back to its state from before the branch.
89 {
90 var it = then_table.keyIterator();
91 while (it.next()) |key| {
92 assert(table.remove(key.*));
93 }
94 }
95
96 var else_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
97 defer else_table.deinit();
98 try analyzeWithTable(arena, table, &else_table, inst.else_body);
99
100 var then_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
101 defer then_entry_deaths.deinit();
102 var else_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
103 defer else_entry_deaths.deinit();
104
105 {
106 var it = else_table.keyIterator();
107 while (it.next()) |key| {
108 const else_death = key.*;
109 if (!then_table.contains(else_death)) {
110 try then_entry_deaths.append(else_death);
111 }
112 }
113 }
114 // This loop is the same, except it's for the then branch, and it additionally
115 // has to put its items back into the table to undo the reset.
116 {
117 var it = then_table.keyIterator();
118 while (it.next()) |key| {
119 const then_death = key.*;
120 if (!else_table.contains(then_death)) {
121 try else_entry_deaths.append(then_death);
122 }
123 try table.put(then_death, {});
124 }
125 }
126 // Now we have to correctly populate new_set.
127 if (new_set) |ns| {
128 try ns.ensureCapacity(@intCast(u32, ns.count() + then_table.count() + else_table.count()));
129 var it = then_table.keyIterator();
130 while (it.next()) |key| {
131 _ = ns.putAssumeCapacity(key.*, {});
132 }
133 it = else_table.keyIterator();
134 while (it.next()) |key| {
135 _ = ns.putAssumeCapacity(key.*, {});
136 }
137 }
138 inst.then_death_count = std.math.cast(@TypeOf(inst.then_death_count), then_entry_deaths.items.len) catch return error.OutOfMemory;
139 inst.else_death_count = std.math.cast(@TypeOf(inst.else_death_count), else_entry_deaths.items.len) catch return error.OutOfMemory;
140 const allocated_slice = try arena.alloc(*ir.Inst, then_entry_deaths.items.len + else_entry_deaths.items.len);
141 inst.deaths = allocated_slice.ptr;
142 std.mem.copy(*ir.Inst, inst.thenDeaths(), then_entry_deaths.items);
143 std.mem.copy(*ir.Inst, inst.elseDeaths(), else_entry_deaths.items);
144
145 // Continue on with the instruction analysis. The following code will find the condition
146 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
147 // condition's lifetime ends immediately before entering any branch.
148 },
149 .switchbr => {
150 const inst = base.castTag(.switchbr).?;
151
152 const Table = std.AutoHashMap(*ir.Inst, void);
153 const case_tables = try table.allocator.alloc(Table, inst.cases.len + 1); // +1 for else
154 defer table.allocator.free(case_tables);
155
156 std.mem.set(Table, case_tables, Table.init(table.allocator));
157 defer for (case_tables) |*ct| ct.deinit();
158
159 for (inst.cases) |case, i| {
160 try analyzeWithTable(arena, table, &case_tables[i], case.body);
161
162 // Reset the table back to its state from before the case.
163 var it = case_tables[i].keyIterator();
164 while (it.next()) |key| {
165 assert(table.remove(key.*));
166 }
167 }
168 { // else
169 try analyzeWithTable(arena, table, &case_tables[case_tables.len - 1], inst.else_body);
170
171 // Reset the table back to its state from before the case.
172 var it = case_tables[case_tables.len - 1].keyIterator();
173 while (it.next()) |key| {
174 assert(table.remove(key.*));
175 }
176 }
177
178 const List = std.ArrayList(*ir.Inst);
179 const case_deaths = try table.allocator.alloc(List, case_tables.len); // +1 for else
180 defer table.allocator.free(case_deaths);
181
182 std.mem.set(List, case_deaths, List.init(table.allocator));
183 defer for (case_deaths) |*cd| cd.deinit();
184
185 var total_deaths: u32 = 0;
186 for (case_tables) |*ct, i| {
187 total_deaths += ct.count();
188 var it = ct.keyIterator();
189 while (it.next()) |key| {
190 const case_death = key.*;
191 for (case_tables) |*ct_inner, j| {
192 if (i == j) continue;
193 if (!ct_inner.contains(case_death)) {
194 // instruction is not referenced in this case
195 try case_deaths[j].append(case_death);
196 }
197 }
198 // undo resetting the table
199 try table.put(case_death, {});
200 }
201 }
202
203 // Now we have to correctly populate new_set.
204 if (new_set) |ns| {
205 try ns.ensureCapacity(@intCast(u32, ns.count() + total_deaths));
206 for (case_tables) |*ct| {
207 var it = ct.keyIterator();
208 while (it.next()) |key| {
209 _ = ns.putAssumeCapacity(key.*, {});
210 }
211 }
212 }
213
214 total_deaths = 0;
215 for (case_deaths[0 .. case_deaths.len - 1]) |*ct, i| {
216 inst.cases[i].index = total_deaths;
217 const len = std.math.cast(@TypeOf(inst.else_deaths), ct.items.len) catch return error.OutOfMemory;
218 inst.cases[i].deaths = len;
219 total_deaths += len;
220 }
221 { // else
222 const else_deaths = std.math.cast(@TypeOf(inst.else_deaths), case_deaths[case_deaths.len - 1].items.len) catch return error.OutOfMemory;
223 inst.else_index = total_deaths;
224 inst.else_deaths = else_deaths;
225 total_deaths += else_deaths;
226 }
227
228 const allocated_slice = try arena.alloc(*ir.Inst, total_deaths);
229 inst.deaths = allocated_slice.ptr;
230 for (case_deaths[0 .. case_deaths.len - 1]) |*cd, i| {
231 std.mem.copy(*ir.Inst, inst.caseDeaths(i), cd.items);
232 }
233 std.mem.copy(*ir.Inst, inst.elseDeaths(), case_deaths[case_deaths.len - 1].items);
234 },
235 else => {},
236 }
237
238 const needed_bits = base.operandCount();
239 if (needed_bits <= ir.Inst.deaths_bits) {
240 var bit_i: ir.Inst.DeathsBitIndex = 0;
241 while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
242 const prev = try table.fetchPut(operand, {});
243 if (prev == null) {
244 // Death.
245 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
246 if (new_set) |ns| try ns.putNoClobber(operand, {});
247 }
248 }
249 } else {
250 @panic("Handle liveness analysis for instructions with many parameters");
251 }
252
253 log.debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
254}
src/main.zig+1
......@@ -365,6 +365,7 @@ const usage_build_generic =
365365 \\ coff Common Object File Format (Windows)
366366 \\ macho macOS relocatables
367367 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
368 \\ plan9 Plan 9 from Bell Labs object format
368369 \\ hex (planned) Intel IHEX
369370 \\ raw (planned) Dump machine code directly
370371 \\ -dirafter [dir] Add directory to AFTER include search path
src/print_air.zig created+413
......@@ -0,0 +1,413 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
4
5const Module = @import("Module.zig");
6const Value = @import("value.zig").Value;
7const Zir = @import("Zir.zig");
8const Air = @import("Air.zig");
9const Liveness = @import("Liveness.zig");
10
11pub fn dump(gpa: *Allocator, air: Air, zir: Zir, liveness: Liveness) void {
12 const instruction_bytes = air.instructions.len *
13 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
14 // the debug safety tag but we want to measure release size.
15 (@sizeOf(Air.Inst.Tag) + 8);
16 const extra_bytes = air.extra.len * @sizeOf(u32);
17 const values_bytes = air.values.len * @sizeOf(Value);
18 const variables_bytes = air.variables.len * @sizeOf(*Module.Var);
19 const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize);
20 const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32);
21 const liveness_special_bytes = liveness.special.count() * 8;
22 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
23 values_bytes * variables_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
24 liveness_special_bytes + tomb_bytes;
25
26 // zig fmt: off
27 std.debug.print(
28 \\# Total AIR+Liveness bytes: {}
29 \\# AIR Instructions: {d} ({})
30 \\# AIR Extra Data: {d} ({})
31 \\# AIR Values Bytes: {d} ({})
32 \\# AIR Variables Bytes: {d} ({})
33 \\# Liveness tomb_bits: {}
34 \\# Liveness Extra Data: {d} ({})
35 \\# Liveness special table: {d} ({})
36 \\
37 , .{
38 fmtIntSizeBin(total_bytes),
39 air.instructions.len, fmtIntSizeBin(instruction_bytes),
40 air.extra.len, fmtIntSizeBin(extra_bytes),
41 air.values.len, fmtIntSizeBin(values_bytes),
42 air.variables.len, fmtIntSizeBin(variables_bytes),
43 fmtIntSizeBin(tomb_bytes),
44 liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),
45 liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
46 });
47 // zig fmt: on
48 var arena = std.heap.ArenaAllocator.init(gpa);
49 defer arena.deinit();
50
51 var writer: Writer = .{
52 .gpa = gpa,
53 .arena = &arena.allocator,
54 .air = air,
55 .zir = zir,
56 .liveness = liveness,
57 .indent = 2,
58 };
59 const stream = std.io.getStdErr().writer();
60 writer.writeAllConstants(stream) catch return;
61 stream.writeByte('\n') catch return;
62 writer.writeBody(stream, air.getMainBody()) catch return;
63}
64
65const Writer = struct {
66 gpa: *Allocator,
67 arena: *Allocator,
68 air: Air,
69 zir: Zir,
70 liveness: Liveness,
71 indent: usize,
72
73 fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void {
74 for (w.air.instructions.items(.tag)) |tag, i| {
75 const inst = @intCast(u32, i);
76 switch (tag) {
77 .constant, .const_ty => {
78 try s.writeByteNTimes(' ', w.indent);
79 try s.print("%{d} ", .{inst});
80 try w.writeInst(s, inst);
81 try s.writeAll(")\n");
82 },
83 else => continue,
84 }
85 }
86 }
87
88 fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void {
89 for (body) |inst| {
90 try s.writeByteNTimes(' ', w.indent);
91 if (w.liveness.isUnused(inst)) {
92 try s.print("%{d}!", .{inst});
93 } else {
94 try s.print("%{d} ", .{inst});
95 }
96 try w.writeInst(s, inst);
97 try s.writeAll(")\n");
98 }
99 }
100
101 fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
102 const tags = w.air.instructions.items(.tag);
103 const tag = tags[inst];
104 try s.print("= {s}(", .{@tagName(tags[inst])});
105 switch (tag) {
106 .arg => try w.writeTyStr(s, inst),
107
108 .add,
109 .addwrap,
110 .sub,
111 .subwrap,
112 .mul,
113 .mulwrap,
114 .div,
115 .bit_and,
116 .bit_or,
117 .xor,
118 .cmp_lt,
119 .cmp_lte,
120 .cmp_eq,
121 .cmp_gte,
122 .cmp_gt,
123 .cmp_neq,
124 .bool_and,
125 .bool_or,
126 .store,
127 => try w.writeBinOp(s, inst),
128
129 .is_null,
130 .is_non_null,
131 .is_null_ptr,
132 .is_non_null_ptr,
133 .is_err,
134 .is_non_err,
135 .is_err_ptr,
136 .is_non_err_ptr,
137 .ptrtoint,
138 .ret,
139 => try w.writeUnOp(s, inst),
140
141 .breakpoint,
142 .unreach,
143 => try w.writeNoOp(s, inst),
144
145 .const_ty,
146 .alloc,
147 => try w.writeTy(s, inst),
148
149 .not,
150 .bitcast,
151 .load,
152 .ref,
153 .floatcast,
154 .intcast,
155 .optional_payload,
156 .optional_payload_ptr,
157 .wrap_optional,
158 .unwrap_errunion_payload,
159 .unwrap_errunion_err,
160 .unwrap_errunion_payload_ptr,
161 .unwrap_errunion_err_ptr,
162 .wrap_errunion_payload,
163 .wrap_errunion_err,
164 => try w.writeTyOp(s, inst),
165
166 .block,
167 .loop,
168 => try w.writeBlock(s, inst),
169
170 .struct_field_ptr => try w.writeStructFieldPtr(s, inst),
171 .varptr => try w.writeVarPtr(s, inst),
172 .constant => try w.writeConstant(s, inst),
173 .assembly => try w.writeAssembly(s, inst),
174 .dbg_stmt => try w.writeDbgStmt(s, inst),
175 .call => try w.writeCall(s, inst),
176 .br => try w.writeBr(s, inst),
177 .cond_br => try w.writeCondBr(s, inst),
178 .switch_br => try w.writeSwitchBr(s, inst),
179 }
180 }
181
182 fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
183 const ty_str = w.air.instructions.items(.data)[inst].ty_str;
184 const name = w.zir.nullTerminatedString(ty_str.str);
185 try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), ty_str.ty });
186 }
187
188 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
189 const bin_op = w.air.instructions.items(.data)[inst].bin_op;
190 try w.writeOperand(s, inst, 0, bin_op.lhs);
191 try s.writeAll(", ");
192 try w.writeOperand(s, inst, 1, bin_op.rhs);
193 }
194
195 fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
196 const un_op = w.air.instructions.items(.data)[inst].un_op;
197 try w.writeOperand(s, inst, 0, un_op);
198 }
199
200 fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
201 _ = w;
202 _ = inst;
203 _ = s;
204 // no-op, no argument to write
205 }
206
207 fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
208 const ty = w.air.instructions.items(.data)[inst].ty;
209 try s.print("{}", .{ty});
210 }
211
212 fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
213 const ty_op = w.air.instructions.items(.data)[inst].ty_op;
214 try s.print("{}, ", .{w.air.getRefType(ty_op.ty)});
215 try w.writeOperand(s, inst, 0, ty_op.operand);
216 }
217
218 fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
219 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
220 const extra = w.air.extraData(Air.Block, ty_pl.payload);
221 const body = w.air.extra[extra.end..][0..extra.data.body_len];
222
223 try s.writeAll("{\n");
224 const old_indent = w.indent;
225 w.indent += 2;
226 try w.writeBody(s, body);
227 w.indent = old_indent;
228 try s.writeByteNTimes(' ', w.indent);
229 try s.writeAll("}");
230 }
231
232 fn writeStructFieldPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
233 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
234 const extra = w.air.extraData(Air.StructField, ty_pl.payload);
235
236 try w.writeOperand(s, inst, 0, extra.data.struct_ptr);
237 try s.print(", {d}", .{extra.data.field_index});
238 }
239
240 fn writeVarPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
241 _ = w;
242 _ = inst;
243 try s.writeAll("TODO");
244 }
245
246 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
247 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
248 const val = w.air.values[ty_pl.payload];
249 try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val });
250 }
251
252 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
253 _ = w;
254 _ = inst;
255 try s.writeAll("TODO");
256 }
257
258 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
259 const dbg_stmt = w.air.instructions.items(.data)[inst].dbg_stmt;
260 try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
261 }
262
263 fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
264 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
265 const extra = w.air.extraData(Air.Call, pl_op.payload);
266 const args = @bitCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]);
267 try w.writeOperand(s, inst, 0, pl_op.operand);
268 try s.writeAll(", [");
269 for (args) |arg, i| {
270 if (i != 0) try s.writeAll(", ");
271 try w.writeOperand(s, inst, 1 + i, arg);
272 }
273 try s.writeAll("]");
274 }
275
276 fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
277 const br = w.air.instructions.items(.data)[inst].br;
278 try w.writeInstIndex(s, br.block_inst, false);
279 try s.writeAll(", ");
280 try w.writeOperand(s, inst, 0, br.operand);
281 }
282
283 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
284 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
285 const extra = w.air.extraData(Air.CondBr, pl_op.payload);
286 const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];
287 const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
288 const liveness_condbr = w.liveness.getCondBr(inst);
289
290 try w.writeOperand(s, inst, 0, pl_op.operand);
291 try s.writeAll(", {\n");
292 const old_indent = w.indent;
293 w.indent += 2;
294
295 if (liveness_condbr.then_deaths.len != 0) {
296 try s.writeByteNTimes(' ', w.indent);
297 for (liveness_condbr.then_deaths) |operand, i| {
298 if (i != 0) try s.writeAll(" ");
299 try s.print("%{d}!", .{operand});
300 }
301 try s.writeAll("\n");
302 }
303
304 try w.writeBody(s, then_body);
305 try s.writeByteNTimes(' ', old_indent);
306 try s.writeAll("}, {\n");
307
308 if (liveness_condbr.else_deaths.len != 0) {
309 try s.writeByteNTimes(' ', w.indent);
310 for (liveness_condbr.else_deaths) |operand, i| {
311 if (i != 0) try s.writeAll(" ");
312 try s.print("%{d}!", .{operand});
313 }
314 try s.writeAll("\n");
315 }
316
317 try w.writeBody(s, else_body);
318 w.indent = old_indent;
319
320 try s.writeByteNTimes(' ', old_indent);
321 try s.writeAll("}");
322 }
323
324 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
325 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
326 const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload);
327 var extra_index: usize = switch_br.end;
328 var case_i: u32 = 0;
329
330 try w.writeOperand(s, inst, 0, pl_op.operand);
331 const old_indent = w.indent;
332 w.indent += 2;
333
334 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
335 const case = w.air.extraData(Air.SwitchBr.Case, extra_index);
336 const items = @bitCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]);
337 const case_body = w.air.extra[case.end + items.len ..][0..case.data.body_len];
338 extra_index = case.end + case.data.items_len + case_body.len;
339
340 try s.writeAll(", [");
341 for (items) |item, item_i| {
342 if (item_i != 0) try s.writeAll(", ");
343 try w.writeInstRef(s, item, false);
344 }
345 try s.writeAll("] => {\n");
346 w.indent += 2;
347 try w.writeBody(s, case_body);
348 w.indent -= 2;
349 try s.writeByteNTimes(' ', w.indent);
350 try s.writeAll("}");
351 }
352
353 const else_body = w.air.extra[extra_index..][0..switch_br.data.else_body_len];
354 if (else_body.len != 0) {
355 try s.writeAll(", else => {\n");
356 w.indent += 2;
357 try w.writeBody(s, else_body);
358 w.indent -= 2;
359 try s.writeByteNTimes(' ', w.indent);
360 try s.writeAll("}");
361 }
362
363 try s.writeAll("\n");
364 try s.writeByteNTimes(' ', old_indent);
365 try s.writeAll("}");
366 }
367
368 fn writeOperand(
369 w: *Writer,
370 s: anytype,
371 inst: Air.Inst.Index,
372 op_index: usize,
373 operand: Air.Inst.Ref,
374 ) @TypeOf(s).Error!void {
375 const dies = if (op_index < Liveness.bpi - 1)
376 w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index))
377 else blk: {
378 // TODO
379 break :blk false;
380 };
381 return w.writeInstRef(s, operand, dies);
382 }
383
384 fn writeInstRef(
385 w: *Writer,
386 s: anytype,
387 operand: Air.Inst.Ref,
388 dies: bool,
389 ) @TypeOf(s).Error!void {
390 var i: usize = @enumToInt(operand);
391
392 if (i < Air.Inst.Ref.typed_value_map.len) {
393 return s.print("@{}", .{operand});
394 }
395 i -= Air.Inst.Ref.typed_value_map.len;
396
397 return w.writeInstIndex(s, @intCast(Air.Inst.Index, i), dies);
398 }
399
400 fn writeInstIndex(
401 w: *Writer,
402 s: anytype,
403 inst: Air.Inst.Index,
404 dies: bool,
405 ) @TypeOf(s).Error!void {
406 _ = w;
407 if (dies) {
408 try s.print("%{d}!", .{inst});
409 } else {
410 try s.print("%{d}", .{inst});
411 }
412 }
413};
src/register_manager.zig+30-47
......@@ -3,7 +3,7 @@ const math = std.math;
33const mem = std.mem;
44const assert = std.debug.assert;
55const Allocator = std.mem.Allocator;
6const ir = @import("air.zig");
6const Air = @import("Air.zig");
77const Type = @import("type.zig").Type;
88const Module = @import("Module.zig");
99const LazySrcLoc = Module.LazySrcLoc;
......@@ -20,7 +20,7 @@ pub fn RegisterManager(
2020) type {
2121 return struct {
2222 /// The key must be canonical register.
23 registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len,
23 registers: [callee_preserved_regs.len]?Air.Inst.Index = [_]?Air.Inst.Index{null} ** callee_preserved_regs.len,
2424 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
2525 /// Tracks all registers allocated in the course of this function
2626 allocated_registers: FreeRegInt = 0,
......@@ -75,7 +75,7 @@ pub fn RegisterManager(
7575 pub fn tryAllocRegs(
7676 self: *Self,
7777 comptime count: comptime_int,
78 insts: [count]?*ir.Inst,
78 insts: [count]?Air.Inst.Index,
7979 exceptions: []const Register,
8080 ) ?[count]Register {
8181 comptime if (callee_preserved_regs.len == 0) return null;
......@@ -113,7 +113,7 @@ pub fn RegisterManager(
113113 /// Allocates a register and optionally tracks it with a
114114 /// corresponding instruction. Returns `null` if all registers
115115 /// are allocated.
116 pub fn tryAllocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) ?Register {
116 pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) ?Register {
117117 return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null;
118118 }
119119
......@@ -123,7 +123,7 @@ pub fn RegisterManager(
123123 pub fn allocRegs(
124124 self: *Self,
125125 comptime count: comptime_int,
126 insts: [count]?*ir.Inst,
126 insts: [count]?Air.Inst.Index,
127127 exceptions: []const Register,
128128 ) ![count]Register {
129129 comptime assert(count > 0 and count <= callee_preserved_regs.len);
......@@ -147,14 +147,14 @@ pub fn RegisterManager(
147147 self.markRegUsed(reg);
148148 } else {
149149 const spilled_inst = self.registers[index].?;
150 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
150 try self.getFunction().spillInstruction(reg, spilled_inst);
151151 }
152152 self.registers[index] = inst;
153153 } else {
154154 // Don't track the register
155155 if (!self.isRegFree(reg)) {
156156 const spilled_inst = self.registers[index].?;
157 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
157 try self.getFunction().spillInstruction(reg, spilled_inst);
158158 self.freeReg(reg);
159159 }
160160 }
......@@ -168,14 +168,14 @@ pub fn RegisterManager(
168168
169169 /// Allocates a register and optionally tracks it with a
170170 /// corresponding instruction.
171 pub fn allocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) !Register {
171 pub fn allocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) !Register {
172172 return (try self.allocRegs(1, .{inst}, exceptions))[0];
173173 }
174174
175175 /// Spills the register if it is currently allocated. If a
176176 /// corresponding instruction is passed, will also track this
177177 /// register.
178 pub fn getReg(self: *Self, reg: Register, inst: ?*ir.Inst) !void {
178 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) !void {
179179 const index = reg.allocIndex() orelse return;
180180
181181 if (inst) |tracked_inst|
......@@ -184,7 +184,7 @@ pub fn RegisterManager(
184184 // stack allocation.
185185 const spilled_inst = self.registers[index].?;
186186 self.registers[index] = tracked_inst;
187 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
187 try self.getFunction().spillInstruction(reg, spilled_inst);
188188 } else {
189189 self.getRegAssumeFree(reg, tracked_inst);
190190 }
......@@ -193,7 +193,7 @@ pub fn RegisterManager(
193193 // Move the instruction that was previously there to a
194194 // stack allocation.
195195 const spilled_inst = self.registers[index].?;
196 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
196 try self.getFunction().spillInstruction(reg, spilled_inst);
197197 self.freeReg(reg);
198198 }
199199 }
......@@ -202,7 +202,7 @@ pub fn RegisterManager(
202202 /// Allocates the specified register with the specified
203203 /// instruction. Asserts that the register is free and no
204204 /// spilling is necessary.
205 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
205 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void {
206206 const index = reg.allocIndex() orelse return;
207207
208208 assert(self.registers[index] == null);
......@@ -264,8 +264,7 @@ fn MockFunction(comptime Register: type) type {
264264 self.spilled.deinit(self.allocator);
265265 }
266266
267 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void {
268 _ = src;
267 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
269268 _ = inst;
270269 try self.spilled.append(self.allocator, reg);
271270 }
......@@ -297,15 +296,11 @@ test "tryAllocReg: no spilling" {
297296 };
298297 defer function.deinit();
299298
300 var mock_instruction = ir.Inst{
301 .tag = .breakpoint,
302 .ty = Type.initTag(.void),
303 .src = .unneeded,
304 };
299 const mock_instruction: Air.Inst.Index = 1;
305300
306 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{}));
307 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{}));
308 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(&mock_instruction, &.{}));
301 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, &.{}));
302 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, &.{}));
303 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, &.{}));
309304
310305 try expect(function.register_manager.isRegAllocated(.r2));
311306 try expect(function.register_manager.isRegAllocated(.r3));
......@@ -329,28 +324,24 @@ test "allocReg: spilling" {
329324 };
330325 defer function.deinit();
331326
332 var mock_instruction = ir.Inst{
333 .tag = .breakpoint,
334 .ty = Type.initTag(.void),
335 .src = .unneeded,
336 };
327 const mock_instruction: Air.Inst.Index = 1;
337328
338 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{}));
339 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{}));
329 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, &.{}));
330 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{}));
340331
341332 // Spill a register
342 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{}));
333 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, &.{}));
343334 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
344335
345336 // No spilling necessary
346337 function.register_manager.freeReg(.r3);
347 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{}));
338 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{}));
348339 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
349340
350341 // Exceptions
351342 function.register_manager.freeReg(.r2);
352343 function.register_manager.freeReg(.r3);
353 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{.r2}));
344 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{.r2}));
354345}
355346
356347test "tryAllocRegs" {
......@@ -378,16 +369,12 @@ test "allocRegs" {
378369 };
379370 defer function.deinit();
380371
381 var mock_instruction = ir.Inst{
382 .tag = .breakpoint,
383 .ty = Type.initTag(.void),
384 .src = .unneeded,
385 };
372 const mock_instruction: Air.Inst.Index = 1;
386373
387374 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{
388 &mock_instruction,
389 &mock_instruction,
390 &mock_instruction,
375 mock_instruction,
376 mock_instruction,
377 mock_instruction,
391378 }, &.{}));
392379
393380 // Exceptions
......@@ -403,13 +390,9 @@ test "getReg" {
403390 };
404391 defer function.deinit();
405392
406 var mock_instruction = ir.Inst{
407 .tag = .breakpoint,
408 .ty = Type.initTag(.void),
409 .src = .unneeded,
410 };
393 const mock_instruction: Air.Inst.Index = 1;
411394
412 try function.register_manager.getReg(.r3, &mock_instruction);
395 try function.register_manager.getReg(.r3, mock_instruction);
413396
414397 try expect(!function.register_manager.isRegAllocated(.r2));
415398 try expect(function.register_manager.isRegAllocated(.r3));
......@@ -417,7 +400,7 @@ test "getReg" {
417400 try expect(!function.register_manager.isRegFree(.r3));
418401
419402 // Spill r3
420 try function.register_manager.getReg(.r3, &mock_instruction);
403 try function.register_manager.getReg(.r3, mock_instruction);
421404
422405 try expect(!function.register_manager.isRegAllocated(.r2));
423406 try expect(function.register_manager.isRegAllocated(.r3));
src/stage1/astgen.cpp-3
......@@ -3821,9 +3821,6 @@ static Stage1ZirInst *astgen_identifier(Stage1AstGen *ag, Scope *scope, AstNode
38213821 const_instruction->value->special = ConstValSpecialStatic;
38223822 const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard;
38233823 return &const_instruction->base;
3824 } else {
3825 add_node_error(ag->codegen, node, buf_sprintf("`_` may only be used to assign things to"));
3826 return ag->codegen->invalid_inst_src;
38273824 }
38283825 }
38293826
src/stage1/ir.cpp+6-14
......@@ -24261,12 +24261,6 @@ static Stage1AirInst *ir_analyze_instruction_src(IrAnalyze *ira, Stage1ZirInstSr
2426124261 return ira->codegen->invalid_inst_gen;
2426224262 }
2426324263
24264 ZigType *u8_ptr = get_pointer_to_type_extra2(
24265 ira->codegen, ira->codegen->builtin_types.entry_u8,
24266 true, false, PtrLenUnknown,
24267 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, ira->codegen->intern.for_zero_byte());
24268 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
24269
2427024264 ZigType *source_location_type = get_builtin_type(ira->codegen, "SourceLocation");
2427124265 if (type_resolve(ira->codegen, source_location_type, ResolveStatusSizeKnown)) {
2427224266 zig_unreachable();
......@@ -24286,18 +24280,16 @@ static Stage1AirInst *ir_analyze_instruction_src(IrAnalyze *ira, Stage1ZirInstSr
2428624280 ZigType *import = instruction->base.source_node->owner;
2428724281 RootStruct *root_struct = import->data.structure.root_struct;
2428824282 Buf *path = root_struct->path;
24289 ZigValue *file_name = create_const_str_lit(ira->codegen, path)->data.x_ptr.data.ref.pointee;
24290 init_const_slice(ira->codegen, fields[0], file_name, 0, buf_len(path), true, nullptr);
24291 fields[0]->type = u8_slice;
24283 fields[0] = create_sentineled_str_lit(
24284 ira->codegen, path,
24285 ira->codegen->intern.for_zero_byte());
2429224286
2429324287 // fn_name: [:0]const u8
2429424288 ensure_field_index(source_location_type, "fn_name", 1);
2429524289 fields[1]->special = ConstValSpecialStatic;
24296
24297 ZigValue *fn_name = create_const_str_lit(ira->codegen, &fn_entry->symbol_name)->data.x_ptr.data.ref.pointee;
24298 init_const_slice(ira->codegen, fields[1], fn_name, 0, buf_len(&fn_entry->symbol_name), true, nullptr);
24299 fields[1]->type = u8_slice;
24300
24290 fields[1] = create_sentineled_str_lit(
24291 ira->codegen, &fn_entry->symbol_name,
24292 ira->codegen->intern.for_zero_byte());
2430124293
2430224294 TokenLoc tok_loc = root_struct->token_locs[instruction->base.source_node->main_token];
2430324295
src/stage1/stage1.h+1
......@@ -97,6 +97,7 @@ enum Os {
9797 OsOpenCL,
9898 OsGLSL450,
9999 OsVulkan,
100 OsPlan9,
100101 OsOther,
101102};
102103
src/stage1/target.cpp+6
......@@ -125,6 +125,7 @@ static const Os os_list[] = {
125125 OsOpenCL,
126126 OsGLSL450,
127127 OsVulkan,
128 OsPlan9,
128129 OsOther,
129130};
130131
......@@ -219,6 +220,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type) {
219220 case OsOpenCL:
220221 case OsGLSL450:
221222 case OsVulkan:
223 case OsPlan9:
222224 case OsOther:
223225 return ZigLLVM_UnknownOS;
224226 case OsAnanas:
......@@ -298,6 +300,8 @@ const char *target_os_name(Os os_type) {
298300 switch (os_type) {
299301 case OsFreestanding:
300302 return "freestanding";
303 case OsPlan9:
304 return "plan9";
301305 case OsUefi:
302306 return "uefi";
303307 case OsOther:
......@@ -667,6 +671,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
667671 case OsWASI:
668672 case OsHaiku:
669673 case OsEmscripten:
674 case OsPlan9:
670675 switch (id) {
671676 case CIntTypeShort:
672677 case CIntTypeUShort:
......@@ -975,6 +980,7 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {
975980 case OsOpenCL:
976981 case OsGLSL450:
977982 case OsVulkan:
983 case OsPlan9:
978984 return ZigLLVM_UnknownEnvironment;
979985 }
980986 zig_unreachable();
src/test.zig+8-3
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const link = @import("link.zig");
34const Compilation = @import("Compilation.zig");
45const Allocator = std.mem.Allocator;
......@@ -610,7 +611,7 @@ pub const TestContext = struct {
610611
611612 fn run(self: *TestContext) !void {
612613 var progress = std.Progress{};
613 const root_node = try progress.start("tests", self.cases.items.len);
614 const root_node = try progress.start("compiler", self.cases.items.len);
614615 defer root_node.end();
615616
616617 var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator);
......@@ -640,8 +641,12 @@ pub const TestContext = struct {
640641 var fail_count: usize = 0;
641642
642643 for (self.cases.items) |case| {
643 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)
644 continue;
644 if (build_options.skip_non_native) {
645 if (case.target.getCpuArch() != builtin.cpu.arch)
646 continue;
647 if (case.target.getObjectFormat() != builtin.object_format)
648 continue;
649 }
645650
646651 // Skip tests that require LLVM backend when it is not available
647652 if (!build_options.have_llvm and case.backend == .llvm)
src/translate_c.zig-4
......@@ -4951,10 +4951,6 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
49514951 const scope = &c.global_scope.base;
49524952
49534953 const init_node = try parseCExpr(c, m, scope);
4954 if (init_node.castTag(.identifier)) |ident_node| {
4955 if (mem.eql(u8, "_", ident_node.data))
4956 return m.fail(c, "unable to translate C expr: illegal identifier _", .{});
4957 }
49584954 const last = m.next().?;
49594955 if (last != .Eof and last != .Nl)
49604956 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
src/value.zig+3-3
......@@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
99const Module = @import("Module.zig");
10const ir = @import("air.zig");
10const Air = @import("Air.zig");
1111
1212/// This is the raw data, with no bookkeeping, no memory awareness,
1313/// no de-duplication, and no type system awareness.
......@@ -573,7 +573,7 @@ pub const Value = extern union {
573573 .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
574574 .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
575575 .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
576 .function => return out_stream.writeAll("(function)"),
576 .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
577577 .extern_fn => return out_stream.writeAll("(extern function)"),
578578 .variable => return out_stream.writeAll("(variable)"),
579579 .ref_val => {
......@@ -1700,7 +1700,7 @@ pub const Value = extern union {
17001700 /// peer type resolution. This is stored in a separate list so that
17011701 /// the items are contiguous in memory and thus can be passed to
17021702 /// `Module.resolvePeerTypes`.
1703 stored_inst_list: std.ArrayListUnmanaged(*ir.Inst) = .{},
1703 stored_inst_list: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
17041704 },
17051705 };
17061706
test/behavior/bugs/3779.zig+18
......@@ -40,3 +40,21 @@ test "@embedFile() returns a string literal" {
4040 try std.testing.expectEqualStrings(expected_contents, actual_contents);
4141 try std.testing.expectEqualStrings(expected_contents, ptr_actual_contents[0..actual_contents.len]);
4242}
43
44fn testFnForSrc() std.builtin.SourceLocation {
45 return @src();
46}
47
48test "@src() returns a struct containing 0-terminated string slices" {
49 const src = testFnForSrc();
50 try std.testing.expectEqual([:0]const u8, @TypeOf(src.file));
51 try std.testing.expect(std.mem.endsWith(u8, src.file, "3779.zig"));
52 try std.testing.expectEqual([:0]const u8, @TypeOf(src.fn_name));
53 try std.testing.expect(std.mem.endsWith(u8, src.fn_name, "testFnForSrc"));
54
55 const ptr_src_file: [*:0]const u8 = src.file;
56 _ = ptr_src_file; // unused
57
58 const ptr_src_fn_name: [*:0]const u8 = src.fn_name;
59 _ = ptr_src_fn_name; // unused
60}
test/run_translated_c.zig+12
......@@ -1647,4 +1647,16 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
16471647 \\ if (a != 1) abort();
16481648 \\}
16491649 , "");
1650
1651 cases.add("Underscore identifiers",
1652 \\#include <stdlib.h>
1653 \\int _ = 10;
1654 \\typedef struct { int _; } S;
1655 \\int main(void) {
1656 \\ if (_ != 10) abort();
1657 \\ S foo = { ._ = _ };
1658 \\ if (foo._ != _) abort();
1659 \\ return 0;
1660 \\}
1661 , "");
16501662}
test/stage2/wasm.zig+62-60
......@@ -479,66 +479,68 @@ pub fn addCases(ctx: *TestContext) !void {
479479 , "30\n");
480480 }
481481
482 {
483 var case = ctx.exe("wasm switch", wasi);
484
485 case.addCompareOutput(
486 \\pub export fn _start() u32 {
487 \\ var val: u32 = 1;
488 \\ var a: u32 = switch (val) {
489 \\ 0, 1 => 2,
490 \\ 2 => 3,
491 \\ 3 => 4,
492 \\ else => 5,
493 \\ };
494 \\
495 \\ return a;
496 \\}
497 , "2\n");
498
499 case.addCompareOutput(
500 \\pub export fn _start() u32 {
501 \\ var val: u32 = 2;
502 \\ var a: u32 = switch (val) {
503 \\ 0, 1 => 2,
504 \\ 2 => 3,
505 \\ 3 => 4,
506 \\ else => 5,
507 \\ };
508 \\
509 \\ return a;
510 \\}
511 , "3\n");
512
513 case.addCompareOutput(
514 \\pub export fn _start() u32 {
515 \\ var val: u32 = 10;
516 \\ var a: u32 = switch (val) {
517 \\ 0, 1 => 2,
518 \\ 2 => 3,
519 \\ 3 => 4,
520 \\ else => 5,
521 \\ };
522 \\
523 \\ return a;
524 \\}
525 , "5\n");
526
527 case.addCompareOutput(
528 \\const MyEnum = enum { One, Two, Three };
529 \\
530 \\pub export fn _start() u32 {
531 \\ var val: MyEnum = .Two;
532 \\ var a: u32 = switch (val) {
533 \\ .One => 1,
534 \\ .Two => 2,
535 \\ .Three => 3,
536 \\ };
537 \\
538 \\ return a;
539 \\}
540 , "2\n");
541 }
482 // This test case is disabled until the codegen for switch is reworked
483 // to take advantage of br_table rather than a series of br_if opcodes.
484 //{
485 // var case = ctx.exe("wasm switch", wasi);
486
487 // case.addCompareOutput(
488 // \\pub export fn _start() u32 {
489 // \\ var val: u32 = 1;
490 // \\ var a: u32 = switch (val) {
491 // \\ 0, 1 => 2,
492 // \\ 2 => 3,
493 // \\ 3 => 4,
494 // \\ else => 5,
495 // \\ };
496 // \\
497 // \\ return a;
498 // \\}
499 // , "2\n");
500
501 // case.addCompareOutput(
502 // \\pub export fn _start() u32 {
503 // \\ var val: u32 = 2;
504 // \\ var a: u32 = switch (val) {
505 // \\ 0, 1 => 2,
506 // \\ 2 => 3,
507 // \\ 3 => 4,
508 // \\ else => 5,
509 // \\ };
510 // \\
511 // \\ return a;
512 // \\}
513 // , "3\n");
514
515 // case.addCompareOutput(
516 // \\pub export fn _start() u32 {
517 // \\ var val: u32 = 10;
518 // \\ var a: u32 = switch (val) {
519 // \\ 0, 1 => 2,
520 // \\ 2 => 3,
521 // \\ 3 => 4,
522 // \\ else => 5,
523 // \\ };
524 // \\
525 // \\ return a;
526 // \\}
527 // , "5\n");
528
529 // case.addCompareOutput(
530 // \\const MyEnum = enum { One, Two, Three };
531 // \\
532 // \\pub export fn _start() u32 {
533 // \\ var val: MyEnum = .Two;
534 // \\ var a: u32 = switch (val) {
535 // \\ .One => 1,
536 // \\ .Two => 2,
537 // \\ .Three => 3,
538 // \\ };
539 // \\
540 // \\ return a;
541 // \\}
542 // , "2\n");
543 //}
542544
543545 {
544546 var case = ctx.exe("wasm error unions", wasi);
test/translate_c.zig+5-2
......@@ -3616,9 +3616,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
36163616 \\}
36173617 });
36183618
3619 cases.add("Don't allow underscore identifier in macros",
3619 cases.add("Use @ syntax for bare underscore identifier in macro or public symbol",
36203620 \\#define FOO _
3621 \\int _ = 42;
36213622 , &[_][]const u8{
3622 \\pub const FOO = @compileError("unable to translate C expr: illegal identifier _");
3623 \\pub const FOO = @"_";
3624 ,
3625 \\pub export var @"_": c_int = 42;
36233626 });
36243627}