authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-22 14:42:46-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-04-22 14:42:46-04:00
loge8545db9d4ced8978c5594c637d9bf76dc26209d
treec51f45b9374f9313636039e6e43310bf81cc359f
parentb5e72c0148e40df418bdc8e1770b1bd42e76732e
parent1eda2ada9ac115c9dbff1bba60b3670f3fbfff57
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5130 from ziglang/stage2-ir

beginnings of non-LLVM self-hosted backend

14 files changed, 3067 insertions(+), 4267 deletions(-)

lib/std/fs.zig+7-5
......@@ -1012,25 +1012,27 @@ pub const Dir = struct {
10121012 /// On success, caller owns returned buffer.
10131013 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
10141014 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {
1015 return self.readFileAllocAligned(allocator, file_path, max_bytes, @alignOf(u8));
1015 return self.readFileAllocOptions(allocator, file_path, max_bytes, @alignOf(u8), null);
10161016 }
10171017
10181018 /// On success, caller owns returned buffer.
10191019 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
1020 pub fn readFileAllocAligned(
1020 /// Allows specifying alignment and a sentinel value.
1021 pub fn readFileAllocOptions(
10211022 self: Dir,
10221023 allocator: *mem.Allocator,
10231024 file_path: []const u8,
10241025 max_bytes: usize,
1025 comptime A: u29,
1026 ) ![]align(A) u8 {
1026 comptime alignment: u29,
1027 comptime optional_sentinel: ?u8,
1028 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
10271029 var file = try self.openFile(file_path, .{});
10281030 defer file.close();
10291031
10301032 const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);
10311033 if (size > max_bytes) return error.FileTooBig;
10321034
1033 const buf = try allocator.alignedAlloc(u8, A, size);
1035 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
10341036 errdefer allocator.free(buf);
10351037
10361038 try file.inStream().readNoEof(buf);
lib/std/math/big/int.zig+14-12
......@@ -143,12 +143,15 @@ pub const Int = struct {
143143 /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and
144144 /// can be modified separately from the original.
145145 pub fn clone(other: Int) !Int {
146 other.assertWritable();
146 return other.clone2(other.allocator.?);
147 }
148
149 pub fn clone2(other: Int, allocator: *Allocator) !Int {
147150 return Int{
148 .allocator = other.allocator,
151 .allocator = allocator,
149152 .metadata = other.metadata,
150153 .limbs = block: {
151 var limbs = try other.allocator.?.alloc(Limb, other.len());
154 var limbs = try allocator.alloc(Limb, other.len());
152155 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
153156 break :block limbs;
154157 },
......@@ -237,7 +240,7 @@ pub const Int = struct {
237240 return bits;
238241 }
239242
240 fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
243 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
241244 if (self.eqZero()) {
242245 return true;
243246 }
......@@ -470,8 +473,8 @@ pub const Int = struct {
470473 break;
471474 }
472475 }
473 } // Non power-of-two: batch divisions per word size.
474 else {
476 } else {
477 // Non power-of-two: batch divisions per word size.
475478 const digits_per_limb = math.log(Limb, base, maxInt(Limb));
476479 var limb_base: Limb = 1;
477480 var j: usize = 0;
......@@ -479,7 +482,7 @@ pub const Int = struct {
479482 limb_base *= base;
480483 }
481484
482 var q = try self.clone();
485 var q = try self.clone2(allocator);
483486 defer q.deinit();
484487 q.abs();
485488 var r = try Int.init(allocator);
......@@ -522,15 +525,13 @@ pub const Int = struct {
522525
523526 /// To allow `std.fmt.printf` to work with Int.
524527 /// TODO make this non-allocating
528 /// TODO support read-only fixed integers
525529 pub fn format(
526530 self: Int,
527531 comptime fmt: []const u8,
528532 options: std.fmt.FormatOptions,
529533 out_stream: var,
530534 ) !void {
531 self.assertWritable();
532 // TODO support read-only fixed integers
533
534535 comptime var radix = 10;
535536 comptime var uppercase = false;
536537
......@@ -550,8 +551,9 @@ pub const Int = struct {
550551 @compileError("Unknown format string: '" ++ fmt ++ "'");
551552 }
552553
553 const str = self.toString(self.allocator.?, radix, uppercase) catch @panic("TODO make this non allocating");
554 defer self.allocator.?.free(str);
554 var buf: [4096]u8 = undefined;
555 var fba = std.heap.FixedBufferAllocator.init(&buf);
556 const str = self.toString(&fba.allocator, radix, uppercase) catch @panic("TODO make this non allocating");
555557 return out_stream.writeAll(str);
556558 }
557559
lib/std/mem.zig+28-3
......@@ -105,6 +105,31 @@ pub const Allocator = struct {
105105 return self.alignedAlloc(T, null, n);
106106 }
107107
108 pub fn allocWithOptions(
109 self: *Allocator,
110 comptime Elem: type,
111 n: usize,
112 /// null means naturally aligned
113 comptime optional_alignment: ?u29,
114 comptime optional_sentinel: ?Elem,
115 ) Error!AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
116 if (optional_sentinel) |sentinel| {
117 const ptr = try self.alignedAlloc(Elem, optional_alignment, n + 1);
118 ptr[n] = sentinel;
119 return ptr[0..n :sentinel];
120 } else {
121 return self.alignedAlloc(Elem, optional_alignment, n);
122 }
123 }
124
125 fn AllocWithOptionsPayload(comptime Elem: type, comptime alignment: ?u29, comptime sentinel: ?Elem) type {
126 if (sentinel) |s| {
127 return [:s]align(alignment orelse @alignOf(T)) Elem;
128 } else {
129 return []align(alignment orelse @alignOf(T)) Elem;
130 }
131 }
132
108133 /// Allocates an array of `n + 1` items of type `T` and sets the first `n`
109134 /// items to `undefined` and the last item to `sentinel`. Depending on the
110135 /// Allocator implementation, it may be required to call `free` once the
......@@ -113,10 +138,10 @@ pub const Allocator = struct {
113138 /// call `free` when done.
114139 ///
115140 /// For allocating a single item, see `create`.
141 ///
142 /// Deprecated; use `allocWithOptions`.
116143 pub fn allocSentinel(self: *Allocator, comptime Elem: type, n: usize, comptime sentinel: Elem) Error![:sentinel]Elem {
117 var ptr = try self.alloc(Elem, n + 1);
118 ptr[n] = sentinel;
119 return ptr[0..n :sentinel];
144 return self.allocWithOptions(Elem, n, null, sentinel);
120145 }
121146
122147 pub fn alignedAlloc(
lib/std/target.zig+1-1
......@@ -761,7 +761,7 @@ pub const Target = struct {
761761 };
762762 }
763763
764 pub fn ptrBitWidth(arch: Arch) u32 {
764 pub fn ptrBitWidth(arch: Arch) u16 {
765765 switch (arch) {
766766 .avr,
767767 .msp430,
lib/std/zig.zig+2-1
......@@ -2,8 +2,9 @@ const tokenizer = @import("zig/tokenizer.zig");
22pub const Token = tokenizer.Token;
33pub const Tokenizer = tokenizer.Tokenizer;
44pub const parse = @import("zig/parse.zig").parse;
5pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStringLiteral;
5pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
66pub const render = @import("zig/render.zig").render;
7pub const renderStringLiteral = @import("zig/string_literal.zig").render;
78pub const ast = @import("zig/ast.zig");
89pub const system = @import("zig/system.zig");
910pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
lib/std/zig/parse_string_literal.zig deleted-125
......@@ -1,125 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3
4const State = enum {
5 Start,
6 Backslash,
7};
8
9pub const ParseStringLiteralError = error{
10 OutOfMemory,
11
12 /// When this is returned, index will be the position of the character.
13 InvalidCharacter,
14};
15
16/// caller owns returned memory
17pub fn parseStringLiteral(
18 allocator: *std.mem.Allocator,
19 bytes: []const u8,
20 bad_index: *usize, // populated if error.InvalidCharacter is returned
21) ParseStringLiteralError![]u8 {
22 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
23
24 var list = std.ArrayList(u8).init(allocator);
25 errdefer list.deinit();
26
27 const slice = bytes[1..];
28 try list.ensureCapacity(slice.len - 1);
29
30 var state = State.Start;
31 var index: usize = 0;
32 while (index < slice.len) : (index += 1) {
33 const b = slice[index];
34
35 switch (state) {
36 State.Start => switch (b) {
37 '\\' => state = State.Backslash,
38 '\n' => {
39 bad_index.* = index;
40 return error.InvalidCharacter;
41 },
42 '"' => return list.toOwnedSlice(),
43 else => try list.append(b),
44 },
45 State.Backslash => switch (b) {
46 'n' => {
47 try list.append('\n');
48 state = State.Start;
49 },
50 'r' => {
51 try list.append('\r');
52 state = State.Start;
53 },
54 '\\' => {
55 try list.append('\\');
56 state = State.Start;
57 },
58 't' => {
59 try list.append('\t');
60 state = State.Start;
61 },
62 '\'' => {
63 try list.append('\'');
64 state = State.Start;
65 },
66 '"' => {
67 try list.append('"');
68 state = State.Start;
69 },
70 'x' => {
71 // TODO: add more/better/broader tests for this.
72 const index_continue = index + 3;
73 if (slice.len >= index_continue)
74 if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |char| {
75 try list.append(char);
76 state = State.Start;
77 index = index_continue - 1; // loop-header increments again
78 continue;
79 } else |_| {};
80
81 bad_index.* = index;
82 return error.InvalidCharacter;
83 },
84 'u' => {
85 // TODO: add more/better/broader tests for this.
86 if (slice.len > index + 2 and slice[index + 1] == '{')
87 if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| {
88 const hex_str = slice[index + 2 .. index_end];
89 if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| {
90 if (uint <= 0x10ffff) {
91 try list.appendSlice(std.mem.toBytes(uint)[0..]);
92 state = State.Start;
93 index = index_end; // loop-header increments
94 continue;
95 }
96 } else |_| {}
97 };
98
99 bad_index.* = index;
100 return error.InvalidCharacter;
101 },
102 else => {
103 bad_index.* = index;
104 return error.InvalidCharacter;
105 },
106 },
107 else => unreachable,
108 }
109 }
110 unreachable;
111}
112
113test "parseStringLiteral" {
114 const expect = std.testing.expect;
115 const eql = std.mem.eql;
116
117 var fixed_buf_mem: [32]u8 = undefined;
118 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]);
119 var alloc = &fixed_buf_alloc.allocator;
120 var bad_index: usize = undefined;
121
122 expect(eql(u8, "foo", try parseStringLiteral(alloc, "\"foo\"", &bad_index)));
123 expect(eql(u8, "foo", try parseStringLiteral(alloc, "\"f\x6f\x6f\"", &bad_index)));
124 expect(eql(u8, "f💯", try parseStringLiteral(alloc, "\"f\u{1f4af}\"", &bad_index)));
125}
lib/std/zig/string_literal.zig created+155
......@@ -0,0 +1,155 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3
4const State = enum {
5 Start,
6 Backslash,
7};
8
9pub const ParseError = error{
10 OutOfMemory,
11
12 /// When this is returned, index will be the position of the character.
13 InvalidCharacter,
14};
15
16/// caller owns returned memory
17pub fn parse(
18 allocator: *std.mem.Allocator,
19 bytes: []const u8,
20 bad_index: *usize, // populated if error.InvalidCharacter is returned
21) ParseError![]u8 {
22 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
23
24 var list = std.ArrayList(u8).init(allocator);
25 errdefer list.deinit();
26
27 const slice = bytes[1..];
28 try list.ensureCapacity(slice.len - 1);
29
30 var state = State.Start;
31 var index: usize = 0;
32 while (index < slice.len) : (index += 1) {
33 const b = slice[index];
34
35 switch (state) {
36 State.Start => switch (b) {
37 '\\' => state = State.Backslash,
38 '\n' => {
39 bad_index.* = index;
40 return error.InvalidCharacter;
41 },
42 '"' => return list.toOwnedSlice(),
43 else => try list.append(b),
44 },
45 State.Backslash => switch (b) {
46 'n' => {
47 try list.append('\n');
48 state = State.Start;
49 },
50 'r' => {
51 try list.append('\r');
52 state = State.Start;
53 },
54 '\\' => {
55 try list.append('\\');
56 state = State.Start;
57 },
58 't' => {
59 try list.append('\t');
60 state = State.Start;
61 },
62 '\'' => {
63 try list.append('\'');
64 state = State.Start;
65 },
66 '"' => {
67 try list.append('"');
68 state = State.Start;
69 },
70 'x' => {
71 // TODO: add more/better/broader tests for this.
72 const index_continue = index + 3;
73 if (slice.len >= index_continue)
74 if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |char| {
75 try list.append(char);
76 state = State.Start;
77 index = index_continue - 1; // loop-header increments again
78 continue;
79 } else |_| {};
80
81 bad_index.* = index;
82 return error.InvalidCharacter;
83 },
84 'u' => {
85 // TODO: add more/better/broader tests for this.
86 if (slice.len > index + 2 and slice[index + 1] == '{')
87 if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| {
88 const hex_str = slice[index + 2 .. index_end];
89 if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| {
90 if (uint <= 0x10ffff) {
91 try list.appendSlice(std.mem.toBytes(uint)[0..]);
92 state = State.Start;
93 index = index_end; // loop-header increments
94 continue;
95 }
96 } else |_| {}
97 };
98
99 bad_index.* = index;
100 return error.InvalidCharacter;
101 },
102 else => {
103 bad_index.* = index;
104 return error.InvalidCharacter;
105 },
106 },
107 else => unreachable,
108 }
109 }
110 unreachable;
111}
112
113test "parse" {
114 const expect = std.testing.expect;
115 const eql = std.mem.eql;
116
117 var fixed_buf_mem: [32]u8 = undefined;
118 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]);
119 var alloc = &fixed_buf_alloc.allocator;
120 var bad_index: usize = undefined;
121
122 expect(eql(u8, "foo", try parse(alloc, "\"foo\"", &bad_index)));
123 expect(eql(u8, "foo", try parse(alloc, "\"f\x6f\x6f\"", &bad_index)));
124 expect(eql(u8, "f💯", try parse(alloc, "\"f\u{1f4af}\"", &bad_index)));
125}
126
127/// Writes a Zig-syntax escaped string literal to the stream. Includes the double quotes.
128pub fn render(utf8: []const u8, out_stream: var) !void {
129 try out_stream.writeByte('"');
130 for (utf8) |byte| switch (byte) {
131 '\n' => try out_stream.writeAll("\\n"),
132 '\r' => try out_stream.writeAll("\\r"),
133 '\t' => try out_stream.writeAll("\\t"),
134 '\\' => try out_stream.writeAll("\\\\"),
135 '"' => try out_stream.writeAll("\\\""),
136 ' ', '!', '#'...'[', ']'...'~' => try out_stream.writeByte(byte),
137 else => try out_stream.print("\\x{x:0>2}", .{byte}),
138 };
139 try out_stream.writeByte('"');
140}
141
142test "render" {
143 const expect = std.testing.expect;
144 const eql = std.mem.eql;
145
146 var fixed_buf_mem: [32]u8 = undefined;
147
148 {
149 var fbs = std.io.fixedBufferStream(&fixed_buf_mem);
150 try render(" \\ hi \x07 \x11 \" derp", fbs.outStream());
151 expect(eql(u8,
152 \\" \\ hi \x07 \x11 \" derp"
153 , fbs.getWritten()));
154 }
155}
src-self-hosted/c_int.zig deleted-169
......@@ -1,169 +0,0 @@
1const Target = @import("std").Target;
2
3pub const CInt = struct {
4 id: Id,
5 zig_name: []const u8,
6 c_name: []const u8,
7 is_signed: bool,
8
9 pub const Id = enum {
10 Short,
11 UShort,
12 Int,
13 UInt,
14 Long,
15 ULong,
16 LongLong,
17 ULongLong,
18 };
19
20 pub const list = [_]CInt{
21 CInt{
22 .id = .Short,
23 .zig_name = "c_short",
24 .c_name = "short",
25 .is_signed = true,
26 },
27 CInt{
28 .id = .UShort,
29 .zig_name = "c_ushort",
30 .c_name = "unsigned short",
31 .is_signed = false,
32 },
33 CInt{
34 .id = .Int,
35 .zig_name = "c_int",
36 .c_name = "int",
37 .is_signed = true,
38 },
39 CInt{
40 .id = .UInt,
41 .zig_name = "c_uint",
42 .c_name = "unsigned int",
43 .is_signed = false,
44 },
45 CInt{
46 .id = .Long,
47 .zig_name = "c_long",
48 .c_name = "long",
49 .is_signed = true,
50 },
51 CInt{
52 .id = .ULong,
53 .zig_name = "c_ulong",
54 .c_name = "unsigned long",
55 .is_signed = false,
56 },
57 CInt{
58 .id = .LongLong,
59 .zig_name = "c_longlong",
60 .c_name = "long long",
61 .is_signed = true,
62 },
63 CInt{
64 .id = .ULongLong,
65 .zig_name = "c_ulonglong",
66 .c_name = "unsigned long long",
67 .is_signed = false,
68 },
69 };
70
71 pub fn sizeInBits(cint: CInt, self: Target) u32 {
72 const arch = self.cpu.arch;
73 switch (self.os.tag) {
74 .freestanding, .other => switch (self.cpu.arch) {
75 .msp430 => switch (cint.id) {
76 .Short,
77 .UShort,
78 .Int,
79 .UInt,
80 => return 16,
81 .Long,
82 .ULong,
83 => return 32,
84 .LongLong,
85 .ULongLong,
86 => return 64,
87 },
88 else => switch (cint.id) {
89 .Short,
90 .UShort,
91 => return 16,
92 .Int,
93 .UInt,
94 => return 32,
95 .Long,
96 .ULong,
97 => return self.cpu.arch.ptrBitWidth(),
98 .LongLong,
99 .ULongLong,
100 => return 64,
101 },
102 },
103
104 .linux,
105 .macosx,
106 .freebsd,
107 .openbsd,
108 => switch (cint.id) {
109 .Short,
110 .UShort,
111 => return 16,
112 .Int,
113 .UInt,
114 => return 32,
115 .Long,
116 .ULong,
117 => return self.cpu.arch.ptrBitWidth(),
118 .LongLong,
119 .ULongLong,
120 => return 64,
121 },
122
123 .windows, .uefi => switch (cint.id) {
124 .Short,
125 .UShort,
126 => return 16,
127 .Int,
128 .UInt,
129 => return 32,
130 .Long,
131 .ULong,
132 .LongLong,
133 .ULongLong,
134 => return 64,
135 },
136
137 .ananas,
138 .cloudabi,
139 .dragonfly,
140 .fuchsia,
141 .ios,
142 .kfreebsd,
143 .lv2,
144 .netbsd,
145 .solaris,
146 .haiku,
147 .minix,
148 .rtems,
149 .nacl,
150 .cnk,
151 .aix,
152 .cuda,
153 .nvcl,
154 .amdhsa,
155 .ps4,
156 .elfiamcu,
157 .tvos,
158 .watchos,
159 .mesa3d,
160 .contiki,
161 .amdpal,
162 .hermit,
163 .hurd,
164 .wasi,
165 .emscripten,
166 => @panic("TODO specify the C integer type sizes for this OS"),
167 }
168 }
169};
src-self-hosted/ir.zig+556-2406
......@@ -1,2590 +1,740 @@
11const std = @import("std");
2const Compilation = @import("compilation.zig").Compilation;
3const Scope = @import("scope.zig").Scope;
4const ast = std.zig.ast;
2const mem = std.mem;
53const Allocator = std.mem.Allocator;
64const Value = @import("value.zig").Value;
7const Type = Value.Type;
5const Type = @import("type.zig").Type;
86const assert = std.debug.assert;
9const Token = std.zig.Token;
10const Span = @import("errmsg.zig").Span;
11const llvm = @import("llvm.zig");
12const codegen = @import("codegen.zig");
13const ObjectFile = codegen.ObjectFile;
14const Decl = @import("decl.zig").Decl;
15const mem = std.mem;
16
17pub const LVal = enum {
18 None,
19 Ptr,
20};
21
22pub const IrVal = union(enum) {
23 Unknown,
24 KnownType: *Type,
25 KnownValue: *Value,
26
27 const Init = enum {
28 Unknown,
29 NoReturn,
30 Void,
31 };
32
33 pub fn dump(self: IrVal) void {
34 switch (self) {
35 .Unknown => std.debug.warn("Unknown", .{}),
36 .KnownType => |typ| {
37 std.debug.warn("KnownType(", .{});
38 typ.dump();
39 std.debug.warn(")", .{});
40 },
41 .KnownValue => |value| {
42 std.debug.warn("KnownValue(", .{});
43 value.dump();
44 std.debug.warn(")", .{});
45 },
46 }
47 }
48};
49
7const text = @import("ir/text.zig");
8const BigInt = std.math.big.Int;
9const Target = std.Target;
10
11/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
12/// of instructions that correspond to the ZIR text format.
13/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
14/// so are the `Value` and `Type`. The value of a constant must be copied into
15/// a memory location for the value to survive after a const instruction.
5016pub const Inst = struct {
51 id: Id,
52 scope: *Scope,
53 debug_id: usize,
54 val: IrVal,
55 ref_count: usize,
56 span: Span,
57 owner_bb: *BasicBlock,
58
59 /// true if this instruction was generated by zig and not from user code
60 is_generated: bool,
61
62 /// the instruction that is derived from this one in analysis
63 child: ?*Inst,
64
65 /// the instruction that this one derives from in analysis
66 parent: ?*Inst,
67
68 /// populated durign codegen
69 llvm_value: ?*llvm.Value,
17 tag: Tag,
18 ty: Type,
19 /// Byte offset into the source.
20 src: usize,
21
22 pub const Tag = enum {
23 unreach,
24 constant,
25 assembly,
26 ptrtoint,
27 };
7028
7129 pub fn cast(base: *Inst, comptime T: type) ?*T {
72 if (base.id == comptime typeToId(T)) {
73 return @fieldParentPtr(T, "base", base);
74 }
75 return null;
76 }
77
78 pub fn typeToId(comptime T: type) Id {
79 inline for (@typeInfo(Id).Enum.fields) |f| {
80 if (T == @field(Inst, f.name)) {
81 return @field(Id, f.name);
82 }
83 }
84 unreachable;
85 }
86
87 pub fn dump(base: *const Inst) void {
88 inline for (@typeInfo(Id).Enum.fields) |f| {
89 if (base.id == @field(Id, f.name)) {
90 const T = @field(Inst, f.name);
91 std.debug.warn("#{} = {}(", .{ base.debug_id, @tagName(base.id) });
92 @fieldParentPtr(T, "base", base).dump();
93 std.debug.warn(")", .{});
94 return;
95 }
96 }
97 unreachable;
98 }
99
100 pub fn hasSideEffects(base: *const Inst) bool {
101 inline for (@typeInfo(Id).Enum.fields) |f| {
102 if (base.id == @field(Id, f.name)) {
103 const T = @field(Inst, f.name);
104 return @fieldParentPtr(T, "base", base).hasSideEffects();
105 }
106 }
107 unreachable;
108 }
109
110 pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
111 switch (base.id) {
112 .Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
113 .Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
114 .Call => return @fieldParentPtr(Call, "base", base).analyze(ira),
115 .DeclRef => return @fieldParentPtr(DeclRef, "base", base).analyze(ira),
116 .Ref => return @fieldParentPtr(Ref, "base", base).analyze(ira),
117 .DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira),
118 .CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira),
119 .Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira),
120 .Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
121 .AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
122 .PtrType => return @fieldParentPtr(PtrType, "base", base).analyze(ira),
123 .VarPtr => return @fieldParentPtr(VarPtr, "base", base).analyze(ira),
124 .LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).analyze(ira),
125 }
126 }
127
128 pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?*llvm.Value) {
129 switch (base.id) {
130 .Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
131 .Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132 .Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
133 .VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val),
134 .LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val),
135 .DeclRef => unreachable,
136 .PtrType => unreachable,
137 .Ref => @panic("TODO"),
138 .DeclVar => @panic("TODO"),
139 .CheckVoidStmt => @panic("TODO"),
140 .Phi => @panic("TODO"),
141 .Br => @panic("TODO"),
142 .AddImplicitReturnType => @panic("TODO"),
143 }
144 }
145
146 fn ref(base: *Inst, builder: *Builder) void {
147 base.ref_count += 1;
148 if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) {
149 base.owner_bb.ref(builder);
150 }
151 }
152
153 fn copyVal(base: *Inst, comp: *Compilation) !*Value {
154 if (base.parent.?.ref_count == 0) {
155 return base.val.KnownValue.derefAndCopy(comp);
156 }
157 return base.val.KnownValue.copy(comp);
158 }
159
160 fn getAsParam(param: *Inst) !*Inst {
161 param.ref_count -= 1;
162 const child = param.child orelse return error.SemanticAnalysisFailed;
163 switch (child.val) {
164 .Unknown => return error.SemanticAnalysisFailed,
165 else => return child,
166 }
167 }
168
169 fn getConstVal(self: *Inst, ira: *Analyze) !*Value {
170 if (self.isCompTime()) {
171 return self.val.KnownValue;
172 } else {
173 try ira.addCompileError(self.span, "unable to evaluate constant expression", .{});
174 return error.SemanticAnalysisFailed;
175 }
176 }
177
178 fn getAsConstType(param: *Inst, ira: *Analyze) !*Type {
179 const meta_type = Type.MetaType.get(ira.irb.comp);
180 meta_type.base.base.deref(ira.irb.comp);
181
182 const inst = try param.getAsParam();
183 const casted = try ira.implicitCast(inst, &meta_type.base);
184 const val = try casted.getConstVal(ira);
185 return val.cast(Value.Type).?;
186 }
187
188 fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 {
189 return error.Unimplemented;
190 //const align_type = Type.Int.get_align(ira.irb.comp);
191 //align_type.base.base.deref(ira.irb.comp);
192
193 //const inst = try param.getAsParam();
194 //const casted = try ira.implicitCast(inst, align_type);
195 //const val = try casted.getConstVal(ira);
196
197 //uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
198 //if (align_bytes == 0) {
199 // ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
200 // return false;
201 //}
202
203 //if (!is_power_of_2(align_bytes)) {
204 // ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
205 // return false;
206 //}
207 }
208
209 /// asserts that the type is known
210 fn getKnownType(self: *Inst) *Type {
211 switch (self.val) {
212 .KnownType => |typ| return typ,
213 .KnownValue => |value| return value.typ,
214 .Unknown => unreachable,
215 }
216 }
30 if (base.tag != T.base_tag)
31 return null;
21732
218 pub fn setGenerated(base: *Inst) void {
219 base.is_generated = true;
33 return @fieldParentPtr(T, "base", base);
22034 }
22135
222 pub fn isNoReturn(base: *const Inst) bool {
223 switch (base.val) {
224 .Unknown => return false,
225 .KnownValue => |x| return x.typ.id == .NoReturn,
226 .KnownType => |typ| return typ.id == .NoReturn,
227 }
36 pub fn Args(comptime T: type) type {
37 return std.meta.fieldInfo(T, "args").field_type;
22838 }
22939
230 pub fn isCompTime(base: *const Inst) bool {
231 return base.val == .KnownValue;
232 }
40 /// Returns `null` if runtime-known.
41 pub fn value(base: *Inst) ?Value {
42 return switch (base.tag) {
43 .unreach => Value.initTag(.noreturn_value),
44 .constant => base.cast(Constant).?.val,
23345
234 pub fn linkToParent(self: *Inst, parent: *Inst) void {
235 assert(self.parent == null);
236 assert(parent.child == null);
237 self.parent = parent;
238 parent.child = self;
46 .assembly,
47 .ptrtoint,
48 => null,
49 };
23950 }
24051
241 pub const Id = enum {
242 Return,
243 Const,
244 Ref,
245 DeclVar,
246 CheckVoidStmt,
247 Phi,
248 Br,
249 AddImplicitReturnType,
250 Call,
251 DeclRef,
252 PtrType,
253 VarPtr,
254 LoadPtr,
255 };
256
257 pub const Call = struct {
52 pub const Unreach = struct {
53 pub const base_tag = Tag.unreach;
25854 base: Inst,
259 params: Params,
260
261 const Params = struct {
262 fn_ref: *Inst,
263 args: []*Inst,
264 };
265
266 const ir_val_init = IrVal.Init.Unknown;
267
268 pub fn dump(self: *const Call) void {
269 std.debug.warn("#{}(", .{self.params.fn_ref.debug_id});
270 for (self.params.args) |arg| {
271 std.debug.warn("#{},", .{arg.debug_id});
272 }
273 std.debug.warn(")", .{});
274 }
275
276 pub fn hasSideEffects(self: *const Call) bool {
277 return true;
278 }
279
280 pub fn analyze(self: *const Call, ira: *Analyze) !*Inst {
281 const fn_ref = try self.params.fn_ref.getAsParam();
282 const fn_ref_type = fn_ref.getKnownType();
283 const fn_type = fn_ref_type.cast(Type.Fn) orelse {
284 try ira.addCompileError(fn_ref.span, "type '{}' not a function", .{fn_ref_type.name});
285 return error.SemanticAnalysisFailed;
286 };
287
288 const fn_type_param_count = fn_type.paramCount();
289
290 if (fn_type_param_count != self.params.args.len) {
291 try ira.addCompileError(self.base.span, "expected {} arguments, found {}", .{
292 fn_type_param_count,
293 self.params.args.len,
294 });
295 return error.SemanticAnalysisFailed;
296 }
297
298 const args = try ira.irb.arena().alloc(*Inst, self.params.args.len);
299 for (self.params.args) |arg, i| {
300 args[i] = try arg.getAsParam();
301 }
302 const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{
303 .fn_ref = fn_ref,
304 .args = args,
305 });
306 new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type };
307 return new_inst;
308 }
309
310 pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
311 const fn_ref = self.params.fn_ref.llvm_value.?;
312
313 const args = try ofile.arena.alloc(*llvm.Value, self.params.args.len);
314 for (self.params.args) |arg, i| {
315 args[i] = arg.llvm_value.?;
316 }
317
318 const llvm_cc = llvm.CCallConv;
319 const call_attr = llvm.CallAttr.Auto;
320
321 return llvm.BuildCall(
322 ofile.builder,
323 fn_ref,
324 args.ptr,
325 @intCast(c_uint, args.len),
326 llvm_cc,
327 call_attr,
328 "",
329 ) orelse error.OutOfMemory;
330 }
55 args: void,
33156 };
33257
333 pub const Const = struct {
58 pub const Constant = struct {
59 pub const base_tag = Tag.constant;
33460 base: Inst,
335 params: Params,
336
337 const Params = struct {};
338
339 // Use Builder.buildConst* methods, or, after building a Const instruction,
340 // manually set the ir_val field.
341 const ir_val_init = IrVal.Init.Unknown;
342
343 pub fn dump(self: *const Const) void {
344 self.base.val.KnownValue.dump();
345 }
346
347 pub fn hasSideEffects(self: *const Const) bool {
348 return false;
349 }
350
351 pub fn analyze(self: *const Const, ira: *Analyze) !*Inst {
352 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
353 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
354 return new_inst;
355 }
35661
357 pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
358 return self.base.val.KnownValue.getLlvmConst(ofile);
359 }
62 val: Value,
36063 };
36164
362 pub const Return = struct {
65 pub const Assembly = struct {
66 pub const base_tag = Tag.assembly;
36367 base: Inst,
364 params: Params,
365
366 const Params = struct {
367 return_value: *Inst,
368 };
369
370 const ir_val_init = IrVal.Init.NoReturn;
371
372 pub fn dump(self: *const Return) void {
373 std.debug.warn("#{}", .{self.params.return_value.debug_id});
374 }
375
376 pub fn hasSideEffects(self: *const Return) bool {
377 return true;
378 }
379
380 pub fn analyze(self: *const Return, ira: *Analyze) !*Inst {
381 const value = try self.params.return_value.getAsParam();
382 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
383
384 // TODO detect returning local variable address
385
386 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
387 }
38868
389 pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
390 const value = self.params.return_value.llvm_value;
391 const return_type = self.params.return_value.getKnownType();
392
393 if (return_type.handleIsPtr()) {
394 @panic("TODO");
395 } else {
396 _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;
397 }
398 return null;
399 }
69 args: struct {
70 asm_source: []const u8,
71 is_volatile: bool,
72 output: ?[]const u8,
73 inputs: []const []const u8,
74 clobbers: []const []const u8,
75 args: []const *Inst,
76 },
40077 };
40178
402 pub const Ref = struct {
403 base: Inst,
404 params: Params,
405
406 const Params = struct {
407 target: *Inst,
408 mut: Type.Pointer.Mut,
409 volatility: Type.Pointer.Vol,
410 };
411
412 const ir_val_init = IrVal.Init.Unknown;
79 pub const PtrToInt = struct {
80 pub const base_tag = Tag.ptrtoint;
41381
414 pub fn dump(inst: *const Ref) void {}
415
416 pub fn hasSideEffects(inst: *const Ref) bool {
417 return false;
418 }
419
420 pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
421 const target = try self.params.target.getAsParam();
422
423 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
424 return ira.getCompTimeRef(
425 val,
426 Value.Ptr.Mut.CompTimeConst,
427 self.params.mut,
428 self.params.volatility,
429 );
430 }
431
432 const new_inst = try ira.irb.build(Ref, self.base.scope, self.base.span, Params{
433 .target = target,
434 .mut = self.params.mut,
435 .volatility = self.params.volatility,
436 });
437 const elem_type = target.getKnownType();
438 const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
439 .child_type = elem_type,
440 .mut = self.params.mut,
441 .vol = self.params.volatility,
442 .size = .One,
443 .alignment = .Abi,
444 });
445 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
446 // could be a ref of a global, for example
447 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
448 // TODO potentially add an alloca entry here
449 return new_inst;
450 }
451 };
452
453 pub const DeclRef = struct {
45482 base: Inst,
455 params: Params,
456
457 const Params = struct {
458 decl: *Decl,
459 lval: LVal,
460 };
461
462 const ir_val_init = IrVal.Init.Unknown;
463
464 pub fn dump(inst: *const DeclRef) void {}
465
466 pub fn hasSideEffects(inst: *const DeclRef) bool {
467 return false;
468 }
469
470 pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
471 (ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) {
472 error.OutOfMemory => return error.OutOfMemory,
473 else => return error.SemanticAnalysisFailed,
474 };
475 switch (self.params.decl.id) {
476 .CompTime => unreachable,
477 .Var => return error.Unimplemented,
478 .Fn => {
479 const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl);
480 const decl_val = switch (fn_decl.value) {
481 .Unresolved => unreachable,
482 .Fn => |fn_val| &fn_val.base,
483 .FnProto => |fn_proto| &fn_proto.base,
484 };
485 switch (self.params.lval) {
486 .None => {
487 return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val);
488 },
489 .Ptr => return error.Unimplemented,
490 }
491 },
492 }
493 }
83 args: struct {
84 ptr: *Inst,
85 },
49486 };
87};
49588
496 pub const VarPtr = struct {
497 base: Inst,
498 params: Params,
499
500 const Params = struct {
501 var_scope: *Scope.Var,
502 };
503
504 const ir_val_init = IrVal.Init.Unknown;
505
506 pub fn dump(inst: *const VarPtr) void {
507 std.debug.warn("{}", .{inst.params.var_scope.name});
508 }
509
510 pub fn hasSideEffects(inst: *const VarPtr) bool {
511 return false;
512 }
89pub const TypedValue = struct {
90 ty: Type,
91 val: Value,
92};
51393
514 pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
515 switch (self.params.var_scope.data) {
516 .Const => @panic("TODO"),
517 .Param => |param| {
518 const new_inst = try ira.irb.build(
519 Inst.VarPtr,
520 self.base.scope,
521 self.base.span,
522 Inst.VarPtr.Params{ .var_scope = self.params.var_scope },
523 );
524 const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
525 .child_type = param.typ,
526 .mut = .Const,
527 .vol = .Non,
528 .size = .One,
529 .alignment = .Abi,
530 });
531 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
532 return new_inst;
533 },
534 }
535 }
94pub const Module = struct {
95 exports: []Export,
96 errors: []ErrorMsg,
97 arena: std.heap.ArenaAllocator,
98 fns: []Fn,
53699
537 pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) *llvm.Value {
538 switch (self.params.var_scope.data) {
539 .Const => unreachable, // turned into Inst.Const in analyze pass
540 .Param => |param| return param.llvm_value,
541 }
542 }
100 pub const Export = struct {
101 name: []const u8,
102 typed_value: TypedValue,
103 src: usize,
543104 };
544105
545 pub const LoadPtr = struct {
546 base: Inst,
547 params: Params,
548
549 const Params = struct {
550 target: *Inst,
551 };
552
553 const ir_val_init = IrVal.Init.Unknown;
554
555 pub fn dump(inst: *const LoadPtr) void {}
556
557 pub fn hasSideEffects(inst: *const LoadPtr) bool {
558 return false;
559 }
560
561 pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
562 const target = try self.params.target.getAsParam();
563 const target_type = target.getKnownType();
564 if (target_type.id != .Pointer) {
565 try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", .{target_type.name});
566 return error.SemanticAnalysisFailed;
567 }
568 const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type);
569 // if (instr_is_comptime(ptr)) {
570 // if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
571 // ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
572 // {
573 // ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value);
574 // if (pointee->special != ConstValSpecialRuntime) {
575 // IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
576 // source_instruction->source_node, child_type);
577 // copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
578 // result->value.type = child_type;
579 // return result;
580 // }
581 // }
582 // }
583 const new_inst = try ira.irb.build(
584 Inst.LoadPtr,
585 self.base.scope,
586 self.base.span,
587 Inst.LoadPtr.Params{ .target = target },
588 );
589 new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type };
590 return new_inst;
591 }
592
593 pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
594 const child_type = self.base.getKnownType();
595 if (!child_type.hasBits()) {
596 return null;
597 }
598 const ptr = self.params.target.llvm_value.?;
599 const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?;
600
601 return try codegen.getHandleValue(ofile, ptr, ptr_type);
602
603 //uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count;
604 //if (unaligned_bit_count == 0)
605 // return get_handle_value(g, ptr, child_type, ptr_type);
606
607 //bool big_endian = g->is_big_endian;
608
609 //assert(!handle_is_ptr(child_type));
610 //LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
611
612 //uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
613 //uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
614 //uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
615
616 //LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
617 //LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
618
619 //return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
620 }
106 pub const Fn = struct {
107 analysis_status: enum { in_progress, failure, success },
108 body: []*Inst,
109 fn_type: Type,
621110 };
622111
623 pub const PtrType = struct {
624 base: Inst,
625 params: Params,
626
627 const Params = struct {
628 child_type: *Inst,
629 mut: Type.Pointer.Mut,
630 vol: Type.Pointer.Vol,
631 size: Type.Pointer.Size,
632 alignment: ?*Inst,
633 };
634
635 const ir_val_init = IrVal.Init.Unknown;
636
637 pub fn dump(inst: *const PtrType) void {}
638
639 pub fn hasSideEffects(inst: *const PtrType) bool {
640 return false;
641 }
112 pub fn deinit(self: *Module, allocator: *Allocator) void {
113 allocator.free(self.exports);
114 allocator.free(self.errors);
115 self.arena.deinit();
116 self.* = undefined;
117 }
118};
642119
643 pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
644 const child_type = try self.params.child_type.getAsConstType(ira);
645 // if (child_type->id == TypeTableEntryIdUnreachable) {
646 // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
647 // return ira->codegen->builtin_types.entry_invalid;
648 // } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) {
649 // ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
650 // return ira->codegen->builtin_types.entry_invalid;
651 // }
652 const alignment = if (self.params.alignment) |align_inst| blk: {
653 const amt = try align_inst.getAsConstAlign(ira);
654 break :blk Type.Pointer.Align{ .Override = amt };
655 } else blk: {
656 break :blk .Abi;
657 };
658 const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
659 .child_type = child_type,
660 .mut = self.params.mut,
661 .vol = self.params.vol,
662 .size = self.params.size,
663 .alignment = alignment,
664 });
665 ptr_type.base.base.deref(ira.irb.comp);
120pub const ErrorMsg = struct {
121 byte_offset: usize,
122 msg: []const u8,
123};
666124
667 return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base);
668 }
125pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
126 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
127
128 var ctx = Analyze{
129 .allocator = allocator,
130 .arena = std.heap.ArenaAllocator.init(allocator),
131 .old_module = &old_module,
132 .errors = std.ArrayList(ErrorMsg).init(allocator),
133 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
134 .exports = std.ArrayList(Module.Export).init(allocator),
135 .fns = std.ArrayList(Module.Fn).init(allocator),
136 .target = native_info.target,
669137 };
670
671 pub const DeclVar = struct {
672 base: Inst,
673 params: Params,
674
675 const Params = struct {
676 variable: *Variable,
677 };
678
679 const ir_val_init = IrVal.Init.Unknown;
680
681 pub fn dump(inst: *const DeclVar) void {}
682
683 pub fn hasSideEffects(inst: *const DeclVar) bool {
684 return true;
685 }
686
687 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst {
688 return error.Unimplemented; // TODO
689 }
138 defer ctx.errors.deinit();
139 defer ctx.decl_table.deinit();
140 defer ctx.exports.deinit();
141 defer ctx.fns.deinit();
142 errdefer ctx.arena.deinit();
143
144 ctx.analyzeRoot() catch |err| switch (err) {
145 error.AnalysisFail => {
146 assert(ctx.errors.items.len != 0);
147 },
148 else => |e| return e,
690149 };
691
692 pub const CheckVoidStmt = struct {
693 base: Inst,
694 params: Params,
695
696 const Params = struct {
697 target: *Inst,
698 };
699
700 const ir_val_init = IrVal.Init.Unknown;
701
702 pub fn dump(self: *const CheckVoidStmt) void {
703 std.debug.warn("#{}", .{self.params.target.debug_id});
704 }
705
706 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
707 return true;
708 }
709
710 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst {
711 const target = try self.params.target.getAsParam();
712 if (target.getKnownType().id != .Void) {
713 try ira.addCompileError(self.base.span, "expression value is ignored", .{});
714 return error.SemanticAnalysisFailed;
715 }
716 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
717 }
150 return Module{
151 .exports = ctx.exports.toOwnedSlice(),
152 .errors = ctx.errors.toOwnedSlice(),
153 .fns = ctx.fns.toOwnedSlice(),
154 .arena = ctx.arena,
718155 };
156}
719157
720 pub const Phi = struct {
721 base: Inst,
722 params: Params,
723
724 const Params = struct {
725 incoming_blocks: []*BasicBlock,
726 incoming_values: []*Inst,
727 };
728
729 const ir_val_init = IrVal.Init.Unknown;
730
731 pub fn dump(inst: *const Phi) void {}
732
733 pub fn hasSideEffects(inst: *const Phi) bool {
734 return false;
735 }
736
737 pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst {
738 return error.Unimplemented; // TODO
739 }
158const Analyze = struct {
159 allocator: *Allocator,
160 arena: std.heap.ArenaAllocator,
161 old_module: *const text.Module,
162 errors: std.ArrayList(ErrorMsg),
163 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
164 exports: std.ArrayList(Module.Export),
165 fns: std.ArrayList(Module.Fn),
166 target: Target,
167
168 const NewDecl = struct {
169 /// null means a semantic analysis error happened
170 ptr: ?*Inst,
740171 };
741172
742 pub const Br = struct {
743 base: Inst,
744 params: Params,
745
746 const Params = struct {
747 dest_block: *BasicBlock,
748 is_comptime: *Inst,
749 };
750
751 const ir_val_init = IrVal.Init.NoReturn;
752
753 pub fn dump(inst: *const Br) void {}
754
755 pub fn hasSideEffects(inst: *const Br) bool {
756 return true;
757 }
758
759 pub fn analyze(self: *const Br, ira: *Analyze) !*Inst {
760 return error.Unimplemented; // TODO
761 }
173 const NewInst = struct {
174 /// null means a semantic analysis error happened
175 ptr: ?*Inst,
762176 };
763177
764 pub const CondBr = struct {
765 base: Inst,
766 params: Params,
767
768 const Params = struct {
769 condition: *Inst,
770 then_block: *BasicBlock,
771 else_block: *BasicBlock,
772 is_comptime: *Inst,
773 };
774
775 const ir_val_init = IrVal.Init.NoReturn;
776
777 pub fn dump(inst: *const CondBr) void {}
778
779 pub fn hasSideEffects(inst: *const CondBr) bool {
780 return true;
781 }
782
783 pub fn analyze(self: *const CondBr, ira: *Analyze) !*Inst {
784 return error.Unimplemented; // TODO
785 }
178 const Fn = struct {
179 body: std.ArrayList(*Inst),
180 inst_table: std.AutoHashMap(*text.Inst, NewInst),
181 /// Index into Module fns array
182 fn_index: usize,
786183 };
787184
788 pub const AddImplicitReturnType = struct {
789 base: Inst,
790 params: Params,
791
792 pub const Params = struct {
793 target: *Inst,
794 };
795
796 const ir_val_init = IrVal.Init.Unknown;
185 const InnerError = error{ OutOfMemory, AnalysisFail };
797186
798 pub fn dump(inst: *const AddImplicitReturnType) void {
799 std.debug.warn("#{}", .{inst.params.target.debug_id});
800 }
801
802 pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool {
803 return true;
804 }
805
806 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst {
807 const target = try self.params.target.getAsParam();
808 try ira.src_implicit_return_type_list.append(target);
809 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
810 }
811 };
812
813 pub const TestErr = struct {
814 base: Inst,
815 params: Params,
816
817 pub const Params = struct {
818 target: *Inst,
819 };
820
821 const ir_val_init = IrVal.Init.Unknown;
822
823 pub fn dump(inst: *const TestErr) void {
824 std.debug.warn("#{}", .{inst.params.target.debug_id});
187 fn analyzeRoot(self: *Analyze) !void {
188 for (self.old_module.decls) |decl| {
189 if (decl.cast(text.Inst.Export)) |export_inst| {
190 try analyzeExport(self, null, export_inst);
191 }
825192 }
193 }
826194
827 pub fn hasSideEffects(inst: *const TestErr) bool {
828 return false;
195 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
196 if (opt_func) |func| {
197 if (func.inst_table.get(old_inst)) |kv| {
198 return kv.value.ptr orelse return error.AnalysisFail;
199 }
829200 }
830201
831 pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst {
832 const target = try self.params.target.getAsParam();
833 const target_type = target.getKnownType();
834 switch (target_type.id) {
835 .ErrorUnion => {
836 return error.Unimplemented;
837 // if (instr_is_comptime(value)) {
838 // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
839 // if (!err_union_val)
840 // return ira->codegen->builtin_types.entry_invalid;
841
842 // if (err_union_val->special != ConstValSpecialRuntime) {
843 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
844 // out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
845 // return ira->codegen->builtin_types.entry_bool;
846 // }
847 // }
848
849 // TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
850 // if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
851 // return ira->codegen->builtin_types.entry_invalid;
852 // }
853 // if (!type_is_global_error_set(err_set_type) &&
854 // err_set_type->data.error_set.err_count == 0)
855 // {
856 // assert(err_set_type->data.error_set.infer_fn == nullptr);
857 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
858 // out_val->data.x_bool = false;
859 // return ira->codegen->builtin_types.entry_bool;
860 // }
861
862 // ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
863 // return ira->codegen->builtin_types.entry_bool;
864 },
865 .ErrorSet => {
866 return ira.irb.buildConstBool(self.base.scope, self.base.span, true);
867 },
868 else => {
869 return ira.irb.buildConstBool(self.base.scope, self.base.span, false);
202 if (self.decl_table.get(old_inst)) |kv| {
203 return kv.value.ptr orelse return error.AnalysisFail;
204 } else {
205 const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) {
206 error.AnalysisFail => {
207 try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
208 return error.AnalysisFail;
870209 },
871 }
210 else => |e| return e,
211 };
212 try self.decl_table.putNoClobber(old_inst, .{ .ptr = new_inst });
213 return new_inst;
872214 }
873 };
215 }
874216
875 pub const TestCompTime = struct {
876 base: Inst,
877 params: Params,
217 fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn {
218 return func orelse return self.fail(src, "instruction illegal outside function body", .{});
219 }
878220
879 pub const Params = struct {
880 target: *Inst,
221 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {
222 const new_inst = try self.resolveInst(func, old_inst);
223 const val = try self.resolveConstValue(new_inst);
224 return TypedValue{
225 .ty = new_inst.ty,
226 .val = val,
881227 };
882
883 const ir_val_init = IrVal.Init.Unknown;
884
885 pub fn dump(inst: *const TestCompTime) void {
886 std.debug.warn("#{}", .{inst.params.target.debug_id});
887 }
888
889 pub fn hasSideEffects(inst: *const TestCompTime) bool {
890 return false;
891 }
892
893 pub fn analyze(self: *const TestCompTime, ira: *Analyze) !*Inst {
894 const target = try self.params.target.getAsParam();
895 return ira.irb.buildConstBool(self.base.scope, self.base.span, target.isCompTime());
896 }
897 };
898
899 pub const SaveErrRetAddr = struct {
900 base: Inst,
901 params: Params,
902
903 const Params = struct {};
904
905 const ir_val_init = IrVal.Init.Unknown;
906
907 pub fn dump(inst: *const SaveErrRetAddr) void {}
908
909 pub fn hasSideEffects(inst: *const SaveErrRetAddr) bool {
910 return true;
911 }
912
913 pub fn analyze(self: *const SaveErrRetAddr, ira: *Analyze) !*Inst {
914 return ira.irb.build(Inst.SaveErrRetAddr, self.base.scope, self.base.span, Params{});
915 }
916 };
917};
918
919pub const Variable = struct {
920 child_scope: *Scope,
921};
922
923pub const BasicBlock = struct {
924 ref_count: usize,
925 name_hint: [*:0]const u8,
926 debug_id: usize,
927 scope: *Scope,
928 instruction_list: std.ArrayList(*Inst),
929 ref_instruction: ?*Inst,
930
931 /// for codegen
932 llvm_block: *llvm.BasicBlock,
933 llvm_exit_block: *llvm.BasicBlock,
934
935 /// the basic block that is derived from this one in analysis
936 child: ?*BasicBlock,
937
938 /// the basic block that this one derives from in analysis
939 parent: ?*BasicBlock,
940
941 pub fn ref(self: *BasicBlock, builder: *Builder) void {
942 self.ref_count += 1;
943228 }
944229
945 pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void {
946 assert(self.parent == null);
947 assert(parent.child == null);
948 self.parent = parent;
949 parent.child = self;
230 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
231 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
950232 }
951};
952233
953/// Stuff that survives longer than Builder
954pub const Code = struct {
955 basic_block_list: std.ArrayList(*BasicBlock),
956 arena: std.heap.ArenaAllocator,
957 return_type: ?*Type,
958 tree_scope: *Scope.AstTree,
234 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
235 const new_inst = try self.resolveInst(func, old_inst);
236 const wanted_type = Type.initTag(.const_slice_u8);
237 const coerced_inst = try self.coerce(wanted_type, new_inst);
238 const val = try self.resolveConstValue(coerced_inst);
239 return val.toAllocatedBytes(&self.arena.allocator);
240 }
959241
960 /// allocator is comp.gpa()
961 pub fn destroy(self: *Code, allocator: *Allocator) void {
962 self.arena.deinit();
963 allocator.destroy(self);
242 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
243 const new_inst = try self.resolveInst(func, old_inst);
244 const wanted_type = Type.initTag(.@"type");
245 const coerced_inst = try self.coerce(wanted_type, new_inst);
246 const val = try self.resolveConstValue(coerced_inst);
247 return val.toType();
964248 }
965249
966 pub fn dump(self: *Code) void {
967 var bb_i: usize = 0;
968 for (self.basic_block_list.span()) |bb| {
969 std.debug.warn("{s}_{}:\n", .{ bb.name_hint, bb.debug_id });
970 for (bb.instruction_list.span()) |instr| {
971 std.debug.warn(" ", .{});
972 instr.dump();
973 std.debug.warn("\n", .{});
974 }
250 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {
251 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);
252 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);
253
254 switch (typed_value.ty.zigTypeTag()) {
255 .Fn => {},
256 else => return self.fail(
257 export_inst.positionals.value.src,
258 "unable to export type '{}'",
259 .{typed_value.ty},
260 ),
975261 }
262 try self.exports.append(.{
263 .name = symbol_name,
264 .typed_value = typed_value,
265 .src = export_inst.base.src,
266 });
976267 }
977268
978 /// returns a ref-incremented value, or adds a compile error
979 pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value {
980 const bb = self.basic_block_list.at(0);
981 for (bb.instruction_list.span()) |inst| {
982 if (inst.cast(Inst.Return)) |ret_inst| {
983 const ret_value = ret_inst.params.return_value;
984 if (ret_value.isCompTime()) {
985 return ret_value.val.KnownValue.getRef();
986 }
987 try comp.addCompileError(
988 self.tree_scope,
989 ret_value.span,
990 "unable to evaluate constant expression",
991 .{},
992 );
993 return error.SemanticAnalysisFailed;
994 } else if (inst.hasSideEffects()) {
995 try comp.addCompileError(
996 self.tree_scope,
997 inst.span,
998 "unable to evaluate constant expression",
999 .{},
1000 );
1001 return error.SemanticAnalysisFailed;
1002 }
1003 }
1004 unreachable;
269 /// TODO should not need the cast on the last parameter at the callsites
270 fn addNewInstArgs(
271 self: *Analyze,
272 func: *Fn,
273 src: usize,
274 ty: Type,
275 comptime T: type,
276 args: Inst.Args(T),
277 ) !*Inst {
278 const inst = try self.addNewInst(func, src, ty, T);
279 inst.args = args;
280 return &inst.base;
1005281 }
1006};
1007282
1008pub const Builder = struct {
1009 comp: *Compilation,
1010 code: *Code,
1011 current_basic_block: *BasicBlock,
1012 next_debug_id: usize,
1013 is_comptime: bool,
1014 is_async: bool,
1015 begin_scope: ?*Scope,
1016
1017 pub const Error = Analyze.Error;
1018
1019 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
1020 const code = try comp.gpa().create(Code);
1021 code.* = Code{
1022 .basic_block_list = undefined,
1023 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
1024 .return_type = null,
1025 .tree_scope = tree_scope,
1026 };
1027 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
1028 errdefer code.destroy(comp.gpa());
1029
1030 return Builder{
1031 .comp = comp,
1032 .current_basic_block = undefined,
1033 .code = code,
1034 .next_debug_id = 0,
1035 .is_comptime = false,
1036 .is_async = false,
1037 .begin_scope = begin_scope,
283 fn addNewInst(self: *Analyze, func: *Fn, src: usize, ty: Type, comptime T: type) !*T {
284 const inst = try self.arena.allocator.create(T);
285 inst.* = .{
286 .base = .{
287 .tag = T.base_tag,
288 .ty = ty,
289 .src = src,
290 },
291 .args = undefined,
1038292 };
293 try func.body.append(&inst.base);
294 return inst;
1039295 }
1040296
1041 pub fn abort(self: *Builder) void {
1042 self.code.destroy(self.comp.gpa());
297 fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst {
298 const const_inst = try self.arena.allocator.create(Inst.Constant);
299 const_inst.* = .{
300 .base = .{
301 .tag = Inst.Constant.base_tag,
302 .ty = typed_value.ty,
303 .src = src,
304 },
305 .val = typed_value.val,
306 };
307 return &const_inst.base;
1043308 }
1044309
1045 /// Call code.destroy() when done
1046 pub fn finish(self: *Builder) *Code {
1047 return self.code;
1048 }
310 fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst {
311 const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);
312 array_payload.* = .{ .len = str.len };
1049313
1050 /// No need to clean up resources thanks to the arena allocator.
1051 pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock {
1052 const basic_block = try self.arena().create(BasicBlock);
1053 basic_block.* = BasicBlock{
1054 .ref_count = 0,
1055 .name_hint = name_hint,
1056 .debug_id = self.next_debug_id,
1057 .scope = scope,
1058 .instruction_list = std.ArrayList(*Inst).init(self.arena()),
1059 .child = null,
1060 .parent = null,
1061 .ref_instruction = null,
1062 .llvm_block = undefined,
1063 .llvm_exit_block = undefined,
1064 };
1065 self.next_debug_id += 1;
1066 return basic_block;
1067 }
314 const ty_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer);
315 ty_payload.* = .{ .pointee_type = Type.initPayload(&array_payload.base) };
1068316
1069 pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void {
1070 try self.code.basic_block_list.append(basic_block);
1071 self.setCursorAtEnd(basic_block);
1072 }
317 const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes);
318 bytes_payload.* = .{ .data = str };
1073319
1074 pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void {
1075 self.current_basic_block = basic_block;
320 return self.constInst(src, .{
321 .ty = Type.initPayload(&ty_payload.base),
322 .val = Value.initPayload(&bytes_payload.base),
323 });
1076324 }
1077325
1078 pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
1079 const alloc = irb.comp.gpa();
1080 var frame = try alloc.create(@Frame(genNode));
1081 defer alloc.destroy(frame);
1082 frame.* = async irb.genNode(node, scope, lval);
1083 return await frame;
326 fn constType(self: *Analyze, src: usize, ty: Type) !*Inst {
327 return self.constInst(src, .{
328 .ty = Type.initTag(.type),
329 .val = try ty.toValue(&self.arena.allocator),
330 });
1084331 }
1085332
1086 pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
1087 switch (node.id) {
1088 .Root => unreachable,
1089 .Use => unreachable,
1090 .TestDecl => unreachable,
1091 .VarDecl => return error.Unimplemented,
1092 .Defer => return error.Unimplemented,
1093 .InfixOp => return error.Unimplemented,
1094 .PrefixOp => {
1095 const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node);
1096 switch (prefix_op.op) {
1097 .AddressOf => return error.Unimplemented,
1098 .ArrayType => |n| return error.Unimplemented,
1099 .Await => return error.Unimplemented,
1100 .BitNot => return error.Unimplemented,
1101 .BoolNot => return error.Unimplemented,
1102 .OptionalType => return error.Unimplemented,
1103 .Negation => return error.Unimplemented,
1104 .NegationWrap => return error.Unimplemented,
1105 .Resume => return error.Unimplemented,
1106 .PtrType => |ptr_info| {
1107 const inst = try irb.genPtrType(prefix_op, ptr_info, scope);
1108 return irb.lvalWrap(scope, inst, lval);
1109 },
1110 .SliceType => |ptr_info| return error.Unimplemented,
1111 .Try => return error.Unimplemented,
1112 }
1113 },
1114 .SuffixOp => {
1115 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
1116 switch (suffix_op.op) {
1117 .Call => |*call| {
1118 const inst = try irb.genCall(suffix_op, call, scope);
1119 return irb.lvalWrap(scope, inst, lval);
1120 },
1121 .ArrayAccess => |n| return error.Unimplemented,
1122 .Slice => |slice| return error.Unimplemented,
1123 .ArrayInitializer => |init_list| return error.Unimplemented,
1124 .StructInitializer => |init_list| return error.Unimplemented,
1125 .Deref => return error.Unimplemented,
1126 .UnwrapOptional => return error.Unimplemented,
1127 }
1128 },
1129 .Switch => return error.Unimplemented,
1130 .While => return error.Unimplemented,
1131 .For => return error.Unimplemented,
1132 .If => return error.Unimplemented,
1133 .ControlFlowExpression => {
1134 const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node);
1135 return irb.genControlFlowExpr(control_flow_expr, scope, lval);
1136 },
1137 .Suspend => return error.Unimplemented,
1138 .VarType => return error.Unimplemented,
1139 .ErrorType => return error.Unimplemented,
1140 .FnProto => return error.Unimplemented,
1141 .AnyFrameType => return error.Unimplemented,
1142 .IntegerLiteral => {
1143 const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node);
1144 return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval);
1145 },
1146 .FloatLiteral => return error.Unimplemented,
1147 .StringLiteral => {
1148 const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node);
1149 const inst = try irb.genStrLit(str_lit, scope);
1150 return irb.lvalWrap(scope, inst, lval);
1151 },
1152 .MultilineStringLiteral => return error.Unimplemented,
1153 .CharLiteral => return error.Unimplemented,
1154 .BoolLiteral => return error.Unimplemented,
1155 .NullLiteral => return error.Unimplemented,
1156 .UndefinedLiteral => return error.Unimplemented,
1157 .Unreachable => return error.Unimplemented,
1158 .Identifier => {
1159 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node);
1160 return irb.genIdentifier(identifier, scope, lval);
1161 },
1162 .GroupedExpression => {
1163 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
1164 return irb.genNodeRecursive(grouped_expr.expr, scope, lval);
1165 },
1166 .BuiltinCall => return error.Unimplemented,
1167 .ErrorSetDecl => return error.Unimplemented,
1168 .ContainerDecl => return error.Unimplemented,
1169 .Asm => return error.Unimplemented,
1170 .Comptime => return error.Unimplemented,
1171 .Block => {
1172 const block = @fieldParentPtr(ast.Node.Block, "base", node);
1173 const inst = try irb.genBlock(block, scope);
1174 return irb.lvalWrap(scope, inst, lval);
1175 },
1176 .DocComment => return error.Unimplemented,
1177 .SwitchCase => return error.Unimplemented,
1178 .SwitchElse => return error.Unimplemented,
1179 .Else => return error.Unimplemented,
1180 .Payload => return error.Unimplemented,
1181 .PointerPayload => return error.Unimplemented,
1182 .PointerIndexPayload => return error.Unimplemented,
1183 .ContainerField => return error.Unimplemented,
1184 .ErrorTag => return error.Unimplemented,
1185 .AsmInput => return error.Unimplemented,
1186 .AsmOutput => return error.Unimplemented,
1187 .ParamDecl => return error.Unimplemented,
1188 .FieldInitializer => return error.Unimplemented,
1189 .EnumLiteral => return error.Unimplemented,
1190 .Noasync => return error.Unimplemented,
1191 }
333 fn constVoid(self: *Analyze, src: usize) !*Inst {
334 return self.constInst(src, .{
335 .ty = Type.initTag(.void),
336 .val = Value.initTag(.void_value),
337 });
1192338 }
1193339
1194 fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
1195 const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None);
1196
1197 const args = try irb.arena().alloc(*Inst, call.params.len);
1198 var it = call.params.iterator(0);
1199 var i: usize = 0;
1200 while (it.next()) |arg_node_ptr| : (i += 1) {
1201 args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None);
1202 }
340 fn constIntUnsigned(self: *Analyze, src: usize, ty: Type, int: u64) !*Inst {
341 const int_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
342 int_payload.* = .{ .int = int };
1203343
1204 //bool is_async = node->data.fn_call_expr.is_async;
1205 //IrInstruction *async_allocator = nullptr;
1206 //if (is_async) {
1207 // if (node->data.fn_call_expr.async_allocator) {
1208 // async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
1209 // if (async_allocator == irb->codegen->invalid_instruction)
1210 // return async_allocator;
1211 // }
1212 //}
1213
1214 return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{
1215 .fn_ref = fn_ref,
1216 .args = args,
344 return self.constInst(src, .{
345 .ty = ty,
346 .val = Value.initPayload(&int_payload.base),
1217347 });
1218 //IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr);
1219 //return ir_lval_wrap(irb, scope, fn_call, lval);
1220348 }
1221349
1222 fn genPtrType(
1223 irb: *Builder,
1224 prefix_op: *ast.Node.PrefixOp,
1225 ptr_info: ast.Node.PrefixOp.PtrInfo,
1226 scope: *Scope,
1227 ) !*Inst {
1228 // TODO port more logic
1229
1230 //assert(node->type == NodeTypePointerType);
1231 //PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
1232 // node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
1233 //bool is_const = node->data.pointer_type.is_const;
1234 //bool is_volatile = node->data.pointer_type.is_volatile;
1235 //AstNode *expr_node = node->data.pointer_type.op_expr;
1236 //AstNode *align_expr = node->data.pointer_type.align_expr;
1237
1238 //IrInstruction *align_value;
1239 //if (align_expr != nullptr) {
1240 // align_value = ir_gen_node(irb, align_expr, scope);
1241 // if (align_value == irb->codegen->invalid_instruction)
1242 // return align_value;
1243 //} else {
1244 // align_value = nullptr;
1245 //}
1246 const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None);
1247
1248 //uint32_t bit_offset_start = 0;
1249 //if (node->data.pointer_type.bit_offset_start != nullptr) {
1250 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
1251 // Buf *val_buf = buf_alloc();
1252 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
1253 // exec_add_error_node(irb->codegen, irb->exec, node,
1254 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1255 // return irb->codegen->invalid_instruction;
1256 // }
1257 // bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
1258 //}
1259
1260 //uint32_t bit_offset_end = 0;
1261 //if (node->data.pointer_type.bit_offset_end != nullptr) {
1262 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) {
1263 // Buf *val_buf = buf_alloc();
1264 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10);
1265 // exec_add_error_node(irb->codegen, irb->exec, node,
1266 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1267 // return irb->codegen->invalid_instruction;
1268 // }
1269 // bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end);
1270 //}
1271
1272 //if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) {
1273 // exec_add_error_node(irb->codegen, irb->exec, node,
1274 // buf_sprintf("bit offset start must be less than bit offset end"));
1275 // return irb->codegen->invalid_instruction;
1276 //}
1277
1278 return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{
1279 .child_type = child_type,
1280 .mut = .Mut,
1281 .vol = .Non,
1282 .size = .Many,
1283 .alignment = null,
1284 });
1285 }
350 fn constIntSigned(self: *Analyze, src: usize, ty: Type, int: i64) !*Inst {
351 const int_payload = try self.arena.allocator.create(Value.Payload.Int_i64);
352 int_payload.* = .{ .int = int };
1286353
1287 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
1288 if (irb.is_comptime)
1289 return true;
1290
1291 var scope = target_scope;
1292 while (true) {
1293 switch (scope.id) {
1294 .CompTime => return true,
1295 .FnDef => return false,
1296 .Decls => unreachable,
1297 .Root => unreachable,
1298 .AstTree => unreachable,
1299 .Block,
1300 .Defer,
1301 .DeferExpr,
1302 .Var,
1303 => scope = scope.parent.?,
1304 }
1305 }
354 return self.constInst(src, .{
355 .ty = ty,
356 .val = Value.initPayload(&int_payload.base),
357 });
1306358 }
1307359
1308 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1309 const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);
1310
1311 var base: u8 = undefined;
1312 var rest: []const u8 = undefined;
1313 if (int_token.len >= 3 and int_token[0] == '0') {
1314 rest = int_token[2..];
1315 switch (int_token[1]) {
1316 'b' => base = 2,
1317 'o' => base = 8,
1318 'x' => base = 16,
1319 else => {
1320 base = 10;
1321 rest = int_token;
1322 },
360 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst {
361 if (big_int.isPositive()) {
362 if (big_int.to(u64)) |x| {
363 return self.constIntUnsigned(src, ty, x);
364 } else |err| switch (err) {
365 error.NegativeIntoUnsigned => unreachable,
366 error.TargetTooSmall => {}, // handled below
1323367 }
1324368 } else {
1325 base = 10;
1326 rest = int_token;
369 if (big_int.to(i64)) |x| {
370 return self.constIntSigned(src, ty, x);
371 } else |err| switch (err) {
372 error.NegativeIntoUnsigned => unreachable,
373 error.TargetTooSmall => {}, // handled below
374 }
1327375 }
1328376
1329 const comptime_int_type = Type.ComptimeInt.get(irb.comp);
1330 defer comptime_int_type.base.base.deref(irb.comp);
1331
1332 const int_val = Value.Int.createFromString(
1333 irb.comp,
1334 &comptime_int_type.base,
1335 base,
1336 rest,
1337 ) catch |err| switch (err) {
1338 error.OutOfMemory => return error.OutOfMemory,
1339 error.InvalidBase => unreachable,
1340 error.InvalidCharForDigit => unreachable,
1341 error.DigitTooLargeForBase => unreachable,
1342 };
1343 errdefer int_val.base.deref(irb.comp);
377 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig);
378 big_int_payload.* = .{ .big_int = big_int };
1344379
1345 const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{});
1346 inst.val = IrVal{ .KnownValue = &int_val.base };
1347 return inst;
380 return self.constInst(src, .{
381 .ty = ty,
382 .val = Value.initPayload(&big_int_payload.base),
383 });
1348384 }
1349385
1350 pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1351 const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
1352 const src_span = Span.token(str_lit.token);
1353
1354 var bad_index: usize = undefined;
1355 var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) {
1356 error.OutOfMemory => return error.OutOfMemory,
1357 error.InvalidCharacter => {
1358 try irb.comp.addCompileError(
1359 irb.code.tree_scope,
1360 src_span,
1361 "invalid character in string literal: '{c}'",
1362 .{str_token[bad_index]},
1363 );
1364 return error.SemanticAnalysisFailed;
386 fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
387 switch (old_inst.tag) {
388 .str => {
389 // We can use this reference because Inst.Const's Value is arena-allocated.
390 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
391 const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;
392 return self.constStr(old_inst.src, bytes);
1365393 },
1366 };
1367 var buf_cleaned = false;
1368 errdefer if (!buf_cleaned) irb.comp.gpa().free(buf);
1369
1370 if (str_token[0] == 'c') {
1371 // first we add a null
1372 buf = try irb.comp.gpa().realloc(buf, buf.len + 1);
1373 buf[buf.len - 1] = 0;
1374
1375 // next make an array value
1376 const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf);
1377 buf_cleaned = true;
1378 defer array_val.base.deref(irb.comp);
1379
1380 // then make a pointer value pointing at the first element
1381 const ptr_val = try Value.Ptr.createArrayElemPtr(
1382 irb.comp,
1383 array_val,
1384 .Const,
1385 .Many,
1386 0,
1387 );
1388 defer ptr_val.base.deref(irb.comp);
1389
1390 return irb.buildConstValue(scope, src_span, &ptr_val.base);
1391 } else {
1392 const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf);
1393 buf_cleaned = true;
1394 defer array_val.base.deref(irb.comp);
1395
1396 return irb.buildConstValue(scope, src_span, &array_val.base);
394 .int => {
395 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
396 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
397 },
398 .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?),
399 .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?),
400 .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?),
401 .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?),
402 .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?),
403 .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?),
404 .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),
405 .@"export" => {
406 try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?);
407 return self.constVoid(old_inst.src);
408 },
409 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
410 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
411 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
1397412 }
1398413 }
1399414
1400 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
1401 const block_scope = try Scope.Block.create(irb.comp, parent_scope);
415 fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {
416 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
1402417
1403 const outer_block_scope = &block_scope.base;
1404 var child_scope = outer_block_scope;
1405
1406 if (parent_scope.findFnDef()) |fndef_scope| {
1407 if (fndef_scope.fn_val.?.block_scope == null) {
1408 fndef_scope.fn_val.?.block_scope = block_scope;
1409 }
1410 }
1411
1412 if (block.statements.len == 0) {
1413 // {}
1414 return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);
1415 }
418 var new_func: Fn = .{
419 .body = std.ArrayList(*Inst).init(self.allocator),
420 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
421 .fn_index = self.fns.items.len,
422 };
423 defer new_func.body.deinit();
424 defer new_func.inst_table.deinit();
425 // Don't hang on to a reference to this when analyzing body instructions, since the memory
426 // could become invalid.
427 (try self.fns.addOne()).* = .{
428 .analysis_status = .in_progress,
429 .fn_type = fn_type,
430 .body = undefined,
431 };
1416432
1417 if (block.label) |label| {
1418 block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena());
1419 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
1420 block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");
1421 block_scope.is_comptime = try irb.buildConstBool(
1422 parent_scope,
1423 Span.token(block.lbrace),
1424 irb.isCompTime(parent_scope),
1425 );
433 for (fn_inst.positionals.body.instructions) |src_inst| {
434 const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| {
435 self.fns.items[new_func.fn_index].analysis_status = .failure;
436 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
437 return err;
438 };
439 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
1426440 }
1427441
1428 var is_continuation_unreachable = false;
1429 var noreturn_return_value: ?*Inst = null;
1430
1431 var stmt_it = block.statements.iterator(0);
1432 while (stmt_it.next()) |statement_node_ptr| {
1433 const statement_node = statement_node_ptr.*;
1434
1435 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
1436 // defer starts a new scope
1437 const defer_token = irb.code.tree_scope.tree.tokens.at(defer_node.defer_token);
1438 const kind = switch (defer_token.id) {
1439 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
1440 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
1441 else => unreachable,
1442 };
1443 const defer_expr_scope = try Scope.DeferExpr.create(irb.comp, parent_scope, defer_node.expr);
1444 const defer_child_scope = try Scope.Defer.create(irb.comp, parent_scope, kind, defer_expr_scope);
1445 child_scope = &defer_child_scope.base;
1446 continue;
1447 }
1448 const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None);
442 const f = &self.fns.items[new_func.fn_index];
443 f.analysis_status = .success;
444 f.body = new_func.body.toOwnedSlice();
1449445
1450 is_continuation_unreachable = statement_value.isNoReturn();
1451 if (is_continuation_unreachable) {
1452 // keep the last noreturn statement value around in case we need to return it
1453 noreturn_return_value = statement_value;
1454 }
446 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
447 fn_payload.* = .{ .index = new_func.fn_index };
1455448
1456 if (statement_value.cast(Inst.DeclVar)) |decl_var| {
1457 // variable declarations start a new scope
1458 child_scope = decl_var.params.variable.child_scope;
1459 } else if (!is_continuation_unreachable) {
1460 // this statement's value must be void
1461 _ = try irb.build(
1462 Inst.CheckVoidStmt,
1463 child_scope,
1464 Span{
1465 .first = statement_node.firstToken(),
1466 .last = statement_node.lastToken(),
1467 },
1468 Inst.CheckVoidStmt.Params{ .target = statement_value },
1469 );
1470 }
1471 }
449 return self.constInst(fn_inst.base.src, .{
450 .ty = fn_type,
451 .val = Value.initPayload(&fn_payload.base),
452 });
453 }
1472454
1473 if (is_continuation_unreachable) {
1474 assert(noreturn_return_value != null);
1475 if (block.label == null or block_scope.incoming_blocks.len == 0) {
1476 return noreturn_return_value.?;
1477 }
455 fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {
456 const return_type = try self.resolveType(func, fntype.positionals.return_type);
1478457
1479 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
1480 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
1481 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
1482 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
1483 });
458 if (return_type.zigTypeTag() == .NoReturn and
459 fntype.positionals.param_types.len == 0 and
460 fntype.kw_args.cc == .Naked)
461 {
462 return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
1484463 }
1485464
1486 if (block.label) |label| {
1487 try block_scope.incoming_blocks.append(irb.current_basic_block);
1488 try block_scope.incoming_values.append(
1489 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
1490 );
1491 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit);
465 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
466 }
1492467
1493 _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{
1494 .dest_block = block_scope.end_block,
1495 .is_comptime = block_scope.is_comptime,
1496 });
468 fn analyzeInstPrimitive(self: *Analyze, func: ?*Fn, primitive: *text.Inst.Primitive) InnerError!*Inst {
469 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
470 }
1497471
1498 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
472 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {
473 const dest_type = try self.resolveType(func, as.positionals.dest_type);
474 const new_inst = try self.resolveInst(func, as.positionals.value);
475 return self.coerce(dest_type, new_inst);
476 }
1499477
1500 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
1501 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
1502 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
1503 });
478 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
479 const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr);
480 if (ptr.ty.zigTypeTag() != .Pointer) {
481 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
1504482 }
1505
1506 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit);
1507 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
483 // TODO handle known-pointer-address
484 const f = try self.requireFunctionBody(func, ptrtoint.base.src);
485 const ty = Type.initTag(.usize);
486 return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
1508487 }
1509488
1510 pub fn genControlFlowExpr(
1511 irb: *Builder,
1512 control_flow_expr: *ast.Node.ControlFlowExpression,
1513 scope: *Scope,
1514 lval: LVal,
1515 ) !*Inst {
1516 switch (control_flow_expr.kind) {
1517 .Break => |arg| return error.Unimplemented,
1518 .Continue => |arg| return error.Unimplemented,
1519 .Return => {
1520 const src_span = Span.token(control_flow_expr.ltoken);
1521 if (scope.findFnDef() == null) {
1522 try irb.comp.addCompileError(
1523 irb.code.tree_scope,
1524 src_span,
1525 "return expression outside function definition",
1526 .{},
1527 );
1528 return error.SemanticAnalysisFailed;
1529 }
1530
1531 if (scope.findDeferExpr()) |scope_defer_expr| {
1532 if (!scope_defer_expr.reported_err) {
1533 try irb.comp.addCompileError(
1534 irb.code.tree_scope,
1535 src_span,
1536 "cannot return from defer expression",
1537 .{},
1538 );
1539 scope_defer_expr.reported_err = true;
1540 }
1541 return error.SemanticAnalysisFailed;
1542 }
1543
1544 const outer_scope = irb.begin_scope.?;
1545 const return_value = if (control_flow_expr.rhs) |rhs| blk: {
1546 break :blk try irb.genNodeRecursive(rhs, scope, .None);
1547 } else blk: {
1548 break :blk try irb.buildConstVoid(scope, src_span, true);
1549 };
1550
1551 const defer_counts = irb.countDefers(scope, outer_scope);
1552 const have_err_defers = defer_counts.error_exit != 0;
1553 if (have_err_defers or irb.comp.have_err_ret_tracing) {
1554 const err_block = try irb.createBasicBlock(scope, "ErrRetErr");
1555 const ok_block = try irb.createBasicBlock(scope, "ErrRetOk");
1556 if (!have_err_defers) {
1557 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);
1558 }
1559
1560 const is_err = try irb.build(
1561 Inst.TestErr,
1562 scope,
1563 src_span,
1564 Inst.TestErr.Params{ .target = return_value },
1565 );
1566
1567 const err_is_comptime = try irb.buildTestCompTime(scope, src_span, is_err);
1568
1569 _ = try irb.buildGen(Inst.CondBr, scope, src_span, Inst.CondBr.Params{
1570 .condition = is_err,
1571 .then_block = err_block,
1572 .else_block = ok_block,
1573 .is_comptime = err_is_comptime,
1574 });
489 fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
490 const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr);
491 const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name);
1575492
1576 const ret_stmt_block = try irb.createBasicBlock(scope, "RetStmt");
1577
1578 try irb.setCursorAtEndAndAppendBlock(err_block);
1579 if (have_err_defers) {
1580 _ = try irb.genDefersForBlock(scope, outer_scope, .ErrorExit);
1581 }
1582 if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) {
1583 _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{});
1584 }
1585 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1586 .dest_block = ret_stmt_block,
1587 .is_comptime = err_is_comptime,
1588 });
1589
1590 try irb.setCursorAtEndAndAppendBlock(ok_block);
1591 if (have_err_defers) {
1592 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);
1593 }
1594 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1595 .dest_block = ret_stmt_block,
1596 .is_comptime = err_is_comptime,
493 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
494 .Pointer => object_ptr.ty.elemType(),
495 else => return self.fail(fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
496 };
497 switch (elem_ty.zigTypeTag()) {
498 .Array => {
499 if (mem.eql(u8, field_name, "len")) {
500 const len_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
501 len_payload.* = .{ .int = elem_ty.arrayLen() };
502
503 const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal);
504 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
505
506 return self.constInst(fieldptr.base.src, .{
507 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
508 .val = Value.initPayload(&ref_payload.base),
1597509 });
1598
1599 try irb.setCursorAtEndAndAppendBlock(ret_stmt_block);
1600 return irb.genAsyncReturn(scope, src_span, return_value, false);
1601510 } else {
1602 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);
1603 return irb.genAsyncReturn(scope, src_span, return_value, false);
1604 }
1605 },
1606 }
1607 }
1608
1609 pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1610 const src_span = Span.token(identifier.token);
1611 const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
1612
1613 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1614 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
1615 // const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
1616 // irb->codegen->builtin_types.entry_void, false);
1617 // const_instruction->base.value.special = ConstValSpecialStatic;
1618 // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard;
1619 // return &const_instruction->base;
1620 //}
1621
1622 if (irb.comp.getPrimitiveType(name)) |result| {
1623 if (result) |primitive_type| {
1624 defer primitive_type.base.deref(irb.comp);
1625 switch (lval) {
1626 // if (lval == LValPtr) {
1627 // return ir_build_ref(irb, scope, node, value, false, false);
1628 .Ptr => return error.Unimplemented,
1629 .None => return irb.buildConstValue(scope, src_span, &primitive_type.base),
1630 }
1631 }
1632 } else |err| switch (err) {
1633 error.Overflow => {
1634 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large", .{});
1635 return error.SemanticAnalysisFailed;
1636 },
1637 error.OutOfMemory => return error.OutOfMemory,
1638 }
1639
1640 switch (irb.findIdent(scope, name)) {
1641 .Decl => |decl| {
1642 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1643 .decl = decl,
1644 .lval = lval,
1645 });
1646 },
1647 .VarScope => |var_scope| {
1648 const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope });
1649 switch (lval) {
1650 .Ptr => return var_ptr,
1651 .None => {
1652 return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr });
1653 },
511 return self.fail(
512 fieldptr.positionals.field_name.src,
513 "no member named '{}' in '{}'",
514 .{ field_name, elem_ty },
515 );
1654516 }
1655517 },
1656 .NotFound => {},
518 else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
1657519 }
1658
1659 //if (node->owner->any_imports_failed) {
1660 // // skip the error message since we had a failing import in this file
1661 // // if an import breaks we don't need redundant undeclared identifier errors
1662 // return irb->codegen->invalid_instruction;
1663 //}
1664
1665 // TODO put a variable of same name with invalid type in global scope
1666 // so that future references to this same name will find a variable with an invalid type
1667
1668 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", .{name});
1669 return error.SemanticAnalysisFailed;
1670520 }
1671521
1672 const DeferCounts = struct {
1673 scope_exit: usize,
1674 error_exit: usize,
1675 };
522 fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
523 const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
524 const new_inst = try self.resolveInst(func, intcast.positionals.value);
1676525
1677 fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts {
1678 var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 };
1679
1680 var scope = inner_scope;
1681 while (scope != outer_scope) {
1682 switch (scope.id) {
1683 .Defer => {
1684 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
1685 switch (defer_scope.kind) {
1686 .ScopeExit => result.scope_exit += 1,
1687 .ErrorExit => result.error_exit += 1,
1688 }
1689 scope = scope.parent orelse break;
526 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
527 .ComptimeInt => true,
528 .Int => false,
529 else => return self.fail(
530 intcast.positionals.dest_type.src,
531 "expected integer type, found '{}'",
532 .{
533 dest_type,
1690534 },
1691 .FnDef => break,
1692
1693 .CompTime,
1694 .Block,
1695 .Decls,
1696 .Root,
1697 .Var,
1698 => scope = scope.parent orelse break,
1699
1700 .DeferExpr => unreachable,
1701 .AstTree => unreachable,
1702 }
1703 }
1704 return result;
1705 }
535 ),
536 };
1706537
1707 fn genDefersForBlock(
1708 irb: *Builder,
1709 inner_scope: *Scope,
1710 outer_scope: *Scope,
1711 gen_kind: Scope.Defer.Kind,
1712 ) !bool {
1713 var scope = inner_scope;
1714 var is_noreturn = false;
1715 while (true) {
1716 switch (scope.id) {
1717 .Defer => {
1718 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
1719 const generate = switch (defer_scope.kind) {
1720 .ScopeExit => true,
1721 .ErrorExit => gen_kind == .ErrorExit,
1722 };
1723 if (generate) {
1724 const defer_expr_scope = defer_scope.defer_expr_scope;
1725 const instruction = try irb.genNodeRecursive(
1726 defer_expr_scope.expr_node,
1727 &defer_expr_scope.base,
1728 .None,
1729 );
1730 if (instruction.isNoReturn()) {
1731 is_noreturn = true;
1732 } else {
1733 _ = try irb.build(
1734 Inst.CheckVoidStmt,
1735 &defer_expr_scope.base,
1736 Span.token(defer_expr_scope.expr_node.lastToken()),
1737 Inst.CheckVoidStmt.Params{ .target = instruction },
1738 );
1739 }
1740 }
1741 },
1742 .FnDef,
1743 .Decls,
1744 .Root,
1745 => return is_noreturn,
1746
1747 .CompTime,
1748 .Block,
1749 .Var,
1750 => scope = scope.parent orelse return is_noreturn,
1751
1752 .DeferExpr => unreachable,
1753 .AstTree => unreachable,
1754 }
538 switch (new_inst.ty.zigTypeTag()) {
539 .ComptimeInt, .Int => {},
540 else => return self.fail(
541 intcast.positionals.value.src,
542 "expected integer type, found '{}'",
543 .{new_inst.ty},
544 ),
1755545 }
1756 }
1757546
1758 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst {
1759 switch (lval) {
1760 .None => return instruction,
1761 .Ptr => {
1762 // We needed a pointer to a value, but we got a value. So we create
1763 // an instruction which just makes a const pointer of it.
1764 return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{
1765 .target = instruction,
1766 .mut = .Const,
1767 .volatility = .Non,
1768 });
1769 },
547 if (dest_is_comptime_int or new_inst.value() != null) {
548 return self.coerce(dest_type, new_inst);
1770549 }
1771 }
1772550
1773 fn arena(self: *Builder) *Allocator {
1774 return &self.code.arena.allocator;
551 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
1775552 }
1776553
1777 fn buildExtra(
1778 self: *Builder,
1779 comptime I: type,
1780 scope: *Scope,
1781 span: Span,
1782 params: I.Params,
1783 is_generated: bool,
1784 ) !*Inst {
1785 const inst = try self.arena().create(I);
1786 inst.* = I{
1787 .base = Inst{
1788 .id = Inst.typeToId(I),
1789 .is_generated = is_generated,
1790 .scope = scope,
1791 .debug_id = self.next_debug_id,
1792 .val = switch (I.ir_val_init) {
1793 .Unknown => IrVal.Unknown,
1794 .NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.comp).base },
1795 .Void => IrVal{ .KnownValue = &Value.Void.get(self.comp).base },
1796 },
1797 .ref_count = 0,
1798 .span = span,
1799 .child = null,
1800 .parent = null,
1801 .llvm_value = undefined,
1802 .owner_bb = self.current_basic_block,
1803 },
1804 .params = params,
554 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
555 const ptr = try self.resolveInst(func, deref.positionals.ptr);
556 const elem_ty = switch (ptr.ty.zigTypeTag()) {
557 .Pointer => ptr.ty.elemType(),
558 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
1805559 };
1806
1807 // Look at the params and ref() other instructions
1808 inline for (@typeInfo(I.Params).Struct.fields) |f| {
1809 switch (f.field_type) {
1810 *Inst => @field(inst.params, f.name).ref(self),
1811 *BasicBlock => @field(inst.params, f.name).ref(self),
1812 ?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self),
1813 []*Inst => {
1814 // TODO https://github.com/ziglang/zig/issues/1269
1815 for (@field(inst.params, f.name)) |other|
1816 other.ref(self);
1817 },
1818 []*BasicBlock => {
1819 // TODO https://github.com/ziglang/zig/issues/1269
1820 for (@field(inst.params, f.name)) |other|
1821 other.ref(self);
1822 },
1823 Type.Pointer.Mut,
1824 Type.Pointer.Vol,
1825 Type.Pointer.Size,
1826 LVal,
1827 *Decl,
1828 *Scope.Var,
1829 => {},
1830 // it's ok to add more types here, just make sure that
1831 // any instructions and basic blocks are ref'd appropriately
1832 else => @compileError("unrecognized type in Params: " ++ @typeName(f.field_type)),
1833 }
560 if (ptr.value()) |val| {
561 return self.constInst(deref.base.src, .{
562 .ty = elem_ty,
563 .val = val.pointerDeref(),
564 });
1834565 }
1835566
1836 self.next_debug_id += 1;
1837 try self.current_basic_block.instruction_list.append(&inst.base);
1838 return &inst.base;
1839 }
1840
1841 fn build(
1842 self: *Builder,
1843 comptime I: type,
1844 scope: *Scope,
1845 span: Span,
1846 params: I.Params,
1847 ) !*Inst {
1848 return self.buildExtra(I, scope, span, params, false);
1849 }
1850
1851 fn buildGen(
1852 self: *Builder,
1853 comptime I: type,
1854 scope: *Scope,
1855 span: Span,
1856 params: I.Params,
1857 ) !*Inst {
1858 return self.buildExtra(I, scope, span, params, true);
1859 }
1860
1861 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst {
1862 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
1863 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };
1864 return inst;
567 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
1865568 }
1866569
1867 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst {
1868 const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated);
1869 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };
1870 return inst;
1871 }
570 fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst {
571 const return_type = try self.resolveType(func, assembly.positionals.return_type);
572 const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source);
573 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null;
1872574
1873 fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst {
1874 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
1875 inst.val = IrVal{ .KnownValue = v.getRef() };
1876 return inst;
1877 }
575 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
576 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
577 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
1878578
1879 /// If the code is explicitly set to be comptime, then builds a const bool,
1880 /// otherwise builds a TestCompTime instruction.
1881 fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst {
1882 if (self.isCompTime(scope)) {
1883 return self.buildConstBool(scope, span, true);
1884 } else {
1885 return self.build(
1886 Inst.TestCompTime,
1887 scope,
1888 span,
1889 Inst.TestCompTime.Params{ .target = target },
1890 );
579 for (inputs) |*elem, i| {
580 elem.* = try self.resolveConstString(func, assembly.kw_args.inputs[i]);
1891581 }
1892 }
1893
1894 fn genAsyncReturn(irb: *Builder, scope: *Scope, span: Span, result: *Inst, is_gen: bool) !*Inst {
1895 _ = try irb.buildGen(
1896 Inst.AddImplicitReturnType,
1897 scope,
1898 span,
1899 Inst.AddImplicitReturnType.Params{ .target = result },
1900 );
1901
1902 if (!irb.is_async) {
1903 return irb.buildExtra(
1904 Inst.Return,
1905 scope,
1906 span,
1907 Inst.Return.Params{ .return_value = result },
1908 is_gen,
1909 );
582 for (clobbers) |*elem, i| {
583 elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]);
1910584 }
1911 return error.Unimplemented;
1912 }
1913
1914 const Ident = union(enum) {
1915 NotFound,
1916 Decl: *Decl,
1917 VarScope: *Scope.Var,
1918 };
1919
1920 fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
1921 var s = scope;
1922 while (true) {
1923 switch (s.id) {
1924 .Root => return .NotFound,
1925 .Decls => {
1926 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1927 const locked_table = decls.table.acquireRead();
1928 defer locked_table.release();
1929 if (locked_table.value.get(name)) |entry| {
1930 return Ident{ .Decl = entry.value };
1931 }
1932 },
1933 .Var => {
1934 const var_scope = @fieldParentPtr(Scope.Var, "base", s);
1935 if (mem.eql(u8, var_scope.name, name)) {
1936 return Ident{ .VarScope = var_scope };
1937 }
1938 },
1939 else => {},
1940 }
1941 s = s.parent.?;
585 for (args) |*elem, i| {
586 elem.* = try self.resolveInst(func, assembly.kw_args.args[i]);
1942587 }
1943 }
1944};
1945
1946const Analyze = struct {
1947 irb: Builder,
1948 old_bb_index: usize,
1949 const_predecessor_bb: ?*BasicBlock,
1950 parent_basic_block: *BasicBlock,
1951 instruction_index: usize,
1952 src_implicit_return_type_list: std.ArrayList(*Inst),
1953 explicit_return_type: ?*Type,
1954
1955 pub const Error = error{
1956 /// This is only for when we have already reported a compile error. It is the poison value.
1957 SemanticAnalysisFailed,
1958
1959 /// This is a placeholder - it is useful to use instead of panicking but once the compiler is
1960 /// done this error code will be removed.
1961 Unimplemented,
1962
1963 OutOfMemory,
1964 };
1965588
1966 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1967 var irb = try Builder.init(comp, tree_scope, null);
1968 errdefer irb.abort();
1969
1970 return Analyze{
1971 .irb = irb,
1972 .old_bb_index = 0,
1973 .const_predecessor_bb = null,
1974 .parent_basic_block = undefined, // initialized with startBasicBlock
1975 .instruction_index = undefined, // initialized with startBasicBlock
1976 .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()),
1977 .explicit_return_type = explicit_return_type,
1978 };
589 const f = try self.requireFunctionBody(func, assembly.base.src);
590 return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
591 .asm_source = asm_source,
592 .is_volatile = assembly.kw_args.@"volatile",
593 .output = output,
594 .inputs = inputs,
595 .clobbers = clobbers,
596 .args = args,
597 });
1979598 }
1980599
1981 pub fn abort(self: *Analyze) void {
1982 self.irb.abort();
600 fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst {
601 const f = try self.requireFunctionBody(func, unreach.base.src);
602 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
1983603 }
1984604
1985 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock {
1986 if (old_bb.child) |child| {
1987 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
1988 return child;
605 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
606 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
607 if (in_memory_result == .ok) {
608 return self.bitcast(dest_type, inst);
1989609 }
1990610
1991 const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint);
1992 new_bb.linkToParent(old_bb);
1993 new_bb.ref_instruction = ref_old_instruction;
1994 return new_bb;
1995 }
1996
1997 pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void {
1998 self.instruction_index = 0;
1999 self.parent_basic_block = old_bb;
2000 self.const_predecessor_bb = const_predecessor_bb;
2001 }
2002
2003 pub fn finishBasicBlock(ira: *Analyze, old_code: *Code) !void {
2004 try ira.irb.code.basic_block_list.append(ira.irb.current_basic_block);
2005 ira.instruction_index += 1;
2006
2007 while (ira.instruction_index < ira.parent_basic_block.instruction_list.len) {
2008 const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
2009
2010 if (!next_instruction.is_generated) {
2011 try ira.addCompileError(next_instruction.span, "unreachable code", .{});
2012 break;
611 // *[N]T to []T
612 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
613 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
614 {
615 const array_type = inst.ty.elemType();
616 const dst_elem_type = dest_type.elemType();
617 if (array_type.zigTypeTag() == .Array and
618 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
619 {
620 return self.coerceArrayPtrToSlice(dest_type, inst);
2013621 }
2014 ira.instruction_index += 1;
2015622 }
2016623
2017 ira.old_bb_index += 1;
2018
2019 var need_repeat = true;
2020 while (true) {
2021 while (ira.old_bb_index < old_code.basic_block_list.len) {
2022 const old_bb = old_code.basic_block_list.at(ira.old_bb_index);
2023 const new_bb = old_bb.child orelse {
2024 ira.old_bb_index += 1;
2025 continue;
2026 };
2027 if (new_bb.instruction_list.len != 0) {
2028 ira.old_bb_index += 1;
2029 continue;
2030 }
2031 ira.irb.current_basic_block = new_bb;
2032
2033 ira.startBasicBlock(old_bb, null);
2034 return;
624 // comptime_int to fixed-width integer
625 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
626 // The representation is already correct; we only need to make sure it fits in the destination type.
627 const val = inst.value().?; // comptime_int always has comptime known value
628 if (!val.intFitsInType(dest_type, self.target)) {
629 return self.fail(inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2035630 }
2036 if (!need_repeat)
2037 return;
2038 need_repeat = false;
2039 ira.old_bb_index = 0;
2040 continue;
631 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
2041632 }
2042 }
2043
2044 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: var) !void {
2045 return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args);
2046 }
2047633
2048 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
2049 // TODO actual implementation
2050 return &Type.Void.get(self.irb.comp).base;
634 return self.fail(inst.src, "TODO implement type coercion", .{});
2051635 }
2052636
2053 fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst {
2054 const dest_type = optional_dest_type orelse return target;
2055 const from_type = target.getKnownType();
2056 if (from_type == dest_type or from_type.id == .NoReturn) return target;
2057 return self.analyzeCast(target, target, dest_type);
637 fn bitcast(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
638 if (inst.value()) |val| {
639 // Keep the comptime Value representation; take the new type.
640 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
641 }
642 return self.fail(inst.src, "TODO implement runtime bitcast", .{});
2058643 }
2059644
2060 fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst {
2061 const from_type = target.getKnownType();
2062
2063 //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
2064 // return ira->codegen->invalid_instruction;
2065 //}
2066
2067 //// perfect match or non-const to const
2068 //ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
2069 // source_node, false);
2070 //if (const_cast_result.id == ConstCastResultIdOk) {
2071 // return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
2072 //}
2073
2074 //// widening conversion
2075 //if (wanted_type->id == TypeTableEntryIdInt &&
2076 // actual_type->id == TypeTableEntryIdInt &&
2077 // wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
2078 // wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
2079 //{
2080 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
2081 //}
2082
2083 //// small enough unsigned ints can get casted to large enough signed ints
2084 //if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed &&
2085 // actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&
2086 // wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
2087 //{
2088 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
2089 //}
2090
2091 //// float widening conversion
2092 //if (wanted_type->id == TypeTableEntryIdFloat &&
2093 // actual_type->id == TypeTableEntryIdFloat &&
2094 // wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
2095 //{
2096 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
2097 //}
2098
2099 //// cast from [N]T to []const T
2100 //if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) {
2101 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2102 // assert(ptr_type->id == TypeTableEntryIdPointer);
2103 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2104 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2105 // source_node, false).id == ConstCastResultIdOk)
2106 // {
2107 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
2108 // }
2109 //}
2110
2111 //// cast from *const [N]T to []const T
2112 //if (is_slice(wanted_type) &&
2113 // actual_type->id == TypeTableEntryIdPointer &&
2114 // actual_type->data.pointer.is_const &&
2115 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
2116 //{
2117 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2118 // assert(ptr_type->id == TypeTableEntryIdPointer);
2119
2120 // TypeTableEntry *array_type = actual_type->data.pointer.child_type;
2121
2122 // if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
2123 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type,
2124 // source_node, false).id == ConstCastResultIdOk)
2125 // {
2126 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
2127 // }
2128 //}
2129
2130 //// cast from [N]T to *const []const T
2131 //if (wanted_type->id == TypeTableEntryIdPointer &&
2132 // wanted_type->data.pointer.is_const &&
2133 // is_slice(wanted_type->data.pointer.child_type) &&
2134 // actual_type->id == TypeTableEntryIdArray)
2135 //{
2136 // TypeTableEntry *ptr_type =
2137 // wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
2138 // assert(ptr_type->id == TypeTableEntryIdPointer);
2139 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2140 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2141 // source_node, false).id == ConstCastResultIdOk)
2142 // {
2143 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
2144 // if (type_is_invalid(cast1->value.type))
2145 // return ira->codegen->invalid_instruction;
2146
2147 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2148 // if (type_is_invalid(cast2->value.type))
2149 // return ira->codegen->invalid_instruction;
2150
2151 // return cast2;
2152 // }
2153 //}
2154
2155 //// cast from [N]T to ?[]const T
2156 //if (wanted_type->id == TypeTableEntryIdOptional &&
2157 // is_slice(wanted_type->data.maybe.child_type) &&
2158 // actual_type->id == TypeTableEntryIdArray)
2159 //{
2160 // TypeTableEntry *ptr_type =
2161 // wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
2162 // assert(ptr_type->id == TypeTableEntryIdPointer);
2163 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2164 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2165 // source_node, false).id == ConstCastResultIdOk)
2166 // {
2167 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
2168 // if (type_is_invalid(cast1->value.type))
2169 // return ira->codegen->invalid_instruction;
2170
2171 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2172 // if (type_is_invalid(cast2->value.type))
2173 // return ira->codegen->invalid_instruction;
2174
2175 // return cast2;
2176 // }
2177 //}
2178
2179 //// *[N]T to [*]T
2180 //if (wanted_type->id == TypeTableEntryIdPointer &&
2181 // wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
2182 // actual_type->id == TypeTableEntryIdPointer &&
2183 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2184 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
2185 // actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
2186 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2187 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2188 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2189 //{
2190 // return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
2191 //}
2192
2193 //// *[N]T to []T
2194 //if (is_slice(wanted_type) &&
2195 // actual_type->id == TypeTableEntryIdPointer &&
2196 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2197 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
2198 //{
2199 // TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2200 // assert(slice_ptr_type->id == TypeTableEntryIdPointer);
2201 // if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
2202 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2203 // !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
2204 // {
2205 // return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
2206 // }
2207 //}
2208
2209 //// cast from T to ?T
2210 //// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
2211 //if (wanted_type->id == TypeTableEntryIdOptional) {
2212 // TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
2213 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
2214 // false).id == ConstCastResultIdOk)
2215 // {
2216 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2217 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2218 // actual_type->id == TypeTableEntryIdComptimeFloat)
2219 // {
2220 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
2221 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2222 // } else {
2223 // return ira->codegen->invalid_instruction;
2224 // }
2225 // } else if (wanted_child_type->id == TypeTableEntryIdPointer &&
2226 // wanted_child_type->data.pointer.is_const &&
2227 // (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type)))
2228 // {
2229 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value);
2230 // if (type_is_invalid(cast1->value.type))
2231 // return ira->codegen->invalid_instruction;
2232
2233 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2234 // if (type_is_invalid(cast2->value.type))
2235 // return ira->codegen->invalid_instruction;
2236
2237 // return cast2;
2238 // }
2239 //}
2240
2241 //// cast from null literal to maybe type
2242 //if (wanted_type->id == TypeTableEntryIdOptional &&
2243 // actual_type->id == TypeTableEntryIdNull)
2244 //{
2245 // return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
2246 //}
2247
2248 //// cast from child type of error type to error type
2249 //if (wanted_type->id == TypeTableEntryIdErrorUnion) {
2250 // if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
2251 // source_node, false).id == ConstCastResultIdOk)
2252 // {
2253 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2254 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2255 // actual_type->id == TypeTableEntryIdComptimeFloat)
2256 // {
2257 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
2258 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2259 // } else {
2260 // return ira->codegen->invalid_instruction;
2261 // }
2262 // }
2263 //}
2264
2265 //// cast from [N]T to E![]const T
2266 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2267 // is_slice(wanted_type->data.error_union.payload_type) &&
2268 // actual_type->id == TypeTableEntryIdArray)
2269 //{
2270 // TypeTableEntry *ptr_type =
2271 // wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
2272 // assert(ptr_type->id == TypeTableEntryIdPointer);
2273 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2274 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2275 // source_node, false).id == ConstCastResultIdOk)
2276 // {
2277 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2278 // if (type_is_invalid(cast1->value.type))
2279 // return ira->codegen->invalid_instruction;
2280
2281 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2282 // if (type_is_invalid(cast2->value.type))
2283 // return ira->codegen->invalid_instruction;
2284
2285 // return cast2;
2286 // }
2287 //}
2288
2289 //// cast from error set to error union type
2290 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2291 // actual_type->id == TypeTableEntryIdErrorSet)
2292 //{
2293 // return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
2294 //}
2295
2296 //// cast from T to E!?T
2297 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2298 // wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional &&
2299 // actual_type->id != TypeTableEntryIdOptional)
2300 //{
2301 // TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
2302 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
2303 // actual_type->id == TypeTableEntryIdNull ||
2304 // actual_type->id == TypeTableEntryIdComptimeInt ||
2305 // actual_type->id == TypeTableEntryIdComptimeFloat)
2306 // {
2307 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2308 // if (type_is_invalid(cast1->value.type))
2309 // return ira->codegen->invalid_instruction;
2310
2311 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2312 // if (type_is_invalid(cast2->value.type))
2313 // return ira->codegen->invalid_instruction;
2314
2315 // return cast2;
2316 // }
2317 //}
2318
2319 // cast from comptime-known integer to another integer where the value fits
2320 if (target.isCompTime() and (from_type.id == .Int or from_type.id == .ComptimeInt)) cast: {
2321 const target_val = target.val.KnownValue;
2322 const from_int = &target_val.cast(Value.Int).?.big_int;
2323 const fits = fits: {
2324 if (dest_type.cast(Type.ComptimeInt)) |ctint| {
2325 break :fits true;
2326 }
2327 if (dest_type.cast(Type.Int)) |int| {
2328 break :fits from_int.fitsInTwosComp(int.key.is_signed, int.key.bit_count);
2329 }
2330 break :cast;
2331 };
2332 if (!fits) {
2333 try ira.addCompileError(source_instr.span, "integer value '{}' cannot be stored in type '{}'", .{
2334 from_int,
2335 dest_type.name,
2336 });
2337 return error.SemanticAnalysisFailed;
2338 }
2339
2340 const new_val = try target.copyVal(ira.irb.comp);
2341 new_val.setType(dest_type, ira.irb.comp);
2342 return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val);
645 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
646 if (inst.value()) |val| {
647 // The comptime Value representation is compatible with both types.
648 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
2343649 }
2344
2345 // cast from number literal to another type
2346 // cast from number literal to *const integer
2347 //if (actual_type->id == TypeTableEntryIdComptimeFloat ||
2348 // actual_type->id == TypeTableEntryIdComptimeInt)
2349 //{
2350 // ensure_complete_type(ira->codegen, wanted_type);
2351 // if (type_is_invalid(wanted_type))
2352 // return ira->codegen->invalid_instruction;
2353 // if (wanted_type->id == TypeTableEntryIdEnum) {
2354 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
2355 // if (type_is_invalid(cast1->value.type))
2356 // return ira->codegen->invalid_instruction;
2357
2358 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2359 // if (type_is_invalid(cast2->value.type))
2360 // return ira->codegen->invalid_instruction;
2361
2362 // return cast2;
2363 // } else if (wanted_type->id == TypeTableEntryIdPointer &&
2364 // wanted_type->data.pointer.is_const)
2365 // {
2366 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
2367 // if (type_is_invalid(cast1->value.type))
2368 // return ira->codegen->invalid_instruction;
2369
2370 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2371 // if (type_is_invalid(cast2->value.type))
2372 // return ira->codegen->invalid_instruction;
2373
2374 // return cast2;
2375 // } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) {
2376 // CastOp op;
2377 // if ((actual_type->id == TypeTableEntryIdComptimeFloat &&
2378 // wanted_type->id == TypeTableEntryIdFloat) ||
2379 // (actual_type->id == TypeTableEntryIdComptimeInt &&
2380 // wanted_type->id == TypeTableEntryIdInt))
2381 // {
2382 // op = CastOpNumLitToConcrete;
2383 // } else if (wanted_type->id == TypeTableEntryIdInt) {
2384 // op = CastOpFloatToInt;
2385 // } else if (wanted_type->id == TypeTableEntryIdFloat) {
2386 // op = CastOpIntToFloat;
2387 // } else {
2388 // zig_unreachable();
2389 // }
2390 // return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false);
2391 // } else {
2392 // return ira->codegen->invalid_instruction;
2393 // }
2394 //}
2395
2396 //// cast from typed number to integer or float literal.
2397 //// works when the number is known at compile time
2398 //if (instr_is_comptime(value) &&
2399 // ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) ||
2400 // (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat)))
2401 //{
2402 // return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
2403 //}
2404
2405 //// cast from union to the enum type of the union
2406 //if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {
2407 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2408 // if (type_is_invalid(actual_type))
2409 // return ira->codegen->invalid_instruction;
2410
2411 // if (actual_type->data.unionation.tag_type == wanted_type) {
2412 // return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type);
2413 // }
2414 //}
2415
2416 //// enum to union which has the enum as the tag type
2417 //if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
2418 // (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2419 // wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
2420 //{
2421 // type_ensure_zero_bits_known(ira->codegen, wanted_type);
2422 // if (wanted_type->data.unionation.tag_type == actual_type) {
2423 // return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
2424 // }
2425 //}
2426
2427 //// enum to &const union which has the enum as the tag type
2428 //if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) {
2429 // TypeTableEntry *union_type = wanted_type->data.pointer.child_type;
2430 // if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2431 // union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
2432 // {
2433 // type_ensure_zero_bits_known(ira->codegen, union_type);
2434 // if (union_type->data.unionation.tag_type == actual_type) {
2435 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
2436 // if (type_is_invalid(cast1->value.type))
2437 // return ira->codegen->invalid_instruction;
2438
2439 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2440 // if (type_is_invalid(cast2->value.type))
2441 // return ira->codegen->invalid_instruction;
2442
2443 // return cast2;
2444 // }
2445 // }
2446 //}
2447
2448 //// cast from *T to *[1]T
2449 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2450 // actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle)
2451 //{
2452 // TypeTableEntry *array_type = wanted_type->data.pointer.child_type;
2453 // if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 &&
2454 // types_match_const_cast_only(ira, array_type->data.array.child_type,
2455 // actual_type->data.pointer.child_type, source_node,
2456 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2457 // {
2458 // if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {
2459 // ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
2460 // add_error_note(ira->codegen, msg, value->source_node,
2461 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name),
2462 // actual_type->data.pointer.alignment));
2463 // add_error_note(ira->codegen, msg, source_instr->source_node,
2464 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name),
2465 // wanted_type->data.pointer.alignment));
2466 // return ira->codegen->invalid_instruction;
2467 // }
2468 // return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
2469 // }
2470 //}
2471
2472 //// cast from T to *T where T is zero bits
2473 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2474 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2475 // actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2476 //{
2477 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2478 // if (type_is_invalid(actual_type)) {
2479 // return ira->codegen->invalid_instruction;
2480 // }
2481 // if (!type_has_bits(actual_type)) {
2482 // return ir_get_ref(ira, source_instr, value, false, false);
2483 // }
2484 //}
2485
2486 //// cast from undefined to anything
2487 //if (actual_type->id == TypeTableEntryIdUndefined) {
2488 // return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
2489 //}
2490
2491 //// cast from something to const pointer of it
2492 //if (!type_requires_comptime(actual_type)) {
2493 // TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
2494 // if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) {
2495 // return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
2496 // }
2497 //}
2498
2499 try ira.addCompileError(source_instr.span, "expected type '{}', found '{}'", .{
2500 dest_type.name,
2501 from_type.name,
2502 });
2503 //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node,
2504 // buf_sprintf("expected type '%s', found '%s'",
2505 // buf_ptr(&wanted_type->name),
2506 // buf_ptr(&actual_type->name)));
2507 //report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg);
2508 return error.SemanticAnalysisFailed;
650 return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2509651 }
2510652
2511 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value {
2512 @panic("TODO");
653 fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError {
654 @setCold(true);
655 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
656 (try self.errors.addOne()).* = .{
657 .byte_offset = src,
658 .msg = msg,
659 };
660 return error.AnalysisFail;
2513661 }
2514662
2515 fn getCompTimeRef(
2516 self: *Analyze,
2517 value: *Value,
2518 ptr_mut: Value.Ptr.Mut,
2519 mut: Type.Pointer.Mut,
2520 volatility: Type.Pointer.Vol,
2521 ) Analyze.Error!*Inst {
2522 return error.Unimplemented;
2523 }
2524};
663 const InMemoryCoercionResult = enum {
664 ok,
665 no_match,
666 };
2525667
2526pub fn gen(
2527 comp: *Compilation,
2528 body_node: *ast.Node,
2529 tree_scope: *Scope.AstTree,
2530 scope: *Scope,
2531) !*Code {
2532 var irb = try Builder.init(comp, tree_scope, scope);
2533 errdefer irb.abort();
2534
2535 const entry_block = try irb.createBasicBlock(scope, "Entry");
2536 entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin.
2537 try irb.setCursorAtEndAndAppendBlock(entry_block);
2538
2539 const result = try irb.genNode(body_node, scope, .None);
2540 if (!result.isNoReturn()) {
2541 // no need for save_err_ret_addr because this cannot return error
2542 _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true);
2543 }
668 fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
669 if (dest_type.eql(src_type))
670 return .ok;
2544671
2545 return irb.finish();
2546}
672 // TODO: implement more of this function
2547673
2548pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
2549 const old_entry_bb = old_code.basic_block_list.at(0);
674 return .no_match;
675 }
676};
2550677
2551 var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
2552 errdefer ira.abort();
678pub fn main() anyerror!void {
679 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
680 defer arena.deinit();
681 const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;
2553682
2554 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
2555 new_entry_bb.ref(&ira.irb);
683 const args = try std.process.argsAlloc(allocator);
2556684
2557 ira.irb.current_basic_block = new_entry_bb;
685 const src_path = args[1];
686 const debug_error_trace = true;
2558687
2559 ira.startBasicBlock(old_entry_bb, null);
688 const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0);
2560689
2561 while (ira.old_bb_index < old_code.basic_block_list.len) {
2562 const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
690 var zir_module = try text.parse(allocator, source);
691 defer zir_module.deinit(allocator);
2563692
2564 if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) {
2565 ira.instruction_index += 1;
2566 continue;
693 if (zir_module.errors.len != 0) {
694 for (zir_module.errors) |err_msg| {
695 const loc = findLineColumn(source, err_msg.byte_offset);
696 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
2567697 }
698 if (debug_error_trace) return error.ParseFailure;
699 std.process.exit(1);
700 }
2568701
2569 const return_inst = try old_instruction.analyze(&ira);
2570 assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point
2571 return_inst.linkToParent(old_instruction);
2572 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
2573 // then here we want to check if ira.isCompTime() and return early if true
702 var analyzed_module = try analyze(allocator, zir_module);
703 defer analyzed_module.deinit(allocator);
2574704
2575 if (return_inst.isNoReturn()) {
2576 try ira.finishBasicBlock(old_code);
2577 continue;
705 if (analyzed_module.errors.len != 0) {
706 for (analyzed_module.errors) |err_msg| {
707 const loc = findLineColumn(source, err_msg.byte_offset);
708 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
2578709 }
2579
2580 ira.instruction_index += 1;
710 if (debug_error_trace) return error.ParseFailure;
711 std.process.exit(1);
2581712 }
2582713
2583 if (ira.src_implicit_return_type_list.len == 0) {
2584 ira.irb.code.return_type = &Type.NoReturn.get(comp).base;
2585 return ira.irb.finish();
2586 }
714 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
715 defer new_zir_module.deinit(allocator);
2587716
2588 ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.span());
2589 return ira.irb.finish();
717 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
718 try new_zir_module.writeToStream(allocator, bos.outStream());
719 try bos.flush();
2590720}
721
722fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
723 var line: usize = 0;
724 var column: usize = 0;
725 for (source[0..byte_offset]) |byte| {
726 switch (byte) {
727 '\n' => {
728 line += 1;
729 column = 0;
730 },
731 else => {
732 column += 1;
733 },
734 }
735 }
736 return .{ .line = line, .column = column };
737}
738
739// Performance optimization ideas:
740// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions
src-self-hosted/ir/text.zig created+1065
......@@ -0,0 +1,1065 @@
1//! This file has to do with parsing and rendering the ZIR text format.
2
3const std = @import("std");
4const mem = std.mem;
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const BigInt = std.math.big.Int;
8const Type = @import("../type.zig").Type;
9const Value = @import("../value.zig").Value;
10const ir = @import("../ir.zig");
11
12/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
13/// in-memory, analyzed instructions with types and values.
14pub const Inst = struct {
15 tag: Tag,
16 /// Byte offset into the source.
17 src: usize,
18
19 /// These names are used directly as the instruction names in the text format.
20 pub const Tag = enum {
21 str,
22 int,
23 ptrtoint,
24 fieldptr,
25 deref,
26 as,
27 @"asm",
28 @"unreachable",
29 @"fn",
30 @"export",
31 primitive,
32 fntype,
33 intcast,
34 };
35
36 pub fn TagToType(tag: Tag) type {
37 return switch (tag) {
38 .str => Str,
39 .int => Int,
40 .ptrtoint => PtrToInt,
41 .fieldptr => FieldPtr,
42 .deref => Deref,
43 .as => As,
44 .@"asm" => Asm,
45 .@"unreachable" => Unreachable,
46 .@"fn" => Fn,
47 .@"export" => Export,
48 .primitive => Primitive,
49 .fntype => FnType,
50 .intcast => IntCast,
51 };
52 }
53
54 pub fn cast(base: *Inst, comptime T: type) ?*T {
55 if (base.tag != T.base_tag)
56 return null;
57
58 return @fieldParentPtr(T, "base", base);
59 }
60
61 pub const Str = struct {
62 pub const base_tag = Tag.str;
63 base: Inst,
64
65 positionals: struct {
66 bytes: []const u8,
67 },
68 kw_args: struct {},
69 };
70
71 pub const Int = struct {
72 pub const base_tag = Tag.int;
73 base: Inst,
74
75 positionals: struct {
76 int: BigInt,
77 },
78 kw_args: struct {},
79 };
80
81 pub const PtrToInt = struct {
82 pub const base_tag = Tag.ptrtoint;
83 base: Inst,
84
85 positionals: struct {
86 ptr: *Inst,
87 },
88 kw_args: struct {},
89 };
90
91 pub const FieldPtr = struct {
92 pub const base_tag = Tag.fieldptr;
93 base: Inst,
94
95 positionals: struct {
96 object_ptr: *Inst,
97 field_name: *Inst,
98 },
99 kw_args: struct {},
100 };
101
102 pub const Deref = struct {
103 pub const base_tag = Tag.deref;
104 base: Inst,
105
106 positionals: struct {
107 ptr: *Inst,
108 },
109 kw_args: struct {},
110 };
111
112 pub const As = struct {
113 pub const base_tag = Tag.as;
114 base: Inst,
115
116 positionals: struct {
117 dest_type: *Inst,
118 value: *Inst,
119 },
120 kw_args: struct {},
121 };
122
123 pub const Asm = struct {
124 pub const base_tag = Tag.@"asm";
125 base: Inst,
126
127 positionals: struct {
128 asm_source: *Inst,
129 return_type: *Inst,
130 },
131 kw_args: struct {
132 @"volatile": bool = false,
133 output: ?*Inst = null,
134 inputs: []*Inst = &[0]*Inst{},
135 clobbers: []*Inst = &[0]*Inst{},
136 args: []*Inst = &[0]*Inst{},
137 },
138 };
139
140 pub const Unreachable = struct {
141 pub const base_tag = Tag.@"unreachable";
142 base: Inst,
143
144 positionals: struct {},
145 kw_args: struct {},
146 };
147
148 pub const Fn = struct {
149 pub const base_tag = Tag.@"fn";
150 base: Inst,
151
152 positionals: struct {
153 fn_type: *Inst,
154 body: Body,
155 },
156 kw_args: struct {},
157
158 pub const Body = struct {
159 instructions: []*Inst,
160 };
161 };
162
163 pub const Export = struct {
164 pub const base_tag = Tag.@"export";
165 base: Inst,
166
167 positionals: struct {
168 symbol_name: *Inst,
169 value: *Inst,
170 },
171 kw_args: struct {},
172 };
173
174 pub const Primitive = struct {
175 pub const base_tag = Tag.primitive;
176 base: Inst,
177
178 positionals: struct {
179 tag: BuiltinType,
180 },
181 kw_args: struct {},
182
183 pub const BuiltinType = enum {
184 @"isize",
185 @"usize",
186 @"c_short",
187 @"c_ushort",
188 @"c_int",
189 @"c_uint",
190 @"c_long",
191 @"c_ulong",
192 @"c_longlong",
193 @"c_ulonglong",
194 @"c_longdouble",
195 @"c_void",
196 @"f16",
197 @"f32",
198 @"f64",
199 @"f128",
200 @"bool",
201 @"void",
202 @"noreturn",
203 @"type",
204 @"anyerror",
205 @"comptime_int",
206 @"comptime_float",
207
208 fn toType(self: BuiltinType) Type {
209 return switch (self) {
210 .@"isize" => Type.initTag(.@"isize"),
211 .@"usize" => Type.initTag(.@"usize"),
212 .@"c_short" => Type.initTag(.@"c_short"),
213 .@"c_ushort" => Type.initTag(.@"c_ushort"),
214 .@"c_int" => Type.initTag(.@"c_int"),
215 .@"c_uint" => Type.initTag(.@"c_uint"),
216 .@"c_long" => Type.initTag(.@"c_long"),
217 .@"c_ulong" => Type.initTag(.@"c_ulong"),
218 .@"c_longlong" => Type.initTag(.@"c_longlong"),
219 .@"c_ulonglong" => Type.initTag(.@"c_ulonglong"),
220 .@"c_longdouble" => Type.initTag(.@"c_longdouble"),
221 .@"c_void" => Type.initTag(.@"c_void"),
222 .@"f16" => Type.initTag(.@"f16"),
223 .@"f32" => Type.initTag(.@"f32"),
224 .@"f64" => Type.initTag(.@"f64"),
225 .@"f128" => Type.initTag(.@"f128"),
226 .@"bool" => Type.initTag(.@"bool"),
227 .@"void" => Type.initTag(.@"void"),
228 .@"noreturn" => Type.initTag(.@"noreturn"),
229 .@"type" => Type.initTag(.@"type"),
230 .@"anyerror" => Type.initTag(.@"anyerror"),
231 .@"comptime_int" => Type.initTag(.@"comptime_int"),
232 .@"comptime_float" => Type.initTag(.@"comptime_float"),
233 };
234 }
235 };
236 };
237
238 pub const FnType = struct {
239 pub const base_tag = Tag.fntype;
240 base: Inst,
241
242 positionals: struct {
243 param_types: []*Inst,
244 return_type: *Inst,
245 },
246 kw_args: struct {
247 cc: std.builtin.CallingConvention = .Unspecified,
248 },
249 };
250
251 pub const IntCast = struct {
252 pub const base_tag = Tag.intcast;
253 base: Inst,
254
255 positionals: struct {
256 dest_type: *Inst,
257 value: *Inst,
258 },
259 kw_args: struct {},
260 };
261};
262
263pub const ErrorMsg = struct {
264 byte_offset: usize,
265 msg: []const u8,
266};
267
268pub const Module = struct {
269 decls: []*Inst,
270 errors: []ErrorMsg,
271 arena: std.heap.ArenaAllocator,
272
273 pub fn deinit(self: *Module, allocator: *Allocator) void {
274 allocator.free(self.decls);
275 allocator.free(self.errors);
276 self.arena.deinit();
277 self.* = undefined;
278 }
279
280 /// This is a debugging utility for rendering the tree to stderr.
281 pub fn dump(self: Module) void {
282 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
283 }
284
285 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body });
286
287 /// The allocator is used for temporary storage, but this function always returns
288 /// with no resources allocated.
289 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
290 // First, build a map of *Inst to @ or % indexes
291 var inst_table = InstPtrTable.init(allocator);
292 defer inst_table.deinit();
293
294 try inst_table.ensureCapacity(self.decls.len);
295
296 for (self.decls) |decl, decl_i| {
297 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
298
299 if (decl.cast(Inst.Fn)) |fn_inst| {
300 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
301 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
302 }
303 }
304 }
305
306 for (self.decls) |decl, i| {
307 try stream.print("@{} ", .{i});
308 try self.writeInstToStream(stream, decl, &inst_table);
309 try stream.writeByte('\n');
310 }
311 }
312
313 fn writeInstToStream(
314 self: Module,
315 stream: var,
316 decl: *Inst,
317 inst_table: *const InstPtrTable,
318 ) @TypeOf(stream).Error!void {
319 // TODO I tried implementing this with an inline for loop and hit a compiler bug
320 switch (decl.tag) {
321 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
322 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
323 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
324 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
325 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
326 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
327 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
328 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
329 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
330 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
331 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
332 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
333 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
334 }
335 }
336
337 fn writeInstToStreamGeneric(
338 self: Module,
339 stream: var,
340 comptime inst_tag: Inst.Tag,
341 base: *Inst,
342 inst_table: *const InstPtrTable,
343 ) !void {
344 const SpecificInst = Inst.TagToType(inst_tag);
345 const inst = @fieldParentPtr(SpecificInst, "base", base);
346 const Positionals = @TypeOf(inst.positionals);
347 try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
348 const pos_fields = @typeInfo(Positionals).Struct.fields;
349 inline for (pos_fields) |arg_field, i| {
350 if (i != 0) {
351 try stream.writeAll(", ");
352 }
353 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
354 }
355
356 comptime var need_comma = pos_fields.len != 0;
357 const KW_Args = @TypeOf(inst.kw_args);
358 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
359 if (@typeInfo(arg_field.field_type) == .Optional) {
360 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
361 if (need_comma) try stream.writeAll(", ");
362 try stream.print("{}=", .{arg_field.name});
363 try self.writeParamToStream(stream, non_optional, inst_table);
364 need_comma = true;
365 }
366 } else {
367 if (need_comma) try stream.writeAll(", ");
368 try stream.print("{}=", .{arg_field.name});
369 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
370 need_comma = true;
371 }
372 }
373
374 try stream.writeByte(')');
375 }
376
377 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {
378 if (@typeInfo(@TypeOf(param)) == .Enum) {
379 return stream.writeAll(@tagName(param));
380 }
381 switch (@TypeOf(param)) {
382 *Inst => return self.writeInstParamToStream(stream, param, inst_table),
383 []*Inst => {
384 try stream.writeByte('[');
385 for (param) |inst, i| {
386 if (i != 0) {
387 try stream.writeAll(", ");
388 }
389 try self.writeInstParamToStream(stream, inst, inst_table);
390 }
391 try stream.writeByte(']');
392 },
393 Inst.Fn.Body => {
394 try stream.writeAll("{\n");
395 for (param.instructions) |inst, i| {
396 try stream.print(" %{} ", .{i});
397 try self.writeInstToStream(stream, inst, inst_table);
398 try stream.writeByte('\n');
399 }
400 try stream.writeByte('}');
401 },
402 bool => return stream.writeByte("01"[@boolToInt(param)]),
403 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
404 BigInt => return stream.print("{}", .{param}),
405 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
406 }
407 }
408
409 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
410 const info = inst_table.getValue(inst).?;
411 const prefix = if (info.fn_body == null) "@" else "%";
412 try stream.print("{}{}", .{ prefix, info.index });
413 }
414};
415
416pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
417 var global_name_map = std.StringHashMap(usize).init(allocator);
418 defer global_name_map.deinit();
419
420 var parser: Parser = .{
421 .allocator = allocator,
422 .arena = std.heap.ArenaAllocator.init(allocator),
423 .i = 0,
424 .source = source,
425 .decls = std.ArrayList(*Inst).init(allocator),
426 .errors = std.ArrayList(ErrorMsg).init(allocator),
427 .global_name_map = &global_name_map,
428 };
429 errdefer parser.arena.deinit();
430
431 parser.parseRoot() catch |err| switch (err) {
432 error.ParseFailure => {
433 assert(parser.errors.items.len != 0);
434 },
435 else => |e| return e,
436 };
437 return Module{
438 .decls = parser.decls.toOwnedSlice(),
439 .errors = parser.errors.toOwnedSlice(),
440 .arena = parser.arena,
441 };
442}
443
444const Parser = struct {
445 allocator: *Allocator,
446 arena: std.heap.ArenaAllocator,
447 i: usize,
448 source: [:0]const u8,
449 errors: std.ArrayList(ErrorMsg),
450 decls: std.ArrayList(*Inst),
451 global_name_map: *std.StringHashMap(usize),
452
453 const Body = struct {
454 instructions: std.ArrayList(*Inst),
455 name_map: std.StringHashMap(usize),
456 };
457
458 fn parseBody(self: *Parser) !Inst.Fn.Body {
459 var body_context = Body{
460 .instructions = std.ArrayList(*Inst).init(self.allocator),
461 .name_map = std.StringHashMap(usize).init(self.allocator),
462 };
463 defer body_context.instructions.deinit();
464 defer body_context.name_map.deinit();
465
466 try requireEatBytes(self, "{");
467 skipSpace(self);
468
469 while (true) : (self.i += 1) switch (self.source[self.i]) {
470 ';' => _ = try skipToAndOver(self, '\n'),
471 '%' => {
472 self.i += 1;
473 const ident = try skipToAndOver(self, ' ');
474 skipSpace(self);
475 try requireEatBytes(self, "=");
476 skipSpace(self);
477 const inst = try parseInstruction(self, &body_context);
478 const ident_index = body_context.instructions.items.len;
479 if (try body_context.name_map.put(ident, ident_index)) |_| {
480 return self.fail("redefinition of identifier '{}'", .{ident});
481 }
482 try body_context.instructions.append(inst);
483 continue;
484 },
485 ' ', '\n' => continue,
486 '}' => {
487 self.i += 1;
488 break;
489 },
490 else => |byte| return self.failByte(byte),
491 };
492
493 return Inst.Fn.Body{
494 .instructions = body_context.instructions.toOwnedSlice(),
495 };
496 }
497
498 fn parseStringLiteral(self: *Parser) ![]u8 {
499 const start = self.i;
500 try self.requireEatBytes("\"");
501
502 while (true) : (self.i += 1) switch (self.source[self.i]) {
503 '"' => {
504 self.i += 1;
505 const span = self.source[start..self.i];
506 var bad_index: usize = undefined;
507 const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
508 error.InvalidCharacter => {
509 self.i = start + bad_index;
510 const bad_byte = self.source[self.i];
511 return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
512 },
513 else => |e| return e,
514 };
515 return parsed;
516 },
517 '\\' => {
518 self.i += 1;
519 continue;
520 },
521 0 => return self.failByte(0),
522 else => continue,
523 };
524 }
525
526 fn parseIntegerLiteral(self: *Parser) !BigInt {
527 const start = self.i;
528 if (self.source[self.i] == '-') self.i += 1;
529 while (true) : (self.i += 1) switch (self.source[self.i]) {
530 '0'...'9' => continue,
531 else => break,
532 };
533 const number_text = self.source[start..self.i];
534 var result = try BigInt.init(&self.arena.allocator);
535 result.setString(10, number_text) catch |err| {
536 self.i = start;
537 switch (err) {
538 error.InvalidBase => unreachable,
539 error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}),
540 error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}),
541 else => |e| return e,
542 }
543 };
544 return result;
545 }
546
547 fn parseRoot(self: *Parser) !void {
548 // The IR format is designed so that it can be tokenized and parsed at the same time.
549 while (true) : (self.i += 1) switch (self.source[self.i]) {
550 ';' => _ = try skipToAndOver(self, '\n'),
551 '@' => {
552 self.i += 1;
553 const ident = try skipToAndOver(self, ' ');
554 skipSpace(self);
555 try requireEatBytes(self, "=");
556 skipSpace(self);
557 const inst = try parseInstruction(self, null);
558 const ident_index = self.decls.items.len;
559 if (try self.global_name_map.put(ident, ident_index)) |_| {
560 return self.fail("redefinition of identifier '{}'", .{ident});
561 }
562 try self.decls.append(inst);
563 continue;
564 },
565 ' ', '\n' => continue,
566 0 => break,
567 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
568 };
569 }
570
571 fn eatByte(self: *Parser, byte: u8) bool {
572 if (self.source[self.i] != byte) return false;
573 self.i += 1;
574 return true;
575 }
576
577 fn skipSpace(self: *Parser) void {
578 while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
579 self.i += 1;
580 }
581 }
582
583 fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
584 const start = self.i;
585 for (bytes) |byte| {
586 if (self.source[self.i] != byte) {
587 self.i = start;
588 return self.fail("expected '{}'", .{bytes});
589 }
590 self.i += 1;
591 }
592 }
593
594 fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
595 const start_i = self.i;
596 while (self.source[self.i] != 0) : (self.i += 1) {
597 if (self.source[self.i] == byte) {
598 const result = self.source[start_i..self.i];
599 self.i += 1;
600 return result;
601 }
602 }
603 return self.fail("unexpected EOF", .{});
604 }
605
606 /// ParseFailure is an internal error code; handled in `parse`.
607 const InnerError = error{ ParseFailure, OutOfMemory };
608
609 fn failByte(self: *Parser, byte: u8) InnerError {
610 if (byte == 0) {
611 return self.fail("unexpected EOF", .{});
612 } else {
613 return self.fail("unexpected byte: '{c}'", .{byte});
614 }
615 }
616
617 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
618 @setCold(true);
619 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
620 (try self.errors.addOne()).* = .{
621 .byte_offset = self.i,
622 .msg = msg,
623 };
624 return error.ParseFailure;
625 }
626
627 fn parseInstruction(self: *Parser, body_ctx: ?*Body) InnerError!*Inst {
628 const fn_name = try skipToAndOver(self, '(');
629 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
630 if (mem.eql(u8, field.name, fn_name)) {
631 const tag = @field(Inst.Tag, field.name);
632 return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx);
633 }
634 }
635 return self.fail("unknown instruction '{}'", .{fn_name});
636 }
637
638 fn parseInstructionGeneric(
639 self: *Parser,
640 comptime fn_name: []const u8,
641 comptime InstType: type,
642 body_ctx: ?*Body,
643 ) !*Inst {
644 const inst_specific = try self.arena.allocator.create(InstType);
645 inst_specific.base = .{
646 .src = self.i,
647 .tag = InstType.base_tag,
648 };
649
650 if (@hasField(InstType, "ty")) {
651 inst_specific.ty = opt_type orelse {
652 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
653 };
654 }
655
656 const Positionals = @TypeOf(inst_specific.positionals);
657 inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
658 if (self.source[self.i] == ',') {
659 self.i += 1;
660 skipSpace(self);
661 } else if (self.source[self.i] == ')') {
662 return self.fail("expected positional parameter '{}'", .{arg_field.name});
663 }
664 @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
665 self,
666 arg_field.field_type,
667 body_ctx,
668 );
669 skipSpace(self);
670 }
671
672 const KW_Args = @TypeOf(inst_specific.kw_args);
673 inst_specific.kw_args = .{}; // assign defaults
674 skipSpace(self);
675 while (eatByte(self, ',')) {
676 skipSpace(self);
677 const name = try skipToAndOver(self, '=');
678 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
679 const field_name = arg_field.name;
680 if (mem.eql(u8, name, field_name)) {
681 const NonOptional = switch (@typeInfo(arg_field.field_type)) {
682 .Optional => |info| info.child,
683 else => arg_field.field_type,
684 };
685 @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
686 break;
687 }
688 } else {
689 return self.fail("unrecognized keyword parameter: '{}'", .{name});
690 }
691 skipSpace(self);
692 }
693 try requireEatBytes(self, ")");
694
695 return &inst_specific.base;
696 }
697
698 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
699 if (@typeInfo(T) == .Enum) {
700 const start = self.i;
701 while (true) : (self.i += 1) switch (self.source[self.i]) {
702 ' ', '\n', ',', ')' => {
703 const enum_name = self.source[start..self.i];
704 return std.meta.stringToEnum(T, enum_name) orelse {
705 return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
706 };
707 },
708 0 => return self.failByte(0),
709 else => continue,
710 };
711 }
712 switch (T) {
713 Inst.Fn.Body => return parseBody(self),
714 bool => {
715 const bool_value = switch (self.source[self.i]) {
716 '0' => false,
717 '1' => true,
718 else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
719 };
720 self.i += 1;
721 return bool_value;
722 },
723 []*Inst => {
724 try requireEatBytes(self, "[");
725 skipSpace(self);
726 if (eatByte(self, ']')) return &[0]*Inst{};
727
728 var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
729 while (true) {
730 skipSpace(self);
731 try instructions.append(try parseParameterInst(self, body_ctx));
732 skipSpace(self);
733 if (!eatByte(self, ',')) break;
734 }
735 try requireEatBytes(self, "]");
736 return instructions.toOwnedSlice();
737 },
738 *Inst => return parseParameterInst(self, body_ctx),
739 []u8, []const u8 => return self.parseStringLiteral(),
740 BigInt => return self.parseIntegerLiteral(),
741 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
742 }
743 return self.fail("TODO parse parameter {}", .{@typeName(T)});
744 }
745
746 fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
747 const local_ref = switch (self.source[self.i]) {
748 '@' => false,
749 '%' => true,
750 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
751 };
752 const map = if (local_ref)
753 if (body_ctx) |bc|
754 &bc.name_map
755 else
756 return self.fail("referencing a % instruction in global scope", .{})
757 else
758 self.global_name_map;
759
760 self.i += 1;
761 const name_start = self.i;
762 while (true) : (self.i += 1) switch (self.source[self.i]) {
763 0, ' ', '\n', ',', ')', ']' => break,
764 else => continue,
765 };
766 const ident = self.source[name_start..self.i];
767 const kv = map.get(ident) orelse {
768 const bad_name = self.source[name_start - 1 .. self.i];
769 self.i = name_start - 1;
770 return self.fail("unrecognized identifier: {}", .{bad_name});
771 };
772 if (local_ref) {
773 return body_ctx.?.instructions.items[kv.value];
774 } else {
775 return self.decls.items[kv.value];
776 }
777 }
778};
779
780pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module {
781 var ctx: EmitZIR = .{
782 .allocator = allocator,
783 .decls = std.ArrayList(*Inst).init(allocator),
784 .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
785 .arena = std.heap.ArenaAllocator.init(allocator),
786 .old_module = &old_module,
787 };
788 defer ctx.decls.deinit();
789 defer ctx.decl_table.deinit();
790 errdefer ctx.arena.deinit();
791
792 try ctx.emit();
793
794 return Module{
795 .decls = ctx.decls.toOwnedSlice(),
796 .arena = ctx.arena,
797 .errors = &[0]ErrorMsg{},
798 };
799}
800
801const EmitZIR = struct {
802 allocator: *Allocator,
803 arena: std.heap.ArenaAllocator,
804 old_module: *const ir.Module,
805 decls: std.ArrayList(*Inst),
806 decl_table: std.AutoHashMap(*ir.Inst, *Inst),
807
808 fn emit(self: *EmitZIR) !void {
809 for (self.old_module.exports) |module_export| {
810 const export_value = try self.emitTypedValue(module_export.src, module_export.typed_value);
811 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.name);
812 const export_inst = try self.arena.allocator.create(Inst.Export);
813 export_inst.* = .{
814 .base = .{ .src = module_export.src, .tag = Inst.Export.base_tag },
815 .positionals = .{
816 .symbol_name = symbol_name,
817 .value = export_value,
818 },
819 .kw_args = .{},
820 };
821 try self.decls.append(&export_inst.base);
822 }
823 }
824
825 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
826 if (inst.cast(ir.Inst.Constant)) |const_inst| {
827 if (self.decl_table.getValue(inst)) |decl| {
828 return decl;
829 }
830 const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
831 try self.decl_table.putNoClobber(inst, new_decl);
832 return new_decl;
833 } else {
834 return inst_table.getValue(inst).?;
835 }
836 }
837
838 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
839 const int_inst = try self.arena.allocator.create(Inst.Int);
840 int_inst.* = .{
841 .base = .{ .src = src, .tag = Inst.Int.base_tag },
842 .positionals = .{
843 .int = try val.toBigInt(&self.arena.allocator),
844 },
845 .kw_args = .{},
846 };
847 try self.decls.append(&int_inst.base);
848 return &int_inst.base;
849 }
850
851 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: ir.TypedValue) Allocator.Error!*Inst {
852 switch (typed_value.ty.zigTypeTag()) {
853 .Pointer => {
854 const ptr_elem_type = typed_value.ty.elemType();
855 switch (ptr_elem_type.zigTypeTag()) {
856 .Array => {
857 // TODO more checks to make sure this can be emitted as a string literal
858 //const array_elem_type = ptr_elem_type.elemType();
859 //if (array_elem_type.eql(Type.initTag(.u8)) and
860 // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
861 //{
862 //}
863 const bytes = try typed_value.val.toAllocatedBytes(&self.arena.allocator);
864 return self.emitStringLiteral(src, bytes);
865 },
866 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
867 }
868 },
869 .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
870 .Int => {
871 const as_inst = try self.arena.allocator.create(Inst.As);
872 as_inst.* = .{
873 .base = .{ .src = src, .tag = Inst.As.base_tag },
874 .positionals = .{
875 .dest_type = try self.emitType(src, typed_value.ty),
876 .value = try self.emitComptimeIntVal(src, typed_value.val),
877 },
878 .kw_args = .{},
879 };
880 try self.decls.append(&as_inst.base);
881
882 return &as_inst.base;
883 },
884 .Type => {
885 const ty = typed_value.val.toType();
886 return self.emitType(src, ty);
887 },
888 .Fn => {
889 const index = typed_value.val.cast(Value.Payload.Function).?.index;
890 const module_fn = self.old_module.fns[index];
891
892 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
893 defer inst_table.deinit();
894
895 var instructions = std.ArrayList(*Inst).init(self.allocator);
896 defer instructions.deinit();
897
898 for (module_fn.body) |inst| {
899 const new_inst = switch (inst.tag) {
900 .unreach => blk: {
901 const unreach_inst = try self.arena.allocator.create(Inst.Unreachable);
902 unreach_inst.* = .{
903 .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag },
904 .positionals = .{},
905 .kw_args = .{},
906 };
907 break :blk &unreach_inst.base;
908 },
909 .constant => unreachable, // excluded from function bodies
910 .assembly => blk: {
911 const old_inst = inst.cast(ir.Inst.Assembly).?;
912 const new_inst = try self.arena.allocator.create(Inst.Asm);
913
914 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
915 for (inputs) |*elem, i| {
916 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
917 }
918
919 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
920 for (clobbers) |*elem, i| {
921 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
922 }
923
924 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
925 for (args) |*elem, i| {
926 elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]);
927 }
928
929 new_inst.* = .{
930 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
931 .positionals = .{
932 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
933 .return_type = try self.emitType(inst.src, inst.ty),
934 },
935 .kw_args = .{
936 .@"volatile" = old_inst.args.is_volatile,
937 .output = if (old_inst.args.output) |o|
938 try self.emitStringLiteral(inst.src, o)
939 else
940 null,
941 .inputs = inputs,
942 .clobbers = clobbers,
943 .args = args,
944 },
945 };
946 break :blk &new_inst.base;
947 },
948 .ptrtoint => blk: {
949 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
950 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
951 new_inst.* = .{
952 .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
953 .positionals = .{
954 .ptr = try self.resolveInst(&inst_table, old_inst.args.ptr),
955 },
956 .kw_args = .{},
957 };
958 break :blk &new_inst.base;
959 },
960 };
961 try instructions.append(new_inst);
962 try inst_table.putNoClobber(inst, new_inst);
963 }
964
965 const fn_type = try self.emitType(src, module_fn.fn_type);
966
967 const fn_inst = try self.arena.allocator.create(Inst.Fn);
968 fn_inst.* = .{
969 .base = .{ .src = src, .tag = Inst.Fn.base_tag },
970 .positionals = .{
971 .fn_type = fn_type,
972 .body = .{
973 .instructions = instructions.toOwnedSlice(),
974 },
975 },
976 .kw_args = .{},
977 };
978 try self.decls.append(&fn_inst.base);
979 return &fn_inst.base;
980 },
981 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
982 }
983 }
984
985 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
986 switch (ty.tag()) {
987 .isize => return self.emitPrimitiveType(src, .isize),
988 .usize => return self.emitPrimitiveType(src, .usize),
989 .c_short => return self.emitPrimitiveType(src, .c_short),
990 .c_ushort => return self.emitPrimitiveType(src, .c_ushort),
991 .c_int => return self.emitPrimitiveType(src, .c_int),
992 .c_uint => return self.emitPrimitiveType(src, .c_uint),
993 .c_long => return self.emitPrimitiveType(src, .c_long),
994 .c_ulong => return self.emitPrimitiveType(src, .c_ulong),
995 .c_longlong => return self.emitPrimitiveType(src, .c_longlong),
996 .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),
997 .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),
998 .c_void => return self.emitPrimitiveType(src, .c_void),
999 .f16 => return self.emitPrimitiveType(src, .f16),
1000 .f32 => return self.emitPrimitiveType(src, .f32),
1001 .f64 => return self.emitPrimitiveType(src, .f64),
1002 .f128 => return self.emitPrimitiveType(src, .f128),
1003 .anyerror => return self.emitPrimitiveType(src, .anyerror),
1004 else => switch (ty.zigTypeTag()) {
1005 .Bool => return self.emitPrimitiveType(src, .bool),
1006 .Void => return self.emitPrimitiveType(src, .void),
1007 .NoReturn => return self.emitPrimitiveType(src, .noreturn),
1008 .Type => return self.emitPrimitiveType(src, .type),
1009 .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),
1010 .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),
1011 .Fn => {
1012 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
1013 defer self.allocator.free(param_types);
1014
1015 ty.fnParamTypes(param_types);
1016 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
1017 for (param_types) |param_type, i| {
1018 emitted_params[i] = try self.emitType(src, param_type);
1019 }
1020
1021 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
1022 fntype_inst.* = .{
1023 .base = .{ .src = src, .tag = Inst.FnType.base_tag },
1024 .positionals = .{
1025 .param_types = emitted_params,
1026 .return_type = try self.emitType(src, ty.fnReturnType()),
1027 },
1028 .kw_args = .{
1029 .cc = ty.fnCallingConvention(),
1030 },
1031 };
1032 try self.decls.append(&fntype_inst.base);
1033 return &fntype_inst.base;
1034 },
1035 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1036 },
1037 }
1038 }
1039
1040 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
1041 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1042 primitive_inst.* = .{
1043 .base = .{ .src = src, .tag = Inst.Primitive.base_tag },
1044 .positionals = .{
1045 .tag = tag,
1046 },
1047 .kw_args = .{},
1048 };
1049 try self.decls.append(&primitive_inst.base);
1050 return &primitive_inst.base;
1051 }
1052
1053 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
1054 const str_inst = try self.arena.allocator.create(Inst.Str);
1055 str_inst.* = .{
1056 .base = .{ .src = src, .tag = Inst.Str.base_tag },
1057 .positionals = .{
1058 .bytes = str,
1059 },
1060 .kw_args = .{},
1061 };
1062 try self.decls.append(&str_inst.base);
1063 return &str_inst.base;
1064 }
1065};
src-self-hosted/type.zig+757-1005
......@@ -1,1075 +1,827 @@
11const std = @import("std");
2const builtin = std.builtin;
3const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;
52const Value = @import("value.zig").Value;
6const llvm = @import("llvm.zig");
7const event = std.event;
8const Allocator = std.mem.Allocator;
93const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;
5const Target = std.Target;
6
7/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
8/// It's important for this struct to be small.
9/// It is not copyable since it may contain references to its inner data.
10/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
11/// of obtaining a lock on a global type table, as well as making the
12/// garbage collection bookkeeping simpler.
13/// This union takes advantage of the fact that the first page of memory
14/// is unmapped, giving us 4096 possible enum tags that have no payload.
15pub const Type = extern union {
16 /// If the tag value is less than Tag.no_payload_count, then no pointer
17 /// dereference is needed.
18 tag_if_small_enough: usize,
19 ptr_otherwise: *Payload,
20
21 pub fn zigTypeTag(self: Type) std.builtin.TypeId {
22 switch (self.tag()) {
23 .@"u8",
24 .@"i8",
25 .@"isize",
26 .@"usize",
27 .@"c_short",
28 .@"c_ushort",
29 .@"c_int",
30 .@"c_uint",
31 .@"c_long",
32 .@"c_ulong",
33 .@"c_longlong",
34 .@"c_ulonglong",
35 .@"c_longdouble",
36 => return .Int,
37
38 .@"f16",
39 .@"f32",
40 .@"f64",
41 .@"f128",
42 => return .Float,
43
44 .@"c_void" => return .Opaque,
45 .@"bool" => return .Bool,
46 .@"void" => return .Void,
47 .@"type" => return .Type,
48 .@"anyerror" => return .ErrorSet,
49 .@"comptime_int" => return .ComptimeInt,
50 .@"comptime_float" => return .ComptimeFloat,
51 .@"noreturn" => return .NoReturn,
52
53 .fn_naked_noreturn_no_args => return .Fn,
54
55 .array, .array_u8_sentinel_0 => return .Array,
56 .single_const_pointer => return .Pointer,
57 .single_const_pointer_to_comptime_int => return .Pointer,
58 .const_slice_u8 => return .Pointer,
59 }
60 }
1061
11pub const Type = struct {
12 base: Value,
13 id: Id,
14 name: []const u8,
15 abi_alignment: AbiAlignment,
16
17 pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
18
19 pub const Id = builtin.TypeId;
62 pub fn initTag(comptime small_tag: Tag) Type {
63 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
64 return .{ .tag_if_small_enough = @enumToInt(small_tag) };
65 }
2066
21 pub fn destroy(base: *Type, comp: *Compilation) void {
22 switch (base.id) {
23 .Struct => @fieldParentPtr(Struct, "base", base).destroy(comp),
24 .Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
25 .Type => @fieldParentPtr(MetaType, "base", base).destroy(comp),
26 .Void => @fieldParentPtr(Void, "base", base).destroy(comp),
27 .Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
28 .NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
29 .Int => @fieldParentPtr(Int, "base", base).destroy(comp),
30 .Float => @fieldParentPtr(Float, "base", base).destroy(comp),
31 .Pointer => @fieldParentPtr(Pointer, "base", base).destroy(comp),
32 .Array => @fieldParentPtr(Array, "base", base).destroy(comp),
33 .ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp),
34 .ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp),
35 .EnumLiteral => @fieldParentPtr(EnumLiteral, "base", base).destroy(comp),
36 .Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp),
37 .Null => @fieldParentPtr(Null, "base", base).destroy(comp),
38 .Optional => @fieldParentPtr(Optional, "base", base).destroy(comp),
39 .ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(comp),
40 .ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp),
41 .Enum => @fieldParentPtr(Enum, "base", base).destroy(comp),
42 .Union => @fieldParentPtr(Union, "base", base).destroy(comp),
43 .BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp),
44 .Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp),
45 .Frame => @fieldParentPtr(Frame, "base", base).destroy(comp),
46 .AnyFrame => @fieldParentPtr(AnyFrame, "base", base).destroy(comp),
47 .Vector => @fieldParentPtr(Vector, "base", base).destroy(comp),
48 }
67 pub fn initPayload(payload: *Payload) Type {
68 assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
69 return .{ .ptr_otherwise = payload };
4970 }
5071
51 pub fn getLlvmType(
52 base: *Type,
53 allocator: *Allocator,
54 llvm_context: *llvm.Context,
55 ) error{OutOfMemory}!*llvm.Type {
56 switch (base.id) {
57 .Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
58 .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
59 .Type => unreachable,
60 .Void => unreachable,
61 .Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
62 .NoReturn => unreachable,
63 .Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
64 .Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
65 .Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
66 .Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
67 .ComptimeFloat => unreachable,
68 .ComptimeInt => unreachable,
69 .EnumLiteral => unreachable,
70 .Undefined => unreachable,
71 .Null => unreachable,
72 .Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
73 .ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
74 .ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
75 .Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
76 .Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
77 .BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
78 .Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
79 .Frame => return @fieldParentPtr(Frame, "base", base).getLlvmType(allocator, llvm_context),
80 .AnyFrame => return @fieldParentPtr(AnyFrame, "base", base).getLlvmType(allocator, llvm_context),
81 .Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context),
72 pub fn tag(self: Type) Tag {
73 if (self.tag_if_small_enough < Tag.no_payload_count) {
74 return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
75 } else {
76 return self.ptr_otherwise.tag;
8277 }
8378 }
8479
85 pub fn handleIsPtr(base: *Type) bool {
86 switch (base.id) {
87 .Type,
88 .ComptimeFloat,
89 .ComptimeInt,
90 .EnumLiteral,
91 .Undefined,
92 .Null,
93 .BoundFn,
94 .Opaque,
95 => unreachable,
80 pub fn cast(self: Type, comptime T: type) ?*T {
81 if (self.tag_if_small_enough < Tag.no_payload_count)
82 return null;
83
84 const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
85 if (self.ptr_otherwise.tag != expected_tag)
86 return null;
9687
97 .NoReturn,
98 .Void,
99 .Bool,
100 .Int,
88 return @fieldParentPtr(T, "base", self.ptr_otherwise);
89 }
90
91 pub fn eql(self: Type, other: Type) bool {
92 //std.debug.warn("test {} == {}\n", .{ self, other });
93 // As a shortcut, if the small tags / addresses match, we're done.
94 if (self.tag_if_small_enough == other.tag_if_small_enough)
95 return true;
96 const zig_tag_a = self.zigTypeTag();
97 const zig_tag_b = self.zigTypeTag();
98 if (zig_tag_a != zig_tag_b)
99 return false;
100 switch (zig_tag_a) {
101 .Type => return true,
102 .Void => return true,
103 .Bool => return true,
104 .NoReturn => return true,
105 .ComptimeFloat => return true,
106 .ComptimeInt => return true,
107 .Undefined => return true,
108 .Null => return true,
109 .Pointer => {
110 const is_slice_a = isSlice(self);
111 const is_slice_b = isSlice(other);
112 if (is_slice_a != is_slice_b)
113 return false;
114 @panic("TODO implement more pointer Type equality comparison");
115 },
116 .Int => {
117 if (self.tag() != other.tag()) {
118 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
119 return false;
120 }
121 // The target will not be branched upon, because we handled target-dependent cases above.
122 const info_a = self.intInfo(@as(Target, undefined));
123 const info_b = self.intInfo(@as(Target, undefined));
124 return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
125 },
101126 .Float,
102 .Pointer,
127 .Array,
128 .Struct,
129 .Optional,
130 .ErrorUnion,
103131 .ErrorSet,
104132 .Enum,
133 .Union,
105134 .Fn,
106 .Frame,
107 .AnyFrame,
108 .Vector,
109 => return false,
110
111 .Struct => @panic("TODO"),
112 .Array => @panic("TODO"),
113 .Optional => @panic("TODO"),
114 .ErrorUnion => @panic("TODO"),
115 .Union => @panic("TODO"),
116 }
117 }
118
119 pub fn hasBits(base: *Type) bool {
120 switch (base.id) {
121 .Type,
122 .ComptimeFloat,
123 .ComptimeInt,
124 .EnumLiteral,
125 .Undefined,
126 .Null,
127135 .BoundFn,
128136 .Opaque,
129 => unreachable,
130
131 .Void,
132 .NoReturn,
133 => return false,
134
135 .Bool,
136 .Int,
137 .Float,
138 .Fn,
139137 .Frame,
140138 .AnyFrame,
141139 .Vector,
142 => return true,
143
144 .Pointer => {
145 const ptr_type = @fieldParentPtr(Pointer, "base", base);
146 return ptr_type.key.child_type.hasBits();
147 },
148
149 .ErrorSet => @panic("TODO"),
150 .Enum => @panic("TODO"),
151 .Struct => @panic("TODO"),
152 .Array => @panic("TODO"),
153 .Optional => @panic("TODO"),
154 .ErrorUnion => @panic("TODO"),
155 .Union => @panic("TODO"),
140 .EnumLiteral,
141 => @panic("TODO implement more Type equality comparison"),
156142 }
157143 }
158144
159 pub fn cast(base: *Type, comptime T: type) ?*T {
160 if (base.id != @field(Id, @typeName(T))) return null;
161 return @fieldParentPtr(T, "base", base);
162 }
163
164 pub fn dump(base: *const Type) void {
165 std.debug.warn("{}", .{@tagName(base.id)});
145 pub fn format(
146 self: Type,
147 comptime fmt: []const u8,
148 options: std.fmt.FormatOptions,
149 out_stream: var,
150 ) !void {
151 comptime assert(fmt.len == 0);
152 var ty = self;
153 while (true) {
154 const t = ty.tag();
155 switch (t) {
156 .@"u8",
157 .@"i8",
158 .@"isize",
159 .@"usize",
160 .@"c_short",
161 .@"c_ushort",
162 .@"c_int",
163 .@"c_uint",
164 .@"c_long",
165 .@"c_ulong",
166 .@"c_longlong",
167 .@"c_ulonglong",
168 .@"c_longdouble",
169 .@"c_void",
170 .@"f16",
171 .@"f32",
172 .@"f64",
173 .@"f128",
174 .@"bool",
175 .@"void",
176 .@"type",
177 .@"anyerror",
178 .@"comptime_int",
179 .@"comptime_float",
180 .@"noreturn",
181 => return out_stream.writeAll(@tagName(t)),
182
183 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
184 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
185 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
186
187 .array_u8_sentinel_0 => {
188 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
189 return out_stream.print("[{}:0]u8", .{payload.len});
190 },
191 .array => {
192 const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
193 try out_stream.print("[{}]", .{payload.len});
194 ty = payload.elem_type;
195 continue;
196 },
197 .single_const_pointer => {
198 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", ty.ptr_otherwise);
199 try out_stream.writeAll("*const ");
200 ty = payload.pointee_type;
201 continue;
202 },
203 }
204 unreachable;
205 }
166206 }
167207
168 fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
169 base.* = Type{
170 .base = Value{
171 .id = .Type,
172 .typ = &MetaType.get(comp).base,
173 .ref_count = std.atomic.Int(usize).init(1),
208 pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
209 switch (self.tag()) {
210 .@"u8" => return Value.initTag(.u8_type),
211 .@"i8" => return Value.initTag(.i8_type),
212 .@"isize" => return Value.initTag(.isize_type),
213 .@"usize" => return Value.initTag(.usize_type),
214 .@"c_short" => return Value.initTag(.c_short_type),
215 .@"c_ushort" => return Value.initTag(.c_ushort_type),
216 .@"c_int" => return Value.initTag(.c_int_type),
217 .@"c_uint" => return Value.initTag(.c_uint_type),
218 .@"c_long" => return Value.initTag(.c_long_type),
219 .@"c_ulong" => return Value.initTag(.c_ulong_type),
220 .@"c_longlong" => return Value.initTag(.c_longlong_type),
221 .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),
222 .@"c_longdouble" => return Value.initTag(.c_longdouble_type),
223 .@"c_void" => return Value.initTag(.c_void_type),
224 .@"f16" => return Value.initTag(.f16_type),
225 .@"f32" => return Value.initTag(.f32_type),
226 .@"f64" => return Value.initTag(.f64_type),
227 .@"f128" => return Value.initTag(.f128_type),
228 .@"bool" => return Value.initTag(.bool_type),
229 .@"void" => return Value.initTag(.void_type),
230 .@"type" => return Value.initTag(.type_type),
231 .@"anyerror" => return Value.initTag(.anyerror_type),
232 .@"comptime_int" => return Value.initTag(.comptime_int_type),
233 .@"comptime_float" => return Value.initTag(.comptime_float_type),
234 .@"noreturn" => return Value.initTag(.noreturn_type),
235 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
236 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
237 .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
238 else => {
239 const ty_payload = try allocator.create(Value.Payload.Ty);
240 ty_payload.* = .{ .ty = self };
241 return Value.initPayload(&ty_payload.base);
174242 },
175 .id = id,
176 .name = name,
177 .abi_alignment = AbiAlignment.init(),
178 };
179 }
180
181 /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
182 /// Otherwise, this one will grab one from the pool and then release it.
183 pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
184 if (base.abi_alignment.start()) |ptr| return ptr.*;
185
186 {
187 const held = try comp.zig_compiler.getAnyLlvmContext();
188 defer held.release(comp.zig_compiler);
189
190 const llvm_context = held.node.data;
191
192 base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
193243 }
194 base.abi_alignment.resolve();
195 return base.abi_alignment.data;
196244 }
197245
198 /// If you have an llvm conext handy, you can use it here.
199 pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
200 if (base.abi_alignment.start()) |ptr| return ptr.*;
201
202 base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
203 base.abi_alignment.resolve();
204 return base.abi_alignment.data;
246 pub fn isSinglePointer(self: Type) bool {
247 return switch (self.tag()) {
248 .@"u8",
249 .@"i8",
250 .@"isize",
251 .@"usize",
252 .@"c_short",
253 .@"c_ushort",
254 .@"c_int",
255 .@"c_uint",
256 .@"c_long",
257 .@"c_ulong",
258 .@"c_longlong",
259 .@"c_ulonglong",
260 .@"c_longdouble",
261 .@"f16",
262 .@"f32",
263 .@"f64",
264 .@"f128",
265 .@"c_void",
266 .@"bool",
267 .@"void",
268 .@"type",
269 .@"anyerror",
270 .@"comptime_int",
271 .@"comptime_float",
272 .@"noreturn",
273 .array,
274 .array_u8_sentinel_0,
275 .const_slice_u8,
276 .fn_naked_noreturn_no_args,
277 => false,
278
279 .single_const_pointer,
280 .single_const_pointer_to_comptime_int,
281 => true,
282 };
205283 }
206284
207 /// Lower level function that does the work. See getAbiAlignment.
208 fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
209 const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
210 return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
285 pub fn isSlice(self: Type) bool {
286 return switch (self.tag()) {
287 .@"u8",
288 .@"i8",
289 .@"isize",
290 .@"usize",
291 .@"c_short",
292 .@"c_ushort",
293 .@"c_int",
294 .@"c_uint",
295 .@"c_long",
296 .@"c_ulong",
297 .@"c_longlong",
298 .@"c_ulonglong",
299 .@"c_longdouble",
300 .@"f16",
301 .@"f32",
302 .@"f64",
303 .@"f128",
304 .@"c_void",
305 .@"bool",
306 .@"void",
307 .@"type",
308 .@"anyerror",
309 .@"comptime_int",
310 .@"comptime_float",
311 .@"noreturn",
312 .array,
313 .array_u8_sentinel_0,
314 .single_const_pointer,
315 .single_const_pointer_to_comptime_int,
316 .fn_naked_noreturn_no_args,
317 => false,
318
319 .const_slice_u8 => true,
320 };
211321 }
212322
213 pub const Struct = struct {
214 base: Type,
215 decls: *Scope.Decls,
216
217 pub fn destroy(self: *Struct, comp: *Compilation) void {
218 comp.gpa().destroy(self);
219 }
220
221 pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
222 @panic("TODO");
223 }
224 };
225
226 pub const Fn = struct {
227 base: Type,
228 key: Key,
229 non_key: NonKey,
230 garbage_node: std.atomic.Stack(*Fn).Node,
323 /// Asserts the type is a pointer type.
324 pub fn pointerIsConst(self: Type) bool {
325 return switch (self.tag()) {
326 .@"u8",
327 .@"i8",
328 .@"isize",
329 .@"usize",
330 .@"c_short",
331 .@"c_ushort",
332 .@"c_int",
333 .@"c_uint",
334 .@"c_long",
335 .@"c_ulong",
336 .@"c_longlong",
337 .@"c_ulonglong",
338 .@"c_longdouble",
339 .@"f16",
340 .@"f32",
341 .@"f64",
342 .@"f128",
343 .@"c_void",
344 .@"bool",
345 .@"void",
346 .@"type",
347 .@"anyerror",
348 .@"comptime_int",
349 .@"comptime_float",
350 .@"noreturn",
351 .array,
352 .array_u8_sentinel_0,
353 .fn_naked_noreturn_no_args,
354 => unreachable,
231355
232 pub const Kind = enum {
233 Normal,
234 Generic,
356 .single_const_pointer,
357 .single_const_pointer_to_comptime_int,
358 .const_slice_u8,
359 => true,
235360 };
361 }
236362
237 pub const NonKey = union {
238 Normal: Normal,
239 Generic: void,
363 /// Asserts the type is a pointer or array type.
364 pub fn elemType(self: Type) Type {
365 return switch (self.tag()) {
366 .@"u8",
367 .@"i8",
368 .@"isize",
369 .@"usize",
370 .@"c_short",
371 .@"c_ushort",
372 .@"c_int",
373 .@"c_uint",
374 .@"c_long",
375 .@"c_ulong",
376 .@"c_longlong",
377 .@"c_ulonglong",
378 .@"c_longdouble",
379 .@"f16",
380 .@"f32",
381 .@"f64",
382 .@"f128",
383 .@"c_void",
384 .@"bool",
385 .@"void",
386 .@"type",
387 .@"anyerror",
388 .@"comptime_int",
389 .@"comptime_float",
390 .@"noreturn",
391 .fn_naked_noreturn_no_args,
392 => unreachable,
240393
241 pub const Normal = struct {
242 variable_list: std.ArrayList(*Scope.Var),
243 };
394 .array => self.cast(Payload.Array).?.elem_type,
395 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
396 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
397 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
244398 };
399 }
245400
246 pub const Key = struct {
247 data: Data,
248 alignment: ?u32,
249
250 pub const Data = union(Kind) {
251 Generic: Generic,
252 Normal: Normal,
253 };
254
255 pub const Normal = struct {
256 params: []Param,
257 return_type: *Type,
258 is_var_args: bool,
259 cc: CallingConvention,
260 };
261
262 pub const Generic = struct {
263 param_count: usize,
264 cc: CallingConvention,
265 };
266
267 pub fn hash(self: *const Key) u32 {
268 var result: u32 = 0;
269 result +%= hashAny(self.alignment, 0);
270 switch (self.data) {
271 .Generic => |generic| {
272 result +%= hashAny(generic.param_count, 1);
273 result +%= hashAny(generic.cc, 3);
274 },
275 .Normal => |normal| {
276 result +%= hashAny(normal.return_type, 4);
277 result +%= hashAny(normal.is_var_args, 5);
278 result +%= hashAny(normal.cc, 6);
279 for (normal.params) |param| {
280 result +%= hashAny(param.is_noalias, 7);
281 result +%= hashAny(param.typ, 8);
282 }
283 },
284 }
285 return result;
286 }
287
288 pub fn eql(self: *const Key, other: *const Key) bool {
289 if ((self.alignment == null) != (other.alignment == null)) return false;
290 if (self.alignment) |self_align| {
291 if (self_align != other.alignment.?) return false;
292 }
293 if (@as(@TagType(Data), self.data) != @as(@TagType(Data), other.data)) return false;
294 switch (self.data) {
295 .Generic => |*self_generic| {
296 const other_generic = &other.data.Generic;
297 if (self_generic.param_count != other_generic.param_count) return false;
298 if (self_generic.cc != other_generic.cc) return false;
299 },
300 .Normal => |*self_normal| {
301 const other_normal = &other.data.Normal;
302 if (self_normal.cc != other_normal.cc) return false;
303 if (self_normal.is_var_args != other_normal.is_var_args) return false;
304 if (self_normal.return_type != other_normal.return_type) return false;
305 for (self_normal.params) |*self_param, i| {
306 const other_param = &other_normal.params[i];
307 if (self_param.is_noalias != other_param.is_noalias) return false;
308 if (self_param.typ != other_param.typ) return false;
309 }
310 },
311 }
312 return true;
313 }
314
315 pub fn deref(key: Key, comp: *Compilation) void {
316 switch (key.data) {
317 .Generic => {},
318 .Normal => |normal| {
319 normal.return_type.base.deref(comp);
320 for (normal.params) |param| {
321 param.typ.base.deref(comp);
322 }
323 },
324 }
325 }
401 /// Asserts the type is an array.
402 pub fn arrayLen(self: Type) u64 {
403 return switch (self.tag()) {
404 .u8,
405 .i8,
406 .isize,
407 .usize,
408 .c_short,
409 .c_ushort,
410 .c_int,
411 .c_uint,
412 .c_long,
413 .c_ulong,
414 .c_longlong,
415 .c_ulonglong,
416 .c_longdouble,
417 .f16,
418 .f32,
419 .f64,
420 .f128,
421 .c_void,
422 .bool,
423 .void,
424 .type,
425 .anyerror,
426 .comptime_int,
427 .comptime_float,
428 .noreturn,
429 .fn_naked_noreturn_no_args,
430 .single_const_pointer,
431 .single_const_pointer_to_comptime_int,
432 .const_slice_u8,
433 => unreachable,
326434
327 pub fn ref(key: Key) void {
328 switch (key.data) {
329 .Generic => {},
330 .Normal => |normal| {
331 normal.return_type.base.ref();
332 for (normal.params) |param| {
333 param.typ.base.ref();
334 }
335 },
336 }
337 }
435 .array => self.cast(Payload.Array).?.len,
436 .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
338437 };
438 }
339439
340 const CallingConvention = builtin.CallingConvention;
440 /// Asserts the type is a fixed-width integer.
441 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
442 return switch (self.tag()) {
443 .@"f16",
444 .@"f32",
445 .@"f64",
446 .@"f128",
447 .@"c_longdouble",
448 .@"c_void",
449 .@"bool",
450 .@"void",
451 .@"type",
452 .@"anyerror",
453 .@"comptime_int",
454 .@"comptime_float",
455 .@"noreturn",
456 .fn_naked_noreturn_no_args,
457 .array,
458 .single_const_pointer,
459 .single_const_pointer_to_comptime_int,
460 .array_u8_sentinel_0,
461 .const_slice_u8,
462 => unreachable,
341463
342 pub const Param = struct {
343 is_noalias: bool,
344 typ: *Type,
464 .@"u8" => .{ .signed = false, .bits = 8 },
465 .@"i8" => .{ .signed = true, .bits = 8 },
466 .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
467 .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
468 .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },
469 .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },
470 .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },
471 .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },
472 .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },
473 .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },
474 .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },
475 .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) },
345476 };
477 }
346478
347 fn ccFnTypeStr(cc: CallingConvention) []const u8 {
348 return switch (cc) {
349 .Unspecified => "",
350 .C => "extern ",
351 .Cold => "coldcc ",
352 .Naked => "nakedcc ",
353 .Stdcall => "stdcallcc ",
354 .Async => "async ",
355 else => unreachable,
356 };
357 }
358
359 pub fn paramCount(self: *Fn) usize {
360 return switch (self.key.data) {
361 .Generic => |generic| generic.param_count,
362 .Normal => |normal| normal.params.len,
363 };
364 }
365
366 /// takes ownership of key.Normal.params on success
367 pub fn get(comp: *Compilation, key: Key) !*Fn {
368 {
369 const held = comp.fn_type_table.acquire();
370 defer held.release();
371
372 if (held.value.get(&key)) |entry| {
373 entry.value.base.base.ref();
374 return entry.value;
375 }
376 }
377
378 key.ref();
379 errdefer key.deref(comp);
380
381 const self = try comp.gpa().create(Fn);
382 self.* = Fn{
383 .base = undefined,
384 .key = key,
385 .non_key = undefined,
386 .garbage_node = undefined,
387 };
388 errdefer comp.gpa().destroy(self);
389
390 var name_buf = std.ArrayList(u8).init(comp.gpa());
391 defer name_buf.deinit();
392
393 const name_stream = name_buf.outStream();
394
395 switch (key.data) {
396 .Generic => |generic| {
397 self.non_key = NonKey{ .Generic = {} };
398 const cc_str = ccFnTypeStr(generic.cc);
399 try name_stream.print("{}fn(", .{cc_str});
400 var param_i: usize = 0;
401 while (param_i < generic.param_count) : (param_i += 1) {
402 const arg = if (param_i == 0) "var" else ", var";
403 try name_stream.write(arg);
404 }
405 try name_stream.write(")");
406 if (key.alignment) |alignment| {
407 try name_stream.print(" align({})", .{alignment});
408 }
409 try name_stream.write(" var");
410 },
411 .Normal => |normal| {
412 self.non_key = NonKey{
413 .Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) },
414 };
415 const cc_str = ccFnTypeStr(normal.cc);
416 try name_stream.print("{}fn(", .{cc_str});
417 for (normal.params) |param, i| {
418 if (i != 0) try name_stream.write(", ");
419 if (param.is_noalias) try name_stream.write("noalias ");
420 try name_stream.write(param.typ.name);
421 }
422 if (normal.is_var_args) {
423 if (normal.params.len != 0) try name_stream.write(", ");
424 try name_stream.write("...");
425 }
426 try name_stream.write(")");
427 if (key.alignment) |alignment| {
428 try name_stream.print(" align({})", .{alignment});
429 }
430 try name_stream.print(" {}", .{normal.return_type.name});
431 },
432 }
433
434 self.base.init(comp, .Fn, name_buf.toOwnedSlice());
435
436 {
437 const held = comp.fn_type_table.acquire();
438 defer held.release();
439
440 _ = try held.value.put(&self.key, self);
441 }
442 return self;
443 }
444
445 pub fn destroy(self: *Fn, comp: *Compilation) void {
446 self.key.deref(comp);
447 switch (self.key.data) {
448 .Generic => {},
449 .Normal => {
450 self.non_key.Normal.variable_list.deinit();
451 },
452 }
453 comp.gpa().destroy(self);
454 }
455
456 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
457 const normal = &self.key.data.Normal;
458 const llvm_return_type = switch (normal.return_type.id) {
459 .Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
460 else => try normal.return_type.getLlvmType(allocator, llvm_context),
461 };
462 const llvm_param_types = try allocator.alloc(*llvm.Type, normal.params.len);
463 defer allocator.free(llvm_param_types);
464 for (llvm_param_types) |*llvm_param_type, i| {
465 llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
466 }
467
468 return llvm.FunctionType(
469 llvm_return_type,
470 llvm_param_types.ptr,
471 @intCast(c_uint, llvm_param_types.len),
472 @boolToInt(normal.is_var_args),
473 ) orelse error.OutOfMemory;
474 }
475 };
476
477 pub const MetaType = struct {
478 base: Type,
479 value: *Type,
480
481 /// Adds 1 reference to the resulting type
482 pub fn get(comp: *Compilation) *MetaType {
483 comp.meta_type.base.base.ref();
484 return comp.meta_type;
485 }
486
487 pub fn destroy(self: *MetaType, comp: *Compilation) void {
488 comp.gpa().destroy(self);
489 }
490 };
491
492 pub const Void = struct {
493 base: Type,
494
495 /// Adds 1 reference to the resulting type
496 pub fn get(comp: *Compilation) *Void {
497 comp.void_type.base.base.ref();
498 return comp.void_type;
499 }
500
501 pub fn destroy(self: *Void, comp: *Compilation) void {
502 comp.gpa().destroy(self);
503 }
504 };
505
506 pub const Bool = struct {
507 base: Type,
508
509 /// Adds 1 reference to the resulting type
510 pub fn get(comp: *Compilation) *Bool {
511 comp.bool_type.base.base.ref();
512 return comp.bool_type;
513 }
514
515 pub fn destroy(self: *Bool, comp: *Compilation) void {
516 comp.gpa().destroy(self);
517 }
518
519 pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
520 @panic("TODO");
521 }
522 };
523
524 pub const NoReturn = struct {
525 base: Type,
526
527 /// Adds 1 reference to the resulting type
528 pub fn get(comp: *Compilation) *NoReturn {
529 comp.noreturn_type.base.base.ref();
530 return comp.noreturn_type;
531 }
532
533 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
534 comp.gpa().destroy(self);
535 }
536 };
537
538 pub const Int = struct {
539 base: Type,
540 key: Key,
541 garbage_node: std.atomic.Stack(*Int).Node,
542
543 pub const Key = struct {
544 bit_count: u32,
545 is_signed: bool,
546
547 pub fn hash(self: *const Key) u32 {
548 var result: u32 = 0;
549 result +%= hashAny(self.is_signed, 0);
550 result +%= hashAny(self.bit_count, 1);
551 return result;
552 }
553
554 pub fn eql(self: *const Key, other: *const Key) bool {
555 return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
556 }
479 /// Asserts the type is a function.
480 pub fn fnParamLen(self: Type) usize {
481 return switch (self.tag()) {
482 .fn_naked_noreturn_no_args => 0,
483
484 .f16,
485 .f32,
486 .f64,
487 .f128,
488 .c_longdouble,
489 .c_void,
490 .bool,
491 .void,
492 .type,
493 .anyerror,
494 .comptime_int,
495 .comptime_float,
496 .noreturn,
497 .array,
498 .single_const_pointer,
499 .single_const_pointer_to_comptime_int,
500 .array_u8_sentinel_0,
501 .const_slice_u8,
502 .u8,
503 .i8,
504 .usize,
505 .isize,
506 .c_short,
507 .c_ushort,
508 .c_int,
509 .c_uint,
510 .c_long,
511 .c_ulong,
512 .c_longlong,
513 .c_ulonglong,
514 => unreachable,
557515 };
516 }
558517
559 pub fn get_u8(comp: *Compilation) *Int {
560 comp.u8_type.base.base.ref();
561 return comp.u8_type;
562 }
563
564 pub fn get(comp: *Compilation, key: Key) !*Int {
565 {
566 const held = comp.int_type_table.acquire();
567 defer held.release();
568
569 if (held.value.get(&key)) |entry| {
570 entry.value.base.base.ref();
571 return entry.value;
572 }
573 }
574
575 const self = try comp.gpa().create(Int);
576 self.* = Int{
577 .base = undefined,
578 .key = key,
579 .garbage_node = undefined,
580 };
581 errdefer comp.gpa().destroy(self);
582
583 const u_or_i = "ui"[@boolToInt(key.is_signed)];
584 const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count });
585 errdefer comp.gpa().free(name);
586
587 self.base.init(comp, .Int, name);
588
589 {
590 const held = comp.int_type_table.acquire();
591 defer held.release();
592
593 _ = try held.value.put(&self.key, self);
594 }
595 return self;
596 }
597
598 pub fn destroy(self: *Int, comp: *Compilation) void {
599 self.garbage_node = std.atomic.Stack(*Int).Node{
600 .data = self,
601 .next = undefined,
602 };
603 comp.registerGarbage(Int, &self.garbage_node);
604 }
605
606 pub fn gcDestroy(self: *Int, comp: *Compilation) void {
607 {
608 const held = comp.int_type_table.acquire();
609 defer held.release();
610
611 _ = held.value.remove(&self.key).?;
612 }
613 // we allocated the name
614 comp.gpa().free(self.base.name);
615 comp.gpa().destroy(self);
616 }
617
618 pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
619 return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
620 }
621 };
622
623 pub const Float = struct {
624 base: Type,
625
626 pub fn destroy(self: *Float, comp: *Compilation) void {
627 comp.gpa().destroy(self);
628 }
629
630 pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
631 @panic("TODO");
518 /// Asserts the type is a function. The length of the slice must be at least the length
519 /// given by `fnParamLen`.
520 pub fn fnParamTypes(self: Type, types: []Type) void {
521 switch (self.tag()) {
522 .fn_naked_noreturn_no_args => return,
523
524 .f16,
525 .f32,
526 .f64,
527 .f128,
528 .c_longdouble,
529 .c_void,
530 .bool,
531 .void,
532 .type,
533 .anyerror,
534 .comptime_int,
535 .comptime_float,
536 .noreturn,
537 .array,
538 .single_const_pointer,
539 .single_const_pointer_to_comptime_int,
540 .array_u8_sentinel_0,
541 .const_slice_u8,
542 .u8,
543 .i8,
544 .usize,
545 .isize,
546 .c_short,
547 .c_ushort,
548 .c_int,
549 .c_uint,
550 .c_long,
551 .c_ulong,
552 .c_longlong,
553 .c_ulonglong,
554 => unreachable,
632555 }
633 };
634 pub const Pointer = struct {
635 base: Type,
636 key: Key,
637 garbage_node: std.atomic.Stack(*Pointer).Node,
638
639 pub const Key = struct {
640 child_type: *Type,
641 mut: Mut,
642 vol: Vol,
643 size: Size,
644 alignment: Align,
645
646 pub fn hash(self: *const Key) u32 {
647 var result: u32 = 0;
648 result +%= switch (self.alignment) {
649 .Abi => 0xf201c090,
650 .Override => |x| hashAny(x, 0),
651 };
652 result +%= hashAny(self.child_type, 1);
653 result +%= hashAny(self.mut, 2);
654 result +%= hashAny(self.vol, 3);
655 result +%= hashAny(self.size, 4);
656 return result;
657 }
658
659 pub fn eql(self: *const Key, other: *const Key) bool {
660 if (self.child_type != other.child_type or
661 self.mut != other.mut or
662 self.vol != other.vol or
663 self.size != other.size or
664 @as(@TagType(Align), self.alignment) != @as(@TagType(Align), other.alignment))
665 {
666 return false;
667 }
668 switch (self.alignment) {
669 .Abi => return true,
670 .Override => |x| return x == other.alignment.Override,
671 }
672 }
673 };
674
675 pub const Mut = enum {
676 Mut,
677 Const,
678 };
556 }
679557
680 pub const Vol = enum {
681 Non,
682 Volatile,
558 /// Asserts the type is a function.
559 pub fn fnReturnType(self: Type) Type {
560 return switch (self.tag()) {
561 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
562
563 .f16,
564 .f32,
565 .f64,
566 .f128,
567 .c_longdouble,
568 .c_void,
569 .bool,
570 .void,
571 .type,
572 .anyerror,
573 .comptime_int,
574 .comptime_float,
575 .noreturn,
576 .array,
577 .single_const_pointer,
578 .single_const_pointer_to_comptime_int,
579 .array_u8_sentinel_0,
580 .const_slice_u8,
581 .u8,
582 .i8,
583 .usize,
584 .isize,
585 .c_short,
586 .c_ushort,
587 .c_int,
588 .c_uint,
589 .c_long,
590 .c_ulong,
591 .c_longlong,
592 .c_ulonglong,
593 => unreachable,
683594 };
595 }
684596
685 pub const Align = union(enum) {
686 Abi,
687 Override: u32,
597 /// Asserts the type is a function.
598 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
599 return switch (self.tag()) {
600 .fn_naked_noreturn_no_args => .Naked,
601
602 .f16,
603 .f32,
604 .f64,
605 .f128,
606 .c_longdouble,
607 .c_void,
608 .bool,
609 .void,
610 .type,
611 .anyerror,
612 .comptime_int,
613 .comptime_float,
614 .noreturn,
615 .array,
616 .single_const_pointer,
617 .single_const_pointer_to_comptime_int,
618 .array_u8_sentinel_0,
619 .const_slice_u8,
620 .u8,
621 .i8,
622 .usize,
623 .isize,
624 .c_short,
625 .c_ushort,
626 .c_int,
627 .c_uint,
628 .c_long,
629 .c_ulong,
630 .c_longlong,
631 .c_ulonglong,
632 => unreachable,
688633 };
634 }
689635
690 pub const Size = builtin.TypeInfo.Pointer.Size;
691
692 pub fn destroy(self: *Pointer, comp: *Compilation) void {
693 self.garbage_node = std.atomic.Stack(*Pointer).Node{
694 .data = self,
695 .next = undefined,
696 };
697 comp.registerGarbage(Pointer, &self.garbage_node);
698 }
699
700 pub fn gcDestroy(self: *Pointer, comp: *Compilation) void {
701 {
702 const held = comp.ptr_type_table.acquire();
703 defer held.release();
704
705 _ = held.value.remove(&self.key).?;
706 }
707 self.key.child_type.base.deref(comp);
708 comp.gpa().destroy(self);
709 }
710
711 pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
712 switch (self.key.alignment) {
713 .Abi => return self.key.child_type.getAbiAlignment(comp),
714 .Override => |alignment| return alignment,
715 }
716 }
717
718 pub fn get(
719 comp: *Compilation,
720 key: Key,
721 ) !*Pointer {
722 var normal_key = key;
723 switch (key.alignment) {
724 .Abi => {},
725 .Override => |alignment| {
726 // TODO https://github.com/ziglang/zig/issues/3190
727 var align_spill = alignment;
728 const abi_align = try key.child_type.getAbiAlignment(comp);
729 if (abi_align == align_spill) {
730 normal_key.alignment = .Abi;
731 }
732 },
733 }
734 {
735 const held = comp.ptr_type_table.acquire();
736 defer held.release();
737
738 if (held.value.get(&normal_key)) |entry| {
739 entry.value.base.base.ref();
740 return entry.value;
741 }
742 }
743
744 const self = try comp.gpa().create(Pointer);
745 self.* = Pointer{
746 .base = undefined,
747 .key = normal_key,
748 .garbage_node = undefined,
749 };
750 errdefer comp.gpa().destroy(self);
751
752 const size_str = switch (self.key.size) {
753 .One => "*",
754 .Many => "[*]",
755 .Slice => "[]",
756 .C => "[*c]",
757 };
758 const mut_str = switch (self.key.mut) {
759 .Const => "const ",
760 .Mut => "",
761 };
762 const vol_str = switch (self.key.vol) {
763 .Volatile => "volatile ",
764 .Non => "",
765 };
766 const name = switch (self.key.alignment) {
767 .Abi => try std.fmt.allocPrint(comp.gpa(), "{}{}{}{}", .{
768 size_str,
769 mut_str,
770 vol_str,
771 self.key.child_type.name,
772 }),
773 .Override => |alignment| try std.fmt.allocPrint(comp.gpa(), "{}align<{}> {}{}{}", .{
774 size_str,
775 alignment,
776 mut_str,
777 vol_str,
778 self.key.child_type.name,
779 }),
780 };
781 errdefer comp.gpa().free(name);
782
783 self.base.init(comp, .Pointer, name);
784
785 {
786 const held = comp.ptr_type_table.acquire();
787 defer held.release();
788
789 _ = try held.value.put(&self.key, self);
790 }
791 return self;
792 }
793
794 pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
795 const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
796 return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
797 }
636 /// This enum does not directly correspond to `std.builtin.TypeId` because
637 /// it has extra enum tags in it, as a way of using less memory. For example,
638 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
639 /// but with different alignment values, in this data structure they are represented
640 /// with different enum tags, because the the former requires more payload data than the latter.
641 /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
642 pub const Tag = enum {
643 // The first section of this enum are tags that require no payload.
644 u8,
645 i8,
646 isize,
647 usize,
648 c_short,
649 c_ushort,
650 c_int,
651 c_uint,
652 c_long,
653 c_ulong,
654 c_longlong,
655 c_ulonglong,
656 c_longdouble,
657 c_void,
658 f16,
659 f32,
660 f64,
661 f128,
662 bool,
663 void,
664 type,
665 anyerror,
666 comptime_int,
667 comptime_float,
668 noreturn,
669 fn_naked_noreturn_no_args,
670 single_const_pointer_to_comptime_int,
671 const_slice_u8, // See last_no_payload_tag below.
672 // After this, the tag requires a payload.
673
674 array_u8_sentinel_0,
675 array,
676 single_const_pointer,
677
678 pub const last_no_payload_tag = Tag.const_slice_u8;
679 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
798680 };
799681
800 pub const Array = struct {
801 base: Type,
802 key: Key,
803 garbage_node: std.atomic.Stack(*Array).Node,
682 pub const Payload = struct {
683 tag: Tag,
804684
805 pub const Key = struct {
806 elem_type: *Type,
807 len: usize,
685 pub const Array_u8_Sentinel0 = struct {
686 base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
808687
809 pub fn hash(self: *const Key) u32 {
810 var result: u32 = 0;
811 result +%= hashAny(self.elem_type, 0);
812 result +%= hashAny(self.len, 1);
813 return result;
814 }
815
816 pub fn eql(self: *const Key, other: *const Key) bool {
817 return self.elem_type == other.elem_type and self.len == other.len;
818 }
688 len: u64,
819689 };
820690
821 pub fn destroy(self: *Array, comp: *Compilation) void {
822 self.key.elem_type.base.deref(comp);
823 comp.gpa().destroy(self);
824 }
825
826 pub fn get(comp: *Compilation, key: Key) !*Array {
827 key.elem_type.base.ref();
828 errdefer key.elem_type.base.deref(comp);
829
830 {
831 const held = comp.array_type_table.acquire();
832 defer held.release();
833
834 if (held.value.get(&key)) |entry| {
835 entry.value.base.base.ref();
836 return entry.value;
837 }
838 }
839
840 const self = try comp.gpa().create(Array);
841 self.* = Array{
842 .base = undefined,
843 .key = key,
844 .garbage_node = undefined,
845 };
846 errdefer comp.gpa().destroy(self);
847
848 const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", .{ key.len, key.elem_type.name });
849 errdefer comp.gpa().free(name);
850
851 self.base.init(comp, .Array, name);
852
853 {
854 const held = comp.array_type_table.acquire();
855 defer held.release();
856
857 _ = try held.value.put(&self.key, self);
858 }
859 return self;
860 }
861
862 pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
863 const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
864 return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
865 }
866 };
867
868 pub const Vector = struct {
869 base: Type,
870
871 pub fn destroy(self: *Vector, comp: *Compilation) void {
872 comp.gpa().destroy(self);
873 }
874
875 pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
876 @panic("TODO");
877 }
878 };
879
880 pub const ComptimeFloat = struct {
881 base: Type,
691 pub const Array = struct {
692 base: Payload = Payload{ .tag = .array },
882693
883 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
884 comp.gpa().destroy(self);
885 }
886 };
887
888 pub const ComptimeInt = struct {
889 base: Type,
890
891 /// Adds 1 reference to the resulting type
892 pub fn get(comp: *Compilation) *ComptimeInt {
893 comp.comptime_int_type.base.base.ref();
894 return comp.comptime_int_type;
895 }
896
897 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
898 comp.gpa().destroy(self);
899 }
900 };
901
902 pub const EnumLiteral = struct {
903 base: Type,
904
905 /// Adds 1 reference to the resulting type
906 pub fn get(comp: *Compilation) *EnumLiteral {
907 comp.comptime_int_type.base.base.ref();
908 return comp.comptime_int_type;
909 }
910
911 pub fn destroy(self: *EnumLiteral, comp: *Compilation) void {
912 comp.gpa().destroy(self);
913 }
914 };
915
916 pub const Undefined = struct {
917 base: Type,
918
919 pub fn destroy(self: *Undefined, comp: *Compilation) void {
920 comp.gpa().destroy(self);
921 }
922 };
923
924 pub const Null = struct {
925 base: Type,
926
927 pub fn destroy(self: *Null, comp: *Compilation) void {
928 comp.gpa().destroy(self);
929 }
930 };
931
932 pub const Optional = struct {
933 base: Type,
934
935 pub fn destroy(self: *Optional, comp: *Compilation) void {
936 comp.gpa().destroy(self);
937 }
938
939 pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
940 @panic("TODO");
941 }
942 };
943
944 pub const ErrorUnion = struct {
945 base: Type,
946
947 pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
948 comp.gpa().destroy(self);
949 }
950
951 pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
952 @panic("TODO");
953 }
954 };
955
956 pub const ErrorSet = struct {
957 base: Type,
958
959 pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
960 comp.gpa().destroy(self);
961 }
962
963 pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
964 @panic("TODO");
965 }
966 };
967
968 pub const Enum = struct {
969 base: Type,
970
971 pub fn destroy(self: *Enum, comp: *Compilation) void {
972 comp.gpa().destroy(self);
973 }
974
975 pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
976 @panic("TODO");
977 }
978 };
979
980 pub const Union = struct {
981 base: Type,
982
983 pub fn destroy(self: *Union, comp: *Compilation) void {
984 comp.gpa().destroy(self);
985 }
986
987 pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
988 @panic("TODO");
989 }
990 };
991
992 pub const BoundFn = struct {
993 base: Type,
994
995 pub fn destroy(self: *BoundFn, comp: *Compilation) void {
996 comp.gpa().destroy(self);
997 }
998
999 pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1000 @panic("TODO");
1001 }
1002 };
694 elem_type: Type,
695 len: u64,
696 };
1003697
1004 pub const Opaque = struct {
1005 base: Type,
698 pub const SingleConstPointer = struct {
699 base: Payload = Payload{ .tag = .single_const_pointer },
1006700
1007 pub fn destroy(self: *Opaque, comp: *Compilation) void {
1008 comp.gpa().destroy(self);
1009 }
1010
1011 pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1012 @panic("TODO");
1013 }
701 pointee_type: Type,
702 };
1014703 };
704};
1015705
1016 pub const Frame = struct {
1017 base: Type,
1018
1019 pub fn destroy(self: *Frame, comp: *Compilation) void {
1020 comp.gpa().destroy(self);
1021 }
706pub const CInteger = enum {
707 short,
708 ushort,
709 int,
710 uint,
711 long,
712 ulong,
713 longlong,
714 ulonglong,
715
716 pub fn sizeInBits(self: CInteger, target: Target) u16 {
717 const arch = target.cpu.arch;
718 switch (target.os.tag) {
719 .freestanding, .other => switch (target.cpu.arch) {
720 .msp430 => switch (self) {
721 .short,
722 .ushort,
723 .int,
724 .uint,
725 => return 16,
726 .long,
727 .ulong,
728 => return 32,
729 .longlong,
730 .ulonglong,
731 => return 64,
732 },
733 else => switch (self) {
734 .short,
735 .ushort,
736 => return 16,
737 .int,
738 .uint,
739 => return 32,
740 .long,
741 .ulong,
742 => return target.cpu.arch.ptrBitWidth(),
743 .longlong,
744 .ulonglong,
745 => return 64,
746 },
747 },
1022748
1023 pub fn getLlvmType(self: *Frame, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1024 @panic("TODO");
1025 }
1026 };
749 .linux,
750 .macosx,
751 .freebsd,
752 .netbsd,
753 .dragonfly,
754 .openbsd,
755 .wasi,
756 .emscripten,
757 => switch (self) {
758 .short,
759 .ushort,
760 => return 16,
761 .int,
762 .uint,
763 => return 32,
764 .long,
765 .ulong,
766 => return target.cpu.arch.ptrBitWidth(),
767 .longlong,
768 .ulonglong,
769 => return 64,
770 },
1027771
1028 pub const AnyFrame = struct {
1029 base: Type,
772 .windows, .uefi => switch (self) {
773 .short,
774 .ushort,
775 => return 16,
776 .int,
777 .uint,
778 .long,
779 .ulong,
780 => return 32,
781 .longlong,
782 .ulonglong,
783 => return 64,
784 },
1030785
1031 pub fn destroy(self: *AnyFrame, comp: *Compilation) void {
1032 comp.gpa().destroy(self);
1033 }
786 .ios => switch (self) {
787 .short,
788 .ushort,
789 => return 16,
790 .int,
791 .uint,
792 => return 32,
793 .long,
794 .ulong,
795 .longlong,
796 .ulonglong,
797 => return 64,
798 },
1034799
1035 pub fn getLlvmType(self: *AnyFrame, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1036 @panic("TODO");
800 .ananas,
801 .cloudabi,
802 .fuchsia,
803 .kfreebsd,
804 .lv2,
805 .solaris,
806 .haiku,
807 .minix,
808 .rtems,
809 .nacl,
810 .cnk,
811 .aix,
812 .cuda,
813 .nvcl,
814 .amdhsa,
815 .ps4,
816 .elfiamcu,
817 .tvos,
818 .watchos,
819 .mesa3d,
820 .contiki,
821 .amdpal,
822 .hermit,
823 .hurd,
824 => @panic("TODO specify the C integer type sizes for this OS"),
1037825 }
1038 };
1039};
1040
1041fn hashAny(x: var, comptime seed: u64) u32 {
1042 switch (@typeInfo(@TypeOf(x))) {
1043 .Int => |info| {
1044 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1045 const unsigned_x = @bitCast(std.meta.IntType(false, info.bits), x);
1046 if (info.bits <= 32) {
1047 return @as(u32, unsigned_x) *% comptime rng.random.scalar(u32);
1048 } else {
1049 return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@TypeOf(unsigned_x)));
1050 }
1051 },
1052 .Pointer => |info| {
1053 switch (info.size) {
1054 .One => return hashAny(@ptrToInt(x), seed),
1055 .Many => @compileError("implement hash function"),
1056 .Slice => @compileError("implement hash function"),
1057 .C => unreachable,
1058 }
1059 },
1060 .Enum => return hashAny(@enumToInt(x), seed),
1061 .Bool => {
1062 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1063 const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) };
1064 return vals[@boolToInt(x)];
1065 },
1066 .Optional => {
1067 if (x) |non_opt| {
1068 return hashAny(non_opt, seed);
1069 } else {
1070 return hashAny(@as(u32, 1), seed);
1071 }
1072 },
1073 else => @compileError("implement hash function for " ++ @typeName(@TypeOf(x))),
1074826 }
1075}
827};
src-self-hosted/value.zig+425-537
......@@ -1,587 +1,475 @@
11const std = @import("std");
2const Scope = @import("scope.zig").Scope;
3const Compilation = @import("compilation.zig").Compilation;
4const ObjectFile = @import("codegen.zig").ObjectFile;
5const llvm = @import("llvm.zig");
6const ArrayListSentineled = std.ArrayListSentineled;
2const Type = @import("type.zig").Type;
3const log2 = std.math.log2;
74const assert = std.debug.assert;
5const BigInt = std.math.big.Int;
6const Target = std.Target;
7const Allocator = std.mem.Allocator;
8
9/// This is the raw data, with no bookkeeping, no memory awareness,
10/// no de-duplication, and no type system awareness.
11/// It's important for this struct to be small.
12/// This union takes advantage of the fact that the first page of memory
13/// is unmapped, giving us 4096 possible enum tags that have no payload.
14pub const Value = extern union {
15 /// If the tag value is less than Tag.no_payload_count, then no pointer
16 /// dereference is needed.
17 tag_if_small_enough: usize,
18 ptr_otherwise: *Payload,
19
20 pub const Tag = enum {
21 // The first section of this enum are tags that require no payload.
22 u8_type,
23 i8_type,
24 isize_type,
25 usize_type,
26 c_short_type,
27 c_ushort_type,
28 c_int_type,
29 c_uint_type,
30 c_long_type,
31 c_ulong_type,
32 c_longlong_type,
33 c_ulonglong_type,
34 c_longdouble_type,
35 f16_type,
36 f32_type,
37 f64_type,
38 f128_type,
39 c_void_type,
40 bool_type,
41 void_type,
42 type_type,
43 anyerror_type,
44 comptime_int_type,
45 comptime_float_type,
46 noreturn_type,
47 fn_naked_noreturn_no_args_type,
48 single_const_pointer_to_comptime_int_type,
49 const_slice_u8_type,
50
51 zero,
52 void_value,
53 noreturn_value,
54 bool_true,
55 bool_false, // See last_no_payload_tag below.
56 // After this, the tag requires a payload.
57
58 ty,
59 int_u64,
60 int_i64,
61 int_big,
62 function,
63 ref,
64 ref_val,
65 bytes,
66
67 pub const last_no_payload_tag = Tag.bool_false;
68 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
69 };
870
9/// Values are ref-counted, heap-allocated, and copy-on-write
10/// If there is only 1 ref then write need not copy
11pub const Value = struct {
12 id: Id,
13 typ: *Type,
14 ref_count: std.atomic.Int(usize),
71 pub fn initTag(comptime small_tag: Tag) Value {
72 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
73 return .{ .tag_if_small_enough = @enumToInt(small_tag) };
74 }
1575
16 /// Thread-safe
17 pub fn ref(base: *Value) void {
18 _ = base.ref_count.incr();
76 pub fn initPayload(payload: *Payload) Value {
77 assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
78 return .{ .ptr_otherwise = payload };
1979 }
2080
21 /// Thread-safe
22 pub fn deref(base: *Value, comp: *Compilation) void {
23 if (base.ref_count.decr() == 1) {
24 base.typ.base.deref(comp);
25 switch (base.id) {
26 .Type => @fieldParentPtr(Type, "base", base).destroy(comp),
27 .Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
28 .FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
29 .Void => @fieldParentPtr(Void, "base", base).destroy(comp),
30 .Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
31 .NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
32 .Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
33 .Int => @fieldParentPtr(Int, "base", base).destroy(comp),
34 .Array => @fieldParentPtr(Array, "base", base).destroy(comp),
35 }
81 pub fn tag(self: Value) Tag {
82 if (self.tag_if_small_enough < Tag.no_payload_count) {
83 return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
84 } else {
85 return self.ptr_otherwise.tag;
3686 }
3787 }
3888
39 pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
40 base.typ.base.deref(comp);
41 new_type.base.ref();
42 base.typ = new_type;
43 }
89 pub fn cast(self: Value, comptime T: type) ?*T {
90 if (self.tag_if_small_enough < Tag.no_payload_count)
91 return null;
4492
45 pub fn getRef(base: *Value) *Value {
46 base.ref();
47 return base;
48 }
93 const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
94 if (self.ptr_otherwise.tag != expected_tag)
95 return null;
4996
50 pub fn cast(base: *Value, comptime T: type) ?*T {
51 if (base.id != @field(Id, @typeName(T))) return null;
52 return @fieldParentPtr(T, "base", base);
97 return @fieldParentPtr(T, "base", self.ptr_otherwise);
5398 }
5499
55 pub fn dump(base: *const Value) void {
56 std.debug.warn("{}", .{@tagName(base.id)});
100 pub fn format(
101 self: Value,
102 comptime fmt: []const u8,
103 options: std.fmt.FormatOptions,
104 out_stream: var,
105 ) !void {
106 comptime assert(fmt.len == 0);
107 var val = self;
108 while (true) switch (val.tag()) {
109 .u8_type => return out_stream.writeAll("u8"),
110 .i8_type => return out_stream.writeAll("i8"),
111 .isize_type => return out_stream.writeAll("isize"),
112 .usize_type => return out_stream.writeAll("usize"),
113 .c_short_type => return out_stream.writeAll("c_short"),
114 .c_ushort_type => return out_stream.writeAll("c_ushort"),
115 .c_int_type => return out_stream.writeAll("c_int"),
116 .c_uint_type => return out_stream.writeAll("c_uint"),
117 .c_long_type => return out_stream.writeAll("c_long"),
118 .c_ulong_type => return out_stream.writeAll("c_ulong"),
119 .c_longlong_type => return out_stream.writeAll("c_longlong"),
120 .c_ulonglong_type => return out_stream.writeAll("c_ulonglong"),
121 .c_longdouble_type => return out_stream.writeAll("c_longdouble"),
122 .f16_type => return out_stream.writeAll("f16"),
123 .f32_type => return out_stream.writeAll("f32"),
124 .f64_type => return out_stream.writeAll("f64"),
125 .f128_type => return out_stream.writeAll("f128"),
126 .c_void_type => return out_stream.writeAll("c_void"),
127 .bool_type => return out_stream.writeAll("bool"),
128 .void_type => return out_stream.writeAll("void"),
129 .type_type => return out_stream.writeAll("type"),
130 .anyerror_type => return out_stream.writeAll("anyerror"),
131 .comptime_int_type => return out_stream.writeAll("comptime_int"),
132 .comptime_float_type => return out_stream.writeAll("comptime_float"),
133 .noreturn_type => return out_stream.writeAll("noreturn"),
134 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
135 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
136 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
137
138 .zero => return out_stream.writeAll("0"),
139 .void_value => return out_stream.writeAll("{}"),
140 .noreturn_value => return out_stream.writeAll("unreachable"),
141 .bool_true => return out_stream.writeAll("true"),
142 .bool_false => return out_stream.writeAll("false"),
143 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
144 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
145 .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
146 .int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}),
147 .function => return out_stream.writeAll("(function)"),
148 .ref => return out_stream.writeAll("(ref)"),
149 .ref_val => {
150 try out_stream.writeAll("*const ");
151 val = val.cast(Payload.RefVal).?.val;
152 continue;
153 },
154 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
155 };
57156 }
58157
59 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?*llvm.Value) {
60 switch (base.id) {
61 .Type => unreachable,
62 .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
63 .FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
64 .Void => return null,
65 .Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
66 .NoReturn => unreachable,
67 .Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
68 .Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
69 .Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
158 /// Asserts that the value is representable as an array of bytes.
159 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
160 pub fn toAllocatedBytes(self: Value, allocator: *Allocator) Allocator.Error![]u8 {
161 if (self.cast(Payload.Bytes)) |bytes| {
162 return std.mem.dupe(allocator, u8, bytes.data);
70163 }
164 unreachable;
71165 }
72166
73 pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
74 if (self.ref_count.get() == 1) {
75 // ( Í¡° ͜ʖ Í¡°)
76 return self;
77 }
78
79 assert(self.ref_count.decr() != 1);
80 return self.copy(comp);
167 /// Asserts that the value is representable as a type.
168 pub fn toType(self: Value) Type {
169 return switch (self.tag()) {
170 .ty => self.cast(Payload.Ty).?.ty,
171
172 .u8_type => Type.initTag(.@"u8"),
173 .i8_type => Type.initTag(.@"i8"),
174 .isize_type => Type.initTag(.@"isize"),
175 .usize_type => Type.initTag(.@"usize"),
176 .c_short_type => Type.initTag(.@"c_short"),
177 .c_ushort_type => Type.initTag(.@"c_ushort"),
178 .c_int_type => Type.initTag(.@"c_int"),
179 .c_uint_type => Type.initTag(.@"c_uint"),
180 .c_long_type => Type.initTag(.@"c_long"),
181 .c_ulong_type => Type.initTag(.@"c_ulong"),
182 .c_longlong_type => Type.initTag(.@"c_longlong"),
183 .c_ulonglong_type => Type.initTag(.@"c_ulonglong"),
184 .c_longdouble_type => Type.initTag(.@"c_longdouble"),
185 .f16_type => Type.initTag(.@"f16"),
186 .f32_type => Type.initTag(.@"f32"),
187 .f64_type => Type.initTag(.@"f64"),
188 .f128_type => Type.initTag(.@"f128"),
189 .c_void_type => Type.initTag(.@"c_void"),
190 .bool_type => Type.initTag(.@"bool"),
191 .void_type => Type.initTag(.@"void"),
192 .type_type => Type.initTag(.@"type"),
193 .anyerror_type => Type.initTag(.@"anyerror"),
194 .comptime_int_type => Type.initTag(.@"comptime_int"),
195 .comptime_float_type => Type.initTag(.@"comptime_float"),
196 .noreturn_type => Type.initTag(.@"noreturn"),
197 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
198 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
199 .const_slice_u8_type => Type.initTag(.const_slice_u8),
200
201 .zero,
202 .void_value,
203 .noreturn_value,
204 .bool_true,
205 .bool_false,
206 .int_u64,
207 .int_i64,
208 .int_big,
209 .function,
210 .ref,
211 .ref_val,
212 .bytes,
213 => unreachable,
214 };
81215 }
82216
83 pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
84 switch (base.id) {
85 .Type => unreachable,
86 .Fn => unreachable,
87 .FnProto => unreachable,
88 .Void => unreachable,
89 .Bool => unreachable,
90 .NoReturn => unreachable,
91 .Ptr => unreachable,
92 .Array => unreachable,
93 .Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
217 /// Asserts the value is an integer.
218 pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt {
219 switch (self.tag()) {
220 .ty,
221 .u8_type,
222 .i8_type,
223 .isize_type,
224 .usize_type,
225 .c_short_type,
226 .c_ushort_type,
227 .c_int_type,
228 .c_uint_type,
229 .c_long_type,
230 .c_ulong_type,
231 .c_longlong_type,
232 .c_ulonglong_type,
233 .c_longdouble_type,
234 .f16_type,
235 .f32_type,
236 .f64_type,
237 .f128_type,
238 .c_void_type,
239 .bool_type,
240 .void_type,
241 .type_type,
242 .anyerror_type,
243 .comptime_int_type,
244 .comptime_float_type,
245 .noreturn_type,
246 .fn_naked_noreturn_no_args_type,
247 .single_const_pointer_to_comptime_int_type,
248 .const_slice_u8_type,
249 .void_value,
250 .noreturn_value,
251 .bool_true,
252 .bool_false,
253 .function,
254 .ref,
255 .ref_val,
256 .bytes,
257 => unreachable,
258
259 .zero => return BigInt.initSet(allocator, 0),
260
261 .int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int),
262 .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),
263 .int_big => return self.cast(Payload.IntBig).?.big_int,
94264 }
95265 }
96266
97 pub const Parent = union(enum) {
98 None,
99 BaseStruct: BaseStruct,
100 BaseArray: BaseArray,
101 BaseUnion: *Value,
102 BaseScalar: *Value,
103
104 pub const BaseStruct = struct {
105 val: *Value,
106 field_index: usize,
107 };
108
109 pub const BaseArray = struct {
110 val: *Value,
111 elem_index: usize,
112 };
113 };
114
115 pub const Id = enum {
116 Type,
117 Fn,
118 Void,
119 Bool,
120 NoReturn,
121 Array,
122 Ptr,
123 Int,
124 FnProto,
125 };
126
127 pub const Type = @import("type.zig").Type;
128
129 pub const FnProto = struct {
130 base: Value,
131
132 /// The main external name that is used in the .o file.
133 /// TODO https://github.com/ziglang/zig/issues/265
134 symbol_name: ArrayListSentineled(u8, 0),
135
136 pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: ArrayListSentineled(u8, 0)) !*FnProto {
137 const self = try comp.gpa().create(FnProto);
138 self.* = FnProto{
139 .base = Value{
140 .id = .FnProto,
141 .typ = &fn_type.base,
142 .ref_count = std.atomic.Int(usize).init(1),
267 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
268 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
269 switch (self.tag()) {
270 .ty,
271 .u8_type,
272 .i8_type,
273 .isize_type,
274 .usize_type,
275 .c_short_type,
276 .c_ushort_type,
277 .c_int_type,
278 .c_uint_type,
279 .c_long_type,
280 .c_ulong_type,
281 .c_longlong_type,
282 .c_ulonglong_type,
283 .c_longdouble_type,
284 .f16_type,
285 .f32_type,
286 .f64_type,
287 .f128_type,
288 .c_void_type,
289 .bool_type,
290 .void_type,
291 .type_type,
292 .anyerror_type,
293 .comptime_int_type,
294 .comptime_float_type,
295 .noreturn_type,
296 .fn_naked_noreturn_no_args_type,
297 .single_const_pointer_to_comptime_int_type,
298 .const_slice_u8_type,
299 .void_value,
300 .noreturn_value,
301 .bool_true,
302 .bool_false,
303 .function,
304 .ref,
305 .ref_val,
306 .bytes,
307 => unreachable,
308
309 .zero => return true,
310
311 .int_u64 => switch (ty.zigTypeTag()) {
312 .Int => {
313 const x = self.cast(Payload.Int_u64).?.int;
314 if (x == 0) return true;
315 const info = ty.intInfo(target);
316 const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signed);
317 return info.bits >= needed_bits;
143318 },
144 .symbol_name = symbol_name,
145 };
146 fn_type.base.base.ref();
147 return self;
148 }
149
150 pub fn destroy(self: *FnProto, comp: *Compilation) void {
151 self.symbol_name.deinit();
152 comp.gpa().destroy(self);
153 }
154
155 pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?*llvm.Value {
156 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
157 const llvm_fn = llvm.AddFunction(
158 ofile.module,
159 self.symbol_name.span(),
160 llvm_fn_type,
161 ) orelse return error.OutOfMemory;
162
163 // TODO port more logic from codegen.cpp:fn_llvm_value
164
165 return llvm_fn;
166 }
167 };
168
169 pub const Fn = struct {
170 base: Value,
171
172 /// The main external name that is used in the .o file.
173 /// TODO https://github.com/ziglang/zig/issues/265
174 symbol_name: ArrayListSentineled(u8, 0),
175
176 /// parent should be the top level decls or container decls
177 fndef_scope: *Scope.FnDef,
178
179 /// parent is scope for last parameter
180 child_scope: *Scope,
181
182 /// parent is child_scope
183 block_scope: ?*Scope.Block,
184
185 /// Path to the object file that contains this function
186 containing_object: ArrayListSentineled(u8, 0),
187
188 link_set_node: *std.TailQueue(?*Value.Fn).Node,
189
190 /// Creates a Fn value with 1 ref
191 /// Takes ownership of symbol_name
192 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: ArrayListSentineled(u8, 0)) !*Fn {
193 const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node);
194 link_set_node.* = Compilation.FnLinkSet.Node{
195 .data = null,
196 .next = undefined,
197 .prev = undefined,
198 };
199 errdefer comp.gpa().destroy(link_set_node);
200
201 const self = try comp.gpa().create(Fn);
202 self.* = Fn{
203 .base = Value{
204 .id = .Fn,
205 .typ = &fn_type.base,
206 .ref_count = std.atomic.Int(usize).init(1),
319 .ComptimeInt => return true,
320 else => unreachable,
321 },
322 .int_i64 => switch (ty.zigTypeTag()) {
323 .Int => {
324 const x = self.cast(Payload.Int_i64).?.int;
325 if (x == 0) return true;
326 const info = ty.intInfo(target);
327 if (!info.signed and x < 0)
328 return false;
329 @panic("TODO implement i64 intFitsInType");
207330 },
208 .fndef_scope = fndef_scope,
209 .child_scope = &fndef_scope.base,
210 .block_scope = null,
211 .symbol_name = symbol_name,
212 .containing_object = ArrayListSentineled(u8, 0).initNull(comp.gpa()),
213 .link_set_node = link_set_node,
214 };
215 fn_type.base.base.ref();
216 fndef_scope.fn_val = self;
217 fndef_scope.base.ref();
218 return self;
219 }
220
221 pub fn destroy(self: *Fn, comp: *Compilation) void {
222 // remove with a tombstone so that we do not have to grab a lock
223 if (self.link_set_node.data != null) {
224 // it's now the job of the link step to find this tombstone and
225 // deallocate it.
226 self.link_set_node.data = null;
227 } else {
228 comp.gpa().destroy(self.link_set_node);
229 }
230
231 self.containing_object.deinit();
232 self.fndef_scope.base.deref(comp);
233 self.symbol_name.deinit();
234 comp.gpa().destroy(self);
235 }
236
237 /// We know that the function definition will end up in an .o file somewhere.
238 /// Here, all we have to do is generate a global prototype.
239 /// TODO cache the prototype per ObjectFile
240 pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?*llvm.Value {
241 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
242 const llvm_fn = llvm.AddFunction(
243 ofile.module,
244 self.symbol_name.span(),
245 llvm_fn_type,
246 ) orelse return error.OutOfMemory;
247
248 // TODO port more logic from codegen.cpp:fn_llvm_value
249
250 return llvm_fn;
251 }
252 };
253
254 pub const Void = struct {
255 base: Value,
256
257 pub fn get(comp: *Compilation) *Void {
258 comp.void_value.base.ref();
259 return comp.void_value;
260 }
261
262 pub fn destroy(self: *Void, comp: *Compilation) void {
263 comp.gpa().destroy(self);
264 }
265 };
266
267 pub const Bool = struct {
268 base: Value,
269 x: bool,
270
271 pub fn get(comp: *Compilation, x: bool) *Bool {
272 if (x) {
273 comp.true_value.base.ref();
274 return comp.true_value;
275 } else {
276 comp.false_value.base.ref();
277 return comp.false_value;
278 }
279 }
280
281 pub fn destroy(self: *Bool, comp: *Compilation) void {
282 comp.gpa().destroy(self);
283 }
284
285 pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) !?*llvm.Value {
286 const llvm_type = llvm.Int1TypeInContext(ofile.context) orelse return error.OutOfMemory;
287 if (self.x) {
288 return llvm.ConstAllOnes(llvm_type);
289 } else {
290 return llvm.ConstNull(llvm_type);
291 }
292 }
293 };
294
295 pub const NoReturn = struct {
296 base: Value,
297
298 pub fn get(comp: *Compilation) *NoReturn {
299 comp.noreturn_value.base.ref();
300 return comp.noreturn_value;
331 .ComptimeInt => return true,
332 else => unreachable,
333 },
334 .int_big => switch (ty.zigTypeTag()) {
335 .Int => {
336 const info = ty.intInfo(target);
337 return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
338 },
339 .ComptimeInt => return true,
340 else => unreachable,
341 },
301342 }
343 }
302344
303 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
304 comp.gpa().destroy(self);
345 /// Asserts the value is a pointer and dereferences it.
346 pub fn pointerDeref(self: Value) Value {
347 switch (self.tag()) {
348 .ty,
349 .u8_type,
350 .i8_type,
351 .isize_type,
352 .usize_type,
353 .c_short_type,
354 .c_ushort_type,
355 .c_int_type,
356 .c_uint_type,
357 .c_long_type,
358 .c_ulong_type,
359 .c_longlong_type,
360 .c_ulonglong_type,
361 .c_longdouble_type,
362 .f16_type,
363 .f32_type,
364 .f64_type,
365 .f128_type,
366 .c_void_type,
367 .bool_type,
368 .void_type,
369 .type_type,
370 .anyerror_type,
371 .comptime_int_type,
372 .comptime_float_type,
373 .noreturn_type,
374 .fn_naked_noreturn_no_args_type,
375 .single_const_pointer_to_comptime_int_type,
376 .const_slice_u8_type,
377 .zero,
378 .void_value,
379 .noreturn_value,
380 .bool_true,
381 .bool_false,
382 .function,
383 .int_u64,
384 .int_i64,
385 .int_big,
386 .bytes,
387 => unreachable,
388
389 .ref => return self.cast(Payload.Ref).?.cell.contents,
390 .ref_val => return self.cast(Payload.RefVal).?.val,
305391 }
306 };
392 }
307393
308 pub const Ptr = struct {
309 base: Value,
310 special: Special,
311 mut: Mut,
394 /// This type is not copyable since it may contain pointers to its inner data.
395 pub const Payload = struct {
396 tag: Tag,
312397
313 pub const Mut = enum {
314 CompTimeConst,
315 CompTimeVar,
316 RunTime,
398 pub const Int_u64 = struct {
399 base: Payload = Payload{ .tag = .int_u64 },
400 int: u64,
317401 };
318402
319 pub const Special = union(enum) {
320 Scalar: *Value,
321 BaseArray: BaseArray,
322 BaseStruct: BaseStruct,
323 HardCodedAddr: u64,
324 Discard,
403 pub const Int_i64 = struct {
404 base: Payload = Payload{ .tag = .int_i64 },
405 int: i64,
325406 };
326407
327 pub const BaseArray = struct {
328 val: *Value,
329 elem_index: usize,
408 pub const IntBig = struct {
409 base: Payload = Payload{ .tag = .int_big },
410 big_int: BigInt,
330411 };
331412
332 pub const BaseStruct = struct {
333 val: *Value,
334 field_index: usize,
413 pub const Function = struct {
414 base: Payload = Payload{ .tag = .function },
415 /// Index into the `fns` array of the `ir.Module`
416 index: usize,
335417 };
336418
337 pub fn createArrayElemPtr(
338 comp: *Compilation,
339 array_val: *Array,
340 mut: Type.Pointer.Mut,
341 size: Type.Pointer.Size,
342 elem_index: usize,
343 ) !*Ptr {
344 array_val.base.ref();
345 errdefer array_val.base.deref(comp);
346
347 const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
348 const ptr_type = try Type.Pointer.get(comp, Type.Pointer.Key{
349 .child_type = elem_type,
350 .mut = mut,
351 .vol = Type.Pointer.Vol.Non,
352 .size = size,
353 .alignment = .Abi,
354 });
355 var ptr_type_consumed = false;
356 errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
357
358 const self = try comp.gpa().create(Value.Ptr);
359 self.* = Value.Ptr{
360 .base = Value{
361 .id = .Ptr,
362 .typ = &ptr_type.base,
363 .ref_count = std.atomic.Int(usize).init(1),
364 },
365 .special = Special{
366 .BaseArray = BaseArray{
367 .val = &array_val.base,
368 .elem_index = 0,
369 },
370 },
371 .mut = Mut.CompTimeConst,
372 };
373 ptr_type_consumed = true;
374 errdefer comp.gpa().destroy(self);
375
376 return self;
377 }
378
379 pub fn destroy(self: *Ptr, comp: *Compilation) void {
380 comp.gpa().destroy(self);
381 }
382
383 pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?*llvm.Value {
384 const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
385 // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
386 switch (self.special) {
387 .Scalar => |scalar| @panic("TODO"),
388 .BaseArray => |base_array| {
389 // TODO put this in one .o file only, and after that, generate extern references to it
390 const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
391 const ptr_bit_count = ofile.comp.target_ptr_bits;
392 const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
393 var indices = [_]*llvm.Value{
394 llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
395 llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
396 };
397 return llvm.ConstInBoundsGEP(
398 array_llvm_value,
399 @ptrCast([*]*llvm.Value, &indices),
400 @intCast(c_uint, indices.len),
401 ) orelse return error.OutOfMemory;
402 },
403 .BaseStruct => |base_struct| @panic("TODO"),
404 .HardCodedAddr => |addr| @panic("TODO"),
405 .Discard => unreachable,
406 }
407 }
408 };
409
410 pub const Array = struct {
411 base: Value,
412 special: Special,
413
414 pub const Special = union(enum) {
415 Undefined,
416 OwnedBuffer: []u8,
417 Explicit: Data,
419 pub const ArraySentinel0_u8_Type = struct {
420 base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
421 len: u64,
418422 };
419423
420 pub const Data = struct {
421 parent: Parent,
422 elements: []*Value,
424 pub const SingleConstPtrType = struct {
425 base: Payload = Payload{ .tag = .single_const_ptr_type },
426 elem_type: *Type,
423427 };
424428
425 /// Takes ownership of buffer
426 pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
427 const u8_type = Type.Int.get_u8(comp);
428 defer u8_type.base.base.deref(comp);
429
430 const array_type = try Type.Array.get(comp, Type.Array.Key{
431 .elem_type = &u8_type.base,
432 .len = buffer.len,
433 });
434 errdefer array_type.base.base.deref(comp);
435
436 const self = try comp.gpa().create(Value.Array);
437 self.* = Value.Array{
438 .base = Value{
439 .id = .Array,
440 .typ = &array_type.base,
441 .ref_count = std.atomic.Int(usize).init(1),
442 },
443 .special = Special{ .OwnedBuffer = buffer },
444 };
445 errdefer comp.gpa().destroy(self);
429 pub const Ref = struct {
430 base: Payload = Payload{ .tag = .ref },
431 cell: *MemoryCell,
432 };
446433
447 return self;
448 }
434 pub const RefVal = struct {
435 base: Payload = Payload{ .tag = .ref_val },
436 val: Value,
437 };
449438
450 pub fn destroy(self: *Array, comp: *Compilation) void {
451 switch (self.special) {
452 .Undefined => {},
453 .OwnedBuffer => |buf| {
454 comp.gpa().free(buf);
455 },
456 .Explicit => {},
457 }
458 comp.gpa().destroy(self);
459 }
439 pub const Bytes = struct {
440 base: Payload = Payload{ .tag = .bytes },
441 data: []const u8,
442 };
460443
461 pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value {
462 switch (self.special) {
463 .Undefined => {
464 const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
465 return llvm.GetUndef(llvm_type);
466 },
467 .OwnedBuffer => |buf| {
468 const dont_null_terminate = 1;
469 const llvm_str_init = llvm.ConstStringInContext(
470 ofile.context,
471 buf.ptr,
472 @intCast(c_uint, buf.len),
473 dont_null_terminate,
474 ) orelse return error.OutOfMemory;
475 const str_init_type = llvm.TypeOf(llvm_str_init);
476 const global = llvm.AddGlobal(ofile.module, str_init_type, "") orelse return error.OutOfMemory;
477 llvm.SetInitializer(global, llvm_str_init);
478 llvm.SetLinkage(global, llvm.PrivateLinkage);
479 llvm.SetGlobalConstant(global, 1);
480 llvm.SetUnnamedAddr(global, 1);
481 llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
482 return global;
483 },
484 .Explicit => @panic("TODO"),
485 }
486
487 //{
488 // uint64_t len = type_entry->data.array.len;
489 // if (const_val->data.x_array.special == ConstArraySpecialUndef) {
490 // return LLVMGetUndef(type_entry->type_ref);
491 // }
492
493 // LLVMValueRef *values = allocate<LLVMValueRef>(len);
494 // LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
495 // bool make_unnamed_struct = false;
496 // for (uint64_t i = 0; i < len; i += 1) {
497 // ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
498 // LLVMValueRef val = gen_const_val(g, elem_value, "");
499 // values[i] = val;
500 // make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
501 // }
502 // if (make_unnamed_struct) {
503 // return LLVMConstStruct(values, len, true);
504 // } else {
505 // return LLVMConstArray(element_type_ref, values, (unsigned)len);
506 // }
507 //}
508 }
444 pub const Ty = struct {
445 base: Payload = Payload{ .tag = .ty },
446 ty: Type,
447 };
509448 };
449};
510450
511 pub const Int = struct {
512 base: Value,
513 big_int: std.math.big.Int,
514
515 pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
516 const self = try comp.gpa().create(Value.Int);
517 self.* = Value.Int{
518 .base = Value{
519 .id = .Int,
520 .typ = typ,
521 .ref_count = std.atomic.Int(usize).init(1),
522 },
523 .big_int = undefined,
524 };
525 typ.base.ref();
526 errdefer comp.gpa().destroy(self);
527
528 self.big_int = try std.math.big.Int.init(comp.gpa());
529 errdefer self.big_int.deinit();
530
531 try self.big_int.setString(base, value);
532
533 return self;
534 }
535
536 pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?*llvm.Value {
537 switch (self.base.typ.id) {
538 .Int => {
539 const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
540 if (self.big_int.len() == 0) {
541 return llvm.ConstNull(type_ref);
542 }
543 const unsigned_val = if (self.big_int.len() == 1) blk: {
544 break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
545 } else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
546 break :blk llvm.ConstIntOfArbitraryPrecision(
547 type_ref,
548 @intCast(c_uint, self.big_int.len()),
549 @ptrCast([*]u64, self.big_int.limbs.ptr),
550 );
551 } else {
552 @compileError("std.math.Big.Int.Limb size does not match LLVM");
553 };
554 return if (self.big_int.isPositive()) unsigned_val else llvm.ConstNeg(unsigned_val);
555 },
556 .ComptimeInt => unreachable,
557 else => unreachable,
558 }
559 }
560
561 pub fn copy(old: *Int, comp: *Compilation) !*Int {
562 old.base.typ.base.ref();
563 errdefer old.base.typ.base.deref(comp);
564
565 const new = try comp.gpa().create(Value.Int);
566 new.* = Value.Int{
567 .base = Value{
568 .id = .Int,
569 .typ = old.base.typ,
570 .ref_count = std.atomic.Int(usize).init(1),
571 },
572 .big_int = undefined,
573 };
574 errdefer comp.gpa().destroy(new);
575
576 new.big_int = try old.big_int.clone();
577 errdefer new.big_int.deinit();
578
579 return new;
580 }
451/// This is the heart of resource management of the Zig compiler. The Zig compiler uses
452/// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources
453/// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents
454/// a root.
455pub const MemoryCell = struct {
456 parent: Parent,
457 contents: Value,
581458
582 pub fn destroy(self: *Int, comp: *Compilation) void {
583 self.big_int.deinit();
584 comp.gpa().destroy(self);
585 }
459 pub const Parent = union(enum) {
460 none,
461 struct_field: struct {
462 struct_base: *MemoryCell,
463 field_index: usize,
464 },
465 array_elem: struct {
466 array_base: *MemoryCell,
467 elem_index: usize,
468 },
469 union_field: *MemoryCell,
470 err_union_code: *MemoryCell,
471 err_union_payload: *MemoryCell,
472 optional_payload: *MemoryCell,
473 optional_flag: *MemoryCell,
586474 };
587475};
src/ir.cpp+3-3
......@@ -11283,9 +11283,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction
1128311283 Buf *val_buf = buf_alloc();
1128411284 bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
1128511285 ir_add_error_node(ira, instruction->base.source_node,
11286 buf_sprintf("integer value %s has no representation in type '%s'",
11287 buf_ptr(val_buf),
11288 buf_ptr(&other_type->name)));
11286 buf_sprintf("type %s cannot represent integer value %s",
11287 buf_ptr(&other_type->name),
11288 buf_ptr(val_buf)));
1128911289 return false;
1129011290 }
1129111291 if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {
test/stage2/ir.zig created+54
......@@ -0,0 +1,54 @@
1test "hello world IR" {
2 exeCmp(
3 \\@0 = str("Hello, world!\n")
4 \\@1 = primitive(void)
5 \\@2 = primitive(usize)
6 \\@3 = fntype([], @1, cc=Naked)
7 \\@4 = int(0)
8 \\@5 = int(1)
9 \\@6 = int(231)
10 \\@7 = str("len")
11 \\
12 \\@8 = fn(@3, {
13 \\ %0 = as(@2, @5) ; SYS_write
14 \\ %1 = as(@2, @5) ; STDOUT_FILENO
15 \\ %2 = ptrtoint(@0) ; msg ptr
16 \\ %3 = fieldptr(@0, @7) ; msg len ptr
17 \\ %4 = deref(%3) ; msg len
18 \\ %sysoutreg = str("={rax}")
19 \\ %rax = str("{rax}")
20 \\ %rdi = str("{rdi}")
21 \\ %rsi = str("{rsi}")
22 \\ %rdx = str("{rdx}")
23 \\ %rcx = str("rcx")
24 \\ %r11 = str("r11")
25 \\ %memory = str("memory")
26 \\ %syscall = str("syscall")
27 \\ %5 = asm(%syscall, @2,
28 \\ volatile=1,
29 \\ output=%sysoutreg,
30 \\ inputs=[%rax, %rdi, %rsi, %rdx],
31 \\ clobbers=[%rcx, %r11, %memory],
32 \\ args=[%0, %1, %2, %4])
33 \\
34 \\ %6 = as(@2, @6) ;SYS_exit_group
35 \\ %7 = as(@2, @4) ;exit code
36 \\ %8 = asm(%syscall, @2,
37 \\ volatile=1,
38 \\ output=%sysoutreg,
39 \\ inputs=[%rax, %rdi],
40 \\ clobbers=[%rcx, %r11, %memory],
41 \\ args=[%6, %7])
42 \\
43 \\ %9 = unreachable()
44 \\})
45 \\
46 \\@9 = str("_start")
47 \\@10 = export(@9, @8)
48 ,
49 \\Hello, world!
50 \\
51 );
52}
53
54fn exeCmp(src: []const u8, expected_stdout: []const u8) void {}