| author | |
| committer | |
| log | d337469e4484ffd160b4508e2366fefd435f6c8a |
| tree | 7ad577eb66febb985ed029ae75c47461611775e2 |
| parent | 7875649c2481f90b918581670c9268d6033f873f |
| parent | 20b4a2cf2cded8904a57714ed2b90c857f12c6b1 |
| signature |
self-hosted: hook up Zig AST to ZIR20 files changed, 2578 insertions(+), 1202 deletions(-)
build.zig+13| ... | @@ -72,9 +72,22 @@ pub fn build(b: *Builder) !void { | ... | @@ -72,9 +72,22 @@ pub fn build(b: *Builder) !void { |
| 72 | if (!only_install_lib_files) { | 72 | if (!only_install_lib_files) { |
| 73 | exe.install(); | 73 | exe.install(); |
| 74 | } | 74 | } |
| 75 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); | ||
| 75 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; | 76 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; |
| 76 | if (link_libc) exe.linkLibC(); | 77 | if (link_libc) exe.linkLibC(); |
| 77 | 78 | ||
| 79 | exe.addBuildOption(bool, "enable_tracy", tracy != null); | ||
| 80 | if (tracy) |tracy_path| { | ||
| 81 | const client_cpp = fs.path.join( | ||
| 82 | b.allocator, | ||
| 83 | &[_][]const u8{ tracy_path, "TracyClient.cpp" }, | ||
| 84 | ) catch unreachable; | ||
| 85 | exe.addIncludeDir(tracy_path); | ||
| 86 | exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); | ||
| 87 | exe.linkSystemLibraryName("c++"); | ||
| 88 | exe.linkLibC(); | ||
| 89 | } | ||
| 90 | |||
| 78 | b.installDirectory(InstallDirectoryOptions{ | 91 | b.installDirectory(InstallDirectoryOptions{ |
| 79 | .source_dir = "lib", | 92 | .source_dir = "lib", |
| 80 | .install_dir = .Lib, | 93 | .install_dir = .Lib, |
lib/std/build.zig+3-2| ... | @@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct { | ... | @@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct { |
| 1905 | builder.allocator, | 1905 | builder.allocator, |
| 1906 | &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) }, | 1906 | &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) }, |
| 1907 | ); | 1907 | ); |
| 1908 | try fs.cwd().writeFile(build_options_file, self.build_options_contents.span()); | 1908 | const path_from_root = builder.pathFromRoot(build_options_file); |
| 1909 | try fs.cwd().writeFile(path_from_root, self.build_options_contents.span()); | ||
| 1909 | try zig_args.append("--pkg-begin"); | 1910 | try zig_args.append("--pkg-begin"); |
| 1910 | try zig_args.append("build_options"); | 1911 | try zig_args.append("build_options"); |
| 1911 | try zig_args.append(builder.pathFromRoot(build_options_file)); | 1912 | try zig_args.append(path_from_root); |
| 1912 | try zig_args.append("--pkg-end"); | 1913 | try zig_args.append("--pkg-end"); |
| 1913 | } | 1914 | } |
| 1914 | 1915 |
lib/std/zig.zig+38| ... | @@ -1,4 +1,6 @@ | ... | @@ -1,4 +1,6 @@ |
| 1 | const std = @import("std.zig"); | ||
| 1 | const tokenizer = @import("zig/tokenizer.zig"); | 2 | const tokenizer = @import("zig/tokenizer.zig"); |
| 3 | |||
| 2 | pub const Token = tokenizer.Token; | 4 | pub const Token = tokenizer.Token; |
| 3 | pub const Tokenizer = tokenizer.Tokenizer; | 5 | pub const Tokenizer = tokenizer.Tokenizer; |
| 4 | pub const parse = @import("zig/parse.zig").parse; | 6 | pub const parse = @import("zig/parse.zig").parse; |
| ... | @@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig"); | ... | @@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig"); |
| 9 | pub const system = @import("zig/system.zig"); | 11 | pub const system = @import("zig/system.zig"); |
| 10 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; | 12 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
| 11 | 13 | ||
| 14 | pub const SrcHash = [16]u8; | ||
| 15 | |||
| 16 | /// If the source is small enough, it is used directly as the hash. | ||
| 17 | /// If it is long, blake3 hash is computed. | ||
| 18 | pub fn hashSrc(src: []const u8) SrcHash { | ||
| 19 | var out: SrcHash = undefined; | ||
| 20 | if (src.len <= SrcHash.len) { | ||
| 21 | std.mem.copy(u8, &out, src); | ||
| 22 | std.mem.set(u8, out[src.len..], 0); | ||
| 23 | } else { | ||
| 24 | std.crypto.Blake3.hash(src, &out); | ||
| 25 | } | ||
| 26 | return out; | ||
| 27 | } | ||
| 28 | |||
| 12 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | 29 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { |
| 13 | var line: usize = 0; | 30 | var line: usize = 0; |
| 14 | var column: usize = 0; | 31 | var column: usize = 0; |
| ... | @@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi | ... | @@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi |
| 26 | return .{ .line = line, .column = column }; | 43 | return .{ .line = line, .column = column }; |
| 27 | } | 44 | } |
| 28 | 45 | ||
| 46 | /// Returns the standard file system basename of a binary generated by the Zig compiler. | ||
| 47 | pub fn binNameAlloc( | ||
| 48 | allocator: *std.mem.Allocator, | ||
| 49 | root_name: []const u8, | ||
| 50 | target: std.Target, | ||
| 51 | output_mode: std.builtin.OutputMode, | ||
| 52 | link_mode: ?std.builtin.LinkMode, | ||
| 53 | ) error{OutOfMemory}![]u8 { | ||
| 54 | switch (output_mode) { | ||
| 55 | .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }), | ||
| 56 | .Lib => { | ||
| 57 | const suffix = switch (link_mode orelse .Static) { | ||
| 58 | .Static => target.staticLibSuffix(), | ||
| 59 | .Dynamic => target.dynamicLibSuffix(), | ||
| 60 | }; | ||
| 61 | return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); | ||
| 62 | }, | ||
| 63 | .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }), | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 29 | test "" { | 67 | test "" { |
| 30 | @import("std").meta.refAllDecls(@This()); | 68 | @import("std").meta.refAllDecls(@This()); |
| 31 | } | 69 | } |
lib/std/zig/ast.zig+2| ... | @@ -2260,6 +2260,8 @@ pub const Node = struct { | ... | @@ -2260,6 +2260,8 @@ pub const Node = struct { |
| 2260 | } | 2260 | } |
| 2261 | }; | 2261 | }; |
| 2262 | 2262 | ||
| 2263 | /// TODO break this into separate Break, Continue, Return AST Nodes to save memory. | ||
| 2264 | /// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more. | ||
| 2263 | pub const ControlFlowExpression = struct { | 2265 | pub const ControlFlowExpression = struct { |
| 2264 | base: Node = Node{ .id = .ControlFlowExpression }, | 2266 | base: Node = Node{ .id = .ControlFlowExpression }, |
| 2265 | ltoken: TokenIndex, | 2267 | ltoken: TokenIndex, |
lib/std/zig/parse.zig+1-1| ... | @@ -3222,7 +3222,7 @@ const Parser = struct { | ... | @@ -3222,7 +3222,7 @@ const Parser = struct { |
| 3222 | } | 3222 | } |
| 3223 | 3223 | ||
| 3224 | /// Op* Child | 3224 | /// Op* Child |
| 3225 | fn parsePrefixOpExpr(p: *Parser, opParseFn: NodeParseFn, childParseFn: NodeParseFn) Error!?*Node { | 3225 | fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node { |
| 3226 | if (try opParseFn(p)) |first_op| { | 3226 | if (try opParseFn(p)) |first_op| { |
| 3227 | var rightmost_op = first_op; | 3227 | var rightmost_op = first_op; |
| 3228 | while (true) { | 3228 | while (true) { |
src-self-hosted/Module.zig+1457-460| ... | @@ -15,13 +15,16 @@ const ir = @import("ir.zig"); | ... | @@ -15,13 +15,16 @@ const ir = @import("ir.zig"); |
| 15 | const zir = @import("zir.zig"); | 15 | const zir = @import("zir.zig"); |
| 16 | const Module = @This(); | 16 | const Module = @This(); |
| 17 | const Inst = ir.Inst; | 17 | const Inst = ir.Inst; |
| 18 | const ast = std.zig.ast; | ||
| 19 | const trace = @import("tracy.zig").trace; | ||
| 18 | 20 | ||
| 19 | /// General-purpose allocator. | 21 | /// General-purpose allocator. |
| 20 | allocator: *Allocator, | 22 | allocator: *Allocator, |
| 21 | /// Pointer to externally managed resource. | 23 | /// Pointer to externally managed resource. |
| 22 | root_pkg: *Package, | 24 | root_pkg: *Package, |
| 23 | /// Module owns this resource. | 25 | /// Module owns this resource. |
| 24 | root_scope: *Scope.ZIRModule, | 26 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. |
| 27 | root_scope: *Scope, | ||
| 25 | bin_file: link.ElfFile, | 28 | bin_file: link.ElfFile, |
| 26 | bin_file_dir: std.fs.Dir, | 29 | bin_file_dir: std.fs.Dir, |
| 27 | bin_file_path: []const u8, | 30 | bin_file_path: []const u8, |
| ... | @@ -35,10 +38,10 @@ decl_exports: std.AutoHashMap(*Decl, []*Export), | ... | @@ -35,10 +38,10 @@ decl_exports: std.AutoHashMap(*Decl, []*Export), |
| 35 | /// This table owns the Export memory. | 38 | /// This table owns the Export memory. |
| 36 | export_owners: std.AutoHashMap(*Decl, []*Export), | 39 | export_owners: std.AutoHashMap(*Decl, []*Export), |
| 37 | /// Maps fully qualified namespaced names to the Decl struct for them. | 40 | /// Maps fully qualified namespaced names to the Decl struct for them. |
| 38 | decl_table: std.AutoHashMap(Decl.Hash, *Decl), | 41 | decl_table: DeclTable, |
| 39 | 42 | ||
| 40 | optimize_mode: std.builtin.Mode, | 43 | optimize_mode: std.builtin.Mode, |
| 41 | link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{}, | 44 | link_error_flags: link.ElfFile.ErrorFlags = .{}, |
| 42 | 45 | ||
| 43 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | 46 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 44 | 47 | ||
| ... | @@ -49,8 +52,8 @@ work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | ... | @@ -49,8 +52,8 @@ work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 49 | /// a Decl can have a failed_decls entry but have analysis status of success. | 52 | /// a Decl can have a failed_decls entry but have analysis status of success. |
| 50 | failed_decls: std.AutoHashMap(*Decl, *ErrorMsg), | 53 | failed_decls: std.AutoHashMap(*Decl, *ErrorMsg), |
| 51 | /// Using a map here for consistency with the other fields here. | 54 | /// Using a map here for consistency with the other fields here. |
| 52 | /// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator. | 55 | /// The ErrorMsg memory is owned by the `Scope`, using Module's allocator. |
| 53 | failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg), | 56 | failed_files: std.AutoHashMap(*Scope, *ErrorMsg), |
| 54 | /// Using a map here for consistency with the other fields here. | 57 | /// Using a map here for consistency with the other fields here. |
| 55 | /// The ErrorMsg memory is owned by the `Export`, using Module's allocator. | 58 | /// The ErrorMsg memory is owned by the `Export`, using Module's allocator. |
| 56 | failed_exports: std.AutoHashMap(*Export, *ErrorMsg), | 59 | failed_exports: std.AutoHashMap(*Export, *ErrorMsg), |
| ... | @@ -60,15 +63,23 @@ failed_exports: std.AutoHashMap(*Export, *ErrorMsg), | ... | @@ -60,15 +63,23 @@ failed_exports: std.AutoHashMap(*Export, *ErrorMsg), |
| 60 | /// previous analysis. | 63 | /// previous analysis. |
| 61 | generation: u32 = 0, | 64 | generation: u32 = 0, |
| 62 | 65 | ||
| 66 | next_anon_name_index: usize = 0, | ||
| 67 | |||
| 63 | /// Candidates for deletion. After a semantic analysis update completes, this list | 68 | /// Candidates for deletion. After a semantic analysis update completes, this list |
| 64 | /// contains Decls that need to be deleted if they end up having no references to them. | 69 | /// contains Decls that need to be deleted if they end up having no references to them. |
| 65 | deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){}, | 70 | deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 71 | |||
| 72 | keep_source_files_loaded: bool, | ||
| 73 | |||
| 74 | const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql); | ||
| 66 | 75 | ||
| 67 | pub const WorkItem = union(enum) { | 76 | const WorkItem = union(enum) { |
| 68 | /// Write the machine code for a Decl to the output file. | 77 | /// Write the machine code for a Decl to the output file. |
| 69 | codegen_decl: *Decl, | 78 | codegen_decl: *Decl, |
| 70 | /// Decl has been determined to be outdated; perform semantic analysis again. | 79 | /// The Decl needs to be analyzed and possibly export itself. |
| 71 | re_analyze_decl: *Decl, | 80 | /// It may have already be analyzed, or it may have been determined |
| 81 | /// to be outdated; in this case perform semantic analysis again. | ||
| 82 | analyze_decl: *Decl, | ||
| 72 | }; | 83 | }; |
| 73 | 84 | ||
| 74 | pub const Export = struct { | 85 | pub const Export = struct { |
| ... | @@ -99,13 +110,12 @@ pub const Decl = struct { | ... | @@ -99,13 +110,12 @@ pub const Decl = struct { |
| 99 | /// mapping them to an address in the output file. | 110 | /// mapping them to an address in the output file. |
| 100 | /// Memory owned by this decl, using Module's allocator. | 111 | /// Memory owned by this decl, using Module's allocator. |
| 101 | name: [*:0]const u8, | 112 | name: [*:0]const u8, |
| 102 | /// The direct parent container of the Decl. This field will need to get more fleshed out when | 113 | /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`. |
| 103 | /// self-hosted supports proper struct types and Zig AST => ZIR. | ||
| 104 | /// Reference to externally owned memory. | 114 | /// Reference to externally owned memory. |
| 105 | scope: *Scope.ZIRModule, | 115 | scope: *Scope, |
| 106 | /// Byte offset into the source file that contains this declaration. | 116 | /// The AST Node decl index or ZIR Inst index that contains this declaration. |
| 107 | /// This is the base offset that src offsets within this Decl are relative to. | 117 | /// Must be recomputed when the corresponding source file is modified. |
| 108 | src: usize, | 118 | src_index: usize, |
| 109 | /// The most recent value of the Decl after a successful semantic analysis. | 119 | /// The most recent value of the Decl after a successful semantic analysis. |
| 110 | typed_value: union(enum) { | 120 | typed_value: union(enum) { |
| 111 | never_succeeded: void, | 121 | never_succeeded: void, |
| ... | @@ -116,6 +126,9 @@ pub const Decl = struct { | ... | @@ -116,6 +126,9 @@ pub const Decl = struct { |
| 116 | /// analysis of the function body is performed with this value set to `success`. Functions | 126 | /// analysis of the function body is performed with this value set to `success`. Functions |
| 117 | /// have their own analysis status field. | 127 | /// have their own analysis status field. |
| 118 | analysis: enum { | 128 | analysis: enum { |
| 129 | /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore | ||
| 130 | /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. | ||
| 131 | unreferenced, | ||
| 119 | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. | 132 | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. |
| 120 | in_progress, | 133 | in_progress, |
| 121 | /// This Decl might be OK but it depends on another one which did not successfully complete | 134 | /// This Decl might be OK but it depends on another one which did not successfully complete |
| ... | @@ -125,6 +138,10 @@ pub const Decl = struct { | ... | @@ -125,6 +138,10 @@ pub const Decl = struct { |
| 125 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | 138 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 126 | sema_failure, | 139 | sema_failure, |
| 127 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | 140 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 141 | /// This indicates the failure was something like running out of disk space, | ||
| 142 | /// and attempting semantic analysis again may succeed. | ||
| 143 | sema_failure_retryable, | ||
| 144 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 128 | codegen_failure, | 145 | codegen_failure, |
| 129 | /// There will be a corresponding ErrorMsg in Module.failed_decls. | 146 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 130 | /// This indicates the failure was something like running out of disk space, | 147 | /// This indicates the failure was something like running out of disk space, |
| ... | @@ -150,7 +167,7 @@ pub const Decl = struct { | ... | @@ -150,7 +167,7 @@ pub const Decl = struct { |
| 150 | /// This is populated regardless of semantic analysis and code generation. | 167 | /// This is populated regardless of semantic analysis and code generation. |
| 151 | link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty, | 168 | link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty, |
| 152 | 169 | ||
| 153 | contents_hash: Hash, | 170 | contents_hash: std.zig.SrcHash, |
| 154 | 171 | ||
| 155 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's | 172 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's |
| 156 | /// typed_value is modified. | 173 | /// typed_value is modified. |
| ... | @@ -169,28 +186,28 @@ pub const Decl = struct { | ... | @@ -169,28 +186,28 @@ pub const Decl = struct { |
| 169 | allocator.destroy(self); | 186 | allocator.destroy(self); |
| 170 | } | 187 | } |
| 171 | 188 | ||
| 172 | pub const Hash = [16]u8; | 189 | pub fn src(self: Decl) usize { |
| 173 | 190 | switch (self.scope.tag) { | |
| 174 | /// If the name is small enough, it is used directly as the hash. | 191 | .file => { |
| 175 | /// If it is long, blake3 hash is computed. | 192 | const file = @fieldParentPtr(Scope.File, "base", self.scope); |
| 176 | pub fn hashSimpleName(name: []const u8) Hash { | 193 | const tree = file.contents.tree; |
| 177 | var out: Hash = undefined; | 194 | const decl_node = tree.root_node.decls()[self.src_index]; |
| 178 | if (name.len <= Hash.len) { | 195 | return tree.token_locs[decl_node.firstToken()].start; |
| 179 | mem.copy(u8, &out, name); | 196 | }, |
| 180 | mem.set(u8, out[name.len..], 0); | 197 | .zir_module => { |
| 181 | } else { | 198 | const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope); |
| 182 | std.crypto.Blake3.hash(name, &out); | 199 | const module = zir_module.contents.module; |
| 200 | const src_decl = module.decls[self.src_index]; | ||
| 201 | return src_decl.inst.src; | ||
| 202 | }, | ||
| 203 | .block => unreachable, | ||
| 204 | .gen_zir => unreachable, | ||
| 205 | .decl => unreachable, | ||
| 183 | } | 206 | } |
| 184 | return out; | ||
| 185 | } | 207 | } |
| 186 | 208 | ||
| 187 | /// Must generate unique bytes with no collisions with other decls. | 209 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { |
| 188 | /// The point of hashing here is only to limit the number of bytes of | 210 | return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name)); |
| 189 | /// the unique identifier to a fixed size (16 bytes). | ||
| 190 | pub fn fullyQualifiedNameHash(self: Decl) Hash { | ||
| 191 | // Right now we only have ZIRModule as the source. So this is simply the | ||
| 192 | // relative name of the decl. | ||
| 193 | return hashSimpleName(mem.spanZ(self.name)); | ||
| 194 | } | 211 | } |
| 195 | 212 | ||
| 196 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { | 213 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { |
| ... | @@ -248,11 +265,9 @@ pub const Decl = struct { | ... | @@ -248,11 +265,9 @@ pub const Decl = struct { |
| 248 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. | 265 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| 249 | pub const Fn = struct { | 266 | pub const Fn = struct { |
| 250 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. | 267 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. |
| 251 | fn_type: Type, | ||
| 252 | analysis: union(enum) { | 268 | analysis: union(enum) { |
| 253 | /// The value is the source instruction. | 269 | queued: *ZIR, |
| 254 | queued: *zir.Inst.Fn, | 270 | in_progress, |
| 255 | in_progress: *Analysis, | ||
| 256 | /// There will be a corresponding ErrorMsg in Module.failed_decls | 271 | /// There will be a corresponding ErrorMsg in Module.failed_decls |
| 257 | sema_failure, | 272 | sema_failure, |
| 258 | /// This Fn might be OK but it depends on another Decl which did not successfully complete | 273 | /// This Fn might be OK but it depends on another Decl which did not successfully complete |
| ... | @@ -266,16 +281,20 @@ pub const Fn = struct { | ... | @@ -266,16 +281,20 @@ pub const Fn = struct { |
| 266 | /// of Fn analysis. | 281 | /// of Fn analysis. |
| 267 | pub const Analysis = struct { | 282 | pub const Analysis = struct { |
| 268 | inner_block: Scope.Block, | 283 | inner_block: Scope.Block, |
| 269 | /// TODO Performance optimization idea: instead of this inst_table, | 284 | }; |
| 270 | /// use a field in the zir.Inst instead to track corresponding instructions | 285 | |
| 271 | inst_table: std.AutoHashMap(*zir.Inst, *Inst), | 286 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. |
| 272 | needed_inst_capacity: usize, | 287 | pub const ZIR = struct { |
| 288 | body: zir.Module.Body, | ||
| 289 | arena: std.heap.ArenaAllocator.State, | ||
| 273 | }; | 290 | }; |
| 274 | }; | 291 | }; |
| 275 | 292 | ||
| 276 | pub const Scope = struct { | 293 | pub const Scope = struct { |
| 277 | tag: Tag, | 294 | tag: Tag, |
| 278 | 295 | ||
| 296 | pub const NameHash = [16]u8; | ||
| 297 | |||
| 279 | pub fn cast(base: *Scope, comptime T: type) ?*T { | 298 | pub fn cast(base: *Scope, comptime T: type) ?*T { |
| 280 | if (base.tag != T.base_tag) | 299 | if (base.tag != T.base_tag) |
| 281 | return null; | 300 | return null; |
| ... | @@ -289,7 +308,9 @@ pub const Scope = struct { | ... | @@ -289,7 +308,9 @@ pub const Scope = struct { |
| 289 | switch (self.tag) { | 308 | switch (self.tag) { |
| 290 | .block => return self.cast(Block).?.arena, | 309 | .block => return self.cast(Block).?.arena, |
| 291 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, | 310 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, |
| 311 | .gen_zir => return &self.cast(GenZIR).?.arena.allocator, | ||
| 292 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, | 312 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, |
| 313 | .file => unreachable, | ||
| 293 | } | 314 | } |
| 294 | } | 315 | } |
| 295 | 316 | ||
| ... | @@ -298,18 +319,45 @@ pub const Scope = struct { | ... | @@ -298,18 +319,45 @@ pub const Scope = struct { |
| 298 | pub fn decl(self: *Scope) ?*Decl { | 319 | pub fn decl(self: *Scope) ?*Decl { |
| 299 | return switch (self.tag) { | 320 | return switch (self.tag) { |
| 300 | .block => self.cast(Block).?.decl, | 321 | .block => self.cast(Block).?.decl, |
| 322 | .gen_zir => self.cast(GenZIR).?.decl, | ||
| 301 | .decl => self.cast(DeclAnalysis).?.decl, | 323 | .decl => self.cast(DeclAnalysis).?.decl, |
| 302 | .zir_module => null, | 324 | .zir_module => null, |
| 325 | .file => null, | ||
| 303 | }; | 326 | }; |
| 304 | } | 327 | } |
| 305 | 328 | ||
| 306 | /// Asserts the scope has a parent which is a ZIRModule and | 329 | /// Asserts the scope has a parent which is a ZIRModule or File and |
| 307 | /// returns it. | 330 | /// returns it. |
| 308 | pub fn namespace(self: *Scope) *ZIRModule { | 331 | pub fn namespace(self: *Scope) *Scope { |
| 309 | switch (self.tag) { | 332 | switch (self.tag) { |
| 310 | .block => return self.cast(Block).?.decl.scope, | 333 | .block => return self.cast(Block).?.decl.scope, |
| 334 | .gen_zir => return self.cast(GenZIR).?.decl.scope, | ||
| 311 | .decl => return self.cast(DeclAnalysis).?.decl.scope, | 335 | .decl => return self.cast(DeclAnalysis).?.decl.scope, |
| 312 | .zir_module => return self.cast(ZIRModule).?, | 336 | .zir_module, .file => return self, |
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Must generate unique bytes with no collisions with other decls. | ||
| 341 | /// The point of hashing here is only to limit the number of bytes of | ||
| 342 | /// the unique identifier to a fixed size (16 bytes). | ||
| 343 | pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash { | ||
| 344 | switch (self.tag) { | ||
| 345 | .block => unreachable, | ||
| 346 | .gen_zir => unreachable, | ||
| 347 | .decl => unreachable, | ||
| 348 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), | ||
| 349 | .file => return self.cast(File).?.fullyQualifiedNameHash(name), | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Asserts the scope is a child of a File and has an AST tree and returns the tree. | ||
| 354 | pub fn tree(self: *Scope) *ast.Tree { | ||
| 355 | switch (self.tag) { | ||
| 356 | .file => return self.cast(File).?.contents.tree, | ||
| 357 | .zir_module => unreachable, | ||
| 358 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree, | ||
| 359 | .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree, | ||
| 360 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree, | ||
| 313 | } | 361 | } |
| 314 | } | 362 | } |
| 315 | 363 | ||
| ... | @@ -325,10 +373,173 @@ pub const Scope = struct { | ... | @@ -325,10 +373,173 @@ pub const Scope = struct { |
| 325 | }); | 373 | }); |
| 326 | } | 374 | } |
| 327 | 375 | ||
| 376 | /// Asserts the scope has a parent which is a ZIRModule or File and | ||
| 377 | /// returns the sub_file_path field. | ||
| 378 | pub fn subFilePath(base: *Scope) []const u8 { | ||
| 379 | switch (base.tag) { | ||
| 380 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, | ||
| 381 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, | ||
| 382 | .block => unreachable, | ||
| 383 | .gen_zir => unreachable, | ||
| 384 | .decl => unreachable, | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | pub fn unload(base: *Scope, allocator: *Allocator) void { | ||
| 389 | switch (base.tag) { | ||
| 390 | .file => return @fieldParentPtr(File, "base", base).unload(allocator), | ||
| 391 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(allocator), | ||
| 392 | .block => unreachable, | ||
| 393 | .gen_zir => unreachable, | ||
| 394 | .decl => unreachable, | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 { | ||
| 399 | switch (base.tag) { | ||
| 400 | .file => return @fieldParentPtr(File, "base", base).getSource(module), | ||
| 401 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), | ||
| 402 | .gen_zir => unreachable, | ||
| 403 | .block => unreachable, | ||
| 404 | .decl => unreachable, | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Asserts the scope is a namespace Scope and removes the Decl from the namespace. | ||
| 409 | pub fn removeDecl(base: *Scope, child: *Decl) void { | ||
| 410 | switch (base.tag) { | ||
| 411 | .file => return @fieldParentPtr(File, "base", base).removeDecl(child), | ||
| 412 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), | ||
| 413 | .block => unreachable, | ||
| 414 | .gen_zir => unreachable, | ||
| 415 | .decl => unreachable, | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. | ||
| 420 | pub fn destroy(base: *Scope, allocator: *Allocator) void { | ||
| 421 | switch (base.tag) { | ||
| 422 | .file => { | ||
| 423 | const scope_file = @fieldParentPtr(File, "base", base); | ||
| 424 | scope_file.deinit(allocator); | ||
| 425 | allocator.destroy(scope_file); | ||
| 426 | }, | ||
| 427 | .zir_module => { | ||
| 428 | const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base); | ||
| 429 | scope_zir_module.deinit(allocator); | ||
| 430 | allocator.destroy(scope_zir_module); | ||
| 431 | }, | ||
| 432 | .block => unreachable, | ||
| 433 | .gen_zir => unreachable, | ||
| 434 | .decl => unreachable, | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | fn name_hash_hash(x: NameHash) u32 { | ||
| 439 | return @truncate(u32, @bitCast(u128, x)); | ||
| 440 | } | ||
| 441 | |||
| 442 | fn name_hash_eql(a: NameHash, b: NameHash) bool { | ||
| 443 | return @bitCast(u128, a) == @bitCast(u128, b); | ||
| 444 | } | ||
| 445 | |||
| 328 | pub const Tag = enum { | 446 | pub const Tag = enum { |
| 447 | /// .zir source code. | ||
| 329 | zir_module, | 448 | zir_module, |
| 449 | /// .zig source code. | ||
| 450 | file, | ||
| 330 | block, | 451 | block, |
| 331 | decl, | 452 | decl, |
| 453 | gen_zir, | ||
| 454 | }; | ||
| 455 | |||
| 456 | pub const File = struct { | ||
| 457 | pub const base_tag: Tag = .file; | ||
| 458 | base: Scope = Scope{ .tag = base_tag }, | ||
| 459 | |||
| 460 | /// Relative to the owning package's root_src_dir. | ||
| 461 | /// Reference to external memory, not owned by File. | ||
| 462 | sub_file_path: []const u8, | ||
| 463 | source: union(enum) { | ||
| 464 | unloaded: void, | ||
| 465 | bytes: [:0]const u8, | ||
| 466 | }, | ||
| 467 | contents: union { | ||
| 468 | not_available: void, | ||
| 469 | tree: *ast.Tree, | ||
| 470 | }, | ||
| 471 | status: enum { | ||
| 472 | never_loaded, | ||
| 473 | unloaded_success, | ||
| 474 | unloaded_parse_failure, | ||
| 475 | loaded_success, | ||
| 476 | }, | ||
| 477 | |||
| 478 | /// Direct children of the file. | ||
| 479 | decls: ArrayListUnmanaged(*Decl), | ||
| 480 | |||
| 481 | pub fn unload(self: *File, allocator: *Allocator) void { | ||
| 482 | switch (self.status) { | ||
| 483 | .never_loaded, | ||
| 484 | .unloaded_parse_failure, | ||
| 485 | .unloaded_success, | ||
| 486 | => {}, | ||
| 487 | |||
| 488 | .loaded_success => { | ||
| 489 | self.contents.tree.deinit(); | ||
| 490 | self.status = .unloaded_success; | ||
| 491 | }, | ||
| 492 | } | ||
| 493 | switch (self.source) { | ||
| 494 | .bytes => |bytes| { | ||
| 495 | allocator.free(bytes); | ||
| 496 | self.source = .{ .unloaded = {} }; | ||
| 497 | }, | ||
| 498 | .unloaded => {}, | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | pub fn deinit(self: *File, allocator: *Allocator) void { | ||
| 503 | self.decls.deinit(allocator); | ||
| 504 | self.unload(allocator); | ||
| 505 | self.* = undefined; | ||
| 506 | } | ||
| 507 | |||
| 508 | pub fn removeDecl(self: *File, child: *Decl) void { | ||
| 509 | for (self.decls.items) |item, i| { | ||
| 510 | if (item == child) { | ||
| 511 | _ = self.decls.swapRemove(i); | ||
| 512 | return; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | |||
| 517 | pub fn dumpSrc(self: *File, src: usize) void { | ||
| 518 | const loc = std.zig.findLineColumn(self.source.bytes, src); | ||
| 519 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | ||
| 520 | } | ||
| 521 | |||
| 522 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { | ||
| 523 | switch (self.source) { | ||
| 524 | .unloaded => { | ||
| 525 | const source = try module.root_pkg.root_src_dir.readFileAllocOptions( | ||
| 526 | module.allocator, | ||
| 527 | self.sub_file_path, | ||
| 528 | std.math.maxInt(u32), | ||
| 529 | 1, | ||
| 530 | 0, | ||
| 531 | ); | ||
| 532 | self.source = .{ .bytes = source }; | ||
| 533 | return source; | ||
| 534 | }, | ||
| 535 | .bytes => |bytes| return bytes, | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash { | ||
| 540 | // We don't have struct scopes yet so this is currently just a simple name hash. | ||
| 541 | return std.zig.hashSrc(name); | ||
| 542 | } | ||
| 332 | }; | 543 | }; |
| 333 | 544 | ||
| 334 | pub const ZIRModule = struct { | 545 | pub const ZIRModule = struct { |
| ... | @@ -355,6 +566,11 @@ pub const Scope = struct { | ... | @@ -355,6 +566,11 @@ pub const Scope = struct { |
| 355 | loaded_success, | 566 | loaded_success, |
| 356 | }, | 567 | }, |
| 357 | 568 | ||
| 569 | /// Even though .zir files only have 1 module, this set is still needed | ||
| 570 | /// because of anonymous Decls, which can exist in the global set, but | ||
| 571 | /// not this one. | ||
| 572 | decls: ArrayListUnmanaged(*Decl), | ||
| 573 | |||
| 358 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { | 574 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { |
| 359 | switch (self.status) { | 575 | switch (self.status) { |
| 360 | .never_loaded, | 576 | .never_loaded, |
| ... | @@ -366,11 +582,13 @@ pub const Scope = struct { | ... | @@ -366,11 +582,13 @@ pub const Scope = struct { |
| 366 | .loaded_success => { | 582 | .loaded_success => { |
| 367 | self.contents.module.deinit(allocator); | 583 | self.contents.module.deinit(allocator); |
| 368 | allocator.destroy(self.contents.module); | 584 | allocator.destroy(self.contents.module); |
| 585 | self.contents = .{ .not_available = {} }; | ||
| 369 | self.status = .unloaded_success; | 586 | self.status = .unloaded_success; |
| 370 | }, | 587 | }, |
| 371 | .loaded_sema_failure => { | 588 | .loaded_sema_failure => { |
| 372 | self.contents.module.deinit(allocator); | 589 | self.contents.module.deinit(allocator); |
| 373 | allocator.destroy(self.contents.module); | 590 | allocator.destroy(self.contents.module); |
| 591 | self.contents = .{ .not_available = {} }; | ||
| 374 | self.status = .unloaded_sema_failure; | 592 | self.status = .unloaded_sema_failure; |
| 375 | }, | 593 | }, |
| 376 | } | 594 | } |
| ... | @@ -384,14 +602,46 @@ pub const Scope = struct { | ... | @@ -384,14 +602,46 @@ pub const Scope = struct { |
| 384 | } | 602 | } |
| 385 | 603 | ||
| 386 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { | 604 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { |
| 605 | self.decls.deinit(allocator); | ||
| 387 | self.unload(allocator); | 606 | self.unload(allocator); |
| 388 | self.* = undefined; | 607 | self.* = undefined; |
| 389 | } | 608 | } |
| 390 | 609 | ||
| 610 | pub fn removeDecl(self: *ZIRModule, child: *Decl) void { | ||
| 611 | for (self.decls.items) |item, i| { | ||
| 612 | if (item == child) { | ||
| 613 | _ = self.decls.swapRemove(i); | ||
| 614 | return; | ||
| 615 | } | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 391 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { | 619 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { |
| 392 | const loc = std.zig.findLineColumn(self.source.bytes, src); | 620 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 393 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | 621 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); |
| 394 | } | 622 | } |
| 623 | |||
| 624 | pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { | ||
| 625 | switch (self.source) { | ||
| 626 | .unloaded => { | ||
| 627 | const source = try module.root_pkg.root_src_dir.readFileAllocOptions( | ||
| 628 | module.allocator, | ||
| 629 | self.sub_file_path, | ||
| 630 | std.math.maxInt(u32), | ||
| 631 | 1, | ||
| 632 | 0, | ||
| 633 | ); | ||
| 634 | self.source = .{ .bytes = source }; | ||
| 635 | return source; | ||
| 636 | }, | ||
| 637 | .bytes => |bytes| return bytes, | ||
| 638 | } | ||
| 639 | } | ||
| 640 | |||
| 641 | pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash { | ||
| 642 | // ZIR modules only have 1 file with all decls global in the same namespace. | ||
| 643 | return std.zig.hashSrc(name); | ||
| 644 | } | ||
| 395 | }; | 645 | }; |
| 396 | 646 | ||
| 397 | /// This is a temporary structure, references to it are valid only | 647 | /// This is a temporary structure, references to it are valid only |
| ... | @@ -399,7 +649,7 @@ pub const Scope = struct { | ... | @@ -399,7 +649,7 @@ pub const Scope = struct { |
| 399 | pub const Block = struct { | 649 | pub const Block = struct { |
| 400 | pub const base_tag: Tag = .block; | 650 | pub const base_tag: Tag = .block; |
| 401 | base: Scope = Scope{ .tag = base_tag }, | 651 | base: Scope = Scope{ .tag = base_tag }, |
| 402 | func: *Fn, | 652 | func: ?*Fn, |
| 403 | decl: *Decl, | 653 | decl: *Decl, |
| 404 | instructions: ArrayListUnmanaged(*Inst), | 654 | instructions: ArrayListUnmanaged(*Inst), |
| 405 | /// Points to the arena allocator of DeclAnalysis | 655 | /// Points to the arena allocator of DeclAnalysis |
| ... | @@ -414,6 +664,16 @@ pub const Scope = struct { | ... | @@ -414,6 +664,16 @@ pub const Scope = struct { |
| 414 | decl: *Decl, | 664 | decl: *Decl, |
| 415 | arena: std.heap.ArenaAllocator, | 665 | arena: std.heap.ArenaAllocator, |
| 416 | }; | 666 | }; |
| 667 | |||
| 668 | /// This is a temporary structure, references to it are valid only | ||
| 669 | /// during semantic analysis of the decl. | ||
| 670 | pub const GenZIR = struct { | ||
| 671 | pub const base_tag: Tag = .gen_zir; | ||
| 672 | base: Scope = Scope{ .tag = base_tag }, | ||
| 673 | decl: *Decl, | ||
| 674 | arena: std.heap.ArenaAllocator, | ||
| 675 | instructions: std.ArrayList(*zir.Inst), | ||
| 676 | }; | ||
| 417 | }; | 677 | }; |
| 418 | 678 | ||
| 419 | pub const Body = struct { | 679 | pub const Body = struct { |
| ... | @@ -463,19 +723,10 @@ pub const InitOptions = struct { | ... | @@ -463,19 +723,10 @@ pub const InitOptions = struct { |
| 463 | link_mode: ?std.builtin.LinkMode = null, | 723 | link_mode: ?std.builtin.LinkMode = null, |
| 464 | object_format: ?std.builtin.ObjectFormat = null, | 724 | object_format: ?std.builtin.ObjectFormat = null, |
| 465 | optimize_mode: std.builtin.Mode = .Debug, | 725 | optimize_mode: std.builtin.Mode = .Debug, |
| 726 | keep_source_files_loaded: bool = false, | ||
| 466 | }; | 727 | }; |
| 467 | 728 | ||
| 468 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { | 729 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 469 | const root_scope = try gpa.create(Scope.ZIRModule); | ||
| 470 | errdefer gpa.destroy(root_scope); | ||
| 471 | |||
| 472 | root_scope.* = .{ | ||
| 473 | .sub_file_path = options.root_pkg.root_src_path, | ||
| 474 | .source = .{ .unloaded = {} }, | ||
| 475 | .contents = .{ .not_available = {} }, | ||
| 476 | .status = .never_loaded, | ||
| 477 | }; | ||
| 478 | |||
| 479 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); | 730 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); |
| 480 | var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{ | 731 | var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{ |
| 481 | .target = options.target, | 732 | .target = options.target, |
| ... | @@ -485,6 +736,32 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -485,6 +736,32 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 485 | }); | 736 | }); |
| 486 | errdefer bin_file.deinit(); | 737 | errdefer bin_file.deinit(); |
| 487 | 738 | ||
| 739 | const root_scope = blk: { | ||
| 740 | if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) { | ||
| 741 | const root_scope = try gpa.create(Scope.File); | ||
| 742 | root_scope.* = .{ | ||
| 743 | .sub_file_path = options.root_pkg.root_src_path, | ||
| 744 | .source = .{ .unloaded = {} }, | ||
| 745 | .contents = .{ .not_available = {} }, | ||
| 746 | .status = .never_loaded, | ||
| 747 | .decls = .{}, | ||
| 748 | }; | ||
| 749 | break :blk &root_scope.base; | ||
| 750 | } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { | ||
| 751 | const root_scope = try gpa.create(Scope.ZIRModule); | ||
| 752 | root_scope.* = .{ | ||
| 753 | .sub_file_path = options.root_pkg.root_src_path, | ||
| 754 | .source = .{ .unloaded = {} }, | ||
| 755 | .contents = .{ .not_available = {} }, | ||
| 756 | .status = .never_loaded, | ||
| 757 | .decls = .{}, | ||
| 758 | }; | ||
| 759 | break :blk &root_scope.base; | ||
| 760 | } else { | ||
| 761 | unreachable; | ||
| 762 | } | ||
| 763 | }; | ||
| 764 | |||
| 488 | return Module{ | 765 | return Module{ |
| 489 | .allocator = gpa, | 766 | .allocator = gpa, |
| 490 | .root_pkg = options.root_pkg, | 767 | .root_pkg = options.root_pkg, |
| ... | @@ -493,13 +770,14 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -493,13 +770,14 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 493 | .bin_file_path = options.bin_file_path, | 770 | .bin_file_path = options.bin_file_path, |
| 494 | .bin_file = bin_file, | 771 | .bin_file = bin_file, |
| 495 | .optimize_mode = options.optimize_mode, | 772 | .optimize_mode = options.optimize_mode, |
| 496 | .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa), | 773 | .decl_table = DeclTable.init(gpa), |
| 497 | .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa), | 774 | .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa), |
| 498 | .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa), | 775 | .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa), |
| 499 | .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa), | 776 | .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa), |
| 500 | .failed_files = std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg).init(gpa), | 777 | .failed_files = std.AutoHashMap(*Scope, *ErrorMsg).init(gpa), |
| 501 | .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa), | 778 | .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa), |
| 502 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), | 779 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 780 | .keep_source_files_loaded = options.keep_source_files_loaded, | ||
| 503 | }; | 781 | }; |
| 504 | } | 782 | } |
| 505 | 783 | ||
| ... | @@ -551,10 +829,7 @@ pub fn deinit(self: *Module) void { | ... | @@ -551,10 +829,7 @@ pub fn deinit(self: *Module) void { |
| 551 | } | 829 | } |
| 552 | self.export_owners.deinit(); | 830 | self.export_owners.deinit(); |
| 553 | } | 831 | } |
| 554 | { | 832 | self.root_scope.destroy(allocator); |
| 555 | self.root_scope.deinit(allocator); | ||
| 556 | allocator.destroy(self.root_scope); | ||
| 557 | } | ||
| 558 | self.* = undefined; | 833 | self.* = undefined; |
| 559 | } | 834 | } |
| 560 | 835 | ||
| ... | @@ -571,19 +846,31 @@ pub fn target(self: Module) std.Target { | ... | @@ -571,19 +846,31 @@ pub fn target(self: Module) std.Target { |
| 571 | 846 | ||
| 572 | /// Detect changes to source files, perform semantic analysis, and update the output files. | 847 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 573 | pub fn update(self: *Module) !void { | 848 | pub fn update(self: *Module) !void { |
| 849 | const tracy = trace(@src()); | ||
| 850 | defer tracy.end(); | ||
| 851 | |||
| 574 | self.generation += 1; | 852 | self.generation += 1; |
| 575 | 853 | ||
| 576 | // TODO Use the cache hash file system to detect which source files changed. | 854 | // TODO Use the cache hash file system to detect which source files changed. |
| 577 | // Here we simulate a full cache miss. | 855 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; |
| 578 | // Analyze the root source file now. | 856 | // to force a refresh we unload now. |
| 579 | // Source files could have been loaded for any reason; to force a refresh we unload now. | 857 | if (self.root_scope.cast(Scope.File)) |zig_file| { |
| 580 | self.root_scope.unload(self.allocator); | 858 | zig_file.unload(self.allocator); |
| 581 | self.analyzeRoot(self.root_scope) catch |err| switch (err) { | 859 | self.analyzeRootSrcFile(zig_file) catch |err| switch (err) { |
| 582 | error.AnalysisFail => { | 860 | error.AnalysisFail => { |
| 583 | assert(self.totalErrorCount() != 0); | 861 | assert(self.totalErrorCount() != 0); |
| 584 | }, | 862 | }, |
| 585 | else => |e| return e, | 863 | else => |e| return e, |
| 586 | }; | 864 | }; |
| 865 | } else if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| { | ||
| 866 | zir_module.unload(self.allocator); | ||
| 867 | self.analyzeRootZIRModule(zir_module) catch |err| switch (err) { | ||
| 868 | error.AnalysisFail => { | ||
| 869 | assert(self.totalErrorCount() != 0); | ||
| 870 | }, | ||
| 871 | else => |e| return e, | ||
| 872 | }; | ||
| 873 | } | ||
| 587 | 874 | ||
| 588 | try self.performAllTheWork(); | 875 | try self.performAllTheWork(); |
| 589 | 876 | ||
| ... | @@ -596,14 +883,16 @@ pub fn update(self: *Module) !void { | ... | @@ -596,14 +883,16 @@ pub fn update(self: *Module) !void { |
| 596 | try self.deleteDecl(decl); | 883 | try self.deleteDecl(decl); |
| 597 | } | 884 | } |
| 598 | 885 | ||
| 886 | self.link_error_flags = self.bin_file.error_flags; | ||
| 887 | |||
| 599 | // If there are any errors, we anticipate the source files being loaded | 888 | // If there are any errors, we anticipate the source files being loaded |
| 600 | // to report error messages. Otherwise we unload all source files to save memory. | 889 | // to report error messages. Otherwise we unload all source files to save memory. |
| 601 | if (self.totalErrorCount() == 0) { | 890 | if (self.totalErrorCount() == 0) { |
| 602 | self.root_scope.unload(self.allocator); | 891 | if (!self.keep_source_files_loaded) { |
| 892 | self.root_scope.unload(self.allocator); | ||
| 893 | } | ||
| 894 | try self.bin_file.flush(); | ||
| 603 | } | 895 | } |
| 604 | |||
| 605 | try self.bin_file.flush(); | ||
| 606 | self.link_error_flags = self.bin_file.error_flags; | ||
| 607 | } | 896 | } |
| 608 | 897 | ||
| 609 | /// Having the file open for writing is problematic as far as executing the | 898 | /// Having the file open for writing is problematic as far as executing the |
| ... | @@ -619,10 +908,10 @@ pub fn makeBinFileWritable(self: *Module) !void { | ... | @@ -619,10 +908,10 @@ pub fn makeBinFileWritable(self: *Module) !void { |
| 619 | } | 908 | } |
| 620 | 909 | ||
| 621 | pub fn totalErrorCount(self: *Module) usize { | 910 | pub fn totalErrorCount(self: *Module) usize { |
| 622 | return self.failed_decls.size + | 911 | const total = self.failed_decls.size + |
| 623 | self.failed_files.size + | 912 | self.failed_files.size + |
| 624 | self.failed_exports.size + | 913 | self.failed_exports.size; |
| 625 | @boolToInt(self.link_error_flags.no_entry_point_found); | 914 | return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; |
| 626 | } | 915 | } |
| 627 | 916 | ||
| 628 | pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | 917 | pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| ... | @@ -637,8 +926,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | ... | @@ -637,8 +926,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 637 | while (it.next()) |kv| { | 926 | while (it.next()) |kv| { |
| 638 | const scope = kv.key; | 927 | const scope = kv.key; |
| 639 | const err_msg = kv.value; | 928 | const err_msg = kv.value; |
| 640 | const source = try self.getSource(scope); | 929 | const source = try scope.getSource(self); |
| 641 | try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*); | 930 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); |
| 642 | } | 931 | } |
| 643 | } | 932 | } |
| 644 | { | 933 | { |
| ... | @@ -646,8 +935,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | ... | @@ -646,8 +935,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 646 | while (it.next()) |kv| { | 935 | while (it.next()) |kv| { |
| 647 | const decl = kv.key; | 936 | const decl = kv.key; |
| 648 | const err_msg = kv.value; | 937 | const err_msg = kv.value; |
| 649 | const source = try self.getSource(decl.scope); | 938 | const source = try decl.scope.getSource(self); |
| 650 | try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*); | 939 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 651 | } | 940 | } |
| 652 | } | 941 | } |
| 653 | { | 942 | { |
| ... | @@ -655,12 +944,12 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | ... | @@ -655,12 +944,12 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 655 | while (it.next()) |kv| { | 944 | while (it.next()) |kv| { |
| 656 | const decl = kv.key.owner_decl; | 945 | const decl = kv.key.owner_decl; |
| 657 | const err_msg = kv.value; | 946 | const err_msg = kv.value; |
| 658 | const source = try self.getSource(decl.scope); | 947 | const source = try decl.scope.getSource(self); |
| 659 | try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*); | 948 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 660 | } | 949 | } |
| 661 | } | 950 | } |
| 662 | 951 | ||
| 663 | if (self.link_error_flags.no_entry_point_found) { | 952 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { |
| 664 | try errors.append(.{ | 953 | try errors.append(.{ |
| 665 | .src_path = self.root_pkg.root_src_path, | 954 | .src_path = self.root_pkg.root_src_path, |
| 666 | .line = 0, | 955 | .line = 0, |
| ... | @@ -683,12 +972,14 @@ const InnerError = error{ OutOfMemory, AnalysisFail }; | ... | @@ -683,12 +972,14 @@ const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 683 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | 972 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 684 | while (self.work_queue.readItem()) |work_item| switch (work_item) { | 973 | while (self.work_queue.readItem()) |work_item| switch (work_item) { |
| 685 | .codegen_decl => |decl| switch (decl.analysis) { | 974 | .codegen_decl => |decl| switch (decl.analysis) { |
| 975 | .unreferenced => unreachable, | ||
| 686 | .in_progress => unreachable, | 976 | .in_progress => unreachable, |
| 687 | .outdated => unreachable, | 977 | .outdated => unreachable, |
| 688 | 978 | ||
| 689 | .sema_failure, | 979 | .sema_failure, |
| 690 | .codegen_failure, | 980 | .codegen_failure, |
| 691 | .dependency_failure, | 981 | .dependency_failure, |
| 982 | .sema_failure_retryable, | ||
| 692 | => continue, | 983 | => continue, |
| 693 | 984 | ||
| 694 | .complete, .codegen_failure_retryable => { | 985 | .complete, .codegen_failure_retryable => { |
| ... | @@ -696,12 +987,10 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | ... | @@ -696,12 +987,10 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 696 | switch (payload.func.analysis) { | 987 | switch (payload.func.analysis) { |
| 697 | .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) { | 988 | .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) { |
| 698 | error.AnalysisFail => { | 989 | error.AnalysisFail => { |
| 699 | if (payload.func.analysis == .queued) { | 990 | assert(payload.func.analysis != .in_progress); |
| 700 | payload.func.analysis = .dependency_failure; | ||
| 701 | } | ||
| 702 | continue; | 991 | continue; |
| 703 | }, | 992 | }, |
| 704 | else => |e| return e, | 993 | error.OutOfMemory => return error.OutOfMemory, |
| 705 | }, | 994 | }, |
| 706 | .in_progress => unreachable, | 995 | .in_progress => unreachable, |
| 707 | .sema_failure, .dependency_failure => continue, | 996 | .sema_failure, .dependency_failure => continue, |
| ... | @@ -720,7 +1009,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | ... | @@ -720,7 +1009,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 720 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | 1009 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 721 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 1010 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 722 | self.allocator, | 1011 | self.allocator, |
| 723 | decl.src, | 1012 | decl.src(), |
| 724 | "unable to codegen: {}", | 1013 | "unable to codegen: {}", |
| 725 | .{@errorName(err)}, | 1014 | .{@errorName(err)}, |
| 726 | )); | 1015 | )); |
| ... | @@ -729,41 +1018,560 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | ... | @@ -729,41 +1018,560 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 729 | }; | 1018 | }; |
| 730 | }, | 1019 | }, |
| 731 | }, | 1020 | }, |
| 732 | .re_analyze_decl => |decl| switch (decl.analysis) { | 1021 | .analyze_decl => |decl| { |
| 733 | .in_progress => unreachable, | 1022 | self.ensureDeclAnalyzed(decl) catch |err| switch (err) { |
| 1023 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1024 | error.AnalysisFail => continue, | ||
| 1025 | }; | ||
| 1026 | }, | ||
| 1027 | }; | ||
| 1028 | } | ||
| 734 | 1029 | ||
| 735 | .sema_failure, | 1030 | fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 736 | .codegen_failure, | 1031 | const tracy = trace(@src()); |
| 737 | .dependency_failure, | 1032 | defer tracy.end(); |
| 738 | .complete, | ||
| 739 | .codegen_failure_retryable, | ||
| 740 | => continue, | ||
| 741 | 1033 | ||
| 742 | .outdated => { | 1034 | const subsequent_analysis = switch (decl.analysis) { |
| 743 | const zir_module = self.getSrcModule(decl.scope) catch |err| switch (err) { | 1035 | .in_progress => unreachable, |
| 744 | error.OutOfMemory => return error.OutOfMemory, | 1036 | |
| 745 | else => { | 1037 | .sema_failure, |
| 746 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | 1038 | .sema_failure_retryable, |
| 747 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 1039 | .codegen_failure, |
| 748 | self.allocator, | 1040 | .dependency_failure, |
| 749 | decl.src, | 1041 | .codegen_failure_retryable, |
| 750 | "unable to load source file '{}': {}", | 1042 | => return error.AnalysisFail, |
| 751 | .{ decl.scope.sub_file_path, @errorName(err) }, | 1043 | |
| 752 | )); | 1044 | .complete, .outdated => blk: { |
| 753 | decl.analysis = .codegen_failure_retryable; | 1045 | if (decl.generation == self.generation) { |
| 754 | continue; | 1046 | assert(decl.analysis == .complete); |
| 1047 | return; | ||
| 1048 | } | ||
| 1049 | //std.debug.warn("re-analyzing {}\n", .{decl.name}); | ||
| 1050 | |||
| 1051 | // The exports this Decl performs will be re-discovered, so we remove them here | ||
| 1052 | // prior to re-analysis. | ||
| 1053 | self.deleteDeclExports(decl); | ||
| 1054 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. | ||
| 1055 | for (decl.dependencies.items) |dep| { | ||
| 1056 | dep.removeDependant(decl); | ||
| 1057 | if (dep.dependants.items.len == 0 and !dep.deletion_flag) { | ||
| 1058 | // We don't perform a deletion here, because this Decl or another one | ||
| 1059 | // may end up referencing it before the update is complete. | ||
| 1060 | dep.deletion_flag = true; | ||
| 1061 | try self.deletion_set.append(self.allocator, dep); | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | decl.dependencies.shrink(self.allocator, 0); | ||
| 1065 | |||
| 1066 | break :blk true; | ||
| 1067 | }, | ||
| 1068 | |||
| 1069 | .unreferenced => false, | ||
| 1070 | }; | ||
| 1071 | |||
| 1072 | const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| | ||
| 1073 | try self.analyzeZirDecl(decl, zir_module.contents.module.decls[decl.src_index]) | ||
| 1074 | else | ||
| 1075 | self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { | ||
| 1076 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1077 | error.AnalysisFail => return error.AnalysisFail, | ||
| 1078 | else => { | ||
| 1079 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | ||
| 1080 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | ||
| 1081 | self.allocator, | ||
| 1082 | decl.src(), | ||
| 1083 | "unable to analyze: {}", | ||
| 1084 | .{@errorName(err)}, | ||
| 1085 | )); | ||
| 1086 | decl.analysis = .sema_failure_retryable; | ||
| 1087 | return error.AnalysisFail; | ||
| 1088 | }, | ||
| 1089 | }; | ||
| 1090 | |||
| 1091 | if (subsequent_analysis) { | ||
| 1092 | // We may need to chase the dependants and re-analyze them. | ||
| 1093 | // However, if the decl is a function, and the type is the same, we do not need to. | ||
| 1094 | if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) { | ||
| 1095 | for (decl.dependants.items) |dep| { | ||
| 1096 | switch (dep.analysis) { | ||
| 1097 | .unreferenced => unreachable, | ||
| 1098 | .in_progress => unreachable, | ||
| 1099 | .outdated => continue, // already queued for update | ||
| 1100 | |||
| 1101 | .dependency_failure, | ||
| 1102 | .sema_failure, | ||
| 1103 | .sema_failure_retryable, | ||
| 1104 | .codegen_failure, | ||
| 1105 | .codegen_failure_retryable, | ||
| 1106 | .complete, | ||
| 1107 | => if (dep.generation != self.generation) { | ||
| 1108 | try self.markOutdatedDecl(dep); | ||
| 755 | }, | 1109 | }, |
| 1110 | } | ||
| 1111 | } | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ||
| 1117 | const tracy = trace(@src()); | ||
| 1118 | defer tracy.end(); | ||
| 1119 | |||
| 1120 | const file_scope = decl.scope.cast(Scope.File).?; | ||
| 1121 | const tree = try self.getAstTree(file_scope); | ||
| 1122 | const ast_node = tree.root_node.decls()[decl.src_index]; | ||
| 1123 | switch (ast_node.id) { | ||
| 1124 | .FnProto => { | ||
| 1125 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node); | ||
| 1126 | |||
| 1127 | decl.analysis = .in_progress; | ||
| 1128 | |||
| 1129 | // This arena allocator's memory is discarded at the end of this function. It is used | ||
| 1130 | // to determine the type of the function, and hence the type of the decl, which is needed | ||
| 1131 | // to complete the Decl analysis. | ||
| 1132 | var fn_type_scope: Scope.GenZIR = .{ | ||
| 1133 | .decl = decl, | ||
| 1134 | .arena = std.heap.ArenaAllocator.init(self.allocator), | ||
| 1135 | .instructions = std.ArrayList(*zir.Inst).init(self.allocator), | ||
| 1136 | }; | ||
| 1137 | defer fn_type_scope.arena.deinit(); | ||
| 1138 | defer fn_type_scope.instructions.deinit(); | ||
| 1139 | |||
| 1140 | const body_node = fn_proto.body_node orelse | ||
| 1141 | return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{}); | ||
| 1142 | if (fn_proto.params_len != 0) { | ||
| 1143 | return self.failTok( | ||
| 1144 | &fn_type_scope.base, | ||
| 1145 | fn_proto.params()[0].name_token.?, | ||
| 1146 | "TODO implement function parameters", | ||
| 1147 | .{}, | ||
| 1148 | ); | ||
| 1149 | } | ||
| 1150 | if (fn_proto.lib_name) |lib_name| { | ||
| 1151 | return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 1152 | } | ||
| 1153 | if (fn_proto.align_expr) |align_expr| { | ||
| 1154 | return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 1155 | } | ||
| 1156 | if (fn_proto.section_expr) |sect_expr| { | ||
| 1157 | return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 1158 | } | ||
| 1159 | if (fn_proto.callconv_expr) |callconv_expr| { | ||
| 1160 | return self.failNode( | ||
| 1161 | &fn_type_scope.base, | ||
| 1162 | callconv_expr, | ||
| 1163 | "TODO implement function calling convention expression", | ||
| 1164 | .{}, | ||
| 1165 | ); | ||
| 1166 | } | ||
| 1167 | const return_type_expr = switch (fn_proto.return_type) { | ||
| 1168 | .Explicit => |node| node, | ||
| 1169 | .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), | ||
| 1170 | .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), | ||
| 1171 | }; | ||
| 1172 | |||
| 1173 | const return_type_inst = try self.astGenExpr(&fn_type_scope.base, return_type_expr); | ||
| 1174 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | ||
| 1175 | const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | ||
| 1176 | .return_type = return_type_inst, | ||
| 1177 | .param_types = &[0]*zir.Inst{}, | ||
| 1178 | }, .{}); | ||
| 1179 | _ = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.Return, .{ .operand = fn_type_inst }, .{}); | ||
| 1180 | |||
| 1181 | // We need the memory for the Type to go into the arena for the Decl | ||
| 1182 | var decl_arena = std.heap.ArenaAllocator.init(self.allocator); | ||
| 1183 | errdefer decl_arena.deinit(); | ||
| 1184 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1185 | |||
| 1186 | var block_scope: Scope.Block = .{ | ||
| 1187 | .func = null, | ||
| 1188 | .decl = decl, | ||
| 1189 | .instructions = .{}, | ||
| 1190 | .arena = &decl_arena.allocator, | ||
| 1191 | }; | ||
| 1192 | defer block_scope.instructions.deinit(self.allocator); | ||
| 1193 | |||
| 1194 | const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{ | ||
| 1195 | .instructions = fn_type_scope.instructions.items, | ||
| 1196 | }); | ||
| 1197 | const new_func = try decl_arena.allocator.create(Fn); | ||
| 1198 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); | ||
| 1199 | |||
| 1200 | const fn_zir = blk: { | ||
| 1201 | // This scope's arena memory is discarded after the ZIR generation | ||
| 1202 | // pass completes, and semantic analysis of it completes. | ||
| 1203 | var gen_scope: Scope.GenZIR = .{ | ||
| 1204 | .decl = decl, | ||
| 1205 | .arena = std.heap.ArenaAllocator.init(self.allocator), | ||
| 1206 | .instructions = std.ArrayList(*zir.Inst).init(self.allocator), | ||
| 756 | }; | 1207 | }; |
| 757 | const decl_name = mem.spanZ(decl.name); | 1208 | errdefer gen_scope.arena.deinit(); |
| 758 | // We already detected deletions, so we know this will be found. | 1209 | defer gen_scope.instructions.deinit(); |
| 759 | const src_decl = zir_module.findDecl(decl_name).?; | 1210 | |
| 760 | self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) { | 1211 | const body_block = body_node.cast(ast.Node.Block).?; |
| 761 | error.OutOfMemory => return error.OutOfMemory, | 1212 | |
| 762 | error.AnalysisFail => continue, | 1213 | try self.astGenBlock(&gen_scope.base, body_block); |
| 1214 | |||
| 1215 | const fn_zir = try gen_scope.arena.allocator.create(Fn.ZIR); | ||
| 1216 | fn_zir.* = .{ | ||
| 1217 | .body = .{ | ||
| 1218 | .instructions = try gen_scope.arena.allocator.dupe(*zir.Inst, gen_scope.instructions.items), | ||
| 1219 | }, | ||
| 1220 | .arena = gen_scope.arena.state, | ||
| 763 | }; | 1221 | }; |
| 764 | }, | 1222 | break :blk fn_zir; |
| 1223 | }; | ||
| 1224 | |||
| 1225 | new_func.* = .{ | ||
| 1226 | .analysis = .{ .queued = fn_zir }, | ||
| 1227 | .owner_decl = decl, | ||
| 1228 | }; | ||
| 1229 | fn_payload.* = .{ .func = new_func }; | ||
| 1230 | |||
| 1231 | var prev_type_has_bits = false; | ||
| 1232 | var type_changed = true; | ||
| 1233 | |||
| 1234 | if (decl.typedValueManaged()) |tvm| { | ||
| 1235 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | ||
| 1236 | type_changed = !tvm.typed_value.ty.eql(fn_type); | ||
| 1237 | |||
| 1238 | tvm.deinit(self.allocator); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | decl_arena_state.* = decl_arena.state; | ||
| 1242 | decl.typed_value = .{ | ||
| 1243 | .most_recent = .{ | ||
| 1244 | .typed_value = .{ | ||
| 1245 | .ty = fn_type, | ||
| 1246 | .val = Value.initPayload(&fn_payload.base), | ||
| 1247 | }, | ||
| 1248 | .arena = decl_arena_state, | ||
| 1249 | }, | ||
| 1250 | }; | ||
| 1251 | decl.analysis = .complete; | ||
| 1252 | decl.generation = self.generation; | ||
| 1253 | |||
| 1254 | if (fn_type.hasCodeGenBits()) { | ||
| 1255 | // We don't fully codegen the decl until later, but we do need to reserve a global | ||
| 1256 | // offset table index for it. This allows us to codegen decls out of dependency order, | ||
| 1257 | // increasing how many computations can be done in parallel. | ||
| 1258 | try self.bin_file.allocateDeclIndexes(decl); | ||
| 1259 | try self.work_queue.writeItem(.{ .codegen_decl = decl }); | ||
| 1260 | } else if (prev_type_has_bits) { | ||
| 1261 | self.bin_file.freeDecl(decl); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { | ||
| 1265 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1266 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1267 | const name_loc = tree.token_locs[fn_proto.name_token.?]; | ||
| 1268 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1269 | // The scope needs to have the decl in it. | ||
| 1270 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1271 | } | ||
| 1272 | } | ||
| 1273 | return type_changed; | ||
| 765 | }, | 1274 | }, |
| 1275 | .VarDecl => @panic("TODO var decl"), | ||
| 1276 | .Comptime => @panic("TODO comptime decl"), | ||
| 1277 | .Use => @panic("TODO usingnamespace decl"), | ||
| 1278 | else => unreachable, | ||
| 1279 | } | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type { | ||
| 1283 | try self.analyzeBody(&block_scope.base, body); | ||
| 1284 | for (block_scope.instructions.items) |inst| { | ||
| 1285 | if (inst.cast(Inst.Ret)) |ret| { | ||
| 1286 | const val = try self.resolveConstValue(&block_scope.base, ret.args.operand); | ||
| 1287 | return val.toType(); | ||
| 1288 | } else { | ||
| 1289 | return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | unreachable; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir.Inst { | ||
| 1296 | switch (ast_node.id) { | ||
| 1297 | .Identifier => return self.astGenIdent(scope, @fieldParentPtr(ast.Node.Identifier, "base", ast_node)), | ||
| 1298 | .Asm => return self.astGenAsm(scope, @fieldParentPtr(ast.Node.Asm, "base", ast_node)), | ||
| 1299 | .StringLiteral => return self.astGenStringLiteral(scope, @fieldParentPtr(ast.Node.StringLiteral, "base", ast_node)), | ||
| 1300 | .IntegerLiteral => return self.astGenIntegerLiteral(scope, @fieldParentPtr(ast.Node.IntegerLiteral, "base", ast_node)), | ||
| 1301 | .BuiltinCall => return self.astGenBuiltinCall(scope, @fieldParentPtr(ast.Node.BuiltinCall, "base", ast_node)), | ||
| 1302 | .Call => return self.astGenCall(scope, @fieldParentPtr(ast.Node.Call, "base", ast_node)), | ||
| 1303 | .Unreachable => return self.astGenUnreachable(scope, @fieldParentPtr(ast.Node.Unreachable, "base", ast_node)), | ||
| 1304 | .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)), | ||
| 1305 | else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}), | ||
| 1306 | } | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | fn astGenControlFlowExpression( | ||
| 1310 | self: *Module, | ||
| 1311 | scope: *Scope, | ||
| 1312 | cfe: *ast.Node.ControlFlowExpression, | ||
| 1313 | ) InnerError!*zir.Inst { | ||
| 1314 | switch (cfe.kind) { | ||
| 1315 | .Break => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Break", .{}), | ||
| 1316 | .Continue => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Continue", .{}), | ||
| 1317 | .Return => {}, | ||
| 1318 | } | ||
| 1319 | const tree = scope.tree(); | ||
| 1320 | const src = tree.token_locs[cfe.ltoken].start; | ||
| 1321 | if (cfe.rhs) |rhs_node| { | ||
| 1322 | const operand = try self.astGenExpr(scope, rhs_node); | ||
| 1323 | return self.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{}); | ||
| 1324 | } else { | ||
| 1325 | return self.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{}); | ||
| 1326 | } | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst { | ||
| 1330 | const tree = scope.tree(); | ||
| 1331 | const ident_name = tree.tokenSlice(ident.token); | ||
| 1332 | if (mem.eql(u8, ident_name, "_")) { | ||
| 1333 | return self.failNode(scope, &ident.base, "TODO implement '_' identifier", .{}); | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | if (getSimplePrimitiveValue(ident_name)) |typed_value| { | ||
| 1337 | const src = tree.token_locs[ident.token].start; | ||
| 1338 | return self.addZIRInstConst(scope, src, typed_value); | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | if (ident_name.len >= 2) integer: { | ||
| 1342 | const first_c = ident_name[0]; | ||
| 1343 | if (first_c == 'i' or first_c == 'u') { | ||
| 1344 | const is_signed = first_c == 'i'; | ||
| 1345 | const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) { | ||
| 1346 | error.Overflow => return self.failNode( | ||
| 1347 | scope, | ||
| 1348 | &ident.base, | ||
| 1349 | "primitive integer type '{}' exceeds maximum bit width of 65535", | ||
| 1350 | .{ident_name}, | ||
| 1351 | ), | ||
| 1352 | error.InvalidCharacter => break :integer, | ||
| 1353 | }; | ||
| 1354 | return self.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}); | ||
| 1355 | } | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | if (self.lookupDeclName(scope, ident_name)) |decl| { | ||
| 1359 | const src = tree.token_locs[ident.token].start; | ||
| 1360 | return try self.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | return self.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{}); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | fn astGenStringLiteral(self: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst { | ||
| 1367 | const tree = scope.tree(); | ||
| 1368 | const unparsed_bytes = tree.tokenSlice(str_lit.token); | ||
| 1369 | const arena = scope.arena(); | ||
| 1370 | |||
| 1371 | var bad_index: usize = undefined; | ||
| 1372 | const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) { | ||
| 1373 | error.InvalidCharacter => { | ||
| 1374 | const bad_byte = unparsed_bytes[bad_index]; | ||
| 1375 | const src = tree.token_locs[str_lit.token].start; | ||
| 1376 | return self.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); | ||
| 1377 | }, | ||
| 1378 | else => |e| return e, | ||
| 766 | }; | 1379 | }; |
| 1380 | |||
| 1381 | const src = tree.token_locs[str_lit.token].start; | ||
| 1382 | return self.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | fn astGenIntegerLiteral(self: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst { | ||
| 1386 | const arena = scope.arena(); | ||
| 1387 | const tree = scope.tree(); | ||
| 1388 | const prefixed_bytes = tree.tokenSlice(int_lit.token); | ||
| 1389 | const base = if (mem.startsWith(u8, prefixed_bytes, "0x")) | ||
| 1390 | 16 | ||
| 1391 | else if (mem.startsWith(u8, prefixed_bytes, "0o")) | ||
| 1392 | 8 | ||
| 1393 | else if (mem.startsWith(u8, prefixed_bytes, "0b")) | ||
| 1394 | 2 | ||
| 1395 | else | ||
| 1396 | @as(u8, 10); | ||
| 1397 | |||
| 1398 | const bytes = if (base == 10) | ||
| 1399 | prefixed_bytes | ||
| 1400 | else | ||
| 1401 | prefixed_bytes[2..]; | ||
| 1402 | |||
| 1403 | if (std.fmt.parseInt(u64, bytes, base)) |small_int| { | ||
| 1404 | const int_payload = try arena.create(Value.Payload.Int_u64); | ||
| 1405 | int_payload.* = .{ .int = small_int }; | ||
| 1406 | const src = tree.token_locs[int_lit.token].start; | ||
| 1407 | return self.addZIRInstConst(scope, src, .{ | ||
| 1408 | .ty = Type.initTag(.comptime_int), | ||
| 1409 | .val = Value.initPayload(&int_payload.base), | ||
| 1410 | }); | ||
| 1411 | } else |err| { | ||
| 1412 | return self.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{}); | ||
| 1413 | } | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | fn astGenBlock(self: *Module, scope: *Scope, block_node: *ast.Node.Block) !void { | ||
| 1417 | const tracy = trace(@src()); | ||
| 1418 | defer tracy.end(); | ||
| 1419 | |||
| 1420 | if (block_node.label) |label| { | ||
| 1421 | return self.failTok(scope, label, "TODO implement labeled blocks", .{}); | ||
| 1422 | } | ||
| 1423 | for (block_node.statements()) |statement| { | ||
| 1424 | _ = try self.astGenExpr(scope, statement); | ||
| 1425 | } | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | fn astGenAsm(self: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { | ||
| 1429 | if (asm_node.outputs.len != 0) { | ||
| 1430 | return self.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); | ||
| 1431 | } | ||
| 1432 | const arena = scope.arena(); | ||
| 1433 | const tree = scope.tree(); | ||
| 1434 | |||
| 1435 | const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len); | ||
| 1436 | const args = try arena.alloc(*zir.Inst, asm_node.inputs.len); | ||
| 1437 | |||
| 1438 | for (asm_node.inputs) |input, i| { | ||
| 1439 | // TODO semantically analyze constraints | ||
| 1440 | inputs[i] = try self.astGenExpr(scope, input.constraint); | ||
| 1441 | args[i] = try self.astGenExpr(scope, input.expr); | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | const src = tree.token_locs[asm_node.asm_token].start; | ||
| 1445 | const return_type = try self.addZIRInstConst(scope, src, .{ | ||
| 1446 | .ty = Type.initTag(.type), | ||
| 1447 | .val = Value.initTag(.void_type), | ||
| 1448 | }); | ||
| 1449 | const asm_inst = try self.addZIRInst(scope, src, zir.Inst.Asm, .{ | ||
| 1450 | .asm_source = try self.astGenExpr(scope, asm_node.template), | ||
| 1451 | .return_type = return_type, | ||
| 1452 | }, .{ | ||
| 1453 | .@"volatile" = asm_node.volatile_token != null, | ||
| 1454 | //.clobbers = TODO handle clobbers | ||
| 1455 | .inputs = inputs, | ||
| 1456 | .args = args, | ||
| 1457 | }); | ||
| 1458 | return asm_inst; | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | fn astGenBuiltinCall(self: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 1462 | const tree = scope.tree(); | ||
| 1463 | const builtin_name = tree.tokenSlice(call.builtin_token); | ||
| 1464 | const src = tree.token_locs[call.builtin_token].start; | ||
| 1465 | |||
| 1466 | inline for (std.meta.declarations(zir.Inst)) |inst| { | ||
| 1467 | if (inst.data != .Type) continue; | ||
| 1468 | const T = inst.data.Type; | ||
| 1469 | if (!@hasDecl(T, "builtin_name")) continue; | ||
| 1470 | if (std.mem.eql(u8, builtin_name, T.builtin_name)) { | ||
| 1471 | var value: T = undefined; | ||
| 1472 | const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct; | ||
| 1473 | if (positionals.fields.len == 0) { | ||
| 1474 | return self.addZIRInst(scope, src, T, value.positionals, value.kw_args); | ||
| 1475 | } | ||
| 1476 | const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len; | ||
| 1477 | if (arg_count) |some| { | ||
| 1478 | if (call.params_len != some) { | ||
| 1479 | return self.failTok(scope, call.builtin_token, "expected {} parameter, found {}", .{ some, call.params_len }); | ||
| 1480 | } | ||
| 1481 | const params = call.params(); | ||
| 1482 | inline for (positionals.fields) |p, i| { | ||
| 1483 | @field(value.positionals, p.name) = try self.astGenExpr(scope, params[i]); | ||
| 1484 | } | ||
| 1485 | } else { | ||
| 1486 | return self.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name}); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | return self.addZIRInst(scope, src, T, value.positionals, .{}); | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | return self.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name}); | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | fn astGenCall(self: *Module, scope: *Scope, call: *ast.Node.Call) InnerError!*zir.Inst { | ||
| 1496 | const tree = scope.tree(); | ||
| 1497 | |||
| 1498 | if (call.params_len != 0) { | ||
| 1499 | return self.failNode(scope, &call.base, "TODO implement fn calls with parameters", .{}); | ||
| 1500 | } | ||
| 1501 | const lhs = try self.astGenExpr(scope, call.lhs); | ||
| 1502 | |||
| 1503 | const src = tree.token_locs[call.lhs.firstToken()].start; | ||
| 1504 | return self.addZIRInst(scope, src, zir.Inst.Call, .{ | ||
| 1505 | .func = lhs, | ||
| 1506 | .args = &[0]*zir.Inst{}, | ||
| 1507 | }, .{}); | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | fn astGenUnreachable(self: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst { | ||
| 1511 | const tree = scope.tree(); | ||
| 1512 | const src = tree.token_locs[unreach_node.token].start; | ||
| 1513 | return self.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{}); | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { | ||
| 1517 | const simple_types = std.ComptimeStringMap(Value.Tag, .{ | ||
| 1518 | .{ "u8", .u8_type }, | ||
| 1519 | .{ "i8", .i8_type }, | ||
| 1520 | .{ "isize", .isize_type }, | ||
| 1521 | .{ "usize", .usize_type }, | ||
| 1522 | .{ "c_short", .c_short_type }, | ||
| 1523 | .{ "c_ushort", .c_ushort_type }, | ||
| 1524 | .{ "c_int", .c_int_type }, | ||
| 1525 | .{ "c_uint", .c_uint_type }, | ||
| 1526 | .{ "c_long", .c_long_type }, | ||
| 1527 | .{ "c_ulong", .c_ulong_type }, | ||
| 1528 | .{ "c_longlong", .c_longlong_type }, | ||
| 1529 | .{ "c_ulonglong", .c_ulonglong_type }, | ||
| 1530 | .{ "c_longdouble", .c_longdouble_type }, | ||
| 1531 | .{ "f16", .f16_type }, | ||
| 1532 | .{ "f32", .f32_type }, | ||
| 1533 | .{ "f64", .f64_type }, | ||
| 1534 | .{ "f128", .f128_type }, | ||
| 1535 | .{ "c_void", .c_void_type }, | ||
| 1536 | .{ "bool", .bool_type }, | ||
| 1537 | .{ "void", .void_type }, | ||
| 1538 | .{ "type", .type_type }, | ||
| 1539 | .{ "anyerror", .anyerror_type }, | ||
| 1540 | .{ "comptime_int", .comptime_int_type }, | ||
| 1541 | .{ "comptime_float", .comptime_float_type }, | ||
| 1542 | .{ "noreturn", .noreturn_type }, | ||
| 1543 | }); | ||
| 1544 | if (simple_types.get(name)) |tag| { | ||
| 1545 | return TypedValue{ | ||
| 1546 | .ty = Type.initTag(.type), | ||
| 1547 | .val = Value.initTag(tag), | ||
| 1548 | }; | ||
| 1549 | } | ||
| 1550 | if (mem.eql(u8, name, "null")) { | ||
| 1551 | return TypedValue{ | ||
| 1552 | .ty = Type.initTag(.@"null"), | ||
| 1553 | .val = Value.initTag(.null_value), | ||
| 1554 | }; | ||
| 1555 | } | ||
| 1556 | if (mem.eql(u8, name, "undefined")) { | ||
| 1557 | return TypedValue{ | ||
| 1558 | .ty = Type.initTag(.@"undefined"), | ||
| 1559 | .val = Value.initTag(.undef), | ||
| 1560 | }; | ||
| 1561 | } | ||
| 1562 | if (mem.eql(u8, name, "true")) { | ||
| 1563 | return TypedValue{ | ||
| 1564 | .ty = Type.initTag(.bool), | ||
| 1565 | .val = Value.initTag(.bool_true), | ||
| 1566 | }; | ||
| 1567 | } | ||
| 1568 | if (mem.eql(u8, name, "false")) { | ||
| 1569 | return TypedValue{ | ||
| 1570 | .ty = Type.initTag(.bool), | ||
| 1571 | .val = Value.initTag(.bool_false), | ||
| 1572 | }; | ||
| 1573 | } | ||
| 1574 | return null; | ||
| 767 | } | 1575 | } |
| 768 | 1576 | ||
| 769 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { | 1577 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { |
| ... | @@ -775,28 +1583,11 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void | ... | @@ -775,28 +1583,11 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void |
| 775 | } else { | 1583 | } else { |
| 776 | depender.dependencies.appendAssumeCapacity(dependee); | 1584 | depender.dependencies.appendAssumeCapacity(dependee); |
| 777 | } | 1585 | } |
| 778 | 1586 | ||
| 779 | for (dependee.dependants.items) |item| { | 1587 | for (dependee.dependants.items) |item| { |
| 780 | if (item == depender) break; // Already in the set. | 1588 | if (item == depender) break; // Already in the set. |
| 781 | } else { | 1589 | } else { |
| 782 | dependee.dependants.appendAssumeCapacity(depender); | 1590 | dependee.dependants.appendAssumeCapacity(depender); |
| 783 | } | ||
| 784 | } | ||
| 785 | |||
| 786 | fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 { | ||
| 787 | switch (root_scope.source) { | ||
| 788 | .unloaded => { | ||
| 789 | const source = try self.root_pkg.root_src_dir.readFileAllocOptions( | ||
| 790 | self.allocator, | ||
| 791 | root_scope.sub_file_path, | ||
| 792 | std.math.maxInt(u32), | ||
| 793 | 1, | ||
| 794 | 0, | ||
| 795 | ); | ||
| 796 | root_scope.source = .{ .bytes = source }; | ||
| 797 | return source; | ||
| 798 | }, | ||
| 799 | .bytes => |bytes| return bytes, | ||
| 800 | } | 1591 | } |
| 801 | } | 1592 | } |
| 802 | 1593 | ||
| ... | @@ -805,7 +1596,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | ... | @@ -805,7 +1596,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 805 | .never_loaded, .unloaded_success => { | 1596 | .never_loaded, .unloaded_success => { |
| 806 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | 1597 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); |
| 807 | 1598 | ||
| 808 | const source = try self.getSource(root_scope); | 1599 | const source = try root_scope.getSource(self); |
| 809 | 1600 | ||
| 810 | var keep_zir_module = false; | 1601 | var keep_zir_module = false; |
| 811 | const zir_module = try self.allocator.create(zir.Module); | 1602 | const zir_module = try self.allocator.create(zir.Module); |
| ... | @@ -816,7 +1607,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | ... | @@ -816,7 +1607,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 816 | 1607 | ||
| 817 | if (zir_module.error_msg) |src_err_msg| { | 1608 | if (zir_module.error_msg) |src_err_msg| { |
| 818 | self.failed_files.putAssumeCapacityNoClobber( | 1609 | self.failed_files.putAssumeCapacityNoClobber( |
| 819 | root_scope, | 1610 | &root_scope.base, |
| 820 | try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), | 1611 | try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), |
| 821 | ); | 1612 | ); |
| 822 | root_scope.status = .unloaded_parse_failure; | 1613 | root_scope.status = .unloaded_parse_failure; |
| ... | @@ -838,90 +1629,189 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | ... | @@ -838,90 +1629,189 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 838 | } | 1629 | } |
| 839 | } | 1630 | } |
| 840 | 1631 | ||
| 841 | fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { | 1632 | fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1633 | const tracy = trace(@src()); | ||
| 1634 | defer tracy.end(); | ||
| 1635 | |||
| 842 | switch (root_scope.status) { | 1636 | switch (root_scope.status) { |
| 843 | .never_loaded => { | 1637 | .never_loaded, .unloaded_success => { |
| 844 | const src_module = try self.getSrcModule(root_scope); | 1638 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); |
| 845 | 1639 | ||
| 846 | // Here we ensure enough queue capacity to store all the decls, so that later we can use | 1640 | const source = try root_scope.getSource(self); |
| 847 | // appendAssumeCapacity. | ||
| 848 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | ||
| 849 | 1641 | ||
| 850 | for (src_module.decls) |decl| { | 1642 | var keep_tree = false; |
| 851 | if (decl.cast(zir.Inst.Export)) |export_inst| { | 1643 | const tree = try std.zig.parse(self.allocator, source); |
| 852 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base); | 1644 | defer if (!keep_tree) tree.deinit(); |
| 853 | } | 1645 | |
| 1646 | if (tree.errors.len != 0) { | ||
| 1647 | const parse_err = tree.errors[0]; | ||
| 1648 | |||
| 1649 | var msg = std.ArrayList(u8).init(self.allocator); | ||
| 1650 | defer msg.deinit(); | ||
| 1651 | |||
| 1652 | try parse_err.render(tree.token_ids, msg.outStream()); | ||
| 1653 | const err_msg = try self.allocator.create(ErrorMsg); | ||
| 1654 | err_msg.* = .{ | ||
| 1655 | .msg = msg.toOwnedSlice(), | ||
| 1656 | .byte_offset = tree.token_locs[parse_err.loc()].start, | ||
| 1657 | }; | ||
| 1658 | |||
| 1659 | self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | ||
| 1660 | root_scope.status = .unloaded_parse_failure; | ||
| 1661 | return error.AnalysisFail; | ||
| 854 | } | 1662 | } |
| 1663 | |||
| 1664 | root_scope.status = .loaded_success; | ||
| 1665 | root_scope.contents = .{ .tree = tree }; | ||
| 1666 | keep_tree = true; | ||
| 1667 | |||
| 1668 | return tree; | ||
| 855 | }, | 1669 | }, |
| 856 | 1670 | ||
| 857 | .unloaded_parse_failure, | 1671 | .unloaded_parse_failure => return error.AnalysisFail, |
| 858 | .unloaded_sema_failure, | 1672 | |
| 859 | .unloaded_success, | 1673 | .loaded_success => return root_scope.contents.tree, |
| 860 | .loaded_sema_failure, | 1674 | } |
| 861 | .loaded_success, | 1675 | } |
| 862 | => { | 1676 | |
| 863 | const src_module = try self.getSrcModule(root_scope); | 1677 | fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 864 | 1678 | // We may be analyzing it for the first time, or this may be | |
| 865 | var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator); | 1679 | // an incremental update. This code handles both cases. |
| 866 | defer exports_to_resolve.deinit(); | 1680 | const tree = try self.getAstTree(root_scope); |
| 867 | 1681 | const decls = tree.root_node.decls(); | |
| 868 | // Keep track of the decls that we expect to see in this file so that | 1682 | |
| 869 | // we know which ones have been deleted. | 1683 | try self.work_queue.ensureUnusedCapacity(decls.len); |
| 870 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); | 1684 | try root_scope.decls.ensureCapacity(self.allocator, decls.len); |
| 871 | defer deleted_decls.deinit(); | 1685 | |
| 872 | try deleted_decls.ensureCapacity(self.decl_table.size); | 1686 | // Keep track of the decls that we expect to see in this file so that |
| 873 | { | 1687 | // we know which ones have been deleted. |
| 874 | var it = self.decl_table.iterator(); | 1688 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); |
| 875 | while (it.next()) |kv| { | 1689 | defer deleted_decls.deinit(); |
| 876 | deleted_decls.putAssumeCapacityNoClobber(kv.value, {}); | 1690 | try deleted_decls.ensureCapacity(root_scope.decls.items.len); |
| 1691 | for (root_scope.decls.items) |file_decl| { | ||
| 1692 | deleted_decls.putAssumeCapacityNoClobber(file_decl, {}); | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | for (decls) |src_decl, decl_i| { | ||
| 1696 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { | ||
| 1697 | // We will create a Decl for it regardless of analysis status. | ||
| 1698 | const name_tok = fn_proto.name_token orelse | ||
| 1699 | @panic("TODO handle missing function name in the parser"); | ||
| 1700 | const name_loc = tree.token_locs[name_tok]; | ||
| 1701 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1702 | const name_hash = root_scope.fullyQualifiedNameHash(name); | ||
| 1703 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1704 | if (self.decl_table.get(name_hash)) |kv| { | ||
| 1705 | const decl = kv.value; | ||
| 1706 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1707 | // have been re-ordered. | ||
| 1708 | decl.src_index = decl_i; | ||
| 1709 | deleted_decls.removeAssertDiscard(decl); | ||
| 1710 | if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1711 | try self.markOutdatedDecl(decl); | ||
| 1712 | decl.contents_hash = contents_hash; | ||
| 877 | } | 1713 | } |
| 878 | } | 1714 | } else { |
| 879 | 1715 | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); | |
| 880 | for (src_module.decls) |src_decl| { | 1716 | root_scope.decls.appendAssumeCapacity(new_decl); |
| 881 | const name_hash = Decl.hashSimpleName(src_decl.name); | 1717 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { |
| 882 | if (self.decl_table.get(name_hash)) |kv| { | 1718 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 883 | const decl = kv.value; | 1719 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 884 | deleted_decls.removeAssertDiscard(decl); | ||
| 885 | const new_contents_hash = Decl.hashSimpleName(src_decl.contents); | ||
| 886 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); | ||
| 887 | if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) { | ||
| 888 | //std.debug.warn("'{}' {x} => {x}\n", .{ src_decl.name, decl.contents_hash, new_contents_hash }); | ||
| 889 | try self.markOutdatedDecl(decl); | ||
| 890 | decl.contents_hash = new_contents_hash; | ||
| 891 | } | 1720 | } |
| 892 | } else if (src_decl.cast(zir.Inst.Export)) |export_inst| { | ||
| 893 | try exports_to_resolve.append(&export_inst.base); | ||
| 894 | } | 1721 | } |
| 895 | } | 1722 | } |
| 896 | { | 1723 | } |
| 897 | // Handle explicitly deleted decls from the source code. Not to be confused | 1724 | // TODO also look for global variable declarations |
| 898 | // with when we delete decls because they are no longer referenced. | 1725 | // TODO also look for comptime blocks and exported globals |
| 899 | var it = deleted_decls.iterator(); | 1726 | } |
| 900 | while (it.next()) |kv| { | 1727 | { |
| 901 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | 1728 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 902 | try self.deleteDecl(kv.key); | 1729 | // with when we delete decls because they are no longer referenced. |
| 903 | } | 1730 | var it = deleted_decls.iterator(); |
| 1731 | while (it.next()) |kv| { | ||
| 1732 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | ||
| 1733 | try self.deleteDecl(kv.key); | ||
| 1734 | } | ||
| 1735 | } | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ||
| 1739 | // We may be analyzing it for the first time, or this may be | ||
| 1740 | // an incremental update. This code handles both cases. | ||
| 1741 | const src_module = try self.getSrcModule(root_scope); | ||
| 1742 | |||
| 1743 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | ||
| 1744 | try root_scope.decls.ensureCapacity(self.allocator, src_module.decls.len); | ||
| 1745 | |||
| 1746 | var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.allocator); | ||
| 1747 | defer exports_to_resolve.deinit(); | ||
| 1748 | |||
| 1749 | // Keep track of the decls that we expect to see in this file so that | ||
| 1750 | // we know which ones have been deleted. | ||
| 1751 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); | ||
| 1752 | defer deleted_decls.deinit(); | ||
| 1753 | try deleted_decls.ensureCapacity(self.decl_table.size); | ||
| 1754 | { | ||
| 1755 | var it = self.decl_table.iterator(); | ||
| 1756 | while (it.next()) |kv| { | ||
| 1757 | deleted_decls.putAssumeCapacityNoClobber(kv.value, {}); | ||
| 1758 | } | ||
| 1759 | } | ||
| 1760 | |||
| 1761 | for (src_module.decls) |src_decl, decl_i| { | ||
| 1762 | const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); | ||
| 1763 | if (self.decl_table.get(name_hash)) |kv| { | ||
| 1764 | const decl = kv.value; | ||
| 1765 | deleted_decls.removeAssertDiscard(decl); | ||
| 1766 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); | ||
| 1767 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { | ||
| 1768 | try self.markOutdatedDecl(decl); | ||
| 1769 | decl.contents_hash = src_decl.contents_hash; | ||
| 904 | } | 1770 | } |
| 905 | for (exports_to_resolve.items) |export_inst| { | 1771 | } else { |
| 906 | _ = try self.resolveDecl(&root_scope.base, export_inst); | 1772 | const new_decl = try self.createNewDecl( |
| 1773 | &root_scope.base, | ||
| 1774 | src_decl.name, | ||
| 1775 | decl_i, | ||
| 1776 | name_hash, | ||
| 1777 | src_decl.contents_hash, | ||
| 1778 | ); | ||
| 1779 | root_scope.decls.appendAssumeCapacity(new_decl); | ||
| 1780 | if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| { | ||
| 1781 | try exports_to_resolve.append(src_decl); | ||
| 907 | } | 1782 | } |
| 908 | }, | 1783 | } |
| 1784 | } | ||
| 1785 | for (exports_to_resolve.items) |export_decl| { | ||
| 1786 | _ = try self.resolveZirDecl(&root_scope.base, export_decl); | ||
| 1787 | } | ||
| 1788 | { | ||
| 1789 | // Handle explicitly deleted decls from the source code. Not to be confused | ||
| 1790 | // with when we delete decls because they are no longer referenced. | ||
| 1791 | var it = deleted_decls.iterator(); | ||
| 1792 | while (it.next()) |kv| { | ||
| 1793 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | ||
| 1794 | try self.deleteDecl(kv.key); | ||
| 1795 | } | ||
| 909 | } | 1796 | } |
| 910 | } | 1797 | } |
| 911 | 1798 | ||
| 912 | fn deleteDecl(self: *Module, decl: *Decl) !void { | 1799 | fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 913 | try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len); | 1800 | try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len); |
| 914 | 1801 | ||
| 1802 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will | ||
| 1803 | // not be present in the set, and this does nothing. | ||
| 1804 | decl.scope.removeDecl(decl); | ||
| 1805 | |||
| 915 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); | 1806 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); |
| 916 | const name_hash = decl.fullyQualifiedNameHash(); | 1807 | const name_hash = decl.fullyQualifiedNameHash(); |
| 917 | self.decl_table.removeAssertDiscard(name_hash); | 1808 | self.decl_table.removeAssertDiscard(name_hash); |
| 918 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. | 1809 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| 919 | for (decl.dependencies.items) |dep| { | 1810 | for (decl.dependencies.items) |dep| { |
| 920 | dep.removeDependant(decl); | 1811 | dep.removeDependant(decl); |
| 921 | if (dep.dependants.items.len == 0) { | 1812 | if (dep.dependants.items.len == 0 and !dep.deletion_flag) { |
| 922 | // We don't recursively perform a deletion here, because during the update, | 1813 | // We don't recursively perform a deletion here, because during the update, |
| 923 | // another reference to it may turn up. | 1814 | // another reference to it may turn up. |
| 924 | assert(!dep.deletion_flag); | ||
| 925 | dep.deletion_flag = true; | 1815 | dep.deletion_flag = true; |
| 926 | self.deletion_set.appendAssumeCapacity(dep); | 1816 | self.deletion_set.appendAssumeCapacity(dep); |
| 927 | } | 1817 | } |
| ... | @@ -974,83 +1864,89 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { | ... | @@ -974,83 +1864,89 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 974 | } | 1864 | } |
| 975 | 1865 | ||
| 976 | fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | 1866 | fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1867 | const tracy = trace(@src()); | ||
| 1868 | defer tracy.end(); | ||
| 1869 | |||
| 977 | // Use the Decl's arena for function memory. | 1870 | // Use the Decl's arena for function memory. |
| 978 | var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator); | 1871 | var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator); |
| 979 | defer decl.typed_value.most_recent.arena.?.* = arena.state; | 1872 | defer decl.typed_value.most_recent.arena.?.* = arena.state; |
| 980 | var analysis: Fn.Analysis = .{ | 1873 | var inner_block: Scope.Block = .{ |
| 981 | .inner_block = .{ | 1874 | .func = func, |
| 982 | .func = func, | 1875 | .decl = decl, |
| 983 | .decl = decl, | 1876 | .instructions = .{}, |
| 984 | .instructions = .{}, | 1877 | .arena = &arena.allocator, |
| 985 | .arena = &arena.allocator, | ||
| 986 | }, | ||
| 987 | .needed_inst_capacity = 0, | ||
| 988 | .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator), | ||
| 989 | }; | 1878 | }; |
| 990 | defer analysis.inner_block.instructions.deinit(self.allocator); | 1879 | defer inner_block.instructions.deinit(self.allocator); |
| 991 | defer analysis.inst_table.deinit(); | ||
| 992 | 1880 | ||
| 993 | const fn_inst = func.analysis.queued; | 1881 | const fn_zir = func.analysis.queued; |
| 994 | func.analysis = .{ .in_progress = &analysis }; | 1882 | defer fn_zir.arena.promote(self.allocator).deinit(); |
| 1883 | func.analysis = .{ .in_progress = {} }; | ||
| 1884 | //std.debug.warn("set {} to in_progress\n", .{decl.name}); | ||
| 995 | 1885 | ||
| 996 | try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body); | 1886 | try self.analyzeBody(&inner_block.base, fn_zir.body); |
| 997 | 1887 | ||
| 998 | func.analysis = .{ | 1888 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); |
| 999 | .success = .{ | 1889 | func.analysis = .{ .success = .{ .instructions = instructions } }; |
| 1000 | .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items), | 1890 | //std.debug.warn("set {} to success\n", .{decl.name}); |
| 1001 | }, | ||
| 1002 | }; | ||
| 1003 | } | 1891 | } |
| 1004 | 1892 | ||
| 1005 | fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void { | 1893 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { |
| 1006 | switch (decl.analysis) { | 1894 | //std.debug.warn("mark {} outdated\n", .{decl.name}); |
| 1007 | .in_progress => unreachable, | 1895 | try self.work_queue.writeItem(.{ .analyze_decl = decl }); |
| 1008 | .dependency_failure, | 1896 | if (self.failed_decls.remove(decl)) |entry| { |
| 1009 | .sema_failure, | 1897 | entry.value.destroy(self.allocator); |
| 1010 | .codegen_failure, | ||
| 1011 | .codegen_failure_retryable, | ||
| 1012 | .complete, | ||
| 1013 | => return, | ||
| 1014 | |||
| 1015 | .outdated => {}, // Decl re-analysis | ||
| 1016 | } | 1898 | } |
| 1017 | //std.debug.warn("re-analyzing {}\n", .{decl.name}); | 1899 | decl.analysis = .outdated; |
| 1018 | decl.src = old_inst.src; | 1900 | } |
| 1019 | 1901 | ||
| 1020 | // The exports this Decl performs will be re-discovered, so we remove them here | 1902 | fn allocateNewDecl( |
| 1021 | // prior to re-analysis. | 1903 | self: *Module, |
| 1022 | self.deleteDeclExports(decl); | 1904 | scope: *Scope, |
| 1023 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. | 1905 | src_index: usize, |
| 1024 | for (decl.dependencies.items) |dep| { | 1906 | contents_hash: std.zig.SrcHash, |
| 1025 | dep.removeDependant(decl); | 1907 | ) !*Decl { |
| 1026 | if (dep.dependants.items.len == 0) { | 1908 | const new_decl = try self.allocator.create(Decl); |
| 1027 | // We don't perform a deletion here, because this Decl or another one | 1909 | new_decl.* = .{ |
| 1028 | // may end up referencing it before the update is complete. | 1910 | .name = "", |
| 1029 | assert(!dep.deletion_flag); | 1911 | .scope = scope.namespace(), |
| 1030 | dep.deletion_flag = true; | 1912 | .src_index = src_index, |
| 1031 | try self.deletion_set.append(self.allocator, dep); | 1913 | .typed_value = .{ .never_succeeded = {} }, |
| 1032 | } | 1914 | .analysis = .unreferenced, |
| 1033 | } | 1915 | .deletion_flag = false, |
| 1034 | decl.dependencies.shrink(self.allocator, 0); | 1916 | .contents_hash = contents_hash, |
| 1917 | .link = link.ElfFile.TextBlock.empty, | ||
| 1918 | .generation = 0, | ||
| 1919 | }; | ||
| 1920 | return new_decl; | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | fn createNewDecl( | ||
| 1924 | self: *Module, | ||
| 1925 | scope: *Scope, | ||
| 1926 | decl_name: []const u8, | ||
| 1927 | src_index: usize, | ||
| 1928 | name_hash: Scope.NameHash, | ||
| 1929 | contents_hash: std.zig.SrcHash, | ||
| 1930 | ) !*Decl { | ||
| 1931 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); | ||
| 1932 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); | ||
| 1933 | errdefer self.allocator.destroy(new_decl); | ||
| 1934 | new_decl.name = try mem.dupeZ(self.allocator, u8, decl_name); | ||
| 1935 | self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); | ||
| 1936 | return new_decl; | ||
| 1937 | } | ||
| 1938 | |||
| 1939 | fn analyzeZirDecl(self: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool { | ||
| 1035 | var decl_scope: Scope.DeclAnalysis = .{ | 1940 | var decl_scope: Scope.DeclAnalysis = .{ |
| 1036 | .decl = decl, | 1941 | .decl = decl, |
| 1037 | .arena = std.heap.ArenaAllocator.init(self.allocator), | 1942 | .arena = std.heap.ArenaAllocator.init(self.allocator), |
| 1038 | }; | 1943 | }; |
| 1039 | errdefer decl_scope.arena.deinit(); | 1944 | errdefer decl_scope.arena.deinit(); |
| 1040 | 1945 | ||
| 1041 | const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) { | 1946 | decl.analysis = .in_progress; |
| 1042 | error.OutOfMemory => return error.OutOfMemory, | 1947 | |
| 1043 | error.AnalysisFail => { | 1948 | const typed_value = try self.analyzeConstInst(&decl_scope.base, src_decl.inst); |
| 1044 | switch (decl.analysis) { | ||
| 1045 | .in_progress => decl.analysis = .dependency_failure, | ||
| 1046 | else => {}, | ||
| 1047 | } | ||
| 1048 | decl.generation = self.generation; | ||
| 1049 | return error.AnalysisFail; | ||
| 1050 | }, | ||
| 1051 | }; | ||
| 1052 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); | 1949 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1053 | arena_state.* = decl_scope.arena.state; | ||
| 1054 | 1950 | ||
| 1055 | var prev_type_has_bits = false; | 1951 | var prev_type_has_bits = false; |
| 1056 | var type_changed = true; | 1952 | var type_changed = true; |
| ... | @@ -1061,6 +1957,8 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi | ... | @@ -1061,6 +1957,8 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi |
| 1061 | 1957 | ||
| 1062 | tvm.deinit(self.allocator); | 1958 | tvm.deinit(self.allocator); |
| 1063 | } | 1959 | } |
| 1960 | |||
| 1961 | arena_state.* = decl_scope.arena.state; | ||
| 1064 | decl.typed_value = .{ | 1962 | decl.typed_value = .{ |
| 1065 | .most_recent = .{ | 1963 | .most_recent = .{ |
| 1066 | .typed_value = typed_value, | 1964 | .typed_value = typed_value, |
| ... | @@ -1079,137 +1977,66 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi | ... | @@ -1079,137 +1977,66 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi |
| 1079 | self.bin_file.freeDecl(decl); | 1977 | self.bin_file.freeDecl(decl); |
| 1080 | } | 1978 | } |
| 1081 | 1979 | ||
| 1082 | // If the decl is a function, and the type is the same, we do not need | 1980 | return type_changed; |
| 1083 | // to chase the dependants. | ||
| 1084 | if (type_changed or typed_value.val.tag() != .function) { | ||
| 1085 | for (decl.dependants.items) |dep| { | ||
| 1086 | switch (dep.analysis) { | ||
| 1087 | .in_progress => unreachable, | ||
| 1088 | .outdated => continue, // already queued for update | ||
| 1089 | |||
| 1090 | .dependency_failure, | ||
| 1091 | .sema_failure, | ||
| 1092 | .codegen_failure, | ||
| 1093 | .codegen_failure_retryable, | ||
| 1094 | .complete, | ||
| 1095 | => if (dep.generation != self.generation) { | ||
| 1096 | try self.markOutdatedDecl(dep); | ||
| 1097 | }, | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | } | 1981 | } |
| 1102 | 1982 | ||
| 1103 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | 1983 | fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { |
| 1104 | //std.debug.warn("mark {} outdated\n", .{decl.name}); | 1984 | const zir_module = self.root_scope.cast(Scope.ZIRModule).?; |
| 1105 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); | 1985 | const entry = zir_module.contents.module.findDecl(src_decl.name).?; |
| 1106 | if (self.failed_decls.remove(decl)) |entry| { | 1986 | return self.resolveZirDeclHavingIndex(scope, src_decl, entry.index); |
| 1107 | entry.value.destroy(self.allocator); | ||
| 1108 | } | ||
| 1109 | decl.analysis = .outdated; | ||
| 1110 | } | 1987 | } |
| 1111 | 1988 | ||
| 1112 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { | 1989 | fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { |
| 1113 | const hash = Decl.hashSimpleName(old_inst.name); | 1990 | const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); |
| 1114 | if (self.decl_table.get(hash)) |kv| { | 1991 | const decl = self.decl_table.getValue(name_hash).?; |
| 1115 | const decl = kv.value; | 1992 | decl.src_index = src_index; |
| 1116 | try self.reAnalyzeDecl(decl, old_inst); | 1993 | try self.ensureDeclAnalyzed(decl); |
| 1117 | return decl; | 1994 | return decl; |
| 1118 | } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| { | ||
| 1119 | // This is just a named reference to another decl. | ||
| 1120 | return self.analyzeDeclVal(scope, decl_val); | ||
| 1121 | } else { | ||
| 1122 | const new_decl = blk: { | ||
| 1123 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); | ||
| 1124 | const new_decl = try self.allocator.create(Decl); | ||
| 1125 | errdefer self.allocator.destroy(new_decl); | ||
| 1126 | const name = try mem.dupeZ(self.allocator, u8, old_inst.name); | ||
| 1127 | errdefer self.allocator.free(name); | ||
| 1128 | new_decl.* = .{ | ||
| 1129 | .name = name, | ||
| 1130 | .scope = scope.namespace(), | ||
| 1131 | .src = old_inst.src, | ||
| 1132 | .typed_value = .{ .never_succeeded = {} }, | ||
| 1133 | .analysis = .in_progress, | ||
| 1134 | .deletion_flag = false, | ||
| 1135 | .contents_hash = Decl.hashSimpleName(old_inst.contents), | ||
| 1136 | .link = link.ElfFile.TextBlock.empty, | ||
| 1137 | .generation = 0, | ||
| 1138 | }; | ||
| 1139 | self.decl_table.putAssumeCapacityNoClobber(hash, new_decl); | ||
| 1140 | break :blk new_decl; | ||
| 1141 | }; | ||
| 1142 | |||
| 1143 | var decl_scope: Scope.DeclAnalysis = .{ | ||
| 1144 | .decl = new_decl, | ||
| 1145 | .arena = std.heap.ArenaAllocator.init(self.allocator), | ||
| 1146 | }; | ||
| 1147 | errdefer decl_scope.arena.deinit(); | ||
| 1148 | |||
| 1149 | const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) { | ||
| 1150 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1151 | error.AnalysisFail => { | ||
| 1152 | switch (new_decl.analysis) { | ||
| 1153 | .in_progress => new_decl.analysis = .dependency_failure, | ||
| 1154 | else => {}, | ||
| 1155 | } | ||
| 1156 | new_decl.generation = self.generation; | ||
| 1157 | return error.AnalysisFail; | ||
| 1158 | }, | ||
| 1159 | }; | ||
| 1160 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1161 | |||
| 1162 | arena_state.* = decl_scope.arena.state; | ||
| 1163 | |||
| 1164 | new_decl.typed_value = .{ | ||
| 1165 | .most_recent = .{ | ||
| 1166 | .typed_value = typed_value, | ||
| 1167 | .arena = arena_state, | ||
| 1168 | }, | ||
| 1169 | }; | ||
| 1170 | new_decl.analysis = .complete; | ||
| 1171 | new_decl.generation = self.generation; | ||
| 1172 | if (typed_value.ty.hasCodeGenBits()) { | ||
| 1173 | // We don't fully codegen the decl until later, but we do need to reserve a global | ||
| 1174 | // offset table index for it. This allows us to codegen decls out of dependency order, | ||
| 1175 | // increasing how many computations can be done in parallel. | ||
| 1176 | try self.bin_file.allocateDeclIndexes(new_decl); | ||
| 1177 | try self.work_queue.writeItem(.{ .codegen_decl = new_decl }); | ||
| 1178 | } | ||
| 1179 | return new_decl; | ||
| 1180 | } | ||
| 1181 | } | 1995 | } |
| 1182 | 1996 | ||
| 1183 | /// Declares a dependency on the decl. | 1997 | /// Declares a dependency on the decl. |
| 1184 | fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { | 1998 | fn resolveCompleteZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { |
| 1185 | const decl = try self.resolveDecl(scope, old_inst); | 1999 | const decl = try self.resolveZirDecl(scope, src_decl); |
| 1186 | switch (decl.analysis) { | 2000 | switch (decl.analysis) { |
| 2001 | .unreferenced => unreachable, | ||
| 1187 | .in_progress => unreachable, | 2002 | .in_progress => unreachable, |
| 1188 | .outdated => unreachable, | 2003 | .outdated => unreachable, |
| 1189 | 2004 | ||
| 1190 | .dependency_failure, | 2005 | .dependency_failure, |
| 1191 | .sema_failure, | 2006 | .sema_failure, |
| 2007 | .sema_failure_retryable, | ||
| 1192 | .codegen_failure, | 2008 | .codegen_failure, |
| 1193 | .codegen_failure_retryable, | 2009 | .codegen_failure_retryable, |
| 1194 | => return error.AnalysisFail, | 2010 | => return error.AnalysisFail, |
| 1195 | 2011 | ||
| 1196 | .complete => {}, | 2012 | .complete => {}, |
| 1197 | } | 2013 | } |
| 1198 | if (scope.decl()) |scope_decl| { | ||
| 1199 | try self.declareDeclDependency(scope_decl, decl); | ||
| 1200 | } | ||
| 1201 | return decl; | 2014 | return decl; |
| 1202 | } | 2015 | } |
| 1203 | 2016 | ||
| 2017 | /// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. | ||
| 1204 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | 2018 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { |
| 1205 | if (scope.cast(Scope.Block)) |block| { | 2019 | if (old_inst.analyzed_inst) |inst| return inst; |
| 1206 | if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| { | 2020 | |
| 1207 | return kv.value; | 2021 | // If this assert trips, the instruction that was referenced did not get properly |
| 1208 | } | 2022 | // analyzed before it was referenced. |
| 1209 | } | 2023 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; |
| 1210 | 2024 | const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: { | |
| 1211 | const decl = try self.resolveCompleteDecl(scope, old_inst); | 2025 | const decl_name = declval.positionals.name; |
| 2026 | const entry = zir_module.contents.module.findDecl(decl_name) orelse | ||
| 2027 | return self.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name}); | ||
| 2028 | break :blk entry; | ||
| 2029 | } else blk: { | ||
| 2030 | // If this assert trips, the instruction that was referenced did not get | ||
| 2031 | // properly analyzed by a previous instruction analysis before it was | ||
| 2032 | // referenced by the current one. | ||
| 2033 | break :blk zir_module.contents.module.findInstDecl(old_inst).?; | ||
| 2034 | }; | ||
| 2035 | const decl = try self.resolveCompleteZirDecl(scope, entry.decl); | ||
| 1212 | const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl); | 2036 | const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl); |
| 2037 | // Note: it would be tempting here to store the result into old_inst.analyzed_inst field, | ||
| 2038 | // but this would prevent the analyzeDeclRef from happening, which is needed to properly | ||
| 2039 | // detect Decl dependencies and dependency failures on updates. | ||
| 1213 | return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); | 2040 | return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); |
| 1214 | } | 2041 | } |
| 1215 | 2042 | ||
| ... | @@ -1258,21 +2085,16 @@ fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { | ... | @@ -1258,21 +2085,16 @@ fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { |
| 1258 | return val.toType(); | 2085 | return val.toType(); |
| 1259 | } | 2086 | } |
| 1260 | 2087 | ||
| 1261 | fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void { | 2088 | fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void { |
| 1262 | try self.decl_exports.ensureCapacity(self.decl_exports.size + 1); | 2089 | try self.ensureDeclAnalyzed(exported_decl); |
| 1263 | try self.export_owners.ensureCapacity(self.export_owners.size + 1); | ||
| 1264 | const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name); | ||
| 1265 | const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value); | ||
| 1266 | const typed_value = exported_decl.typed_value.most_recent.typed_value; | 2090 | const typed_value = exported_decl.typed_value.most_recent.typed_value; |
| 1267 | switch (typed_value.ty.zigTypeTag()) { | 2091 | switch (typed_value.ty.zigTypeTag()) { |
| 1268 | .Fn => {}, | 2092 | .Fn => {}, |
| 1269 | else => return self.fail( | 2093 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), |
| 1270 | scope, | ||
| 1271 | export_inst.positionals.value.src, | ||
| 1272 | "unable to export type '{}'", | ||
| 1273 | .{typed_value.ty}, | ||
| 1274 | ), | ||
| 1275 | } | 2094 | } |
| 2095 | try self.decl_exports.ensureCapacity(self.decl_exports.size + 1); | ||
| 2096 | try self.export_owners.ensureCapacity(self.export_owners.size + 1); | ||
| 2097 | |||
| 1276 | const new_export = try self.allocator.create(Export); | 2098 | const new_export = try self.allocator.create(Export); |
| 1277 | errdefer self.allocator.destroy(new_export); | 2099 | errdefer self.allocator.destroy(new_export); |
| 1278 | 2100 | ||
| ... | @@ -1280,7 +2102,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In | ... | @@ -1280,7 +2102,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In |
| 1280 | 2102 | ||
| 1281 | new_export.* = .{ | 2103 | new_export.* = .{ |
| 1282 | .options = .{ .name = symbol_name }, | 2104 | .options = .{ .name = symbol_name }, |
| 1283 | .src = export_inst.base.src, | 2105 | .src = src, |
| 1284 | .link = .{}, | 2106 | .link = .{}, |
| 1285 | .owner_decl = owner_decl, | 2107 | .owner_decl = owner_decl, |
| 1286 | .exported_decl = exported_decl, | 2108 | .exported_decl = exported_decl, |
| ... | @@ -1311,7 +2133,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In | ... | @@ -1311,7 +2133,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In |
| 1311 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); | 2133 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); |
| 1312 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( | 2134 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 1313 | self.allocator, | 2135 | self.allocator, |
| 1314 | export_inst.base.src, | 2136 | src, |
| 1315 | "unable to export: {}", | 2137 | "unable to export: {}", |
| 1316 | .{@errorName(err)}, | 2138 | .{@errorName(err)}, |
| 1317 | )); | 2139 | )); |
| ... | @@ -1320,7 +2142,6 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In | ... | @@ -1320,7 +2142,6 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In |
| 1320 | }; | 2142 | }; |
| 1321 | } | 2143 | } |
| 1322 | 2144 | ||
| 1323 | /// TODO should not need the cast on the last parameter at the callsites | ||
| 1324 | fn addNewInstArgs( | 2145 | fn addNewInstArgs( |
| 1325 | self: *Module, | 2146 | self: *Module, |
| 1326 | block: *Scope.Block, | 2147 | block: *Scope.Block, |
| ... | @@ -1334,6 +2155,46 @@ fn addNewInstArgs( | ... | @@ -1334,6 +2155,46 @@ fn addNewInstArgs( |
| 1334 | return &inst.base; | 2155 | return &inst.base; |
| 1335 | } | 2156 | } |
| 1336 | 2157 | ||
| 2158 | fn newZIRInst( | ||
| 2159 | allocator: *Allocator, | ||
| 2160 | src: usize, | ||
| 2161 | comptime T: type, | ||
| 2162 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | ||
| 2163 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | ||
| 2164 | ) !*zir.Inst { | ||
| 2165 | const inst = try allocator.create(T); | ||
| 2166 | inst.* = .{ | ||
| 2167 | .base = .{ | ||
| 2168 | .tag = T.base_tag, | ||
| 2169 | .src = src, | ||
| 2170 | }, | ||
| 2171 | .positionals = positionals, | ||
| 2172 | .kw_args = kw_args, | ||
| 2173 | }; | ||
| 2174 | return &inst.base; | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | fn addZIRInst( | ||
| 2178 | self: *Module, | ||
| 2179 | scope: *Scope, | ||
| 2180 | src: usize, | ||
| 2181 | comptime T: type, | ||
| 2182 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | ||
| 2183 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | ||
| 2184 | ) !*zir.Inst { | ||
| 2185 | const gen_zir = scope.cast(Scope.GenZIR).?; | ||
| 2186 | try gen_zir.instructions.ensureCapacity(gen_zir.instructions.items.len + 1); | ||
| 2187 | const inst = try newZIRInst(&gen_zir.arena.allocator, src, T, positionals, kw_args); | ||
| 2188 | gen_zir.instructions.appendAssumeCapacity(inst); | ||
| 2189 | return inst; | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | /// TODO The existence of this function is a workaround for a bug in stage1. | ||
| 2193 | fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst { | ||
| 2194 | const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type; | ||
| 2195 | return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{}); | ||
| 2196 | } | ||
| 2197 | |||
| 1337 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { | 2198 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { |
| 1338 | const inst = try block.arena.create(T); | 2199 | const inst = try block.arena.create(T); |
| 1339 | inst.* = .{ | 2200 | inst.* = .{ |
| ... | @@ -1361,19 +2222,6 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) | ... | @@ -1361,19 +2222,6 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) |
| 1361 | return &const_inst.base; | 2222 | return &const_inst.base; |
| 1362 | } | 2223 | } |
| 1363 | 2224 | ||
| 1364 | fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst { | ||
| 1365 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | ||
| 1366 | ty_payload.* = .{ .len = str.len }; | ||
| 1367 | |||
| 1368 | const bytes_payload = try scope.arena().create(Value.Payload.Bytes); | ||
| 1369 | bytes_payload.* = .{ .data = str }; | ||
| 1370 | |||
| 1371 | return self.constInst(scope, src, .{ | ||
| 1372 | .ty = Type.initPayload(&ty_payload.base), | ||
| 1373 | .val = Value.initPayload(&bytes_payload.base), | ||
| 1374 | }); | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | 2225 | fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { |
| 1378 | return self.constInst(scope, src, .{ | 2226 | return self.constInst(scope, src, .{ |
| 1379 | .ty = Type.initTag(.type), | 2227 | .ty = Type.initTag(.type), |
| ... | @@ -1388,6 +2236,13 @@ fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | ... | @@ -1388,6 +2236,13 @@ fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { |
| 1388 | }); | 2236 | }); |
| 1389 | } | 2237 | } |
| 1390 | 2238 | ||
| 2239 | fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | ||
| 2240 | return self.constInst(scope, src, .{ | ||
| 2241 | .ty = Type.initTag(.noreturn), | ||
| 2242 | .val = Value.initTag(.the_one_possible_value), | ||
| 2243 | }); | ||
| 2244 | } | ||
| 2245 | |||
| 1391 | fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | 2246 | fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { |
| 1392 | return self.constInst(scope, src, .{ | 2247 | return self.constInst(scope, src, .{ |
| 1393 | .ty = ty, | 2248 | .ty = ty, |
| ... | @@ -1451,7 +2306,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI | ... | @@ -1451,7 +2306,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI |
| 1451 | }); | 2306 | }); |
| 1452 | } | 2307 | } |
| 1453 | 2308 | ||
| 1454 | fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { | 2309 | fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { |
| 1455 | const new_inst = try self.analyzeInst(scope, old_inst); | 2310 | const new_inst = try self.analyzeInst(scope, old_inst); |
| 1456 | return TypedValue{ | 2311 | return TypedValue{ |
| 1457 | .ty = new_inst.ty, | 2312 | .ty = new_inst.ty, |
| ... | @@ -1459,20 +2314,24 @@ fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerErro | ... | @@ -1459,20 +2314,24 @@ fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerErro |
| 1459 | }; | 2314 | }; |
| 1460 | } | 2315 | } |
| 1461 | 2316 | ||
| 2317 | fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { | ||
| 2318 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | ||
| 2319 | // after analysis. | ||
| 2320 | const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena()); | ||
| 2321 | return self.constInst(scope, const_inst.base.src, typed_value_copy); | ||
| 2322 | } | ||
| 2323 | |||
| 1462 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | 2324 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { |
| 1463 | switch (old_inst.tag) { | 2325 | switch (old_inst.tag) { |
| 1464 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), | 2326 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), |
| 1465 | .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), | 2327 | .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), |
| 1466 | .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?), | 2328 | .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?), |
| 2329 | .@"const" => return self.analyzeInstConst(scope, old_inst.cast(zir.Inst.Const).?), | ||
| 1467 | .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?), | 2330 | .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?), |
| 2331 | .declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.cast(zir.Inst.DeclRefStr).?), | ||
| 1468 | .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?), | 2332 | .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?), |
| 1469 | .str => { | 2333 | .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.cast(zir.Inst.DeclValInModule).?), |
| 1470 | const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes; | 2334 | .str => return self.analyzeInstStr(scope, old_inst.cast(zir.Inst.Str).?), |
| 1471 | // The bytes references memory inside the ZIR module, which can get deallocated | ||
| 1472 | // after semantic analysis is complete. We need the memory to be in the Decl's arena. | ||
| 1473 | const arena_bytes = try scope.arena().dupe(u8, bytes); | ||
| 1474 | return self.constStr(scope, old_inst.src, arena_bytes); | ||
| 1475 | }, | ||
| 1476 | .int => { | 2335 | .int => { |
| 1477 | const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int; | 2336 | const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int; |
| 1478 | return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); | 2337 | return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); |
| ... | @@ -1484,13 +2343,10 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In | ... | @@ -1484,13 +2343,10 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 1484 | .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?), | 2343 | .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?), |
| 1485 | .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?), | 2344 | .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?), |
| 1486 | .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?), | 2345 | .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?), |
| 2346 | .returnvoid => return self.analyzeInstRetVoid(scope, old_inst.cast(zir.Inst.ReturnVoid).?), | ||
| 1487 | .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?), | 2347 | .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?), |
| 1488 | .@"export" => { | 2348 | .@"export" => return self.analyzeInstExport(scope, old_inst.cast(zir.Inst.Export).?), |
| 1489 | try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?); | ||
| 1490 | return self.constVoid(scope, old_inst.src); | ||
| 1491 | }, | ||
| 1492 | .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?), | 2349 | .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?), |
| 1493 | .ref => return self.analyzeInstRef(scope, old_inst.cast(zir.Inst.Ref).?), | ||
| 1494 | .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?), | 2350 | .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?), |
| 1495 | .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?), | 2351 | .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?), |
| 1496 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?), | 2352 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?), |
| ... | @@ -1503,39 +2359,104 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In | ... | @@ -1503,39 +2359,104 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 1503 | } | 2359 | } |
| 1504 | } | 2360 | } |
| 1505 | 2361 | ||
| 2362 | fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { | ||
| 2363 | // The bytes references memory inside the ZIR module, which can get deallocated | ||
| 2364 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. | ||
| 2365 | var new_decl_arena = std.heap.ArenaAllocator.init(self.allocator); | ||
| 2366 | const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); | ||
| 2367 | |||
| 2368 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | ||
| 2369 | ty_payload.* = .{ .len = arena_bytes.len }; | ||
| 2370 | |||
| 2371 | const bytes_payload = try scope.arena().create(Value.Payload.Bytes); | ||
| 2372 | bytes_payload.* = .{ .data = arena_bytes }; | ||
| 2373 | |||
| 2374 | const new_decl = try self.createAnonymousDecl(scope, &new_decl_arena, .{ | ||
| 2375 | .ty = Type.initPayload(&ty_payload.base), | ||
| 2376 | .val = Value.initPayload(&bytes_payload.base), | ||
| 2377 | }); | ||
| 2378 | return self.analyzeDeclRef(scope, str_inst.base.src, new_decl); | ||
| 2379 | } | ||
| 2380 | |||
| 2381 | fn createAnonymousDecl( | ||
| 2382 | self: *Module, | ||
| 2383 | scope: *Scope, | ||
| 2384 | decl_arena: *std.heap.ArenaAllocator, | ||
| 2385 | typed_value: TypedValue, | ||
| 2386 | ) !*Decl { | ||
| 2387 | const name_index = self.getNextAnonNameIndex(); | ||
| 2388 | const scope_decl = scope.decl().?; | ||
| 2389 | const name = try std.fmt.allocPrint(self.allocator, "{}${}", .{ scope_decl.name, name_index }); | ||
| 2390 | defer self.allocator.free(name); | ||
| 2391 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); | ||
| 2392 | const src_hash: std.zig.SrcHash = undefined; | ||
| 2393 | const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | ||
| 2394 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 2395 | |||
| 2396 | decl_arena_state.* = decl_arena.state; | ||
| 2397 | new_decl.typed_value = .{ | ||
| 2398 | .most_recent = .{ | ||
| 2399 | .typed_value = typed_value, | ||
| 2400 | .arena = decl_arena_state, | ||
| 2401 | }, | ||
| 2402 | }; | ||
| 2403 | new_decl.analysis = .complete; | ||
| 2404 | new_decl.generation = self.generation; | ||
| 2405 | |||
| 2406 | // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size. | ||
| 2407 | // We should be able to further improve the compiler to not omit Decls which are only referenced at | ||
| 2408 | // compile-time and not runtime. | ||
| 2409 | if (typed_value.ty.hasCodeGenBits()) { | ||
| 2410 | try self.bin_file.allocateDeclIndexes(new_decl); | ||
| 2411 | try self.work_queue.writeItem(.{ .codegen_decl = new_decl }); | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | return new_decl; | ||
| 2415 | } | ||
| 2416 | |||
| 2417 | fn getNextAnonNameIndex(self: *Module) usize { | ||
| 2418 | return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic); | ||
| 2419 | } | ||
| 2420 | |||
| 2421 | fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | ||
| 2422 | const namespace = scope.namespace(); | ||
| 2423 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); | ||
| 2424 | return self.decl_table.getValue(name_hash); | ||
| 2425 | } | ||
| 2426 | |||
| 2427 | fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { | ||
| 2428 | const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name); | ||
| 2429 | const exported_decl = self.lookupDeclName(scope, export_inst.positionals.decl_name) orelse | ||
| 2430 | return self.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name}); | ||
| 2431 | try self.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl); | ||
| 2432 | return self.constVoid(scope, export_inst.base.src); | ||
| 2433 | } | ||
| 2434 | |||
| 1506 | fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { | 2435 | fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { |
| 1507 | return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); | 2436 | return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); |
| 1508 | } | 2437 | } |
| 1509 | 2438 | ||
| 1510 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { | 2439 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { |
| 1511 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | 2440 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 1512 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); | 2441 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {}); |
| 1513 | } | 2442 | } |
| 1514 | 2443 | ||
| 1515 | fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst { | 2444 | fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { |
| 1516 | const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand); | 2445 | const decl_name = try self.resolveConstString(scope, inst.positionals.name); |
| 1517 | return self.analyzeDeclRef(scope, inst.base.src, decl); | 2446 | return self.analyzeDeclRefByName(scope, inst.base.src, decl_name); |
| 1518 | } | 2447 | } |
| 1519 | 2448 | ||
| 1520 | fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { | 2449 | fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { |
| 1521 | const decl_name = try self.resolveConstString(scope, inst.positionals.name); | 2450 | return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); |
| 1522 | // This will need to get more fleshed out when there are proper structs & namespaces. | ||
| 1523 | const zir_module = scope.namespace(); | ||
| 1524 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse | ||
| 1525 | return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name}); | ||
| 1526 | |||
| 1527 | const decl = try self.resolveCompleteDecl(scope, src_decl); | ||
| 1528 | return self.analyzeDeclRef(scope, inst.base.src, decl); | ||
| 1529 | } | 2451 | } |
| 1530 | 2452 | ||
| 1531 | fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { | 2453 | fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { |
| 1532 | const decl_name = inst.positionals.name; | 2454 | const decl_name = inst.positionals.name; |
| 1533 | // This will need to get more fleshed out when there are proper structs & namespaces. | 2455 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; |
| 1534 | const zir_module = scope.namespace(); | ||
| 1535 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse | 2456 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse |
| 1536 | return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); | 2457 | return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); |
| 1537 | 2458 | ||
| 1538 | const decl = try self.resolveCompleteDecl(scope, src_decl); | 2459 | const decl = try self.resolveCompleteZirDecl(scope, src_decl.decl); |
| 1539 | 2460 | ||
| 1540 | return decl; | 2461 | return decl; |
| 1541 | } | 2462 | } |
| ... | @@ -1546,18 +2467,46 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn | ... | @@ -1546,18 +2467,46 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn |
| 1546 | return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | 2467 | return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); |
| 1547 | } | 2468 | } |
| 1548 | 2469 | ||
| 2470 | fn analyzeInstDeclValInModule(self: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst { | ||
| 2471 | const decl = inst.positionals.decl; | ||
| 2472 | const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl); | ||
| 2473 | return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | ||
| 2474 | } | ||
| 2475 | |||
| 1549 | fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | 2476 | fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { |
| 2477 | const scope_decl = scope.decl().?; | ||
| 2478 | try self.declareDeclDependency(scope_decl, decl); | ||
| 2479 | self.ensureDeclAnalyzed(decl) catch |err| { | ||
| 2480 | if (scope.cast(Scope.Block)) |block| { | ||
| 2481 | if (block.func) |func| { | ||
| 2482 | func.analysis = .dependency_failure; | ||
| 2483 | } else { | ||
| 2484 | block.decl.analysis = .dependency_failure; | ||
| 2485 | } | ||
| 2486 | } else { | ||
| 2487 | scope_decl.analysis = .dependency_failure; | ||
| 2488 | } | ||
| 2489 | return err; | ||
| 2490 | }; | ||
| 2491 | |||
| 1550 | const decl_tv = try decl.typedValue(); | 2492 | const decl_tv = try decl.typedValue(); |
| 1551 | const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); | 2493 | const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); |
| 1552 | ty_payload.* = .{ .pointee_type = decl_tv.ty }; | 2494 | ty_payload.* = .{ .pointee_type = decl_tv.ty }; |
| 1553 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); | 2495 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); |
| 1554 | val_payload.* = .{ .decl = decl }; | 2496 | val_payload.* = .{ .decl = decl }; |
| 2497 | |||
| 1555 | return self.constInst(scope, src, .{ | 2498 | return self.constInst(scope, src, .{ |
| 1556 | .ty = Type.initPayload(&ty_payload.base), | 2499 | .ty = Type.initPayload(&ty_payload.base), |
| 1557 | .val = Value.initPayload(&val_payload.base), | 2500 | .val = Value.initPayload(&val_payload.base), |
| 1558 | }); | 2501 | }); |
| 1559 | } | 2502 | } |
| 1560 | 2503 | ||
| 2504 | fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { | ||
| 2505 | const decl = self.lookupDeclName(scope, decl_name) orelse | ||
| 2506 | return self.fail(scope, src, "decl '{}' not found", .{decl_name}); | ||
| 2507 | return self.analyzeDeclRef(scope, src, decl); | ||
| 2508 | } | ||
| 2509 | |||
| 1561 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | 2510 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { |
| 1562 | const func = try self.resolveInst(scope, inst.positionals.func); | 2511 | const func = try self.resolveInst(scope, inst.positionals.func); |
| 1563 | if (func.ty.zigTypeTag() != .Fn) | 2512 | if (func.ty.zigTypeTag() != .Fn) |
| ... | @@ -1616,7 +2565,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro | ... | @@ -1616,7 +2565,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro |
| 1616 | } | 2565 | } |
| 1617 | 2566 | ||
| 1618 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | 2567 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 1619 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){ | 2568 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{ |
| 1620 | .func = func, | 2569 | .func = func, |
| 1621 | .args = casted_args, | 2570 | .args = casted_args, |
| 1622 | }); | 2571 | }); |
| ... | @@ -1624,10 +2573,22 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro | ... | @@ -1624,10 +2573,22 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro |
| 1624 | 2573 | ||
| 1625 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { | 2574 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { |
| 1626 | const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type); | 2575 | const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type); |
| 2576 | const fn_zir = blk: { | ||
| 2577 | var fn_arena = std.heap.ArenaAllocator.init(self.allocator); | ||
| 2578 | errdefer fn_arena.deinit(); | ||
| 2579 | |||
| 2580 | const fn_zir = try scope.arena().create(Fn.ZIR); | ||
| 2581 | fn_zir.* = .{ | ||
| 2582 | .body = .{ | ||
| 2583 | .instructions = fn_inst.positionals.body.instructions, | ||
| 2584 | }, | ||
| 2585 | .arena = fn_arena.state, | ||
| 2586 | }; | ||
| 2587 | break :blk fn_zir; | ||
| 2588 | }; | ||
| 1627 | const new_func = try scope.arena().create(Fn); | 2589 | const new_func = try scope.arena().create(Fn); |
| 1628 | new_func.* = .{ | 2590 | new_func.* = .{ |
| 1629 | .fn_type = fn_type, | 2591 | .analysis = .{ .queued = fn_zir }, |
| 1630 | .analysis = .{ .queued = fn_inst }, | ||
| 1631 | .owner_decl = scope.decl().?, | 2592 | .owner_decl = scope.decl().?, |
| 1632 | }; | 2593 | }; |
| 1633 | const fn_payload = try scope.arena().create(Value.Payload.Function); | 2594 | const fn_payload = try scope.arena().create(Value.Payload.Function); |
| ... | @@ -1648,6 +2609,13 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn | ... | @@ -1648,6 +2609,13 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn |
| 1648 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); | 2609 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); |
| 1649 | } | 2610 | } |
| 1650 | 2611 | ||
| 2612 | if (return_type.zigTypeTag() == .Void and | ||
| 2613 | fntype.positionals.param_types.len == 0 and | ||
| 2614 | fntype.kw_args.cc == .Unspecified) | ||
| 2615 | { | ||
| 2616 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); | ||
| 2617 | } | ||
| 2618 | |||
| 1651 | if (return_type.zigTypeTag() == .NoReturn and | 2619 | if (return_type.zigTypeTag() == .NoReturn and |
| 1652 | fntype.positionals.param_types.len == 0 and | 2620 | fntype.positionals.param_types.len == 0 and |
| 1653 | fntype.kw_args.cc == .Naked) | 2621 | fntype.kw_args.cc == .Naked) |
| ... | @@ -1683,7 +2651,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn | ... | @@ -1683,7 +2651,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn |
| 1683 | // TODO handle known-pointer-address | 2651 | // TODO handle known-pointer-address |
| 1684 | const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src); | 2652 | const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src); |
| 1685 | const ty = Type.initTag(.usize); | 2653 | const ty = Type.initTag(.usize); |
| 1686 | return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); | 2654 | return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr }); |
| 1687 | } | 2655 | } |
| 1688 | 2656 | ||
| 1689 | fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { | 2657 | fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { |
| ... | @@ -1875,7 +2843,7 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr | ... | @@ -1875,7 +2843,7 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr |
| 1875 | } | 2843 | } |
| 1876 | 2844 | ||
| 1877 | const b = try self.requireRuntimeBlock(scope, assembly.base.src); | 2845 | const b = try self.requireRuntimeBlock(scope, assembly.base.src); |
| 1878 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | 2846 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{ |
| 1879 | .asm_source = asm_source, | 2847 | .asm_source = asm_source, |
| 1880 | .is_volatile = assembly.kw_args.@"volatile", | 2848 | .is_volatile = assembly.kw_args.@"volatile", |
| 1881 | .output = output, | 2849 | .output = output, |
| ... | @@ -1911,20 +2879,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError! | ... | @@ -1911,20 +2879,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError! |
| 1911 | } | 2879 | } |
| 1912 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | 2880 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 1913 | switch (op) { | 2881 | switch (op) { |
| 1914 | .eq => return self.addNewInstArgs( | 2882 | .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{ |
| 1915 | b, | 2883 | .operand = opt_operand, |
| 1916 | inst.base.src, | 2884 | }), |
| 1917 | Type.initTag(.bool), | 2885 | .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{ |
| 1918 | Inst.IsNull, | 2886 | .operand = opt_operand, |
| 1919 | Inst.Args(Inst.IsNull){ .operand = opt_operand }, | 2887 | }), |
| 1920 | ), | ||
| 1921 | .neq => return self.addNewInstArgs( | ||
| 1922 | b, | ||
| 1923 | inst.base.src, | ||
| 1924 | Type.initTag(.bool), | ||
| 1925 | Inst.IsNonNull, | ||
| 1926 | Inst.Args(Inst.IsNonNull){ .operand = opt_operand }, | ||
| 1927 | ), | ||
| 1928 | else => unreachable, | 2888 | else => unreachable, |
| 1929 | } | 2889 | } |
| 1930 | } else if (is_equality_cmp and | 2890 | } else if (is_equality_cmp and |
| ... | @@ -2019,23 +2979,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea | ... | @@ -2019,23 +2979,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea |
| 2019 | } | 2979 | } |
| 2020 | 2980 | ||
| 2021 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst { | 2981 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst { |
| 2982 | const operand = try self.resolveInst(scope, inst.positionals.operand); | ||
| 2983 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | ||
| 2984 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand }); | ||
| 2985 | } | ||
| 2986 | |||
| 2987 | fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst { | ||
| 2022 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | 2988 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 2023 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {}); | 2989 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {}); |
| 2024 | } | 2990 | } |
| 2025 | 2991 | ||
| 2026 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { | 2992 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { |
| 2027 | if (scope.cast(Scope.Block)) |b| { | 2993 | for (body.instructions) |src_inst| { |
| 2028 | const analysis = b.func.analysis.in_progress; | 2994 | src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst); |
| 2029 | analysis.needed_inst_capacity += body.instructions.len; | ||
| 2030 | try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity); | ||
| 2031 | for (body.instructions) |src_inst| { | ||
| 2032 | const new_inst = try self.analyzeInst(scope, src_inst); | ||
| 2033 | analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst); | ||
| 2034 | } | ||
| 2035 | } else { | ||
| 2036 | for (body.instructions) |src_inst| { | ||
| 2037 | _ = try self.analyzeInst(scope, src_inst); | ||
| 2038 | } | ||
| 2039 | } | 2995 | } |
| 2040 | } | 2996 | } |
| 2041 | 2997 | ||
| ... | @@ -2118,7 +3074,7 @@ fn cmpNumeric( | ... | @@ -2118,7 +3074,7 @@ fn cmpNumeric( |
| 2118 | }; | 3074 | }; |
| 2119 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | 3075 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 2120 | const casted_rhs = try self.coerce(scope, dest_type, rhs); | 3076 | const casted_rhs = try self.coerce(scope, dest_type, rhs); |
| 2121 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | 3077 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{ |
| 2122 | .lhs = casted_lhs, | 3078 | .lhs = casted_lhs, |
| 2123 | .rhs = casted_rhs, | 3079 | .rhs = casted_rhs, |
| 2124 | .op = op, | 3080 | .op = op, |
| ... | @@ -2222,7 +3178,7 @@ fn cmpNumeric( | ... | @@ -2222,7 +3178,7 @@ fn cmpNumeric( |
| 2222 | const casted_lhs = try self.coerce(scope, dest_type, lhs); | 3178 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 2223 | const casted_rhs = try self.coerce(scope, dest_type, lhs); | 3179 | const casted_rhs = try self.coerce(scope, dest_type, lhs); |
| 2224 | 3180 | ||
| 2225 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | 3181 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{ |
| 2226 | .lhs = casted_lhs, | 3182 | .lhs = casted_lhs, |
| 2227 | .rhs = casted_rhs, | 3183 | .rhs = casted_rhs, |
| 2228 | .op = op, | 3184 | .op = op, |
| ... | @@ -2299,7 +3255,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | ... | @@ -2299,7 +3255,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 2299 | } | 3255 | } |
| 2300 | // TODO validate the type size and other compile errors | 3256 | // TODO validate the type size and other compile errors |
| 2301 | const b = try self.requireRuntimeBlock(scope, inst.src); | 3257 | const b = try self.requireRuntimeBlock(scope, inst.src); |
| 2302 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | 3258 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst }); |
| 2303 | } | 3259 | } |
| 2304 | 3260 | ||
| 2305 | fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | 3261 | fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| ... | @@ -2316,6 +3272,30 @@ fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, a | ... | @@ -2316,6 +3272,30 @@ fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, a |
| 2316 | return self.failWithOwnedErrorMsg(scope, src, err_msg); | 3272 | return self.failWithOwnedErrorMsg(scope, src, err_msg); |
| 2317 | } | 3273 | } |
| 2318 | 3274 | ||
| 3275 | fn failTok( | ||
| 3276 | self: *Module, | ||
| 3277 | scope: *Scope, | ||
| 3278 | token_index: ast.TokenIndex, | ||
| 3279 | comptime format: []const u8, | ||
| 3280 | args: var, | ||
| 3281 | ) InnerError { | ||
| 3282 | @setCold(true); | ||
| 3283 | const src = scope.tree().token_locs[token_index].start; | ||
| 3284 | return self.fail(scope, src, format, args); | ||
| 3285 | } | ||
| 3286 | |||
| 3287 | fn failNode( | ||
| 3288 | self: *Module, | ||
| 3289 | scope: *Scope, | ||
| 3290 | ast_node: *ast.Node, | ||
| 3291 | comptime format: []const u8, | ||
| 3292 | args: var, | ||
| 3293 | ) InnerError { | ||
| 3294 | @setCold(true); | ||
| 3295 | const src = scope.tree().token_locs[ast_node.firstToken()].start; | ||
| 3296 | return self.fail(scope, src, format, args); | ||
| 3297 | } | ||
| 3298 | |||
| 2319 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { | 3299 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { |
| 2320 | { | 3300 | { |
| 2321 | errdefer err_msg.destroy(self.allocator); | 3301 | errdefer err_msg.destroy(self.allocator); |
| ... | @@ -2326,18 +3306,31 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err | ... | @@ -2326,18 +3306,31 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err |
| 2326 | .decl => { | 3306 | .decl => { |
| 2327 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; | 3307 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; |
| 2328 | decl.analysis = .sema_failure; | 3308 | decl.analysis = .sema_failure; |
| 3309 | decl.generation = self.generation; | ||
| 2329 | self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); | 3310 | self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); |
| 2330 | }, | 3311 | }, |
| 2331 | .block => { | 3312 | .block => { |
| 2332 | const block = scope.cast(Scope.Block).?; | 3313 | const block = scope.cast(Scope.Block).?; |
| 2333 | block.func.analysis = .sema_failure; | 3314 | if (block.func) |func| { |
| 3315 | func.analysis = .sema_failure; | ||
| 3316 | } else { | ||
| 3317 | block.decl.analysis = .sema_failure; | ||
| 3318 | block.decl.generation = self.generation; | ||
| 3319 | } | ||
| 2334 | self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); | 3320 | self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); |
| 2335 | }, | 3321 | }, |
| 3322 | .gen_zir => { | ||
| 3323 | const gen_zir = scope.cast(Scope.GenZIR).?; | ||
| 3324 | gen_zir.decl.analysis = .sema_failure; | ||
| 3325 | gen_zir.decl.generation = self.generation; | ||
| 3326 | self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); | ||
| 3327 | }, | ||
| 2336 | .zir_module => { | 3328 | .zir_module => { |
| 2337 | const zir_module = scope.cast(Scope.ZIRModule).?; | 3329 | const zir_module = scope.cast(Scope.ZIRModule).?; |
| 2338 | zir_module.status = .loaded_sema_failure; | 3330 | zir_module.status = .loaded_sema_failure; |
| 2339 | self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg); | 3331 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); |
| 2340 | }, | 3332 | }, |
| 3333 | .file => unreachable, | ||
| 2341 | } | 3334 | } |
| 2342 | return error.AnalysisFail; | 3335 | return error.AnalysisFail; |
| 2343 | } | 3336 | } |
| ... | @@ -2385,3 +3378,7 @@ pub const ErrorMsg = struct { | ... | @@ -2385,3 +3378,7 @@ pub const ErrorMsg = struct { |
| 2385 | self.* = undefined; | 3378 | self.* = undefined; |
| 2386 | } | 3379 | } |
| 2387 | }; | 3380 | }; |
| 3381 | |||
| 3382 | fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { | ||
| 3383 | return @bitCast(u128, a) == @bitCast(u128, b); | ||
| 3384 | } |
src-self-hosted/TypedValue.zig+8| ... | @@ -21,3 +21,11 @@ pub const Managed = struct { | ... | @@ -21,3 +21,11 @@ pub const Managed = struct { |
| 21 | self.* = undefined; | 21 | self.* = undefined; |
| 22 | } | 22 | } |
| 23 | }; | 23 | }; |
| 24 | |||
| 25 | /// Assumes arena allocation. Does a recursive copy. | ||
| 26 | pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue { | ||
| 27 | return TypedValue{ | ||
| 28 | .ty = try self.ty.copy(allocator), | ||
| 29 | .val = try self.val.copy(allocator), | ||
| 30 | }; | ||
| 31 | } |
src-self-hosted/codegen.zig+20-3| ... | @@ -10,6 +10,7 @@ const Module = @import("Module.zig"); | ... | @@ -10,6 +10,7 @@ const Module = @import("Module.zig"); |
| 10 | const ErrorMsg = Module.ErrorMsg; | 10 | const ErrorMsg = Module.ErrorMsg; |
| 11 | const Target = std.Target; | 11 | const Target = std.Target; |
| 12 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 13 | const trace = @import("tracy.zig").trace; | ||
| 13 | 14 | ||
| 14 | pub const Result = union(enum) { | 15 | pub const Result = union(enum) { |
| 15 | /// The `code` parameter passed to `generateSymbol` has the value appended. | 16 | /// The `code` parameter passed to `generateSymbol` has the value appended. |
| ... | @@ -29,6 +30,9 @@ pub fn generateSymbol( | ... | @@ -29,6 +30,9 @@ pub fn generateSymbol( |
| 29 | /// A Decl that this symbol depends on had a semantic analysis failure. | 30 | /// A Decl that this symbol depends on had a semantic analysis failure. |
| 30 | AnalysisFail, | 31 | AnalysisFail, |
| 31 | }!Result { | 32 | }!Result { |
| 33 | const tracy = trace(@src()); | ||
| 34 | defer tracy.end(); | ||
| 35 | |||
| 32 | switch (typed_value.ty.zigTypeTag()) { | 36 | switch (typed_value.ty.zigTypeTag()) { |
| 33 | .Fn => { | 37 | .Fn => { |
| 34 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; | 38 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; |
| ... | @@ -178,6 +182,7 @@ const Function = struct { | ... | @@ -178,6 +182,7 @@ const Function = struct { |
| 178 | .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), | 182 | .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), |
| 179 | .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), | 183 | .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), |
| 180 | .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), | 184 | .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), |
| 185 | .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?), | ||
| 181 | .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), | 186 | .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), |
| 182 | .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), | 187 | .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), |
| 183 | .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), | 188 | .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), |
| ... | @@ -213,7 +218,7 @@ const Function = struct { | ... | @@ -213,7 +218,7 @@ const Function = struct { |
| 213 | try self.code.resize(self.code.items.len + 7); | 218 | try self.code.resize(self.code.items.len + 7); |
| 214 | self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 }; | 219 | self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 }; |
| 215 | mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr); | 220 | mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr); |
| 216 | const return_type = func.fn_type.fnReturnType(); | 221 | const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType(); |
| 217 | switch (return_type.zigTypeTag()) { | 222 | switch (return_type.zigTypeTag()) { |
| 218 | .Void => return MCValue{ .none = {} }, | 223 | .Void => return MCValue{ .none = {} }, |
| 219 | .NoReturn => return MCValue{ .unreach = {} }, | 224 | .NoReturn => return MCValue{ .unreach = {} }, |
| ... | @@ -230,16 +235,28 @@ const Function = struct { | ... | @@ -230,16 +235,28 @@ const Function = struct { |
| 230 | } | 235 | } |
| 231 | } | 236 | } |
| 232 | 237 | ||
| 233 | fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { | 238 | fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue { |
| 239 | if (mcv != .none) { | ||
| 240 | return self.fail(src, "TODO implement return with non-void operand", .{}); | ||
| 241 | } | ||
| 234 | switch (self.target.cpu.arch) { | 242 | switch (self.target.cpu.arch) { |
| 235 | .i386, .x86_64 => { | 243 | .i386, .x86_64 => { |
| 236 | try self.code.append(0xc3); // ret | 244 | try self.code.append(0xc3); // ret |
| 237 | }, | 245 | }, |
| 238 | else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}), | 246 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 239 | } | 247 | } |
| 240 | return .unreach; | 248 | return .unreach; |
| 241 | } | 249 | } |
| 242 | 250 | ||
| 251 | fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { | ||
| 252 | const operand = try self.resolveInst(inst.args.operand); | ||
| 253 | return self.ret(inst.base.src, operand); | ||
| 254 | } | ||
| 255 | |||
| 256 | fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue { | ||
| 257 | return self.ret(inst.base.src, .none); | ||
| 258 | } | ||
| 259 | |||
| 243 | fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { | 260 | fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { |
| 244 | switch (self.target.cpu.arch) { | 261 | switch (self.target.cpu.arch) { |
| 245 | else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), | 262 | else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), |
src-self-hosted/ir.zig+9| ... | @@ -26,6 +26,7 @@ pub const Inst = struct { | ... | @@ -26,6 +26,7 @@ pub const Inst = struct { |
| 26 | isnull, | 26 | isnull, |
| 27 | ptrtoint, | 27 | ptrtoint, |
| 28 | ret, | 28 | ret, |
| 29 | retvoid, | ||
| 29 | unreach, | 30 | unreach, |
| 30 | }; | 31 | }; |
| 31 | 32 | ||
| ... | @@ -146,6 +147,14 @@ pub const Inst = struct { | ... | @@ -146,6 +147,14 @@ pub const Inst = struct { |
| 146 | pub const Ret = struct { | 147 | pub const Ret = struct { |
| 147 | pub const base_tag = Tag.ret; | 148 | pub const base_tag = Tag.ret; |
| 148 | base: Inst, | 149 | base: Inst, |
| 150 | args: struct { | ||
| 151 | operand: *Inst, | ||
| 152 | }, | ||
| 153 | }; | ||
| 154 | |||
| 155 | pub const RetVoid = struct { | ||
| 156 | pub const base_tag = Tag.retvoid; | ||
| 157 | base: Inst, | ||
| 149 | args: void, | 158 | args: void, |
| 150 | }; | 159 | }; |
| 151 | 160 |
src-self-hosted/link.zig+12-13| ... | @@ -369,7 +369,7 @@ pub const ElfFile = struct { | ... | @@ -369,7 +369,7 @@ pub const ElfFile = struct { |
| 369 | const file_size = self.options.program_code_size_hint; | 369 | const file_size = self.options.program_code_size_hint; |
| 370 | const p_align = 0x1000; | 370 | const p_align = 0x1000; |
| 371 | const off = self.findFreeSpace(file_size, p_align); | 371 | const off = self.findFreeSpace(file_size, p_align); |
| 372 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 372 | //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 373 | try self.program_headers.append(self.allocator, .{ | 373 | try self.program_headers.append(self.allocator, .{ |
| 374 | .p_type = elf.PT_LOAD, | 374 | .p_type = elf.PT_LOAD, |
| 375 | .p_offset = off, | 375 | .p_offset = off, |
| ... | @@ -390,7 +390,7 @@ pub const ElfFile = struct { | ... | @@ -390,7 +390,7 @@ pub const ElfFile = struct { |
| 390 | // page align. | 390 | // page align. |
| 391 | const p_align = 0x1000; | 391 | const p_align = 0x1000; |
| 392 | const off = self.findFreeSpace(file_size, p_align); | 392 | const off = self.findFreeSpace(file_size, p_align); |
| 393 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 393 | //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 394 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. | 394 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 395 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something | 395 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 396 | // else in virtual memory. | 396 | // else in virtual memory. |
| ... | @@ -412,7 +412,7 @@ pub const ElfFile = struct { | ... | @@ -412,7 +412,7 @@ pub const ElfFile = struct { |
| 412 | assert(self.shstrtab.items.len == 0); | 412 | assert(self.shstrtab.items.len == 0); |
| 413 | try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0 | 413 | try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0 |
| 414 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); | 414 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); |
| 415 | //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len }); | 415 | //std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len }); |
| 416 | try self.sections.append(self.allocator, .{ | 416 | try self.sections.append(self.allocator, .{ |
| 417 | .sh_name = try self.makeString(".shstrtab"), | 417 | .sh_name = try self.makeString(".shstrtab"), |
| 418 | .sh_type = elf.SHT_STRTAB, | 418 | .sh_type = elf.SHT_STRTAB, |
| ... | @@ -470,7 +470,7 @@ pub const ElfFile = struct { | ... | @@ -470,7 +470,7 @@ pub const ElfFile = struct { |
| 470 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); | 470 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 471 | const file_size = self.options.symbol_count_hint * each_size; | 471 | const file_size = self.options.symbol_count_hint * each_size; |
| 472 | const off = self.findFreeSpace(file_size, min_align); | 472 | const off = self.findFreeSpace(file_size, min_align); |
| 473 | //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 473 | //std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 474 | 474 | ||
| 475 | try self.sections.append(self.allocator, .{ | 475 | try self.sections.append(self.allocator, .{ |
| 476 | .sh_name = try self.makeString(".symtab"), | 476 | .sh_name = try self.makeString(".symtab"), |
| ... | @@ -586,7 +586,7 @@ pub const ElfFile = struct { | ... | @@ -586,7 +586,7 @@ pub const ElfFile = struct { |
| 586 | shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1); | 586 | shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1); |
| 587 | } | 587 | } |
| 588 | shstrtab_sect.sh_size = needed_size; | 588 | shstrtab_sect.sh_size = needed_size; |
| 589 | //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size }); | 589 | //std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size }); |
| 590 | 590 | ||
| 591 | try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); | 591 | try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); |
| 592 | if (!self.shdr_table_dirty) { | 592 | if (!self.shdr_table_dirty) { |
| ... | @@ -632,7 +632,7 @@ pub const ElfFile = struct { | ... | @@ -632,7 +632,7 @@ pub const ElfFile = struct { |
| 632 | 632 | ||
| 633 | for (buf) |*shdr, i| { | 633 | for (buf) |*shdr, i| { |
| 634 | shdr.* = self.sections.items[i]; | 634 | shdr.* = self.sections.items[i]; |
| 635 | //std.debug.warn("writing section {}\n", .{shdr.*}); | 635 | //std.log.debug(.link, "writing section {}\n", .{shdr.*}); |
| 636 | if (foreign_endian) { | 636 | if (foreign_endian) { |
| 637 | bswapAllFields(elf.Elf64_Shdr, shdr); | 637 | bswapAllFields(elf.Elf64_Shdr, shdr); |
| 638 | } | 638 | } |
| ... | @@ -956,10 +956,10 @@ pub const ElfFile = struct { | ... | @@ -956,10 +956,10 @@ pub const ElfFile = struct { |
| 956 | try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len); | 956 | try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len); |
| 957 | 957 | ||
| 958 | if (self.local_symbol_free_list.popOrNull()) |i| { | 958 | if (self.local_symbol_free_list.popOrNull()) |i| { |
| 959 | //std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name}); | 959 | //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name}); |
| 960 | decl.link.local_sym_index = i; | 960 | decl.link.local_sym_index = i; |
| 961 | } else { | 961 | } else { |
| 962 | //std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name}); | 962 | //std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name}); |
| 963 | decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len); | 963 | decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| 964 | _ = self.local_symbols.addOneAssumeCapacity(); | 964 | _ = self.local_symbols.addOneAssumeCapacity(); |
| 965 | } | 965 | } |
| ... | @@ -1002,7 +1002,7 @@ pub const ElfFile = struct { | ... | @@ -1002,7 +1002,7 @@ pub const ElfFile = struct { |
| 1002 | defer code_buffer.deinit(); | 1002 | defer code_buffer.deinit(); |
| 1003 | 1003 | ||
| 1004 | const typed_value = decl.typed_value.most_recent.typed_value; | 1004 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 1005 | const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) { | 1005 | const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) { |
| 1006 | .externally_managed => |x| x, | 1006 | .externally_managed => |x| x, |
| 1007 | .appended => code_buffer.items, | 1007 | .appended => code_buffer.items, |
| 1008 | .fail => |em| { | 1008 | .fail => |em| { |
| ... | @@ -1027,11 +1027,11 @@ pub const ElfFile = struct { | ... | @@ -1027,11 +1027,11 @@ pub const ElfFile = struct { |
| 1027 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); | 1027 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 1028 | if (need_realloc) { | 1028 | if (need_realloc) { |
| 1029 | const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment); | 1029 | const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment); |
| 1030 | //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); | 1030 | //std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); |
| 1031 | if (vaddr != local_sym.st_value) { | 1031 | if (vaddr != local_sym.st_value) { |
| 1032 | local_sym.st_value = vaddr; | 1032 | local_sym.st_value = vaddr; |
| 1033 | 1033 | ||
| 1034 | //std.debug.warn(" (writing new offset table entry)\n", .{}); | 1034 | //std.log.debug(.link, " (writing new offset table entry)\n", .{}); |
| 1035 | self.offset_table.items[decl.link.offset_table_index] = vaddr; | 1035 | self.offset_table.items[decl.link.offset_table_index] = vaddr; |
| 1036 | try self.writeOffsetTableEntry(decl.link.offset_table_index); | 1036 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 1037 | } | 1037 | } |
| ... | @@ -1049,7 +1049,7 @@ pub const ElfFile = struct { | ... | @@ -1049,7 +1049,7 @@ pub const ElfFile = struct { |
| 1049 | const decl_name = mem.spanZ(decl.name); | 1049 | const decl_name = mem.spanZ(decl.name); |
| 1050 | const name_str_index = try self.makeString(decl_name); | 1050 | const name_str_index = try self.makeString(decl_name); |
| 1051 | const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment); | 1051 | const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment); |
| 1052 | //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr }); | 1052 | //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr }); |
| 1053 | errdefer self.freeTextBlock(&decl.link); | 1053 | errdefer self.freeTextBlock(&decl.link); |
| 1054 | 1054 | ||
| 1055 | local_sym.* = .{ | 1055 | local_sym.* = .{ |
| ... | @@ -1307,7 +1307,6 @@ pub const ElfFile = struct { | ... | @@ -1307,7 +1307,6 @@ pub const ElfFile = struct { |
| 1307 | .p32 => @sizeOf(elf.Elf32_Sym), | 1307 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 1308 | .p64 => @sizeOf(elf.Elf64_Sym), | 1308 | .p64 => @sizeOf(elf.Elf64_Sym), |
| 1309 | }; | 1309 | }; |
| 1310 | //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size }); | ||
| 1311 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 1310 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 1312 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; | 1311 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; |
| 1313 | switch (self.ptr_width) { | 1312 | switch (self.ptr_width) { |
src-self-hosted/main.zig+30-16| ... | @@ -38,6 +38,29 @@ const usage = | ... | @@ -38,6 +38,29 @@ const usage = |
| 38 | \\ | 38 | \\ |
| 39 | ; | 39 | ; |
| 40 | 40 | ||
| 41 | pub fn log( | ||
| 42 | comptime level: std.log.Level, | ||
| 43 | comptime scope: @TypeOf(.EnumLiteral), | ||
| 44 | comptime format: []const u8, | ||
| 45 | args: var, | ||
| 46 | ) void { | ||
| 47 | if (@enumToInt(level) > @enumToInt(std.log.level)) | ||
| 48 | return; | ||
| 49 | |||
| 50 | const scope_prefix = "(" ++ switch (scope) { | ||
| 51 | // Uncomment to hide logs | ||
| 52 | //.compiler, | ||
| 53 | .link => return, | ||
| 54 | |||
| 55 | else => @tagName(scope), | ||
| 56 | } ++ "): "; | ||
| 57 | |||
| 58 | const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix; | ||
| 59 | |||
| 60 | // Print the message to stderr, silently ignoring any errors | ||
| 61 | std.debug.print(prefix ++ format, args); | ||
| 62 | } | ||
| 63 | |||
| 41 | pub fn main() !void { | 64 | pub fn main() !void { |
| 42 | // TODO general purpose allocator in the zig std lib | 65 | // TODO general purpose allocator in the zig std lib |
| 43 | const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator; | 66 | const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator; |
| ... | @@ -86,7 +109,7 @@ const usage_build_generic = | ... | @@ -86,7 +109,7 @@ const usage_build_generic = |
| 86 | \\ zig build-obj <options> [files] | 109 | \\ zig build-obj <options> [files] |
| 87 | \\ | 110 | \\ |
| 88 | \\Supported file types: | 111 | \\Supported file types: |
| 89 | \\ (planned) .zig Zig source code | 112 | \\ .zig Zig source code |
| 90 | \\ .zir Zig Intermediate Representation code | 113 | \\ .zir Zig Intermediate Representation code |
| 91 | \\ (planned) .o ELF object file | 114 | \\ (planned) .o ELF object file |
| 92 | \\ (planned) .o MACH-O (macOS) object file | 115 | \\ (planned) .o MACH-O (macOS) object file |
| ... | @@ -407,21 +430,7 @@ fn buildOutputType( | ... | @@ -407,21 +430,7 @@ fn buildOutputType( |
| 407 | std.debug.warn("-fno-emit-bin not supported yet", .{}); | 430 | std.debug.warn("-fno-emit-bin not supported yet", .{}); |
| 408 | process.exit(1); | 431 | process.exit(1); |
| 409 | }, | 432 | }, |
| 410 | .yes_default_path => switch (output_mode) { | 433 | .yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode), |
| 411 | .Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }), | ||
| 412 | .Lib => blk: { | ||
| 413 | const suffix = switch (link_mode orelse .Static) { | ||
| 414 | .Static => target_info.target.staticLibSuffix(), | ||
| 415 | .Dynamic => target_info.target.dynamicLibSuffix(), | ||
| 416 | }; | ||
| 417 | break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{ | ||
| 418 | target_info.target.libPrefix(), | ||
| 419 | root_name, | ||
| 420 | suffix, | ||
| 421 | }); | ||
| 422 | }, | ||
| 423 | .Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }), | ||
| 424 | }, | ||
| 425 | .yes => |p| p, | 434 | .yes => |p| p, |
| 426 | }; | 435 | }; |
| 427 | 436 | ||
| ... | @@ -450,6 +459,7 @@ fn buildOutputType( | ... | @@ -450,6 +459,7 @@ fn buildOutputType( |
| 450 | .link_mode = link_mode, | 459 | .link_mode = link_mode, |
| 451 | .object_format = object_format, | 460 | .object_format = object_format, |
| 452 | .optimize_mode = build_mode, | 461 | .optimize_mode = build_mode, |
| 462 | .keep_source_files_loaded = zir_out_path != null, | ||
| 453 | }); | 463 | }); |
| 454 | defer module.deinit(); | 464 | defer module.deinit(); |
| 455 | 465 | ||
| ... | @@ -487,7 +497,9 @@ fn buildOutputType( | ... | @@ -487,7 +497,9 @@ fn buildOutputType( |
| 487 | } | 497 | } |
| 488 | 498 | ||
| 489 | fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void { | 499 | fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void { |
| 500 | var timer = try std.time.Timer.start(); | ||
| 490 | try module.update(); | 501 | try module.update(); |
| 502 | const update_nanos = timer.read(); | ||
| 491 | 503 | ||
| 492 | var errors = try module.getAllErrorsAlloc(); | 504 | var errors = try module.getAllErrorsAlloc(); |
| 493 | defer errors.deinit(module.allocator); | 505 | defer errors.deinit(module.allocator); |
| ... | @@ -501,6 +513,8 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo | ... | @@ -501,6 +513,8 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo |
| 501 | full_err_msg.msg, | 513 | full_err_msg.msg, |
| 502 | }); | 514 | }); |
| 503 | } | 515 | } |
| 516 | } else { | ||
| 517 | std.log.info(.compiler, "Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms}); | ||
| 504 | } | 518 | } |
| 505 | 519 | ||
| 506 | if (zir_out_path) |zop| { | 520 | if (zir_out_path) |zop| { |
src-self-hosted/test.zig+146-156| ... | @@ -21,32 +21,7 @@ const ErrorMsg = struct { | ... | @@ -21,32 +21,7 @@ const ErrorMsg = struct { |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | pub const TestContext = struct { | 23 | pub const TestContext = struct { |
| 24 | // TODO: remove these. They are deprecated. | 24 | zir_cases: std.ArrayList(Case), |
| 25 | zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase), | ||
| 26 | |||
| 27 | /// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases) | ||
| 28 | zir_cases: std.ArrayList(ZIRCase), | ||
| 29 | |||
| 30 | // TODO: remove | ||
| 31 | pub const ZIRCompareOutputCase = struct { | ||
| 32 | name: []const u8, | ||
| 33 | src_list: []const []const u8, | ||
| 34 | expected_stdout_list: []const []const u8, | ||
| 35 | }; | ||
| 36 | |||
| 37 | pub const ZIRUpdateType = enum { | ||
| 38 | /// A transformation update transforms the input ZIR and tests against | ||
| 39 | /// the expected output | ||
| 40 | Transformation, | ||
| 41 | /// An error update attempts to compile bad code, and ensures that it | ||
| 42 | /// fails to compile, and for the expected reasons | ||
| 43 | Error, | ||
| 44 | /// An execution update compiles and runs the input ZIR, feeding in | ||
| 45 | /// provided input and ensuring that the outputs match what is expected | ||
| 46 | Execution, | ||
| 47 | /// A compilation update checks that the ZIR compiles without any issues | ||
| 48 | Compiles, | ||
| 49 | }; | ||
| 50 | 25 | ||
| 51 | pub const ZIRUpdate = struct { | 26 | pub const ZIRUpdate = struct { |
| 52 | /// The input to the current update. We simulate an incremental update | 27 | /// The input to the current update. We simulate an incremental update |
| ... | @@ -57,58 +32,55 @@ pub const TestContext = struct { | ... | @@ -57,58 +32,55 @@ pub const TestContext = struct { |
| 57 | /// you can keep it mostly consistent, with small changes, testing the | 32 | /// you can keep it mostly consistent, with small changes, testing the |
| 58 | /// effects of the incremental compilation. | 33 | /// effects of the incremental compilation. |
| 59 | src: [:0]const u8, | 34 | src: [:0]const u8, |
| 60 | case: union(ZIRUpdateType) { | 35 | case: union(enum) { |
| 61 | /// The expected output ZIR | 36 | /// A transformation update transforms the input ZIR and tests against |
| 37 | /// the expected output ZIR. | ||
| 62 | Transformation: [:0]const u8, | 38 | Transformation: [:0]const u8, |
| 39 | /// An error update attempts to compile bad code, and ensures that it | ||
| 40 | /// fails to compile, and for the expected reasons. | ||
| 63 | /// A slice containing the expected errors *in sequential order*. | 41 | /// A slice containing the expected errors *in sequential order*. |
| 64 | Error: []const ErrorMsg, | 42 | Error: []const ErrorMsg, |
| 65 | 43 | /// An execution update compiles and runs the input ZIR, feeding in | |
| 66 | /// Input to feed to the program, and expected outputs. | 44 | /// provided input and ensuring that the stdout match what is expected. |
| 67 | /// | 45 | Execution: []const u8, |
| 68 | /// If stdout, stderr, and exit_code are all null, addZIRCase will | ||
| 69 | /// discard the test. To test for successful compilation, use a | ||
| 70 | /// dedicated Compile update instead. | ||
| 71 | Execution: struct { | ||
| 72 | stdin: ?[]const u8, | ||
| 73 | stdout: ?[]const u8, | ||
| 74 | stderr: ?[]const u8, | ||
| 75 | exit_code: ?u8, | ||
| 76 | }, | ||
| 77 | /// A Compiles test checks only that compilation of the given ZIR | ||
| 78 | /// succeeds. To test outputs, use an Execution test. It is good to | ||
| 79 | /// use a Compiles test before an Execution, as the overhead should | ||
| 80 | /// be low (due to incremental compilation) and TODO: provide a way | ||
| 81 | /// to check changed / new / etc decls in testing mode | ||
| 82 | /// (usingnamespace a debug info struct with a comptime flag?) | ||
| 83 | Compiles: void, | ||
| 84 | }, | 46 | }, |
| 85 | }; | 47 | }; |
| 86 | 48 | ||
| 87 | /// A ZIRCase consists of a set of *updates*. A update can transform ZIR, | 49 | /// A Case consists of a set of *updates*. A update can transform ZIR, |
| 88 | /// compile it, ensure that compilation fails, and more. The same Module is | 50 | /// compile it, ensure that compilation fails, and more. The same Module is |
| 89 | /// used for each update, so each update's source is treated as a single file | 51 | /// used for each update, so each update's source is treated as a single file |
| 90 | /// being updated by the test harness and incrementally compiled. | 52 | /// being updated by the test harness and incrementally compiled. |
| 91 | pub const ZIRCase = struct { | 53 | pub const Case = struct { |
| 92 | name: []const u8, | 54 | name: []const u8, |
| 93 | /// The platform the ZIR targets. For non-native platforms, an emulator | 55 | /// The platform the ZIR targets. For non-native platforms, an emulator |
| 94 | /// such as QEMU is required for tests to complete. | 56 | /// such as QEMU is required for tests to complete. |
| 95 | target: std.zig.CrossTarget, | 57 | target: std.zig.CrossTarget, |
| 96 | updates: std.ArrayList(ZIRUpdate), | 58 | updates: std.ArrayList(ZIRUpdate), |
| 59 | output_mode: std.builtin.OutputMode, | ||
| 60 | /// Either ".zir" or ".zig" | ||
| 61 | extension: [4]u8, | ||
| 97 | 62 | ||
| 98 | /// Adds a subcase in which the module is updated with new ZIR, and the | 63 | /// Adds a subcase in which the module is updated with new ZIR, and the |
| 99 | /// resulting ZIR is validated. | 64 | /// resulting ZIR is validated. |
| 100 | pub fn addTransform(self: *ZIRCase, src: [:0]const u8, result: [:0]const u8) void { | 65 | pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { |
| 101 | self.updates.append(.{ | 66 | self.updates.append(.{ |
| 102 | .src = src, | 67 | .src = src, |
| 103 | .case = .{ .Transformation = result }, | 68 | .case = .{ .Transformation = result }, |
| 104 | }) catch unreachable; | 69 | }) catch unreachable; |
| 105 | } | 70 | } |
| 106 | 71 | ||
| 72 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { | ||
| 73 | self.updates.append(.{ | ||
| 74 | .src = src, | ||
| 75 | .case = .{ .Execution = result }, | ||
| 76 | }) catch unreachable; | ||
| 77 | } | ||
| 78 | |||
| 107 | /// Adds a subcase in which the module is updated with invalid ZIR, and | 79 | /// Adds a subcase in which the module is updated with invalid ZIR, and |
| 108 | /// ensures that compilation fails for the expected reasons. | 80 | /// ensures that compilation fails for the expected reasons. |
| 109 | /// | 81 | /// |
| 110 | /// Errors must be specified in sequential order. | 82 | /// Errors must be specified in sequential order. |
| 111 | pub fn addError(self: *ZIRCase, src: [:0]const u8, errors: []const []const u8) void { | 83 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { |
| 112 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; | 84 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; |
| 113 | for (errors) |e, i| { | 85 | for (errors) |e, i| { |
| 114 | if (e[0] != ':') { | 86 | if (e[0] != ':') { |
| ... | @@ -146,15 +118,65 @@ pub const TestContext = struct { | ... | @@ -146,15 +118,65 @@ pub const TestContext = struct { |
| 146 | } | 118 | } |
| 147 | }; | 119 | }; |
| 148 | 120 | ||
| 149 | pub fn addZIRMulti( | 121 | pub fn addExeZIR( |
| 122 | ctx: *TestContext, | ||
| 123 | name: []const u8, | ||
| 124 | target: std.zig.CrossTarget, | ||
| 125 | ) *Case { | ||
| 126 | const case = Case{ | ||
| 127 | .name = name, | ||
| 128 | .target = target, | ||
| 129 | .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator), | ||
| 130 | .output_mode = .Exe, | ||
| 131 | .extension = ".zir".*, | ||
| 132 | }; | ||
| 133 | ctx.zir_cases.append(case) catch unreachable; | ||
| 134 | return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1]; | ||
| 135 | } | ||
| 136 | |||
| 137 | pub fn addObjZIR( | ||
| 150 | ctx: *TestContext, | 138 | ctx: *TestContext, |
| 151 | name: []const u8, | 139 | name: []const u8, |
| 152 | target: std.zig.CrossTarget, | 140 | target: std.zig.CrossTarget, |
| 153 | ) *ZIRCase { | 141 | ) *Case { |
| 154 | const case = ZIRCase{ | 142 | const case = Case{ |
| 155 | .name = name, | 143 | .name = name, |
| 156 | .target = target, | 144 | .target = target, |
| 157 | .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator), | 145 | .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator), |
| 146 | .output_mode = .Obj, | ||
| 147 | .extension = ".zir".*, | ||
| 148 | }; | ||
| 149 | ctx.zir_cases.append(case) catch unreachable; | ||
| 150 | return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1]; | ||
| 151 | } | ||
| 152 | |||
| 153 | pub fn addExe( | ||
| 154 | ctx: *TestContext, | ||
| 155 | name: []const u8, | ||
| 156 | target: std.zig.CrossTarget, | ||
| 157 | ) *Case { | ||
| 158 | const case = Case{ | ||
| 159 | .name = name, | ||
| 160 | .target = target, | ||
| 161 | .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator), | ||
| 162 | .output_mode = .Exe, | ||
| 163 | .extension = ".zig".*, | ||
| 164 | }; | ||
| 165 | ctx.zir_cases.append(case) catch unreachable; | ||
| 166 | return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1]; | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn addObj( | ||
| 170 | ctx: *TestContext, | ||
| 171 | name: []const u8, | ||
| 172 | target: std.zig.CrossTarget, | ||
| 173 | ) *Case { | ||
| 174 | const case = Case{ | ||
| 175 | .name = name, | ||
| 176 | .target = target, | ||
| 177 | .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator), | ||
| 178 | .output_mode = .Obj, | ||
| 179 | .extension = ".zig".*, | ||
| 158 | }; | 180 | }; |
| 159 | ctx.zir_cases.append(case) catch unreachable; | 181 | ctx.zir_cases.append(case) catch unreachable; |
| 160 | return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1]; | 182 | return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1]; |
| ... | @@ -163,14 +185,21 @@ pub const TestContext = struct { | ... | @@ -163,14 +185,21 @@ pub const TestContext = struct { |
| 163 | pub fn addZIRCompareOutput( | 185 | pub fn addZIRCompareOutput( |
| 164 | ctx: *TestContext, | 186 | ctx: *TestContext, |
| 165 | name: []const u8, | 187 | name: []const u8, |
| 166 | src_list: []const []const u8, | 188 | src: [:0]const u8, |
| 167 | expected_stdout_list: []const []const u8, | 189 | expected_stdout: []const u8, |
| 168 | ) void { | 190 | ) void { |
| 169 | ctx.zir_cmp_output_cases.append(.{ | 191 | var c = ctx.addExeZIR(name, .{}); |
| 170 | .name = name, | 192 | c.addCompareOutput(src, expected_stdout); |
| 171 | .src_list = src_list, | 193 | } |
| 172 | .expected_stdout_list = expected_stdout_list, | 194 | |
| 173 | }) catch unreachable; | 195 | pub fn addCompareOutput( |
| 196 | ctx: *TestContext, | ||
| 197 | name: []const u8, | ||
| 198 | src: [:0]const u8, | ||
| 199 | expected_stdout: []const u8, | ||
| 200 | ) void { | ||
| 201 | var c = ctx.addExe(name, .{}); | ||
| 202 | c.addCompareOutput(src, expected_stdout); | ||
| 174 | } | 203 | } |
| 175 | 204 | ||
| 176 | pub fn addZIRTransform( | 205 | pub fn addZIRTransform( |
| ... | @@ -180,7 +209,7 @@ pub const TestContext = struct { | ... | @@ -180,7 +209,7 @@ pub const TestContext = struct { |
| 180 | src: [:0]const u8, | 209 | src: [:0]const u8, |
| 181 | result: [:0]const u8, | 210 | result: [:0]const u8, |
| 182 | ) void { | 211 | ) void { |
| 183 | var c = ctx.addZIRMulti(name, target); | 212 | var c = ctx.addObjZIR(name, target); |
| 184 | c.addTransform(src, result); | 213 | c.addTransform(src, result); |
| 185 | } | 214 | } |
| 186 | 215 | ||
| ... | @@ -191,20 +220,18 @@ pub const TestContext = struct { | ... | @@ -191,20 +220,18 @@ pub const TestContext = struct { |
| 191 | src: [:0]const u8, | 220 | src: [:0]const u8, |
| 192 | expected_errors: []const []const u8, | 221 | expected_errors: []const []const u8, |
| 193 | ) void { | 222 | ) void { |
| 194 | var c = ctx.addZIRMulti(name, target); | 223 | var c = ctx.addObjZIR(name, target); |
| 195 | c.addError(src, expected_errors); | 224 | c.addError(src, expected_errors); |
| 196 | } | 225 | } |
| 197 | 226 | ||
| 198 | fn init() TestContext { | 227 | fn init() TestContext { |
| 199 | const allocator = std.heap.page_allocator; | 228 | const allocator = std.heap.page_allocator; |
| 200 | return .{ | 229 | return .{ |
| 201 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator), | 230 | .zir_cases = std.ArrayList(Case).init(allocator), |
| 202 | .zir_cases = std.ArrayList(ZIRCase).init(allocator), | ||
| 203 | }; | 231 | }; |
| 204 | } | 232 | } |
| 205 | 233 | ||
| 206 | fn deinit(self: *TestContext) void { | 234 | fn deinit(self: *TestContext) void { |
| 207 | self.zir_cmp_output_cases.deinit(); | ||
| 208 | for (self.zir_cases.items) |c| { | 235 | for (self.zir_cases.items) |c| { |
| 209 | for (c.updates.items) |u| { | 236 | for (c.updates.items) |u| { |
| 210 | if (u.case == .Error) { | 237 | if (u.case == .Error) { |
| ... | @@ -226,30 +253,32 @@ pub const TestContext = struct { | ... | @@ -226,30 +253,32 @@ pub const TestContext = struct { |
| 226 | 253 | ||
| 227 | for (self.zir_cases.items) |case| { | 254 | for (self.zir_cases.items) |case| { |
| 228 | std.testing.base_allocator_instance.reset(); | 255 | std.testing.base_allocator_instance.reset(); |
| 229 | const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target); | ||
| 230 | try self.runOneZIRCase(std.testing.allocator, root_node, case, info.target); | ||
| 231 | try std.testing.allocator_instance.validate(); | ||
| 232 | } | ||
| 233 | 256 | ||
| 234 | // TODO: wipe the rest of this function | 257 | var prg_node = root_node.start(case.name, case.updates.items.len); |
| 235 | for (self.zir_cmp_output_cases.items) |case| { | 258 | prg_node.activate(); |
| 236 | std.testing.base_allocator_instance.reset(); | 259 | defer prg_node.end(); |
| 237 | try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target); | 260 | |
| 261 | // So that we can see which test case failed when the leak checker goes off. | ||
| 262 | progress.refresh(); | ||
| 263 | |||
| 264 | const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target); | ||
| 265 | try self.runOneCase(std.testing.allocator, &prg_node, case, info.target); | ||
| 238 | try std.testing.allocator_instance.validate(); | 266 | try std.testing.allocator_instance.validate(); |
| 239 | } | 267 | } |
| 240 | } | 268 | } |
| 241 | 269 | ||
| 242 | fn runOneZIRCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: ZIRCase, target: std.Target) !void { | 270 | fn runOneCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: Case, target: std.Target) !void { |
| 243 | var tmp = std.testing.tmpDir(.{}); | 271 | var tmp = std.testing.tmpDir(.{}); |
| 244 | defer tmp.cleanup(); | 272 | defer tmp.cleanup(); |
| 245 | 273 | ||
| 246 | const tmp_src_path = "test_case.zir"; | 274 | const root_name = "test_case"; |
| 275 | const tmp_src_path = try std.fmt.allocPrint(allocator, "{}{}", .{ root_name, case.extension }); | ||
| 276 | defer allocator.free(tmp_src_path); | ||
| 247 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); | 277 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); |
| 248 | defer root_pkg.destroy(); | 278 | defer root_pkg.destroy(); |
| 249 | 279 | ||
| 250 | var prg_node = root_node.start(case.name, case.updates.items.len); | 280 | const bin_name = try std.zig.binNameAlloc(allocator, root_name, target, case.output_mode, null); |
| 251 | prg_node.activate(); | 281 | defer allocator.free(bin_name); |
| 252 | defer prg_node.end(); | ||
| 253 | 282 | ||
| 254 | var module = try Module.init(allocator, .{ | 283 | var module = try Module.init(allocator, .{ |
| 255 | .target = target, | 284 | .target = target, |
| ... | @@ -259,16 +288,17 @@ pub const TestContext = struct { | ... | @@ -259,16 +288,17 @@ pub const TestContext = struct { |
| 259 | // TODO: support tests for object file building, and library builds | 288 | // TODO: support tests for object file building, and library builds |
| 260 | // and linking. This will require a rework to support multi-file | 289 | // and linking. This will require a rework to support multi-file |
| 261 | // tests. | 290 | // tests. |
| 262 | .output_mode = .Obj, | 291 | .output_mode = case.output_mode, |
| 263 | // TODO: support testing optimizations | 292 | // TODO: support testing optimizations |
| 264 | .optimize_mode = .Debug, | 293 | .optimize_mode = .Debug, |
| 265 | .bin_file_dir = tmp.dir, | 294 | .bin_file_dir = tmp.dir, |
| 266 | .bin_file_path = "test_case.o", | 295 | .bin_file_path = bin_name, |
| 267 | .root_pkg = root_pkg, | 296 | .root_pkg = root_pkg, |
| 297 | .keep_source_files_loaded = true, | ||
| 268 | }); | 298 | }); |
| 269 | defer module.deinit(); | 299 | defer module.deinit(); |
| 270 | 300 | ||
| 271 | for (case.updates.items) |update| { | 301 | for (case.updates.items) |update, update_index| { |
| 272 | var update_node = prg_node.start("update", 4); | 302 | var update_node = prg_node.start("update", 4); |
| 273 | update_node.activate(); | 303 | update_node.activate(); |
| 274 | defer update_node.end(); | 304 | defer update_node.end(); |
| ... | @@ -280,6 +310,7 @@ pub const TestContext = struct { | ... | @@ -280,6 +310,7 @@ pub const TestContext = struct { |
| 280 | 310 | ||
| 281 | var module_node = update_node.start("parse/analysis/codegen", null); | 311 | var module_node = update_node.start("parse/analysis/codegen", null); |
| 282 | module_node.activate(); | 312 | module_node.activate(); |
| 313 | try module.makeBinFileWritable(); | ||
| 283 | try module.update(); | 314 | try module.update(); |
| 284 | module_node.end(); | 315 | module_node.end(); |
| 285 | 316 | ||
| ... | @@ -328,82 +359,41 @@ pub const TestContext = struct { | ... | @@ -328,82 +359,41 @@ pub const TestContext = struct { |
| 328 | } | 359 | } |
| 329 | } | 360 | } |
| 330 | }, | 361 | }, |
| 331 | 362 | .Execution => |expected_stdout| { | |
| 332 | else => return error.unimplemented, | 363 | var exec_result = x: { |
| 333 | } | 364 | var exec_node = update_node.start("execute", null); |
| 334 | } | 365 | exec_node.activate(); |
| 335 | } | 366 | defer exec_node.end(); |
| 336 | 367 | ||
| 337 | fn runOneZIRCmpOutputCase( | 368 | try module.makeBinFileExecutable(); |
| 338 | self: *TestContext, | 369 | |
| 339 | allocator: *Allocator, | 370 | const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name}); |
| 340 | root_node: *std.Progress.Node, | 371 | defer allocator.free(exe_path); |
| 341 | case: ZIRCompareOutputCase, | 372 | |
| 342 | target: std.Target, | 373 | break :x try std.ChildProcess.exec(.{ |
| 343 | ) !void { | 374 | .allocator = allocator, |
| 344 | var tmp = std.testing.tmpDir(.{}); | 375 | .argv = &[_][]const u8{exe_path}, |
| 345 | defer tmp.cleanup(); | 376 | .cwd_dir = tmp.dir, |
| 346 | 377 | }); | |
| 347 | const tmp_src_path = "test-case.zir"; | 378 | }; |
| 348 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); | 379 | defer allocator.free(exec_result.stdout); |
| 349 | defer root_pkg.destroy(); | 380 | defer allocator.free(exec_result.stderr); |
| 350 | 381 | switch (exec_result.term) { | |
| 351 | var prg_node = root_node.start(case.name, case.src_list.len); | 382 | .Exited => |code| { |
| 352 | prg_node.activate(); | 383 | if (code != 0) { |
| 353 | defer prg_node.end(); | 384 | std.debug.warn("elf file exited with code {}\n", .{code}); |
| 354 | 385 | return error.BinaryBadExitCode; | |
| 355 | var module = try Module.init(allocator, .{ | 386 | } |
| 356 | .target = target, | 387 | }, |
| 357 | .output_mode = .Exe, | 388 | else => return error.BinaryCrashed, |
| 358 | .optimize_mode = .Debug, | 389 | } |
| 359 | .bin_file_dir = tmp.dir, | 390 | if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) { |
| 360 | .bin_file_path = "a.out", | 391 | std.debug.panic( |
| 361 | .root_pkg = root_pkg, | 392 | "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n", |
| 362 | }); | 393 | .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout }, |
| 363 | defer module.deinit(); | 394 | ); |
| 364 | |||
| 365 | for (case.src_list) |source, i| { | ||
| 366 | var src_node = prg_node.start("update", 2); | ||
| 367 | src_node.activate(); | ||
| 368 | defer src_node.end(); | ||
| 369 | |||
| 370 | try tmp.dir.writeFile(tmp_src_path, source); | ||
| 371 | |||
| 372 | var update_node = src_node.start("parse,analysis,codegen", null); | ||
| 373 | update_node.activate(); | ||
| 374 | try module.makeBinFileWritable(); | ||
| 375 | try module.update(); | ||
| 376 | update_node.end(); | ||
| 377 | |||
| 378 | var exec_result = x: { | ||
| 379 | var exec_node = src_node.start("execute", null); | ||
| 380 | exec_node.activate(); | ||
| 381 | defer exec_node.end(); | ||
| 382 | |||
| 383 | try module.makeBinFileExecutable(); | ||
| 384 | break :x try std.ChildProcess.exec(.{ | ||
| 385 | .allocator = allocator, | ||
| 386 | .argv = &[_][]const u8{"./a.out"}, | ||
| 387 | .cwd_dir = tmp.dir, | ||
| 388 | }); | ||
| 389 | }; | ||
| 390 | defer allocator.free(exec_result.stdout); | ||
| 391 | defer allocator.free(exec_result.stderr); | ||
| 392 | switch (exec_result.term) { | ||
| 393 | .Exited => |code| { | ||
| 394 | if (code != 0) { | ||
| 395 | std.debug.warn("elf file exited with code {}\n", .{code}); | ||
| 396 | return error.BinaryBadExitCode; | ||
| 397 | } | 395 | } |
| 398 | }, | 396 | }, |
| 399 | else => return error.BinaryCrashed, | ||
| 400 | } | ||
| 401 | const expected_stdout = case.expected_stdout_list[i]; | ||
| 402 | if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) { | ||
| 403 | std.debug.panic( | ||
| 404 | "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n", | ||
| 405 | .{ i, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout }, | ||
| 406 | ); | ||
| 407 | } | 397 | } |
| 408 | } | 398 | } |
| 409 | } | 399 | } |
src-self-hosted/tracy.zig created+45| ... | @@ -0,0 +1,45 @@ | ||
| 1 | pub const std = @import("std"); | ||
| 2 | |||
| 3 | pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy; | ||
| 4 | |||
| 5 | extern fn ___tracy_emit_zone_begin_callstack( | ||
| 6 | srcloc: *const ___tracy_source_location_data, | ||
| 7 | depth: c_int, | ||
| 8 | active: c_int, | ||
| 9 | ) ___tracy_c_zone_context; | ||
| 10 | |||
| 11 | extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void; | ||
| 12 | |||
| 13 | pub const ___tracy_source_location_data = extern struct { | ||
| 14 | name: ?[*:0]const u8, | ||
| 15 | function: [*:0]const u8, | ||
| 16 | file: [*:0]const u8, | ||
| 17 | line: u32, | ||
| 18 | color: u32, | ||
| 19 | }; | ||
| 20 | |||
| 21 | pub const ___tracy_c_zone_context = extern struct { | ||
| 22 | id: u32, | ||
| 23 | active: c_int, | ||
| 24 | |||
| 25 | pub fn end(self: ___tracy_c_zone_context) void { | ||
| 26 | ___tracy_emit_zone_end(self); | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | |||
| 30 | pub const Ctx = if (enable) ___tracy_c_zone_context else struct { | ||
| 31 | pub fn end(self: Ctx) void {} | ||
| 32 | }; | ||
| 33 | |||
| 34 | pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx { | ||
| 35 | if (!enable) return .{}; | ||
| 36 | |||
| 37 | const loc: ___tracy_source_location_data = .{ | ||
| 38 | .name = null, | ||
| 39 | .function = src.fn_name.ptr, | ||
| 40 | .file = src.file.ptr, | ||
| 41 | .line = src.line, | ||
| 42 | .color = 0, | ||
| 43 | }; | ||
| 44 | return ___tracy_emit_zone_begin_callstack(&loc, 1, 1); | ||
| 45 | } | ||
src-self-hosted/type.zig+102-1| ... | @@ -54,6 +54,7 @@ pub const Type = extern union { | ... | @@ -54,6 +54,7 @@ pub const Type = extern union { |
| 54 | .@"undefined" => return .Undefined, | 54 | .@"undefined" => return .Undefined, |
| 55 | 55 | ||
| 56 | .fn_noreturn_no_args => return .Fn, | 56 | .fn_noreturn_no_args => return .Fn, |
| 57 | .fn_void_no_args => return .Fn, | ||
| 57 | .fn_naked_noreturn_no_args => return .Fn, | 58 | .fn_naked_noreturn_no_args => return .Fn, |
| 58 | .fn_ccc_void_no_args => return .Fn, | 59 | .fn_ccc_void_no_args => return .Fn, |
| 59 | 60 | ||
| ... | @@ -112,6 +113,12 @@ pub const Type = extern union { | ... | @@ -112,6 +113,12 @@ pub const Type = extern union { |
| 112 | .Undefined => return true, | 113 | .Undefined => return true, |
| 113 | .Null => return true, | 114 | .Null => return true, |
| 114 | .Pointer => { | 115 | .Pointer => { |
| 116 | // Hot path for common case: | ||
| 117 | if (a.cast(Payload.SingleConstPointer)) |a_payload| { | ||
| 118 | if (b.cast(Payload.SingleConstPointer)) |b_payload| { | ||
| 119 | return eql(a_payload.pointee_type, b_payload.pointee_type); | ||
| 120 | } | ||
| 121 | } | ||
| 115 | const is_slice_a = isSlice(a); | 122 | const is_slice_a = isSlice(a); |
| 116 | const is_slice_b = isSlice(b); | 123 | const is_slice_b = isSlice(b); |
| 117 | if (is_slice_a != is_slice_b) | 124 | if (is_slice_a != is_slice_b) |
| ... | @@ -163,6 +170,77 @@ pub const Type = extern union { | ... | @@ -163,6 +170,77 @@ pub const Type = extern union { |
| 163 | } | 170 | } |
| 164 | } | 171 | } |
| 165 | 172 | ||
| 173 | pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type { | ||
| 174 | if (self.tag_if_small_enough < Tag.no_payload_count) { | ||
| 175 | return Type{ .tag_if_small_enough = self.tag_if_small_enough }; | ||
| 176 | } else switch (self.ptr_otherwise.tag) { | ||
| 177 | .u8, | ||
| 178 | .i8, | ||
| 179 | .isize, | ||
| 180 | .usize, | ||
| 181 | .c_short, | ||
| 182 | .c_ushort, | ||
| 183 | .c_int, | ||
| 184 | .c_uint, | ||
| 185 | .c_long, | ||
| 186 | .c_ulong, | ||
| 187 | .c_longlong, | ||
| 188 | .c_ulonglong, | ||
| 189 | .c_longdouble, | ||
| 190 | .c_void, | ||
| 191 | .f16, | ||
| 192 | .f32, | ||
| 193 | .f64, | ||
| 194 | .f128, | ||
| 195 | .bool, | ||
| 196 | .void, | ||
| 197 | .type, | ||
| 198 | .anyerror, | ||
| 199 | .comptime_int, | ||
| 200 | .comptime_float, | ||
| 201 | .noreturn, | ||
| 202 | .@"null", | ||
| 203 | .@"undefined", | ||
| 204 | .fn_noreturn_no_args, | ||
| 205 | .fn_void_no_args, | ||
| 206 | .fn_naked_noreturn_no_args, | ||
| 207 | .fn_ccc_void_no_args, | ||
| 208 | .single_const_pointer_to_comptime_int, | ||
| 209 | .const_slice_u8, | ||
| 210 | => unreachable, | ||
| 211 | |||
| 212 | .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), | ||
| 213 | .array => { | ||
| 214 | const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise); | ||
| 215 | const new_payload = try allocator.create(Payload.Array); | ||
| 216 | new_payload.* = .{ | ||
| 217 | .base = payload.base, | ||
| 218 | .len = payload.len, | ||
| 219 | .elem_type = try payload.elem_type.copy(allocator), | ||
| 220 | }; | ||
| 221 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 222 | }, | ||
| 223 | .single_const_pointer => { | ||
| 224 | const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise); | ||
| 225 | const new_payload = try allocator.create(Payload.SingleConstPointer); | ||
| 226 | new_payload.* = .{ | ||
| 227 | .base = payload.base, | ||
| 228 | .pointee_type = try payload.pointee_type.copy(allocator), | ||
| 229 | }; | ||
| 230 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 231 | }, | ||
| 232 | .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), | ||
| 233 | .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type { | ||
| 238 | const payload = @fieldParentPtr(T, "base", self.ptr_otherwise); | ||
| 239 | const new_payload = try allocator.create(T); | ||
| 240 | new_payload.* = payload.*; | ||
| 241 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 242 | } | ||
| 243 | |||
| 166 | pub fn format( | 244 | pub fn format( |
| 167 | self: Type, | 245 | self: Type, |
| 168 | comptime fmt: []const u8, | 246 | comptime fmt: []const u8, |
| ... | @@ -206,6 +284,7 @@ pub const Type = extern union { | ... | @@ -206,6 +284,7 @@ pub const Type = extern union { |
| 206 | 284 | ||
| 207 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | 285 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 208 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), | 286 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| 287 | .fn_void_no_args => return out_stream.writeAll("fn() void"), | ||
| 209 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | 288 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 210 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), | 289 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), |
| 211 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), | 290 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), |
| ... | @@ -269,6 +348,7 @@ pub const Type = extern union { | ... | @@ -269,6 +348,7 @@ pub const Type = extern union { |
| 269 | .@"null" => return Value.initTag(.null_type), | 348 | .@"null" => return Value.initTag(.null_type), |
| 270 | .@"undefined" => return Value.initTag(.undefined_type), | 349 | .@"undefined" => return Value.initTag(.undefined_type), |
| 271 | .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type), | 350 | .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type), |
| 351 | .fn_void_no_args => return Value.initTag(.fn_void_no_args_type), | ||
| 272 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), | 352 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 273 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), | 353 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), |
| 274 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), | 354 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), |
| ... | @@ -303,6 +383,7 @@ pub const Type = extern union { | ... | @@ -303,6 +383,7 @@ pub const Type = extern union { |
| 303 | .bool, | 383 | .bool, |
| 304 | .anyerror, | 384 | .anyerror, |
| 305 | .fn_noreturn_no_args, | 385 | .fn_noreturn_no_args, |
| 386 | .fn_void_no_args, | ||
| 306 | .fn_naked_noreturn_no_args, | 387 | .fn_naked_noreturn_no_args, |
| 307 | .fn_ccc_void_no_args, | 388 | .fn_ccc_void_no_args, |
| 308 | .single_const_pointer_to_comptime_int, | 389 | .single_const_pointer_to_comptime_int, |
| ... | @@ -333,6 +414,7 @@ pub const Type = extern union { | ... | @@ -333,6 +414,7 @@ pub const Type = extern union { |
| 333 | .i8, | 414 | .i8, |
| 334 | .bool, | 415 | .bool, |
| 335 | .fn_noreturn_no_args, // represents machine code; not a pointer | 416 | .fn_noreturn_no_args, // represents machine code; not a pointer |
| 417 | .fn_void_no_args, // represents machine code; not a pointer | ||
| 336 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer | 418 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer |
| 337 | .fn_ccc_void_no_args, // represents machine code; not a pointer | 419 | .fn_ccc_void_no_args, // represents machine code; not a pointer |
| 338 | .array_u8_sentinel_0, | 420 | .array_u8_sentinel_0, |
| ... | @@ -420,6 +502,7 @@ pub const Type = extern union { | ... | @@ -420,6 +502,7 @@ pub const Type = extern union { |
| 420 | .array_u8_sentinel_0, | 502 | .array_u8_sentinel_0, |
| 421 | .const_slice_u8, | 503 | .const_slice_u8, |
| 422 | .fn_noreturn_no_args, | 504 | .fn_noreturn_no_args, |
| 505 | .fn_void_no_args, | ||
| 423 | .fn_naked_noreturn_no_args, | 506 | .fn_naked_noreturn_no_args, |
| 424 | .fn_ccc_void_no_args, | 507 | .fn_ccc_void_no_args, |
| 425 | .int_unsigned, | 508 | .int_unsigned, |
| ... | @@ -466,6 +549,7 @@ pub const Type = extern union { | ... | @@ -466,6 +549,7 @@ pub const Type = extern union { |
| 466 | .single_const_pointer, | 549 | .single_const_pointer, |
| 467 | .single_const_pointer_to_comptime_int, | 550 | .single_const_pointer_to_comptime_int, |
| 468 | .fn_noreturn_no_args, | 551 | .fn_noreturn_no_args, |
| 552 | .fn_void_no_args, | ||
| 469 | .fn_naked_noreturn_no_args, | 553 | .fn_naked_noreturn_no_args, |
| 470 | .fn_ccc_void_no_args, | 554 | .fn_ccc_void_no_args, |
| 471 | .int_unsigned, | 555 | .int_unsigned, |
| ... | @@ -509,6 +593,7 @@ pub const Type = extern union { | ... | @@ -509,6 +593,7 @@ pub const Type = extern union { |
| 509 | .array, | 593 | .array, |
| 510 | .array_u8_sentinel_0, | 594 | .array_u8_sentinel_0, |
| 511 | .fn_noreturn_no_args, | 595 | .fn_noreturn_no_args, |
| 596 | .fn_void_no_args, | ||
| 512 | .fn_naked_noreturn_no_args, | 597 | .fn_naked_noreturn_no_args, |
| 513 | .fn_ccc_void_no_args, | 598 | .fn_ccc_void_no_args, |
| 514 | .int_unsigned, | 599 | .int_unsigned, |
| ... | @@ -553,6 +638,7 @@ pub const Type = extern union { | ... | @@ -553,6 +638,7 @@ pub const Type = extern union { |
| 553 | .@"null", | 638 | .@"null", |
| 554 | .@"undefined", | 639 | .@"undefined", |
| 555 | .fn_noreturn_no_args, | 640 | .fn_noreturn_no_args, |
| 641 | .fn_void_no_args, | ||
| 556 | .fn_naked_noreturn_no_args, | 642 | .fn_naked_noreturn_no_args, |
| 557 | .fn_ccc_void_no_args, | 643 | .fn_ccc_void_no_args, |
| 558 | .int_unsigned, | 644 | .int_unsigned, |
| ... | @@ -597,6 +683,7 @@ pub const Type = extern union { | ... | @@ -597,6 +683,7 @@ pub const Type = extern union { |
| 597 | .@"null", | 683 | .@"null", |
| 598 | .@"undefined", | 684 | .@"undefined", |
| 599 | .fn_noreturn_no_args, | 685 | .fn_noreturn_no_args, |
| 686 | .fn_void_no_args, | ||
| 600 | .fn_naked_noreturn_no_args, | 687 | .fn_naked_noreturn_no_args, |
| 601 | .fn_ccc_void_no_args, | 688 | .fn_ccc_void_no_args, |
| 602 | .single_const_pointer, | 689 | .single_const_pointer, |
| ... | @@ -642,6 +729,7 @@ pub const Type = extern union { | ... | @@ -642,6 +729,7 @@ pub const Type = extern union { |
| 642 | .@"null", | 729 | .@"null", |
| 643 | .@"undefined", | 730 | .@"undefined", |
| 644 | .fn_noreturn_no_args, | 731 | .fn_noreturn_no_args, |
| 732 | .fn_void_no_args, | ||
| 645 | .fn_naked_noreturn_no_args, | 733 | .fn_naked_noreturn_no_args, |
| 646 | .fn_ccc_void_no_args, | 734 | .fn_ccc_void_no_args, |
| 647 | .single_const_pointer, | 735 | .single_const_pointer, |
| ... | @@ -675,6 +763,7 @@ pub const Type = extern union { | ... | @@ -675,6 +763,7 @@ pub const Type = extern union { |
| 675 | .@"null", | 763 | .@"null", |
| 676 | .@"undefined", | 764 | .@"undefined", |
| 677 | .fn_noreturn_no_args, | 765 | .fn_noreturn_no_args, |
| 766 | .fn_void_no_args, | ||
| 678 | .fn_naked_noreturn_no_args, | 767 | .fn_naked_noreturn_no_args, |
| 679 | .fn_ccc_void_no_args, | 768 | .fn_ccc_void_no_args, |
| 680 | .array, | 769 | .array, |
| ... | @@ -721,6 +810,7 @@ pub const Type = extern union { | ... | @@ -721,6 +810,7 @@ pub const Type = extern union { |
| 721 | .@"null", | 810 | .@"null", |
| 722 | .@"undefined", | 811 | .@"undefined", |
| 723 | .fn_noreturn_no_args, | 812 | .fn_noreturn_no_args, |
| 813 | .fn_void_no_args, | ||
| 724 | .fn_naked_noreturn_no_args, | 814 | .fn_naked_noreturn_no_args, |
| 725 | .fn_ccc_void_no_args, | 815 | .fn_ccc_void_no_args, |
| 726 | .array, | 816 | .array, |
| ... | @@ -777,6 +867,7 @@ pub const Type = extern union { | ... | @@ -777,6 +867,7 @@ pub const Type = extern union { |
| 777 | pub fn fnParamLen(self: Type) usize { | 867 | pub fn fnParamLen(self: Type) usize { |
| 778 | return switch (self.tag()) { | 868 | return switch (self.tag()) { |
| 779 | .fn_noreturn_no_args => 0, | 869 | .fn_noreturn_no_args => 0, |
| 870 | .fn_void_no_args => 0, | ||
| 780 | .fn_naked_noreturn_no_args => 0, | 871 | .fn_naked_noreturn_no_args => 0, |
| 781 | .fn_ccc_void_no_args => 0, | 872 | .fn_ccc_void_no_args => 0, |
| 782 | 873 | ||
| ... | @@ -823,6 +914,7 @@ pub const Type = extern union { | ... | @@ -823,6 +914,7 @@ pub const Type = extern union { |
| 823 | pub fn fnParamTypes(self: Type, types: []Type) void { | 914 | pub fn fnParamTypes(self: Type, types: []Type) void { |
| 824 | switch (self.tag()) { | 915 | switch (self.tag()) { |
| 825 | .fn_noreturn_no_args => return, | 916 | .fn_noreturn_no_args => return, |
| 917 | .fn_void_no_args => return, | ||
| 826 | .fn_naked_noreturn_no_args => return, | 918 | .fn_naked_noreturn_no_args => return, |
| 827 | .fn_ccc_void_no_args => return, | 919 | .fn_ccc_void_no_args => return, |
| 828 | 920 | ||
| ... | @@ -869,7 +961,10 @@ pub const Type = extern union { | ... | @@ -869,7 +961,10 @@ pub const Type = extern union { |
| 869 | return switch (self.tag()) { | 961 | return switch (self.tag()) { |
| 870 | .fn_noreturn_no_args => Type.initTag(.noreturn), | 962 | .fn_noreturn_no_args => Type.initTag(.noreturn), |
| 871 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), | 963 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), |
| 872 | .fn_ccc_void_no_args => Type.initTag(.void), | 964 | |
| 965 | .fn_void_no_args, | ||
| 966 | .fn_ccc_void_no_args, | ||
| 967 | => Type.initTag(.void), | ||
| 873 | 968 | ||
| 874 | .f16, | 969 | .f16, |
| 875 | .f32, | 970 | .f32, |
| ... | @@ -913,6 +1008,7 @@ pub const Type = extern union { | ... | @@ -913,6 +1008,7 @@ pub const Type = extern union { |
| 913 | pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { | 1008 | pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { |
| 914 | return switch (self.tag()) { | 1009 | return switch (self.tag()) { |
| 915 | .fn_noreturn_no_args => .Unspecified, | 1010 | .fn_noreturn_no_args => .Unspecified, |
| 1011 | .fn_void_no_args => .Unspecified, | ||
| 916 | .fn_naked_noreturn_no_args => .Naked, | 1012 | .fn_naked_noreturn_no_args => .Naked, |
| 917 | .fn_ccc_void_no_args => .C, | 1013 | .fn_ccc_void_no_args => .C, |
| 918 | 1014 | ||
| ... | @@ -958,6 +1054,7 @@ pub const Type = extern union { | ... | @@ -958,6 +1054,7 @@ pub const Type = extern union { |
| 958 | pub fn fnIsVarArgs(self: Type) bool { | 1054 | pub fn fnIsVarArgs(self: Type) bool { |
| 959 | return switch (self.tag()) { | 1055 | return switch (self.tag()) { |
| 960 | .fn_noreturn_no_args => false, | 1056 | .fn_noreturn_no_args => false, |
| 1057 | .fn_void_no_args => false, | ||
| 961 | .fn_naked_noreturn_no_args => false, | 1058 | .fn_naked_noreturn_no_args => false, |
| 962 | .fn_ccc_void_no_args => false, | 1059 | .fn_ccc_void_no_args => false, |
| 963 | 1060 | ||
| ... | @@ -1033,6 +1130,7 @@ pub const Type = extern union { | ... | @@ -1033,6 +1130,7 @@ pub const Type = extern union { |
| 1033 | .@"null", | 1130 | .@"null", |
| 1034 | .@"undefined", | 1131 | .@"undefined", |
| 1035 | .fn_noreturn_no_args, | 1132 | .fn_noreturn_no_args, |
| 1133 | .fn_void_no_args, | ||
| 1036 | .fn_naked_noreturn_no_args, | 1134 | .fn_naked_noreturn_no_args, |
| 1037 | .fn_ccc_void_no_args, | 1135 | .fn_ccc_void_no_args, |
| 1038 | .array, | 1136 | .array, |
| ... | @@ -1070,6 +1168,7 @@ pub const Type = extern union { | ... | @@ -1070,6 +1168,7 @@ pub const Type = extern union { |
| 1070 | .type, | 1168 | .type, |
| 1071 | .anyerror, | 1169 | .anyerror, |
| 1072 | .fn_noreturn_no_args, | 1170 | .fn_noreturn_no_args, |
| 1171 | .fn_void_no_args, | ||
| 1073 | .fn_naked_noreturn_no_args, | 1172 | .fn_naked_noreturn_no_args, |
| 1074 | .fn_ccc_void_no_args, | 1173 | .fn_ccc_void_no_args, |
| 1075 | .single_const_pointer_to_comptime_int, | 1174 | .single_const_pointer_to_comptime_int, |
| ... | @@ -1126,6 +1225,7 @@ pub const Type = extern union { | ... | @@ -1126,6 +1225,7 @@ pub const Type = extern union { |
| 1126 | .type, | 1225 | .type, |
| 1127 | .anyerror, | 1226 | .anyerror, |
| 1128 | .fn_noreturn_no_args, | 1227 | .fn_noreturn_no_args, |
| 1228 | .fn_void_no_args, | ||
| 1129 | .fn_naked_noreturn_no_args, | 1229 | .fn_naked_noreturn_no_args, |
| 1130 | .fn_ccc_void_no_args, | 1230 | .fn_ccc_void_no_args, |
| 1131 | .single_const_pointer_to_comptime_int, | 1231 | .single_const_pointer_to_comptime_int, |
| ... | @@ -1180,6 +1280,7 @@ pub const Type = extern union { | ... | @@ -1180,6 +1280,7 @@ pub const Type = extern union { |
| 1180 | @"null", | 1280 | @"null", |
| 1181 | @"undefined", | 1281 | @"undefined", |
| 1182 | fn_noreturn_no_args, | 1282 | fn_noreturn_no_args, |
| 1283 | fn_void_no_args, | ||
| 1183 | fn_naked_noreturn_no_args, | 1284 | fn_naked_noreturn_no_args, |
| 1184 | fn_ccc_void_no_args, | 1285 | fn_ccc_void_no_args, |
| 1185 | single_const_pointer_to_comptime_int, | 1286 | single_const_pointer_to_comptime_int, |
src-self-hosted/value.zig+117-7| ... | @@ -49,6 +49,7 @@ pub const Value = extern union { | ... | @@ -49,6 +49,7 @@ pub const Value = extern union { |
| 49 | null_type, | 49 | null_type, |
| 50 | undefined_type, | 50 | undefined_type, |
| 51 | fn_noreturn_no_args_type, | 51 | fn_noreturn_no_args_type, |
| 52 | fn_void_no_args_type, | ||
| 52 | fn_naked_noreturn_no_args_type, | 53 | fn_naked_noreturn_no_args_type, |
| 53 | fn_ccc_void_no_args_type, | 54 | fn_ccc_void_no_args_type, |
| 54 | single_const_pointer_to_comptime_int_type, | 55 | single_const_pointer_to_comptime_int_type, |
| ... | @@ -78,8 +79,8 @@ pub const Value = extern union { | ... | @@ -78,8 +79,8 @@ pub const Value = extern union { |
| 78 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 79 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| 79 | }; | 80 | }; |
| 80 | 81 | ||
| 81 | pub fn initTag(comptime small_tag: Tag) Value { | 82 | pub fn initTag(small_tag: Tag) Value { |
| 82 | comptime assert(@enumToInt(small_tag) < Tag.no_payload_count); | 83 | assert(@enumToInt(small_tag) < Tag.no_payload_count); |
| 83 | return .{ .tag_if_small_enough = @enumToInt(small_tag) }; | 84 | return .{ .tag_if_small_enough = @enumToInt(small_tag) }; |
| 84 | } | 85 | } |
| 85 | 86 | ||
| ... | @@ -107,6 +108,109 @@ pub const Value = extern union { | ... | @@ -107,6 +108,109 @@ pub const Value = extern union { |
| 107 | return @fieldParentPtr(T, "base", self.ptr_otherwise); | 108 | return @fieldParentPtr(T, "base", self.ptr_otherwise); |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 111 | pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value { | ||
| 112 | if (self.tag_if_small_enough < Tag.no_payload_count) { | ||
| 113 | return Value{ .tag_if_small_enough = self.tag_if_small_enough }; | ||
| 114 | } else switch (self.ptr_otherwise.tag) { | ||
| 115 | .u8_type, | ||
| 116 | .i8_type, | ||
| 117 | .isize_type, | ||
| 118 | .usize_type, | ||
| 119 | .c_short_type, | ||
| 120 | .c_ushort_type, | ||
| 121 | .c_int_type, | ||
| 122 | .c_uint_type, | ||
| 123 | .c_long_type, | ||
| 124 | .c_ulong_type, | ||
| 125 | .c_longlong_type, | ||
| 126 | .c_ulonglong_type, | ||
| 127 | .c_longdouble_type, | ||
| 128 | .f16_type, | ||
| 129 | .f32_type, | ||
| 130 | .f64_type, | ||
| 131 | .f128_type, | ||
| 132 | .c_void_type, | ||
| 133 | .bool_type, | ||
| 134 | .void_type, | ||
| 135 | .type_type, | ||
| 136 | .anyerror_type, | ||
| 137 | .comptime_int_type, | ||
| 138 | .comptime_float_type, | ||
| 139 | .noreturn_type, | ||
| 140 | .null_type, | ||
| 141 | .undefined_type, | ||
| 142 | .fn_noreturn_no_args_type, | ||
| 143 | .fn_void_no_args_type, | ||
| 144 | .fn_naked_noreturn_no_args_type, | ||
| 145 | .fn_ccc_void_no_args_type, | ||
| 146 | .single_const_pointer_to_comptime_int_type, | ||
| 147 | .const_slice_u8_type, | ||
| 148 | .undef, | ||
| 149 | .zero, | ||
| 150 | .the_one_possible_value, | ||
| 151 | .null_value, | ||
| 152 | .bool_true, | ||
| 153 | .bool_false, | ||
| 154 | => unreachable, | ||
| 155 | |||
| 156 | .ty => { | ||
| 157 | const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise); | ||
| 158 | const new_payload = try allocator.create(Payload.Ty); | ||
| 159 | new_payload.* = .{ | ||
| 160 | .base = payload.base, | ||
| 161 | .ty = try payload.ty.copy(allocator), | ||
| 162 | }; | ||
| 163 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 164 | }, | ||
| 165 | .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64), | ||
| 166 | .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64), | ||
| 167 | .int_big_positive => { | ||
| 168 | @panic("TODO implement copying of big ints"); | ||
| 169 | }, | ||
| 170 | .int_big_negative => { | ||
| 171 | @panic("TODO implement copying of big ints"); | ||
| 172 | }, | ||
| 173 | .function => return self.copyPayloadShallow(allocator, Payload.Function), | ||
| 174 | .ref_val => { | ||
| 175 | const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise); | ||
| 176 | const new_payload = try allocator.create(Payload.RefVal); | ||
| 177 | new_payload.* = .{ | ||
| 178 | .base = payload.base, | ||
| 179 | .val = try payload.val.copy(allocator), | ||
| 180 | }; | ||
| 181 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 182 | }, | ||
| 183 | .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef), | ||
| 184 | .elem_ptr => { | ||
| 185 | const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise); | ||
| 186 | const new_payload = try allocator.create(Payload.ElemPtr); | ||
| 187 | new_payload.* = .{ | ||
| 188 | .base = payload.base, | ||
| 189 | .array_ptr = try payload.array_ptr.copy(allocator), | ||
| 190 | .index = payload.index, | ||
| 191 | }; | ||
| 192 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 193 | }, | ||
| 194 | .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes), | ||
| 195 | .repeated => { | ||
| 196 | const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise); | ||
| 197 | const new_payload = try allocator.create(Payload.Repeated); | ||
| 198 | new_payload.* = .{ | ||
| 199 | .base = payload.base, | ||
| 200 | .val = try payload.val.copy(allocator), | ||
| 201 | }; | ||
| 202 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 203 | }, | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value { | ||
| 208 | const payload = @fieldParentPtr(T, "base", self.ptr_otherwise); | ||
| 209 | const new_payload = try allocator.create(T); | ||
| 210 | new_payload.* = payload.*; | ||
| 211 | return Value{ .ptr_otherwise = &new_payload.base }; | ||
| 212 | } | ||
| 213 | |||
| 110 | pub fn format( | 214 | pub fn format( |
| 111 | self: Value, | 215 | self: Value, |
| 112 | comptime fmt: []const u8, | 216 | comptime fmt: []const u8, |
| ... | @@ -144,6 +248,7 @@ pub const Value = extern union { | ... | @@ -144,6 +248,7 @@ pub const Value = extern union { |
| 144 | .null_type => return out_stream.writeAll("@TypeOf(null)"), | 248 | .null_type => return out_stream.writeAll("@TypeOf(null)"), |
| 145 | .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"), | 249 | .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"), |
| 146 | .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"), | 250 | .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"), |
| 251 | .fn_void_no_args_type => return out_stream.writeAll("fn() void"), | ||
| 147 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | 252 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 148 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), | 253 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), |
| 149 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), | 254 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| ... | @@ -229,6 +334,7 @@ pub const Value = extern union { | ... | @@ -229,6 +334,7 @@ pub const Value = extern union { |
| 229 | .null_type => Type.initTag(.@"null"), | 334 | .null_type => Type.initTag(.@"null"), |
| 230 | .undefined_type => Type.initTag(.@"undefined"), | 335 | .undefined_type => Type.initTag(.@"undefined"), |
| 231 | .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args), | 336 | .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args), |
| 337 | .fn_void_no_args_type => Type.initTag(.fn_void_no_args), | ||
| 232 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), | 338 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), |
| 233 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), | 339 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), |
| 234 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), | 340 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| ... | @@ -286,6 +392,7 @@ pub const Value = extern union { | ... | @@ -286,6 +392,7 @@ pub const Value = extern union { |
| 286 | .null_type, | 392 | .null_type, |
| 287 | .undefined_type, | 393 | .undefined_type, |
| 288 | .fn_noreturn_no_args_type, | 394 | .fn_noreturn_no_args_type, |
| 395 | .fn_void_no_args_type, | ||
| 289 | .fn_naked_noreturn_no_args_type, | 396 | .fn_naked_noreturn_no_args_type, |
| 290 | .fn_ccc_void_no_args_type, | 397 | .fn_ccc_void_no_args_type, |
| 291 | .single_const_pointer_to_comptime_int_type, | 398 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -345,6 +452,7 @@ pub const Value = extern union { | ... | @@ -345,6 +452,7 @@ pub const Value = extern union { |
| 345 | .null_type, | 452 | .null_type, |
| 346 | .undefined_type, | 453 | .undefined_type, |
| 347 | .fn_noreturn_no_args_type, | 454 | .fn_noreturn_no_args_type, |
| 455 | .fn_void_no_args_type, | ||
| 348 | .fn_naked_noreturn_no_args_type, | 456 | .fn_naked_noreturn_no_args_type, |
| 349 | .fn_ccc_void_no_args_type, | 457 | .fn_ccc_void_no_args_type, |
| 350 | .single_const_pointer_to_comptime_int_type, | 458 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -405,6 +513,7 @@ pub const Value = extern union { | ... | @@ -405,6 +513,7 @@ pub const Value = extern union { |
| 405 | .null_type, | 513 | .null_type, |
| 406 | .undefined_type, | 514 | .undefined_type, |
| 407 | .fn_noreturn_no_args_type, | 515 | .fn_noreturn_no_args_type, |
| 516 | .fn_void_no_args_type, | ||
| 408 | .fn_naked_noreturn_no_args_type, | 517 | .fn_naked_noreturn_no_args_type, |
| 409 | .fn_ccc_void_no_args_type, | 518 | .fn_ccc_void_no_args_type, |
| 410 | .single_const_pointer_to_comptime_int_type, | 519 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -470,6 +579,7 @@ pub const Value = extern union { | ... | @@ -470,6 +579,7 @@ pub const Value = extern union { |
| 470 | .null_type, | 579 | .null_type, |
| 471 | .undefined_type, | 580 | .undefined_type, |
| 472 | .fn_noreturn_no_args_type, | 581 | .fn_noreturn_no_args_type, |
| 582 | .fn_void_no_args_type, | ||
| 473 | .fn_naked_noreturn_no_args_type, | 583 | .fn_naked_noreturn_no_args_type, |
| 474 | .fn_ccc_void_no_args_type, | 584 | .fn_ccc_void_no_args_type, |
| 475 | .single_const_pointer_to_comptime_int_type, | 585 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -564,6 +674,7 @@ pub const Value = extern union { | ... | @@ -564,6 +674,7 @@ pub const Value = extern union { |
| 564 | .null_type, | 674 | .null_type, |
| 565 | .undefined_type, | 675 | .undefined_type, |
| 566 | .fn_noreturn_no_args_type, | 676 | .fn_noreturn_no_args_type, |
| 677 | .fn_void_no_args_type, | ||
| 567 | .fn_naked_noreturn_no_args_type, | 678 | .fn_naked_noreturn_no_args_type, |
| 568 | .fn_ccc_void_no_args_type, | 679 | .fn_ccc_void_no_args_type, |
| 569 | .single_const_pointer_to_comptime_int_type, | 680 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -620,6 +731,7 @@ pub const Value = extern union { | ... | @@ -620,6 +731,7 @@ pub const Value = extern union { |
| 620 | .null_type, | 731 | .null_type, |
| 621 | .undefined_type, | 732 | .undefined_type, |
| 622 | .fn_noreturn_no_args_type, | 733 | .fn_noreturn_no_args_type, |
| 734 | .fn_void_no_args_type, | ||
| 623 | .fn_naked_noreturn_no_args_type, | 735 | .fn_naked_noreturn_no_args_type, |
| 624 | .fn_ccc_void_no_args_type, | 736 | .fn_ccc_void_no_args_type, |
| 625 | .single_const_pointer_to_comptime_int_type, | 737 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -721,6 +833,7 @@ pub const Value = extern union { | ... | @@ -721,6 +833,7 @@ pub const Value = extern union { |
| 721 | .null_type, | 833 | .null_type, |
| 722 | .undefined_type, | 834 | .undefined_type, |
| 723 | .fn_noreturn_no_args_type, | 835 | .fn_noreturn_no_args_type, |
| 836 | .fn_void_no_args_type, | ||
| 724 | .fn_naked_noreturn_no_args_type, | 837 | .fn_naked_noreturn_no_args_type, |
| 725 | .fn_ccc_void_no_args_type, | 838 | .fn_ccc_void_no_args_type, |
| 726 | .single_const_pointer_to_comptime_int_type, | 839 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -783,6 +896,7 @@ pub const Value = extern union { | ... | @@ -783,6 +896,7 @@ pub const Value = extern union { |
| 783 | .null_type, | 896 | .null_type, |
| 784 | .undefined_type, | 897 | .undefined_type, |
| 785 | .fn_noreturn_no_args_type, | 898 | .fn_noreturn_no_args_type, |
| 899 | .fn_void_no_args_type, | ||
| 786 | .fn_naked_noreturn_no_args_type, | 900 | .fn_naked_noreturn_no_args_type, |
| 787 | .fn_ccc_void_no_args_type, | 901 | .fn_ccc_void_no_args_type, |
| 788 | .single_const_pointer_to_comptime_int_type, | 902 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -862,6 +976,7 @@ pub const Value = extern union { | ... | @@ -862,6 +976,7 @@ pub const Value = extern union { |
| 862 | .null_type, | 976 | .null_type, |
| 863 | .undefined_type, | 977 | .undefined_type, |
| 864 | .fn_noreturn_no_args_type, | 978 | .fn_noreturn_no_args_type, |
| 979 | .fn_void_no_args_type, | ||
| 865 | .fn_naked_noreturn_no_args_type, | 980 | .fn_naked_noreturn_no_args_type, |
| 866 | .fn_ccc_void_no_args_type, | 981 | .fn_ccc_void_no_args_type, |
| 867 | .single_const_pointer_to_comptime_int_type, | 982 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -929,11 +1044,6 @@ pub const Value = extern union { | ... | @@ -929,11 +1044,6 @@ pub const Value = extern union { |
| 929 | len: u64, | 1044 | len: u64, |
| 930 | }; | 1045 | }; |
| 931 | 1046 | ||
| 932 | pub const SingleConstPtrType = struct { | ||
| 933 | base: Payload = Payload{ .tag = .single_const_ptr_type }, | ||
| 934 | elem_type: *Type, | ||
| 935 | }; | ||
| 936 | |||
| 937 | /// Represents a pointer to another immutable value. | 1047 | /// Represents a pointer to another immutable value. |
| 938 | pub const RefVal = struct { | 1048 | pub const RefVal = struct { |
| 939 | base: Payload = Payload{ .tag = .ref_val }, | 1049 | base: Payload = Payload{ .tag = .ref_val }, |
src-self-hosted/zir.zig+287-181| ... | @@ -12,27 +12,43 @@ const TypedValue = @import("TypedValue.zig"); | ... | @@ -12,27 +12,43 @@ const TypedValue = @import("TypedValue.zig"); |
| 12 | const ir = @import("ir.zig"); | 12 | const ir = @import("ir.zig"); |
| 13 | const IrModule = @import("Module.zig"); | 13 | const IrModule = @import("Module.zig"); |
| 14 | 14 | ||
| 15 | /// This struct is relevent only for the ZIR Module text format. It is not used for | ||
| 16 | /// semantic analysis of Zig source code. | ||
| 17 | pub const Decl = struct { | ||
| 18 | name: []const u8, | ||
| 19 | |||
| 20 | /// Hash of slice into the source of the part after the = and before the next instruction. | ||
| 21 | contents_hash: std.zig.SrcHash, | ||
| 22 | |||
| 23 | inst: *Inst, | ||
| 24 | }; | ||
| 25 | |||
| 15 | /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for | 26 | /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for |
| 16 | /// in-memory, analyzed instructions with types and values. | 27 | /// in-memory, analyzed instructions with types and values. |
| 17 | pub const Inst = struct { | 28 | pub const Inst = struct { |
| 18 | tag: Tag, | 29 | tag: Tag, |
| 19 | /// Byte offset into the source. | 30 | /// Byte offset into the source. |
| 20 | src: usize, | 31 | src: usize, |
| 21 | name: []const u8, | 32 | /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions. |
| 22 | 33 | analyzed_inst: ?*ir.Inst = null, | |
| 23 | /// Slice into the source of the part after the = and before the next instruction. | ||
| 24 | contents: []const u8 = &[0]u8{}, | ||
| 25 | 34 | ||
| 26 | /// These names are used directly as the instruction names in the text format. | 35 | /// These names are used directly as the instruction names in the text format. |
| 27 | pub const Tag = enum { | 36 | pub const Tag = enum { |
| 28 | breakpoint, | 37 | breakpoint, |
| 29 | call, | 38 | call, |
| 30 | compileerror, | 39 | compileerror, |
| 40 | /// Special case, has no textual representation. | ||
| 41 | @"const", | ||
| 31 | /// Represents a pointer to a global decl by name. | 42 | /// Represents a pointer to a global decl by name. |
| 32 | declref, | 43 | declref, |
| 44 | /// Represents a pointer to a global decl by string name. | ||
| 45 | declref_str, | ||
| 33 | /// The syntax `@foo` is equivalent to `declval("foo")`. | 46 | /// The syntax `@foo` is equivalent to `declval("foo")`. |
| 34 | /// declval is equivalent to declref followed by deref. | 47 | /// declval is equivalent to declref followed by deref. |
| 35 | declval, | 48 | declval, |
| 49 | /// Same as declval but the parameter is a `*Module.Decl` rather than a name. | ||
| 50 | declval_in_module, | ||
| 51 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. | ||
| 36 | str, | 52 | str, |
| 37 | int, | 53 | int, |
| 38 | ptrtoint, | 54 | ptrtoint, |
| ... | @@ -42,11 +58,11 @@ pub const Inst = struct { | ... | @@ -42,11 +58,11 @@ pub const Inst = struct { |
| 42 | @"asm", | 58 | @"asm", |
| 43 | @"unreachable", | 59 | @"unreachable", |
| 44 | @"return", | 60 | @"return", |
| 61 | returnvoid, | ||
| 45 | @"fn", | 62 | @"fn", |
| 63 | fntype, | ||
| 46 | @"export", | 64 | @"export", |
| 47 | primitive, | 65 | primitive, |
| 48 | ref, | ||
| 49 | fntype, | ||
| 50 | intcast, | 66 | intcast, |
| 51 | bitcast, | 67 | bitcast, |
| 52 | elemptr, | 68 | elemptr, |
| ... | @@ -62,8 +78,11 @@ pub const Inst = struct { | ... | @@ -62,8 +78,11 @@ pub const Inst = struct { |
| 62 | .breakpoint => Breakpoint, | 78 | .breakpoint => Breakpoint, |
| 63 | .call => Call, | 79 | .call => Call, |
| 64 | .declref => DeclRef, | 80 | .declref => DeclRef, |
| 81 | .declref_str => DeclRefStr, | ||
| 65 | .declval => DeclVal, | 82 | .declval => DeclVal, |
| 83 | .declval_in_module => DeclValInModule, | ||
| 66 | .compileerror => CompileError, | 84 | .compileerror => CompileError, |
| 85 | .@"const" => Const, | ||
| 67 | .str => Str, | 86 | .str => Str, |
| 68 | .int => Int, | 87 | .int => Int, |
| 69 | .ptrtoint => PtrToInt, | 88 | .ptrtoint => PtrToInt, |
| ... | @@ -73,10 +92,10 @@ pub const Inst = struct { | ... | @@ -73,10 +92,10 @@ pub const Inst = struct { |
| 73 | .@"asm" => Asm, | 92 | .@"asm" => Asm, |
| 74 | .@"unreachable" => Unreachable, | 93 | .@"unreachable" => Unreachable, |
| 75 | .@"return" => Return, | 94 | .@"return" => Return, |
| 95 | .returnvoid => ReturnVoid, | ||
| 76 | .@"fn" => Fn, | 96 | .@"fn" => Fn, |
| 77 | .@"export" => Export, | 97 | .@"export" => Export, |
| 78 | .primitive => Primitive, | 98 | .primitive => Primitive, |
| 79 | .ref => Ref, | ||
| 80 | .fntype => FnType, | 99 | .fntype => FnType, |
| 81 | .intcast => IntCast, | 100 | .intcast => IntCast, |
| 82 | .bitcast => BitCast, | 101 | .bitcast => BitCast, |
| ... | @@ -121,6 +140,16 @@ pub const Inst = struct { | ... | @@ -121,6 +140,16 @@ pub const Inst = struct { |
| 121 | pub const base_tag = Tag.declref; | 140 | pub const base_tag = Tag.declref; |
| 122 | base: Inst, | 141 | base: Inst, |
| 123 | 142 | ||
| 143 | positionals: struct { | ||
| 144 | name: []const u8, | ||
| 145 | }, | ||
| 146 | kw_args: struct {}, | ||
| 147 | }; | ||
| 148 | |||
| 149 | pub const DeclRefStr = struct { | ||
| 150 | pub const base_tag = Tag.declref_str; | ||
| 151 | base: Inst, | ||
| 152 | |||
| 124 | positionals: struct { | 153 | positionals: struct { |
| 125 | name: *Inst, | 154 | name: *Inst, |
| 126 | }, | 155 | }, |
| ... | @@ -137,6 +166,16 @@ pub const Inst = struct { | ... | @@ -137,6 +166,16 @@ pub const Inst = struct { |
| 137 | kw_args: struct {}, | 166 | kw_args: struct {}, |
| 138 | }; | 167 | }; |
| 139 | 168 | ||
| 169 | pub const DeclValInModule = struct { | ||
| 170 | pub const base_tag = Tag.declval_in_module; | ||
| 171 | base: Inst, | ||
| 172 | |||
| 173 | positionals: struct { | ||
| 174 | decl: *IrModule.Decl, | ||
| 175 | }, | ||
| 176 | kw_args: struct {}, | ||
| 177 | }; | ||
| 178 | |||
| 140 | pub const CompileError = struct { | 179 | pub const CompileError = struct { |
| 141 | pub const base_tag = Tag.compileerror; | 180 | pub const base_tag = Tag.compileerror; |
| 142 | base: Inst, | 181 | base: Inst, |
| ... | @@ -147,6 +186,16 @@ pub const Inst = struct { | ... | @@ -147,6 +186,16 @@ pub const Inst = struct { |
| 147 | kw_args: struct {}, | 186 | kw_args: struct {}, |
| 148 | }; | 187 | }; |
| 149 | 188 | ||
| 189 | pub const Const = struct { | ||
| 190 | pub const base_tag = Tag.@"const"; | ||
| 191 | base: Inst, | ||
| 192 | |||
| 193 | positionals: struct { | ||
| 194 | typed_value: TypedValue, | ||
| 195 | }, | ||
| 196 | kw_args: struct {}, | ||
| 197 | }; | ||
| 198 | |||
| 150 | pub const Str = struct { | 199 | pub const Str = struct { |
| 151 | pub const base_tag = Tag.str; | 200 | pub const base_tag = Tag.str; |
| 152 | base: Inst, | 201 | base: Inst, |
| ... | @@ -168,6 +217,7 @@ pub const Inst = struct { | ... | @@ -168,6 +217,7 @@ pub const Inst = struct { |
| 168 | }; | 217 | }; |
| 169 | 218 | ||
| 170 | pub const PtrToInt = struct { | 219 | pub const PtrToInt = struct { |
| 220 | pub const builtin_name = "@ptrToInt"; | ||
| 171 | pub const base_tag = Tag.ptrtoint; | 221 | pub const base_tag = Tag.ptrtoint; |
| 172 | base: Inst, | 222 | base: Inst, |
| 173 | 223 | ||
| ... | @@ -238,6 +288,16 @@ pub const Inst = struct { | ... | @@ -238,6 +288,16 @@ pub const Inst = struct { |
| 238 | pub const base_tag = Tag.@"return"; | 288 | pub const base_tag = Tag.@"return"; |
| 239 | base: Inst, | 289 | base: Inst, |
| 240 | 290 | ||
| 291 | positionals: struct { | ||
| 292 | operand: *Inst, | ||
| 293 | }, | ||
| 294 | kw_args: struct {}, | ||
| 295 | }; | ||
| 296 | |||
| 297 | pub const ReturnVoid = struct { | ||
| 298 | pub const base_tag = Tag.returnvoid; | ||
| 299 | base: Inst, | ||
| 300 | |||
| 241 | positionals: struct {}, | 301 | positionals: struct {}, |
| 242 | kw_args: struct {}, | 302 | kw_args: struct {}, |
| 243 | }; | 303 | }; |
| ... | @@ -253,23 +313,26 @@ pub const Inst = struct { | ... | @@ -253,23 +313,26 @@ pub const Inst = struct { |
| 253 | kw_args: struct {}, | 313 | kw_args: struct {}, |
| 254 | }; | 314 | }; |
| 255 | 315 | ||
| 256 | pub const Export = struct { | 316 | pub const FnType = struct { |
| 257 | pub const base_tag = Tag.@"export"; | 317 | pub const base_tag = Tag.fntype; |
| 258 | base: Inst, | 318 | base: Inst, |
| 259 | 319 | ||
| 260 | positionals: struct { | 320 | positionals: struct { |
| 261 | symbol_name: *Inst, | 321 | param_types: []*Inst, |
| 262 | value: *Inst, | 322 | return_type: *Inst, |
| 323 | }, | ||
| 324 | kw_args: struct { | ||
| 325 | cc: std.builtin.CallingConvention = .Unspecified, | ||
| 263 | }, | 326 | }, |
| 264 | kw_args: struct {}, | ||
| 265 | }; | 327 | }; |
| 266 | 328 | ||
| 267 | pub const Ref = struct { | 329 | pub const Export = struct { |
| 268 | pub const base_tag = Tag.ref; | 330 | pub const base_tag = Tag.@"export"; |
| 269 | base: Inst, | 331 | base: Inst, |
| 270 | 332 | ||
| 271 | positionals: struct { | 333 | positionals: struct { |
| 272 | operand: *Inst, | 334 | symbol_name: *Inst, |
| 335 | decl_name: []const u8, | ||
| 273 | }, | 336 | }, |
| 274 | kw_args: struct {}, | 337 | kw_args: struct {}, |
| 275 | }; | 338 | }; |
| ... | @@ -348,19 +411,6 @@ pub const Inst = struct { | ... | @@ -348,19 +411,6 @@ pub const Inst = struct { |
| 348 | }; | 411 | }; |
| 349 | }; | 412 | }; |
| 350 | 413 | ||
| 351 | pub const FnType = struct { | ||
| 352 | pub const base_tag = Tag.fntype; | ||
| 353 | base: Inst, | ||
| 354 | |||
| 355 | positionals: struct { | ||
| 356 | param_types: []*Inst, | ||
| 357 | return_type: *Inst, | ||
| 358 | }, | ||
| 359 | kw_args: struct { | ||
| 360 | cc: std.builtin.CallingConvention = .Unspecified, | ||
| 361 | }, | ||
| 362 | }; | ||
| 363 | |||
| 364 | pub const IntCast = struct { | 414 | pub const IntCast = struct { |
| 365 | pub const base_tag = Tag.intcast; | 415 | pub const base_tag = Tag.intcast; |
| 366 | base: Inst, | 416 | base: Inst, |
| ... | @@ -456,7 +506,7 @@ pub const ErrorMsg = struct { | ... | @@ -456,7 +506,7 @@ pub const ErrorMsg = struct { |
| 456 | }; | 506 | }; |
| 457 | 507 | ||
| 458 | pub const Module = struct { | 508 | pub const Module = struct { |
| 459 | decls: []*Inst, | 509 | decls: []*Decl, |
| 460 | arena: std.heap.ArenaAllocator, | 510 | arena: std.heap.ArenaAllocator, |
| 461 | error_msg: ?ErrorMsg = null, | 511 | error_msg: ?ErrorMsg = null, |
| 462 | 512 | ||
| ... | @@ -475,13 +525,33 @@ pub const Module = struct { | ... | @@ -475,13 +525,33 @@ pub const Module = struct { |
| 475 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; | 525 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; |
| 476 | } | 526 | } |
| 477 | 527 | ||
| 478 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize }); | 528 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 }); |
| 529 | |||
| 530 | const DeclAndIndex = struct { | ||
| 531 | decl: *Decl, | ||
| 532 | index: usize, | ||
| 533 | }; | ||
| 479 | 534 | ||
| 480 | /// TODO Look into making a table to speed this up. | 535 | /// TODO Look into making a table to speed this up. |
| 481 | pub fn findDecl(self: Module, name: []const u8) ?*Inst { | 536 | pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex { |
| 482 | for (self.decls) |decl| { | 537 | for (self.decls) |decl, i| { |
| 483 | if (mem.eql(u8, decl.name, name)) { | 538 | if (mem.eql(u8, decl.name, name)) { |
| 484 | return decl; | 539 | return DeclAndIndex{ |
| 540 | .decl = decl, | ||
| 541 | .index = i, | ||
| 542 | }; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | return null; | ||
| 546 | } | ||
| 547 | |||
| 548 | pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex { | ||
| 549 | for (self.decls) |decl, i| { | ||
| 550 | if (decl.inst == inst) { | ||
| 551 | return DeclAndIndex{ | ||
| 552 | .decl = decl, | ||
| 553 | .index = i, | ||
| 554 | }; | ||
| 485 | } | 555 | } |
| 486 | } | 556 | } |
| 487 | return null; | 557 | return null; |
| ... | @@ -497,18 +567,18 @@ pub const Module = struct { | ... | @@ -497,18 +567,18 @@ pub const Module = struct { |
| 497 | try inst_table.ensureCapacity(self.decls.len); | 567 | try inst_table.ensureCapacity(self.decls.len); |
| 498 | 568 | ||
| 499 | for (self.decls) |decl, decl_i| { | 569 | for (self.decls) |decl, decl_i| { |
| 500 | try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null }); | 570 | try inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name }); |
| 501 | 571 | ||
| 502 | if (decl.cast(Inst.Fn)) |fn_inst| { | 572 | if (decl.inst.cast(Inst.Fn)) |fn_inst| { |
| 503 | for (fn_inst.positionals.body.instructions) |inst, inst_i| { | 573 | for (fn_inst.positionals.body.instructions) |inst, inst_i| { |
| 504 | try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i }); | 574 | try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i, .name = undefined }); |
| 505 | } | 575 | } |
| 506 | } | 576 | } |
| 507 | } | 577 | } |
| 508 | 578 | ||
| 509 | for (self.decls) |decl, i| { | 579 | for (self.decls) |decl, i| { |
| 510 | try stream.print("@{} ", .{decl.name}); | 580 | try stream.print("@{} ", .{decl.name}); |
| 511 | try self.writeInstToStream(stream, decl, &inst_table); | 581 | try self.writeInstToStream(stream, decl.inst, &inst_table); |
| 512 | try stream.writeByte('\n'); | 582 | try stream.writeByte('\n'); |
| 513 | } | 583 | } |
| 514 | } | 584 | } |
| ... | @@ -516,38 +586,41 @@ pub const Module = struct { | ... | @@ -516,38 +586,41 @@ pub const Module = struct { |
| 516 | fn writeInstToStream( | 586 | fn writeInstToStream( |
| 517 | self: Module, | 587 | self: Module, |
| 518 | stream: var, | 588 | stream: var, |
| 519 | decl: *Inst, | 589 | inst: *Inst, |
| 520 | inst_table: *const InstPtrTable, | 590 | inst_table: *const InstPtrTable, |
| 521 | ) @TypeOf(stream).Error!void { | 591 | ) @TypeOf(stream).Error!void { |
| 522 | // TODO I tried implementing this with an inline for loop and hit a compiler bug | 592 | // TODO I tried implementing this with an inline for loop and hit a compiler bug |
| 523 | switch (decl.tag) { | 593 | switch (inst.tag) { |
| 524 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), | 594 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, inst, inst_table), |
| 525 | .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table), | 595 | .call => return self.writeInstToStreamGeneric(stream, .call, inst, inst_table), |
| 526 | .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table), | 596 | .declref => return self.writeInstToStreamGeneric(stream, .declref, inst, inst_table), |
| 527 | .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table), | 597 | .declref_str => return self.writeInstToStreamGeneric(stream, .declref_str, inst, inst_table), |
| 528 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table), | 598 | .declval => return self.writeInstToStreamGeneric(stream, .declval, inst, inst_table), |
| 529 | .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), | 599 | .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst, inst_table), |
| 530 | .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), | 600 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst, inst_table), |
| 531 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), | 601 | .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst, inst_table), |
| 532 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table), | 602 | .str => return self.writeInstToStreamGeneric(stream, .str, inst, inst_table), |
| 533 | .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table), | 603 | .int => return self.writeInstToStreamGeneric(stream, .int, inst, inst_table), |
| 534 | .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table), | 604 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, inst, inst_table), |
| 535 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table), | 605 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, inst, inst_table), |
| 536 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table), | 606 | .deref => return self.writeInstToStreamGeneric(stream, .deref, inst, inst_table), |
| 537 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table), | 607 | .as => return self.writeInstToStreamGeneric(stream, .as, inst, inst_table), |
| 538 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table), | 608 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", inst, inst_table), |
| 539 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table), | 609 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", inst, inst_table), |
| 540 | .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table), | 610 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", inst, inst_table), |
| 541 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table), | 611 | .returnvoid => return self.writeInstToStreamGeneric(stream, .returnvoid, inst, inst_table), |
| 542 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table), | 612 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", inst, inst_table), |
| 543 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table), | 613 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", inst, inst_table), |
| 544 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table), | 614 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, inst, inst_table), |
| 545 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table), | 615 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, inst, inst_table), |
| 546 | .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table), | 616 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, inst, inst_table), |
| 547 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table), | 617 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst, inst_table), |
| 548 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table), | 618 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst, inst_table), |
| 549 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table), | 619 | .add => return self.writeInstToStreamGeneric(stream, .add, inst, inst_table), |
| 550 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table), | 620 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, inst, inst_table), |
| 621 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, inst, inst_table), | ||
| 622 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, inst, inst_table), | ||
| 623 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, inst, inst_table), | ||
| 551 | } | 624 | } |
| 552 | } | 625 | } |
| 553 | 626 | ||
| ... | @@ -619,6 +692,8 @@ pub const Module = struct { | ... | @@ -619,6 +692,8 @@ pub const Module = struct { |
| 619 | bool => return stream.writeByte("01"[@boolToInt(param)]), | 692 | bool => return stream.writeByte("01"[@boolToInt(param)]), |
| 620 | []u8, []const u8 => return std.zig.renderStringLiteral(param, stream), | 693 | []u8, []const u8 => return std.zig.renderStringLiteral(param, stream), |
| 621 | BigIntConst => return stream.print("{}", .{param}), | 694 | BigIntConst => return stream.print("{}", .{param}), |
| 695 | TypedValue => unreachable, // this is a special case | ||
| 696 | *IrModule.Decl => unreachable, // this is a special case | ||
| 622 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), | 697 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| 623 | } | 698 | } |
| 624 | } | 699 | } |
| ... | @@ -628,13 +703,16 @@ pub const Module = struct { | ... | @@ -628,13 +703,16 @@ pub const Module = struct { |
| 628 | if (info.index) |i| { | 703 | if (info.index) |i| { |
| 629 | try stream.print("%{}", .{info.index}); | 704 | try stream.print("%{}", .{info.index}); |
| 630 | } else { | 705 | } else { |
| 631 | try stream.print("@{}", .{info.inst.name}); | 706 | try stream.print("@{}", .{info.name}); |
| 632 | } | 707 | } |
| 633 | } else if (inst.cast(Inst.DeclVal)) |decl_val| { | 708 | } else if (inst.cast(Inst.DeclVal)) |decl_val| { |
| 634 | try stream.print("@{}", .{decl_val.positionals.name}); | 709 | try stream.print("@{}", .{decl_val.positionals.name}); |
| 710 | } else if (inst.cast(Inst.DeclValInModule)) |decl_val| { | ||
| 711 | try stream.print("@{}", .{decl_val.positionals.decl.name}); | ||
| 635 | } else { | 712 | } else { |
| 636 | //try stream.print("?", .{}); | 713 | // This should be unreachable in theory, but since ZIR is used for debugging the compiler |
| 637 | unreachable; | 714 | // we output some debug text instead. |
| 715 | try stream.print("?{}?", .{@tagName(inst.tag)}); | ||
| 638 | } | 716 | } |
| 639 | } | 717 | } |
| 640 | }; | 718 | }; |
| ... | @@ -673,7 +751,7 @@ const Parser = struct { | ... | @@ -673,7 +751,7 @@ const Parser = struct { |
| 673 | arena: std.heap.ArenaAllocator, | 751 | arena: std.heap.ArenaAllocator, |
| 674 | i: usize, | 752 | i: usize, |
| 675 | source: [:0]const u8, | 753 | source: [:0]const u8, |
| 676 | decls: std.ArrayListUnmanaged(*Inst), | 754 | decls: std.ArrayListUnmanaged(*Decl), |
| 677 | global_name_map: *std.StringHashMap(usize), | 755 | global_name_map: *std.StringHashMap(usize), |
| 678 | error_msg: ?ErrorMsg = null, | 756 | error_msg: ?ErrorMsg = null, |
| 679 | unnamed_index: usize, | 757 | unnamed_index: usize, |
| ... | @@ -702,12 +780,12 @@ const Parser = struct { | ... | @@ -702,12 +780,12 @@ const Parser = struct { |
| 702 | skipSpace(self); | 780 | skipSpace(self); |
| 703 | try requireEatBytes(self, "="); | 781 | try requireEatBytes(self, "="); |
| 704 | skipSpace(self); | 782 | skipSpace(self); |
| 705 | const inst = try parseInstruction(self, &body_context, ident); | 783 | const decl = try parseInstruction(self, &body_context, ident); |
| 706 | const ident_index = body_context.instructions.items.len; | 784 | const ident_index = body_context.instructions.items.len; |
| 707 | if (try body_context.name_map.put(ident, ident_index)) |_| { | 785 | if (try body_context.name_map.put(ident, ident_index)) |_| { |
| 708 | return self.fail("redefinition of identifier '{}'", .{ident}); | 786 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| 709 | } | 787 | } |
| 710 | try body_context.instructions.append(inst); | 788 | try body_context.instructions.append(decl.inst); |
| 711 | continue; | 789 | continue; |
| 712 | }, | 790 | }, |
| 713 | ' ', '\n' => continue, | 791 | ' ', '\n' => continue, |
| ... | @@ -857,7 +935,7 @@ const Parser = struct { | ... | @@ -857,7 +935,7 @@ const Parser = struct { |
| 857 | return error.ParseFailure; | 935 | return error.ParseFailure; |
| 858 | } | 936 | } |
| 859 | 937 | ||
| 860 | fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst { | 938 | fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl { |
| 861 | const contents_start = self.i; | 939 | const contents_start = self.i; |
| 862 | const fn_name = try skipToAndOver(self, '('); | 940 | const fn_name = try skipToAndOver(self, '('); |
| 863 | inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { | 941 | inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { |
| ... | @@ -876,10 +954,9 @@ const Parser = struct { | ... | @@ -876,10 +954,9 @@ const Parser = struct { |
| 876 | body_ctx: ?*Body, | 954 | body_ctx: ?*Body, |
| 877 | inst_name: []const u8, | 955 | inst_name: []const u8, |
| 878 | contents_start: usize, | 956 | contents_start: usize, |
| 879 | ) InnerError!*Inst { | 957 | ) InnerError!*Decl { |
| 880 | const inst_specific = try self.arena.allocator.create(InstType); | 958 | const inst_specific = try self.arena.allocator.create(InstType); |
| 881 | inst_specific.base = .{ | 959 | inst_specific.base = .{ |
| 882 | .name = inst_name, | ||
| 883 | .src = self.i, | 960 | .src = self.i, |
| 884 | .tag = InstType.base_tag, | 961 | .tag = InstType.base_tag, |
| 885 | }; | 962 | }; |
| ... | @@ -929,10 +1006,15 @@ const Parser = struct { | ... | @@ -929,10 +1006,15 @@ const Parser = struct { |
| 929 | } | 1006 | } |
| 930 | try requireEatBytes(self, ")"); | 1007 | try requireEatBytes(self, ")"); |
| 931 | 1008 | ||
| 932 | inst_specific.base.contents = self.source[contents_start..self.i]; | 1009 | const decl = try self.arena.allocator.create(Decl); |
| 1010 | decl.* = .{ | ||
| 1011 | .name = inst_name, | ||
| 1012 | .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]), | ||
| 1013 | .inst = &inst_specific.base, | ||
| 1014 | }; | ||
| 933 | //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents }); | 1015 | //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents }); |
| 934 | 1016 | ||
| 935 | return &inst_specific.base; | 1017 | return decl; |
| 936 | } | 1018 | } |
| 937 | 1019 | ||
| 938 | fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T { | 1020 | fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T { |
| ... | @@ -978,6 +1060,8 @@ const Parser = struct { | ... | @@ -978,6 +1060,8 @@ const Parser = struct { |
| 978 | *Inst => return parseParameterInst(self, body_ctx), | 1060 | *Inst => return parseParameterInst(self, body_ctx), |
| 979 | []u8, []const u8 => return self.parseStringLiteral(), | 1061 | []u8, []const u8 => return self.parseStringLiteral(), |
| 980 | BigIntConst => return self.parseIntegerLiteral(), | 1062 | BigIntConst => return self.parseIntegerLiteral(), |
| 1063 | TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), | ||
| 1064 | *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), | ||
| 981 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), | 1065 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 982 | } | 1066 | } |
| 983 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); | 1067 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); |
| ... | @@ -1014,7 +1098,6 @@ const Parser = struct { | ... | @@ -1014,7 +1098,6 @@ const Parser = struct { |
| 1014 | const declval = try self.arena.allocator.create(Inst.DeclVal); | 1098 | const declval = try self.arena.allocator.create(Inst.DeclVal); |
| 1015 | declval.* = .{ | 1099 | declval.* = .{ |
| 1016 | .base = .{ | 1100 | .base = .{ |
| 1017 | .name = try self.generateName(), | ||
| 1018 | .src = src, | 1101 | .src = src, |
| 1019 | .tag = Inst.DeclVal.base_tag, | 1102 | .tag = Inst.DeclVal.base_tag, |
| 1020 | }, | 1103 | }, |
| ... | @@ -1027,7 +1110,7 @@ const Parser = struct { | ... | @@ -1027,7 +1110,7 @@ const Parser = struct { |
| 1027 | if (local_ref) { | 1110 | if (local_ref) { |
| 1028 | return body_ctx.?.instructions.items[kv.value]; | 1111 | return body_ctx.?.instructions.items[kv.value]; |
| 1029 | } else { | 1112 | } else { |
| 1030 | return self.decls.items[kv.value]; | 1113 | return self.decls.items[kv.value].inst; |
| 1031 | } | 1114 | } |
| 1032 | } | 1115 | } |
| 1033 | 1116 | ||
| ... | @@ -1046,7 +1129,7 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module { | ... | @@ -1046,7 +1129,7 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module { |
| 1046 | .old_module = &old_module, | 1129 | .old_module = &old_module, |
| 1047 | .next_auto_name = 0, | 1130 | .next_auto_name = 0, |
| 1048 | .names = std.StringHashMap(void).init(allocator), | 1131 | .names = std.StringHashMap(void).init(allocator), |
| 1049 | .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator), | 1132 | .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), |
| 1050 | }; | 1133 | }; |
| 1051 | defer ctx.decls.deinit(allocator); | 1134 | defer ctx.decls.deinit(allocator); |
| 1052 | defer ctx.names.deinit(); | 1135 | defer ctx.names.deinit(); |
| ... | @@ -1065,10 +1148,10 @@ const EmitZIR = struct { | ... | @@ -1065,10 +1148,10 @@ const EmitZIR = struct { |
| 1065 | allocator: *Allocator, | 1148 | allocator: *Allocator, |
| 1066 | arena: std.heap.ArenaAllocator, | 1149 | arena: std.heap.ArenaAllocator, |
| 1067 | old_module: *const IrModule, | 1150 | old_module: *const IrModule, |
| 1068 | decls: std.ArrayListUnmanaged(*Inst), | 1151 | decls: std.ArrayListUnmanaged(*Decl), |
| 1069 | names: std.StringHashMap(void), | 1152 | names: std.StringHashMap(void), |
| 1070 | next_auto_name: usize, | 1153 | next_auto_name: usize, |
| 1071 | primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst), | 1154 | primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl), |
| 1072 | 1155 | ||
| 1073 | fn emit(self: *EmitZIR) !void { | 1156 | fn emit(self: *EmitZIR) !void { |
| 1074 | // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced | 1157 | // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced |
| ... | @@ -1087,52 +1170,90 @@ const EmitZIR = struct { | ... | @@ -1087,52 +1170,90 @@ const EmitZIR = struct { |
| 1087 | } | 1170 | } |
| 1088 | std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { | 1171 | std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { |
| 1089 | fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool { | 1172 | fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool { |
| 1090 | return a.src < b.src; | 1173 | return a.src_index < b.src_index; |
| 1091 | } | 1174 | } |
| 1092 | }).lessThan); | 1175 | }).lessThan); |
| 1093 | 1176 | ||
| 1094 | // Emit all the decls. | 1177 | // Emit all the decls. |
| 1095 | for (src_decls.items) |ir_decl| { | 1178 | for (src_decls.items) |ir_decl| { |
| 1179 | switch (ir_decl.analysis) { | ||
| 1180 | .unreferenced => continue, | ||
| 1181 | .complete => {}, | ||
| 1182 | .in_progress => unreachable, | ||
| 1183 | .outdated => unreachable, | ||
| 1184 | |||
| 1185 | .sema_failure, | ||
| 1186 | .sema_failure_retryable, | ||
| 1187 | .codegen_failure, | ||
| 1188 | .dependency_failure, | ||
| 1189 | .codegen_failure_retryable, | ||
| 1190 | => if (self.old_module.failed_decls.getValue(ir_decl)) |err_msg| { | ||
| 1191 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | ||
| 1192 | fail_inst.* = .{ | ||
| 1193 | .base = .{ | ||
| 1194 | .src = ir_decl.src(), | ||
| 1195 | .tag = Inst.CompileError.base_tag, | ||
| 1196 | }, | ||
| 1197 | .positionals = .{ | ||
| 1198 | .msg = try self.arena.allocator.dupe(u8, err_msg.msg), | ||
| 1199 | }, | ||
| 1200 | .kw_args = .{}, | ||
| 1201 | }; | ||
| 1202 | const decl = try self.arena.allocator.create(Decl); | ||
| 1203 | decl.* = .{ | ||
| 1204 | .name = mem.spanZ(ir_decl.name), | ||
| 1205 | .contents_hash = undefined, | ||
| 1206 | .inst = &fail_inst.base, | ||
| 1207 | }; | ||
| 1208 | try self.decls.append(self.allocator, decl); | ||
| 1209 | continue; | ||
| 1210 | }, | ||
| 1211 | } | ||
| 1096 | if (self.old_module.export_owners.getValue(ir_decl)) |exports| { | 1212 | if (self.old_module.export_owners.getValue(ir_decl)) |exports| { |
| 1097 | for (exports) |module_export| { | 1213 | for (exports) |module_export| { |
| 1098 | const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name)); | ||
| 1099 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); | 1214 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); |
| 1100 | const export_inst = try self.arena.allocator.create(Inst.Export); | 1215 | const export_inst = try self.arena.allocator.create(Inst.Export); |
| 1101 | export_inst.* = .{ | 1216 | export_inst.* = .{ |
| 1102 | .base = .{ | 1217 | .base = .{ |
| 1103 | .name = try self.autoName(), | ||
| 1104 | .src = module_export.src, | 1218 | .src = module_export.src, |
| 1105 | .tag = Inst.Export.base_tag, | 1219 | .tag = Inst.Export.base_tag, |
| 1106 | }, | 1220 | }, |
| 1107 | .positionals = .{ | 1221 | .positionals = .{ |
| 1108 | .symbol_name = symbol_name, | 1222 | .symbol_name = symbol_name.inst, |
| 1109 | .value = declval, | 1223 | .decl_name = mem.spanZ(module_export.exported_decl.name), |
| 1110 | }, | 1224 | }, |
| 1111 | .kw_args = .{}, | 1225 | .kw_args = .{}, |
| 1112 | }; | 1226 | }; |
| 1113 | try self.decls.append(self.allocator, &export_inst.base); | 1227 | _ = try self.emitUnnamedDecl(&export_inst.base); |
| 1114 | } | 1228 | } |
| 1115 | } else { | 1229 | } else { |
| 1116 | const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value); | 1230 | const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value); |
| 1117 | new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name)); | 1231 | new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name)); |
| 1118 | } | 1232 | } |
| 1119 | } | 1233 | } |
| 1120 | } | 1234 | } |
| 1121 | 1235 | ||
| 1122 | fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst { | 1236 | const ZirBody = struct { |
| 1237 | inst_table: *std.AutoHashMap(*ir.Inst, *Inst), | ||
| 1238 | instructions: *std.ArrayList(*Inst), | ||
| 1239 | }; | ||
| 1240 | |||
| 1241 | fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst { | ||
| 1123 | if (inst.cast(ir.Inst.Constant)) |const_inst| { | 1242 | if (inst.cast(ir.Inst.Constant)) |const_inst| { |
| 1124 | const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: { | 1243 | const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: { |
| 1125 | const owner_decl = func_pl.func.owner_decl; | 1244 | const owner_decl = func_pl.func.owner_decl; |
| 1126 | break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); | 1245 | break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); |
| 1127 | } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: { | 1246 | } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: { |
| 1128 | break :blk try self.emitDeclRef(inst.src, declref.decl); | 1247 | const decl_ref = try self.emitDeclRef(inst.src, declref.decl); |
| 1248 | try new_body.instructions.append(decl_ref); | ||
| 1249 | break :blk decl_ref; | ||
| 1129 | } else blk: { | 1250 | } else blk: { |
| 1130 | break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); | 1251 | break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst; |
| 1131 | }; | 1252 | }; |
| 1132 | try inst_table.putNoClobber(inst, new_decl); | 1253 | try new_body.inst_table.putNoClobber(inst, new_inst); |
| 1133 | return new_decl; | 1254 | return new_inst; |
| 1134 | } else { | 1255 | } else { |
| 1135 | return inst_table.getValue(inst).?; | 1256 | return new_body.inst_table.getValue(inst).?; |
| 1136 | } | 1257 | } |
| 1137 | } | 1258 | } |
| 1138 | 1259 | ||
| ... | @@ -1140,7 +1261,6 @@ const EmitZIR = struct { | ... | @@ -1140,7 +1261,6 @@ const EmitZIR = struct { |
| 1140 | const declval = try self.arena.allocator.create(Inst.DeclVal); | 1261 | const declval = try self.arena.allocator.create(Inst.DeclVal); |
| 1141 | declval.* = .{ | 1262 | declval.* = .{ |
| 1142 | .base = .{ | 1263 | .base = .{ |
| 1143 | .name = try self.autoName(), | ||
| 1144 | .src = src, | 1264 | .src = src, |
| 1145 | .tag = Inst.DeclVal.base_tag, | 1265 | .tag = Inst.DeclVal.base_tag, |
| 1146 | }, | 1266 | }, |
| ... | @@ -1150,12 +1270,11 @@ const EmitZIR = struct { | ... | @@ -1150,12 +1270,11 @@ const EmitZIR = struct { |
| 1150 | return &declval.base; | 1270 | return &declval.base; |
| 1151 | } | 1271 | } |
| 1152 | 1272 | ||
| 1153 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { | 1273 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl { |
| 1154 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); | 1274 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); |
| 1155 | const int_inst = try self.arena.allocator.create(Inst.Int); | 1275 | const int_inst = try self.arena.allocator.create(Inst.Int); |
| 1156 | int_inst.* = .{ | 1276 | int_inst.* = .{ |
| 1157 | .base = .{ | 1277 | .base = .{ |
| 1158 | .name = try self.autoName(), | ||
| 1159 | .src = src, | 1278 | .src = src, |
| 1160 | .tag = Inst.Int.base_tag, | 1279 | .tag = Inst.Int.base_tag, |
| 1161 | }, | 1280 | }, |
| ... | @@ -1164,34 +1283,29 @@ const EmitZIR = struct { | ... | @@ -1164,34 +1283,29 @@ const EmitZIR = struct { |
| 1164 | }, | 1283 | }, |
| 1165 | .kw_args = .{}, | 1284 | .kw_args = .{}, |
| 1166 | }; | 1285 | }; |
| 1167 | try self.decls.append(self.allocator, &int_inst.base); | 1286 | return self.emitUnnamedDecl(&int_inst.base); |
| 1168 | return &int_inst.base; | ||
| 1169 | } | 1287 | } |
| 1170 | 1288 | ||
| 1171 | fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst { | 1289 | fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst { |
| 1172 | const declval = try self.emitDeclVal(src, mem.spanZ(decl.name)); | 1290 | const declref_inst = try self.arena.allocator.create(Inst.DeclRef); |
| 1173 | const ref_inst = try self.arena.allocator.create(Inst.Ref); | 1291 | declref_inst.* = .{ |
| 1174 | ref_inst.* = .{ | ||
| 1175 | .base = .{ | 1292 | .base = .{ |
| 1176 | .name = try self.autoName(), | ||
| 1177 | .src = src, | 1293 | .src = src, |
| 1178 | .tag = Inst.Ref.base_tag, | 1294 | .tag = Inst.DeclRef.base_tag, |
| 1179 | }, | 1295 | }, |
| 1180 | .positionals = .{ | 1296 | .positionals = .{ |
| 1181 | .operand = declval, | 1297 | .name = mem.spanZ(module_decl.name), |
| 1182 | }, | 1298 | }, |
| 1183 | .kw_args = .{}, | 1299 | .kw_args = .{}, |
| 1184 | }; | 1300 | }; |
| 1185 | try self.decls.append(self.allocator, &ref_inst.base); | 1301 | return &declref_inst.base; |
| 1186 | |||
| 1187 | return &ref_inst.base; | ||
| 1188 | } | 1302 | } |
| 1189 | 1303 | ||
| 1190 | fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst { | 1304 | fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl { |
| 1191 | const allocator = &self.arena.allocator; | 1305 | const allocator = &self.arena.allocator; |
| 1192 | if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| { | 1306 | if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| { |
| 1193 | const decl = decl_ref.decl; | 1307 | const decl = decl_ref.decl; |
| 1194 | return self.emitDeclRef(src, decl); | 1308 | return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl)); |
| 1195 | } | 1309 | } |
| 1196 | switch (typed_value.ty.zigTypeTag()) { | 1310 | switch (typed_value.ty.zigTypeTag()) { |
| 1197 | .Pointer => { | 1311 | .Pointer => { |
| ... | @@ -1218,18 +1332,16 @@ const EmitZIR = struct { | ... | @@ -1218,18 +1332,16 @@ const EmitZIR = struct { |
| 1218 | const as_inst = try self.arena.allocator.create(Inst.As); | 1332 | const as_inst = try self.arena.allocator.create(Inst.As); |
| 1219 | as_inst.* = .{ | 1333 | as_inst.* = .{ |
| 1220 | .base = .{ | 1334 | .base = .{ |
| 1221 | .name = try self.autoName(), | ||
| 1222 | .src = src, | 1335 | .src = src, |
| 1223 | .tag = Inst.As.base_tag, | 1336 | .tag = Inst.As.base_tag, |
| 1224 | }, | 1337 | }, |
| 1225 | .positionals = .{ | 1338 | .positionals = .{ |
| 1226 | .dest_type = try self.emitType(src, typed_value.ty), | 1339 | .dest_type = (try self.emitType(src, typed_value.ty)).inst, |
| 1227 | .value = try self.emitComptimeIntVal(src, typed_value.val), | 1340 | .value = (try self.emitComptimeIntVal(src, typed_value.val)).inst, |
| 1228 | }, | 1341 | }, |
| 1229 | .kw_args = .{}, | 1342 | .kw_args = .{}, |
| 1230 | }; | 1343 | }; |
| 1231 | 1344 | return self.emitUnnamedDecl(&as_inst.base); | |
| 1232 | return &as_inst.base; | ||
| 1233 | }, | 1345 | }, |
| 1234 | .Type => { | 1346 | .Type => { |
| 1235 | const ty = typed_value.val.toType(); | 1347 | const ty = typed_value.val.toType(); |
| ... | @@ -1255,7 +1367,6 @@ const EmitZIR = struct { | ... | @@ -1255,7 +1367,6 @@ const EmitZIR = struct { |
| 1255 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | 1367 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1256 | fail_inst.* = .{ | 1368 | fail_inst.* = .{ |
| 1257 | .base = .{ | 1369 | .base = .{ |
| 1258 | .name = try self.autoName(), | ||
| 1259 | .src = src, | 1370 | .src = src, |
| 1260 | .tag = Inst.CompileError.base_tag, | 1371 | .tag = Inst.CompileError.base_tag, |
| 1261 | }, | 1372 | }, |
| ... | @@ -1270,7 +1381,6 @@ const EmitZIR = struct { | ... | @@ -1270,7 +1381,6 @@ const EmitZIR = struct { |
| 1270 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | 1381 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1271 | fail_inst.* = .{ | 1382 | fail_inst.* = .{ |
| 1272 | .base = .{ | 1383 | .base = .{ |
| 1273 | .name = try self.autoName(), | ||
| 1274 | .src = src, | 1384 | .src = src, |
| 1275 | .tag = Inst.CompileError.base_tag, | 1385 | .tag = Inst.CompileError.base_tag, |
| 1276 | }, | 1386 | }, |
| ... | @@ -1283,7 +1393,7 @@ const EmitZIR = struct { | ... | @@ -1283,7 +1393,7 @@ const EmitZIR = struct { |
| 1283 | }, | 1393 | }, |
| 1284 | } | 1394 | } |
| 1285 | 1395 | ||
| 1286 | const fn_type = try self.emitType(src, module_fn.fn_type); | 1396 | const fn_type = try self.emitType(src, typed_value.ty); |
| 1287 | 1397 | ||
| 1288 | const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); | 1398 | const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); |
| 1289 | mem.copy(*Inst, arena_instrs, instructions.items); | 1399 | mem.copy(*Inst, arena_instrs, instructions.items); |
| ... | @@ -1291,18 +1401,16 @@ const EmitZIR = struct { | ... | @@ -1291,18 +1401,16 @@ const EmitZIR = struct { |
| 1291 | const fn_inst = try self.arena.allocator.create(Inst.Fn); | 1401 | const fn_inst = try self.arena.allocator.create(Inst.Fn); |
| 1292 | fn_inst.* = .{ | 1402 | fn_inst.* = .{ |
| 1293 | .base = .{ | 1403 | .base = .{ |
| 1294 | .name = try self.autoName(), | ||
| 1295 | .src = src, | 1404 | .src = src, |
| 1296 | .tag = Inst.Fn.base_tag, | 1405 | .tag = Inst.Fn.base_tag, |
| 1297 | }, | 1406 | }, |
| 1298 | .positionals = .{ | 1407 | .positionals = .{ |
| 1299 | .fn_type = fn_type, | 1408 | .fn_type = fn_type.inst, |
| 1300 | .body = .{ .instructions = arena_instrs }, | 1409 | .body = .{ .instructions = arena_instrs }, |
| 1301 | }, | 1410 | }, |
| 1302 | .kw_args = .{}, | 1411 | .kw_args = .{}, |
| 1303 | }; | 1412 | }; |
| 1304 | try self.decls.append(self.allocator, &fn_inst.base); | 1413 | return self.emitUnnamedDecl(&fn_inst.base); |
| 1305 | return &fn_inst.base; | ||
| 1306 | }, | 1414 | }, |
| 1307 | .Array => { | 1415 | .Array => { |
| 1308 | // TODO more checks to make sure this can be emitted as a string literal | 1416 | // TODO more checks to make sure this can be emitted as a string literal |
| ... | @@ -1318,7 +1426,6 @@ const EmitZIR = struct { | ... | @@ -1318,7 +1426,6 @@ const EmitZIR = struct { |
| 1318 | const str_inst = try self.arena.allocator.create(Inst.Str); | 1426 | const str_inst = try self.arena.allocator.create(Inst.Str); |
| 1319 | str_inst.* = .{ | 1427 | str_inst.* = .{ |
| 1320 | .base = .{ | 1428 | .base = .{ |
| 1321 | .name = try self.autoName(), | ||
| 1322 | .src = src, | 1429 | .src = src, |
| 1323 | .tag = Inst.Str.base_tag, | 1430 | .tag = Inst.Str.base_tag, |
| 1324 | }, | 1431 | }, |
| ... | @@ -1327,8 +1434,7 @@ const EmitZIR = struct { | ... | @@ -1327,8 +1434,7 @@ const EmitZIR = struct { |
| 1327 | }, | 1434 | }, |
| 1328 | .kw_args = .{}, | 1435 | .kw_args = .{}, |
| 1329 | }; | 1436 | }; |
| 1330 | try self.decls.append(self.allocator, &str_inst.base); | 1437 | return self.emitUnnamedDecl(&str_inst.base); |
| 1331 | return &str_inst.base; | ||
| 1332 | }, | 1438 | }, |
| 1333 | .Void => return self.emitPrimitive(src, .void_value), | 1439 | .Void => return self.emitPrimitive(src, .void_value), |
| 1334 | else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), | 1440 | else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), |
| ... | @@ -1339,7 +1445,6 @@ const EmitZIR = struct { | ... | @@ -1339,7 +1445,6 @@ const EmitZIR = struct { |
| 1339 | const new_inst = try self.arena.allocator.create(T); | 1445 | const new_inst = try self.arena.allocator.create(T); |
| 1340 | new_inst.* = .{ | 1446 | new_inst.* = .{ |
| 1341 | .base = .{ | 1447 | .base = .{ |
| 1342 | .name = try self.autoName(), | ||
| 1343 | .src = src, | 1448 | .src = src, |
| 1344 | .tag = T.base_tag, | 1449 | .tag = T.base_tag, |
| 1345 | }, | 1450 | }, |
| ... | @@ -1355,6 +1460,10 @@ const EmitZIR = struct { | ... | @@ -1355,6 +1460,10 @@ const EmitZIR = struct { |
| 1355 | inst_table: *std.AutoHashMap(*ir.Inst, *Inst), | 1460 | inst_table: *std.AutoHashMap(*ir.Inst, *Inst), |
| 1356 | instructions: *std.ArrayList(*Inst), | 1461 | instructions: *std.ArrayList(*Inst), |
| 1357 | ) Allocator.Error!void { | 1462 | ) Allocator.Error!void { |
| 1463 | const new_body = ZirBody{ | ||
| 1464 | .inst_table = inst_table, | ||
| 1465 | .instructions = instructions, | ||
| 1466 | }; | ||
| 1358 | for (body.instructions) |inst| { | 1467 | for (body.instructions) |inst| { |
| 1359 | const new_inst = switch (inst.tag) { | 1468 | const new_inst = switch (inst.tag) { |
| 1360 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), | 1469 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), |
| ... | @@ -1364,16 +1473,15 @@ const EmitZIR = struct { | ... | @@ -1364,16 +1473,15 @@ const EmitZIR = struct { |
| 1364 | 1473 | ||
| 1365 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | 1474 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); |
| 1366 | for (args) |*elem, i| { | 1475 | for (args) |*elem, i| { |
| 1367 | elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]); | 1476 | elem.* = try self.resolveInst(new_body, old_inst.args.args[i]); |
| 1368 | } | 1477 | } |
| 1369 | new_inst.* = .{ | 1478 | new_inst.* = .{ |
| 1370 | .base = .{ | 1479 | .base = .{ |
| 1371 | .name = try self.autoName(), | ||
| 1372 | .src = inst.src, | 1480 | .src = inst.src, |
| 1373 | .tag = Inst.Call.base_tag, | 1481 | .tag = Inst.Call.base_tag, |
| 1374 | }, | 1482 | }, |
| 1375 | .positionals = .{ | 1483 | .positionals = .{ |
| 1376 | .func = try self.resolveInst(inst_table, old_inst.args.func), | 1484 | .func = try self.resolveInst(new_body, old_inst.args.func), |
| 1377 | .args = args, | 1485 | .args = args, |
| 1378 | }, | 1486 | }, |
| 1379 | .kw_args = .{}, | 1487 | .kw_args = .{}, |
| ... | @@ -1381,7 +1489,22 @@ const EmitZIR = struct { | ... | @@ -1381,7 +1489,22 @@ const EmitZIR = struct { |
| 1381 | break :blk &new_inst.base; | 1489 | break :blk &new_inst.base; |
| 1382 | }, | 1490 | }, |
| 1383 | .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), | 1491 | .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), |
| 1384 | .ret => try self.emitTrivial(inst.src, Inst.Return), | 1492 | .ret => blk: { |
| 1493 | const old_inst = inst.cast(ir.Inst.Ret).?; | ||
| 1494 | const new_inst = try self.arena.allocator.create(Inst.Return); | ||
| 1495 | new_inst.* = .{ | ||
| 1496 | .base = .{ | ||
| 1497 | .src = inst.src, | ||
| 1498 | .tag = Inst.Return.base_tag, | ||
| 1499 | }, | ||
| 1500 | .positionals = .{ | ||
| 1501 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | ||
| 1502 | }, | ||
| 1503 | .kw_args = .{}, | ||
| 1504 | }; | ||
| 1505 | break :blk &new_inst.base; | ||
| 1506 | }, | ||
| 1507 | .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid), | ||
| 1385 | .constant => unreachable, // excluded from function bodies | 1508 | .constant => unreachable, // excluded from function bodies |
| 1386 | .assembly => blk: { | 1509 | .assembly => blk: { |
| 1387 | const old_inst = inst.cast(ir.Inst.Assembly).?; | 1510 | const old_inst = inst.cast(ir.Inst.Assembly).?; |
| ... | @@ -1389,33 +1512,32 @@ const EmitZIR = struct { | ... | @@ -1389,33 +1512,32 @@ const EmitZIR = struct { |
| 1389 | 1512 | ||
| 1390 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); | 1513 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); |
| 1391 | for (inputs) |*elem, i| { | 1514 | for (inputs) |*elem, i| { |
| 1392 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]); | 1515 | elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.inputs[i])).inst; |
| 1393 | } | 1516 | } |
| 1394 | 1517 | ||
| 1395 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); | 1518 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); |
| 1396 | for (clobbers) |*elem, i| { | 1519 | for (clobbers) |*elem, i| { |
| 1397 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]); | 1520 | elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i])).inst; |
| 1398 | } | 1521 | } |
| 1399 | 1522 | ||
| 1400 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | 1523 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); |
| 1401 | for (args) |*elem, i| { | 1524 | for (args) |*elem, i| { |
| 1402 | elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]); | 1525 | elem.* = try self.resolveInst(new_body, old_inst.args.args[i]); |
| 1403 | } | 1526 | } |
| 1404 | 1527 | ||
| 1405 | new_inst.* = .{ | 1528 | new_inst.* = .{ |
| 1406 | .base = .{ | 1529 | .base = .{ |
| 1407 | .name = try self.autoName(), | ||
| 1408 | .src = inst.src, | 1530 | .src = inst.src, |
| 1409 | .tag = Inst.Asm.base_tag, | 1531 | .tag = Inst.Asm.base_tag, |
| 1410 | }, | 1532 | }, |
| 1411 | .positionals = .{ | 1533 | .positionals = .{ |
| 1412 | .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source), | 1534 | .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst, |
| 1413 | .return_type = try self.emitType(inst.src, inst.ty), | 1535 | .return_type = (try self.emitType(inst.src, inst.ty)).inst, |
| 1414 | }, | 1536 | }, |
| 1415 | .kw_args = .{ | 1537 | .kw_args = .{ |
| 1416 | .@"volatile" = old_inst.args.is_volatile, | 1538 | .@"volatile" = old_inst.args.is_volatile, |
| 1417 | .output = if (old_inst.args.output) |o| | 1539 | .output = if (old_inst.args.output) |o| |
| 1418 | try self.emitStringLiteral(inst.src, o) | 1540 | (try self.emitStringLiteral(inst.src, o)).inst |
| 1419 | else | 1541 | else |
| 1420 | null, | 1542 | null, |
| 1421 | .inputs = inputs, | 1543 | .inputs = inputs, |
| ... | @@ -1430,12 +1552,11 @@ const EmitZIR = struct { | ... | @@ -1430,12 +1552,11 @@ const EmitZIR = struct { |
| 1430 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); | 1552 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); |
| 1431 | new_inst.* = .{ | 1553 | new_inst.* = .{ |
| 1432 | .base = .{ | 1554 | .base = .{ |
| 1433 | .name = try self.autoName(), | ||
| 1434 | .src = inst.src, | 1555 | .src = inst.src, |
| 1435 | .tag = Inst.PtrToInt.base_tag, | 1556 | .tag = Inst.PtrToInt.base_tag, |
| 1436 | }, | 1557 | }, |
| 1437 | .positionals = .{ | 1558 | .positionals = .{ |
| 1438 | .ptr = try self.resolveInst(inst_table, old_inst.args.ptr), | 1559 | .ptr = try self.resolveInst(new_body, old_inst.args.ptr), |
| 1439 | }, | 1560 | }, |
| 1440 | .kw_args = .{}, | 1561 | .kw_args = .{}, |
| 1441 | }; | 1562 | }; |
| ... | @@ -1446,13 +1567,12 @@ const EmitZIR = struct { | ... | @@ -1446,13 +1567,12 @@ const EmitZIR = struct { |
| 1446 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | 1567 | const new_inst = try self.arena.allocator.create(Inst.BitCast); |
| 1447 | new_inst.* = .{ | 1568 | new_inst.* = .{ |
| 1448 | .base = .{ | 1569 | .base = .{ |
| 1449 | .name = try self.autoName(), | ||
| 1450 | .src = inst.src, | 1570 | .src = inst.src, |
| 1451 | .tag = Inst.BitCast.base_tag, | 1571 | .tag = Inst.BitCast.base_tag, |
| 1452 | }, | 1572 | }, |
| 1453 | .positionals = .{ | 1573 | .positionals = .{ |
| 1454 | .dest_type = try self.emitType(inst.src, inst.ty), | 1574 | .dest_type = (try self.emitType(inst.src, inst.ty)).inst, |
| 1455 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | 1575 | .operand = try self.resolveInst(new_body, old_inst.args.operand), |
| 1456 | }, | 1576 | }, |
| 1457 | .kw_args = .{}, | 1577 | .kw_args = .{}, |
| 1458 | }; | 1578 | }; |
| ... | @@ -1463,13 +1583,12 @@ const EmitZIR = struct { | ... | @@ -1463,13 +1583,12 @@ const EmitZIR = struct { |
| 1463 | const new_inst = try self.arena.allocator.create(Inst.Cmp); | 1583 | const new_inst = try self.arena.allocator.create(Inst.Cmp); |
| 1464 | new_inst.* = .{ | 1584 | new_inst.* = .{ |
| 1465 | .base = .{ | 1585 | .base = .{ |
| 1466 | .name = try self.autoName(), | ||
| 1467 | .src = inst.src, | 1586 | .src = inst.src, |
| 1468 | .tag = Inst.Cmp.base_tag, | 1587 | .tag = Inst.Cmp.base_tag, |
| 1469 | }, | 1588 | }, |
| 1470 | .positionals = .{ | 1589 | .positionals = .{ |
| 1471 | .lhs = try self.resolveInst(inst_table, old_inst.args.lhs), | 1590 | .lhs = try self.resolveInst(new_body, old_inst.args.lhs), |
| 1472 | .rhs = try self.resolveInst(inst_table, old_inst.args.rhs), | 1591 | .rhs = try self.resolveInst(new_body, old_inst.args.rhs), |
| 1473 | .op = old_inst.args.op, | 1592 | .op = old_inst.args.op, |
| 1474 | }, | 1593 | }, |
| 1475 | .kw_args = .{}, | 1594 | .kw_args = .{}, |
| ... | @@ -1491,12 +1610,11 @@ const EmitZIR = struct { | ... | @@ -1491,12 +1610,11 @@ const EmitZIR = struct { |
| 1491 | const new_inst = try self.arena.allocator.create(Inst.CondBr); | 1610 | const new_inst = try self.arena.allocator.create(Inst.CondBr); |
| 1492 | new_inst.* = .{ | 1611 | new_inst.* = .{ |
| 1493 | .base = .{ | 1612 | .base = .{ |
| 1494 | .name = try self.autoName(), | ||
| 1495 | .src = inst.src, | 1613 | .src = inst.src, |
| 1496 | .tag = Inst.CondBr.base_tag, | 1614 | .tag = Inst.CondBr.base_tag, |
| 1497 | }, | 1615 | }, |
| 1498 | .positionals = .{ | 1616 | .positionals = .{ |
| 1499 | .condition = try self.resolveInst(inst_table, old_inst.args.condition), | 1617 | .condition = try self.resolveInst(new_body, old_inst.args.condition), |
| 1500 | .true_body = .{ .instructions = true_body.toOwnedSlice() }, | 1618 | .true_body = .{ .instructions = true_body.toOwnedSlice() }, |
| 1501 | .false_body = .{ .instructions = false_body.toOwnedSlice() }, | 1619 | .false_body = .{ .instructions = false_body.toOwnedSlice() }, |
| 1502 | }, | 1620 | }, |
| ... | @@ -1509,12 +1627,11 @@ const EmitZIR = struct { | ... | @@ -1509,12 +1627,11 @@ const EmitZIR = struct { |
| 1509 | const new_inst = try self.arena.allocator.create(Inst.IsNull); | 1627 | const new_inst = try self.arena.allocator.create(Inst.IsNull); |
| 1510 | new_inst.* = .{ | 1628 | new_inst.* = .{ |
| 1511 | .base = .{ | 1629 | .base = .{ |
| 1512 | .name = try self.autoName(), | ||
| 1513 | .src = inst.src, | 1630 | .src = inst.src, |
| 1514 | .tag = Inst.IsNull.base_tag, | 1631 | .tag = Inst.IsNull.base_tag, |
| 1515 | }, | 1632 | }, |
| 1516 | .positionals = .{ | 1633 | .positionals = .{ |
| 1517 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | 1634 | .operand = try self.resolveInst(new_body, old_inst.args.operand), |
| 1518 | }, | 1635 | }, |
| 1519 | .kw_args = .{}, | 1636 | .kw_args = .{}, |
| 1520 | }; | 1637 | }; |
| ... | @@ -1525,12 +1642,11 @@ const EmitZIR = struct { | ... | @@ -1525,12 +1642,11 @@ const EmitZIR = struct { |
| 1525 | const new_inst = try self.arena.allocator.create(Inst.IsNonNull); | 1642 | const new_inst = try self.arena.allocator.create(Inst.IsNonNull); |
| 1526 | new_inst.* = .{ | 1643 | new_inst.* = .{ |
| 1527 | .base = .{ | 1644 | .base = .{ |
| 1528 | .name = try self.autoName(), | ||
| 1529 | .src = inst.src, | 1645 | .src = inst.src, |
| 1530 | .tag = Inst.IsNonNull.base_tag, | 1646 | .tag = Inst.IsNonNull.base_tag, |
| 1531 | }, | 1647 | }, |
| 1532 | .positionals = .{ | 1648 | .positionals = .{ |
| 1533 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | 1649 | .operand = try self.resolveInst(new_body, old_inst.args.operand), |
| 1534 | }, | 1650 | }, |
| 1535 | .kw_args = .{}, | 1651 | .kw_args = .{}, |
| 1536 | }; | 1652 | }; |
| ... | @@ -1542,7 +1658,7 @@ const EmitZIR = struct { | ... | @@ -1542,7 +1658,7 @@ const EmitZIR = struct { |
| 1542 | } | 1658 | } |
| 1543 | } | 1659 | } |
| 1544 | 1660 | ||
| 1545 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { | 1661 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl { |
| 1546 | switch (ty.tag()) { | 1662 | switch (ty.tag()) { |
| 1547 | .isize => return self.emitPrimitive(src, .isize), | 1663 | .isize => return self.emitPrimitive(src, .isize), |
| 1548 | .usize => return self.emitPrimitive(src, .usize), | 1664 | .usize => return self.emitPrimitive(src, .usize), |
| ... | @@ -1575,26 +1691,24 @@ const EmitZIR = struct { | ... | @@ -1575,26 +1691,24 @@ const EmitZIR = struct { |
| 1575 | ty.fnParamTypes(param_types); | 1691 | ty.fnParamTypes(param_types); |
| 1576 | const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len); | 1692 | const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len); |
| 1577 | for (param_types) |param_type, i| { | 1693 | for (param_types) |param_type, i| { |
| 1578 | emitted_params[i] = try self.emitType(src, param_type); | 1694 | emitted_params[i] = (try self.emitType(src, param_type)).inst; |
| 1579 | } | 1695 | } |
| 1580 | 1696 | ||
| 1581 | const fntype_inst = try self.arena.allocator.create(Inst.FnType); | 1697 | const fntype_inst = try self.arena.allocator.create(Inst.FnType); |
| 1582 | fntype_inst.* = .{ | 1698 | fntype_inst.* = .{ |
| 1583 | .base = .{ | 1699 | .base = .{ |
| 1584 | .name = try self.autoName(), | ||
| 1585 | .src = src, | 1700 | .src = src, |
| 1586 | .tag = Inst.FnType.base_tag, | 1701 | .tag = Inst.FnType.base_tag, |
| 1587 | }, | 1702 | }, |
| 1588 | .positionals = .{ | 1703 | .positionals = .{ |
| 1589 | .param_types = emitted_params, | 1704 | .param_types = emitted_params, |
| 1590 | .return_type = try self.emitType(src, ty.fnReturnType()), | 1705 | .return_type = (try self.emitType(src, ty.fnReturnType())).inst, |
| 1591 | }, | 1706 | }, |
| 1592 | .kw_args = .{ | 1707 | .kw_args = .{ |
| 1593 | .cc = ty.fnCallingConvention(), | 1708 | .cc = ty.fnCallingConvention(), |
| 1594 | }, | 1709 | }, |
| 1595 | }; | 1710 | }; |
| 1596 | try self.decls.append(self.allocator, &fntype_inst.base); | 1711 | return self.emitUnnamedDecl(&fntype_inst.base); |
| 1597 | return &fntype_inst.base; | ||
| 1598 | }, | 1712 | }, |
| 1599 | else => std.debug.panic("TODO implement emitType for {}", .{ty}), | 1713 | else => std.debug.panic("TODO implement emitType for {}", .{ty}), |
| 1600 | }, | 1714 | }, |
| ... | @@ -1613,13 +1727,12 @@ const EmitZIR = struct { | ... | @@ -1613,13 +1727,12 @@ const EmitZIR = struct { |
| 1613 | } | 1727 | } |
| 1614 | } | 1728 | } |
| 1615 | 1729 | ||
| 1616 | fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst { | 1730 | fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl { |
| 1617 | const gop = try self.primitive_table.getOrPut(tag); | 1731 | const gop = try self.primitive_table.getOrPut(tag); |
| 1618 | if (!gop.found_existing) { | 1732 | if (!gop.found_existing) { |
| 1619 | const primitive_inst = try self.arena.allocator.create(Inst.Primitive); | 1733 | const primitive_inst = try self.arena.allocator.create(Inst.Primitive); |
| 1620 | primitive_inst.* = .{ | 1734 | primitive_inst.* = .{ |
| 1621 | .base = .{ | 1735 | .base = .{ |
| 1622 | .name = try self.autoName(), | ||
| 1623 | .src = src, | 1736 | .src = src, |
| 1624 | .tag = Inst.Primitive.base_tag, | 1737 | .tag = Inst.Primitive.base_tag, |
| 1625 | }, | 1738 | }, |
| ... | @@ -1628,17 +1741,15 @@ const EmitZIR = struct { | ... | @@ -1628,17 +1741,15 @@ const EmitZIR = struct { |
| 1628 | }, | 1741 | }, |
| 1629 | .kw_args = .{}, | 1742 | .kw_args = .{}, |
| 1630 | }; | 1743 | }; |
| 1631 | try self.decls.append(self.allocator, &primitive_inst.base); | 1744 | gop.kv.value = try self.emitUnnamedDecl(&primitive_inst.base); |
| 1632 | gop.kv.value = &primitive_inst.base; | ||
| 1633 | } | 1745 | } |
| 1634 | return gop.kv.value; | 1746 | return gop.kv.value; |
| 1635 | } | 1747 | } |
| 1636 | 1748 | ||
| 1637 | fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst { | 1749 | fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl { |
| 1638 | const str_inst = try self.arena.allocator.create(Inst.Str); | 1750 | const str_inst = try self.arena.allocator.create(Inst.Str); |
| 1639 | str_inst.* = .{ | 1751 | str_inst.* = .{ |
| 1640 | .base = .{ | 1752 | .base = .{ |
| 1641 | .name = try self.autoName(), | ||
| 1642 | .src = src, | 1753 | .src = src, |
| 1643 | .tag = Inst.Str.base_tag, | 1754 | .tag = Inst.Str.base_tag, |
| 1644 | }, | 1755 | }, |
| ... | @@ -1647,22 +1758,17 @@ const EmitZIR = struct { | ... | @@ -1647,22 +1758,17 @@ const EmitZIR = struct { |
| 1647 | }, | 1758 | }, |
| 1648 | .kw_args = .{}, | 1759 | .kw_args = .{}, |
| 1649 | }; | 1760 | }; |
| 1650 | try self.decls.append(self.allocator, &str_inst.base); | 1761 | return self.emitUnnamedDecl(&str_inst.base); |
| 1762 | } | ||
| 1651 | 1763 | ||
| 1652 | const ref_inst = try self.arena.allocator.create(Inst.Ref); | 1764 | fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl { |
| 1653 | ref_inst.* = .{ | 1765 | const decl = try self.arena.allocator.create(Decl); |
| 1654 | .base = .{ | 1766 | decl.* = .{ |
| 1655 | .name = try self.autoName(), | 1767 | .name = try self.autoName(), |
| 1656 | .src = src, | 1768 | .contents_hash = undefined, |
| 1657 | .tag = Inst.Ref.base_tag, | 1769 | .inst = inst, |
| 1658 | }, | ||
| 1659 | .positionals = .{ | ||
| 1660 | .operand = &str_inst.base, | ||
| 1661 | }, | ||
| 1662 | .kw_args = .{}, | ||
| 1663 | }; | 1770 | }; |
| 1664 | try self.decls.append(self.allocator, &ref_inst.base); | 1771 | try self.decls.append(self.allocator, decl); |
| 1665 | 1772 | return decl; | |
| 1666 | return &ref_inst.base; | ||
| 1667 | } | 1773 | } |
| 1668 | }; | 1774 | }; |
src/codegen.cpp+18-2| ... | @@ -5583,8 +5583,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir | ... | @@ -5583,8 +5583,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir |
| 5583 | 5583 | ||
| 5584 | bool val_is_undef = value_is_all_undef(g, instruction->byte->value); | 5584 | bool val_is_undef = value_is_all_undef(g, instruction->byte->value); |
| 5585 | LLVMValueRef fill_char; | 5585 | LLVMValueRef fill_char; |
| 5586 | if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.base.scope)) { | 5586 | if (val_is_undef) { |
| 5587 | fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); | 5587 | if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) { |
| 5588 | fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); | ||
| 5589 | } else { | ||
| 5590 | return nullptr; | ||
| 5591 | } | ||
| 5588 | } else { | 5592 | } else { |
| 5589 | fill_char = ir_llvm_value(g, instruction->byte); | 5593 | fill_char = ir_llvm_value(g, instruction->byte); |
| 5590 | } | 5594 | } |
| ... | @@ -7473,6 +7477,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -7473,6 +7477,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 7473 | continue; | 7477 | continue; |
| 7474 | } | 7478 | } |
| 7475 | ZigValue *field_val = const_val->data.x_struct.fields[i]; | 7479 | ZigValue *field_val = const_val->data.x_struct.fields[i]; |
| 7480 | if (field_val == nullptr) { | ||
| 7481 | add_node_error(g, type_struct_field->decl_node, | ||
| 7482 | buf_sprintf("compiler bug: generating const value for struct field '%s'", | ||
| 7483 | buf_ptr(type_struct_field->name))); | ||
| 7484 | codegen_report_errors_and_exit(g); | ||
| 7485 | } | ||
| 7476 | ZigType *field_type = field_val->type; | 7486 | ZigType *field_type = field_val->type; |
| 7477 | assert(field_type != nullptr); | 7487 | assert(field_type != nullptr); |
| 7478 | if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) { | 7488 | if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) { |
| ... | @@ -9465,9 +9475,15 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa | ... | @@ -9465,9 +9475,15 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa |
| 9465 | const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", | 9475 | const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", |
| 9466 | buf_ptr(g->zig_lib_dir))); | 9476 | buf_ptr(g->zig_lib_dir))); |
| 9467 | 9477 | ||
| 9478 | const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", | ||
| 9479 | buf_ptr(g->zig_lib_dir))); | ||
| 9480 | |||
| 9468 | args.append("-isystem"); | 9481 | args.append("-isystem"); |
| 9469 | args.append(libcxx_include_path); | 9482 | args.append(libcxx_include_path); |
| 9470 | 9483 | ||
| 9484 | args.append("-isystem"); | ||
| 9485 | args.append(libcxxabi_include_path); | ||
| 9486 | |||
| 9471 | if (target_abi_is_musl(g->zig_target->abi)) { | 9487 | if (target_abi_is_musl(g->zig_target->abi)) { |
| 9472 | args.append("-D_LIBCPP_HAS_MUSL_LIBC"); | 9488 | args.append("-D_LIBCPP_HAS_MUSL_LIBC"); |
| 9473 | } | 9489 | } |
test/stage2/compare_output.zig+115-22| ... | @@ -1,28 +1,121 @@ | ... | @@ -1,28 +1,121 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | // self-hosted does not yet support PE executable files / COFF object files | ||
| 4 | // or mach-o files. So we do these test cases cross compiling for x86_64-linux. | ||
| 5 | const linux_x64 = std.zig.CrossTarget{ | ||
| 6 | .cpu_arch = .x86_64, | ||
| 7 | .os_tag = .linux, | ||
| 8 | }; | ||
| 3 | 9 | ||
| 4 | pub fn addCases(ctx: *TestContext) !void { | 10 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // TODO: re-enable these tests. | 11 | if (std.Target.current.os.tag != .linux or |
| 6 | // https://github.com/ziglang/zig/issues/1364 | 12 | std.Target.current.cpu.arch != .x86_64) |
| 13 | { | ||
| 14 | // TODO implement self-hosted PE (.exe file) linking | ||
| 15 | // TODO implement more ZIR so we don't depend on x86_64-linux | ||
| 16 | return; | ||
| 17 | } | ||
| 7 | 18 | ||
| 8 | //// hello world | 19 | { |
| 9 | //try ctx.testCompareOutputLibC( | 20 | var case = ctx.addExe("hello world with updates", linux_x64); |
| 10 | // \\extern fn puts([*]const u8) void; | 21 | // Regular old hello world |
| 11 | // \\pub export fn main() c_int { | 22 | case.addCompareOutput( |
| 12 | // \\ puts("Hello, world!"); | 23 | \\export fn _start() noreturn { |
| 13 | // \\ return 0; | 24 | \\ print(); |
| 14 | // \\} | 25 | \\ |
| 15 | //, "Hello, world!" ++ std.cstr.line_sep); | 26 | \\ exit(); |
| 16 | 27 | \\} | |
| 17 | //// function calling another function | 28 | \\ |
| 18 | //try ctx.testCompareOutputLibC( | 29 | \\fn print() void { |
| 19 | // \\extern fn puts(s: [*]const u8) void; | 30 | \\ asm volatile ("syscall" |
| 20 | // \\pub export fn main() c_int { | 31 | \\ : |
| 21 | // \\ return foo("OK"); | 32 | \\ : [number] "{rax}" (1), |
| 22 | // \\} | 33 | \\ [arg1] "{rdi}" (1), |
| 23 | // \\fn foo(s: [*]const u8) c_int { | 34 | \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), |
| 24 | // \\ puts(s); | 35 | \\ [arg3] "{rdx}" (14) |
| 25 | // \\ return 0; | 36 | \\ : "rcx", "r11", "memory" |
| 26 | // \\} | 37 | \\ ); |
| 27 | //, "OK" ++ std.cstr.line_sep); | 38 | \\ return; |
| 39 | \\} | ||
| 40 | \\ | ||
| 41 | \\fn exit() noreturn { | ||
| 42 | \\ asm volatile ("syscall" | ||
| 43 | \\ : | ||
| 44 | \\ : [number] "{rax}" (231), | ||
| 45 | \\ [arg1] "{rdi}" (0) | ||
| 46 | \\ : "rcx", "r11", "memory" | ||
| 47 | \\ ); | ||
| 48 | \\ unreachable; | ||
| 49 | \\} | ||
| 50 | , | ||
| 51 | "Hello, World!\n", | ||
| 52 | ); | ||
| 53 | // Now change the message only | ||
| 54 | case.addCompareOutput( | ||
| 55 | \\export fn _start() noreturn { | ||
| 56 | \\ print(); | ||
| 57 | \\ | ||
| 58 | \\ exit(); | ||
| 59 | \\} | ||
| 60 | \\ | ||
| 61 | \\fn print() void { | ||
| 62 | \\ asm volatile ("syscall" | ||
| 63 | \\ : | ||
| 64 | \\ : [number] "{rax}" (1), | ||
| 65 | \\ [arg1] "{rdi}" (1), | ||
| 66 | \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), | ||
| 67 | \\ [arg3] "{rdx}" (104) | ||
| 68 | \\ : "rcx", "r11", "memory" | ||
| 69 | \\ ); | ||
| 70 | \\ return; | ||
| 71 | \\} | ||
| 72 | \\ | ||
| 73 | \\fn exit() noreturn { | ||
| 74 | \\ asm volatile ("syscall" | ||
| 75 | \\ : | ||
| 76 | \\ : [number] "{rax}" (231), | ||
| 77 | \\ [arg1] "{rdi}" (0) | ||
| 78 | \\ : "rcx", "r11", "memory" | ||
| 79 | \\ ); | ||
| 80 | \\ unreachable; | ||
| 81 | \\} | ||
| 82 | , | ||
| 83 | "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", | ||
| 84 | ); | ||
| 85 | // Now we print it twice. | ||
| 86 | case.addCompareOutput( | ||
| 87 | \\export fn _start() noreturn { | ||
| 88 | \\ print(); | ||
| 89 | \\ print(); | ||
| 90 | \\ | ||
| 91 | \\ exit(); | ||
| 92 | \\} | ||
| 93 | \\ | ||
| 94 | \\fn print() void { | ||
| 95 | \\ asm volatile ("syscall" | ||
| 96 | \\ : | ||
| 97 | \\ : [number] "{rax}" (1), | ||
| 98 | \\ [arg1] "{rdi}" (1), | ||
| 99 | \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), | ||
| 100 | \\ [arg3] "{rdx}" (104) | ||
| 101 | \\ : "rcx", "r11", "memory" | ||
| 102 | \\ ); | ||
| 103 | \\ return; | ||
| 104 | \\} | ||
| 105 | \\ | ||
| 106 | \\fn exit() noreturn { | ||
| 107 | \\ asm volatile ("syscall" | ||
| 108 | \\ : | ||
| 109 | \\ : [number] "{rax}" (231), | ||
| 110 | \\ [arg1] "{rdi}" (0) | ||
| 111 | \\ : "rcx", "r11", "memory" | ||
| 112 | \\ ); | ||
| 113 | \\ unreachable; | ||
| 114 | \\} | ||
| 115 | , | ||
| 116 | \\What is up? This is a longer message that will force the data to be relocated in virtual address space. | ||
| 117 | \\What is up? This is a longer message that will force the data to be relocated in virtual address space. | ||
| 118 | \\ | ||
| 119 | ); | ||
| 120 | } | ||
| 28 | } | 121 | } |
test/stage2/compile_errors.zig+3-5| ... | @@ -27,9 +27,8 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -27,9 +27,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 27 | \\ %0 = call(@notafunc, []) | 27 | \\ %0 = call(@notafunc, []) |
| 28 | \\}) | 28 | \\}) |
| 29 | \\@0 = str("_start") | 29 | \\@0 = str("_start") |
| 30 | \\@1 = ref(@0) | 30 | \\@1 = export(@0, "start") |
| 31 | \\@2 = export(@1, @start) | 31 | , &[_][]const u8{":5:13: error: decl 'notafunc' not found"}); |
| 32 | , &[_][]const u8{":5:13: error: use of undeclared identifier 'notafunc'"}); | ||
| 33 | 32 | ||
| 34 | // TODO: this error should occur at the call site, not the fntype decl | 33 | // TODO: this error should occur at the call site, not the fntype decl |
| 35 | ctx.addZIRError("call naked function", linux_x64, | 34 | ctx.addZIRError("call naked function", linux_x64, |
| ... | @@ -41,8 +40,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -41,8 +40,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 41 | \\ %0 = call(@s, []) | 40 | \\ %0 = call(@s, []) |
| 42 | \\}) | 41 | \\}) |
| 43 | \\@0 = str("_start") | 42 | \\@0 = str("_start") |
| 44 | \\@1 = ref(@0) | 43 | \\@1 = export(@0, "start") |
| 45 | \\@2 = export(@1, @start) | ||
| 46 | , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"}); | 44 | , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"}); |
| 47 | 45 | ||
| 48 | // TODO: re-enable these tests. | 46 | // TODO: re-enable these tests. |
test/stage2/zir.zig+152-333| ... | @@ -14,23 +14,21 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -14,23 +14,21 @@ pub fn addCases(ctx: *TestContext) void { |
| 14 | \\@fnty = fntype([], @void, cc=C) | 14 | \\@fnty = fntype([], @void, cc=C) |
| 15 | \\ | 15 | \\ |
| 16 | \\@9 = str("entry") | 16 | \\@9 = str("entry") |
| 17 | \\@10 = ref(@9) | 17 | \\@11 = export(@9, "entry") |
| 18 | \\@11 = export(@10, @entry) | ||
| 19 | \\ | 18 | \\ |
| 20 | \\@entry = fn(@fnty, { | 19 | \\@entry = fn(@fnty, { |
| 21 | \\ %11 = return() | 20 | \\ %11 = returnvoid() |
| 22 | \\}) | 21 | \\}) |
| 23 | , | 22 | , |
| 24 | \\@void = primitive(void) | 23 | \\@void = primitive(void) |
| 25 | \\@fnty = fntype([], @void, cc=C) | 24 | \\@fnty = fntype([], @void, cc=C) |
| 26 | \\@9 = str("entry") | 25 | \\@9 = declref("9$0") |
| 27 | \\@10 = ref(@9) | 26 | \\@9$0 = str("entry") |
| 28 | \\@unnamed$6 = str("entry") | 27 | \\@unnamed$4 = str("entry") |
| 29 | \\@unnamed$7 = ref(@unnamed$6) | 28 | \\@unnamed$5 = export(@unnamed$4, "entry") |
| 30 | \\@unnamed$8 = export(@unnamed$7, @entry) | 29 | \\@unnamed$6 = fntype([], @void, cc=C) |
| 31 | \\@unnamed$10 = fntype([], @void, cc=C) | 30 | \\@entry = fn(@unnamed$6, { |
| 32 | \\@entry = fn(@unnamed$10, { | 31 | \\ %0 = returnvoid() |
| 33 | \\ %0 = return() | ||
| 34 | \\}) | 32 | \\}) |
| 35 | \\ | 33 | \\ |
| 36 | ); | 34 | ); |
| ... | @@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void { |
| 45 | \\ | 43 | \\ |
| 46 | \\@entry = fn(@fnty, { | 44 | \\@entry = fn(@fnty, { |
| 47 | \\ %a = str("\x32\x08\x01\x0a") | 45 | \\ %a = str("\x32\x08\x01\x0a") |
| 48 | \\ %aref = ref(%a) | 46 | \\ %eptr0 = elemptr(%a, @0) |
| 49 | \\ %eptr0 = elemptr(%aref, @0) | 47 | \\ %eptr1 = elemptr(%a, @1) |
| 50 | \\ %eptr1 = elemptr(%aref, @1) | 48 | \\ %eptr2 = elemptr(%a, @2) |
| 51 | \\ %eptr2 = elemptr(%aref, @2) | 49 | \\ %eptr3 = elemptr(%a, @3) |
| 52 | \\ %eptr3 = elemptr(%aref, @3) | ||
| 53 | \\ %v0 = deref(%eptr0) | 50 | \\ %v0 = deref(%eptr0) |
| 54 | \\ %v1 = deref(%eptr1) | 51 | \\ %v1 = deref(%eptr1) |
| 55 | \\ %v2 = deref(%eptr2) | 52 | \\ %v2 = deref(%eptr2) |
| ... | @@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void { |
| 61 | \\ %expected = int(69) | 58 | \\ %expected = int(69) |
| 62 | \\ %ok = cmp(%result, eq, %expected) | 59 | \\ %ok = cmp(%result, eq, %expected) |
| 63 | \\ %10 = condbr(%ok, { | 60 | \\ %10 = condbr(%ok, { |
| 64 | \\ %11 = return() | 61 | \\ %11 = returnvoid() |
| 65 | \\ }, { | 62 | \\ }, { |
| 66 | \\ %12 = breakpoint() | 63 | \\ %12 = breakpoint() |
| 67 | \\ }) | 64 | \\ }) |
| 68 | \\}) | 65 | \\}) |
| 69 | \\ | 66 | \\ |
| 70 | \\@9 = str("entry") | 67 | \\@9 = str("entry") |
| 71 | \\@10 = ref(@9) | 68 | \\@11 = export(@9, "entry") |
| 72 | \\@11 = export(@10, @entry) | ||
| 73 | , | 69 | , |
| 74 | \\@void = primitive(void) | 70 | \\@void = primitive(void) |
| 75 | \\@fnty = fntype([], @void, cc=C) | 71 | \\@fnty = fntype([], @void, cc=C) |
| ... | @@ -77,65 +73,62 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -77,65 +73,62 @@ pub fn addCases(ctx: *TestContext) void { |
| 77 | \\@1 = int(1) | 73 | \\@1 = int(1) |
| 78 | \\@2 = int(2) | 74 | \\@2 = int(2) |
| 79 | \\@3 = int(3) | 75 | \\@3 = int(3) |
| 80 | \\@unnamed$7 = fntype([], @void, cc=C) | 76 | \\@unnamed$6 = fntype([], @void, cc=C) |
| 81 | \\@entry = fn(@unnamed$7, { | 77 | \\@entry = fn(@unnamed$6, { |
| 82 | \\ %0 = return() | 78 | \\ %0 = returnvoid() |
| 83 | \\}) | 79 | \\}) |
| 84 | \\@a = str("2\x08\x01\n") | 80 | \\@entry$1 = str("2\x08\x01\n") |
| 85 | \\@9 = str("entry") | 81 | \\@9 = declref("9$0") |
| 86 | \\@10 = ref(@9) | 82 | \\@9$0 = str("entry") |
| 87 | \\@unnamed$14 = str("entry") | 83 | \\@unnamed$11 = str("entry") |
| 88 | \\@unnamed$15 = ref(@unnamed$14) | 84 | \\@unnamed$12 = export(@unnamed$11, "entry") |
| 89 | \\@unnamed$16 = export(@unnamed$15, @entry) | ||
| 90 | \\ | 85 | \\ |
| 91 | ); | 86 | ); |
| 92 | 87 | ||
| 93 | { | 88 | { |
| 94 | var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64); | 89 | var case = ctx.addObjZIR("reference cycle with compile error in the cycle", linux_x64); |
| 95 | case.addTransform( | 90 | case.addTransform( |
| 96 | \\@void = primitive(void) | 91 | \\@void = primitive(void) |
| 97 | \\@fnty = fntype([], @void, cc=C) | 92 | \\@fnty = fntype([], @void, cc=C) |
| 98 | \\ | 93 | \\ |
| 99 | \\@9 = str("entry") | 94 | \\@9 = str("entry") |
| 100 | \\@10 = ref(@9) | 95 | \\@11 = export(@9, "entry") |
| 101 | \\@11 = export(@10, @entry) | ||
| 102 | \\ | 96 | \\ |
| 103 | \\@entry = fn(@fnty, { | 97 | \\@entry = fn(@fnty, { |
| 104 | \\ %0 = call(@a, []) | 98 | \\ %0 = call(@a, []) |
| 105 | \\ %1 = return() | 99 | \\ %1 = returnvoid() |
| 106 | \\}) | 100 | \\}) |
| 107 | \\ | 101 | \\ |
| 108 | \\@a = fn(@fnty, { | 102 | \\@a = fn(@fnty, { |
| 109 | \\ %0 = call(@b, []) | 103 | \\ %0 = call(@b, []) |
| 110 | \\ %1 = return() | 104 | \\ %1 = returnvoid() |
| 111 | \\}) | 105 | \\}) |
| 112 | \\ | 106 | \\ |
| 113 | \\@b = fn(@fnty, { | 107 | \\@b = fn(@fnty, { |
| 114 | \\ %0 = call(@a, []) | 108 | \\ %0 = call(@a, []) |
| 115 | \\ %1 = return() | 109 | \\ %1 = returnvoid() |
| 116 | \\}) | 110 | \\}) |
| 117 | , | 111 | , |
| 118 | \\@void = primitive(void) | 112 | \\@void = primitive(void) |
| 119 | \\@fnty = fntype([], @void, cc=C) | 113 | \\@fnty = fntype([], @void, cc=C) |
| 120 | \\@9 = str("entry") | 114 | \\@9 = declref("9$0") |
| 121 | \\@10 = ref(@9) | 115 | \\@9$0 = str("entry") |
| 122 | \\@unnamed$6 = str("entry") | 116 | \\@unnamed$4 = str("entry") |
| 123 | \\@unnamed$7 = ref(@unnamed$6) | 117 | \\@unnamed$5 = export(@unnamed$4, "entry") |
| 124 | \\@unnamed$8 = export(@unnamed$7, @entry) | 118 | \\@unnamed$6 = fntype([], @void, cc=C) |
| 125 | \\@unnamed$12 = fntype([], @void, cc=C) | 119 | \\@entry = fn(@unnamed$6, { |
| 126 | \\@entry = fn(@unnamed$12, { | ||
| 127 | \\ %0 = call(@a, [], modifier=auto) | 120 | \\ %0 = call(@a, [], modifier=auto) |
| 128 | \\ %1 = return() | 121 | \\ %1 = returnvoid() |
| 129 | \\}) | 122 | \\}) |
| 130 | \\@unnamed$17 = fntype([], @void, cc=C) | 123 | \\@unnamed$8 = fntype([], @void, cc=C) |
| 131 | \\@a = fn(@unnamed$17, { | 124 | \\@a = fn(@unnamed$8, { |
| 132 | \\ %0 = call(@b, [], modifier=auto) | 125 | \\ %0 = call(@b, [], modifier=auto) |
| 133 | \\ %1 = return() | 126 | \\ %1 = returnvoid() |
| 134 | \\}) | 127 | \\}) |
| 135 | \\@unnamed$22 = fntype([], @void, cc=C) | 128 | \\@unnamed$10 = fntype([], @void, cc=C) |
| 136 | \\@b = fn(@unnamed$22, { | 129 | \\@b = fn(@unnamed$10, { |
| 137 | \\ %0 = call(@a, [], modifier=auto) | 130 | \\ %0 = call(@a, [], modifier=auto) |
| 138 | \\ %1 = return() | 131 | \\ %1 = returnvoid() |
| 139 | \\}) | 132 | \\}) |
| 140 | \\ | 133 | \\ |
| 141 | ); | 134 | ); |
| ... | @@ -145,27 +138,26 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -145,27 +138,26 @@ pub fn addCases(ctx: *TestContext) void { |
| 145 | \\@fnty = fntype([], @void, cc=C) | 138 | \\@fnty = fntype([], @void, cc=C) |
| 146 | \\ | 139 | \\ |
| 147 | \\@9 = str("entry") | 140 | \\@9 = str("entry") |
| 148 | \\@10 = ref(@9) | 141 | \\@11 = export(@9, "entry") |
| 149 | \\@11 = export(@10, @entry) | ||
| 150 | \\ | 142 | \\ |
| 151 | \\@entry = fn(@fnty, { | 143 | \\@entry = fn(@fnty, { |
| 152 | \\ %0 = call(@a, []) | 144 | \\ %0 = call(@a, []) |
| 153 | \\ %1 = return() | 145 | \\ %1 = returnvoid() |
| 154 | \\}) | 146 | \\}) |
| 155 | \\ | 147 | \\ |
| 156 | \\@a = fn(@fnty, { | 148 | \\@a = fn(@fnty, { |
| 157 | \\ %0 = call(@b, []) | 149 | \\ %0 = call(@b, []) |
| 158 | \\ %1 = return() | 150 | \\ %1 = returnvoid() |
| 159 | \\}) | 151 | \\}) |
| 160 | \\ | 152 | \\ |
| 161 | \\@b = fn(@fnty, { | 153 | \\@b = fn(@fnty, { |
| 162 | \\ %9 = compileerror("message") | 154 | \\ %9 = compileerror("message") |
| 163 | \\ %0 = call(@a, []) | 155 | \\ %0 = call(@a, []) |
| 164 | \\ %1 = return() | 156 | \\ %1 = returnvoid() |
| 165 | \\}) | 157 | \\}) |
| 166 | , | 158 | , |
| 167 | &[_][]const u8{ | 159 | &[_][]const u8{ |
| 168 | ":19:21: error: message", | 160 | ":18:21: error: message", |
| 169 | }, | 161 | }, |
| 170 | ); | 162 | ); |
| 171 | // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are | 163 | // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are |
| ... | @@ -176,34 +168,32 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -176,34 +168,32 @@ pub fn addCases(ctx: *TestContext) void { |
| 176 | \\@fnty = fntype([], @void, cc=C) | 168 | \\@fnty = fntype([], @void, cc=C) |
| 177 | \\ | 169 | \\ |
| 178 | \\@9 = str("entry") | 170 | \\@9 = str("entry") |
| 179 | \\@10 = ref(@9) | 171 | \\@11 = export(@9, "entry") |
| 180 | \\@11 = export(@10, @entry) | ||
| 181 | \\ | 172 | \\ |
| 182 | \\@entry = fn(@fnty, { | 173 | \\@entry = fn(@fnty, { |
| 183 | \\ %1 = return() | 174 | \\ %0 = returnvoid() |
| 184 | \\}) | 175 | \\}) |
| 185 | \\ | 176 | \\ |
| 186 | \\@a = fn(@fnty, { | 177 | \\@a = fn(@fnty, { |
| 187 | \\ %0 = call(@b, []) | 178 | \\ %0 = call(@b, []) |
| 188 | \\ %1 = return() | 179 | \\ %1 = returnvoid() |
| 189 | \\}) | 180 | \\}) |
| 190 | \\ | 181 | \\ |
| 191 | \\@b = fn(@fnty, { | 182 | \\@b = fn(@fnty, { |
| 192 | \\ %9 = compileerror("message") | 183 | \\ %9 = compileerror("message") |
| 193 | \\ %0 = call(@a, []) | 184 | \\ %0 = call(@a, []) |
| 194 | \\ %1 = return() | 185 | \\ %1 = returnvoid() |
| 195 | \\}) | 186 | \\}) |
| 196 | , | 187 | , |
| 197 | \\@void = primitive(void) | 188 | \\@void = primitive(void) |
| 198 | \\@fnty = fntype([], @void, cc=C) | 189 | \\@fnty = fntype([], @void, cc=C) |
| 199 | \\@9 = str("entry") | 190 | \\@9 = declref("9$2") |
| 200 | \\@10 = ref(@9) | 191 | \\@9$2 = str("entry") |
| 201 | \\@unnamed$6 = str("entry") | 192 | \\@unnamed$4 = str("entry") |
| 202 | \\@unnamed$7 = ref(@unnamed$6) | 193 | \\@unnamed$5 = export(@unnamed$4, "entry") |
| 203 | \\@unnamed$8 = export(@unnamed$7, @entry) | 194 | \\@unnamed$6 = fntype([], @void, cc=C) |
| 204 | \\@unnamed$10 = fntype([], @void, cc=C) | 195 | \\@entry = fn(@unnamed$6, { |
| 205 | \\@entry = fn(@unnamed$10, { | 196 | \\ %0 = returnvoid() |
| 206 | \\ %0 = return() | ||
| 207 | \\}) | 197 | \\}) |
| 208 | \\ | 198 | \\ |
| 209 | ); | 199 | ); |
| ... | @@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void { | ... | @@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void { |
| 217 | return; | 207 | return; |
| 218 | } | 208 | } |
| 219 | 209 | ||
| 220 | ctx.addZIRCompareOutput( | 210 | ctx.addZIRCompareOutput("hello world ZIR", |
| 221 | "hello world ZIR, update msg", | 211 | \\@noreturn = primitive(noreturn) |
| 222 | &[_][]const u8{ | 212 | \\@void = primitive(void) |
| 223 | \\@noreturn = primitive(noreturn) | 213 | \\@usize = primitive(usize) |
| 224 | \\@void = primitive(void) | 214 | \\@0 = int(0) |
| 225 | \\@usize = primitive(usize) | 215 | \\@1 = int(1) |
| 226 | \\@0 = int(0) | 216 | \\@2 = int(2) |
| 227 | \\@1 = int(1) | 217 | \\@3 = int(3) |
| 228 | \\@2 = int(2) | 218 | \\ |
| 229 | \\@3 = int(3) | 219 | \\@msg = str("Hello, world!\n") |
| 230 | \\ | 220 | \\ |
| 231 | \\@syscall_array = str("syscall") | 221 | \\@start_fnty = fntype([], @noreturn, cc=Naked) |
| 232 | \\@sysoutreg_array = str("={rax}") | 222 | \\@start = fn(@start_fnty, { |
| 233 | \\@rax_array = str("{rax}") | 223 | \\ %SYS_exit_group = int(231) |
| 234 | \\@rdi_array = str("{rdi}") | 224 | \\ %exit_code = as(@usize, @0) |
| 235 | \\@rcx_array = str("rcx") | 225 | \\ |
| 236 | \\@r11_array = str("r11") | 226 | \\ %syscall = str("syscall") |
| 237 | \\@rdx_array = str("{rdx}") | 227 | \\ %sysoutreg = str("={rax}") |
| 238 | \\@rsi_array = str("{rsi}") | 228 | \\ %rax = str("{rax}") |
| 239 | \\@memory_array = str("memory") | 229 | \\ %rdi = str("{rdi}") |
| 240 | \\@len_array = str("len") | 230 | \\ %rcx = str("rcx") |
| 241 | \\ | 231 | \\ %rdx = str("{rdx}") |
| 242 | \\@msg = str("Hello, world!\n") | 232 | \\ %rsi = str("{rsi}") |
| 243 | \\ | 233 | \\ %r11 = str("r11") |
| 244 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | 234 | \\ %memory = str("memory") |
| 245 | \\@start = fn(@start_fnty, { | 235 | \\ |
| 246 | \\ %SYS_exit_group = int(231) | 236 | \\ %SYS_write = as(@usize, @1) |
| 247 | \\ %exit_code = as(@usize, @0) | 237 | \\ %STDOUT_FILENO = as(@usize, @1) |
| 248 | \\ | 238 | \\ |
| 249 | \\ %syscall = ref(@syscall_array) | 239 | \\ %msg_addr = ptrtoint(@msg) |
| 250 | \\ %sysoutreg = ref(@sysoutreg_array) | 240 | \\ |
| 251 | \\ %rax = ref(@rax_array) | 241 | \\ %len_name = str("len") |
| 252 | \\ %rdi = ref(@rdi_array) | 242 | \\ %msg_len_ptr = fieldptr(@msg, %len_name) |
| 253 | \\ %rcx = ref(@rcx_array) | 243 | \\ %msg_len = deref(%msg_len_ptr) |
| 254 | \\ %rdx = ref(@rdx_array) | 244 | \\ %rc_write = asm(%syscall, @usize, |
| 255 | \\ %rsi = ref(@rsi_array) | 245 | \\ volatile=1, |
| 256 | \\ %r11 = ref(@r11_array) | 246 | \\ output=%sysoutreg, |
| 257 | \\ %memory = ref(@memory_array) | 247 | \\ inputs=[%rax, %rdi, %rsi, %rdx], |
| 258 | \\ | 248 | \\ clobbers=[%rcx, %r11, %memory], |
| 259 | \\ %SYS_write = as(@usize, @1) | 249 | \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) |
| 260 | \\ %STDOUT_FILENO = as(@usize, @1) | 250 | \\ |
| 261 | \\ | 251 | \\ %rc_exit = asm(%syscall, @usize, |
| 262 | \\ %msg_ptr = ref(@msg) | 252 | \\ volatile=1, |
| 263 | \\ %msg_addr = ptrtoint(%msg_ptr) | 253 | \\ output=%sysoutreg, |
| 264 | \\ | 254 | \\ inputs=[%rax, %rdi], |
| 265 | \\ %len_name = ref(@len_array) | 255 | \\ clobbers=[%rcx, %r11, %memory], |
| 266 | \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name) | 256 | \\ args=[%SYS_exit_group, %exit_code]) |
| 267 | \\ %msg_len = deref(%msg_len_ptr) | 257 | \\ |
| 268 | \\ %rc_write = asm(%syscall, @usize, | 258 | \\ %99 = unreachable() |
| 269 | \\ volatile=1, | 259 | \\}); |
| 270 | \\ output=%sysoutreg, | 260 | \\ |
| 271 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | 261 | \\@9 = str("_start") |
| 272 | \\ clobbers=[%rcx, %r11, %memory], | 262 | \\@11 = export(@9, "start") |
| 273 | \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) | 263 | , |
| 274 | \\ | 264 | \\Hello, world! |
| 275 | \\ %rc_exit = asm(%syscall, @usize, | 265 | \\ |
| 276 | \\ volatile=1, | ||
| 277 | \\ output=%sysoutreg, | ||
| 278 | \\ inputs=[%rax, %rdi], | ||
| 279 | \\ clobbers=[%rcx, %r11, %memory], | ||
| 280 | \\ args=[%SYS_exit_group, %exit_code]) | ||
| 281 | \\ | ||
| 282 | \\ %99 = unreachable() | ||
| 283 | \\}); | ||
| 284 | \\ | ||
| 285 | \\@9 = str("_start") | ||
| 286 | \\@10 = ref(@9) | ||
| 287 | \\@11 = export(@10, @start) | ||
| 288 | , | ||
| 289 | \\@noreturn = primitive(noreturn) | ||
| 290 | \\@void = primitive(void) | ||
| 291 | \\@usize = primitive(usize) | ||
| 292 | \\@0 = int(0) | ||
| 293 | \\@1 = int(1) | ||
| 294 | \\@2 = int(2) | ||
| 295 | \\@3 = int(3) | ||
| 296 | \\ | ||
| 297 | \\@syscall_array = str("syscall") | ||
| 298 | \\@sysoutreg_array = str("={rax}") | ||
| 299 | \\@rax_array = str("{rax}") | ||
| 300 | \\@rdi_array = str("{rdi}") | ||
| 301 | \\@rcx_array = str("rcx") | ||
| 302 | \\@r11_array = str("r11") | ||
| 303 | \\@rdx_array = str("{rdx}") | ||
| 304 | \\@rsi_array = str("{rsi}") | ||
| 305 | \\@memory_array = str("memory") | ||
| 306 | \\@len_array = str("len") | ||
| 307 | \\ | ||
| 308 | \\@msg = str("Hello, world!\n") | ||
| 309 | \\@msg2 = str("HELL WORLD\n") | ||
| 310 | \\ | ||
| 311 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | ||
| 312 | \\@start = fn(@start_fnty, { | ||
| 313 | \\ %SYS_exit_group = int(231) | ||
| 314 | \\ %exit_code = as(@usize, @0) | ||
| 315 | \\ | ||
| 316 | \\ %syscall = ref(@syscall_array) | ||
| 317 | \\ %sysoutreg = ref(@sysoutreg_array) | ||
| 318 | \\ %rax = ref(@rax_array) | ||
| 319 | \\ %rdi = ref(@rdi_array) | ||
| 320 | \\ %rcx = ref(@rcx_array) | ||
| 321 | \\ %rdx = ref(@rdx_array) | ||
| 322 | \\ %rsi = ref(@rsi_array) | ||
| 323 | \\ %r11 = ref(@r11_array) | ||
| 324 | \\ %memory = ref(@memory_array) | ||
| 325 | \\ | ||
| 326 | \\ %SYS_write = as(@usize, @1) | ||
| 327 | \\ %STDOUT_FILENO = as(@usize, @1) | ||
| 328 | \\ | ||
| 329 | \\ %msg_ptr = ref(@msg2) | ||
| 330 | \\ %msg_addr = ptrtoint(%msg_ptr) | ||
| 331 | \\ | ||
| 332 | \\ %len_name = ref(@len_array) | ||
| 333 | \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name) | ||
| 334 | \\ %msg_len = deref(%msg_len_ptr) | ||
| 335 | \\ %rc_write = asm(%syscall, @usize, | ||
| 336 | \\ volatile=1, | ||
| 337 | \\ output=%sysoutreg, | ||
| 338 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | ||
| 339 | \\ clobbers=[%rcx, %r11, %memory], | ||
| 340 | \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) | ||
| 341 | \\ | ||
| 342 | \\ %rc_exit = asm(%syscall, @usize, | ||
| 343 | \\ volatile=1, | ||
| 344 | \\ output=%sysoutreg, | ||
| 345 | \\ inputs=[%rax, %rdi], | ||
| 346 | \\ clobbers=[%rcx, %r11, %memory], | ||
| 347 | \\ args=[%SYS_exit_group, %exit_code]) | ||
| 348 | \\ | ||
| 349 | \\ %99 = unreachable() | ||
| 350 | \\}); | ||
| 351 | \\ | ||
| 352 | \\@9 = str("_start") | ||
| 353 | \\@10 = ref(@9) | ||
| 354 | \\@11 = export(@10, @start) | ||
| 355 | , | ||
| 356 | \\@noreturn = primitive(noreturn) | ||
| 357 | \\@void = primitive(void) | ||
| 358 | \\@usize = primitive(usize) | ||
| 359 | \\@0 = int(0) | ||
| 360 | \\@1 = int(1) | ||
| 361 | \\@2 = int(2) | ||
| 362 | \\@3 = int(3) | ||
| 363 | \\ | ||
| 364 | \\@syscall_array = str("syscall") | ||
| 365 | \\@sysoutreg_array = str("={rax}") | ||
| 366 | \\@rax_array = str("{rax}") | ||
| 367 | \\@rdi_array = str("{rdi}") | ||
| 368 | \\@rcx_array = str("rcx") | ||
| 369 | \\@r11_array = str("r11") | ||
| 370 | \\@rdx_array = str("{rdx}") | ||
| 371 | \\@rsi_array = str("{rsi}") | ||
| 372 | \\@memory_array = str("memory") | ||
| 373 | \\@len_array = str("len") | ||
| 374 | \\ | ||
| 375 | \\@msg = str("Hello, world!\n") | ||
| 376 | \\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n") | ||
| 377 | \\ | ||
| 378 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | ||
| 379 | \\@start = fn(@start_fnty, { | ||
| 380 | \\ %SYS_exit_group = int(231) | ||
| 381 | \\ %exit_code = as(@usize, @0) | ||
| 382 | \\ | ||
| 383 | \\ %syscall = ref(@syscall_array) | ||
| 384 | \\ %sysoutreg = ref(@sysoutreg_array) | ||
| 385 | \\ %rax = ref(@rax_array) | ||
| 386 | \\ %rdi = ref(@rdi_array) | ||
| 387 | \\ %rcx = ref(@rcx_array) | ||
| 388 | \\ %rdx = ref(@rdx_array) | ||
| 389 | \\ %rsi = ref(@rsi_array) | ||
| 390 | \\ %r11 = ref(@r11_array) | ||
| 391 | \\ %memory = ref(@memory_array) | ||
| 392 | \\ | ||
| 393 | \\ %SYS_write = as(@usize, @1) | ||
| 394 | \\ %STDOUT_FILENO = as(@usize, @1) | ||
| 395 | \\ | ||
| 396 | \\ %msg_ptr = ref(@msg2) | ||
| 397 | \\ %msg_addr = ptrtoint(%msg_ptr) | ||
| 398 | \\ | ||
| 399 | \\ %len_name = ref(@len_array) | ||
| 400 | \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name) | ||
| 401 | \\ %msg_len = deref(%msg_len_ptr) | ||
| 402 | \\ %rc_write = asm(%syscall, @usize, | ||
| 403 | \\ volatile=1, | ||
| 404 | \\ output=%sysoutreg, | ||
| 405 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | ||
| 406 | \\ clobbers=[%rcx, %r11, %memory], | ||
| 407 | \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) | ||
| 408 | \\ | ||
| 409 | \\ %rc_exit = asm(%syscall, @usize, | ||
| 410 | \\ volatile=1, | ||
| 411 | \\ output=%sysoutreg, | ||
| 412 | \\ inputs=[%rax, %rdi], | ||
| 413 | \\ clobbers=[%rcx, %r11, %memory], | ||
| 414 | \\ args=[%SYS_exit_group, %exit_code]) | ||
| 415 | \\ | ||
| 416 | \\ %99 = unreachable() | ||
| 417 | \\}); | ||
| 418 | \\ | ||
| 419 | \\@9 = str("_start") | ||
| 420 | \\@10 = ref(@9) | ||
| 421 | \\@11 = export(@10, @start) | ||
| 422 | }, | ||
| 423 | &[_][]const u8{ | ||
| 424 | \\Hello, world! | ||
| 425 | \\ | ||
| 426 | , | ||
| 427 | \\HELL WORLD | ||
| 428 | \\ | ||
| 429 | , | ||
| 430 | \\Editing the same msg2 decl but this time with a much longer message which will | ||
| 431 | \\cause the data to need to be relocated in virtual address space. | ||
| 432 | \\ | ||
| 433 | }, | ||
| 434 | ); | 266 | ); |
| 435 | 267 | ||
| 436 | ctx.addZIRCompareOutput( | 268 | ctx.addZIRCompareOutput("function call with no args no return value", |
| 437 | "function call with no args no return value", | 269 | \\@noreturn = primitive(noreturn) |
| 438 | &[_][]const u8{ | 270 | \\@void = primitive(void) |
| 439 | \\@noreturn = primitive(noreturn) | 271 | \\@usize = primitive(usize) |
| 440 | \\@void = primitive(void) | 272 | \\@0 = int(0) |
| 441 | \\@usize = primitive(usize) | 273 | \\@1 = int(1) |
| 442 | \\@0 = int(0) | 274 | \\@2 = int(2) |
| 443 | \\@1 = int(1) | 275 | \\@3 = int(3) |
| 444 | \\@2 = int(2) | 276 | \\ |
| 445 | \\@3 = int(3) | 277 | \\@exit0_fnty = fntype([], @noreturn) |
| 446 | \\ | 278 | \\@exit0 = fn(@exit0_fnty, { |
| 447 | \\@syscall_array = str("syscall") | 279 | \\ %SYS_exit_group = int(231) |
| 448 | \\@sysoutreg_array = str("={rax}") | 280 | \\ %exit_code = as(@usize, @0) |
| 449 | \\@rax_array = str("{rax}") | 281 | \\ |
| 450 | \\@rdi_array = str("{rdi}") | 282 | \\ %syscall = str("syscall") |
| 451 | \\@rcx_array = str("rcx") | 283 | \\ %sysoutreg = str("={rax}") |
| 452 | \\@r11_array = str("r11") | 284 | \\ %rax = str("{rax}") |
| 453 | \\@memory_array = str("memory") | 285 | \\ %rdi = str("{rdi}") |
| 454 | \\ | 286 | \\ %rcx = str("rcx") |
| 455 | \\@exit0_fnty = fntype([], @noreturn) | 287 | \\ %r11 = str("r11") |
| 456 | \\@exit0 = fn(@exit0_fnty, { | 288 | \\ %memory = str("memory") |
| 457 | \\ %SYS_exit_group = int(231) | 289 | \\ |
| 458 | \\ %exit_code = as(@usize, @0) | 290 | \\ %rc = asm(%syscall, @usize, |
| 459 | \\ | 291 | \\ volatile=1, |
| 460 | \\ %syscall = ref(@syscall_array) | 292 | \\ output=%sysoutreg, |
| 461 | \\ %sysoutreg = ref(@sysoutreg_array) | 293 | \\ inputs=[%rax, %rdi], |
| 462 | \\ %rax = ref(@rax_array) | 294 | \\ clobbers=[%rcx, %r11, %memory], |
| 463 | \\ %rdi = ref(@rdi_array) | 295 | \\ args=[%SYS_exit_group, %exit_code]) |
| 464 | \\ %rcx = ref(@rcx_array) | 296 | \\ |
| 465 | \\ %r11 = ref(@r11_array) | 297 | \\ %99 = unreachable() |
| 466 | \\ %memory = ref(@memory_array) | 298 | \\}); |
| 467 | \\ | 299 | \\ |
| 468 | \\ %rc = asm(%syscall, @usize, | 300 | \\@start_fnty = fntype([], @noreturn, cc=Naked) |
| 469 | \\ volatile=1, | 301 | \\@start = fn(@start_fnty, { |
| 470 | \\ output=%sysoutreg, | 302 | \\ %0 = call(@exit0, []) |
| 471 | \\ inputs=[%rax, %rdi], | 303 | \\}) |
| 472 | \\ clobbers=[%rcx, %r11, %memory], | 304 | \\@9 = str("_start") |
| 473 | \\ args=[%SYS_exit_group, %exit_code]) | 305 | \\@11 = export(@9, "start") |
| 474 | \\ | 306 | , ""); |
| 475 | \\ %99 = unreachable() | ||
| 476 | \\}); | ||
| 477 | \\ | ||
| 478 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | ||
| 479 | \\@start = fn(@start_fnty, { | ||
| 480 | \\ %0 = call(@exit0, []) | ||
| 481 | \\}) | ||
| 482 | \\@9 = str("_start") | ||
| 483 | \\@10 = ref(@9) | ||
| 484 | \\@11 = export(@10, @start) | ||
| 485 | }, | ||
| 486 | &[_][]const u8{""}, | ||
| 487 | ); | ||
| 488 | } | 307 | } |