authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-21 03:18:39-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-07-21 03:18:39-04:00
log26984852bdfdbe3564b19f3ff7b3ecfd606c9902
treea64c806e3e9900c4eb0cd281a9d6946ce07421f5
parentbfe20051673e285d3b1788cd637fab9ca84d1cb1
parentc39c46c0d12b15874b1586ff47cf473b31867918
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9353 from ziglang/stage2-air

stage2: rework AIR memory layout

34 files changed, 7566 insertions(+), 6251 deletions(-)

CMakeLists.txt+2-2
...@@ -564,7 +564,7 @@ set(ZIG_STAGE2_SOURCES...@@ -564,7 +564,7 @@ set(ZIG_STAGE2_SOURCES
564 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"564 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"
565 "${CMAKE_SOURCE_DIR}/src/glibc.zig"565 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
566 "${CMAKE_SOURCE_DIR}/src/introspect.zig"566 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
567 "${CMAKE_SOURCE_DIR}/src/air.zig"567 "${CMAKE_SOURCE_DIR}/src/Air.zig"
568 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"568 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
569 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"569 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
570 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"570 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
...@@ -597,7 +597,7 @@ set(ZIG_STAGE2_SOURCES...@@ -597,7 +597,7 @@ set(ZIG_STAGE2_SOURCES
597 "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig"597 "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig"
598 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"598 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
599 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"599 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
600 "${CMAKE_SOURCE_DIR}/src/liveness.zig"600 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
601 "${CMAKE_SOURCE_DIR}/src/main.zig"601 "${CMAKE_SOURCE_DIR}/src/main.zig"
602 "${CMAKE_SOURCE_DIR}/src/mingw.zig"602 "${CMAKE_SOURCE_DIR}/src/mingw.zig"
603 "${CMAKE_SOURCE_DIR}/src/musl.zig"603 "${CMAKE_SOURCE_DIR}/src/musl.zig"
lib/std/Progress.zig+61-74
...@@ -63,6 +63,10 @@ done: bool = true,...@@ -63,6 +63,10 @@ done: bool = true,
63/// while it was still being accessed by the `refresh` function.63/// while it was still being accessed by the `refresh` function.
64update_lock: std.Thread.Mutex = .{},64update_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
66/// Represents one unit of progress. Each node can have children nodes, or70/// Represents one unit of progress. Each node can have children nodes, or
67/// one can use integers with `update`.71/// one can use integers with `update`.
68pub const Node = struct {72pub const Node = struct {
...@@ -160,6 +164,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*...@@ -160,6 +164,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
160 .unprotected_estimated_total_items = estimated_total_items,164 .unprotected_estimated_total_items = estimated_total_items,
161 .unprotected_completed_items = 0,165 .unprotected_completed_items = 0,
162 };166 };
167 self.columns_written = 0;
163 self.prev_refresh_timestamp = 0;168 self.prev_refresh_timestamp = 0;
164 self.timer = try std.time.Timer.start();169 self.timer = try std.time.Timer.start();
165 self.done = false;170 self.done = false;
...@@ -187,15 +192,6 @@ pub fn refresh(self: *Progress) void {...@@ -187,15 +192,6 @@ pub fn refresh(self: *Progress) void {
187 return self.refreshWithHeldLock();192 return self.refreshWithHeldLock();
188}193}
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
199fn refreshWithHeldLock(self: *Progress) void {195fn refreshWithHeldLock(self: *Progress) void {
200 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
201 if (is_dumb and self.dont_print_on_dumb) return;197 if (is_dumb and self.dont_print_on_dumb) return;
...@@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void {
203 const file = self.terminal orelse return;199 const file = self.terminal orelse return;
204200
205 var end: usize = 0;201 var end: usize = 0;
206 // Save the cursor position and clear the part of the screen below.202 if (self.columns_written > 0) {
207 // Clearing only the line is not enough as the terminal may wrap the line203 // restore the cursor position by moving the cursor
208 // when it becomes too long.204 // `columns_written` cells to the left, then clear the rest of the
209 var saved_cursor_pos: windows.COORD = undefined;205 // line
210 if (self.supports_ansi_escape_codes) {206 if (self.supports_ansi_escape_codes) {
211 const seq_before = DECSC ++ ED;207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
212 std.mem.copy(u8, self.output_buffer[end..], seq_before);208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
213 end += seq_before.len;209 } else if (std.builtin.os.tag == .windows) winapi: {
214 } else if (std.builtin.os.tag == .windows) winapi: {210 std.debug.assert(self.is_windows_terminal);
215 std.debug.assert(self.is_windows_terminal);211
216212 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
217 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;213 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
218 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)214 unreachable;
219 unreachable;215
220216 var cursor_pos = windows.COORD{
221 saved_cursor_pos = info.dwCursorPosition;217 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
222218 .Y = info.dwCursorPosition.Y,
223 const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1);219 };
224 const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1);220
225 // Number of terminal cells to clear, starting from the cursor position221 if (cursor_pos.X < 0)
226 // and ending at the window bottom right corner.222 cursor_pos.X = 0;
227 const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: {223
228 break :chars window_width * (window_height -224 const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
229 @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) -225
230 @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left);226 var written: windows.DWORD = undefined;
231 };227 if (windows.kernel32.FillConsoleOutputAttribute(
232228 file.handle,
233 var written: windows.DWORD = undefined;229 info.wAttributes,
234 if (windows.kernel32.FillConsoleOutputAttribute(230 fill_chars,
235 file.handle,231 cursor_pos,
236 info.wAttributes,232 &written,
237 fill_chars,233 ) != windows.TRUE) {
238 saved_cursor_pos,234 // Stop trying to write to this file.
239 &written,235 self.terminal = null;
240 ) != windows.TRUE) {236 break :winapi;
241 // Stop trying to write to this file.237 }
242 self.terminal = null;238 if (windows.kernel32.FillConsoleOutputCharacterW(
243 break :winapi;239 file.handle,
244 }240 ' ',
245 if (windows.kernel32.FillConsoleOutputCharacterW(241 fill_chars,
246 file.handle,242 cursor_pos,
247 ' ',243 &written,
248 fill_chars,244 ) != windows.TRUE) unreachable;
249 saved_cursor_pos,245
250 &written,246 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
251 ) != windows.TRUE) {247 unreachable;
252 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;
253 }252 }
253
254 self.columns_written = 0;
254 }255 }
255256
256 if (!self.done) {257 if (!self.done) {
...@@ -285,28 +286,10 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -285,28 +286,10 @@ fn refreshWithHeldLock(self: *Progress) void {
285 }286 }
286 }287 }
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
298 _ = file.write(self.output_buffer[0..end]) catch {289 _ = file.write(self.output_buffer[0..end]) catch {
299 // Stop trying to write to this file once it errors.290 // Stop trying to write to this file once it errors.
300 self.terminal = null;291 self.terminal = null;
301 };292 };
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
310 self.prev_refresh_timestamp = self.timer.read();293 self.prev_refresh_timestamp = self.timer.read();
311}294}
312295
...@@ -317,14 +300,17 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {...@@ -317,14 +300,17 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
317 self.terminal = null;300 self.terminal = null;
318 return;301 return;
319 };302 };
303 self.columns_written = 0;
320}304}
321305
322fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {306fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
323 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {307 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
324 const amt = written.len;308 const amt = written.len;
325 end.* += amt;309 end.* += amt;
310 self.columns_written += amt;
326 } else |err| switch (err) {311 } else |err| switch (err) {
327 error.NoSpaceLeft => {312 error.NoSpaceLeft => {
313 self.columns_written += self.output_buffer.len - end.*;
328 end.* = self.output_buffer.len;314 end.* = self.output_buffer.len;
329 },315 },
330 }316 }
...@@ -332,6 +318,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any...@@ -332,6 +318,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any
332 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;318 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
333 if (end.* > max_end) {319 if (end.* > max_end) {
334 const suffix = "... ";320 const suffix = "... ";
321 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
335 std.mem.copy(u8, self.output_buffer[max_end..], suffix);322 std.mem.copy(u8, self.output_buffer[max_end..], suffix);
336 end.* = max_end + suffix.len;323 end.* = max_end + suffix.len;
337 }324 }
lib/std/Thread.zig+43-21
...@@ -505,8 +505,8 @@ const LinuxThreadImpl = struct {...@@ -505,8 +505,8 @@ const LinuxThreadImpl = struct {
505 /// Ported over from musl libc's pthread detached implementation:505 /// Ported over from musl libc's pthread detached implementation:
506 /// https://github.com/ifduyue/musl/search?q=__unmapself506 /// https://github.com/ifduyue/musl/search?q=__unmapself
507 fn freeAndExit(self: *ThreadCompletion) noreturn {507 fn freeAndExit(self: *ThreadCompletion) noreturn {
508 const unmap_and_exit: []const u8 = switch (target.cpu.arch) {508 switch (target.cpu.arch) {
509 .i386 => (509 .i386 => asm volatile (
510 \\ movl $91, %%eax510 \\ movl $91, %%eax
511 \\ movl %[ptr], %%ebx511 \\ movl %[ptr], %%ebx
512 \\ movl %[len], %%ecx512 \\ movl %[len], %%ecx
...@@ -514,8 +514,12 @@ const LinuxThreadImpl = struct {...@@ -514,8 +514,12 @@ const LinuxThreadImpl = struct {
514 \\ movl $1, %%eax514 \\ movl $1, %%eax
515 \\ movl $0, %%ebx515 \\ movl $0, %%ebx
516 \\ int $128516 \\ int $128
517 :
518 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
519 [len] "r" (self.mapped.len)
520 : "memory"
517 ),521 ),
518 .x86_64 => (522 .x86_64 => asm volatile (
519 \\ movq $11, %%rax523 \\ movq $11, %%rax
520 \\ movq %[ptr], %%rbx524 \\ movq %[ptr], %%rbx
521 \\ movq %[len], %%rcx525 \\ movq %[len], %%rcx
...@@ -523,8 +527,12 @@ const LinuxThreadImpl = struct {...@@ -523,8 +527,12 @@ const LinuxThreadImpl = struct {
523 \\ movq $60, %%rax527 \\ movq $60, %%rax
524 \\ movq $1, %%rdi528 \\ movq $1, %%rdi
525 \\ syscall529 \\ syscall
530 :
531 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
532 [len] "r" (self.mapped.len)
533 : "memory"
526 ),534 ),
527 .arm, .armeb, .thumb, .thumbeb => (535 .arm, .armeb, .thumb, .thumbeb => asm volatile (
528 \\ mov r7, #91536 \\ mov r7, #91
529 \\ mov r0, %[ptr]537 \\ mov r0, %[ptr]
530 \\ mov r1, %[len]538 \\ mov r1, %[len]
...@@ -532,8 +540,12 @@ const LinuxThreadImpl = struct {...@@ -532,8 +540,12 @@ const LinuxThreadImpl = struct {
532 \\ mov r7, #1540 \\ mov r7, #1
533 \\ mov r0, #0541 \\ mov r0, #0
534 \\ svc 0542 \\ svc 0
543 :
544 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
545 [len] "r" (self.mapped.len)
546 : "memory"
535 ),547 ),
536 .aarch64, .aarch64_be, .aarch64_32 => (548 .aarch64, .aarch64_be, .aarch64_32 => asm volatile (
537 \\ mov x8, #215549 \\ mov x8, #215
538 \\ mov x0, %[ptr]550 \\ mov x0, %[ptr]
539 \\ mov x1, %[len]551 \\ mov x1, %[len]
...@@ -541,8 +553,12 @@ const LinuxThreadImpl = struct {...@@ -541,8 +553,12 @@ const LinuxThreadImpl = struct {
541 \\ mov x8, #93553 \\ mov x8, #93
542 \\ mov x0, #0554 \\ mov x0, #0
543 \\ svc 0555 \\ svc 0
556 :
557 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
558 [len] "r" (self.mapped.len)
559 : "memory"
544 ),560 ),
545 .mips, .mipsel => (561 .mips, .mipsel => asm volatile (
546 \\ move $sp, $25562 \\ move $sp, $25
547 \\ li $2, 4091563 \\ li $2, 4091
548 \\ move $4, %[ptr]564 \\ move $4, %[ptr]
...@@ -551,8 +567,12 @@ const LinuxThreadImpl = struct {...@@ -551,8 +567,12 @@ const LinuxThreadImpl = struct {
551 \\ li $2, 4001567 \\ li $2, 4001
552 \\ li $4, 0568 \\ li $4, 0
553 \\ syscall569 \\ syscall
570 :
571 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
572 [len] "r" (self.mapped.len)
573 : "memory"
554 ),574 ),
555 .mips64, .mips64el => (575 .mips64, .mips64el => asm volatile (
556 \\ li $2, 4091576 \\ li $2, 4091
557 \\ move $4, %[ptr]577 \\ move $4, %[ptr]
558 \\ move $5, %[len]578 \\ move $5, %[len]
...@@ -560,8 +580,12 @@ const LinuxThreadImpl = struct {...@@ -560,8 +580,12 @@ const LinuxThreadImpl = struct {
560 \\ li $2, 4001580 \\ li $2, 4001
561 \\ li $4, 0581 \\ li $4, 0
562 \\ syscall582 \\ syscall
583 :
584 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
585 [len] "r" (self.mapped.len)
586 : "memory"
563 ),587 ),
564 .powerpc, .powerpcle, .powerpc64, .powerpc64le => (588 .powerpc, .powerpcle, .powerpc64, .powerpc64le => asm volatile (
565 \\ li 0, 91589 \\ li 0, 91
566 \\ mr %[ptr], 3590 \\ mr %[ptr], 3
567 \\ mr %[len], 4591 \\ mr %[len], 4
...@@ -570,8 +594,12 @@ const LinuxThreadImpl = struct {...@@ -570,8 +594,12 @@ const LinuxThreadImpl = struct {
570 \\ li 3, 0594 \\ li 3, 0
571 \\ sc595 \\ sc
572 \\ blr596 \\ blr
597 :
598 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
599 [len] "r" (self.mapped.len)
600 : "memory"
573 ),601 ),
574 .riscv64 => (602 .riscv64 => asm volatile (
575 \\ li a7, 215603 \\ li a7, 215
576 \\ mv a0, %[ptr]604 \\ mv a0, %[ptr]
577 \\ mv a1, %[len]605 \\ mv a1, %[len]
...@@ -579,19 +607,13 @@ const LinuxThreadImpl = struct {...@@ -579,19 +607,13 @@ const LinuxThreadImpl = struct {
579 \\ li a7, 93607 \\ li a7, 93
580 \\ mv a0, zero608 \\ mv a0, zero
581 \\ ecall609 \\ ecall
610 :
611 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
612 [len] "r" (self.mapped.len)
613 : "memory"
582 ),614 ),
583 else => |cpu_arch| {615 else => |cpu_arch| @compileError("Unsupported linux arch: " ++ @tagName(cpu_arch)),
584 @compileLog("Unsupported linux arch ", cpu_arch);616 }
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
595 unreachable;617 unreachable;
596 }618 }
597 };619 };
lib/std/array_list.zig+10-9
...@@ -227,10 +227,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -227,10 +227,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
227 /// Append the slice of items to the list, asserting the capacity is already227 /// Append the slice of items to the list, asserting the capacity is already
228 /// enough to store the new items. **Does not** invalidate pointers.228 /// enough to store the new items. **Does not** invalidate pointers.
229 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {229 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
230 const oldlen = self.items.len;230 const old_len = self.items.len;
231 const newlen = self.items.len + items.len;231 const new_len = old_len + items.len;
232 self.items.len = newlen;232 assert(new_len <= self.capacity);
233 mem.copy(T, self.items[oldlen..], items);233 self.items.len = new_len;
234 mem.copy(T, self.items[old_len..], items);
234 }235 }
235236
236 pub usingnamespace if (T != u8) struct {} else struct {237 pub usingnamespace if (T != u8) struct {} else struct {
...@@ -570,11 +571,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -570,11 +571,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
570 /// Append the slice of items to the list, asserting the capacity is enough571 /// Append the slice of items to the list, asserting the capacity is enough
571 /// to store the new items.572 /// to store the new items.
572 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {573 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
573 const oldlen = self.items.len;574 const old_len = self.items.len;
574 const newlen = self.items.len + items.len;575 const new_len = old_len + items.len;
575576 assert(new_len <= self.capacity);
576 self.items.len = newlen;577 self.items.len = new_len;
577 mem.copy(T, self.items[oldlen..], items);578 mem.copy(T, self.items[old_len..], items);
578 }579 }
579580
580 /// Append a value to the list `n` times.581 /// Append a value to the list `n` times.
lib/std/atomic.zig+21-17
...@@ -46,34 +46,38 @@ test "fence/compilerFence" {...@@ -46,34 +46,38 @@ test "fence/compilerFence" {
4646
47/// Signals to the processor that the caller is inside a busy-wait spin-loop.47/// Signals to the processor that the caller is inside a busy-wait spin-loop.
48pub inline fn spinLoopHint() void {48pub inline fn spinLoopHint() void {
49 const hint_instruction = switch (target.cpu.arch) {49 switch (target.cpu.arch) {
50 // No-op instruction that can hint to save (or share with a hardware-thread) pipelining/power resources50 // No-op instruction that can hint to save (or share with a hardware-thread)
51 // pipelining/power resources
51 // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html52 // 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
54 // No-op instruction that serves as a hardware-thread resource yield hint.55 // No-op instruction that serves as a hardware-thread resource yield hint.
55 // https://stackoverflow.com/a/758894156 // 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.
59 // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=825860461 // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
60 // https://bugs.mysql.com/bug.php?id=10066462 // 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
63 // `yield` was introduced in v6k but is also available on v6m.65 // `yield` was introduced in v6k but is also available on v6m.
64 // https://www.keil.com/support/man/docs/armasm/armasm_dom1361289926796.htm66 // https://www.keil.com/support/man/docs/armasm/armasm_dom1361289926796.htm
65 .arm, .armeb, .thumb, .thumbeb => blk: {67 .arm, .armeb, .thumb, .thumbeb => {
66 const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{ .has_v6k, .has_v6m });68 const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{
67 const instruction = if (can_yield) "yield" else "";69 .has_v6k, .has_v6m,
68 break :blk instruction;70 });
71 if (can_yield) {
72 asm volatile ("yield" ::: "memory");
73 } else {
74 asm volatile ("" ::: "memory");
75 }
69 },76 },
7077 // Memory barrier to prevent the compiler from optimizing away the spin-loop
71 else => "",78 // even if no hint_instruction was provided.
72 };79 else => asm volatile ("" ::: "memory"),
7380 }
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}81}
7882
79test "spinLoopHint" {83test "spinLoopHint" {
lib/std/atomic/Atomic.zig+70-18
...@@ -178,26 +178,78 @@ pub fn Atomic(comptime T: type) type {...@@ -178,26 +178,78 @@ pub fn Atomic(comptime T: type) type {
178 ) u1 {178 ) u1 {
179 // x86 supports dedicated bitwise instructions179 // x86 supports dedicated bitwise instructions
180 if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) {180 if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) {
181 const instruction = switch (op) {181 const old_bit: u8 = switch (@sizeOf(T)) {
182 .Set => "lock bts",182 2 => switch (op) {
183 .Reset => "lock btr",183 .Set => asm volatile ("lock btsw %[bit], %[ptr]"
184 .Toggle => "lock btc",184 // LLVM doesn't support u1 flag register return values
185 };185 : [result] "={@ccc}" (-> u8)
186186 : [ptr] "*p" (&self.value),
187 const suffix = switch (@sizeOf(T)) {187 [bit] "X" (@as(T, bit))
188 2 => "w",188 : "cc", "memory"
189 4 => "l",189 ),
190 8 => "q",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 },
191 else => @compileError("Invalid atomic type " ++ @typeName(T)),251 else => @compileError("Invalid atomic type " ++ @typeName(T)),
192 };252 };
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
201 return @intCast(u1, old_bit);253 return @intCast(u1, old_bit);
202 }254 }
203255
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+107-102
...@@ -989,7 +989,7 @@ fn suspendExpr(...@@ -989,7 +989,7 @@ fn suspendExpr(
989 }989 }
990 try suspend_scope.setBlockBody(suspend_inst);990 try suspend_scope.setBlockBody(suspend_inst);
991991
992 return gz.indexToRef(suspend_inst);992 return indexToRef(suspend_inst);
993}993}
994994
995fn awaitExpr(995fn awaitExpr(
...@@ -1300,7 +1300,7 @@ fn arrayInitExprRlPtr(...@@ -1300,7 +1300,7 @@ fn arrayInitExprRlPtr(
1300 .lhs = result_ptr,1300 .lhs = result_ptr,
1301 .rhs = index_inst,1301 .rhs = index_inst,
1302 });1302 });
1303 elem_ptr_list[i] = gz.refToIndex(elem_ptr).?;1303 elem_ptr_list[i] = refToIndex(elem_ptr).?;
1304 _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);1304 _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
1305 }1305 }
1306 _ = try gz.addPlNode(.validate_array_init_ptr, node, Zir.Inst.Block{1306 _ = try gz.addPlNode(.validate_array_init_ptr, node, Zir.Inst.Block{
...@@ -1455,7 +1455,7 @@ fn structInitExprRlPtr(...@@ -1455,7 +1455,7 @@ fn structInitExprRlPtr(
1455 .lhs = result_ptr,1455 .lhs = result_ptr,
1456 .field_name_start = str_index,1456 .field_name_start = str_index,
1457 });1457 });
1458 field_ptr_list[i] = gz.refToIndex(field_ptr).?;1458 field_ptr_list[i] = refToIndex(field_ptr).?;
1459 _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);1459 _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
1460 }1460 }
1461 _ = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{1461 _ = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{
...@@ -1489,7 +1489,7 @@ fn structInitExprRlTy(...@@ -1489,7 +1489,7 @@ fn structInitExprRlTy(
1489 .name_start = str_index,1489 .name_start = str_index,
1490 });1490 });
1491 fields_list[i] = .{1491 fields_list[i] = .{
1492 .field_type = gz.refToIndex(field_ty_inst).?,1492 .field_type = refToIndex(field_ty_inst).?,
1493 .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init),1493 .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init),
1494 };1494 };
1495 }1495 }
...@@ -1786,7 +1786,7 @@ fn labeledBlockExpr(...@@ -1786,7 +1786,7 @@ fn labeledBlockExpr(
1786 }1786 }
1787 try block_scope.setBlockBody(block_inst);1787 try block_scope.setBlockBody(block_inst);
17881788
1789 return gz.indexToRef(block_inst);1789 return indexToRef(block_inst);
1790 },1790 },
1791 .break_operand => {1791 .break_operand => {
1792 // All break operands are values that did not use the result location pointer.1792 // All break operands are values that did not use the result location pointer.
...@@ -1800,7 +1800,7 @@ fn labeledBlockExpr(...@@ -1800,7 +1800,7 @@ fn labeledBlockExpr(
1800 } else {1800 } else {
1801 try block_scope.setBlockBody(block_inst);1801 try block_scope.setBlockBody(block_inst);
1802 }1802 }
1803 const block_ref = gz.indexToRef(block_inst);1803 const block_ref = indexToRef(block_inst);
1804 switch (rl) {1804 switch (rl) {
1805 .ref => return block_ref,1805 .ref => return block_ref,
1806 else => return rvalue(gz, rl, block_ref, block_node),1806 else => return rvalue(gz, rl, block_ref, block_node),
...@@ -1878,7 +1878,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner...@@ -1878,7 +1878,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
1878 // we want to avoid adding the ZIR instruction if possible for performance.1878 // we want to avoid adding the ZIR instruction if possible for performance.
1879 const maybe_unused_result = try expr(gz, scope, .none, statement);1879 const maybe_unused_result = try expr(gz, scope, .none, statement);
1880 var noreturn_src_node: ast.Node.Index = 0;1880 var noreturn_src_node: ast.Node.Index = 0;
1881 const elide_check = if (gz.refToIndex(maybe_unused_result)) |inst| b: {1881 const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
1882 // Note that this array becomes invalid after appending more items to it1882 // Note that this array becomes invalid after appending more items to it
1883 // in the above while loop.1883 // in the above while loop.
1884 const zir_tags = gz.astgen.instructions.items(.tag);1884 const zir_tags = gz.astgen.instructions.items(.tag);
...@@ -1922,8 +1922,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner...@@ -1922,8 +1922,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
1922 .bool_br_and,1922 .bool_br_and,
1923 .bool_br_or,1923 .bool_br_or,
1924 .bool_not,1924 .bool_not,
1925 .bool_and,
1926 .bool_or,
1927 .call_compile_time,1925 .call_compile_time,
1928 .call_nosuspend,1926 .call_nosuspend,
1929 .call_async,1927 .call_async,
...@@ -2440,7 +2438,7 @@ fn varDecl(...@@ -2440,7 +2438,7 @@ fn varDecl(
2440 // the alloc instruction and the store_to_block_ptr instruction.2438 // the alloc instruction and the store_to_block_ptr instruction.
2441 try parent_zir.ensureUnusedCapacity(gpa, init_scope.instructions.items.len);2439 try parent_zir.ensureUnusedCapacity(gpa, init_scope.instructions.items.len);
2442 for (init_scope.instructions.items) |src_inst| {2440 for (init_scope.instructions.items) |src_inst| {
2443 if (gz.indexToRef(src_inst) == init_scope.rl_ptr) continue;2441 if (indexToRef(src_inst) == init_scope.rl_ptr) continue;
2444 if (zir_tags[src_inst] == .store_to_block_ptr) {2442 if (zir_tags[src_inst] == .store_to_block_ptr) {
2445 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;2443 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
2446 }2444 }
...@@ -2743,7 +2741,7 @@ fn ptrType(...@@ -2743,7 +2741,7 @@ fn ptrType(
2743 }2741 }
27442742
2745 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);2743 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
2746 const result = gz.indexToRef(new_index);2744 const result = indexToRef(new_index);
2747 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{2745 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
2748 .ptr_type = .{2746 .ptr_type = .{
2749 .flags = .{2747 .flags = .{
...@@ -3473,7 +3471,7 @@ fn structDeclInner(...@@ -3473,7 +3471,7 @@ fn structDeclInner(
3473 .body_len = 0,3471 .body_len = 0,
3474 .decls_len = 0,3472 .decls_len = 0,
3475 });3473 });
3476 return gz.indexToRef(decl_inst);3474 return indexToRef(decl_inst);
3477 }3475 }
34783476
3479 const astgen = gz.astgen;3477 const astgen = gz.astgen;
...@@ -3492,7 +3490,6 @@ fn structDeclInner(...@@ -3492,7 +3490,6 @@ fn structDeclInner(
3492 .astgen = astgen,3490 .astgen = astgen,
3493 .force_comptime = true,3491 .force_comptime = true,
3494 .in_defer = false,3492 .in_defer = false,
3495 .ref_start_index = gz.ref_start_index,
3496 };3493 };
3497 defer block_scope.instructions.deinit(gpa);3494 defer block_scope.instructions.deinit(gpa);
34983495
...@@ -3730,7 +3727,7 @@ fn structDeclInner(...@@ -3730,7 +3727,7 @@ fn structDeclInner(
3730 }3727 }
3731 astgen.extra.appendSliceAssumeCapacity(fields_data.items);3728 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
37323729
3733 return gz.indexToRef(decl_inst);3730 return indexToRef(decl_inst);
3734}3731}
37353732
3736fn unionDeclInner(3733fn unionDeclInner(
...@@ -3758,7 +3755,6 @@ fn unionDeclInner(...@@ -3758,7 +3755,6 @@ fn unionDeclInner(
3758 .astgen = astgen,3755 .astgen = astgen,
3759 .force_comptime = true,3756 .force_comptime = true,
3760 .in_defer = false,3757 .in_defer = false,
3761 .ref_start_index = gz.ref_start_index,
3762 };3758 };
3763 defer block_scope.instructions.deinit(gpa);3759 defer block_scope.instructions.deinit(gpa);
37643760
...@@ -4006,7 +4002,7 @@ fn unionDeclInner(...@@ -4006,7 +4002,7 @@ fn unionDeclInner(
4006 astgen.extra.appendAssumeCapacity(cur_bit_bag);4002 astgen.extra.appendAssumeCapacity(cur_bit_bag);
4007 astgen.extra.appendSliceAssumeCapacity(fields_data.items);4003 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
40084004
4009 return gz.indexToRef(decl_inst);4005 return indexToRef(decl_inst);
4010}4006}
40114007
4012fn containerDecl(4008fn containerDecl(
...@@ -4170,7 +4166,6 @@ fn containerDecl(...@@ -4170,7 +4166,6 @@ fn containerDecl(
4170 .astgen = astgen,4166 .astgen = astgen,
4171 .force_comptime = true,4167 .force_comptime = true,
4172 .in_defer = false,4168 .in_defer = false,
4173 .ref_start_index = gz.ref_start_index,
4174 };4169 };
4175 defer block_scope.instructions.deinit(gpa);4170 defer block_scope.instructions.deinit(gpa);
41764171
...@@ -4398,7 +4393,7 @@ fn containerDecl(...@@ -4398,7 +4393,7 @@ fn containerDecl(
4398 astgen.extra.appendAssumeCapacity(cur_bit_bag);4393 astgen.extra.appendAssumeCapacity(cur_bit_bag);
4399 astgen.extra.appendSliceAssumeCapacity(fields_data.items);4394 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
44004395
4401 return rvalue(gz, rl, gz.indexToRef(decl_inst), node);4396 return rvalue(gz, rl, indexToRef(decl_inst), node);
4402 },4397 },
4403 .keyword_opaque => {4398 .keyword_opaque => {
4404 var namespace: Scope.Namespace = .{ .parent = scope };4399 var namespace: Scope.Namespace = .{ .parent = scope };
...@@ -4559,7 +4554,7 @@ fn containerDecl(...@@ -4559,7 +4554,7 @@ fn containerDecl(
4559 }4554 }
4560 astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items);4555 astgen.extra.appendSliceAssumeCapacity(wip_decls.payload.items);
45614556
4562 return rvalue(gz, rl, gz.indexToRef(decl_inst), node);4557 return rvalue(gz, rl, indexToRef(decl_inst), node);
4563 },4558 },
4564 else => unreachable,4559 else => unreachable,
4565 }4560 }
...@@ -4797,7 +4792,7 @@ fn finishThenElseBlock(...@@ -4797,7 +4792,7 @@ fn finishThenElseBlock(
4797 }4792 }
4798 assert(!strat.elide_store_to_block_ptr_instructions);4793 assert(!strat.elide_store_to_block_ptr_instructions);
4799 try setCondBrPayload(condbr, cond, then_scope, else_scope);4794 try setCondBrPayload(condbr, cond, then_scope, else_scope);
4800 return parent_gz.indexToRef(main_block);4795 return indexToRef(main_block);
4801 },4796 },
4802 .break_operand => {4797 .break_operand => {
4803 if (!parent_gz.refIsNoReturn(then_result)) {4798 if (!parent_gz.refIsNoReturn(then_result)) {
...@@ -4815,7 +4810,7 @@ fn finishThenElseBlock(...@@ -4815,7 +4810,7 @@ fn finishThenElseBlock(
4815 } else {4810 } else {
4816 try setCondBrPayload(condbr, cond, then_scope, else_scope);4811 try setCondBrPayload(condbr, cond, then_scope, else_scope);
4817 }4812 }
4818 const block_ref = parent_gz.indexToRef(main_block);4813 const block_ref = indexToRef(main_block);
4819 switch (rl) {4814 switch (rl) {
4820 .ref => return block_ref,4815 .ref => return block_ref,
4821 else => return rvalue(parent_gz, rl, block_ref, node),4816 else => return rvalue(parent_gz, rl, block_ref, node),
...@@ -4937,7 +4932,7 @@ fn boolBinOp(...@@ -4937,7 +4932,7 @@ fn boolBinOp(
4937 }4932 }
4938 try rhs_scope.setBoolBrBody(bool_br);4933 try rhs_scope.setBoolBrBody(bool_br);
49394934
4940 const block_ref = gz.indexToRef(bool_br);4935 const block_ref = indexToRef(bool_br);
4941 return rvalue(gz, rl, block_ref, node);4936 return rvalue(gz, rl, block_ref, node);
4942}4937}
49434938
...@@ -5959,7 +5954,7 @@ fn switchExpr(...@@ -5959,7 +5954,7 @@ fn switchExpr(
5959 if (!strat.elide_store_to_block_ptr_instructions) {5954 if (!strat.elide_store_to_block_ptr_instructions) {
5960 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);5955 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
5961 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);5956 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
5962 return parent_gz.indexToRef(switch_block);5957 return indexToRef(switch_block);
5963 }5958 }
59645959
5965 // There will necessarily be a store_to_block_ptr for5960 // There will necessarily be a store_to_block_ptr for
...@@ -6003,7 +5998,7 @@ fn switchExpr(...@@ -6003,7 +5998,7 @@ fn switchExpr(
6003 .lhs = block_scope.rl_ty_inst,5998 .lhs = block_scope.rl_ty_inst,
6004 .rhs = zir_datas[break_inst].@"break".operand,5999 .rhs = zir_datas[break_inst].@"break".operand,
6005 };6000 };
6006 zir_datas[break_inst].@"break".operand = parent_gz.indexToRef(store_inst);6001 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
6007 } else {6002 } else {
6008 scalar_cases_payload.items[body_len_index] -= 1;6003 scalar_cases_payload.items[body_len_index] -= 1;
6009 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);6004 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
...@@ -6045,7 +6040,7 @@ fn switchExpr(...@@ -6045,7 +6040,7 @@ fn switchExpr(
6045 .lhs = block_scope.rl_ty_inst,6040 .lhs = block_scope.rl_ty_inst,
6046 .rhs = zir_datas[break_inst].@"break".operand,6041 .rhs = zir_datas[break_inst].@"break".operand,
6047 };6042 };
6048 zir_datas[break_inst].@"break".operand = parent_gz.indexToRef(store_inst);6043 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
6049 } else {6044 } else {
6050 scalar_cases_payload.items[body_len_index] -= 1;6045 scalar_cases_payload.items[body_len_index] -= 1;
6051 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);6046 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
...@@ -6091,7 +6086,7 @@ fn switchExpr(...@@ -6091,7 +6086,7 @@ fn switchExpr(
6091 .lhs = block_scope.rl_ty_inst,6086 .lhs = block_scope.rl_ty_inst,
6092 .rhs = zir_datas[break_inst].@"break".operand,6087 .rhs = zir_datas[break_inst].@"break".operand,
6093 };6088 };
6094 zir_datas[break_inst].@"break".operand = parent_gz.indexToRef(store_inst);6089 zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
6095 } else {6090 } else {
6096 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);6091 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
6097 multi_cases_payload.items[body_len_index] -= 1;6092 multi_cases_payload.items[body_len_index] -= 1;
...@@ -6102,7 +6097,7 @@ fn switchExpr(...@@ -6102,7 +6097,7 @@ fn switchExpr(
6102 }6097 }
6103 }6098 }
61046099
6105 const block_ref = parent_gz.indexToRef(switch_block);6100 const block_ref = indexToRef(switch_block);
6106 switch (rl) {6101 switch (rl) {
6107 .ref => return block_ref,6102 .ref => return block_ref,
6108 else => return rvalue(parent_gz, rl, block_ref, switch_node),6103 else => return rvalue(parent_gz, rl, block_ref, switch_node),
...@@ -6162,7 +6157,7 @@ fn switchExpr(...@@ -6162,7 +6157,7 @@ fn switchExpr(
6162 }6157 }
6163 }6158 }
61646159
6165 return parent_gz.indexToRef(switch_block);6160 return indexToRef(switch_block);
6166 },6161 },
6167 }6162 }
6168}6163}
...@@ -6417,37 +6412,12 @@ fn multilineStringLiteral(...@@ -6417,37 +6412,12 @@ fn multilineStringLiteral(
6417 node: ast.Node.Index,6412 node: ast.Node.Index,
6418) InnerError!Zir.Inst.Ref {6413) InnerError!Zir.Inst.Ref {
6419 const astgen = gz.astgen;6414 const astgen = gz.astgen;
6420 const tree = astgen.tree;6415 const str = try astgen.strLitNodeAsString(node);
6421 const node_datas = tree.nodes.items(.data);
6422
6423 const start = node_datas[node].lhs;
6424 const end = node_datas[node].rhs;
6425
6426 const gpa = gz.astgen.gpa;
6427 const string_bytes = &gz.astgen.string_bytes;
6428 const str_index = string_bytes.items.len;
6429
6430 // First line: do not append a newline.
6431 var tok_i = start;
6432 {
6433 const slice = tree.tokenSlice(tok_i);
6434 const line_bytes = slice[2 .. slice.len - 1];
6435 try string_bytes.appendSlice(gpa, line_bytes);
6436 tok_i += 1;
6437 }
6438 // Following lines: each line prepends a newline.
6439 while (tok_i <= end) : (tok_i += 1) {
6440 const slice = tree.tokenSlice(tok_i);
6441 const line_bytes = slice[2 .. slice.len - 1];
6442 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
6443 string_bytes.appendAssumeCapacity('\n');
6444 string_bytes.appendSliceAssumeCapacity(line_bytes);
6445 }
6446 const result = try gz.add(.{6416 const result = try gz.add(.{
6447 .tag = .str,6417 .tag = .str,
6448 .data = .{ .str = .{6418 .data = .{ .str = .{
6449 .start = @intCast(u32, str_index),6419 .start = str.index,
6450 .len = @intCast(u32, string_bytes.items.len - str_index),6420 .len = str.len,
6451 } },6421 } },
6452 });6422 });
6453 return rvalue(gz, rl, result, node);6423 return rvalue(gz, rl, result, node);
...@@ -6594,12 +6564,12 @@ fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir...@@ -6594,12 +6564,12 @@ fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: ast.Node.Index) InnerError!Zir
6594 } else std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {6564 } else std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
6595 error.InvalidCharacter => unreachable, // validated by tokenizer6565 error.InvalidCharacter => unreachable, // validated by tokenizer
6596 };6566 };
6597 // 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.
6598 @setFloatMode(.Strict);6568 @setFloatMode(.Strict);
6599 const smaller_float = @floatCast(f32, float_number);6569 const smaller_float = @floatCast(f64, float_number);
6600 const bigger_again: f128 = smaller_float;6570 const bigger_again: f128 = smaller_float;
6601 if (bigger_again == float_number) {6571 if (bigger_again == float_number) {
6602 const result = try gz.addFloat(smaller_float, node);6572 const result = try gz.addFloat(smaller_float);
6603 return rvalue(gz, rl, result, node);6573 return rvalue(gz, rl, result, node);
6604 }6574 }
6605 // We need to use 128 bits. Break the float into 4 u32 values so we can6575 // We need to use 128 bits. Break the float into 4 u32 values so we can
...@@ -6625,9 +6595,14 @@ fn asmExpr(...@@ -6625,9 +6595,14 @@ fn asmExpr(
6625 const tree = astgen.tree;6595 const tree = astgen.tree;
6626 const main_tokens = tree.nodes.items(.main_token);6596 const main_tokens = tree.nodes.items(.main_token);
6627 const node_datas = tree.nodes.items(.data);6597 const node_datas = tree.nodes.items(.data);
6598 const node_tags = tree.nodes.items(.tag);
6628 const token_tags = tree.tokens.items(.tag);6599 const token_tags = tree.tokens.items(.tag);
66296600
6630 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 };
66316606
6632 // See https://github.com/ziglang/zig/issues/215 and related issues discussing6607 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
6633 // possible inline assembly improvements. Until then here is status quo AstGen6608 // possible inline assembly improvements. Until then here is status quo AstGen
...@@ -6757,7 +6732,7 @@ fn asmExpr(...@@ -6757,7 +6732,7 @@ fn asmExpr(
67576732
6758 const result = try gz.addAsm(.{6733 const result = try gz.addAsm(.{
6759 .node = node,6734 .node = node,
6760 .asm_source = asm_source,6735 .asm_source = asm_source.index,
6761 .is_volatile = full.volatile_token != null,6736 .is_volatile = full.volatile_token != null,
6762 .output_type_bits = output_type_bits,6737 .output_type_bits = output_type_bits,
6763 .outputs = outputs,6738 .outputs = outputs,
...@@ -6861,7 +6836,7 @@ fn asRlPtr(...@@ -6861,7 +6836,7 @@ fn asRlPtr(
6861 const zir_datas = astgen.instructions.items(.data);6836 const zir_datas = astgen.instructions.items(.data);
6862 try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len);6837 try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len);
6863 for (as_scope.instructions.items) |src_inst| {6838 for (as_scope.instructions.items) |src_inst| {
6864 if (parent_gz.indexToRef(src_inst) == as_scope.rl_ptr) continue;6839 if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
6865 if (zir_tags[src_inst] == .store_to_block_ptr) {6840 if (zir_tags[src_inst] == .store_to_block_ptr) {
6866 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;6841 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
6867 }6842 }
...@@ -6992,10 +6967,10 @@ fn builtinCall(...@@ -6992,10 +6967,10 @@ fn builtinCall(
6992 const str_lit_token = main_tokens[operand_node];6967 const str_lit_token = main_tokens[operand_node];
6993 const str = try astgen.strLitAsString(str_lit_token);6968 const str = try astgen.strLitAsString(str_lit_token);
6994 const result = try gz.addStrTok(.import, str.index, str_lit_token);6969 const result = try gz.addStrTok(.import, str.index, str_lit_token);
6995 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);6970 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
6996 if (!gop.found_existing) {6971 if (!gop.found_existing) {
6997 gop.value_ptr.* = str_lit_token;6972 gop.value_ptr.* = str_lit_token;
6998 }6973 }
6999 return rvalue(gz, rl, result, node);6974 return rvalue(gz, rl, result, node);
7000 },6975 },
7001 .compile_log => {6976 .compile_log => {
...@@ -8584,6 +8559,41 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !IndexSlice {...@@ -8584,6 +8559,41 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !IndexSlice {
8584 }8559 }
8585}8560}
85868561
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
8587fn testNameString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !u32 {8597fn testNameString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !u32 {
8588 const gpa = astgen.gpa;8598 const gpa = astgen.gpa;
8589 const string_bytes = &astgen.string_bytes;8599 const string_bytes = &astgen.string_bytes;
...@@ -8705,9 +8715,6 @@ const GenZir = struct {...@@ -8705,9 +8715,6 @@ const GenZir = struct {
8705 in_defer: bool,8715 in_defer: bool,
8706 /// How decls created in this scope should be named.8716 /// How decls created in this scope should be named.
8707 anon_name_strategy: Zir.Inst.NameStrategy = .anon,8717 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
8708 /// The end of special indexes. `Zir.Inst.Ref` subtracts against this number to convert
8709 /// to `Zir.Inst.Index`. The default here is correct if there are 0 parameters.
8710 ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len,
8711 /// The containing decl AST node.8718 /// The containing decl AST node.
8712 decl_node_index: ast.Node.Index,8719 decl_node_index: ast.Node.Index,
8713 /// The containing decl line index, absolute.8720 /// The containing decl line index, absolute.
...@@ -8751,7 +8758,6 @@ const GenZir = struct {...@@ -8751,7 +8758,6 @@ const GenZir = struct {
8751 return .{8758 return .{
8752 .force_comptime = gz.force_comptime,8759 .force_comptime = gz.force_comptime,
8753 .in_defer = gz.in_defer,8760 .in_defer = gz.in_defer,
8754 .ref_start_index = gz.ref_start_index,
8755 .decl_node_index = gz.decl_node_index,8761 .decl_node_index = gz.decl_node_index,
8756 .decl_line = gz.decl_line,8762 .decl_line = gz.decl_line,
8757 .parent = scope,8763 .parent = scope,
...@@ -8769,7 +8775,7 @@ const GenZir = struct {...@@ -8769,7 +8775,7 @@ const GenZir = struct {
87698775
8770 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {8776 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
8771 if (inst_ref == .unreachable_value) return true;8777 if (inst_ref == .unreachable_value) return true;
8772 if (gz.refToIndex(inst_ref)) |inst_index| {8778 if (refToIndex(inst_ref)) |inst_index| {
8773 return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();8779 return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();
8774 }8780 }
8775 return false;8781 return false;
...@@ -8807,19 +8813,6 @@ const GenZir = struct {...@@ -8807,19 +8813,6 @@ const GenZir = struct {
8807 return gz.astgen.tree.firstToken(gz.decl_node_index);8813 return gz.astgen.tree.firstToken(gz.decl_node_index);
8808 }8814 }
88098815
8810 fn indexToRef(gz: GenZir, inst: Zir.Inst.Index) Zir.Inst.Ref {
8811 return @intToEnum(Zir.Inst.Ref, gz.ref_start_index + inst);
8812 }
8813
8814 fn refToIndex(gz: GenZir, inst: Zir.Inst.Ref) ?Zir.Inst.Index {
8815 const ref_int = @enumToInt(inst);
8816 if (ref_int >= gz.ref_start_index) {
8817 return ref_int - gz.ref_start_index;
8818 } else {
8819 return null;
8820 }
8821 }
8822
8823 fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {8816 fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
8824 // Depending on whether the result location is a pointer or value, different8817 // Depending on whether the result location is a pointer or value, different
8825 // ZIR needs to be generated. In the former case we rely on storing to the8818 // ZIR needs to be generated. In the former case we rely on storing to the
...@@ -8998,7 +8991,7 @@ const GenZir = struct {...@@ -8998,7 +8991,7 @@ const GenZir = struct {
8998 } },8991 } },
8999 });8992 });
9000 gz.instructions.appendAssumeCapacity(new_index);8993 gz.instructions.appendAssumeCapacity(new_index);
9001 return gz.indexToRef(new_index);8994 return indexToRef(new_index);
9002 } else {8995 } else {
9003 try gz.astgen.extra.ensureUnusedCapacity(8996 try gz.astgen.extra.ensureUnusedCapacity(
9004 gpa,8997 gpa,
...@@ -9025,7 +9018,7 @@ const GenZir = struct {...@@ -9025,7 +9018,7 @@ const GenZir = struct {
9025 } },9018 } },
9026 });9019 });
9027 gz.instructions.appendAssumeCapacity(new_index);9020 gz.instructions.appendAssumeCapacity(new_index);
9028 return gz.indexToRef(new_index);9021 return indexToRef(new_index);
9029 }9022 }
9030 }9023 }
90319024
...@@ -9079,7 +9072,7 @@ const GenZir = struct {...@@ -9079,7 +9072,7 @@ const GenZir = struct {
9079 } },9072 } },
9080 });9073 });
9081 gz.instructions.appendAssumeCapacity(new_index);9074 gz.instructions.appendAssumeCapacity(new_index);
9082 return gz.indexToRef(new_index);9075 return indexToRef(new_index);
9083 }9076 }
90849077
9085 fn addCall(9078 fn addCall(
...@@ -9113,7 +9106,7 @@ const GenZir = struct {...@@ -9113,7 +9106,7 @@ const GenZir = struct {
9113 } },9106 } },
9114 });9107 });
9115 gz.instructions.appendAssumeCapacity(new_index);9108 gz.instructions.appendAssumeCapacity(new_index);
9116 return gz.indexToRef(new_index);9109 return indexToRef(new_index);
9117 }9110 }
91189111
9119 /// Note that this returns a `Zir.Inst.Index` not a ref.9112 /// Note that this returns a `Zir.Inst.Index` not a ref.
...@@ -9164,16 +9157,13 @@ const GenZir = struct {...@@ -9164,16 +9157,13 @@ const GenZir = struct {
9164 });9157 });
9165 gz.instructions.appendAssumeCapacity(new_index);9158 gz.instructions.appendAssumeCapacity(new_index);
9166 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));9159 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
9167 return gz.indexToRef(new_index);9160 return indexToRef(new_index);
9168 }9161 }
91699162
9170 fn addFloat(gz: *GenZir, number: f32, src_node: ast.Node.Index) !Zir.Inst.Ref {9163 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
9171 return gz.add(.{9164 return gz.add(.{
9172 .tag = .float,9165 .tag = .float,
9173 .data = .{ .float = .{9166 .data = .{ .float = number },
9174 .src_node = gz.nodeIndexToRelative(src_node),
9175 .number = number,
9176 } },
9177 });9167 });
9178 }9168 }
91799169
...@@ -9215,7 +9205,7 @@ const GenZir = struct {...@@ -9215,7 +9205,7 @@ const GenZir = struct {
9215 } },9205 } },
9216 });9206 });
9217 gz.instructions.appendAssumeCapacity(new_index);9207 gz.instructions.appendAssumeCapacity(new_index);
9218 return gz.indexToRef(new_index);9208 return indexToRef(new_index);
9219 }9209 }
92209210
9221 fn addExtendedPayload(9211 fn addExtendedPayload(
...@@ -9239,7 +9229,7 @@ const GenZir = struct {...@@ -9239,7 +9229,7 @@ const GenZir = struct {
9239 } },9229 } },
9240 });9230 });
9241 gz.instructions.appendAssumeCapacity(new_index);9231 gz.instructions.appendAssumeCapacity(new_index);
9242 return gz.indexToRef(new_index);9232 return indexToRef(new_index);
9243 }9233 }
92449234
9245 fn addExtendedMultiOp(9235 fn addExtendedMultiOp(
...@@ -9272,7 +9262,7 @@ const GenZir = struct {...@@ -9272,7 +9262,7 @@ const GenZir = struct {
9272 });9262 });
9273 gz.instructions.appendAssumeCapacity(new_index);9263 gz.instructions.appendAssumeCapacity(new_index);
9274 astgen.appendRefsAssumeCapacity(operands);9264 astgen.appendRefsAssumeCapacity(operands);
9275 return gz.indexToRef(new_index);9265 return indexToRef(new_index);
9276 }9266 }
92779267
9278 fn addArrayTypeSentinel(9268 fn addArrayTypeSentinel(
...@@ -9298,7 +9288,7 @@ const GenZir = struct {...@@ -9298,7 +9288,7 @@ const GenZir = struct {
9298 } },9288 } },
9299 });9289 });
9300 gz.instructions.appendAssumeCapacity(new_index);9290 gz.instructions.appendAssumeCapacity(new_index);
9301 return gz.indexToRef(new_index);9291 return indexToRef(new_index);
9302 }9292 }
93039293
9304 fn addUnTok(9294 fn addUnTok(
...@@ -9457,7 +9447,7 @@ const GenZir = struct {...@@ -9457,7 +9447,7 @@ const GenZir = struct {
9457 } },9447 } },
9458 });9448 });
9459 gz.instructions.appendAssumeCapacity(new_index);9449 gz.instructions.appendAssumeCapacity(new_index);
9460 return gz.indexToRef(new_index);9450 return indexToRef(new_index);
9461 }9451 }
94629452
9463 fn addAsm(9453 fn addAsm(
...@@ -9465,7 +9455,7 @@ const GenZir = struct {...@@ -9465,7 +9455,7 @@ const GenZir = struct {
9465 args: struct {9455 args: struct {
9466 /// Absolute node index. This function does the conversion to offset from Decl.9456 /// Absolute node index. This function does the conversion to offset from Decl.
9467 node: ast.Node.Index,9457 node: ast.Node.Index,
9468 asm_source: Zir.Inst.Ref,9458 asm_source: u32,
9469 output_type_bits: u32,9459 output_type_bits: u32,
9470 is_volatile: bool,9460 is_volatile: bool,
9471 outputs: []const Zir.Inst.Asm.Output,9461 outputs: []const Zir.Inst.Asm.Output,
...@@ -9515,7 +9505,7 @@ const GenZir = struct {...@@ -9515,7 +9505,7 @@ const GenZir = struct {
9515 } },9505 } },
9516 });9506 });
9517 gz.instructions.appendAssumeCapacity(new_index);9507 gz.instructions.appendAssumeCapacity(new_index);
9518 return gz.indexToRef(new_index);9508 return indexToRef(new_index);
9519 }9509 }
95209510
9521 /// Note that this returns a `Zir.Inst.Index` not a ref.9511 /// Note that this returns a `Zir.Inst.Index` not a ref.
...@@ -9693,7 +9683,7 @@ const GenZir = struct {...@@ -9693,7 +9683,7 @@ const GenZir = struct {
9693 }9683 }
96949684
9695 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {9685 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
9696 return gz.indexToRef(try gz.addAsIndex(inst));9686 return indexToRef(try gz.addAsIndex(inst));
9697 }9687 }
96989688
9699 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {9689 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
...@@ -9840,3 +9830,18 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {...@@ -9840,3 +9830,18 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
9840 astgen.source_line = line;9830 astgen.source_line = line;
9841 astgen.source_column = column;9831 astgen.source_column = column;
9842}9832}
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+84-41
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Compilation = @This();1const Compilation = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
4const mem = std.mem;5const mem = std.mem;
5const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;7const assert = std.debug.assert;
...@@ -13,7 +14,7 @@ const target_util = @import("target.zig");...@@ -13,7 +14,7 @@ const target_util = @import("target.zig");
13const Package = @import("Package.zig");14const Package = @import("Package.zig");
14const link = @import("link.zig");15const link = @import("link.zig");
15const trace = @import("tracy.zig").trace;16const trace = @import("tracy.zig").trace;
16const liveness = @import("liveness.zig");17const Liveness = @import("Liveness.zig");
17const build_options = @import("build_options");18const build_options = @import("build_options");
18const LibCInstallation = @import("libc_installation.zig").LibCInstallation;19const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
19const glibc = @import("glibc.zig");20const glibc = @import("glibc.zig");
...@@ -148,7 +149,7 @@ emit_docs: ?EmitLoc,...@@ -148,7 +149,7 @@ emit_docs: ?EmitLoc,
148work_queue_wait_group: WaitGroup,149work_queue_wait_group: WaitGroup,
149astgen_wait_group: WaitGroup,150astgen_wait_group: WaitGroup,
150151
151pub const InnerError = Module.InnerError;152pub const SemaError = Module.SemaError;
152153
153pub const CRTFile = struct {154pub const CRTFile = struct {
154 lock: Cache.Lock,155 lock: Cache.Lock,
...@@ -168,8 +169,10 @@ pub const CSourceFile = struct {...@@ -168,8 +169,10 @@ pub const CSourceFile = struct {
168};169};
169170
170const Job = union(enum) {171const 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.
172 codegen_decl: *Module.Decl,173 codegen_decl: *Module.Decl,
174 /// Write the machine code for a function to the output file.
175 codegen_func: *Module.Fn,
173 /// Render the .h file snippet for the Decl.176 /// Render the .h file snippet for the Decl.
174 emit_h_decl: *Module.Decl,177 emit_h_decl: *Module.Decl,
175 /// The Decl needs to be analyzed and possibly export itself.178 /// The Decl needs to be analyzed and possibly export itself.
...@@ -907,7 +910,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -907,7 +910,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
907 // comptime conditions910 // comptime conditions
908 ((build_options.have_llvm and comptime std.Target.current.isDarwin()) and911 ((build_options.have_llvm and comptime std.Target.current.isDarwin()) and
909 // runtime conditions912 // runtime conditions
910 (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()));913 (use_lld and builtin.os.tag == .macos and options.target.isDarwin()));
911914
912 const sysroot = blk: {915 const sysroot = blk: {
913 if (options.sysroot) |sysroot| {916 if (options.sysroot) |sysroot| {
...@@ -1922,6 +1925,7 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {...@@ -1922,6 +1925,7 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
1922}1925}
19231926
1924pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {1927pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
1928 const gpa = self.gpa;
1925 // If the terminal is dumb, we dont want to show the user all the1929 // If the terminal is dumb, we dont want to show the user all the
1926 // output.1930 // output.
1927 var progress: std.Progress = .{ .dont_print_on_dumb = true };1931 var progress: std.Progress = .{ .dont_print_on_dumb = true };
...@@ -2003,33 +2007,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2003,33 +2007,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2003 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2007 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2004 const module = self.bin_file.options.module.?;2008 const module = self.bin_file.options.module.?;
2005 assert(decl.has_tv);2009 assert(decl.has_tv);
2006 if (decl.val.castTag(.function)) |payload| {
2007 const func = payload.data;
2008 switch (func.state) {
2009 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
2010 error.AnalysisFail => {
2011 assert(func.state != .in_progress);
2012 continue;
2013 },
2014 error.OutOfMemory => return error.OutOfMemory,
2015 },
2016 .in_progress => unreachable,
2017 .inline_only => unreachable, // don't queue work for this
2018 .sema_failure, .dependency_failure => continue,
2019 .success => {},
2020 }
2021 // Here we tack on additional allocations to the Decl's arena. The allocations
2022 // are lifetime annotations in the ZIR.
2023 var decl_arena = decl.value_arena.?.promote(module.gpa);
2024 defer decl.value_arena.?.* = decl_arena.state;
2025 log.debug("analyze liveness of {s}", .{decl.name});
2026 try liveness.analyze(module.gpa, &decl_arena.allocator, func.body);
2027
2028 if (std.builtin.mode == .Debug and self.verbose_air) {
2029 func.dump(module.*);
2030 }
2031 }
2032
2033 assert(decl.ty.hasCodeGenBits());2010 assert(decl.ty.hasCodeGenBits());
20342011
2035 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {2012 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
...@@ -2039,9 +2016,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2039,9 +2016,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2039 continue;2016 continue;
2040 },2017 },
2041 else => {2018 else => {
2042 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.count() + 1);2019 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2043 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(2020 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2044 module.gpa,2021 gpa,
2045 decl.srcLoc(),2022 decl.srcLoc(),
2046 "unable to codegen: {s}",2023 "unable to codegen: {s}",
2047 .{@errorName(err)},2024 .{@errorName(err)},
...@@ -2052,6 +2029,72 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2052,6 +2029,72 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2052 };2029 };
2053 },2030 },
2054 },2031 },
2032 .codegen_func => |func| switch (func.owner_decl.analysis) {
2033 .unreferenced => unreachable,
2034 .in_progress => unreachable,
2035 .outdated => unreachable,
2036
2037 .file_failure,
2038 .sema_failure,
2039 .codegen_failure,
2040 .dependency_failure,
2041 .sema_failure_retryable,
2042 => continue,
2043
2044 .complete, .codegen_failure_retryable => {
2045 if (build_options.omit_stage2)
2046 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2047 switch (func.state) {
2048 .sema_failure, .dependency_failure => continue,
2049 .queued => {},
2050 .in_progress => unreachable,
2051 .inline_only => unreachable, // don't queue work for this
2052 .success => unreachable, // don't queue it twice
2053 }
2054
2055 const module = self.bin_file.options.module.?;
2056 const decl = func.owner_decl;
2057
2058 var air = module.analyzeFnBody(decl, func) catch |err| switch (err) {
2059 error.AnalysisFail => {
2060 assert(func.state != .in_progress);
2061 continue;
2062 },
2063 error.OutOfMemory => return error.OutOfMemory,
2064 };
2065 defer air.deinit(gpa);
2066
2067 log.debug("analyze liveness of {s}", .{decl.name});
2068 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
2069 defer liveness.deinit(gpa);
2070
2071 if (builtin.mode == .Debug and self.verbose_air) {
2072 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2073 @import("print_air.zig").dump(gpa, air, decl.namespace.file_scope.zir, liveness);
2074 std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
2075 }
2076
2077 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2078 error.OutOfMemory => return error.OutOfMemory,
2079 error.AnalysisFail => {
2080 decl.analysis = .codegen_failure;
2081 continue;
2082 },
2083 else => {
2084 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2085 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2086 gpa,
2087 decl.srcLoc(),
2088 "unable to codegen: {s}",
2089 .{@errorName(err)},
2090 ));
2091 decl.analysis = .codegen_failure_retryable;
2092 continue;
2093 },
2094 };
2095 continue;
2096 },
2097 },
2055 .emit_h_decl => |decl| switch (decl.analysis) {2098 .emit_h_decl => |decl| switch (decl.analysis) {
2056 .unreferenced => unreachable,2099 .unreferenced => unreachable,
2057 .in_progress => unreachable,2100 .in_progress => unreachable,
...@@ -2070,7 +2113,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2070,7 +2113,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2070 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2113 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2071 const module = self.bin_file.options.module.?;2114 const module = self.bin_file.options.module.?;
2072 const emit_h = module.emit_h.?;2115 const emit_h = module.emit_h.?;
2073 _ = try emit_h.decl_table.getOrPut(module.gpa, decl);2116 _ = try emit_h.decl_table.getOrPut(gpa, decl);
2074 const decl_emit_h = decl.getEmitH(module);2117 const decl_emit_h = decl.getEmitH(module);
2075 const fwd_decl = &decl_emit_h.fwd_decl;2118 const fwd_decl = &decl_emit_h.fwd_decl;
2076 fwd_decl.shrinkRetainingCapacity(0);2119 fwd_decl.shrinkRetainingCapacity(0);
...@@ -2079,7 +2122,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2079,7 +2122,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2079 .module = module,2122 .module = module,
2080 .error_msg = null,2123 .error_msg = null,
2081 .decl = decl,2124 .decl = decl,
2082 .fwd_decl = fwd_decl.toManaged(module.gpa),2125 .fwd_decl = fwd_decl.toManaged(gpa),
2083 // we don't want to emit optionals and error unions to headers since they have no ABI2126 // we don't want to emit optionals and error unions to headers since they have no ABI
2084 .typedefs = undefined,2127 .typedefs = undefined,
2085 };2128 };
...@@ -2087,14 +2130,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2087,14 +2130,14 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20872130
2088 c_codegen.genHeader(&dg) catch |err| switch (err) {2131 c_codegen.genHeader(&dg) catch |err| switch (err) {
2089 error.AnalysisFail => {2132 error.AnalysisFail => {
2090 try emit_h.failed_decls.put(module.gpa, decl, dg.error_msg.?);2133 try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?);
2091 continue;2134 continue;
2092 },2135 },
2093 else => |e| return e,2136 else => |e| return e,
2094 };2137 };
20952138
2096 fwd_decl.* = dg.fwd_decl.moveToUnmanaged();2139 fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
2097 fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len);2140 fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len);
2098 },2141 },
2099 },2142 },
2100 .analyze_decl => |decl| {2143 .analyze_decl => |decl| {
...@@ -2111,9 +2154,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2111,9 +2154,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2111 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2154 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2112 const module = self.bin_file.options.module.?;2155 const module = self.bin_file.options.module.?;
2113 self.bin_file.updateDeclLineNumber(module, decl) catch |err| {2156 self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
2114 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.count() + 1);2157 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2115 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(2158 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2116 module.gpa,2159 gpa,
2117 decl.srcLoc(),2160 decl.srcLoc(),
2118 "unable to update line number: {s}",2161 "unable to update line number: {s}",
2119 .{@errorName(err)},2162 .{@errorName(err)},
...@@ -3147,7 +3190,7 @@ pub fn addCCArgs(...@@ -3147,7 +3190,7 @@ pub fn addCCArgs(
3147 try argv.appendSlice(comp.clang_argv);3190 try argv.appendSlice(comp.clang_argv);
3148}3191}
31493192
3150fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {3193fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) SemaError {
3151 @setCold(true);3194 @setCold(true);
3152 const err_msg = blk: {3195 const err_msg = blk: {
3153 const msg = try std.fmt.allocPrint(comp.gpa, format, args);3196 const msg = try std.fmt.allocPrint(comp.gpa, format, args);
...@@ -3168,7 +3211,7 @@ fn failCObjWithOwnedErrorMsg(...@@ -3168,7 +3211,7 @@ fn failCObjWithOwnedErrorMsg(
3168 comp: *Compilation,3211 comp: *Compilation,
3169 c_object: *CObject,3212 c_object: *CObject,
3170 err_msg: *CObject.ErrorMsg,3213 err_msg: *CObject.ErrorMsg,
3171) InnerError {3214) SemaError {
3172 @setCold(true);3215 @setCold(true);
3173 {3216 {
3174 const lock = comp.mutex.acquire();3217 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;...@@ -21,7 +21,7 @@ const Type = @import("type.zig").Type;
21const TypedValue = @import("TypedValue.zig");21const TypedValue = @import("TypedValue.zig");
22const Package = @import("Package.zig");22const Package = @import("Package.zig");
23const link = @import("link.zig");23const link = @import("link.zig");
24const ir = @import("air.zig");24const Air = @import("Air.zig");
25const Zir = @import("Zir.zig");25const Zir = @import("Zir.zig");
26const trace = @import("tracy.zig").trace;26const trace = @import("tracy.zig").trace;
27const AstGen = @import("AstGen.zig");27const AstGen = @import("AstGen.zig");
...@@ -739,8 +739,6 @@ pub const Union = struct {...@@ -739,8 +739,6 @@ pub const Union = struct {
739pub const Fn = struct {739pub const Fn = struct {
740 /// The Decl that corresponds to the function itself.740 /// The Decl that corresponds to the function itself.
741 owner_decl: *Decl,741 owner_decl: *Decl,
742 /// undefined unless analysis state is `success`.
743 body: ir.Body,
744 /// The ZIR instruction that is a function instruction. Use this to find742 /// The ZIR instruction that is a function instruction. Use this to find
745 /// the body. We store this rather than the body directly so that when ZIR743 /// the body. We store this rather than the body directly so that when ZIR
746 /// is regenerated on update(), we can map this to the new corresponding744 /// is regenerated on update(), we can map this to the new corresponding
...@@ -771,11 +769,6 @@ pub const Fn = struct {...@@ -771,11 +769,6 @@ pub const Fn = struct {
771 success,769 success,
772 };770 };
773771
774 /// For debugging purposes.
775 pub fn dump(func: *Fn, mod: Module) void {
776 ir.dumpFn(mod, func);
777 }
778
779 pub fn deinit(func: *Fn, gpa: *Allocator) void {772 pub fn deinit(func: *Fn, gpa: *Allocator) void {
780 if (func.getInferredErrorSet()) |map| {773 if (func.getInferredErrorSet()) |map| {
781 map.deinit(gpa);774 map.deinit(gpa);
...@@ -1157,7 +1150,7 @@ pub const Scope = struct {...@@ -1157,7 +1150,7 @@ pub const Scope = struct {
1157 /// This can vary during inline or comptime function calls. See `Sema.owner_decl`1150 /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
1158 /// for the one that will be the same for all Block instances.1151 /// for the one that will be the same for all Block instances.
1159 src_decl: *Decl,1152 src_decl: *Decl,
1160 instructions: ArrayListUnmanaged(*ir.Inst),1153 instructions: ArrayListUnmanaged(Air.Inst.Index),
1161 label: ?*Label = null,1154 label: ?*Label = null,
1162 inlining: ?*Inlining,1155 inlining: ?*Inlining,
1163 /// If runtime_index is not 0 then one of these is guaranteed to be non null.1156 /// If runtime_index is not 0 then one of these is guaranteed to be non null.
...@@ -1189,14 +1182,14 @@ pub const Scope = struct {...@@ -1189,14 +1182,14 @@ pub const Scope = struct {
1189 };1182 };
11901183
1191 pub const Merges = struct {1184 pub const Merges = struct {
1192 block_inst: *ir.Inst.Block,1185 block_inst: Air.Inst.Index,
1193 /// Separate array list from break_inst_list so that it can be passed directly1186 /// Separate array list from break_inst_list so that it can be passed directly
1194 /// to resolvePeerTypes.1187 /// to resolvePeerTypes.
1195 results: ArrayListUnmanaged(*ir.Inst),1188 results: ArrayListUnmanaged(Air.Inst.Ref),
1196 /// Keeps track of the break instructions so that the operand can be replaced1189 /// Keeps track of the break instructions so that the operand can be replaced
1197 /// if we need to add type coercion at the end of block analysis.1190 /// if we need to add type coercion at the end of block analysis.
1198 /// Same indexes, capacity, length as `results`.1191 /// Same indexes, capacity, length as `results`.
1199 br_list: ArrayListUnmanaged(*ir.Inst.Br),1192 br_list: ArrayListUnmanaged(Air.Inst.Index),
1200 };1193 };
12011194
1202 /// For debugging purposes.1195 /// For debugging purposes.
...@@ -1233,185 +1226,94 @@ pub const Scope = struct {...@@ -1233,185 +1226,94 @@ pub const Scope = struct {
1233 return block.src_decl.namespace.file_scope;1226 return block.src_decl.namespace.file_scope;
1234 }1227 }
12351228
1236 pub fn addNoOp(1229 pub fn addTy(
1237 block: *Scope.Block,1230 block: *Block,
1238 src: LazySrcLoc,1231 tag: Air.Inst.Tag,
1239 ty: Type,1232 ty: Type,
1240 comptime tag: ir.Inst.Tag,1233 ) error{OutOfMemory}!Air.Inst.Ref {
1241 ) !*ir.Inst {1234 return block.addInst(.{
1242 const inst = try block.sema.arena.create(tag.Type());1235 .tag = tag,
1243 inst.* = .{1236 .data = .{ .ty = ty },
1244 .base = .{1237 });
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;
1252 }1238 }
12531239
1254 pub fn addUnOp(1240 pub fn addTyOp(
1255 block: *Scope.Block,1241 block: *Block,
1256 src: LazySrcLoc,1242 tag: Air.Inst.Tag,
1257 ty: Type,1243 ty: Type,
1258 tag: ir.Inst.Tag,1244 operand: Air.Inst.Ref,
1259 operand: *ir.Inst,1245 ) error{OutOfMemory}!Air.Inst.Ref {
1260 ) !*ir.Inst {1246 return block.addInst(.{
1261 const inst = try block.sema.arena.create(ir.Inst.UnOp);1247 .tag = tag,
1262 inst.* = .{1248 .data = .{ .ty_op = .{
1263 .base = .{1249 .ty = try block.sema.addType(ty),
1264 .tag = tag,1250 .operand = operand,
1265 .ty = ty,1251 } },
1266 .src = src,1252 });
1267 },
1268 .operand = operand,
1269 };
1270 try block.instructions.append(block.sema.gpa, &inst.base);
1271 return &inst.base;
1272 }1253 }
12731254
1274 pub fn addBinOp(1255 pub fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
1275 block: *Scope.Block,1256 return block.addInst(.{
1276 src: LazySrcLoc,1257 .tag = tag,
1277 ty: Type,1258 .data = .{ .no_op = {} },
1278 tag: ir.Inst.Tag,1259 });
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;
1294 }1260 }
12951261
1296 pub fn addBr(1262 pub fn addUnOp(
1297 scope_block: *Scope.Block,1263 block: *Block,
1298 src: LazySrcLoc,1264 tag: Air.Inst.Tag,
1299 target_block: *ir.Inst.Block,1265 operand: Air.Inst.Ref,
1300 operand: *ir.Inst,1266 ) error{OutOfMemory}!Air.Inst.Ref {
1301 ) !*ir.Inst.Br {1267 return block.addInst(.{
1302 const inst = try scope_block.sema.arena.create(ir.Inst.Br);1268 .tag = tag,
1303 inst.* = .{1269 .data = .{ .un_op = operand },
1304 .base = .{1270 });
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;
1314 }1271 }
13151272
1316 pub fn addCondBr(1273 pub fn addBr(
1317 block: *Scope.Block,1274 block: *Block,
1318 src: LazySrcLoc,1275 target_block: Air.Inst.Index,
1319 condition: *ir.Inst,1276 operand: Air.Inst.Ref,
1320 then_body: ir.Body,1277 ) error{OutOfMemory}!Air.Inst.Ref {
1321 else_body: ir.Body,1278 return block.addInst(.{
1322 ) !*ir.Inst {1279 .tag = .br,
1323 const inst = try block.sema.arena.create(ir.Inst.CondBr);1280 .data = .{ .br = .{
1324 inst.* = .{1281 .block_inst = target_block,
1325 .base = .{1282 .operand = operand,
1326 .tag = .condbr,1283 } },
1327 .ty = Type.initTag(.noreturn),1284 });
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;
1336 }1285 }
13371286
1338 pub fn addCall(1287 pub fn addBinOp(
1339 block: *Scope.Block,1288 block: *Block,
1340 src: LazySrcLoc,1289 tag: Air.Inst.Tag,
1341 ty: Type,1290 lhs: Air.Inst.Ref,
1342 func: *ir.Inst,1291 rhs: Air.Inst.Ref,
1343 args: []const *ir.Inst,1292 ) error{OutOfMemory}!Air.Inst.Ref {
1344 ) !*ir.Inst {1293 return block.addInst(.{
1345 const inst = try block.sema.arena.create(ir.Inst.Call);1294 .tag = tag,
1346 inst.* = .{1295 .data = .{ .bin_op = .{
1347 .base = .{1296 .lhs = lhs,
1348 .tag = .call,1297 .rhs = rhs,
1349 .ty = ty,1298 } },
1350 .src = src,1299 });
1351 },
1352 .func = func,
1353 .args = args,
1354 };
1355 try block.instructions.append(block.sema.gpa, &inst.base);
1356 return &inst.base;
1357 }1300 }
13581301
1359 pub fn addSwitchBr(1302 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
1360 block: *Scope.Block,1303 return Air.indexToRef(try block.addInstAsIndex(inst));
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;
1379 }1304 }
13801305
1381 pub fn addDbgStmt(block: *Scope.Block, src: LazySrcLoc, line: u32, column: u32) !*ir.Inst {1306 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
1382 const inst = try block.sema.arena.create(ir.Inst.DbgStmt);1307 const sema = block.sema;
1383 inst.* = .{1308 const gpa = sema.gpa;
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 }
13951309
1396 pub fn addStructFieldPtr(1310 try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
1397 block: *Scope.Block,1311 try block.instructions.ensureUnusedCapacity(gpa, 1);
1398 src: LazySrcLoc,1312
1399 ty: Type,1313 const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
1400 struct_ptr: *ir.Inst,1314 sema.air_instructions.appendAssumeCapacity(inst);
1401 field_index: u32,1315 block.instructions.appendAssumeCapacity(result_index);
1402 ) !*ir.Inst {1316 return result_index;
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;
1415 }1317 }
1416 };1318 };
1417};1319};
...@@ -2130,7 +2032,8 @@ pub const LazySrcLoc = union(enum) {...@@ -2130,7 +2032,8 @@ pub const LazySrcLoc = union(enum) {
2130 }2032 }
2131};2033};
21322034
2133pub const InnerError = error{ OutOfMemory, AnalysisFail };2035pub const SemaError = error{ OutOfMemory, AnalysisFail };
2036pub const CompileError = error{ OutOfMemory, AnalysisFail, NeededSourceLocation };
21342037
2135pub fn deinit(mod: *Module) void {2038pub fn deinit(mod: *Module) void {
2136 const gpa = mod.gpa;2039 const gpa = mod.gpa;
...@@ -2769,7 +2672,7 @@ pub fn mapOldZirToNew(...@@ -2769,7 +2672,7 @@ pub fn mapOldZirToNew(
2769 }2672 }
2770}2673}
27712674
2772pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {2675pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
2773 const tracy = trace(@src());2676 const tracy = trace(@src());
2774 defer tracy.end();2677 defer tracy.end();
27752678
...@@ -2869,7 +2772,7 @@ pub fn semaPkg(mod: *Module, pkg: *Package) !void {...@@ -2869,7 +2772,7 @@ pub fn semaPkg(mod: *Module, pkg: *Package) !void {
28692772
2870/// Regardless of the file status, will create a `Decl` so that we2773/// Regardless of the file status, will create a `Decl` so that we
2871/// can track dependencies and re-analyze when the file becomes outdated.2774/// 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 {
2873 const tracy = trace(@src());2776 const tracy = trace(@src());
2874 defer tracy.end();2777 defer tracy.end();
28752778
...@@ -2999,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2999,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2999 decl.generation = mod.generation;2902 decl.generation = mod.generation;
3000 return false;2903 return false;
3001 }2904 }
2905 log.debug("semaDecl {*} ({s})", .{ decl, decl.name });
30022906
3003 var block_scope: Scope.Block = .{2907 var block_scope: Scope.Block = .{
3004 .parent = null,2908 .parent = null,
...@@ -3035,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3035,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3035 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);2939 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
30362940
3037 if (decl_tv.val.castTag(.function)) |fn_payload| {2941 if (decl_tv.val.castTag(.function)) |fn_payload| {
3038 var prev_type_has_bits = false;2942 const func = fn_payload.data;
3039 var prev_is_inline = false;2943 const owns_tv = func.owner_decl == decl;
3040 var type_changed = true;2944 if (owns_tv) {
30412945 var prev_type_has_bits = false;
3042 if (decl.has_tv) {2946 var prev_is_inline = false;
3043 prev_type_has_bits = decl.ty.hasCodeGenBits();2947 var type_changed = true;
3044 type_changed = !decl.ty.eql(decl_tv.ty);2948
3045 if (decl.getFunction()) |prev_func| {2949 if (decl.has_tv) {
3046 prev_is_inline = prev_func.state == .inline_only;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);
3047 }2956 }
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;2958 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3063 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {2959 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3064 // We don't fully codegen the decl until later, but we do need to reserve a global2960 decl.align_val = try align_val.copy(&decl_arena.allocator);
3065 // offset table index for it. This allows us to codegen decls out of dependency order,2961 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3066 // increasing how many computations can be done in parallel.2962 decl.has_tv = true;
3067 try mod.comp.bin_file.allocateDeclIndexes(decl);2963 decl.owns_tv = owns_tv;
3068 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });2964 decl_arena_state.* = decl_arena.state;
3069 if (type_changed and mod.emit_h != null) {2965 decl.value_arena = decl_arena_state;
3070 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });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);
3071 }2981 }
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) {2983 if (decl.is_exported) {
3077 const export_src = src; // TODO make this point at `export` token2984 const export_src = src; // TODO make this point at `export` token
3078 if (is_inline) {2985 if (is_inline) {
3079 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});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);
3080 }2990 }
3081 // The scope needs to have the decl in it.2991 return type_changed or is_inline != prev_is_inline;
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);
3090 }2992 }
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;3000 decl.owns_tv = false;
3093 var queue_linker_work = false;3001 var queue_linker_work = false;
3094 if (decl_tv.val.castTag(.variable)) |payload| {3002 if (decl_tv.val.castTag(.variable)) |payload| {
3095 const variable = payload.data;3003 const variable = payload.data;
3096 if (variable.owner_decl == decl) {3004 if (variable.owner_decl == decl) {
3097 decl.owns_tv = true;3005 decl.owns_tv = true;
3098 queue_linker_work = true;3006 queue_linker_work = true;
30993007
3100 const copied_init = try variable.init.copy(&decl_arena.allocator);3008 const copied_init = try variable.init.copy(&decl_arena.allocator);
3101 variable.init = copied_init;3009 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 }
3109 }3010 }
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);3019 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3112 decl.val = try decl_tv.val.copy(&decl_arena.allocator);3020 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3113 decl.align_val = try align_val.copy(&decl_arena.allocator);3021 decl.align_val = try align_val.copy(&decl_arena.allocator);
3114 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);3022 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3115 decl.has_tv = true;3023 decl.has_tv = true;
3116 decl_arena_state.* = decl_arena.state;3024 decl_arena_state.* = decl_arena.state;
3117 decl.value_arena = decl_arena_state;3025 decl.value_arena = decl_arena_state;
3118 decl.analysis = .complete;3026 decl.analysis = .complete;
3119 decl.generation = mod.generation;3027 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 });
31243028
3125 if (type_changed and mod.emit_h != null) {3029 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3126 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });3030 try mod.comp.bin_file.allocateDeclIndexes(decl);
3127 }3031 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
3128 }
31293032
3130 if (decl.is_exported) {3033 if (type_changed and mod.emit_h != null) {
3131 const export_src = src; // TODO point to the export token3034 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
3132 // The scope needs to have the decl in it.
3133 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3134 }3035 }
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);
3137 }3042 }
3043
3044 return type_changed;
3138}3045}
31393046
3140/// Returns the depender's index of the dependee.3047/// Returns the depender's index of the dependee.
...@@ -3284,7 +3191,7 @@ pub fn scanNamespace(...@@ -3284,7 +3191,7 @@ pub fn scanNamespace(
3284 extra_start: usize,3191 extra_start: usize,
3285 decls_len: u32,3192 decls_len: u32,
3286 parent_decl: *Decl,3193 parent_decl: *Decl,
3287) InnerError!usize {3194) SemaError!usize {
3288 const tracy = trace(@src());3195 const tracy = trace(@src());
3289 defer tracy.end();3196 defer tracy.end();
32903197
...@@ -3331,7 +3238,7 @@ const ScanDeclIter = struct {...@@ -3331,7 +3238,7 @@ const ScanDeclIter = struct {
3331 unnamed_test_index: usize = 0,3238 unnamed_test_index: usize = 0,
3332};3239};
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 {
3335 const tracy = trace(@src());3242 const tracy = trace(@src());
3336 defer tracy.end();3243 defer tracy.end();
33373244
...@@ -3585,39 +3492,25 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {...@@ -3585,39 +3492,25 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
3585 mod.gpa.free(kv.value);3492 mod.gpa.free(kv.value);
3586}3493}
35873494
3588pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {3495pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3589 const tracy = trace(@src());3496 const tracy = trace(@src());
3590 defer tracy.end();3497 defer tracy.end();
35913498
3499 const gpa = mod.gpa;
3500
3592 // Use the Decl's arena for function memory.3501 // 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);
3594 defer decl.value_arena.?.* = arena.state;3503 defer decl.value_arena.?.* = arena.state;
35953504
3596 const fn_ty = decl.ty;3505 const fn_ty = decl.ty;
3597 const param_inst_list = try mod.gpa.alloc(*ir.Inst, fn_ty.fnParamLen());3506 const param_inst_list = try gpa.alloc(Air.Inst.Ref, fn_ty.fnParamLen());
3598 defer mod.gpa.free(param_inst_list);3507 defer 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;
36153508
3616 var sema: Sema = .{3509 var sema: Sema = .{
3617 .mod = mod,3510 .mod = mod,
3618 .gpa = mod.gpa,3511 .gpa = gpa,
3619 .arena = &arena.allocator,3512 .arena = &arena.allocator,
3620 .code = zir,3513 .code = decl.namespace.file_scope.zir,
3621 .owner_decl = decl,3514 .owner_decl = decl,
3622 .namespace = decl.namespace,3515 .namespace = decl.namespace,
3623 .func = func,3516 .func = func,
...@@ -3626,6 +3519,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {...@@ -3626,6 +3519,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
3626 };3519 };
3627 defer sema.deinit();3520 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
3629 var inner_block: Scope.Block = .{3527 var inner_block: Scope.Block = .{
3630 .parent = null,3528 .parent = null,
3631 .sema = &sema,3529 .sema = &sema,
...@@ -3634,20 +3532,50 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {...@@ -3634,20 +3532,50 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
3634 .inlining = null,3532 .inlining = null,
3635 .is_comptime = false,3533 .is_comptime = false,
3636 };3534 };
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 instructions3537 // AIR requires the arg parameters to be the first N instructions.
3640 try inner_block.instructions.appendSlice(mod.gpa, param_inst_list);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
3642 func.state = .in_progress;3556 func.state = .in_progress;
3643 log.debug("set {s} to in_progress", .{decl.name});3557 log.debug("set {s} to in_progress", .{decl.name});
36443558
3645 try sema.analyzeFnBody(&inner_block, func.zir_body_inst);3559 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
3648 func.state = .success;3570 func.state = .success;
3649 func.body = .{ .instructions = instructions };
3650 log.debug("set {s} to success", .{decl.name});3571 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 };
3651}3579}
36523580
3653fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {3581fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
...@@ -3801,94 +3729,6 @@ pub fn analyzeExport(...@@ -3801,94 +3729,6 @@ pub fn analyzeExport(
3801 de_gop.value_ptr.*[de_gop.value_ptr.len - 1] = new_export;3729 de_gop.value_ptr.*[de_gop.value_ptr.len - 1] = new_export;
3802 errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);3730 errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
3803}3731}
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
3893pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {3733pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
3894 const scope_decl = scope.ownerDecl().?;3734 const scope_decl = scope.ownerDecl().?;
...@@ -4006,7 +3846,7 @@ pub fn fail(...@@ -4006,7 +3846,7 @@ pub fn fail(
4006 src: LazySrcLoc,3846 src: LazySrcLoc,
4007 comptime format: []const u8,3847 comptime format: []const u8,
4008 args: anytype,3848 args: anytype,
4009) InnerError {3849) CompileError {
4010 const err_msg = try mod.errMsg(scope, src, format, args);3850 const err_msg = try mod.errMsg(scope, src, format, args);
4011 return mod.failWithOwnedErrorMsg(scope, err_msg);3851 return mod.failWithOwnedErrorMsg(scope, err_msg);
4012}3852}
...@@ -4019,7 +3859,7 @@ pub fn failTok(...@@ -4019,7 +3859,7 @@ pub fn failTok(
4019 token_index: ast.TokenIndex,3859 token_index: ast.TokenIndex,
4020 comptime format: []const u8,3860 comptime format: []const u8,
4021 args: anytype,3861 args: anytype,
4022) InnerError {3862) CompileError {
4023 const src = scope.srcDecl().?.tokSrcLoc(token_index);3863 const src = scope.srcDecl().?.tokSrcLoc(token_index);
4024 return mod.fail(scope, src, format, args);3864 return mod.fail(scope, src, format, args);
4025}3865}
...@@ -4032,18 +3872,21 @@ pub fn failNode(...@@ -4032,18 +3872,21 @@ pub fn failNode(
4032 node_index: ast.Node.Index,3872 node_index: ast.Node.Index,
4033 comptime format: []const u8,3873 comptime format: []const u8,
4034 args: anytype,3874 args: anytype,
4035) InnerError {3875) CompileError {
4036 const src = scope.srcDecl().?.nodeSrcLoc(node_index);3876 const src = scope.srcDecl().?.nodeSrcLoc(node_index);
4037 return mod.fail(scope, src, format, args);3877 return mod.fail(scope, src, format, args);
4038}3878}
40393879
4040pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {3880pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) CompileError {
4041 @setCold(true);3881 @setCold(true);
40423882
4043 {3883 {
4044 errdefer err_msg.destroy(mod.gpa);3884 errdefer err_msg.destroy(mod.gpa);
4045 try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.count() + 1);3885 if (err_msg.src_loc.lazy == .unneeded) {
4046 try mod.failed_files.ensureCapacity(mod.gpa, mod.failed_files.count() + 1);3886 return error.NeededSourceLocation;
3887 }
3888 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
3889 try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1);
4047 }3890 }
4048 switch (scope.tag) {3891 switch (scope.tag) {
4049 .block => {3892 .block => {
...@@ -4301,13 +4144,11 @@ pub fn floatMul(...@@ -4301,13 +4144,11 @@ pub fn floatMul(
4301}4144}
43024145
4303pub fn simplePtrType(4146pub fn simplePtrType(
4304 mod: *Module,
4305 arena: *Allocator,4147 arena: *Allocator,
4306 elem_ty: Type,4148 elem_ty: Type,
4307 mutable: bool,4149 mutable: bool,
4308 size: std.builtin.TypeInfo.Pointer.Size,4150 size: std.builtin.TypeInfo.Pointer.Size,
4309) Allocator.Error!Type {4151) Allocator.Error!Type {
4310 _ = mod;
4311 if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {4152 if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
4312 return Type.initTag(.const_slice_u8);4153 return Type.initTag(.const_slice_u8);
4313 }4154 }
...@@ -4424,38 +4265,6 @@ pub fn errorUnionType(...@@ -4424,38 +4265,6 @@ pub fn errorUnionType(
4424 });4265 });
4425}4266}
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
4459pub fn getTarget(mod: Module) Target {4268pub fn getTarget(mod: Module) Target {
4460 return mod.comp.bin_file.options.target;4269 return mod.comp.bin_file.options.target;
4461}4270}
...@@ -4576,7 +4385,7 @@ pub const SwitchProngSrc = union(enum) {...@@ -4576,7 +4385,7 @@ pub const SwitchProngSrc = union(enum) {
4576 }4385 }
4577};4386};
45784387
4579pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {4388pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void {
4580 const tracy = trace(@src());4389 const tracy = trace(@src());
4581 defer tracy.end();4390 defer tracy.end();
45824391
...@@ -4726,7 +4535,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {...@@ -4726,7 +4535,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {
4726 }4535 }
4727}4536}
47284537
4729pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void {4538pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
4730 const tracy = trace(@src());4539 const tracy = trace(@src());
4731 defer tracy.end();4540 defer tracy.end();
47324541
src/Sema.zig+1755-1461
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1//! Semantic analysis of ZIR instructions.1//! Semantic analysis of ZIR instructions.
2//! Shared to every Block. Stored on the stack.2//! 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.
4//! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions.4//! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions.
5//! Does type checking, comptime control flow, and safety-check generation.5//! Does type checking, comptime control flow, and safety-check generation.
6//! This is the the heart of the Zig compiler.6//! This is the the heart of the Zig compiler.
...@@ -11,6 +11,10 @@ gpa: *Allocator,...@@ -11,6 +11,10 @@ gpa: *Allocator,
11/// Points to the arena allocator of the Decl.11/// Points to the arena allocator of the Decl.
12arena: *Allocator,12arena: *Allocator,
13code: Zir,13code: Zir,
14air_instructions: std.MultiArrayList(Air.Inst) = .{},
15air_extra: std.ArrayListUnmanaged(u32) = .{},
16air_values: std.ArrayListUnmanaged(Value) = .{},
17air_variables: std.ArrayListUnmanaged(*Module.Var) = .{},
14/// Maps ZIR to AIR.18/// Maps ZIR to AIR.
15inst_map: InstMap = .{},19inst_map: InstMap = .{},
16/// When analyzing an inline function call, owner_decl is the Decl of the caller20/// When analyzing an inline function call, owner_decl is the Decl of the caller
...@@ -32,7 +36,7 @@ func: ?*Module.Fn,...@@ -32,7 +36,7 @@ func: ?*Module.Fn,
32/// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function,36/// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function,
33/// > otherwise it is the number of parameters of the function.37/// > otherwise it is the number of parameters of the function.
34/// > param_count: u3238/// > param_count: u32
35param_inst_list: []const *ir.Inst,39param_inst_list: []const Air.Inst.Ref,
36branch_quota: u32 = 1000,40branch_quota: u32 = 1000,
37branch_count: u32 = 0,41branch_count: u32 = 0,
38/// This field is updated when a new source location becomes active, so that42/// This field is updated when a new source location becomes active, so that
...@@ -41,6 +45,7 @@ branch_count: u32 = 0,...@@ -41,6 +45,7 @@ branch_count: u32 = 0,
41/// contain a mapped source location.45/// contain a mapped source location.
42src: LazySrcLoc = .{ .token_offset = 0 },46src: LazySrcLoc = .{ .token_offset = 0 },
43next_arg_index: usize = 0,47next_arg_index: usize = 0,
48decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},
4449
45const std = @import("std");50const std = @import("std");
46const mem = std.mem;51const mem = std.mem;
...@@ -52,23 +57,28 @@ const Sema = @This();...@@ -52,23 +57,28 @@ const Sema = @This();
52const Value = @import("value.zig").Value;57const Value = @import("value.zig").Value;
53const Type = @import("type.zig").Type;58const Type = @import("type.zig").Type;
54const TypedValue = @import("TypedValue.zig");59const TypedValue = @import("TypedValue.zig");
55const ir = @import("air.zig");60const Air = @import("Air.zig");
56const Zir = @import("Zir.zig");61const Zir = @import("Zir.zig");
57const Module = @import("Module.zig");62const Module = @import("Module.zig");
58const Inst = ir.Inst;
59const Body = ir.Body;
60const trace = @import("tracy.zig").trace;63const trace = @import("tracy.zig").trace;
61const Scope = Module.Scope;64const Scope = Module.Scope;
62const InnerError = Module.InnerError;65const CompileError = Module.CompileError;
66const SemaError = Module.SemaError;
63const Decl = Module.Decl;67const Decl = Module.Decl;
64const LazySrcLoc = Module.LazySrcLoc;68const LazySrcLoc = Module.LazySrcLoc;
65const RangeSet = @import("RangeSet.zig");69const RangeSet = @import("RangeSet.zig");
66const target_util = @import("target.zig");70const 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
70pub fn deinit(sema: *Sema) void {74pub 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);
72 sema.* = undefined;82 sema.* = undefined;
73}83}
7484
...@@ -76,7 +86,7 @@ pub fn analyzeFnBody(...@@ -76,7 +86,7 @@ pub fn analyzeFnBody(
76 sema: *Sema,86 sema: *Sema,
77 block: *Scope.Block,87 block: *Scope.Block,
78 fn_body_inst: Zir.Inst.Index,88 fn_body_inst: Zir.Inst.Index,
79) InnerError!void {89) SemaError!void {
80 const tags = sema.code.instructions.items(.tag);90 const tags = sema.code.instructions.items(.tag);
81 const datas = sema.code.instructions.items(.data);91 const datas = sema.code.instructions.items(.data);
82 const body: []const Zir.Inst.Index = switch (tags[fn_body_inst]) {92 const body: []const Zir.Inst.Index = switch (tags[fn_body_inst]) {
...@@ -102,13 +112,16 @@ pub fn analyzeFnBody(...@@ -102,13 +112,16 @@ pub fn analyzeFnBody(
102 },112 },
103 else => unreachable,113 else => unreachable,
104 };114 };
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 };
106}119}
107120
108/// Returns only the result from the body that is specified.121/// Returns only the result from the body that is specified.
109/// Only appropriate to call when it is determined at comptime that this body122/// Only appropriate to call when it is determined at comptime that this body
110/// has no peers.123/// 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 {
112 const break_inst = try sema.analyzeBody(block, body);125 const break_inst = try sema.analyzeBody(block, body);
113 const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;126 const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;
114 return sema.resolveInst(operand_ref);127 return sema.resolveInst(operand_ref);
...@@ -118,7 +131,7 @@ fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const Zir.Inst.Index) I...@@ -118,7 +131,7 @@ fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const Zir.Inst.Index) I
118/// return type of `analyzeBody` so that we can tail call them.131/// return type of `analyzeBody` so that we can tail call them.
119/// Only appropriate to return when the instruction is known to be NoReturn132/// Only appropriate to return when the instruction is known to be NoReturn
120/// solely based on the ZIR tag.133/// 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
123/// This function is the main loop of `Sema` and it can be used in two different ways:136/// This function is the main loop of `Sema` and it can be used in two different ways:
124/// * The traditional way where there are N breaks out of the block and peer type137/// * The traditional way where there are N breaks out of the block and peer type
...@@ -133,7 +146,7 @@ pub fn analyzeBody(...@@ -133,7 +146,7 @@ pub fn analyzeBody(
133 sema: *Sema,146 sema: *Sema,
134 block: *Scope.Block,147 block: *Scope.Block,
135 body: []const Zir.Inst.Index,148 body: []const Zir.Inst.Index,
136) InnerError!Zir.Inst.Index {149) CompileError!Zir.Inst.Index {
137 // No tracy calls here, to avoid interfering with the tail call mechanism.150 // No tracy calls here, to avoid interfering with the tail call mechanism.
138151
139 const map = &block.sema.inst_map;152 const map = &block.sema.inst_map;
...@@ -149,7 +162,7 @@ pub fn analyzeBody(...@@ -149,7 +162,7 @@ pub fn analyzeBody(
149 var i: usize = 0;162 var i: usize = 0;
150 while (true) {163 while (true) {
151 const inst = body[i];164 const inst = body[i];
152 const air_inst = switch (tags[inst]) {165 const air_inst: Air.Inst.Ref = switch (tags[inst]) {
153 // zig fmt: off166 // zig fmt: off
154 .arg => try sema.zirArg(block, inst),167 .arg => try sema.zirArg(block, inst),
155 .alloc => try sema.zirAlloc(block, inst),168 .alloc => try sema.zirAlloc(block, inst),
...@@ -174,8 +187,6 @@ pub fn analyzeBody(...@@ -174,8 +187,6 @@ pub fn analyzeBody(
174 .block => try sema.zirBlock(block, inst),187 .block => try sema.zirBlock(block, inst),
175 .suspend_block => try sema.zirSuspendBlock(block, inst),188 .suspend_block => try sema.zirSuspendBlock(block, inst),
176 .bool_not => try sema.zirBoolNot(block, inst),189 .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),
179 .bool_br_and => try sema.zirBoolBr(block, inst, false),190 .bool_br_and => try sema.zirBoolBr(block, inst, false),
180 .bool_br_or => try sema.zirBoolBr(block, inst, true),191 .bool_br_or => try sema.zirBoolBr(block, inst, true),
181 .c_import => try sema.zirCImport(block, inst),192 .c_import => try sema.zirCImport(block, inst),
...@@ -504,7 +515,7 @@ pub fn analyzeBody(...@@ -504,7 +515,7 @@ pub fn analyzeBody(
504 const break_inst = try sema.analyzeBody(block, inline_body);515 const break_inst = try sema.analyzeBody(block, inline_body);
505 const break_data = datas[break_inst].@"break";516 const break_data = datas[break_inst].@"break";
506 if (inst == break_data.block_inst) {517 if (inst == break_data.block_inst) {
507 break :blk try sema.resolveInst(break_data.operand);518 break :blk sema.resolveInst(break_data.operand);
508 } else {519 } else {
509 return break_inst;520 return break_inst;
510 }521 }
...@@ -520,20 +531,20 @@ pub fn analyzeBody(...@@ -520,20 +531,20 @@ pub fn analyzeBody(
520 const break_inst = try sema.analyzeBody(block, inline_body);531 const break_inst = try sema.analyzeBody(block, inline_body);
521 const break_data = datas[break_inst].@"break";532 const break_data = datas[break_inst].@"break";
522 if (inst == break_data.block_inst) {533 if (inst == break_data.block_inst) {
523 break :blk try sema.resolveInst(break_data.operand);534 break :blk sema.resolveInst(break_data.operand);
524 } else {535 } else {
525 return break_inst;536 return break_inst;
526 }537 }
527 },538 },
528 };539 };
529 if (air_inst.ty.isNoReturn())540 if (sema.typeOf(air_inst).isNoReturn())
530 return always_noreturn;541 return always_noreturn;
531 try map.put(sema.gpa, inst, air_inst);542 try map.put(sema.gpa, inst, air_inst);
532 i += 1;543 i += 1;
533 }544 }
534}545}
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 {
537 const extended = sema.code.instructions.items(.data)[inst].extended;548 const extended = sema.code.instructions.items(.data)[inst].extended;
538 switch (extended.opcode) {549 switch (extended.opcode) {
539 // zig fmt: off550 // zig fmt: off
...@@ -552,7 +563,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -552,7 +563,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
552 .frame_address => return sema.zirFrameAddress( block, extended),563 .frame_address => return sema.zirFrameAddress( block, extended),
553 .alloc => return sema.zirAllocExtended( block, extended),564 .alloc => return sema.zirAllocExtended( block, extended),
554 .builtin_extern => return sema.zirBuiltinExtern( block, extended),565 .builtin_extern => return sema.zirBuiltinExtern( block, extended),
555 .@"asm" => return sema.zirAsm( block, extended),566 .@"asm" => return sema.zirAsm( block, extended, inst),
556 .typeof_peer => return sema.zirTypeofPeer( block, extended),567 .typeof_peer => return sema.zirTypeofPeer( block, extended),
557 .compile_log => return sema.zirCompileLog( block, extended),568 .compile_log => return sema.zirCompileLog( block, extended),
558 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended),569 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended),
...@@ -568,18 +579,13 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -568,18 +579,13 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
568 }579 }
569}580}
570581
571/// TODO when we rework AIR memory layout, this function will no longer have a possible error.582pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref {
572pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.Inst {
573 var i: usize = @enumToInt(zir_ref);583 var i: usize = @enumToInt(zir_ref);
574584
575 // First section of indexes correspond to a set number of constant values.585 // First section of indexes correspond to a set number of constant values.
576 if (i < Zir.Inst.Ref.typed_value_map.len) {586 if (i < Zir.Inst.Ref.typed_value_map.len) {
577 // TODO when we rework AIR memory layout, this function can be as simple as:587 // We intentionally map the same indexes to the same values between ZIR and AIR.
578 // if (zir_ref < Zir.const_inst_list.len + sema.param_count)588 return zir_ref;
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]);
583 }589 }
584 i -= Zir.Inst.Ref.typed_value_map.len;590 i -= Zir.Inst.Ref.typed_value_map.len;
585591
...@@ -593,7 +599,7 @@ fn resolveConstBool(...@@ -593,7 +599,7 @@ fn resolveConstBool(
593 src: LazySrcLoc,599 src: LazySrcLoc,
594 zir_ref: Zir.Inst.Ref,600 zir_ref: Zir.Inst.Ref,
595) !bool {601) !bool {
596 const air_inst = try sema.resolveInst(zir_ref);602 const air_inst = sema.resolveInst(zir_ref);
597 const wanted_type = Type.initTag(.bool);603 const wanted_type = Type.initTag(.bool);
598 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);604 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
599 const val = try sema.resolveConstValue(block, src, coerced_inst);605 const val = try sema.resolveConstValue(block, src, coerced_inst);
...@@ -606,7 +612,7 @@ fn resolveConstString(...@@ -606,7 +612,7 @@ fn resolveConstString(
606 src: LazySrcLoc,612 src: LazySrcLoc,
607 zir_ref: Zir.Inst.Ref,613 zir_ref: Zir.Inst.Ref,
608) ![]u8 {614) ![]u8 {
609 const air_inst = try sema.resolveInst(zir_ref);615 const air_inst = sema.resolveInst(zir_ref);
610 const wanted_type = Type.initTag(.const_slice_u8);616 const wanted_type = Type.initTag(.const_slice_u8);
611 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);617 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
612 const val = try sema.resolveConstValue(block, src, coerced_inst);618 const val = try sema.resolveConstValue(block, src, coerced_inst);
...@@ -614,24 +620,39 @@ fn resolveConstString(...@@ -614,24 +620,39 @@ fn resolveConstString(
614}620}
615621
616pub fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {622pub fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
617 const air_inst = try sema.resolveInst(zir_ref);623 const air_inst = sema.resolveInst(zir_ref);
618 return sema.resolveAirAsType(block, src, air_inst);624 return sema.analyzeAsType(block, src, air_inst);
619}625}
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 {
622 const wanted_type = Type.initTag(.@"type");633 const wanted_type = Type.initTag(.@"type");
623 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);634 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
624 const val = try sema.resolveConstValue(block, src, coerced_inst);635 const val = try sema.resolveConstValue(block, src, coerced_inst);
625 return val.toType(sema.arena);636 return val.toType(sema.arena);
626}637}
627638
628fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value {639fn resolveConstValue(
629 return (try sema.resolveDefinedValue(block, src, base)) orelse640 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
630 return sema.failWithNeededComptime(block, src);646 return sema.failWithNeededComptime(block, src);
631}647}
632648
633fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value {649fn resolveDefinedValue(
634 if (try sema.resolvePossiblyUndefinedValue(block, src, base)) |val| {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| {
635 if (val.isUndef()) {656 if (val.isUndef()) {
636 return sema.failWithUseOfUndef(block, src);657 return sema.failWithUseOfUndef(block, src);
637 }658 }
...@@ -644,20 +665,36 @@ fn resolvePossiblyUndefinedValue(...@@ -644,20 +665,36 @@ fn resolvePossiblyUndefinedValue(
644 sema: *Sema,665 sema: *Sema,
645 block: *Scope.Block,666 block: *Scope.Block,
646 src: LazySrcLoc,667 src: LazySrcLoc,
647 base: *ir.Inst,668 inst: Air.Inst.Ref,
648) !?Value {669) CompileError!?Value {
649 if (try sema.typeHasOnePossibleValue(block, src, base.ty)) |opv| {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| {
650 return opv;678 return opv;
651 }679 }
652 const inst = base.castTag(.constant) orelse return null;680
653 return inst.val;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 }
654}691}
655692
656fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {693fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError {
657 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});694 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
658}695}
659696
660fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {697fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError {
661 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});698 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
662}699}
663700
...@@ -672,7 +709,7 @@ fn resolveAlreadyCoercedInt(...@@ -672,7 +709,7 @@ fn resolveAlreadyCoercedInt(
672 comptime Int: type,709 comptime Int: type,
673) !Int {710) !Int {
674 comptime assert(@typeInfo(Int).Int.bits <= 64);711 comptime assert(@typeInfo(Int).Int.bits <= 64);
675 const air_inst = try sema.resolveInst(zir_ref);712 const air_inst = sema.resolveInst(zir_ref);
676 const val = try sema.resolveConstValue(block, src, air_inst);713 const val = try sema.resolveConstValue(block, src, air_inst);
677 switch (@typeInfo(Int).Int.signedness) {714 switch (@typeInfo(Int).Int.signedness) {
678 .signed => return @intCast(Int, val.toSignedInt()),715 .signed => return @intCast(Int, val.toSignedInt()),
...@@ -687,7 +724,7 @@ fn resolveInt(...@@ -687,7 +724,7 @@ fn resolveInt(
687 zir_ref: Zir.Inst.Ref,724 zir_ref: Zir.Inst.Ref,
688 dest_type: Type,725 dest_type: Type,
689) !u64 {726) !u64 {
690 const air_inst = try sema.resolveInst(zir_ref);727 const air_inst = sema.resolveInst(zir_ref);
691 const coerced = try sema.coerce(block, dest_type, air_inst, src);728 const coerced = try sema.coerce(block, dest_type, air_inst, src);
692 const val = try sema.resolveConstValue(block, src, coerced);729 const val = try sema.resolveConstValue(block, src, coerced);
693730
...@@ -699,22 +736,22 @@ pub fn resolveInstConst(...@@ -699,22 +736,22 @@ pub fn resolveInstConst(
699 block: *Scope.Block,736 block: *Scope.Block,
700 src: LazySrcLoc,737 src: LazySrcLoc,
701 zir_ref: Zir.Inst.Ref,738 zir_ref: Zir.Inst.Ref,
702) InnerError!TypedValue {739) CompileError!TypedValue {
703 const air_inst = try sema.resolveInst(zir_ref);740 const air_ref = sema.resolveInst(zir_ref);
704 const val = try sema.resolveConstValue(block, src, air_inst);741 const val = try sema.resolveConstValue(block, src, air_ref);
705 return TypedValue{742 return TypedValue{
706 .ty = air_inst.ty,743 .ty = sema.typeOf(air_ref),
707 .val = val,744 .val = val,
708 };745 };
709}746}
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 {
712 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;749 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
713 const src = inst_data.src();750 const src = inst_data.src();
714 return sema.mod.fail(&block.base, src, "TODO implement zir_sema.zirBitcastResultPtr", .{});751 return sema.mod.fail(&block.base, src, "TODO implement zir_sema.zirBitcastResultPtr", .{});
715}752}
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 {
718 _ = inst;755 _ = inst;
719 const tracy = trace(@src());756 const tracy = trace(@src());
720 defer tracy.end();757 defer tracy.end();
...@@ -726,7 +763,7 @@ pub fn analyzeStructDecl(...@@ -726,7 +763,7 @@ pub fn analyzeStructDecl(
726 new_decl: *Decl,763 new_decl: *Decl,
727 inst: Zir.Inst.Index,764 inst: Zir.Inst.Index,
728 struct_obj: *Module.Struct,765 struct_obj: *Module.Struct,
729) InnerError!void {766) SemaError!void {
730 const extended = sema.code.instructions.items(.data)[inst].extended;767 const extended = sema.code.instructions.items(.data)[inst].extended;
731 assert(extended.opcode == .struct_decl);768 assert(extended.opcode == .struct_decl);
732 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);769 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
...@@ -749,7 +786,7 @@ fn zirStructDecl(...@@ -749,7 +786,7 @@ fn zirStructDecl(
749 block: *Scope.Block,786 block: *Scope.Block,
750 extended: Zir.Inst.Extended.InstData,787 extended: Zir.Inst.Extended.InstData,
751 inst: Zir.Inst.Index,788 inst: Zir.Inst.Index,
752) InnerError!*Inst {789) CompileError!Air.Inst.Ref {
753 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);790 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
754 const src: LazySrcLoc = if (small.has_src_node) blk: {791 const src: LazySrcLoc = if (small.has_src_node) blk: {
755 const node_offset = @bitCast(i32, sema.code.extra[extended.operand]);792 const node_offset = @bitCast(i32, sema.code.extra[extended.operand]);
...@@ -820,7 +857,7 @@ fn zirEnumDecl(...@@ -820,7 +857,7 @@ fn zirEnumDecl(
820 sema: *Sema,857 sema: *Sema,
821 block: *Scope.Block,858 block: *Scope.Block,
822 extended: Zir.Inst.Extended.InstData,859 extended: Zir.Inst.Extended.InstData,
823) InnerError!*Inst {860) CompileError!Air.Inst.Ref {
824 const tracy = trace(@src());861 const tracy = trace(@src());
825 defer tracy.end();862 defer tracy.end();
826863
...@@ -1017,7 +1054,7 @@ fn zirUnionDecl(...@@ -1017,7 +1054,7 @@ fn zirUnionDecl(
1017 block: *Scope.Block,1054 block: *Scope.Block,
1018 extended: Zir.Inst.Extended.InstData,1055 extended: Zir.Inst.Extended.InstData,
1019 inst: Zir.Inst.Index,1056 inst: Zir.Inst.Index,
1020) InnerError!*Inst {1057) CompileError!Air.Inst.Ref {
1021 const tracy = trace(@src());1058 const tracy = trace(@src());
1022 defer tracy.end();1059 defer tracy.end();
10231060
...@@ -1081,7 +1118,7 @@ fn zirOpaqueDecl(...@@ -1081,7 +1118,7 @@ fn zirOpaqueDecl(
1081 block: *Scope.Block,1118 block: *Scope.Block,
1082 inst: Zir.Inst.Index,1119 inst: Zir.Inst.Index,
1083 name_strategy: Zir.Inst.NameStrategy,1120 name_strategy: Zir.Inst.NameStrategy,
1084) InnerError!*Inst {1121) CompileError!Air.Inst.Ref {
1085 const tracy = trace(@src());1122 const tracy = trace(@src());
1086 defer tracy.end();1123 defer tracy.end();
10871124
...@@ -1101,7 +1138,7 @@ fn zirErrorSetDecl(...@@ -1101,7 +1138,7 @@ fn zirErrorSetDecl(
1101 block: *Scope.Block,1138 block: *Scope.Block,
1102 inst: Zir.Inst.Index,1139 inst: Zir.Inst.Index,
1103 name_strategy: Zir.Inst.NameStrategy,1140 name_strategy: Zir.Inst.NameStrategy,
1104) InnerError!*Inst {1141) CompileError!Air.Inst.Ref {
1105 const tracy = trace(@src());1142 const tracy = trace(@src());
1106 defer tracy.end();1143 defer tracy.end();
11071144
...@@ -1141,7 +1178,7 @@ fn zirRetPtr(...@@ -1141,7 +1178,7 @@ fn zirRetPtr(
1141 sema: *Sema,1178 sema: *Sema,
1142 block: *Scope.Block,1179 block: *Scope.Block,
1143 extended: Zir.Inst.Extended.InstData,1180 extended: Zir.Inst.Extended.InstData,
1144) InnerError!*Inst {1181) CompileError!Air.Inst.Ref {
1145 const tracy = trace(@src());1182 const tracy = trace(@src());
1146 defer tracy.end();1183 defer tracy.end();
11471184
...@@ -1149,16 +1186,16 @@ fn zirRetPtr(...@@ -1149,16 +1186,16 @@ fn zirRetPtr(
1149 try sema.requireFunctionBlock(block, src);1186 try sema.requireFunctionBlock(block, src);
1150 const fn_ty = sema.func.?.owner_decl.ty;1187 const fn_ty = sema.func.?.owner_decl.ty;
1151 const ret_type = fn_ty.fnReturnType();1188 const ret_type = fn_ty.fnReturnType();
1152 const ptr_type = try sema.mod.simplePtrType(sema.arena, ret_type, true, .One);1189 const ptr_type = try Module.simplePtrType(sema.arena, ret_type, true, .One);
1153 return block.addNoOp(src, ptr_type, .alloc);1190 return block.addTy(.alloc, ptr_type);
1154}1191}
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 {
1157 const tracy = trace(@src());1194 const tracy = trace(@src());
1158 defer tracy.end();1195 defer tracy.end();
11591196
1160 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;1197 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);
1162 return sema.analyzeRef(block, inst_data.src(), operand);1199 return sema.analyzeRef(block, inst_data.src(), operand);
1163}1200}
11641201
...@@ -1166,7 +1203,7 @@ fn zirRetType(...@@ -1166,7 +1203,7 @@ fn zirRetType(
1166 sema: *Sema,1203 sema: *Sema,
1167 block: *Scope.Block,1204 block: *Scope.Block,
1168 extended: Zir.Inst.Extended.InstData,1205 extended: Zir.Inst.Extended.InstData,
1169) InnerError!*Inst {1206) CompileError!Air.Inst.Ref {
1170 const tracy = trace(@src());1207 const tracy = trace(@src());
1171 defer tracy.end();1208 defer tracy.end();
11721209
...@@ -1174,15 +1211,15 @@ fn zirRetType(...@@ -1174,15 +1211,15 @@ fn zirRetType(
1174 try sema.requireFunctionBlock(block, src);1211 try sema.requireFunctionBlock(block, src);
1175 const fn_ty = sema.func.?.owner_decl.ty;1212 const fn_ty = sema.func.?.owner_decl.ty;
1176 const ret_type = fn_ty.fnReturnType();1213 const ret_type = fn_ty.fnReturnType();
1177 return sema.mod.constType(sema.arena, src, ret_type);1214 return sema.addType(ret_type);
1178}1215}
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 {
1181 const tracy = trace(@src());1218 const tracy = trace(@src());
1182 defer tracy.end();1219 defer tracy.end();
11831220
1184 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1221 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);
1186 const src = inst_data.src();1223 const src = inst_data.src();
11871224
1188 return sema.ensureResultUsed(block, operand, src);1225 return sema.ensureResultUsed(block, operand, src);
...@@ -1191,37 +1228,39 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) I...@@ -1191,37 +1228,39 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) I
1191fn ensureResultUsed(1228fn ensureResultUsed(
1192 sema: *Sema,1229 sema: *Sema,
1193 block: *Scope.Block,1230 block: *Scope.Block,
1194 operand: *Inst,1231 operand: Air.Inst.Ref,
1195 src: LazySrcLoc,1232 src: LazySrcLoc,
1196) InnerError!void {1233) CompileError!void {
1197 switch (operand.ty.zigTypeTag()) {1234 const operand_ty = sema.typeOf(operand);
1235 switch (operand_ty.zigTypeTag()) {
1198 .Void, .NoReturn => return,1236 .Void, .NoReturn => return,
1199 else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}),1237 else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}),
1200 }1238 }
1201}1239}
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 {
1204 const tracy = trace(@src());1242 const tracy = trace(@src());
1205 defer tracy.end();1243 defer tracy.end();
12061244
1207 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1245 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);
1209 const src = inst_data.src();1247 const src = inst_data.src();
1210 switch (operand.ty.zigTypeTag()) {1248 const operand_ty = sema.typeOf(operand);
1249 switch (operand_ty.zigTypeTag()) {
1211 .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}),1250 .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}),
1212 else => return,1251 else => return,
1213 }1252 }
1214}1253}
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 {
1217 const tracy = trace(@src());1256 const tracy = trace(@src());
1218 defer tracy.end();1257 defer tracy.end();
12191258
1220 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1259 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1221 const src = inst_data.src();1260 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();
1225 if (!elem_ty.isIndexable()) {1264 if (!elem_ty.isIndexable()) {
1226 const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node };1265 const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node };
1227 const msg = msg: {1266 const msg = msg: {
...@@ -1244,46 +1283,47 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In...@@ -1244,46 +1283,47 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
1244 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);1283 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
1245 }1284 }
1246 const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src);1285 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);
1248}1288}
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 {
1251 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;1291 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
1252 const arg_name = inst_data.get(sema.code);1292 const arg_name = inst_data.get(sema.code);
1253 const arg_index = sema.next_arg_index;1293 const arg_index = sema.next_arg_index;
1254 sema.next_arg_index += 1;1294 sema.next_arg_index += 1;
12551295
1256 // TODO check if arg_name shadows a Decl1296 // TODO check if arg_name shadows a Decl
1297 _ = arg_name;
12571298
1258 if (block.inlining) |_| {1299 if (block.inlining) |_| {
1259 return sema.param_inst_list[arg_index];1300 return sema.param_inst_list[arg_index];
1260 }1301 }
12611302
1262 // Need to set the name of the Air.Arg instruction.1303 // Set the name of the Air.Arg instruction for use by codegen debug info.
1263 const air_arg = sema.param_inst_list[arg_index].castTag(.arg).?;1304 const air_arg = sema.param_inst_list[arg_index];
1264 air_arg.name = arg_name;1305 sema.air_instructions.items(.data)[Air.refToIndex(air_arg).?].ty_str.str = inst_data.start;
1265 return &air_arg.base;1306 return air_arg;
1266}1307}
12671308
1268fn zirAllocExtended(1309fn zirAllocExtended(
1269 sema: *Sema,1310 sema: *Sema,
1270 block: *Scope.Block,1311 block: *Scope.Block,
1271 extended: Zir.Inst.Extended.InstData,1312 extended: Zir.Inst.Extended.InstData,
1272) InnerError!*Inst {1313) CompileError!Air.Inst.Ref {
1273 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);1314 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
1274 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };1315 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
1275 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended", .{});1316 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended", .{});
1276}1317}
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 {
1279 const tracy = trace(@src());1320 const tracy = trace(@src());
1280 defer tracy.end();1321 defer tracy.end();
12811322
1282 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1323 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1283 const src = inst_data.src();
1284 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };1324 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1285 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);1325 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
1288 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);1328 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);
1289 val_payload.* = .{1329 val_payload.* = .{
...@@ -1292,19 +1332,16 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne...@@ -1292,19 +1332,16 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
1292 .val = undefined, // astgen guarantees there will be a store before the first load1332 .val = undefined, // astgen guarantees there will be a store before the first load
1293 },1333 },
1294 };1334 };
1295 return sema.mod.constInst(sema.arena, src, .{1335 return sema.addConstant(ptr_type, Value.initPayload(&val_payload.base));
1296 .ty = ptr_type,
1297 .val = Value.initPayload(&val_payload.base),
1298 });
1299}1336}
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 {
1302 const src_node = sema.code.instructions.items(.data)[inst].node;1339 const src_node = sema.code.instructions.items(.data)[inst].node;
1303 const src: LazySrcLoc = .{ .node_offset = src_node };1340 const src: LazySrcLoc = .{ .node_offset = src_node };
1304 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocInferredComptime", .{});1341 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocInferredComptime", .{});
1305}1342}
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 {
1308 const tracy = trace(@src());1345 const tracy = trace(@src());
1309 defer tracy.end();1346 defer tracy.end();
13101347
...@@ -1312,12 +1349,12 @@ fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*...@@ -1312,12 +1349,12 @@ fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*
1312 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };1349 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1313 const var_decl_src = inst_data.src();1350 const var_decl_src = inst_data.src();
1314 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);1351 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);
1316 try sema.requireRuntimeBlock(block, var_decl_src);1353 try sema.requireRuntimeBlock(block, var_decl_src);
1317 return block.addNoOp(var_decl_src, ptr_type, .alloc);1354 return block.addTy(.alloc, ptr_type);
1318}1355}
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 {
1321 const tracy = trace(@src());1358 const tracy = trace(@src());
1322 defer tracy.end();1359 defer tracy.end();
13231360
...@@ -1326,9 +1363,9 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -1326,9 +1363,9 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
1326 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };1363 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1327 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);1364 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
1328 try sema.validateVarType(block, ty_src, var_type);1365 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);
1330 try sema.requireRuntimeBlock(block, var_decl_src);1367 try sema.requireRuntimeBlock(block, var_decl_src);
1331 return block.addNoOp(var_decl_src, ptr_type, .alloc);1368 return block.addTy(.alloc, ptr_type);
1332}1369}
13331370
1334fn zirAllocInferred(1371fn zirAllocInferred(
...@@ -1336,7 +1373,7 @@ fn zirAllocInferred(...@@ -1336,7 +1373,7 @@ fn zirAllocInferred(
1336 block: *Scope.Block,1373 block: *Scope.Block,
1337 inst: Zir.Inst.Index,1374 inst: Zir.Inst.Index,
1338 inferred_alloc_ty: Type,1375 inferred_alloc_ty: Type,
1339) InnerError!*Inst {1376) CompileError!Air.Inst.Ref {
1340 const tracy = trace(@src());1377 const tracy = trace(@src());
1341 defer tracy.end();1378 defer tracy.end();
13421379
...@@ -1351,27 +1388,27 @@ fn zirAllocInferred(...@@ -1351,27 +1388,27 @@ fn zirAllocInferred(
1351 // not needed in the case of constant values. However here, we plan to "downgrade"1388 // not needed in the case of constant values. However here, we plan to "downgrade"
1352 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append1389 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
1353 // to the block even though it is currently a `.constant`.1390 // to the block even though it is currently a `.constant`.
1354 const result = try sema.mod.constInst(sema.arena, src, .{1391 const result = try sema.addConstant(inferred_alloc_ty, Value.initPayload(&val_payload.base));
1355 .ty = inferred_alloc_ty,
1356 .val = Value.initPayload(&val_payload.base),
1357 });
1358 try sema.requireFunctionBlock(block, src);1392 try sema.requireFunctionBlock(block, src);
1359 try block.instructions.append(sema.gpa, result);1393 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
1360 return result;1394 return result;
1361}1395}
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 {
1364 const tracy = trace(@src());1398 const tracy = trace(@src());
1365 defer tracy.end();1399 defer tracy.end();
13661400
1367 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1401 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1368 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };1402 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1369 const ptr = try sema.resolveInst(inst_data.operand);1403 const ptr = sema.resolveInst(inst_data.operand);
1370 const ptr_val = ptr.castTag(.constant).?.val;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];
1371 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;1408 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
1372 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;1409 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
1373 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);1410 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()) {
1375 .inferred_alloc_const => false,1412 .inferred_alloc_const => false,
1376 .inferred_alloc_mut => true,1413 .inferred_alloc_mut => true,
1377 else => unreachable,1414 else => unreachable,
...@@ -1379,14 +1416,16 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde...@@ -1379,14 +1416,16 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
1379 if (var_is_mut) {1416 if (var_is_mut) {
1380 try sema.validateVarType(block, ty_src, final_elem_ty);1417 try sema.validateVarType(block, ty_src, final_elem_ty);
1381 }1418 }
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
1384 // Change it to a normal alloc.1421 // Change it to a normal alloc.
1385 ptr.ty = final_ptr_ty;1422 sema.air_instructions.set(ptr_inst, .{
1386 ptr.tag = .alloc;1423 .tag = .alloc,
1424 .data = .{ .ty = final_ptr_ty },
1425 });
1387}1426}
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 {
1390 const tracy = trace(@src());1429 const tracy = trace(@src());
1391 defer tracy.end();1430 defer tracy.end();
13921431
...@@ -1400,8 +1439,8 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind...@@ -1400,8 +1439,8 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
1400 const struct_obj: *Module.Struct = s: {1439 const struct_obj: *Module.Struct = s: {
1401 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;1440 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
1402 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;1441 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);1442 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
1404 break :s object_ptr.ty.elemType().castTag(.@"struct").?.data;1443 break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data;
1405 };1444 };
14061445
1407 // Maps field index to field_ptr index of where it was already initialized.1446 // 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...@@ -1459,7 +1498,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
1459 }1498 }
1460}1499}
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 {
1463 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;1502 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1464 const src = inst_data.src();1503 const src = inst_data.src();
1465 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirValidateArrayInitPtr", .{});1504 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirValidateArrayInitPtr", .{});
...@@ -1471,7 +1510,7 @@ fn failWithBadFieldAccess(...@@ -1471,7 +1510,7 @@ fn failWithBadFieldAccess(
1471 struct_obj: *Module.Struct,1510 struct_obj: *Module.Struct,
1472 field_src: LazySrcLoc,1511 field_src: LazySrcLoc,
1473 field_name: []const u8,1512 field_name: []const u8,
1474) InnerError {1513) CompileError {
1475 const mod = sema.mod;1514 const mod = sema.mod;
1476 const gpa = sema.gpa;1515 const gpa = sema.gpa;
14771516
...@@ -1498,7 +1537,7 @@ fn failWithBadUnionFieldAccess(...@@ -1498,7 +1537,7 @@ fn failWithBadUnionFieldAccess(
1498 union_obj: *Module.Union,1537 union_obj: *Module.Union,
1499 field_src: LazySrcLoc,1538 field_src: LazySrcLoc,
1500 field_name: []const u8,1539 field_name: []const u8,
1501) InnerError {1540) CompileError {
1502 const mod = sema.mod;1541 const mod = sema.mod;
1503 const gpa = sema.gpa;1542 const gpa = sema.gpa;
15041543
...@@ -1519,7 +1558,7 @@ fn failWithBadUnionFieldAccess(...@@ -1519,7 +1558,7 @@ fn failWithBadUnionFieldAccess(
1519 return mod.failWithOwnedErrorMsg(&block.base, msg);1558 return mod.failWithOwnedErrorMsg(&block.base, msg);
1520}1559}
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 {
1523 const tracy = trace(@src());1562 const tracy = trace(@src());
1524 defer tracy.end();1563 defer tracy.end();
15251564
...@@ -1529,37 +1568,41 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In...@@ -1529,37 +1568,41 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
1529 // to omit it.1568 // to omit it.
1530 return;1569 return;
1531 }1570 }
1532 const ptr = try sema.resolveInst(bin_inst.lhs);1571 const ptr = sema.resolveInst(bin_inst.lhs);
1533 const value = try sema.resolveInst(bin_inst.rhs);1572 const value = sema.resolveInst(bin_inst.rhs);
1534 const ptr_ty = try sema.mod.simplePtrType(sema.arena, value.ty, true, .One);1573 const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One);
1535 // TODO detect when this store should be done at compile-time. For example,1574 // TODO detect when this store should be done at compile-time. For example,
1536 // if expressions should force it when the condition is compile-time known.1575 // if expressions should force it when the condition is compile-time known.
1537 const src: LazySrcLoc = .unneeded;1576 const src: LazySrcLoc = .unneeded;
1538 try sema.requireRuntimeBlock(block, src);1577 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);
1540 return sema.storePtr(block, src, bitcasted_ptr, value);1579 return sema.storePtr(block, src, bitcasted_ptr, value);
1541}1580}
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 {
1544 const tracy = trace(@src());1583 const tracy = trace(@src());
1545 defer tracy.end();1584 defer tracy.end();
15461585
1547 const src: LazySrcLoc = .unneeded;1586 const src: LazySrcLoc = .unneeded;
1548 const bin_inst = sema.code.instructions.items(.data)[inst].bin;1587 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1549 const ptr = try sema.resolveInst(bin_inst.lhs);1588 const ptr = sema.resolveInst(bin_inst.lhs);
1550 const value = try sema.resolveInst(bin_inst.rhs);1589 const value = sema.resolveInst(bin_inst.rhs);
1551 const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;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).?;
1552 // Add the stored instruction to the set we will use to resolve peer types1595 // Add the stored instruction to the set we will use to resolve peer types
1553 // for the inferred allocation.1596 // for the inferred allocation.
1554 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);1597 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);
1555 // Create a runtime bitcast instruction with exactly the type the pointer wants.1598 // 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);
1557 try sema.requireRuntimeBlock(block, src);1600 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);
1559 return sema.storePtr(block, src, bitcasted_ptr, value);1602 return sema.storePtr(block, src, bitcasted_ptr, value);
1560}1603}
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 {
1563 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1606 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1564 const src = inst_data.src();1607 const src = inst_data.src();
1565 const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32);1608 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)...@@ -1567,51 +1610,54 @@ fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
1567 sema.branch_quota = quota;1610 sema.branch_quota = quota;
1568}1611}
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 {
1571 const tracy = trace(@src());1614 const tracy = trace(@src());
1572 defer tracy.end();1615 defer tracy.end();
15731616
1574 const bin_inst = sema.code.instructions.items(.data)[inst].bin;1617 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1575 const ptr = try sema.resolveInst(bin_inst.lhs);1618 const ptr = sema.resolveInst(bin_inst.lhs);
1576 const value = try sema.resolveInst(bin_inst.rhs);1619 const value = sema.resolveInst(bin_inst.rhs);
1577 return sema.storePtr(block, sema.src, ptr, value);1620 return sema.storePtr(block, sema.src, ptr, value);
1578}1621}
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 {
1581 const tracy = trace(@src());1624 const tracy = trace(@src());
1582 defer tracy.end();1625 defer tracy.end();
15831626
1584 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;1627 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1585 const src = inst_data.src();1628 const src = inst_data.src();
1586 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;1629 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
1587 const ptr = try sema.resolveInst(extra.lhs);1630 const ptr = sema.resolveInst(extra.lhs);
1588 const value = try sema.resolveInst(extra.rhs);1631 const value = sema.resolveInst(extra.rhs);
1589 return sema.storePtr(block, src, ptr, value);1632 return sema.storePtr(block, src, ptr, value);
1590}1633}
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 {
1593 const tracy = trace(@src());1636 const tracy = trace(@src());
1594 defer tracy.end();1637 defer tracy.end();
15951638
1596 const src: LazySrcLoc = .unneeded;1639 const src = sema.src;
1640 const fn_inst_src = sema.src;
1641
1597 const inst_data = sema.code.instructions.items(.data)[inst].param_type;1642 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);
1599 const param_index = inst_data.param_index;1645 const param_index = inst_data.param_index;
16001646
1601 const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {1647 const fn_ty: Type = switch (fn_inst_ty.zigTypeTag()) {
1602 .Fn => fn_inst.ty,1648 .Fn => fn_inst_ty,
1603 .BoundFn => {1649 .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", .{});
1605 },1651 },
1606 else => {1652 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});
1608 },1654 },
1609 };1655 };
16101656
1611 const param_count = fn_ty.fnParamLen();1657 const param_count = fn_ty.fnParamLen();
1612 if (param_index >= param_count) {1658 if (param_index >= param_count) {
1613 if (fn_ty.fnIsVarArgs()) {1659 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));
1615 }1661 }
1616 return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{1662 return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{
1617 param_index,1663 param_index,
...@@ -1622,10 +1668,10 @@ fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -1622,10 +1668,10 @@ fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
16221668
1623 // TODO support generic functions1669 // TODO support generic functions
1624 const param_type = fn_ty.fnParamType(param_index);1670 const param_type = fn_ty.fnParamType(param_index);
1625 return sema.mod.constType(sema.arena, src, param_type);1671 return sema.addType(param_type);
1626}1672}
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 {
1629 const tracy = trace(@src());1675 const tracy = trace(@src());
1630 defer tracy.end();1676 defer tracy.end();
16311677
...@@ -1653,16 +1699,16 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In...@@ -1653,16 +1699,16 @@ fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
1653 return sema.analyzeDeclRef(block, .unneeded, new_decl);1699 return sema.analyzeDeclRef(block, .unneeded, new_decl);
1654}1700}
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 {
1657 _ = block;1703 _ = block;
1658 const tracy = trace(@src());1704 const tracy = trace(@src());
1659 defer tracy.end();1705 defer tracy.end();
16601706
1661 const int = sema.code.instructions.items(.data)[inst].int;1707 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);
1663}1709}
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 {
1666 _ = block;1712 _ = block;
1667 const tracy = trace(@src());1713 const tracy = trace(@src());
1668 defer tracy.end();1714 defer tracy.end();
...@@ -1674,40 +1720,35 @@ fn zirIntBig(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!...@@ -1674,40 +1720,35 @@ fn zirIntBig(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
1674 const limbs = try arena.alloc(std.math.big.Limb, int.len);1720 const limbs = try arena.alloc(std.math.big.Limb, int.len);
1675 mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes);1721 mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes);
16761722
1677 return sema.mod.constInst(arena, .unneeded, .{1723 return sema.addConstant(
1678 .ty = Type.initTag(.comptime_int),1724 Type.initTag(.comptime_int),
1679 .val = try Value.Tag.int_big_positive.create(arena, limbs),1725 try Value.Tag.int_big_positive.create(arena, limbs),
1680 });1726 );
1681}1727}
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 {
1684 _ = block;1730 _ = block;
1685 const arena = sema.arena;1731 const arena = sema.arena;
1686 const inst_data = sema.code.instructions.items(.data)[inst].float;1732 const number = sema.code.instructions.items(.data)[inst].float;
1687 const src = inst_data.src();1733 return sema.addConstant(
1688 const number = inst_data.number;1734 Type.initTag(.comptime_float),
16891735 try Value.Tag.float_64.create(arena, number),
1690 return sema.mod.constInst(arena, src, .{1736 );
1691 .ty = Type.initTag(.comptime_float),
1692 .val = try Value.Tag.float_32.create(arena, number),
1693 });
1694}1737}
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 {
1697 _ = block;1740 _ = block;
1698 const arena = sema.arena;1741 const arena = sema.arena;
1699 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;1742 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1700 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;1743 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
1701 const src = inst_data.src();
1702 const number = extra.get();1744 const number = extra.get();
17031745 return sema.addConstant(
1704 return sema.mod.constInst(arena, src, .{1746 Type.initTag(.comptime_float),
1705 .ty = Type.initTag(.comptime_float),1747 try Value.Tag.float_128.create(arena, number),
1706 .val = try Value.Tag.float_128.create(arena, number),1748 );
1707 });
1708}1749}
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 {
1711 const tracy = trace(@src());1752 const tracy = trace(@src());
1712 defer tracy.end();1753 defer tracy.end();
17131754
...@@ -1722,7 +1763,7 @@ fn zirCompileLog(...@@ -1722,7 +1763,7 @@ fn zirCompileLog(
1722 sema: *Sema,1763 sema: *Sema,
1723 block: *Scope.Block,1764 block: *Scope.Block,
1724 extended: Zir.Inst.Extended.InstData,1765 extended: Zir.Inst.Extended.InstData,
1725) InnerError!*Inst {1766) CompileError!Air.Inst.Ref {
1726 var managed = sema.mod.compile_log_text.toManaged(sema.gpa);1767 var managed = sema.mod.compile_log_text.toManaged(sema.gpa);
1727 defer sema.mod.compile_log_text = managed.moveToUnmanaged();1768 defer sema.mod.compile_log_text = managed.moveToUnmanaged();
1728 const writer = managed.writer();1769 const writer = managed.writer();
...@@ -1735,11 +1776,12 @@ fn zirCompileLog(...@@ -1735,11 +1776,12 @@ fn zirCompileLog(
1735 for (args) |arg_ref, i| {1776 for (args) |arg_ref, i| {
1736 if (i != 0) try writer.print(", ", .{});1777 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);
1739 if (try sema.resolvePossiblyUndefinedValue(block, src, arg)) |val| {1781 if (try sema.resolvePossiblyUndefinedValue(block, src, arg)) |val| {
1740 try writer.print("@as({}, {})", .{ arg.ty, val });1782 try writer.print("@as({}, {})", .{ arg_ty, val });
1741 } else {1783 } else {
1742 try writer.print("@as({}, [runtime value])", .{arg.ty});1784 try writer.print("@as({}, [runtime value])", .{arg_ty});
1743 }1785 }
1744 }1786 }
1745 try writer.print("\n", .{});1787 try writer.print("\n", .{});
...@@ -1748,13 +1790,10 @@ fn zirCompileLog(...@@ -1748,13 +1790,10 @@ fn zirCompileLog(
1748 if (!gop.found_existing) {1790 if (!gop.found_existing) {
1749 gop.value_ptr.* = src_node;1791 gop.value_ptr.* = src_node;
1750 }1792 }
1751 return sema.mod.constInst(sema.arena, src, .{1793 return Air.Inst.Ref.void_value;
1752 .ty = Type.initTag(.void),
1753 .val = Value.initTag(.void_value),
1754 });
1755}1794}
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 {
1758 const tracy = trace(@src());1797 const tracy = trace(@src());
1759 defer tracy.end();1798 defer tracy.end();
17601799
...@@ -1764,15 +1803,15 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!...@@ -1764,15 +1803,15 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
1764 return always_noreturn;1803 return always_noreturn;
1765}1804}
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 {
1768 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1807 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1769 const src: LazySrcLoc = inst_data.src();1808 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
1772 return sema.panicWithMsg(block, src, msg_inst);1811 return sema.panicWithMsg(block, src, msg_inst);
1773}1812}
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 {
1776 const tracy = trace(@src());1815 const tracy = trace(@src());
1777 defer tracy.end();1816 defer tracy.end();
17781817
...@@ -1780,18 +1819,26 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -1780,18 +1819,26 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
1780 const src = inst_data.src();1819 const src = inst_data.src();
1781 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);1820 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1782 const body = sema.code.extra[extra.end..][0..extra.data.body_len];1821 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1822 const gpa = sema.gpa;
17831823
1784 // AIR expects a block outside the loop block too.1824 // AIR expects a block outside the loop block too.
1785 const block_inst = try sema.arena.create(Inst.Block);1825 // Reserve space for a Loop instruction so that generated Break instructions can
1786 block_inst.* = .{1826 // point to it, even if it doesn't end up getting used because the code ends up being
1787 .base = .{1827 // comptime evaluated.
1788 .tag = Inst.Block.base_tag,1828 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
1789 .ty = undefined,1829 const loop_inst = block_inst + 1;
1790 .src = src,1830 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
1791 },1831 sema.air_instructions.appendAssumeCapacity(.{
1792 .body = undefined,1832 .tag = .block,
1793 };1833 .data = undefined,
17941834 });
1835 sema.air_instructions.appendAssumeCapacity(.{
1836 .tag = .loop,
1837 .data = .{ .ty_pl = .{
1838 .ty = .noreturn_type,
1839 .payload = undefined,
1840 } },
1841 });
1795 var label: Scope.Block.Label = .{1842 var label: Scope.Block.Label = .{
1796 .zir_block = inst,1843 .zir_block = inst,
1797 .merges = .{1844 .merges = .{
...@@ -1807,37 +1854,28 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -1807,37 +1854,28 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
1807 child_block.runtime_index += 1;1854 child_block.runtime_index += 1;
1808 const merges = &child_block.label.?.merges;1855 const merges = &child_block.label.?.merges;
18091856
1810 defer child_block.instructions.deinit(sema.gpa);1857 defer child_block.instructions.deinit(gpa);
1811 defer merges.results.deinit(sema.gpa);1858 defer merges.results.deinit(gpa);
1812 defer merges.br_list.deinit(sema.gpa);1859 defer merges.br_list.deinit(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 };
18261860
1827 var loop_block = child_block.makeSubBlock();1861 var loop_block = child_block.makeSubBlock();
1828 defer loop_block.instructions.deinit(sema.gpa);1862 defer loop_block.instructions.deinit(gpa);
18291863
1830 _ = try sema.analyzeBody(&loop_block, body);1864 _ = try sema.analyzeBody(&loop_block, body);
18311865
1832 // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.1866 // 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);1869 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
1835 loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) };1870 loop_block.instructions.items.len);
18361871 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);
1837 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);1875 return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
1838}1876}
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 {
1841 const tracy = trace(@src());1879 const tracy = trace(@src());
1842 defer tracy.end();1880 defer tracy.end();
18431881
...@@ -1847,33 +1885,34 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inn...@@ -1847,33 +1885,34 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inn
1847 return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirCImport", .{});1885 return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirCImport", .{});
1848}1886}
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 {
1851 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;1889 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1852 const src = inst_data.src();1890 const src = inst_data.src();
1853 return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirSuspendBlock", .{});1891 return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirSuspendBlock", .{});
1854}1892}
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 {
1857 const tracy = trace(@src());1899 const tracy = trace(@src());
1858 defer tracy.end();1900 defer tracy.end();
18591901
1860 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;1902 const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
1861 const src = inst_data.src();1903 const src = pl_node.src();
1862 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);1904 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
1863 const body = sema.code.extra[extra.end..][0..extra.data.body_len];1905 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1906 const gpa = sema.gpa;
18641907
1865 // Reserve space for a Block instruction so that generated Break instructions can1908 // Reserve space for a Block instruction so that generated Break instructions can
1866 // point to it, even if it doesn't end up getting used because the code ends up being1909 // point to it, even if it doesn't end up getting used because the code ends up being
1867 // comptime evaluated.1910 // comptime evaluated.
1868 const block_inst = try sema.arena.create(Inst.Block);1911 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
1869 block_inst.* = .{1912 try sema.air_instructions.append(gpa, .{
1870 .base = .{1913 .tag = .block,
1871 .tag = Inst.Block.base_tag,1914 .data = undefined,
1872 .ty = undefined, // Set after analysis.1915 });
1873 .src = src,
1874 },
1875 .body = undefined,
1876 };
18771916
1878 var label: Scope.Block.Label = .{1917 var label: Scope.Block.Label = .{
1879 .zir_block = inst,1918 .zir_block = inst,
...@@ -1895,9 +1934,9 @@ fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inner...@@ -1895,9 +1934,9 @@ fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inner
1895 };1934 };
1896 const merges = &child_block.label.?.merges;1935 const merges = &child_block.label.?.merges;
18971936
1898 defer child_block.instructions.deinit(sema.gpa);1937 defer child_block.instructions.deinit(gpa);
1899 defer merges.results.deinit(sema.gpa);1938 defer merges.results.deinit(gpa);
1900 defer merges.br_list.deinit(sema.gpa);1939 defer merges.br_list.deinit(gpa);
19011940
1902 _ = try sema.analyzeBody(&child_block, body);1941 _ = try sema.analyzeBody(&child_block, body);
19031942
...@@ -1911,7 +1950,7 @@ fn resolveBlockBody(...@@ -1911,7 +1950,7 @@ fn resolveBlockBody(
1911 child_block: *Scope.Block,1950 child_block: *Scope.Block,
1912 body: []const Zir.Inst.Index,1951 body: []const Zir.Inst.Index,
1913 merges: *Scope.Block.Merges,1952 merges: *Scope.Block.Merges,
1914) InnerError!*Inst {1953) CompileError!Air.Inst.Ref {
1915 _ = try sema.analyzeBody(child_block, body);1954 _ = try sema.analyzeBody(child_block, body);
1916 return sema.analyzeBlockBody(parent_block, src, child_block, merges);1955 return sema.analyzeBlockBody(parent_block, src, child_block, merges);
1917}1956}
...@@ -1922,30 +1961,31 @@ fn analyzeBlockBody(...@@ -1922,30 +1961,31 @@ fn analyzeBlockBody(
1922 src: LazySrcLoc,1961 src: LazySrcLoc,
1923 child_block: *Scope.Block,1962 child_block: *Scope.Block,
1924 merges: *Scope.Block.Merges,1963 merges: *Scope.Block.Merges,
1925) InnerError!*Inst {1964) CompileError!Air.Inst.Ref {
1926 const tracy = trace(@src());1965 const tracy = trace(@src());
1927 defer tracy.end();1966 defer tracy.end();
19281967
1968 const gpa = sema.gpa;
1969
1929 // Blocks must terminate with noreturn instruction.1970 // Blocks must terminate with noreturn instruction.
1930 assert(child_block.instructions.items.len != 0);1971 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
1933 if (merges.results.items.len == 0) {1974 if (merges.results.items.len == 0) {
1934 // No need for a block instruction. We can put the new instructions1975 // No need for a block instruction. We can put the new instructions
1935 // directly into the parent block.1976 // directly into the parent block.
1936 const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items);1977 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
1937 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);1978 return Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]);
1938 return copied_instructions[copied_instructions.len - 1];
1939 }1979 }
1940 if (merges.results.items.len == 1) {1980 if (merges.results.items.len == 1) {
1941 const last_inst_index = child_block.instructions.items.len - 1;1981 const last_inst_index = child_block.instructions.items.len - 1;
1942 const last_inst = child_block.instructions.items[last_inst_index];1982 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| {
1944 if (br_block == merges.block_inst) {1984 if (br_block == merges.block_inst) {
1945 // No need for a block instruction. We can put the new instructions directly1985 // No need for a block instruction. We can put the new instructions directly
1946 // into the parent block. Here we omit the break instruction.1986 // 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]);1987 const without_break = child_block.instructions.items[0..last_inst_index];
1948 try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);1988 try parent_block.instructions.appendSlice(gpa, without_break);
1949 return merges.results.items[0];1989 return merges.results.items[0];
1950 }1990 }
1951 }1991 }
...@@ -1955,50 +1995,70 @@ fn analyzeBlockBody(...@@ -1955,50 +1995,70 @@ fn analyzeBlockBody(
19551995
1956 // Need to set the type and emit the Block instruction. This allows machine code generation1996 // Need to set the type and emit the Block instruction. This allows machine code generation
1957 // to emit a jump instruction to after the block when it encounters the break.1997 // 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);
1959 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items);1999 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items);
1960 merges.block_inst.base.ty = resolved_ty;2000 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
1961 merges.block_inst.body = .{2001 child_block.instructions.items.len);
1962 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),2002 sema.air_instructions.items(.data)[merges.block_inst] = .{ .ty_pl = .{
1963 };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);
1964 // Now that the block has its type resolved, we need to go back into all the break2009 // Now that the block has its type resolved, we need to go back into all the break
1965 // instructions, and insert type coercion on the operands.2010 // instructions, and insert type coercion on the operands.
1966 for (merges.br_list.items) |br| {2011 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)) {
1968 // No type coercion needed.2016 // No type coercion needed.
1969 continue;2017 continue;
1970 }2018 }
1971 var coerce_block = parent_block.makeSubBlock();2019 var coerce_block = parent_block.makeSubBlock();
1972 defer coerce_block.instructions.deinit(sema.gpa);2020 defer coerce_block.instructions.deinit(gpa);
1973 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src);2021 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
1974 // If no instructions were produced, such as in the case of a coercion of a2022 // If no instructions were produced, such as in the case of a coercion of a
1975 // constant value to a new type, we can simply point the br operand to it.2023 // constant value to a new type, we can simply point the br operand to it.
1976 if (coerce_block.instructions.items.len == 0) {2024 if (coerce_block.instructions.items.len == 0) {
1977 br.operand = coerced_operand;2025 sema.air_instructions.items(.data)[br].br.operand = coerced_operand;
1978 continue;2026 continue;
1979 }2027 }
1980 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);2028 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==
1981 // Here we depend on the br instruction having been over-allocated (if necessary)2029 Air.refToIndex(coerced_operand).?);
1982 // inside zirBreak so that it can be converted into a br_block_flat instruction.2030
1983 const br_src = br.base.src;2031 // Convert the br operand to a block.
1984 const br_ty = br.base.ty;2032 const br_operand_ty_ref = try sema.addType(br_operand_ty);
1985 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);2033 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
1986 br_block_flat.* = .{2034 coerce_block.instructions.items.len);
1987 .base = .{2035 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
1988 .src = br_src,2036 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
1989 .ty = br_ty,2037 const sub_br_inst = sub_block_inst + 1;
1990 .tag = .br_block_flat,2038 sema.air_instructions.items(.data)[br].br.operand = Air.indexToRef(sub_block_inst);
1991 },2039 sema.air_instructions.appendAssumeCapacity(.{
1992 .block = merges.block_inst,2040 .tag = .block,
1993 .body = .{2041 .data = .{ .ty_pl = .{
1994 .instructions = try sema.arena.dupe(*Inst, coerce_block.instructions.items),2042 .ty = br_operand_ty_ref,
1995 },2043 .payload = sema.addExtraAssumeCapacity(Air.Block{
1996 };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 });
1997 }2057 }
1998 return &merges.block_inst.base;2058 return Air.indexToRef(merges.block_inst);
1999}2059}
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 {
2002 const tracy = trace(@src());2062 const tracy = trace(@src());
2003 defer tracy.end();2063 defer tracy.end();
20042064
...@@ -2034,13 +2094,13 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!...@@ -2034,13 +2094,13 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
2034 try sema.mod.analyzeExport(&block.base, src, export_name, decl);2094 try sema.mod.analyzeExport(&block.base, src, export_name, decl);
2035}2095}
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 {
2038 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2098 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2039 const src: LazySrcLoc = inst_data.src();2099 const src: LazySrcLoc = inst_data.src();
2040 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetAlignStack", .{});2100 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetAlignStack", .{});
2041}2101}
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 {
2044 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2104 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2045 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2105 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2046 const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand);2106 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...@@ -2048,67 +2108,49 @@ fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
2048 func.is_cold = is_cold;2108 func.is_cold = is_cold;
2049}2109}
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 {
2052 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2112 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2053 const src: LazySrcLoc = inst_data.src();2113 const src: LazySrcLoc = inst_data.src();
2054 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetFloatMode", .{});2114 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetFloatMode", .{});
2055}2115}
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 {
2058 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2118 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2059 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2119 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2060 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);2120 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
2061}2121}
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 {
2064 const tracy = trace(@src());2124 const tracy = trace(@src());
2065 defer tracy.end();2125 defer tracy.end();
20662126
2067 const src_node = sema.code.instructions.items(.data)[inst].node;2127 const src_node = sema.code.instructions.items(.data)[inst].node;
2068 const src: LazySrcLoc = .{ .node_offset = src_node };2128 const src: LazySrcLoc = .{ .node_offset = src_node };
2069 try sema.requireRuntimeBlock(block, src);2129 try sema.requireRuntimeBlock(block, src);
2070 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);2130 _ = try block.addNoOp(.breakpoint);
2071}2131}
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 {
2074 const src_node = sema.code.instructions.items(.data)[inst].node;2134 const src_node = sema.code.instructions.items(.data)[inst].node;
2075 const src: LazySrcLoc = .{ .node_offset = src_node };2135 const src: LazySrcLoc = .{ .node_offset = src_node };
2076 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{});2136 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{});
2077}2137}
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 {
2080 const tracy = trace(@src());2140 const tracy = trace(@src());
2081 defer tracy.end();2141 defer tracy.end();
20822142
2083 const inst_data = sema.code.instructions.items(.data)[inst].@"break";2143 const inst_data = sema.code.instructions.items(.data)[inst].@"break";
2084 const src = sema.src;2144 const operand = sema.resolveInst(inst_data.operand);
2085 const operand = try sema.resolveInst(inst_data.operand);
2086 const zir_block = inst_data.block_inst;2145 const zir_block = inst_data.block_inst;
20872146
2088 var block = start_block;2147 var block = start_block;
2089 while (true) {2148 while (true) {
2090 if (block.label) |label| {2149 if (block.label) |label| {
2091 if (label.zir_block == zir_block) {2150 if (label.zir_block == zir_block) {
2092 // Here we add a br instruction, but we over-allocate a little bit2151 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
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);
2110 try label.merges.results.append(sema.gpa, operand);2152 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).?);
2112 return inst;2154 return inst;
2113 }2155 }
2114 }2156 }
...@@ -2116,7 +2158,7 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -2116,7 +2158,7 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
2116 }2158 }
2117}2159}
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 {
2120 const tracy = trace(@src());2162 const tracy = trace(@src());
2121 defer tracy.end();2163 defer tracy.end();
21222164
...@@ -2127,10 +2169,16 @@ fn zirDbgStmt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -2127,10 +2169,16 @@ fn zirDbgStmt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
2127 if (block.is_comptime) return;2169 if (block.is_comptime) return;
21282170
2129 const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;2171 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 });
2131}2179}
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 {
2134 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;2182 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
2135 const src = inst_data.src();2183 const src = inst_data.src();
2136 const decl_name = inst_data.get(sema.code);2184 const decl_name = inst_data.get(sema.code);
...@@ -2138,7 +2186,7 @@ fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -2138,7 +2186,7 @@ fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
2138 return sema.analyzeDeclRef(block, src, decl);2186 return sema.analyzeDeclRef(block, src, decl);
2139}2187}
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 {
2142 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;2190 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
2143 const src = inst_data.src();2191 const src = inst_data.src();
2144 const decl_name = inst_data.get(sema.code);2192 const decl_name = inst_data.get(sema.code);
...@@ -2164,7 +2212,7 @@ fn lookupInNamespace(...@@ -2164,7 +2212,7 @@ fn lookupInNamespace(
2164 sema: *Sema,2212 sema: *Sema,
2165 namespace: *Scope.Namespace,2213 namespace: *Scope.Namespace,
2166 ident_name: []const u8,2214 ident_name: []const u8,
2167) InnerError!?*Decl {2215) CompileError!?*Decl {
2168 const namespace_decl = namespace.getDecl();2216 const namespace_decl = namespace.getDecl();
2169 if (namespace_decl.analysis == .file_failure) {2217 if (namespace_decl.analysis == .file_failure) {
2170 try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);2218 try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);
...@@ -2192,7 +2240,7 @@ fn zirCall(...@@ -2192,7 +2240,7 @@ fn zirCall(
2192 inst: Zir.Inst.Index,2240 inst: Zir.Inst.Index,
2193 modifier: std.builtin.CallOptions.Modifier,2241 modifier: std.builtin.CallOptions.Modifier,
2194 ensure_result_used: bool,2242 ensure_result_used: bool,
2195) InnerError!*Inst {2243) CompileError!Air.Inst.Ref {
2196 const tracy = trace(@src());2244 const tracy = trace(@src());
2197 defer tracy.end();2245 defer tracy.end();
21982246
...@@ -2202,12 +2250,12 @@ fn zirCall(...@@ -2202,12 +2250,12 @@ fn zirCall(
2202 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);2250 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);
2203 const args = sema.code.refSlice(extra.end, extra.data.args_len);2251 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);
2206 // TODO handle function calls of generic functions2254 // 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);
2208 for (args) |zir_arg, i| {2256 for (args) |zir_arg, i| {
2209 // the args are already casted to the result of a param type instruction.2257 // 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);
2211 }2259 }
22122260
2213 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);2261 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
...@@ -2216,17 +2264,18 @@ fn zirCall(...@@ -2216,17 +2264,18 @@ fn zirCall(
2216fn analyzeCall(2264fn analyzeCall(
2217 sema: *Sema,2265 sema: *Sema,
2218 block: *Scope.Block,2266 block: *Scope.Block,
2219 func: *ir.Inst,2267 func: Air.Inst.Ref,
2220 func_src: LazySrcLoc,2268 func_src: LazySrcLoc,
2221 call_src: LazySrcLoc,2269 call_src: LazySrcLoc,
2222 modifier: std.builtin.CallOptions.Modifier,2270 modifier: std.builtin.CallOptions.Modifier,
2223 ensure_result_used: bool,2271 ensure_result_used: bool,
2224 args: []const *ir.Inst,2272 args: []const Air.Inst.Ref,
2225) InnerError!*ir.Inst {2273) CompileError!Air.Inst.Ref {
2226 if (func.ty.zigTypeTag() != .Fn)2274 const func_ty = sema.typeOf(func);
2227 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});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();
2230 if (cc == .Naked) {2279 if (cc == .Naked) {
2231 // TODO add error note: declared here2280 // TODO add error note: declared here
2232 return sema.mod.fail(2281 return sema.mod.fail(
...@@ -2236,8 +2285,8 @@ fn analyzeCall(...@@ -2236,8 +2285,8 @@ fn analyzeCall(
2236 .{},2285 .{},
2237 );2286 );
2238 }2287 }
2239 const fn_params_len = func.ty.fnParamLen();2288 const fn_params_len = func_ty.fnParamLen();
2240 if (func.ty.fnIsVarArgs()) {2289 if (func_ty.fnIsVarArgs()) {
2241 assert(cc == .C);2290 assert(cc == .C);
2242 if (args.len < fn_params_len) {2291 if (args.len < fn_params_len) {
2243 // TODO add error note: declared here2292 // TODO add error note: declared here
...@@ -2274,12 +2323,12 @@ fn analyzeCall(...@@ -2274,12 +2323,12 @@ fn analyzeCall(
2274 }),2323 }),
2275 }2324 }
22762325
2277 const ret_type = func.ty.fnReturnType();2326 const gpa = sema.gpa;
22782327
2279 const is_comptime_call = block.is_comptime or modifier == .compile_time;2328 const is_comptime_call = block.is_comptime or modifier == .compile_time;
2280 const is_inline_call = is_comptime_call or modifier == .always_inline or2329 const is_inline_call = is_comptime_call or modifier == .always_inline or
2281 func.ty.fnCallingConvention() == .Inline;2330 func_ty.fnCallingConvention() == .Inline;
2282 const result: *Inst = if (is_inline_call) res: {2331 const result: Air.Inst.Ref = if (is_inline_call) res: {
2283 const func_val = try sema.resolveConstValue(block, func_src, func);2332 const func_val = try sema.resolveConstValue(block, func_src, func);
2284 const module_fn = switch (func_val.tag()) {2333 const module_fn = switch (func_val.tag()) {
2285 .function => func_val.castTag(.function).?.data,2334 .function => func_val.castTag(.function).?.data,
...@@ -2294,15 +2343,11 @@ fn analyzeCall(...@@ -2294,15 +2343,11 @@ fn analyzeCall(
2294 // set to in the `Scope.Block`.2343 // set to in the `Scope.Block`.
2295 // This block instruction will be used to capture the return value from the2344 // This block instruction will be used to capture the return value from the
2296 // inlined function.2345 // inlined function.
2297 const block_inst = try sema.arena.create(Inst.Block);2346 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
2298 block_inst.* = .{2347 try sema.air_instructions.append(gpa, .{
2299 .base = .{2348 .tag = .block,
2300 .tag = Inst.Block.base_tag,2349 .data = undefined,
2301 .ty = ret_type,2350 });
2302 .src = call_src,
2303 },
2304 .body = undefined,
2305 };
2306 // This one is shared among sub-blocks within the same callee, but not2351 // This one is shared among sub-blocks within the same callee, but not
2307 // shared among the entire inline/comptime call stack.2352 // shared among the entire inline/comptime call stack.
2308 var inlining: Scope.Block.Inlining = .{2353 var inlining: Scope.Block.Inlining = .{
...@@ -2321,7 +2366,7 @@ fn analyzeCall(...@@ -2321,7 +2366,7 @@ fn analyzeCall(
2321 const parent_inst_map = sema.inst_map;2366 const parent_inst_map = sema.inst_map;
2322 sema.inst_map = .{};2367 sema.inst_map = .{};
2323 defer {2368 defer {
2324 sema.inst_map.deinit(sema.gpa);2369 sema.inst_map.deinit(gpa);
2325 sema.inst_map = parent_inst_map;2370 sema.inst_map = parent_inst_map;
2326 }2371 }
23272372
...@@ -2353,9 +2398,9 @@ fn analyzeCall(...@@ -2353,9 +2398,9 @@ fn analyzeCall(
23532398
2354 const merges = &child_block.inlining.?.merges;2399 const merges = &child_block.inlining.?.merges;
23552400
2356 defer child_block.instructions.deinit(sema.gpa);2401 defer child_block.instructions.deinit(gpa);
2357 defer merges.results.deinit(sema.gpa);2402 defer merges.results.deinit(gpa);
2358 defer merges.br_list.deinit(sema.gpa);2403 defer merges.br_list.deinit(gpa);
23592404
2360 try sema.emitBackwardBranch(&child_block, call_src);2405 try sema.emitBackwardBranch(&child_block, call_src);
23612406
...@@ -2368,7 +2413,19 @@ fn analyzeCall(...@@ -2368,7 +2413,19 @@ fn analyzeCall(
2368 break :res result;2413 break :res result;
2369 } else res: {2414 } else res: {
2370 try sema.requireRuntimeBlock(block, call_src);2415 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;
2372 };2429 };
23732430
2374 if (ensure_result_used) {2431 if (ensure_result_used) {
...@@ -2377,19 +2434,18 @@ fn analyzeCall(...@@ -2377,19 +2434,18 @@ fn analyzeCall(
2377 return result;2434 return result;
2378}2435}
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 {
2381 _ = block;2438 _ = block;
2382 const tracy = trace(@src());2439 const tracy = trace(@src());
2383 defer tracy.end();2440 defer tracy.end();
23842441
2385 const int_type = sema.code.instructions.items(.data)[inst].int_type;2442 const int_type = sema.code.instructions.items(.data)[inst].int_type;
2386 const src = int_type.src();
2387 const ty = try Module.makeIntType(sema.arena, int_type.signedness, int_type.bit_count);2443 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);
2390}2446}
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 {
2393 const tracy = trace(@src());2449 const tracy = trace(@src());
2394 defer tracy.end();2450 defer tracy.end();
23952451
...@@ -2398,20 +2454,19 @@ fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner...@@ -2398,20 +2454,19 @@ fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
2398 const child_type = try sema.resolveType(block, src, inst_data.operand);2454 const child_type = try sema.resolveType(block, src, inst_data.operand);
2399 const opt_type = try sema.mod.optionalType(sema.arena, child_type);2455 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);
2402}2458}
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 {
2405 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2461 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2406 const src = inst_data.src();2462 const src = inst_data.src();
2407 const array_type = try sema.resolveType(block, src, inst_data.operand);2463 const array_type = try sema.resolveType(block, src, inst_data.operand);
2408 const elem_type = array_type.elemType();2464 const elem_type = array_type.elemType();
2409 return sema.mod.constType(sema.arena, src, elem_type);2465 return sema.addType(elem_type);
2410}2466}
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 {
2413 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;2469 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2414 const src = inst_data.src();
2415 const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2470 const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2416 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };2471 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2417 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;2472 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...@@ -2421,10 +2476,10 @@ fn zirVectorType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
2421 .len = len,2476 .len = len,
2422 .elem_type = elem_type,2477 .elem_type = elem_type,
2423 });2478 });
2424 return sema.mod.constType(sema.arena, src, vector_type);2479 return sema.addType(vector_type);
2425}2480}
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 {
2428 const tracy = trace(@src());2483 const tracy = trace(@src());
2429 defer tracy.end();2484 defer tracy.end();
24302485
...@@ -2434,10 +2489,10 @@ fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -2434,10 +2489,10 @@ fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
2434 const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);2489 const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);
2435 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type);2490 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);
2438}2493}
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 {
2441 const tracy = trace(@src());2496 const tracy = trace(@src());
2442 defer tracy.end();2497 defer tracy.end();
24432498
...@@ -2449,29 +2504,27 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)...@@ -2449,29 +2504,27 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
2449 const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type);2504 const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type);
2450 const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type);2505 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);
2453}2508}
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 {
2456 const tracy = trace(@src());2511 const tracy = trace(@src());
2457 defer tracy.end();2512 defer tracy.end();
24582513
2459 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2514 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2460 const src = inst_data.src();
2461 const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node };2515 const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node };
2462 const return_type = try sema.resolveType(block, operand_src, inst_data.operand);2516 const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
2463 const anyframe_type = try Type.Tag.anyframe_T.create(sema.arena, return_type);2517 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);
2466}2520}
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 {
2469 const tracy = trace(@src());2523 const tracy = trace(@src());
2470 defer tracy.end();2524 defer tracy.end();
24712525
2472 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;2526 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2473 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;2527 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 };
2475 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };2528 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
2476 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };2529 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
2477 const error_union = try sema.resolveType(block, lhs_src, extra.lhs);2530 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...@@ -2483,59 +2536,55 @@ fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
2483 });2536 });
2484 }2537 }
2485 const err_union_ty = try sema.mod.errorUnionType(sema.arena, error_union, payload);2538 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);
2487}2540}
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 {
2490 _ = block;2543 _ = block;
2491 const tracy = trace(@src());2544 const tracy = trace(@src());
2492 defer tracy.end();2545 defer tracy.end();
24932546
2494 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;2547 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
2495 const src = inst_data.src();
24962548
2497 // Create an anonymous error set type with only this error value, and return the value.2549 // Create an anonymous error set type with only this error value, and return the value.
2498 const kv = try sema.mod.getErrorValue(inst_data.get(sema.code));2550 const kv = try sema.mod.getErrorValue(inst_data.get(sema.code));
2499 const result_type = try Type.Tag.error_set_single.create(sema.arena, kv.key);2551 const result_type = try Type.Tag.error_set_single.create(sema.arena, kv.key);
2500 return sema.mod.constInst(sema.arena, src, .{2552 return sema.addConstant(
2501 .ty = result_type,2553 result_type,
2502 .val = try Value.Tag.@"error".create(sema.arena, .{2554 try Value.Tag.@"error".create(sema.arena, .{
2503 .name = kv.key,2555 .name = kv.key,
2504 }),2556 }),
2505 });2557 );
2506}2558}
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 {
2509 const tracy = trace(@src());2561 const tracy = trace(@src());
2510 defer tracy.end();2562 defer tracy.end();
25112563
2512 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2564 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2513 const src = inst_data.src();2565 const src = inst_data.src();
2514 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2566 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);
2516 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);2568 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
2517 const result_ty = Type.initTag(.u16);2569 const result_ty = Type.initTag(.u16);
25182570
2519 if (try sema.resolvePossiblyUndefinedValue(block, src, op_coerced)) |val| {2571 if (try sema.resolvePossiblyUndefinedValue(block, src, op_coerced)) |val| {
2520 if (val.isUndef()) {2572 if (val.isUndef()) {
2521 return sema.mod.constUndef(sema.arena, src, result_ty);2573 return sema.addConstUndef(result_ty);
2522 }2574 }
2523 const payload = try sema.arena.create(Value.Payload.U64);2575 const payload = try sema.arena.create(Value.Payload.U64);
2524 payload.* = .{2576 payload.* = .{
2525 .base = .{ .tag = .int_u64 },2577 .base = .{ .tag = .int_u64 },
2526 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,2578 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
2527 };2579 };
2528 return sema.mod.constInst(sema.arena, src, .{2580 return sema.addConstant(result_ty, Value.initPayload(&payload.base));
2529 .ty = result_ty,
2530 .val = Value.initPayload(&payload.base),
2531 });
2532 }2581 }
25332582
2534 try sema.requireRuntimeBlock(block, src);2583 try sema.requireRuntimeBlock(block, src);
2535 return block.addUnOp(src, result_ty, .bitcast, op_coerced);2584 return block.addTyOp(.bitcast, result_ty, op_coerced);
2536}2585}
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 {
2539 const tracy = trace(@src());2588 const tracy = trace(@src());
2540 defer tracy.end();2589 defer tracy.end();
25412590
...@@ -2543,7 +2592,7 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr...@@ -2543,7 +2592,7 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
2543 const src = inst_data.src();2592 const src = inst_data.src();
2544 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2593 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
2548 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {2597 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
2549 const int = value.toUnsignedInt();2598 const int = value.toUnsignedInt();
...@@ -2554,10 +2603,7 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr...@@ -2554,10 +2603,7 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
2554 .base = .{ .tag = .@"error" },2603 .base = .{ .tag = .@"error" },
2555 .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },2604 .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },
2556 };2605 };
2557 return sema.mod.constInst(sema.arena, src, .{2606 return sema.addConstant(Type.initTag(.anyerror), Value.initPayload(&payload.base));
2558 .ty = Type.initTag(.anyerror),
2559 .val = Value.initPayload(&payload.base),
2560 });
2561 }2607 }
2562 try sema.requireRuntimeBlock(block, src);2608 try sema.requireRuntimeBlock(block, src);
2563 if (block.wantSafety()) {2609 if (block.wantSafety()) {
...@@ -2565,10 +2611,10 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr...@@ -2565,10 +2611,10 @@ fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
2565 // const is_gt_max = @panic("TODO get max errors in compilation");2611 // const is_gt_max = @panic("TODO get max errors in compilation");
2566 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);2612 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
2567 }2613 }
2568 return block.addUnOp(src, Type.initTag(.anyerror), .bitcast, op);2614 return block.addTyOp(.bitcast, Type.initTag(.anyerror), op);
2569}2615}
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 {
2572 const tracy = trace(@src());2618 const tracy = trace(@src());
2573 defer tracy.end();2619 defer tracy.end();
25742620
...@@ -2577,9 +2623,9 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn...@@ -2577,9 +2623,9 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
2577 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };2623 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
2578 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };2624 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
2579 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };2625 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
2580 const lhs = try sema.resolveInst(extra.lhs);2626 const lhs = sema.resolveInst(extra.lhs);
2581 const rhs = try sema.resolveInst(extra.rhs);2627 const rhs = sema.resolveInst(extra.rhs);
2582 if (rhs.ty.zigTypeTag() == .Bool and lhs.ty.zigTypeTag() == .Bool) {2628 if (sema.typeOf(lhs).zigTypeTag() == .Bool and sema.typeOf(rhs).zigTypeTag() == .Bool) {
2583 const msg = msg: {2629 const msg = msg: {
2584 const msg = try sema.mod.errMsg(&block.base, lhs_src, "expected error set type, found 'bool'", .{});2630 const msg = try sema.mod.errMsg(&block.base, lhs_src, "expected error set type, found 'bool'", .{});
2585 errdefer msg.destroy(sema.gpa);2631 errdefer msg.destroy(sema.gpa);
...@@ -2588,19 +2634,16 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn...@@ -2588,19 +2634,16 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
2588 };2634 };
2589 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);2635 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
2590 }2636 }
2591 const rhs_ty = try sema.resolveAirAsType(block, rhs_src, rhs);2637 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
2592 const lhs_ty = try sema.resolveAirAsType(block, lhs_src, lhs);2638 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
2593 if (rhs_ty.zigTypeTag() != .ErrorSet)
2594 return sema.mod.fail(&block.base, rhs_src, "expected error set type, found {}", .{rhs_ty});
2595 if (lhs_ty.zigTypeTag() != .ErrorSet)2639 if (lhs_ty.zigTypeTag() != .ErrorSet)
2596 return sema.mod.fail(&block.base, lhs_src, "expected error set type, found {}", .{lhs_ty});2640 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
2598 // Anything merged with anyerror is anyerror.2644 // Anything merged with anyerror is anyerror.
2599 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {2645 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
2600 return sema.mod.constInst(sema.arena, src, .{2646 return Air.Inst.Ref.anyerror_type;
2601 .ty = Type.initTag(.type),
2602 .val = Value.initTag(.anyerror_type),
2603 });
2604 }2647 }
2605 // When we support inferred error sets, we'll want to use a data structure that can2648 // When we support inferred error sets, we'll want to use a data structure that can
2606 // represent a merged set of errors without forcing them to be resolved here. Until then2649 // 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...@@ -2652,38 +2695,35 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
2652 .names_len = @intCast(u32, new_names.len),2695 .names_len = @intCast(u32, new_names.len),
2653 };2696 };
2654 const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);2697 const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);
2655 return sema.mod.constInst(sema.arena, src, .{2698 return sema.addConstant(Type.initTag(.type), try Value.Tag.ty.create(sema.arena, error_set_ty));
2656 .ty = Type.initTag(.type),
2657 .val = try Value.Tag.ty.create(sema.arena, error_set_ty),
2658 });
2659}2699}
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 {
2662 _ = block;2702 _ = block;
2663 const tracy = trace(@src());2703 const tracy = trace(@src());
2664 defer tracy.end();2704 defer tracy.end();
26652705
2666 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;2706 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
2667 const src = inst_data.src();
2668 const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code));2707 const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code));
2669 return sema.mod.constInst(sema.arena, src, .{2708 return sema.addConstant(
2670 .ty = Type.initTag(.enum_literal),2709 Type.initTag(.enum_literal),
2671 .val = try Value.Tag.enum_literal.create(sema.arena, duped_name),2710 try Value.Tag.enum_literal.create(sema.arena, duped_name),
2672 });2711 );
2673}2712}
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 {
2676 const mod = sema.mod;2715 const mod = sema.mod;
2677 const arena = sema.arena;2716 const arena = sema.arena;
2678 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2717 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2679 const src = inst_data.src();2718 const src = inst_data.src();
2680 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2719 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()) {
2684 .Enum => operand,2724 .Enum => operand,
2685 .Union => {2725 .Union => {
2686 //if (!operand.ty.unionHasTag()) {2726 //if (!operand_ty.unionHasTag()) {
2687 // return mod.fail(2727 // return mod.fail(
2688 // &block.base,2728 // &block.base,
2689 // operand_src,2729 // operand_src,
...@@ -2695,91 +2735,73 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -2695,91 +2735,73 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
2695 },2735 },
2696 else => {2736 else => {
2697 return mod.fail(&block.base, operand_src, "expected enum or tagged union, found {}", .{2737 return mod.fail(&block.base, operand_src, "expected enum or tagged union, found {}", .{
2698 operand.ty,2738 operand_ty,
2699 });2739 });
2700 },2740 },
2701 };2741 };
2742 const enum_tag_ty = sema.typeOf(enum_tag);
27022743
2703 var int_tag_type_buffer: Type.Payload.Bits = undefined;2744 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| {2747 if (try sema.typeHasOnePossibleValue(block, src, enum_tag_ty)) |opv| {
2707 return mod.constInst(arena, src, .{2748 return sema.addConstant(int_tag_ty, opv);
2708 .ty = int_tag_ty,
2709 .val = opv,
2710 });
2711 }2749 }
27122750
2713 if (enum_tag.value()) |enum_tag_val| {2751 if (try sema.resolvePossiblyUndefinedValue(block, operand_src, enum_tag)) |enum_tag_val| {
2714 if (enum_tag_val.castTag(.enum_field_index)) |enum_field_payload| {2752 if (enum_tag_val.castTag(.enum_field_index)) |enum_field_payload| {
2715 const field_index = enum_field_payload.data;2753 const field_index = enum_field_payload.data;
2716 switch (enum_tag.ty.tag()) {2754 switch (enum_tag_ty.tag()) {
2717 .enum_full => {2755 .enum_full => {
2718 const enum_full = enum_tag.ty.castTag(.enum_full).?.data;2756 const enum_full = enum_tag_ty.castTag(.enum_full).?.data;
2719 if (enum_full.values.count() != 0) {2757 if (enum_full.values.count() != 0) {
2720 const val = enum_full.values.keys()[field_index];2758 const val = enum_full.values.keys()[field_index];
2721 return mod.constInst(arena, src, .{2759 return sema.addConstant(int_tag_ty, val);
2722 .ty = int_tag_ty,
2723 .val = val,
2724 });
2725 } else {2760 } else {
2726 // Field index and integer values are the same.2761 // Field index and integer values are the same.
2727 const val = try Value.Tag.int_u64.create(arena, field_index);2762 const val = try Value.Tag.int_u64.create(arena, field_index);
2728 return mod.constInst(arena, src, .{2763 return sema.addConstant(int_tag_ty, val);
2729 .ty = int_tag_ty,
2730 .val = val,
2731 });
2732 }2764 }
2733 },2765 },
2734 .enum_simple => {2766 .enum_simple => {
2735 // Field index and integer values are the same.2767 // Field index and integer values are the same.
2736 const val = try Value.Tag.int_u64.create(arena, field_index);2768 const val = try Value.Tag.int_u64.create(arena, field_index);
2737 return mod.constInst(arena, src, .{2769 return sema.addConstant(int_tag_ty, val);
2738 .ty = int_tag_ty,
2739 .val = val,
2740 });
2741 },2770 },
2742 else => unreachable,2771 else => unreachable,
2743 }2772 }
2744 } else {2773 } else {
2745 // Assume it is already an integer and return it directly.2774 // Assume it is already an integer and return it directly.
2746 return mod.constInst(arena, src, .{2775 return sema.addConstant(int_tag_ty, enum_tag_val);
2747 .ty = int_tag_ty,
2748 .val = enum_tag_val,
2749 });
2750 }2776 }
2751 }2777 }
27522778
2753 try sema.requireRuntimeBlock(block, src);2779 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);
2755}2781}
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 {
2758 const mod = sema.mod;2784 const mod = sema.mod;
2759 const target = mod.getTarget();2785 const target = mod.getTarget();
2760 const arena = sema.arena;
2761 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;2786 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2762 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;2787 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
2763 const src = inst_data.src();2788 const src = inst_data.src();
2764 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2789 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2765 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };2790 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2766 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);2791 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
2769 if (dest_ty.zigTypeTag() != .Enum) {2794 if (dest_ty.zigTypeTag() != .Enum) {
2770 return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty});2795 return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty});
2771 }2796 }
27722797
2773 if (dest_ty.isNonexhaustiveEnum()) {2798 if (try sema.resolvePossiblyUndefinedValue(block, operand_src, operand)) |int_val| {
2774 if (operand.value()) |int_val| {2799 if (dest_ty.isNonexhaustiveEnum()) {
2775 return mod.constInst(arena, src, .{2800 return sema.addConstant(dest_ty, int_val);
2776 .ty = dest_ty,2801 }
2777 .val = int_val,2802 if (int_val.isUndef()) {
2778 });2803 return sema.failWithUseOfUndef(block, operand_src);
2779 }2804 }
2780 }
2781
2782 if (try sema.resolveDefinedValue(block, operand_src, operand)) |int_val| {
2783 if (!dest_ty.enumHasInt(int_val, target)) {2805 if (!dest_ty.enumHasInt(int_val, target)) {
2784 const msg = msg: {2806 const msg = msg: {
2785 const msg = try mod.errMsg(2807 const msg = try mod.errMsg(
...@@ -2799,14 +2821,11 @@ fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -2799,14 +2821,11 @@ fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
2799 };2821 };
2800 return mod.failWithOwnedErrorMsg(&block.base, msg);2822 return mod.failWithOwnedErrorMsg(&block.base, msg);
2801 }2823 }
2802 return mod.constInst(arena, src, .{2824 return sema.addConstant(dest_ty, int_val);
2803 .ty = dest_ty,
2804 .val = int_val,
2805 });
2806 }2825 }
28072826
2808 try sema.requireRuntimeBlock(block, src);2827 try sema.requireRuntimeBlock(block, src);
2809 return block.addUnOp(src, dest_ty, .bitcast, operand);2828 return block.addTyOp(.bitcast, dest_ty, operand);
2810}2829}
28112830
2812/// Pointer in, pointer out.2831/// Pointer in, pointer out.
...@@ -2815,41 +2834,39 @@ fn zirOptionalPayloadPtr(...@@ -2815,41 +2834,39 @@ fn zirOptionalPayloadPtr(
2815 block: *Scope.Block,2834 block: *Scope.Block,
2816 inst: Zir.Inst.Index,2835 inst: Zir.Inst.Index,
2817 safety_check: bool,2836 safety_check: bool,
2818) InnerError!*Inst {2837) CompileError!Air.Inst.Ref {
2819 const tracy = trace(@src());2838 const tracy = trace(@src());
2820 defer tracy.end();2839 defer tracy.end();
28212840
2822 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2841 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2823 const optional_ptr = try sema.resolveInst(inst_data.operand);2842 const optional_ptr = sema.resolveInst(inst_data.operand);
2824 assert(optional_ptr.ty.zigTypeTag() == .Pointer);2843 const optional_ptr_ty = sema.typeOf(optional_ptr);
2844 assert(optional_ptr_ty.zigTypeTag() == .Pointer);
2825 const src = inst_data.src();2845 const src = inst_data.src();
28262846
2827 const opt_type = optional_ptr.ty.elemType();2847 const opt_type = optional_ptr_ty.elemType();
2828 if (opt_type.zigTypeTag() != .Optional) {2848 if (opt_type.zigTypeTag() != .Optional) {
2829 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});2849 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
2830 }2850 }
28312851
2832 const child_type = try opt_type.optionalChildAlloc(sema.arena);2852 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| {
2836 const val = try pointer_val.pointerDeref(sema.arena);2856 const val = try pointer_val.pointerDeref(sema.arena);
2837 if (val.isNull()) {2857 if (val.isNull()) {
2838 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});2858 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
2839 }2859 }
2840 // The same Value represents the pointer to the optional and the payload.2860 // The same Value represents the pointer to the optional and the payload.
2841 return sema.mod.constInst(sema.arena, src, .{2861 return sema.addConstant(child_pointer, pointer_val);
2842 .ty = child_pointer,
2843 .val = pointer_val,
2844 });
2845 }2862 }
28462863
2847 try sema.requireRuntimeBlock(block, src);2864 try sema.requireRuntimeBlock(block, src);
2848 if (safety_check and block.wantSafety()) {2865 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);
2850 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);2867 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
2851 }2868 }
2852 return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr);2869 return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
2853}2870}
28542871
2855/// Value in, value out.2872/// Value in, value out.
...@@ -2858,36 +2875,34 @@ fn zirOptionalPayload(...@@ -2858,36 +2875,34 @@ fn zirOptionalPayload(
2858 block: *Scope.Block,2875 block: *Scope.Block,
2859 inst: Zir.Inst.Index,2876 inst: Zir.Inst.Index,
2860 safety_check: bool,2877 safety_check: bool,
2861) InnerError!*Inst {2878) CompileError!Air.Inst.Ref {
2862 const tracy = trace(@src());2879 const tracy = trace(@src());
2863 defer tracy.end();2880 defer tracy.end();
28642881
2865 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2882 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2866 const src = inst_data.src();2883 const src = inst_data.src();
2867 const operand = try sema.resolveInst(inst_data.operand);2884 const operand = sema.resolveInst(inst_data.operand);
2868 const opt_type = operand.ty;2885 const operand_ty = sema.typeOf(operand);
2886 const opt_type = operand_ty;
2869 if (opt_type.zigTypeTag() != .Optional) {2887 if (opt_type.zigTypeTag() != .Optional) {
2870 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});2888 return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type});
2871 }2889 }
28722890
2873 const child_type = try opt_type.optionalChildAlloc(sema.arena);2891 const child_type = try opt_type.optionalChildAlloc(sema.arena);
28742892
2875 if (operand.value()) |val| {2893 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
2876 if (val.isNull()) {2894 if (val.isNull()) {
2877 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});2895 return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
2878 }2896 }
2879 return sema.mod.constInst(sema.arena, src, .{2897 return sema.addConstant(child_type, val);
2880 .ty = child_type,
2881 .val = val,
2882 });
2883 }2898 }
28842899
2885 try sema.requireRuntimeBlock(block, src);2900 try sema.requireRuntimeBlock(block, src);
2886 if (safety_check and block.wantSafety()) {2901 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);
2888 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);2903 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
2889 }2904 }
2890 return block.addUnOp(src, child_type, .optional_payload, operand);2905 return block.addTyOp(.optional_payload, child_type, operand);
2891}2906}
28922907
2893/// Value in, value out2908/// Value in, value out
...@@ -2896,32 +2911,35 @@ fn zirErrUnionPayload(...@@ -2896,32 +2911,35 @@ fn zirErrUnionPayload(
2896 block: *Scope.Block,2911 block: *Scope.Block,
2897 inst: Zir.Inst.Index,2912 inst: Zir.Inst.Index,
2898 safety_check: bool,2913 safety_check: bool,
2899) InnerError!*Inst {2914) CompileError!Air.Inst.Ref {
2900 const tracy = trace(@src());2915 const tracy = trace(@src());
2901 defer tracy.end();2916 defer tracy.end();
29022917
2903 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2918 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2904 const src = inst_data.src();2919 const src = inst_data.src();
2905 const operand = try sema.resolveInst(inst_data.operand);2920 const operand = sema.resolveInst(inst_data.operand);
2906 if (operand.ty.zigTypeTag() != .ErrorUnion)2921 const operand_src = src;
2907 return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty});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| {
2910 if (val.getError()) |name| {2927 if (val.getError()) |name| {
2911 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});2928 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
2912 }2929 }
2913 const data = val.castTag(.error_union).?.data;2930 const data = val.castTag(.error_union).?.data;
2914 return sema.mod.constInst(sema.arena, src, .{2931 return sema.addConstant(
2915 .ty = operand.ty.castTag(.error_union).?.data.payload,2932 operand_ty.castTag(.error_union).?.data.payload,
2916 .val = data,2933 data,
2917 });2934 );
2918 }2935 }
2919 try sema.requireRuntimeBlock(block, src);2936 try sema.requireRuntimeBlock(block, src);
2920 if (safety_check and block.wantSafety()) {2937 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);
2922 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);2939 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
2923 }2940 }
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);
2925}2943}
29262944
2927/// Pointer in, pointer out.2945/// Pointer in, pointer out.
...@@ -2930,109 +2948,107 @@ fn zirErrUnionPayloadPtr(...@@ -2930,109 +2948,107 @@ fn zirErrUnionPayloadPtr(
2930 block: *Scope.Block,2948 block: *Scope.Block,
2931 inst: Zir.Inst.Index,2949 inst: Zir.Inst.Index,
2932 safety_check: bool,2950 safety_check: bool,
2933) InnerError!*Inst {2951) CompileError!Air.Inst.Ref {
2934 const tracy = trace(@src());2952 const tracy = trace(@src());
2935 defer tracy.end();2953 defer tracy.end();
29362954
2937 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2955 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2938 const src = inst_data.src();2956 const src = inst_data.src();
2939 const operand = try sema.resolveInst(inst_data.operand);2957 const operand = sema.resolveInst(inst_data.operand);
2940 assert(operand.ty.zigTypeTag() == .Pointer);2958 const operand_ty = sema.typeOf(operand);
2959 assert(operand_ty.zigTypeTag() == .Pointer);
29412960
2942 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)2961 if (operand_ty.elemType().zigTypeTag() != .ErrorUnion)
2943 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});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| {
2948 const val = try pointer_val.pointerDeref(sema.arena);2967 const val = try pointer_val.pointerDeref(sema.arena);
2949 if (val.getError()) |name| {2968 if (val.getError()) |name| {
2950 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});2969 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});
2951 }2970 }
2952 const data = val.castTag(.error_union).?.data;2971 const data = val.castTag(.error_union).?.data;
2953 // The same Value represents the pointer to the error union and the payload.2972 // The same Value represents the pointer to the error union and the payload.
2954 return sema.mod.constInst(sema.arena, src, .{2973 return sema.addConstant(
2955 .ty = operand_pointer_ty,2974 operand_pointer_ty,
2956 .val = try Value.Tag.ref_val.create(2975 try Value.Tag.ref_val.create(
2957 sema.arena,2976 sema.arena,
2958 data,2977 data,
2959 ),2978 ),
2960 });2979 );
2961 }2980 }
29622981
2963 try sema.requireRuntimeBlock(block, src);2982 try sema.requireRuntimeBlock(block, src);
2964 if (safety_check and block.wantSafety()) {2983 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);
2966 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);2985 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
2967 }2986 }
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);
2969}2988}
29702989
2971/// Value in, value out2990/// 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 {
2973 const tracy = trace(@src());2992 const tracy = trace(@src());
2974 defer tracy.end();2993 defer tracy.end();
29752994
2976 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2995 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2977 const src = inst_data.src();2996 const src = inst_data.src();
2978 const operand = try sema.resolveInst(inst_data.operand);2997 const operand = sema.resolveInst(inst_data.operand);
2979 if (operand.ty.zigTypeTag() != .ErrorUnion)2998 const operand_ty = sema.typeOf(operand);
2980 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});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| {
2985 assert(val.getError() != null);3005 assert(val.getError() != null);
2986 const data = val.castTag(.error_union).?.data;3006 const data = val.castTag(.error_union).?.data;
2987 return sema.mod.constInst(sema.arena, src, .{3007 return sema.addConstant(result_ty, data);
2988 .ty = result_ty,
2989 .val = data,
2990 });
2991 }3008 }
29923009
2993 try sema.requireRuntimeBlock(block, src);3010 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);
2995}3012}
29963013
2997/// Pointer in, value out3014/// 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 {
2999 const tracy = trace(@src());3016 const tracy = trace(@src());
3000 defer tracy.end();3017 defer tracy.end();
30013018
3002 const inst_data = sema.code.instructions.items(.data)[inst].un_node;3019 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3003 const src = inst_data.src();3020 const src = inst_data.src();
3004 const operand = try sema.resolveInst(inst_data.operand);3021 const operand = sema.resolveInst(inst_data.operand);
3005 assert(operand.ty.zigTypeTag() == .Pointer);3022 const operand_ty = sema.typeOf(operand);
3023 assert(operand_ty.zigTypeTag() == .Pointer);
30063024
3007 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)3025 if (operand_ty.elemType().zigTypeTag() != .ErrorUnion)
3008 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});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| {
3013 const val = try pointer_val.pointerDeref(sema.arena);3031 const val = try pointer_val.pointerDeref(sema.arena);
3014 assert(val.getError() != null);3032 assert(val.getError() != null);
3015 const data = val.castTag(.error_union).?.data;3033 const data = val.castTag(.error_union).?.data;
3016 return sema.mod.constInst(sema.arena, src, .{3034 return sema.addConstant(result_ty, data);
3017 .ty = result_ty,
3018 .val = data,
3019 });
3020 }3035 }
30213036
3022 try sema.requireRuntimeBlock(block, src);3037 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);
3024}3039}
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 {
3027 const tracy = trace(@src());3042 const tracy = trace(@src());
3028 defer tracy.end();3043 defer tracy.end();
30293044
3030 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;3045 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
3031 const src = inst_data.src();3046 const src = inst_data.src();
3032 const operand = try sema.resolveInst(inst_data.operand);3047 const operand = sema.resolveInst(inst_data.operand);
3033 if (operand.ty.zigTypeTag() != .ErrorUnion)3048 const operand_ty = sema.typeOf(operand);
3034 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});3049 if (operand_ty.zigTypeTag() != .ErrorUnion)
3035 if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {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) {
3036 return sema.mod.fail(&block.base, src, "expression value is ignored", .{});3052 return sema.mod.fail(&block.base, src, "expression value is ignored", .{});
3037 }3053 }
3038}3054}
...@@ -3042,7 +3058,7 @@ fn zirFunc(...@@ -3042,7 +3058,7 @@ fn zirFunc(
3042 block: *Scope.Block,3058 block: *Scope.Block,
3043 inst: Zir.Inst.Index,3059 inst: Zir.Inst.Index,
3044 inferred_error_set: bool,3060 inferred_error_set: bool,
3045) InnerError!*Inst {3061) CompileError!Air.Inst.Ref {
3046 const tracy = trace(@src());3062 const tracy = trace(@src());
3047 defer tracy.end();3063 defer tracy.end();
30483064
...@@ -3093,7 +3109,7 @@ fn funcCommon(...@@ -3093,7 +3109,7 @@ fn funcCommon(
3093 is_extern: bool,3109 is_extern: bool,
3094 src_locs: Zir.Inst.Func.SrcLocs,3110 src_locs: Zir.Inst.Func.SrcLocs,
3095 opt_lib_name: ?[]const u8,3111 opt_lib_name: ?[]const u8,
3096) InnerError!*Inst {3112) CompileError!Air.Inst.Ref {
3097 const src: LazySrcLoc = .{ .node_offset = src_node_offset };3113 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
3098 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };3114 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
3099 const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);3115 const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
...@@ -3199,14 +3215,14 @@ fn funcCommon(...@@ -3199,14 +3215,14 @@ fn funcCommon(
3199 }3215 }
32003216
3201 if (is_extern) {3217 if (is_extern) {
3202 return sema.mod.constInst(sema.arena, src, .{3218 return sema.addConstant(
3203 .ty = fn_ty,3219 fn_ty,
3204 .val = try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl),3220 try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl),
3205 });3221 );
3206 }3222 }
32073223
3208 if (body_inst == 0) {3224 if (body_inst == 0) {
3209 return mod.constType(sema.arena, src, fn_ty);3225 return sema.addType(fn_ty);
3210 }3226 }
32113227
3212 const is_inline = fn_ty.fnCallingConvention() == .Inline;3228 const is_inline = fn_ty.fnCallingConvention() == .Inline;
...@@ -3217,7 +3233,6 @@ fn funcCommon(...@@ -3217,7 +3233,6 @@ fn funcCommon(
3217 .state = anal_state,3233 .state = anal_state,
3218 .zir_body_inst = body_inst,3234 .zir_body_inst = body_inst,
3219 .owner_decl = sema.owner_decl,3235 .owner_decl = sema.owner_decl,
3220 .body = undefined,
3221 .lbrace_line = src_locs.lbrace_line,3236 .lbrace_line = src_locs.lbrace_line,
3222 .rbrace_line = src_locs.rbrace_line,3237 .rbrace_line = src_locs.rbrace_line,
3223 .lbrace_column = @truncate(u16, src_locs.columns),3238 .lbrace_column = @truncate(u16, src_locs.columns),
...@@ -3227,14 +3242,10 @@ fn funcCommon(...@@ -3227,14 +3242,10 @@ fn funcCommon(
3227 .base = .{ .tag = .function },3242 .base = .{ .tag = .function },
3228 .data = new_func,3243 .data = new_func,
3229 };3244 };
3230 const result = try sema.mod.constInst(sema.arena, src, .{3245 return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base));
3231 .ty = fn_ty,
3232 .val = Value.initPayload(&fn_payload.base),
3233 });
3234 return result;
3235}3246}
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 {
3238 const tracy = trace(@src());3249 const tracy = trace(@src());
3239 defer tracy.end();3250 defer tracy.end();
32403251
...@@ -3242,7 +3253,7 @@ fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Ins...@@ -3242,7 +3253,7 @@ fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Ins
3242 return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);3253 return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);
3243}3254}
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 {
3246 const tracy = trace(@src());3257 const tracy = trace(@src());
3247 defer tracy.end();3258 defer tracy.end();
32483259
...@@ -3258,30 +3269,30 @@ fn analyzeAs(...@@ -3258,30 +3269,30 @@ fn analyzeAs(
3258 src: LazySrcLoc,3269 src: LazySrcLoc,
3259 zir_dest_type: Zir.Inst.Ref,3270 zir_dest_type: Zir.Inst.Ref,
3260 zir_operand: Zir.Inst.Ref,3271 zir_operand: Zir.Inst.Ref,
3261) InnerError!*Inst {3272) CompileError!Air.Inst.Ref {
3262 const dest_type = try sema.resolveType(block, src, zir_dest_type);3273 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);
3264 return sema.coerce(block, dest_type, operand, src);3275 return sema.coerce(block, dest_type, operand, src);
3265}3276}
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 {
3268 const tracy = trace(@src());3279 const tracy = trace(@src());
3269 defer tracy.end();3280 defer tracy.end();
32703281
3271 const inst_data = sema.code.instructions.items(.data)[inst].un_node;3282 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
3272 const ptr = try sema.resolveInst(inst_data.operand);3283 const ptr = sema.resolveInst(inst_data.operand);
3273 if (ptr.ty.zigTypeTag() != .Pointer) {3284 const ptr_ty = sema.typeOf(ptr);
3285 if (ptr_ty.zigTypeTag() != .Pointer) {
3274 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };3286 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});
3276 }3288 }
3277 // TODO handle known-pointer-address3289 // TODO handle known-pointer-address
3278 const src = inst_data.src();3290 const src = inst_data.src();
3279 try sema.requireRuntimeBlock(block, src);3291 try sema.requireRuntimeBlock(block, src);
3280 const ty = Type.initTag(.usize);3292 return block.addUnOp(.ptrtoint, ptr);
3281 return block.addUnOp(src, ty, .ptrtoint, ptr);
3282}3293}
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 {
3285 const tracy = trace(@src());3296 const tracy = trace(@src());
3286 defer tracy.end();3297 defer tracy.end();
32873298
...@@ -3290,16 +3301,17 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -3290,16 +3301,17 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
3290 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };3301 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
3291 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;3302 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
3292 const field_name = sema.code.nullTerminatedString(extra.field_name_start);3303 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
3293 const object = try sema.resolveInst(extra.lhs);3304 const object = sema.resolveInst(extra.lhs);
3294 const object_ptr = if (object.ty.zigTypeTag() == .Pointer)3305 const object_ptr = if (sema.typeOf(object).zigTypeTag() == .Pointer)
3295 object3306 object
3296 else3307 else
3297 try sema.analyzeRef(block, src, object);3308 try sema.analyzeRef(block, src, object);
3298 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);3309 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);
3300}3312}
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 {
3303 const tracy = trace(@src());3315 const tracy = trace(@src());
3304 defer tracy.end();3316 defer tracy.end();
33053317
...@@ -3308,11 +3320,11 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -3308,11 +3320,11 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
3308 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };3320 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
3309 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;3321 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
3310 const field_name = sema.code.nullTerminatedString(extra.field_name_start);3322 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);
3312 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);3324 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
3313}3325}
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 {
3316 const tracy = trace(@src());3328 const tracy = trace(@src());
3317 defer tracy.end();3329 defer tracy.end();
33183330
...@@ -3320,14 +3332,14 @@ fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne...@@ -3320,14 +3332,14 @@ fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
3320 const src = inst_data.src();3332 const src = inst_data.src();
3321 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };3333 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
3322 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;3334 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);
3324 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);3336 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
3325 const object_ptr = try sema.analyzeRef(block, src, object);3337 const object_ptr = try sema.analyzeRef(block, src, object);
3326 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);3338 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
3327 return sema.analyzeLoad(block, src, result_ptr, src);3339 return sema.analyzeLoad(block, src, result_ptr, src);
3328}3340}
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 {
3331 const tracy = trace(@src());3343 const tracy = trace(@src());
3332 defer tracy.end();3344 defer tracy.end();
33333345
...@@ -3335,12 +3347,12 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne...@@ -3335,12 +3347,12 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
3335 const src = inst_data.src();3347 const src = inst_data.src();
3336 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };3348 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
3337 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;3349 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);
3339 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);3351 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
3340 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);3352 return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
3341}3353}
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 {
3344 const tracy = trace(@src());3356 const tracy = trace(@src());
3345 defer tracy.end();3357 defer tracy.end();
33463358
...@@ -3351,7 +3363,7 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -3351,7 +3363,7 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
3351 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;3363 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
33523364
3353 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);3365 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
3356 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {3368 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
3357 .ComptimeInt => true,3369 .ComptimeInt => true,
...@@ -3364,17 +3376,18 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -3364,17 +3376,18 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
3364 ),3376 ),
3365 };3377 };
33663378
3367 switch (operand.ty.zigTypeTag()) {3379 const operand_ty = sema.typeOf(operand);
3380 switch (operand_ty.zigTypeTag()) {
3368 .ComptimeInt, .Int => {},3381 .ComptimeInt, .Int => {},
3369 else => return sema.mod.fail(3382 else => return sema.mod.fail(
3370 &block.base,3383 &block.base,
3371 operand_src,3384 operand_src,
3372 "expected integer type, found '{}'",3385 "expected integer type, found '{}'",
3373 .{operand.ty},3386 .{operand_ty},
3374 ),3387 ),
3375 }3388 }
33763389
3377 if (operand.value() != null) {3390 if (try sema.isComptimeKnown(block, operand_src, operand)) {
3378 return sema.coerce(block, dest_type, operand, operand_src);3391 return sema.coerce(block, dest_type, operand, operand_src);
3379 } else if (dest_is_comptime_int) {3392 } else if (dest_is_comptime_int) {
3380 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});3393 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...@@ -3383,20 +3396,21 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
3383 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{});3396 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{});
3384}3397}
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 {
3387 const tracy = trace(@src());3400 const tracy = trace(@src());
3388 defer tracy.end();3401 defer tracy.end();
33893402
3390 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;3403 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3391 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };3404 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 };
3392 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;3406 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
33933407
3394 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);3408 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
3395 const operand = try sema.resolveInst(extra.rhs);3409 const operand = sema.resolveInst(extra.rhs);
3396 return sema.bitcast(block, dest_type, operand);3410 return sema.bitcast(block, dest_type, operand, operand_src);
3397}3411}
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 {
3400 const tracy = trace(@src());3414 const tracy = trace(@src());
3401 defer tracy.end();3415 defer tracy.end();
34023416
...@@ -3407,7 +3421,7 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -3407,7 +3421,7 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
3407 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;3421 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
34083422
3409 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);3423 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
3412 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {3426 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
3413 .ComptimeFloat => true,3427 .ComptimeFloat => true,
...@@ -3420,17 +3434,18 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -3420,17 +3434,18 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
3420 ),3434 ),
3421 };3435 };
34223436
3423 switch (operand.ty.zigTypeTag()) {3437 const operand_ty = sema.typeOf(operand);
3438 switch (operand_ty.zigTypeTag()) {
3424 .ComptimeFloat, .Float, .ComptimeInt => {},3439 .ComptimeFloat, .Float, .ComptimeInt => {},
3425 else => return sema.mod.fail(3440 else => return sema.mod.fail(
3426 &block.base,3441 &block.base,
3427 operand_src,3442 operand_src,
3428 "expected float type, found '{}'",3443 "expected float type, found '{}'",
3429 .{operand.ty},3444 .{operand_ty},
3430 ),3445 ),
3431 }3446 }
34323447
3433 if (operand.value() != null) {3448 if (try sema.isComptimeKnown(block, operand_src, operand)) {
3434 return sema.coerce(block, dest_type, operand, operand_src);3449 return sema.coerce(block, dest_type, operand, operand_src);
3435 } else if (dest_is_comptime_float) {3450 } else if (dest_is_comptime_float) {
3436 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});3451 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...@@ -3439,22 +3454,23 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
3439 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});3454 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});
3440}3455}
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 {
3443 const tracy = trace(@src());3458 const tracy = trace(@src());
3444 defer tracy.end();3459 defer tracy.end();
34453460
3446 const bin_inst = sema.code.instructions.items(.data)[inst].bin;3461 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3447 const array = try sema.resolveInst(bin_inst.lhs);3462 const array = sema.resolveInst(bin_inst.lhs);
3448 const array_ptr = if (array.ty.zigTypeTag() == .Pointer)3463 const array_ty = sema.typeOf(array);
3464 const array_ptr = if (array_ty.zigTypeTag() == .Pointer)
3449 array3465 array
3450 else3466 else
3451 try sema.analyzeRef(block, sema.src, array);3467 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);
3453 const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);3469 const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
3454 return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);3470 return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);
3455}3471}
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 {
3458 const tracy = trace(@src());3474 const tracy = trace(@src());
3459 defer tracy.end();3475 defer tracy.end();
34603476
...@@ -3462,27 +3478,28 @@ fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -3462,27 +3478,28 @@ fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
3462 const src = inst_data.src();3478 const src = inst_data.src();
3463 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };3479 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
3464 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;3480 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
3465 const array = try sema.resolveInst(extra.lhs);3481 const array = sema.resolveInst(extra.lhs);
3466 const array_ptr = if (array.ty.zigTypeTag() == .Pointer)3482 const array_ty = sema.typeOf(array);
3483 const array_ptr = if (array_ty.zigTypeTag() == .Pointer)
3467 array3484 array
3468 else3485 else
3469 try sema.analyzeRef(block, src, array);3486 try sema.analyzeRef(block, src, array);
3470 const elem_index = try sema.resolveInst(extra.rhs);3487 const elem_index = sema.resolveInst(extra.rhs);
3471 const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);3488 const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
3472 return sema.analyzeLoad(block, src, result_ptr, src);3489 return sema.analyzeLoad(block, src, result_ptr, src);
3473}3490}
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 {
3476 const tracy = trace(@src());3493 const tracy = trace(@src());
3477 defer tracy.end();3494 defer tracy.end();
34783495
3479 const bin_inst = sema.code.instructions.items(.data)[inst].bin;3496 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
3480 const array_ptr = try sema.resolveInst(bin_inst.lhs);3497 const array_ptr = sema.resolveInst(bin_inst.lhs);
3481 const elem_index = try sema.resolveInst(bin_inst.rhs);3498 const elem_index = sema.resolveInst(bin_inst.rhs);
3482 return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);3499 return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
3483}3500}
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 {
3486 const tracy = trace(@src());3503 const tracy = trace(@src());
3487 defer tracy.end();3504 defer tracy.end();
34883505
...@@ -3490,39 +3507,39 @@ fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -3490,39 +3507,39 @@ fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
3490 const src = inst_data.src();3507 const src = inst_data.src();
3491 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };3508 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
3492 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;3509 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
3493 const array_ptr = try sema.resolveInst(extra.lhs);3510 const array_ptr = sema.resolveInst(extra.lhs);
3494 const elem_index = try sema.resolveInst(extra.rhs);3511 const elem_index = sema.resolveInst(extra.rhs);
3495 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);3512 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
3496}3513}
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 {
3499 const tracy = trace(@src());3516 const tracy = trace(@src());
3500 defer tracy.end();3517 defer tracy.end();
35013518
3502 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;3519 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3503 const src = inst_data.src();3520 const src = inst_data.src();
3504 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;3521 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
3505 const array_ptr = try sema.resolveInst(extra.lhs);3522 const array_ptr = sema.resolveInst(extra.lhs);
3506 const start = try sema.resolveInst(extra.start);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);
3509}3526}
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 {
3512 const tracy = trace(@src());3529 const tracy = trace(@src());
3513 defer tracy.end();3530 defer tracy.end();
35143531
3515 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;3532 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3516 const src = inst_data.src();3533 const src = inst_data.src();
3517 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;3534 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
3518 const array_ptr = try sema.resolveInst(extra.lhs);3535 const array_ptr = sema.resolveInst(extra.lhs);
3519 const start = try sema.resolveInst(extra.start);3536 const start = sema.resolveInst(extra.start);
3520 const end = try sema.resolveInst(extra.end);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);
3523}3540}
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 {
3526 const tracy = trace(@src());3543 const tracy = trace(@src());
3527 defer tracy.end();3544 defer tracy.end();
35283545
...@@ -3530,10 +3547,10 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne...@@ -3530,10 +3547,10 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
3530 const src = inst_data.src();3547 const src = inst_data.src();
3531 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };3548 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
3532 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;3549 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
3533 const array_ptr = try sema.resolveInst(extra.lhs);3550 const array_ptr = sema.resolveInst(extra.lhs);
3534 const start = try sema.resolveInst(extra.start);3551 const start = sema.resolveInst(extra.start);
3535 const end = try sema.resolveInst(extra.end);3552 const end = sema.resolveInst(extra.end);
3536 const sentinel = try sema.resolveInst(extra.sentinel);3553 const sentinel = sema.resolveInst(extra.sentinel);
35373554
3538 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);3555 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
3539}3556}
...@@ -3544,7 +3561,7 @@ fn zirSwitchCapture(...@@ -3544,7 +3561,7 @@ fn zirSwitchCapture(
3544 inst: Zir.Inst.Index,3561 inst: Zir.Inst.Index,
3545 is_multi: bool,3562 is_multi: bool,
3546 is_ref: bool,3563 is_ref: bool,
3547) InnerError!*Inst {3564) CompileError!Air.Inst.Ref {
3548 const tracy = trace(@src());3565 const tracy = trace(@src());
3549 defer tracy.end();3566 defer tracy.end();
35503567
...@@ -3563,7 +3580,7 @@ fn zirSwitchCaptureElse(...@@ -3563,7 +3580,7 @@ fn zirSwitchCaptureElse(
3563 block: *Scope.Block,3580 block: *Scope.Block,
3564 inst: Zir.Inst.Index,3581 inst: Zir.Inst.Index,
3565 is_ref: bool,3582 is_ref: bool,
3566) InnerError!*Inst {3583) CompileError!Air.Inst.Ref {
3567 const tracy = trace(@src());3584 const tracy = trace(@src());
3568 defer tracy.end();3585 defer tracy.end();
35693586
...@@ -3582,7 +3599,7 @@ fn zirSwitchBlock(...@@ -3582,7 +3599,7 @@ fn zirSwitchBlock(
3582 inst: Zir.Inst.Index,3599 inst: Zir.Inst.Index,
3583 is_ref: bool,3600 is_ref: bool,
3584 special_prong: Zir.SpecialProng,3601 special_prong: Zir.SpecialProng,
3585) InnerError!*Inst {3602) CompileError!Air.Inst.Ref {
3586 const tracy = trace(@src());3603 const tracy = trace(@src());
3587 defer tracy.end();3604 defer tracy.end();
35883605
...@@ -3591,7 +3608,7 @@ fn zirSwitchBlock(...@@ -3591,7 +3608,7 @@ fn zirSwitchBlock(
3591 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };3608 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
3592 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);3609 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);
3595 const operand = if (is_ref)3612 const operand = if (is_ref)
3596 try sema.analyzeLoad(block, src, operand_ptr, operand_src)3613 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
3597 else3614 else
...@@ -3615,7 +3632,7 @@ fn zirSwitchBlockMulti(...@@ -3615,7 +3632,7 @@ fn zirSwitchBlockMulti(
3615 inst: Zir.Inst.Index,3632 inst: Zir.Inst.Index,
3616 is_ref: bool,3633 is_ref: bool,
3617 special_prong: Zir.SpecialProng,3634 special_prong: Zir.SpecialProng,
3618) InnerError!*Inst {3635) CompileError!Air.Inst.Ref {
3619 const tracy = trace(@src());3636 const tracy = trace(@src());
3620 defer tracy.end();3637 defer tracy.end();
36213638
...@@ -3624,7 +3641,7 @@ fn zirSwitchBlockMulti(...@@ -3624,7 +3641,7 @@ fn zirSwitchBlockMulti(
3624 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };3641 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
3625 const extra = sema.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);3642 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);
3628 const operand = if (is_ref)3645 const operand = if (is_ref)
3629 try sema.analyzeLoad(block, src, operand_ptr, operand_src)3646 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
3630 else3647 else
...@@ -3645,14 +3662,14 @@ fn zirSwitchBlockMulti(...@@ -3645,14 +3662,14 @@ fn zirSwitchBlockMulti(
3645fn analyzeSwitch(3662fn analyzeSwitch(
3646 sema: *Sema,3663 sema: *Sema,
3647 block: *Scope.Block,3664 block: *Scope.Block,
3648 operand: *Inst,3665 operand: Air.Inst.Ref,
3649 extra_end: usize,3666 extra_end: usize,
3650 special_prong: Zir.SpecialProng,3667 special_prong: Zir.SpecialProng,
3651 scalar_cases_len: usize,3668 scalar_cases_len: usize,
3652 multi_cases_len: usize,3669 multi_cases_len: usize,
3653 switch_inst: Zir.Inst.Index,3670 switch_inst: Zir.Inst.Index,
3654 src_node_offset: i32,3671 src_node_offset: i32,
3655) InnerError!*Inst {3672) CompileError!Air.Inst.Ref {
3656 const gpa = sema.gpa;3673 const gpa = sema.gpa;
3657 const mod = sema.mod;3674 const mod = sema.mod;
36583675
...@@ -3671,9 +3688,10 @@ fn analyzeSwitch(...@@ -3671,9 +3688,10 @@ fn analyzeSwitch(
3671 const src: LazySrcLoc = .{ .node_offset = src_node_offset };3688 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
3672 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };3689 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
3673 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };3690 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
3691 const operand_ty = sema.typeOf(operand);
36743692
3675 // Validate usage of '_' prongs.3693 // Validate usage of '_' prongs.
3676 if (special_prong == .under and !operand.ty.isNonexhaustiveEnum()) {3694 if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) {
3677 const msg = msg: {3695 const msg = msg: {
3678 const msg = try mod.errMsg(3696 const msg = try mod.errMsg(
3679 &block.base,3697 &block.base,
...@@ -3695,9 +3713,9 @@ fn analyzeSwitch(...@@ -3695,9 +3713,9 @@ fn analyzeSwitch(
3695 }3713 }
36963714
3697 // Validate for duplicate items, missing else prong, and invalid range.3715 // Validate for duplicate items, missing else prong, and invalid range.
3698 switch (operand.ty.zigTypeTag()) {3716 switch (operand_ty.zigTypeTag()) {
3699 .Enum => {3717 .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());
3701 defer gpa.free(seen_fields);3719 defer gpa.free(seen_fields);
37023720
3703 mem.set(?Module.SwitchProngSrc, seen_fields, null);3721 mem.set(?Module.SwitchProngSrc, seen_fields, null);
...@@ -3743,7 +3761,7 @@ fn analyzeSwitch(...@@ -3743,7 +3761,7 @@ fn analyzeSwitch(
3743 );3761 );
3744 }3762 }
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);
3747 }3765 }
3748 }3766 }
3749 const all_tags_handled = for (seen_fields) |seen_src| {3767 const all_tags_handled = for (seen_fields) |seen_src| {
...@@ -3764,7 +3782,7 @@ fn analyzeSwitch(...@@ -3764,7 +3782,7 @@ fn analyzeSwitch(
3764 for (seen_fields) |seen_src, i| {3782 for (seen_fields) |seen_src, i| {
3765 if (seen_src != null) continue;3783 if (seen_src != null) continue;
37663784
3767 const field_name = operand.ty.enumFieldName(i);3785 const field_name = operand_ty.enumFieldName(i);
37683786
3769 // TODO have this point to the tag decl instead of here3787 // TODO have this point to the tag decl instead of here
3770 try mod.errNote(3788 try mod.errNote(
...@@ -3776,10 +3794,10 @@ fn analyzeSwitch(...@@ -3776,10 +3794,10 @@ fn analyzeSwitch(
3776 );3794 );
3777 }3795 }
3778 try mod.errNoteNonLazy(3796 try mod.errNoteNonLazy(
3779 operand.ty.declSrcLoc(),3797 operand_ty.declSrcLoc(),
3780 msg,3798 msg,
3781 "enum '{}' declared here",3799 "enum '{}' declared here",
3782 .{operand.ty},3800 .{operand_ty},
3783 );3801 );
3784 break :msg msg;3802 break :msg msg;
3785 };3803 };
...@@ -3874,12 +3892,12 @@ fn analyzeSwitch(...@@ -3874,12 +3892,12 @@ fn analyzeSwitch(
3874 }3892 }
38753893
3876 check_range: {3894 check_range: {
3877 if (operand.ty.zigTypeTag() == .Int) {3895 if (operand_ty.zigTypeTag() == .Int) {
3878 var arena = std.heap.ArenaAllocator.init(gpa);3896 var arena = std.heap.ArenaAllocator.init(gpa);
3879 defer arena.deinit();3897 defer arena.deinit();
38803898
3881 const min_int = try operand.ty.minInt(&arena, mod.getTarget());3899 const min_int = try operand_ty.minInt(&arena, mod.getTarget());
3882 const max_int = try operand.ty.maxInt(&arena, mod.getTarget());3900 const max_int = try operand_ty.maxInt(&arena, mod.getTarget());
3883 if (try range_set.spans(min_int, max_int)) {3901 if (try range_set.spans(min_int, max_int)) {
3884 if (special_prong == .@"else") {3902 if (special_prong == .@"else") {
3885 return mod.fail(3903 return mod.fail(
...@@ -3949,7 +3967,7 @@ fn analyzeSwitch(...@@ -3949,7 +3967,7 @@ fn analyzeSwitch(
3949 );3967 );
3950 }3968 }
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);
3953 }3971 }
3954 }3972 }
3955 switch (special_prong) {3973 switch (special_prong) {
...@@ -3981,7 +3999,7 @@ fn analyzeSwitch(...@@ -3981,7 +3999,7 @@ fn analyzeSwitch(
3981 &block.base,3999 &block.base,
3982 src,4000 src,
3983 "else prong required when switching on type '{}'",4001 "else prong required when switching on type '{}'",
3984 .{operand.ty},4002 .{operand_ty},
3985 );4003 );
3986 }4004 }
39874005
...@@ -4029,7 +4047,7 @@ fn analyzeSwitch(...@@ -4029,7 +4047,7 @@ fn analyzeSwitch(
4029 );4047 );
4030 }4048 }
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);
4033 }4051 }
4034 }4052 }
4035 },4053 },
...@@ -4049,20 +4067,15 @@ fn analyzeSwitch(...@@ -4049,20 +4067,15 @@ fn analyzeSwitch(
4049 .ComptimeFloat,4067 .ComptimeFloat,
4050 .Float,4068 .Float,
4051 => return mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{4069 => return mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
4052 operand.ty,4070 operand_ty,
4053 }),4071 }),
4054 }4072 }
40554073
4056 const block_inst = try sema.arena.create(Inst.Block);4074 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
4057 block_inst.* = .{4075 try sema.air_instructions.append(gpa, .{
4058 .base = .{4076 .tag = .block,
4059 .tag = Inst.Block.base_tag,4077 .data = undefined,
4060 .ty = undefined, // Set after analysis.4078 });
4061 .src = src,
4062 },
4063 .body = undefined,
4064 };
4065
4066 var label: Scope.Block.Label = .{4079 var label: Scope.Block.Label = .{
4067 .zir_block = switch_inst,4080 .zir_block = switch_inst,
4068 .merges = .{4081 .merges = .{
...@@ -4098,8 +4111,8 @@ fn analyzeSwitch(...@@ -4098,8 +4111,8 @@ fn analyzeSwitch(
4098 const body = sema.code.extra[extra_index..][0..body_len];4111 const body = sema.code.extra[extra_index..][0..body_len];
4099 extra_index += body_len;4112 extra_index += body_len;
41004113
4114 const item = sema.resolveInst(item_ref);
4101 // Validation above ensured these will succeed.4115 // Validation above ensured these will succeed.
4102 const item = sema.resolveInst(item_ref) catch unreachable;
4103 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;4116 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
4104 if (operand_val.eql(item_val)) {4117 if (operand_val.eql(item_val)) {
4105 return sema.resolveBlockBody(block, src, &child_block, body, merges);4118 return sema.resolveBlockBody(block, src, &child_block, body, merges);
...@@ -4120,9 +4133,9 @@ fn analyzeSwitch(...@@ -4120,9 +4133,9 @@ fn analyzeSwitch(
4120 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];4133 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
41214134
4122 for (items) |item_ref| {4135 for (items) |item_ref| {
4136 const item = sema.resolveInst(item_ref);
4123 // Validation above ensured these will succeed.4137 // Validation above ensured these will succeed.
4124 const item = sema.resolveInst(item_ref) catch unreachable;4138 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
4125 const item_val = sema.resolveConstValue(&child_block, item.src, item) catch unreachable;
4126 if (operand_val.eql(item_val)) {4139 if (operand_val.eql(item_val)) {
4127 return sema.resolveBlockBody(block, src, &child_block, body, merges);4140 return sema.resolveBlockBody(block, src, &child_block, body, merges);
4128 }4141 }
...@@ -4157,13 +4170,15 @@ fn analyzeSwitch(...@@ -4157,13 +4170,15 @@ fn analyzeSwitch(
41574170
4158 try sema.requireRuntimeBlock(block, src);4171 try sema.requireRuntimeBlock(block, src);
41594172
4160 // TODO when reworking AIR memory layout make multi cases get generated as cases,4173 var cases_extra: std.ArrayListUnmanaged(u32) = .{};
4161 // not as part of the "else" block.4174 defer cases_extra.deinit(gpa);
4162 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);4175
4176 try cases_extra.ensureTotalCapacity(gpa, (scalar_cases_len + multi_cases_len) *
4177 @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2);
41634178
4164 var case_block = child_block.makeSubBlock();4179 var case_block = child_block.makeSubBlock();
4165 case_block.runtime_loop = null;4180 case_block.runtime_loop = null;
4166 case_block.runtime_cond = operand.src;4181 case_block.runtime_cond = operand_src;
4167 case_block.runtime_index += 1;4182 case_block.runtime_index += 1;
4168 defer case_block.instructions.deinit(gpa);4183 defer case_block.instructions.deinit(gpa);
41694184
...@@ -4179,21 +4194,26 @@ fn analyzeSwitch(...@@ -4179,21 +4194,26 @@ fn analyzeSwitch(
4179 extra_index += body_len;4194 extra_index += body_len;
41804195
4181 case_block.instructions.shrinkRetainingCapacity(0);4196 case_block.instructions.shrinkRetainingCapacity(0);
4182 // We validate these above; these two calls are guaranteed to succeed.4197 const item = sema.resolveInst(item_ref);
4183 const item = sema.resolveInst(item_ref) catch unreachable;4198 // `item` is already guaranteed to be constant known.
4184 const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
41854199
4186 _ = try sema.analyzeBody(&case_block, body);4200 _ = try sema.analyzeBody(&case_block, body);
41874201
4188 cases[scalar_i] = .{4202 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
4189 .item = item_val,4203 cases_extra.appendAssumeCapacity(1); // items_len
4190 .body = .{ .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items) },4204 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
4191 };4205 cases_extra.appendAssumeCapacity(@enumToInt(item));
4206 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
4192 }4207 }
41934208
4194 var first_else_body: Body = undefined;4209 var is_first = true;
4195 var prev_condbr: ?*Inst.CondBr = null;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;
4197 var multi_i: usize = 0;4217 var multi_i: usize = 0;
4198 while (multi_i < multi_cases_len) : (multi_i += 1) {4218 while (multi_i < multi_cases_len) : (multi_i += 1) {
4199 const items_len = sema.code.extra[extra_index];4219 const items_len = sema.code.extra[extra_index];
...@@ -4207,116 +4227,146 @@ fn analyzeSwitch(...@@ -4207,116 +4227,146 @@ fn analyzeSwitch(
42074227
4208 case_block.instructions.shrinkRetainingCapacity(0);4228 case_block.instructions.shrinkRetainingCapacity(0);
42094229
4210 var any_ok: ?*Inst = null;4230 var any_ok: Air.Inst.Ref = .none;
4211 const bool_ty = comptime Type.initTag(.bool);
42124231
4213 for (items) |item_ref| {4232 // If there are any ranges, we have to put all the items into the
4214 const item = try sema.resolveInst(item_ref);4233 // else prong. Otherwise, we can take advantage of multiple items
4215 _ = try sema.resolveConstValue(&child_block, item.src, item);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);4238 const body = sema.code.extra[extra_index..][0..body_len];
4218 if (any_ok) |some| {4239 extra_index += body_len;
4219 any_ok = try case_block.addBinOp(item.src, bool_ty, .bool_or, some, cmp_ok);4240 _ = try sema.analyzeBody(&case_block, body);
4220 } else {4241
4221 any_ok = cmp_ok;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));
4222 }4251 }
4223 }
42244252
4225 var range_i: usize = 0;4253 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
4226 while (range_i < ranges_len) : (range_i += 1) {4254 } else {
4227 const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);4255 for (items) |item_ref| {
4228 extra_index += 1;4256 const item = sema.resolveInst(item_ref);
4229 const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);4257 const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
4230 extra_index += 1;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);4265 var range_i: usize = 0;
4233 const item_last = try sema.resolveInst(last_ref);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);4272 const item_first = sema.resolveInst(first_ref);
4236 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);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 <= last4298 const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{
4241 const range_first_ok = try case_block.addBinOp(4299 .pl_op = .{
4242 item_first.src,4300 .operand = any_ok,
4243 bool_ty,4301 .payload = undefined,
4244 .cmp_gte,4302 },
4245 operand,4303 } });
4246 item_first,4304 var cond_body = case_block.instructions.toOwnedSlice(gpa);
4247 );4305 defer gpa.free(cond_body);
4248 const range_last_ok = try case_block.addBinOp(4306
4249 item_last.src,4307 case_block.instructions.shrinkRetainingCapacity(0);
4250 bool_ty,4308 const body = sema.code.extra[extra_index..][0..body_len];
4251 .cmp_lte,4309 extra_index += body_len;
4252 operand,4310 _ = try sema.analyzeBody(&case_block, body);
4253 item_last,4311
4254 );4312 if (is_first) {
4255 const range_ok = try case_block.addBinOp(4313 is_first = false;
4256 range_src,4314 first_else_body = cond_body;
4257 bool_ty,4315 cond_body = &.{};
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);
4264 } else {4316 } 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);
4266 }4329 }
4330 prev_then_body = case_block.instructions.toOwnedSlice(gpa);
4331 prev_cond_br = new_cond_br;
4267 }4332 }
4333 }
42684334
4269 const new_condbr = try sema.arena.create(Inst.CondBr);4335 var final_else_body: []const Air.Inst.Index = &.{};
4270 new_condbr.* = .{4336 if (special.body.len != 0) {
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
4286 case_block.instructions.shrinkRetainingCapacity(0);4337 case_block.instructions.shrinkRetainingCapacity(0);
4287 const body = sema.code.extra[extra_index..][0..body_len];4338 _ = try sema.analyzeBody(&case_block, special.body);
4288 extra_index += body_len;4339
4289 _ = try sema.analyzeBody(&case_block, body);4340 if (is_first) {
4290 new_condbr.then_body = .{4341 final_else_body = case_block.instructions.items;
4291 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
4292 };
4293 if (prev_condbr) |condbr| {
4294 condbr.else_body = cond_body;
4295 } else {4342 } 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;
4297 }4354 }
4298 prev_condbr = new_condbr;
4299 }4355 }
43004356
4301 const final_else_body: Body = blk: {4357 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
4302 if (special.body.len != 0) {4358 cases_extra.items.len + final_else_body.len);
4303 case_block.instructions.shrinkRetainingCapacity(0);4359
4304 _ = try sema.analyzeBody(&case_block, special.body);4360 _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{
4305 const else_body: Body = .{4361 .operand = operand,
4306 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),4362 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
4307 };4363 .cases_len = @intCast(u32, cases_len),
4308 if (prev_condbr) |condbr| {4364 .else_body_len = @intCast(u32, final_else_body.len),
4309 condbr.else_body = else_body;4365 }),
4310 break :blk first_else_body;4366 } } });
4311 } else {4367 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
4312 break :blk else_body;4368 sema.air_extra.appendSliceAssumeCapacity(final_else_body);
4313 }
4314 } else {
4315 break :blk .{ .instructions = &.{} };
4316 }
4317 };
43184369
4319 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
4320 return sema.analyzeBlockBody(block, src, &child_block, merges);4370 return sema.analyzeBlockBody(block, src, &child_block, merges);
4321}4371}
43224372
...@@ -4327,20 +4377,24 @@ fn resolveSwitchItemVal(...@@ -4327,20 +4377,24 @@ fn resolveSwitchItemVal(
4327 switch_node_offset: i32,4377 switch_node_offset: i32,
4328 switch_prong_src: Module.SwitchProngSrc,4378 switch_prong_src: Module.SwitchProngSrc,
4329 range_expand: Module.SwitchProngSrc.RangeExpand,4379 range_expand: Module.SwitchProngSrc.RangeExpand,
4330) InnerError!TypedValue {4380) CompileError!TypedValue {
4331 const item = try sema.resolveInst(item_ref);4381 const item = sema.resolveInst(item_ref);
4332 // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc4382 const item_ty = sema.typeOf(item);
4333 // because we only have the switch AST node. Only if we know for sure we need to report4383 // Constructing a LazySrcLoc is costly because we only have the switch AST node.
4334 // a compile error do we resolve the full source locations.4384 // Only if we know for sure we need to report a compile error do we resolve the
4335 if (item.value()) |val| {4385 // full source locations.
4336 if (val.isUndef()) {4386 if (sema.resolveConstValue(block, .unneeded, item)) |val| {
4387 return TypedValue{ .ty = item_ty, .val = val };
4388 } else |err| switch (err) {
4389 error.NeededSourceLocation => {
4337 const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);4390 const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
4338 return sema.failWithUseOfUndef(block, src);4391 return TypedValue{
4339 }4392 .ty = item_ty,
4340 return TypedValue{ .ty = item.ty, .val = val };4393 .val = try sema.resolveConstValue(block, src, item),
4394 };
4395 },
4396 else => |e| return e,
4341 }4397 }
4342 const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
4343 return sema.failWithNeededComptime(block, src);
4344}4398}
43454399
4346fn validateSwitchRange(4400fn validateSwitchRange(
...@@ -4351,7 +4405,7 @@ fn validateSwitchRange(...@@ -4351,7 +4405,7 @@ fn validateSwitchRange(
4351 last_ref: Zir.Inst.Ref,4405 last_ref: Zir.Inst.Ref,
4352 src_node_offset: i32,4406 src_node_offset: i32,
4353 switch_prong_src: Module.SwitchProngSrc,4407 switch_prong_src: Module.SwitchProngSrc,
4354) InnerError!void {4408) CompileError!void {
4355 const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;4409 const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
4356 const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;4410 const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
4357 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);4411 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);
...@@ -4365,7 +4419,7 @@ fn validateSwitchItem(...@@ -4365,7 +4419,7 @@ fn validateSwitchItem(
4365 item_ref: Zir.Inst.Ref,4419 item_ref: Zir.Inst.Ref,
4366 src_node_offset: i32,4420 src_node_offset: i32,
4367 switch_prong_src: Module.SwitchProngSrc,4421 switch_prong_src: Module.SwitchProngSrc,
4368) InnerError!void {4422) CompileError!void {
4369 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;4423 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
4370 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);4424 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);
4371 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);4425 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
...@@ -4378,7 +4432,7 @@ fn validateSwitchItemEnum(...@@ -4378,7 +4432,7 @@ fn validateSwitchItemEnum(
4378 item_ref: Zir.Inst.Ref,4432 item_ref: Zir.Inst.Ref,
4379 src_node_offset: i32,4433 src_node_offset: i32,
4380 switch_prong_src: Module.SwitchProngSrc,4434 switch_prong_src: Module.SwitchProngSrc,
4381) InnerError!void {4435) CompileError!void {
4382 const mod = sema.mod;4436 const mod = sema.mod;
4383 const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);4437 const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
4384 const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse {4438 const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse {
...@@ -4412,7 +4466,7 @@ fn validateSwitchDupe(...@@ -4412,7 +4466,7 @@ fn validateSwitchDupe(
4412 maybe_prev_src: ?Module.SwitchProngSrc,4466 maybe_prev_src: ?Module.SwitchProngSrc,
4413 switch_prong_src: Module.SwitchProngSrc,4467 switch_prong_src: Module.SwitchProngSrc,
4414 src_node_offset: i32,4468 src_node_offset: i32,
4415) InnerError!void {4469) CompileError!void {
4416 const prev_prong_src = maybe_prev_src orelse return;4470 const prev_prong_src = maybe_prev_src orelse return;
4417 const mod = sema.mod;4471 const mod = sema.mod;
4418 const gpa = sema.gpa;4472 const gpa = sema.gpa;
...@@ -4446,7 +4500,7 @@ fn validateSwitchItemBool(...@@ -4446,7 +4500,7 @@ fn validateSwitchItemBool(
4446 item_ref: Zir.Inst.Ref,4500 item_ref: Zir.Inst.Ref,
4447 src_node_offset: i32,4501 src_node_offset: i32,
4448 switch_prong_src: Module.SwitchProngSrc,4502 switch_prong_src: Module.SwitchProngSrc,
4449) InnerError!void {4503) CompileError!void {
4450 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;4504 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
4451 if (item_val.toBool()) {4505 if (item_val.toBool()) {
4452 true_count.* += 1;4506 true_count.* += 1;
...@@ -4468,7 +4522,7 @@ fn validateSwitchItemSparse(...@@ -4468,7 +4522,7 @@ fn validateSwitchItemSparse(
4468 item_ref: Zir.Inst.Ref,4522 item_ref: Zir.Inst.Ref,
4469 src_node_offset: i32,4523 src_node_offset: i32,
4470 switch_prong_src: Module.SwitchProngSrc,4524 switch_prong_src: Module.SwitchProngSrc,
4471) InnerError!void {4525) CompileError!void {
4472 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;4526 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
4473 const kv = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;4527 const kv = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
4474 return sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);4528 return sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
...@@ -4480,7 +4534,7 @@ fn validateSwitchNoRange(...@@ -4480,7 +4534,7 @@ fn validateSwitchNoRange(
4480 ranges_len: u32,4534 ranges_len: u32,
4481 operand_ty: Type,4535 operand_ty: Type,
4482 src_node_offset: i32,4536 src_node_offset: i32,
4483) InnerError!void {4537) CompileError!void {
4484 if (ranges_len == 0)4538 if (ranges_len == 0)
4485 return;4539 return;
44864540
...@@ -4507,7 +4561,7 @@ fn validateSwitchNoRange(...@@ -4507,7 +4561,7 @@ fn validateSwitchNoRange(
4507 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);4561 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
4508}4562}
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 {
4511 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;4565 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4512 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;4566 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4513 _ = extra;4567 _ = extra;
...@@ -4516,16 +4570,14 @@ fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -4516,16 +4570,14 @@ fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
4516 return sema.mod.fail(&block.base, src, "TODO implement zirHasField", .{});4570 return sema.mod.fail(&block.base, src, "TODO implement zirHasField", .{});
4517}4571}
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 {
4520 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;4574 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4521 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;4575 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4522 const src = inst_data.src();
4523 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };4576 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
4524 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };4577 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
4525 const container_type = try sema.resolveType(block, lhs_src, extra.lhs);4578 const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
4526 const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs);4579 const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs);
4527 const mod = sema.mod;4580 const mod = sema.mod;
4528 const arena = sema.arena;
45294581
4530 const namespace = container_type.getNamespace() orelse return mod.fail(4582 const namespace = container_type.getNamespace() orelse return mod.fail(
4531 &block.base,4583 &block.base,
...@@ -4535,13 +4587,13 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -4535,13 +4587,13 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
4535 );4587 );
4536 if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {4588 if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
4537 if (decl.is_pub or decl.namespace.file_scope == block.base.namespace().file_scope) {4589 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;
4539 }4591 }
4540 }4592 }
4541 return mod.constBool(arena, src, false);4593 return Air.Inst.Ref.bool_false;
4542}4594}
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 {
4545 const tracy = trace(@src());4597 const tracy = trace(@src());
4546 defer tracy.end();4598 defer tracy.end();
45474599
...@@ -4563,16 +4615,16 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!...@@ -4563,16 +4615,16 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
4563 try mod.semaFile(result.file);4615 try mod.semaFile(result.file);
4564 const file_root_decl = result.file.root_decl.?;4616 const file_root_decl = result.file.root_decl.?;
4565 try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl);4617 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);
4567}4619}
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 {
4570 _ = block;4622 _ = block;
4571 _ = inst;4623 _ = inst;
4572 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});4624 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
4573}4625}
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 {
4576 const tracy = trace(@src());4628 const tracy = trace(@src());
4577 defer tracy.end();4629 defer tracy.end();
45784630
...@@ -4581,7 +4633,7 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In...@@ -4581,7 +4633,7 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
4581 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{});4633 return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{});
4582}4634}
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 {
4585 const tracy = trace(@src());4637 const tracy = trace(@src());
4586 defer tracy.end();4638 defer tracy.end();
45874639
...@@ -4593,8 +4645,8 @@ fn zirBitwise(...@@ -4593,8 +4645,8 @@ fn zirBitwise(
4593 sema: *Sema,4645 sema: *Sema,
4594 block: *Scope.Block,4646 block: *Scope.Block,
4595 inst: Zir.Inst.Index,4647 inst: Zir.Inst.Index,
4596 ir_tag: ir.Inst.Tag,4648 air_tag: Air.Inst.Tag,
4597) InnerError!*Inst {4649) CompileError!Air.Inst.Ref {
4598 const tracy = trace(@src());4650 const tracy = trace(@src());
4599 defer tracy.end();4651 defer tracy.end();
46004652
...@@ -4603,10 +4655,12 @@ fn zirBitwise(...@@ -4603,10 +4655,12 @@ fn zirBitwise(
4603 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };4655 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
4604 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };4656 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4605 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;4657 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4606 const lhs = try sema.resolveInst(extra.lhs);4658 const lhs = sema.resolveInst(extra.lhs);
4607 const rhs = try sema.resolveInst(extra.rhs);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 };
4610 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);4664 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
4611 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);4665 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
4612 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);4666 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
...@@ -4618,41 +4672,41 @@ fn zirBitwise(...@@ -4618,41 +4672,41 @@ fn zirBitwise(
46184672
4619 const scalar_tag = scalar_type.zigTypeTag();4673 const scalar_tag = scalar_type.zigTypeTag();
46204674
4621 if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {4675 if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) {
4622 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {4676 if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
4623 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{4677 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
4624 lhs.ty.arrayLen(),4678 lhs_ty.arrayLen(),
4625 rhs.ty.arrayLen(),4679 rhs_ty.arrayLen(),
4626 });4680 });
4627 }4681 }
4628 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBitwise", .{});4682 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) {
4630 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{4684 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
4631 lhs.ty,4685 lhs_ty,
4632 rhs.ty,4686 rhs_ty,
4633 });4687 });
4634 }4688 }
46354689
4636 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;4690 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
46374691
4638 if (!is_int) {4692 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()) });
4640 }4694 }
46414695
4642 if (casted_lhs.value()) |lhs_val| {4696 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| {
4643 if (casted_rhs.value()) |rhs_val| {4697 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
4644 if (lhs_val.isUndef() or rhs_val.isUndef()) {4698 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4645 return sema.mod.constUndef(sema.arena, src, resolved_type);4699 return sema.addConstUndef(resolved_type);
4646 }4700 }
4647 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});4701 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
4648 }4702 }
4649 }4703 }
46504704
4651 try sema.requireRuntimeBlock(block, src);4705 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);
4653}4707}
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 {
4656 const tracy = trace(@src());4710 const tracy = trace(@src());
4657 defer tracy.end();4711 defer tracy.end();
46584712
...@@ -4660,7 +4714,7 @@ fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!...@@ -4660,7 +4714,7 @@ fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
4660 return sema.mod.fail(&block.base, sema.src, "TODO implement zirBitNot", .{});4714 return sema.mod.fail(&block.base, sema.src, "TODO implement zirBitNot", .{});
4661}4715}
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 {
4664 const tracy = trace(@src());4718 const tracy = trace(@src());
4665 defer tracy.end();4719 defer tracy.end();
46664720
...@@ -4668,7 +4722,7 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -4668,7 +4722,7 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
4668 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});4722 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});
4669}4723}
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 {
4672 const tracy = trace(@src());4726 const tracy = trace(@src());
4673 defer tracy.end();4727 defer tracy.end();
46744728
...@@ -4681,7 +4735,7 @@ fn zirNegate(...@@ -4681,7 +4735,7 @@ fn zirNegate(
4681 block: *Scope.Block,4735 block: *Scope.Block,
4682 inst: Zir.Inst.Index,4736 inst: Zir.Inst.Index,
4683 tag_override: Zir.Inst.Tag,4737 tag_override: Zir.Inst.Tag,
4684) InnerError!*Inst {4738) CompileError!Air.Inst.Ref {
4685 const tracy = trace(@src());4739 const tracy = trace(@src());
4686 defer tracy.end();4740 defer tracy.end();
46874741
...@@ -4689,13 +4743,13 @@ fn zirNegate(...@@ -4689,13 +4743,13 @@ fn zirNegate(
4689 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };4743 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
4690 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };4744 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
4691 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };4745 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4692 const lhs = try sema.resolveInst(.zero);4746 const lhs = sema.resolveInst(.zero);
4693 const rhs = try sema.resolveInst(inst_data.operand);4747 const rhs = sema.resolveInst(inst_data.operand);
46944748
4695 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);4749 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
4696}4750}
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 {
4699 const tracy = trace(@src());4753 const tracy = trace(@src());
4700 defer tracy.end();4754 defer tracy.end();
47014755
...@@ -4705,8 +4759,8 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr...@@ -4705,8 +4759,8 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
4705 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };4759 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
4706 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };4760 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4707 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;4761 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4708 const lhs = try sema.resolveInst(extra.lhs);4762 const lhs = sema.resolveInst(extra.lhs);
4709 const rhs = try sema.resolveInst(extra.rhs);4763 const rhs = sema.resolveInst(extra.rhs);
47104764
4711 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);4765 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);
4712}4766}
...@@ -4715,7 +4769,7 @@ fn zirOverflowArithmetic(...@@ -4715,7 +4769,7 @@ fn zirOverflowArithmetic(
4715 sema: *Sema,4769 sema: *Sema,
4716 block: *Scope.Block,4770 block: *Scope.Block,
4717 extended: Zir.Inst.Extended.InstData,4771 extended: Zir.Inst.Extended.InstData,
4718) InnerError!*Inst {4772) CompileError!Air.Inst.Ref {
4719 const tracy = trace(@src());4773 const tracy = trace(@src());
4720 defer tracy.end();4774 defer tracy.end();
47214775
...@@ -4729,13 +4783,30 @@ fn analyzeArithmetic(...@@ -4729,13 +4783,30 @@ fn analyzeArithmetic(
4729 sema: *Sema,4783 sema: *Sema,
4730 block: *Scope.Block,4784 block: *Scope.Block,
4731 zir_tag: Zir.Inst.Tag,4785 zir_tag: Zir.Inst.Tag,
4732 lhs: *Inst,4786 lhs: Air.Inst.Ref,
4733 rhs: *Inst,4787 rhs: Air.Inst.Ref,
4734 src: LazySrcLoc,4788 src: LazySrcLoc,
4735 lhs_src: LazySrcLoc,4789 lhs_src: LazySrcLoc,
4736 rhs_src: LazySrcLoc,4790 rhs_src: LazySrcLoc,
4737) InnerError!*Inst {4791) CompileError!Air.Inst.Ref {
4738 const instructions = &[_]*Inst{ lhs, rhs };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 };
4739 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);4810 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
4740 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);4811 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
4741 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);4812 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
...@@ -4747,42 +4818,24 @@ fn analyzeArithmetic(...@@ -4747,42 +4818,24 @@ fn analyzeArithmetic(
47474818
4748 const scalar_tag = scalar_type.zigTypeTag();4819 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
4765 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;4821 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
4766 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;4822 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
47674823
4768 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {4824 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()) });
4770 }4826 }
47714827
4772 if (casted_lhs.value()) |lhs_val| {4828 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| {
4773 if (casted_rhs.value()) |rhs_val| {4829 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
4774 if (lhs_val.isUndef() or rhs_val.isUndef()) {4830 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4775 return sema.mod.constUndef(sema.arena, src, resolved_type);4831 return sema.addConstUndef(resolved_type);
4776 }4832 }
4777 // incase rhs is 0, simply return lhs without doing any calculations4833 // incase rhs is 0, simply return lhs without doing any calculations
4778 // TODO Once division is implemented we should throw an error when dividing by 0.4834 // TODO Once division is implemented we should throw an error when dividing by 0.
4779 if (rhs_val.compareWithZero(.eq)) {4835 if (rhs_val.compareWithZero(.eq)) {
4780 switch (zir_tag) {4836 switch (zir_tag) {
4781 .add, .addwrap, .sub, .subwrap => {4837 .add, .addwrap, .sub, .subwrap => {
4782 return sema.mod.constInst(sema.arena, src, .{4838 return sema.addConstant(scalar_type, lhs_val);
4783 .ty = scalar_type,
4784 .val = lhs_val,
4785 });
4786 },4839 },
4787 else => {},4840 else => {},
4788 }4841 }
...@@ -4822,15 +4875,12 @@ fn analyzeArithmetic(...@@ -4822,15 +4875,12 @@ fn analyzeArithmetic(
48224875
4823 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });4876 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
48244877
4825 return sema.mod.constInst(sema.arena, src, .{4878 return sema.addConstant(scalar_type, value);
4826 .ty = scalar_type,
4827 .val = value,
4828 });
4829 }4879 }
4830 }4880 }
48314881
4832 try sema.requireRuntimeBlock(block, src);4882 try sema.requireRuntimeBlock(block, src);
4833 const ir_tag: Inst.Tag = switch (zir_tag) {4883 const air_tag: Air.Inst.Tag = switch (zir_tag) {
4834 .add => .add,4884 .add => .add,
4835 .addwrap => .addwrap,4885 .addwrap => .addwrap,
4836 .sub => .sub,4886 .sub => .sub,
...@@ -4841,17 +4891,17 @@ fn analyzeArithmetic(...@@ -4841,17 +4891,17 @@ fn analyzeArithmetic(
4841 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}),4891 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}),
4842 };4892 };
48434893
4844 return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);4894 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
4845}4895}
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 {
4848 const tracy = trace(@src());4898 const tracy = trace(@src());
4849 defer tracy.end();4899 defer tracy.end();
48504900
4851 const inst_data = sema.code.instructions.items(.data)[inst].un_node;4901 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
4852 const src = inst_data.src();4902 const src = inst_data.src();
4853 const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };4903 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);
4855 return sema.analyzeLoad(block, src, ptr, ptr_src);4905 return sema.analyzeLoad(block, src, ptr, ptr_src);
4856}4906}
48574907
...@@ -4859,19 +4909,17 @@ fn zirAsm(...@@ -4859,19 +4909,17 @@ fn zirAsm(
4859 sema: *Sema,4909 sema: *Sema,
4860 block: *Scope.Block,4910 block: *Scope.Block,
4861 extended: Zir.Inst.Extended.InstData,4911 extended: Zir.Inst.Extended.InstData,
4862) InnerError!*Inst {4912 inst: Zir.Inst.Index,
4913) CompileError!Air.Inst.Ref {
4863 const tracy = trace(@src());4914 const tracy = trace(@src());
4864 defer tracy.end();4915 defer tracy.end();
48654916
4866 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);4917 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
4867 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };4918 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
4868 const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = extra.data.src_node };
4869 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };4919 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);
4871 const outputs_len = @truncate(u5, extended.small);4920 const outputs_len = @truncate(u5, extended.small);
4872 const inputs_len = @truncate(u5, extended.small >> 5);4921 const inputs_len = @truncate(u5, extended.small >> 5);
4873 const clobbers_len = @truncate(u5, extended.small >> 10);4922 const clobbers_len = @truncate(u5, extended.small >> 10);
4874 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
48754923
4876 if (outputs_len > 1) {4924 if (outputs_len > 1) {
4877 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{});4925 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{});
...@@ -4899,7 +4947,7 @@ fn zirAsm(...@@ -4899,7 +4947,7 @@ fn zirAsm(
4899 };4947 };
4900 };4948 };
49014949
4902 const args = try sema.arena.alloc(*Inst, inputs_len);4950 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
4903 const inputs = try sema.arena.alloc([]const u8, inputs_len);4951 const inputs = try sema.arena.alloc([]const u8, inputs_len);
49044952
4905 for (args) |*arg, arg_i| {4953 for (args) |*arg, arg_i| {
...@@ -4909,7 +4957,7 @@ fn zirAsm(...@@ -4909,7 +4957,7 @@ fn zirAsm(
4909 const name = sema.code.nullTerminatedString(input.data.name);4957 const name = sema.code.nullTerminatedString(input.data.name);
4910 _ = name; // TODO: use the name4958 _ = name; // TODO: use the name
49114959
4912 arg.* = try sema.resolveInst(input.data.operand);4960 arg.* = sema.resolveInst(input.data.operand);
4913 inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);4961 inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);
4914 }4962 }
49154963
...@@ -4920,22 +4968,19 @@ fn zirAsm(...@@ -4920,22 +4968,19 @@ fn zirAsm(
4920 }4968 }
49214969
4922 try sema.requireRuntimeBlock(block, src);4970 try sema.requireRuntimeBlock(block, src);
4923 const asm_air = try sema.arena.create(Inst.Assembly);4971 const gpa = sema.gpa;
4924 asm_air.* = .{4972 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len);
4925 .base = .{4973 const asm_air = try block.addInst(.{
4926 .tag = .assembly,4974 .tag = .assembly,
4927 .ty = if (output) |o| o.ty else Type.initTag(.void),4975 .data = .{ .ty_pl = .{
4928 .src = src,4976 .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
4929 },4977 .payload = sema.addExtraAssumeCapacity(Air.Asm{
4930 .asm_source = asm_source,4978 .zir_index = inst,
4931 .is_volatile = is_volatile,4979 }),
4932 .output_constraint = if (output) |o| o.constraint else null,4980 } },
4933 .inputs = inputs,4981 });
4934 .clobbers = clobbers,4982 sema.appendRefsAssumeCapacity(args);
4935 .args = args,4983 return asm_air;
4936 };
4937 try block.instructions.append(sema.gpa, &asm_air.base);
4938 return &asm_air.base;
4939}4984}
49404985
4941fn zirCmp(4986fn zirCmp(
...@@ -4943,7 +4988,7 @@ fn zirCmp(...@@ -4943,7 +4988,7 @@ fn zirCmp(
4943 block: *Scope.Block,4988 block: *Scope.Block,
4944 inst: Zir.Inst.Index,4989 inst: Zir.Inst.Index,
4945 op: std.math.CompareOperator,4990 op: std.math.CompareOperator,
4946) InnerError!*Inst {4991) CompileError!Air.Inst.Ref {
4947 const tracy = trace(@src());4992 const tracy = trace(@src());
4948 defer tracy.end();4993 defer tracy.end();
49494994
...@@ -4954,18 +4999,24 @@ fn zirCmp(...@@ -4954,18 +4999,24 @@ fn zirCmp(
4954 const src: LazySrcLoc = inst_data.src();4999 const src: LazySrcLoc = inst_data.src();
4955 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };5000 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
4956 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };5001 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4957 const lhs = try sema.resolveInst(extra.lhs);5002 const lhs = sema.resolveInst(extra.lhs);
4958 const rhs = try sema.resolveInst(extra.rhs);5003 const rhs = sema.resolveInst(extra.rhs);
49595004
4960 const is_equality_cmp = switch (op) {5005 const is_equality_cmp = switch (op) {
4961 .eq, .neq => true,5006 .eq, .neq => true,
4962 else => false,5007 else => false,
4963 };5008 };
4964 const lhs_ty_tag = lhs.ty.zigTypeTag();5009 const lhs_ty = sema.typeOf(lhs);
4965 const rhs_ty_tag = rhs.ty.zigTypeTag();5010 const rhs_ty = sema.typeOf(rhs);
5011 const lhs_ty_tag = lhs_ty.zigTypeTag();
5012 const rhs_ty_tag = rhs_ty.zigTypeTag();
4966 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {5013 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
4967 // null == null, null != null5014 // 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 }
4969 } else if (is_equality_cmp and5020 } else if (is_equality_cmp and
4970 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or5021 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
4971 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))5022 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
...@@ -4974,11 +5025,11 @@ fn zirCmp(...@@ -4974,11 +5025,11 @@ fn zirCmp(
4974 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;5025 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
4975 return sema.analyzeIsNull(block, src, opt_operand, op == .neq);5026 return sema.analyzeIsNull(block, src, opt_operand, op == .neq);
4976 } else if (is_equality_cmp and5027 } 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())))
4978 {5029 {
4979 return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});5030 return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});
4980 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {5031 } 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;
4982 return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});5033 return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
4983 } else if (is_equality_cmp and5034 } else if (is_equality_cmp and
4984 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or5035 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
...@@ -4989,27 +5040,45 @@ fn zirCmp(...@@ -4989,27 +5040,45 @@ fn zirCmp(
4989 if (!is_equality_cmp) {5040 if (!is_equality_cmp) {
4990 return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});5041 return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});
4991 }5042 }
4992 if (rhs.value()) |rval| {5043 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lval| {
4993 if (lhs.value()) |lval| {5044 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rval| {
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 faster5045 if (lval.isUndef() or rval.isUndef()) {
4995 return mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));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 }
4996 }5058 }
4997 }5059 }
4998 try sema.requireRuntimeBlock(block, src);5060 try sema.requireRuntimeBlock(block, src);
4999 return block.addBinOp(src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs);5061 const tag: Air.Inst.Tag = if (op == .eq) .cmp_eq else .cmp_neq;
5000 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {5062 return block.addBinOp(tag, lhs, rhs);
5063 } else if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) {
5001 // This operation allows any combination of integer and float types, regardless of the5064 // This operation allows any combination of integer and float types, regardless of the
5002 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for5065 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
5003 // numeric types.5066 // numeric types.
5004 return sema.cmpNumeric(block, src, lhs, rhs, op);5067 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
5005 } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {5068 } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
5006 if (!is_equality_cmp) {5069 if (!is_equality_cmp) {
5007 return mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});5070 return mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});
5008 }5071 }
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 }
5010 }5079 }
50115080
5012 const instructions = &[_]*Inst{ lhs, rhs };5081 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
5013 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);5082 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
5014 if (!resolved_type.isSelfComparable(is_equality_cmp)) {5083 if (!resolved_type.isSelfComparable(is_equality_cmp)) {
5015 return mod.fail(&block.base, src, "operator not allowed for type '{}'", .{resolved_type});5084 return mod.fail(&block.base, src, "operator not allowed for type '{}'", .{resolved_type});
...@@ -5018,18 +5087,21 @@ fn zirCmp(...@@ -5018,18 +5087,21 @@ fn zirCmp(
5018 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);5087 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
5019 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);5088 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
50205089
5021 if (casted_lhs.value()) |lhs_val| {5090 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, casted_lhs)) |lhs_val| {
5022 if (casted_rhs.value()) |rhs_val| {5091 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
5023 if (lhs_val.isUndef() or rhs_val.isUndef()) {5092 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;
5025 }5099 }
5026 const result = lhs_val.compare(op, rhs_val);
5027 return sema.mod.constBool(sema.arena, src, result);
5028 }5100 }
5029 }5101 }
50305102
5031 try sema.requireRuntimeBlock(block, src);5103 try sema.requireRuntimeBlock(block, src);
5032 const tag: Inst.Tag = switch (op) {5104 const tag: Air.Inst.Tag = switch (op) {
5033 .lt => .cmp_lt,5105 .lt => .cmp_lt,
5034 .lte => .cmp_lte,5106 .lte => .cmp_lte,
5035 .eq => .cmp_eq,5107 .eq => .cmp_eq,
...@@ -5037,35 +5109,33 @@ fn zirCmp(...@@ -5037,35 +5109,33 @@ fn zirCmp(
5037 .gt => .cmp_gt,5109 .gt => .cmp_gt,
5038 .neq => .cmp_neq,5110 .neq => .cmp_neq,
5039 };5111 };
5040 const bool_type = Type.initTag(.bool); // TODO handle vectors5112 // TODO handle vectors
5041 return block.addBinOp(src, bool_type, tag, casted_lhs, casted_rhs);5113 return block.addBinOp(tag, casted_lhs, casted_rhs);
5042}5114}
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 {
5045 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5117 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5046 const src = inst_data.src();
5047 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };5118 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
5048 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);5119 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
5049 const target = sema.mod.getTarget();5120 const target = sema.mod.getTarget();
5050 const abi_size = operand_ty.abiSize(target);5121 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);
5052}5123}
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 {
5055 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5126 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5056 const src = inst_data.src();
5057 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };5127 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
5058 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);5128 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
5059 const target = sema.mod.getTarget();5129 const target = sema.mod.getTarget();
5060 const bit_size = operand_ty.bitSize(target);5130 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);
5062}5132}
50635133
5064fn zirThis(5134fn zirThis(
5065 sema: *Sema,5135 sema: *Sema,
5066 block: *Scope.Block,5136 block: *Scope.Block,
5067 extended: Zir.Inst.Extended.InstData,5137 extended: Zir.Inst.Extended.InstData,
5068) InnerError!*Inst {5138) CompileError!Air.Inst.Ref {
5069 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };5139 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
5070 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirThis", .{});5140 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirThis", .{});
5071}5141}
...@@ -5074,7 +5144,7 @@ fn zirRetAddr(...@@ -5074,7 +5144,7 @@ fn zirRetAddr(
5074 sema: *Sema,5144 sema: *Sema,
5075 block: *Scope.Block,5145 block: *Scope.Block,
5076 extended: Zir.Inst.Extended.InstData,5146 extended: Zir.Inst.Extended.InstData,
5077) InnerError!*Inst {5147) CompileError!Air.Inst.Ref {
5078 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };5148 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
5079 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirRetAddr", .{});5149 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirRetAddr", .{});
5080}5150}
...@@ -5083,12 +5153,12 @@ fn zirBuiltinSrc(...@@ -5083,12 +5153,12 @@ fn zirBuiltinSrc(
5083 sema: *Sema,5153 sema: *Sema,
5084 block: *Scope.Block,5154 block: *Scope.Block,
5085 extended: Zir.Inst.Extended.InstData,5155 extended: Zir.Inst.Extended.InstData,
5086) InnerError!*Inst {5156) CompileError!Air.Inst.Ref {
5087 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };5157 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
5088 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinSrc", .{});5158 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinSrc", .{});
5089}5159}
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 {
5092 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5162 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5093 const src = inst_data.src();5163 const src = inst_data.src();
5094 const ty = try sema.resolveType(block, src, inst_data.operand);5164 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...@@ -5114,16 +5184,16 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
5114 // args: []const FnArg,5184 // args: []const FnArg,
5115 field_values[5] = Value.initTag(.null_value); // TODO5185 field_values[5] = Value.initTag(.null_value); // TODO
51165186
5117 return sema.mod.constInst(sema.arena, src, .{5187 return sema.addConstant(
5118 .ty = type_info_ty,5188 type_info_ty,
5119 .val = try Value.Tag.@"union".create(sema.arena, .{5189 try Value.Tag.@"union".create(sema.arena, .{
5120 .tag = try Value.Tag.enum_field_index.create(5190 .tag = try Value.Tag.enum_field_index.create(
5121 sema.arena,5191 sema.arena,
5122 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),5192 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),
5123 ),5193 ),
5124 .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),5194 .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),
5125 }),5195 }),
5126 });5196 );
5127 },5197 },
5128 else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{5198 else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{
5129 @tagName(t),5199 @tagName(t),
...@@ -5131,31 +5201,30 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -5131,31 +5201,30 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
5131 }5201 }
5132}5202}
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 {
5135 _ = block;5205 _ = block;
5136 const zir_datas = sema.code.instructions.items(.data);5206 const zir_datas = sema.code.instructions.items(.data);
5137 const inst_data = zir_datas[inst].un_node;5207 const inst_data = zir_datas[inst].un_node;
5138 const src = inst_data.src();5208 const operand = sema.resolveInst(inst_data.operand);
5139 const operand = try sema.resolveInst(inst_data.operand);5209 const operand_ty = sema.typeOf(operand);
5140 return sema.mod.constType(sema.arena, src, operand.ty);5210 return sema.addType(operand_ty);
5141}5211}
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 {
5144 _ = block;5214 _ = block;
5145 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5215 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5146 const src = inst_data.src();5216 const operand_ptr = sema.resolveInst(inst_data.operand);
5147 const operand_ptr = try sema.resolveInst(inst_data.operand);5217 const elem_ty = sema.typeOf(operand_ptr).elemType();
5148 const elem_ty = operand_ptr.ty.elemType();5218 return sema.addType(elem_ty);
5149 return sema.mod.constType(sema.arena, src, elem_ty);
5150}5219}
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 {
5153 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5222 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5154 const src = inst_data.src();5223 const src = inst_data.src();
5155 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeofLog2IntType", .{});5224 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeofLog2IntType", .{});
5156}5225}
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 {
5159 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5228 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5160 const src = inst_data.src();5229 const src = inst_data.src();
5161 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});5230 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});
...@@ -5165,7 +5234,7 @@ fn zirTypeofPeer(...@@ -5165,7 +5234,7 @@ fn zirTypeofPeer(
5165 sema: *Sema,5234 sema: *Sema,
5166 block: *Scope.Block,5235 block: *Scope.Block,
5167 extended: Zir.Inst.Extended.InstData,5236 extended: Zir.Inst.Extended.InstData,
5168) InnerError!*Inst {5237) CompileError!Air.Inst.Ref {
5169 const tracy = trace(@src());5238 const tracy = trace(@src());
5170 defer tracy.end();5239 defer tracy.end();
51715240
...@@ -5173,63 +5242,37 @@ fn zirTypeofPeer(...@@ -5173,63 +5242,37 @@ fn zirTypeofPeer(
5173 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };5242 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
5174 const args = sema.code.refSlice(extra.end, extended.small);5243 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);
5177 defer sema.gpa.free(inst_list);5246 defer sema.gpa.free(inst_list);
51785247
5179 for (args) |arg_ref, i| {5248 for (args) |arg_ref, i| {
5180 inst_list[i] = try sema.resolveInst(arg_ref);5249 inst_list[i] = sema.resolveInst(arg_ref);
5181 }5250 }
51825251
5183 const result_type = try sema.resolvePeerTypes(block, src, inst_list);5252 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);
5185}5254}
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 {
5188 const tracy = trace(@src());5257 const tracy = trace(@src());
5189 defer tracy.end();5258 defer tracy.end();
51905259
5191 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5260 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5192 const src = inst_data.src();5261 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
5195 const bool_type = Type.initTag(.bool);5265 const bool_type = Type.initTag(.bool);
5196 const operand = try sema.coerce(block, bool_type, uncasted_operand, uncasted_operand.src);5266 const operand = try sema.coerce(block, bool_type, uncasted_operand, operand_src);
5197 if (try sema.resolveDefinedValue(block, src, operand)) |val| {5267 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
5198 return sema.mod.constBool(sema.arena, src, !val.toBool());5268 if (val.toBool()) {
5199 }5269 return Air.Inst.Ref.bool_false;
5200 try sema.requireRuntimeBlock(block, src);5270 } else {
5201 return block.addUnOp(src, bool_type, .not, operand);5271 return Air.Inst.Ref.bool_true;
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 }
5228 }5272 }
5229 }5273 }
5230 try sema.requireRuntimeBlock(block, src);5274 try sema.requireRuntimeBlock(block, src);
5231 const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and;5275 return block.addTyOp(.not, bool_type, operand);
5232 return block.addBinOp(src, bool_type, tag, lhs, rhs);
5233}5276}
52345277
5235fn zirBoolBr(5278fn zirBoolBr(
...@@ -5237,20 +5280,25 @@ fn zirBoolBr(...@@ -5237,20 +5280,25 @@ fn zirBoolBr(
5237 parent_block: *Scope.Block,5280 parent_block: *Scope.Block,
5238 inst: Zir.Inst.Index,5281 inst: Zir.Inst.Index,
5239 is_bool_or: bool,5282 is_bool_or: bool,
5240) InnerError!*Inst {5283) CompileError!Air.Inst.Ref {
5241 const tracy = trace(@src());5284 const tracy = trace(@src());
5242 defer tracy.end();5285 defer tracy.end();
52435286
5244 const datas = sema.code.instructions.items(.data);5287 const datas = sema.code.instructions.items(.data);
5245 const inst_data = datas[inst].bool_br;5288 const inst_data = datas[inst].bool_br;
5246 const src: LazySrcLoc = .unneeded;5289 const lhs = sema.resolveInst(inst_data.lhs);
5247 const lhs = try sema.resolveInst(inst_data.lhs);5290 const lhs_src = sema.src;
5248 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);5291 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
5249 const body = sema.code.extra[extra.end..][0..extra.data.body_len];5292 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| {
5252 if (lhs_val.toBool() == is_bool_or) {5296 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 }
5254 }5302 }
5255 // comptime-known left-hand side. No need for a block here; the result5303 // comptime-known left-hand side. No need for a block here; the result
5256 // is simply the rhs expression. Here we rely on there only being 15304 // is simply the rhs expression. Here we rely on there only being 1
...@@ -5258,62 +5306,72 @@ fn zirBoolBr(...@@ -5258,62 +5306,72 @@ fn zirBoolBr(
5258 return sema.resolveBody(parent_block, body);5306 return sema.resolveBody(parent_block, body);
5259 }5307 }
52605308
5261 const block_inst = try sema.arena.create(Inst.Block);5309 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
5262 block_inst.* = .{5310 try sema.air_instructions.append(gpa, .{
5263 .base = .{5311 .tag = .block,
5264 .tag = Inst.Block.base_tag,5312 .data = .{ .ty_pl = .{
5265 .ty = Type.initTag(.bool),5313 .ty = .bool_type,
5266 .src = src,5314 .payload = undefined,
5267 },5315 } },
5268 .body = undefined,5316 });
5269 };
52705317
5271 var child_block = parent_block.makeSubBlock();5318 var child_block = parent_block.makeSubBlock();
5272 child_block.runtime_loop = null;5319 child_block.runtime_loop = null;
5273 child_block.runtime_cond = lhs.src;5320 child_block.runtime_cond = lhs_src;
5274 child_block.runtime_index += 1;5321 child_block.runtime_index += 1;
5275 defer child_block.instructions.deinit(sema.gpa);5322 defer child_block.instructions.deinit(gpa);
52765323
5277 var then_block = child_block.makeSubBlock();5324 var then_block = child_block.makeSubBlock();
5278 defer then_block.instructions.deinit(sema.gpa);5325 defer then_block.instructions.deinit(gpa);
52795326
5280 var else_block = child_block.makeSubBlock();5327 var else_block = child_block.makeSubBlock();
5281 defer else_block.instructions.deinit(sema.gpa);5328 defer else_block.instructions.deinit(gpa);
52825329
5283 const lhs_block = if (is_bool_or) &then_block else &else_block;5330 const lhs_block = if (is_bool_or) &then_block else &else_block;
5284 const rhs_block = if (is_bool_or) &else_block else &then_block;5331 const rhs_block = if (is_bool_or) &else_block else &then_block;
52855332
5286 const lhs_result = try sema.mod.constInst(sema.arena, src, .{5333 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
5287 .ty = Type.initTag(.bool),5334 _ = try lhs_block.addBr(block_inst, lhs_result);
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);
52915335
5292 const rhs_result = try sema.resolveBody(rhs_block, body);5336 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) };5339 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
5296 const air_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, else_block.instructions.items) };5340 then_block.instructions.items.len + else_block.instructions.items.len +
5297 _ = try child_block.addCondBr(src, lhs, air_then_body, air_else_body);5341 @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len);
52985342
5299 block_inst.body = .{5343 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
5300 .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),5344 .then_body_len = @intCast(u32, then_block.instructions.items.len),
5301 };5345 .else_body_len = @intCast(u32, else_block.instructions.items.len),
5302 try parent_block.instructions.append(sema.gpa, &block_inst.base);5346 });
5303 return &block_inst.base;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);
5304}5362}
53055363
5306fn zirIsNonNull(5364fn zirIsNonNull(
5307 sema: *Sema,5365 sema: *Sema,
5308 block: *Scope.Block,5366 block: *Scope.Block,
5309 inst: Zir.Inst.Index,5367 inst: Zir.Inst.Index,
5310) InnerError!*Inst {5368) CompileError!Air.Inst.Ref {
5311 const tracy = trace(@src());5369 const tracy = trace(@src());
5312 defer tracy.end();5370 defer tracy.end();
53135371
5314 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5372 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5315 const src = inst_data.src();5373 const src = inst_data.src();
5316 const operand = try sema.resolveInst(inst_data.operand);5374 const operand = sema.resolveInst(inst_data.operand);
5317 return sema.analyzeIsNull(block, src, operand, true);5375 return sema.analyzeIsNull(block, src, operand, true);
5318}5376}
53195377
...@@ -5321,33 +5379,33 @@ fn zirIsNonNullPtr(...@@ -5321,33 +5379,33 @@ fn zirIsNonNullPtr(
5321 sema: *Sema,5379 sema: *Sema,
5322 block: *Scope.Block,5380 block: *Scope.Block,
5323 inst: Zir.Inst.Index,5381 inst: Zir.Inst.Index,
5324) InnerError!*Inst {5382) CompileError!Air.Inst.Ref {
5325 const tracy = trace(@src());5383 const tracy = trace(@src());
5326 defer tracy.end();5384 defer tracy.end();
53275385
5328 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5386 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5329 const src = inst_data.src();5387 const src = inst_data.src();
5330 const ptr = try sema.resolveInst(inst_data.operand);5388 const ptr = sema.resolveInst(inst_data.operand);
5331 const loaded = try sema.analyzeLoad(block, src, ptr, src);5389 const loaded = try sema.analyzeLoad(block, src, ptr, src);
5332 return sema.analyzeIsNull(block, src, loaded, true);5390 return sema.analyzeIsNull(block, src, loaded, true);
5333}5391}
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 {
5336 const tracy = trace(@src());5394 const tracy = trace(@src());
5337 defer tracy.end();5395 defer tracy.end();
53385396
5339 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5397 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);
5341 return sema.analyzeIsNonErr(block, inst_data.src(), operand);5399 return sema.analyzeIsNonErr(block, inst_data.src(), operand);
5342}5400}
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 {
5345 const tracy = trace(@src());5403 const tracy = trace(@src());
5346 defer tracy.end();5404 defer tracy.end();
53475405
5348 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5406 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5349 const src = inst_data.src();5407 const src = inst_data.src();
5350 const ptr = try sema.resolveInst(inst_data.operand);5408 const ptr = sema.resolveInst(inst_data.operand);
5351 const loaded = try sema.analyzeLoad(block, src, ptr, src);5409 const loaded = try sema.analyzeLoad(block, src, ptr, src);
5352 return sema.analyzeIsNonErr(block, src, loaded);5410 return sema.analyzeIsNonErr(block, src, loaded);
5353}5411}
...@@ -5356,7 +5414,7 @@ fn zirCondbr(...@@ -5356,7 +5414,7 @@ fn zirCondbr(
5356 sema: *Sema,5414 sema: *Sema,
5357 parent_block: *Scope.Block,5415 parent_block: *Scope.Block,
5358 inst: Zir.Inst.Index,5416 inst: Zir.Inst.Index,
5359) InnerError!Zir.Inst.Index {5417) CompileError!Zir.Inst.Index {
5360 const tracy = trace(@src());5418 const tracy = trace(@src());
5361 defer tracy.end();5419 defer tracy.end();
53625420
...@@ -5368,7 +5426,7 @@ fn zirCondbr(...@@ -5368,7 +5426,7 @@ fn zirCondbr(
5368 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];5426 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
5369 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];5427 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);
5372 const cond = try sema.coerce(parent_block, Type.initTag(.bool), uncasted_cond, cond_src);5430 const cond = try sema.coerce(parent_block, Type.initTag(.bool), uncasted_cond, cond_src);
53735431
5374 if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {5432 if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
...@@ -5377,29 +5435,39 @@ fn zirCondbr(...@@ -5377,29 +5435,39 @@ fn zirCondbr(
5377 return always_noreturn;5435 return always_noreturn;
5378 }5436 }
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.
5380 var sub_block = parent_block.makeSubBlock();5442 var sub_block = parent_block.makeSubBlock();
5381 sub_block.runtime_loop = null;5443 sub_block.runtime_loop = null;
5382 sub_block.runtime_cond = cond.src;5444 sub_block.runtime_cond = cond_src;
5383 sub_block.runtime_index += 1;5445 sub_block.runtime_index += 1;
5384 defer sub_block.instructions.deinit(sema.gpa);5446 defer sub_block.instructions.deinit(gpa);
53855447
5386 _ = try sema.analyzeBody(&sub_block, then_body);5448 _ = try sema.analyzeBody(&sub_block, then_body);
5387 const air_then_body: ir.Body = .{5449 const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
5388 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),5450 defer gpa.free(true_instructions);
5389 };
5390
5391 sub_block.instructions.shrinkRetainingCapacity(0);
53925451
5393 _ = try sema.analyzeBody(&sub_block, else_body);5452 _ = try sema.analyzeBody(&sub_block, else_body);
5394 const air_else_body: ir.Body = .{5453 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
5395 .instructions = try sema.arena.dupe(*Inst, sub_block.instructions.items),5454 true_instructions.len + sub_block.instructions.items.len);
5396 };5455 _ = try parent_block.addInst(.{
53975456 .tag = .cond_br,
5398 _ = try parent_block.addCondBr(src, cond, air_then_body, air_else_body);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);
5399 return always_noreturn;5467 return always_noreturn;
5400}5468}
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 {
5403 const tracy = trace(@src());5471 const tracy = trace(@src());
5404 defer tracy.end();5472 defer tracy.end();
54055473
...@@ -5411,7 +5479,7 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -5411,7 +5479,7 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
5411 if (safety_check and block.wantSafety()) {5479 if (safety_check and block.wantSafety()) {
5412 return sema.safetyPanic(block, src, .unreach);5480 return sema.safetyPanic(block, src, .unreach);
5413 } else {5481 } else {
5414 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);5482 _ = try block.addNoOp(.unreach);
5415 return always_noreturn;5483 return always_noreturn;
5416 }5484 }
5417}5485}
...@@ -5420,7 +5488,7 @@ fn zirRetErrValue(...@@ -5420,7 +5488,7 @@ fn zirRetErrValue(
5420 sema: *Sema,5488 sema: *Sema,
5421 block: *Scope.Block,5489 block: *Scope.Block,
5422 inst: Zir.Inst.Index,5490 inst: Zir.Inst.Index,
5423) InnerError!Zir.Inst.Index {5491) CompileError!Zir.Inst.Index {
5424 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;5492 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
5425 const err_name = inst_data.get(sema.code);5493 const err_name = inst_data.get(sema.code);
5426 const src = inst_data.src();5494 const src = inst_data.src();
...@@ -5433,10 +5501,10 @@ fn zirRetErrValue(...@@ -5433,10 +5501,10 @@ fn zirRetErrValue(
5433 }5501 }
5434 // Return the error code from the function.5502 // Return the error code from the function.
5435 const kv = try sema.mod.getErrorValue(err_name);5503 const kv = try sema.mod.getErrorValue(err_name);
5436 const result_inst = try sema.mod.constInst(sema.arena, src, .{5504 const result_inst = try sema.addConstant(
5437 .ty = try Type.Tag.error_set_single.create(sema.arena, kv.key),5505 try Type.Tag.error_set_single.create(sema.arena, kv.key),
5438 .val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),5506 try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
5439 });5507 );
5440 return sema.analyzeRet(block, result_inst, src, true);5508 return sema.analyzeRet(block, result_inst, src, true);
5441}5509}
54425510
...@@ -5445,23 +5513,23 @@ fn zirRetCoerce(...@@ -5445,23 +5513,23 @@ fn zirRetCoerce(
5445 block: *Scope.Block,5513 block: *Scope.Block,
5446 inst: Zir.Inst.Index,5514 inst: Zir.Inst.Index,
5447 need_coercion: bool,5515 need_coercion: bool,
5448) InnerError!Zir.Inst.Index {5516) CompileError!Zir.Inst.Index {
5449 const tracy = trace(@src());5517 const tracy = trace(@src());
5450 defer tracy.end();5518 defer tracy.end();
54515519
5452 const inst_data = sema.code.instructions.items(.data)[inst].un_tok;5520 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);
5454 const src = inst_data.src();5522 const src = inst_data.src();
54555523
5456 return sema.analyzeRet(block, operand, src, need_coercion);5524 return sema.analyzeRet(block, operand, src, need_coercion);
5457}5525}
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 {
5460 const tracy = trace(@src());5528 const tracy = trace(@src());
5461 defer tracy.end();5529 defer tracy.end();
54625530
5463 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5531 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);
5465 const src = inst_data.src();5533 const src = inst_data.src();
54665534
5467 return sema.analyzeRet(block, operand, src, false);5535 return sema.analyzeRet(block, operand, src, false);
...@@ -5470,14 +5538,14 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -5470,14 +5538,14 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
5470fn analyzeRet(5538fn analyzeRet(
5471 sema: *Sema,5539 sema: *Sema,
5472 block: *Scope.Block,5540 block: *Scope.Block,
5473 operand: *Inst,5541 operand: Air.Inst.Ref,
5474 src: LazySrcLoc,5542 src: LazySrcLoc,
5475 need_coercion: bool,5543 need_coercion: bool,
5476) InnerError!Zir.Inst.Index {5544) CompileError!Zir.Inst.Index {
5477 if (block.inlining) |inlining| {5545 if (block.inlining) |inlining| {
5478 // We are inlining a function call; rewrite the `ret` as a `break`.5546 // We are inlining a function call; rewrite the `ret` as a `break`.
5479 try inlining.merges.results.append(sema.gpa, operand);5547 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);
5481 return always_noreturn;5549 return always_noreturn;
5482 }5550 }
54835551
...@@ -5486,14 +5554,11 @@ fn analyzeRet(...@@ -5486,14 +5554,11 @@ fn analyzeRet(
5486 const fn_ty = func.owner_decl.ty;5554 const fn_ty = func.owner_decl.ty;
5487 const fn_ret_ty = fn_ty.fnReturnType();5555 const fn_ret_ty = fn_ty.fnReturnType();
5488 const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src);5556 const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src);
5489 if (fn_ret_ty.zigTypeTag() == .Void)5557 _ = try block.addUnOp(.ret, casted_operand);
5490 _ = try block.addNoOp(src, Type.initTag(.noreturn), .retvoid)
5491 else
5492 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
5493 return always_noreturn;5558 return always_noreturn;
5494 }5559 }
5495 }5560 }
5496 _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);5561 _ = try block.addUnOp(.ret, operand);
5497 return always_noreturn;5562 return always_noreturn;
5498}5563}
54995564
...@@ -5505,7 +5570,7 @@ fn floatOpAllowed(tag: Zir.Inst.Tag) bool {...@@ -5505,7 +5570,7 @@ fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
5505 };5570 };
5506}5571}
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 {
5509 const tracy = trace(@src());5574 const tracy = trace(@src());
5510 defer tracy.end();5575 defer tracy.end();
55115576
...@@ -5523,10 +5588,10 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne...@@ -5523,10 +5588,10 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
5523 inst_data.is_volatile,5588 inst_data.is_volatile,
5524 inst_data.size,5589 inst_data.size,
5525 );5590 );
5526 return sema.mod.constType(sema.arena, .unneeded, ty);5591 return sema.addType(ty);
5527}5592}
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 {
5530 const tracy = trace(@src());5595 const tracy = trace(@src());
5531 defer tracy.end();5596 defer tracy.end();
55325597
...@@ -5577,10 +5642,10 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError...@@ -5577,10 +5642,10 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError
5577 inst_data.flags.is_volatile,5642 inst_data.flags.is_volatile,
5578 inst_data.size,5643 inst_data.size,
5579 );5644 );
5580 return sema.mod.constType(sema.arena, src, ty);5645 return sema.addType(ty);
5581}5646}
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 {
5584 const tracy = trace(@src());5649 const tracy = trace(@src());
5585 defer tracy.end();5650 defer tracy.end();
55865651
...@@ -5588,19 +5653,16 @@ fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In...@@ -5588,19 +5653,16 @@ fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
5588 const src = inst_data.src();5653 const src = inst_data.src();
5589 const struct_type = try sema.resolveType(block, src, inst_data.operand);5654 const struct_type = try sema.resolveType(block, src, inst_data.operand);
55905655
5591 return sema.mod.constInst(sema.arena, src, .{5656 return sema.addConstant(struct_type, Value.initTag(.empty_struct_value));
5592 .ty = struct_type,
5593 .val = Value.initTag(.empty_struct_value),
5594 });
5595}5657}
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 {
5598 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5660 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5599 const src = inst_data.src();5661 const src = inst_data.src();
5600 return sema.mod.fail(&block.base, src, "TODO: Sema.zirUnionInitPtr", .{});5662 return sema.mod.fail(&block.base, src, "TODO: Sema.zirUnionInitPtr", .{});
5601}5663}
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 {
5604 const mod = sema.mod;5666 const mod = sema.mod;
5605 const gpa = sema.gpa;5667 const gpa = sema.gpa;
5606 const zir_datas = sema.code.instructions.items(.data);5668 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:...@@ -5622,7 +5684,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
5622 mem.set(Zir.Inst.Index, found_fields, 0);5684 mem.set(Zir.Inst.Index, found_fields, 0);
56235685
5624 // The init values to use for the struct instance.5686 // 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());
5626 defer gpa.free(field_inits);5688 defer gpa.free(field_inits);
56275689
5628 var field_i: u32 = 0;5690 var field_i: u32 = 0;
...@@ -5651,7 +5713,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:...@@ -5651,7 +5713,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
5651 return mod.failWithOwnedErrorMsg(&block.base, msg);5713 return mod.failWithOwnedErrorMsg(&block.base, msg);
5652 }5714 }
5653 found_fields[field_index] = item.data.field_type;5715 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);
5655 }5717 }
56565718
5657 var root_msg: ?*Module.ErrorMsg = null;5719 var root_msg: ?*Module.ErrorMsg = null;
...@@ -5671,10 +5733,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:...@@ -5671,10 +5733,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
5671 root_msg = try mod.errMsg(&block.base, src, template, args);5733 root_msg = try mod.errMsg(&block.base, src, template, args);
5672 }5734 }
5673 } else {5735 } else {
5674 field_inits[i] = try mod.constInst(sema.arena, src, .{5736 field_inits[i] = try sema.addConstant(field.ty, field.default_val);
5675 .ty = field.ty,
5676 .val = field.default_val,
5677 });
5678 }5737 }
5679 }5738 }
5680 if (root_msg) |msg| {5739 if (root_msg) |msg| {
...@@ -5694,7 +5753,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:...@@ -5694,7 +5753,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
5694 }5753 }
56955754
5696 const is_comptime = for (field_inits) |field_init| {5755 const is_comptime = for (field_inits) |field_init| {
5697 if (field_init.value() == null) {5756 if (!(try sema.isComptimeKnown(block, src, field_init))) {
5698 break false;5757 break false;
5699 }5758 }
5700 } else true;5759 } else true;
...@@ -5702,18 +5761,15 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:...@@ -5702,18 +5761,15 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
5702 if (is_comptime) {5761 if (is_comptime) {
5703 const values = try sema.arena.alloc(Value, field_inits.len);5762 const values = try sema.arena.alloc(Value, field_inits.len);
5704 for (field_inits) |field_init, i| {5763 for (field_inits) |field_init, i| {
5705 values[i] = field_init.value().?;5764 values[i] = (sema.resolvePossiblyUndefinedValue(block, src, field_init) catch unreachable).?;
5706 }5765 }
5707 return mod.constInst(sema.arena, src, .{5766 return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr));
5708 .ty = struct_ty,
5709 .val = try Value.Tag.@"struct".create(sema.arena, values.ptr),
5710 });
5711 }5767 }
57125768
5713 return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});5769 return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
5714}5770}
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 {
5717 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5773 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5718 const src = inst_data.src();5774 const src = inst_data.src();
57195775
...@@ -5721,7 +5777,7 @@ fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_...@@ -5721,7 +5777,7 @@ fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_
5721 return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInitAnon", .{});5777 return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInitAnon", .{});
5722}5778}
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 {
5725 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5781 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5726 const src = inst_data.src();5782 const src = inst_data.src();
57275783
...@@ -5729,7 +5785,7 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:...@@ -5729,7 +5785,7 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
5729 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{});5785 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{});
5730}5786}
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 {
5733 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5789 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5734 const src = inst_data.src();5790 const src = inst_data.src();
57355791
...@@ -5737,13 +5793,13 @@ fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_r...@@ -5737,13 +5793,13 @@ fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_r
5737 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInitAnon", .{});5793 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInitAnon", .{});
5738}5794}
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 {
5741 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5797 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5742 const src = inst_data.src();5798 const src = inst_data.src();
5743 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldTypeRef", .{});5799 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldTypeRef", .{});
5744}5800}
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 {
5747 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5803 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5748 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;5804 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
5749 const src = inst_data.src();5805 const src = inst_data.src();
...@@ -5758,14 +5814,14 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr...@@ -5758,14 +5814,14 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
5758 const struct_obj = struct_ty.castTag(.@"struct").?.data;5814 const struct_obj = struct_ty.castTag(.@"struct").?.data;
5759 const field = struct_obj.fields.get(field_name) orelse5815 const field = struct_obj.fields.get(field_name) orelse
5760 return sema.failWithBadFieldAccess(block, struct_obj, src, field_name);5816 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);
5762}5818}
57635819
5764fn zirErrorReturnTrace(5820fn zirErrorReturnTrace(
5765 sema: *Sema,5821 sema: *Sema,
5766 block: *Scope.Block,5822 block: *Scope.Block,
5767 extended: Zir.Inst.Extended.InstData,5823 extended: Zir.Inst.Extended.InstData,
5768) InnerError!*Inst {5824) CompileError!Air.Inst.Ref {
5769 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };5825 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
5770 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorReturnTrace", .{});5826 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorReturnTrace", .{});
5771}5827}
...@@ -5774,7 +5830,7 @@ fn zirFrame(...@@ -5774,7 +5830,7 @@ fn zirFrame(
5774 sema: *Sema,5830 sema: *Sema,
5775 block: *Scope.Block,5831 block: *Scope.Block,
5776 extended: Zir.Inst.Extended.InstData,5832 extended: Zir.Inst.Extended.InstData,
5777) InnerError!*Inst {5833) CompileError!Air.Inst.Ref {
5778 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };5834 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
5779 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrame", .{});5835 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrame", .{});
5780}5836}
...@@ -5783,91 +5839,91 @@ fn zirFrameAddress(...@@ -5783,91 +5839,91 @@ fn zirFrameAddress(
5783 sema: *Sema,5839 sema: *Sema,
5784 block: *Scope.Block,5840 block: *Scope.Block,
5785 extended: Zir.Inst.Extended.InstData,5841 extended: Zir.Inst.Extended.InstData,
5786) InnerError!*Inst {5842) CompileError!Air.Inst.Ref {
5787 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };5843 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
5788 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameAddress", .{});5844 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameAddress", .{});
5789}5845}
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 {
5792 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5848 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5793 const src = inst_data.src();5849 const src = inst_data.src();
5794 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignOf", .{});5850 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignOf", .{});
5795}5851}
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 {
5798 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5854 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5799 const src = inst_data.src();5855 const src = inst_data.src();
5800 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBoolToInt", .{});5856 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBoolToInt", .{});
5801}5857}
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 {
5804 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5860 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5805 const src = inst_data.src();5861 const src = inst_data.src();
5806 return sema.mod.fail(&block.base, src, "TODO: Sema.zirEmbedFile", .{});5862 return sema.mod.fail(&block.base, src, "TODO: Sema.zirEmbedFile", .{});
5807}5863}
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 {
5810 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5866 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5811 const src = inst_data.src();5867 const src = inst_data.src();
5812 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorName", .{});5868 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorName", .{});
5813}5869}
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 {
5816 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5872 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5817 const src = inst_data.src();5873 const src = inst_data.src();
5818 return sema.mod.fail(&block.base, src, "TODO: Sema.zirUnaryMath", .{});5874 return sema.mod.fail(&block.base, src, "TODO: Sema.zirUnaryMath", .{});
5819}5875}
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 {
5822 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5878 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5823 const src = inst_data.src();5879 const src = inst_data.src();
5824 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTagName", .{});5880 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTagName", .{});
5825}5881}
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 {
5828 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5884 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5829 const src = inst_data.src();5885 const src = inst_data.src();
5830 return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify", .{});5886 return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify", .{});
5831}5887}
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 {
5834 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5890 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5835 const src = inst_data.src();5891 const src = inst_data.src();
5836 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTypeName", .{});5892 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTypeName", .{});
5837}5893}
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 {
5840 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5896 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5841 const src = inst_data.src();5897 const src = inst_data.src();
5842 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameType", .{});5898 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameType", .{});
5843}5899}
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 {
5846 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5902 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5847 const src = inst_data.src();5903 const src = inst_data.src();
5848 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameSize", .{});5904 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameSize", .{});
5849}5905}
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 {
5852 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5908 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5853 const src = inst_data.src();5909 const src = inst_data.src();
5854 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});5910 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
5855}5911}
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 {
5858 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5914 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5859 const src = inst_data.src();5915 const src = inst_data.src();
5860 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});5916 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});
5861}5917}
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 {
5864 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5920 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5865 const src = inst_data.src();5921 const src = inst_data.src();
58665922
5867 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;5923 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
58685924
5869 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };5925 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);
5871 const operand_coerced = try sema.coerce(block, Type.initTag(.usize), operand_res, operand_src);5927 const operand_coerced = try sema.coerce(block, Type.initTag(.usize), operand_res, operand_src);
58725928
5873 const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };5929 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...@@ -5888,20 +5944,13 @@ fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
5888 .base = .{ .tag = .int_u64 },5944 .base = .{ .tag = .int_u64 },
5889 .data = addr,5945 .data = addr,
5890 };5946 };
5891 return sema.mod.constInst(sema.arena, src, .{5947 return sema.addConstant(type_res, Value.initPayload(&val_payload.base));
5892 .ty = type_res,
5893 .val = Value.initPayload(&val_payload.base),
5894 });
5895 }5948 }
58965949
5897 try sema.requireRuntimeBlock(block, src);5950 try sema.requireRuntimeBlock(block, src);
5898 if (block.wantSafety()) {5951 if (block.wantSafety()) {
5899 const zero = try sema.mod.constInst(sema.arena, src, .{
5900 .ty = Type.initTag(.u64),
5901 .val = Value.initTag(.zero),
5902 });
5903 if (!type_res.isAllowzeroPtr()) {5952 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);
5905 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);5954 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
5906 }5955 }
59075956
...@@ -5911,211 +5960,211 @@ fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro...@@ -5911,211 +5960,211 @@ fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
5911 .base = .{ .tag = .int_u64 },5960 .base = .{ .tag = .int_u64 },
5912 .data = ptr_align - 1,5961 .data = ptr_align - 1,
5913 };5962 };
5914 const align_minus_1 = try sema.mod.constInst(sema.arena, src, .{5963 const align_minus_1 = try sema.addConstant(
5915 .ty = Type.initTag(.u64),5964 Type.initTag(.usize),
5916 .val = Value.initPayload(&val_payload.base),5965 Value.initPayload(&val_payload.base),
5917 });5966 );
5918 const remainder = try block.addBinOp(src, Type.initTag(.u64), .bit_and, operand_coerced, align_minus_1);5967 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
5919 const is_aligned = try block.addBinOp(src, Type.initTag(.bool), .cmp_eq, remainder, zero);5968 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
5920 try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);5969 try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
5921 }5970 }
5922 }5971 }
5923 return block.addUnOp(src, type_res, .bitcast, operand_coerced);5972 return block.addTyOp(.bitcast, type_res, operand_coerced);
5924}5973}
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 {
5927 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5976 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5928 const src = inst_data.src();5977 const src = inst_data.src();
5929 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrSetCast", .{});5978 return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrSetCast", .{});
5930}5979}
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 {
5933 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5982 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5934 const src = inst_data.src();5983 const src = inst_data.src();
5935 return sema.mod.fail(&block.base, src, "TODO: Sema.zirPtrCast", .{});5984 return sema.mod.fail(&block.base, src, "TODO: Sema.zirPtrCast", .{});
5936}5985}
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 {
5939 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5988 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5940 const src = inst_data.src();5989 const src = inst_data.src();
5941 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTruncate", .{});5990 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTruncate", .{});
5942}5991}
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 {
5945 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5994 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5946 const src = inst_data.src();5995 const src = inst_data.src();
5947 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignCast", .{});5996 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignCast", .{});
5948}5997}
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 {
5951 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6000 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5952 const src = inst_data.src();6001 const src = inst_data.src();
5953 return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});6002 return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});
5954}6003}
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 {
5957 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6006 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5958 const src = inst_data.src();6007 const src = inst_data.src();
5959 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});6008 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});
5960}6009}
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 {
5963 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6012 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5964 const src = inst_data.src();6013 const src = inst_data.src();
5965 return sema.mod.fail(&block.base, src, "TODO: Sema.zirPopCount", .{});6014 return sema.mod.fail(&block.base, src, "TODO: Sema.zirPopCount", .{});
5966}6015}
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 {
5969 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6018 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5970 const src = inst_data.src();6019 const src = inst_data.src();
5971 return sema.mod.fail(&block.base, src, "TODO: Sema.zirByteSwap", .{});6020 return sema.mod.fail(&block.base, src, "TODO: Sema.zirByteSwap", .{});
5972}6021}
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 {
5975 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6024 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5976 const src = inst_data.src();6025 const src = inst_data.src();
5977 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBitReverse", .{});6026 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBitReverse", .{});
5978}6027}
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 {
5981 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6030 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5982 const src = inst_data.src();6031 const src = inst_data.src();
5983 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivExact", .{});6032 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivExact", .{});
5984}6033}
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 {
5987 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6036 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5988 const src = inst_data.src();6037 const src = inst_data.src();
5989 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{});6038 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{});
5990}6039}
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 {
5993 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6042 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5994 const src = inst_data.src();6043 const src = inst_data.src();
5995 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});6044 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});
5996}6045}
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 {
5999 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6048 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6000 const src = inst_data.src();6049 const src = inst_data.src();
6001 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMod", .{});6050 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMod", .{});
6002}6051}
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 {
6005 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6054 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6006 const src = inst_data.src();6055 const src = inst_data.src();
6007 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRem", .{});6056 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRem", .{});
6008}6057}
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 {
6011 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6060 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6012 const src = inst_data.src();6061 const src = inst_data.src();
6013 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShlExact", .{});6062 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShlExact", .{});
6014}6063}
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 {
6017 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6066 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6018 const src = inst_data.src();6067 const src = inst_data.src();
6019 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShrExact", .{});6068 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShrExact", .{});
6020}6069}
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 {
6023 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6072 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6024 const src = inst_data.src();6073 const src = inst_data.src();
6025 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBitOffsetOf", .{});6074 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBitOffsetOf", .{});
6026}6075}
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 {
6029 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6078 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6030 const src = inst_data.src();6079 const src = inst_data.src();
6031 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});6080 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
6032}6081}
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 {
6035 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6084 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6036 const src = inst_data.src();6085 const src = inst_data.src();
6037 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCmpxchg", .{});6086 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCmpxchg", .{});
6038}6087}
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 {
6041 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6090 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6042 const src = inst_data.src();6091 const src = inst_data.src();
6043 return sema.mod.fail(&block.base, src, "TODO: Sema.zirSplat", .{});6092 return sema.mod.fail(&block.base, src, "TODO: Sema.zirSplat", .{});
6044}6093}
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 {
6047 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6096 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6048 const src = inst_data.src();6097 const src = inst_data.src();
6049 return sema.mod.fail(&block.base, src, "TODO: Sema.zirReduce", .{});6098 return sema.mod.fail(&block.base, src, "TODO: Sema.zirReduce", .{});
6050}6099}
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 {
6053 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6102 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6054 const src = inst_data.src();6103 const src = inst_data.src();
6055 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShuffle", .{});6104 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShuffle", .{});
6056}6105}
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 {
6059 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6108 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6060 const src = inst_data.src();6109 const src = inst_data.src();
6061 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicLoad", .{});6110 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicLoad", .{});
6062}6111}
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 {
6065 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6114 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6066 const src = inst_data.src();6115 const src = inst_data.src();
6067 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicRmw", .{});6116 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicRmw", .{});
6068}6117}
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 {
6071 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6120 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6072 const src = inst_data.src();6121 const src = inst_data.src();
6073 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicStore", .{});6122 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicStore", .{});
6074}6123}
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 {
6077 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6126 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6078 const src = inst_data.src();6127 const src = inst_data.src();
6079 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMulAdd", .{});6128 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMulAdd", .{});
6080}6129}
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 {
6083 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6132 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6084 const src = inst_data.src();6133 const src = inst_data.src();
6085 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinCall", .{});6134 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinCall", .{});
6086}6135}
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 {
6089 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6138 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6090 const src = inst_data.src();6139 const src = inst_data.src();
6091 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldPtrType", .{});6140 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldPtrType", .{});
6092}6141}
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 {
6095 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6144 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6096 const src = inst_data.src();6145 const src = inst_data.src();
6097 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});6146 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});
6098}6147}
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 {
6101 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6150 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6102 const src = inst_data.src();6151 const src = inst_data.src();
6103 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{});6152 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{});
6104}6153}
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 {
6107 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6156 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6108 const src = inst_data.src();6157 const src = inst_data.src();
6109 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});6158 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
6110}6159}
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 {
6113 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6162 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6114 const src = inst_data.src();6163 const src = inst_data.src();
6115 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinAsyncCall", .{});6164 return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinAsyncCall", .{});
6116}6165}
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 {
6119 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6168 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6120 const src = inst_data.src();6169 const src = inst_data.src();
6121 return sema.mod.fail(&block.base, src, "TODO: Sema.zirResume", .{});6170 return sema.mod.fail(&block.base, src, "TODO: Sema.zirResume", .{});
...@@ -6126,7 +6175,7 @@ fn zirAwait(...@@ -6126,7 +6175,7 @@ fn zirAwait(
6126 block: *Scope.Block,6175 block: *Scope.Block,
6127 inst: Zir.Inst.Index,6176 inst: Zir.Inst.Index,
6128 is_nosuspend: bool,6177 is_nosuspend: bool,
6129) InnerError!*Inst {6178) CompileError!Air.Inst.Ref {
6130 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6179 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6131 const src = inst_data.src();6180 const src = inst_data.src();
61326181
...@@ -6138,7 +6187,7 @@ fn zirVarExtended(...@@ -6138,7 +6187,7 @@ fn zirVarExtended(
6138 sema: *Sema,6187 sema: *Sema,
6139 block: *Scope.Block,6188 block: *Scope.Block,
6140 extended: Zir.Inst.Extended.InstData,6189 extended: Zir.Inst.Extended.InstData,
6141) InnerError!*Inst {6190) CompileError!Air.Inst.Ref {
6142 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);6191 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
6143 const src = sema.src;6192 const src = sema.src;
6144 const ty_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at type6193 const ty_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at type
...@@ -6192,10 +6241,10 @@ fn zirVarExtended(...@@ -6192,10 +6241,10 @@ fn zirVarExtended(
6192 .is_mutable = true, // TODO get rid of this unused field6241 .is_mutable = true, // TODO get rid of this unused field
6193 .is_threadlocal = small.is_threadlocal,6242 .is_threadlocal = small.is_threadlocal,
6194 };6243 };
6195 const result = try sema.mod.constInst(sema.arena, src, .{6244 const result = try sema.addConstant(
6196 .ty = var_ty,6245 var_ty,
6197 .val = try Value.Tag.variable.create(sema.arena, new_var),6246 try Value.Tag.variable.create(sema.arena, new_var),
6198 });6247 );
6199 return result;6248 return result;
6200}6249}
62016250
...@@ -6204,7 +6253,7 @@ fn zirFuncExtended(...@@ -6204,7 +6253,7 @@ fn zirFuncExtended(
6204 block: *Scope.Block,6253 block: *Scope.Block,
6205 extended: Zir.Inst.Extended.InstData,6254 extended: Zir.Inst.Extended.InstData,
6206 inst: Zir.Inst.Index,6255 inst: Zir.Inst.Index,
6207) InnerError!*Inst {6256) CompileError!Air.Inst.Ref {
6208 const tracy = trace(@src());6257 const tracy = trace(@src());
6209 defer tracy.end();6258 defer tracy.end();
62106259
...@@ -6271,7 +6320,7 @@ fn zirCUndef(...@@ -6271,7 +6320,7 @@ fn zirCUndef(
6271 sema: *Sema,6320 sema: *Sema,
6272 block: *Scope.Block,6321 block: *Scope.Block,
6273 extended: Zir.Inst.Extended.InstData,6322 extended: Zir.Inst.Extended.InstData,
6274) InnerError!*Inst {6323) CompileError!Air.Inst.Ref {
6275 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;6324 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
6276 const src: LazySrcLoc = .{ .node_offset = extra.node };6325 const src: LazySrcLoc = .{ .node_offset = extra.node };
6277 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCUndef", .{});6326 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCUndef", .{});
...@@ -6281,7 +6330,7 @@ fn zirCInclude(...@@ -6281,7 +6330,7 @@ fn zirCInclude(
6281 sema: *Sema,6330 sema: *Sema,
6282 block: *Scope.Block,6331 block: *Scope.Block,
6283 extended: Zir.Inst.Extended.InstData,6332 extended: Zir.Inst.Extended.InstData,
6284) InnerError!*Inst {6333) CompileError!Air.Inst.Ref {
6285 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;6334 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
6286 const src: LazySrcLoc = .{ .node_offset = extra.node };6335 const src: LazySrcLoc = .{ .node_offset = extra.node };
6287 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCInclude", .{});6336 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCInclude", .{});
...@@ -6291,7 +6340,7 @@ fn zirCDefine(...@@ -6291,7 +6340,7 @@ fn zirCDefine(
6291 sema: *Sema,6340 sema: *Sema,
6292 block: *Scope.Block,6341 block: *Scope.Block,
6293 extended: Zir.Inst.Extended.InstData,6342 extended: Zir.Inst.Extended.InstData,
6294) InnerError!*Inst {6343) CompileError!Air.Inst.Ref {
6295 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;6344 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
6296 const src: LazySrcLoc = .{ .node_offset = extra.node };6345 const src: LazySrcLoc = .{ .node_offset = extra.node };
6297 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCDefine", .{});6346 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCDefine", .{});
...@@ -6301,7 +6350,7 @@ fn zirWasmMemorySize(...@@ -6301,7 +6350,7 @@ fn zirWasmMemorySize(
6301 sema: *Sema,6350 sema: *Sema,
6302 block: *Scope.Block,6351 block: *Scope.Block,
6303 extended: Zir.Inst.Extended.InstData,6352 extended: Zir.Inst.Extended.InstData,
6304) InnerError!*Inst {6353) CompileError!Air.Inst.Ref {
6305 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;6354 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
6306 const src: LazySrcLoc = .{ .node_offset = extra.node };6355 const src: LazySrcLoc = .{ .node_offset = extra.node };
6307 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirWasmMemorySize", .{});6356 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirWasmMemorySize", .{});
...@@ -6311,7 +6360,7 @@ fn zirWasmMemoryGrow(...@@ -6311,7 +6360,7 @@ fn zirWasmMemoryGrow(
6311 sema: *Sema,6360 sema: *Sema,
6312 block: *Scope.Block,6361 block: *Scope.Block,
6313 extended: Zir.Inst.Extended.InstData,6362 extended: Zir.Inst.Extended.InstData,
6314) InnerError!*Inst {6363) CompileError!Air.Inst.Ref {
6315 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;6364 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
6316 const src: LazySrcLoc = .{ .node_offset = extra.node };6365 const src: LazySrcLoc = .{ .node_offset = extra.node };
6317 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});6366 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});
...@@ -6321,7 +6370,7 @@ fn zirBuiltinExtern(...@@ -6321,7 +6370,7 @@ fn zirBuiltinExtern(
6321 sema: *Sema,6370 sema: *Sema,
6322 block: *Scope.Block,6371 block: *Scope.Block,
6323 extended: Zir.Inst.Extended.InstData,6372 extended: Zir.Inst.Extended.InstData,
6324) InnerError!*Inst {6373) CompileError!Air.Inst.Ref {
6325 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;6374 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
6326 const src: LazySrcLoc = .{ .node_offset = extra.node };6375 const src: LazySrcLoc = .{ .node_offset = extra.node };
6327 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinExtern", .{});6376 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinExtern", .{});
...@@ -6355,32 +6404,13 @@ pub const PanicId = enum {...@@ -6355,32 +6404,13 @@ pub const PanicId = enum {
6355 invalid_error_code,6404 invalid_error_code,
6356};6405};
63576406
6358fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {6407fn addSafetyCheck(
6359 const block_inst = try sema.arena.create(Inst.Block);6408 sema: *Sema,
6360 block_inst.* = .{6409 parent_block: *Scope.Block,
6361 .base = .{6410 ok: Air.Inst.Ref,
6362 .tag = Inst.Block.base_tag,6411 panic_id: PanicId,
6363 .ty = Type.initTag(.void),6412) !void {
6364 .src = ok.src,6413 const gpa = sema.gpa;
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;
63846414
6385 var fail_block: Scope.Block = .{6415 var fail_block: Scope.Block = .{
6386 .parent = parent_block,6416 .parent = parent_block,
...@@ -6391,33 +6421,62 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:...@@ -6391,33 +6421,62 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
6391 .is_comptime = parent_block.is_comptime,6421 .is_comptime = parent_block.is_comptime,
6392 };6422 };
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);6430 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
6401 condbr.* = .{6431 1 + // The main block only needs space for the cond_br.
6402 .base = .{6432 @typeInfo(Air.CondBr).Struct.fields.len +
6403 .tag = .condbr,6433 1 + // The ok branch of the cond_br only needs space for the br.
6404 .ty = Type.initTag(.noreturn),6434 fail_block.instructions.items.len);
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;
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);
6414}6473}
64156474
6416fn panicWithMsg(6475fn panicWithMsg(
6417 sema: *Sema,6476 sema: *Sema,
6418 block: *Scope.Block,6477 block: *Scope.Block,
6419 src: LazySrcLoc,6478 src: LazySrcLoc,
6420 msg_inst: *ir.Inst,6479 msg_inst: Air.Inst.Ref,
6421) !Zir.Inst.Index {6480) !Zir.Inst.Index {
6422 const mod = sema.mod;6481 const mod = sema.mod;
6423 const arena = sema.arena;6482 const arena = sema.arena;
...@@ -6426,19 +6485,19 @@ fn panicWithMsg(...@@ -6426,19 +6485,19 @@ fn panicWithMsg(
6426 mod.comp.bin_file.options.object_format == .c;6485 mod.comp.bin_file.options.object_format == .c;
6427 if (!this_feature_is_implemented_in_the_backend) {6486 if (!this_feature_is_implemented_in_the_backend) {
6428 // TODO implement this feature in all the backends and then delete this branch6487 // TODO implement this feature in all the backends and then delete this branch
6429 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);6488 _ = try block.addNoOp(.breakpoint);
6430 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);6489 _ = try block.addNoOp(.unreach);
6431 return always_noreturn;6490 return always_noreturn;
6432 }6491 }
6433 const panic_fn = try sema.getBuiltin(block, src, "panic");6492 const panic_fn = try sema.getBuiltin(block, src, "panic");
6434 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");6493 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
6435 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);6494 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);6495 const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One);
6437 const null_stack_trace = try mod.constInst(arena, src, .{6496 const null_stack_trace = try sema.addConstant(
6438 .ty = try mod.optionalType(arena, ptr_stack_trace_ty),6497 try mod.optionalType(arena, ptr_stack_trace_ty),
6439 .val = Value.initTag(.null_value),6498 Value.initTag(.null_value),
6440 });6499 );
6441 const args = try arena.create([2]*ir.Inst);6500 const args = try arena.create([2]Air.Inst.Ref);
6442 args.* = .{ msg_inst, null_stack_trace };6501 args.* = .{ msg_inst, null_stack_trace };
6443 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);6502 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
6444 return always_noreturn;6503 return always_noreturn;
...@@ -6478,7 +6537,6 @@ fn safetyPanic(...@@ -6478,7 +6537,6 @@ fn safetyPanic(
6478 };6537 };
64796538
6480 const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);6539 const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);
6481
6482 return sema.panicWithMsg(block, src, casted_msg_inst);6540 return sema.panicWithMsg(block, src, casted_msg_inst);
6483}6541}
64846542
...@@ -6494,27 +6552,29 @@ fn namedFieldPtr(...@@ -6494,27 +6552,29 @@ fn namedFieldPtr(
6494 sema: *Sema,6552 sema: *Sema,
6495 block: *Scope.Block,6553 block: *Scope.Block,
6496 src: LazySrcLoc,6554 src: LazySrcLoc,
6497 object_ptr: *Inst,6555 object_ptr: Air.Inst.Ref,
6498 field_name: []const u8,6556 field_name: []const u8,
6499 field_name_src: LazySrcLoc,6557 field_name_src: LazySrcLoc,
6500) InnerError!*Inst {6558) CompileError!Air.Inst.Ref {
6501 const mod = sema.mod;6559 const mod = sema.mod;
6502 const arena = sema.arena;6560 const arena = sema.arena;
65036561
6504 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {6562 const object_ptr_src = src; // TODO better source location
6505 .Pointer => object_ptr.ty.elemType(),6563 const object_ptr_ty = sema.typeOf(object_ptr);
6506 else => return mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),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}),
6507 };6567 };
6508 switch (elem_ty.zigTypeTag()) {6568 switch (elem_ty.zigTypeTag()) {
6509 .Array => {6569 .Array => {
6510 if (mem.eql(u8, field_name, "len")) {6570 if (mem.eql(u8, field_name, "len")) {
6511 return mod.constInst(arena, src, .{6571 return sema.addConstant(
6512 .ty = Type.initTag(.single_const_pointer_to_comptime_int),6572 Type.initTag(.single_const_pointer_to_comptime_int),
6513 .val = try Value.Tag.ref_val.create(6573 try Value.Tag.ref_val.create(
6514 arena,6574 arena,
6515 try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),6575 try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),
6516 ),6576 ),
6517 });6577 );
6518 } else {6578 } else {
6519 return mod.fail(6579 return mod.fail(
6520 &block.base,6580 &block.base,
...@@ -6529,13 +6589,13 @@ fn namedFieldPtr(...@@ -6529,13 +6589,13 @@ fn namedFieldPtr(
6529 switch (ptr_child.zigTypeTag()) {6589 switch (ptr_child.zigTypeTag()) {
6530 .Array => {6590 .Array => {
6531 if (mem.eql(u8, field_name, "len")) {6591 if (mem.eql(u8, field_name, "len")) {
6532 return mod.constInst(arena, src, .{6592 return sema.addConstant(
6533 .ty = Type.initTag(.single_const_pointer_to_comptime_int),6593 Type.initTag(.single_const_pointer_to_comptime_int),
6534 .val = try Value.Tag.ref_val.create(6594 try Value.Tag.ref_val.create(
6535 arena,6595 arena,
6536 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),6596 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
6537 ),6597 ),
6538 });6598 );
6539 } else {6599 } else {
6540 return mod.fail(6600 return mod.fail(
6541 &block.base,6601 &block.base,
...@@ -6549,9 +6609,9 @@ fn namedFieldPtr(...@@ -6549,9 +6609,9 @@ fn namedFieldPtr(
6549 }6609 }
6550 },6610 },
6551 .Type => {6611 .Type => {
6552 _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);6612 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
6553 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);6613 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
6554 const val = result.value().?;6614 const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?;
6555 const child_type = try val.toType(arena);6615 const child_type = try val.toType(arena);
6556 switch (child_type.zigTypeTag()) {6616 switch (child_type.zigTypeTag()) {
6557 .ErrorSet => {6617 .ErrorSet => {
...@@ -6572,15 +6632,15 @@ fn namedFieldPtr(...@@ -6572,15 +6632,15 @@ fn namedFieldPtr(
6572 });6632 });
6573 } else (try mod.getErrorValue(field_name)).key;6633 } else (try mod.getErrorValue(field_name)).key;
65746634
6575 return mod.constInst(arena, src, .{6635 return sema.addConstant(
6576 .ty = try mod.simplePtrType(arena, child_type, false, .One),6636 try Module.simplePtrType(arena, child_type, false, .One),
6577 .val = try Value.Tag.ref_val.create(6637 try Value.Tag.ref_val.create(
6578 arena,6638 arena,
6579 try Value.Tag.@"error".create(arena, .{6639 try Value.Tag.@"error".create(arena, .{
6580 .name = name,6640 .name = name,
6581 }),6641 }),
6582 ),6642 ),
6583 });6643 );
6584 },6644 },
6585 .Struct, .Opaque, .Union => {6645 .Struct, .Opaque, .Union => {
6586 if (child_type.getNamespace()) |namespace| {6646 if (child_type.getNamespace()) |namespace| {
...@@ -6626,10 +6686,10 @@ fn namedFieldPtr(...@@ -6626,10 +6686,10 @@ fn namedFieldPtr(
6626 };6686 };
6627 const field_index_u32 = @intCast(u32, field_index);6687 const field_index_u32 = @intCast(u32, field_index);
6628 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);6688 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
6629 return mod.constInst(arena, src, .{6689 return sema.addConstant(
6630 .ty = try mod.simplePtrType(arena, child_type, false, .One),6690 try Module.simplePtrType(arena, child_type, false, .One),
6631 .val = try Value.Tag.ref_val.create(arena, enum_val),6691 try Value.Tag.ref_val.create(arena, enum_val),
6632 });6692 );
6633 },6693 },
6634 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),6694 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
6635 }6695 }
...@@ -6647,7 +6707,7 @@ fn analyzeNamespaceLookup(...@@ -6647,7 +6707,7 @@ fn analyzeNamespaceLookup(
6647 src: LazySrcLoc,6707 src: LazySrcLoc,
6648 namespace: *Scope.Namespace,6708 namespace: *Scope.Namespace,
6649 decl_name: []const u8,6709 decl_name: []const u8,
6650) InnerError!?*Inst {6710) CompileError!?Air.Inst.Ref {
6651 const mod = sema.mod;6711 const mod = sema.mod;
6652 const gpa = sema.gpa;6712 const gpa = sema.gpa;
6653 if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {6713 if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
...@@ -6671,12 +6731,11 @@ fn analyzeStructFieldPtr(...@@ -6671,12 +6731,11 @@ fn analyzeStructFieldPtr(
6671 sema: *Sema,6731 sema: *Sema,
6672 block: *Scope.Block,6732 block: *Scope.Block,
6673 src: LazySrcLoc,6733 src: LazySrcLoc,
6674 struct_ptr: *Inst,6734 struct_ptr: Air.Inst.Ref,
6675 field_name: []const u8,6735 field_name: []const u8,
6676 field_name_src: LazySrcLoc,6736 field_name_src: LazySrcLoc,
6677 unresolved_struct_ty: Type,6737 unresolved_struct_ty: Type,
6678) InnerError!*Inst {6738) CompileError!Air.Inst.Ref {
6679 const mod = sema.mod;
6680 const arena = sema.arena;6739 const arena = sema.arena;
6681 assert(unresolved_struct_ty.zigTypeTag() == .Struct);6740 assert(unresolved_struct_ty.zigTypeTag() == .Struct);
66826741
...@@ -6686,31 +6745,40 @@ fn analyzeStructFieldPtr(...@@ -6686,31 +6745,40 @@ fn analyzeStructFieldPtr(
6686 const field_index = struct_obj.fields.getIndex(field_name) orelse6745 const field_index = struct_obj.fields.getIndex(field_name) orelse
6687 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);6746 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
6688 const field = struct_obj.fields.values()[field_index];6747 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
6691 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {6750 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
6692 return mod.constInst(arena, src, .{6751 return sema.addConstant(
6693 .ty = ptr_field_ty,6752 ptr_field_ty,
6694 .val = try Value.Tag.field_ptr.create(arena, .{6753 try Value.Tag.field_ptr.create(arena, .{
6695 .container_ptr = struct_ptr_val,6754 .container_ptr = struct_ptr_val,
6696 .field_index = field_index,6755 .field_index = field_index,
6697 }),6756 }),
6698 });6757 );
6699 }6758 }
67006759
6701 try sema.requireRuntimeBlock(block, src);6760 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 });
6703}6771}
67046772
6705fn analyzeUnionFieldPtr(6773fn analyzeUnionFieldPtr(
6706 sema: *Sema,6774 sema: *Sema,
6707 block: *Scope.Block,6775 block: *Scope.Block,
6708 src: LazySrcLoc,6776 src: LazySrcLoc,
6709 union_ptr: *Inst,6777 union_ptr: Air.Inst.Ref,
6710 field_name: []const u8,6778 field_name: []const u8,
6711 field_name_src: LazySrcLoc,6779 field_name_src: LazySrcLoc,
6712 unresolved_union_ty: Type,6780 unresolved_union_ty: Type,
6713) InnerError!*Inst {6781) CompileError!Air.Inst.Ref {
6714 const mod = sema.mod;6782 const mod = sema.mod;
6715 const arena = sema.arena;6783 const arena = sema.arena;
6716 assert(unresolved_union_ty.zigTypeTag() == .Union);6784 assert(unresolved_union_ty.zigTypeTag() == .Union);
...@@ -6722,17 +6790,17 @@ fn analyzeUnionFieldPtr(...@@ -6722,17 +6790,17 @@ fn analyzeUnionFieldPtr(
6722 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);6790 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
67236791
6724 const field = union_obj.fields.values()[field_index];6792 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
6727 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {6795 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
6728 // TODO detect inactive union field and emit compile error6796 // TODO detect inactive union field and emit compile error
6729 return mod.constInst(arena, src, .{6797 return sema.addConstant(
6730 .ty = ptr_field_ty,6798 ptr_field_ty,
6731 .val = try Value.Tag.field_ptr.create(arena, .{6799 try Value.Tag.field_ptr.create(arena, .{
6732 .container_ptr = union_ptr_val,6800 .container_ptr = union_ptr_val,
6733 .field_index = field_index,6801 .field_index = field_index,
6734 }),6802 }),
6735 });6803 );
6736 }6804 }
67376805
6738 try sema.requireRuntimeBlock(block, src);6806 try sema.requireRuntimeBlock(block, src);
...@@ -6743,20 +6811,22 @@ fn elemPtr(...@@ -6743,20 +6811,22 @@ fn elemPtr(
6743 sema: *Sema,6811 sema: *Sema,
6744 block: *Scope.Block,6812 block: *Scope.Block,
6745 src: LazySrcLoc,6813 src: LazySrcLoc,
6746 array_ptr: *Inst,6814 array_ptr: Air.Inst.Ref,
6747 elem_index: *Inst,6815 elem_index: Air.Inst.Ref,
6748 elem_index_src: LazySrcLoc,6816 elem_index_src: LazySrcLoc,
6749) InnerError!*Inst {6817) CompileError!Air.Inst.Ref {
6750 const array_ty = switch (array_ptr.ty.zigTypeTag()) {6818 const array_ptr_src = src; // TODO better source location
6751 .Pointer => array_ptr.ty.elemType(),6819 const array_ptr_ty = sema.typeOf(array_ptr);
6752 else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),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}),
6753 };6823 };
6754 if (!array_ty.isIndexable()) {6824 if (!array_ty.isIndexable()) {
6755 return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{array_ty});6825 return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{array_ty});
6756 }6826 }
6757 if (array_ty.isSinglePointer() and array_ty.elemType().zigTypeTag() == .Array) {6827 if (array_ty.isSinglePointer() and array_ty.elemType().zigTypeTag() == .Array) {
6758 // we have to deref the ptr operand to get the actual array pointer6828 // 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);
6760 return sema.elemPtrArray(block, src, array_ptr_deref, elem_index, elem_index_src);6830 return sema.elemPtrArray(block, src, array_ptr_deref, elem_index, elem_index_src);
6761 }6831 }
6762 if (array_ty.zigTypeTag() == .Array) {6832 if (array_ty.zigTypeTag() == .Array) {
...@@ -6770,23 +6840,23 @@ fn elemPtrArray(...@@ -6770,23 +6840,23 @@ fn elemPtrArray(
6770 sema: *Sema,6840 sema: *Sema,
6771 block: *Scope.Block,6841 block: *Scope.Block,
6772 src: LazySrcLoc,6842 src: LazySrcLoc,
6773 array_ptr: *Inst,6843 array_ptr: Air.Inst.Ref,
6774 elem_index: *Inst,6844 elem_index: Air.Inst.Ref,
6775 elem_index_src: LazySrcLoc,6845 elem_index_src: LazySrcLoc,
6776) InnerError!*Inst {6846) CompileError!Air.Inst.Ref {
6777 if (array_ptr.value()) |array_ptr_val| {6847 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
6778 if (elem_index.value()) |index_val| {6848 if (try sema.resolveDefinedValue(block, src, elem_index)) |index_val| {
6779 // Both array pointer and index are compile-time known.6849 // Both array pointer and index are compile-time known.
6780 const index_u64 = index_val.toUnsignedInt();6850 const index_u64 = index_val.toUnsignedInt();
6781 // @intCast here because it would have been impossible to construct a value that6851 // @intCast here because it would have been impossible to construct a value that
6782 // required a larger index.6852 // required a larger index.
6783 const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));6853 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, .{6856 return sema.addConstant(
6787 .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),6857 try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),
6788 .val = elem_ptr,6858 elem_ptr,
6789 });6859 );
6790 }6860 }
6791 }6861 }
6792 _ = elem_index;6862 _ = elem_index;
...@@ -6798,39 +6868,41 @@ fn coerce(...@@ -6798,39 +6868,41 @@ fn coerce(
6798 sema: *Sema,6868 sema: *Sema,
6799 block: *Scope.Block,6869 block: *Scope.Block,
6800 dest_type: Type,6870 dest_type: Type,
6801 inst: *Inst,6871 inst: Air.Inst.Ref,
6802 inst_src: LazySrcLoc,6872 inst_src: LazySrcLoc,
6803) InnerError!*Inst {6873) CompileError!Air.Inst.Ref {
6804 if (dest_type.tag() == .var_args_param) {6874 if (dest_type.tag() == .var_args_param) {
6805 return sema.coerceVarArgParam(block, inst);6875 return sema.coerceVarArgParam(block, inst, inst_src);
6806 }6876 }
6877
6878 const inst_ty = sema.typeOf(inst);
6807 // If the types are the same, we can return the operand.6879 // 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))
6809 return inst;6881 return inst;
68106882
6811 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);6883 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty);
6812 if (in_memory_result == .ok) {6884 if (in_memory_result == .ok) {
6813 return sema.bitcast(block, dest_type, inst);6885 return sema.bitcast(block, dest_type, inst, inst_src);
6814 }6886 }
68156887
6816 const mod = sema.mod;6888 const mod = sema.mod;
6817 const arena = sema.arena;6889 const arena = sema.arena;
68186890
6819 // undefined to anything6891 // undefined to anything
6820 if (inst.value()) |val| {6892 if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| {
6821 if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {6893 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
6822 return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = val });6894 return sema.addConstant(dest_type, val);
6823 }6895 }
6824 }6896 }
6825 assert(inst.ty.zigTypeTag() != .Undefined);6897 assert(inst_ty.zigTypeTag() != .Undefined);
68266898
6827 // T to E!T or E to E!T6899 // T to E!T or E to E!T
6828 if (dest_type.tag() == .error_union) {6900 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);
6830 }6902 }
68316903
6832 // comptime known number to other number6904 // 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|
6834 return some;6906 return some;
68356907
6836 const target = mod.getTarget();6908 const target = mod.getTarget();
...@@ -6838,28 +6910,28 @@ fn coerce(...@@ -6838,28 +6910,28 @@ fn coerce(
6838 switch (dest_type.zigTypeTag()) {6910 switch (dest_type.zigTypeTag()) {
6839 .Optional => {6911 .Optional => {
6840 // null to ?T6912 // null to ?T
6841 if (inst.ty.zigTypeTag() == .Null) {6913 if (inst_ty.zigTypeTag() == .Null) {
6842 return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });6914 return sema.addConstant(dest_type, Value.initTag(.null_value));
6843 }6915 }
68446916
6845 // T to ?T6917 // T to ?T
6846 var buf: Type.Payload.ElemType = undefined;6918 var buf: Type.Payload.ElemType = undefined;
6847 const child_type = dest_type.optionalChild(&buf);6919 const child_type = dest_type.optionalChild(&buf);
6848 if (child_type.eql(inst.ty)) {6920 if (child_type.eql(inst_ty)) {
6849 return sema.wrapOptional(block, dest_type, inst);6921 return sema.wrapOptional(block, dest_type, inst, inst_src);
6850 } else if (try sema.coerceNum(block, child_type, inst)) |some| {6922 } else if (try sema.coerceNum(block, child_type, inst, inst_src)) |some| {
6851 return sema.wrapOptional(block, dest_type, some);6923 return sema.wrapOptional(block, dest_type, some, inst_src);
6852 }6924 }
6853 },6925 },
6854 .Pointer => {6926 .Pointer => {
6855 // Coercions where the source is a single pointer to an array.6927 // Coercions where the source is a single pointer to an array.
6856 src_array_ptr: {6928 src_array_ptr: {
6857 if (!inst.ty.isSinglePointer()) break :src_array_ptr;6929 if (!inst_ty.isSinglePointer()) break :src_array_ptr;
6858 const array_type = inst.ty.elemType();6930 const array_type = inst_ty.elemType();
6859 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;6931 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
6860 const array_elem_type = array_type.elemType();6932 const array_elem_type = array_type.elemType();
6861 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;6933 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;6934 if (inst_ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
68636935
6864 const dst_elem_type = dest_type.elemType();6936 const dst_elem_type = dest_type.elemType();
6865 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {6937 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
...@@ -6870,11 +6942,11 @@ fn coerce(...@@ -6870,11 +6942,11 @@ fn coerce(
6870 switch (dest_type.ptrSize()) {6942 switch (dest_type.ptrSize()) {
6871 .Slice => {6943 .Slice => {
6872 // *[N]T to []T6944 // *[N]T to []T
6873 return sema.coerceArrayPtrToSlice(block, dest_type, inst);6945 return sema.coerceArrayPtrToSlice(block, dest_type, inst, inst_src);
6874 },6946 },
6875 .C => {6947 .C => {
6876 // *[N]T to [*c]T6948 // *[N]T to [*c]T
6877 return sema.coerceArrayPtrToMany(block, dest_type, inst);6949 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
6878 },6950 },
6879 .Many => {6951 .Many => {
6880 // *[N]T to [*]T6952 // *[N]T to [*]T
...@@ -6882,12 +6954,12 @@ fn coerce(...@@ -6882,12 +6954,12 @@ fn coerce(
6882 const src_sentinel = array_type.sentinel();6954 const src_sentinel = array_type.sentinel();
6883 const dst_sentinel = dest_type.sentinel();6955 const dst_sentinel = dest_type.sentinel();
6884 if (src_sentinel == null and dst_sentinel == null)6956 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
6887 if (src_sentinel) |src_s| {6959 if (src_sentinel) |src_s| {
6888 if (dst_sentinel) |dst_s| {6960 if (dst_sentinel) |dst_s| {
6889 if (src_s.eql(dst_s)) {6961 if (src_s.eql(dst_s)) {
6890 return sema.coerceArrayPtrToMany(block, dest_type, inst);6962 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
6891 }6963 }
6892 }6964 }
6893 }6965 }
...@@ -6898,36 +6970,36 @@ fn coerce(...@@ -6898,36 +6970,36 @@ fn coerce(
6898 },6970 },
6899 .Int => {6971 .Int => {
6900 // integer widening6972 // integer widening
6901 if (inst.ty.zigTypeTag() == .Int) {6973 if (inst_ty.zigTypeTag() == .Int) {
6902 assert(inst.value() == null); // handled above6974 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
69036975
6904 const dst_info = dest_type.intInfo(target);6976 const dst_info = dest_type.intInfo(target);
6905 const src_info = inst.ty.intInfo(target);6977 const src_info = inst_ty.intInfo(target);
6906 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or6978 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
6907 // small enough unsigned ints can get casted to large enough signed ints6979 // small enough unsigned ints can get casted to large enough signed ints
6908 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))6980 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
6909 {6981 {
6910 try sema.requireRuntimeBlock(block, inst_src);6982 try sema.requireRuntimeBlock(block, inst_src);
6911 return block.addUnOp(inst_src, dest_type, .intcast, inst);6983 return block.addTyOp(.intcast, dest_type, inst);
6912 }6984 }
6913 }6985 }
6914 },6986 },
6915 .Float => {6987 .Float => {
6916 // float widening6988 // float widening
6917 if (inst.ty.zigTypeTag() == .Float) {6989 if (inst_ty.zigTypeTag() == .Float) {
6918 assert(inst.value() == null); // handled above6990 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);
6921 const dst_bits = dest_type.floatBits(target);6993 const dst_bits = dest_type.floatBits(target);
6922 if (dst_bits >= src_bits) {6994 if (dst_bits >= src_bits) {
6923 try sema.requireRuntimeBlock(block, inst_src);6995 try sema.requireRuntimeBlock(block, inst_src);
6924 return block.addUnOp(inst_src, dest_type, .floatcast, inst);6996 return block.addTyOp(.floatcast, dest_type, inst);
6925 }6997 }
6926 }6998 }
6927 },6999 },
6928 .Enum => {7000 .Enum => {
6929 // enum literal to enum7001 // enum literal to enum
6930 if (inst.ty.zigTypeTag() == .EnumLiteral) {7002 if (inst_ty.zigTypeTag() == .EnumLiteral) {
6931 const val = try sema.resolveConstValue(block, inst_src, inst);7003 const val = try sema.resolveConstValue(block, inst_src, inst);
6932 const bytes = val.castTag(.enum_literal).?.data;7004 const bytes = val.castTag(.enum_literal).?.data;
6933 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);7005 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);
...@@ -6950,16 +7022,16 @@ fn coerce(...@@ -6950,16 +7022,16 @@ fn coerce(
6950 };7022 };
6951 return mod.failWithOwnedErrorMsg(&block.base, msg);7023 return mod.failWithOwnedErrorMsg(&block.base, msg);
6952 };7024 };
6953 return mod.constInst(arena, inst_src, .{7025 return sema.addConstant(
6954 .ty = resolved_dest_type,7026 resolved_dest_type,
6955 .val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),7027 try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
6956 });7028 );
6957 }7029 }
6958 },7030 },
6959 else => {},7031 else => {},
6960 }7032 }
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 });
6963}7035}
69647036
6965const InMemoryCoercionResult = enum {7037const InMemoryCoercionResult = enum {
...@@ -6976,9 +7048,16 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult...@@ -6976,9 +7048,16 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult
6976 return .no_match;7048 return .no_match;
6977}7049}
69787050
6979fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst {7051fn coerceNum(
6980 const val = inst.value() orelse return null;7052 sema: *Sema,
6981 const src_zig_tag = inst.ty.zigTypeTag();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();
6982 const dst_zig_tag = dest_type.zigTypeTag();7061 const dst_zig_tag = dest_type.zigTypeTag();
69837062
6984 const target = sema.mod.getTarget();7063 const target = sema.mod.getTarget();
...@@ -6986,37 +7065,43 @@ fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) Inn...@@ -6986,37 +7065,43 @@ fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) Inn
6986 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {7065 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
6987 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {7066 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
6988 if (val.floatHasFraction()) {7067 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 });
6990 }7069 }
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", .{});
6992 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {7071 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
6993 if (!val.intFitsInType(dest_type, target)) {7072 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 });
6995 }7074 }
6996 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });7075 return try sema.addConstant(dest_type, val);
6997 }7076 }
6998 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {7077 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
6999 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {7078 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
7000 const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) {7079 const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) {
7001 error.Overflow => return sema.mod.fail(7080 error.Overflow => return sema.mod.fail(
7002 &block.base,7081 &block.base,
7003 inst.src,7082 inst_src,
7004 "cast of value {} to type '{}' loses information",7083 "cast of value {} to type '{}' loses information",
7005 .{ val, dest_type },7084 .{ val, dest_type },
7006 ),7085 ),
7007 error.OutOfMemory => return error.OutOfMemory,7086 error.OutOfMemory => return error.OutOfMemory,
7008 };7087 };
7009 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = res });7088 return try sema.addConstant(dest_type, res);
7010 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {7089 } 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", .{});
7012 }7091 }
7013 }7092 }
7014 return null;7093 return null;
7015}7094}
70167095
7017fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst {7096fn coerceVarArgParam(
7018 switch (inst.ty.zigTypeTag()) {7097 sema: *Sema,
7019 .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}),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", .{}),
7020 else => {},7105 else => {},
7021 }7106 }
7022 // TODO implement more of this function.7107 // TODO implement more of this function.
...@@ -7027,13 +7112,14 @@ fn storePtr(...@@ -7027,13 +7112,14 @@ fn storePtr(
7027 sema: *Sema,7112 sema: *Sema,
7028 block: *Scope.Block,7113 block: *Scope.Block,
7029 src: LazySrcLoc,7114 src: LazySrcLoc,
7030 ptr: *Inst,7115 ptr: Air.Inst.Ref,
7031 uncasted_value: *Inst,7116 uncasted_value: Air.Inst.Ref,
7032) !void {7117) !void {
7033 if (ptr.ty.isConstPtr())7118 const ptr_ty = sema.typeOf(ptr);
7119 if (ptr_ty.isConstPtr())
7034 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});7120 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();
7037 const value = try sema.coerce(block, elem_ty, uncasted_value, src);7123 const value = try sema.coerce(block, elem_ty, uncasted_value, src);
7038 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)7124 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
7039 return;7125 return;
...@@ -7073,41 +7159,73 @@ fn storePtr(...@@ -7073,41 +7159,73 @@ fn storePtr(
7073 // TODO handle if the element type requires comptime7159 // TODO handle if the element type requires comptime
70747160
7075 try sema.requireRuntimeBlock(block, src);7161 try sema.requireRuntimeBlock(block, src);
7076 _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value);7162 _ = try block.addBinOp(.store, ptr, value);
7077}7163}
70787164
7079fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {7165fn bitcast(
7080 if (inst.value()) |val| {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| {
7081 // Keep the comptime Value representation; take the new type.7173 // 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);
7083 }7175 }
7084 // TODO validate the type size and other compile errors7176 // TODO validate the type size and other compile errors
7085 try sema.requireRuntimeBlock(block, inst.src);7177 try sema.requireRuntimeBlock(block, inst_src);
7086 return block.addUnOp(inst.src, dest_type, .bitcast, inst);7178 return block.addTyOp(.bitcast, dest_type, inst);
7087}7179}
70887180
7089fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {7181fn coerceArrayPtrToSlice(
7090 if (inst.value()) |val| {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| {
7091 // The comptime Value representation is compatible with both types.7189 // 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);
7093 }7191 }
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", .{});
7095}7193}
70967194
7097fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {7195fn coerceArrayPtrToMany(
7098 if (inst.value()) |val| {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| {
7099 // The comptime Value representation is compatible with both types.7203 // 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);
7101 }7205 }
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", .{});
7103}7207}
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 }
7106 const decl_ref = try sema.analyzeDeclRef(block, src, decl);7218 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;
7108}7226}
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 {
7111 try sema.mod.declareDeclDependency(sema.owner_decl, decl);7229 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
7112 sema.mod.ensureDeclAnalyzed(decl) catch |err| {7230 sema.mod.ensureDeclAnalyzed(decl) catch |err| {
7113 if (sema.func) |func| {7231 if (sema.func) |func| {
...@@ -7122,136 +7240,140 @@ fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl...@@ -7122,136 +7240,140 @@ fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl
7122 if (decl_tv.val.tag() == .variable) {7240 if (decl_tv.val.tag() == .variable) {
7123 return sema.analyzeVarRef(block, src, decl_tv);7241 return sema.analyzeVarRef(block, src, decl_tv);
7124 }7242 }
7125 return sema.mod.constInst(sema.arena, src, .{7243 return sema.addConstant(
7126 .ty = try sema.mod.simplePtrType(sema.arena, decl_tv.ty, false, .One),7244 try Module.simplePtrType(sema.arena, decl_tv.ty, false, .One),
7127 .val = try Value.Tag.decl_ref.create(sema.arena, decl),7245 try Value.Tag.decl_ref.create(sema.arena, decl),
7128 });7246 );
7129}7247}
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 {
7132 const variable = tv.val.castTag(.variable).?.data;7250 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);
7135 if (!variable.is_mutable and !variable.is_extern) {7253 if (!variable.is_mutable and !variable.is_extern) {
7136 return sema.mod.constInst(sema.arena, src, .{7254 return sema.addConstant(ty, try Value.Tag.ref_val.create(sema.arena, variable.init));
7137 .ty = ty,
7138 .val = try Value.Tag.ref_val.create(sema.arena, variable.init),
7139 });
7140 }7255 }
71417256
7257 const gpa = sema.gpa;
7142 try sema.requireRuntimeBlock(block, src);7258 try sema.requireRuntimeBlock(block, src);
7143 const inst = try sema.arena.create(Inst.VarPtr);7259 try sema.air_variables.append(gpa, variable);
7144 inst.* = .{7260 const result_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
7145 .base = .{7261 try sema.air_instructions.append(gpa, .{
7146 .tag = .varptr,7262 .tag = .varptr,
7147 .ty = ty,7263 .data = .{ .ty_pl = .{
7148 .src = src,7264 .ty = try sema.addType(ty),
7149 },7265 .payload = @intCast(u32, sema.air_variables.items.len - 1),
7150 .variable = variable,7266 } },
7151 };7267 });
7152 try block.instructions.append(sema.gpa, &inst.base);7268 try block.instructions.append(gpa, result_inst);
7153 return &inst.base;7269 return Air.indexToRef(result_inst);
7154}7270}
71557271
7156fn analyzeRef(7272fn analyzeRef(
7157 sema: *Sema,7273 sema: *Sema,
7158 block: *Scope.Block,7274 block: *Scope.Block,
7159 src: LazySrcLoc,7275 src: LazySrcLoc,
7160 operand: *Inst,7276 operand: Air.Inst.Ref,
7161) InnerError!*Inst {7277) CompileError!Air.Inst.Ref {
7162 const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One);7278 const operand_ty = sema.typeOf(operand);
7279 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
71637280
7164 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |val| {7281 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |val| {
7165 return sema.mod.constInst(sema.arena, src, .{7282 return sema.addConstant(ptr_type, try Value.Tag.ref_val.create(sema.arena, val));
7166 .ty = ptr_type,
7167 .val = try Value.Tag.ref_val.create(sema.arena, val),
7168 });
7169 }7283 }
71707284
7171 try sema.requireRuntimeBlock(block, src);7285 try sema.requireRuntimeBlock(block, src);
7172 return block.addUnOp(src, ptr_type, .ref, operand);7286 return block.addTyOp(.ref, ptr_type, operand);
7173}7287}
71747288
7175fn analyzeLoad(7289fn analyzeLoad(
7176 sema: *Sema,7290 sema: *Sema,
7177 block: *Scope.Block,7291 block: *Scope.Block,
7178 src: LazySrcLoc,7292 src: LazySrcLoc,
7179 ptr: *Inst,7293 ptr: Air.Inst.Ref,
7180 ptr_src: LazySrcLoc,7294 ptr_src: LazySrcLoc,
7181) InnerError!*Inst {7295) CompileError!Air.Inst.Ref {
7182 const elem_ty = switch (ptr.ty.zigTypeTag()) {7296 const ptr_ty = sema.typeOf(ptr);
7183 .Pointer => ptr.ty.elemType(),7297 const elem_ty = switch (ptr_ty.zigTypeTag()) {
7184 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),7298 .Pointer => ptr_ty.elemType(),
7299 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
7185 };7300 };
7186 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {7301 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {
7187 if (ptr_val.tag() == .int_u64)7302 if (ptr_val.tag() == .int_u64)
7188 break :blk; // do it at runtime7303 break :blk; // do it at runtime
71897304
7190 return sema.mod.constInst(sema.arena, src, .{7305 return sema.addConstant(elem_ty, try ptr_val.pointerDeref(sema.arena));
7191 .ty = elem_ty,
7192 .val = try ptr_val.pointerDeref(sema.arena),
7193 });
7194 }7306 }
71957307
7196 try sema.requireRuntimeBlock(block, src);7308 try sema.requireRuntimeBlock(block, src);
7197 return block.addUnOp(src, elem_ty, .load, ptr);7309 return block.addTyOp(.load, elem_ty, ptr);
7198}7310}
71997311
7200fn analyzeIsNull(7312fn analyzeIsNull(
7201 sema: *Sema,7313 sema: *Sema,
7202 block: *Scope.Block,7314 block: *Scope.Block,
7203 src: LazySrcLoc,7315 src: LazySrcLoc,
7204 operand: *Inst,7316 operand: Air.Inst.Ref,
7205 invert_logic: bool,7317 invert_logic: bool,
7206) InnerError!*Inst {7318) CompileError!Air.Inst.Ref {
7207 const result_ty = Type.initTag(.bool);7319 const result_ty = Type.initTag(.bool);
7208 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |opt_val| {7320 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |opt_val| {
7209 if (opt_val.isUndef()) {7321 if (opt_val.isUndef()) {
7210 return sema.mod.constUndef(sema.arena, src, result_ty);7322 return sema.addConstUndef(result_ty);
7211 }7323 }
7212 const is_null = opt_val.isNull();7324 const is_null = opt_val.isNull();
7213 const bool_value = if (invert_logic) !is_null else is_null;7325 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 }
7215 }7331 }
7216 try sema.requireRuntimeBlock(block, src);7332 try sema.requireRuntimeBlock(block, src);
7217 const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;7333 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
7218 return block.addUnOp(src, result_ty, inst_tag, operand);7334 return block.addUnOp(air_tag, operand);
7219}7335}
72207336
7221fn analyzeIsNonErr(7337fn analyzeIsNonErr(
7222 sema: *Sema,7338 sema: *Sema,
7223 block: *Scope.Block,7339 block: *Scope.Block,
7224 src: LazySrcLoc,7340 src: LazySrcLoc,
7225 operand: *Inst,7341 operand: Air.Inst.Ref,
7226) InnerError!*Inst {7342) CompileError!Air.Inst.Ref {
7227 const ot = operand.ty.zigTypeTag();7343 const operand_ty = sema.typeOf(operand);
7228 if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true);7344 const ot = operand_ty.zigTypeTag();
7229 if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false);7345 if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true;
7346 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
7230 assert(ot == .ErrorUnion);7347 assert(ot == .ErrorUnion);
7231 const result_ty = Type.initTag(.bool);7348 const result_ty = Type.initTag(.bool);
7232 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {7349 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
7233 if (err_union.isUndef()) {7350 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;
7235 }7357 }
7236 return sema.mod.constBool(sema.arena, src, err_union.getError() == null);
7237 }7358 }
7238 try sema.requireRuntimeBlock(block, src);7359 try sema.requireRuntimeBlock(block, src);
7239 return block.addUnOp(src, result_ty, .is_non_err, operand);7360 return block.addUnOp(.is_non_err, operand);
7240}7361}
72417362
7242fn analyzeSlice(7363fn analyzeSlice(
7243 sema: *Sema,7364 sema: *Sema,
7244 block: *Scope.Block,7365 block: *Scope.Block,
7245 src: LazySrcLoc,7366 src: LazySrcLoc,
7246 array_ptr: *Inst,7367 array_ptr: Air.Inst.Ref,
7247 start: *Inst,7368 start: Air.Inst.Ref,
7248 end_opt: ?*Inst,7369 end_opt: Air.Inst.Ref,
7249 sentinel_opt: ?*Inst,7370 sentinel_opt: Air.Inst.Ref,
7250 sentinel_src: LazySrcLoc,7371 sentinel_src: LazySrcLoc,
7251) InnerError!*Inst {7372) CompileError!Air.Inst.Ref {
7252 const ptr_child = switch (array_ptr.ty.zigTypeTag()) {7373 const array_ptr_ty = sema.typeOf(array_ptr);
7253 .Pointer => array_ptr.ty.elemType(),7374 const ptr_child = switch (array_ptr_ty.zigTypeTag()) {
7254 else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}),7375 .Pointer => array_ptr_ty.elemType(),
7376 else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr_ty}),
7255 };7377 };
72567378
7257 var array_type = ptr_child;7379 var array_type = ptr_child;
...@@ -7271,16 +7393,16 @@ fn analyzeSlice(...@@ -7271,16 +7393,16 @@ fn analyzeSlice(
7271 else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}),7393 else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}),
7272 };7394 };
72737395
7274 const slice_sentinel = if (sentinel_opt) |sentinel| blk: {7396 const slice_sentinel = if (sentinel_opt != .none) blk: {
7275 const casted = try sema.coerce(block, elem_type, sentinel, sentinel.src);7397 const casted = try sema.coerce(block, elem_type, sentinel_opt, sentinel_src);
7276 break :blk try sema.resolveConstValue(block, sentinel_src, casted);7398 break :blk try sema.resolveConstValue(block, sentinel_src, casted);
7277 } else null;7399 } else null;
72787400
7279 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;7401 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
7280 var return_elem_type = elem_type;7402 var return_elem_type = elem_type;
7281 if (end_opt) |end| {7403 if (end_opt != .none) {
7282 if (end.value()) |end_val| {7404 if (try sema.resolveDefinedValue(block, src, end_opt)) |end_val| {
7283 if (start.value()) |start_val| {7405 if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
7284 const start_u64 = start_val.toUnsignedInt();7406 const start_u64 = start_val.toUnsignedInt();
7285 const end_u64 = end_val.toUnsignedInt();7407 const end_u64 = end_val.toUnsignedInt();
7286 if (start_u64 > end_u64) {7408 if (start_u64 > end_u64) {
...@@ -7300,7 +7422,7 @@ fn analyzeSlice(...@@ -7300,7 +7422,7 @@ fn analyzeSlice(
7300 const return_type = try sema.mod.ptrType(7422 const return_type = try sema.mod.ptrType(
7301 sema.arena,7423 sema.arena,
7302 return_elem_type,7424 return_elem_type,
7303 if (end_opt == null) slice_sentinel else null,7425 if (end_opt == .none) slice_sentinel else null,
7304 0, // TODO alignment7426 0, // TODO alignment
7305 0,7427 0,
7306 0,7428 0,
...@@ -7319,34 +7441,46 @@ fn cmpNumeric(...@@ -7319,34 +7441,46 @@ fn cmpNumeric(
7319 sema: *Sema,7441 sema: *Sema,
7320 block: *Scope.Block,7442 block: *Scope.Block,
7321 src: LazySrcLoc,7443 src: LazySrcLoc,
7322 lhs: *Inst,7444 lhs: Air.Inst.Ref,
7323 rhs: *Inst,7445 rhs: Air.Inst.Ref,
7324 op: std.math.CompareOperator,7446 op: std.math.CompareOperator,
7325) InnerError!*Inst {7447 lhs_src: LazySrcLoc,
7326 assert(lhs.ty.isNumeric());7448 rhs_src: LazySrcLoc,
7327 assert(rhs.ty.isNumeric());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();7453 assert(lhs_ty.isNumeric());
7330 const rhs_ty_tag = rhs.ty.zigTypeTag();7454 assert(rhs_ty.isNumeric());
7455
7456 const lhs_ty_tag = lhs_ty.zigTypeTag();
7457 const rhs_ty_tag = rhs_ty.zigTypeTag();
73317458
7332 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {7459 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()) {
7334 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{7461 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
7335 lhs.ty.arrayLen(),7462 lhs_ty.arrayLen(),
7336 rhs.ty.arrayLen(),7463 rhs_ty.arrayLen(),
7337 });7464 });
7338 }7465 }
7339 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{});7466 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{});
7340 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {7467 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
7341 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{7468 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
7342 lhs.ty,7469 lhs_ty,
7343 rhs.ty,7470 rhs_ty,
7344 });7471 });
7345 }7472 }
73467473
7347 if (lhs.value()) |lhs_val| {7474 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lhs_val| {
7348 if (rhs.value()) |rhs_val| {7475 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rhs_val| {
7349 return sema.mod.constBool(sema.arena, src, Value.compare(lhs_val, op, 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 }
7350 }7484 }
7351 }7485 }
73527486
...@@ -7372,19 +7506,19 @@ fn cmpNumeric(...@@ -7372,19 +7506,19 @@ fn cmpNumeric(
7372 // Implicit cast the smaller one to the larger one.7506 // Implicit cast the smaller one to the larger one.
7373 const dest_type = x: {7507 const dest_type = x: {
7374 if (lhs_ty_tag == .ComptimeFloat) {7508 if (lhs_ty_tag == .ComptimeFloat) {
7375 break :x rhs.ty;7509 break :x rhs_ty;
7376 } else if (rhs_ty_tag == .ComptimeFloat) {7510 } else if (rhs_ty_tag == .ComptimeFloat) {
7377 break :x lhs.ty;7511 break :x lhs_ty;
7378 }7512 }
7379 if (lhs.ty.floatBits(target) >= rhs.ty.floatBits(target)) {7513 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
7380 break :x lhs.ty;7514 break :x lhs_ty;
7381 } else {7515 } else {
7382 break :x rhs.ty;7516 break :x rhs_ty;
7383 }7517 }
7384 };7518 };
7385 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);7519 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);7520 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);7521 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
7388 }7522 }
7389 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.7523 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
7390 // For mixed signed and unsigned integers, implicit cast both operands to a signed7524 // For mixed signed and unsigned integers, implicit cast both operands to a signed
...@@ -7392,22 +7526,22 @@ fn cmpNumeric(...@@ -7392,22 +7526,22 @@ fn cmpNumeric(
7392 // For mixed floats and integers, extract the integer part from the float, cast that to7526 // For mixed floats and integers, extract the integer part from the float, cast that to
7393 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,7527 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
7394 // add/subtract 1.7528 // 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|
7396 lhs_val.compareWithZero(.lt)7530 lhs_val.compareWithZero(.lt)
7397 else7531 else
7398 (lhs.ty.isFloat() or lhs.ty.isSignedInt());7532 (lhs_ty.isFloat() or lhs_ty.isSignedInt());
7399 const rhs_is_signed = if (rhs.value()) |rhs_val|7533 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
7400 rhs_val.compareWithZero(.lt)7534 rhs_val.compareWithZero(.lt)
7401 else7535 else
7402 (rhs.ty.isFloat() or rhs.ty.isSignedInt());7536 (rhs_ty.isFloat() or rhs_ty.isSignedInt());
7403 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;7537 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
74047538
7405 var dest_float_type: ?Type = null;7539 var dest_float_type: ?Type = null;
74067540
7407 var lhs_bits: usize = undefined;7541 var lhs_bits: usize = undefined;
7408 if (lhs.value()) |lhs_val| {7542 if (try sema.resolvePossiblyUndefinedValue(block, lhs_src, lhs)) |lhs_val| {
7409 if (lhs_val.isUndef())7543 if (lhs_val.isUndef())
7410 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));7544 return sema.addConstUndef(Type.initTag(.bool));
7411 const is_unsigned = if (lhs_is_float) x: {7545 const is_unsigned = if (lhs_is_float) x: {
7412 var bigint_space: Value.BigIntSpace = undefined;7546 var bigint_space: Value.BigIntSpace = undefined;
7413 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);7547 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
...@@ -7415,8 +7549,8 @@ fn cmpNumeric(...@@ -7415,8 +7549,8 @@ fn cmpNumeric(
7415 const zcmp = lhs_val.orderAgainstZero();7549 const zcmp = lhs_val.orderAgainstZero();
7416 if (lhs_val.floatHasFraction()) {7550 if (lhs_val.floatHasFraction()) {
7417 switch (op) {7551 switch (op) {
7418 .eq => return sema.mod.constBool(sema.arena, src, false),7552 .eq => return Air.Inst.Ref.bool_false,
7419 .neq => return sema.mod.constBool(sema.arena, src, true),7553 .neq => return Air.Inst.Ref.bool_true,
7420 else => {},7554 else => {},
7421 }7555 }
7422 if (zcmp == .lt) {7556 if (zcmp == .lt) {
...@@ -7433,16 +7567,16 @@ fn cmpNumeric(...@@ -7433,16 +7567,16 @@ fn cmpNumeric(
7433 };7567 };
7434 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);7568 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
7435 } else if (lhs_is_float) {7569 } else if (lhs_is_float) {
7436 dest_float_type = lhs.ty;7570 dest_float_type = lhs_ty;
7437 } else {7571 } else {
7438 const int_info = lhs.ty.intInfo(target);7572 const int_info = lhs_ty.intInfo(target);
7439 lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);7573 lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
7440 }7574 }
74417575
7442 var rhs_bits: usize = undefined;7576 var rhs_bits: usize = undefined;
7443 if (rhs.value()) |rhs_val| {7577 if (try sema.resolvePossiblyUndefinedValue(block, rhs_src, rhs)) |rhs_val| {
7444 if (rhs_val.isUndef())7578 if (rhs_val.isUndef())
7445 return sema.mod.constUndef(sema.arena, src, Type.initTag(.bool));7579 return sema.addConstUndef(Type.initTag(.bool));
7446 const is_unsigned = if (rhs_is_float) x: {7580 const is_unsigned = if (rhs_is_float) x: {
7447 var bigint_space: Value.BigIntSpace = undefined;7581 var bigint_space: Value.BigIntSpace = undefined;
7448 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);7582 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
...@@ -7450,8 +7584,8 @@ fn cmpNumeric(...@@ -7450,8 +7584,8 @@ fn cmpNumeric(
7450 const zcmp = rhs_val.orderAgainstZero();7584 const zcmp = rhs_val.orderAgainstZero();
7451 if (rhs_val.floatHasFraction()) {7585 if (rhs_val.floatHasFraction()) {
7452 switch (op) {7586 switch (op) {
7453 .eq => return sema.mod.constBool(sema.arena, src, false),7587 .eq => return Air.Inst.Ref.bool_false,
7454 .neq => return sema.mod.constBool(sema.arena, src, true),7588 .neq => return Air.Inst.Ref.bool_true,
7455 else => {},7589 else => {},
7456 }7590 }
7457 if (zcmp == .lt) {7591 if (zcmp == .lt) {
...@@ -7468,9 +7602,9 @@ fn cmpNumeric(...@@ -7468,9 +7602,9 @@ fn cmpNumeric(
7468 };7602 };
7469 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);7603 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
7470 } else if (rhs_is_float) {7604 } else if (rhs_is_float) {
7471 dest_float_type = rhs.ty;7605 dest_float_type = rhs_ty;
7472 } else {7606 } else {
7473 const int_info = rhs.ty.intInfo(target);7607 const int_info = rhs_ty.intInfo(target);
7474 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);7608 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
7475 }7609 }
74767610
...@@ -7482,26 +7616,39 @@ fn cmpNumeric(...@@ -7482,26 +7616,39 @@ fn cmpNumeric(
7482 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;7616 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
7483 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);7617 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
7484 };7618 };
7485 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs.src);7619 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);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);
7489}7623}
74907624
7491fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {7625fn wrapOptional(
7492 if (inst.value()) |val| {7626 sema: *Sema,
7493 return sema.mod.constInst(sema.arena, inst.src, .{ .ty = dest_type, .val = val });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);
7494 }7634 }
74957635
7496 try sema.requireRuntimeBlock(block, inst.src);7636 try sema.requireRuntimeBlock(block, inst_src);
7497 return block.addUnOp(inst.src, dest_type, .wrap_optional, inst);7637 return block.addTyOp(.wrap_optional, dest_type, inst);
7498}7638}
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);
7501 const err_union = dest_type.castTag(.error_union).?;7648 const err_union = dest_type.castTag(.error_union).?;
7502 if (inst.value()) |val| {7649 if (try sema.resolvePossiblyUndefinedValue(block, inst_src, inst)) |val| {
7503 if (inst.ty.zigTypeTag() != .ErrorSet) {7650 if (inst_ty.zigTypeTag() != .ErrorSet) {
7504 _ = try sema.coerce(block, err_union.data.payload, inst, inst.src);7651 _ = try sema.coerce(block, err_union.data.payload, inst, inst_src);
7505 } else switch (err_union.data.error_set.tag()) {7652 } else switch (err_union.data.error_set.tag()) {
7506 .anyerror => {},7653 .anyerror => {},
7507 .error_set_single => {7654 .error_set_single => {
...@@ -7510,9 +7657,9 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst...@@ -7510,9 +7657,9 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
7510 if (!mem.eql(u8, expected_name, n)) {7657 if (!mem.eql(u8, expected_name, n)) {
7511 return sema.mod.fail(7658 return sema.mod.fail(
7512 &block.base,7659 &block.base,
7513 inst.src,7660 inst_src,
7514 "expected type '{}', found type '{}'",7661 "expected type '{}', found type '{}'",
7515 .{ err_union.data.error_set, inst.ty },7662 .{ err_union.data.error_set, inst_ty },
7516 );7663 );
7517 }7664 }
7518 },7665 },
...@@ -7528,9 +7675,9 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst...@@ -7528,9 +7675,9 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
7528 if (!found) {7675 if (!found) {
7529 return sema.mod.fail(7676 return sema.mod.fail(
7530 &block.base,7677 &block.base,
7531 inst.src,7678 inst_src,
7532 "expected type '{}', found type '{}'",7679 "expected type '{}', found type '{}'",
7533 .{ err_union.data.error_set, inst.ty },7680 .{ err_union.data.error_set, inst_ty },
7534 );7681 );
7535 }7682 }
7536 },7683 },
...@@ -7540,109 +7687,113 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst...@@ -7540,109 +7687,113 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
7540 if (!map.contains(expected_name)) {7687 if (!map.contains(expected_name)) {
7541 return sema.mod.fail(7688 return sema.mod.fail(
7542 &block.base,7689 &block.base,
7543 inst.src,7690 inst_src,
7544 "expected type '{}', found type '{}'",7691 "expected type '{}', found type '{}'",
7545 .{ err_union.data.error_set, inst.ty },7692 .{ err_union.data.error_set, inst_ty },
7546 );7693 );
7547 }7694 }
7548 },7695 },
7549 else => unreachable,7696 else => unreachable,
7550 }7697 }
75517698
7552 return sema.mod.constInst(sema.arena, inst.src, .{7699 // Create a SubValue for the error_union payload.
7553 .ty = dest_type,7700 return sema.addConstant(dest_type, try Value.Tag.error_union.create(sema.arena, val));
7554 // creating a SubValue for the error_union payload
7555 .val = try Value.Tag.error_union.create(sema.arena, val),
7556 });
7557 }7701 }
75587702
7559 try sema.requireRuntimeBlock(block, inst.src);7703 try sema.requireRuntimeBlock(block, inst_src);
75607704
7561 // we are coercing from E to E!T7705 // we are coercing from E to E!T
7562 if (inst.ty.zigTypeTag() == .ErrorSet) {7706 if (inst_ty.zigTypeTag() == .ErrorSet) {
7563 var coerced = try sema.coerce(block, err_union.data.error_set, inst, inst.src);7707 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);7708 return block.addTyOp(.wrap_errunion_err, dest_type, coerced);
7565 } else {7709 } else {
7566 var coerced = try sema.coerce(block, err_union.data.payload, inst, inst.src);7710 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);7711 return block.addTyOp(.wrap_errunion_payload, dest_type, coerced);
7568 }7712 }
7569}7713}
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 {
7572 if (instructions.len == 0)7721 if (instructions.len == 0)
7573 return Type.initTag(.noreturn);7722 return Type.initTag(.noreturn);
75747723
7575 if (instructions.len == 1)7724 if (instructions.len == 1)
7576 return instructions[0].ty;7725 return sema.typeOf(instructions[0]);
75777726
7578 const target = sema.mod.getTarget();7727 const target = sema.mod.getTarget();
75797728
7580 var chosen = instructions[0];7729 var chosen = instructions[0];
7581 for (instructions[1..]) |candidate| {7730 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))
7583 continue;7734 continue;
7584 if (candidate.ty.zigTypeTag() == .NoReturn)7735 if (candidate_ty.zigTypeTag() == .NoReturn)
7585 continue;7736 continue;
7586 if (chosen.ty.zigTypeTag() == .NoReturn) {7737 if (chosen_ty.zigTypeTag() == .NoReturn) {
7587 chosen = candidate;7738 chosen = candidate;
7588 continue;7739 continue;
7589 }7740 }
7590 if (candidate.ty.zigTypeTag() == .Undefined)7741 if (candidate_ty.zigTypeTag() == .Undefined)
7591 continue;7742 continue;
7592 if (chosen.ty.zigTypeTag() == .Undefined) {7743 if (chosen_ty.zigTypeTag() == .Undefined) {
7593 chosen = candidate;7744 chosen = candidate;
7594 continue;7745 continue;
7595 }7746 }
7596 if (chosen.ty.isInt() and7747 if (chosen_ty.isInt() and
7597 candidate.ty.isInt() and7748 candidate_ty.isInt() and
7598 chosen.ty.isSignedInt() == candidate.ty.isSignedInt())7749 chosen_ty.isSignedInt() == candidate_ty.isSignedInt())
7599 {7750 {
7600 if (chosen.ty.intInfo(target).bits < candidate.ty.intInfo(target).bits) {7751 if (chosen_ty.intInfo(target).bits < candidate_ty.intInfo(target).bits) {
7601 chosen = candidate;7752 chosen = candidate;
7602 }7753 }
7603 continue;7754 continue;
7604 }7755 }
7605 if (chosen.ty.isFloat() and candidate.ty.isFloat()) {7756 if (chosen_ty.isFloat() and candidate_ty.isFloat()) {
7606 if (chosen.ty.floatBits(target) < candidate.ty.floatBits(target)) {7757 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
7607 chosen = candidate;7758 chosen = candidate;
7608 }7759 }
7609 continue;7760 continue;
7610 }7761 }
76117762
7612 if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) {7763 if (chosen_ty.zigTypeTag() == .ComptimeInt and candidate_ty.isInt()) {
7613 chosen = candidate;7764 chosen = candidate;
7614 continue;7765 continue;
7615 }7766 }
76167767
7617 if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) {7768 if (chosen_ty.isInt() and candidate_ty.zigTypeTag() == .ComptimeInt) {
7618 continue;7769 continue;
7619 }7770 }
76207771
7621 if (chosen.ty.zigTypeTag() == .ComptimeFloat and candidate.ty.isFloat()) {7772 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {
7622 chosen = candidate;7773 chosen = candidate;
7623 continue;7774 continue;
7624 }7775 }
76257776
7626 if (chosen.ty.isFloat() and candidate.ty.zigTypeTag() == .ComptimeFloat) {7777 if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
7627 continue;7778 continue;
7628 }7779 }
76297780
7630 if (chosen.ty.zigTypeTag() == .Enum and candidate.ty.zigTypeTag() == .EnumLiteral) {7781 if (chosen_ty.zigTypeTag() == .Enum and candidate_ty.zigTypeTag() == .EnumLiteral) {
7631 continue;7782 continue;
7632 }7783 }
7633 if (chosen.ty.zigTypeTag() == .EnumLiteral and candidate.ty.zigTypeTag() == .Enum) {7784 if (chosen_ty.zigTypeTag() == .EnumLiteral and candidate_ty.zigTypeTag() == .Enum) {
7634 chosen = candidate;7785 chosen = candidate;
7635 continue;7786 continue;
7636 }7787 }
76377788
7638 // TODO error notes pointing out each type7789 // 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 });
7640 }7791 }
76417792
7642 return chosen.ty;7793 return sema.typeOf(chosen);
7643}7794}
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 {
7646 switch (ty.tag()) {7797 switch (ty.tag()) {
7647 .@"struct" => {7798 .@"struct" => {
7648 const struct_obj = ty.castTag(.@"struct").?.data;7799 const struct_obj = ty.castTag(.@"struct").?.data;
...@@ -7694,7 +7845,7 @@ fn resolveBuiltinTypeFields(...@@ -7694,7 +7845,7 @@ fn resolveBuiltinTypeFields(
7694 block: *Scope.Block,7845 block: *Scope.Block,
7695 src: LazySrcLoc,7846 src: LazySrcLoc,
7696 name: []const u8,7847 name: []const u8,
7697) InnerError!Type {7848) CompileError!Type {
7698 const resolved_ty = try sema.getBuiltinType(block, src, name);7849 const resolved_ty = try sema.getBuiltinType(block, src, name);
7699 return sema.resolveTypeFields(block, src, resolved_ty);7850 return sema.resolveTypeFields(block, src, resolved_ty);
7700}7851}
...@@ -7704,7 +7855,7 @@ fn getBuiltin(...@@ -7704,7 +7855,7 @@ fn getBuiltin(
7704 block: *Scope.Block,7855 block: *Scope.Block,
7705 src: LazySrcLoc,7856 src: LazySrcLoc,
7706 name: []const u8,7857 name: []const u8,
7707) InnerError!*ir.Inst {7858) CompileError!Air.Inst.Ref {
7708 const mod = sema.mod;7859 const mod = sema.mod;
7709 const std_pkg = mod.root_pkg.table.get("std").?;7860 const std_pkg = mod.root_pkg.table.get("std").?;
7710 const std_file = (mod.importPkg(std_pkg) catch unreachable).file;7861 const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
...@@ -7715,7 +7866,7 @@ fn getBuiltin(...@@ -7715,7 +7866,7 @@ fn getBuiltin(
7715 "builtin",7866 "builtin",
7716 );7867 );
7717 const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);7868 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);
7719 const opt_ty_inst = try sema.analyzeNamespaceLookup(7870 const opt_ty_inst = try sema.analyzeNamespaceLookup(
7720 block,7871 block,
7721 src,7872 src,
...@@ -7730,9 +7881,9 @@ fn getBuiltinType(...@@ -7730,9 +7881,9 @@ fn getBuiltinType(
7730 block: *Scope.Block,7881 block: *Scope.Block,
7731 src: LazySrcLoc,7882 src: LazySrcLoc,
7732 name: []const u8,7883 name: []const u8,
7733) InnerError!Type {7884) CompileError!Type {
7734 const ty_inst = try sema.getBuiltin(block, src, name);7885 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);
7736}7887}
77377888
7738/// There is another implementation of this in `Type.onePossibleValue`. This one7889/// There is another implementation of this in `Type.onePossibleValue`. This one
...@@ -7744,7 +7895,7 @@ fn typeHasOnePossibleValue(...@@ -7744,7 +7895,7 @@ fn typeHasOnePossibleValue(
7744 block: *Scope.Block,7895 block: *Scope.Block,
7745 src: LazySrcLoc,7896 src: LazySrcLoc,
7746 starting_type: Type,7897 starting_type: Type,
7747) InnerError!?Value {7898) CompileError!?Value {
7748 var ty = starting_type;7899 var ty = starting_type;
7749 while (true) switch (ty.tag()) {7900 while (true) switch (ty.tag()) {
7750 .f16,7901 .f16,
...@@ -7882,7 +8033,7 @@ fn typeHasOnePossibleValue(...@@ -7882,7 +8033,7 @@ fn typeHasOnePossibleValue(
7882 };8033 };
7883}8034}
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 {
7886 return block.src_decl.namespace.file_scope.getTree(sema.gpa) catch |err| {8037 return block.src_decl.namespace.file_scope.getTree(sema.gpa) catch |err| {
7887 log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});8038 log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
7888 return error.AnalysisFail;8039 return error.AnalysisFail;
...@@ -7931,3 +8082,146 @@ fn enumFieldSrcLoc(...@@ -7931,3 +8082,146 @@ fn enumFieldSrcLoc(
7931 }8082 }
7932 } else unreachable;8083 } else unreachable;
7933}8084}
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+9-31
...@@ -22,7 +22,6 @@ const Zir = @This();...@@ -22,7 +22,6 @@ const Zir = @This();
22const Type = @import("type.zig").Type;22const Type = @import("type.zig").Type;
23const Value = @import("value.zig").Value;23const Value = @import("value.zig").Value;
24const TypedValue = @import("TypedValue.zig");24const TypedValue = @import("TypedValue.zig");
25const ir = @import("air.zig");
26const Module = @import("Module.zig");25const Module = @import("Module.zig");
27const LazySrcLoc = Module.LazySrcLoc;26const LazySrcLoc = Module.LazySrcLoc;
2827
...@@ -214,7 +213,7 @@ pub const Inst = struct {...@@ -214,7 +213,7 @@ pub const Inst = struct {
214 as_node,213 as_node,
215 /// Bitwise AND. `&`214 /// Bitwise AND. `&`
216 bit_and,215 bit_and,
217 /// Bitcast a value to a different type.216 /// Reinterpret the memory representation of a value as a different type.
218 /// Uses the pl_node field with payload `Bin`.217 /// Uses the pl_node field with payload `Bin`.
219 bitcast,218 bitcast,
220 /// A typed result location pointer is bitcasted to a new result location pointer.219 /// A typed result location pointer is bitcasted to a new result location pointer.
...@@ -237,15 +236,9 @@ pub const Inst = struct {...@@ -237,15 +236,9 @@ pub const Inst = struct {
237 /// Implements `suspend {...}`.236 /// Implements `suspend {...}`.
238 /// Uses the `pl_node` union field. Payload is `Block`.237 /// Uses the `pl_node` union field. Payload is `Block`.
239 suspend_block,238 suspend_block,
240 /// Boolean AND. See also `bit_and`.
241 /// Uses the `pl_node` union field. Payload is `Bin`.
242 bool_and,
243 /// Boolean NOT. See also `bit_not`.239 /// Boolean NOT. See also `bit_not`.
244 /// Uses the `un_node` field.240 /// Uses the `un_node` field.
245 bool_not,241 bool_not,
246 /// Boolean OR. See also `bit_or`.
247 /// Uses the `pl_node` union field. Payload is `Bin`.
248 bool_or,
249 /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand242 /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
250 /// is a block, which is evaluated if `lhs` is `true`.243 /// is a block, which is evaluated if `lhs` is `true`.
251 /// Uses the `bool_br` union field.244 /// Uses the `bool_br` union field.
...@@ -393,7 +386,7 @@ pub const Inst = struct {...@@ -393,7 +386,7 @@ pub const Inst = struct {
393 int,386 int,
394 /// Arbitrary sized integer literal. Uses the `str` union field.387 /// Arbitrary sized integer literal. Uses the `str` union field.
395 int_big,388 int_big,
396 /// 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.
397 float,390 float,
398 /// A float literal that fits in a f128. Uses the `pl_node` union value.391 /// A float literal that fits in a f128. Uses the `pl_node` union value.
399 /// Payload is `Float128`.392 /// Payload is `Float128`.
...@@ -999,8 +992,6 @@ pub const Inst = struct {...@@ -999,8 +992,6 @@ pub const Inst = struct {
999 .bool_br_and,992 .bool_br_and,
1000 .bool_br_or,993 .bool_br_or,
1001 .bool_not,994 .bool_not,
1002 .bool_and,
1003 .bool_or,
1004 .breakpoint,995 .breakpoint,
1005 .fence,996 .fence,
1006 .call,997 .call,
...@@ -1249,9 +1240,7 @@ pub const Inst = struct {...@@ -1249,9 +1240,7 @@ pub const Inst = struct {
1249 .block = .pl_node,1240 .block = .pl_node,
1250 .block_inline = .pl_node,1241 .block_inline = .pl_node,
1251 .suspend_block = .pl_node,1242 .suspend_block = .pl_node,
1252 .bool_and = .pl_node,
1253 .bool_not = .un_node,1243 .bool_not = .un_node,
1254 .bool_or = .pl_node,
1255 .bool_br_and = .bool_br,1244 .bool_br_and = .bool_br,
1256 .bool_br_or = .bool_br,1245 .bool_br_or = .bool_br,
1257 .@"break" = .@"break",1246 .@"break" = .@"break",
...@@ -2069,16 +2058,7 @@ pub const Inst = struct {...@@ -2069,16 +2058,7 @@ pub const Inst = struct {
2069 /// Offset from Decl AST node index.2058 /// Offset from Decl AST node index.
2070 node: i32,2059 node: i32,
2071 int: u64,2060 int: u64,
2072 float: struct {2061 float: f64,
2073 /// Offset from Decl AST node index.
2074 /// `Tag` determines which kind of AST node this points to.
2075 src_node: i32,
2076 number: f32,
2077
2078 pub fn src(self: @This()) LazySrcLoc {
2079 return .{ .node_offset = self.src_node };
2080 }
2081 },
2082 array_type_sentinel: struct {2062 array_type_sentinel: struct {
2083 len: Ref,2063 len: Ref,
2084 /// index into extra, points to an `ArrayTypeSentinel`2064 /// index into extra, points to an `ArrayTypeSentinel`
...@@ -2196,7 +2176,8 @@ pub const Inst = struct {...@@ -2196,7 +2176,8 @@ pub const Inst = struct {
2196 /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.2176 /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
2197 pub const Asm = struct {2177 pub const Asm = struct {
2198 src_node: i32,2178 src_node: i32,
2199 asm_source: Ref,2179 // null-terminated string index
2180 asm_source: u32,
2200 /// 1 bit for each outputs_len: whether it uses `-> T` or not.2181 /// 1 bit for each outputs_len: whether it uses `-> T` or not.
2201 /// 0b0 - operand is a pointer to where to store the output.2182 /// 0b0 - operand is a pointer to where to store the output.
2202 /// 0b1 - operand is a type; asm expression has the output as the result.2183 /// 0b1 - operand is a type; asm expression has the output as the result.
...@@ -2982,8 +2963,6 @@ const Writer = struct {...@@ -2982,8 +2963,6 @@ const Writer = struct {
2982 .mulwrap,2963 .mulwrap,
2983 .sub,2964 .sub,
2984 .subwrap,2965 .subwrap,
2985 .bool_and,
2986 .bool_or,
2987 .cmp_lt,2966 .cmp_lt,
2988 .cmp_lte,2967 .cmp_lte,
2989 .cmp_eq,2968 .cmp_eq,
...@@ -3269,10 +3248,8 @@ const Writer = struct {...@@ -3269,10 +3248,8 @@ const Writer = struct {
3269 }3248 }
32703249
3271 fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void {3250 fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void {
3272 const inst_data = self.code.instructions.items(.data)[inst].float;3251 const number = self.code.instructions.items(.data)[inst].float;
3273 const src = inst_data.src();3252 try stream.print("{d})", .{number});
3274 try stream.print("{d}) ", .{inst_data.number});
3275 try self.writeSrc(stream, src);
3276 }3253 }
32773254
3278 fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void {3255 fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void {
...@@ -3407,9 +3384,10 @@ const Writer = struct {...@@ -3407,9 +3384,10 @@ const Writer = struct {
3407 const inputs_len = @truncate(u5, extended.small >> 5);3384 const inputs_len = @truncate(u5, extended.small >> 5);
3408 const clobbers_len = @truncate(u5, extended.small >> 10);3385 const clobbers_len = @truncate(u5, extended.small >> 10);
3409 const is_volatile = @truncate(u1, extended.small >> 15) != 0;3386 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3387 const asm_source = self.code.nullTerminatedString(extra.data.asm_source);
34103388
3411 try self.writeFlag(stream, "volatile, ", is_volatile);3389 try self.writeFlag(stream, "volatile, ", is_volatile);
3412 try self.writeInstRef(stream, extra.data.asm_source);3390 try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)});
3413 try stream.writeAll(", ");3391 try stream.writeAll(", ");
34143392
3415 var extra_i: usize = extra.end;3393 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");...@@ -2,7 +2,9 @@ const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const math = std.math;3const math = std.math;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const ir = @import("air.zig");5const Air = @import("Air.zig");
6const Zir = @import("Zir.zig");
7const Liveness = @import("Liveness.zig");
6const Type = @import("type.zig").Type;8const Type = @import("type.zig").Type;
7const Value = @import("value.zig").Value;9const Value = @import("value.zig").Value;
8const TypedValue = @import("TypedValue.zig");10const TypedValue = @import("TypedValue.zig");
...@@ -22,6 +24,11 @@ const RegisterManager = @import("register_manager.zig").RegisterManager;...@@ -22,6 +24,11 @@ const RegisterManager = @import("register_manager.zig").RegisterManager;
2224
23const X8664Encoder = @import("codegen/x86_64.zig").Encoder;25const 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};
25pub const Result = union(enum) {32pub const Result = union(enum) {
26 /// The `code` parameter passed to `generateSymbol` has the value appended.33 /// The `code` parameter passed to `generateSymbol` has the value appended.
27 appended: void,34 appended: void,
...@@ -45,6 +52,71 @@ pub const DebugInfoOutput = union(enum) {...@@ -45,6 +52,71 @@ pub const DebugInfoOutput = union(enum) {
45 none,52 none,
46};53};
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
48pub fn generateSymbol(120pub fn generateSymbol(
49 bin_file: *link.File,121 bin_file: *link.File,
50 src_loc: Module.SrcLoc,122 src_loc: Module.SrcLoc,
...@@ -57,60 +129,14 @@ pub fn generateSymbol(...@@ -57,60 +129,14 @@ pub fn generateSymbol(
57129
58 switch (typed_value.ty.zigTypeTag()) {130 switch (typed_value.ty.zigTypeTag()) {
59 .Fn => {131 .Fn => {
60 switch (bin_file.options.target.cpu.arch) {132 return Result{
61 .wasm32 => unreachable, // has its own code path133 .fail = try ErrorMsg.create(
62 .wasm64 => unreachable, // has its own code path134 bin_file.allocator,
63 .arm => return Function(.arm).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),135 src_loc,
64 .armeb => return Function(.armeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),136 "TODO implement generateSymbol function pointers",
65 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),137 .{},
66 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),138 ),
67 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output),139 };
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 }
114 },140 },
115 .Array => {141 .Array => {
116 // TODO populate .debug_info for the array142 // TODO populate .debug_info for the array
...@@ -262,6 +288,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -262,6 +288,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
262288
263 return struct {289 return struct {
264 gpa: *Allocator,290 gpa: *Allocator,
291 air: Air,
292 liveness: Liveness,
265 bin_file: *link.File,293 bin_file: *link.File,
266 target: *const std.Target,294 target: *const std.Target,
267 mod_fn: *const Module.Fn,295 mod_fn: *const Module.Fn,
...@@ -297,7 +325,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -297,7 +325,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
297 /// across each runtime branch upon joining.325 /// across each runtime branch upon joining.
298 branch_stack: *std.ArrayList(Branch),326 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
302 register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},331 register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
303 /// Maps offset to what is stored there.332 /// Maps offset to what is stored there.
...@@ -309,6 +338,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -309,6 +338,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
309 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.338 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
310 next_stack_offset: u32 = 0,339 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
312 const MCValue = union(enum) {346 const MCValue = union(enum) {
313 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.347 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
314 /// TODO Look into deleting this tag and using `dead` instead, since every use348 /// 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 {...@@ -383,7 +417,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
383 };417 };
384418
385 const Branch = struct {419 const Branch = struct {
386 inst_table: std.AutoArrayHashMapUnmanaged(*ir.Inst, MCValue) = .{},420 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
387421
388 fn deinit(self: *Branch, gpa: *Allocator) void {422 fn deinit(self: *Branch, gpa: *Allocator) void {
389 self.inst_table.deinit(gpa);423 self.inst_table.deinit(gpa);
...@@ -392,7 +426,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -392,7 +426,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
392 };426 };
393427
394 const StackAllocation = struct {428 const StackAllocation = struct {
395 inst: *ir.Inst,429 inst: Air.Inst.Index,
396 /// TODO do we need size? should be determined by inst.ty.abiSize()430 /// TODO do we need size? should be determined by inst.ty.abiSize()
397 size: u32,431 size: u32,
398 };432 };
...@@ -418,21 +452,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -418,21 +452,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
418 },452 },
419 };453 };
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
421 const Self = @This();492 const Self = @This();
422493
423 fn generateSymbol(494 fn generate(
424 bin_file: *link.File,495 bin_file: *link.File,
425 src_loc: Module.SrcLoc,496 src_loc: Module.SrcLoc,
426 typed_value: TypedValue,497 module_fn: *Module.Fn,
498 air: Air,
499 liveness: Liveness,
427 code: *std.ArrayList(u8),500 code: *std.ArrayList(u8),
428 debug_output: DebugInfoOutput,501 debug_output: DebugInfoOutput,
429 ) GenerateSymbolError!Result {502 ) GenerateSymbolError!FnResult {
430 if (build_options.skip_non_native and std.Target.current.cpu.arch != arch) {503 if (build_options.skip_non_native and std.Target.current.cpu.arch != arch) {
431 @panic("Attempted to compile for architecture that was disabled by build configuration");504 @panic("Attempted to compile for architecture that was disabled by build configuration");
432 }505 }
433506
434 const module_fn = typed_value.val.castTag(.function).?.data;
435
436 assert(module_fn.owner_decl.has_tv);507 assert(module_fn.owner_decl.has_tv);
437 const fn_type = module_fn.owner_decl.ty;508 const fn_type = module_fn.owner_decl.ty;
438509
...@@ -446,6 +517,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -446,6 +517,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
446517
447 var function = Self{518 var function = Self{
448 .gpa = bin_file.allocator,519 .gpa = bin_file.allocator,
520 .air = air,
521 .liveness = liveness,
449 .target = &bin_file.options.target,522 .target = &bin_file.options.target,
450 .bin_file = bin_file,523 .bin_file = bin_file,
451 .mod_fn = module_fn,524 .mod_fn = module_fn,
...@@ -469,8 +542,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -469,8 +542,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
469 defer function.blocks.deinit(bin_file.allocator);542 defer function.blocks.deinit(bin_file.allocator);
470 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);543 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) {545 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
473 error.CodegenFail => return Result{ .fail = function.err_msg.? },546 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
474 else => |e| return e,547 else => |e| return e,
475 };548 };
476 defer call_info.deinit(&function);549 defer call_info.deinit(&function);
...@@ -481,14 +554,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -481,14 +554,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
481 function.max_end_stack = call_info.stack_byte_count;554 function.max_end_stack = call_info.stack_byte_count;
482555
483 function.gen() catch |err| switch (err) {556 function.gen() catch |err| switch (err) {
484 error.CodegenFail => return Result{ .fail = function.err_msg.? },557 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
485 else => |e| return e,558 else => |e| return e,
486 };559 };
487560
488 if (function.err_msg) |em| {561 if (function.err_msg) |em| {
489 return Result{ .fail = em };562 return FnResult{ .fail = em };
490 } else {563 } else {
491 return Result{ .appended = {} };564 return FnResult{ .appended = {} };
492 }565 }
493 }566 }
494567
...@@ -512,7 +585,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -512,7 +585,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
512 self.code.items.len += 4;585 self.code.items.len += 4;
513586
514 try self.dbgSetPrologueEnd();587 try self.dbgSetPrologueEnd();
515 try self.genBody(self.mod_fn.body);588 try self.genBody(self.air.getMainBody());
516589
517 const stack_end = self.max_end_stack;590 const stack_end = self.max_end_stack;
518 if (stack_end > math.maxInt(i32))591 if (stack_end > math.maxInt(i32))
...@@ -553,7 +626,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -553,7 +626,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
553 });626 });
554 } else {627 } else {
555 try self.dbgSetPrologueEnd();628 try self.dbgSetPrologueEnd();
556 try self.genBody(self.mod_fn.body);629 try self.genBody(self.air.getMainBody());
557 try self.dbgSetEpilogueBegin();630 try self.dbgSetEpilogueBegin();
558 }631 }
559 },632 },
...@@ -569,7 +642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -569,7 +642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
569642
570 try self.dbgSetPrologueEnd();643 try self.dbgSetPrologueEnd();
571644
572 try self.genBody(self.mod_fn.body);645 try self.genBody(self.air.getMainBody());
573646
574 // Backpatch push callee saved regs647 // Backpatch push callee saved regs
575 var saved_regs = Instruction.RegisterList{648 var saved_regs = Instruction.RegisterList{
...@@ -630,7 +703,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -630,7 +703,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
630 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32());703 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32());
631 } else {704 } else {
632 try self.dbgSetPrologueEnd();705 try self.dbgSetPrologueEnd();
633 try self.genBody(self.mod_fn.body);706 try self.genBody(self.air.getMainBody());
634 try self.dbgSetEpilogueBegin();707 try self.dbgSetEpilogueBegin();
635 }708 }
636 },709 },
...@@ -654,7 +727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -654,7 +727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
654727
655 try self.dbgSetPrologueEnd();728 try self.dbgSetPrologueEnd();
656729
657 try self.genBody(self.mod_fn.body);730 try self.genBody(self.air.getMainBody());
658731
659 // Backpatch stack offset732 // Backpatch stack offset
660 const stack_end = self.max_end_stack;733 const stack_end = self.max_end_stack;
...@@ -706,13 +779,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -706,13 +779,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
706 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());779 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
707 } else {780 } else {
708 try self.dbgSetPrologueEnd();781 try self.dbgSetPrologueEnd();
709 try self.genBody(self.mod_fn.body);782 try self.genBody(self.air.getMainBody());
710 try self.dbgSetEpilogueBegin();783 try self.dbgSetEpilogueBegin();
711 }784 }
712 },785 },
713 else => {786 else => {
714 try self.dbgSetPrologueEnd();787 try self.dbgSetPrologueEnd();
715 try self.genBody(self.mod_fn.body);788 try self.genBody(self.air.getMainBody());
716 try self.dbgSetEpilogueBegin();789 try self.dbgSetEpilogueBegin();
717 },790 },
718 }791 }
...@@ -720,21 +793,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -720,21 +793,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
720 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);793 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
721 }794 }
722795
723 fn genBody(self: *Self, body: ir.Body) InnerError!void {796 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
724 for (body.instructions) |inst| {797 const air_tags = self.air.instructions.items(.tag);
725 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));798
726799 for (body) |inst| {
727 const mcv = try self.genFuncInst(inst);800 const old_air_bookkeeping = self.air_bookkeeping;
728 if (!inst.isUnused()) {801 try self.ensureProcessDeathCapacity(Liveness.bpi);
729 log.debug("{*} => {}", .{ inst, mcv });802
730 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];803 switch (air_tags[inst]) {
731 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);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
732 }872 }
733873 if (std.debug.runtime_safety) {
734 var i: ir.Inst.DeathsBitIndex = 0;874 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
735 while (inst.getOperand(i)) |operand| : (i += 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] });
736 if (inst.operandDies(i))876 }
737 self.processDeath(operand);
738 }877 }
739 }878 }
740 }879 }
...@@ -784,8 +923,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -784,8 +923,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
784 }923 }
785924
786 /// Asserts there is already capacity to insert into top branch inst_table.925 /// Asserts there is already capacity to insert into top branch inst_table.
787 fn processDeath(self: *Self, inst: *ir.Inst) void {926 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
788 if (inst.tag == .constant) return; // Constants are immortal.927 const air_tags = self.air.instructions.items(.tag);
928 if (air_tags[inst] == .constant) return; // Constants are immortal.
789 // When editing this function, note that the logic must synchronize with `reuseOperand`.929 // When editing this function, note that the logic must synchronize with `reuseOperand`.
790 const prev_value = self.getResolvedInstValue(inst);930 const prev_value = self.getResolvedInstValue(inst);
791 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];931 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 {...@@ -799,9 +939,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
799 }939 }
800 }940 }
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
802 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {969 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
803 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;970 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);
805 }972 }
806973
807 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,974 /// 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 {...@@ -826,74 +993,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
826 }993 }
827 }994 }
828995
829 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {996 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
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 {
897 if (abi_align > self.stack_align)997 if (abi_align > self.stack_align)
898 self.stack_align = abi_align;998 self.stack_align = abi_align;
899 // TODO find a free slot instead of always appending999 // TODO find a free slot instead of always appending
...@@ -909,20 +1009,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -909,20 +1009,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
909 }1009 }
9101010
911 /// Use a pointer instruction as the basis for allocating stack memory.1011 /// Use a pointer instruction as the basis for allocating stack memory.
912 fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {1012 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
913 const elem_ty = inst.ty.elemType();1013 const elem_ty = self.air.typeOfIndex(inst).elemType();
914 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {1014 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});
916 };1016 };
917 // TODO swap this for inst.ty.ptrAlign1017 // TODO swap this for inst.ty.ptrAlign
918 const abi_align = elem_ty.abiAlignment(self.target.*);1018 const abi_align = elem_ty.abiAlignment(self.target.*);
919 return self.allocMem(inst, abi_size, abi_align);1019 return self.allocMem(inst, abi_size, abi_align);
920 }1020 }
9211021
922 fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {1022 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
923 const elem_ty = inst.ty;1023 const elem_ty = self.air.typeOfIndex(inst);
924 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {1024 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});
926 };1026 };
927 const abi_align = elem_ty.abiAlignment(self.target.*);1027 const abi_align = elem_ty.abiAlignment(self.target.*);
928 if (abi_align > self.stack_align)1028 if (abi_align > self.stack_align)
...@@ -942,310 +1042,299 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -942,310 +1042,299 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
942 return MCValue{ .stack_offset = stack_offset };1042 return MCValue{ .stack_offset = stack_offset };
943 }1043 }
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 {
946 const stack_mcv = try self.allocRegOrMem(inst, false);1046 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 });
948 const reg_mcv = self.getResolvedInstValue(inst);1048 const reg_mcv = self.getResolvedInstValue(inst);
949 assert(reg == toCanonicalReg(reg_mcv.register));1049 assert(reg == toCanonicalReg(reg_mcv.register));
950 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1050 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
951 try branch.inst_table.put(self.gpa, inst, stack_mcv);1051 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);
953 }1053 }
9541054
955 /// Copies a value to a register without tracking the register. The register is not considered1055 /// Copies a value to a register without tracking the register. The register is not considered
956 /// allocated. A second call to `copyToTmpRegister` may return the same register.1056 /// allocated. A second call to `copyToTmpRegister` may return the same register.
957 /// This can have a side effect of spilling instructions to the stack to free up a register.1057 /// 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 {
959 const reg = try self.register_manager.allocReg(null, &.{});1059 const reg = try self.register_manager.allocReg(null, &.{});
960 try self.genSetReg(src, ty, reg, mcv);1060 try self.genSetReg(ty, reg, mcv);
961 return reg;1061 return reg;
962 }1062 }
9631063
964 /// Allocates a new register and copies `mcv` into it.1064 /// Allocates a new register and copies `mcv` into it.
965 /// `reg_owner` is the instruction that gets associated with the register in the register table.1065 /// `reg_owner` is the instruction that gets associated with the register in the register table.
966 /// This can have a side effect of spilling instructions to the stack to free up a register.1066 /// 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 {
968 const reg = try self.register_manager.allocReg(reg_owner, &.{});1068 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);
970 return MCValue{ .register = reg };1070 return MCValue{ .register = reg };
971 }1071 }
9721072
973 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {1073 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
974 const stack_offset = try self.allocMemPtr(&inst.base);1074 const stack_offset = try self.allocMemPtr(inst);
975 return MCValue{ .ptr_stack_offset = stack_offset };1075 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
976 }1076 }
9771077
978 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1078 fn airFloatCast(self: *Self, inst: Air.Inst.Index) !void {
979 // No side effects, so if it's unreferenced, do nothing.1079 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
980 if (inst.base.isUnused())1080 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
981 return MCValue.dead;1081 else => return self.fail("TODO implement floatCast for {}", .{self.target.cpu.arch}),
982 switch (arch) {1082 };
983 else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),1083 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
984 }
985 }1084 }
9861085
987 fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1086 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
988 // No side effects, so if it's unreferenced, do nothing.1087 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
989 if (inst.base.isUnused())1088 if (self.liveness.isUnused(inst))
990 return MCValue.dead;1089 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
9911090
992 const operand = try self.resolveInst(inst.operand);1091 const operand_ty = self.air.typeOf(ty_op.operand);
993 const info_a = inst.operand.ty.intInfo(self.target.*);1092 const operand = try self.resolveInst(ty_op.operand);
994 const info_b = inst.base.ty.intInfo(self.target.*);1093 const info_a = operand_ty.intInfo(self.target.*);
1094 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
995 if (info_a.signedness != info_b.signedness)1095 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
998 if (info_a.bits == info_b.bits)1098 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) {1101 const result: MCValue = switch (arch) {
1002 else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),1102 else => return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}),
1003 }1103 };
1104 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1004 }1105 }
10051106
1006 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1107 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1007 // No side effects, so if it's unreferenced, do nothing.1108 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1008 if (inst.base.isUnused())1109 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1009 return MCValue.dead;1110 const operand = try self.resolveInst(ty_op.operand);
1010 const operand = try self.resolveInst(inst.operand);1111 switch (operand) {
1011 switch (operand) {1112 .dead => unreachable,
1012 .dead => unreachable,1113 .unreach => unreachable,
1013 .unreach => unreachable,1114 .compare_flags_unsigned => |op| {
1014 .compare_flags_unsigned => |op| return MCValue{1115 const r = MCValue{
1015 .compare_flags_unsigned = switch (op) {1116 .compare_flags_unsigned = switch (op) {
1016 .gte => .lt,1117 .gte => .lt,
1017 .gt => .lte,1118 .gt => .lte,
1018 .neq => .eq,1119 .neq => .eq,
1019 .lt => .gte,1120 .lt => .gte,
1020 .lte => .gt,1121 .lte => .gt,
1021 .eq => .neq,1122 .eq => .neq,
1123 },
1124 };
1125 break :result r;
1022 },1126 },
1023 },1127 .compare_flags_signed => |op| {
1024 .compare_flags_signed => |op| return MCValue{1128 const r = MCValue{
1025 .compare_flags_signed = switch (op) {1129 .compare_flags_signed = switch (op) {
1026 .gte => .lt,1130 .gte => .lt,
1027 .gt => .lte,1131 .gt => .lte,
1028 .neq => .eq,1132 .neq => .eq,
1029 .lt => .gte,1133 .lt => .gte,
1030 .lte => .gt,1134 .lte => .gt,
1031 .eq => .neq,1135 .eq => .neq,
1136 },
1137 };
1138 break :result r;
1032 },1139 },
1033 },1140 else => {},
1034 else => {},1141 }
1035 }
10361142
1037 switch (arch) {1143 switch (arch) {
1038 .x86_64 => {1144 .x86_64 => {
1039 var imm = ir.Inst.Constant{1145 break :result try self.genX8664BinMath(inst, ty_op.operand, .bool_true);
1040 .base = .{1146 },
1041 .tag = .constant,1147 .arm, .armeb => {
1042 .deaths = 0,1148 break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
1043 .ty = inst.operand.ty,1149 },
1044 .src = inst.operand.src,1150 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),
1045 },1151 }
1046 .val = Value.initTag(.bool_true),1152 };
1047 };1153 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
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 }
1064 }1154 }
10651155
1066 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1156 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
1067 // No side effects, so if it's unreferenced, do nothing.1157 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1068 if (inst.base.isUnused())1158 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1069 return MCValue.dead;1159 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1070 switch (arch) {1160 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
1071 .x86_64 => {1161 else => return self.fail("TODO implement add for {}", .{self.target.cpu.arch}),
1072 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs);1162 };
1073 },1163 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
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 }
1077 }1164 }
10781165
1079 fn genAddWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1166 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
1080 // No side effects, so if it's unreferenced, do nothing.1167 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1081 if (inst.base.isUnused())1168 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1082 return MCValue.dead;1169 else => return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}),
1083 switch (arch) {1170 };
1084 else => return self.fail(inst.base.src, "TODO implement addwrap for {}", .{self.target.cpu.arch}),1171 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1085 }
1086 }1172 }
10871173
1088 fn genMul(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1174 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
1089 // No side effects, so if it's unreferenced, do nothing.1175 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1090 if (inst.base.isUnused())1176 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1091 return MCValue.dead;1177 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1092 switch (arch) {1178 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .sub),
1093 .x86_64 => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),1179 else => return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}),
1094 .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs),1180 };
1095 else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}),1181 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1096 }
1097 }1182 }
10981183
1099 fn genMulWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1184 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
1100 // No side effects, so if it's unreferenced, do nothing.1185 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1101 if (inst.base.isUnused())1186 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1102 return MCValue.dead;1187 else => return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}),
1103 switch (arch) {1188 };
1104 else => return self.fail(inst.base.src, "TODO implement mulwrap for {}", .{self.target.cpu.arch}),1189 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1105 }
1106 }1190 }
11071191
1108 fn genDiv(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1192 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
1109 // No side effects, so if it's unreferenced, do nothing.1193 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1110 if (inst.base.isUnused())1194 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1111 return MCValue.dead;1195 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1112 switch (arch) {1196 .arm, .armeb => try self.genArmMul(inst, bin_op.lhs, bin_op.rhs),
1113 else => return self.fail(inst.base.src, "TODO implement div for {}", .{self.target.cpu.arch}),1197 else => return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}),
1114 }1198 };
1199 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1115 }1200 }
11161201
1117 fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1202 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
1118 // No side effects, so if it's unreferenced, do nothing.1203 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1119 if (inst.base.isUnused())1204 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1120 return MCValue.dead;1205 else => return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}),
1121 switch (arch) {1206 };
1122 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_and),1207 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1123 else => return self.fail(inst.base.src, "TODO implement bitwise and for {}", .{self.target.cpu.arch}),
1124 }
1125 }1208 }
11261209
1127 fn genBitOr(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1210 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1128 // No side effects, so if it's unreferenced, do nothing.1211 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1129 if (inst.base.isUnused())1212 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1130 return MCValue.dead;1213 else => return self.fail("TODO implement div for {}", .{self.target.cpu.arch}),
1131 switch (arch) {1214 };
1132 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_or),1215 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1133 else => return self.fail(inst.base.src, "TODO implement bitwise or for {}", .{self.target.cpu.arch}),
1134 }
1135 }1216 }
11361217
1137 fn genXor(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1218 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
1138 // No side effects, so if it's unreferenced, do nothing.1219 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1139 if (inst.base.isUnused())1220 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1140 return MCValue.dead;1221 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),
1141 switch (arch) {1222 else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}),
1142 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .xor),1223 };
1143 else => return self.fail(inst.base.src, "TODO implement xor for {}", .{self.target.cpu.arch}),1224 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1144 }
1145 }1225 }
11461226
1147 fn genOptionalPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1227 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
1148 // No side effects, so if it's unreferenced, do nothing.1228 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1149 if (inst.base.isUnused())1229 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1150 return MCValue.dead;1230 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),
1151 switch (arch) {1231 else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}),
1152 else => return self.fail(inst.base.src, "TODO implement .optional_payload for {}", .{self.target.cpu.arch}),1232 };
1153 }1233 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1154 }1234 }
11551235
1156 fn genOptionalPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1236 fn airXor(self: *Self, inst: Air.Inst.Index) !void {
1157 // No side effects, so if it's unreferenced, do nothing.1237 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1158 if (inst.base.isUnused())1238 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1159 return MCValue.dead;1239 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),
1160 switch (arch) {1240 else => return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}),
1161 else => return self.fail(inst.base.src, "TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),1241 };
1162 }1242 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1163 }1243 }
11641244
1165 fn genUnwrapErrErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1245 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1166 // No side effects, so if it's unreferenced, do nothing.1246 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1167 if (inst.base.isUnused())1247 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1168 return MCValue.dead;1248 else => return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}),
1169 switch (arch) {1249 };
1170 else => return self.fail(inst.base.src, "TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),1250 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1171 }
1172 }1251 }
11731252
1174 fn genUnwrapErrPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1253 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1175 // No side effects, so if it's unreferenced, do nothing.1254 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1176 if (inst.base.isUnused())1255 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1177 return MCValue.dead;1256 else => return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),
1178 switch (arch) {1257 };
1179 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),1258 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1180 }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 });
1181 }1267 }
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
1182 // *(E!T) -> E1277 // *(E!T) -> E
1183 fn genUnwrapErrErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1278 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
1184 // No side effects, so if it's unreferenced, do nothing.1279 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1185 if (inst.base.isUnused())1280 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1186 return MCValue.dead;1281 else => return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
1187 switch (arch) {1282 };
1188 else => return self.fail(inst.base.src, "TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),1283 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1189 }
1190 }1284 }
1285
1191 // *(E!T) -> *T1286 // *(E!T) -> *T
1192 fn genUnwrapErrPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1287 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1193 // No side effects, so if it's unreferenced, do nothing.1288 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1194 if (inst.base.isUnused())1289 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1195 return MCValue.dead;1290 else => return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
1196 switch (arch) {1291 };
1197 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),1292 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1198 }
1199 }1293 }
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.1295 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1204 if (inst.base.isUnused())1296 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1205 return MCValue.dead;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 true1300 // Optional with a zero-bit payload type is just a boolean true
1208 if (optional_ty.abiSize(self.target.*) == 1)1301 if (optional_ty.abiSize(self.target.*) == 1)
1209 return MCValue{ .immediate = 1 };1302 break :result MCValue{ .immediate = 1 };
12101303
1211 switch (arch) {1304 switch (arch) {
1212 else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),1305 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1213 }1306 }
1307 };
1308 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1214 }1309 }
12151310
1216 /// T to E!T1311 /// T to E!T
1217 fn genWrapErrUnionPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1312 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1218 // No side effects, so if it's unreferenced, do nothing.1313 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1219 if (inst.base.isUnused())1314 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1220 return MCValue.dead;1315 else => return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
12211316 };
1222 switch (arch) {1317 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1223 else => return self.fail(inst.base.src, "TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
1224 }
1225 }1318 }
12261319
1227 /// E to E!T1320 /// E to E!T
1228 fn genWrapErrUnionErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1321 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1229 // No side effects, so if it's unreferenced, do nothing.1322 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1230 if (inst.base.isUnused())1323 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1231 return MCValue.dead;1324 else => return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
12321325 };
1233 switch (arch) {1326 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1234 else => return self.fail(inst.base.src, "TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
1235 }
1236 }1327 }
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) {1329 fn airVarPtr(self: *Self, inst: Air.Inst.Index) !void {
1243 else => return self.fail(inst.base.src, "TODO implement varptr for {}", .{self.target.cpu.arch}),1330 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1244 }1331 else => return self.fail("TODO implement varptr for {}", .{self.target.cpu.arch}),
1332 };
1333 return self.finishAir(inst, result, .{ .none, .none, .none });
1245 }1334 }
12461335
1247 fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {1336 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1248 if (!inst.operandDies(op_index))1337 if (!self.liveness.operandDies(inst, op_index))
1249 return false;1338 return false;
12501339
1251 switch (mcv) {1340 switch (mcv) {
...@@ -1257,40 +1346,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1257,40 +1346,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1257 self.register_manager.registers[index] = inst;1346 self.register_manager.registers[index] = inst;
1258 }1347 }
1259 }1348 }
1260 log.debug("reusing {} => {*}", .{ reg, inst });1349 log.debug("%{d} => {} (reused)", .{ inst, reg });
1261 },1350 },
1262 .stack_offset => |off| {1351 .stack_offset => |off| {
1263 log.debug("reusing stack offset {} => {*}", .{ off, inst });1352 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1264 },1353 },
1265 else => return false,1354 else => return false,
1266 }1355 }
12671356
1268 // Prevent the operand deaths processing code from deallocating it.1357 // Prevent the operand deaths processing code from deallocating it.
1269 inst.clearOperandDeath(op_index);1358 self.liveness.clearOperandDeath(inst, op_index);
12701359
1271 // That makes us responsible for doing the rest of the stuff that processDeath would have done.1360 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1272 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1361 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
1275 return true;1364 return true;
1276 }1365 }
12771366
1278 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1367 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) !void {
1279 const elem_ty = inst.base.ty;1368 const elem_ty = ptr_ty.elemType();
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 };
1294 switch (ptr) {1369 switch (ptr) {
1295 .none => unreachable,1370 .none => unreachable,
1296 .undef => unreachable,1371 .undef => unreachable,
...@@ -1298,31 +1373,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1298,31 +1373,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1298 .dead => unreachable,1373 .dead => unreachable,
1299 .compare_flags_unsigned => unreachable,1374 .compare_flags_unsigned => unreachable,
1300 .compare_flags_signed => unreachable,1375 .compare_flags_signed => unreachable,
1301 .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),1376 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1302 .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),1377 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
1303 .ptr_embedded_in_code => |off| {1378 .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 });
1305 },1380 },
1306 .embedded_in_code => {1381 .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", .{});
1308 },1383 },
1309 .register => {1384 .register => {
1310 return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});1385 return self.fail("TODO implement loading from MCValue.register", .{});
1311 },1386 },
1312 .memory => {1387 .memory => {
1313 return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});1388 return self.fail("TODO implement loading from MCValue.memory", .{});
1314 },1389 },
1315 .stack_offset => {1390 .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", .{});
1317 },1392 },
1318 }1393 }
1319 return dst_mcv;
1320 }1394 }
13211395
1322 fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1396 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1323 const ptr = try self.resolveInst(inst.lhs);1397 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1324 const value = try self.resolveInst(inst.rhs);1398 const elem_ty = self.air.typeOfIndex(inst);
1325 const elem_ty = inst.rhs.ty;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);
1326 switch (ptr) {1427 switch (ptr) {
1327 .none => unreachable,1428 .none => unreachable,
1328 .undef => unreachable,1429 .undef => unreachable,
...@@ -1331,57 +1432,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1331,57 +1432,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1331 .compare_flags_unsigned => unreachable,1432 .compare_flags_unsigned => unreachable,
1332 .compare_flags_signed => unreachable,1433 .compare_flags_signed => unreachable,
1333 .immediate => |imm| {1434 .immediate => |imm| {
1334 try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);1435 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1335 },1436 },
1336 .ptr_stack_offset => |off| {1437 .ptr_stack_offset => |off| {
1337 try self.genSetStack(inst.base.src, elem_ty, off, value);1438 try self.genSetStack(elem_ty, off, value);
1338 },1439 },
1339 .ptr_embedded_in_code => |off| {1440 .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);
1341 },1442 },
1342 .embedded_in_code => {1443 .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", .{});
1344 },1445 },
1345 .register => {1446 .register => {
1346 return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});1447 return self.fail("TODO implement storing to MCValue.register", .{});
1347 },1448 },
1348 .memory => {1449 .memory => {
1349 return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});1450 return self.fail("TODO implement storing to MCValue.memory", .{});
1350 },1451 },
1351 .stack_offset => {1452 .stack_offset => {
1352 return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});1453 return self.fail("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);
1369 },1454 },
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}),
1372 }1455 }
1456 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1373 }1457 }
13741458
1375 fn genSubWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1459 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
1376 // No side effects, so if it's unreferenced, do nothing.1460 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1377 if (inst.base.isUnused())1461 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1378 return MCValue.dead;1462 _ = extra;
1379 switch (arch) {1463 return self.fail("TODO implement codegen struct_field_ptr", .{});
1380 else => return self.fail(inst.base.src, "TODO implement subwrap for {}", .{self.target.cpu.arch}),1464 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1381 }
1382 }1465 }
13831466
1384 fn armOperandShouldBeRegister(self: *Self, src: LazySrcLoc, mcv: MCValue) !bool {1467 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
1385 return switch (mcv) {1468 return switch (mcv) {
1386 .none => unreachable,1469 .none => unreachable,
1387 .undef => unreachable,1470 .undef => unreachable,
...@@ -1391,7 +1474,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1391,7 +1474,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1391 .ptr_stack_offset => unreachable,1474 .ptr_stack_offset => unreachable,
1392 .ptr_embedded_in_code => unreachable,1475 .ptr_embedded_in_code => unreachable,
1393 .immediate => |imm| blk: {1476 .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
1396 // Load immediate into register if it doesn't fit1479 // Load immediate into register if it doesn't fit
1397 // in an operand1480 // in an operand
...@@ -1405,16 +1488,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1405,16 +1488,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1405 };1488 };
1406 }1489 }
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 {
1409 const lhs = try self.resolveInst(op_lhs);1492 const lhs = try self.resolveInst(op_lhs);
1410 const rhs = try self.resolveInst(op_rhs);1493 const rhs = try self.resolveInst(op_rhs);
14111494
1412 const lhs_is_register = lhs == .register;1495 const lhs_is_register = lhs == .register;
1413 const rhs_is_register = rhs == .register;1496 const rhs_is_register = rhs == .register;
1414 const lhs_should_be_register = try self.armOperandShouldBeRegister(op_lhs.src, lhs);1497 const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
1415 const rhs_should_be_register = try self.armOperandShouldBeRegister(op_rhs.src, rhs);1498 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1416 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);1499 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1417 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);1500 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
14181501
1419 // Destination must be a register1502 // Destination must be a register
1420 var dst_mcv: MCValue = undefined;1503 var dst_mcv: MCValue = undefined;
...@@ -1427,15 +1510,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1427,15 +1510,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1427 if (reuse_lhs) {1510 if (reuse_lhs) {
1428 // Allocate 0 or 1 registers1511 // Allocate 0 or 1 registers
1429 if (!rhs_is_register and rhs_should_be_register) {1512 if (!rhs_is_register and rhs_should_be_register) {
1430 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };1513 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1431 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);1514 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1432 }1515 }
1433 dst_mcv = lhs;1516 dst_mcv = lhs;
1434 } else if (reuse_rhs) {1517 } else if (reuse_rhs) {
1435 // Allocate 0 or 1 registers1518 // Allocate 0 or 1 registers
1436 if (!lhs_is_register and lhs_should_be_register) {1519 if (!lhs_is_register and lhs_should_be_register) {
1437 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };1520 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1438 branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv);1521 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1439 }1522 }
1440 dst_mcv = rhs;1523 dst_mcv = rhs;
14411524
...@@ -1455,12 +1538,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1455,12 +1538,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1455 lhs_mcv = dst_mcv;1538 lhs_mcv = dst_mcv;
1456 } else {1539 } else {
1457 // Move LHS and RHS to register1540 // 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).? }, &.{});
1459 lhs_mcv = MCValue{ .register = regs[0] };1542 lhs_mcv = MCValue{ .register = regs[0] };
1460 rhs_mcv = MCValue{ .register = regs[1] };1543 rhs_mcv = MCValue{ .register = regs[1] };
1461 dst_mcv = lhs_mcv;1544 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);
1464 }1547 }
1465 } else if (lhs_should_be_register) {1548 } else if (lhs_should_be_register) {
1466 // RHS is immediate1549 // RHS is immediate
...@@ -1485,14 +1568,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1485,14 +1568,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14851568
1486 // Move the operands to the newly allocated registers1569 // Move the operands to the newly allocated registers
1487 if (lhs_mcv == .register and !lhs_is_register) {1570 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);
1489 }1572 }
1490 if (rhs_mcv == .register and !rhs_is_register) {1573 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);
1492 }1575 }
14931576
1494 try self.genArmBinOpCode(1577 try self.genArmBinOpCode(
1495 inst.src,
1496 dst_mcv.register,1578 dst_mcv.register,
1497 lhs_mcv,1579 lhs_mcv,
1498 rhs_mcv,1580 rhs_mcv,
...@@ -1504,14 +1586,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1504,14 +1586,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15041586
1505 fn genArmBinOpCode(1587 fn genArmBinOpCode(
1506 self: *Self,1588 self: *Self,
1507 src: LazySrcLoc,
1508 dst_reg: Register,1589 dst_reg: Register,
1509 lhs_mcv: MCValue,1590 lhs_mcv: MCValue,
1510 rhs_mcv: MCValue,1591 rhs_mcv: MCValue,
1511 swap_lhs_and_rhs: bool,1592 swap_lhs_and_rhs: bool,
1512 op: ir.Inst.Tag,1593 op: Air.Inst.Tag,
1513 ) !void {1594 ) !void {
1514 _ = src;
1515 assert(lhs_mcv == .register or rhs_mcv == .register);1595 assert(lhs_mcv == .register or rhs_mcv == .register);
15161596
1517 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;1597 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 {...@@ -1560,14 +1640,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1560 }1640 }
1561 }1641 }
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 {
1564 const lhs = try self.resolveInst(op_lhs);1644 const lhs = try self.resolveInst(op_lhs);
1565 const rhs = try self.resolveInst(op_rhs);1645 const rhs = try self.resolveInst(op_rhs);
15661646
1567 const lhs_is_register = lhs == .register;1647 const lhs_is_register = lhs == .register;
1568 const rhs_is_register = rhs == .register;1648 const rhs_is_register = rhs == .register;
1569 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);1649 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1570 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);1650 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
15711651
1572 // Destination must be a register1652 // Destination must be a register
1573 // LHS must be a register1653 // LHS must be a register
...@@ -1581,15 +1661,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1581,15 +1661,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1581 if (reuse_lhs) {1661 if (reuse_lhs) {
1582 // Allocate 0 or 1 registers1662 // Allocate 0 or 1 registers
1583 if (!rhs_is_register) {1663 if (!rhs_is_register) {
1584 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };1664 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1585 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);1665 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1586 }1666 }
1587 dst_mcv = lhs;1667 dst_mcv = lhs;
1588 } else if (reuse_rhs) {1668 } else if (reuse_rhs) {
1589 // Allocate 0 or 1 registers1669 // Allocate 0 or 1 registers
1590 if (!lhs_is_register) {1670 if (!lhs_is_register) {
1591 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };1671 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1592 branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv);1672 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1593 }1673 }
1594 dst_mcv = rhs;1674 dst_mcv = rhs;
1595 } else {1675 } else {
...@@ -1606,21 +1686,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1606,21 +1686,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1606 lhs_mcv = dst_mcv;1686 lhs_mcv = dst_mcv;
1607 } else {1687 } else {
1608 // Move LHS and RHS to register1688 // 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).? }, &.{});
1610 lhs_mcv = MCValue{ .register = regs[0] };1690 lhs_mcv = MCValue{ .register = regs[0] };
1611 rhs_mcv = MCValue{ .register = regs[1] };1691 rhs_mcv = MCValue{ .register = regs[1] };
1612 dst_mcv = lhs_mcv;1692 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);
1615 }1695 }
1616 }1696 }
16171697
1618 // Move the operands to the newly allocated registers1698 // Move the operands to the newly allocated registers
1619 if (!lhs_is_register) {1699 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);
1621 }1701 }
1622 if (!rhs_is_register) {1702 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);
1624 }1704 }
16251705
1626 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());1706 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 {...@@ -1630,7 +1710,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1630 /// Perform "binary" operators, excluding comparisons.1710 /// Perform "binary" operators, excluding comparisons.
1631 /// Currently, the following ops are supported:1711 /// Currently, the following ops are supported:
1632 /// ADD, SUB, XOR, OR, AND1712 /// 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 {
1634 // We'll handle these ops in two steps.1714 // We'll handle these ops in two steps.
1635 // 1) Prepare an output location (register or memory)1715 // 1) Prepare an output location (register or memory)
1636 // This location will be the location of the operand that dies (if one exists)1716 // 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 {...@@ -1653,8 +1733,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1653 // as the result MCValue.1733 // as the result MCValue.
1654 var dst_mcv: MCValue = undefined;1734 var dst_mcv: MCValue = undefined;
1655 var src_mcv: MCValue = undefined;1735 var src_mcv: MCValue = undefined;
1656 var src_inst: *ir.Inst = undefined;1736 var src_inst: Air.Inst.Ref = undefined;
1657 if (self.reuseOperand(inst, 0, lhs)) {1737 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
1658 // LHS dies; use it as the destination.1738 // LHS dies; use it as the destination.
1659 // Both operands cannot be memory.1739 // Both operands cannot be memory.
1660 src_inst = op_rhs;1740 src_inst = op_rhs;
...@@ -1665,7 +1745,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1665,7 +1745,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1665 dst_mcv = lhs;1745 dst_mcv = lhs;
1666 src_mcv = rhs;1746 src_mcv = rhs;
1667 }1747 }
1668 } else if (self.reuseOperand(inst, 1, rhs)) {1748 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {
1669 // RHS dies; use it as the destination.1749 // RHS dies; use it as the destination.
1670 // Both operands cannot be memory.1750 // Both operands cannot be memory.
1671 src_inst = op_lhs;1751 src_inst = op_lhs;
...@@ -1695,22 +1775,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1695,22 +1775,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1695 switch (src_mcv) {1775 switch (src_mcv) {
1696 .immediate => |imm| {1776 .immediate => |imm| {
1697 if (imm > math.maxInt(u31)) {1777 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) };
1699 }1779 }
1700 },1780 },
1701 else => {},1781 else => {},
1702 }1782 }
17031783
1704 // Now for step 2, we perform the actual op1784 // 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]) {
1706 // TODO: Generate wrapping and non-wrapping versions separately1788 // TODO: Generate wrapping and non-wrapping versions separately
1707 .add, .addwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 0, 0x00),1789 .add, .addwrap => try self.genX8664BinMathCode(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),1790 .bool_or, .bit_or => try self.genX8664BinMathCode(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),1791 .bool_and, .bit_and => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 4, 0x20),
1710 .sub, .subwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 5, 0x28),1792 .sub, .subwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 5, 0x28),
1711 .xor, .not => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 6, 0x30),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),
1714 else => unreachable,1796 else => unreachable,
1715 }1797 }
17161798
...@@ -1718,16 +1800,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1718,16 +1800,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1718 }1800 }
17191801
1720 /// Wrap over Instruction.encodeInto to translate errors1802 /// Wrap over Instruction.encodeInto to translate errors
1721 fn encodeX8664Instruction(1803 fn encodeX8664Instruction(self: *Self, inst: Instruction) !void {
1722 self: *Self,
1723 src: LazySrcLoc,
1724 inst: Instruction,
1725 ) !void {
1726 inst.encodeInto(self.code) catch |err| {1804 inst.encodeInto(self.code) catch |err| {
1727 if (err == error.OutOfMemory)1805 if (err == error.OutOfMemory)
1728 return error.OutOfMemory1806 return error.OutOfMemory
1729 else1807 else
1730 return self.fail(src, "Instruction.encodeInto failed because {s}", .{@errorName(err)});1808 return self.fail("Instruction.encodeInto failed because {s}", .{@errorName(err)});
1731 };1809 };
1732 }1810 }
17331811
...@@ -1799,7 +1877,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1799,7 +1877,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1799 /// d3 /opx | *r/m16/32/64*, CL (for context, CL is register 1)1877 /// d3 /opx | *r/m16/32/64*, CL (for context, CL is register 1)
1800 fn genX8664BinMathCode(1878 fn genX8664BinMathCode(
1801 self: *Self,1879 self: *Self,
1802 src: LazySrcLoc,
1803 dst_ty: Type,1880 dst_ty: Type,
1804 dst_mcv: MCValue,1881 dst_mcv: MCValue,
1805 src_mcv: MCValue,1882 src_mcv: MCValue,
...@@ -1817,7 +1894,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1817,7 +1894,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1817 .register => |dst_reg| {1894 .register => |dst_reg| {
1818 switch (src_mcv) {1895 switch (src_mcv) {
1819 .none => unreachable,1896 .none => unreachable,
1820 .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),1897 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
1821 .dead, .unreach => unreachable,1898 .dead, .unreach => unreachable,
1822 .ptr_stack_offset => unreachable,1899 .ptr_stack_offset => unreachable,
1823 .ptr_embedded_in_code => unreachable,1900 .ptr_embedded_in_code => unreachable,
...@@ -1871,7 +1948,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1871,7 +1948,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1871 }1948 }
1872 },1949 },
1873 .embedded_in_code, .memory => {1950 .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", .{});
1875 },1952 },
1876 .stack_offset => |off| {1953 .stack_offset => |off| {
1877 // register, indirect use mr + 31954 // register, indirect use mr + 3
...@@ -1879,7 +1956,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1879,7 +1956,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1879 const abi_size = dst_ty.abiSize(self.target.*);1956 const abi_size = dst_ty.abiSize(self.target.*);
1880 const adj_off = off + abi_size;1957 const adj_off = off + abi_size;
1881 if (off > math.maxInt(i32)) {1958 if (off > math.maxInt(i32)) {
1882 return self.fail(src, "stack offset too large", .{});1959 return self.fail("stack offset too large", .{});
1883 }1960 }
1884 const encoder = try X8664Encoder.init(self.code, 7);1961 const encoder = try X8664Encoder.init(self.code, 7);
1885 encoder.rex(.{1962 encoder.rex(.{
...@@ -1902,40 +1979,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1902,40 +1979,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1902 }1979 }
1903 },1980 },
1904 .compare_flags_unsigned => {1981 .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)", .{});
1906 },1983 },
1907 .compare_flags_signed => {1984 .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)", .{});
1909 },1986 },
1910 }1987 }
1911 },1988 },
1912 .stack_offset => |off| {1989 .stack_offset => |off| {
1913 switch (src_mcv) {1990 switch (src_mcv) {
1914 .none => unreachable,1991 .none => unreachable,
1915 .undef => return self.genSetStack(src, dst_ty, off, .undef),1992 .undef => return self.genSetStack(dst_ty, off, .undef),
1916 .dead, .unreach => unreachable,1993 .dead, .unreach => unreachable,
1917 .ptr_stack_offset => unreachable,1994 .ptr_stack_offset => unreachable,
1918 .ptr_embedded_in_code => unreachable,1995 .ptr_embedded_in_code => unreachable,
1919 .register => |src_reg| {1996 .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);
1921 },1998 },
1922 .immediate => |imm| {1999 .immediate => |imm| {
1923 _ = imm;2000 _ = 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", .{});
1925 },2002 },
1926 .embedded_in_code, .memory, .stack_offset => {2003 .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", .{});
1928 },2005 },
1929 .compare_flags_unsigned => {2006 .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)", .{});
1931 },2008 },
1932 .compare_flags_signed => {2009 .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)", .{});
1934 },2011 },
1935 }2012 }
1936 },2013 },
1937 .embedded_in_code, .memory => {2014 .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", .{});
1939 },2016 },
1940 }2017 }
1941 }2018 }
...@@ -1943,7 +2020,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1943,7 +2020,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1943 /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.2020 /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
1944 fn genX8664Imul(2021 fn genX8664Imul(
1945 self: *Self,2022 self: *Self,
1946 src: LazySrcLoc,
1947 dst_ty: Type,2023 dst_ty: Type,
1948 dst_mcv: MCValue,2024 dst_mcv: MCValue,
1949 src_mcv: MCValue,2025 src_mcv: MCValue,
...@@ -1959,7 +2035,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1959,7 +2035,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1959 .register => |dst_reg| {2035 .register => |dst_reg| {
1960 switch (src_mcv) {2036 switch (src_mcv) {
1961 .none => unreachable,2037 .none => unreachable,
1962 .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),2038 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
1963 .dead, .unreach => unreachable,2039 .dead, .unreach => unreachable,
1964 .ptr_stack_offset => unreachable,2040 .ptr_stack_offset => unreachable,
1965 .ptr_embedded_in_code => unreachable,2041 .ptr_embedded_in_code => unreachable,
...@@ -2025,31 +2101,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2025,31 +2101,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2025 );2101 );
2026 encoder.imm32(@intCast(i32, imm));2102 encoder.imm32(@intCast(i32, imm));
2027 } else {2103 } else {
2028 const src_reg = try self.copyToTmpRegister(src, dst_ty, src_mcv);2104 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
2029 return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg });2105 return self.genX8664Imul(dst_ty, dst_mcv, MCValue{ .register = src_reg });
2030 }2106 }
2031 },2107 },
2032 .embedded_in_code, .memory, .stack_offset => {2108 .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", .{});
2034 },2110 },
2035 .compare_flags_unsigned => {2111 .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)", .{});
2037 },2113 },
2038 .compare_flags_signed => {2114 .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)", .{});
2040 },2116 },
2041 }2117 }
2042 },2118 },
2043 .stack_offset => |off| {2119 .stack_offset => |off| {
2044 switch (src_mcv) {2120 switch (src_mcv) {
2045 .none => unreachable,2121 .none => unreachable,
2046 .undef => return self.genSetStack(src, dst_ty, off, .undef),2122 .undef => return self.genSetStack(dst_ty, off, .undef),
2047 .dead, .unreach => unreachable,2123 .dead, .unreach => unreachable,
2048 .ptr_stack_offset => unreachable,2124 .ptr_stack_offset => unreachable,
2049 .ptr_embedded_in_code => unreachable,2125 .ptr_embedded_in_code => unreachable,
2050 .register => |src_reg| {2126 .register => |src_reg| {
2051 // copy dst to a register2127 // 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);
2053 // multiply into dst_reg2129 // multiply into dst_reg
2054 // register, register2130 // register, register
2055 // Use the following imul opcode2131 // Use the following imul opcode
...@@ -2067,34 +2143,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2067,34 +2143,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2067 src_reg.low_id(),2143 src_reg.low_id(),
2068 );2144 );
2069 // copy dst_reg back out2145 // 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 });
2071 },2147 },
2072 .immediate => |imm| {2148 .immediate => |imm| {
2073 _ = imm;2149 _ = imm;
2074 return self.fail(src, "TODO implement x86 multiply source immediate", .{});2150 return self.fail("TODO implement x86 multiply source immediate", .{});
2075 },2151 },
2076 .embedded_in_code, .memory, .stack_offset => {2152 .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", .{});
2078 },2154 },
2079 .compare_flags_unsigned => {2155 .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)", .{});
2081 },2157 },
2082 .compare_flags_signed => {2158 .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)", .{});
2084 },2160 },
2085 }2161 }
2086 },2162 },
2087 .embedded_in_code, .memory => {2163 .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", .{});
2089 },2165 },
2090 }2166 }
2091 }2167 }
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 {
2094 const abi_size = ty.abiSize(self.target.*);2170 const abi_size = ty.abiSize(self.target.*);
2095 const adj_off = off + abi_size;2171 const adj_off = off + abi_size;
2096 if (off > math.maxInt(i32)) {2172 if (off > math.maxInt(i32)) {
2097 return self.fail(src, "stack offset too large", .{});2173 return self.fail("stack offset too large", .{});
2098 }2174 }
20992175
2100 const i_adj_off = -@intCast(i32, adj_off);2176 const i_adj_off = -@intCast(i32, adj_off);
...@@ -2121,8 +2197,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2121,8 +2197,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2121 }2197 }
2122 }2198 }
21232199
2124 fn genArgDbgInfo(self: *Self, inst: *ir.Inst.Arg, mcv: MCValue) !void {2200 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2125 const name_with_null = inst.name[0 .. mem.lenZ(inst.name) + 1];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
2127 switch (mcv) {2207 switch (mcv) {
2128 .register => |reg| {2208 .register => |reg| {
...@@ -2135,7 +2215,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2135,7 +2215,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2135 reg.dwarfLocOp(),2215 reg.dwarfLocOp(),
2136 });2216 });
2137 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);2217 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_ref42218 try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
2139 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string2219 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
2140 },2220 },
2141 .none => {},2221 .none => {},
...@@ -2146,12 +2226,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2146,12 +2226,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2146 .dwarf => |dbg_out| {2226 .dwarf => |dbg_out| {
2147 switch (arch) {2227 switch (arch) {
2148 .arm, .armeb => {2228 .arm, .armeb => {
2149 const ty = inst.base.ty;
2150 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {2229 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});
2152 };2231 };
2153 const adjusted_stack_offset = math.negateCast(offset + abi_size) catch {2232 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", .{});
2155 };2234 };
21562235
2157 try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter);2236 try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter);
...@@ -2167,7 +2246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2167,7 +2246,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2167 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);2246 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);
21682247
2169 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);2248 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_ref42249 try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
2171 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string2250 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
2172 },2251 },
2173 else => {},2252 else => {},
...@@ -2180,23 +2259,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2180,23 +2259,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2180 }2259 }
2181 }2260 }
21822261
2183 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {2262 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2184 const arg_index = self.arg_index;2263 const arg_index = self.arg_index;
2185 self.arg_index += 1;2264 self.arg_index += 1;
21862265
2266 const ty = self.air.typeOfIndex(inst);
2267
2187 const result = self.args[arg_index];2268 const result = self.args[arg_index];
2188 const mcv = switch (arch) {2269 const mcv = switch (arch) {
2189 // TODO support stack-only arguments on all target architectures2270 // TODO support stack-only arguments on all target architectures
2190 .arm, .armeb, .aarch64, .aarch64_32, .aarch64_be => switch (result) {2271 .arm, .armeb, .aarch64, .aarch64_32, .aarch64_be => switch (result) {
2191 // Copy registers to the stack2272 // Copy registers to the stack
2192 .register => |reg| blk: {2273 .register => |reg| blk: {
2193 const ty = inst.base.ty;
2194 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {2274 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});
2196 };2276 };
2197 const abi_align = ty.abiAlignment(self.target.*);2277 const abi_align = ty.abiAlignment(self.target.*);
2198 const stack_offset = try self.allocMem(&inst.base, abi_size, abi_align);2278 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
2199 try self.genSetStack(inst.base.src, ty, stack_offset, MCValue{ .register = reg });2279 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
22002280
2201 break :blk MCValue{ .stack_offset = stack_offset };2281 break :blk MCValue{ .stack_offset = stack_offset };
2202 },2282 },
...@@ -2206,20 +2286,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2206,20 +2286,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2206 };2286 };
2207 try self.genArgDbgInfo(inst, mcv);2287 try self.genArgDbgInfo(inst, mcv);
22082288
2209 if (inst.base.isUnused())2289 if (self.liveness.isUnused(inst))
2210 return MCValue.dead;2290 return self.finishAirBookkeeping();
22112291
2212 switch (mcv) {2292 switch (mcv) {
2213 .register => |reg| {2293 .register => |reg| {
2214 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);2294 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst);
2215 },2295 },
2216 else => {},2296 else => {},
2217 }2297 }
22182298
2219 return mcv;2299 return self.finishAir(inst, mcv, .{ .none, .none, .none });
2220 }2300 }
22212301
2222 fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue {2302 fn airBreakpoint(self: *Self) !void {
2223 switch (arch) {2303 switch (arch) {
2224 .i386, .x86_64 => {2304 .i386, .x86_64 => {
2225 try self.code.append(0xcc); // int32305 try self.code.append(0xcc); // int3
...@@ -2233,13 +2313,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2233,13 +2313,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2233 .aarch64 => {2313 .aarch64 => {
2234 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());2314 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());
2235 },2315 },
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}),
2237 }2317 }
2238 return .none;2318 return self.finishAirBookkeeping();
2239 }2319 }
22402320
2241 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {2321 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2242 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);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);
2243 defer info.deinit(self);2329 defer info.deinit(self);
22442330
2245 // Due to incremental compilation, how function calls are generated depends2331 // Due to incremental compilation, how function calls are generated depends
...@@ -2248,26 +2334,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2248,26 +2334,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2248 switch (arch) {2334 switch (arch) {
2249 .x86_64 => {2335 .x86_64 => {
2250 for (info.args) |mc_arg, arg_i| {2336 for (info.args) |mc_arg, arg_i| {
2251 const arg = inst.args[arg_i];2337 const arg = args[arg_i];
2252 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2338 const arg_ty = self.air.typeOf(arg);
2339 const arg_mcv = try self.resolveInst(args[arg_i]);
2253 // Here we do not use setRegOrMem even though the logic is similar, because2340 // Here we do not use setRegOrMem even though the logic is similar, because
2254 // the function call will move the stack pointer, so the offsets are different.2341 // the function call will move the stack pointer, so the offsets are different.
2255 switch (mc_arg) {2342 switch (mc_arg) {
2256 .none => continue,2343 .none => continue,
2257 .register => |reg| {2344 .register => |reg| {
2258 try self.register_manager.getReg(reg, null);2345 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);
2260 },2347 },
2261 .stack_offset => |off| {2348 .stack_offset => |off| {
2262 // Here we need to emit instructions like this:2349 // Here we need to emit instructions like this:
2263 // mov qword ptr [rsp + stack_offset], x2350 // 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);
2265 },2352 },
2266 .ptr_stack_offset => {2353 .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", .{});
2268 },2355 },
2269 .ptr_embedded_in_code => {2356 .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", .{});
2271 },2358 },
2272 .undef => unreachable,2359 .undef => unreachable,
2273 .immediate => unreachable,2360 .immediate => unreachable,
...@@ -2280,7 +2367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2280,7 +2367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2280 }2367 }
2281 }2368 }
22822369
2283 if (inst.func.value()) |func_value| {2370 if (self.air.value(callee)) |func_value| {
2284 if (func_value.castTag(.function)) |func_payload| {2371 if (func_value.castTag(.function)) |func_payload| {
2285 const func = func_payload.data;2372 const func = func_payload.data;
22862373
...@@ -2299,18 +2386,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2299,18 +2386,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2299 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });2386 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2300 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);2387 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
2301 } else if (func_value.castTag(.extern_fn)) |_| {2388 } 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", .{});
2303 } else {2390 } else {
2304 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2391 return self.fail("TODO implement calling bitcasted functions", .{});
2305 }2392 }
2306 } else {2393 } 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", .{});
2308 }2395 }
2309 },2396 },
2310 .riscv64 => {2397 .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| {
2314 if (func_value.castTag(.function)) |func_payload| {2401 if (func_value.castTag(.function)) |func_payload| {
2315 const func = func_payload.data;2402 const func = func_payload.data;
23162403
...@@ -2324,21 +2411,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2324,21 +2411,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2324 else2411 else
2325 unreachable;2412 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 });
2328 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());2415 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
2329 } else if (func_value.castTag(.extern_fn)) |_| {2416 } 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", .{});
2331 } else {2418 } else {
2332 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2419 return self.fail("TODO implement calling bitcasted functions", .{});
2333 }2420 }
2334 } else {2421 } 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", .{});
2336 }2423 }
2337 },2424 },
2338 .arm, .armeb => {2425 .arm, .armeb => {
2339 for (info.args) |mc_arg, arg_i| {2426 for (info.args) |mc_arg, arg_i| {
2340 const arg = inst.args[arg_i];2427 const arg = args[arg_i];
2341 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2428 const arg_ty = self.air.typeOf(arg);
2429 const arg_mcv = try self.resolveInst(args[arg_i]);
23422430
2343 switch (mc_arg) {2431 switch (mc_arg) {
2344 .none => continue,2432 .none => continue,
...@@ -2352,21 +2440,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2352,21 +2440,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2352 .compare_flags_unsigned => unreachable,2440 .compare_flags_unsigned => unreachable,
2353 .register => |reg| {2441 .register => |reg| {
2354 try self.register_manager.getReg(reg, null);2442 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);
2356 },2444 },
2357 .stack_offset => {2445 .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", .{});
2359 },2447 },
2360 .ptr_stack_offset => {2448 .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", .{});
2362 },2450 },
2363 .ptr_embedded_in_code => {2451 .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", .{});
2365 },2453 },
2366 }2454 }
2367 }2455 }
23682456
2369 if (inst.func.value()) |func_value| {2457 if (self.air.value(callee)) |func_value| {
2370 if (func_value.castTag(.function)) |func_payload| {2458 if (func_value.castTag(.function)) |func_payload| {
2371 const func = func_payload.data;2459 const func = func_payload.data;
2372 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2460 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
...@@ -2379,7 +2467,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2379,7 +2467,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2379 else2467 else
2380 unreachable;2468 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
2384 // TODO: add Instruction.supportedOn2472 // TODO: add Instruction.supportedOn
2385 // function for ARM2473 // function for ARM
...@@ -2390,18 +2478,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2390,18 +2478,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2390 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());2478 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
2391 }2479 }
2392 } else if (func_value.castTag(.extern_fn)) |_| {2480 } 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", .{});
2394 } else {2482 } else {
2395 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2483 return self.fail("TODO implement calling bitcasted functions", .{});
2396 }2484 }
2397 } else {2485 } 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", .{});
2399 }2487 }
2400 },2488 },
2401 .aarch64 => {2489 .aarch64 => {
2402 for (info.args) |mc_arg, arg_i| {2490 for (info.args) |mc_arg, arg_i| {
2403 const arg = inst.args[arg_i];2491 const arg = args[arg_i];
2404 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2492 const arg_ty = self.air.typeOf(arg);
2493 const arg_mcv = try self.resolveInst(args[arg_i]);
24052494
2406 switch (mc_arg) {2495 switch (mc_arg) {
2407 .none => continue,2496 .none => continue,
...@@ -2415,21 +2504,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2415,21 +2504,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2415 .compare_flags_unsigned => unreachable,2504 .compare_flags_unsigned => unreachable,
2416 .register => |reg| {2505 .register => |reg| {
2417 try self.register_manager.getReg(reg, null);2506 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);
2419 },2508 },
2420 .stack_offset => {2509 .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", .{});
2422 },2511 },
2423 .ptr_stack_offset => {2512 .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", .{});
2425 },2514 },
2426 .ptr_embedded_in_code => {2515 .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", .{});
2428 },2517 },
2429 }2518 }
2430 }2519 }
24312520
2432 if (inst.func.value()) |func_value| {2521 if (self.air.value(callee)) |func_value| {
2433 if (func_value.castTag(.function)) |func_payload| {2522 if (func_value.castTag(.function)) |func_payload| {
2434 const func = func_payload.data;2523 const func = func_payload.data;
2435 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2524 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
...@@ -2442,24 +2531,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2442,24 +2531,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2442 else2531 else
2443 unreachable;2532 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
2447 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2536 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2448 } else if (func_value.castTag(.extern_fn)) |_| {2537 } 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", .{});
2450 } else {2539 } else {
2451 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2540 return self.fail("TODO implement calling bitcasted functions", .{});
2452 }2541 }
2453 } else {2542 } 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", .{});
2455 }2544 }
2456 },2545 },
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}),
2458 }2547 }
2459 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {2548 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2460 for (info.args) |mc_arg, arg_i| {2549 for (info.args) |mc_arg, arg_i| {
2461 const arg = inst.args[arg_i];2550 const arg = args[arg_i];
2462 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2551 const arg_ty = self.air.typeOf(arg);
2552 const arg_mcv = try self.resolveInst(args[arg_i]);
2463 // Here we do not use setRegOrMem even though the logic is similar, because2553 // Here we do not use setRegOrMem even though the logic is similar, because
2464 // the function call will move the stack pointer, so the offsets are different.2554 // the function call will move the stack pointer, so the offsets are different.
2465 switch (mc_arg) {2555 switch (mc_arg) {
...@@ -2470,18 +2560,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2470,18 +2560,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2470 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),2560 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
2471 else => unreachable,2561 else => unreachable,
2472 }2562 }
2473 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2563 try self.genSetReg(arg_ty, reg, arg_mcv);
2474 },2564 },
2475 .stack_offset => {2565 .stack_offset => {
2476 // Here we need to emit instructions like this:2566 // Here we need to emit instructions like this:
2477 // mov qword ptr [rsp + stack_offset], x2567 // 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", .{});
2479 },2569 },
2480 .ptr_stack_offset => {2570 .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", .{});
2482 },2572 },
2483 .ptr_embedded_in_code => {2573 .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", .{});
2485 },2575 },
2486 .undef => unreachable,2576 .undef => unreachable,
2487 .immediate => unreachable,2577 .immediate => unreachable,
...@@ -2494,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2494,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2494 }2584 }
2495 }2585 }
24962586
2497 if (inst.func.value()) |func_value| {2587 if (self.air.value(callee)) |func_value| {
2498 if (func_value.castTag(.function)) |func_payload| {2588 if (func_value.castTag(.function)) |func_payload| {
2499 const func = func_payload.data;2589 const func = func_payload.data;
2500 const got_addr = blk: {2590 const got_addr = blk: {
...@@ -2505,13 +2595,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2505,13 +2595,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2505 log.debug("got_addr = 0x{x}", .{got_addr});2595 log.debug("got_addr = 0x{x}", .{got_addr});
2506 switch (arch) {2596 switch (arch) {
2507 .x86_64 => {2597 .x86_64 => {
2508 try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .memory = got_addr });2598 try self.genSetReg(Type.initTag(.u64), .rax, .{ .memory = got_addr });
2509 // callq *%rax2599 // callq *%rax
2510 try self.code.ensureCapacity(self.code.items.len + 2);2600 try self.code.ensureCapacity(self.code.items.len + 2);
2511 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });2601 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
2512 },2602 },
2513 .aarch64 => {2603 .aarch64 => {
2514 try self.genSetReg(inst.base.src, Type.initTag(.u64), .x30, .{ .memory = got_addr });2604 try self.genSetReg(Type.initTag(.u64), .x30, .{ .memory = got_addr });
2515 // blr x302605 // blr x30
2516 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2606 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2517 },2607 },
...@@ -2551,35 +2641,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2551,35 +2641,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2551 });2641 });
2552 // We mark the space and fix it up later.2642 // We mark the space and fix it up later.
2553 } else {2643 } else {
2554 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2644 return self.fail("TODO implement calling bitcasted functions", .{});
2555 }2645 }
2556 } else {2646 } else {
2557 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2647 return self.fail("TODO implement calling runtime known function pointer", .{});
2558 }2648 }
2559 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {2649 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2560 switch (arch) {2650 switch (arch) {
2561 .x86_64 => {2651 .x86_64 => {
2562 for (info.args) |mc_arg, arg_i| {2652 for (info.args) |mc_arg, arg_i| {
2563 const arg = inst.args[arg_i];2653 const arg = args[arg_i];
2564 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2654 const arg_ty = self.air.typeOf(arg);
2655 const arg_mcv = try self.resolveInst(args[arg_i]);
2565 // Here we do not use setRegOrMem even though the logic is similar, because2656 // Here we do not use setRegOrMem even though the logic is similar, because
2566 // the function call will move the stack pointer, so the offsets are different.2657 // the function call will move the stack pointer, so the offsets are different.
2567 switch (mc_arg) {2658 switch (mc_arg) {
2568 .none => continue,2659 .none => continue,
2569 .register => |reg| {2660 .register => |reg| {
2570 try self.register_manager.getReg(reg, null);2661 try self.register_manager.getReg(reg, null);
2571 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2662 try self.genSetReg(arg_ty, reg, arg_mcv);
2572 },2663 },
2573 .stack_offset => {2664 .stack_offset => {
2574 // Here we need to emit instructions like this:2665 // Here we need to emit instructions like this:
2575 // mov qword ptr [rsp + stack_offset], x2666 // mov qword ptr [rsp + stack_offset], x
2576 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2667 return self.fail("TODO implement calling with parameters in memory", .{});
2577 },2668 },
2578 .ptr_stack_offset => {2669 .ptr_stack_offset => {
2579 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2670 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2580 },2671 },
2581 .ptr_embedded_in_code => {2672 .ptr_embedded_in_code => {
2582 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2673 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2583 },2674 },
2584 .undef => unreachable,2675 .undef => unreachable,
2585 .immediate => unreachable,2676 .immediate => unreachable,
...@@ -2591,7 +2682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2591,7 +2682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2591 .compare_flags_unsigned => unreachable,2682 .compare_flags_unsigned => unreachable,
2592 }2683 }
2593 }2684 }
2594 if (inst.func.value()) |func_value| {2685 if (self.air.value(callee)) |func_value| {
2595 if (func_value.castTag(.function)) |func_payload| {2686 if (func_value.castTag(.function)) |func_payload| {
2596 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2687 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2597 const ptr_bytes: u64 = @divExact(ptr_bits, 8);2688 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
...@@ -2602,15 +2693,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2602,15 +2693,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2602 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });2693 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2603 const fn_got_addr = got_addr + got_index * ptr_bytes;2694 const fn_got_addr = got_addr + got_index * ptr_bytes;
2604 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));2695 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));
2605 } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{});2696 } else return self.fail("TODO implement calling extern fn on plan9", .{});
2606 } else {2697 } else {
2607 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2698 return self.fail("TODO implement calling runtime known function pointer", .{});
2608 }2699 }
2609 },2700 },
2610 .aarch64 => {2701 .aarch64 => {
2611 for (info.args) |mc_arg, arg_i| {2702 for (info.args) |mc_arg, arg_i| {
2612 const arg = inst.args[arg_i];2703 const arg = args[arg_i];
2613 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2704 const arg_ty = self.air.typeOf(arg);
2705 const arg_mcv = try self.resolveInst(args[arg_i]);
26142706
2615 switch (mc_arg) {2707 switch (mc_arg) {
2616 .none => continue,2708 .none => continue,
...@@ -2624,20 +2716,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2624,20 +2716,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2624 .compare_flags_unsigned => unreachable,2716 .compare_flags_unsigned => unreachable,
2625 .register => |reg| {2717 .register => |reg| {
2626 try self.register_manager.getReg(reg, null);2718 try self.register_manager.getReg(reg, null);
2627 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2719 try self.genSetReg(arg_ty, reg, arg_mcv);
2628 },2720 },
2629 .stack_offset => {2721 .stack_offset => {
2630 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2722 return self.fail("TODO implement calling with parameters in memory", .{});
2631 },2723 },
2632 .ptr_stack_offset => {2724 .ptr_stack_offset => {
2633 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2725 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2634 },2726 },
2635 .ptr_embedded_in_code => {2727 .ptr_embedded_in_code => {
2636 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2728 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2637 },2729 },
2638 }2730 }
2639 }2731 }
2640 if (inst.func.value()) |func_value| {2732 if (self.air.value(callee)) |func_value| {
2641 if (func_value.castTag(.function)) |func_payload| {2733 if (func_value.castTag(.function)) |func_payload| {
2642 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2734 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2643 const ptr_bytes: u64 = @divExact(ptr_bits, 8);2735 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
...@@ -2645,65 +2737,84 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2645,65 +2737,84 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2645 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;2737 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
2646 const fn_got_addr = got_addr + got_index * ptr_bytes;2738 const fn_got_addr = got_addr + got_index * ptr_bytes;
26472739
2648 try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });2740 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
26492741
2650 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2742 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2651 } else if (func_value.castTag(.extern_fn)) |_| {2743 } else if (func_value.castTag(.extern_fn)) |_| {
2652 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});2744 return self.fail("TODO implement calling extern functions", .{});
2653 } else {2745 } else {
2654 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2746 return self.fail("TODO implement calling bitcasted functions", .{});
2655 }2747 }
2656 } else {2748 } else {
2657 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2749 return self.fail("TODO implement calling runtime known function pointer", .{});
2658 }2750 }
2659 },2751 },
2660 else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}),2752 else => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
2661 }2753 }
2662 } else unreachable;2754 } else unreachable;
26632755
2664 switch (info.return_value) {2756 const result: MCValue = result: {
2665 .register => |reg| {2757 switch (info.return_value) {
2666 if (Register.allocIndex(reg) == null) {2758 .register => |reg| {
2667 // Save function return value in a callee saved register2759 if (Register.allocIndex(reg) == null) {
2668 return try self.copyToNewRegister(&inst.base, info.return_value);2760 // Save function return value in a callee saved register
2669 }2761 break :result try self.copyToNewRegister(inst, info.return_value);
2670 },2762 }
2671 else => {},2763 },
2672 }2764 else => {},
2765 }
2766 break :result info.return_value;
2767 };
26732768
2674 return info.return_value;2769 if (args.len <= Liveness.bpi - 2) {
2770 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2771 buf[0] = callee;
2772 std.mem.copy(Air.Inst.Ref, buf[1..], args);
2773 return self.finishAir(inst, result, buf);
2774 }
2775 var bt = try self.iterateBigTomb(inst, 1 + args.len);
2776 bt.feed(callee);
2777 for (args) |arg| {
2778 bt.feed(arg);
2779 }
2780 return bt.finishAir(result);
2675 }2781 }
26762782
2677 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {2783 fn airRef(self: *Self, inst: Air.Inst.Index) !void {
2678 const operand = try self.resolveInst(inst.operand);2784 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2679 switch (operand) {2785 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2680 .unreach => unreachable,2786 const operand_ty = self.air.typeOf(ty_op.operand);
2681 .dead => unreachable,2787 const operand = try self.resolveInst(ty_op.operand);
2682 .none => return .none,2788 switch (operand) {
26832789 .unreach => unreachable,
2684 .immediate,2790 .dead => unreachable,
2685 .register,2791 .none => break :result MCValue{ .none = {} },
2686 .ptr_stack_offset,2792
2687 .ptr_embedded_in_code,2793 .immediate,
2688 .compare_flags_unsigned,2794 .register,
2689 .compare_flags_signed,2795 .ptr_stack_offset,
2690 => {2796 .ptr_embedded_in_code,
2691 const stack_offset = try self.allocMemPtr(&inst.base);2797 .compare_flags_unsigned,
2692 try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);2798 .compare_flags_signed,
2693 return MCValue{ .ptr_stack_offset = stack_offset };2799 => {
2694 },2800 const stack_offset = try self.allocMemPtr(inst);
2801 try self.genSetStack(operand_ty, stack_offset, operand);
2802 break :result MCValue{ .ptr_stack_offset = stack_offset };
2803 },
26952804
2696 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },2805 .stack_offset => |offset| break :result MCValue{ .ptr_stack_offset = offset },
2697 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },2806 .embedded_in_code => |offset| break :result MCValue{ .ptr_embedded_in_code = offset },
2698 .memory => |vaddr| return MCValue{ .immediate = vaddr },2807 .memory => |vaddr| break :result MCValue{ .immediate = vaddr },
26992808
2700 .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),2809 .undef => return self.fail("TODO implement ref on an undefined value", .{}),
2701 }2810 }
2811 };
2812 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2702 }2813 }
27032814
2704 fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue {2815 fn ret(self: *Self, mcv: MCValue) !void {
2705 const ret_ty = self.fn_type.fnReturnType();2816 const ret_ty = self.fn_type.fnReturnType();
2706 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);2817 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
2707 switch (arch) {2818 switch (arch) {
2708 .i386 => {2819 .i386 => {
2709 try self.code.append(0xc3); // ret2820 try self.code.append(0xc3); // ret
...@@ -2729,58 +2840,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2729,58 +2840,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2729 try self.code.resize(self.code.items.len + 4);2840 try self.code.resize(self.code.items.len + 4);
2730 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);2841 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
2731 },2842 },
2732 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),2843 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),
2733 }2844 }
2734 return .unreach;
2735 }2845 }
27362846
2737 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {2847 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
2738 const operand = try self.resolveInst(inst.operand);2848 const un_op = self.air.instructions.items(.data)[inst].un_op;
2739 return self.ret(inst.base.src, operand);2849 const operand = try self.resolveInst(un_op);
2850 try self.ret(operand);
2851 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
2740 }2852 }
27412853
2742 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {2854 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2743 return self.ret(inst.base.src, .none);2855 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2744 }2856 if (self.liveness.isUnused(inst))
27452857 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2746 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: math.CompareOperator) !MCValue {2858 const ty = self.air.typeOf(bin_op.lhs);
2747 // No side effects, so if it's unreferenced, do nothing.2859 assert(ty.eql(self.air.typeOf(bin_op.rhs)));
2748 if (inst.base.isUnused())2860 if (ty.zigTypeTag() == .ErrorSet)
2749 return MCValue{ .dead = {} };2861 return self.fail("TODO implement cmp for errors", .{});
2750 if (inst.lhs.ty.zigTypeTag() == .ErrorSet or inst.rhs.ty.zigTypeTag() == .ErrorSet)2862
2751 return self.fail(inst.base.src, "TODO implement cmp for errors", .{});2863 const lhs = try self.resolveInst(bin_op.lhs);
2752 switch (arch) {2864 const rhs = try self.resolveInst(bin_op.rhs);
2753 .x86_64 => {2865 const result: MCValue = switch (arch) {
2866 .x86_64 => result: {
2754 try self.code.ensureCapacity(self.code.items.len + 8);2867 try self.code.ensureCapacity(self.code.items.len + 8);
27552868
2756 const lhs = try self.resolveInst(inst.lhs);
2757 const rhs = try self.resolveInst(inst.rhs);
2758
2759 // There are 2 operands, destination and source.2869 // There are 2 operands, destination and source.
2760 // Either one, but not both, can be a memory operand.2870 // Either one, but not both, can be a memory operand.
2761 // Source operand can be an immediate, 8 bits or 32 bits.2871 // Source operand can be an immediate, 8 bits or 32 bits.
2762 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))2872 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
2763 try self.copyToNewRegister(&inst.base, lhs)2873 try self.copyToNewRegister(inst, lhs)
2764 else2874 else
2765 lhs;2875 lhs;
2766 // This instruction supports only signed 32-bit immediates at most.2876 // This instruction supports only signed 32-bit immediates at most.
2767 const src_mcv = try self.limitImmediateType(inst.rhs, i32);2877 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
27682878
2769 try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);2879 try self.genX8664BinMathCode(Type.initTag(.bool), dst_mcv, src_mcv, 7, 0x38);
2770 const info = inst.lhs.ty.intInfo(self.target.*);2880 const info = ty.intInfo(self.target.*);
2771 return switch (info.signedness) {2881 break :result switch (info.signedness) {
2772 .signed => MCValue{ .compare_flags_signed = op },2882 .signed => MCValue{ .compare_flags_signed = op },
2773 .unsigned => MCValue{ .compare_flags_unsigned = op },2883 .unsigned => MCValue{ .compare_flags_unsigned = op },
2774 };2884 };
2775 },2885 },
2776 .arm, .armeb => {2886 .arm, .armeb => result: {
2777 const lhs = try self.resolveInst(inst.lhs);
2778 const rhs = try self.resolveInst(inst.rhs);
2779
2780 const lhs_is_register = lhs == .register;2887 const lhs_is_register = lhs == .register;
2781 const rhs_is_register = rhs == .register;2888 const rhs_is_register = rhs == .register;
2782 // lhs should always be a register2889 // lhs should always be a register
2783 const rhs_should_be_register = try self.armOperandShouldBeRegister(inst.rhs.src, rhs);2890 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
27842891
2785 var lhs_mcv = lhs;2892 var lhs_mcv = lhs;
2786 var rhs_mcv = rhs;2893 var rhs_mcv = rhs;
...@@ -2788,53 +2895,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2788,53 +2895,57 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2788 // Allocate registers2895 // Allocate registers
2789 if (rhs_should_be_register) {2896 if (rhs_should_be_register) {
2790 if (!lhs_is_register and !rhs_is_register) {2897 if (!lhs_is_register and !rhs_is_register) {
2791 const regs = try self.register_manager.allocRegs(2, .{ inst.rhs, inst.lhs }, &.{});2898 const regs = try self.register_manager.allocRegs(2, .{
2899 Air.refToIndex(bin_op.rhs).?, Air.refToIndex(bin_op.lhs).?,
2900 }, &.{});
2792 lhs_mcv = MCValue{ .register = regs[0] };2901 lhs_mcv = MCValue{ .register = regs[0] };
2793 rhs_mcv = MCValue{ .register = regs[1] };2902 rhs_mcv = MCValue{ .register = regs[1] };
2794 } else if (!rhs_is_register) {2903 } else if (!rhs_is_register) {
2795 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.rhs, &.{}) };2904 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.rhs).?, &.{}) };
2796 }2905 }
2797 }2906 }
2798 if (!lhs_is_register) {2907 if (!lhs_is_register) {
2799 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.lhs, &.{}) };2908 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?, &.{}) };
2800 }2909 }
28012910
2802 // Move the operands to the newly allocated registers2911 // Move the operands to the newly allocated registers
2803 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];2912 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2804 if (lhs_mcv == .register and !lhs_is_register) {2913 if (lhs_mcv == .register and !lhs_is_register) {
2805 try self.genSetReg(inst.lhs.src, inst.lhs.ty, lhs_mcv.register, lhs);2914 try self.genSetReg(ty, lhs_mcv.register, lhs);
2806 branch.inst_table.putAssumeCapacity(inst.lhs, lhs);2915 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs);
2807 }2916 }
2808 if (rhs_mcv == .register and !rhs_is_register) {2917 if (rhs_mcv == .register and !rhs_is_register) {
2809 try self.genSetReg(inst.rhs.src, inst.rhs.ty, rhs_mcv.register, rhs);2918 try self.genSetReg(ty, rhs_mcv.register, rhs);
2810 branch.inst_table.putAssumeCapacity(inst.rhs, rhs);2919 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs);
2811 }2920 }
28122921
2813 // The destination register is not present in the cmp instruction2922 // The destination register is not present in the cmp instruction
2814 try self.genArmBinOpCode(inst.base.src, undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);2923 try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
28152924
2816 const info = inst.lhs.ty.intInfo(self.target.*);2925 const info = ty.intInfo(self.target.*);
2817 return switch (info.signedness) {2926 break :result switch (info.signedness) {
2818 .signed => MCValue{ .compare_flags_signed = op },2927 .signed => MCValue{ .compare_flags_signed = op },
2819 .unsigned => MCValue{ .compare_flags_unsigned = op },2928 .unsigned => MCValue{ .compare_flags_unsigned = op },
2820 };2929 };
2821 },2930 },
2822 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),2931 else => return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}),
2823 }2932 };
2933 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2824 }2934 }
28252935
2826 fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue {2936 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
2827 // TODO when reworking AIR memory layout, rework source locations here as2937 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
2828 // well to be more efficient, as well as support inlined function calls correctly.2938 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
2829 // For now we convert LazySrcLoc to absolute byte offset, to match what the2939 return self.finishAirBookkeeping();
2830 // existing codegen code expects.
2831 try self.dbgAdvancePCAndLine(inst.line, inst.column);
2832 assert(inst.base.isUnused());
2833 return MCValue.dead;
2834 }2940 }
28352941
2836 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {2942 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2837 const cond = try self.resolveInst(inst.condition);2943 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2944 const cond = try self.resolveInst(pl_op.operand);
2945 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2946 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2947 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2948 const liveness_condbr = self.liveness.getCondBr(inst);
28382949
2839 const reloc: Reloc = switch (arch) {2950 const reloc: Reloc = switch (arch) {
2840 .i386, .x86_64 => reloc: {2951 .i386, .x86_64 => reloc: {
...@@ -2883,7 +2994,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2883,7 +2994,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2883 encoder.disp8(1);2994 encoder.disp8(1);
2884 break :blk 0x84;2995 break :blk 0x84;
2885 },2996 },
2886 else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),2997 else => return self.fail("TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
2887 };2998 };
2888 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });2999 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
2889 const reloc = Reloc{ .rel32 = self.code.items.len };3000 const reloc = Reloc{ .rel32 = self.code.items.len };
...@@ -2909,7 +3020,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2909,7 +3020,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2909 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());3020 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());
2910 break :blk .ne;3021 break :blk .ne;
2911 },3022 },
2912 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),3023 else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
2913 };3024 };
29143025
2915 const reloc = Reloc{3026 const reloc = Reloc{
...@@ -2921,7 +3032,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2921,7 +3032,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2921 try self.code.resize(self.code.items.len + 4);3032 try self.code.resize(self.code.items.len + 4);
2922 break :reloc reloc;3033 break :reloc reloc;
2923 },3034 },
2924 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}),3035 else => return self.fail("TODO implement condbr {}", .{self.target.cpu.arch}),
2925 };3036 };
29263037
2927 // Capture the state of register and stack allocation state so that we can revert to it.3038 // Capture the state of register and stack allocation state so that we can revert to it.
...@@ -2933,12 +3044,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2933,12 +3044,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29333044
2934 try self.branch_stack.append(.{});3045 try self.branch_stack.append(.{});
29353046
2936 const then_deaths = inst.thenDeaths();3047 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
2937 try self.ensureProcessDeathCapacity(then_deaths.len);3048 for (liveness_condbr.then_deaths) |operand| {
2938 for (then_deaths) |operand| {
2939 self.processDeath(operand);3049 self.processDeath(operand);
2940 }3050 }
2941 try self.genBody(inst.then_body);3051 try self.genBody(then_body);
29423052
2943 // Revert to the previous register and stack allocation state.3053 // Revert to the previous register and stack allocation state.
29443054
...@@ -2954,16 +3064,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2954,16 +3064,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2954 self.next_stack_offset = parent_next_stack_offset;3064 self.next_stack_offset = parent_next_stack_offset;
2955 self.register_manager.free_registers = parent_free_registers;3065 self.register_manager.free_registers = parent_free_registers;
29563066
2957 try self.performReloc(inst.base.src, reloc);3067 try self.performReloc(reloc);
2958 const else_branch = self.branch_stack.addOneAssumeCapacity();3068 const else_branch = self.branch_stack.addOneAssumeCapacity();
2959 else_branch.* = .{};3069 else_branch.* = .{};
29603070
2961 const else_deaths = inst.elseDeaths();3071 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
2962 try self.ensureProcessDeathCapacity(else_deaths.len);3072 for (liveness_condbr.else_deaths) |operand| {
2963 for (else_deaths) |operand| {
2964 self.processDeath(operand);3073 self.processDeath(operand);
2965 }3074 }
2966 try self.genBody(inst.else_body);3075 try self.genBody(else_body);
29673076
2968 // At this point, each branch will possibly have conflicting values for where3077 // At this point, each branch will possibly have conflicting values for where
2969 // each instruction is stored. They agree, however, on which instructions are alive/dead.3078 // each instruction is stored. They agree, however, on which instructions are alive/dead.
...@@ -2974,8 +3083,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2974,8 +3083,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2974 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers3083 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
2975 // rather than assigning it.3084 // rather than assigning it.
2976 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];3085 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
2977 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +3086 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
2978 else_branch.inst_table.count());
29793087
2980 const else_slice = else_branch.inst_table.entries.slice();3088 const else_slice = else_branch.inst_table.entries.slice();
2981 const else_keys = else_slice.items(.key);3089 const else_keys = else_slice.items(.key);
...@@ -3003,14 +3111,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3003,14 +3111,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3003 }3111 }
3004 }3112 }
3005 };3113 };
3006 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });3114 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
3007 // TODO make sure the destination stack offset / register does not already have something3115 // TODO make sure the destination stack offset / register does not already have something
3008 // going on there.3116 // going on there.
3009 try self.setRegOrMem(inst.base.src, else_key.ty, canon_mcv, else_value);3117 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
3010 // TODO track the new register / stack allocation3118 // TODO track the new register / stack allocation
3011 }3119 }
3012 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +3120 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
3013 saved_then_branch.inst_table.count());
3014 const then_slice = saved_then_branch.inst_table.entries.slice();3121 const then_slice = saved_then_branch.inst_table.entries.slice();
3015 const then_keys = then_slice.items(.key);3122 const then_keys = then_slice.items(.key);
3016 const then_values = then_slice.items(.value);3123 const then_values = then_slice.items(.value);
...@@ -3031,70 +3138,175 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3031,70 +3138,175 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3031 }3138 }
3032 }3139 }
3033 };3140 };
3034 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });3141 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
3035 // TODO make sure the destination stack offset / register does not already have something3142 // TODO make sure the destination stack offset / register does not already have something
3036 // going on there.3143 // going on there.
3037 try self.setRegOrMem(inst.base.src, then_key.ty, parent_mcv, then_value);3144 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
3038 // TODO track the new register / stack allocation3145 // TODO track the new register / stack allocation
3039 }3146 }
30403147
3041 self.branch_stack.pop().deinit(self.gpa);3148 self.branch_stack.pop().deinit(self.gpa);
30423149
3043 return MCValue.unreach;3150 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
3044 }3151 }
30453152
3046 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3153 fn isNull(self: *Self, operand: MCValue) !MCValue {
3154 _ = operand;
3155 // Here you can specialize this instruction if it makes sense to, otherwise the default
3156 // will call isNonNull and invert the result.
3047 switch (arch) {3157 switch (arch) {
3048 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),3158 else => return self.fail("TODO call isNonNull and invert the result", .{}),
3049 }3159 }
3050 }3160 }
30513161
3052 fn genIsNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3162 fn isNonNull(self: *Self, operand: MCValue) !MCValue {
3053 return self.fail(inst.base.src, "TODO load the operand and call genIsNull", .{});3163 _ = operand;
3054 }
3055
3056 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3057 // Here you can specialize this instruction if it makes sense to, otherwise the default3164 // Here you can specialize this instruction if it makes sense to, otherwise the default
3058 // will call genIsNull and invert the result.3165 // will call isNull and invert the result.
3059 switch (arch) {3166 switch (arch) {
3060 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),3167 else => return self.fail("TODO call isNull and invert the result", .{}),
3061 }3168 }
3062 }3169 }
30633170
3064 fn genIsNonNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3171 fn isErr(self: *Self, operand: MCValue) !MCValue {
3065 return self.fail(inst.base.src, "TODO load the operand and call genIsNonNull", .{});3172 _ = operand;
3173 // Here you can specialize this instruction if it makes sense to, otherwise the default
3174 // will call isNonNull and invert the result.
3175 switch (arch) {
3176 else => return self.fail("TODO call isNonErr and invert the result", .{}),
3177 }
3066 }3178 }
30673179
3068 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3180 fn isNonErr(self: *Self, operand: MCValue) !MCValue {
3181 _ = operand;
3182 // Here you can specialize this instruction if it makes sense to, otherwise the default
3183 // will call isNull and invert the result.
3069 switch (arch) {3184 switch (arch) {
3070 else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),3185 else => return self.fail("TODO call isErr and invert the result", .{}),
3071 }3186 }
3072 }3187 }
30733188
3074 fn genIsErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3189 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
3075 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});3190 const un_op = self.air.instructions.items(.data)[inst].un_op;
3191 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3192 const operand = try self.resolveInst(un_op);
3193 break :result try self.isNull(operand);
3194 };
3195 return self.finishAir(inst, result, .{ un_op, .none, .none });
3076 }3196 }
30773197
3078 fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3198 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3079 switch (arch) {3199 const un_op = self.air.instructions.items(.data)[inst].un_op;
3080 else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}),3200 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3081 }3201 const operand_ptr = try self.resolveInst(un_op);
3202 const operand: MCValue = blk: {
3203 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3204 // The MCValue that holds the pointer can be re-used as the value.
3205 break :blk operand_ptr;
3206 } else {
3207 break :blk try self.allocRegOrMem(inst, true);
3208 }
3209 };
3210 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3211 break :result try self.isNull(operand);
3212 };
3213 return self.finishAir(inst, result, .{ un_op, .none, .none });
3214 }
3215
3216 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
3217 const un_op = self.air.instructions.items(.data)[inst].un_op;
3218 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3219 const operand = try self.resolveInst(un_op);
3220 break :result try self.isNonNull(operand);
3221 };
3222 return self.finishAir(inst, result, .{ un_op, .none, .none });
3223 }
3224
3225 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3226 const un_op = self.air.instructions.items(.data)[inst].un_op;
3227 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3228 const operand_ptr = try self.resolveInst(un_op);
3229 const operand: MCValue = blk: {
3230 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3231 // The MCValue that holds the pointer can be re-used as the value.
3232 break :blk operand_ptr;
3233 } else {
3234 break :blk try self.allocRegOrMem(inst, true);
3235 }
3236 };
3237 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3238 break :result try self.isNonNull(operand);
3239 };
3240 return self.finishAir(inst, result, .{ un_op, .none, .none });
3241 }
3242
3243 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
3244 const un_op = self.air.instructions.items(.data)[inst].un_op;
3245 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3246 const operand = try self.resolveInst(un_op);
3247 break :result try self.isErr(operand);
3248 };
3249 return self.finishAir(inst, result, .{ un_op, .none, .none });
3250 }
3251
3252 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3253 const un_op = self.air.instructions.items(.data)[inst].un_op;
3254 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3255 const operand_ptr = try self.resolveInst(un_op);
3256 const operand: MCValue = blk: {
3257 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3258 // The MCValue that holds the pointer can be re-used as the value.
3259 break :blk operand_ptr;
3260 } else {
3261 break :blk try self.allocRegOrMem(inst, true);
3262 }
3263 };
3264 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3265 break :result try self.isErr(operand);
3266 };
3267 return self.finishAir(inst, result, .{ un_op, .none, .none });
3082 }3268 }
30833269
3084 fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3270 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
3085 return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{});3271 const un_op = self.air.instructions.items(.data)[inst].un_op;
3272 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3273 const operand = try self.resolveInst(un_op);
3274 break :result try self.isNonErr(operand);
3275 };
3276 return self.finishAir(inst, result, .{ un_op, .none, .none });
3086 }3277 }
30873278
3088 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {3279 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3280 const un_op = self.air.instructions.items(.data)[inst].un_op;
3281 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3282 const operand_ptr = try self.resolveInst(un_op);
3283 const operand: MCValue = blk: {
3284 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3285 // The MCValue that holds the pointer can be re-used as the value.
3286 break :blk operand_ptr;
3287 } else {
3288 break :blk try self.allocRegOrMem(inst, true);
3289 }
3290 };
3291 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3292 break :result try self.isNonErr(operand);
3293 };
3294 return self.finishAir(inst, result, .{ un_op, .none, .none });
3295 }
3296
3297 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
3089 // A loop is a setup to be able to jump back to the beginning.3298 // A loop is a setup to be able to jump back to the beginning.
3299 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3300 const loop = self.air.extraData(Air.Block, ty_pl.payload);
3301 const body = self.air.extra[loop.end..][0..loop.data.body_len];
3090 const start_index = self.code.items.len;3302 const start_index = self.code.items.len;
3091 try self.genBody(inst.body);3303 try self.genBody(body);
3092 try self.jump(inst.base.src, start_index);3304 try self.jump(start_index);
3093 return MCValue.unreach;3305 return self.finishAirBookkeeping();
3094 }3306 }
30953307
3096 /// Send control flow to the `index` of `self.code`.3308 /// Send control flow to the `index` of `self.code`.
3097 fn jump(self: *Self, src: LazySrcLoc, index: usize) !void {3309 fn jump(self: *Self, index: usize) !void {
3098 switch (arch) {3310 switch (arch) {
3099 .i386, .x86_64 => {3311 .i386, .x86_64 => {
3100 try self.code.ensureCapacity(self.code.items.len + 5);3312 try self.code.ensureCapacity(self.code.items.len + 5);
...@@ -3111,21 +3323,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3111,21 +3323,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3111 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {3323 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
3112 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());3324 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());
3113 } else |_| {3325 } else |_| {
3114 return self.fail(src, "TODO: enable larger branch offset", .{});3326 return self.fail("TODO: enable larger branch offset", .{});
3115 }3327 }
3116 },3328 },
3117 .aarch64, .aarch64_be, .aarch64_32 => {3329 .aarch64, .aarch64_be, .aarch64_32 => {
3118 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {3330 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
3119 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());3331 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());
3120 } else |_| {3332 } else |_| {
3121 return self.fail(src, "TODO: enable larger branch offset", .{});3333 return self.fail("TODO: enable larger branch offset", .{});
3122 }3334 }
3123 },3335 },
3124 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),3336 else => return self.fail("TODO implement jump for {}", .{self.target.cpu.arch}),
3125 }3337 }
3126 }3338 }
31273339
3128 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {3340 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
3129 try self.blocks.putNoClobber(self.gpa, inst, .{3341 try self.blocks.putNoClobber(self.gpa, inst, .{
3130 // A block is a setup to be able to jump to the end.3342 // A block is a setup to be able to jump to the end.
3131 .relocs = .{},3343 .relocs = .{},
...@@ -3139,20 +3351,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3139,20 +3351,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3139 const block_data = self.blocks.getPtr(inst).?;3351 const block_data = self.blocks.getPtr(inst).?;
3140 defer block_data.relocs.deinit(self.gpa);3352 defer block_data.relocs.deinit(self.gpa);
31413353
3142 try self.genBody(inst.body);3354 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3355 const extra = self.air.extraData(Air.Block, ty_pl.payload);
3356 const body = self.air.extra[extra.end..][0..extra.data.body_len];
3357 try self.genBody(body);
31433358
3144 for (block_data.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);3359 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
31453360
3146 return @bitCast(MCValue, block_data.mcv);3361 const result = @bitCast(MCValue, block_data.mcv);
3362 return self.finishAir(inst, result, .{ .none, .none, .none });
3147 }3363 }
31483364
3149 fn genSwitch(self: *Self, inst: *ir.Inst.SwitchBr) !MCValue {3365 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
3366 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3367 const condition = pl_op.operand;
3150 switch (arch) {3368 switch (arch) {
3151 else => return self.fail(inst.base.src, "TODO genSwitch for {}", .{self.target.cpu.arch}),3369 else => return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}),
3152 }3370 }
3371 return self.finishAir(inst, .dead, .{ condition, .none, .none });
3153 }3372 }
31543373
3155 fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void {3374 fn performReloc(self: *Self, reloc: Reloc) !void {
3156 switch (reloc) {3375 switch (reloc) {
3157 .rel32 => |pos| {3376 .rel32 => |pos| {
3158 const amt = self.code.items.len - (pos + 4);3377 const amt = self.code.items.len - (pos + 4);
...@@ -3163,7 +3382,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3163,7 +3382,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3163 // best place to elide jumps will be in semantic analysis, by inlining blocks that only3382 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
3164 // only have 1 break instruction.3383 // only have 1 break instruction.
3165 const s32_amt = math.cast(i32, amt) catch3384 const s32_amt = math.cast(i32, amt) catch
3166 return self.fail(src, "unable to perform relocation: jump too far", .{});3385 return self.fail("unable to perform relocation: jump too far", .{});
3167 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);3386 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
3168 },3387 },
3169 .arm_branch => |info| {3388 .arm_branch => |info| {
...@@ -3173,7 +3392,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3173,7 +3392,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3173 if (math.cast(i26, amt)) |delta| {3392 if (math.cast(i26, amt)) |delta| {
3174 writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());3393 writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());
3175 } else |_| {3394 } else |_| {
3176 return self.fail(src, "TODO: enable larger branch offset", .{});3395 return self.fail("TODO: enable larger branch offset", .{});
3177 }3396 }
3178 },3397 },
3179 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch3398 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch
...@@ -3182,56 +3401,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3182,56 +3401,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3182 }3401 }
3183 }3402 }
31843403
3185 fn genBrBlockFlat(self: *Self, inst: *ir.Inst.BrBlockFlat) !MCValue {3404 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
3186 try self.genBody(inst.body);3405 const branch = self.air.instructions.items(.data)[inst].br;
3187 const last = inst.body.instructions[inst.body.instructions.len - 1];3406 try self.br(branch.block_inst, branch.operand);
3188 return self.br(inst.base.src, inst.block, last);3407 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
3189 }
3190
3191 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
3192 return self.br(inst.base.src, inst.block, inst.operand);
3193 }3408 }
31943409
3195 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {3410 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
3196 return self.brVoid(inst.base.src, inst.block);3411 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3197 }3412 const air_tags = self.air.instructions.items(.tag);
31983413 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
3199 fn genBoolOp(self: *Self, inst: *ir.Inst.BinOp) !MCValue {3414 .x86_64 => switch (air_tags[inst]) {
3200 if (inst.base.isUnused())
3201 return MCValue.dead;
3202 switch (arch) {
3203 .x86_64 => switch (inst.base.tag) {
3204 // lhs AND rhs3415 // lhs AND rhs
3205 .bool_and => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),3416 .bool_and => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
3206 // lhs OR rhs3417 // lhs OR rhs
3207 .bool_or => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),3418 .bool_or => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
3208 else => unreachable, // Not a boolean operation3419 else => unreachable, // Not a boolean operation
3209 },3420 },
3210 .arm, .armeb => switch (inst.base.tag) {3421 .arm, .armeb => switch (air_tags[inst]) {
3211 .bool_and => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_and),3422 .bool_and => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),
3212 .bool_or => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_or),3423 .bool_or => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),
3213 else => unreachable, // Not a boolean operation3424 else => unreachable, // Not a boolean operation
3214 },3425 },
3215 else => return self.fail(inst.base.src, "TODO implement boolean operations for {}", .{self.target.cpu.arch}),3426 else => return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}),
3216 }3427 };
3428 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3217 }3429 }
32183430
3219 fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {3431 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
3220 const block_data = self.blocks.getPtr(block).?;3432 const block_data = self.blocks.getPtr(block).?;
32213433
3222 if (operand.ty.hasCodeGenBits()) {3434 if (self.air.typeOf(operand).hasCodeGenBits()) {
3223 const operand_mcv = try self.resolveInst(operand);3435 const operand_mcv = try self.resolveInst(operand);
3224 const block_mcv = block_data.mcv;3436 const block_mcv = block_data.mcv;
3225 if (block_mcv == .none) {3437 if (block_mcv == .none) {
3226 block_data.mcv = operand_mcv;3438 block_data.mcv = operand_mcv;
3227 } else {3439 } else {
3228 try self.setRegOrMem(src, block.base.ty, block_mcv, operand_mcv);3440 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
3229 }3441 }
3230 }3442 }
3231 return self.brVoid(src, block);3443 return self.brVoid(block);
3232 }3444 }
32333445
3234 fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue {3446 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3235 const block_data = self.blocks.getPtr(block).?;3447 const block_data = self.blocks.getPtr(block).?;
32363448
3237 // Emit a jump with a relocation. It will be patched up after the block ends.3449 // Emit a jump with a relocation. It will be patched up after the block ends.
...@@ -3255,201 +3467,265 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3255,201 +3467,265 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3255 },3467 },
3256 });3468 });
3257 },3469 },
3258 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),3470 else => return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}),
3259 }3471 }
3260 return .none;
3261 }3472 }
32623473
3263 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {3474 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3264 if (!inst.is_volatile and inst.base.isUnused())3475 const air_datas = self.air.instructions.items(.data);
3265 return MCValue.dead;3476 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3266 switch (arch) {3477 const zir = self.mod_fn.owner_decl.namespace.file_scope.zir;
3267 .arm, .armeb => {3478 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3268 for (inst.inputs) |input, i| {3479 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3269 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3480 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3270 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3481 const outputs_len = @truncate(u5, extended.small);
3482 const args_len = @truncate(u5, extended.small >> 5);
3483 const clobbers_len = @truncate(u5, extended.small >> 10);
3484 _ = clobbers_len; // TODO honor these
3485 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3486 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
3487 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
3488
3489 if (outputs_len > 1) {
3490 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3491 }
3492 var extra_i: usize = zir_extra.end;
3493 const output_constraint: ?[]const u8 = out: {
3494 var i: usize = 0;
3495 while (i < outputs_len) : (i += 1) {
3496 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3497 extra_i = output.end;
3498 break :out zir.nullTerminatedString(output.data.constraint);
3499 }
3500 break :out null;
3501 };
3502
3503 const dead = !is_volatile and self.liveness.isUnused(inst);
3504 const result: MCValue = if (dead) .dead else switch (arch) {
3505 .arm, .armeb => result: {
3506 for (args) |arg| {
3507 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3508 extra_i = input.end;
3509 const constraint = zir.nullTerminatedString(input.data.constraint);
3510
3511 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3512 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3271 }3513 }
3272 const reg_name = input[1 .. input.len - 1];3514 const reg_name = constraint[1 .. constraint.len - 1];
3273 const reg = parseRegName(reg_name) orelse3515 const reg = parseRegName(reg_name) orelse
3274 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3516 return self.fail("unrecognized register: '{s}'", .{reg_name});
32753517
3276 const arg = inst.args[i];
3277 const arg_mcv = try self.resolveInst(arg);3518 const arg_mcv = try self.resolveInst(arg);
3278 try self.register_manager.getReg(reg, null);3519 try self.register_manager.getReg(reg, null);
3279 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3520 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3280 }3521 }
32813522
3282 if (mem.eql(u8, inst.asm_source, "svc #0")) {3523 if (mem.eql(u8, asm_source, "svc #0")) {
3283 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());3524 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
3284 } else {3525 } else {
3285 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});3526 return self.fail("TODO implement support for more arm assembly instructions", .{});
3286 }3527 }
32873528
3288 if (inst.output_constraint) |output| {3529 if (output_constraint) |output| {
3289 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3530 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3290 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3531 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3291 }3532 }
3292 const reg_name = output[2 .. output.len - 1];3533 const reg_name = output[2 .. output.len - 1];
3293 const reg = parseRegName(reg_name) orelse3534 const reg = parseRegName(reg_name) orelse
3294 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3535 return self.fail("unrecognized register: '{s}'", .{reg_name});
3295 return MCValue{ .register = reg };3536
3537 break :result MCValue{ .register = reg };
3296 } else {3538 } else {
3297 return MCValue.none;3539 break :result MCValue{ .none = {} };
3298 }3540 }
3299 },3541 },
3300 .aarch64 => {3542 .aarch64 => result: {
3301 for (inst.inputs) |input, i| {3543 for (args) |arg| {
3302 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3544 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3303 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3545 extra_i = input.end;
3546 const constraint = zir.nullTerminatedString(input.data.constraint);
3547
3548 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3549 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3304 }3550 }
3305 const reg_name = input[1 .. input.len - 1];3551 const reg_name = constraint[1 .. constraint.len - 1];
3306 const reg = parseRegName(reg_name) orelse3552 const reg = parseRegName(reg_name) orelse
3307 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3553 return self.fail("unrecognized register: '{s}'", .{reg_name});
33083554
3309 const arg = inst.args[i];
3310 const arg_mcv = try self.resolveInst(arg);3555 const arg_mcv = try self.resolveInst(arg);
3311 try self.register_manager.getReg(reg, null);3556 try self.register_manager.getReg(reg, null);
3312 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3557 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3313 }3558 }
33143559
3315 if (mem.eql(u8, inst.asm_source, "svc #0")) {3560 if (mem.eql(u8, asm_source, "svc #0")) {
3316 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());3561 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
3317 } else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {3562 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
3318 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());3563 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
3319 } else {3564 } else {
3320 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});3565 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
3321 }3566 }
33223567
3323 if (inst.output_constraint) |output| {3568 if (output_constraint) |output| {
3324 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3569 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3325 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3570 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3326 }3571 }
3327 const reg_name = output[2 .. output.len - 1];3572 const reg_name = output[2 .. output.len - 1];
3328 const reg = parseRegName(reg_name) orelse3573 const reg = parseRegName(reg_name) orelse
3329 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3574 return self.fail("unrecognized register: '{s}'", .{reg_name});
3330 return MCValue{ .register = reg };3575 break :result MCValue{ .register = reg };
3331 } else {3576 } else {
3332 return MCValue.none;3577 break :result MCValue{ .none = {} };
3333 }3578 }
3334 },3579 },
3335 .riscv64 => {3580 .riscv64 => result: {
3336 for (inst.inputs) |input, i| {3581 for (args) |arg| {
3337 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3582 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3338 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3583 extra_i = input.end;
3584 const constraint = zir.nullTerminatedString(input.data.constraint);
3585
3586 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3587 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3339 }3588 }
3340 const reg_name = input[1 .. input.len - 1];3589 const reg_name = constraint[1 .. constraint.len - 1];
3341 const reg = parseRegName(reg_name) orelse3590 const reg = parseRegName(reg_name) orelse
3342 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3591 return self.fail("unrecognized register: '{s}'", .{reg_name});
33433592
3344 const arg = inst.args[i];
3345 const arg_mcv = try self.resolveInst(arg);3593 const arg_mcv = try self.resolveInst(arg);
3346 try self.register_manager.getReg(reg, null);3594 try self.register_manager.getReg(reg, null);
3347 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3595 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3348 }3596 }
33493597
3350 if (mem.eql(u8, inst.asm_source, "ecall")) {3598 if (mem.eql(u8, asm_source, "ecall")) {
3351 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());3599 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
3352 } else {3600 } else {
3353 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});3601 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
3354 }3602 }
33553603
3356 if (inst.output_constraint) |output| {3604 if (output_constraint) |output| {
3357 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3605 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3358 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3606 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3359 }3607 }
3360 const reg_name = output[2 .. output.len - 1];3608 const reg_name = output[2 .. output.len - 1];
3361 const reg = parseRegName(reg_name) orelse3609 const reg = parseRegName(reg_name) orelse
3362 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3610 return self.fail("unrecognized register: '{s}'", .{reg_name});
3363 return MCValue{ .register = reg };3611 break :result MCValue{ .register = reg };
3364 } else {3612 } else {
3365 return MCValue.none;3613 break :result MCValue{ .none = {} };
3366 }3614 }
3367 },3615 },
3368 .x86_64, .i386 => {3616 .x86_64, .i386 => result: {
3369 for (inst.inputs) |input, i| {3617 for (args) |arg| {
3370 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3618 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3371 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3619 extra_i = input.end;
3620 const constraint = zir.nullTerminatedString(input.data.constraint);
3621
3622 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3623 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3372 }3624 }
3373 const reg_name = input[1 .. input.len - 1];3625 const reg_name = constraint[1 .. constraint.len - 1];
3374 const reg = parseRegName(reg_name) orelse3626 const reg = parseRegName(reg_name) orelse
3375 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3627 return self.fail("unrecognized register: '{s}'", .{reg_name});
33763628
3377 const arg = inst.args[i];
3378 const arg_mcv = try self.resolveInst(arg);3629 const arg_mcv = try self.resolveInst(arg);
3379 try self.register_manager.getReg(reg, null);3630 try self.register_manager.getReg(reg, null);
3380 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3631 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3381 }3632 }
33823633
3383 {3634 {
3384 var iter = std.mem.tokenize(inst.asm_source, "\n\r");3635 var iter = std.mem.tokenize(asm_source, "\n\r");
3385 while (iter.next()) |ins| {3636 while (iter.next()) |ins| {
3386 if (mem.eql(u8, ins, "syscall")) {3637 if (mem.eql(u8, ins, "syscall")) {
3387 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });3638 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
3388 } else if (mem.indexOf(u8, ins, "push")) |_| {3639 } else if (mem.indexOf(u8, ins, "push")) |_| {
3389 const arg = ins[4..];3640 const arg = ins[4..];
3390 if (mem.indexOf(u8, arg, "$")) |l| {3641 if (mem.indexOf(u8, arg, "$")) |l| {
3391 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", .{});3642 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail("TODO implement more inline asm int parsing", .{});
3392 try self.code.appendSlice(&.{ 0x6a, n });3643 try self.code.appendSlice(&.{ 0x6a, n });
3393 } else if (mem.indexOf(u8, arg, "%%")) |l| {3644 } else if (mem.indexOf(u8, arg, "%%")) |l| {
3394 const reg_name = ins[4 + l + 2 ..];3645 const reg_name = ins[4 + l + 2 ..];
3395 const reg = parseRegName(reg_name) orelse3646 const reg = parseRegName(reg_name) orelse
3396 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3647 return self.fail("unrecognized register: '{s}'", .{reg_name});
3397 const low_id: u8 = reg.low_id();3648 const low_id: u8 = reg.low_id();
3398 if (reg.isExtended()) {3649 if (reg.isExtended()) {
3399 try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });3650 try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });
3400 } else {3651 } else {
3401 try self.code.append(0b1010000 | low_id);3652 try self.code.append(0b1010000 | low_id);
3402 }3653 }
3403 } else return self.fail(inst.base.src, "TODO more push operands", .{});3654 } else return self.fail("TODO more push operands", .{});
3404 } else if (mem.indexOf(u8, ins, "pop")) |_| {3655 } else if (mem.indexOf(u8, ins, "pop")) |_| {
3405 const arg = ins[3..];3656 const arg = ins[3..];
3406 if (mem.indexOf(u8, arg, "%%")) |l| {3657 if (mem.indexOf(u8, arg, "%%")) |l| {
3407 const reg_name = ins[3 + l + 2 ..];3658 const reg_name = ins[3 + l + 2 ..];
3408 const reg = parseRegName(reg_name) orelse3659 const reg = parseRegName(reg_name) orelse
3409 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3660 return self.fail("unrecognized register: '{s}'", .{reg_name});
3410 const low_id: u8 = reg.low_id();3661 const low_id: u8 = reg.low_id();
3411 if (reg.isExtended()) {3662 if (reg.isExtended()) {
3412 try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });3663 try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });
3413 } else {3664 } else {
3414 try self.code.append(0b1011000 | low_id);3665 try self.code.append(0b1011000 | low_id);
3415 }3666 }
3416 } else return self.fail(inst.base.src, "TODO more pop operands", .{});3667 } else return self.fail("TODO more pop operands", .{});
3417 } else {3668 } else {
3418 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});3669 return self.fail("TODO implement support for more x86 assembly instructions", .{});
3419 }3670 }
3420 }3671 }
3421 }3672 }
34223673
3423 if (inst.output_constraint) |output| {3674 if (output_constraint) |output| {
3424 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3675 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3425 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3676 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3426 }3677 }
3427 const reg_name = output[2 .. output.len - 1];3678 const reg_name = output[2 .. output.len - 1];
3428 const reg = parseRegName(reg_name) orelse3679 const reg = parseRegName(reg_name) orelse
3429 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3680 return self.fail("unrecognized register: '{s}'", .{reg_name});
3430 return MCValue{ .register = reg };3681 break :result MCValue{ .register = reg };
3431 } else {3682 } else {
3432 return MCValue.none;3683 break :result MCValue{ .none = {} };
3433 }3684 }
3434 },3685 },
3435 else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),3686 else => return self.fail("TODO implement inline asm support for more architectures", .{}),
3687 };
3688 if (outputs.len + args.len <= Liveness.bpi - 1) {
3689 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3690 std.mem.copy(Air.Inst.Ref, &buf, outputs);
3691 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
3692 return self.finishAir(inst, result, buf);
3693 }
3694 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
3695 for (outputs) |output| {
3696 bt.feed(output);
3697 }
3698 for (args) |arg| {
3699 bt.feed(arg);
3436 }3700 }
3701 return bt.finishAir(result);
3702 }
3703
3704 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
3705 try self.ensureProcessDeathCapacity(operand_count + 1);
3706 return BigTomb{
3707 .function = self,
3708 .inst = inst,
3709 .tomb_bits = self.liveness.getTombBits(inst),
3710 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
3711 .bit_index = 0,
3712 };
3437 }3713 }
34383714
3439 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.3715 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
3440 fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void {3716 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
3441 switch (loc) {3717 switch (loc) {
3442 .none => return,3718 .none => return,
3443 .register => |reg| return self.genSetReg(src, ty, reg, val),3719 .register => |reg| return self.genSetReg(ty, reg, val),
3444 .stack_offset => |off| return self.genSetStack(src, ty, off, val),3720 .stack_offset => |off| return self.genSetStack(ty, off, val),
3445 .memory => {3721 .memory => {
3446 return self.fail(src, "TODO implement setRegOrMem for memory", .{});3722 return self.fail("TODO implement setRegOrMem for memory", .{});
3447 },3723 },
3448 else => unreachable,3724 else => unreachable,
3449 }3725 }
3450 }3726 }
34513727
3452 fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {3728 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
3453 switch (arch) {3729 switch (arch) {
3454 .arm, .armeb => switch (mcv) {3730 .arm, .armeb => switch (mcv) {
3455 .dead => unreachable,3731 .dead => unreachable,
...@@ -3461,28 +3737,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3461,28 +3737,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3461 return; // The already existing value will do just fine.3737 return; // The already existing value will do just fine.
3462 // TODO Upgrade this to a memset call when we have that available.3738 // TODO Upgrade this to a memset call when we have that available.
3463 switch (ty.abiSize(self.target.*)) {3739 switch (ty.abiSize(self.target.*)) {
3464 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),3740 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3465 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),3741 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3466 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3742 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3467 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3743 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3468 else => return self.fail(src, "TODO implement memset", .{}),3744 else => return self.fail("TODO implement memset", .{}),
3469 }3745 }
3470 },3746 },
3471 .compare_flags_unsigned => |op| {3747 .compare_flags_unsigned => |op| {
3472 _ = op;3748 _ = op;
3473 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});3749 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
3474 },3750 },
3475 .compare_flags_signed => |op| {3751 .compare_flags_signed => |op| {
3476 _ = op;3752 _ = op;
3477 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});3753 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
3478 },3754 },
3479 .immediate => {3755 .immediate => {
3480 const reg = try self.copyToTmpRegister(src, ty, mcv);3756 const reg = try self.copyToTmpRegister(ty, mcv);
3481 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3757 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3482 },3758 },
3483 .embedded_in_code => |code_offset| {3759 .embedded_in_code => |code_offset| {
3484 _ = code_offset;3760 _ = code_offset;
3485 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});3761 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
3486 },3762 },
3487 .register => |reg| {3763 .register => |reg| {
3488 const abi_size = ty.abiSize(self.target.*);3764 const abi_size = ty.abiSize(self.target.*);
...@@ -3492,7 +3768,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3492,7 +3768,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3492 1, 4 => {3768 1, 4 => {
3493 const offset = if (math.cast(u12, adj_off)) |imm| blk: {3769 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
3494 break :blk Instruction.Offset.imm(imm);3770 break :blk Instruction.Offset.imm(imm);
3495 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);3771 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3496 const str = switch (abi_size) {3772 const str = switch (abi_size) {
3497 1 => Instruction.strb,3773 1 => Instruction.strb,
3498 4 => Instruction.str,3774 4 => Instruction.str,
...@@ -3507,26 +3783,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3507,26 +3783,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3507 2 => {3783 2 => {
3508 const offset = if (adj_off <= math.maxInt(u8)) blk: {3784 const offset = if (adj_off <= math.maxInt(u8)) blk: {
3509 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));3785 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
3510 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));3786 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
35113787
3512 writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{3788 writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{
3513 .offset = offset,3789 .offset = offset,
3514 .positive = false,3790 .positive = false,
3515 }).toU32());3791 }).toU32());
3516 },3792 },
3517 else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),3793 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
3518 }3794 }
3519 },3795 },
3520 .memory => |vaddr| {3796 .memory => |vaddr| {
3521 _ = vaddr;3797 _ = vaddr;
3522 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3798 return self.fail("TODO implement set stack variable from memory vaddr", .{});
3523 },3799 },
3524 .stack_offset => |off| {3800 .stack_offset => |off| {
3525 if (stack_offset == off)3801 if (stack_offset == off)
3526 return; // Copy stack variable to itself; nothing to do.3802 return; // Copy stack variable to itself; nothing to do.
35273803
3528 const reg = try self.copyToTmpRegister(src, ty, mcv);3804 const reg = try self.copyToTmpRegister(ty, mcv);
3529 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3805 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3530 },3806 },
3531 },3807 },
3532 .x86_64 => switch (mcv) {3808 .x86_64 => switch (mcv) {
...@@ -3539,34 +3815,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3539,34 +3815,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3539 return; // The already existing value will do just fine.3815 return; // The already existing value will do just fine.
3540 // TODO Upgrade this to a memset call when we have that available.3816 // TODO Upgrade this to a memset call when we have that available.
3541 switch (ty.abiSize(self.target.*)) {3817 switch (ty.abiSize(self.target.*)) {
3542 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),3818 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3543 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),3819 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3544 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3820 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3545 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3821 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3546 else => return self.fail(src, "TODO implement memset", .{}),3822 else => return self.fail("TODO implement memset", .{}),
3547 }3823 }
3548 },3824 },
3549 .compare_flags_unsigned => |op| {3825 .compare_flags_unsigned => |op| {
3550 _ = op;3826 _ = op;
3551 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});3827 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
3552 },3828 },
3553 .compare_flags_signed => |op| {3829 .compare_flags_signed => |op| {
3554 _ = op;3830 _ = op;
3555 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});3831 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
3556 },3832 },
3557 .immediate => |x_big| {3833 .immediate => |x_big| {
3558 const abi_size = ty.abiSize(self.target.*);3834 const abi_size = ty.abiSize(self.target.*);
3559 const adj_off = stack_offset + abi_size;3835 const adj_off = stack_offset + abi_size;
3560 if (adj_off > 128) {3836 if (adj_off > 128) {
3561 return self.fail(src, "TODO implement set stack variable with large stack offset", .{});3837 return self.fail("TODO implement set stack variable with large stack offset", .{});
3562 }3838 }
3563 try self.code.ensureCapacity(self.code.items.len + 8);3839 try self.code.ensureCapacity(self.code.items.len + 8);
3564 switch (abi_size) {3840 switch (abi_size) {
3565 1 => {3841 1 => {
3566 return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});3842 return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{});
3567 },3843 },
3568 2 => {3844 2 => {
3569 return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});3845 return self.fail("TODO implement set abi_size=2 stack variable with immediate", .{});
3570 },3846 },
3571 4 => {3847 4 => {
3572 const x = @intCast(u32, x_big);3848 const x = @intCast(u32, x_big);
...@@ -3599,22 +3875,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3599,22 +3875,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3599 self.code.appendSliceAssumeCapacity(buf[0..4]);3875 self.code.appendSliceAssumeCapacity(buf[0..4]);
3600 },3876 },
3601 else => {3877 else => {
3602 return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});3878 return self.fail("TODO implement set abi_size=large stack variable with immediate", .{});
3603 },3879 },
3604 }3880 }
3605 },3881 },
3606 .embedded_in_code => {3882 .embedded_in_code => {
3607 // TODO this and `.stack_offset` below need to get improved to support types greater than3883 // TODO this and `.stack_offset` below need to get improved to support types greater than
3608 // register size, and do general memcpy3884 // register size, and do general memcpy
3609 const reg = try self.copyToTmpRegister(src, ty, mcv);3885 const reg = try self.copyToTmpRegister(ty, mcv);
3610 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3886 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3611 },3887 },
3612 .register => |reg| {3888 .register => |reg| {
3613 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);3889 try self.genX8664ModRMRegToStack(ty, stack_offset, reg, 0x89);
3614 },3890 },
3615 .memory => |vaddr| {3891 .memory => |vaddr| {
3616 _ = vaddr;3892 _ = vaddr;
3617 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3893 return self.fail("TODO implement set stack variable from memory vaddr", .{});
3618 },3894 },
3619 .stack_offset => |off| {3895 .stack_offset => |off| {
3620 // TODO this and `.embedded_in_code` above need to get improved to support types greater than3896 // TODO this and `.embedded_in_code` above need to get improved to support types greater than
...@@ -3623,8 +3899,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3623,8 +3899,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3623 if (stack_offset == off)3899 if (stack_offset == off)
3624 return; // Copy stack variable to itself; nothing to do.3900 return; // Copy stack variable to itself; nothing to do.
36253901
3626 const reg = try self.copyToTmpRegister(src, ty, mcv);3902 const reg = try self.copyToTmpRegister(ty, mcv);
3627 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3903 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3628 },3904 },
3629 },3905 },
3630 .aarch64, .aarch64_be, .aarch64_32 => switch (mcv) {3906 .aarch64, .aarch64_be, .aarch64_32 => switch (mcv) {
...@@ -3637,28 +3913,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3637,28 +3913,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3637 return; // The already existing value will do just fine.3913 return; // The already existing value will do just fine.
3638 // TODO Upgrade this to a memset call when we have that available.3914 // TODO Upgrade this to a memset call when we have that available.
3639 switch (ty.abiSize(self.target.*)) {3915 switch (ty.abiSize(self.target.*)) {
3640 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),3916 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3641 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),3917 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3642 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3918 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3643 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3919 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3644 else => return self.fail(src, "TODO implement memset", .{}),3920 else => return self.fail("TODO implement memset", .{}),
3645 }3921 }
3646 },3922 },
3647 .compare_flags_unsigned => |op| {3923 .compare_flags_unsigned => |op| {
3648 _ = op;3924 _ = op;
3649 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});3925 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
3650 },3926 },
3651 .compare_flags_signed => |op| {3927 .compare_flags_signed => |op| {
3652 _ = op;3928 _ = op;
3653 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});3929 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
3654 },3930 },
3655 .immediate => {3931 .immediate => {
3656 const reg = try self.copyToTmpRegister(src, ty, mcv);3932 const reg = try self.copyToTmpRegister(ty, mcv);
3657 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3933 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3658 },3934 },
3659 .embedded_in_code => |code_offset| {3935 .embedded_in_code => |code_offset| {
3660 _ = code_offset;3936 _ = code_offset;
3661 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});3937 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
3662 },3938 },
3663 .register => |reg| {3939 .register => |reg| {
3664 const abi_size = ty.abiSize(self.target.*);3940 const abi_size = ty.abiSize(self.target.*);
...@@ -3669,7 +3945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3669,7 +3945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3669 const offset = if (math.cast(i9, adj_off)) |imm|3945 const offset = if (math.cast(i9, adj_off)) |imm|
3670 Instruction.LoadStoreOffset.imm_post_index(-imm)3946 Instruction.LoadStoreOffset.imm_post_index(-imm)
3671 else |_|3947 else |_|
3672 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));3948 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
3673 const rn: Register = switch (arch) {3949 const rn: Register = switch (arch) {
3674 .aarch64, .aarch64_be => .x29,3950 .aarch64, .aarch64_be => .x29,
3675 .aarch64_32 => .w29,3951 .aarch64_32 => .w29,
...@@ -3686,26 +3962,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3686,26 +3962,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3686 .offset = offset,3962 .offset = offset,
3687 }).toU32());3963 }).toU32());
3688 },3964 },
3689 else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),3965 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
3690 }3966 }
3691 },3967 },
3692 .memory => |vaddr| {3968 .memory => |vaddr| {
3693 _ = vaddr;3969 _ = vaddr;
3694 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3970 return self.fail("TODO implement set stack variable from memory vaddr", .{});
3695 },3971 },
3696 .stack_offset => |off| {3972 .stack_offset => |off| {
3697 if (stack_offset == off)3973 if (stack_offset == off)
3698 return; // Copy stack variable to itself; nothing to do.3974 return; // Copy stack variable to itself; nothing to do.
36993975
3700 const reg = try self.copyToTmpRegister(src, ty, mcv);3976 const reg = try self.copyToTmpRegister(ty, mcv);
3701 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3977 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3702 },3978 },
3703 },3979 },
3704 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),3980 else => return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch}),
3705 }3981 }
3706 }3982 }
37073983
3708 fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void {3984 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3709 switch (arch) {3985 switch (arch) {
3710 .arm, .armeb => switch (mcv) {3986 .arm, .armeb => switch (mcv) {
3711 .dead => unreachable,3987 .dead => unreachable,
...@@ -3716,7 +3992,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3716,7 +3992,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3716 if (!self.wantSafety())3992 if (!self.wantSafety())
3717 return; // The already existing value will do just fine.3993 return; // The already existing value will do just fine.
3718 // Write the debug undefined value.3994 // Write the debug undefined value.
3719 return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa });3995 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa });
3720 },3996 },
3721 .compare_flags_unsigned,3997 .compare_flags_unsigned,
3722 .compare_flags_signed,3998 .compare_flags_signed,
...@@ -3735,7 +4011,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3735,7 +4011,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3735 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());4011 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());
3736 },4012 },
3737 .immediate => |x| {4013 .immediate => |x| {
3738 if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{});4014 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});
37394015
3740 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {4016 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
3741 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());4017 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());
...@@ -3781,7 +4057,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3781,7 +4057,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3781 .memory => |addr| {4057 .memory => |addr| {
3782 // The value is in memory at a hard-coded address.4058 // The value is in memory at a hard-coded address.
3783 // If the type is a pointer, it means the pointer address is at this memory location.4059 // If the type is a pointer, it means the pointer address is at this memory location.
3784 try self.genSetReg(src, ty, reg, .{ .immediate = addr });4060 try self.genSetReg(ty, reg, .{ .immediate = addr });
3785 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());4061 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
3786 },4062 },
3787 .stack_offset => |unadjusted_off| {4063 .stack_offset => |unadjusted_off| {
...@@ -3793,7 +4069,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3793,7 +4069,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3793 1, 4 => {4069 1, 4 => {
3794 const offset = if (adj_off <= math.maxInt(u12)) blk: {4070 const offset = if (adj_off <= math.maxInt(u12)) blk: {
3795 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));4071 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
3796 } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);4072 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3797 const ldr = switch (abi_size) {4073 const ldr = switch (abi_size) {
3798 1 => Instruction.ldrb,4074 1 => Instruction.ldrb,
3799 4 => Instruction.ldr,4075 4 => Instruction.ldr,
...@@ -3808,17 +4084,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3808,17 +4084,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3808 2 => {4084 2 => {
3809 const offset = if (adj_off <= math.maxInt(u8)) blk: {4085 const offset = if (adj_off <= math.maxInt(u8)) blk: {
3810 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));4086 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
3811 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));4087 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
38124088
3813 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{4089 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{
3814 .offset = offset,4090 .offset = offset,
3815 .positive = false,4091 .positive = false,
3816 }).toU32());4092 }).toU32());
3817 },4093 },
3818 else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}),4094 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
3819 }4095 }
3820 },4096 },
3821 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),4097 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
3822 },4098 },
3823 .aarch64 => switch (mcv) {4099 .aarch64 => switch (mcv) {
3824 .dead => unreachable,4100 .dead => unreachable,
...@@ -3830,8 +4106,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3830,8 +4106,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3830 return; // The already existing value will do just fine.4106 return; // The already existing value will do just fine.
3831 // Write the debug undefined value.4107 // Write the debug undefined value.
3832 switch (reg.size()) {4108 switch (reg.size()) {
3833 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),4109 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
3834 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),4110 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3835 else => unreachable, // unexpected register size4111 else => unreachable, // unexpected register size
3836 }4112 }
3837 },4113 },
...@@ -3879,7 +4155,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3879,7 +4155,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3879 .size = 4,4155 .size = 4,
3880 });4156 });
3881 } else {4157 } else {
3882 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});4158 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
3883 }4159 }
3884 mem.writeIntLittle(4160 mem.writeIntLittle(
3885 u32,4161 u32,
...@@ -3896,7 +4172,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3896,7 +4172,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3896 } else {4172 } else {
3897 // The value is in memory at a hard-coded address.4173 // The value is in memory at a hard-coded address.
3898 // If the type is a pointer, it means the pointer address is at this memory location.4174 // If the type is a pointer, it means the pointer address is at this memory location.
3899 try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr });4175 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
3900 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());4176 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
3901 }4177 }
3902 },4178 },
...@@ -3914,7 +4190,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3914,7 +4190,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3914 const offset = if (math.cast(i9, adj_off)) |imm|4190 const offset = if (math.cast(i9, adj_off)) |imm|
3915 Instruction.LoadStoreOffset.imm_post_index(-imm)4191 Instruction.LoadStoreOffset.imm_post_index(-imm)
3916 else |_|4192 else |_|
3917 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));4193 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
39184194
3919 switch (abi_size) {4195 switch (abi_size) {
3920 1, 2 => {4196 1, 2 => {
...@@ -3934,10 +4210,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3934,10 +4210,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3934 .offset = offset,4210 .offset = offset,
3935 } }).toU32());4211 } }).toU32());
3936 },4212 },
3937 else => return self.fail(src, "TODO implement genSetReg other types abi_size={}", .{abi_size}),4213 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
3938 }4214 }
3939 },4215 },
3940 else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}),4216 else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}),
3941 },4217 },
3942 .riscv64 => switch (mcv) {4218 .riscv64 => switch (mcv) {
3943 .dead => unreachable,4219 .dead => unreachable,
...@@ -3948,7 +4224,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3948,7 +4224,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3948 if (!self.wantSafety())4224 if (!self.wantSafety())
3949 return; // The already existing value will do just fine.4225 return; // The already existing value will do just fine.
3950 // Write the debug undefined value.4226 // Write the debug undefined value.
3951 return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });4227 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
3952 },4228 },
3953 .immediate => |unsigned_x| {4229 .immediate => |unsigned_x| {
3954 const x = @bitCast(i64, unsigned_x);4230 const x = @bitCast(i64, unsigned_x);
...@@ -3968,19 +4244,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3968,19 +4244,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3968 }4244 }
3969 // li rd, immediate4245 // li rd, immediate
3970 // "Myriad sequences"4246 // "Myriad sequences"
3971 return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf4247 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
3972 },4248 },
3973 .memory => |addr| {4249 .memory => |addr| {
3974 // The value is in memory at a hard-coded address.4250 // The value is in memory at a hard-coded address.
3975 // If the type is a pointer, it means the pointer address is at this memory location.4251 // If the type is a pointer, it means the pointer address is at this memory location.
3976 try self.genSetReg(src, ty, reg, .{ .immediate = addr });4252 try self.genSetReg(ty, reg, .{ .immediate = addr });
39774253
3978 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());4254 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
3979 // LOAD imm=[i12 offset = 0], rs1 =4255 // LOAD imm=[i12 offset = 0], rs1 =
39804256
3981 // return self.fail("TODO implement genSetReg memory for riscv64");4257 // return self.fail("TODO implement genSetReg memory for riscv64");
3982 },4258 },
3983 else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),4259 else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}),
3984 },4260 },
3985 .x86_64 => switch (mcv) {4261 .x86_64 => switch (mcv) {
3986 .dead => unreachable,4262 .dead => unreachable,
...@@ -3992,10 +4268,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3992,10 +4268,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3992 return; // The already existing value will do just fine.4268 return; // The already existing value will do just fine.
3993 // Write the debug undefined value.4269 // Write the debug undefined value.
3994 switch (reg.size()) {4270 switch (reg.size()) {
3995 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }),4271 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }),
3996 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }),4272 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }),
3997 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),4273 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
3998 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),4274 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3999 else => unreachable,4275 else => unreachable,
4000 }4276 }
4001 },4277 },
...@@ -4022,7 +4298,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4022,7 +4298,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4022 },4298 },
4023 .compare_flags_signed => |op| {4299 .compare_flags_signed => |op| {
4024 _ = op;4300 _ = op;
4025 return self.fail(src, "TODO set register with compare flags value (signed)", .{});4301 return self.fail("TODO set register with compare flags value (signed)", .{});
4026 },4302 },
4027 .immediate => |x| {4303 .immediate => |x| {
4028 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit4304 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
...@@ -4155,7 +4431,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4155,7 +4431,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4155 .size = 4,4431 .size = 4,
4156 });4432 });
4157 } else {4433 } else {
4158 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});4434 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
4159 }4435 }
41604436
4161 // MOV reg, [reg]4437 // MOV reg, [reg]
...@@ -4211,7 +4487,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4211,7 +4487,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4211 assert(id3 != 4 and id3 != 5);4487 assert(id3 != 4 and id3 != 5);
42124488
4213 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.4489 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
4214 try self.genSetReg(src, ty, reg, MCValue{ .immediate = x });4490 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
42154491
4216 // Now, the register contains the address of the value to load into it4492 // Now, the register contains the address of the value to load into it
4217 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.4493 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
...@@ -4234,7 +4510,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4234,7 +4510,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4234 const abi_size = ty.abiSize(self.target.*);4510 const abi_size = ty.abiSize(self.target.*);
4235 const off = unadjusted_off + abi_size;4511 const off = unadjusted_off + abi_size;
4236 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {4512 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
4237 return self.fail(src, "stack offset too large", .{});4513 return self.fail("stack offset too large", .{});
4238 }4514 }
4239 const ioff = -@intCast(i32, off);4515 const ioff = -@intCast(i32, off);
4240 const encoder = try X8664Encoder.init(self.code, 3);4516 const encoder = try X8664Encoder.init(self.code, 3);
...@@ -4254,39 +4530,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4254,39 +4530,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4254 }4530 }
4255 },4531 },
4256 },4532 },
4257 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),4533 else => return self.fail("TODO implement getSetReg for {}", .{self.target.cpu.arch}),
4258 }4534 }
4259 }4535 }
42604536
4261 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {4537 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
4262 // no-op4538 const un_op = self.air.instructions.items(.data)[inst].un_op;
4263 return self.resolveInst(inst.operand);4539 const result = try self.resolveInst(un_op);
4540 return self.finishAir(inst, result, .{ un_op, .none, .none });
4264 }4541 }
42654542
4266 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {4543 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
4267 const operand = try self.resolveInst(inst.operand);4544 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4268 return operand;4545 const result = try self.resolveInst(ty_op.operand);
4546 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4269 }4547 }
42704548
4271 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {4549 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4272 // If the type has no codegen bits, no need to store it.4550 // First section of indexes correspond to a set number of constant values.
4273 if (!inst.ty.hasCodeGenBits())4551 const ref_int = @enumToInt(inst);
4274 return MCValue.none;4552 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
42754553 const tv = Air.Inst.Ref.typed_value_map[ref_int];
4276 // Constants have static lifetimes, so they are always memoized in the outer most table.4554 if (!tv.ty.hasCodeGenBits()) {
4277 if (inst.castTag(.constant)) |const_inst| {4555 return MCValue{ .none = {} };
4278 const branch = &self.branch_stack.items[0];
4279 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
4280 if (!gop.found_existing) {
4281 gop.value_ptr.* = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
4282 }4556 }
4283 return gop.value_ptr.*;4557 return self.genTypedValue(tv);
4284 }4558 }
42854559
4286 return self.getResolvedInstValue(inst);4560 // If the type has no codegen bits, no need to store it.
4561 const inst_ty = self.air.typeOf(inst);
4562 if (!inst_ty.hasCodeGenBits())
4563 return MCValue{ .none = {} };
4564
4565 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
4566 switch (self.air.instructions.items(.tag)[inst_index]) {
4567 .constant => {
4568 // Constants have static lifetimes, so they are always memoized in the outer most table.
4569 const branch = &self.branch_stack.items[0];
4570 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
4571 if (!gop.found_existing) {
4572 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
4573 gop.value_ptr.* = try self.genTypedValue(.{
4574 .ty = inst_ty,
4575 .val = self.air.values[ty_pl.payload],
4576 });
4577 }
4578 return gop.value_ptr.*;
4579 },
4580 .const_ty => unreachable,
4581 else => return self.getResolvedInstValue(inst_index),
4582 }
4287 }4583 }
42884584
4289 fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {4585 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
4290 // Treat each stack item as a "layer" on top of the previous one.4586 // Treat each stack item as a "layer" on top of the previous one.
4291 var i: usize = self.branch_stack.items.len;4587 var i: usize = self.branch_stack.items.len;
4292 while (true) {4588 while (true) {
...@@ -4303,15 +4599,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4303,15 +4599,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4303 /// A potential opportunity for future optimization here would be keeping track4599 /// A potential opportunity for future optimization here would be keeping track
4304 /// of the fact that the instruction is available both as an immediate4600 /// of the fact that the instruction is available both as an immediate
4305 /// and as a register.4601 /// and as a register.
4306 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {4602 fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
4307 const mcv = try self.resolveInst(inst);4603 const mcv = try self.resolveInst(operand);
4308 const ti = @typeInfo(T).Int;4604 const ti = @typeInfo(T).Int;
4309 switch (mcv) {4605 switch (mcv) {
4310 .immediate => |imm| {4606 .immediate => |imm| {
4311 // This immediate is unsigned.4607 // This immediate is unsigned.
4312 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));4608 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
4313 if (imm >= math.maxInt(U)) {4609 if (imm >= math.maxInt(U)) {
4314 return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) };4610 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
4315 }4611 }
4316 },4612 },
4317 else => {},4613 else => {},
...@@ -4319,7 +4615,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4319,7 +4615,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4319 return mcv;4615 return mcv;
4320 }4616 }
43214617
4322 fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {4618 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
4323 if (typed_value.val.isUndef())4619 if (typed_value.val.isUndef())
4324 return MCValue{ .undef = {} };4620 return MCValue{ .undef = {} };
4325 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4621 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
...@@ -4329,7 +4625,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4329,7 +4625,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4329 .Slice => {4625 .Slice => {
4330 var buf: Type.Payload.ElemType = undefined;4626 var buf: Type.Payload.ElemType = undefined;
4331 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);4627 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
4332 const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val });4628 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
4333 const slice_len = typed_value.val.sliceLen();4629 const slice_len = typed_value.val.sliceLen();
4334 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean4630 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
4335 // the Sema code needs to use anonymous Decls or alloca instructions to store data.4631 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
...@@ -4337,7 +4633,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4337,7 +4633,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4337 _ = slice_len;4633 _ = slice_len;
4338 _ = ptr_imm;4634 _ = ptr_imm;
4339 // We need more general support for const data being stored in memory to make this work.4635 // We need more general support for const data being stored in memory to make this work.
4340 return self.fail(src, "TODO codegen for const slices", .{});4636 return self.fail("TODO codegen for const slices", .{});
4341 },4637 },
4342 else => {4638 else => {
4343 if (typed_value.val.castTag(.decl_ref)) |payload| {4639 if (typed_value.val.castTag(.decl_ref)) |payload| {
...@@ -4363,19 +4659,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4363,19 +4659,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4363 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;4659 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
4364 return MCValue{ .memory = got_addr };4660 return MCValue{ .memory = got_addr };
4365 } else {4661 } else {
4366 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});4662 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
4367 }4663 }
4368 }4664 }
4369 if (typed_value.val.tag() == .int_u64) {4665 if (typed_value.val.tag() == .int_u64) {
4370 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };4666 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4371 }4667 }
4372 return self.fail(src, "TODO codegen more kinds of const pointers", .{});4668 return self.fail("TODO codegen more kinds of const pointers", .{});
4373 },4669 },
4374 },4670 },
4375 .Int => {4671 .Int => {
4376 const info = typed_value.ty.intInfo(self.target.*);4672 const info = typed_value.ty.intInfo(self.target.*);
4377 if (info.bits > ptr_bits or info.signedness == .signed) {4673 if (info.bits > ptr_bits or info.signedness == .signed) {
4378 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});4674 return self.fail("TODO const int bigger than ptr and signed int", .{});
4379 }4675 }
4380 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };4676 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4381 },4677 },
...@@ -4390,16 +4686,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4390,16 +4686,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4390 return MCValue{ .immediate = 0 };4686 return MCValue{ .immediate = 0 };
43914687
4392 var buf: Type.Payload.ElemType = undefined;4688 var buf: Type.Payload.ElemType = undefined;
4393 return self.genTypedValue(src, .{4689 return self.genTypedValue(.{
4394 .ty = typed_value.ty.optionalChild(&buf),4690 .ty = typed_value.ty.optionalChild(&buf),
4395 .val = typed_value.val,4691 .val = typed_value.val,
4396 });4692 });
4397 } else if (typed_value.ty.abiSize(self.target.*) == 1) {4693 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
4398 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };4694 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
4399 }4695 }
4400 return self.fail(src, "TODO non pointer optionals", .{});4696 return self.fail("TODO non pointer optionals", .{});
4401 },4697 },
4402 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),4698 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
4403 }4699 }
4404 }4700 }
44054701
...@@ -4416,7 +4712,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4416,7 +4712,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4416 };4712 };
44174713
4418 /// Caller must call `CallMCValues.deinit`.4714 /// Caller must call `CallMCValues.deinit`.
4419 fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues {4715 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4420 const cc = fn_ty.fnCallingConvention();4716 const cc = fn_ty.fnCallingConvention();
4421 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());4717 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
4422 defer self.gpa.free(param_types);4718 defer self.gpa.free(param_types);
...@@ -4485,7 +4781,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4485,7 +4781,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4485 result.stack_byte_count = next_stack_offset;4781 result.stack_byte_count = next_stack_offset;
4486 result.stack_align = 16;4782 result.stack_align = 16;
4487 },4783 },
4488 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),4784 else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}),
4489 }4785 }
4490 },4786 },
4491 .arm, .armeb => {4787 .arm, .armeb => {
...@@ -4512,10 +4808,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4512,10 +4808,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4512 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };4808 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
4513 ncrn += 1;4809 ncrn += 1;
4514 } else {4810 } else {
4515 return self.fail(src, "TODO MCValues with multiple registers", .{});4811 return self.fail("TODO MCValues with multiple registers", .{});
4516 }4812 }
4517 } else if (ncrn < 4 and nsaa == 0) {4813 } else if (ncrn < 4 and nsaa == 0) {
4518 return self.fail(src, "TODO MCValues split between registers and stack", .{});4814 return self.fail("TODO MCValues split between registers and stack", .{});
4519 } else {4815 } else {
4520 ncrn = 4;4816 ncrn = 4;
4521 if (ty.abiAlignment(self.target.*) == 8)4817 if (ty.abiAlignment(self.target.*) == 8)
...@@ -4529,7 +4825,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4529,7 +4825,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4529 result.stack_byte_count = nsaa;4825 result.stack_byte_count = nsaa;
4530 result.stack_align = 4;4826 result.stack_align = 4;
4531 },4827 },
4532 else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),4828 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
4533 }4829 }
4534 },4830 },
4535 .aarch64 => {4831 .aarch64 => {
...@@ -4560,10 +4856,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4560,10 +4856,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4560 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };4856 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
4561 ncrn += 1;4857 ncrn += 1;
4562 } else {4858 } else {
4563 return self.fail(src, "TODO MCValues with multiple registers", .{});4859 return self.fail("TODO MCValues with multiple registers", .{});
4564 }4860 }
4565 } else if (ncrn < 8 and nsaa == 0) {4861 } else if (ncrn < 8 and nsaa == 0) {
4566 return self.fail(src, "TODO MCValues split between registers and stack", .{});4862 return self.fail("TODO MCValues split between registers and stack", .{});
4567 } else {4863 } else {
4568 ncrn = 8;4864 ncrn = 8;
4569 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided4865 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided
...@@ -4582,11 +4878,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4582,11 +4878,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4582 result.stack_byte_count = nsaa;4878 result.stack_byte_count = nsaa;
4583 result.stack_align = 16;4879 result.stack_align = 16;
4584 },4880 },
4585 else => return self.fail(src, "TODO implement function parameters for {} on aarch64", .{cc}),4881 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
4586 }4882 }
4587 },4883 },
4588 else => if (param_types.len != 0)4884 else => if (param_types.len != 0)
4589 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),4885 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
4590 }4886 }
45914887
4592 if (ret_ty.zigTypeTag() == .NoReturn) {4888 if (ret_ty.zigTypeTag() == .NoReturn) {
...@@ -4601,7 +4897,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4601,7 +4897,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4601 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);4897 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
4602 result.return_value = .{ .register = aliased_reg };4898 result.return_value = .{ .register = aliased_reg };
4603 },4899 },
4604 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),4900 else => return self.fail("TODO implement function return values for {}", .{cc}),
4605 },4901 },
4606 .arm, .armeb => switch (cc) {4902 .arm, .armeb => switch (cc) {
4607 .Naked => unreachable,4903 .Naked => unreachable,
...@@ -4610,10 +4906,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4610,10 +4906,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4610 if (ret_ty_size <= 4) {4906 if (ret_ty_size <= 4) {
4611 result.return_value = .{ .register = c_abi_int_return_regs[0] };4907 result.return_value = .{ .register = c_abi_int_return_regs[0] };
4612 } else {4908 } else {
4613 return self.fail(src, "TODO support more return types for ARM backend", .{});4909 return self.fail("TODO support more return types for ARM backend", .{});
4614 }4910 }
4615 },4911 },
4616 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),4912 else => return self.fail("TODO implement function return values for {}", .{cc}),
4617 },4913 },
4618 .aarch64 => switch (cc) {4914 .aarch64 => switch (cc) {
4619 .Naked => unreachable,4915 .Naked => unreachable,
...@@ -4622,12 +4918,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4622,12 +4918,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4622 if (ret_ty_size <= 8) {4918 if (ret_ty_size <= 8) {
4623 result.return_value = .{ .register = c_abi_int_return_regs[0] };4919 result.return_value = .{ .register = c_abi_int_return_regs[0] };
4624 } else {4920 } else {
4625 return self.fail(src, "TODO support more return types for ARM backend", .{});4921 return self.fail("TODO support more return types for ARM backend", .{});
4626 }4922 }
4627 },4923 },
4628 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),4924 else => return self.fail("TODO implement function return values for {}", .{cc}),
4629 },4925 },
4630 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),4926 else => return self.fail("TODO implement codegen return values for {}", .{self.target.cpu.arch}),
4631 }4927 }
4632 return result;4928 return result;
4633 }4929 }
...@@ -4642,14 +4938,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4642,14 +4938,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4642 };4938 };
4643 }4939 }
46444940
4645 fn fail(self: *Self, src: LazySrcLoc, comptime format: []const u8, args: anytype) InnerError {4941 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
4646 @setCold(true);4942 @setCold(true);
4647 assert(self.err_msg == null);4943 assert(self.err_msg == null);
4648 const src_loc = if (src != .unneeded)4944 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
4649 src.toSrcLocWithDecl(self.mod_fn.owner_decl)
4650 else
4651 self.src_loc;
4652 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src_loc, format, args);
4653 return error.CodegenFail;4945 return error.CodegenFail;
4654 }4946 }
46554947
src/codegen/c.zig+436-276
...@@ -6,8 +6,6 @@ const log = std.log.scoped(.c);...@@ -6,8 +6,6 @@ const log = std.log.scoped(.c);
6const link = @import("../link.zig");6const link = @import("../link.zig");
7const Module = @import("../Module.zig");7const Module = @import("../Module.zig");
8const Compilation = @import("../Compilation.zig");8const Compilation = @import("../Compilation.zig");
9const ir = @import("../air.zig");
10const Inst = ir.Inst;
11const Value = @import("../value.zig").Value;9const Value = @import("../value.zig").Value;
12const Type = @import("../type.zig").Type;10const Type = @import("../type.zig").Type;
13const TypedValue = @import("../TypedValue.zig");11const TypedValue = @import("../TypedValue.zig");
...@@ -15,6 +13,9 @@ const C = link.File.C;...@@ -15,6 +13,9 @@ const C = link.File.C;
15const Decl = Module.Decl;13const Decl = Module.Decl;
16const trace = @import("../tracy.zig").trace;14const trace = @import("../tracy.zig").trace;
17const LazySrcLoc = Module.LazySrcLoc;15const LazySrcLoc = Module.LazySrcLoc;
16const Air = @import("../Air.zig");
17const Zir = @import("../Zir.zig");
18const Liveness = @import("../Liveness.zig");
1819
19const Mutability = enum { Const, Mut };20const Mutability = enum { Const, Mut };
2021
...@@ -25,7 +26,7 @@ pub const CValue = union(enum) {...@@ -25,7 +26,7 @@ pub const CValue = union(enum) {
25 /// Index into local_names, but take the address.26 /// Index into local_names, but take the address.
26 local_ref: usize,27 local_ref: usize,
27 /// A constant instruction, to be rendered inline.28 /// A constant instruction, to be rendered inline.
28 constant: *Inst,29 constant: Air.Inst.Ref,
29 /// Index into the parameters30 /// Index into the parameters
30 arg: usize,31 arg: usize,
31 /// By-value32 /// By-value
...@@ -38,7 +39,7 @@ const BlockData = struct {...@@ -38,7 +39,7 @@ const BlockData = struct {
38 result: CValue,39 result: CValue,
39};40};
4041
41pub const CValueMap = std.AutoHashMap(*Inst, CValue);42pub const CValueMap = std.AutoHashMap(Air.Inst.Index, CValue);
42pub const TypedefMap = std.ArrayHashMap(43pub const TypedefMap = std.ArrayHashMap(
43 Type,44 Type,
44 struct { name: []const u8, rendered: []u8 },45 struct { name: []const u8, rendered: []u8 },
...@@ -94,20 +95,23 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {...@@ -94,20 +95,23 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
94/// It is not available when generating .h file.95/// It is not available when generating .h file.
95pub const Object = struct {96pub const Object = struct {
96 dg: DeclGen,97 dg: DeclGen,
98 air: Air,
99 liveness: Liveness,
97 gpa: *mem.Allocator,100 gpa: *mem.Allocator,
98 code: std.ArrayList(u8),101 code: std.ArrayList(u8),
99 value_map: CValueMap,102 value_map: CValueMap,
100 blocks: std.AutoHashMapUnmanaged(*ir.Inst.Block, BlockData) = .{},103 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
101 next_arg_index: usize = 0,104 next_arg_index: usize = 0,
102 next_local_index: usize = 0,105 next_local_index: usize = 0,
103 next_block_index: usize = 0,106 next_block_index: usize = 0,
104 indent_writer: IndentWriter(std.ArrayList(u8).Writer),107 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
105108
106 fn resolveInst(o: *Object, inst: *Inst) !CValue {109 fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue {
107 if (inst.value()) |_| {110 if (o.air.value(inst)) |_| {
108 return CValue{ .constant = inst };111 return CValue{ .constant = inst };
109 }112 }
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.
111 }115 }
112116
113 fn allocLocalValue(o: *Object) CValue {117 fn allocLocalValue(o: *Object) CValue {
...@@ -131,7 +135,11 @@ pub const Object = struct {...@@ -131,7 +135,11 @@ pub const Object = struct {
131 .none => unreachable,135 .none => unreachable,
132 .local => |i| return w.print("t{d}", .{i}),136 .local => |i| return w.print("t{d}", .{i}),
133 .local_ref => |i| return w.print("&t{d}", .{i}),137 .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 },
135 .arg => |i| return w.print("a{d}", .{i}),143 .arg => |i| return w.print("a{d}", .{i}),
136 .decl => |decl| return w.writeAll(mem.span(decl.name)),144 .decl => |decl| return w.writeAll(mem.span(decl.name)),
137 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),145 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),
...@@ -211,8 +219,9 @@ pub const DeclGen = struct {...@@ -211,8 +219,9 @@ pub const DeclGen = struct {
211 error_msg: ?*Module.ErrorMsg,219 error_msg: ?*Module.ErrorMsg,
212 typedefs: TypedefMap,220 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 } {
215 @setCold(true);223 @setCold(true);
224 const src: LazySrcLoc = .{ .node_offset = 0 };
216 const src_loc = src.toSrcLocWithDecl(dg.decl);225 const src_loc = src.toSrcLocWithDecl(dg.decl);
217 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args);226 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args);
218 return error.AnalysisFail;227 return error.AnalysisFail;
...@@ -228,7 +237,7 @@ pub const DeclGen = struct {...@@ -228,7 +237,7 @@ pub const DeclGen = struct {
228 // This should lower to 0xaa bytes in safe modes, and for unsafe modes should237 // This should lower to 0xaa bytes in safe modes, and for unsafe modes should
229 // lower to leaving variables uninitialized (that might need to be implemented238 // lower to leaving variables uninitialized (that might need to be implemented
230 // outside of this function).239 // 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", .{});
232 }241 }
233 switch (t.zigTypeTag()) {242 switch (t.zigTypeTag()) {
234 .Int => {243 .Int => {
...@@ -438,7 +447,7 @@ pub const DeclGen = struct {...@@ -438,7 +447,7 @@ pub const DeclGen = struct {
438 },447 },
439 else => unreachable,448 else => unreachable,
440 },449 },
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}", .{
442 @tagName(e),451 @tagName(e),
443 }),452 }),
444 }453 }
...@@ -517,14 +526,14 @@ pub const DeclGen = struct {...@@ -517,14 +526,14 @@ pub const DeclGen = struct {
517 break;526 break;
518 }527 }
519 } else {528 } 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", .{});
521 }530 }
522 },531 },
523 else => unreachable,532 else => unreachable,
524 }533 }
525 },534 },
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
529 .Pointer => {538 .Pointer => {
530 if (t.isSlice()) {539 if (t.isSlice()) {
...@@ -679,7 +688,7 @@ pub const DeclGen = struct {...@@ -679,7 +688,7 @@ pub const DeclGen = struct {
679688
680 try dg.renderType(w, int_tag_ty);689 try dg.renderType(w, int_tag_ty);
681 },690 },
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", .{}),
683 .Fn => {692 .Fn => {
684 try dg.renderType(w, t.fnReturnType());693 try dg.renderType(w, t.fnReturnType());
685 try w.writeAll(" (*)(");694 try w.writeAll(" (*)(");
...@@ -702,10 +711,10 @@ pub const DeclGen = struct {...@@ -702,10 +711,10 @@ pub const DeclGen = struct {
702 }711 }
703 try w.writeByte(')');712 try w.writeByte(')');
704 },713 },
705 .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}),714 .Opaque => return dg.fail("TODO: C backend: implement type Opaque", .{}),
706 .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}),715 .Frame => return dg.fail("TODO: C backend: implement type Frame", .{}),
707 .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}),716 .AnyFrame => return dg.fail("TODO: C backend: implement type AnyFrame", .{}),
708 .Vector => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Vector", .{}),717 .Vector => return dg.fail("TODO: C backend: implement type Vector", .{}),
709718
710 .Null,719 .Null,
711 .Undefined,720 .Undefined,
...@@ -758,7 +767,8 @@ pub fn genDecl(o: *Object) !void {...@@ -758,7 +767,8 @@ pub fn genDecl(o: *Object) !void {
758 try o.dg.renderFunctionSignature(o.writer(), is_global);767 try o.dg.renderFunctionSignature(o.writer(), is_global);
759768
760 try o.writer().writeByte(' ');769 try o.writer().writeByte(' ');
761 try genBody(o, func.body);770 const main_body = o.air.getMainBody();
771 try genBody(o, main_body);
762772
763 try o.indent_writer.insertNewline();773 try o.indent_writer.insertNewline();
764 return;774 return;
...@@ -831,9 +841,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {...@@ -831,9 +841,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
831 }841 }
832}842}
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 {
835 const writer = o.writer();845 const writer = o.writer();
836 if (body.instructions.len == 0) {846 if (body.len == 0) {
837 try writer.writeAll("{}");847 try writer.writeAll("{}");
838 return;848 return;
839 }849 }
...@@ -841,82 +851,92 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -841,82 +851,92 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
841 try writer.writeAll("{\n");851 try writer.writeAll("{\n");
842 o.indent_writer.pushIndent();852 o.indent_writer.pushIndent();
843853
844 for (body.instructions) |inst| {854 const air_tags = o.air.instructions.items(.tag);
845 const result_value = switch (inst.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
846 // TODO use a different strategy for add that communicates to the optimizer866 // TODO use a different strategy for add that communicates to the optimizer
847 // that wrapping is UB.867 // that wrapping is UB.
848 .add => try genBinOp(o, inst.castTag(.add).?, " + "),868 .add => try airBinOp( o, inst, " + "),
849 .addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"),869 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
850 // TODO use a different strategy for sub that communicates to the optimizer870 // TODO use a different strategy for sub that communicates to the optimizer
851 // that wrapping is UB.871 // that wrapping is UB.
852 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),872 .sub => try airBinOp( o, inst, " - "),
853 .subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"),873 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
854 // TODO use a different strategy for mul that communicates to the optimizer874 // TODO use a different strategy for mul that communicates to the optimizer
855 // that wrapping is UB.875 // that wrapping is UB.
856 .mul => try genBinOp(o, inst.castTag(.sub).?, " * "),876 .mul => try airBinOp( o, inst, " * "),
857 .mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"),877 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
858 // TODO use a different strategy for div that communicates to the optimizer878 // TODO use a different strategy for div that communicates to the optimizer
859 // that wrapping is UB.879 // 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).?),
888 // bool_and and bool_or are non-short-circuit operations889 // bool_and and bool_or are non-short-circuit operations
889 .bool_and => try genBinOp(o, inst.castTag(.bool_and).?, " & "),890 .bool_and => try airBinOp(o, inst, " & "),
890 .bool_or => try genBinOp(o, inst.castTag(.bool_or).?, " | "),891 .bool_or => try airBinOp(o, inst, " | "),
891 .bit_and => try genBinOp(o, inst.castTag(.bit_and).?, " & "),892 .bit_and => try airBinOp(o, inst, " & "),
892 .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "),893 .bit_or => try airBinOp(o, inst, " | "),
893 .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "),894 .xor => try airBinOp(o, inst, " ^ "),
894 .not => try genUnOp(o, inst.castTag(.not).?, "!"),895
895 .is_null => try genIsNull(o, inst.castTag(.is_null).?),896 .not => try airNot( o, inst),
896 .is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?),897
897 .is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?),898 .optional_payload => try airOptionalPayload(o, inst),
898 .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?),899 .optional_payload_ptr => try airOptionalPayload(o, inst),
899 .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?),900
900 .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?),901 .is_err => try airIsErr(o, inst, "", ".", "!="),
901 .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),902 .is_non_err => try airIsErr(o, inst, "", ".", "=="),
902 .ref => try genRef(o, inst.castTag(.ref).?),903 .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="),
903 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),904 .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="),
904905
905 .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="),906 .is_null => try airIsNull(o, inst, "==", ""),
906 .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="),907 .is_non_null => try airIsNull(o, inst, "!=", ""),
907 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="),908 .is_null_ptr => try airIsNull(o, inst, "==", "[0]"),
908 .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="),909 .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"),
909910
910 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),911 .alloc => try airAlloc(o, inst),
911 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),912 .assembly => try airAsm(o, inst),
912 .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),913 .block => try airBlock(o, inst),
913 .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),914 .bitcast => try airBitcast(o, inst),
914 .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),915 .call => try airCall(o, inst),
915 .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),916 .dbg_stmt => try airDbgStmt(o, inst),
916 .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}),917 .intcast => try airIntCast(o, inst),
917 .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}),918 .load => try airLoad(o, inst),
918 .varptr => try genVarPtr(o, inst.castTag(.varptr).?),919 .ret => try airRet(o, inst),
919 .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}),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
920 };940 };
921 switch (result_value) {941 switch (result_value) {
922 .none => {},942 .none => {},
...@@ -928,38 +948,40 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -928,38 +948,40 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
928 try writer.writeAll("}");948 try writer.writeAll("}");
929}949}
930950
931fn genVarPtr(o: *Object, inst: *Inst.VarPtr) !CValue {951fn airVarPtr(o: *Object, inst: Air.Inst.Index) !CValue {
932 _ = o;952 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
933 return CValue{ .decl_ref = inst.variable.owner_decl };953 const variable = o.air.variables[ty_pl.payload];
954 return CValue{ .decl_ref = variable.owner_decl };
934}955}
935956
936fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {957fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue {
937 const writer = o.writer();958 const writer = o.writer();
959 const inst_ty = o.air.typeOfIndex(inst);
938960
939 // First line: the variable used as data storage.961 // First line: the variable used as data storage.
940 const elem_type = alloc.base.ty.elemType();962 const elem_type = inst_ty.elemType();
941 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;963 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
942 const local = try o.allocLocal(elem_type, mutability);964 const local = try o.allocLocal(elem_type, mutability);
943 try writer.writeAll(";\n");965 try writer.writeAll(";\n");
944966
945 return CValue{ .local_ref = local.local };967 return CValue{ .local_ref = local.local };
946}968}
947969
948fn genArg(o: *Object) CValue {970fn airArg(o: *Object) CValue {
949 const i = o.next_arg_index;971 const i = o.next_arg_index;
950 o.next_arg_index += 1;972 o.next_arg_index += 1;
951 return .{ .arg = i };973 return .{ .arg = i };
952}974}
953975
954fn genRetVoid(o: *Object) !CValue {976fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue {
955 try o.writer().print("return;\n", .{});977 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
956 return CValue.none;978 const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr();
957}979 if (!is_volatile and o.liveness.isUnused(inst))
958980 return CValue.none;
959fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {981 const inst_ty = o.air.typeOfIndex(inst);
960 const operand = try o.resolveInst(inst.operand);982 const operand = try o.resolveInst(ty_op.operand);
961 const writer = o.writer();983 const writer = o.writer();
962 const local = try o.allocLocal(inst.base.ty, .Const);984 const local = try o.allocLocal(inst_ty, .Const);
963 switch (operand) {985 switch (operand) {
964 .local_ref => |i| {986 .local_ref => |i| {
965 const wrapped: CValue = .{ .local = i };987 const wrapped: CValue = .{ .local = i };
...@@ -982,35 +1004,43 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -982,35 +1004,43 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
982 return local;1004 return local;
983}1005}
9841006
985fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {1007fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
986 const operand = try o.resolveInst(inst.operand);1008 const un_op = o.air.instructions.items(.data)[inst].un_op;
987 const writer = o.writer();1009 const writer = o.writer();
988 try writer.writeAll("return ");1010 if (o.air.typeOf(un_op).hasCodeGenBits()) {
989 try o.writeCValue(writer, operand);1011 const operand = try o.resolveInst(un_op);
990 try writer.writeAll(";\n");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 }
991 return CValue.none;1018 return CValue.none;
992}1019}
9931020
994fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue {1021fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
995 if (inst.base.isUnused())1022 if (o.liveness.isUnused(inst))
996 return CValue.none;1023 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
1000 const writer = o.writer();1028 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);
1002 try writer.writeAll(" = (");1031 try writer.writeAll(" = (");
1003 try o.dg.renderType(writer, inst.base.ty);1032 try o.dg.renderType(writer, inst_ty);
1004 try writer.writeAll(")");1033 try writer.writeAll(")");
1005 try o.writeCValue(writer, from);1034 try o.writeCValue(writer, from);
1006 try writer.writeAll(";\n");1035 try writer.writeAll(";\n");
1007 return local;1036 return local;
1008}1037}
10091038
1010fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {1039fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
1011 // *a = b;1040 // *a = b;
1012 const dest_ptr = try o.resolveInst(inst.lhs);1041 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1013 const src_val = try o.resolveInst(inst.rhs);1042 const dest_ptr = try o.resolveInst(bin_op.lhs);
1043 const src_val = try o.resolveInst(bin_op.rhs);
10141044
1015 const writer = o.writer();1045 const writer = o.writer();
1016 switch (dest_ptr) {1046 switch (dest_ptr) {
...@@ -1039,11 +1069,18 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {...@@ -1039,11 +1069,18 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
1039 return CValue.none;1069 return CValue.none;
1040}1070}
10411071
1042fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {1072fn airWrapOp(
1043 if (inst.base.isUnused())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))
1044 return CValue.none;1079 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());
1047 const bits = int_info.bits;1084 const bits = int_info.bits;
10481085
1049 // if it's an unsigned int with non-arbitrary bit size then we can just add1086 // 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...@@ -1052,19 +1089,19 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1052 8, 16, 32, 64, 128 => true,1089 8, 16, 32, 64, 128 => true,
1053 else => false,1090 else => false,
1054 };1091 };
1055 if (ok_bits or inst.base.ty.tag() != .int_unsigned) {1092 if (ok_bits or inst_ty.tag() != .int_unsigned) {
1056 return try genBinOp(o, inst, str_op);1093 return try airBinOp(o, inst, str_op);
1057 }1094 }
1058 }1095 }
10591096
1060 if (bits > 64) {1097 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", .{});
1062 }1099 }
10631100
1064 var min_buf: [80]u8 = undefined;1101 var min_buf: [80]u8 = undefined;
1065 const min = switch (int_info.signedness) {1102 const min = switch (int_info.signedness) {
1066 .unsigned => "0",1103 .unsigned => "0",
1067 else => switch (inst.base.ty.tag()) {1104 else => switch (inst_ty.tag()) {
1068 .c_short => "SHRT_MIN",1105 .c_short => "SHRT_MIN",
1069 .c_int => "INT_MIN",1106 .c_int => "INT_MIN",
1070 .c_long => "LONG_MIN",1107 .c_long => "LONG_MIN",
...@@ -1081,7 +1118,7 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c...@@ -1081,7 +1118,7 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1081 };1118 };
10821119
1083 var max_buf: [80]u8 = undefined;1120 var max_buf: [80]u8 = undefined;
1084 const max = switch (inst.base.ty.tag()) {1121 const max = switch (inst_ty.tag()) {
1085 .c_short => "SHRT_MAX",1122 .c_short => "SHRT_MAX",
1086 .c_ushort => "USHRT_MAX",1123 .c_ushort => "USHRT_MAX",
1087 .c_int => "INT_MAX",1124 .c_int => "INT_MAX",
...@@ -1105,14 +1142,14 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c...@@ -1105,14 +1142,14 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1105 },1142 },
1106 };1143 };
11071144
1108 const lhs = try o.resolveInst(inst.lhs);1145 const lhs = try o.resolveInst(bin_op.lhs);
1109 const rhs = try o.resolveInst(inst.rhs);1146 const rhs = try o.resolveInst(bin_op.rhs);
1110 const w = o.writer();1147 const w = o.writer();
11111148
1112 const ret = try o.allocLocal(inst.base.ty, .Mut);1149 const ret = try o.allocLocal(inst_ty, .Mut);
1113 try w.print(" = zig_{s}", .{fn_op});1150 try w.print(" = zig_{s}", .{fn_op});
11141151
1115 switch (inst.base.ty.tag()) {1152 switch (inst_ty.tag()) {
1116 .isize => try w.writeAll("isize"),1153 .isize => try w.writeAll("isize"),
1117 .c_short => try w.writeAll("short"),1154 .c_short => try w.writeAll("short"),
1118 .c_int => try w.writeAll("int"),1155 .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...@@ -1149,53 +1186,65 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c
1149 return ret;1186 return ret;
1150}1187}
11511188
1152fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {1189fn airNot(o: *Object, inst: Air.Inst.Index) !CValue {
1153 if (inst.base.isUnused())1190 if (o.liveness.isUnused(inst))
1154 return CValue.none;1191 return CValue.none;
11551192
1156 const lhs = try o.resolveInst(inst.lhs);1193 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1157 const rhs = try o.resolveInst(inst.rhs);1194 const op = try o.resolveInst(ty_op.operand);
11581195
1159 const writer = o.writer();1196 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
1162 try writer.writeAll(" = ");1200 try writer.writeAll(" = ");
1163 try o.writeCValue(writer, lhs);1201 if (inst_ty.zigTypeTag() == .Bool)
1164 try writer.print("{s}", .{operator});1202 try writer.writeAll("!")
1165 try o.writeCValue(writer, rhs);1203 else
1204 try writer.writeAll("~");
1205 try o.writeCValue(writer, op);
1166 try writer.writeAll(";\n");1206 try writer.writeAll(";\n");
11671207
1168 return local;1208 return local;
1169}1209}
11701210
1171fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue {1211fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1172 if (inst.base.isUnused())1212 if (o.liveness.isUnused(inst))
1173 return CValue.none;1213 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
1177 const writer = o.writer();1219 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});1223 try writer.writeAll(" = ");
1181 try o.writeCValue(writer, operand);1224 try o.writeCValue(writer, lhs);
1225 try writer.print("{s}", .{operator});
1226 try o.writeCValue(writer, rhs);
1182 try writer.writeAll(";\n");1227 try writer.writeAll(";\n");
11831228
1184 return local;1229 return local;
1185}1230}
11861231
1187fn genCall(o: *Object, inst: *Inst.Call) !CValue {1232fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
1188 if (inst.func.castTag(.constant)) |func_inst| {1233 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1189 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|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|
1190 extern_fn.data1239 extern_fn.data
1191 else if (func_inst.val.castTag(.function)) |func_payload|1240 else if (func_val.castTag(.function)) |func_payload|
1192 func_payload.data.owner_decl1241 func_payload.data.owner_decl
1193 else1242 else
1194 unreachable;1243 unreachable;
11951244
1196 const fn_ty = fn_decl.ty;1245 const fn_ty = fn_decl.ty;
1197 const ret_ty = fn_ty.fnReturnType();1246 const ret_ty = fn_ty.fnReturnType();
1198 const unused_result = inst.base.isUnused();1247 const unused_result = o.liveness.isUnused(inst);
1199 var result_local: CValue = .none;1248 var result_local: CValue = .none;
12001249
1201 const writer = o.writer();1250 const writer = o.writer();
...@@ -1209,41 +1258,44 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {...@@ -1209,41 +1258,44 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {
1209 }1258 }
1210 const fn_name = mem.spanZ(fn_decl.name);1259 const fn_name = mem.spanZ(fn_decl.name);
1211 try writer.print("{s}(", .{fn_name});1260 try writer.print("{s}(", .{fn_name});
1212 if (inst.args.len != 0) {1261 for (args) |arg, i| {
1213 for (inst.args) |arg, i| {1262 if (i != 0) {
1214 if (i > 0) {1263 try writer.writeAll(", ");
1215 try writer.writeAll(", ");1264 }
1216 }1265 if (o.air.value(arg)) |val| {
1217 if (arg.value()) |val| {1266 try o.dg.renderValue(writer, o.air.typeOf(arg), val);
1218 try o.dg.renderValue(writer, arg.ty, val);1267 } else {
1219 } else {1268 const val = try o.resolveInst(arg);
1220 const val = try o.resolveInst(arg);1269 try o.writeCValue(writer, val);
1221 try o.writeCValue(writer, val);
1222 }
1223 }1270 }
1224 }1271 }
1225 try writer.writeAll(");\n");1272 try writer.writeAll(");\n");
1226 return result_local;1273 return result_local;
1227 } else {1274 } 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", .{});
1229 }1276 }
1230}1277}
12311278
1232fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue {1279fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue {
1233 _ = o;1280 const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt;
1234 _ = inst;1281 const writer = o.writer();
1235 // TODO emit #line directive here with line number and filename1282 try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
1236 return CValue.none;1283 return CValue.none;
1237}1284}
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
1240 const block_id: usize = o.next_block_index;1291 const block_id: usize = o.next_block_index;
1241 o.next_block_index += 1;1292 o.next_block_index += 1;
1242 const writer = o.writer();1293 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: {
1245 // allocate a location for the result1297 // 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);
1247 try writer.writeAll(";\n");1299 try writer.writeAll(";\n");
1248 break :blk local;1300 break :blk local;
1249 } else CValue{ .none = {} };1301 } else CValue{ .none = {} };
...@@ -1253,42 +1305,44 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue {...@@ -1253,42 +1305,44 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
1253 .result = result,1305 .result = result,
1254 });1306 });
12551307
1256 try genBody(o, inst.body);1308 try genBody(o, body);
1257 try o.indent_writer.insertNewline();1309 try o.indent_writer.insertNewline();
1258 // label must be followed by an expression, add an empty one.1310 // label must be followed by an expression, add an empty one.
1259 try writer.print("zig_block_{d}:;\n", .{block_id});1311 try writer.print("zig_block_{d}:;\n", .{block_id});
1260 return result;1312 return result;
1261}1313}
12621314
1263fn genBr(o: *Object, inst: *Inst.Br) !CValue {1315fn airBr(o: *Object, inst: Air.Inst.Index) !CValue {
1264 const result = o.blocks.get(inst.block).?.result;1316 const branch = o.air.instructions.items(.data)[inst].br;
1317 const block = o.blocks.get(branch.block_inst).?;
1318 const result = block.result;
1265 const writer = o.writer();1319 const writer = o.writer();
12661320
1267 // If result is .none then the value of the block is unused.1321 // If result is .none then the value of the block is unused.
1268 if (inst.operand.ty.tag() != .void and result != .none) {1322 if (result != .none) {
1269 const operand = try o.resolveInst(inst.operand);1323 const operand = try o.resolveInst(branch.operand);
1270 try o.writeCValue(writer, result);1324 try o.writeCValue(writer, result);
1271 try writer.writeAll(" = ");1325 try writer.writeAll(" = ");
1272 try o.writeCValue(writer, operand);1326 try o.writeCValue(writer, operand);
1273 try writer.writeAll(";\n");1327 try writer.writeAll(";\n");
1274 }1328 }
12751329
1276 return genBrVoid(o, inst.block);1330 try o.writer().print("goto zig_block_{d};\n", .{block.block_id});
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});
1281 return CValue.none;1331 return CValue.none;
1282}1332}
12831333
1284fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {1334fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue {
1285 const operand = try o.resolveInst(inst.operand);1335 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1336 const operand = try o.resolveInst(ty_op.operand);
12861337
1287 const writer = o.writer();1338 const writer = o.writer();
1288 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {1339 const inst_ty = o.air.typeOfIndex(inst);
1289 const local = try o.allocLocal(inst.base.ty, .Const);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);
1290 try writer.writeAll(" = (");1344 try writer.writeAll(" = (");
1291 try o.dg.renderType(writer, inst.base.ty);1345 try o.dg.renderType(writer, inst_ty);
12921346
1293 try writer.writeAll(")");1347 try writer.writeAll(")");
1294 try o.writeCValue(writer, operand);1348 try o.writeCValue(writer, operand);
...@@ -1296,7 +1350,7 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1296,7 +1350,7 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
1296 return local;1350 return local;
1297 }1351 }
12981352
1299 const local = try o.allocLocal(inst.base.ty, .Mut);1353 const local = try o.allocLocal(inst_ty, .Mut);
1300 try writer.writeAll(";\n");1354 try writer.writeAll(";\n");
13011355
1302 try writer.writeAll("memcpy(&");1356 try writer.writeAll("memcpy(&");
...@@ -1310,60 +1364,79 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1310,60 +1364,79 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
1310 return local;1364 return local;
1311}1365}
13121366
1313fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue {1367fn airBreakpoint(o: *Object) !CValue {
1314 _ = inst;
1315 try o.writer().writeAll("zig_breakpoint();\n");1368 try o.writer().writeAll("zig_breakpoint();\n");
1316 return CValue.none;1369 return CValue.none;
1317}1370}
13181371
1319fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue {1372fn airUnreach(o: *Object) !CValue {
1320 _ = inst;
1321 try o.writer().writeAll("zig_unreachable();\n");1373 try o.writer().writeAll("zig_unreachable();\n");
1322 return CValue.none;1374 return CValue.none;
1323}1375}
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];
1326 try o.writer().writeAll("while (true) ");1381 try o.writer().writeAll("while (true) ");
1327 try genBody(o, inst.body);1382 try genBody(o, body);
1328 try o.indent_writer.insertNewline();1383 try o.indent_writer.insertNewline();
1329 return CValue.none;1384 return CValue.none;
1330}1385}
13311386
1332fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue {1387fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue {
1333 const cond = try o.resolveInst(inst.condition);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];
1334 const writer = o.writer();1393 const writer = o.writer();
13351394
1336 try writer.writeAll("if (");1395 try writer.writeAll("if (");
1337 try o.writeCValue(writer, cond);1396 try o.writeCValue(writer, cond);
1338 try writer.writeAll(") ");1397 try writer.writeAll(") ");
1339 try genBody(o, inst.then_body);1398 try genBody(o, then_body);
1340 try writer.writeAll(" else ");1399 try writer.writeAll(" else ");
1341 try genBody(o, inst.else_body);1400 try genBody(o, else_body);
1342 try o.indent_writer.insertNewline();1401 try o.indent_writer.insertNewline();
13431402
1344 return CValue.none;1403 return CValue.none;
1345}1404}
13461405
1347fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {1406fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue {
1348 const target = try o.resolveInst(inst.target);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);
1349 const writer = o.writer();1411 const writer = o.writer();
13501412
1351 try writer.writeAll("switch (");1413 try writer.writeAll("switch (");
1352 try o.writeCValue(writer, target);1414 try o.writeCValue(writer, condition);
1353 try writer.writeAll(") {\n");1415 try writer.writeAll(") {");
1354 o.indent_writer.pushIndent();1416 o.indent_writer.pushIndent();
13551417
1356 for (inst.cases) |case| {1418 var extra_index: usize = switch_br.end;
1357 try writer.writeAll("case ");1419 var case_i: u32 = 0;
1358 try o.dg.renderValue(writer, inst.target.ty, case.item);1420 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
1359 try writer.writeAll(": ");1421 const case = o.air.extraData(Air.SwitchBr.Case, extra_index);
1360 // the case body must be noreturn so we don't need to insert a break1422 const items = @bitCast([]const Air.Inst.Ref, o.air.extra[case.end..][0..case.data.items_len]);
1361 try genBody(o, case.body);1423 const case_body = o.air.extra[case.end + items.len ..][0..case.data.body_len];
1362 try o.indent_writer.insertNewline();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);
1363 }1434 }
13641435
1436 const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len];
1437 try o.indent_writer.insertNewline();
1365 try writer.writeAll("default: ");1438 try writer.writeAll("default: ");
1366 try genBody(o, inst.else_body);1439 try genBody(o, else_body);
1367 try o.indent_writer.insertNewline();1440 try o.indent_writer.insertNewline();
13681441
1369 o.indent_writer.popIndent();1442 o.indent_writer.popIndent();
...@@ -1371,39 +1444,75 @@ fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {...@@ -1371,39 +1444,75 @@ fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {
1371 return CValue.none;1444 return CValue.none;
1372}1445}
13731446
1374fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {1447fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1375 if (as.base.isUnused() and !as.is_volatile)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)
1376 return CValue.none;1467 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
1378 const writer = o.writer();1481 const writer = o.writer();
1379 for (as.inputs) |i, index| {1482 for (args) |arg| {
1380 if (i[0] == '{' and i[i.len - 1] == '}') {1483 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1381 const reg = i[1 .. i.len - 1];1484 extra_i = input.end;
1382 const arg = as.args[index];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];
1383 const arg_c_value = try o.resolveInst(arg);1488 const arg_c_value = try o.resolveInst(arg);
1384 try writer.writeAll("register ");1489 try writer.writeAll("register ");
1385 try o.dg.renderType(writer, arg.ty);1490 try o.dg.renderType(writer, o.air.typeOf(arg));
13861491
1387 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });1492 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
1388 try o.writeCValue(writer, arg_c_value);1493 try o.writeCValue(writer, arg_c_value);
1389 try writer.writeAll(";\n");1494 try writer.writeAll(";\n");
1390 } else {1495 } 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", .{});
1392 }1497 }
1393 }1498 }
1394 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";1499 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
1395 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });1500 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
1396 if (as.output_constraint) |_| {1501 if (output_constraint) |_| {
1397 return o.dg.fail(.{ .node_offset = 0 }, "TODO: CBE inline asm output", .{});1502 return o.dg.fail("TODO: CBE inline asm output", .{});
1398 }1503 }
1399 if (as.inputs.len > 0) {1504 if (args.len > 0) {
1400 if (as.output_constraint == null) {1505 if (output_constraint == null) {
1401 try writer.writeAll(" :");1506 try writer.writeAll(" :");
1402 }1507 }
1403 try writer.writeAll(": ");1508 try writer.writeAll(": ");
1404 for (as.inputs) |i, index| {1509 extra_i = args_extra_begin;
1405 if (i[0] == '{' and i[i.len - 1] == '}') {1510 for (args) |_, index| {
1406 const reg = i[1 .. i.len - 1];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];
1407 if (index > 0) {1516 if (index > 0) {
1408 try writer.writeAll(", ");1517 try writer.writeAll(", ");
1409 }1518 }
...@@ -1416,40 +1525,51 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {...@@ -1416,40 +1525,51 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
1416 }1525 }
1417 try writer.writeAll(");\n");1526 try writer.writeAll(");\n");
14181527
1419 if (as.base.isUnused())1528 if (o.liveness.isUnused(inst))
1420 return CValue.none;1529 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", .{});
1423}1532}
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;
1426 const writer = o.writer();1544 const writer = o.writer();
1427 const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr;1545 const operand = try o.resolveInst(un_op);
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);
14311546
1432 const local = try o.allocLocal(Type.initTag(.bool), .Const);1547 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1433 try writer.writeAll(" = (");1548 try writer.writeAll(" = (");
1434 try o.writeCValue(writer, operand);1549 try o.writeCValue(writer, operand);
14351550
1436 if (inst.operand.ty.isPtrLikeOptional()) {1551 if (o.air.typeOf(un_op).isPtrLikeOptional()) {
1437 // operand is a regular pointer, test `operand !=/== NULL`1552 // 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 });
1439 } else {1554 } 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 });
1441 }1556 }
1442 return local;1557 return local;
1443}1558}
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;
1446 const writer = o.writer();1565 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)1569 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
1450 inst.operand.ty.elemType()1570 operand_ty.elemType()
1451 else1571 else
1452 inst.operand.ty;1572 operand_ty;
14531573
1454 if (opt_ty.isPtrLikeOptional()) {1574 if (opt_ty.isPtrLikeOptional()) {
1455 // the operand is just a regular pointer, no need to do anything special.1575 // 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 {...@@ -1457,10 +1577,11 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
1457 return operand;1577 return operand;
1458 }1578 }
14591579
1460 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1580 const inst_ty = o.air.typeOfIndex(inst);
1461 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";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);
1464 try writer.print(" = {s}(", .{maybe_addrof});1585 try writer.print(" = {s}(", .{maybe_addrof});
1465 try o.writeCValue(writer, operand);1586 try o.writeCValue(writer, operand);
14661587
...@@ -1468,24 +1589,36 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1468,24 +1589,36 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
1468 return local;1589 return local;
1469}1590}
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;
1472 const writer = o.writer();1597 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);
1476 try writer.writeAll(" = ");1602 try writer.writeAll(" = ");
1477 try o.writeCValue(writer, operand);1603 try o.writeCValue(writer, operand);
1478 try writer.writeAll(";\n");1604 try writer.writeAll(";\n");
1479 return local;1605 return local;
1480}1606}
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;
1483 const writer = o.writer();1614 const writer = o.writer();
1484 const struct_ptr = try o.resolveInst(inst.struct_ptr);1615 const struct_ptr = try o.resolveInst(extra.struct_ptr);
1485 const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data;1616 const struct_ptr_ty = o.air.typeOf(extra.struct_ptr);
1486 const field_name = struct_obj.fields.keys()[inst.field_index];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);
1489 switch (struct_ptr) {1622 switch (struct_ptr) {
1490 .local_ref => |i| {1623 .local_ref => |i| {
1491 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });1624 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
...@@ -1500,17 +1633,20 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {...@@ -1500,17 +1633,20 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
1500}1633}
15011634
1502// *(E!T) -> E NOT *E1635// *(E!T) -> E NOT *E
1503fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {1636fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1504 if (inst.base.isUnused())1637 if (o.liveness.isUnused(inst))
1505 return CValue.none;1638 return CValue.none;
15061639
1640 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1641 const inst_ty = o.air.typeOfIndex(inst);
1507 const writer = o.writer();1642 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();
1511 if (!payload_ty.hasCodeGenBits()) {1647 if (!payload_ty.hasCodeGenBits()) {
1512 if (inst.operand.ty.zigTypeTag() == .Pointer) {1648 if (operand_ty.zigTypeTag() == .Pointer) {
1513 const local = try o.allocLocal(inst.base.ty, .Const);1649 const local = try o.allocLocal(inst_ty, .Const);
1514 try writer.writeAll(" = *");1650 try writer.writeAll(" = *");
1515 try o.writeCValue(writer, operand);1651 try o.writeCValue(writer, operand);
1516 try writer.writeAll(";\n");1652 try writer.writeAll(";\n");
...@@ -1520,9 +1656,9 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1520,9 +1656,9 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1520 }1656 }
1521 }1657 }
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);
1526 try writer.writeAll(" = (");1662 try writer.writeAll(" = (");
1527 try o.writeCValue(writer, operand);1663 try o.writeCValue(writer, operand);
15281664
...@@ -1530,22 +1666,25 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1530,22 +1666,25 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1530 return local;1666 return local;
1531}1667}
15321668
1533fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {1669fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1534 if (inst.base.isUnused())1670 if (o.liveness.isUnused(inst))
1535 return CValue.none;1671 return CValue.none;
15361672
1673 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1537 const writer = o.writer();1674 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();
1541 if (!payload_ty.hasCodeGenBits()) {1679 if (!payload_ty.hasCodeGenBits()) {
1542 return CValue.none;1680 return CValue.none;
1543 }1681 }
15441682
1545 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1683 const inst_ty = o.air.typeOfIndex(inst);
1546 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";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);
1549 try writer.print(" = {s}(", .{maybe_addrof});1688 try writer.print(" = {s}(", .{maybe_addrof});
1550 try o.writeCValue(writer, operand);1689 try o.writeCValue(writer, operand);
15511690
...@@ -1553,54 +1692,75 @@ fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1553,54 +1692,75 @@ fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1553 return local;1692 return local;
1554}1693}
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;
1557 const writer = o.writer();1700 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()) {
1561 // the operand is just a regular pointer, no need to do anything special.1705 // the operand is just a regular pointer, no need to do anything special.
1562 return operand;1706 return operand;
1563 }1707 }
15641708
1565 // .wrap_optional is used to convert non-optionals into optionals so it can never be null.1709 // .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);
1567 try writer.writeAll(" = { .is_null = false, .payload =");1711 try writer.writeAll(" = { .is_null = false, .payload =");
1568 try o.writeCValue(writer, operand);1712 try o.writeCValue(writer, operand);
1569 try writer.writeAll("};\n");1713 try writer.writeAll("};\n");
1570 return local;1714 return local;
1571}1715}
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
1573 const writer = o.writer();1720 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);
1577 try writer.writeAll(" = { .error = ");1726 try writer.writeAll(" = { .error = ");
1578 try o.writeCValue(writer, operand);1727 try o.writeCValue(writer, operand);
1579 try writer.writeAll(" };\n");1728 try writer.writeAll(" };\n");
1580 return local;1729 return local;
1581}1730}
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;
1583 const writer = o.writer();1737 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);
1587 try writer.writeAll(" = { .error = 0, .payload = ");1742 try writer.writeAll(" = { .error = 0, .payload = ");
1588 try o.writeCValue(writer, operand);1743 try o.writeCValue(writer, operand);
1589 try writer.writeAll(" };\n");1744 try writer.writeAll(" };\n");
1590 return local;1745 return local;
1591}1746}
15921747
1593fn genIsErr(1748fn airIsErr(
1594 o: *Object,1749 o: *Object,
1595 inst: *Inst.UnOp,1750 inst: Air.Inst.Index,
1596 deref_prefix: [*:0]const u8,1751 deref_prefix: [*:0]const u8,
1597 deref_suffix: [*:0]const u8,1752 deref_suffix: [*:0]const u8,
1598 op_str: [*:0]const u8,1753 op_str: [*:0]const u8,
1599) !CValue {1754) !CValue {
1755 if (o.liveness.isUnused(inst))
1756 return CValue.none;
1757
1758 const un_op = o.air.instructions.items(.data)[inst].un_op;
1600 const writer = o.writer();1759 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);
1602 const local = try o.allocLocal(Type.initTag(.bool), .Const);1762 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1603 const payload_ty = inst.operand.ty.errorUnionChild();1763 const payload_ty = operand_ty.errorUnionChild();
1604 if (!payload_ty.hasCodeGenBits()) {1764 if (!payload_ty.hasCodeGenBits()) {
1605 try writer.print(" = {s}", .{deref_prefix});1765 try writer.print(" = {s}", .{deref_prefix});
1606 try o.writeCValue(writer, operand);1766 try o.writeCValue(writer, operand);
src/codegen/llvm.zig+278-195
...@@ -9,8 +9,8 @@ const math = std.math;...@@ -9,8 +9,8 @@ const math = std.math;
99
10const Module = @import("../Module.zig");10const Module = @import("../Module.zig");
11const TypedValue = @import("../TypedValue.zig");11const TypedValue = @import("../TypedValue.zig");
12const ir = @import("../air.zig");12const Air = @import("../Air.zig");
13const Inst = ir.Inst;13const Liveness = @import("../Liveness.zig");
1414
15const Value = @import("../value.zig").Value;15const Value = @import("../value.zig").Value;
16const Type = @import("../type.zig").Type;16const Type = @import("../type.zig").Type;
...@@ -276,6 +276,70 @@ pub const Object = struct {...@@ -276,6 +276,70 @@ pub const Object = struct {
276 }276 }
277 }277 }
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
279 pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void {343 pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void {
280 var dg: DeclGen = .{344 var dg: DeclGen = .{
281 .object = self,345 .object = self,
...@@ -327,44 +391,8 @@ pub const DeclGen = struct {...@@ -327,44 +391,8 @@ pub const DeclGen = struct {
327 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });391 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
328392
329 if (decl.val.castTag(.function)) |func_payload| {393 if (decl.val.castTag(.function)) |func_payload| {
330 const func = func_payload.data;394 _ = func_payload;
331395 @panic("TODO llvm backend genDecl function pointer");
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);
368 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {396 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
369 _ = try self.resolveLLVMFunction(extern_fn.data);397 _ = try self.resolveLLVMFunction(extern_fn.data);
370 } else {398 } else {
...@@ -590,29 +618,31 @@ pub const DeclGen = struct {...@@ -590,29 +618,31 @@ pub const DeclGen = struct {
590};618};
591619
592pub const FuncGen = struct {620pub const FuncGen = struct {
621 gpa: *Allocator,
593 dg: *DeclGen,622 dg: *DeclGen,
623 air: Air,
624 liveness: Liveness,
594625
595 builder: *const llvm.Builder,626 builder: *const llvm.Builder,
596627
597 /// This stores the LLVM values used in a function, such that they can be628 /// This stores the LLVM values used in a function, such that they can be referred to
598 /// referred to in other instructions. This table is cleared before every function is generated.629 /// 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 blocks630 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value),
600 /// in here, however if a block ends, the instructions can be thrown away.
601 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *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.
604 args: []*const llvm.Value,634 args: []*const llvm.Value,
605 arg_index: usize,635 arg_index: usize,
606636
607 entry_block: *const llvm.BasicBlock,637 entry_block: *const llvm.BasicBlock,
608 /// This fields stores the last alloca instruction, such that we can append more alloca instructions638 /// This fields stores the last alloca instruction, such that we can append
609 /// to the top of the function.639 /// more alloca instructions to the top of the function.
610 latest_alloca_inst: ?*const llvm.Value,640 latest_alloca_inst: ?*const llvm.Value,
611641
612 llvm_func: *const llvm.Value,642 llvm_func: *const llvm.Value,
613643
614 /// This data structure is used to implement breaking to blocks.644 /// 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 {
616 parent_bb: *const llvm.BasicBlock,646 parent_bb: *const llvm.BasicBlock,
617 break_bbs: *BreakBasicBlocks,647 break_bbs: *BreakBasicBlocks,
618 break_vals: *BreakValues,648 break_vals: *BreakValues,
...@@ -623,9 +653,9 @@ pub const FuncGen = struct {...@@ -623,9 +653,9 @@ pub const FuncGen = struct {
623653
624 fn deinit(self: *FuncGen) void {654 fn deinit(self: *FuncGen) void {
625 self.builder.dispose();655 self.builder.dispose();
626 self.func_inst_table.deinit(self.gpa());656 self.func_inst_table.deinit(self.gpa);
627 self.gpa().free(self.args);657 self.gpa.free(self.args);
628 self.blocks.deinit(self.gpa());658 self.blocks.deinit(self.gpa);
629 }659 }
630660
631 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {661 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
...@@ -641,65 +671,68 @@ pub const FuncGen = struct {...@@ -641,65 +671,68 @@ pub const FuncGen = struct {
641 return self.dg.object.context;671 return self.dg.object.context;
642 }672 }
643673
644 fn gpa(self: *FuncGen) *Allocator {674 fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {
645 return self.dg.gpa;675 if (self.air.value(inst)) |val| {
646 }676 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self);
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);
651 }677 }
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
654 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});681 return self.todo("implement global llvm values (or the value is not in the func_inst_table table)", .{});
655 }682 }
656683
657 fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void {684 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {
658 for (body.instructions) |inst| {685 const air_tags = self.air.instructions.items(.tag);
659 const opt_value = switch (inst.tag) {686 for (body) |inst| {
660 .add => try self.genAdd(inst.castTag(.add).?),687 const opt_value = switch (air_tags[inst]) {
661 .alloc => try self.genAlloc(inst.castTag(.alloc).?),688 .add => try self.airAdd(inst),
662 .arg => try self.genArg(inst.castTag(.arg).?),689 .sub => try self.airSub(inst),
663 .bitcast => try self.genBitCast(inst.castTag(.bitcast).?),690
664 .block => try self.genBlock(inst.castTag(.block).?),691 .cmp_eq => try self.airCmp(inst, .eq),
665 .br => try self.genBr(inst.castTag(.br).?),692 .cmp_gt => try self.airCmp(inst, .gt),
666 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),693 .cmp_gte => try self.airCmp(inst, .gte),
667 .br_void => try self.genBrVoid(inst.castTag(.br_void).?),694 .cmp_lt => try self.airCmp(inst, .lt),
668 .call => try self.genCall(inst.castTag(.call).?),695 .cmp_lte => try self.airCmp(inst, .lte),
669 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),696 .cmp_neq => try self.airCmp(inst, .neq),
670 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),697
671 .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?, .gte),698 .is_non_null => try self.airIsNonNull(inst, false),
672 .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?, .lt),699 .is_non_null_ptr => try self.airIsNonNull(inst, true),
673 .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?, .lte),700 .is_null => try self.airIsNull(inst, false),
674 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),701 .is_null_ptr => try self.airIsNull(inst, true),
675 .condbr => try self.genCondBr(inst.castTag(.condbr).?),702
676 .intcast => try self.genIntCast(inst.castTag(.intcast).?),703 .alloc => try self.airAlloc(inst),
677 .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false),704 .arg => try self.airArg(inst),
678 .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true),705 .bitcast => try self.airBitCast(inst),
679 .is_null => try self.genIsNull(inst.castTag(.is_null).?, false),706 .block => try self.airBlock(inst),
680 .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true),707 .br => try self.airBr(inst),
681 .load => try self.genLoad(inst.castTag(.load).?),708 .breakpoint => try self.airBreakpoint(inst),
682 .loop => try self.genLoop(inst.castTag(.loop).?),709 .call => try self.airCall(inst),
683 .not => try self.genNot(inst.castTag(.not).?),710 .cond_br => try self.airCondBr(inst),
684 .ret => try self.genRet(inst.castTag(.ret).?),711 .intcast => try self.airIntCast(inst),
685 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),712 .load => try self.airLoad(inst),
686 .store => try self.genStore(inst.castTag(.store).?),713 .loop => try self.airLoop(inst),
687 .sub => try self.genSub(inst.castTag(.sub).?),714 .not => try self.airNot(inst),
688 .unreach => self.genUnreach(inst.castTag(.unreach).?),715 .ret => try self.airRet(inst),
689 .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false),716 .store => try self.airStore(inst),
690 .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true),717 .unreach => self.airUnreach(inst),
718 .optional_payload => try self.airOptionalPayload(inst, false),
719 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
691 .dbg_stmt => blk: {720 .dbg_stmt => blk: {
692 // TODO: implement debug info721 // TODO: implement debug info
693 break :blk null;722 break :blk null;
694 },723 },
695 else => |tag| return self.todo("implement TZIR instruction: {}", .{tag}),724 else => |tag| return self.todo("implement AIR instruction: {}", .{tag}),
696 };725 };
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);
698 }727 }
699 }728 }
700729
701 fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value {730 fn airCall(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
702 if (inst.func.value()) |func_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| {
703 const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|736 const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|
704 extern_fn.data737 extern_fn.data
705 else if (func_value.castTag(.function)) |func_payload|738 else if (func_value.castTag(.function)) |func_payload|
...@@ -711,12 +744,10 @@ pub const FuncGen = struct {...@@ -711,12 +744,10 @@ pub const FuncGen = struct {
711 const zig_fn_type = fn_decl.ty;744 const zig_fn_type = fn_decl.ty;
712 const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl);745 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);750 for (args) |arg, i| {
717 defer self.gpa().free(llvm_param_vals);
718
719 for (inst.args) |arg, i| {
720 llvm_param_vals[i] = try self.resolveInst(arg);751 llvm_param_vals[i] = try self.resolveInst(arg);
721 }752 }
722753
...@@ -724,8 +755,8 @@ pub const FuncGen = struct {...@@ -724,8 +755,8 @@ pub const FuncGen = struct {
724 // Do we need that?755 // Do we need that?
725 const call = self.builder.buildCall(756 const call = self.builder.buildCall(
726 llvm_fn,757 llvm_fn,
727 if (num_args == 0) null else llvm_param_vals.ptr,758 if (args.len == 0) null else llvm_param_vals.ptr,
728 @intCast(c_uint, num_args),759 @intCast(c_uint, args.len),
729 "",760 "",
730 );761 );
731762
...@@ -743,31 +774,31 @@ pub const FuncGen = struct {...@@ -743,31 +774,31 @@ pub const FuncGen = struct {
743 }774 }
744 }775 }
745776
746 fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {777 fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
747 _ = inst;778 const un_op = self.air.instructions.items(.data)[inst].un_op;
748 _ = self.builder.buildRetVoid();779 if (!self.air.typeOf(un_op).hasCodeGenBits()) {
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.
755 _ = self.builder.buildRetVoid();780 _ = self.builder.buildRetVoid();
756 return null;781 return null;
757 }782 }
758 _ = self.builder.buildRet(try self.resolveInst(inst.operand));783 const operand = try self.resolveInst(un_op);
784 _ = self.builder.buildRet(operand);
759 return null;785 return null;
760 }786 }
761787
762 fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value {788 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator) !?*const llvm.Value {
763 const lhs = try self.resolveInst(inst.lhs);789 if (self.liveness.isUnused(inst))
764 const rhs = try self.resolveInst(inst.rhs);790 return null;
765791
766 if (!inst.base.ty.isInt())792 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
767 if (inst.base.ty.tag() != .bool)793 const lhs = try self.resolveInst(bin_op.lhs);
768 return self.todo("implement 'genCmp' for type {}", .{inst.base.ty});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();
771 const operation = switch (op) {802 const operation = switch (op) {
772 .eq => .EQ,803 .eq => .EQ,
773 .neq => .NE,804 .neq => .NE,
...@@ -780,32 +811,36 @@ pub const FuncGen = struct {...@@ -780,32 +811,36 @@ pub const FuncGen = struct {
780 return self.builder.buildICmp(operation, lhs, rhs, "");811 return self.builder.buildICmp(operation, lhs, rhs, "");
781 }812 }
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];
784 const parent_bb = self.context().createBasicBlock("Block");818 const parent_bb = self.context().createBasicBlock("Block");
785819
786 // 5 breaks to a block seems like a reasonable default.820 // 5 breaks to a block seems like a reasonable default.
787 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5);821 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5);
788 var break_vals = try BreakValues.initCapacity(self.gpa(), 5);822 var break_vals = try BreakValues.initCapacity(self.gpa, 5);
789 try self.blocks.putNoClobber(self.gpa(), inst, .{823 try self.blocks.putNoClobber(self.gpa, inst, .{
790 .parent_bb = parent_bb,824 .parent_bb = parent_bb,
791 .break_bbs = &break_bbs,825 .break_bbs = &break_bbs,
792 .break_vals = &break_vals,826 .break_vals = &break_vals,
793 });827 });
794 defer {828 defer {
795 assert(self.blocks.remove(inst));829 assert(self.blocks.remove(inst));
796 break_bbs.deinit(self.gpa());830 break_bbs.deinit(self.gpa);
797 break_vals.deinit(self.gpa());831 break_vals.deinit(self.gpa);
798 }832 }
799833
800 try self.genBody(inst.body);834 try self.genBody(body);
801835
802 self.llvm_func.appendExistingBasicBlock(parent_bb);836 self.llvm_func.appendExistingBasicBlock(parent_bb);
803 self.builder.positionBuilderAtEnd(parent_bb);837 self.builder.positionBuilderAtEnd(parent_bb);
804838
805 // If the block does not return a value, we dont have to create a phi node.839 // 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), "");
809 phi_node.addIncoming(844 phi_node.addIncoming(
810 break_vals.items.ptr,845 break_vals.items.ptr,
811 break_bbs.items.ptr,846 break_bbs.items.ptr,
...@@ -814,35 +849,30 @@ pub const FuncGen = struct {...@@ -814,35 +849,30 @@ pub const FuncGen = struct {
814 return phi_node;849 return phi_node;
815 }850 }
816851
817 fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value {852 fn airBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
818 var block = self.blocks.get(inst.block).?;853 const branch = self.air.instructions.items(.data)[inst].br;
854 const block = self.blocks.get(branch.block_inst).?;
819855
820 // If the break doesn't break a value, then we don't have to add856 // If the break doesn't break a value, then we don't have to add
821 // the values to the lists.857 // the values to the lists.
822 if (!inst.operand.ty.hasCodeGenBits()) {858 if (self.air.typeOf(branch.operand).hasCodeGenBits()) {
823 // TODO: in astgen these instructions should turn into `br_void` instructions.859 const val = try self.resolveInst(branch.operand);
824 _ = self.builder.buildBr(block.parent_bb);
825 } else {
826 const val = try self.resolveInst(inst.operand);
827860
828 // For the phi node, we need the basic blocks and the values of the861 // For the phi node, we need the basic blocks and the values of the
829 // break instructions.862 // break instructions.
830 try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock());863 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
831 try block.break_vals.append(self.gpa(), val);864 try block.break_vals.append(self.gpa, val);
832
833 _ = self.builder.buildBr(block.parent_bb);
834 }865 }
835 return null;
836 }
837
838 fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value {
839 var block = self.blocks.get(inst.block).?;
840 _ = self.builder.buildBr(block.parent_bb);866 _ = self.builder.buildBr(block.parent_bb);
841 return null;867 return null;
842 }868 }
843869
844 fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value {870 fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
845 const condition_value = try self.resolveInst(inst.condition);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
847 const then_block = self.context().appendBasicBlock(self.llvm_func, "Then");877 const then_block = self.context().appendBasicBlock(self.llvm_func, "Then");
848 const else_block = self.context().appendBasicBlock(self.llvm_func, "Else");878 const else_block = self.context().appendBasicBlock(self.llvm_func, "Else");
...@@ -851,38 +881,51 @@ pub const FuncGen = struct {...@@ -851,38 +881,51 @@ pub const FuncGen = struct {
851 defer self.builder.positionBuilderAtEnd(prev_block);881 defer self.builder.positionBuilderAtEnd(prev_block);
852882
853 self.builder.positionBuilderAtEnd(then_block);883 self.builder.positionBuilderAtEnd(then_block);
854 try self.genBody(inst.then_body);884 try self.genBody(then_body);
855885
856 self.builder.positionBuilderAtEnd(else_block);886 self.builder.positionBuilderAtEnd(else_block);
857 try self.genBody(inst.else_body);887 try self.genBody(else_body);
858 }888 }
859 _ = self.builder.buildCondBr(condition_value, then_block, else_block);889 _ = self.builder.buildCondBr(cond, then_block, else_block);
860 return null;890 return null;
861 }891 }
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];
864 const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop");897 const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop");
865 _ = self.builder.buildBr(loop_block);898 _ = self.builder.buildBr(loop_block);
866899
867 self.builder.positionBuilderAtEnd(loop_block);900 self.builder.positionBuilderAtEnd(loop_block);
868 try self.genBody(inst.body);901 try self.genBody(body);
869902
870 _ = self.builder.buildBr(loop_block);903 _ = self.builder.buildBr(loop_block);
871 return null;904 return null;
872 }905 }
873906
874 fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {907 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
875 return self.builder.buildNot(try self.resolveInst(inst.operand), "");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, "");
876 }915 }
877916
878 fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value {917 fn airUnreach(self: *FuncGen, inst: Air.Inst.Index) ?*const llvm.Value {
879 _ = inst;918 _ = inst;
880 _ = self.builder.buildUnreachable();919 _ = self.builder.buildUnreachable();
881 return null;920 return null;
882 }921 }
883922
884 fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {923 fn airIsNonNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
885 const operand = try self.resolveInst(inst.operand);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
887 if (operand_is_ptr) {930 if (operand_is_ptr) {
888 const index_type = self.context().intType(32);931 const index_type = self.context().intType(32);
...@@ -898,12 +941,23 @@ pub const FuncGen = struct {...@@ -898,12 +941,23 @@ pub const FuncGen = struct {
898 }941 }
899 }942 }
900943
901 fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {944 fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
902 return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");945 if (self.liveness.isUnused(inst))
946 return null;
947
948 return self.builder.buildNot((try self.airIsNonNull(inst, operand_is_ptr)).?, "");
903 }949 }
904950
905 fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {951 fn airOptionalPayload(
906 const operand = try self.resolveInst(inst.operand);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
908 if (operand_is_ptr) {962 if (operand_is_ptr) {
909 const index_type = self.context().intType(32);963 const index_type = self.context().intType(32);
...@@ -919,61 +973,83 @@ pub const FuncGen = struct {...@@ -919,61 +973,83 @@ pub const FuncGen = struct {
919 }973 }
920 }974 }
921975
922 fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {976 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
923 const lhs = try self.resolveInst(inst.lhs);977 if (self.liveness.isUnused(inst))
924 const rhs = try self.resolveInst(inst.rhs);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())984 if (!inst_ty.isInt())
927 return self.todo("implement 'genAdd' for type {}", .{inst.base.ty});985 return self.todo("implement 'airAdd' for type {}", .{inst_ty});
928986
929 return if (inst.base.ty.isSignedInt())987 return if (inst_ty.isSignedInt())
930 self.builder.buildNSWAdd(lhs, rhs, "")988 self.builder.buildNSWAdd(lhs, rhs, "")
931 else989 else
932 self.builder.buildNUWAdd(lhs, rhs, "");990 self.builder.buildNUWAdd(lhs, rhs, "");
933 }991 }
934992
935 fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {993 fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
936 const lhs = try self.resolveInst(inst.lhs);994 if (self.liveness.isUnused(inst))
937 const rhs = try self.resolveInst(inst.rhs);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())1001 if (!inst_ty.isInt())
940 return self.todo("implement 'genSub' for type {}", .{inst.base.ty});1002 return self.todo("implement 'airSub' for type {}", .{inst_ty});
9411003
942 return if (inst.base.ty.isSignedInt())1004 return if (inst_ty.isSignedInt())
943 self.builder.buildNSWSub(lhs, rhs, "")1005 self.builder.buildNSWSub(lhs, rhs, "")
944 else1006 else
945 self.builder.buildNUWSub(lhs, rhs, "");1007 self.builder.buildNUWSub(lhs, rhs, "");
946 }1008 }
9471009
948 fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {1010 fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
949 const val = try self.resolveInst(inst.operand);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();
952 // TODO: Should we use intcast here or just a simple bitcast?1019 // TODO: Should we use intcast here or just a simple bitcast?
953 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes1020 // 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), "");
955 }1022 }
9561023
957 fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {1024 fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
958 const val = try self.resolveInst(inst.operand);1025 if (self.liveness.isUnused(inst))
959 const dest_type = try self.dg.getLLVMType(inst.base.ty);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, "");
962 }1034 }
9631035
964 fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value {1036 fn airArg(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
965 const arg_val = self.args[self.arg_index];1037 const arg_val = self.args[self.arg_index];
966 self.arg_index += 1;1038 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));
969 _ = self.builder.buildStore(arg_val, ptr_val);1042 _ = self.builder.buildStore(arg_val, ptr_val);
970 return self.builder.buildLoad(ptr_val, "");1043 return self.builder.buildLoad(ptr_val, "");
971 }1044 }
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;
974 // buildAlloca expects the pointee type, not the pointer type, so assert that1049 // buildAlloca expects the pointee type, not the pointer type, so assert that
975 // a Payload.PointerSimple is passed to the alloc instruction.1050 // 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
978 // TODO: figure out a way to get the name of the var decl.1054 // TODO: figure out a way to get the name of the var decl.
979 // TODO: set alignment and volatile1055 // TODO: set alignment and volatile
...@@ -1004,19 +1080,26 @@ pub const FuncGen = struct {...@@ -1004,19 +1080,26 @@ pub const FuncGen = struct {
1004 return val;1080 return val;
1005 }1081 }
10061082
1007 fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value {1083 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1008 const val = try self.resolveInst(inst.rhs);1084 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1009 const ptr = try self.resolveInst(inst.lhs);1085 const dest_ptr = try self.resolveInst(bin_op.lhs);
1010 _ = self.builder.buildStore(val, ptr);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);
1011 return null;1089 return null;
1012 }1090 }
10131091
1014 fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value {1092 fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1015 const ptr_val = try self.resolveInst(inst.operand);1093 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1016 return self.builder.buildLoad(ptr_val, "");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, "");
1017 }1100 }
10181101
1019 fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value {1102 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1020 _ = inst;1103 _ = inst;
1021 const llvn_fn = self.getIntrinsic("llvm.debugtrap");1104 const llvn_fn = self.getIntrinsic("llvm.debugtrap");
1022 _ = self.builder.buildCall(llvn_fn, null, 0, "");1105 _ = self.builder.buildCall(llvn_fn, null, 0, "");
src/codegen/spirv.zig+247-245
...@@ -12,21 +12,21 @@ const Decl = Module.Decl;...@@ -12,21 +12,21 @@ const Decl = Module.Decl;
12const Type = @import("../type.zig").Type;12const Type = @import("../type.zig").Type;
13const Value = @import("../value.zig").Value;13const Value = @import("../value.zig").Value;
14const LazySrcLoc = Module.LazySrcLoc;14const LazySrcLoc = Module.LazySrcLoc;
15const ir = @import("../air.zig");15const Air = @import("../Air.zig");
16const Inst = ir.Inst;16const Liveness = @import("../Liveness.zig");
1717
18pub const Word = u32;18pub const Word = u32;
19pub const ResultId = u32;19pub const ResultId = u32;
2020
21pub const TypeMap = std.HashMap(Type, u32, Type.HashContext64, std.hash_map.default_max_load_percentage);21pub 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
24const IncomingBlock = struct {24const IncomingBlock = struct {
25 src_label_id: ResultId,25 src_label_id: ResultId,
26 break_value_id: ResultId,26 break_value_id: ResultId,
27};27};
2828
29pub const BlockMap = std.AutoHashMap(*Inst.Block, struct {29pub const BlockMap = std.AutoHashMap(Air.Inst.Index, struct {
30 label_id: ResultId,30 label_id: ResultId,
31 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),31 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),
32});32});
...@@ -160,7 +160,11 @@ pub const DeclGen = struct {...@@ -160,7 +160,11 @@ pub const DeclGen = struct {
160 /// The SPIR-V module code should be put in.160 /// The SPIR-V module code should be put in.
161 spv: *SPIRVModule,161 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.
164 args: std.ArrayList(ResultId),168 args: std.ArrayList(ResultId),
165169
166 /// A counter to keep track of how many `arg` instructions we've seen yet.170 /// A counter to keep track of how many `arg` instructions we've seen yet.
...@@ -169,33 +173,35 @@ pub const DeclGen = struct {...@@ -169,33 +173,35 @@ pub const DeclGen = struct {
169 /// A map keeping track of which instruction generated which result-id.173 /// A map keeping track of which instruction generated which result-id.
170 inst_results: InstMap,174 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.
173 blocks: BlockMap,178 blocks: BlockMap,
174179
175 /// The label of the SPIR-V block we are currently generating.180 /// The label of the SPIR-V block we are currently generating.
176 current_block_label_id: ResultId,181 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't183 /// The actual instructions for this function. We need to declare all locals in
179 /// know which locals there are going to be, we're just going to generate everything after the locals-section in this array.184 /// the first block, and because we don't know which locals there are going to be,
180 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the initial OpLabel. These will be generated185 /// we're just going to generate everything after the locals-section in this array.
181 /// into spv.binary.fn_decls directly.186 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the
187 /// initial OpLabel. These will be generated into spv.binary.fn_decls directly.
182 code: std.ArrayList(Word),188 code: std.ArrayList(Word),
183189
184 /// The decl we are currently generating code for.190 /// The decl we are currently generating code for.
185 decl: *Decl,191 decl: *Decl,
186192
187 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message. Memory is owned by193 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message.
188 /// `module.gpa`.194 /// Memory is owned by `module.gpa`.
189 error_msg: ?*Module.ErrorMsg,195 error_msg: ?*Module.ErrorMsg,
190196
191 /// Possible errors the `gen` function may return.197 /// Possible errors the `gen` function may return.
192 const Error = error{ AnalysisFail, OutOfMemory };198 const Error = error{ AnalysisFail, OutOfMemory };
193199
194 /// This structure is used to return information about a type typically used for arithmetic operations.200 /// This structure is used to return information about a type typically used for
195 /// These types may either be integers, floats, or a vector of these. Most scalar operations also work on vectors,201 /// arithmetic operations. These types may either be integers, floats, or a vector
196 /// so we can easily represent those as arithmetic types.202 /// of these. Most scalar operations also work on vectors, so we can easily represent
197 /// If the type is a scalar, 'inner type' refers to the scalar type. Otherwise, if its a vector, it refers203 /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the
198 /// to the vector's element type.204 /// scalar type. Otherwise, if its a vector, it refers to the vector's element type.
199 const ArithmeticTypeInfo = struct {205 const ArithmeticTypeInfo = struct {
200 /// A classification of the inner type.206 /// A classification of the inner type.
201 const Class = enum {207 const Class = enum {
...@@ -207,13 +213,14 @@ pub const DeclGen = struct {...@@ -207,13 +213,14 @@ pub const DeclGen = struct {
207 /// the relevant capability is enabled).213 /// the relevant capability is enabled).
208 integer,214 integer,
209215
210 /// A regular float. These are all required to be natively supported. Floating points for216 /// A regular float. These are all required to be natively supported. Floating points
211 /// which the relevant capability is not enabled are not emulated.217 /// for which the relevant capability is not enabled are not emulated.
212 float,218 float,
213219
214 /// An integer of a 'strange' size (which' bit size is not the same as its backing type. **Note**: this220 /// An integer of a 'strange' size (which' bit size is not the same as its backing
215 /// may **also** include power-of-2 integers for which the relevant capability is not enabled), but still221 /// type. **Note**: this may **also** include power-of-2 integers for which the
216 /// within the limits of the largest natively supported integer type.222 /// relevant capability is not enabled), but still within the limits of the largest
223 /// natively supported integer type.
217 strange_integer,224 strange_integer,
218225
219 /// An integer with more bits than the largest natively supported integer type.226 /// An integer with more bits than the largest natively supported integer type.
...@@ -221,7 +228,7 @@ pub const DeclGen = struct {...@@ -221,7 +228,7 @@ pub const DeclGen = struct {
221 };228 };
222229
223 /// The number of bits in the inner type.230 /// 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.
225 bits: u16,232 bits: u16,
226233
227 /// Whether the type is a vector.234 /// Whether the type is a vector.
...@@ -235,10 +242,13 @@ pub const DeclGen = struct {...@@ -235,10 +242,13 @@ pub const DeclGen = struct {
235 class: Class,242 class: Class,
236 };243 };
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.
239 pub fn init(spv: *SPIRVModule) DeclGen {247 pub fn init(spv: *SPIRVModule) DeclGen {
240 return .{248 return .{
241 .spv = spv,249 .spv = spv,
250 .air = undefined,
251 .liveness = undefined,
242 .args = std.ArrayList(ResultId).init(spv.gpa),252 .args = std.ArrayList(ResultId).init(spv.gpa),
243 .next_arg_index = undefined,253 .next_arg_index = undefined,
244 .inst_results = InstMap.init(spv.gpa),254 .inst_results = InstMap.init(spv.gpa),
...@@ -251,10 +261,12 @@ pub const DeclGen = struct {...@@ -251,10 +261,12 @@ pub const DeclGen = struct {
251 }261 }
252262
253 /// Generate the code for `decl`. If a reportable error occured during code generation,263 /// 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 such264 /// a message is returned by this function. Callee owns the memory. If this function
255 /// a reportable error, it is valid to be called again for a different decl.265 /// returns such a reportable error, it is valid to be called again for a different decl.
256 pub fn gen(self: *DeclGen, decl: *Decl) !?*Module.ErrorMsg {266 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
257 // Reset internal resources, we don't want to re-allocate these.267 // Reset internal resources, we don't want to re-allocate these.
268 self.air = air;
269 self.liveness = liveness;
258 self.args.items.len = 0;270 self.args.items.len = 0;
259 self.next_arg_index = 0;271 self.next_arg_index = 0;
260 self.inst_results.clearRetainingCapacity();272 self.inst_results.clearRetainingCapacity();
...@@ -280,19 +292,20 @@ pub const DeclGen = struct {...@@ -280,19 +292,20 @@ pub const DeclGen = struct {
280 return self.spv.module.getTarget();292 return self.spv.module.getTarget();
281 }293 }
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 {
284 @setCold(true);296 @setCold(true);
297 const src: LazySrcLoc = .{ .node_offset = 0 };
285 const src_loc = src.toSrcLocWithDecl(self.decl);298 const src_loc = src.toSrcLocWithDecl(self.decl);
286 self.error_msg = try Module.ErrorMsg.create(self.spv.module.gpa, src_loc, format, args);299 self.error_msg = try Module.ErrorMsg.create(self.spv.module.gpa, src_loc, format, args);
287 return error.AnalysisFail;300 return error.AnalysisFail;
288 }301 }
289302
290 fn resolve(self: *DeclGen, inst: *Inst) !ResultId {303 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !ResultId {
291 if (inst.value()) |val| {304 if (self.air.value(inst)) |val| {
292 return self.genConstant(inst.src, inst.ty, val);305 return self.genConstant(self.air.typeOf(inst), val);
293 }306 }
294307 const index = Air.refToIndex(inst).?;
295 return self.inst_results.get(inst).?; // Instruction does not dominate all uses!308 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
296 }309 }
297310
298 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {311 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {
...@@ -314,7 +327,7 @@ pub const DeclGen = struct {...@@ -314,7 +327,7 @@ pub const DeclGen = struct {
314 const target = self.getTarget();327 const target = self.getTarget();
315328
316 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.329 // 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
319 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.332 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
320 // 32-bit integers are always supported (see spec, 2.16.1, Data rules).333 // 32-bit integers are always supported (see spec, 2.16.1, Data rules).
...@@ -388,19 +401,19 @@ pub const DeclGen = struct {...@@ -388,19 +401,19 @@ pub const DeclGen = struct {
388 .composite_integer };401 .composite_integer };
389 },402 },
390 // As of yet, there is no vector support in the self-hosted compiler.403 // 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", .{}),
392 // TODO: For which types is this the case?405 // 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}),
394 };407 };
395 }408 }
396409
397 /// Generate a constant representing `val`.410 /// Generate a constant representing `val`.
398 /// TODO: Deduplication?411 /// 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 {
400 const target = self.getTarget();413 const target = self.getTarget();
401 const code = &self.spv.binary.types_globals_constants;414 const code = &self.spv.binary.types_globals_constants;
402 const result_id = self.spv.allocResultId();415 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
405 if (val.isUndef()) {418 if (val.isUndef()) {
406 try writeInstruction(code, .OpUndef, &[_]Word{ result_type_id, result_id });419 try writeInstruction(code, .OpUndef, &[_]Word{ result_type_id, result_id });
...@@ -412,13 +425,13 @@ pub const DeclGen = struct {...@@ -412,13 +425,13 @@ pub const DeclGen = struct {
412 const int_info = ty.intInfo(target);425 const int_info = ty.intInfo(target);
413 const backing_bits = self.backingIntBits(int_info.bits) orelse {426 const backing_bits = self.backingIntBits(int_info.bits) orelse {
414 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.427 // 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});
416 };429 };
417430
418 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any431 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any
419 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this432 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this
420 // might need to be updated.433 // might need to be updated.
421 std.debug.assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64));434 assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64));
422 var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt();435 var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt();
423436
424 // Mask the low bits which make up the actual integer. This is to make sure that negative values437 // 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 {...@@ -470,13 +483,13 @@ pub const DeclGen = struct {
470 }483 }
471 },484 },
472 .Void => unreachable,485 .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}),
474 }487 }
475488
476 return result_id;489 return result_id;
477 }490 }
478491
479 fn genType(self: *DeclGen, src: LazySrcLoc, ty: Type) Error!ResultId {492 fn genType(self: *DeclGen, ty: Type) Error!ResultId {
480 // We can't use getOrPut here so we can recursively generate types.493 // We can't use getOrPut here so we can recursively generate types.
481 if (self.spv.types.get(ty)) |already_generated| {494 if (self.spv.types.get(ty)) |already_generated| {
482 return already_generated;495 return already_generated;
...@@ -493,7 +506,7 @@ pub const DeclGen = struct {...@@ -493,7 +506,7 @@ pub const DeclGen = struct {
493 const int_info = ty.intInfo(target);506 const int_info = ty.intInfo(target);
494 const backing_bits = self.backingIntBits(int_info.bits) orelse {507 const backing_bits = self.backingIntBits(int_info.bits) orelse {
495 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.508 // 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});
497 };510 };
498511
499 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.512 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.
...@@ -519,7 +532,7 @@ pub const DeclGen = struct {...@@ -519,7 +532,7 @@ pub const DeclGen = struct {
519 };532 };
520533
521 if (!supported) {534 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});
523 }536 }
524537
525 try writeInstruction(code, .OpTypeFloat, &[_]Word{ result_id, bits });538 try writeInstruction(code, .OpTypeFloat, &[_]Word{ result_id, bits });
...@@ -527,19 +540,19 @@ pub const DeclGen = struct {...@@ -527,19 +540,19 @@ pub const DeclGen = struct {
527 .Fn => {540 .Fn => {
528 // We only support zig-calling-convention functions, no varargs.541 // We only support zig-calling-convention functions, no varargs.
529 if (ty.fnCallingConvention() != .Unspecified)542 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", .{});
531 if (ty.fnIsVarArgs())544 if (ty.fnIsVarArgs())
532 return self.fail(src, "VarArgs unsupported for SPIR-V", .{});545 return self.fail("VarArgs unsupported for SPIR-V", .{});
533546
534 // In order to avoid a temporary here, first generate all the required types and then simply look them up547 // In order to avoid a temporary here, first generate all the required types and then simply look them up
535 // when generating the function type.548 // when generating the function type.
536 const params = ty.fnParamLen();549 const params = ty.fnParamLen();
537 var i: usize = 0;550 var i: usize = 0;
538 while (i < params) : (i += 1) {551 while (i < params) : (i += 1) {
539 _ = try self.genType(src, ty.fnParamType(i));552 _ = try self.genType(ty.fnParamType(i));
540 }553 }
541554
542 const return_type_id = try self.genType(src, ty.fnReturnType());555 const return_type_id = try self.genType(ty.fnReturnType());
543556
544 // result id + result type id + parameter type ids.557 // result id + result type id + parameter type ids.
545 try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u16, ty.fnParamLen()));558 try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u16, ty.fnParamLen()));
...@@ -552,7 +565,7 @@ pub const DeclGen = struct {...@@ -552,7 +565,7 @@ pub const DeclGen = struct {
552 }565 }
553 },566 },
554 // When recursively generating a type, we cannot infer the pointer's storage class. See genPointerType.567 // 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", .{}),
556 .Vector => {569 .Vector => {
557 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations570 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
558 // which work on them), so simply use those.571 // which work on them), so simply use those.
...@@ -562,7 +575,7 @@ pub const DeclGen = struct {...@@ -562,7 +575,7 @@ pub const DeclGen = struct {
562 // is adequate at all for this.575 // is adequate at all for this.
563576
564 // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems.577 // 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", .{});
566 },579 },
567 .Null,580 .Null,
568 .Undefined,581 .Undefined,
...@@ -574,7 +587,7 @@ pub const DeclGen = struct {...@@ -574,7 +587,7 @@ pub const DeclGen = struct {
574587
575 .BoundFn => unreachable, // this type will be deleted from the language.588 .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}),
578 }591 }
579592
580 try self.spv.types.putNoClobber(ty, result_id);593 try self.spv.types.putNoClobber(ty, result_id);
...@@ -583,8 +596,8 @@ pub const DeclGen = struct {...@@ -583,8 +596,8 @@ pub const DeclGen = struct {
583596
584 /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that.597 /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that.
585 /// TODO: The result of this needs to be cached.598 /// TODO: The result of this needs to be cached.
586 fn genPointerType(self: *DeclGen, src: LazySrcLoc, ty: Type, storage_class: spec.StorageClass) !ResultId {599 fn genPointerType(self: *DeclGen, ty: Type, storage_class: spec.StorageClass) !ResultId {
587 std.debug.assert(ty.zigTypeTag() == .Pointer);600 assert(ty.zigTypeTag() == .Pointer);
588601
589 const code = &self.spv.binary.types_globals_constants;602 const code = &self.spv.binary.types_globals_constants;
590 const result_id = self.spv.allocResultId();603 const result_id = self.spv.allocResultId();
...@@ -592,7 +605,7 @@ pub const DeclGen = struct {...@@ -592,7 +605,7 @@ pub const DeclGen = struct {
592 // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types605 // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types
593 // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled.606 // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled.
594 // These also relates to the pointer's address space.607 // 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
597 try writeInstruction(code, .OpTypePointer, &[_]Word{ result_id, @enumToInt(storage_class), child_id });610 try writeInstruction(code, .OpTypePointer, &[_]Word{ result_id, @enumToInt(storage_class), child_id });
598611
...@@ -603,9 +616,9 @@ pub const DeclGen = struct {...@@ -603,9 +616,9 @@ pub const DeclGen = struct {
603 const decl = self.decl;616 const decl = self.decl;
604 const result_id = decl.fn_link.spirv.id;617 const result_id = decl.fn_link.spirv.id;
605618
606 if (decl.val.castTag(.function)) |func_payload| {619 if (decl.val.castTag(.function)) |_| {
607 std.debug.assert(decl.ty.zigTypeTag() == .Fn);620 assert(decl.ty.zigTypeTag() == .Fn);
608 const prototype_id = try self.genType(.{ .node_offset = 0 }, decl.ty);621 const prototype_id = try self.genType(decl.ty);
609 try writeInstruction(&self.spv.binary.fn_decls, .OpFunction, &[_]Word{622 try writeInstruction(&self.spv.binary.fn_decls, .OpFunction, &[_]Word{
610 self.spv.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.623 self.spv.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.
611 result_id,624 result_id,
...@@ -632,189 +645,171 @@ pub const DeclGen = struct {...@@ -632,189 +645,171 @@ pub const DeclGen = struct {
632 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{root_block_id});645 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{root_block_id});
633 self.current_block_label_id = root_block_id;646 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
637 // Append the actual code into the fn_decls section.651 // Append the actual code into the fn_decls section.
638 try self.spv.binary.fn_decls.appendSlice(self.code.items);652 try self.spv.binary.fn_decls.appendSlice(self.code.items);
639 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});653 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});
640 } else {654 } 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()});
642 }656 }
643 }657 }
644658
645 fn genBody(self: *DeclGen, body: ir.Body) Error!void {659 fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void {
646 for (body.instructions) |inst| {660 for (body) |inst| {
647 try self.genInst(inst);661 try self.genInst(inst);
648 }662 }
649 }663 }
650664
651 fn genInst(self: *DeclGen, inst: *Inst) !void {665 fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
652 const result_id = switch (inst.tag) {666 const air_tags = self.air.instructions.items(.tag);
653 .add, .addwrap => try self.genBinOp(inst.castTag(.add).?),667 const result_id = switch (air_tags[inst]) {
654 .sub, .subwrap => try self.genBinOp(inst.castTag(.sub).?),668 // zig fmt: off
655 .mul, .mulwrap => try self.genBinOp(inst.castTag(.mul).?),669 .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}),
656 .div => try self.genBinOp(inst.castTag(.div).?),670 .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}),
657 .bit_and => try self.genBinOp(inst.castTag(.bit_and).?),671 .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}),
658 .bit_or => try self.genBinOp(inst.castTag(.bit_or).?),672 .div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}),
659 .xor => try self.genBinOp(inst.castTag(.xor).?),673
660 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?),674 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),
661 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?),675 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),
662 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?),676 .xor => try self.airBinOpSimple(inst, .OpBitwiseXor),
663 .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?),677 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
664 .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?),678 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),
665 .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?),679
666 .bool_and => try self.genBinOp(inst.castTag(.bool_and).?),680 .not => try self.airNot(inst),
667 .bool_or => try self.genBinOp(inst.castTag(.bool_or).?),681
668 .not => try self.genUnOp(inst.castTag(.not).?),682 .cmp_eq => try self.airCmp(inst, .{.OpFOrdEqual, .OpLogicalEqual, .OpIEqual}),
669 .alloc => try self.genAlloc(inst.castTag(.alloc).?),683 .cmp_neq => try self.airCmp(inst, .{.OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual}),
670 .arg => self.genArg(),684 .cmp_gt => try self.airCmp(inst, .{.OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan}),
671 .block => (try self.genBlock(inst.castTag(.block).?)) orelse return,685 .cmp_gte => try self.airCmp(inst, .{.OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual}),
672 .br => return try self.genBr(inst.castTag(.br).?),686 .cmp_lt => try self.airCmp(inst, .{.OpFOrdLessThan, .OpSLessThan, .OpULessThan}),
673 .br_void => return try self.genBrVoid(inst.castTag(.br_void).?),687 .cmp_lte => try self.airCmp(inst, .{.OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual}),
674 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them688
675 // throughout the IR.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),
676 .breakpoint => return,695 .breakpoint => return,
677 .condbr => return try self.genCondBr(inst.castTag(.condbr).?),696 .cond_br => return self.airCondBr(inst),
678 .constant => unreachable,697 .constant => unreachable,
679 .dbg_stmt => return try self.genDbgStmt(inst.castTag(.dbg_stmt).?),698 .dbg_stmt => return self.airDbgStmt(inst),
680 .load => try self.genLoad(inst.castTag(.load).?),699 .loop => return self.airLoop(inst),
681 .loop => return try self.genLoop(inst.castTag(.loop).?),700 .ret => return self.airRet(inst),
682 .ret => return try self.genRet(inst.castTag(.ret).?),701 .store => return self.airStore(inst),
683 .retvoid => return try self.genRetVoid(),702 .unreach => return self.airUnreach(),
684 .store => return try self.genStore(inst.castTag(.store).?),703 // zig fmt: on
685 .unreach => return try self.genUnreach(),704
686 else => return self.fail(inst.src, "TODO: SPIR-V backend: implement inst {s}", .{@tagName(inst.tag)}),705 else => |tag| return self.fail("TODO: SPIR-V backend: implement AIR tag {s}", .{
706 @tagName(tag),
707 }),
687 };708 };
688709
689 try self.inst_results.putNoClobber(inst, result_id);710 try self.inst_results.putNoClobber(inst, result_id);
690 }711 }
691712
692 fn genBinOp(self: *DeclGen, inst: *Inst.BinOp) !ResultId {713 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, opcode: Opcode) !ResultId {
693 // TODO: Will lhs and rhs have the same type?714 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
694 const lhs_id = try self.resolve(inst.lhs);715 const lhs_id = try self.resolve(bin_op.lhs);
695 const rhs_id = try self.resolve(inst.rhs);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
697 const result_id = self.spv.allocResultId();733 const result_id = self.spv.allocResultId();
698 const result_type_id = try self.genType(inst.base.src, inst.base.ty);734 const result_type_id = try self.genType(ty);
699735
700 // TODO: Is the result the same as the argument types?736 assert(self.air.typeOf(bin_op.lhs).eql(ty));
701 // This is supposed to be the case for SPIR-V.737 assert(self.air.typeOf(bin_op.rhs).eql(ty));
702 std.debug.assert(inst.rhs.ty.eql(inst.lhs.ty));738
703 std.debug.assert(inst.base.ty.tag() == .bool or inst.base.ty.eql(inst.lhs.ty));739 // Binary operations are generally applicable to both scalar and vector operations
704740 // in SPIR-V, but int and float versions of operations require different opcodes.
705 // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float741 const info = try self.arithmeticTypeInfo(ty);
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 }
716742
717 const is_float = info.class == .float;743 const opcode_index: usize = switch (info.class) {
718 const is_signed = info.signedness == .signed;744 .composite_integer => {
719 // **Note**: All these operations must be valid for vectors as well!745 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});
720 const opcode = switch (inst.base.tag) {746 },
721 // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,747 .strange_integer => {
722 // we can just switch on both cases here.748 return self.fail("TODO: SPIR-V backend: binary operations for strange integers", .{});
723 .add, .addwrap => if (is_float) Opcode.OpFAdd else Opcode.OpIAdd,749 },
724 .sub, .subwrap => if (is_float) Opcode.OpFSub else Opcode.OpISub,750 .integer => switch (info.signedness) {
725 .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul,751 .signed => @as(usize, 1),
726 // TODO: Trap if divisor is 0?752 .unsigned => @as(usize, 2),
727 // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful.753 },
728 // => Those are probably for divTrunc and divFloor, though the compiler does not yet generate those.754 .float => 0,
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,
739 else => unreachable,755 else => unreachable,
740 };756 };
741757 const opcode = ops[opcode_index];
742 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });758 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
743759
744 // TODO: Trap on overflow? Probably going to be annoying.760 // TODO: Trap on overflow? Probably going to be annoying.
745 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.761 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
746762
747 if (info.class != .strange_integer)763 return result_id;
748 return result_id;
749
750 return self.fail(inst.base.src, "TODO: SPIR-V backend: strange integer operation mask", .{});
751 }764 }
752765
753 fn genCmp(self: *DeclGen, inst: *Inst.BinOp) !ResultId {766 fn airCmp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId {
754 const lhs_id = try self.resolve(inst.lhs);767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
755 const rhs_id = try self.resolve(inst.rhs);768 const lhs_id = try self.resolve(bin_op.lhs);
756769 const rhs_id = try self.resolve(bin_op.rhs);
757 const result_id = self.spv.allocResultId();770 const result_id = self.spv.allocResultId();
758 const result_type_id = try self.genType(inst.base.src, inst.base.ty);771 const result_type_id = try self.genType(Type.initTag(.bool));
759772 const op_ty = self.air.typeOf(bin_op.lhs);
760 // All of these operations should be 2 equal types -> bool773 assert(op_ty.eql(self.air.typeOf(bin_op.rhs)));
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 }
775774
776 const is_bool = info.class == .bool;775 // Comparisons are generally applicable to both scalar and vector operations in SPIR-V,
777 const is_float = info.class == .float;776 // but int and float versions of operations require different opcodes.
778 const is_signed = info.signedness == .signed;777 const info = try self.arithmeticTypeInfo(op_ty);
779778
780 // **Note**: All these operations must be valid for vectors as well!779 const opcode_index: usize = switch (info.class) {
781 // For floating points, we generally want ordered operations (which return false if either operand is nan).780 .composite_integer => {
782 const opcode = switch (inst.base.tag) {781 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});
783 .cmp_eq => if (is_float) Opcode.OpFOrdEqual else if (is_bool) Opcode.OpLogicalEqual else Opcode.OpIEqual,782 },
784 .cmp_neq => if (is_float) Opcode.OpFOrdNotEqual else if (is_bool) Opcode.OpLogicalNotEqual else Opcode.OpINotEqual,783 .strange_integer => {
785 // TODO: Verify that these OpFOrd type operations produce the right value.784 return self.fail("TODO: SPIR-V backend: comparison for strange integers", .{});
786 // TODO: Is there a more fundamental difference between OpU and OpS operations here than just the type?785 },
787 .cmp_gt => if (is_float) Opcode.OpFOrdGreaterThan else if (is_signed) Opcode.OpSGreaterThan else Opcode.OpUGreaterThan,786 .float => 0,
788 .cmp_gte => if (is_float) Opcode.OpFOrdGreaterThanEqual else if (is_signed) Opcode.OpSGreaterThanEqual else Opcode.OpUGreaterThanEqual,787 .bool => 1,
789 .cmp_lt => if (is_float) Opcode.OpFOrdLessThan else if (is_signed) Opcode.OpSLessThan else Opcode.OpULessThan,788 .integer => switch (info.signedness) {
790 .cmp_lte => if (is_float) Opcode.OpFOrdLessThanEqual else if (is_signed) Opcode.OpSLessThanEqual else Opcode.OpULessThanEqual,789 .signed => @as(usize, 1),
791 else => unreachable,790 .unsigned => @as(usize, 2),
791 },
792 };792 };
793 const opcode = ops[opcode_index];
793794
794 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });795 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
795 return result_id;796 return result_id;
796 }797 }
797798
798 fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !ResultId {799 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
799 const operand_id = try self.resolve(inst.operand);800 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
800801 const operand_id = try self.resolve(ty_op.operand);
801 const result_id = self.spv.allocResultId();802 const result_id = self.spv.allocResultId();
802 const result_type_id = try self.genType(inst.base.src, inst.base.ty);803 const result_type_id = try self.genType(Type.initTag(.bool));
803804 const opcode: Opcode = .OpLogicalNot;
804 const opcode = switch (inst.base.tag) {
805 // Bool -> bool
806 .not => Opcode.OpLogicalNot,
807 else => unreachable,
808 };
809
810 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, operand_id });805 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, operand_id });
811
812 return result_id;806 return result_id;
813 }807 }
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);
816 const storage_class = spec.StorageClass.Function;811 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);
818 const result_id = self.spv.allocResultId();813 const result_id = self.spv.allocResultId();
819814
820 // Rather than generating into code here, we're just going to generate directly into the fn_decls section so that815 // 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 {...@@ -824,12 +819,12 @@ pub const DeclGen = struct {
824 return result_id;819 return result_id;
825 }820 }
826821
827 fn genArg(self: *DeclGen) ResultId {822 fn airArg(self: *DeclGen) ResultId {
828 defer self.next_arg_index += 1;823 defer self.next_arg_index += 1;
829 return self.args.items[self.next_arg_index];824 return self.args.items[self.next_arg_index];
830 }825 }
831826
832 fn genBlock(self: *DeclGen, inst: *Inst.Block) !?ResultId {827 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?ResultId {
833 // 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 and828 // 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
834 // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up829 // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up
835 // the current block by first generating the code of the block, then a label, and then generate the rest of the current830 // 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 {...@@ -849,11 +844,16 @@ pub const DeclGen = struct {
849 incoming_blocks.deinit(self.spv.gpa);844 incoming_blocks.deinit(self.spv.gpa);
850 }845 }
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);
853 try self.beginSPIRVBlock(label_id);853 try self.beginSPIRVBlock(label_id);
854854
855 // If this block didn't produce a value, simply return here.855 // If this block didn't produce a value, simply return here.
856 if (!inst.base.ty.hasCodeGenBits())856 if (!ty.hasCodeGenBits())
857 return null;857 return null;
858858
859 // Combine the result from the blocks using the Phi instruction.859 // Combine the result from the blocks using the Phi instruction.
...@@ -863,7 +863,7 @@ pub const DeclGen = struct {...@@ -863,7 +863,7 @@ pub const DeclGen = struct {
863 // TODO: OpPhi is limited in the types that it may produce, such as pointers. Figure out which other types863 // TODO: OpPhi is limited in the types that it may produce, such as pointers. Figure out which other types
864 // are not allowed to be created from a phi node, and throw an error for those. For now, genType already throws864 // are not allowed to be created from a phi node, and throw an error for those. For now, genType already throws
865 // an error for pointers.865 // 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);
867 _ = result_type_id;867 _ = result_type_id;
868868
869 try writeOpcode(&self.code, .OpPhi, 2 + @intCast(u16, incoming_blocks.items.len * 2)); // result type + result + variable/parent...869 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 {...@@ -875,30 +875,26 @@ pub const DeclGen = struct {
875 return result_id;875 return result_id;
876 }876 }
877877
878 fn genBr(self: *DeclGen, inst: *Inst.Br) !void {878 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
879 // TODO: This instruction needs to be the last in a block. Is that guaranteed?879 const br = self.air.instructions.items(.data)[inst].br;
880 const target = self.blocks.get(inst.block).?;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 (operand_ty.hasCodeGenBits()) {
883 if (inst.operand.ty.hasCodeGenBits()) {884 const operand_id = try self.resolve(br.operand);
884 const operand_id = try self.resolve(inst.operand);
885 // current_block_label_id should not be undefined here, lest there is a br or br_void in the function's body.885 // 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 });
887 }887 }
888888
889 try writeInstruction(&self.code, .OpBranch, &[_]Word{target.label_id});889 try writeInstruction(&self.code, .OpBranch, &[_]Word{block.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});
897 }890 }
898891
899 fn genCondBr(self: *DeclGen, inst: *Inst.CondBr) !void {892 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {
900 // TODO: This instruction needs to be the last in a block. Is that guaranteed?893 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
901 const condition_id = try self.resolve(inst.condition);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
903 // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block.899 // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block.
904 const then_label_id = self.spv.allocResultId();900 const then_label_id = self.spv.allocResultId();
...@@ -914,23 +910,26 @@ pub const DeclGen = struct {...@@ -914,23 +910,26 @@ pub const DeclGen = struct {
914 });910 });
915911
916 try self.beginSPIRVBlock(then_label_id);912 try self.beginSPIRVBlock(then_label_id);
917 try self.genBody(inst.then_body);913 try self.genBody(then_body);
918 try self.beginSPIRVBlock(else_label_id);914 try self.beginSPIRVBlock(else_label_id);
919 try self.genBody(inst.else_body);915 try self.genBody(else_body);
920 }916 }
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;
923 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);920 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 });
925 }922 }
926923
927 fn genLoad(self: *DeclGen, inst: *Inst.UnOp) !ResultId {924 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
928 const operand_id = try self.resolve(inst.operand);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);
931 const result_id = self.spv.allocResultId();930 const result_id = self.spv.allocResultId();
932931
933 const operands = if (inst.base.ty.isVolatilePtr())932 const operands = if (ty.isVolatilePtr())
934 &[_]Word{ result_type_id, result_id, operand_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }933 &[_]Word{ result_type_id, result_id, operand_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }
935 else934 else
936 &[_]Word{ result_type_id, result_id, operand_id };935 &[_]Word{ result_type_id, result_id, operand_id };
...@@ -940,8 +939,10 @@ pub const DeclGen = struct {...@@ -940,8 +939,10 @@ pub const DeclGen = struct {
940 return result_id;939 return result_id;
941 }940 }
942941
943 fn genLoop(self: *DeclGen, inst: *Inst.Loop) !void {942 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {
944 // TODO: This instruction needs to be the last in a block. Is that guaranteed?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];
945 const loop_label_id = self.spv.allocResultId();946 const loop_label_id = self.spv.allocResultId();
946947
947 // Jump to the loop entry point948 // Jump to the loop entry point
...@@ -950,27 +951,29 @@ pub const DeclGen = struct {...@@ -950,27 +951,29 @@ pub const DeclGen = struct {
950 // TODO: Look into OpLoopMerge.951 // TODO: Look into OpLoopMerge.
951952
952 try self.beginSPIRVBlock(loop_label_id);953 try self.beginSPIRVBlock(loop_label_id);
953 try self.genBody(inst.body);954 try self.genBody(body);
954955
955 try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id});956 try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id});
956 }957 }
957958
958 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !void {959 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
959 const operand_id = try self.resolve(inst.operand);960 const operand = self.air.instructions.items(.data)[inst].un_op;
960 // TODO: This instruction needs to be the last in a block. Is that guaranteed?961 const operand_ty = self.air.typeOf(operand);
961 try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id});962 if (operand_ty.hasCodeGenBits()) {
962 }963 const operand_id = try self.resolve(operand);
963964 try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id});
964 fn genRetVoid(self: *DeclGen) !void {965 } else {
965 // TODO: This instruction needs to be the last in a block. Is that guaranteed?966 try writeInstruction(&self.code, .OpReturn, &[_]Word{});
966 try writeInstruction(&self.code, .OpReturn, &[_]Word{});967 }
967 }968 }
968969
969 fn genStore(self: *DeclGen, inst: *Inst.BinOp) !void {970 fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void {
970 const dst_ptr_id = try self.resolve(inst.lhs);971 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
971 const src_val_id = try self.resolve(inst.rhs);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())
974 &[_]Word{ dst_ptr_id, src_val_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }977 &[_]Word{ dst_ptr_id, src_val_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }
975 else978 else
976 &[_]Word{ dst_ptr_id, src_val_id };979 &[_]Word{ dst_ptr_id, src_val_id };
...@@ -978,8 +981,7 @@ pub const DeclGen = struct {...@@ -978,8 +981,7 @@ pub const DeclGen = struct {
978 try writeInstruction(&self.code, .OpStore, operands);981 try writeInstruction(&self.code, .OpStore, operands);
979 }982 }
980983
981 fn genUnreach(self: *DeclGen) !void {984 fn airUnreach(self: *DeclGen) !void {
982 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
983 try writeInstruction(&self.code, .OpUnreachable, &[_]Word{});985 try writeInstruction(&self.code, .OpUnreachable, &[_]Word{});
984 }986 }
985};987};
src/codegen/wasm.zig+293-242
...@@ -9,14 +9,14 @@ const wasm = std.wasm;...@@ -9,14 +9,14 @@ const wasm = std.wasm;
99
10const Module = @import("../Module.zig");10const Module = @import("../Module.zig");
11const Decl = Module.Decl;11const Decl = Module.Decl;
12const ir = @import("../air.zig");
13const Inst = ir.Inst;
14const Type = @import("../type.zig").Type;12const Type = @import("../type.zig").Type;
15const Value = @import("../value.zig").Value;13const Value = @import("../value.zig").Value;
16const Compilation = @import("../Compilation.zig");14const Compilation = @import("../Compilation.zig");
17const LazySrcLoc = Module.LazySrcLoc;15const LazySrcLoc = Module.LazySrcLoc;
18const link = @import("../link.zig");16const link = @import("../link.zig");
19const TypedValue = @import("../TypedValue.zig");17const TypedValue = @import("../TypedValue.zig");
18const Air = @import("../Air.zig");
19const Liveness = @import("../Liveness.zig");
2020
21/// Wasm Value, created when generating an instruction21/// Wasm Value, created when generating an instruction
22const WValue = union(enum) {22const WValue = union(enum) {
...@@ -24,8 +24,8 @@ const WValue = union(enum) {...@@ -24,8 +24,8 @@ const WValue = union(enum) {
24 none: void,24 none: void,
25 /// Index of the local variable25 /// Index of the local variable
26 local: u32,26 local: u32,
27 /// Instruction holding a constant `Value`27 /// Holds a memoized typed value
28 constant: *Inst,28 constant: TypedValue,
29 /// Offset position in the list of bytecode instructions29 /// Offset position in the list of bytecode instructions
30 code_offset: usize,30 code_offset: usize,
31 /// Used for variables that create multiple locals on the stack when allocated31 /// Used for variables that create multiple locals on the stack when allocated
...@@ -483,8 +483,8 @@ pub const Result = union(enum) {...@@ -483,8 +483,8 @@ pub const Result = union(enum) {
483 externally_managed: []const u8,483 externally_managed: []const u8,
484};484};
485485
486/// Hashmap to store generated `WValue` for each `Inst`486/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
487pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue);487pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
488488
489/// Code represents the `Code` section of wasm that489/// Code represents the `Code` section of wasm that
490/// belongs to a function490/// belongs to a function
...@@ -492,11 +492,13 @@ pub const Context = struct {...@@ -492,11 +492,13 @@ pub const Context = struct {
492 /// Reference to the function declaration the code492 /// Reference to the function declaration the code
493 /// section belongs to493 /// section belongs to
494 decl: *Decl,494 decl: *Decl,
495 air: Air,
496 liveness: Liveness,
495 gpa: *mem.Allocator,497 gpa: *mem.Allocator,
496 /// Table to save `WValue`'s generated by an `Inst`498 /// Table to save `WValue`'s generated by an `Air.Inst`
497 values: ValueTable,499 values: ValueTable,
498 /// Mapping from *Inst.Block to block ids500 /// Mapping from Air.Inst.Index to block ids
499 blocks: std.AutoArrayHashMapUnmanaged(*Inst.Block, u32) = .{},501 blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
500 /// `bytes` contains the wasm bytecode belonging to the 'code' section.502 /// `bytes` contains the wasm bytecode belonging to the 'code' section.
501 code: ArrayList(u8),503 code: ArrayList(u8),
502 /// Contains the generated function type bytecode for the current function504 /// Contains the generated function type bytecode for the current function
...@@ -536,7 +538,8 @@ pub const Context = struct {...@@ -536,7 +538,8 @@ pub const Context = struct {
536 }538 }
537539
538 /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig540 /// 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 };
540 const src_loc = src.toSrcLocWithDecl(self.decl);543 const src_loc = src.toSrcLocWithDecl(self.decl);
541 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);544 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
542 return error.CodegenFail;545 return error.CodegenFail;
...@@ -544,59 +547,66 @@ pub const Context = struct {...@@ -544,59 +547,66 @@ pub const Context = struct {
544547
545 /// Resolves the `WValue` for the given instruction `inst`548 /// Resolves the `WValue` for the given instruction `inst`
546 /// When the given instruction has a `Value`, it returns a constant instead549 /// When the given instruction has a `Value`, it returns a constant instead
547 fn resolveInst(self: Context, inst: *Inst) WValue {550 fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
548 if (!inst.ty.hasCodeGenBits()) return .none;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()) |_| {562 if (self.air.instructions.items(.tag)[inst_index] == .constant) {
551 return WValue{ .constant = inst };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] } };
552 }565 }
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!
555 }568 }
556569
557 /// Using a given `Type`, returns the corresponding wasm Valtype570 /// 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 {
559 return switch (ty.zigTypeTag()) {572 return switch (ty.zigTypeTag()) {
560 .Float => blk: {573 .Float => blk: {
561 const bits = ty.floatBits(self.target);574 const bits = ty.floatBits(self.target);
562 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;575 if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32;
563 if (bits == 64) break :blk wasm.Valtype.f64;576 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});
565 },578 },
566 .Int => blk: {579 .Int => blk: {
567 const info = ty.intInfo(self.target);580 const info = ty.intInfo(self.target);
568 if (info.bits <= 32) break :blk wasm.Valtype.i32;581 if (info.bits <= 32) break :blk wasm.Valtype.i32;
569 if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64;582 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});
571 },584 },
572 .Enum => switch (ty.tag()) {585 .Enum => switch (ty.tag()) {
573 .enum_simple => wasm.Valtype.i32,586 .enum_simple => wasm.Valtype.i32,
574 else => self.typeToValtype(587 else => self.typeToValtype(ty.cast(Type.Payload.EnumFull).?.data.tag_ty),
575 src,
576 ty.cast(Type.Payload.EnumFull).?.data.tag_ty,
577 ),
578 },588 },
579 .Bool,589 .Bool,
580 .Pointer,590 .Pointer,
581 .ErrorSet,591 .ErrorSet,
582 => wasm.Valtype.i32,592 => wasm.Valtype.i32,
583 .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually.593 .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()}),
585 };595 };
586 }596 }
587597
588 /// Using a given `Type`, returns the byte representation of its wasm value type598 /// Using a given `Type`, returns the byte representation of its wasm value type
589 fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {599 fn genValtype(self: *Context, ty: Type) InnerError!u8 {
590 return wasm.valtype(try self.typeToValtype(src, ty));600 return wasm.valtype(try self.typeToValtype(ty));
591 }601 }
592602
593 /// Using a given `Type`, returns the corresponding wasm value type603 /// Using a given `Type`, returns the corresponding wasm value type
594 /// Differently from `genValtype` this also allows `void` to create a block604 /// Differently from `genValtype` this also allows `void` to create a block
595 /// with no return type605 /// with no return type
596 fn genBlockType(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {606 fn genBlockType(self: *Context, ty: Type) InnerError!u8 {
597 return switch (ty.tag()) {607 return switch (ty.tag()) {
598 .void, .noreturn => wasm.block_empty,608 .void, .noreturn => wasm.block_empty,
599 else => self.genValtype(src, ty),609 else => self.genValtype(ty),
600 };610 };
601 }611 }
602612
...@@ -610,7 +620,7 @@ pub const Context = struct {...@@ -610,7 +620,7 @@ pub const Context = struct {
610 try writer.writeByte(wasm.opcode(.local_get));620 try writer.writeByte(wasm.opcode(.local_get));
611 try leb.writeULEB128(writer, idx);621 try leb.writeULEB128(writer, idx);
612 },622 },
613 .constant => |inst| try self.emitConstant(inst.src, inst.value().?, inst.ty), // creates a new constant onto the stack623 .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack
614 }624 }
615 }625 }
616626
...@@ -626,10 +636,7 @@ pub const Context = struct {...@@ -626,10 +636,7 @@ pub const Context = struct {
626 const fields_len = @intCast(u32, struct_data.fields.count());636 const fields_len = @intCast(u32, struct_data.fields.count());
627 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);637 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
628 for (struct_data.fields.values()) |*value| {638 for (struct_data.fields.values()) |*value| {
629 const val_type = try self.genValtype(639 const val_type = try self.genValtype(value.ty);
630 .{ .node_offset = struct_data.node_offset },
631 value.ty,
632 );
633 self.locals.appendAssumeCapacity(val_type);640 self.locals.appendAssumeCapacity(val_type);
634 self.local_index += 1;641 self.local_index += 1;
635 }642 }
...@@ -640,7 +647,7 @@ pub const Context = struct {...@@ -640,7 +647,7 @@ pub const Context = struct {
640 },647 },
641 .ErrorUnion => {648 .ErrorUnion => {
642 const payload_type = ty.errorUnionChild();649 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
645 // we emit the error value as the first local, and the payload as the following.652 // we emit the error value as the first local, and the payload as the following.
646 // The first local is also used to find the index of the error and payload.653 // The first local is also used to find the index of the error and payload.
...@@ -657,7 +664,7 @@ pub const Context = struct {...@@ -657,7 +664,7 @@ pub const Context = struct {
657 } };664 } };
658 },665 },
659 else => {666 else => {
660 const valtype = try self.genValtype(.{ .node_offset = 0 }, ty);667 const valtype = try self.genValtype(ty);
661 try self.locals.append(self.gpa, valtype);668 try self.locals.append(self.gpa, valtype);
662 self.local_index += 1;669 self.local_index += 1;
663 return WValue{ .local = initial_index };670 return WValue{ .local = initial_index };
...@@ -680,7 +687,7 @@ pub const Context = struct {...@@ -680,7 +687,7 @@ pub const Context = struct {
680 ty.fnParamTypes(params);687 ty.fnParamTypes(params);
681 for (params) |param_type| {688 for (params) |param_type| {
682 // Can we maybe get the source index of each param?689 // 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);
684 try writer.writeByte(val_type);691 try writer.writeByte(val_type);
685 }692 }
686 }693 }
...@@ -689,13 +696,10 @@ pub const Context = struct {...@@ -689,13 +696,10 @@ pub const Context = struct {
689 const return_type = ty.fnReturnType();696 const return_type = ty.fnReturnType();
690 switch (return_type.zigTypeTag()) {697 switch (return_type.zigTypeTag()) {
691 .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)),698 .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", .{}),699 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
693 .Optional => return self.fail(.{ .node_offset = 0 }, "TODO: Implement optionals as return type for wasm", .{}),700 .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
694 .ErrorUnion => {701 .ErrorUnion => {
695 const val_type = try self.genValtype(702 const val_type = try self.genValtype(return_type.errorUnionChild());
696 .{ .node_offset = 0 },
697 return_type.errorUnionChild(),
698 );
699703
700 // write down the amount of return values704 // write down the amount of return values
701 try leb.writeULEB128(writer, @as(u32, 2));705 try leb.writeULEB128(writer, @as(u32, 2));
...@@ -705,58 +709,57 @@ pub const Context = struct {...@@ -705,58 +709,57 @@ pub const Context = struct {
705 else => {709 else => {
706 try leb.writeULEB128(writer, @as(u32, 1));710 try leb.writeULEB128(writer, @as(u32, 1));
707 // Can we maybe get the source index of the return type?711 // 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);
709 try writer.writeByte(val_type);713 try writer.writeByte(val_type);
710 },714 },
711 }715 }
712 }716 }
713717
714 /// Generates the wasm bytecode for the function declaration belonging to `Context`718 pub fn genFunc(self: *Context) InnerError!Result {
715 pub fn gen(self: *Context, typed_value: TypedValue) InnerError!Result {719 try self.genFunctype();
716 switch (typed_value.ty.zigTypeTag()) {720 // TODO: check for and handle death of instructions
717 .Fn => {
718 try self.genFunctype();
719721
720 // Write instructions722 // Reserve space to write the size after generating the code as well as space for locals count
721 // TODO: check for and handle death of instructions723 try self.code.resize(10);
722 const mod_fn = blk: {724
723 if (typed_value.val.castTag(.function)) |func| break :blk func.data;725 try self.genBody(self.air.getMainBody());
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 }
749726
750 const writer = self.code.writer();727 // finally, write our local types at the 'offset' position
751 try writer.writeByte(wasm.opcode(.end));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 the731 // offset into 'code' section where we will put our locals types
754 // beginning of the buffer.732 var local_offset: usize = 10;
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));
757733
758 // codegen data has been appended to `code`734 // emit the actual locals amount
759 return Result.appended;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", .{});
760 },763 },
761 .Array => {764 .Array => {
762 if (typed_value.val.castTag(.bytes)) |payload| {765 if (typed_value.val.castTag(.bytes)) |payload| {
...@@ -775,7 +778,7 @@ pub const Context = struct {...@@ -775,7 +778,7 @@ pub const Context = struct {
775 }778 }
776 }779 }
777 return Result{ .externally_managed = payload.data };780 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", .{});
779 },782 },
780 .Int => {783 .Int => {
781 const info = typed_value.ty.intInfo(self.target);784 const info = typed_value.ty.intInfo(self.target);
...@@ -784,85 +787,91 @@ pub const Context = struct {...@@ -784,85 +787,91 @@ pub const Context = struct {
784 try self.code.append(@intCast(u8, int_byte));787 try self.code.append(@intCast(u8, int_byte));
785 return Result.appended;788 return Result.appended;
786 }789 }
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});
788 },791 },
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}),
790 }793 }
791 }794 }
792795
793 fn genInst(self: *Context, inst: *Inst) InnerError!WValue {796 fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
794 return switch (inst.tag) {797 const air_tags = self.air.instructions.items(.tag);
795 .add => self.genBinOp(inst.castTag(.add).?, .add),798 return switch (air_tags[inst]) {
796 .alloc => self.genAlloc(inst.castTag(.alloc).?),799 .add => self.airBinOp(inst, .add),
797 .arg => self.genArg(inst.castTag(.arg).?),800 .sub => self.airBinOp(inst, .sub),
798 .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),801 .mul => self.airBinOp(inst, .mul),
799 .bitcast => self.genBitcast(inst.castTag(.bitcast).?),802 .div => self.airBinOp(inst, .div),
800 .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),803 .bit_and => self.airBinOp(inst, .@"and"),
801 .block => self.genBlock(inst.castTag(.block).?),804 .bit_or => self.airBinOp(inst, .@"or"),
802 .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),805 .bool_and => self.airBinOp(inst, .@"and"),
803 .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),806 .bool_or => self.airBinOp(inst, .@"or"),
804 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),807 .xor => self.airBinOp(inst, .xor),
805 .br => self.genBr(inst.castTag(.br).?),808
806 .call => self.genCall(inst.castTag(.call).?),809 .cmp_eq => self.airCmp(inst, .eq),
807 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),810 .cmp_gte => self.airCmp(inst, .gte),
808 .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),811 .cmp_gt => self.airCmp(inst, .gt),
809 .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),812 .cmp_lte => self.airCmp(inst, .lte),
810 .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),813 .cmp_lt => self.airCmp(inst, .lt),
811 .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),814 .cmp_neq => self.airCmp(inst, .neq),
812 .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),815
813 .condbr => self.genCondBr(inst.castTag(.condbr).?),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),
814 .constant => unreachable,824 .constant => unreachable,
815 .dbg_stmt => WValue.none,825 .dbg_stmt => WValue.none,
816 .div => self.genBinOp(inst.castTag(.div).?, .div),826 .is_err => self.airIsErr(inst, .i32_ne),
817 .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),827 .is_non_err => self.airIsErr(inst, .i32_eq),
818 .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),828 .load => self.airLoad(inst),
819 .load => self.genLoad(inst.castTag(.load).?),829 .loop => self.airLoop(inst),
820 .loop => self.genLoop(inst.castTag(.loop).?),830 .not => self.airNot(inst),
821 .mul => self.genBinOp(inst.castTag(.mul).?, .mul),831 .ret => self.airRet(inst),
822 .not => self.genNot(inst.castTag(.not).?),832 .store => self.airStore(inst),
823 .ret => self.genRet(inst.castTag(.ret).?),833 .struct_field_ptr => self.airStructFieldPtr(inst),
824 .retvoid => WValue.none,834 .switch_br => self.airSwitchBr(inst),
825 .store => self.genStore(inst.castTag(.store).?),835 .unreach => self.airUnreachable(inst),
826 .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),836 .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst),
827 .sub => self.genBinOp(inst.castTag(.sub).?, .sub),837 .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
828 .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?),838 else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
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}),
834 };839 };
835 }840 }
836841
837 fn genBody(self: *Context, body: ir.Body) InnerError!void {842 fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
838 for (body.instructions) |inst| {843 for (body) |inst| {
839 const result = try self.genInst(inst);844 const result = try self.genInst(inst);
840 try self.values.putNoClobber(self.gpa, inst, result);845 try self.values.putNoClobber(self.gpa, inst, result);
841 }846 }
842 }847 }
843848
844 fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue {849 fn airRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
845 // TODO: Implement tail calls850 const un_op = self.air.instructions.items(.data)[inst].un_op;
846 const operand = self.resolveInst(inst.operand);851 const operand = self.resolveInst(un_op);
847 try self.emitWValue(operand);852 try self.emitWValue(operand);
848 try self.code.append(wasm.opcode(.@"return"));853 try self.code.append(wasm.opcode(.@"return"));
849 return .none;854 return .none;
850 }855 }
851856
852 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {857 fn airCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
853 const func_val = inst.func.value().?;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
855 const target: *Decl = blk: {862 const target: *Decl = blk: {
863 const func_val = self.air.value(pl_op.operand).?;
864
856 if (func_val.castTag(.function)) |func| {865 if (func_val.castTag(.function)) |func| {
857 break :blk func.data.owner_decl;866 break :blk func.data.owner_decl;
858 } else if (func_val.castTag(.extern_fn)) |ext_fn| {867 } else if (func_val.castTag(.extern_fn)) |ext_fn| {
859 break :blk ext_fn.data;868 break :blk ext_fn.data;
860 }869 }
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()});
862 };871 };
863872
864 for (inst.args) |arg| {873 for (args) |arg| {
865 const arg_val = self.resolveInst(arg);874 const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
866 try self.emitWValue(arg_val);875 try self.emitWValue(arg_val);
867 }876 }
868877
...@@ -878,16 +887,17 @@ pub const Context = struct {...@@ -878,16 +887,17 @@ pub const Context = struct {
878 return .none;887 return .none;
879 }888 }
880889
881 fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {890 fn airAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
882 const elem_type = inst.base.ty.elemType();891 const elem_type = self.air.typeOfIndex(inst).elemType();
883 return self.allocLocal(elem_type);892 return self.allocLocal(elem_type);
884 }893 }
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;
887 const writer = self.code.writer();897 const writer = self.code.writer();
888898
889 const lhs = self.resolveInst(inst.lhs);899 const lhs = self.resolveInst(bin_op.lhs);
890 const rhs = self.resolveInst(inst.rhs);900 const rhs = self.resolveInst(bin_op.rhs);
891901
892 switch (lhs) {902 switch (lhs) {
893 .multi_value => |multi_value| switch (rhs) {903 .multi_value => |multi_value| switch (rhs) {
...@@ -895,7 +905,7 @@ pub const Context = struct {...@@ -895,7 +905,7 @@ pub const Context = struct {
895 // we simply assign the local_index to the rhs one.905 // we simply assign the local_index to the rhs one.
896 // This allows us to update struct fields without having to individually906 // This allows us to update struct fields without having to individually
897 // set each local as each field's index will be calculated off the struct's base index907 // 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!
899 .constant, .none => {909 .constant, .none => {
900 // emit all values onto the stack if constant910 // emit all values onto the stack if constant
901 try self.emitWValue(rhs);911 try self.emitWValue(rhs);
...@@ -921,20 +931,22 @@ pub const Context = struct {...@@ -921,20 +931,22 @@ pub const Context = struct {
921 return .none;931 return .none;
922 }932 }
923933
924 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {934 fn airLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
925 return self.resolveInst(inst.operand);935 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
936 return self.resolveInst(ty_op.operand);
926 }937 }
927938
928 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {939 fn airArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
929 _ = inst;940 _ = inst;
930 // arguments share the index with locals941 // arguments share the index with locals
931 defer self.local_index += 1;942 defer self.local_index += 1;
932 return WValue{ .local = self.local_index };943 return WValue{ .local = self.local_index };
933 }944 }
934945
935 fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue {946 fn airBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
936 const lhs = self.resolveInst(inst.lhs);947 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
937 const rhs = self.resolveInst(inst.rhs);948 const lhs = self.resolveInst(bin_op.lhs);
949 const rhs = self.resolveInst(bin_op.rhs);
938950
939 // it's possible for both lhs and/or rhs to return an offset as well,951 // it's possible for both lhs and/or rhs to return an offset as well,
940 // in which case we return the first offset occurance we find.952 // in which case we return the first offset occurance we find.
...@@ -947,23 +959,24 @@ pub const Context = struct {...@@ -947,23 +959,24 @@ pub const Context = struct {
947 try self.emitWValue(lhs);959 try self.emitWValue(lhs);
948 try self.emitWValue(rhs);960 try self.emitWValue(rhs);
949961
962 const bin_ty = self.air.typeOf(bin_op.lhs);
950 const opcode: wasm.Opcode = buildOpcode(.{963 const opcode: wasm.Opcode = buildOpcode(.{
951 .op = op,964 .op = op,
952 .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),965 .valtype1 = try self.typeToValtype(bin_ty),
953 .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,966 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
954 });967 });
955 try self.code.append(wasm.opcode(opcode));968 try self.code.append(wasm.opcode(opcode));
956 return WValue{ .code_offset = offset };969 return WValue{ .code_offset = offset };
957 }970 }
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 {
960 const writer = self.code.writer();973 const writer = self.code.writer();
961 switch (ty.zigTypeTag()) {974 switch (ty.zigTypeTag()) {
962 .Int => {975 .Int => {
963 // write opcode976 // write opcode
964 const opcode: wasm.Opcode = buildOpcode(.{977 const opcode: wasm.Opcode = buildOpcode(.{
965 .op = .@"const",978 .op = .@"const",
966 .valtype1 = try self.typeToValtype(src, ty),979 .valtype1 = try self.typeToValtype(ty),
967 });980 });
968 try writer.writeByte(wasm.opcode(opcode));981 try writer.writeByte(wasm.opcode(opcode));
969 // write constant982 // write constant
...@@ -982,14 +995,14 @@ pub const Context = struct {...@@ -982,14 +995,14 @@ pub const Context = struct {
982 // write opcode995 // write opcode
983 const opcode: wasm.Opcode = buildOpcode(.{996 const opcode: wasm.Opcode = buildOpcode(.{
984 .op = .@"const",997 .op = .@"const",
985 .valtype1 = try self.typeToValtype(src, ty),998 .valtype1 = try self.typeToValtype(ty),
986 });999 });
987 try writer.writeByte(wasm.opcode(opcode));1000 try writer.writeByte(wasm.opcode(opcode));
988 // write constant1001 // write constant
989 switch (ty.floatBits(self.target)) {1002 switch (ty.floatBits(self.target)) {
990 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),1003 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),
991 64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))),1004 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}),
993 }1006 }
994 },1007 },
995 .Pointer => {1008 .Pointer => {
...@@ -1006,7 +1019,7 @@ pub const Context = struct {...@@ -1006,7 +1019,7 @@ pub const Context = struct {
1006 try writer.writeByte(wasm.opcode(.i32_load));1019 try writer.writeByte(wasm.opcode(.i32_load));
1007 try leb.writeULEB128(writer, @as(u32, 0));1020 try leb.writeULEB128(writer, @as(u32, 0));
1008 try leb.writeULEB128(writer, @as(u32, 0));1021 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()});
1010 },1023 },
1011 .Void => {},1024 .Void => {},
1012 .Enum => {1025 .Enum => {
...@@ -1020,7 +1033,7 @@ pub const Context = struct {...@@ -1020,7 +1033,7 @@ pub const Context = struct {
1020 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;1033 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
1021 if (enum_full.values.count() != 0) {1034 if (enum_full.values.count() != 0) {
1022 const tag_val = enum_full.values.keys()[field_index.data];1035 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);
1024 } else {1037 } else {
1025 try writer.writeByte(wasm.opcode(.i32_const));1038 try writer.writeByte(wasm.opcode(.i32_const));
1026 try leb.writeULEB128(writer, field_index.data);1039 try leb.writeULEB128(writer, field_index.data);
...@@ -1031,7 +1044,7 @@ pub const Context = struct {...@@ -1031,7 +1044,7 @@ pub const Context = struct {
1031 } else {1044 } else {
1032 var int_tag_buffer: Type.Payload.Bits = undefined;1045 var int_tag_buffer: Type.Payload.Bits = undefined;
1033 const int_tag_ty = ty.intTagType(&int_tag_buffer);1046 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);
1035 }1048 }
1036 },1049 },
1037 .ErrorSet => {1050 .ErrorSet => {
...@@ -1045,12 +1058,12 @@ pub const Context = struct {...@@ -1045,12 +1058,12 @@ pub const Context = struct {
1045 const payload_type = ty.errorUnionChild();1058 const payload_type = ty.errorUnionChild();
1046 if (value.getError()) |_| {1059 if (value.getError()) |_| {
1047 // write the error value1060 // write the error value
1048 try self.emitConstant(src, data, error_type);1061 try self.emitConstant(data, error_type);
10491062
1050 // no payload, so write a '0' const1063 // no payload, so write a '0' const
1051 const opcode: wasm.Opcode = buildOpcode(.{1064 const opcode: wasm.Opcode = buildOpcode(.{
1052 .op = .@"const",1065 .op = .@"const",
1053 .valtype1 = try self.typeToValtype(src, payload_type),1066 .valtype1 = try self.typeToValtype(payload_type),
1054 });1067 });
1055 try writer.writeByte(wasm.opcode(opcode));1068 try writer.writeByte(wasm.opcode(opcode));
1056 try leb.writeULEB128(writer, @as(u32, 0));1069 try leb.writeULEB128(writer, @as(u32, 0));
...@@ -1059,21 +1072,24 @@ pub const Context = struct {...@@ -1059,21 +1072,24 @@ pub const Context = struct {
1059 try writer.writeByte(wasm.opcode(.i32_const));1072 try writer.writeByte(wasm.opcode(.i32_const));
1060 try leb.writeULEB128(writer, @as(u32, 0));1073 try leb.writeULEB128(writer, @as(u32, 0));
1061 // after the error code, we emit the payload1074 // after the error code, we emit the payload
1062 try self.emitConstant(src, data, payload_type);1075 try self.emitConstant(data, payload_type);
1063 }1076 }
1064 },1077 },
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}),
1066 }1079 }
1067 }1080 }
10681081
1069 fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {1082 fn airBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1070 const block_ty = try self.genBlockType(block.base.src, block.base.ty);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
1072 try self.startBlock(.block, block_ty, null);1088 try self.startBlock(.block, block_ty, null);
1073 // Here we set the current block idx, so breaks know the depth to jump1089 // Here we set the current block idx, so breaks know the depth to jump
1074 // to when breaking out.1090 // to when breaking out.
1075 try self.blocks.putNoClobber(self.gpa, block, self.block_depth);1091 try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
1076 try self.genBody(block.body);1092 try self.genBody(body);
1077 try self.endBlock();1093 try self.endBlock();
10781094
1079 return .none;1095 return .none;
...@@ -1097,11 +1113,15 @@ pub const Context = struct {...@@ -1097,11 +1113,15 @@ pub const Context = struct {
1097 self.block_depth -= 1;1113 self.block_depth -= 1;
1098 }1114 }
10991115
1100 fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {1116 fn airLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1101 const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);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);1121 // result type of loop is always 'noreturn', meaning we can always
1104 try self.genBody(loop.body);1122 // emit the wasm type 'block_empty'.
1123 try self.startBlock(.loop, wasm.block_empty, null);
1124 try self.genBody(body);
11051125
1106 // breaking to the index of a loop block will continue the loop instead1126 // breaking to the index of a loop block will continue the loop instead
1107 try self.code.append(wasm.opcode(.br));1127 try self.code.append(wasm.opcode(.br));
...@@ -1112,8 +1132,12 @@ pub const Context = struct {...@@ -1112,8 +1132,12 @@ pub const Context = struct {
1112 return .none;1132 return .none;
1113 }1133 }
11141134
1115 fn genCondBr(self: *Context, condbr: *Inst.CondBr) InnerError!WValue {1135 fn airCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1116 const condition = self.resolveInst(condbr.condition);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];
1117 const writer = self.code.writer();1141 const writer = self.code.writer();
11181142
1119 // TODO: Handle death instructions for then and else body1143 // TODO: Handle death instructions for then and else body
...@@ -1128,8 +1152,9 @@ pub const Context = struct {...@@ -1128,8 +1152,9 @@ pub const Context = struct {
1128 break :blk offset;1152 break :blk offset;
1129 },1153 },
1130 };1154 };
1131 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);1155
1132 try self.startBlock(.block, block_ty, offset);1156 // result type is always noreturn, so use `block_empty` as type.
1157 try self.startBlock(.block, wasm.block_empty, offset);
11331158
1134 // we inserted the block in front of the condition1159 // we inserted the block in front of the condition
1135 // so now check if condition matches. If not, break outside this block1160 // so now check if condition matches. If not, break outside this block
...@@ -1137,35 +1162,37 @@ pub const Context = struct {...@@ -1137,35 +1162,37 @@ pub const Context = struct {
1137 try writer.writeByte(wasm.opcode(.br_if));1162 try writer.writeByte(wasm.opcode(.br_if));
1138 try leb.writeULEB128(writer, @as(u32, 0));1163 try leb.writeULEB128(writer, @as(u32, 0));
11391164
1140 try self.genBody(condbr.else_body);1165 try self.genBody(else_body);
1141 try self.endBlock();1166 try self.endBlock();
11421167
1143 // Outer block that matches the condition1168 // Outer block that matches the condition
1144 try self.genBody(condbr.then_body);1169 try self.genBody(then_body);
11451170
1146 return .none;1171 return .none;
1147 }1172 }
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 {
1150 // save offset, so potential conditions can insert blocks in front of1175 // save offset, so potential conditions can insert blocks in front of
1151 // the comparison that we can later jump back to1176 // the comparison that we can later jump back to
1152 const offset = self.code.items.len;1177 const offset = self.code.items.len;
11531178
1154 const lhs = self.resolveInst(inst.lhs);1179 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
1155 const rhs = self.resolveInst(inst.rhs);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
1157 try self.emitWValue(lhs);1184 try self.emitWValue(lhs);
1158 try self.emitWValue(rhs);1185 try self.emitWValue(rhs);
11591186
1160 const signedness: std.builtin.Signedness = blk: {1187 const signedness: std.builtin.Signedness = blk: {
1161 // by default we tell the operand type is unsigned (i.e. bools and enum values)1188 // 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
1164 // incase of an actual integer, we emit the correct signedness1191 // 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;
1166 };1193 };
1167 const opcode: wasm.Opcode = buildOpcode(.{1194 const opcode: wasm.Opcode = buildOpcode(.{
1168 .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),1195 .valtype1 = try self.typeToValtype(lhs_ty),
1169 .op = switch (op) {1196 .op = switch (op) {
1170 .lt => .lt,1197 .lt => .lt,
1171 .lte => .le,1198 .lte => .le,
...@@ -1180,16 +1207,17 @@ pub const Context = struct {...@@ -1180,16 +1207,17 @@ pub const Context = struct {
1180 return WValue{ .code_offset = offset };1207 return WValue{ .code_offset = offset };
1181 }1208 }
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
1184 // if operand has codegen bits we should break with a value1213 // if operand has codegen bits we should break with a value
1185 if (br.operand.ty.hasCodeGenBits()) {1214 if (self.air.typeOf(br.operand).hasCodeGenBits()) {
1186 const operand = self.resolveInst(br.operand);1215 try self.emitWValue(self.resolveInst(br.operand));
1187 try self.emitWValue(operand);
1188 }1216 }
11891217
1190 // We map every block to its block index.1218 // We map every block to its block index.
1191 // We then determine how far we have to jump to it by substracting it from current block depth1219 // 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).?;
1193 const writer = self.code.writer();1221 const writer = self.code.writer();
1194 try writer.writeByte(wasm.opcode(.br));1222 try writer.writeByte(wasm.opcode(.br));
1195 try leb.writeULEB128(writer, idx);1223 try leb.writeULEB128(writer, idx);
...@@ -1197,10 +1225,11 @@ pub const Context = struct {...@@ -1197,10 +1225,11 @@ pub const Context = struct {
1197 return .none;1225 return .none;
1198 }1226 }
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;
1201 const offset = self.code.items.len;1230 const offset = self.code.items.len;
12021231
1203 const operand = self.resolveInst(not.operand);1232 const operand = self.resolveInst(ty_op.operand);
1204 try self.emitWValue(operand);1233 try self.emitWValue(operand);
12051234
1206 // wasm does not have booleans nor the `not` instruction, therefore compare with 01235 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
...@@ -1214,73 +1243,93 @@ pub const Context = struct {...@@ -1214,73 +1243,93 @@ pub const Context = struct {
1214 return WValue{ .code_offset = offset };1243 return WValue{ .code_offset = offset };
1215 }1244 }
12161245
1217 fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {1246 fn airBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1218 _ = self;1247 _ = self;
1219 _ = breakpoint;1248 _ = inst;
1220 // unsupported by wasm itself. Can be implemented once we support DWARF1249 // unsupported by wasm itself. Can be implemented once we support DWARF
1221 // for wasm1250 // for wasm
1222 return .none;1251 return .none;
1223 }1252 }
12241253
1225 fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {1254 fn airUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1226 _ = unreach;1255 _ = inst;
1227 try self.code.append(wasm.opcode(.@"unreachable"));1256 try self.code.append(wasm.opcode(.@"unreachable"));
1228 return .none;1257 return .none;
1229 }1258 }
12301259
1231 fn genBitcast(self: *Context, bitcast: *Inst.UnOp) InnerError!WValue {1260 fn airBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1232 return self.resolveInst(bitcast.operand);1261 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1262 return self.resolveInst(ty_op.operand);
1233 }1263 }
12341264
1235 fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue {1265 fn airStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1236 const struct_ptr = self.resolveInst(inst.struct_ptr);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) };
1239 }1271 }
12401272
1241 fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue {1273 fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1242 const target = self.resolveInst(inst.target);1274 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1243 const target_ty = inst.target.ty;1275 const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
1244 const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty);1276 const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
1245 const blocktype = try self.genBlockType(inst.base.src, inst.base.ty);1277 const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
12461278
1247 const signedness: std.builtin.Signedness = blk: {1279 const target = self.resolveInst(pl_op.operand);
1248 // by default we tell the operand type is unsigned (i.e. bools and enum values)1280 const target_ty = self.air.typeOf(pl_op.operand);
1249 if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;1281 const valtype = try self.typeToValtype(target_ty);
12501282 // result type is always 'noreturn'
1251 // incase of an actual integer, we emit the correct signedness1283 const blocktype = wasm.block_empty;
1252 break :blk target_ty.intInfo(self.target).signedness;1284
1253 };1285 _ = valtype;
1254 for (inst.cases) |case| {1286 _ = blocktype;
1255 // create a block for each case, when the condition does not match we break out of it1287 _ = target;
1256 try self.startBlock(.block, blocktype, null);1288 _ = else_body;
1257 try self.emitWValue(target);1289 return self.fail("TODO implement wasm codegen for switch", .{});
1258 try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty);1290 //const signedness: std.builtin.Signedness = blk: {
1259 const opcode = buildOpcode(.{1291 // // by default we tell the operand type is unsigned (i.e. bools and enum values)
1260 .valtype1 = valtype,1292 // if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
1261 .op = .ne, // not equal because we jump out the block if it does not match the condition1293
1262 .signedness = signedness,1294 // // incase of an actual integer, we emit the correct signedness
1263 });1295 // break :blk target_ty.intInfo(self.target).signedness;
1264 try self.code.append(wasm.opcode(opcode));1296 //};
1265 try self.code.append(wasm.opcode(.br_if));1297 //for (cases) |case_idx| {
1266 try leb.writeULEB128(self.code.writer(), @as(u32, 0));1298 // const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
12671299 // const case_body = self.air.extra[case.end..][0..case.data.body_len];
1268 // emit our block code1300
1269 try self.genBody(case.body);1301 // // create a block for each case, when the condition does not match we break out of it
12701302 // try self.startBlock(.block, blocktype, null);
1271 // end the block we created earlier1303 // try self.emitWValue(target);
1272 try self.endBlock();1304
1273 }1305 // const val = self.air.value(case.data.item).?;
12741306 // try self.emitConstant(val, target_ty);
1275 // finally, emit the else case if it exists. Here we will not have to1307 // const opcode = buildOpcode(.{
1276 // check for a condition, so also no need to emit a block.1308 // .valtype1 = valtype,
1277 try self.genBody(inst.else_body);1309 // .op = .ne, // not equal because we jump out the block if it does not match the condition
12781310 // .signedness = signedness,
1279 return .none;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;
1280 }1328 }
12811329
1282 fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {1330 fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
1283 const operand = self.resolveInst(inst.operand);1331 const un_op = self.air.instructions.items(.data)[inst].un_op;
1332 const operand = self.resolveInst(un_op);
1284 const offset = self.code.items.len;1333 const offset = self.code.items.len;
1285 const writer = self.code.writer();1334 const writer = self.code.writer();
12861335
...@@ -1295,8 +1344,9 @@ pub const Context = struct {...@@ -1295,8 +1344,9 @@ pub const Context = struct {
1295 return WValue{ .code_offset = offset };1344 return WValue{ .code_offset = offset };
1296 }1345 }
12971346
1298 fn genUnwrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue {1347 fn airUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1299 const operand = self.resolveInst(inst.operand);1348 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1349 const operand = self.resolveInst(ty_op.operand);
1300 // The index of multi_value contains the error code. To get the initial index of the payload we get1350 // The index of multi_value contains the error code. To get the initial index of the payload we get
1301 // the following index. Next, convert it to a `WValue.local`1351 // the following index. Next, convert it to a `WValue.local`
1302 //1352 //
...@@ -1304,7 +1354,8 @@ pub const Context = struct {...@@ -1304,7 +1354,8 @@ pub const Context = struct {
1304 return WValue{ .local = operand.multi_value.index + 1 };1354 return WValue{ .local = operand.multi_value.index + 1 };
1305 }1355 }
13061356
1307 fn genWrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue {1357 fn airWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1308 return self.resolveInst(inst.operand);1358 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1359 return self.resolveInst(ty_op.operand);
1309 }1360 }
1310};1361};
src/link.zig+29-5
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const mem = std.mem;3const mem = std.mem;
3const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
4const fs = std.fs;5const fs = std.fs;
...@@ -14,8 +15,10 @@ const Cache = @import("Cache.zig");...@@ -14,8 +15,10 @@ const Cache = @import("Cache.zig");
14const build_options = @import("build_options");15const build_options = @import("build_options");
15const LibCInstallation = @import("libc_installation.zig").LibCInstallation;16const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
16const wasi_libc = @import("wasi_libc.zig");17const 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
20pub const Emit = struct {23pub const Emit = struct {
21 /// Where the output will go.24 /// Where the output will go.
...@@ -313,13 +316,34 @@ pub const File = struct {...@@ -313,13 +316,34 @@ pub const File = struct {
313 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty });316 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty });
314 assert(decl.has_tv);317 assert(decl.has_tv);
315 switch (base.tag) {318 switch (base.tag) {
316 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),319 // zig fmt: off
317 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),320 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
321 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
318 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),322 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
319 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),323 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
320 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),324 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
321 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),325 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
322 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl),326 .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
323 }347 }
324 }348 }
325349
src/link/C.zig+22-6
...@@ -2,14 +2,17 @@ const std = @import("std");...@@ -2,14 +2,17 @@ const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const fs = std.fs;
6
7const C = @This();
5const Module = @import("../Module.zig");8const Module = @import("../Module.zig");
6const Compilation = @import("../Compilation.zig");9const Compilation = @import("../Compilation.zig");
7const fs = std.fs;
8const codegen = @import("../codegen/c.zig");10const codegen = @import("../codegen/c.zig");
9const link = @import("../link.zig");11const link = @import("../link.zig");
10const trace = @import("../tracy.zig").trace;12const trace = @import("../tracy.zig").trace;
11const C = @This();
12const Type = @import("../type.zig").Type;13const Type = @import("../type.zig").Type;
14const Air = @import("../Air.zig");
15const Liveness = @import("../Liveness.zig");
1316
14pub const base_tag: link.File.Tag = .c;17pub const base_tag: link.File.Tag = .c;
15pub const zig_h = @embedFile("C/zig.h");18pub const zig_h = @embedFile("C/zig.h");
...@@ -95,10 +98,7 @@ fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void {...@@ -95,10 +98,7 @@ fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void {
95 decl.fn_link.c.typedefs.deinit(gpa);98 decl.fn_link.c.typedefs.deinit(gpa);
96}99}
97100
98pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {101pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, liveness: Liveness) !void {
99 const tracy = trace(@src());
100 defer tracy.end();
101
102 // Keep track of all decls so we can iterate over them on flush().102 // Keep track of all decls so we can iterate over them on flush().
103 _ = try self.decl_table.getOrPut(self.base.allocator, decl);103 _ = 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 {...@@ -126,6 +126,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
126 .code = code.toManaged(module.gpa),126 .code = code.toManaged(module.gpa),
127 .value_map = codegen.CValueMap.init(module.gpa),127 .value_map = codegen.CValueMap.init(module.gpa),
128 .indent_writer = undefined, // set later so we can get a pointer to object.code128 .indent_writer = undefined, // set later so we can get a pointer to object.code
129 .air = air,
130 .liveness = liveness,
129 };131 };
130 object.indent_writer = .{ .underlying_writer = object.code.writer() };132 object.indent_writer = .{ .underlying_writer = object.code.writer() };
131 defer {133 defer {
...@@ -157,6 +159,20 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -157,6 +159,20 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
157 code.shrinkAndFree(module.gpa, code.items.len);159 code.shrinkAndFree(module.gpa, code.items.len);
158}160}
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
160pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {176pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
161 // The C backend does not have the ability to fix line numbers without re-generating177 // The C backend does not have the ability to fix line numbers without re-generating
162 // the entire Decl.178 // the entire Decl.
src/link/Coff.zig+56-5
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Coff = @This();1const Coff = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
4const log = std.log.scoped(.link);5const log = std.log.scoped(.link);
5const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;7const assert = std.debug.assert;
...@@ -17,6 +18,8 @@ const build_options = @import("build_options");...@@ -17,6 +18,8 @@ const build_options = @import("build_options");
17const Cache = @import("../Cache.zig");18const Cache = @import("../Cache.zig");
18const mingw = @import("../mingw.zig");19const mingw = @import("../mingw.zig");
19const llvm_backend = @import("../codegen/llvm.zig");20const llvm_backend = @import("../codegen/llvm.zig");
21const Air = @import("../Air.zig");
22const Liveness = @import("../Liveness.zig");
2023
21const allocation_padding = 4 / 3;24const allocation_padding = 4 / 3;
22const minimum_text_block_size = 64 * allocation_padding;25const minimum_text_block_size = 64 * allocation_padding;
...@@ -653,19 +656,63 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {...@@ -653,19 +656,63 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
653 }656 }
654}657}
655658
656pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {659pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
657 // TODO COFF/PE debug information660 if (build_options.skip_non_native and
658 // TODO Implement exports661 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 }
659 const tracy = trace(@src());671 const tracy = trace(@src());
660 defer tracy.end();672 defer tracy.end();
661673
662 if (build_options.have_llvm)674 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
663 if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);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
665 if (decl.val.tag() == .extern_fn) {709 if (decl.val.tag() == .extern_fn) {
666 return; // TODO Should we do more when front-end analyzed extern decl?710 return; // TODO Should we do more when front-end analyzed extern decl?
667 }711 }
668712
713 // TODO COFF/PE debug information
714 // TODO Implement exports
715
669 var code_buffer = std.ArrayList(u8).init(self.base.allocator);716 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
670 defer code_buffer.deinit();717 defer code_buffer.deinit();
671718
...@@ -683,6 +730,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {...@@ -683,6 +730,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
683 },730 },
684 };731 };
685732
733 return self.finishUpdateDecl(module, decl, code);
734}
735
736fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []const u8) !void {
686 const required_alignment = decl.ty.abiAlignment(self.base.options.target);737 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
687 const curr_size = decl.link.coff.size;738 const curr_size = decl.link.coff.size;
688 if (curr_size != 0) {739 if (curr_size != 0) {
src/link/Elf.zig+310-252
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Elf = @This();1const Elf = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
4const mem = std.mem;5const mem = std.mem;
5const assert = std.debug.assert;6const assert = std.debug.assert;
6const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
...@@ -10,7 +11,6 @@ const log = std.log.scoped(.link);...@@ -10,7 +11,6 @@ const log = std.log.scoped(.link);
10const DW = std.dwarf;11const DW = std.dwarf;
11const leb128 = std.leb;12const leb128 = std.leb;
1213
13const ir = @import("../air.zig");
14const Module = @import("../Module.zig");14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");15const Compilation = @import("../Compilation.zig");
16const codegen = @import("../codegen.zig");16const codegen = @import("../codegen.zig");
...@@ -26,6 +26,8 @@ const glibc = @import("../glibc.zig");...@@ -26,6 +26,8 @@ const glibc = @import("../glibc.zig");
26const musl = @import("../musl.zig");26const musl = @import("../musl.zig");
27const Cache = @import("../Cache.zig");27const Cache = @import("../Cache.zig");
28const llvm_backend = @import("../codegen/llvm.zig");28const llvm_backend = @import("../codegen/llvm.zig");
29const Air = @import("../Air.zig");
30const Liveness = @import("../Liveness.zig");
2931
30const default_entry_addr = 0x8000000;32const default_entry_addr = 0x8000000;
3133
...@@ -2155,138 +2157,17 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {...@@ -2155,138 +2157,17 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
2155 }2157 }
2156}2158}
21572159
2158pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {2160fn deinitRelocs(gpa: *Allocator, table: *File.DbgInfoTypeRelocsTable) void {
2159 const tracy = trace(@src());2161 var it = table.valueIterator();
2160 defer tracy.end();2162 while (it.next()) |value| {
21612163 value.relocs.deinit(gpa);
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
2264 }2164 }
2265 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;2165 table.deinit(gpa);
2266 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{2166}
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 };
22852167
2168fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
2286 const required_alignment = decl.ty.abiAlignment(self.base.options.target);2169 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
2290 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()2171 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
2291 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];2172 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
2292 if (local_sym.st_size != 0) {2173 if (local_sym.st_size != 0) {
...@@ -2338,128 +2219,16 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -2338,128 +2219,16 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2338 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;2219 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
2339 try self.base.file.?.pwriteAll(code, file_offset);2220 try self.base.file.?.pwriteAll(code, file_offset);
23402221
2341 const target_endian = self.base.options.target.cpu.arch.endian();2222 return local_sym;
23422223}
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 }
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 {
2463 // Now we emit the .debug_info types of the Decl. These will count towards the size of2232 // Now we emit the .debug_info types of the Decl. These will count towards the size of
2464 // the buffer, so we have to do it before computing the offset, and we can't perform the actual2233 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
2465 // relocations yet.2234 // relocations yet.
...@@ -2467,12 +2236,15 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -2467,12 +2236,15 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2467 var it = dbg_info_type_relocs.iterator();2236 var it = dbg_info_type_relocs.iterator();
2468 while (it.next()) |entry| {2237 while (it.next()) |entry| {
2469 entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);2238 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);
2471 }2240 }
2472 }2241 }
24732242
2243 const text_block = &decl.link.elf;
2474 try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));2244 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
2476 {2248 {
2477 // Now that we have the offset assigned we can finally perform type relocations.2249 // Now that we have the offset assigned we can finally perform type relocations.
2478 var it = dbg_info_type_relocs.valueIterator();2250 var it = dbg_info_type_relocs.valueIterator();
...@@ -2495,6 +2267,292 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -2495,6 +2267,292 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2495 return self.updateDeclExports(module, decl, decl_exports);2267 return self.updateDeclExports(module, decl, decl_exports);
2496}2268}
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
2498/// Asserts the type has codegen bits.2556/// Asserts the type has codegen bits.
2499fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {2557fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
2500 switch (ty.zigTypeTag()) {2558 switch (ty.zigTypeTag()) {
...@@ -3022,7 +3080,7 @@ fn pwriteDbgLineNops(...@@ -3022,7 +3080,7 @@ fn pwriteDbgLineNops(
30223080
3023 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;3081 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
3024 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };3082 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;
3026 var vec_index: usize = 0;3084 var vec_index: usize = 0;
3027 {3085 {
3028 var padding_left = prev_padding_size;3086 var padding_left = prev_padding_size;
src/link/MachO.zig+195-100
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const MachO = @This();1const MachO = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
4const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;6const assert = std.debug.assert;
6const fmt = std.fmt;7const fmt = std.fmt;
...@@ -22,11 +23,14 @@ const link = @import("../link.zig");...@@ -22,11 +23,14 @@ const link = @import("../link.zig");
22const File = link.File;23const File = link.File;
23const Cache = @import("../Cache.zig");24const Cache = @import("../Cache.zig");
24const target_util = @import("../target.zig");25const target_util = @import("../target.zig");
26const Air = @import("../Air.zig");
27const Liveness = @import("../Liveness.zig");
2528
26const DebugSymbols = @import("MachO/DebugSymbols.zig");29const DebugSymbols = @import("MachO/DebugSymbols.zig");
27const Trie = @import("MachO/Trie.zig");30const Trie = @import("MachO/Trie.zig");
28const CodeSignature = @import("MachO/CodeSignature.zig");31const CodeSignature = @import("MachO/CodeSignature.zig");
29const Zld = @import("MachO/Zld.zig");32const Zld = @import("MachO/Zld.zig");
33const llvm_backend = @import("../codegen/llvm.zig");
3034
31usingnamespace @import("MachO/commands.zig");35usingnamespace @import("MachO/commands.zig");
3236
...@@ -34,6 +38,9 @@ pub const base_tag: File.Tag = File.Tag.macho;...@@ -34,6 +38,9 @@ pub const base_tag: File.Tag = File.Tag.macho;
3438
35base: File,39base: File,
3640
41/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
42llvm_object: ?*llvm_backend.Object = null,
43
37/// Debug symbols bundle (or dSym).44/// Debug symbols bundle (or dSym).
38d_sym: ?DebugSymbols = null,45d_sym: ?DebugSymbols = null,
3946
...@@ -344,8 +351,13 @@ pub const SrcFn = struct {...@@ -344,8 +351,13 @@ pub const SrcFn = struct {
344pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {351pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
345 assert(options.object_format == .macho);352 assert(options.object_format == .macho);
346353
347 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO354 if (build_options.have_llvm and options.use_llvm) {
348 if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO355 const self = try createEmpty(allocator, options);
356 errdefer self.base.destroy();
357
358 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
359 return self;
360 }
349361
350 const file = try options.emit.?.directory.handle.createFile(sub_path, .{362 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
351 .truncate = false,363 .truncate = false,
...@@ -1132,20 +1144,28 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {...@@ -1132,20 +1144,28 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
1132 };1144 };
1133}1145}
11341146
1135pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {1147pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
1148 if (build_options.skip_non_native and builtin.object_format != .macho) {
1149 @panic("Attempted to compile for object format that was disabled by build configuration");
1150 }
1151 if (build_options.have_llvm) {
1152 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
1153 }
1136 const tracy = trace(@src());1154 const tracy = trace(@src());
1137 defer tracy.end();1155 defer tracy.end();
11381156
1139 if (decl.val.tag() == .extern_fn) {1157 const decl = func.owner_decl;
1140 return; // TODO Should we do more when front-end analyzed extern decl?
1141 }
11421158
1143 var code_buffer = std.ArrayList(u8).init(self.base.allocator);1159 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
1144 defer code_buffer.deinit();1160 defer code_buffer.deinit();
11451161
1146 var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null;1162 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
1163 const debug_buffers = if (self.d_sym) |*ds| blk: {
1164 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
1165 break :blk &debug_buffers_buf;
1166 } else null;
1147 defer {1167 defer {
1148 if (debug_buffers) |*dbg| {1168 if (debug_buffers) |dbg| {
1149 dbg.dbg_line_buffer.deinit();1169 dbg.dbg_line_buffer.deinit();
1150 dbg.dbg_info_buffer.deinit();1170 dbg.dbg_info_buffer.deinit();
1151 var it = dbg.dbg_info_type_relocs.valueIterator();1171 var it = dbg.dbg_info_type_relocs.valueIterator();
...@@ -1156,11 +1176,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1156,11 +1176,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1156 }1176 }
1157 }1177 }
11581178
1159 const res = if (debug_buffers) |*dbg|1179 const res = if (debug_buffers) |dbg|
1160 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{1180 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
1161 .ty = decl.ty,
1162 .val = decl.val,
1163 }, &code_buffer, .{
1164 .dwarf = .{1181 .dwarf = .{
1165 .dbg_line = &dbg.dbg_line_buffer,1182 .dbg_line = &dbg.dbg_line_buffer,
1166 .dbg_info = &dbg.dbg_info_buffer,1183 .dbg_info = &dbg.dbg_info_buffer,
...@@ -1168,14 +1185,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1168,14 +1185,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1168 },1185 },
1169 })1186 })
1170 else1187 else
1171 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{1188 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
1172 .ty = decl.ty,1189 switch (res) {
1173 .val = decl.val,1190 .appended => {},
1174 }, &code_buffer, .none);
1175
1176 const code = switch (res) {
1177 .externally_managed => |x| x,
1178 .appended => code_buffer.items,
1179 .fail => |em| {1191 .fail => |em| {
1180 // Clear any PIE fixups for this decl.1192 // Clear any PIE fixups for this decl.
1181 self.pie_fixups.shrinkRetainingCapacity(0);1193 self.pie_fixups.shrinkRetainingCapacity(0);
...@@ -1185,76 +1197,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1185,76 +1197,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1185 try module.failed_decls.put(module.gpa, decl, em);1197 try module.failed_decls.put(module.gpa, decl, em);
1186 return;1198 return;
1187 },1199 },
1188 };
1189
1190 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
1191 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
1192 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
1193
1194 if (decl.link.macho.size != 0) {
1195 const capacity = decl.link.macho.capacity(self.*);
1196 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
1197 if (need_realloc) {
1198 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
1199
1200 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
1201
1202 if (vaddr != symbol.n_value) {
1203 log.debug(" (writing new offset table entry)", .{});
1204 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1205 .kind = .Local,
1206 .symbol = decl.link.macho.local_sym_index,
1207 .index = decl.link.macho.offset_table_index,
1208 };
1209 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1210 }
1211
1212 symbol.n_value = vaddr;
1213 } else if (code.len < decl.link.macho.size) {
1214 self.shrinkTextBlock(&decl.link.macho, code.len);
1215 }
1216 decl.link.macho.size = code.len;
1217
1218 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
1219 defer self.base.allocator.free(new_name);
1220
1221 symbol.n_strx = try self.updateString(symbol.n_strx, new_name);
1222 symbol.n_type = macho.N_SECT;
1223 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
1224 symbol.n_desc = 0;
1225
1226 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1227 if (self.d_sym) |*ds|
1228 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
1229 } else {
1230 const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
1231 defer self.base.allocator.free(decl_name);
1232
1233 const name_str_index = try self.makeString(decl_name);
1234 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
1235
1236 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
1237
1238 errdefer self.freeTextBlock(&decl.link.macho);
1239
1240 symbol.* = .{
1241 .n_strx = name_str_index,
1242 .n_type = macho.N_SECT,
1243 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
1244 .n_desc = 0,
1245 .n_value = addr,
1246 };
1247 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1248 .kind = .Local,
1249 .symbol = decl.link.macho.local_sym_index,
1250 .index = decl.link.macho.offset_table_index,
1251 };
1252
1253 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1254 if (self.d_sym) |*ds|
1255 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
1256 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1257 }1200 }
1201 const symbol = try self.placeDecl(decl, code_buffer.items.len);
12581202
1259 // Calculate displacements to target addr (if any).1203 // Calculate displacements to target addr (if any).
1260 while (self.pie_fixups.popOrNull()) |fixup| {1204 while (self.pie_fixups.popOrNull()) |fixup| {
...@@ -1271,7 +1215,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1271,7 +1215,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1271 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).1215 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1272 {1216 {
1273 const inst = code_buffer.items[fixup.offset..][0..4];1217 const inst = code_buffer.items[fixup.offset..][0..4];
1274 var parsed = mem.bytesAsValue(meta.TagPayload(1218 const parsed = mem.bytesAsValue(meta.TagPayload(
1275 aarch64.Instruction,1219 aarch64.Instruction,
1276 aarch64.Instruction.pc_relative_address,1220 aarch64.Instruction.pc_relative_address,
1277 ), inst);1221 ), inst);
...@@ -1283,7 +1227,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1283,7 +1227,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1283 }1227 }
1284 {1228 {
1285 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];1229 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
1286 var parsed = mem.bytesAsValue(meta.TagPayload(1230 const parsed = mem.bytesAsValue(meta.TagPayload(
1287 aarch64.Instruction,1231 aarch64.Instruction,
1288 aarch64.Instruction.load_store_register,1232 aarch64.Instruction.load_store_register,
1289 ), inst);1233 ), inst);
...@@ -1306,13 +1250,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1306,13 +1250,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1306 .x86_64 => {1250 .x86_64 => {
1307 assert(stub_addr >= text_addr + fixup.len);1251 assert(stub_addr >= text_addr + fixup.len);
1308 const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len);1252 const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len);
1309 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];1253 const placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1310 mem.writeIntSliceLittle(u32, placeholder, displacement);1254 mem.writeIntSliceLittle(u32, placeholder, displacement);
1311 },1255 },
1312 .aarch64 => {1256 .aarch64 => {
1313 assert(stub_addr >= text_addr);1257 assert(stub_addr >= text_addr);
1314 const displacement = try math.cast(i28, stub_addr - text_addr);1258 const displacement = try math.cast(i28, stub_addr - text_addr);
1315 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1259 const placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1316 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32());1260 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32());
1317 },1261 },
1318 else => unreachable, // unsupported target architecture1262 else => unreachable, // unsupported target architecture
...@@ -1328,12 +1272,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1328,12 +1272,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1328 }1272 }
1329 self.stub_fixups.shrinkRetainingCapacity(0);1273 self.stub_fixups.shrinkRetainingCapacity(0);
13301274
1331 const text_section = text_segment.sections.items[self.text_section_index.?];1275 try self.writeCode(symbol, code_buffer.items);
1332 const section_offset = symbol.n_value - text_section.addr;
1333 const file_offset = text_section.offset + section_offset;
1334 try self.base.file.?.pwriteAll(code, file_offset);
13351276
1336 if (debug_buffers) |*db| {1277 if (debug_buffers) |db| {
1337 try self.d_sym.?.commitDeclDebugInfo(1278 try self.d_sym.?.commitDeclDebugInfo(
1338 self.base.allocator,1279 self.base.allocator,
1339 module,1280 module,
...@@ -1343,11 +1284,165 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1343,11 +1284,165 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1343 );1284 );
1344 }1285 }
13451286
1346 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.1287 // Since we updated the vaddr and the size, each corresponding export symbol also
1288 // needs to be updated.
1347 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};1289 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1348 try self.updateDeclExports(module, decl, decl_exports);1290 try self.updateDeclExports(module, decl, decl_exports);
1349}1291}
13501292
1293pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1294 if (build_options.skip_non_native and builtin.object_format != .macho) {
1295 @panic("Attempted to compile for object format that was disabled by build configuration");
1296 }
1297 if (build_options.have_llvm) {
1298 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
1299 }
1300 const tracy = trace(@src());
1301 defer tracy.end();
1302
1303 if (decl.val.tag() == .extern_fn) {
1304 return; // TODO Should we do more when front-end analyzed extern decl?
1305 }
1306
1307 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
1308 defer code_buffer.deinit();
1309
1310 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
1311 const debug_buffers = if (self.d_sym) |*ds| blk: {
1312 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
1313 break :blk &debug_buffers_buf;
1314 } else null;
1315 defer {
1316 if (debug_buffers) |dbg| {
1317 dbg.dbg_line_buffer.deinit();
1318 dbg.dbg_info_buffer.deinit();
1319 var it = dbg.dbg_info_type_relocs.valueIterator();
1320 while (it.next()) |value| {
1321 value.relocs.deinit(self.base.allocator);
1322 }
1323 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
1324 }
1325 }
1326
1327 const res = if (debug_buffers) |dbg|
1328 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
1329 .ty = decl.ty,
1330 .val = decl.val,
1331 }, &code_buffer, .{
1332 .dwarf = .{
1333 .dbg_line = &dbg.dbg_line_buffer,
1334 .dbg_info = &dbg.dbg_info_buffer,
1335 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
1336 },
1337 })
1338 else
1339 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
1340 .ty = decl.ty,
1341 .val = decl.val,
1342 }, &code_buffer, .none);
1343
1344 const code = switch (res) {
1345 .externally_managed => |x| x,
1346 .appended => code_buffer.items,
1347 .fail => |em| {
1348 decl.analysis = .codegen_failure;
1349 try module.failed_decls.put(module.gpa, decl, em);
1350 return;
1351 },
1352 };
1353 const symbol = try self.placeDecl(decl, code.len);
1354 assert(self.pie_fixups.items.len == 0);
1355 assert(self.stub_fixups.items.len == 0);
1356
1357 try self.writeCode(symbol, code);
1358
1359 // Since we updated the vaddr and the size, each corresponding export symbol also
1360 // needs to be updated.
1361 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1362 try self.updateDeclExports(module, decl, decl_exports);
1363}
1364
1365fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
1366 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
1367 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
1368 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
1369
1370 if (decl.link.macho.size != 0) {
1371 const capacity = decl.link.macho.capacity(self.*);
1372 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
1373 if (need_realloc) {
1374 const vaddr = try self.growTextBlock(&decl.link.macho, code_len, required_alignment);
1375
1376 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
1377
1378 if (vaddr != symbol.n_value) {
1379 log.debug(" (writing new offset table entry)", .{});
1380 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1381 .kind = .Local,
1382 .symbol = decl.link.macho.local_sym_index,
1383 .index = decl.link.macho.offset_table_index,
1384 };
1385 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1386 }
1387
1388 symbol.n_value = vaddr;
1389 } else if (code_len < decl.link.macho.size) {
1390 self.shrinkTextBlock(&decl.link.macho, code_len);
1391 }
1392 decl.link.macho.size = code_len;
1393
1394 const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
1395 defer self.base.allocator.free(new_name);
1396
1397 symbol.n_strx = try self.updateString(symbol.n_strx, new_name);
1398 symbol.n_type = macho.N_SECT;
1399 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
1400 symbol.n_desc = 0;
1401
1402 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1403 if (self.d_sym) |*ds|
1404 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
1405 } else {
1406 const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
1407 defer self.base.allocator.free(decl_name);
1408
1409 const name_str_index = try self.makeString(decl_name);
1410 const addr = try self.allocateTextBlock(&decl.link.macho, code_len, required_alignment);
1411
1412 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
1413
1414 errdefer self.freeTextBlock(&decl.link.macho);
1415
1416 symbol.* = .{
1417 .n_strx = name_str_index,
1418 .n_type = macho.N_SECT,
1419 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
1420 .n_desc = 0,
1421 .n_value = addr,
1422 };
1423 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1424 .kind = .Local,
1425 .symbol = decl.link.macho.local_sym_index,
1426 .index = decl.link.macho.offset_table_index,
1427 };
1428
1429 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1430 if (self.d_sym) |*ds|
1431 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
1432 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1433 }
1434
1435 return symbol;
1436}
1437
1438fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void {
1439 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1440 const text_section = text_segment.sections.items[self.text_section_index.?];
1441 const section_offset = symbol.n_value - text_section.addr;
1442 const file_offset = text_section.offset + section_offset;
1443 try self.base.file.?.pwriteAll(code, file_offset);
1444}
1445
1351pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {1446pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
1352 if (self.d_sym) |*ds| {1447 if (self.d_sym) |*ds| {
1353 try ds.updateDeclLineNumber(module, decl);1448 try ds.updateDeclLineNumber(module, decl);
src/link/Plan9.zig+230-155
...@@ -2,18 +2,21 @@...@@ -2,18 +2,21 @@
2//! would be to add incremental linking in a similar way as ELF does.2//! would be to add incremental linking in a similar way as ELF does.
33
4const Plan9 = @This();4const Plan9 = @This();
5
6const std = @import("std");
7const link = @import("../link.zig");5const link = @import("../link.zig");
8const Module = @import("../Module.zig");6const Module = @import("../Module.zig");
9const Compilation = @import("../Compilation.zig");7const Compilation = @import("../Compilation.zig");
10const aout = @import("Plan9/aout.zig");8const aout = @import("Plan9/aout.zig");
11const codegen = @import("../codegen.zig");9const codegen = @import("../codegen.zig");
12const trace = @import("../tracy.zig").trace;10const trace = @import("../tracy.zig").trace;
13const mem = std.mem;
14const File = link.File;11const 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;
17const log = std.log.scoped(.link);20const log = std.log.scoped(.link);
18const assert = std.debug.assert;21const assert = std.debug.assert;
1922
...@@ -22,20 +25,22 @@ sixtyfour_bit: bool,...@@ -22,20 +25,22 @@ sixtyfour_bit: bool,
22error_flags: File.ErrorFlags = File.ErrorFlags{},25error_flags: File.ErrorFlags = File.ErrorFlags{},
23bases: Bases,26bases: Bases,
2427
25decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},28/// A symbol's value is just casted down when compiling
26/// is just casted down when 32 bit29/// for a 32 bit target.
27syms: std.ArrayListUnmanaged(aout.Sym) = .{},30syms: std.ArrayListUnmanaged(aout.Sym) = .{},
28text_buf: std.ArrayListUnmanaged(u8) = .{},31
29data_buf: std.ArrayListUnmanaged(u8) = .{},32fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
33data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
3034
31hdr: aout.ExecHdr = undefined,35hdr: aout.ExecHdr = undefined,
3236
33entry_decl: ?*Module.Decl = null,37entry_val: ?u64 = null,
38
39got_len: u64 = 0,
3440
35got: std.ArrayListUnmanaged(u64) = .{},
36const Bases = struct {41const Bases = struct {
37 text: u64,42 text: u64,
38 /// the addr of the got43 /// the Global Offset Table starts at the beginning of the data section
39 data: u64,44 data: u64,
40};45};
4146
...@@ -46,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {...@@ -46,14 +51,6 @@ fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
46 else => unreachable,51 else => unreachable,
47 };52 };
48}53}
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
58fn getSymAddr(self: Plan9, s: aout.Sym) u64 {55fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
59 return self.getAddr(s.value, s.type);56 return self.getAddr(s.value, s.type);
...@@ -120,9 +117,84 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {...@@ -120,9 +117,84 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
120 return self;117 return self;
121}118}
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
123pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {143pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
124 _ = module;144 if (decl.val.tag() == .extern_fn) {
125 _ = try self.decl_table.getOrPut(self.base.allocator, decl);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 }
126}198}
127199
128pub fn flush(self: *Plan9, comp: *Compilation) !void {200pub fn flush(self: *Plan9, comp: *Compilation) !void {
...@@ -138,6 +210,10 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {...@@ -138,6 +210,10 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
138}210}
139211
140pub fn flushModule(self: *Plan9, comp: *Compilation) !void {212pub 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
141 _ = comp;217 _ = comp;
142 const tracy = trace(@src());218 const tracy = trace(@src());
143 defer tracy.end();219 defer tracy.end();
...@@ -146,160 +222,147 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {...@@ -146,160 +222,147 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
146222
147 defer assert(self.hdr.entry != 0x0);223 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;227 assert(self.got_len == self.fn_decl_table.count() + self.data_decl_table.count());
152 self.data_buf.items.len = 0;228 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
153 // ensure space to write the got later229 var got_table = try self.base.allocator.alloc(u8, got_size);
154 assert(self.got.items.len == self.decl_table.count());230 defer self.base.allocator.free(got_table);
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 }
191231
192 if (module.decl_exports.get(decl)) |exports| {232 // + 2 for header, got, symbols
193 for (exports) |exp| {233 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);
194 // plan9 does not support custom sections234 defer self.base.allocator.free(iovecs);
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 }
221235
222 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });236 const file = self.base.file.?;
223 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{237
224 .ty = decl.ty,238 var hdr_buf: [40]u8 = undefined;
225 .val = decl.val,239 // account for the fat header
226 }, &code_buffer, .{ .none = {} });240 const hdr_size = if (self.sixtyfour_bit) @as(usize, 40) else 32;
227 const code = switch (res) {241 const hdr_slice: []u8 = hdr_buf[0..hdr_size];
228 .externally_managed => |x| x,242 var foff = hdr_size;
229 .appended => code_buffer.items,243 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
230 .fail => |em| {244 var iovecs_i: u64 = 1;
231 decl.analysis = .codegen_failure;245 var text_i: u64 = 0;
232 try module.failed_decls.put(module.gpa, decl, em);246 // text
233 // TODO try to do more decls247 {
234 return;248 var it = self.fn_decl_table.iterator();
235 },249 while (it.next()) |entry| {
236 };250 const decl = entry.key_ptr.*;
237 if (is_fn) {251 const code = entry.value_ptr.*;
238 try self.text_buf.appendSlice(self.base.allocator, code);252 log.debug("write text decl {*} ({s})", .{ decl, decl.name });
239 code_buffer.items.len = 0;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());
240 } else {262 } else {
241 try self.data_buf.appendSlice(self.base.allocator, code);263 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
242 code_buffer.items.len = 0;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);
243 }268 }
244 }269 }
270 // etext symbol
271 self.syms.items[2].value = self.getAddr(text_i, .t);
245 }272 }
246273 // global offset table is in data
247 // write the got274 iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };
248 if (!self.sixtyfour_bit) {275 iovecs_i += 1;
249 for (self.got.items) |p, i| {276 // data
250 mem.writeInt(u32, self.data_buf.items[i * 4 ..][0..4], @intCast(u32, p), self.base.options.target.cpu.arch.endian());277 var data_i: u64 = got_size;
251 }278 {
252 } else {279 var it = self.data_decl_table.iterator();
253 for (self.got.items) |p, i| {280 while (it.next()) |entry| {
254 mem.writeInt(u64, self.data_buf.items[i * 8 ..][0..8], p, self.base.options.target.cpu.arch.endian());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 }
255 }300 }
301 // edata symbol
302 self.syms.items[0].value = self.getAddr(data_i, .b);
256 }303 }
257304 // edata
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);
262 self.syms.items[1].value = self.getAddr(0x0, .b);305 self.syms.items[1].value = self.getAddr(0x0, .b);
263 self.syms.items[2].value = self.getAddr(self.text_buf.items.len, .t);
264
265 var sym_buf = std.ArrayList(u8).init(self.base.allocator);306 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
266 defer sym_buf.deinit();307 defer sym_buf.deinit();
267 try self.writeSyms(&sym_buf);308 try self.writeSyms(&sym_buf);
268309 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;
269 // generate the header312 // generate the header
270 self.hdr = .{313 self.hdr = .{
271 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),314 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
272 .text = @intCast(u32, self.text_buf.items.len),315 .text = @intCast(u32, text_i),
273 .data = @intCast(u32, self.data_buf.items.len),316 .data = @intCast(u32, data_i),
274 .syms = @intCast(u32, sym_buf.items.len),317 .syms = @intCast(u32, sym_buf.items.len),
275 .bss = 0,318 .bss = 0,
276 .pcsz = 0,319 .pcsz = 0,
277 .spsz = 0,320 .spsz = 0,
278 .entry = self.hdr.entry,321 .entry = @intCast(u32, self.entry_val.?),
279 };322 };
280323 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
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;
287 // write the fat header for 64 bit entry points324 // write the fat header for 64 bit entry points
288 if (self.sixtyfour_bit) {325 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.?);
290 }327 }
291 // write it all!328 // write it all!
292 var vectors: [4]std.os.iovec_const = .{329 try file.pwritevAll(iovecs, 0);
293 .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_size },330}
294 .{ .iov_base = self.text_buf.items.ptr, .iov_len = self.text_buf.items.len },331fn addDeclExports(
295 .{ .iov_base = self.data_buf.items.ptr, .iov_len = self.data_buf.items.len },332 self: *Plan9,
296 .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len },333 module: *Module,
297 // TODO spsz, pcsz334 decl: *Module.Decl,
298 };335 exports: []const *Module.Export,
299 try file.pwritevAll(&vectors, 0);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 }
300}358}
359
301pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {360pub 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));
303}366}
304367
305pub fn updateDeclExports(368pub fn updateDeclExports(
...@@ -315,11 +378,17 @@ pub fn updateDeclExports(...@@ -315,11 +378,17 @@ pub fn updateDeclExports(
315 _ = exports;378 _ = exports;
316}379}
317pub fn deinit(self: *Plan9) void {380pub 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);
319 self.syms.deinit(self.base.allocator);391 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);
323}392}
324393
325pub const Export = ?usize;394pub const Export = ?usize;
...@@ -366,18 +435,24 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -366,18 +435,24 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
366pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {435pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
367 const writer = buf.writer();436 const writer = buf.writer();
368 for (self.syms.items) |sym| {437 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;
369 if (!self.sixtyfour_bit) {442 if (!self.sixtyfour_bit) {
370 try writer.writeIntBig(u32, @intCast(u32, sym.value));443 try writer.writeIntBig(u32, @intCast(u32, sym.value));
371 } else {444 } else {
372 try writer.writeIntBig(u64, sym.value);445 try writer.writeIntBig(u64, sym.value);
373 }446 }
374 try writer.writeByte(@enumToInt(sym.type));447 try writer.writeByte(@enumToInt(sym.type));
375 try writer.writeAll(std.mem.span(sym.name));448 try writer.writeAll(sym.name);
376 try writer.writeByte(0);449 try writer.writeByte(0);
377 }450 }
378}451}
379452
380pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {453pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
381 try self.got.append(self.base.allocator, 0xdeadbeef);454 if (decl.link.plan9.got_index == null) {
382 decl.link.plan9.got_index = self.got.items.len - 1;455 self.got_len += 1;
456 decl.link.plan9.got_index = self.got_len - 1;
457 }
383}458}
src/link/SpirV.zig+35-3
...@@ -36,6 +36,8 @@ const ResultId = codegen.ResultId;...@@ -36,6 +36,8 @@ const ResultId = codegen.ResultId;
36const trace = @import("../tracy.zig").trace;36const trace = @import("../tracy.zig").trace;
37const build_options = @import("build_options");37const build_options = @import("build_options");
38const spec = @import("../codegen/spirv/spec.zig");38const spec = @import("../codegen/spirv/spec.zig");
39const Air = @import("../Air.zig");
40const Liveness = @import("../Liveness.zig");
3941
40// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?42// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?
41pub const FnData = struct {43pub const FnData = struct {
...@@ -49,7 +51,12 @@ base: link.File,...@@ -49,7 +51,12 @@ base: link.File,
49/// This linker backend does not try to incrementally link output SPIR-V code.51/// This linker backend does not try to incrementally link output SPIR-V code.
50/// Instead, it tracks all declarations in this table, and iterates over it52/// Instead, it tracks all declarations in this table, and iterates over it
51/// in the flush function.53/// 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
54pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {61pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV {
55 const spirv = try gpa.create(SpirV);62 const spirv = try gpa.create(SpirV);
...@@ -101,7 +108,23 @@ pub fn deinit(self: *SpirV) void {...@@ -101,7 +108,23 @@ pub fn deinit(self: *SpirV) void {
101 self.decl_table.deinit(self.base.allocator);108 self.decl_table.deinit(self.base.allocator);
102}109}
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
104pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {124pub 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 }
105 _ = module;128 _ = module;
106 // Keep track of all decls so we can iterate over them on flush().129 // Keep track of all decls so we can iterate over them on flush().
107 _ = try self.decl_table.getOrPut(self.base.allocator, decl);130 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
...@@ -132,6 +155,10 @@ pub fn flush(self: *SpirV, comp: *Compilation) !void {...@@ -132,6 +155,10 @@ pub fn flush(self: *SpirV, comp: *Compilation) !void {
132}155}
133156
134pub fn flushModule(self: *SpirV, comp: *Compilation) !void {157pub 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
135 const tracy = trace(@src());162 const tracy = trace(@src());
136 defer tracy.end();163 defer tracy.end();
137164
...@@ -159,10 +186,15 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -159,10 +186,15 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
159 var decl_gen = codegen.DeclGen.init(&spv);186 var decl_gen = codegen.DeclGen.init(&spv);
160 defer decl_gen.deinit();187 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.*;
163 if (!decl.has_tv) continue;192 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| {
166 try module.failed_decls.put(module.gpa, decl, msg);198 try module.failed_decls.put(module.gpa, decl, msg);
167 return; // TODO: Attempt to generate more decls?199 return; // TODO: Attempt to generate more decls?
168 }200 }
src/link/Wasm.zig+76-8
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Wasm = @This();1const Wasm = @This();
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
4const mem = std.mem;5const mem = std.mem;
5const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;7const assert = std.debug.assert;
...@@ -18,10 +19,15 @@ const build_options = @import("build_options");...@@ -18,10 +19,15 @@ const build_options = @import("build_options");
18const wasi_libc = @import("../wasi_libc.zig");19const wasi_libc = @import("../wasi_libc.zig");
19const Cache = @import("../Cache.zig");20const Cache = @import("../Cache.zig");
20const TypedValue = @import("../TypedValue.zig");21const TypedValue = @import("../TypedValue.zig");
22const llvm_backend = @import("../codegen/llvm.zig");
23const Air = @import("../Air.zig");
24const Liveness = @import("../Liveness.zig");
2125
22pub const base_tag = link.File.Tag.wasm;26pub const base_tag = link.File.Tag.wasm;
2327
24base: link.File,28base: 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,
25/// List of all function Decls to be written to the output file. The index of31/// List of all function Decls to be written to the output file. The index of
26/// each Decl in this list at the time of writing the binary is used as the32/// each Decl in this list at the time of writing the binary is used as the
27/// function index. In the event where ext_funcs' size is not 0, the index of33/// function index. In the event where ext_funcs' size is not 0, the index of
...@@ -111,8 +117,13 @@ pub const DeclBlock = struct {...@@ -111,8 +117,13 @@ pub const DeclBlock = struct {
111pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {117pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
112 assert(options.object_format == .wasm);118 assert(options.object_format == .wasm);
113119
114 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO120 if (build_options.have_llvm and options.use_llvm) {
115 if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO121 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
117 // TODO: read the file and keep valid parts instead of truncating128 // TODO: read the file and keep valid parts instead of truncating
118 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });129 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 {...@@ -186,11 +197,60 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
186 }197 }
187}198}
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
189// Generate code for the Decl, storing it in memory to be later written to242// Generate code for the Decl, storing it in memory to be later written to
190// the file on flush().243// the file on flush().
191pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {244pub 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
194 const fn_data = &decl.fn_link.wasm;254 const fn_data = &decl.fn_link.wasm;
195 fn_data.functype.items.len = 0;255 fn_data.functype.items.len = 0;
196 fn_data.code.items.len = 0;256 fn_data.code.items.len = 0;
...@@ -198,6 +258,8 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -198,6 +258,8 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
198258
199 var context = codegen.Context{259 var context = codegen.Context{
200 .gpa = self.base.allocator,260 .gpa = self.base.allocator,
261 .air = undefined,
262 .liveness = undefined,
201 .values = .{},263 .values = .{},
202 .code = fn_data.code.toManaged(self.base.allocator),264 .code = fn_data.code.toManaged(self.base.allocator),
203 .func_type_data = fn_data.functype.toManaged(self.base.allocator),265 .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 {...@@ -219,14 +281,20 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
219 else => |e| return e,281 else => |e| return e,
220 };282 };
221283
222 const code: []const u8 = switch (result) {284 return self.finishUpdateDecl(decl, result, &context);
223 .appended => @as([]const u8, context.code.items),285}
224 .externally_managed => |payload| payload,286
225 };287fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, result: codegen.Result, context: *codegen.Context) !void {
288 const fn_data: *FnData = &decl.fn_link.wasm;
226289
227 fn_data.code = context.code.toUnmanaged();290 fn_data.code = context.code.toUnmanaged();
228 fn_data.functype = context.func_type_data.toUnmanaged();291 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
230 const block = &decl.link.wasm;298 const block = &decl.link.wasm;
231 if (decl.ty.zigTypeTag() == .Fn) {299 if (decl.ty.zigTypeTag() == .Fn) {
232 // as locals are patched afterwards, the offsets of funcidx's are off,300 // as locals are patched afterwards, the offsets of funcidx's are off,
...@@ -521,7 +589,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -521,7 +589,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
521 var data_offset = offset_table_size;589 var data_offset = offset_table_size;
522 while (cur) |cur_block| : (cur = cur_block.next) {590 while (cur) |cur_block| : (cur = cur_block.next) {
523 if (cur_block.size == 0) continue;591 if (cur_block.size == 0) continue;
524 std.debug.assert(cur_block.init);592 assert(cur_block.init);
525593
526 const offset = (cur_block.offset_index) * ptr_width;594 const offset = (cur_block.offset_index) * ptr_width;
527 var buf: [4]u8 = undefined;595 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 =...@@ -365,6 +365,7 @@ const usage_build_generic =
365 \\ coff Common Object File Format (Windows)365 \\ coff Common Object File Format (Windows)
366 \\ macho macOS relocatables366 \\ macho macOS relocatables
367 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)367 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
368 \\ plan9 Plan 9 from Bell Labs object format
368 \\ hex (planned) Intel IHEX369 \\ hex (planned) Intel IHEX
369 \\ raw (planned) Dump machine code directly370 \\ raw (planned) Dump machine code directly
370 \\ -dirafter [dir] Add directory to AFTER include search path371 \\ -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;...@@ -3,7 +3,7 @@ const math = std.math;
3const mem = std.mem;3const mem = std.mem;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const ir = @import("air.zig");6const Air = @import("Air.zig");
7const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
8const Module = @import("Module.zig");8const Module = @import("Module.zig");
9const LazySrcLoc = Module.LazySrcLoc;9const LazySrcLoc = Module.LazySrcLoc;
...@@ -20,7 +20,7 @@ pub fn RegisterManager(...@@ -20,7 +20,7 @@ pub fn RegisterManager(
20) type {20) type {
21 return struct {21 return struct {
22 /// The key must be canonical register.22 /// 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,
24 free_registers: FreeRegInt = math.maxInt(FreeRegInt),24 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
25 /// Tracks all registers allocated in the course of this function25 /// Tracks all registers allocated in the course of this function
26 allocated_registers: FreeRegInt = 0,26 allocated_registers: FreeRegInt = 0,
...@@ -75,7 +75,7 @@ pub fn RegisterManager(...@@ -75,7 +75,7 @@ pub fn RegisterManager(
75 pub fn tryAllocRegs(75 pub fn tryAllocRegs(
76 self: *Self,76 self: *Self,
77 comptime count: comptime_int,77 comptime count: comptime_int,
78 insts: [count]?*ir.Inst,78 insts: [count]?Air.Inst.Index,
79 exceptions: []const Register,79 exceptions: []const Register,
80 ) ?[count]Register {80 ) ?[count]Register {
81 comptime if (callee_preserved_regs.len == 0) return null;81 comptime if (callee_preserved_regs.len == 0) return null;
...@@ -113,7 +113,7 @@ pub fn RegisterManager(...@@ -113,7 +113,7 @@ pub fn RegisterManager(
113 /// Allocates a register and optionally tracks it with a113 /// Allocates a register and optionally tracks it with a
114 /// corresponding instruction. Returns `null` if all registers114 /// corresponding instruction. Returns `null` if all registers
115 /// are allocated.115 /// 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 {
117 return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null;117 return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null;
118 }118 }
119119
...@@ -123,7 +123,7 @@ pub fn RegisterManager(...@@ -123,7 +123,7 @@ pub fn RegisterManager(
123 pub fn allocRegs(123 pub fn allocRegs(
124 self: *Self,124 self: *Self,
125 comptime count: comptime_int,125 comptime count: comptime_int,
126 insts: [count]?*ir.Inst,126 insts: [count]?Air.Inst.Index,
127 exceptions: []const Register,127 exceptions: []const Register,
128 ) ![count]Register {128 ) ![count]Register {
129 comptime assert(count > 0 and count <= callee_preserved_regs.len);129 comptime assert(count > 0 and count <= callee_preserved_regs.len);
...@@ -147,14 +147,14 @@ pub fn RegisterManager(...@@ -147,14 +147,14 @@ pub fn RegisterManager(
147 self.markRegUsed(reg);147 self.markRegUsed(reg);
148 } else {148 } else {
149 const spilled_inst = self.registers[index].?;149 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);
151 }151 }
152 self.registers[index] = inst;152 self.registers[index] = inst;
153 } else {153 } else {
154 // Don't track the register154 // Don't track the register
155 if (!self.isRegFree(reg)) {155 if (!self.isRegFree(reg)) {
156 const spilled_inst = self.registers[index].?;156 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);
158 self.freeReg(reg);158 self.freeReg(reg);
159 }159 }
160 }160 }
...@@ -168,14 +168,14 @@ pub fn RegisterManager(...@@ -168,14 +168,14 @@ pub fn RegisterManager(
168168
169 /// Allocates a register and optionally tracks it with a169 /// Allocates a register and optionally tracks it with a
170 /// corresponding instruction.170 /// 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 {
172 return (try self.allocRegs(1, .{inst}, exceptions))[0];172 return (try self.allocRegs(1, .{inst}, exceptions))[0];
173 }173 }
174174
175 /// Spills the register if it is currently allocated. If a175 /// Spills the register if it is currently allocated. If a
176 /// corresponding instruction is passed, will also track this176 /// corresponding instruction is passed, will also track this
177 /// register.177 /// 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 {
179 const index = reg.allocIndex() orelse return;179 const index = reg.allocIndex() orelse return;
180180
181 if (inst) |tracked_inst|181 if (inst) |tracked_inst|
...@@ -184,7 +184,7 @@ pub fn RegisterManager(...@@ -184,7 +184,7 @@ pub fn RegisterManager(
184 // stack allocation.184 // stack allocation.
185 const spilled_inst = self.registers[index].?;185 const spilled_inst = self.registers[index].?;
186 self.registers[index] = tracked_inst;186 self.registers[index] = tracked_inst;
187 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);187 try self.getFunction().spillInstruction(reg, spilled_inst);
188 } else {188 } else {
189 self.getRegAssumeFree(reg, tracked_inst);189 self.getRegAssumeFree(reg, tracked_inst);
190 }190 }
...@@ -193,7 +193,7 @@ pub fn RegisterManager(...@@ -193,7 +193,7 @@ pub fn RegisterManager(
193 // Move the instruction that was previously there to a193 // Move the instruction that was previously there to a
194 // stack allocation.194 // stack allocation.
195 const spilled_inst = self.registers[index].?;195 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);
197 self.freeReg(reg);197 self.freeReg(reg);
198 }198 }
199 }199 }
...@@ -202,7 +202,7 @@ pub fn RegisterManager(...@@ -202,7 +202,7 @@ pub fn RegisterManager(
202 /// Allocates the specified register with the specified202 /// Allocates the specified register with the specified
203 /// instruction. Asserts that the register is free and no203 /// instruction. Asserts that the register is free and no
204 /// spilling is necessary.204 /// 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 {
206 const index = reg.allocIndex() orelse return;206 const index = reg.allocIndex() orelse return;
207207
208 assert(self.registers[index] == null);208 assert(self.registers[index] == null);
...@@ -264,8 +264,7 @@ fn MockFunction(comptime Register: type) type {...@@ -264,8 +264,7 @@ fn MockFunction(comptime Register: type) type {
264 self.spilled.deinit(self.allocator);264 self.spilled.deinit(self.allocator);
265 }265 }
266266
267 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void {267 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
268 _ = src;
269 _ = inst;268 _ = inst;
270 try self.spilled.append(self.allocator, reg);269 try self.spilled.append(self.allocator, reg);
271 }270 }
...@@ -297,15 +296,11 @@ test "tryAllocReg: no spilling" {...@@ -297,15 +296,11 @@ test "tryAllocReg: no spilling" {
297 };296 };
298 defer function.deinit();297 defer function.deinit();
299298
300 var mock_instruction = ir.Inst{299 const mock_instruction: Air.Inst.Index = 1;
301 .tag = .breakpoint,
302 .ty = Type.initTag(.void),
303 .src = .unneeded,
304 };
305300
306 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{}));301 try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, &.{}));
307 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{}));302 try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, &.{}));
308 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(&mock_instruction, &.{}));303 try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, &.{}));
309304
310 try expect(function.register_manager.isRegAllocated(.r2));305 try expect(function.register_manager.isRegAllocated(.r2));
311 try expect(function.register_manager.isRegAllocated(.r3));306 try expect(function.register_manager.isRegAllocated(.r3));
...@@ -329,28 +324,24 @@ test "allocReg: spilling" {...@@ -329,28 +324,24 @@ test "allocReg: spilling" {
329 };324 };
330 defer function.deinit();325 defer function.deinit();
331326
332 var mock_instruction = ir.Inst{327 const mock_instruction: Air.Inst.Index = 1;
333 .tag = .breakpoint,
334 .ty = Type.initTag(.void),
335 .src = .unneeded,
336 };
337328
338 try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{}));329 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, &.{}));330 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{}));
340331
341 // Spill a register332 // 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, &.{}));
343 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);334 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
344335
345 // No spilling necessary336 // No spilling necessary
346 function.register_manager.freeReg(.r3);337 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, &.{}));
348 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);339 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
349340
350 // Exceptions341 // Exceptions
351 function.register_manager.freeReg(.r2);342 function.register_manager.freeReg(.r2);
352 function.register_manager.freeReg(.r3);343 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}));
354}345}
355346
356test "tryAllocRegs" {347test "tryAllocRegs" {
...@@ -378,16 +369,12 @@ test "allocRegs" {...@@ -378,16 +369,12 @@ test "allocRegs" {
378 };369 };
379 defer function.deinit();370 defer function.deinit();
380371
381 var mock_instruction = ir.Inst{372 const mock_instruction: Air.Inst.Index = 1;
382 .tag = .breakpoint,
383 .ty = Type.initTag(.void),
384 .src = .unneeded,
385 };
386373
387 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{374 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{
388 &mock_instruction,375 mock_instruction,
389 &mock_instruction,376 mock_instruction,
390 &mock_instruction,377 mock_instruction,
391 }, &.{}));378 }, &.{}));
392379
393 // Exceptions380 // Exceptions
...@@ -403,13 +390,9 @@ test "getReg" {...@@ -403,13 +390,9 @@ test "getReg" {
403 };390 };
404 defer function.deinit();391 defer function.deinit();
405392
406 var mock_instruction = ir.Inst{393 const mock_instruction: Air.Inst.Index = 1;
407 .tag = .breakpoint,
408 .ty = Type.initTag(.void),
409 .src = .unneeded,
410 };
411394
412 try function.register_manager.getReg(.r3, &mock_instruction);395 try function.register_manager.getReg(.r3, mock_instruction);
413396
414 try expect(!function.register_manager.isRegAllocated(.r2));397 try expect(!function.register_manager.isRegAllocated(.r2));
415 try expect(function.register_manager.isRegAllocated(.r3));398 try expect(function.register_manager.isRegAllocated(.r3));
...@@ -417,7 +400,7 @@ test "getReg" {...@@ -417,7 +400,7 @@ test "getReg" {
417 try expect(!function.register_manager.isRegFree(.r3));400 try expect(!function.register_manager.isRegFree(.r3));
418401
419 // Spill r3402 // Spill r3
420 try function.register_manager.getReg(.r3, &mock_instruction);403 try function.register_manager.getReg(.r3, mock_instruction);
421404
422 try expect(!function.register_manager.isRegAllocated(.r2));405 try expect(!function.register_manager.isRegAllocated(.r2));
423 try expect(function.register_manager.isRegAllocated(.r3));406 try expect(function.register_manager.isRegAllocated(.r3));
src/test.zig+8-3
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const link = @import("link.zig");3const link = @import("link.zig");
3const Compilation = @import("Compilation.zig");4const Compilation = @import("Compilation.zig");
4const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
...@@ -610,7 +611,7 @@ pub const TestContext = struct {...@@ -610,7 +611,7 @@ pub const TestContext = struct {
610611
611 fn run(self: *TestContext) !void {612 fn run(self: *TestContext) !void {
612 var progress = std.Progress{};613 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);
614 defer root_node.end();615 defer root_node.end();
615616
616 var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator);617 var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator);
...@@ -640,8 +641,12 @@ pub const TestContext = struct {...@@ -640,8 +641,12 @@ pub const TestContext = struct {
640 var fail_count: usize = 0;641 var fail_count: usize = 0;
641642
642 for (self.cases.items) |case| {643 for (self.cases.items) |case| {
643 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)644 if (build_options.skip_non_native) {
644 continue;645 if (case.target.getCpuArch() != builtin.cpu.arch)
646 continue;
647 if (case.target.getObjectFormat() != builtin.object_format)
648 continue;
649 }
645650
646 // Skip tests that require LLVM backend when it is not available651 // Skip tests that require LLVM backend when it is not available
647 if (!build_options.have_llvm and case.backend == .llvm)652 if (!build_options.have_llvm and case.backend == .llvm)
src/value.zig+3-3
...@@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable;...@@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable;
7const Target = std.Target;7const Target = std.Target;
8const Allocator = std.mem.Allocator;8const Allocator = std.mem.Allocator;
9const Module = @import("Module.zig");9const Module = @import("Module.zig");
10const ir = @import("air.zig");10const Air = @import("Air.zig");
1111
12/// This is the raw data, with no bookkeeping, no memory awareness,12/// This is the raw data, with no bookkeeping, no memory awareness,
13/// no de-duplication, and no type system awareness.13/// no de-duplication, and no type system awareness.
...@@ -573,7 +573,7 @@ pub const Value = extern union {...@@ -573,7 +573,7 @@ pub const Value = extern union {
573 .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),573 .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
574 .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),574 .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
575 .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),575 .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}),
577 .extern_fn => return out_stream.writeAll("(extern function)"),577 .extern_fn => return out_stream.writeAll("(extern function)"),
578 .variable => return out_stream.writeAll("(variable)"),578 .variable => return out_stream.writeAll("(variable)"),
579 .ref_val => {579 .ref_val => {
...@@ -1700,7 +1700,7 @@ pub const Value = extern union {...@@ -1700,7 +1700,7 @@ pub const Value = extern union {
1700 /// peer type resolution. This is stored in a separate list so that1700 /// peer type resolution. This is stored in a separate list so that
1701 /// the items are contiguous in memory and thus can be passed to1701 /// the items are contiguous in memory and thus can be passed to
1702 /// `Module.resolvePeerTypes`.1702 /// `Module.resolvePeerTypes`.
1703 stored_inst_list: std.ArrayListUnmanaged(*ir.Inst) = .{},1703 stored_inst_list: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
1704 },1704 },
1705 };1705 };
17061706
test/stage2/wasm.zig+62-60
...@@ -479,66 +479,68 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -479,66 +479,68 @@ pub fn addCases(ctx: *TestContext) !void {
479 , "30\n");479 , "30\n");
480 }480 }
481481
482 {482 // This test case is disabled until the codegen for switch is reworked
483 var case = ctx.exe("wasm switch", wasi);483 // to take advantage of br_table rather than a series of br_if opcodes.
484484 //{
485 case.addCompareOutput(485 // var case = ctx.exe("wasm switch", wasi);
486 \\pub export fn _start() u32 {486
487 \\ var val: u32 = 1;487 // case.addCompareOutput(
488 \\ var a: u32 = switch (val) {488 // \\pub export fn _start() u32 {
489 \\ 0, 1 => 2,489 // \\ var val: u32 = 1;
490 \\ 2 => 3,490 // \\ var a: u32 = switch (val) {
491 \\ 3 => 4,491 // \\ 0, 1 => 2,
492 \\ else => 5,492 // \\ 2 => 3,
493 \\ };493 // \\ 3 => 4,
494 \\494 // \\ else => 5,
495 \\ return a;495 // \\ };
496 \\}496 // \\
497 , "2\n");497 // \\ return a;
498498 // \\}
499 case.addCompareOutput(499 // , "2\n");
500 \\pub export fn _start() u32 {500
501 \\ var val: u32 = 2;501 // case.addCompareOutput(
502 \\ var a: u32 = switch (val) {502 // \\pub export fn _start() u32 {
503 \\ 0, 1 => 2,503 // \\ var val: u32 = 2;
504 \\ 2 => 3,504 // \\ var a: u32 = switch (val) {
505 \\ 3 => 4,505 // \\ 0, 1 => 2,
506 \\ else => 5,506 // \\ 2 => 3,
507 \\ };507 // \\ 3 => 4,
508 \\508 // \\ else => 5,
509 \\ return a;509 // \\ };
510 \\}510 // \\
511 , "3\n");511 // \\ return a;
512512 // \\}
513 case.addCompareOutput(513 // , "3\n");
514 \\pub export fn _start() u32 {514
515 \\ var val: u32 = 10;515 // case.addCompareOutput(
516 \\ var a: u32 = switch (val) {516 // \\pub export fn _start() u32 {
517 \\ 0, 1 => 2,517 // \\ var val: u32 = 10;
518 \\ 2 => 3,518 // \\ var a: u32 = switch (val) {
519 \\ 3 => 4,519 // \\ 0, 1 => 2,
520 \\ else => 5,520 // \\ 2 => 3,
521 \\ };521 // \\ 3 => 4,
522 \\522 // \\ else => 5,
523 \\ return a;523 // \\ };
524 \\}524 // \\
525 , "5\n");525 // \\ return a;
526526 // \\}
527 case.addCompareOutput(527 // , "5\n");
528 \\const MyEnum = enum { One, Two, Three };528
529 \\529 // case.addCompareOutput(
530 \\pub export fn _start() u32 {530 // \\const MyEnum = enum { One, Two, Three };
531 \\ var val: MyEnum = .Two;531 // \\
532 \\ var a: u32 = switch (val) {532 // \\pub export fn _start() u32 {
533 \\ .One => 1,533 // \\ var val: MyEnum = .Two;
534 \\ .Two => 2,534 // \\ var a: u32 = switch (val) {
535 \\ .Three => 3,535 // \\ .One => 1,
536 \\ };536 // \\ .Two => 2,
537 \\537 // \\ .Three => 3,
538 \\ return a;538 // \\ };
539 \\}539 // \\
540 , "2\n");540 // \\ return a;
541 }541 // \\}
542 // , "2\n");
543 //}
542544
543 {545 {
544 var case = ctx.exe("wasm error unions", wasi);546 var case = ctx.exe("wasm error unions", wasi);