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 {...@@ -1012,25 +1012,27 @@ pub const Dir = struct {
1012 /// On success, caller owns returned buffer.1012 /// On success, caller owns returned buffer.
1013 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.1013 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
1014 pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {1014 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);
1016 }1016 }
10171017
1018 /// On success, caller owns returned buffer.1018 /// On success, caller owns returned buffer.
1019 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.1019 /// 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(
1021 self: Dir,1022 self: Dir,
1022 allocator: *mem.Allocator,1023 allocator: *mem.Allocator,
1023 file_path: []const u8,1024 file_path: []const u8,
1024 max_bytes: usize,1025 max_bytes: usize,
1025 comptime A: u29,1026 comptime alignment: u29,
1026 ) ![]align(A) u8 {1027 comptime optional_sentinel: ?u8,
1028 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
1027 var file = try self.openFile(file_path, .{});1029 var file = try self.openFile(file_path, .{});
1028 defer file.close();1030 defer file.close();
10291031
1030 const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);1032 const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);
1031 if (size > max_bytes) return error.FileTooBig;1033 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);
1034 errdefer allocator.free(buf);1036 errdefer allocator.free(buf);
10351037
1036 try file.inStream().readNoEof(buf);1038 try file.inStream().readNoEof(buf);
lib/std/math/big/int.zig+14-12
...@@ -143,12 +143,15 @@ pub const Int = struct {...@@ -143,12 +143,15 @@ pub const Int = struct {
143 /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and143 /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and
144 /// can be modified separately from the original.144 /// can be modified separately from the original.
145 pub fn clone(other: Int) !Int {145 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 {
147 return Int{150 return Int{
148 .allocator = other.allocator,151 .allocator = allocator,
149 .metadata = other.metadata,152 .metadata = other.metadata,
150 .limbs = block: {153 .limbs = block: {
151 var limbs = try other.allocator.?.alloc(Limb, other.len());154 var limbs = try allocator.alloc(Limb, other.len());
152 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);155 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
153 break :block limbs;156 break :block limbs;
154 },157 },
...@@ -237,7 +240,7 @@ pub const Int = struct {...@@ -237,7 +240,7 @@ pub const Int = struct {
237 return bits;240 return bits;
238 }241 }
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 {
241 if (self.eqZero()) {244 if (self.eqZero()) {
242 return true;245 return true;
243 }246 }
...@@ -470,8 +473,8 @@ pub const Int = struct {...@@ -470,8 +473,8 @@ pub const Int = struct {
470 break;473 break;
471 }474 }
472 }475 }
473 } // Non power-of-two: batch divisions per word size.476 } else {
474 else {477 // Non power-of-two: batch divisions per word size.
475 const digits_per_limb = math.log(Limb, base, maxInt(Limb));478 const digits_per_limb = math.log(Limb, base, maxInt(Limb));
476 var limb_base: Limb = 1;479 var limb_base: Limb = 1;
477 var j: usize = 0;480 var j: usize = 0;
...@@ -479,7 +482,7 @@ pub const Int = struct {...@@ -479,7 +482,7 @@ pub const Int = struct {
479 limb_base *= base;482 limb_base *= base;
480 }483 }
481484
482 var q = try self.clone();485 var q = try self.clone2(allocator);
483 defer q.deinit();486 defer q.deinit();
484 q.abs();487 q.abs();
485 var r = try Int.init(allocator);488 var r = try Int.init(allocator);
...@@ -522,15 +525,13 @@ pub const Int = struct {...@@ -522,15 +525,13 @@ pub const Int = struct {
522525
523 /// To allow `std.fmt.printf` to work with Int.526 /// To allow `std.fmt.printf` to work with Int.
524 /// TODO make this non-allocating527 /// TODO make this non-allocating
528 /// TODO support read-only fixed integers
525 pub fn format(529 pub fn format(
526 self: Int,530 self: Int,
527 comptime fmt: []const u8,531 comptime fmt: []const u8,
528 options: std.fmt.FormatOptions,532 options: std.fmt.FormatOptions,
529 out_stream: var,533 out_stream: var,
530 ) !void {534 ) !void {
531 self.assertWritable();
532 // TODO support read-only fixed integers
533
534 comptime var radix = 10;535 comptime var radix = 10;
535 comptime var uppercase = false;536 comptime var uppercase = false;
536537
...@@ -550,8 +551,9 @@ pub const Int = struct {...@@ -550,8 +551,9 @@ pub const Int = struct {
550 @compileError("Unknown format string: '" ++ fmt ++ "'");551 @compileError("Unknown format string: '" ++ fmt ++ "'");
551 }552 }
552553
553 const str = self.toString(self.allocator.?, radix, uppercase) catch @panic("TODO make this non allocating");554 var buf: [4096]u8 = undefined;
554 defer self.allocator.?.free(str);555 var fba = std.heap.FixedBufferAllocator.init(&buf);
556 const str = self.toString(&fba.allocator, radix, uppercase) catch @panic("TODO make this non allocating");
555 return out_stream.writeAll(str);557 return out_stream.writeAll(str);
556 }558 }
557559
lib/std/mem.zig+28-3
...@@ -105,6 +105,31 @@ pub const Allocator = struct {...@@ -105,6 +105,31 @@ pub const Allocator = struct {
105 return self.alignedAlloc(T, null, n);105 return self.alignedAlloc(T, null, n);
106 }106 }
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
108 /// Allocates an array of `n + 1` items of type `T` and sets the first `n`133 /// Allocates an array of `n + 1` items of type `T` and sets the first `n`
109 /// items to `undefined` and the last item to `sentinel`. Depending on the134 /// items to `undefined` and the last item to `sentinel`. Depending on the
110 /// Allocator implementation, it may be required to call `free` once the135 /// Allocator implementation, it may be required to call `free` once the
...@@ -113,10 +138,10 @@ pub const Allocator = struct {...@@ -113,10 +138,10 @@ pub const Allocator = struct {
113 /// call `free` when done.138 /// call `free` when done.
114 ///139 ///
115 /// For allocating a single item, see `create`.140 /// For allocating a single item, see `create`.
141 ///
142 /// Deprecated; use `allocWithOptions`.
116 pub fn allocSentinel(self: *Allocator, comptime Elem: type, n: usize, comptime sentinel: Elem) Error![:sentinel]Elem {143 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);144 return self.allocWithOptions(Elem, n, null, sentinel);
118 ptr[n] = sentinel;
119 return ptr[0..n :sentinel];
120 }145 }
121146
122 pub fn alignedAlloc(147 pub fn alignedAlloc(
lib/std/target.zig+1-1
...@@ -761,7 +761,7 @@ pub const Target = struct {...@@ -761,7 +761,7 @@ pub const Target = struct {
761 };761 };
762 }762 }
763763
764 pub fn ptrBitWidth(arch: Arch) u32 {764 pub fn ptrBitWidth(arch: Arch) u16 {
765 switch (arch) {765 switch (arch) {
766 .avr,766 .avr,
767 .msp430,767 .msp430,
lib/std/zig.zig+2-1
...@@ -2,8 +2,9 @@ const tokenizer = @import("zig/tokenizer.zig");...@@ -2,8 +2,9 @@ const tokenizer = @import("zig/tokenizer.zig");
2pub const Token = tokenizer.Token;2pub const Token = tokenizer.Token;
3pub const Tokenizer = tokenizer.Tokenizer;3pub const Tokenizer = tokenizer.Tokenizer;
4pub const parse = @import("zig/parse.zig").parse;4pub 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;
6pub const render = @import("zig/render.zig").render;6pub const render = @import("zig/render.zig").render;
7pub const renderStringLiteral = @import("zig/string_literal.zig").render;
7pub const ast = @import("zig/ast.zig");8pub const ast = @import("zig/ast.zig");
8pub const system = @import("zig/system.zig");9pub const system = @import("zig/system.zig");
9pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;10pub 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 @@...@@ -1,2590 +1,740 @@
1const std = @import("std");1const std = @import("std");
2const Compilation = @import("compilation.zig").Compilation;2const mem = std.mem;
3const Scope = @import("scope.zig").Scope;
4const ast = std.zig.ast;
5const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
6const Value = @import("value.zig").Value;4const Value = @import("value.zig").Value;
7const Type = Value.Type;5const Type = @import("type.zig").Type;
8const assert = std.debug.assert;6const assert = std.debug.assert;
9const Token = std.zig.Token;7const text = @import("ir/text.zig");
10const Span = @import("errmsg.zig").Span;8const BigInt = std.math.big.Int;
11const llvm = @import("llvm.zig");9const Target = std.Target;
12const codegen = @import("codegen.zig");10
13const ObjectFile = codegen.ObjectFile;11/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
14const Decl = @import("decl.zig").Decl;12/// of instructions that correspond to the ZIR text format.
15const mem = std.mem;13/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
1614/// so are the `Value` and `Type`. The value of a constant must be copied into
17pub const LVal = enum {15/// a memory location for the value to survive after a const instruction.
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
50pub const Inst = struct {16pub const Inst = struct {
51 id: Id,17 tag: Tag,
52 scope: *Scope,18 ty: Type,
53 debug_id: usize,19 /// Byte offset into the source.
54 val: IrVal,20 src: usize,
55 ref_count: usize,21
56 span: Span,22 pub const Tag = enum {
57 owner_bb: *BasicBlock,23 unreach,
5824 constant,
59 /// true if this instruction was generated by zig and not from user code25 assembly,
60 is_generated: bool,26 ptrtoint,
6127 };
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,
7028
71 pub fn cast(base: *Inst, comptime T: type) ?*T {29 pub fn cast(base: *Inst, comptime T: type) ?*T {
72 if (base.id == comptime typeToId(T)) {30 if (base.tag != T.base_tag)
73 return @fieldParentPtr(T, "base", base);31 return null;
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 }
21732
218 pub fn setGenerated(base: *Inst) void {33 return @fieldParentPtr(T, "base", base);
219 base.is_generated = true;
220 }34 }
22135
222 pub fn isNoReturn(base: *const Inst) bool {36 pub fn Args(comptime T: type) type {
223 switch (base.val) {37 return std.meta.fieldInfo(T, "args").field_type;
224 .Unknown => return false,
225 .KnownValue => |x| return x.typ.id == .NoReturn,
226 .KnownType => |typ| return typ.id == .NoReturn,
227 }
228 }38 }
22939
230 pub fn isCompTime(base: *const Inst) bool {40 /// Returns `null` if runtime-known.
231 return base.val == .KnownValue;41 pub fn value(base: *Inst) ?Value {
232 }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 {46 .assembly,
235 assert(self.parent == null);47 .ptrtoint,
236 assert(parent.child == null);48 => null,
237 self.parent = parent;49 };
238 parent.child = self;
239 }50 }
24051
241 pub const Id = enum {52 pub const Unreach = struct {
242 Return,53 pub const base_tag = Tag.unreach;
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 {
258 base: Inst,54 base: Inst,
259 params: Params,55 args: void,
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 }
331 };56 };
33257
333 pub const Const = struct {58 pub const Constant = struct {
59 pub const base_tag = Tag.constant;
334 base: Inst,60 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 {62 val: Value,
358 return self.base.val.KnownValue.getLlvmConst(ofile);
359 }
360 };63 };
36164
362 pub const Return = struct {65 pub const Assembly = struct {
66 pub const base_tag = Tag.assembly;
363 base: Inst,67 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 {69 args: struct {
390 const value = self.params.return_value.llvm_value;70 asm_source: []const u8,
391 const return_type = self.params.return_value.getKnownType();71 is_volatile: bool,
39272 output: ?[]const u8,
393 if (return_type.handleIsPtr()) {73 inputs: []const []const u8,
394 @panic("TODO");74 clobbers: []const []const u8,
395 } else {75 args: []const *Inst,
396 _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;76 },
397 }
398 return null;
399 }
400 };77 };
40178
402 pub const Ref = struct {79 pub const PtrToInt = struct {
403 base: Inst,80 pub const base_tag = Tag.ptrtoint;
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;
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 {
454 base: Inst,82 base: Inst,
455 params: Params,83 args: struct {
45684 ptr: *Inst,
457 const Params = struct {85 },
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 }
494 };86 };
87};
49588
496 pub const VarPtr = struct {89pub const TypedValue = struct {
497 base: Inst,90 ty: Type,
498 params: Params,91 val: Value,
49992};
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 }
51393
514 pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {94pub const Module = struct {
515 switch (self.params.var_scope.data) {95 exports: []Export,
516 .Const => @panic("TODO"),96 errors: []ErrorMsg,
517 .Param => |param| {97 arena: std.heap.ArenaAllocator,
518 const new_inst = try ira.irb.build(98 fns: []Fn,
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 }
53699
537 pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) *llvm.Value {100 pub const Export = struct {
538 switch (self.params.var_scope.data) {101 name: []const u8,
539 .Const => unreachable, // turned into Inst.Const in analyze pass102 typed_value: TypedValue,
540 .Param => |param| return param.llvm_value,103 src: usize,
541 }
542 }
543 };104 };
544105
545 pub const LoadPtr = struct {106 pub const Fn = struct {
546 base: Inst,107 analysis_status: enum { in_progress, failure, success },
547 params: Params,108 body: []*Inst,
548109 fn_type: Type,
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 }
621 };110 };
622111
623 pub const PtrType = struct {112 pub fn deinit(self: *Module, allocator: *Allocator) void {
624 base: Inst,113 allocator.free(self.exports);
625 params: Params,114 allocator.free(self.errors);
626115 self.arena.deinit();
627 const Params = struct {116 self.* = undefined;
628 child_type: *Inst,117 }
629 mut: Type.Pointer.Mut,118};
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 }
642119
643 pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {120pub const ErrorMsg = struct {
644 const child_type = try self.params.child_type.getAsConstType(ira);121 byte_offset: usize,
645 // if (child_type->id == TypeTableEntryIdUnreachable) {122 msg: []const u8,
646 // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));123};
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);
666124
667 return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base);125pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
668 }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,
669 };137 };
670138 defer ctx.errors.deinit();
671 pub const DeclVar = struct {139 defer ctx.decl_table.deinit();
672 base: Inst,140 defer ctx.exports.deinit();
673 params: Params,141 defer ctx.fns.deinit();
674142 errdefer ctx.arena.deinit();
675 const Params = struct {143
676 variable: *Variable,144 ctx.analyzeRoot() catch |err| switch (err) {
677 };145 error.AnalysisFail => {
678146 assert(ctx.errors.items.len != 0);
679 const ir_val_init = IrVal.Init.Unknown;147 },
680148 else => |e| return e,
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 }
690 };149 };
691150 return Module{
692 pub const CheckVoidStmt = struct {151 .exports = ctx.exports.toOwnedSlice(),
693 base: Inst,152 .errors = ctx.errors.toOwnedSlice(),
694 params: Params,153 .fns = ctx.fns.toOwnedSlice(),
695154 .arena = ctx.arena,
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 }
718 };155 };
156}
719157
720 pub const Phi = struct {158const Analyze = struct {
721 base: Inst,159 allocator: *Allocator,
722 params: Params,160 arena: std.heap.ArenaAllocator,
723161 old_module: *const text.Module,
724 const Params = struct {162 errors: std.ArrayList(ErrorMsg),
725 incoming_blocks: []*BasicBlock,163 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
726 incoming_values: []*Inst,164 exports: std.ArrayList(Module.Export),
727 };165 fns: std.ArrayList(Module.Fn),
728166 target: Target,
729 const ir_val_init = IrVal.Init.Unknown;167
730168 const NewDecl = struct {
731 pub fn dump(inst: *const Phi) void {}169 /// null means a semantic analysis error happened
732170 ptr: ?*Inst,
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 }
740 };171 };
741172
742 pub const Br = struct {173 const NewInst = struct {
743 base: Inst,174 /// null means a semantic analysis error happened
744 params: Params,175 ptr: ?*Inst,
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 }
762 };176 };
763177
764 pub const CondBr = struct {178 const Fn = struct {
765 base: Inst,179 body: std.ArrayList(*Inst),
766 params: Params,180 inst_table: std.AutoHashMap(*text.Inst, NewInst),
767181 /// Index into Module fns array
768 const Params = struct {182 fn_index: usize,
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 }
786 };183 };
787184
788 pub const AddImplicitReturnType = struct {185 const InnerError = error{ OutOfMemory, AnalysisFail };
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;
797186
798 pub fn dump(inst: *const AddImplicitReturnType) void {187 fn analyzeRoot(self: *Analyze) !void {
799 std.debug.warn("#{}", .{inst.params.target.debug_id});188 for (self.old_module.decls) |decl| {
800 }189 if (decl.cast(text.Inst.Export)) |export_inst| {
801190 try analyzeExport(self, null, export_inst);
802 pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool {191 }
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});
825 }192 }
193 }
826194
827 pub fn hasSideEffects(inst: *const TestErr) bool {195 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
828 return false;196 if (opt_func) |func| {
197 if (func.inst_table.get(old_inst)) |kv| {
198 return kv.value.ptr orelse return error.AnalysisFail;
199 }
829 }200 }
830201
831 pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst {202 if (self.decl_table.get(old_inst)) |kv| {
832 const target = try self.params.target.getAsParam();203 return kv.value.ptr orelse return error.AnalysisFail;
833 const target_type = target.getKnownType();204 } else {
834 switch (target_type.id) {205 const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) {
835 .ErrorUnion => {206 error.AnalysisFail => {
836 return error.Unimplemented;207 try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
837 // if (instr_is_comptime(value)) {208 return error.AnalysisFail;
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);
870 },209 },
871 }210 else => |e| return e,
211 };
212 try self.decl_table.putNoClobber(old_inst, .{ .ptr = new_inst });
213 return new_inst;
872 }214 }
873 };215 }
874216
875 pub const TestCompTime = struct {217 fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn {
876 base: Inst,218 return func orelse return self.fail(src, "instruction illegal outside function body", .{});
877 params: Params,219 }
878220
879 pub const Params = struct {221 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {
880 target: *Inst,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,
881 };227 };
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;
943 }228 }
944229
945 pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void {230 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
946 assert(self.parent == null);231 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
947 assert(parent.child == null);
948 self.parent = parent;
949 parent.child = self;
950 }232 }
951};
952233
953/// Stuff that survives longer than Builder234 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
954pub const Code = struct {235 const new_inst = try self.resolveInst(func, old_inst);
955 basic_block_list: std.ArrayList(*BasicBlock),236 const wanted_type = Type.initTag(.const_slice_u8);
956 arena: std.heap.ArenaAllocator,237 const coerced_inst = try self.coerce(wanted_type, new_inst);
957 return_type: ?*Type,238 const val = try self.resolveConstValue(coerced_inst);
958 tree_scope: *Scope.AstTree,239 return val.toAllocatedBytes(&self.arena.allocator);
240 }
959241
960 /// allocator is comp.gpa()242 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
961 pub fn destroy(self: *Code, allocator: *Allocator) void {243 const new_inst = try self.resolveInst(func, old_inst);
962 self.arena.deinit();244 const wanted_type = Type.initTag(.@"type");
963 allocator.destroy(self);245 const coerced_inst = try self.coerce(wanted_type, new_inst);
246 const val = try self.resolveConstValue(coerced_inst);
247 return val.toType();
964 }248 }
965249
966 pub fn dump(self: *Code) void {250 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {
967 var bb_i: usize = 0;251 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);
968 for (self.basic_block_list.span()) |bb| {252 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);
969 std.debug.warn("{s}_{}:\n", .{ bb.name_hint, bb.debug_id });253
970 for (bb.instruction_list.span()) |instr| {254 switch (typed_value.ty.zigTypeTag()) {
971 std.debug.warn(" ", .{});255 .Fn => {},
972 instr.dump();256 else => return self.fail(
973 std.debug.warn("\n", .{});257 export_inst.positionals.value.src,
974 }258 "unable to export type '{}'",
259 .{typed_value.ty},
260 ),
975 }261 }
262 try self.exports.append(.{
263 .name = symbol_name,
264 .typed_value = typed_value,
265 .src = export_inst.base.src,
266 });
976 }267 }
977268
978 /// returns a ref-incremented value, or adds a compile error269 /// TODO should not need the cast on the last parameter at the callsites
979 pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value {270 fn addNewInstArgs(
980 const bb = self.basic_block_list.at(0);271 self: *Analyze,
981 for (bb.instruction_list.span()) |inst| {272 func: *Fn,
982 if (inst.cast(Inst.Return)) |ret_inst| {273 src: usize,
983 const ret_value = ret_inst.params.return_value;274 ty: Type,
984 if (ret_value.isCompTime()) {275 comptime T: type,
985 return ret_value.val.KnownValue.getRef();276 args: Inst.Args(T),
986 }277 ) !*Inst {
987 try comp.addCompileError(278 const inst = try self.addNewInst(func, src, ty, T);
988 self.tree_scope,279 inst.args = args;
989 ret_value.span,280 return &inst.base;
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;
1005 }281 }
1006};
1007282
1008pub const Builder = struct {283 fn addNewInst(self: *Analyze, func: *Fn, src: usize, ty: Type, comptime T: type) !*T {
1009 comp: *Compilation,284 const inst = try self.arena.allocator.create(T);
1010 code: *Code,285 inst.* = .{
1011 current_basic_block: *BasicBlock,286 .base = .{
1012 next_debug_id: usize,287 .tag = T.base_tag,
1013 is_comptime: bool,288 .ty = ty,
1014 is_async: bool,289 .src = src,
1015 begin_scope: ?*Scope,290 },
1016291 .args = undefined,
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,
1038 };292 };
293 try func.body.append(&inst.base);
294 return inst;
1039 }295 }
1040296
1041 pub fn abort(self: *Builder) void {297 fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst {
1042 self.code.destroy(self.comp.gpa());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;
1043 }308 }
1044309
1045 /// Call code.destroy() when done310 fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst {
1046 pub fn finish(self: *Builder) *Code {311 const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);
1047 return self.code;312 array_payload.* = .{ .len = str.len };
1048 }
1049313
1050 /// No need to clean up resources thanks to the arena allocator.314 const ty_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer);
1051 pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock {315 ty_payload.* = .{ .pointee_type = Type.initPayload(&array_payload.base) };
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 }
1068316
1069 pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void {317 const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes);
1070 try self.code.basic_block_list.append(basic_block);318 bytes_payload.* = .{ .data = str };
1071 self.setCursorAtEnd(basic_block);
1072 }
1073319
1074 pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void {320 return self.constInst(src, .{
1075 self.current_basic_block = basic_block;321 .ty = Type.initPayload(&ty_payload.base),
322 .val = Value.initPayload(&bytes_payload.base),
323 });
1076 }324 }
1077325
1078 pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {326 fn constType(self: *Analyze, src: usize, ty: Type) !*Inst {
1079 const alloc = irb.comp.gpa();327 return self.constInst(src, .{
1080 var frame = try alloc.create(@Frame(genNode));328 .ty = Type.initTag(.type),
1081 defer alloc.destroy(frame);329 .val = try ty.toValue(&self.arena.allocator),
1082 frame.* = async irb.genNode(node, scope, lval);330 });
1083 return await frame;
1084 }331 }
1085332
1086 pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {333 fn constVoid(self: *Analyze, src: usize) !*Inst {
1087 switch (node.id) {334 return self.constInst(src, .{
1088 .Root => unreachable,335 .ty = Type.initTag(.void),
1089 .Use => unreachable,336 .val = Value.initTag(.void_value),
1090 .TestDecl => unreachable,337 });
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 }
1192 }338 }
1193339
1194 fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {340 fn constIntUnsigned(self: *Analyze, src: usize, ty: Type, int: u64) !*Inst {
1195 const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None);341 const int_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
1196342 int_payload.* = .{ .int = int };
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 }
1203343
1204 //bool is_async = node->data.fn_call_expr.is_async;344 return self.constInst(src, .{
1205 //IrInstruction *async_allocator = nullptr;345 .ty = ty,
1206 //if (is_async) {346 .val = Value.initPayload(&int_payload.base),
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,
1217 });347 });
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);
1220 }348 }
1221349
1222 fn genPtrType(350 fn constIntSigned(self: *Analyze, src: usize, ty: Type, int: i64) !*Inst {
1223 irb: *Builder,351 const int_payload = try self.arena.allocator.create(Value.Payload.Int_i64);
1224 prefix_op: *ast.Node.PrefixOp,352 int_payload.* = .{ .int = int };
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 }
1286353
1287 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {354 return self.constInst(src, .{
1288 if (irb.is_comptime)355 .ty = ty,
1289 return true;356 .val = Value.initPayload(&int_payload.base),
1290357 });
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 }
1306 }358 }
1307359
1308 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {360 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst {
1309 const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);361 if (big_int.isPositive()) {
1310362 if (big_int.to(u64)) |x| {
1311 var base: u8 = undefined;363 return self.constIntUnsigned(src, ty, x);
1312 var rest: []const u8 = undefined;364 } else |err| switch (err) {
1313 if (int_token.len >= 3 and int_token[0] == '0') {365 error.NegativeIntoUnsigned => unreachable,
1314 rest = int_token[2..];366 error.TargetTooSmall => {}, // handled below
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 },
1323 }367 }
1324 } else {368 } else {
1325 base = 10;369 if (big_int.to(i64)) |x| {
1326 rest = int_token;370 return self.constIntSigned(src, ty, x);
371 } else |err| switch (err) {
372 error.NegativeIntoUnsigned => unreachable,
373 error.TargetTooSmall => {}, // handled below
374 }
1327 }375 }
1328376
1329 const comptime_int_type = Type.ComptimeInt.get(irb.comp);377 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig);
1330 defer comptime_int_type.base.base.deref(irb.comp);378 big_int_payload.* = .{ .big_int = big_int };
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);
1344379
1345 const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{});380 return self.constInst(src, .{
1346 inst.val = IrVal{ .KnownValue = &int_val.base };381 .ty = ty,
1347 return inst;382 .val = Value.initPayload(&big_int_payload.base),
383 });
1348 }384 }
1349385
1350 pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {386 fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
1351 const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);387 switch (old_inst.tag) {
1352 const src_span = Span.token(str_lit.token);388 .str => {
1353389 // We can use this reference because Inst.Const's Value is arena-allocated.
1354 var bad_index: usize = undefined;390 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
1355 var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) {391 const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;
1356 error.OutOfMemory => return error.OutOfMemory,392 return self.constStr(old_inst.src, bytes);
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;
1365 },393 },
1366 };394 .int => {
1367 var buf_cleaned = false;395 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
1368 errdefer if (!buf_cleaned) irb.comp.gpa().free(buf);396 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
1369397 },
1370 if (str_token[0] == 'c') {398 .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?),
1371 // first we add a null399 .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?),
1372 buf = try irb.comp.gpa().realloc(buf, buf.len + 1);400 .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?),
1373 buf[buf.len - 1] = 0;401 .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?),
1374402 .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?),
1375 // next make an array value403 .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?),
1376 const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf);404 .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),
1377 buf_cleaned = true;405 .@"export" => {
1378 defer array_val.base.deref(irb.comp);406 try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?);
1379407 return self.constVoid(old_inst.src);
1380 // then make a pointer value pointing at the first element408 },
1381 const ptr_val = try Value.Ptr.createArrayElemPtr(409 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
1382 irb.comp,410 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
1383 array_val,411 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
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);
1397 }412 }
1398 }413 }
1399414
1400 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {415 fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {
1401 const block_scope = try Scope.Block.create(irb.comp, parent_scope);416 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
1402417
1403 const outer_block_scope = &block_scope.base;418 var new_func: Fn = .{
1404 var child_scope = outer_block_scope;419 .body = std.ArrayList(*Inst).init(self.allocator),
1405420 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
1406 if (parent_scope.findFnDef()) |fndef_scope| {421 .fn_index = self.fns.items.len,
1407 if (fndef_scope.fn_val.?.block_scope == null) {422 };
1408 fndef_scope.fn_val.?.block_scope = block_scope;423 defer new_func.body.deinit();
1409 }424 defer new_func.inst_table.deinit();
1410 }425 // Don't hang on to a reference to this when analyzing body instructions, since the memory
1411426 // could become invalid.
1412 if (block.statements.len == 0) {427 (try self.fns.addOne()).* = .{
1413 // {}428 .analysis_status = .in_progress,
1414 return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);429 .fn_type = fn_type,
1415 }430 .body = undefined,
431 };
1416432
1417 if (block.label) |label| {433 for (fn_inst.positionals.body.instructions) |src_inst| {
1418 block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena());434 const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| {
1419 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());435 self.fns.items[new_func.fn_index].analysis_status = .failure;
1420 block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");436 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
1421 block_scope.is_comptime = try irb.buildConstBool(437 return err;
1422 parent_scope,438 };
1423 Span.token(block.lbrace),439 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
1424 irb.isCompTime(parent_scope),
1425 );
1426 }440 }
1427441
1428 var is_continuation_unreachable = false;442 const f = &self.fns.items[new_func.fn_index];
1429 var noreturn_return_value: ?*Inst = null;443 f.analysis_status = .success;
1430444 f.body = new_func.body.toOwnedSlice();
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);
1449445
1450 is_continuation_unreachable = statement_value.isNoReturn();446 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
1451 if (is_continuation_unreachable) {447 fn_payload.* = .{ .index = new_func.fn_index };
1452 // keep the last noreturn statement value around in case we need to return it
1453 noreturn_return_value = statement_value;
1454 }
1455448
1456 if (statement_value.cast(Inst.DeclVar)) |decl_var| {449 return self.constInst(fn_inst.base.src, .{
1457 // variable declarations start a new scope450 .ty = fn_type,
1458 child_scope = decl_var.params.variable.child_scope;451 .val = Value.initPayload(&fn_payload.base),
1459 } else if (!is_continuation_unreachable) {452 });
1460 // this statement's value must be void453 }
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 }
1472454
1473 if (is_continuation_unreachable) {455 fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {
1474 assert(noreturn_return_value != null);456 const return_type = try self.resolveType(func, fntype.positionals.return_type);
1475 if (block.label == null or block_scope.incoming_blocks.len == 0) {
1476 return noreturn_return_value.?;
1477 }
1478457
1479 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);458 if (return_type.zigTypeTag() == .NoReturn and
1480 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{459 fntype.positionals.param_types.len == 0 and
1481 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),460 fntype.kw_args.cc == .Naked)
1482 .incoming_values = block_scope.incoming_values.toOwnedSlice(),461 {
1483 });462 return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
1484 }463 }
1485464
1486 if (block.label) |label| {465 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
1487 try block_scope.incoming_blocks.append(irb.current_basic_block);466 }
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);
1492467
1493 _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{468 fn analyzeInstPrimitive(self: *Analyze, func: ?*Fn, primitive: *text.Inst.Primitive) InnerError!*Inst {
1494 .dest_block = block_scope.end_block,469 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
1495 .is_comptime = block_scope.is_comptime,470 }
1496 });
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{478 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
1501 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),479 const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr);
1502 .incoming_values = block_scope.incoming_values.toOwnedSlice(),480 if (ptr.ty.zigTypeTag() != .Pointer) {
1503 });481 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
1504 }482 }
1505483 // TODO handle known-pointer-address
1506 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit);484 const f = try self.requireFunctionBody(func, ptrtoint.base.src);
1507 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);485 const ty = Type.initTag(.usize);
486 return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
1508 }487 }
1509488
1510 pub fn genControlFlowExpr(489 fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
1511 irb: *Builder,490 const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr);
1512 control_flow_expr: *ast.Node.ControlFlowExpression,491 const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name);
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 });
1575492
1576 const ret_stmt_block = try irb.createBasicBlock(scope, "RetStmt");493 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
1577494 .Pointer => object_ptr.ty.elemType(),
1578 try irb.setCursorAtEndAndAppendBlock(err_block);495 else => return self.fail(fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
1579 if (have_err_defers) {496 };
1580 _ = try irb.genDefersForBlock(scope, outer_scope, .ErrorExit);497 switch (elem_ty.zigTypeTag()) {
1581 }498 .Array => {
1582 if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) {499 if (mem.eql(u8, field_name, "len")) {
1583 _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{});500 const len_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
1584 }501 len_payload.* = .{ .int = elem_ty.arrayLen() };
1585 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{502
1586 .dest_block = ret_stmt_block,503 const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal);
1587 .is_comptime = err_is_comptime,504 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
1588 });505
1589506 return self.constInst(fieldptr.base.src, .{
1590 try irb.setCursorAtEndAndAppendBlock(ok_block);507 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
1591 if (have_err_defers) {508 .val = Value.initPayload(&ref_payload.base),
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,
1597 });509 });
1598
1599 try irb.setCursorAtEndAndAppendBlock(ret_stmt_block);
1600 return irb.genAsyncReturn(scope, src_span, return_value, false);
1601 } else {510 } else {
1602 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);511 return self.fail(
1603 return irb.genAsyncReturn(scope, src_span, return_value, false);512 fieldptr.positionals.field_name.src,
1604 }513 "no member named '{}' in '{}'",
1605 },514 .{ field_name, elem_ty },
1606 }515 );
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 },
1654 }516 }
1655 },517 },
1656 .NotFound => {},518 else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
1657 }519 }
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;
1670 }520 }
1671521
1672 const DeferCounts = struct {522 fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
1673 scope_exit: usize,523 const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
1674 error_exit: usize,524 const new_inst = try self.resolveInst(func, intcast.positionals.value);
1675 };
1676525
1677 fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts {526 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
1678 var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 };527 .ComptimeInt => true,
1679528 .Int => false,
1680 var scope = inner_scope;529 else => return self.fail(
1681 while (scope != outer_scope) {530 intcast.positionals.dest_type.src,
1682 switch (scope.id) {531 "expected integer type, found '{}'",
1683 .Defer => {532 .{
1684 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);533 dest_type,
1685 switch (defer_scope.kind) {
1686 .ScopeExit => result.scope_exit += 1,
1687 .ErrorExit => result.error_exit += 1,
1688 }
1689 scope = scope.parent orelse break;
1690 },534 },
1691 .FnDef => break,535 ),
1692536 };
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 }
1706537
1707 fn genDefersForBlock(538 switch (new_inst.ty.zigTypeTag()) {
1708 irb: *Builder,539 .ComptimeInt, .Int => {},
1709 inner_scope: *Scope,540 else => return self.fail(
1710 outer_scope: *Scope,541 intcast.positionals.value.src,
1711 gen_kind: Scope.Defer.Kind,542 "expected integer type, found '{}'",
1712 ) !bool {543 .{new_inst.ty},
1713 var scope = inner_scope;544 ),
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 }
1755 }545 }
1756 }
1757546
1758 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst {547 if (dest_is_comptime_int or new_inst.value() != null) {
1759 switch (lval) {548 return self.coerce(dest_type, new_inst);
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 },
1770 }549 }
1771 }
1772550
1773 fn arena(self: *Builder) *Allocator {551 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
1774 return &self.code.arena.allocator;
1775 }552 }
1776553
1777 fn buildExtra(554 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
1778 self: *Builder,555 const ptr = try self.resolveInst(func, deref.positionals.ptr);
1779 comptime I: type,556 const elem_ty = switch (ptr.ty.zigTypeTag()) {
1780 scope: *Scope,557 .Pointer => ptr.ty.elemType(),
1781 span: Span,558 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
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,
1805 };559 };
1806560 if (ptr.value()) |val| {
1807 // Look at the params and ref() other instructions561 return self.constInst(deref.base.src, .{
1808 inline for (@typeInfo(I.Params).Struct.fields) |f| {562 .ty = elem_ty,
1809 switch (f.field_type) {563 .val = val.pointerDeref(),
1810 *Inst => @field(inst.params, f.name).ref(self),564 });
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 }
1834 }565 }
1835566
1836 self.next_debug_id += 1;567 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
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;
1865 }568 }
1866569
1867 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst {570 fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst {
1868 const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated);571 const return_type = try self.resolveType(func, assembly.positionals.return_type);
1869 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };572 const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source);
1870 return inst;573 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null;
1871 }
1872574
1873 fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst {575 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
1874 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});576 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
1875 inst.val = IrVal{ .KnownValue = v.getRef() };577 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
1876 return inst;
1877 }
1878578
1879 /// If the code is explicitly set to be comptime, then builds a const bool,579 for (inputs) |*elem, i| {
1880 /// otherwise builds a TestCompTime instruction.580 elem.* = try self.resolveConstString(func, assembly.kw_args.inputs[i]);
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 );
1891 }581 }
1892 }582 for (clobbers) |*elem, i| {
1893583 elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]);
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 );
1910 }584 }
1911 return error.Unimplemented;585 for (args) |*elem, i| {
1912 }586 elem.* = try self.resolveInst(func, assembly.kw_args.args[i]);
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.?;
1942 }587 }
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 {589 const f = try self.requireFunctionBody(func, assembly.base.src);
1967 var irb = try Builder.init(comp, tree_scope, null);590 return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
1968 errdefer irb.abort();591 .asm_source = asm_source,
1969592 .is_volatile = assembly.kw_args.@"volatile",
1970 return Analyze{593 .output = output,
1971 .irb = irb,594 .inputs = inputs,
1972 .old_bb_index = 0,595 .clobbers = clobbers,
1973 .const_predecessor_bb = null,596 .args = args,
1974 .parent_basic_block = undefined, // initialized with startBasicBlock597 });
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 };
1979 }598 }
1980599
1981 pub fn abort(self: *Analyze) void {600 fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst {
1982 self.irb.abort();601 const f = try self.requireFunctionBody(func, unreach.base.src);
602 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
1983 }603 }
1984604
1985 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock {605 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
1986 if (old_bb.child) |child| {606 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
1987 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)607 if (in_memory_result == .ok) {
1988 return child;608 return self.bitcast(dest_type, inst);
1989 }609 }
1990610
1991 const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint);611 // *[N]T to []T
1992 new_bb.linkToParent(old_bb);612 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
1993 new_bb.ref_instruction = ref_old_instruction;613 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
1994 return new_bb;614 {
1995 }615 const array_type = inst.ty.elemType();
1996616 const dst_elem_type = dest_type.elemType();
1997 pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void {617 if (array_type.zigTypeTag() == .Array and
1998 self.instruction_index = 0;618 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
1999 self.parent_basic_block = old_bb;619 {
2000 self.const_predecessor_bb = const_predecessor_bb;620 return self.coerceArrayPtrToSlice(dest_type, inst);
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;
2013 }621 }
2014 ira.instruction_index += 1;
2015 }622 }
2016623
2017 ira.old_bb_index += 1;624 // comptime_int to fixed-width integer
2018625 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
2019 var need_repeat = true;626 // The representation is already correct; we only need to make sure it fits in the destination type.
2020 while (true) {627 const val = inst.value().?; // comptime_int always has comptime known value
2021 while (ira.old_bb_index < old_code.basic_block_list.len) {628 if (!val.intFitsInType(dest_type, self.target)) {
2022 const old_bb = old_code.basic_block_list.at(ira.old_bb_index);629 return self.fail(inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
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;
2035 }630 }
2036 if (!need_repeat)631 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
2037 return;
2038 need_repeat = false;
2039 ira.old_bb_index = 0;
2040 continue;
2041 }632 }
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 {634 return self.fail(inst.src, "TODO implement type coercion", .{});
2049 // TODO actual implementation
2050 return &Type.Void.get(self.irb.comp).base;
2051 }635 }
2052636
2053 fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst {637 fn bitcast(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
2054 const dest_type = optional_dest_type orelse return target;638 if (inst.value()) |val| {
2055 const from_type = target.getKnownType();639 // Keep the comptime Value representation; take the new type.
2056 if (from_type == dest_type or from_type.id == .NoReturn) return target;640 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
2057 return self.analyzeCast(target, target, dest_type);641 }
642 return self.fail(inst.src, "TODO implement runtime bitcast", .{});
2058 }643 }
2059644
2060 fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst {645 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
2061 const from_type = target.getKnownType();646 if (inst.value()) |val| {
2062647 // The comptime Value representation is compatible with both types.
2063 //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {648 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
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);
2343 }649 }
2344650 return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
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;
2509 }651 }
2510652
2511 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value {653 fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError {
2512 @panic("TODO");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;
2513 }661 }
2514662
2515 fn getCompTimeRef(663 const InMemoryCoercionResult = enum {
2516 self: *Analyze,664 ok,
2517 value: *Value,665 no_match,
2518 ptr_mut: Value.Ptr.Mut,666 };
2519 mut: Type.Pointer.Mut,
2520 volatility: Type.Pointer.Vol,
2521 ) Analyze.Error!*Inst {
2522 return error.Unimplemented;
2523 }
2524};
2525667
2526pub fn gen(668 fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
2527 comp: *Compilation,669 if (dest_type.eql(src_type))
2528 body_node: *ast.Node,670 return .ok;
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 }
2544671
2545 return irb.finish();672 // TODO: implement more of this function
2546}
2547673
2548pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {674 return .no_match;
2549 const old_entry_bb = old_code.basic_block_list.at(0);675 }
676};
2550677
2551 var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);678pub fn main() anyerror!void {
2552 errdefer ira.abort();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);683 const args = try std.process.argsAlloc(allocator);
2555 new_entry_bb.ref(&ira.irb);
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) {690 var zir_module = try text.parse(allocator, source);
2562 const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);691 defer zir_module.deinit(allocator);
2563692
2564 if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) {693 if (zir_module.errors.len != 0) {
2565 ira.instruction_index += 1;694 for (zir_module.errors) |err_msg| {
2566 continue;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 });
2567 }697 }
698 if (debug_error_trace) return error.ParseFailure;
699 std.process.exit(1);
700 }
2568701
2569 const return_inst = try old_instruction.analyze(&ira);702 var analyzed_module = try analyze(allocator, zir_module);
2570 assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point703 defer analyzed_module.deinit(allocator);
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
2574704
2575 if (return_inst.isNoReturn()) {705 if (analyzed_module.errors.len != 0) {
2576 try ira.finishBasicBlock(old_code);706 for (analyzed_module.errors) |err_msg| {
2577 continue;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 });
2578 }709 }
2579710 if (debug_error_trace) return error.ParseFailure;
2580 ira.instruction_index += 1;711 std.process.exit(1);
2581 }712 }
2582713
2583 if (ira.src_implicit_return_type_list.len == 0) {714 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
2584 ira.irb.code.return_type = &Type.NoReturn.get(comp).base;715 defer new_zir_module.deinit(allocator);
2585 return ira.irb.finish();
2586 }
2587716
2588 ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.span());717 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
2589 return ira.irb.finish();718 try new_zir_module.writeToStream(allocator, bos.outStream());
719 try bos.flush();
2590}720}
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 @@...@@ -1,1075 +1,827 @@
1const std = @import("std");1const std = @import("std");
2const builtin = std.builtin;
3const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;
5const Value = @import("value.zig").Value;2const Value = @import("value.zig").Value;
6const llvm = @import("llvm.zig");
7const event = std.event;
8const Allocator = std.mem.Allocator;
9const assert = std.debug.assert;3const 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 {62 pub fn initTag(comptime small_tag: Tag) Type {
12 base: Value,63 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
13 id: Id,64 return .{ .tag_if_small_enough = @enumToInt(small_tag) };
14 name: []const u8,65 }
15 abi_alignment: AbiAlignment,
16
17 pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
18
19 pub const Id = builtin.TypeId;
2066
21 pub fn destroy(base: *Type, comp: *Compilation) void {67 pub fn initPayload(payload: *Payload) Type {
22 switch (base.id) {68 assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
23 .Struct => @fieldParentPtr(Struct, "base", base).destroy(comp),69 return .{ .ptr_otherwise = payload };
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 }
49 }70 }
5071
51 pub fn getLlvmType(72 pub fn tag(self: Type) Tag {
52 base: *Type,73 if (self.tag_if_small_enough < Tag.no_payload_count) {
53 allocator: *Allocator,74 return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
54 llvm_context: *llvm.Context,75 } else {
55 ) error{OutOfMemory}!*llvm.Type {76 return self.ptr_otherwise.tag;
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),
82 }77 }
83 }78 }
8479
85 pub fn handleIsPtr(base: *Type) bool {80 pub fn cast(self: Type, comptime T: type) ?*T {
86 switch (base.id) {81 if (self.tag_if_small_enough < Tag.no_payload_count)
87 .Type,82 return null;
88 .ComptimeFloat,83
89 .ComptimeInt,84 const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
90 .EnumLiteral,85 if (self.ptr_otherwise.tag != expected_tag)
91 .Undefined,86 return null;
92 .Null,
93 .BoundFn,
94 .Opaque,
95 => unreachable,
9687
97 .NoReturn,88 return @fieldParentPtr(T, "base", self.ptr_otherwise);
98 .Void,89 }
99 .Bool,90
100 .Int,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 },
101 .Float,126 .Float,
102 .Pointer,127 .Array,
128 .Struct,
129 .Optional,
130 .ErrorUnion,
103 .ErrorSet,131 .ErrorSet,
104 .Enum,132 .Enum,
133 .Union,
105 .Fn,134 .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,
127 .BoundFn,135 .BoundFn,
128 .Opaque,136 .Opaque,
129 => unreachable,
130
131 .Void,
132 .NoReturn,
133 => return false,
134
135 .Bool,
136 .Int,
137 .Float,
138 .Fn,
139 .Frame,137 .Frame,
140 .AnyFrame,138 .AnyFrame,
141 .Vector,139 .Vector,
142 => return true,140 .EnumLiteral,
143141 => @panic("TODO implement more Type equality comparison"),
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"),
156 }142 }
157 }143 }
158144
159 pub fn cast(base: *Type, comptime T: type) ?*T {145 pub fn format(
160 if (base.id != @field(Id, @typeName(T))) return null;146 self: Type,
161 return @fieldParentPtr(T, "base", base);147 comptime fmt: []const u8,
162 }148 options: std.fmt.FormatOptions,
163149 out_stream: var,
164 pub fn dump(base: *const Type) void {150 ) !void {
165 std.debug.warn("{}", .{@tagName(base.id)});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 }
166 }206 }
167207
168 fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {208 pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
169 base.* = Type{209 switch (self.tag()) {
170 .base = Value{210 .@"u8" => return Value.initTag(.u8_type),
171 .id = .Type,211 .@"i8" => return Value.initTag(.i8_type),
172 .typ = &MetaType.get(comp).base,212 .@"isize" => return Value.initTag(.isize_type),
173 .ref_count = std.atomic.Int(usize).init(1),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);
174 },242 },
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);
193 }243 }
194 base.abi_alignment.resolve();
195 return base.abi_alignment.data;
196 }244 }
197245
198 /// If you have an llvm conext handy, you can use it here.246 pub fn isSinglePointer(self: Type) bool {
199 pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {247 return switch (self.tag()) {
200 if (base.abi_alignment.start()) |ptr| return ptr.*;248 .@"u8",
201249 .@"i8",
202 base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);250 .@"isize",
203 base.abi_alignment.resolve();251 .@"usize",
204 return base.abi_alignment.data;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 };
205 }283 }
206284
207 /// Lower level function that does the work. See getAbiAlignment.285 pub fn isSlice(self: Type) bool {
208 fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {286 return switch (self.tag()) {
209 const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);287 .@"u8",
210 return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));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 };
211 }321 }
212322
213 pub const Struct = struct {323 /// Asserts the type is a pointer type.
214 base: Type,324 pub fn pointerIsConst(self: Type) bool {
215 decls: *Scope.Decls,325 return switch (self.tag()) {
216326 .@"u8",
217 pub fn destroy(self: *Struct, comp: *Compilation) void {327 .@"i8",
218 comp.gpa().destroy(self);328 .@"isize",
219 }329 .@"usize",
220330 .@"c_short",
221 pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {331 .@"c_ushort",
222 @panic("TODO");332 .@"c_int",
223 }333 .@"c_uint",
224 };334 .@"c_long",
225335 .@"c_ulong",
226 pub const Fn = struct {336 .@"c_longlong",
227 base: Type,337 .@"c_ulonglong",
228 key: Key,338 .@"c_longdouble",
229 non_key: NonKey,339 .@"f16",
230 garbage_node: std.atomic.Stack(*Fn).Node,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 {356 .single_const_pointer,
233 Normal,357 .single_const_pointer_to_comptime_int,
234 Generic,358 .const_slice_u8,
359 => true,
235 };360 };
361 }
236362
237 pub const NonKey = union {363 /// Asserts the type is a pointer or array type.
238 Normal: Normal,364 pub fn elemType(self: Type) Type {
239 Generic: void,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 {394 .array => self.cast(Payload.Array).?.elem_type,
242 variable_list: std.ArrayList(*Scope.Var),395 .single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
243 };396 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
397 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
244 };398 };
399 }
245400
246 pub const Key = struct {401 /// Asserts the type is an array.
247 data: Data,402 pub fn arrayLen(self: Type) u64 {
248 alignment: ?u32,403 return switch (self.tag()) {
249404 .u8,
250 pub const Data = union(Kind) {405 .i8,
251 Generic: Generic,406 .isize,
252 Normal: Normal,407 .usize,
253 };408 .c_short,
254409 .c_ushort,
255 pub const Normal = struct {410 .c_int,
256 params: []Param,411 .c_uint,
257 return_type: *Type,412 .c_long,
258 is_var_args: bool,413 .c_ulong,
259 cc: CallingConvention,414 .c_longlong,
260 };415 .c_ulonglong,
261416 .c_longdouble,
262 pub const Generic = struct {417 .f16,
263 param_count: usize,418 .f32,
264 cc: CallingConvention,419 .f64,
265 };420 .f128,
266421 .c_void,
267 pub fn hash(self: *const Key) u32 {422 .bool,
268 var result: u32 = 0;423 .void,
269 result +%= hashAny(self.alignment, 0);424 .type,
270 switch (self.data) {425 .anyerror,
271 .Generic => |generic| {426 .comptime_int,
272 result +%= hashAny(generic.param_count, 1);427 .comptime_float,
273 result +%= hashAny(generic.cc, 3);428 .noreturn,
274 },429 .fn_naked_noreturn_no_args,
275 .Normal => |normal| {430 .single_const_pointer,
276 result +%= hashAny(normal.return_type, 4);431 .single_const_pointer_to_comptime_int,
277 result +%= hashAny(normal.is_var_args, 5);432 .const_slice_u8,
278 result +%= hashAny(normal.cc, 6);433 => unreachable,
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 }
326434
327 pub fn ref(key: Key) void {435 .array => self.cast(Payload.Array).?.len,
328 switch (key.data) {436 .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
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 }
338 };437 };
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 {464 .@"u8" => .{ .signed = false, .bits = 8 },
343 is_noalias: bool,465 .@"i8" => .{ .signed = true, .bits = 8 },
344 typ: *Type,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) },
345 };476 };
477 }
346478
347 fn ccFnTypeStr(cc: CallingConvention) []const u8 {479 /// Asserts the type is a function.
348 return switch (cc) {480 pub fn fnParamLen(self: Type) usize {
349 .Unspecified => "",481 return switch (self.tag()) {
350 .C => "extern ",482 .fn_naked_noreturn_no_args => 0,
351 .Cold => "coldcc ",483
352 .Naked => "nakedcc ",484 .f16,
353 .Stdcall => "stdcallcc ",485 .f32,
354 .Async => "async ",486 .f64,
355 else => unreachable,487 .f128,
356 };488 .c_longdouble,
357 }489 .c_void,
358490 .bool,
359 pub fn paramCount(self: *Fn) usize {491 .void,
360 return switch (self.key.data) {492 .type,
361 .Generic => |generic| generic.param_count,493 .anyerror,
362 .Normal => |normal| normal.params.len,494 .comptime_int,
363 };495 .comptime_float,
364 }496 .noreturn,
365497 .array,
366 /// takes ownership of key.Normal.params on success498 .single_const_pointer,
367 pub fn get(comp: *Compilation, key: Key) !*Fn {499 .single_const_pointer_to_comptime_int,
368 {500 .array_u8_sentinel_0,
369 const held = comp.fn_type_table.acquire();501 .const_slice_u8,
370 defer held.release();502 .u8,
371503 .i8,
372 if (held.value.get(&key)) |entry| {504 .usize,
373 entry.value.base.base.ref();505 .isize,
374 return entry.value;506 .c_short,
375 }507 .c_ushort,
376 }508 .c_int,
377509 .c_uint,
378 key.ref();510 .c_long,
379 errdefer key.deref(comp);511 .c_ulong,
380512 .c_longlong,
381 const self = try comp.gpa().create(Fn);513 .c_ulonglong,
382 self.* = Fn{514 => unreachable,
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 }
557 };515 };
516 }
558517
559 pub fn get_u8(comp: *Compilation) *Int {518 /// Asserts the type is a function. The length of the slice must be at least the length
560 comp.u8_type.base.base.ref();519 /// given by `fnParamLen`.
561 return comp.u8_type;520 pub fn fnParamTypes(self: Type, types: []Type) void {
562 }521 switch (self.tag()) {
563522 .fn_naked_noreturn_no_args => return,
564 pub fn get(comp: *Compilation, key: Key) !*Int {523
565 {524 .f16,
566 const held = comp.int_type_table.acquire();525 .f32,
567 defer held.release();526 .f64,
568527 .f128,
569 if (held.value.get(&key)) |entry| {528 .c_longdouble,
570 entry.value.base.base.ref();529 .c_void,
571 return entry.value;530 .bool,
572 }531 .void,
573 }532 .type,
574533 .anyerror,
575 const self = try comp.gpa().create(Int);534 .comptime_int,
576 self.* = Int{535 .comptime_float,
577 .base = undefined,536 .noreturn,
578 .key = key,537 .array,
579 .garbage_node = undefined,538 .single_const_pointer,
580 };539 .single_const_pointer_to_comptime_int,
581 errdefer comp.gpa().destroy(self);540 .array_u8_sentinel_0,
582541 .const_slice_u8,
583 const u_or_i = "ui"[@boolToInt(key.is_signed)];542 .u8,
584 const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", .{ u_or_i, key.bit_count });543 .i8,
585 errdefer comp.gpa().free(name);544 .usize,
586545 .isize,
587 self.base.init(comp, .Int, name);546 .c_short,
588547 .c_ushort,
589 {548 .c_int,
590 const held = comp.int_type_table.acquire();549 .c_uint,
591 defer held.release();550 .c_long,
592551 .c_ulong,
593 _ = try held.value.put(&self.key, self);552 .c_longlong,
594 }553 .c_ulonglong,
595 return self;554 => unreachable,
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");
632 }555 }
633 };556 }
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 };
679557
680 pub const Vol = enum {558 /// Asserts the type is a function.
681 Non,559 pub fn fnReturnType(self: Type) Type {
682 Volatile,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,
683 };594 };
595 }
684596
685 pub const Align = union(enum) {597 /// Asserts the type is a function.
686 Abi,598 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
687 Override: u32,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,
688 };633 };
634 }
689635
690 pub const Size = builtin.TypeInfo.Pointer.Size;636 /// This enum does not directly correspond to `std.builtin.TypeId` because
691637 /// it has extra enum tags in it, as a way of using less memory. For example,
692 pub fn destroy(self: *Pointer, comp: *Compilation) void {638 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
693 self.garbage_node = std.atomic.Stack(*Pointer).Node{639 /// but with different alignment values, in this data structure they are represented
694 .data = self,640 /// with different enum tags, because the the former requires more payload data than the latter.
695 .next = undefined,641 /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
696 };642 pub const Tag = enum {
697 comp.registerGarbage(Pointer, &self.garbage_node);643 // The first section of this enum are tags that require no payload.
698 }644 u8,
699645 i8,
700 pub fn gcDestroy(self: *Pointer, comp: *Compilation) void {646 isize,
701 {647 usize,
702 const held = comp.ptr_type_table.acquire();648 c_short,
703 defer held.release();649 c_ushort,
704650 c_int,
705 _ = held.value.remove(&self.key).?;651 c_uint,
706 }652 c_long,
707 self.key.child_type.base.deref(comp);653 c_ulong,
708 comp.gpa().destroy(self);654 c_longlong,
709 }655 c_ulonglong,
710656 c_longdouble,
711 pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {657 c_void,
712 switch (self.key.alignment) {658 f16,
713 .Abi => return self.key.child_type.getAbiAlignment(comp),659 f32,
714 .Override => |alignment| return alignment,660 f64,
715 }661 f128,
716 }662 bool,
717663 void,
718 pub fn get(664 type,
719 comp: *Compilation,665 anyerror,
720 key: Key,666 comptime_int,
721 ) !*Pointer {667 comptime_float,
722 var normal_key = key;668 noreturn,
723 switch (key.alignment) {669 fn_naked_noreturn_no_args,
724 .Abi => {},670 single_const_pointer_to_comptime_int,
725 .Override => |alignment| {671 const_slice_u8, // See last_no_payload_tag below.
726 // TODO https://github.com/ziglang/zig/issues/3190672 // After this, the tag requires a payload.
727 var align_spill = alignment;673
728 const abi_align = try key.child_type.getAbiAlignment(comp);674 array_u8_sentinel_0,
729 if (abi_align == align_spill) {675 array,
730 normal_key.alignment = .Abi;676 single_const_pointer,
731 }677
732 },678 pub const last_no_payload_tag = Tag.const_slice_u8;
733 }679 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
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 }
798 };680 };
799681
800 pub const Array = struct {682 pub const Payload = struct {
801 base: Type,683 tag: Tag,
802 key: Key,
803 garbage_node: std.atomic.Stack(*Array).Node,
804684
805 pub const Key = struct {685 pub const Array_u8_Sentinel0 = struct {
806 elem_type: *Type,686 base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
807 len: usize,
808687
809 pub fn hash(self: *const Key) u32 {688 len: u64,
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 }
819 };689 };
820690
821 pub fn destroy(self: *Array, comp: *Compilation) void {691 pub const Array = struct {
822 self.key.elem_type.base.deref(comp);692 base: Payload = Payload{ .tag = .array },
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,
882693
883 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {694 elem_type: Type,
884 comp.gpa().destroy(self);695 len: u64,
885 }696 };
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 };
1003697
1004 pub const Opaque = struct {698 pub const SingleConstPointer = struct {
1005 base: Type,699 base: Payload = Payload{ .tag = .single_const_pointer },
1006700
1007 pub fn destroy(self: *Opaque, comp: *Compilation) void {701 pointee_type: Type,
1008 comp.gpa().destroy(self);702 };
1009 }
1010
1011 pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
1012 @panic("TODO");
1013 }
1014 };703 };
704};
1015705
1016 pub const Frame = struct {706pub const CInteger = enum {
1017 base: Type,707 short,
1018708 ushort,
1019 pub fn destroy(self: *Frame, comp: *Compilation) void {709 int,
1020 comp.gpa().destroy(self);710 uint,
1021 }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 {749 .linux,
1024 @panic("TODO");750 .macosx,
1025 }751 .freebsd,
1026 };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 {772 .windows, .uefi => switch (self) {
1029 base: Type,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 {786 .ios => switch (self) {
1032 comp.gpa().destroy(self);787 .short,
1033 }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 {800 .ananas,
1036 @panic("TODO");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"),
1037 }825 }
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))),
1074 }826 }
1075}827};
src-self-hosted/value.zig+425-537
...@@ -1,587 +1,475 @@...@@ -1,587 +1,475 @@
1const std = @import("std");1const std = @import("std");
2const Scope = @import("scope.zig").Scope;2const Type = @import("type.zig").Type;
3const Compilation = @import("compilation.zig").Compilation;3const log2 = std.math.log2;
4const ObjectFile = @import("codegen.zig").ObjectFile;
5const llvm = @import("llvm.zig");
6const ArrayListSentineled = std.ArrayListSentineled;
7const assert = std.debug.assert;4const 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-write71 pub fn initTag(comptime small_tag: Tag) Value {
10/// If there is only 1 ref then write need not copy72 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
11pub const Value = struct {73 return .{ .tag_if_small_enough = @enumToInt(small_tag) };
12 id: Id,74 }
13 typ: *Type,
14 ref_count: std.atomic.Int(usize),
1575
16 /// Thread-safe76 pub fn initPayload(payload: *Payload) Value {
17 pub fn ref(base: *Value) void {77 assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
18 _ = base.ref_count.incr();78 return .{ .ptr_otherwise = payload };
19 }79 }
2080
21 /// Thread-safe81 pub fn tag(self: Value) Tag {
22 pub fn deref(base: *Value, comp: *Compilation) void {82 if (self.tag_if_small_enough < Tag.no_payload_count) {
23 if (base.ref_count.decr() == 1) {83 return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
24 base.typ.base.deref(comp);84 } else {
25 switch (base.id) {85 return self.ptr_otherwise.tag;
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 }
36 }86 }
37 }87 }
3888
39 pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {89 pub fn cast(self: Value, comptime T: type) ?*T {
40 base.typ.base.deref(comp);90 if (self.tag_if_small_enough < Tag.no_payload_count)
41 new_type.base.ref();91 return null;
42 base.typ = new_type;
43 }
4492
45 pub fn getRef(base: *Value) *Value {93 const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
46 base.ref();94 if (self.ptr_otherwise.tag != expected_tag)
47 return base;95 return null;
48 }
4996
50 pub fn cast(base: *Value, comptime T: type) ?*T {97 return @fieldParentPtr(T, "base", self.ptr_otherwise);
51 if (base.id != @field(Id, @typeName(T))) return null;
52 return @fieldParentPtr(T, "base", base);
53 }98 }
5499
55 pub fn dump(base: *const Value) void {100 pub fn format(
56 std.debug.warn("{}", .{@tagName(base.id)});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 };
57 }156 }
58157
59 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?*llvm.Value) {158 /// Asserts that the value is representable as an array of bytes.
60 switch (base.id) {159 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
61 .Type => unreachable,160 pub fn toAllocatedBytes(self: Value, allocator: *Allocator) Allocator.Error![]u8 {
62 .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),161 if (self.cast(Payload.Bytes)) |bytes| {
63 .FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),162 return std.mem.dupe(allocator, u8, bytes.data);
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),
70 }163 }
164 unreachable;
71 }165 }
72166
73 pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {167 /// Asserts that the value is representable as a type.
74 if (self.ref_count.get() == 1) {168 pub fn toType(self: Value) Type {
75 // ( Í¡° ͜ʖ Í¡°)169 return switch (self.tag()) {
76 return self;170 .ty => self.cast(Payload.Ty).?.ty,
77 }171
78172 .u8_type => Type.initTag(.@"u8"),
79 assert(self.ref_count.decr() != 1);173 .i8_type => Type.initTag(.@"i8"),
80 return self.copy(comp);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 };
81 }215 }
82216
83 pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {217 /// Asserts the value is an integer.
84 switch (base.id) {218 pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt {
85 .Type => unreachable,219 switch (self.tag()) {
86 .Fn => unreachable,220 .ty,
87 .FnProto => unreachable,221 .u8_type,
88 .Void => unreachable,222 .i8_type,
89 .Bool => unreachable,223 .isize_type,
90 .NoReturn => unreachable,224 .usize_type,
91 .Ptr => unreachable,225 .c_short_type,
92 .Array => unreachable,226 .c_ushort_type,
93 .Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,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,
94 }264 }
95 }265 }
96266
97 pub const Parent = union(enum) {267 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
98 None,268 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
99 BaseStruct: BaseStruct,269 switch (self.tag()) {
100 BaseArray: BaseArray,270 .ty,
101 BaseUnion: *Value,271 .u8_type,
102 BaseScalar: *Value,272 .i8_type,
103273 .isize_type,
104 pub const BaseStruct = struct {274 .usize_type,
105 val: *Value,275 .c_short_type,
106 field_index: usize,276 .c_ushort_type,
107 };277 .c_int_type,
108278 .c_uint_type,
109 pub const BaseArray = struct {279 .c_long_type,
110 val: *Value,280 .c_ulong_type,
111 elem_index: usize,281 .c_longlong_type,
112 };282 .c_ulonglong_type,
113 };283 .c_longdouble_type,
114284 .f16_type,
115 pub const Id = enum {285 .f32_type,
116 Type,286 .f64_type,
117 Fn,287 .f128_type,
118 Void,288 .c_void_type,
119 Bool,289 .bool_type,
120 NoReturn,290 .void_type,
121 Array,291 .type_type,
122 Ptr,292 .anyerror_type,
123 Int,293 .comptime_int_type,
124 FnProto,294 .comptime_float_type,
125 };295 .noreturn_type,
126296 .fn_naked_noreturn_no_args_type,
127 pub const Type = @import("type.zig").Type;297 .single_const_pointer_to_comptime_int_type,
128298 .const_slice_u8_type,
129 pub const FnProto = struct {299 .void_value,
130 base: Value,300 .noreturn_value,
131301 .bool_true,
132 /// The main external name that is used in the .o file.302 .bool_false,
133 /// TODO https://github.com/ziglang/zig/issues/265303 .function,
134 symbol_name: ArrayListSentineled(u8, 0),304 .ref,
135305 .ref_val,
136 pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: ArrayListSentineled(u8, 0)) !*FnProto {306 .bytes,
137 const self = try comp.gpa().create(FnProto);307 => unreachable,
138 self.* = FnProto{308
139 .base = Value{309 .zero => return true,
140 .id = .FnProto,310
141 .typ = &fn_type.base,311 .int_u64 => switch (ty.zigTypeTag()) {
142 .ref_count = std.atomic.Int(usize).init(1),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;
143 },318 },
144 .symbol_name = symbol_name,319 .ComptimeInt => return true,
145 };320 else => unreachable,
146 fn_type.base.base.ref();321 },
147 return self;322 .int_i64 => switch (ty.zigTypeTag()) {
148 }323 .Int => {
149324 const x = self.cast(Payload.Int_i64).?.int;
150 pub fn destroy(self: *FnProto, comp: *Compilation) void {325 if (x == 0) return true;
151 self.symbol_name.deinit();326 const info = ty.intInfo(target);
152 comp.gpa().destroy(self);327 if (!info.signed and x < 0)
153 }328 return false;
154329 @panic("TODO implement i64 intFitsInType");
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),
207 },330 },
208 .fndef_scope = fndef_scope,331 .ComptimeInt => return true,
209 .child_scope = &fndef_scope.base,332 else => unreachable,
210 .block_scope = null,333 },
211 .symbol_name = symbol_name,334 .int_big => switch (ty.zigTypeTag()) {
212 .containing_object = ArrayListSentineled(u8, 0).initNull(comp.gpa()),335 .Int => {
213 .link_set_node = link_set_node,336 const info = ty.intInfo(target);
214 };337 return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
215 fn_type.base.base.ref();338 },
216 fndef_scope.fn_val = self;339 .ComptimeInt => return true,
217 fndef_scope.base.ref();340 else => unreachable,
218 return self;341 },
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;
301 }342 }
343 }
302344
303 pub fn destroy(self: *NoReturn, comp: *Compilation) void {345 /// Asserts the value is a pointer and dereferences it.
304 comp.gpa().destroy(self);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,
305 }391 }
306 };392 }
307393
308 pub const Ptr = struct {394 /// This type is not copyable since it may contain pointers to its inner data.
309 base: Value,395 pub const Payload = struct {
310 special: Special,396 tag: Tag,
311 mut: Mut,
312397
313 pub const Mut = enum {398 pub const Int_u64 = struct {
314 CompTimeConst,399 base: Payload = Payload{ .tag = .int_u64 },
315 CompTimeVar,400 int: u64,
316 RunTime,
317 };401 };
318402
319 pub const Special = union(enum) {403 pub const Int_i64 = struct {
320 Scalar: *Value,404 base: Payload = Payload{ .tag = .int_i64 },
321 BaseArray: BaseArray,405 int: i64,
322 BaseStruct: BaseStruct,
323 HardCodedAddr: u64,
324 Discard,
325 };406 };
326407
327 pub const BaseArray = struct {408 pub const IntBig = struct {
328 val: *Value,409 base: Payload = Payload{ .tag = .int_big },
329 elem_index: usize,410 big_int: BigInt,
330 };411 };
331412
332 pub const BaseStruct = struct {413 pub const Function = struct {
333 val: *Value,414 base: Payload = Payload{ .tag = .function },
334 field_index: usize,415 /// Index into the `fns` array of the `ir.Module`
416 index: usize,
335 };417 };
336418
337 pub fn createArrayElemPtr(419 pub const ArraySentinel0_u8_Type = struct {
338 comp: *Compilation,420 base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
339 array_val: *Array,421 len: u64,
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,
418 };422 };
419423
420 pub const Data = struct {424 pub const SingleConstPtrType = struct {
421 parent: Parent,425 base: Payload = Payload{ .tag = .single_const_ptr_type },
422 elements: []*Value,426 elem_type: *Type,
423 };427 };
424428
425 /// Takes ownership of buffer429 pub const Ref = struct {
426 pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {430 base: Payload = Payload{ .tag = .ref },
427 const u8_type = Type.Int.get_u8(comp);431 cell: *MemoryCell,
428 defer u8_type.base.base.deref(comp);432 };
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);
446433
447 return self;434 pub const RefVal = struct {
448 }435 base: Payload = Payload{ .tag = .ref_val },
436 val: Value,
437 };
449438
450 pub fn destroy(self: *Array, comp: *Compilation) void {439 pub const Bytes = struct {
451 switch (self.special) {440 base: Payload = Payload{ .tag = .bytes },
452 .Undefined => {},441 data: []const u8,
453 .OwnedBuffer => |buf| {442 };
454 comp.gpa().free(buf);
455 },
456 .Explicit => {},
457 }
458 comp.gpa().destroy(self);
459 }
460443
461 pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value {444 pub const Ty = struct {
462 switch (self.special) {445 base: Payload = Payload{ .tag = .ty },
463 .Undefined => {446 ty: Type,
464 const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);447 };
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 }
509 };448 };
449};
510450
511 pub const Int = struct {451/// This is the heart of resource management of the Zig compiler. The Zig compiler uses
512 base: Value,452/// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources
513 big_int: std.math.big.Int,453/// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents
514454/// a root.
515 pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {455pub const MemoryCell = struct {
516 const self = try comp.gpa().create(Value.Int);456 parent: Parent,
517 self.* = Value.Int{457 contents: Value,
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 }
581458
582 pub fn destroy(self: *Int, comp: *Compilation) void {459 pub const Parent = union(enum) {
583 self.big_int.deinit();460 none,
584 comp.gpa().destroy(self);461 struct_field: struct {
585 }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,
586 };474 };
587};475};
src/ir.cpp+3-3
...@@ -11283,9 +11283,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction...@@ -11283,9 +11283,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction
11283 Buf *val_buf = buf_alloc();11283 Buf *val_buf = buf_alloc();
11284 bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);11284 bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
11285 ir_add_error_node(ira, instruction->base.source_node,11285 ir_add_error_node(ira, instruction->base.source_node,
11286 buf_sprintf("integer value %s has no representation in type '%s'",11286 buf_sprintf("type %s cannot represent integer value %s",
11287 buf_ptr(val_buf),11287 buf_ptr(&other_type->name),
11288 buf_ptr(&other_type->name)));11288 buf_ptr(val_buf)));
11289 return false;11289 return false;
11290 }11290 }
11291 if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {11291 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 {}