| author | |
| committer | |
| log | e8545db9d4ced8978c5594c637d9bf76dc26209d |
| tree | c51f45b9374f9313636039e6e43310bf81cc359f |
| parent | b5e72c0148e40df418bdc8e1770b1bd42e76732e |
| parent | 1eda2ada9ac115c9dbff1bba60b3670f3fbfff57 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
beginnings of non-LLVM self-hosted backend14 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 | } |
| 1017 | 1017 | ||
| 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(); |
| 1029 | 1031 | ||
| 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; |
| 1032 | 1034 | ||
| 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); |
| 1035 | 1037 | ||
| 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 and | 143 | /// 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 | } |
| 239 | 242 | ||
| 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 | } |
| 481 | 484 | ||
| 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 { |
| 522 | 525 | ||
| 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-allocating | 527 | /// 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; |
| 536 | 537 | ||
| ... | @@ -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 | } |
| 552 | 553 | ||
| 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 | } |
| 557 | 559 |
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 | } |
| 107 | 107 | ||
| 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 the | 134 | /// items to `undefined` and the last item to `sentinel`. Depending on the |
| 110 | /// Allocator implementation, it may be required to call `free` once the | 135 | /// 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 | } |
| 121 | 146 | ||
| 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 | } |
| 763 | 763 | ||
| 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"); |
| 2 | pub const Token = tokenizer.Token; | 2 | pub const Token = tokenizer.Token; |
| 3 | pub const Tokenizer = tokenizer.Tokenizer; | 3 | pub const Tokenizer = tokenizer.Tokenizer; |
| 4 | pub const parse = @import("zig/parse.zig").parse; | 4 | pub const parse = @import("zig/parse.zig").parse; |
| 5 | pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStringLiteral; | 5 | pub const parseStringLiteral = @import("zig/string_literal.zig").parse; |
| 6 | pub const render = @import("zig/render.zig").render; | 6 | pub const render = @import("zig/render.zig").render; |
| 7 | pub const renderStringLiteral = @import("zig/string_literal.zig").render; | ||
| 7 | pub const ast = @import("zig/ast.zig"); | 8 | pub const ast = @import("zig/ast.zig"); |
| 8 | pub const system = @import("zig/system.zig"); | 9 | pub const system = @import("zig/system.zig"); |
| 9 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; | 10 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
lib/std/zig/parse_string_literal.zig deleted-125| ... | @@ -1,125 +0,0 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | |||
| 4 | const State = enum { | ||
| 5 | Start, | ||
| 6 | Backslash, | ||
| 7 | }; | ||
| 8 | |||
| 9 | pub 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 | ||
| 17 | pub 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 | |||
| 113 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | |||
| 4 | const State = enum { | ||
| 5 | Start, | ||
| 6 | Backslash, | ||
| 7 | }; | ||
| 8 | |||
| 9 | pub 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 | ||
| 17 | pub 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 | |||
| 113 | test "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. | ||
| 128 | pub 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 | |||
| 142 | test "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 @@ | ||
| 1 | const Target = @import("std").Target; | ||
| 2 | |||
| 3 | pub 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Compilation = @import("compilation.zig").Compilation; | 2 | const mem = std.mem; |
| 3 | const Scope = @import("scope.zig").Scope; | ||
| 4 | const ast = std.zig.ast; | ||
| 5 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 6 | const Value = @import("value.zig").Value; | 4 | const Value = @import("value.zig").Value; |
| 7 | const Type = Value.Type; | 5 | const Type = @import("type.zig").Type; |
| 8 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 9 | const Token = std.zig.Token; | 7 | const text = @import("ir/text.zig"); |
| 10 | const Span = @import("errmsg.zig").Span; | 8 | const BigInt = std.math.big.Int; |
| 11 | const llvm = @import("llvm.zig"); | 9 | const Target = std.Target; |
| 12 | const codegen = @import("codegen.zig"); | 10 | |
| 13 | const ObjectFile = codegen.ObjectFile; | 11 | /// These are in-memory, analyzed instructions. See `text.Inst` for the representation |
| 14 | const Decl = @import("decl.zig").Decl; | 12 | /// of instructions that correspond to the ZIR text format. |
| 15 | const mem = std.mem; | 13 | /// This struct owns the `Value` and `Type` memory. When the struct is deallocated, |
| 16 | 14 | /// so are the `Value` and `Type`. The value of a constant must be copied into | |
| 17 | pub const LVal = enum { | 15 | /// a memory location for the value to survive after a const instruction. |
| 18 | None, | ||
| 19 | Ptr, | ||
| 20 | }; | ||
| 21 | |||
| 22 | pub 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 | |||
| 50 | pub const Inst = struct { | 16 | pub 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, |
| 58 | 24 | constant, | |
| 59 | /// true if this instruction was generated by zig and not from user code | 25 | assembly, |
| 60 | is_generated: bool, | 26 | ptrtoint, |
| 61 | 27 | }; | |
| 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, | ||
| 70 | 28 | ||
| 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 | } | ||
| 217 | 32 | ||
| 218 | pub fn setGenerated(base: *Inst) void { | 33 | return @fieldParentPtr(T, "base", base); |
| 219 | base.is_generated = true; | ||
| 220 | } | 34 | } |
| 221 | 35 | ||
| 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 | } |
| 229 | 39 | ||
| 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, | ||
| 233 | 45 | ||
| 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 | } |
| 240 | 51 | ||
| 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 | }; |
| 332 | 57 | ||
| 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 | } | ||
| 356 | 61 | ||
| 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 | }; |
| 361 | 64 | ||
| 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 | } | ||
| 388 | 68 | ||
| 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, |
| 392 | 72 | 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 | }; |
| 401 | 78 | ||
| 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; | ||
| 413 | 81 | ||
| 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 { |
| 456 | 84 | 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 | }; | ||
| 495 | 88 | ||
| 496 | pub const VarPtr = struct { | 89 | pub const TypedValue = struct { |
| 497 | base: Inst, | 90 | ty: Type, |
| 498 | params: Params, | 91 | val: Value, |
| 499 | 92 | }; | |
| 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 | } | ||
| 513 | 93 | ||
| 514 | pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { | 94 | pub 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 | } | ||
| 536 | 99 | ||
| 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 pass | 102 | typed_value: TypedValue, |
| 540 | .Param => |param| return param.llvm_value, | 103 | src: usize, |
| 541 | } | ||
| 542 | } | ||
| 543 | }; | 104 | }; |
| 544 | 105 | ||
| 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, |
| 548 | 109 | 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 | }; |
| 622 | 111 | ||
| 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); |
| 626 | 115 | 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 | } | ||
| 642 | 119 | ||
| 643 | pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { | 120 | pub 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); | ||
| 666 | 124 | ||
| 667 | return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base); | 125 | pub 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 | }; |
| 670 | 138 | 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(); |
| 674 | 142 | errdefer ctx.arena.deinit(); | |
| 675 | const Params = struct { | 143 | |
| 676 | variable: *Variable, | 144 | ctx.analyzeRoot() catch |err| switch (err) { |
| 677 | }; | 145 | error.AnalysisFail => { |
| 678 | 146 | assert(ctx.errors.items.len != 0); | |
| 679 | const ir_val_init = IrVal.Init.Unknown; | 147 | }, |
| 680 | 148 | 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 | }; |
| 691 | 150 | 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(), |
| 695 | 154 | .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 | } | ||
| 719 | 157 | ||
| 720 | pub const Phi = struct { | 158 | const Analyze = struct { |
| 721 | base: Inst, | 159 | allocator: *Allocator, |
| 722 | params: Params, | 160 | arena: std.heap.ArenaAllocator, |
| 723 | 161 | 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), |
| 728 | 166 | target: Target, | |
| 729 | const ir_val_init = IrVal.Init.Unknown; | 167 | |
| 730 | 168 | const NewDecl = struct { | |
| 731 | pub fn dump(inst: *const Phi) void {} | 169 | /// null means a semantic analysis error happened |
| 732 | 170 | 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 | }; |
| 741 | 172 | ||
| 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 | }; |
| 763 | 177 | ||
| 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), |
| 767 | 181 | /// 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 | }; |
| 787 | 184 | ||
| 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; | ||
| 797 | 186 | ||
| 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| { |
| 801 | 190 | 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 | } | ||
| 826 | 194 | ||
| 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 | } |
| 830 | 201 | ||
| 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 | } |
| 874 | 216 | ||
| 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 | } |
| 878 | 220 | ||
| 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 | |||
| 919 | pub const Variable = struct { | ||
| 920 | child_scope: *Scope, | ||
| 921 | }; | ||
| 922 | |||
| 923 | pub 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 | } |
| 944 | 229 | ||
| 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 | }; | ||
| 952 | 233 | ||
| 953 | /// Stuff that survives longer than Builder | 234 | fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 { |
| 954 | pub 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 | } | ||
| 959 | 241 | ||
| 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 | } |
| 965 | 249 | ||
| 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 | } |
| 977 | 268 | ||
| 978 | /// returns a ref-incremented value, or adds a compile error | 269 | /// 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 | }; | ||
| 1007 | 282 | ||
| 1008 | pub 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 | }, |
| 1016 | 291 | .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 | } |
| 1040 | 296 | ||
| 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 | } |
| 1044 | 309 | ||
| 1045 | /// Call code.destroy() when done | 310 | 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 | } | ||
| 1049 | 313 | ||
| 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 | } | ||
| 1068 | 316 | ||
| 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 | } | ||
| 1073 | 319 | ||
| 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 | } |
| 1077 | 325 | ||
| 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 | } |
| 1085 | 332 | ||
| 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 | } |
| 1193 | 339 | ||
| 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); |
| 1196 | 342 | 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 | } | ||
| 1203 | 343 | ||
| 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 | } |
| 1221 | 349 | ||
| 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 | } | ||
| 1286 | 353 | ||
| 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), |
| 1290 | 357 | }); | |
| 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 | } |
| 1307 | 359 | ||
| 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()) { |
| 1310 | 362 | 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 | } |
| 1328 | 376 | ||
| 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); | ||
| 1344 | 379 | ||
| 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 | } |
| 1349 | 385 | ||
| 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 => { |
| 1353 | 389 | // 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); |
| 1369 | 397 | }, | |
| 1370 | if (str_token[0] == 'c') { | 398 | .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?), |
| 1371 | // first we add a null | 399 | .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).?), |
| 1374 | 402 | .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?), | |
| 1375 | // next make an array value | 403 | .@"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).?); |
| 1379 | 407 | return self.constVoid(old_inst.src); | |
| 1380 | // then make a pointer value pointing at the first element | 408 | }, |
| 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 | } |
| 1399 | 414 | ||
| 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); |
| 1402 | 417 | ||
| 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), |
| 1405 | 420 | .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 |
| 1411 | 426 | // 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 | }; | ||
| 1416 | 432 | ||
| 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 | } |
| 1427 | 441 | ||
| 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; |
| 1430 | 444 | 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); | ||
| 1449 | 445 | ||
| 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 | } | ||
| 1455 | 448 | ||
| 1456 | if (statement_value.cast(Inst.DeclVar)) |decl_var| { | 449 | return self.constInst(fn_inst.base.src, .{ |
| 1457 | // variable declarations start a new scope | 450 | .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 void | 453 | } |
| 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 | } | ||
| 1472 | 454 | ||
| 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 | } | ||
| 1478 | 457 | ||
| 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 | } |
| 1485 | 464 | ||
| 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); | ||
| 1492 | 467 | ||
| 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 | }); | ||
| 1497 | 471 | ||
| 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 | } | ||
| 1499 | 477 | ||
| 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 | } |
| 1505 | 483 | // 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 | } |
| 1509 | 488 | ||
| 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 | }); | ||
| 1575 | 492 | ||
| 1576 | const ret_stmt_block = try irb.createBasicBlock(scope, "RetStmt"); | 493 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { |
| 1577 | 494 | .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 | |
| 1589 | 506 | 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 | } |
| 1671 | 521 | ||
| 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 | }; | ||
| 1676 | 525 | ||
| 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, |
| 1679 | 528 | .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 | ), |
| 1692 | 536 | }; | |
| 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 | } | ||
| 1706 | 537 | ||
| 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 | } | ||
| 1757 | 546 | ||
| 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 | } | ||
| 1772 | 550 | ||
| 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 | } |
| 1776 | 553 | ||
| 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 | }; |
| 1806 | 560 | if (ptr.value()) |val| { | |
| 1807 | // Look at the params and ref() other instructions | 561 | 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 | } |
| 1835 | 566 | ||
| 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 | } |
| 1866 | 569 | ||
| 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 | } | ||
| 1872 | 574 | ||
| 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 | } | ||
| 1878 | 578 | ||
| 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| { |
| 1893 | 583 | 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 | |||
| 1946 | const 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 | }; | ||
| 1965 | 588 | ||
| 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, |
| 1969 | 592 | .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 startBasicBlock | 597 | }); |
| 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 | } |
| 1980 | 599 | ||
| 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 | } |
| 1984 | 604 | ||
| 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 | } |
| 1990 | 610 | ||
| 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(); |
| 1996 | 616 | 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 | } |
| 2016 | 623 | ||
| 2017 | ira.old_bb_index += 1; | 624 | // comptime_int to fixed-width integer |
| 2018 | 625 | 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 | } | ||
| 2047 | 633 | ||
| 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 | } |
| 2052 | 636 | ||
| 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 | } |
| 2059 | 644 | ||
| 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| { |
| 2062 | 647 | // 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 | } |
| 2344 | 650 | 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 | } |
| 2510 | 652 | ||
| 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 | } |
| 2514 | 662 | ||
| 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 | }; | ||
| 2525 | 667 | ||
| 2526 | pub 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 | } | ||
| 2544 | 671 | ||
| 2545 | return irb.finish(); | 672 | // TODO: implement more of this function |
| 2546 | } | ||
| 2547 | 673 | ||
| 2548 | pub 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 | }; | ||
| 2550 | 677 | ||
| 2551 | var ira = try Analyze.init(comp, old_code.tree_scope, expected_type); | 678 | pub 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; | ||
| 2553 | 682 | ||
| 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); | ||
| 2556 | 684 | ||
| 2557 | ira.irb.current_basic_block = new_entry_bb; | 685 | const src_path = args[1]; |
| 686 | const debug_error_trace = true; | ||
| 2558 | 687 | ||
| 2559 | ira.startBasicBlock(old_entry_bb, null); | 688 | const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0); |
| 2560 | 689 | ||
| 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); |
| 2563 | 692 | ||
| 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 | } | ||
| 2568 | 701 | ||
| 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 point | 703 | 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 | ||
| 2574 | 704 | ||
| 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 | } |
| 2579 | 710 | if (debug_error_trace) return error.ParseFailure; | |
| 2580 | ira.instruction_index += 1; | 711 | std.process.exit(1); |
| 2581 | } | 712 | } |
| 2582 | 713 | ||
| 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 | } | ||
| 2587 | 716 | ||
| 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 | |||
| 722 | fn 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 | |||
| 3 | const std = @import("std"); | ||
| 4 | const mem = std.mem; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const BigInt = std.math.big.Int; | ||
| 8 | const Type = @import("../type.zig").Type; | ||
| 9 | const Value = @import("../value.zig").Value; | ||
| 10 | const 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. | ||
| 14 | pub 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 | |||
| 263 | pub const ErrorMsg = struct { | ||
| 264 | byte_offset: usize, | ||
| 265 | msg: []const u8, | ||
| 266 | }; | ||
| 267 | |||
| 268 | pub 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 | |||
| 416 | pub 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 | |||
| 444 | const 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 | |||
| 780 | pub 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 | |||
| 801 | const 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = std.builtin; | ||
| 3 | const Scope = @import("scope.zig").Scope; | ||
| 4 | const Compilation = @import("compilation.zig").Compilation; | ||
| 5 | const Value = @import("value.zig").Value; | 2 | const Value = @import("value.zig").Value; |
| 6 | const llvm = @import("llvm.zig"); | ||
| 7 | const event = std.event; | ||
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | const 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. | ||
| 15 | pub 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 | } | ||
| 10 | 61 | ||
| 11 | pub 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; | ||
| 20 | 66 | ||
| 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 | } |
| 50 | 71 | ||
| 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 | } |
| 84 | 79 | ||
| 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, | ||
| 96 | 87 | ||
| 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, |
| 143 | 141 | => @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 | } |
| 158 | 144 | ||
| 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, |
| 163 | 149 | 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 | } |
| 167 | 207 | ||
| 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 | } |
| 197 | 245 | ||
| 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", |
| 201 | 249 | .@"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 | } |
| 206 | 284 | ||
| 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 | } |
| 212 | 322 | ||
| 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()) { |
| 216 | 326 | .@"u8", | |
| 217 | pub fn destroy(self: *Struct, comp: *Compilation) void { | 327 | .@"i8", |
| 218 | comp.gpa().destroy(self); | 328 | .@"isize", |
| 219 | } | 329 | .@"usize", |
| 220 | 330 | .@"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", |
| 225 | 335 | .@"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, | ||
| 231 | 355 | ||
| 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 | } | ||
| 236 | 362 | ||
| 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, | ||
| 240 | 393 | ||
| 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 | } | ||
| 245 | 400 | ||
| 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()) { |
| 249 | 404 | .u8, | |
| 250 | pub const Data = union(Kind) { | 405 | .i8, |
| 251 | Generic: Generic, | 406 | .isize, |
| 252 | Normal: Normal, | 407 | .usize, |
| 253 | }; | 408 | .c_short, |
| 254 | 409 | .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, |
| 261 | 416 | .c_longdouble, | |
| 262 | pub const Generic = struct { | 417 | .f16, |
| 263 | param_count: usize, | 418 | .f32, |
| 264 | cc: CallingConvention, | 419 | .f64, |
| 265 | }; | 420 | .f128, |
| 266 | 421 | .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 | } | ||
| 326 | 434 | ||
| 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 | } | ||
| 339 | 439 | ||
| 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, | ||
| 341 | 463 | ||
| 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 | } | ||
| 346 | 478 | ||
| 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, |
| 358 | 490 | .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, |
| 365 | 497 | .array, | |
| 366 | /// takes ownership of key.Normal.params on success | 498 | .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, |
| 371 | 503 | .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, |
| 377 | 509 | .c_uint, | |
| 378 | key.ref(); | 510 | .c_long, |
| 379 | errdefer key.deref(comp); | 511 | .c_ulong, |
| 380 | 512 | .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 | } | ||
| 558 | 517 | ||
| 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()) { |
| 563 | 522 | .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, |
| 568 | 527 | .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, |
| 574 | 533 | .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, |
| 582 | 541 | .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, |
| 586 | 545 | .isize, | |
| 587 | self.base.init(comp, .Int, name); | 546 | .c_short, |
| 588 | 547 | .c_ushort, | |
| 589 | { | 548 | .c_int, |
| 590 | const held = comp.int_type_table.acquire(); | 549 | .c_uint, |
| 591 | defer held.release(); | 550 | .c_long, |
| 592 | 551 | .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 | }; | ||
| 679 | 557 | ||
| 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 | } | ||
| 684 | 596 | ||
| 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 | } | ||
| 689 | 635 | ||
| 690 | pub const Size = builtin.TypeInfo.Pointer.Size; | 636 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 691 | 637 | /// 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, |
| 699 | 645 | 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, |
| 704 | 650 | 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, |
| 710 | 656 | 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, |
| 717 | 663 | 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/3190 | 672 | // 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 | }; |
| 799 | 681 | ||
| 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, | ||
| 804 | 684 | ||
| 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, | ||
| 808 | 687 | ||
| 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 | }; |
| 820 | 690 | ||
| 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, | ||
| 882 | 693 | ||
| 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 | }; | ||
| 1003 | 697 | ||
| 1004 | pub const Opaque = struct { | 698 | pub const SingleConstPointer = struct { |
| 1005 | base: Type, | 699 | base: Payload = Payload{ .tag = .single_const_pointer }, |
| 1006 | 700 | ||
| 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 | }; | ||
| 1015 | 705 | ||
| 1016 | pub const Frame = struct { | 706 | pub const CInteger = enum { |
| 1017 | base: Type, | 707 | short, |
| 1018 | 708 | 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 | }, | ||
| 1022 | 748 | ||
| 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 | }, | ||
| 1027 | 771 | ||
| 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 | }, | ||
| 1030 | 785 | ||
| 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 | }, | ||
| 1034 | 799 | ||
| 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 | |||
| 1041 | fn 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Scope = @import("scope.zig").Scope; | 2 | const Type = @import("type.zig").Type; |
| 3 | const Compilation = @import("compilation.zig").Compilation; | 3 | const log2 = std.math.log2; |
| 4 | const ObjectFile = @import("codegen.zig").ObjectFile; | ||
| 5 | const llvm = @import("llvm.zig"); | ||
| 6 | const ArrayListSentineled = std.ArrayListSentineled; | ||
| 7 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const BigInt = std.math.big.Int; | ||
| 6 | const Target = std.Target; | ||
| 7 | const 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. | ||
| 14 | pub 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 | }; | ||
| 8 | 70 | ||
| 9 | /// Values are ref-counted, heap-allocated, and copy-on-write | 71 | pub fn initTag(comptime small_tag: Tag) Value { |
| 10 | /// If there is only 1 ref then write need not copy | 72 | comptime assert(@enumToInt(small_tag) < Tag.no_payload_count); |
| 11 | pub 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), | ||
| 15 | 75 | ||
| 16 | /// Thread-safe | 76 | 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 | } |
| 20 | 80 | ||
| 21 | /// Thread-safe | 81 | 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 | } |
| 38 | 88 | ||
| 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 | } | ||
| 44 | 92 | ||
| 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 | } | ||
| 49 | 96 | ||
| 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 | } |
| 54 | 99 | ||
| 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 | } |
| 58 | 157 | ||
| 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 | } |
| 72 | 166 | ||
| 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 | |
| 78 | 172 | .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 | } |
| 82 | 216 | ||
| 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 | } |
| 96 | 266 | ||
| 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, |
| 103 | 273 | .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, |
| 108 | 278 | .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, |
| 114 | 284 | .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, |
| 126 | 296 | .fn_naked_noreturn_no_args_type, | |
| 127 | pub const Type = @import("type.zig").Type; | 297 | .single_const_pointer_to_comptime_int_type, |
| 128 | 298 | .const_slice_u8_type, | |
| 129 | pub const FnProto = struct { | 299 | .void_value, |
| 130 | base: Value, | 300 | .noreturn_value, |
| 131 | 301 | .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/265 | 303 | .function, |
| 134 | symbol_name: ArrayListSentineled(u8, 0), | 304 | .ref, |
| 135 | 305 | .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 => { |
| 149 | 324 | 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; |
| 154 | 329 | @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 | } | ||
| 302 | 344 | ||
| 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 | } |
| 307 | 393 | ||
| 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, | ||
| 312 | 397 | ||
| 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 | }; |
| 318 | 402 | ||
| 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 | }; |
| 326 | 407 | ||
| 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 | }; |
| 331 | 412 | ||
| 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 | }; |
| 336 | 418 | ||
| 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 | }; |
| 419 | 423 | ||
| 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 | }; |
| 424 | 428 | ||
| 425 | /// Takes ownership of buffer | 429 | 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); | ||
| 446 | 433 | ||
| 447 | return self; | 434 | pub const RefVal = struct { |
| 448 | } | 435 | base: Payload = Payload{ .tag = .ref_val }, |
| 436 | val: Value, | ||
| 437 | }; | ||
| 449 | 438 | ||
| 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 | } | ||
| 460 | 443 | ||
| 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 | }; | ||
| 510 | 450 | ||
| 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 |
| 514 | 454 | /// a root. | |
| 515 | pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int { | 455 | pub 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 | } | ||
| 581 | 458 | ||
| 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 @@ | ||
| 1 | test "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 | |||
| 54 | fn exeCmp(src: []const u8, expected_stdout: []const u8) void {} | ||