| 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 | 72 | if (!only_install_lib_files) { |
| 73 | 73 | exe.install(); |
| 74 | 74 | } |
| 75 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); | |
| 75 | 76 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; |
| 76 | 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 | 91 | b.installDirectory(InstallDirectoryOptions{ |
| 79 | 92 | .source_dir = "lib", |
| 80 | 93 | .install_dir = .Lib, |
lib/std/build.zig+3-2| ... | ... | @@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct { |
| 1905 | 1905 | builder.allocator, |
| 1906 | 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 | 1910 | try zig_args.append("--pkg-begin"); |
| 1910 | 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 | 1913 | try zig_args.append("--pkg-end"); |
| 1913 | 1914 | } |
| 1914 | 1915 |
lib/std/zig.zig+38| ... | ... | @@ -1,4 +1,6 @@ |
| 1 | const std = @import("std.zig"); | |
| 1 | 2 | const tokenizer = @import("zig/tokenizer.zig"); |
| 3 | ||
| 2 | 4 | pub const Token = tokenizer.Token; |
| 3 | 5 | pub const Tokenizer = tokenizer.Tokenizer; |
| 4 | 6 | pub const parse = @import("zig/parse.zig").parse; |
| ... | ... | @@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig"); |
| 9 | 11 | pub const system = @import("zig/system.zig"); |
| 10 | 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 | 29 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { |
| 13 | 30 | var line: usize = 0; |
| 14 | 31 | var column: usize = 0; |
| ... | ... | @@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi |
| 26 | 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 | 67 | test "" { |
| 30 | 68 | @import("std").meta.refAllDecls(@This()); |
| 31 | 69 | } |
lib/std/zig/ast.zig+2| ... | ... | @@ -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 | 2265 | pub const ControlFlowExpression = struct { |
| 2264 | 2266 | base: Node = Node{ .id = .ControlFlowExpression }, |
| 2265 | 2267 | ltoken: TokenIndex, |
lib/std/zig/parse.zig+1-1| ... | ... | @@ -3222,7 +3222,7 @@ const Parser = struct { |
| 3222 | 3222 | } |
| 3223 | 3223 | |
| 3224 | 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 | 3226 | if (try opParseFn(p)) |first_op| { |
| 3227 | 3227 | var rightmost_op = first_op; |
| 3228 | 3228 | while (true) { |
src-self-hosted/Module.zig+1457-460| ... | ... | @@ -15,13 +15,16 @@ const ir = @import("ir.zig"); |
| 15 | 15 | const zir = @import("zir.zig"); |
| 16 | 16 | const Module = @This(); |
| 17 | 17 | const Inst = ir.Inst; |
| 18 | const ast = std.zig.ast; | |
| 19 | const trace = @import("tracy.zig").trace; | |
| 18 | 20 | |
| 19 | 21 | /// General-purpose allocator. |
| 20 | 22 | allocator: *Allocator, |
| 21 | 23 | /// Pointer to externally managed resource. |
| 22 | 24 | root_pkg: *Package, |
| 23 | 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 | 28 | bin_file: link.ElfFile, |
| 26 | 29 | bin_file_dir: std.fs.Dir, |
| 27 | 30 | bin_file_path: []const u8, |
| ... | ... | @@ -35,10 +38,10 @@ decl_exports: std.AutoHashMap(*Decl, []*Export), |
| 35 | 38 | /// This table owns the Export memory. |
| 36 | 39 | export_owners: std.AutoHashMap(*Decl, []*Export), |
| 37 | 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 | 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 | 46 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 44 | 47 | |
| ... | ... | @@ -49,8 +52,8 @@ work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 49 | 52 | /// a Decl can have a failed_decls entry but have analysis status of success. |
| 50 | 53 | failed_decls: std.AutoHashMap(*Decl, *ErrorMsg), |
| 51 | 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. | |
| 53 | failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg), | |
| 55 | /// The ErrorMsg memory is owned by the `Scope`, using Module's allocator. | |
| 56 | failed_files: std.AutoHashMap(*Scope, *ErrorMsg), | |
| 54 | 57 | /// Using a map here for consistency with the other fields here. |
| 55 | 58 | /// The ErrorMsg memory is owned by the `Export`, using Module's allocator. |
| 56 | 59 | failed_exports: std.AutoHashMap(*Export, *ErrorMsg), |
| ... | ... | @@ -60,15 +63,23 @@ failed_exports: std.AutoHashMap(*Export, *ErrorMsg), |
| 60 | 63 | /// previous analysis. |
| 61 | 64 | generation: u32 = 0, |
| 62 | 65 | |
| 66 | next_anon_name_index: usize = 0, | |
| 67 | ||
| 63 | 68 | /// Candidates for deletion. After a semantic analysis update completes, this list |
| 64 | 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 | 77 | /// Write the machine code for a Decl to the output file. |
| 69 | 78 | codegen_decl: *Decl, |
| 70 | /// Decl has been determined to be outdated; perform semantic analysis again. | |
| 71 | re_analyze_decl: *Decl, | |
| 79 | /// The Decl needs to be analyzed and possibly export itself. | |
| 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 | 85 | pub const Export = struct { |
| ... | ... | @@ -99,13 +110,12 @@ pub const Decl = struct { |
| 99 | 110 | /// mapping them to an address in the output file. |
| 100 | 111 | /// Memory owned by this decl, using Module's allocator. |
| 101 | 112 | name: [*:0]const u8, |
| 102 | /// The direct parent container of the Decl. This field will need to get more fleshed out when | |
| 103 | /// self-hosted supports proper struct types and Zig AST => ZIR. | |
| 113 | /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`. | |
| 104 | 114 | /// Reference to externally owned memory. |
| 105 | scope: *Scope.ZIRModule, | |
| 106 | /// Byte offset into the source file that contains this declaration. | |
| 107 | /// This is the base offset that src offsets within this Decl are relative to. | |
| 108 | src: usize, | |
| 115 | scope: *Scope, | |
| 116 | /// The AST Node decl index or ZIR Inst index that contains this declaration. | |
| 117 | /// Must be recomputed when the corresponding source file is modified. | |
| 118 | src_index: usize, | |
| 109 | 119 | /// The most recent value of the Decl after a successful semantic analysis. |
| 110 | 120 | typed_value: union(enum) { |
| 111 | 121 | never_succeeded: void, |
| ... | ... | @@ -116,6 +126,9 @@ pub const Decl = struct { |
| 116 | 126 | /// analysis of the function body is performed with this value set to `success`. Functions |
| 117 | 127 | /// have their own analysis status field. |
| 118 | 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 | 132 | /// Semantic analysis for this Decl is running right now. This state detects dependency loops. |
| 120 | 133 | in_progress, |
| 121 | 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 | 138 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 126 | 139 | sema_failure, |
| 127 | 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 | 145 | codegen_failure, |
| 129 | 146 | /// There will be a corresponding ErrorMsg in Module.failed_decls. |
| 130 | 147 | /// This indicates the failure was something like running out of disk space, |
| ... | ... | @@ -150,7 +167,7 @@ pub const Decl = struct { |
| 150 | 167 | /// This is populated regardless of semantic analysis and code generation. |
| 151 | 168 | link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty, |
| 152 | 169 | |
| 153 | contents_hash: Hash, | |
| 170 | contents_hash: std.zig.SrcHash, | |
| 154 | 171 | |
| 155 | 172 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's |
| 156 | 173 | /// typed_value is modified. |
| ... | ... | @@ -169,28 +186,28 @@ pub const Decl = struct { |
| 169 | 186 | allocator.destroy(self); |
| 170 | 187 | } |
| 171 | 188 | |
| 172 | pub const Hash = [16]u8; | |
| 173 | ||
| 174 | /// If the name is small enough, it is used directly as the hash. | |
| 175 | /// If it is long, blake3 hash is computed. | |
| 176 | pub fn hashSimpleName(name: []const u8) Hash { | |
| 177 | var out: Hash = undefined; | |
| 178 | if (name.len <= Hash.len) { | |
| 179 | mem.copy(u8, &out, name); | |
| 180 | mem.set(u8, out[name.len..], 0); | |
| 181 | } else { | |
| 182 | std.crypto.Blake3.hash(name, &out); | |
| 189 | pub fn src(self: Decl) usize { | |
| 190 | switch (self.scope.tag) { | |
| 191 | .file => { | |
| 192 | const file = @fieldParentPtr(Scope.File, "base", self.scope); | |
| 193 | const tree = file.contents.tree; | |
| 194 | const decl_node = tree.root_node.decls()[self.src_index]; | |
| 195 | return tree.token_locs[decl_node.firstToken()].start; | |
| 196 | }, | |
| 197 | .zir_module => { | |
| 198 | const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope); | |
| 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. | |
| 188 | /// The point of hashing here is only to limit the number of bytes of | |
| 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)); | |
| 209 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { | |
| 210 | return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name)); | |
| 194 | 211 | } |
| 195 | 212 | |
| 196 | 213 | pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { |
| ... | ... | @@ -248,11 +265,9 @@ pub const Decl = struct { |
| 248 | 265 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| 249 | 266 | pub const Fn = struct { |
| 250 | 267 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. |
| 251 | fn_type: Type, | |
| 252 | 268 | analysis: union(enum) { |
| 253 | /// The value is the source instruction. | |
| 254 | queued: *zir.Inst.Fn, | |
| 255 | in_progress: *Analysis, | |
| 269 | queued: *ZIR, | |
| 270 | in_progress, | |
| 256 | 271 | /// There will be a corresponding ErrorMsg in Module.failed_decls |
| 257 | 272 | sema_failure, |
| 258 | 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 | 281 | /// of Fn analysis. |
| 267 | 282 | pub const Analysis = struct { |
| 268 | 283 | inner_block: Scope.Block, |
| 269 | /// TODO Performance optimization idea: instead of this inst_table, | |
| 270 | /// use a field in the zir.Inst instead to track corresponding instructions | |
| 271 | inst_table: std.AutoHashMap(*zir.Inst, *Inst), | |
| 272 | needed_inst_capacity: usize, | |
| 284 | }; | |
| 285 | ||
| 286 | /// Contains un-analyzed ZIR instructions generated from Zig source AST. | |
| 287 | pub const ZIR = struct { | |
| 288 | body: zir.Module.Body, | |
| 289 | arena: std.heap.ArenaAllocator.State, | |
| 273 | 290 | }; |
| 274 | 291 | }; |
| 275 | 292 | |
| 276 | 293 | pub const Scope = struct { |
| 277 | 294 | tag: Tag, |
| 278 | 295 | |
| 296 | pub const NameHash = [16]u8; | |
| 297 | ||
| 279 | 298 | pub fn cast(base: *Scope, comptime T: type) ?*T { |
| 280 | 299 | if (base.tag != T.base_tag) |
| 281 | 300 | return null; |
| ... | ... | @@ -289,7 +308,9 @@ pub const Scope = struct { |
| 289 | 308 | switch (self.tag) { |
| 290 | 309 | .block => return self.cast(Block).?.arena, |
| 291 | 310 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, |
| 311 | .gen_zir => return &self.cast(GenZIR).?.arena.allocator, | |
| 292 | 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 | 319 | pub fn decl(self: *Scope) ?*Decl { |
| 299 | 320 | return switch (self.tag) { |
| 300 | 321 | .block => self.cast(Block).?.decl, |
| 322 | .gen_zir => self.cast(GenZIR).?.decl, | |
| 301 | 323 | .decl => self.cast(DeclAnalysis).?.decl, |
| 302 | 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 | 330 | /// returns it. |
| 308 | pub fn namespace(self: *Scope) *ZIRModule { | |
| 331 | pub fn namespace(self: *Scope) *Scope { | |
| 309 | 332 | switch (self.tag) { |
| 310 | 333 | .block => return self.cast(Block).?.decl.scope, |
| 334 | .gen_zir => return self.cast(GenZIR).?.decl.scope, | |
| 311 | 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 | 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 | 446 | pub const Tag = enum { |
| 447 | /// .zir source code. | |
| 329 | 448 | zir_module, |
| 449 | /// .zig source code. | |
| 450 | file, | |
| 330 | 451 | block, |
| 331 | 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 | 545 | pub const ZIRModule = struct { |
| ... | ... | @@ -355,6 +566,11 @@ pub const Scope = struct { |
| 355 | 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 | 574 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { |
| 359 | 575 | switch (self.status) { |
| 360 | 576 | .never_loaded, |
| ... | ... | @@ -366,11 +582,13 @@ pub const Scope = struct { |
| 366 | 582 | .loaded_success => { |
| 367 | 583 | self.contents.module.deinit(allocator); |
| 368 | 584 | allocator.destroy(self.contents.module); |
| 585 | self.contents = .{ .not_available = {} }; | |
| 369 | 586 | self.status = .unloaded_success; |
| 370 | 587 | }, |
| 371 | 588 | .loaded_sema_failure => { |
| 372 | 589 | self.contents.module.deinit(allocator); |
| 373 | 590 | allocator.destroy(self.contents.module); |
| 591 | self.contents = .{ .not_available = {} }; | |
| 374 | 592 | self.status = .unloaded_sema_failure; |
| 375 | 593 | }, |
| 376 | 594 | } |
| ... | ... | @@ -384,14 +602,46 @@ pub const Scope = struct { |
| 384 | 602 | } |
| 385 | 603 | |
| 386 | 604 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { |
| 605 | self.decls.deinit(allocator); | |
| 387 | 606 | self.unload(allocator); |
| 388 | 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 | 619 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { |
| 392 | 620 | const loc = std.zig.findLineColumn(self.source.bytes, src); |
| 393 | 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 | 647 | /// This is a temporary structure, references to it are valid only |
| ... | ... | @@ -399,7 +649,7 @@ pub const Scope = struct { |
| 399 | 649 | pub const Block = struct { |
| 400 | 650 | pub const base_tag: Tag = .block; |
| 401 | 651 | base: Scope = Scope{ .tag = base_tag }, |
| 402 | func: *Fn, | |
| 652 | func: ?*Fn, | |
| 403 | 653 | decl: *Decl, |
| 404 | 654 | instructions: ArrayListUnmanaged(*Inst), |
| 405 | 655 | /// Points to the arena allocator of DeclAnalysis |
| ... | ... | @@ -414,6 +664,16 @@ pub const Scope = struct { |
| 414 | 664 | decl: *Decl, |
| 415 | 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 | 679 | pub const Body = struct { |
| ... | ... | @@ -463,19 +723,10 @@ pub const InitOptions = struct { |
| 463 | 723 | link_mode: ?std.builtin.LinkMode = null, |
| 464 | 724 | object_format: ?std.builtin.ObjectFormat = null, |
| 465 | 725 | optimize_mode: std.builtin.Mode = .Debug, |
| 726 | keep_source_files_loaded: bool = false, | |
| 466 | 727 | }; |
| 467 | 728 | |
| 468 | 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 | 730 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); |
| 480 | 731 | var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{ |
| 481 | 732 | .target = options.target, |
| ... | ... | @@ -485,6 +736,32 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 485 | 736 | }); |
| 486 | 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 | 765 | return Module{ |
| 489 | 766 | .allocator = gpa, |
| 490 | 767 | .root_pkg = options.root_pkg, |
| ... | ... | @@ -493,13 +770,14 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 493 | 770 | .bin_file_path = options.bin_file_path, |
| 494 | 771 | .bin_file = bin_file, |
| 495 | 772 | .optimize_mode = options.optimize_mode, |
| 496 | .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa), | |
| 773 | .decl_table = DeclTable.init(gpa), | |
| 497 | 774 | .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa), |
| 498 | 775 | .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa), |
| 499 | 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 | 778 | .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa), |
| 502 | 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 | 829 | } |
| 552 | 830 | self.export_owners.deinit(); |
| 553 | 831 | } |
| 554 | { | |
| 555 | self.root_scope.deinit(allocator); | |
| 556 | allocator.destroy(self.root_scope); | |
| 557 | } | |
| 832 | self.root_scope.destroy(allocator); | |
| 558 | 833 | self.* = undefined; |
| 559 | 834 | } |
| 560 | 835 | |
| ... | ... | @@ -571,19 +846,31 @@ pub fn target(self: Module) std.Target { |
| 571 | 846 | |
| 572 | 847 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 573 | 848 | pub fn update(self: *Module) !void { |
| 849 | const tracy = trace(@src()); | |
| 850 | defer tracy.end(); | |
| 851 | ||
| 574 | 852 | self.generation += 1; |
| 575 | 853 | |
| 576 | 854 | // TODO Use the cache hash file system to detect which source files changed. |
| 577 | // Here we simulate a full cache miss. | |
| 578 | // Analyze the root source file now. | |
| 579 | // Source files could have been loaded for any reason; to force a refresh we unload now. | |
| 580 | self.root_scope.unload(self.allocator); | |
| 581 | self.analyzeRoot(self.root_scope) catch |err| switch (err) { | |
| 582 | error.AnalysisFail => { | |
| 583 | assert(self.totalErrorCount() != 0); | |
| 584 | }, | |
| 585 | else => |e| return e, | |
| 586 | }; | |
| 855 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; | |
| 856 | // to force a refresh we unload now. | |
| 857 | if (self.root_scope.cast(Scope.File)) |zig_file| { | |
| 858 | zig_file.unload(self.allocator); | |
| 859 | self.analyzeRootSrcFile(zig_file) catch |err| switch (err) { | |
| 860 | error.AnalysisFail => { | |
| 861 | assert(self.totalErrorCount() != 0); | |
| 862 | }, | |
| 863 | else => |e| return e, | |
| 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 | 875 | try self.performAllTheWork(); |
| 589 | 876 | |
| ... | ... | @@ -596,14 +883,16 @@ pub fn update(self: *Module) !void { |
| 596 | 883 | try self.deleteDecl(decl); |
| 597 | 884 | } |
| 598 | 885 | |
| 886 | self.link_error_flags = self.bin_file.error_flags; | |
| 887 | ||
| 599 | 888 | // If there are any errors, we anticipate the source files being loaded |
| 600 | 889 | // to report error messages. Otherwise we unload all source files to save memory. |
| 601 | 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 | 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 | 908 | } |
| 620 | 909 | |
| 621 | 910 | pub fn totalErrorCount(self: *Module) usize { |
| 622 | return self.failed_decls.size + | |
| 911 | const total = self.failed_decls.size + | |
| 623 | 912 | self.failed_files.size + |
| 624 | self.failed_exports.size + | |
| 625 | @boolToInt(self.link_error_flags.no_entry_point_found); | |
| 913 | self.failed_exports.size; | |
| 914 | return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; | |
| 626 | 915 | } |
| 627 | 916 | |
| 628 | 917 | pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| ... | ... | @@ -637,8 +926,8 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 637 | 926 | while (it.next()) |kv| { |
| 638 | 927 | const scope = kv.key; |
| 639 | 928 | const err_msg = kv.value; |
| 640 | const source = try self.getSource(scope); | |
| 641 | try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*); | |
| 929 | const source = try scope.getSource(self); | |
| 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 | 935 | while (it.next()) |kv| { |
| 647 | 936 | const decl = kv.key; |
| 648 | 937 | const err_msg = kv.value; |
| 649 | const source = try self.getSource(decl.scope); | |
| 650 | try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*); | |
| 938 | const source = try decl.scope.getSource(self); | |
| 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 | 944 | while (it.next()) |kv| { |
| 656 | 945 | const decl = kv.key.owner_decl; |
| 657 | 946 | const err_msg = kv.value; |
| 658 | const source = try self.getSource(decl.scope); | |
| 659 | try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*); | |
| 947 | const source = try decl.scope.getSource(self); | |
| 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 | 953 | try errors.append(.{ |
| 665 | 954 | .src_path = self.root_pkg.root_src_path, |
| 666 | 955 | .line = 0, |
| ... | ... | @@ -683,12 +972,14 @@ const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 683 | 972 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 684 | 973 | while (self.work_queue.readItem()) |work_item| switch (work_item) { |
| 685 | 974 | .codegen_decl => |decl| switch (decl.analysis) { |
| 975 | .unreferenced => unreachable, | |
| 686 | 976 | .in_progress => unreachable, |
| 687 | 977 | .outdated => unreachable, |
| 688 | 978 | |
| 689 | 979 | .sema_failure, |
| 690 | 980 | .codegen_failure, |
| 691 | 981 | .dependency_failure, |
| 982 | .sema_failure_retryable, | |
| 692 | 983 | => continue, |
| 693 | 984 | |
| 694 | 985 | .complete, .codegen_failure_retryable => { |
| ... | ... | @@ -696,12 +987,10 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 696 | 987 | switch (payload.func.analysis) { |
| 697 | 988 | .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) { |
| 698 | 989 | error.AnalysisFail => { |
| 699 | if (payload.func.analysis == .queued) { | |
| 700 | payload.func.analysis = .dependency_failure; | |
| 701 | } | |
| 990 | assert(payload.func.analysis != .in_progress); | |
| 702 | 991 | continue; |
| 703 | 992 | }, |
| 704 | else => |e| return e, | |
| 993 | error.OutOfMemory => return error.OutOfMemory, | |
| 705 | 994 | }, |
| 706 | 995 | .in_progress => unreachable, |
| 707 | 996 | .sema_failure, .dependency_failure => continue, |
| ... | ... | @@ -720,7 +1009,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 720 | 1009 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 721 | 1010 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 722 | 1011 | self.allocator, |
| 723 | decl.src, | |
| 1012 | decl.src(), | |
| 724 | 1013 | "unable to codegen: {}", |
| 725 | 1014 | .{@errorName(err)}, |
| 726 | 1015 | )); |
| ... | ... | @@ -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) { | |
| 733 | .in_progress => unreachable, | |
| 1021 | .analyze_decl => |decl| { | |
| 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, | |
| 736 | .codegen_failure, | |
| 737 | .dependency_failure, | |
| 738 | .complete, | |
| 739 | .codegen_failure_retryable, | |
| 740 | => continue, | |
| 1030 | fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | |
| 1031 | const tracy = trace(@src()); | |
| 1032 | defer tracy.end(); | |
| 741 | 1033 | |
| 742 | .outdated => { | |
| 743 | const zir_module = self.getSrcModule(decl.scope) catch |err| switch (err) { | |
| 744 | error.OutOfMemory => return error.OutOfMemory, | |
| 745 | else => { | |
| 746 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | |
| 747 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 748 | self.allocator, | |
| 749 | decl.src, | |
| 750 | "unable to load source file '{}': {}", | |
| 751 | .{ decl.scope.sub_file_path, @errorName(err) }, | |
| 752 | )); | |
| 753 | decl.analysis = .codegen_failure_retryable; | |
| 754 | continue; | |
| 1034 | const subsequent_analysis = switch (decl.analysis) { | |
| 1035 | .in_progress => unreachable, | |
| 1036 | ||
| 1037 | .sema_failure, | |
| 1038 | .sema_failure_retryable, | |
| 1039 | .codegen_failure, | |
| 1040 | .dependency_failure, | |
| 1041 | .codegen_failure_retryable, | |
| 1042 | => return error.AnalysisFail, | |
| 1043 | ||
| 1044 | .complete, .outdated => blk: { | |
| 1045 | if (decl.generation == self.generation) { | |
| 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); | |
| 758 | // We already detected deletions, so we know this will be found. | |
| 759 | const src_decl = zir_module.findDecl(decl_name).?; | |
| 760 | self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) { | |
| 761 | error.OutOfMemory => return error.OutOfMemory, | |
| 762 | error.AnalysisFail => continue, | |
| 1208 | errdefer gen_scope.arena.deinit(); | |
| 1209 | defer gen_scope.instructions.deinit(); | |
| 1210 | ||
| 1211 | const body_block = body_node.cast(ast.Node.Block).?; | |
| 1212 | ||
| 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 | 1577 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { |
| ... | ... | @@ -775,28 +1583,11 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void |
| 775 | 1583 | } else { |
| 776 | 1584 | depender.dependencies.appendAssumeCapacity(dependee); |
| 777 | 1585 | } |
| 778 | ||
| 779 | for (dependee.dependants.items) |item| { | |
| 780 | if (item == depender) break; // Already in the set. | |
| 781 | } else { | |
| 782 | 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, | |
| 1586 | ||
| 1587 | for (dependee.dependants.items) |item| { | |
| 1588 | if (item == depender) break; // Already in the set. | |
| 1589 | } else { | |
| 1590 | dependee.dependants.appendAssumeCapacity(depender); | |
| 800 | 1591 | } |
| 801 | 1592 | } |
| 802 | 1593 | |
| ... | ... | @@ -805,7 +1596,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 805 | 1596 | .never_loaded, .unloaded_success => { |
| 806 | 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 | 1601 | var keep_zir_module = false; |
| 811 | 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 | 1607 | |
| 817 | 1608 | if (zir_module.error_msg) |src_err_msg| { |
| 818 | 1609 | self.failed_files.putAssumeCapacityNoClobber( |
| 819 | root_scope, | |
| 1610 | &root_scope.base, | |
| 820 | 1611 | try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), |
| 821 | 1612 | ); |
| 822 | 1613 | root_scope.status = .unloaded_parse_failure; |
| ... | ... | @@ -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 | 1636 | switch (root_scope.status) { |
| 843 | .never_loaded => { | |
| 844 | const src_module = try self.getSrcModule(root_scope); | |
| 1637 | .never_loaded, .unloaded_success => { | |
| 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 | |
| 847 | // appendAssumeCapacity. | |
| 848 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | |
| 1640 | const source = try root_scope.getSource(self); | |
| 849 | 1641 | |
| 850 | for (src_module.decls) |decl| { | |
| 851 | if (decl.cast(zir.Inst.Export)) |export_inst| { | |
| 852 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base); | |
| 853 | } | |
| 1642 | var keep_tree = false; | |
| 1643 | const tree = try std.zig.parse(self.allocator, source); | |
| 1644 | defer if (!keep_tree) tree.deinit(); | |
| 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, | |
| 858 | .unloaded_sema_failure, | |
| 859 | .unloaded_success, | |
| 860 | .loaded_sema_failure, | |
| 861 | .loaded_success, | |
| 862 | => { | |
| 863 | const src_module = try self.getSrcModule(root_scope); | |
| 864 | ||
| 865 | var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator); | |
| 866 | defer exports_to_resolve.deinit(); | |
| 867 | ||
| 868 | // Keep track of the decls that we expect to see in this file so that | |
| 869 | // we know which ones have been deleted. | |
| 870 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); | |
| 871 | defer deleted_decls.deinit(); | |
| 872 | try deleted_decls.ensureCapacity(self.decl_table.size); | |
| 873 | { | |
| 874 | var it = self.decl_table.iterator(); | |
| 875 | while (it.next()) |kv| { | |
| 876 | deleted_decls.putAssumeCapacityNoClobber(kv.value, {}); | |
| 1671 | .unloaded_parse_failure => return error.AnalysisFail, | |
| 1672 | ||
| 1673 | .loaded_success => return root_scope.contents.tree, | |
| 1674 | } | |
| 1675 | } | |
| 1676 | ||
| 1677 | fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { | |
| 1678 | // We may be analyzing it for the first time, or this may be | |
| 1679 | // an incremental update. This code handles both cases. | |
| 1680 | const tree = try self.getAstTree(root_scope); | |
| 1681 | const decls = tree.root_node.decls(); | |
| 1682 | ||
| 1683 | try self.work_queue.ensureUnusedCapacity(decls.len); | |
| 1684 | try root_scope.decls.ensureCapacity(self.allocator, decls.len); | |
| 1685 | ||
| 1686 | // Keep track of the decls that we expect to see in this file so that | |
| 1687 | // we know which ones have been deleted. | |
| 1688 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); | |
| 1689 | defer deleted_decls.deinit(); | |
| 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 | } | |
| 879 | ||
| 880 | for (src_module.decls) |src_decl| { | |
| 881 | const name_hash = Decl.hashSimpleName(src_decl.name); | |
| 882 | if (self.decl_table.get(name_hash)) |kv| { | |
| 883 | const decl = kv.value; | |
| 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; | |
| 1714 | } else { | |
| 1715 | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); | |
| 1716 | root_scope.decls.appendAssumeCapacity(new_decl); | |
| 1717 | if (fn_proto.extern_export_inline_token) |maybe_export_token| { | |
| 1718 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | |
| 1719 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | |
| 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 | { | |
| 897 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 898 | // with when we delete decls because they are no longer referenced. | |
| 899 | var it = deleted_decls.iterator(); | |
| 900 | while (it.next()) |kv| { | |
| 901 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | |
| 902 | try self.deleteDecl(kv.key); | |
| 903 | } | |
| 1723 | } | |
| 1724 | // TODO also look for global variable declarations | |
| 1725 | // TODO also look for comptime blocks and exported globals | |
| 1726 | } | |
| 1727 | { | |
| 1728 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 1729 | // with when we delete decls because they are no longer referenced. | |
| 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| { | |
| 906 | _ = try self.resolveDecl(&root_scope.base, export_inst); | |
| 1771 | } else { | |
| 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 | 1799 | fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 913 | 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 | 1806 | //std.debug.warn("deleting decl '{}'\n", .{decl.name}); |
| 916 | 1807 | const name_hash = decl.fullyQualifiedNameHash(); |
| 917 | 1808 | self.decl_table.removeAssertDiscard(name_hash); |
| 918 | 1809 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| 919 | 1810 | for (decl.dependencies.items) |dep| { |
| 920 | 1811 | dep.removeDependant(decl); |
| 921 | if (dep.dependants.items.len == 0) { | |
| 1812 | if (dep.dependants.items.len == 0 and !dep.deletion_flag) { | |
| 922 | 1813 | // We don't recursively perform a deletion here, because during the update, |
| 923 | 1814 | // another reference to it may turn up. |
| 924 | assert(!dep.deletion_flag); | |
| 925 | 1815 | dep.deletion_flag = true; |
| 926 | 1816 | self.deletion_set.appendAssumeCapacity(dep); |
| 927 | 1817 | } |
| ... | ... | @@ -974,83 +1864,89 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 974 | 1864 | } |
| 975 | 1865 | |
| 976 | 1866 | fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1867 | const tracy = trace(@src()); | |
| 1868 | defer tracy.end(); | |
| 1869 | ||
| 977 | 1870 | // Use the Decl's arena for function memory. |
| 978 | 1871 | var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator); |
| 979 | 1872 | defer decl.typed_value.most_recent.arena.?.* = arena.state; |
| 980 | var analysis: Fn.Analysis = .{ | |
| 981 | .inner_block = .{ | |
| 982 | .func = func, | |
| 983 | .decl = decl, | |
| 984 | .instructions = .{}, | |
| 985 | .arena = &arena.allocator, | |
| 986 | }, | |
| 987 | .needed_inst_capacity = 0, | |
| 988 | .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator), | |
| 1873 | var inner_block: Scope.Block = .{ | |
| 1874 | .func = func, | |
| 1875 | .decl = decl, | |
| 1876 | .instructions = .{}, | |
| 1877 | .arena = &arena.allocator, | |
| 989 | 1878 | }; |
| 990 | defer analysis.inner_block.instructions.deinit(self.allocator); | |
| 991 | defer analysis.inst_table.deinit(); | |
| 1879 | defer inner_block.instructions.deinit(self.allocator); | |
| 992 | 1880 | |
| 993 | const fn_inst = func.analysis.queued; | |
| 994 | func.analysis = .{ .in_progress = &analysis }; | |
| 1881 | const fn_zir = func.analysis.queued; | |
| 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 = .{ | |
| 999 | .success = .{ | |
| 1000 | .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items), | |
| 1001 | }, | |
| 1002 | }; | |
| 1888 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); | |
| 1889 | func.analysis = .{ .success = .{ .instructions = instructions } }; | |
| 1890 | //std.debug.warn("set {} to success\n", .{decl.name}); | |
| 1003 | 1891 | } |
| 1004 | 1892 | |
| 1005 | fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void { | |
| 1006 | switch (decl.analysis) { | |
| 1007 | .in_progress => unreachable, | |
| 1008 | .dependency_failure, | |
| 1009 | .sema_failure, | |
| 1010 | .codegen_failure, | |
| 1011 | .codegen_failure_retryable, | |
| 1012 | .complete, | |
| 1013 | => return, | |
| 1014 | ||
| 1015 | .outdated => {}, // Decl re-analysis | |
| 1893 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | |
| 1894 | //std.debug.warn("mark {} outdated\n", .{decl.name}); | |
| 1895 | try self.work_queue.writeItem(.{ .analyze_decl = decl }); | |
| 1896 | if (self.failed_decls.remove(decl)) |entry| { | |
| 1897 | entry.value.destroy(self.allocator); | |
| 1016 | 1898 | } |
| 1017 | //std.debug.warn("re-analyzing {}\n", .{decl.name}); | |
| 1018 | decl.src = old_inst.src; | |
| 1899 | decl.analysis = .outdated; | |
| 1900 | } | |
| 1019 | 1901 | |
| 1020 | // The exports this Decl performs will be re-discovered, so we remove them here | |
| 1021 | // prior to re-analysis. | |
| 1022 | self.deleteDeclExports(decl); | |
| 1023 | // Dependencies will be re-discovered, so we remove them here prior to re-analysis. | |
| 1024 | for (decl.dependencies.items) |dep| { | |
| 1025 | dep.removeDependant(decl); | |
| 1026 | if (dep.dependants.items.len == 0) { | |
| 1027 | // We don't perform a deletion here, because this Decl or another one | |
| 1028 | // may end up referencing it before the update is complete. | |
| 1029 | assert(!dep.deletion_flag); | |
| 1030 | dep.deletion_flag = true; | |
| 1031 | try self.deletion_set.append(self.allocator, dep); | |
| 1032 | } | |
| 1033 | } | |
| 1034 | decl.dependencies.shrink(self.allocator, 0); | |
| 1902 | fn allocateNewDecl( | |
| 1903 | self: *Module, | |
| 1904 | scope: *Scope, | |
| 1905 | src_index: usize, | |
| 1906 | contents_hash: std.zig.SrcHash, | |
| 1907 | ) !*Decl { | |
| 1908 | const new_decl = try self.allocator.create(Decl); | |
| 1909 | new_decl.* = .{ | |
| 1910 | .name = "", | |
| 1911 | .scope = scope.namespace(), | |
| 1912 | .src_index = src_index, | |
| 1913 | .typed_value = .{ .never_succeeded = {} }, | |
| 1914 | .analysis = .unreferenced, | |
| 1915 | .deletion_flag = false, | |
| 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 | 1940 | var decl_scope: Scope.DeclAnalysis = .{ |
| 1036 | 1941 | .decl = decl, |
| 1037 | 1942 | .arena = std.heap.ArenaAllocator.init(self.allocator), |
| 1038 | 1943 | }; |
| 1039 | 1944 | errdefer decl_scope.arena.deinit(); |
| 1040 | 1945 | |
| 1041 | const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) { | |
| 1042 | error.OutOfMemory => return error.OutOfMemory, | |
| 1043 | error.AnalysisFail => { | |
| 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 | }; | |
| 1946 | decl.analysis = .in_progress; | |
| 1947 | ||
| 1948 | const typed_value = try self.analyzeConstInst(&decl_scope.base, src_decl.inst); | |
| 1052 | 1949 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1053 | arena_state.* = decl_scope.arena.state; | |
| 1054 | 1950 | |
| 1055 | 1951 | var prev_type_has_bits = false; |
| 1056 | 1952 | var type_changed = true; |
| ... | ... | @@ -1061,6 +1957,8 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi |
| 1061 | 1957 | |
| 1062 | 1958 | tvm.deinit(self.allocator); |
| 1063 | 1959 | } |
| 1960 | ||
| 1961 | arena_state.* = decl_scope.arena.state; | |
| 1064 | 1962 | decl.typed_value = .{ |
| 1065 | 1963 | .most_recent = .{ |
| 1066 | 1964 | .typed_value = typed_value, |
| ... | ... | @@ -1079,137 +1977,66 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi |
| 1079 | 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 | |
| 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 | } | |
| 1980 | return type_changed; | |
| 1101 | 1981 | } |
| 1102 | 1982 | |
| 1103 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | |
| 1104 | //std.debug.warn("mark {} outdated\n", .{decl.name}); | |
| 1105 | try self.work_queue.writeItem(.{ .re_analyze_decl = decl }); | |
| 1106 | if (self.failed_decls.remove(decl)) |entry| { | |
| 1107 | entry.value.destroy(self.allocator); | |
| 1108 | } | |
| 1109 | decl.analysis = .outdated; | |
| 1983 | fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { | |
| 1984 | const zir_module = self.root_scope.cast(Scope.ZIRModule).?; | |
| 1985 | const entry = zir_module.contents.module.findDecl(src_decl.name).?; | |
| 1986 | return self.resolveZirDeclHavingIndex(scope, src_decl, entry.index); | |
| 1110 | 1987 | } |
| 1111 | 1988 | |
| 1112 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { | |
| 1113 | const hash = Decl.hashSimpleName(old_inst.name); | |
| 1114 | if (self.decl_table.get(hash)) |kv| { | |
| 1115 | const decl = kv.value; | |
| 1116 | try self.reAnalyzeDecl(decl, old_inst); | |
| 1117 | 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 | } | |
| 1989 | fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { | |
| 1990 | const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); | |
| 1991 | const decl = self.decl_table.getValue(name_hash).?; | |
| 1992 | decl.src_index = src_index; | |
| 1993 | try self.ensureDeclAnalyzed(decl); | |
| 1994 | return decl; | |
| 1181 | 1995 | } |
| 1182 | 1996 | |
| 1183 | 1997 | /// Declares a dependency on the decl. |
| 1184 | fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { | |
| 1185 | const decl = try self.resolveDecl(scope, old_inst); | |
| 1998 | fn resolveCompleteZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { | |
| 1999 | const decl = try self.resolveZirDecl(scope, src_decl); | |
| 1186 | 2000 | switch (decl.analysis) { |
| 2001 | .unreferenced => unreachable, | |
| 1187 | 2002 | .in_progress => unreachable, |
| 1188 | 2003 | .outdated => unreachable, |
| 1189 | 2004 | |
| 1190 | 2005 | .dependency_failure, |
| 1191 | 2006 | .sema_failure, |
| 2007 | .sema_failure_retryable, | |
| 1192 | 2008 | .codegen_failure, |
| 1193 | 2009 | .codegen_failure_retryable, |
| 1194 | 2010 | => return error.AnalysisFail, |
| 1195 | 2011 | |
| 1196 | 2012 | .complete => {}, |
| 1197 | 2013 | } |
| 1198 | if (scope.decl()) |scope_decl| { | |
| 1199 | try self.declareDeclDependency(scope_decl, decl); | |
| 1200 | } | |
| 1201 | 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 | 2018 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { |
| 1205 | if (scope.cast(Scope.Block)) |block| { | |
| 1206 | if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| { | |
| 1207 | return kv.value; | |
| 1208 | } | |
| 1209 | } | |
| 1210 | ||
| 1211 | const decl = try self.resolveCompleteDecl(scope, old_inst); | |
| 2019 | if (old_inst.analyzed_inst) |inst| return inst; | |
| 2020 | ||
| 2021 | // If this assert trips, the instruction that was referenced did not get properly | |
| 2022 | // analyzed before it was referenced. | |
| 2023 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; | |
| 2024 | const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: { | |
| 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 | 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 | 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 | 2085 | return val.toType(); |
| 1259 | 2086 | } |
| 1260 | 2087 | |
| 1261 | fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void { | |
| 1262 | try self.decl_exports.ensureCapacity(self.decl_exports.size + 1); | |
| 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); | |
| 2088 | fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void { | |
| 2089 | try self.ensureDeclAnalyzed(exported_decl); | |
| 1266 | 2090 | const typed_value = exported_decl.typed_value.most_recent.typed_value; |
| 1267 | 2091 | switch (typed_value.ty.zigTypeTag()) { |
| 1268 | 2092 | .Fn => {}, |
| 1269 | else => return self.fail( | |
| 1270 | scope, | |
| 1271 | export_inst.positionals.value.src, | |
| 1272 | "unable to export type '{}'", | |
| 1273 | .{typed_value.ty}, | |
| 1274 | ), | |
| 2093 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), | |
| 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 | 2098 | const new_export = try self.allocator.create(Export); |
| 1277 | 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 | 2102 | |
| 1281 | 2103 | new_export.* = .{ |
| 1282 | 2104 | .options = .{ .name = symbol_name }, |
| 1283 | .src = export_inst.base.src, | |
| 2105 | .src = src, | |
| 1284 | 2106 | .link = .{}, |
| 1285 | 2107 | .owner_decl = owner_decl, |
| 1286 | 2108 | .exported_decl = exported_decl, |
| ... | ... | @@ -1311,7 +2133,7 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In |
| 1311 | 2133 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); |
| 1312 | 2134 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 1313 | 2135 | self.allocator, |
| 1314 | export_inst.base.src, | |
| 2136 | src, | |
| 1315 | 2137 | "unable to export: {}", |
| 1316 | 2138 | .{@errorName(err)}, |
| 1317 | 2139 | )); |
| ... | ... | @@ -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 | 2145 | fn addNewInstArgs( |
| 1325 | 2146 | self: *Module, |
| 1326 | 2147 | block: *Scope.Block, |
| ... | ... | @@ -1334,6 +2155,46 @@ fn addNewInstArgs( |
| 1334 | 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 | 2198 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { |
| 1338 | 2199 | const inst = try block.arena.create(T); |
| 1339 | 2200 | inst.* = .{ |
| ... | ... | @@ -1361,19 +2222,6 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) |
| 1361 | 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 | 2225 | fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { |
| 1378 | 2226 | return self.constInst(scope, src, .{ |
| 1379 | 2227 | .ty = Type.initTag(.type), |
| ... | ... | @@ -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 | 2246 | fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { |
| 1392 | 2247 | return self.constInst(scope, src, .{ |
| 1393 | 2248 | .ty = ty, |
| ... | ... | @@ -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 | 2310 | const new_inst = try self.analyzeInst(scope, old_inst); |
| 1456 | 2311 | return TypedValue{ |
| 1457 | 2312 | .ty = new_inst.ty, |
| ... | ... | @@ -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 | 2324 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { |
| 1463 | 2325 | switch (old_inst.tag) { |
| 1464 | 2326 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), |
| 1465 | 2327 | .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), |
| 1466 | 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 | 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 | 2332 | .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?), |
| 1469 | .str => { | |
| 1470 | const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes; | |
| 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 | }, | |
| 2333 | .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.cast(zir.Inst.DeclValInModule).?), | |
| 2334 | .str => return self.analyzeInstStr(scope, old_inst.cast(zir.Inst.Str).?), | |
| 1476 | 2335 | .int => { |
| 1477 | 2336 | const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int; |
| 1478 | 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 | 2343 | .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?), |
| 1485 | 2344 | .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?), |
| 1486 | 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 | 2347 | .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?), |
| 1488 | .@"export" => { | |
| 1489 | try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?); | |
| 1490 | return self.constVoid(scope, old_inst.src); | |
| 1491 | }, | |
| 2348 | .@"export" => return self.analyzeInstExport(scope, old_inst.cast(zir.Inst.Export).?), | |
| 1492 | 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 | 2350 | .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?), |
| 1495 | 2351 | .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?), |
| 1496 | 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 | 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 | 2435 | fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { |
| 1507 | 2436 | return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); |
| 1508 | 2437 | } |
| 1509 | 2438 | |
| 1510 | 2439 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { |
| 1511 | 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 { | |
| 1516 | const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand); | |
| 1517 | return self.analyzeDeclRef(scope, inst.base.src, decl); | |
| 2444 | fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { | |
| 2445 | const decl_name = try self.resolveConstString(scope, inst.positionals.name); | |
| 2446 | return self.analyzeDeclRefByName(scope, inst.base.src, decl_name); | |
| 1518 | 2447 | } |
| 1519 | 2448 | |
| 1520 | 2449 | fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { |
| 1521 | const decl_name = try self.resolveConstString(scope, 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); | |
| 2450 | return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); | |
| 1529 | 2451 | } |
| 1530 | 2452 | |
| 1531 | 2453 | fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { |
| 1532 | 2454 | const decl_name = inst.positionals.name; |
| 1533 | // This will need to get more fleshed out when there are proper structs & namespaces. | |
| 1534 | const zir_module = scope.namespace(); | |
| 2455 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; | |
| 1535 | 2456 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse |
| 1536 | 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 | 2461 | return decl; |
| 1541 | 2462 | } |
| ... | ... | @@ -1546,18 +2467,46 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn |
| 1546 | 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 | 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 | 2492 | const decl_tv = try decl.typedValue(); |
| 1551 | 2493 | const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); |
| 1552 | 2494 | ty_payload.* = .{ .pointee_type = decl_tv.ty }; |
| 1553 | 2495 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); |
| 1554 | 2496 | val_payload.* = .{ .decl = decl }; |
| 2497 | ||
| 1555 | 2498 | return self.constInst(scope, src, .{ |
| 1556 | 2499 | .ty = Type.initPayload(&ty_payload.base), |
| 1557 | 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 | 2510 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { |
| 1562 | 2511 | const func = try self.resolveInst(scope, inst.positionals.func); |
| 1563 | 2512 | if (func.ty.zigTypeTag() != .Fn) |
| ... | ... | @@ -1616,7 +2565,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro |
| 1616 | 2565 | } |
| 1617 | 2566 | |
| 1618 | 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 | 2569 | .func = func, |
| 1621 | 2570 | .args = casted_args, |
| 1622 | 2571 | }); |
| ... | ... | @@ -1624,10 +2573,22 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro |
| 1624 | 2573 | |
| 1625 | 2574 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { |
| 1626 | 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 | 2589 | const new_func = try scope.arena().create(Fn); |
| 1628 | 2590 | new_func.* = .{ |
| 1629 | .fn_type = fn_type, | |
| 1630 | .analysis = .{ .queued = fn_inst }, | |
| 2591 | .analysis = .{ .queued = fn_zir }, | |
| 1631 | 2592 | .owner_decl = scope.decl().?, |
| 1632 | 2593 | }; |
| 1633 | 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 | 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 | 2619 | if (return_type.zigTypeTag() == .NoReturn and |
| 1652 | 2620 | fntype.positionals.param_types.len == 0 and |
| 1653 | 2621 | fntype.kw_args.cc == .Naked) |
| ... | ... | @@ -1683,7 +2651,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn |
| 1683 | 2651 | // TODO handle known-pointer-address |
| 1684 | 2652 | const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src); |
| 1685 | 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 | 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 | 2843 | } |
| 1876 | 2844 | |
| 1877 | 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 | 2847 | .asm_source = asm_source, |
| 1880 | 2848 | .is_volatile = assembly.kw_args.@"volatile", |
| 1881 | 2849 | .output = output, |
| ... | ... | @@ -1911,20 +2879,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError! |
| 1911 | 2879 | } |
| 1912 | 2880 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 1913 | 2881 | switch (op) { |
| 1914 | .eq => return self.addNewInstArgs( | |
| 1915 | b, | |
| 1916 | inst.base.src, | |
| 1917 | Type.initTag(.bool), | |
| 1918 | Inst.IsNull, | |
| 1919 | Inst.Args(Inst.IsNull){ .operand = opt_operand }, | |
| 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 | ), | |
| 2882 | .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{ | |
| 2883 | .operand = opt_operand, | |
| 2884 | }), | |
| 2885 | .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{ | |
| 2886 | .operand = opt_operand, | |
| 2887 | }), | |
| 1928 | 2888 | else => unreachable, |
| 1929 | 2889 | } |
| 1930 | 2890 | } else if (is_equality_cmp and |
| ... | ... | @@ -2019,23 +2979,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea |
| 2019 | 2979 | } |
| 2020 | 2980 | |
| 2021 | 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 | 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 | 2992 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { |
| 2027 | if (scope.cast(Scope.Block)) |b| { | |
| 2028 | const analysis = b.func.analysis.in_progress; | |
| 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 | } | |
| 2993 | for (body.instructions) |src_inst| { | |
| 2994 | src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst); | |
| 2039 | 2995 | } |
| 2040 | 2996 | } |
| 2041 | 2997 | |
| ... | ... | @@ -2118,7 +3074,7 @@ fn cmpNumeric( |
| 2118 | 3074 | }; |
| 2119 | 3075 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 2120 | 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 | 3078 | .lhs = casted_lhs, |
| 2123 | 3079 | .rhs = casted_rhs, |
| 2124 | 3080 | .op = op, |
| ... | ... | @@ -2222,7 +3178,7 @@ fn cmpNumeric( |
| 2222 | 3178 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 2223 | 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 | 3182 | .lhs = casted_lhs, |
| 2227 | 3183 | .rhs = casted_rhs, |
| 2228 | 3184 | .op = op, |
| ... | ... | @@ -2299,7 +3255,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 2299 | 3255 | } |
| 2300 | 3256 | // TODO validate the type size and other compile errors |
| 2301 | 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 | 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 | 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 | 3299 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { |
| 2320 | 3300 | { |
| 2321 | 3301 | errdefer err_msg.destroy(self.allocator); |
| ... | ... | @@ -2326,18 +3306,31 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err |
| 2326 | 3306 | .decl => { |
| 2327 | 3307 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; |
| 2328 | 3308 | decl.analysis = .sema_failure; |
| 3309 | decl.generation = self.generation; | |
| 2329 | 3310 | self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); |
| 2330 | 3311 | }, |
| 2331 | 3312 | .block => { |
| 2332 | 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 | 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 | 3328 | .zir_module => { |
| 2337 | 3329 | const zir_module = scope.cast(Scope.ZIRModule).?; |
| 2338 | 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 | 3335 | return error.AnalysisFail; |
| 2343 | 3336 | } |
| ... | ... | @@ -2385,3 +3378,7 @@ pub const ErrorMsg = struct { |
| 2385 | 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 | 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 | 10 | const ErrorMsg = Module.ErrorMsg; |
| 11 | 11 | const Target = std.Target; |
| 12 | 12 | const Allocator = mem.Allocator; |
| 13 | const trace = @import("tracy.zig").trace; | |
| 13 | 14 | |
| 14 | 15 | pub const Result = union(enum) { |
| 15 | 16 | /// The `code` parameter passed to `generateSymbol` has the value appended. |
| ... | ... | @@ -29,6 +30,9 @@ pub fn generateSymbol( |
| 29 | 30 | /// A Decl that this symbol depends on had a semantic analysis failure. |
| 30 | 31 | AnalysisFail, |
| 31 | 32 | }!Result { |
| 33 | const tracy = trace(@src()); | |
| 34 | defer tracy.end(); | |
| 35 | ||
| 32 | 36 | switch (typed_value.ty.zigTypeTag()) { |
| 33 | 37 | .Fn => { |
| 34 | 38 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; |
| ... | ... | @@ -178,6 +182,7 @@ const Function = struct { |
| 178 | 182 | .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), |
| 179 | 183 | .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), |
| 180 | 184 | .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), |
| 185 | .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?), | |
| 181 | 186 | .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), |
| 182 | 187 | .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), |
| 183 | 188 | .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), |
| ... | ... | @@ -213,7 +218,7 @@ const Function = struct { |
| 213 | 218 | try self.code.resize(self.code.items.len + 7); |
| 214 | 219 | self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 }; |
| 215 | 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 | 222 | switch (return_type.zigTypeTag()) { |
| 218 | 223 | .Void => return MCValue{ .none = {} }, |
| 219 | 224 | .NoReturn => return MCValue{ .unreach = {} }, |
| ... | ... | @@ -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 | 242 | switch (self.target.cpu.arch) { |
| 235 | 243 | .i386, .x86_64 => { |
| 236 | 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 | 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 | 260 | fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { |
| 244 | 261 | switch (self.target.cpu.arch) { |
| 245 | 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 | 26 | isnull, |
| 27 | 27 | ptrtoint, |
| 28 | 28 | ret, |
| 29 | retvoid, | |
| 29 | 30 | unreach, |
| 30 | 31 | }; |
| 31 | 32 | |
| ... | ... | @@ -146,6 +147,14 @@ pub const Inst = struct { |
| 146 | 147 | pub const Ret = struct { |
| 147 | 148 | pub const base_tag = Tag.ret; |
| 148 | 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 | 158 | args: void, |
| 150 | 159 | }; |
| 151 | 160 |
src-self-hosted/link.zig+12-13| ... | ... | @@ -369,7 +369,7 @@ pub const ElfFile = struct { |
| 369 | 369 | const file_size = self.options.program_code_size_hint; |
| 370 | 370 | const p_align = 0x1000; |
| 371 | 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 | 373 | try self.program_headers.append(self.allocator, .{ |
| 374 | 374 | .p_type = elf.PT_LOAD, |
| 375 | 375 | .p_offset = off, |
| ... | ... | @@ -390,7 +390,7 @@ pub const ElfFile = struct { |
| 390 | 390 | // page align. |
| 391 | 391 | const p_align = 0x1000; |
| 392 | 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 | 394 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 395 | 395 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 396 | 396 | // else in virtual memory. |
| ... | ... | @@ -412,7 +412,7 @@ pub const ElfFile = struct { |
| 412 | 412 | assert(self.shstrtab.items.len == 0); |
| 413 | 413 | try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0 |
| 414 | 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 | 416 | try self.sections.append(self.allocator, .{ |
| 417 | 417 | .sh_name = try self.makeString(".shstrtab"), |
| 418 | 418 | .sh_type = elf.SHT_STRTAB, |
| ... | ... | @@ -470,7 +470,7 @@ pub const ElfFile = struct { |
| 470 | 470 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 471 | 471 | const file_size = self.options.symbol_count_hint * each_size; |
| 472 | 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 | 475 | try self.sections.append(self.allocator, .{ |
| 476 | 476 | .sh_name = try self.makeString(".symtab"), |
| ... | ... | @@ -586,7 +586,7 @@ pub const ElfFile = struct { |
| 586 | 586 | shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1); |
| 587 | 587 | } |
| 588 | 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 | 591 | try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); |
| 592 | 592 | if (!self.shdr_table_dirty) { |
| ... | ... | @@ -632,7 +632,7 @@ pub const ElfFile = struct { |
| 632 | 632 | |
| 633 | 633 | for (buf) |*shdr, i| { |
| 634 | 634 | shdr.* = self.sections.items[i]; |
| 635 | //std.debug.warn("writing section {}\n", .{shdr.*}); | |
| 635 | //std.log.debug(.link, "writing section {}\n", .{shdr.*}); | |
| 636 | 636 | if (foreign_endian) { |
| 637 | 637 | bswapAllFields(elf.Elf64_Shdr, shdr); |
| 638 | 638 | } |
| ... | ... | @@ -956,10 +956,10 @@ pub const ElfFile = struct { |
| 956 | 956 | try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len); |
| 957 | 957 | |
| 958 | 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 | 960 | decl.link.local_sym_index = i; |
| 961 | 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 | 963 | decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| 964 | 964 | _ = self.local_symbols.addOneAssumeCapacity(); |
| 965 | 965 | } |
| ... | ... | @@ -1002,7 +1002,7 @@ pub const ElfFile = struct { |
| 1002 | 1002 | defer code_buffer.deinit(); |
| 1003 | 1003 | |
| 1004 | 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 | 1006 | .externally_managed => |x| x, |
| 1007 | 1007 | .appended => code_buffer.items, |
| 1008 | 1008 | .fail => |em| { |
| ... | ... | @@ -1027,11 +1027,11 @@ pub const ElfFile = struct { |
| 1027 | 1027 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 1028 | 1028 | if (need_realloc) { |
| 1029 | 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 | 1031 | if (vaddr != local_sym.st_value) { |
| 1032 | 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 | 1035 | self.offset_table.items[decl.link.offset_table_index] = vaddr; |
| 1036 | 1036 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 1037 | 1037 | } |
| ... | ... | @@ -1049,7 +1049,7 @@ pub const ElfFile = struct { |
| 1049 | 1049 | const decl_name = mem.spanZ(decl.name); |
| 1050 | 1050 | const name_str_index = try self.makeString(decl_name); |
| 1051 | 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 | 1053 | errdefer self.freeTextBlock(&decl.link); |
| 1054 | 1054 | |
| 1055 | 1055 | local_sym.* = .{ |
| ... | ... | @@ -1307,7 +1307,6 @@ pub const ElfFile = struct { |
| 1307 | 1307 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 1308 | 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 | 1310 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 1312 | 1311 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; |
| 1313 | 1312 | switch (self.ptr_width) { |
src-self-hosted/main.zig+30-16| ... | ... | @@ -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 | 64 | pub fn main() !void { |
| 42 | 65 | // TODO general purpose allocator in the zig std lib |
| 43 | 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 | 109 | \\ zig build-obj <options> [files] |
| 87 | 110 | \\ |
| 88 | 111 | \\Supported file types: |
| 89 | \\ (planned) .zig Zig source code | |
| 112 | \\ .zig Zig source code | |
| 90 | 113 | \\ .zir Zig Intermediate Representation code |
| 91 | 114 | \\ (planned) .o ELF object file |
| 92 | 115 | \\ (planned) .o MACH-O (macOS) object file |
| ... | ... | @@ -407,21 +430,7 @@ fn buildOutputType( |
| 407 | 430 | std.debug.warn("-fno-emit-bin not supported yet", .{}); |
| 408 | 431 | process.exit(1); |
| 409 | 432 | }, |
| 410 | .yes_default_path => switch (output_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 | }, | |
| 433 | .yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode), | |
| 425 | 434 | .yes => |p| p, |
| 426 | 435 | }; |
| 427 | 436 | |
| ... | ... | @@ -450,6 +459,7 @@ fn buildOutputType( |
| 450 | 459 | .link_mode = link_mode, |
| 451 | 460 | .object_format = object_format, |
| 452 | 461 | .optimize_mode = build_mode, |
| 462 | .keep_source_files_loaded = zir_out_path != null, | |
| 453 | 463 | }); |
| 454 | 464 | defer module.deinit(); |
| 455 | 465 | |
| ... | ... | @@ -487,7 +497,9 @@ fn buildOutputType( |
| 487 | 497 | } |
| 488 | 498 | |
| 489 | 499 | fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void { |
| 500 | var timer = try std.time.Timer.start(); | |
| 490 | 501 | try module.update(); |
| 502 | const update_nanos = timer.read(); | |
| 491 | 503 | |
| 492 | 504 | var errors = try module.getAllErrorsAlloc(); |
| 493 | 505 | defer errors.deinit(module.allocator); |
| ... | ... | @@ -501,6 +513,8 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo |
| 501 | 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 | 520 | if (zir_out_path) |zop| { |
src-self-hosted/test.zig+146-156| ... | ... | @@ -21,32 +21,7 @@ const ErrorMsg = struct { |
| 21 | 21 | }; |
| 22 | 22 | |
| 23 | 23 | pub const TestContext = struct { |
| 24 | // TODO: remove these. They are deprecated. | |
| 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 | }; | |
| 24 | zir_cases: std.ArrayList(Case), | |
| 50 | 25 | |
| 51 | 26 | pub const ZIRUpdate = struct { |
| 52 | 27 | /// The input to the current update. We simulate an incremental update |
| ... | ... | @@ -57,58 +32,55 @@ pub const TestContext = struct { |
| 57 | 32 | /// you can keep it mostly consistent, with small changes, testing the |
| 58 | 33 | /// effects of the incremental compilation. |
| 59 | 34 | src: [:0]const u8, |
| 60 | case: union(ZIRUpdateType) { | |
| 61 | /// The expected output ZIR | |
| 35 | case: union(enum) { | |
| 36 | /// A transformation update transforms the input ZIR and tests against | |
| 37 | /// the expected output ZIR. | |
| 62 | 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 | 41 | /// A slice containing the expected errors *in sequential order*. |
| 64 | 42 | Error: []const ErrorMsg, |
| 65 | ||
| 66 | /// Input to feed to the program, and expected outputs. | |
| 67 | /// | |
| 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, | |
| 43 | /// An execution update compiles and runs the input ZIR, feeding in | |
| 44 | /// provided input and ensuring that the stdout match what is expected. | |
| 45 | Execution: []const u8, | |
| 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 | 50 | /// compile it, ensure that compilation fails, and more. The same Module is |
| 89 | 51 | /// used for each update, so each update's source is treated as a single file |
| 90 | 52 | /// being updated by the test harness and incrementally compiled. |
| 91 | pub const ZIRCase = struct { | |
| 53 | pub const Case = struct { | |
| 92 | 54 | name: []const u8, |
| 93 | 55 | /// The platform the ZIR targets. For non-native platforms, an emulator |
| 94 | 56 | /// such as QEMU is required for tests to complete. |
| 95 | 57 | target: std.zig.CrossTarget, |
| 96 | 58 | updates: std.ArrayList(ZIRUpdate), |
| 59 | output_mode: std.builtin.OutputMode, | |
| 60 | /// Either ".zir" or ".zig" | |
| 61 | extension: [4]u8, | |
| 97 | 62 | |
| 98 | 63 | /// Adds a subcase in which the module is updated with new ZIR, and the |
| 99 | 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 | 66 | self.updates.append(.{ |
| 102 | 67 | .src = src, |
| 103 | 68 | .case = .{ .Transformation = result }, |
| 104 | 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 | 79 | /// Adds a subcase in which the module is updated with invalid ZIR, and |
| 108 | 80 | /// ensures that compilation fails for the expected reasons. |
| 109 | 81 | /// |
| 110 | 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 | 84 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; |
| 113 | 85 | for (errors) |e, i| { |
| 114 | 86 | if (e[0] != ':') { |
| ... | ... | @@ -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 | 138 | ctx: *TestContext, |
| 151 | 139 | name: []const u8, |
| 152 | 140 | target: std.zig.CrossTarget, |
| 153 | ) *ZIRCase { | |
| 154 | const case = ZIRCase{ | |
| 141 | ) *Case { | |
| 142 | const case = Case{ | |
| 155 | 143 | .name = name, |
| 156 | 144 | .target = target, |
| 157 | 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 | 181 | ctx.zir_cases.append(case) catch unreachable; |
| 160 | 182 | return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1]; |
| ... | ... | @@ -163,14 +185,21 @@ pub const TestContext = struct { |
| 163 | 185 | pub fn addZIRCompareOutput( |
| 164 | 186 | ctx: *TestContext, |
| 165 | 187 | name: []const u8, |
| 166 | src_list: []const []const u8, | |
| 167 | expected_stdout_list: []const []const u8, | |
| 188 | src: [:0]const u8, | |
| 189 | expected_stdout: []const u8, | |
| 168 | 190 | ) void { |
| 169 | ctx.zir_cmp_output_cases.append(.{ | |
| 170 | .name = name, | |
| 171 | .src_list = src_list, | |
| 172 | .expected_stdout_list = expected_stdout_list, | |
| 173 | }) catch unreachable; | |
| 191 | var c = ctx.addExeZIR(name, .{}); | |
| 192 | c.addCompareOutput(src, expected_stdout); | |
| 193 | } | |
| 194 | ||
| 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 | 205 | pub fn addZIRTransform( |
| ... | ... | @@ -180,7 +209,7 @@ pub const TestContext = struct { |
| 180 | 209 | src: [:0]const u8, |
| 181 | 210 | result: [:0]const u8, |
| 182 | 211 | ) void { |
| 183 | var c = ctx.addZIRMulti(name, target); | |
| 212 | var c = ctx.addObjZIR(name, target); | |
| 184 | 213 | c.addTransform(src, result); |
| 185 | 214 | } |
| 186 | 215 | |
| ... | ... | @@ -191,20 +220,18 @@ pub const TestContext = struct { |
| 191 | 220 | src: [:0]const u8, |
| 192 | 221 | expected_errors: []const []const u8, |
| 193 | 222 | ) void { |
| 194 | var c = ctx.addZIRMulti(name, target); | |
| 223 | var c = ctx.addObjZIR(name, target); | |
| 195 | 224 | c.addError(src, expected_errors); |
| 196 | 225 | } |
| 197 | 226 | |
| 198 | 227 | fn init() TestContext { |
| 199 | 228 | const allocator = std.heap.page_allocator; |
| 200 | 229 | return .{ |
| 201 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator), | |
| 202 | .zir_cases = std.ArrayList(ZIRCase).init(allocator), | |
| 230 | .zir_cases = std.ArrayList(Case).init(allocator), | |
| 203 | 231 | }; |
| 204 | 232 | } |
| 205 | 233 | |
| 206 | 234 | fn deinit(self: *TestContext) void { |
| 207 | self.zir_cmp_output_cases.deinit(); | |
| 208 | 235 | for (self.zir_cases.items) |c| { |
| 209 | 236 | for (c.updates.items) |u| { |
| 210 | 237 | if (u.case == .Error) { |
| ... | ... | @@ -226,30 +253,32 @@ pub const TestContext = struct { |
| 226 | 253 | |
| 227 | 254 | for (self.zir_cases.items) |case| { |
| 228 | 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 | |
| 235 | for (self.zir_cmp_output_cases.items) |case| { | |
| 236 | std.testing.base_allocator_instance.reset(); | |
| 237 | try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target); | |
| 257 | var prg_node = root_node.start(case.name, case.updates.items.len); | |
| 258 | prg_node.activate(); | |
| 259 | defer prg_node.end(); | |
| 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 | 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 | 271 | var tmp = std.testing.tmpDir(.{}); |
| 244 | 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 | 277 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); |
| 248 | 278 | defer root_pkg.destroy(); |
| 249 | 279 | |
| 250 | var prg_node = root_node.start(case.name, case.updates.items.len); | |
| 251 | prg_node.activate(); | |
| 252 | defer prg_node.end(); | |
| 280 | const bin_name = try std.zig.binNameAlloc(allocator, root_name, target, case.output_mode, null); | |
| 281 | defer allocator.free(bin_name); | |
| 253 | 282 | |
| 254 | 283 | var module = try Module.init(allocator, .{ |
| 255 | 284 | .target = target, |
| ... | ... | @@ -259,16 +288,17 @@ pub const TestContext = struct { |
| 259 | 288 | // TODO: support tests for object file building, and library builds |
| 260 | 289 | // and linking. This will require a rework to support multi-file |
| 261 | 290 | // tests. |
| 262 | .output_mode = .Obj, | |
| 291 | .output_mode = case.output_mode, | |
| 263 | 292 | // TODO: support testing optimizations |
| 264 | 293 | .optimize_mode = .Debug, |
| 265 | 294 | .bin_file_dir = tmp.dir, |
| 266 | .bin_file_path = "test_case.o", | |
| 295 | .bin_file_path = bin_name, | |
| 267 | 296 | .root_pkg = root_pkg, |
| 297 | .keep_source_files_loaded = true, | |
| 268 | 298 | }); |
| 269 | 299 | defer module.deinit(); |
| 270 | 300 | |
| 271 | for (case.updates.items) |update| { | |
| 301 | for (case.updates.items) |update, update_index| { | |
| 272 | 302 | var update_node = prg_node.start("update", 4); |
| 273 | 303 | update_node.activate(); |
| 274 | 304 | defer update_node.end(); |
| ... | ... | @@ -280,6 +310,7 @@ pub const TestContext = struct { |
| 280 | 310 | |
| 281 | 311 | var module_node = update_node.start("parse/analysis/codegen", null); |
| 282 | 312 | module_node.activate(); |
| 313 | try module.makeBinFileWritable(); | |
| 283 | 314 | try module.update(); |
| 284 | 315 | module_node.end(); |
| 285 | 316 | |
| ... | ... | @@ -328,82 +359,41 @@ pub const TestContext = struct { |
| 328 | 359 | } |
| 329 | 360 | } |
| 330 | 361 | }, |
| 331 | ||
| 332 | else => return error.unimplemented, | |
| 333 | } | |
| 334 | } | |
| 335 | } | |
| 336 | ||
| 337 | fn runOneZIRCmpOutputCase( | |
| 338 | self: *TestContext, | |
| 339 | allocator: *Allocator, | |
| 340 | root_node: *std.Progress.Node, | |
| 341 | case: ZIRCompareOutputCase, | |
| 342 | target: std.Target, | |
| 343 | ) !void { | |
| 344 | var tmp = std.testing.tmpDir(.{}); | |
| 345 | defer tmp.cleanup(); | |
| 346 | ||
| 347 | const tmp_src_path = "test-case.zir"; | |
| 348 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); | |
| 349 | defer root_pkg.destroy(); | |
| 350 | ||
| 351 | var prg_node = root_node.start(case.name, case.src_list.len); | |
| 352 | prg_node.activate(); | |
| 353 | defer prg_node.end(); | |
| 354 | ||
| 355 | var module = try Module.init(allocator, .{ | |
| 356 | .target = target, | |
| 357 | .output_mode = .Exe, | |
| 358 | .optimize_mode = .Debug, | |
| 359 | .bin_file_dir = tmp.dir, | |
| 360 | .bin_file_path = "a.out", | |
| 361 | .root_pkg = root_pkg, | |
| 362 | }); | |
| 363 | defer module.deinit(); | |
| 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; | |
| 362 | .Execution => |expected_stdout| { | |
| 363 | var exec_result = x: { | |
| 364 | var exec_node = update_node.start("execute", null); | |
| 365 | exec_node.activate(); | |
| 366 | defer exec_node.end(); | |
| 367 | ||
| 368 | try module.makeBinFileExecutable(); | |
| 369 | ||
| 370 | const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name}); | |
| 371 | defer allocator.free(exe_path); | |
| 372 | ||
| 373 | break :x try std.ChildProcess.exec(.{ | |
| 374 | .allocator = allocator, | |
| 375 | .argv = &[_][]const u8{exe_path}, | |
| 376 | .cwd_dir = tmp.dir, | |
| 377 | }); | |
| 378 | }; | |
| 379 | defer allocator.free(exec_result.stdout); | |
| 380 | defer allocator.free(exec_result.stderr); | |
| 381 | switch (exec_result.term) { | |
| 382 | .Exited => |code| { | |
| 383 | if (code != 0) { | |
| 384 | std.debug.warn("elf file exited with code {}\n", .{code}); | |
| 385 | return error.BinaryBadExitCode; | |
| 386 | } | |
| 387 | }, | |
| 388 | else => return error.BinaryCrashed, | |
| 389 | } | |
| 390 | if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) { | |
| 391 | std.debug.panic( | |
| 392 | "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n", | |
| 393 | .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout }, | |
| 394 | ); | |
| 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 | 54 | .@"undefined" => return .Undefined, |
| 55 | 55 | |
| 56 | 56 | .fn_noreturn_no_args => return .Fn, |
| 57 | .fn_void_no_args => return .Fn, | |
| 57 | 58 | .fn_naked_noreturn_no_args => return .Fn, |
| 58 | 59 | .fn_ccc_void_no_args => return .Fn, |
| 59 | 60 | |
| ... | ... | @@ -112,6 +113,12 @@ pub const Type = extern union { |
| 112 | 113 | .Undefined => return true, |
| 113 | 114 | .Null => return true, |
| 114 | 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 | 122 | const is_slice_a = isSlice(a); |
| 116 | 123 | const is_slice_b = isSlice(b); |
| 117 | 124 | if (is_slice_a != is_slice_b) |
| ... | ... | @@ -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 | 244 | pub fn format( |
| 167 | 245 | self: Type, |
| 168 | 246 | comptime fmt: []const u8, |
| ... | ... | @@ -206,6 +284,7 @@ pub const Type = extern union { |
| 206 | 284 | |
| 207 | 285 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 208 | 286 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| 287 | .fn_void_no_args => return out_stream.writeAll("fn() void"), | |
| 209 | 288 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 210 | 289 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), |
| 211 | 290 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), |
| ... | ... | @@ -269,6 +348,7 @@ pub const Type = extern union { |
| 269 | 348 | .@"null" => return Value.initTag(.null_type), |
| 270 | 349 | .@"undefined" => return Value.initTag(.undefined_type), |
| 271 | 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 | 352 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 273 | 353 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), |
| 274 | 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 | 383 | .bool, |
| 304 | 384 | .anyerror, |
| 305 | 385 | .fn_noreturn_no_args, |
| 386 | .fn_void_no_args, | |
| 306 | 387 | .fn_naked_noreturn_no_args, |
| 307 | 388 | .fn_ccc_void_no_args, |
| 308 | 389 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -333,6 +414,7 @@ pub const Type = extern union { |
| 333 | 414 | .i8, |
| 334 | 415 | .bool, |
| 335 | 416 | .fn_noreturn_no_args, // represents machine code; not a pointer |
| 417 | .fn_void_no_args, // represents machine code; not a pointer | |
| 336 | 418 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer |
| 337 | 419 | .fn_ccc_void_no_args, // represents machine code; not a pointer |
| 338 | 420 | .array_u8_sentinel_0, |
| ... | ... | @@ -420,6 +502,7 @@ pub const Type = extern union { |
| 420 | 502 | .array_u8_sentinel_0, |
| 421 | 503 | .const_slice_u8, |
| 422 | 504 | .fn_noreturn_no_args, |
| 505 | .fn_void_no_args, | |
| 423 | 506 | .fn_naked_noreturn_no_args, |
| 424 | 507 | .fn_ccc_void_no_args, |
| 425 | 508 | .int_unsigned, |
| ... | ... | @@ -466,6 +549,7 @@ pub const Type = extern union { |
| 466 | 549 | .single_const_pointer, |
| 467 | 550 | .single_const_pointer_to_comptime_int, |
| 468 | 551 | .fn_noreturn_no_args, |
| 552 | .fn_void_no_args, | |
| 469 | 553 | .fn_naked_noreturn_no_args, |
| 470 | 554 | .fn_ccc_void_no_args, |
| 471 | 555 | .int_unsigned, |
| ... | ... | @@ -509,6 +593,7 @@ pub const Type = extern union { |
| 509 | 593 | .array, |
| 510 | 594 | .array_u8_sentinel_0, |
| 511 | 595 | .fn_noreturn_no_args, |
| 596 | .fn_void_no_args, | |
| 512 | 597 | .fn_naked_noreturn_no_args, |
| 513 | 598 | .fn_ccc_void_no_args, |
| 514 | 599 | .int_unsigned, |
| ... | ... | @@ -553,6 +638,7 @@ pub const Type = extern union { |
| 553 | 638 | .@"null", |
| 554 | 639 | .@"undefined", |
| 555 | 640 | .fn_noreturn_no_args, |
| 641 | .fn_void_no_args, | |
| 556 | 642 | .fn_naked_noreturn_no_args, |
| 557 | 643 | .fn_ccc_void_no_args, |
| 558 | 644 | .int_unsigned, |
| ... | ... | @@ -597,6 +683,7 @@ pub const Type = extern union { |
| 597 | 683 | .@"null", |
| 598 | 684 | .@"undefined", |
| 599 | 685 | .fn_noreturn_no_args, |
| 686 | .fn_void_no_args, | |
| 600 | 687 | .fn_naked_noreturn_no_args, |
| 601 | 688 | .fn_ccc_void_no_args, |
| 602 | 689 | .single_const_pointer, |
| ... | ... | @@ -642,6 +729,7 @@ pub const Type = extern union { |
| 642 | 729 | .@"null", |
| 643 | 730 | .@"undefined", |
| 644 | 731 | .fn_noreturn_no_args, |
| 732 | .fn_void_no_args, | |
| 645 | 733 | .fn_naked_noreturn_no_args, |
| 646 | 734 | .fn_ccc_void_no_args, |
| 647 | 735 | .single_const_pointer, |
| ... | ... | @@ -675,6 +763,7 @@ pub const Type = extern union { |
| 675 | 763 | .@"null", |
| 676 | 764 | .@"undefined", |
| 677 | 765 | .fn_noreturn_no_args, |
| 766 | .fn_void_no_args, | |
| 678 | 767 | .fn_naked_noreturn_no_args, |
| 679 | 768 | .fn_ccc_void_no_args, |
| 680 | 769 | .array, |
| ... | ... | @@ -721,6 +810,7 @@ pub const Type = extern union { |
| 721 | 810 | .@"null", |
| 722 | 811 | .@"undefined", |
| 723 | 812 | .fn_noreturn_no_args, |
| 813 | .fn_void_no_args, | |
| 724 | 814 | .fn_naked_noreturn_no_args, |
| 725 | 815 | .fn_ccc_void_no_args, |
| 726 | 816 | .array, |
| ... | ... | @@ -777,6 +867,7 @@ pub const Type = extern union { |
| 777 | 867 | pub fn fnParamLen(self: Type) usize { |
| 778 | 868 | return switch (self.tag()) { |
| 779 | 869 | .fn_noreturn_no_args => 0, |
| 870 | .fn_void_no_args => 0, | |
| 780 | 871 | .fn_naked_noreturn_no_args => 0, |
| 781 | 872 | .fn_ccc_void_no_args => 0, |
| 782 | 873 | |
| ... | ... | @@ -823,6 +914,7 @@ pub const Type = extern union { |
| 823 | 914 | pub fn fnParamTypes(self: Type, types: []Type) void { |
| 824 | 915 | switch (self.tag()) { |
| 825 | 916 | .fn_noreturn_no_args => return, |
| 917 | .fn_void_no_args => return, | |
| 826 | 918 | .fn_naked_noreturn_no_args => return, |
| 827 | 919 | .fn_ccc_void_no_args => return, |
| 828 | 920 | |
| ... | ... | @@ -869,7 +961,10 @@ pub const Type = extern union { |
| 869 | 961 | return switch (self.tag()) { |
| 870 | 962 | .fn_noreturn_no_args => Type.initTag(.noreturn), |
| 871 | 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 | 969 | .f16, |
| 875 | 970 | .f32, |
| ... | ... | @@ -913,6 +1008,7 @@ pub const Type = extern union { |
| 913 | 1008 | pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { |
| 914 | 1009 | return switch (self.tag()) { |
| 915 | 1010 | .fn_noreturn_no_args => .Unspecified, |
| 1011 | .fn_void_no_args => .Unspecified, | |
| 916 | 1012 | .fn_naked_noreturn_no_args => .Naked, |
| 917 | 1013 | .fn_ccc_void_no_args => .C, |
| 918 | 1014 | |
| ... | ... | @@ -958,6 +1054,7 @@ pub const Type = extern union { |
| 958 | 1054 | pub fn fnIsVarArgs(self: Type) bool { |
| 959 | 1055 | return switch (self.tag()) { |
| 960 | 1056 | .fn_noreturn_no_args => false, |
| 1057 | .fn_void_no_args => false, | |
| 961 | 1058 | .fn_naked_noreturn_no_args => false, |
| 962 | 1059 | .fn_ccc_void_no_args => false, |
| 963 | 1060 | |
| ... | ... | @@ -1033,6 +1130,7 @@ pub const Type = extern union { |
| 1033 | 1130 | .@"null", |
| 1034 | 1131 | .@"undefined", |
| 1035 | 1132 | .fn_noreturn_no_args, |
| 1133 | .fn_void_no_args, | |
| 1036 | 1134 | .fn_naked_noreturn_no_args, |
| 1037 | 1135 | .fn_ccc_void_no_args, |
| 1038 | 1136 | .array, |
| ... | ... | @@ -1070,6 +1168,7 @@ pub const Type = extern union { |
| 1070 | 1168 | .type, |
| 1071 | 1169 | .anyerror, |
| 1072 | 1170 | .fn_noreturn_no_args, |
| 1171 | .fn_void_no_args, | |
| 1073 | 1172 | .fn_naked_noreturn_no_args, |
| 1074 | 1173 | .fn_ccc_void_no_args, |
| 1075 | 1174 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -1126,6 +1225,7 @@ pub const Type = extern union { |
| 1126 | 1225 | .type, |
| 1127 | 1226 | .anyerror, |
| 1128 | 1227 | .fn_noreturn_no_args, |
| 1228 | .fn_void_no_args, | |
| 1129 | 1229 | .fn_naked_noreturn_no_args, |
| 1130 | 1230 | .fn_ccc_void_no_args, |
| 1131 | 1231 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -1180,6 +1280,7 @@ pub const Type = extern union { |
| 1180 | 1280 | @"null", |
| 1181 | 1281 | @"undefined", |
| 1182 | 1282 | fn_noreturn_no_args, |
| 1283 | fn_void_no_args, | |
| 1183 | 1284 | fn_naked_noreturn_no_args, |
| 1184 | 1285 | fn_ccc_void_no_args, |
| 1185 | 1286 | single_const_pointer_to_comptime_int, |
src-self-hosted/value.zig+117-7| ... | ... | @@ -49,6 +49,7 @@ pub const Value = extern union { |
| 49 | 49 | null_type, |
| 50 | 50 | undefined_type, |
| 51 | 51 | fn_noreturn_no_args_type, |
| 52 | fn_void_no_args_type, | |
| 52 | 53 | fn_naked_noreturn_no_args_type, |
| 53 | 54 | fn_ccc_void_no_args_type, |
| 54 | 55 | single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -78,8 +79,8 @@ pub const Value = extern union { |
| 78 | 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 | comptime assert(@enumToInt(small_tag) < Tag.no_payload_count); | |
| 82 | pub fn initTag(small_tag: Tag) Value { | |
| 83 | assert(@enumToInt(small_tag) < Tag.no_payload_count); | |
| 83 | 84 | return .{ .tag_if_small_enough = @enumToInt(small_tag) }; |
| 84 | 85 | } |
| 85 | 86 | |
| ... | ... | @@ -107,6 +108,109 @@ pub const Value = extern union { |
| 107 | 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 | 214 | pub fn format( |
| 111 | 215 | self: Value, |
| 112 | 216 | comptime fmt: []const u8, |
| ... | ... | @@ -144,6 +248,7 @@ pub const Value = extern union { |
| 144 | 248 | .null_type => return out_stream.writeAll("@TypeOf(null)"), |
| 145 | 249 | .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"), |
| 146 | 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 | 252 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 148 | 253 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), |
| 149 | 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 | 334 | .null_type => Type.initTag(.@"null"), |
| 230 | 335 | .undefined_type => Type.initTag(.@"undefined"), |
| 231 | 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 | 338 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), |
| 233 | 339 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), |
| 234 | 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 | 392 | .null_type, |
| 287 | 393 | .undefined_type, |
| 288 | 394 | .fn_noreturn_no_args_type, |
| 395 | .fn_void_no_args_type, | |
| 289 | 396 | .fn_naked_noreturn_no_args_type, |
| 290 | 397 | .fn_ccc_void_no_args_type, |
| 291 | 398 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -345,6 +452,7 @@ pub const Value = extern union { |
| 345 | 452 | .null_type, |
| 346 | 453 | .undefined_type, |
| 347 | 454 | .fn_noreturn_no_args_type, |
| 455 | .fn_void_no_args_type, | |
| 348 | 456 | .fn_naked_noreturn_no_args_type, |
| 349 | 457 | .fn_ccc_void_no_args_type, |
| 350 | 458 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -405,6 +513,7 @@ pub const Value = extern union { |
| 405 | 513 | .null_type, |
| 406 | 514 | .undefined_type, |
| 407 | 515 | .fn_noreturn_no_args_type, |
| 516 | .fn_void_no_args_type, | |
| 408 | 517 | .fn_naked_noreturn_no_args_type, |
| 409 | 518 | .fn_ccc_void_no_args_type, |
| 410 | 519 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -470,6 +579,7 @@ pub const Value = extern union { |
| 470 | 579 | .null_type, |
| 471 | 580 | .undefined_type, |
| 472 | 581 | .fn_noreturn_no_args_type, |
| 582 | .fn_void_no_args_type, | |
| 473 | 583 | .fn_naked_noreturn_no_args_type, |
| 474 | 584 | .fn_ccc_void_no_args_type, |
| 475 | 585 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -564,6 +674,7 @@ pub const Value = extern union { |
| 564 | 674 | .null_type, |
| 565 | 675 | .undefined_type, |
| 566 | 676 | .fn_noreturn_no_args_type, |
| 677 | .fn_void_no_args_type, | |
| 567 | 678 | .fn_naked_noreturn_no_args_type, |
| 568 | 679 | .fn_ccc_void_no_args_type, |
| 569 | 680 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -620,6 +731,7 @@ pub const Value = extern union { |
| 620 | 731 | .null_type, |
| 621 | 732 | .undefined_type, |
| 622 | 733 | .fn_noreturn_no_args_type, |
| 734 | .fn_void_no_args_type, | |
| 623 | 735 | .fn_naked_noreturn_no_args_type, |
| 624 | 736 | .fn_ccc_void_no_args_type, |
| 625 | 737 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -721,6 +833,7 @@ pub const Value = extern union { |
| 721 | 833 | .null_type, |
| 722 | 834 | .undefined_type, |
| 723 | 835 | .fn_noreturn_no_args_type, |
| 836 | .fn_void_no_args_type, | |
| 724 | 837 | .fn_naked_noreturn_no_args_type, |
| 725 | 838 | .fn_ccc_void_no_args_type, |
| 726 | 839 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -783,6 +896,7 @@ pub const Value = extern union { |
| 783 | 896 | .null_type, |
| 784 | 897 | .undefined_type, |
| 785 | 898 | .fn_noreturn_no_args_type, |
| 899 | .fn_void_no_args_type, | |
| 786 | 900 | .fn_naked_noreturn_no_args_type, |
| 787 | 901 | .fn_ccc_void_no_args_type, |
| 788 | 902 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -862,6 +976,7 @@ pub const Value = extern union { |
| 862 | 976 | .null_type, |
| 863 | 977 | .undefined_type, |
| 864 | 978 | .fn_noreturn_no_args_type, |
| 979 | .fn_void_no_args_type, | |
| 865 | 980 | .fn_naked_noreturn_no_args_type, |
| 866 | 981 | .fn_ccc_void_no_args_type, |
| 867 | 982 | .single_const_pointer_to_comptime_int_type, |
| ... | ... | @@ -929,11 +1044,6 @@ pub const Value = extern union { |
| 929 | 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 | 1047 | /// Represents a pointer to another immutable value. |
| 938 | 1048 | pub const RefVal = struct { |
| 939 | 1049 | base: Payload = Payload{ .tag = .ref_val }, |
src-self-hosted/zir.zig+287-181| ... | ... | @@ -12,27 +12,43 @@ const TypedValue = @import("TypedValue.zig"); |
| 12 | 12 | const ir = @import("ir.zig"); |
| 13 | 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 | 26 | /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for |
| 16 | 27 | /// in-memory, analyzed instructions with types and values. |
| 17 | 28 | pub const Inst = struct { |
| 18 | 29 | tag: Tag, |
| 19 | 30 | /// Byte offset into the source. |
| 20 | 31 | src: usize, |
| 21 | name: []const u8, | |
| 22 | ||
| 23 | /// Slice into the source of the part after the = and before the next instruction. | |
| 24 | contents: []const u8 = &[0]u8{}, | |
| 32 | /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions. | |
| 33 | analyzed_inst: ?*ir.Inst = null, | |
| 25 | 34 | |
| 26 | 35 | /// These names are used directly as the instruction names in the text format. |
| 27 | 36 | pub const Tag = enum { |
| 28 | 37 | breakpoint, |
| 29 | 38 | call, |
| 30 | 39 | compileerror, |
| 40 | /// Special case, has no textual representation. | |
| 41 | @"const", | |
| 31 | 42 | /// Represents a pointer to a global decl by name. |
| 32 | 43 | declref, |
| 44 | /// Represents a pointer to a global decl by string name. | |
| 45 | declref_str, | |
| 33 | 46 | /// The syntax `@foo` is equivalent to `declval("foo")`. |
| 34 | 47 | /// declval is equivalent to declref followed by deref. |
| 35 | 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 | 52 | str, |
| 37 | 53 | int, |
| 38 | 54 | ptrtoint, |
| ... | ... | @@ -42,11 +58,11 @@ pub const Inst = struct { |
| 42 | 58 | @"asm", |
| 43 | 59 | @"unreachable", |
| 44 | 60 | @"return", |
| 61 | returnvoid, | |
| 45 | 62 | @"fn", |
| 63 | fntype, | |
| 46 | 64 | @"export", |
| 47 | 65 | primitive, |
| 48 | ref, | |
| 49 | fntype, | |
| 50 | 66 | intcast, |
| 51 | 67 | bitcast, |
| 52 | 68 | elemptr, |
| ... | ... | @@ -62,8 +78,11 @@ pub const Inst = struct { |
| 62 | 78 | .breakpoint => Breakpoint, |
| 63 | 79 | .call => Call, |
| 64 | 80 | .declref => DeclRef, |
| 81 | .declref_str => DeclRefStr, | |
| 65 | 82 | .declval => DeclVal, |
| 83 | .declval_in_module => DeclValInModule, | |
| 66 | 84 | .compileerror => CompileError, |
| 85 | .@"const" => Const, | |
| 67 | 86 | .str => Str, |
| 68 | 87 | .int => Int, |
| 69 | 88 | .ptrtoint => PtrToInt, |
| ... | ... | @@ -73,10 +92,10 @@ pub const Inst = struct { |
| 73 | 92 | .@"asm" => Asm, |
| 74 | 93 | .@"unreachable" => Unreachable, |
| 75 | 94 | .@"return" => Return, |
| 95 | .returnvoid => ReturnVoid, | |
| 76 | 96 | .@"fn" => Fn, |
| 77 | 97 | .@"export" => Export, |
| 78 | 98 | .primitive => Primitive, |
| 79 | .ref => Ref, | |
| 80 | 99 | .fntype => FnType, |
| 81 | 100 | .intcast => IntCast, |
| 82 | 101 | .bitcast => BitCast, |
| ... | ... | @@ -121,6 +140,16 @@ pub const Inst = struct { |
| 121 | 140 | pub const base_tag = Tag.declref; |
| 122 | 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 | 153 | positionals: struct { |
| 125 | 154 | name: *Inst, |
| 126 | 155 | }, |
| ... | ... | @@ -137,6 +166,16 @@ pub const Inst = struct { |
| 137 | 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 | 179 | pub const CompileError = struct { |
| 141 | 180 | pub const base_tag = Tag.compileerror; |
| 142 | 181 | base: Inst, |
| ... | ... | @@ -147,6 +186,16 @@ pub const Inst = struct { |
| 147 | 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 | 199 | pub const Str = struct { |
| 151 | 200 | pub const base_tag = Tag.str; |
| 152 | 201 | base: Inst, |
| ... | ... | @@ -168,6 +217,7 @@ pub const Inst = struct { |
| 168 | 217 | }; |
| 169 | 218 | |
| 170 | 219 | pub const PtrToInt = struct { |
| 220 | pub const builtin_name = "@ptrToInt"; | |
| 171 | 221 | pub const base_tag = Tag.ptrtoint; |
| 172 | 222 | base: Inst, |
| 173 | 223 | |
| ... | ... | @@ -238,6 +288,16 @@ pub const Inst = struct { |
| 238 | 288 | pub const base_tag = Tag.@"return"; |
| 239 | 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 | 301 | positionals: struct {}, |
| 242 | 302 | kw_args: struct {}, |
| 243 | 303 | }; |
| ... | ... | @@ -253,23 +313,26 @@ pub const Inst = struct { |
| 253 | 313 | kw_args: struct {}, |
| 254 | 314 | }; |
| 255 | 315 | |
| 256 | pub const Export = struct { | |
| 257 | pub const base_tag = Tag.@"export"; | |
| 316 | pub const FnType = struct { | |
| 317 | pub const base_tag = Tag.fntype; | |
| 258 | 318 | base: Inst, |
| 259 | 319 | |
| 260 | 320 | positionals: struct { |
| 261 | symbol_name: *Inst, | |
| 262 | value: *Inst, | |
| 321 | param_types: []*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 { | |
| 268 | pub const base_tag = Tag.ref; | |
| 329 | pub const Export = struct { | |
| 330 | pub const base_tag = Tag.@"export"; | |
| 269 | 331 | base: Inst, |
| 270 | 332 | |
| 271 | 333 | positionals: struct { |
| 272 | operand: *Inst, | |
| 334 | symbol_name: *Inst, | |
| 335 | decl_name: []const u8, | |
| 273 | 336 | }, |
| 274 | 337 | kw_args: struct {}, |
| 275 | 338 | }; |
| ... | ... | @@ -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 | 414 | pub const IntCast = struct { |
| 365 | 415 | pub const base_tag = Tag.intcast; |
| 366 | 416 | base: Inst, |
| ... | ... | @@ -456,7 +506,7 @@ pub const ErrorMsg = struct { |
| 456 | 506 | }; |
| 457 | 507 | |
| 458 | 508 | pub const Module = struct { |
| 459 | decls: []*Inst, | |
| 509 | decls: []*Decl, | |
| 460 | 510 | arena: std.heap.ArenaAllocator, |
| 461 | 511 | error_msg: ?ErrorMsg = null, |
| 462 | 512 | |
| ... | ... | @@ -475,13 +525,33 @@ pub const Module = struct { |
| 475 | 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 | 535 | /// TODO Look into making a table to speed this up. |
| 481 | pub fn findDecl(self: Module, name: []const u8) ?*Inst { | |
| 482 | for (self.decls) |decl| { | |
| 536 | pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex { | |
| 537 | for (self.decls) |decl, i| { | |
| 483 | 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 | 557 | return null; |
| ... | ... | @@ -497,18 +567,18 @@ pub const Module = struct { |
| 497 | 567 | try inst_table.ensureCapacity(self.decls.len); |
| 498 | 568 | |
| 499 | 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 | 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 | 579 | for (self.decls) |decl, i| { |
| 510 | 580 | try stream.print("@{} ", .{decl.name}); |
| 511 | try self.writeInstToStream(stream, decl, &inst_table); | |
| 581 | try self.writeInstToStream(stream, decl.inst, &inst_table); | |
| 512 | 582 | try stream.writeByte('\n'); |
| 513 | 583 | } |
| 514 | 584 | } |
| ... | ... | @@ -516,38 +586,41 @@ pub const Module = struct { |
| 516 | 586 | fn writeInstToStream( |
| 517 | 587 | self: Module, |
| 518 | 588 | stream: var, |
| 519 | decl: *Inst, | |
| 589 | inst: *Inst, | |
| 520 | 590 | inst_table: *const InstPtrTable, |
| 521 | 591 | ) @TypeOf(stream).Error!void { |
| 522 | 592 | // TODO I tried implementing this with an inline for loop and hit a compiler bug |
| 523 | switch (decl.tag) { | |
| 524 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), | |
| 525 | .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table), | |
| 526 | .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table), | |
| 527 | .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table), | |
| 528 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table), | |
| 529 | .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), | |
| 530 | .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), | |
| 531 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), | |
| 532 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table), | |
| 533 | .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table), | |
| 534 | .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table), | |
| 535 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table), | |
| 536 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table), | |
| 537 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table), | |
| 538 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table), | |
| 539 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table), | |
| 540 | .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table), | |
| 541 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table), | |
| 542 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table), | |
| 543 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table), | |
| 544 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table), | |
| 545 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table), | |
| 546 | .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table), | |
| 547 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table), | |
| 548 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table), | |
| 549 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table), | |
| 550 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table), | |
| 593 | switch (inst.tag) { | |
| 594 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, inst, inst_table), | |
| 595 | .call => return self.writeInstToStreamGeneric(stream, .call, inst, inst_table), | |
| 596 | .declref => return self.writeInstToStreamGeneric(stream, .declref, inst, inst_table), | |
| 597 | .declref_str => return self.writeInstToStreamGeneric(stream, .declref_str, inst, inst_table), | |
| 598 | .declval => return self.writeInstToStreamGeneric(stream, .declval, inst, inst_table), | |
| 599 | .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst, inst_table), | |
| 600 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst, inst_table), | |
| 601 | .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst, inst_table), | |
| 602 | .str => return self.writeInstToStreamGeneric(stream, .str, inst, inst_table), | |
| 603 | .int => return self.writeInstToStreamGeneric(stream, .int, inst, inst_table), | |
| 604 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, inst, inst_table), | |
| 605 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, inst, inst_table), | |
| 606 | .deref => return self.writeInstToStreamGeneric(stream, .deref, inst, inst_table), | |
| 607 | .as => return self.writeInstToStreamGeneric(stream, .as, inst, inst_table), | |
| 608 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", inst, inst_table), | |
| 609 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", inst, inst_table), | |
| 610 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", inst, inst_table), | |
| 611 | .returnvoid => return self.writeInstToStreamGeneric(stream, .returnvoid, inst, inst_table), | |
| 612 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", inst, inst_table), | |
| 613 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", inst, inst_table), | |
| 614 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, inst, inst_table), | |
| 615 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, inst, inst_table), | |
| 616 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, inst, inst_table), | |
| 617 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst, inst_table), | |
| 618 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst, inst_table), | |
| 619 | .add => return self.writeInstToStreamGeneric(stream, .add, inst, 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 | 692 | bool => return stream.writeByte("01"[@boolToInt(param)]), |
| 620 | 693 | []u8, []const u8 => return std.zig.renderStringLiteral(param, stream), |
| 621 | 694 | BigIntConst => return stream.print("{}", .{param}), |
| 695 | TypedValue => unreachable, // this is a special case | |
| 696 | *IrModule.Decl => unreachable, // this is a special case | |
| 622 | 697 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| 623 | 698 | } |
| 624 | 699 | } |
| ... | ... | @@ -628,13 +703,16 @@ pub const Module = struct { |
| 628 | 703 | if (info.index) |i| { |
| 629 | 704 | try stream.print("%{}", .{info.index}); |
| 630 | 705 | } else { |
| 631 | try stream.print("@{}", .{info.inst.name}); | |
| 706 | try stream.print("@{}", .{info.name}); | |
| 632 | 707 | } |
| 633 | 708 | } else if (inst.cast(Inst.DeclVal)) |decl_val| { |
| 634 | 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 | 712 | } else { |
| 636 | //try stream.print("?", .{}); | |
| 637 | unreachable; | |
| 713 | // This should be unreachable in theory, but since ZIR is used for debugging the compiler | |
| 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 | 751 | arena: std.heap.ArenaAllocator, |
| 674 | 752 | i: usize, |
| 675 | 753 | source: [:0]const u8, |
| 676 | decls: std.ArrayListUnmanaged(*Inst), | |
| 754 | decls: std.ArrayListUnmanaged(*Decl), | |
| 677 | 755 | global_name_map: *std.StringHashMap(usize), |
| 678 | 756 | error_msg: ?ErrorMsg = null, |
| 679 | 757 | unnamed_index: usize, |
| ... | ... | @@ -702,12 +780,12 @@ const Parser = struct { |
| 702 | 780 | skipSpace(self); |
| 703 | 781 | try requireEatBytes(self, "="); |
| 704 | 782 | skipSpace(self); |
| 705 | const inst = try parseInstruction(self, &body_context, ident); | |
| 783 | const decl = try parseInstruction(self, &body_context, ident); | |
| 706 | 784 | const ident_index = body_context.instructions.items.len; |
| 707 | 785 | if (try body_context.name_map.put(ident, ident_index)) |_| { |
| 708 | 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 | 789 | continue; |
| 712 | 790 | }, |
| 713 | 791 | ' ', '\n' => continue, |
| ... | ... | @@ -857,7 +935,7 @@ const Parser = struct { |
| 857 | 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 | 939 | const contents_start = self.i; |
| 862 | 940 | const fn_name = try skipToAndOver(self, '('); |
| 863 | 941 | inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { |
| ... | ... | @@ -876,10 +954,9 @@ const Parser = struct { |
| 876 | 954 | body_ctx: ?*Body, |
| 877 | 955 | inst_name: []const u8, |
| 878 | 956 | contents_start: usize, |
| 879 | ) InnerError!*Inst { | |
| 957 | ) InnerError!*Decl { | |
| 880 | 958 | const inst_specific = try self.arena.allocator.create(InstType); |
| 881 | 959 | inst_specific.base = .{ |
| 882 | .name = inst_name, | |
| 883 | 960 | .src = self.i, |
| 884 | 961 | .tag = InstType.base_tag, |
| 885 | 962 | }; |
| ... | ... | @@ -929,10 +1006,15 @@ const Parser = struct { |
| 929 | 1006 | } |
| 930 | 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 | 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 | 1020 | fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T { |
| ... | ... | @@ -978,6 +1060,8 @@ const Parser = struct { |
| 978 | 1060 | *Inst => return parseParameterInst(self, body_ctx), |
| 979 | 1061 | []u8, []const u8 => return self.parseStringLiteral(), |
| 980 | 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 | 1065 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 982 | 1066 | } |
| 983 | 1067 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); |
| ... | ... | @@ -1014,7 +1098,6 @@ const Parser = struct { |
| 1014 | 1098 | const declval = try self.arena.allocator.create(Inst.DeclVal); |
| 1015 | 1099 | declval.* = .{ |
| 1016 | 1100 | .base = .{ |
| 1017 | .name = try self.generateName(), | |
| 1018 | 1101 | .src = src, |
| 1019 | 1102 | .tag = Inst.DeclVal.base_tag, |
| 1020 | 1103 | }, |
| ... | ... | @@ -1027,7 +1110,7 @@ const Parser = struct { |
| 1027 | 1110 | if (local_ref) { |
| 1028 | 1111 | return body_ctx.?.instructions.items[kv.value]; |
| 1029 | 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 | 1129 | .old_module = &old_module, |
| 1047 | 1130 | .next_auto_name = 0, |
| 1048 | 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 | 1134 | defer ctx.decls.deinit(allocator); |
| 1052 | 1135 | defer ctx.names.deinit(); |
| ... | ... | @@ -1065,10 +1148,10 @@ const EmitZIR = struct { |
| 1065 | 1148 | allocator: *Allocator, |
| 1066 | 1149 | arena: std.heap.ArenaAllocator, |
| 1067 | 1150 | old_module: *const IrModule, |
| 1068 | decls: std.ArrayListUnmanaged(*Inst), | |
| 1151 | decls: std.ArrayListUnmanaged(*Decl), | |
| 1069 | 1152 | names: std.StringHashMap(void), |
| 1070 | 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 | 1156 | fn emit(self: *EmitZIR) !void { |
| 1074 | 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 | 1170 | } |
| 1088 | 1171 | std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { |
| 1089 | 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 | 1175 | }).lessThan); |
| 1093 | 1176 | |
| 1094 | 1177 | // Emit all the decls. |
| 1095 | 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 | 1212 | if (self.old_module.export_owners.getValue(ir_decl)) |exports| { |
| 1097 | 1213 | for (exports) |module_export| { |
| 1098 | const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name)); | |
| 1099 | 1214 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); |
| 1100 | 1215 | const export_inst = try self.arena.allocator.create(Inst.Export); |
| 1101 | 1216 | export_inst.* = .{ |
| 1102 | 1217 | .base = .{ |
| 1103 | .name = try self.autoName(), | |
| 1104 | 1218 | .src = module_export.src, |
| 1105 | 1219 | .tag = Inst.Export.base_tag, |
| 1106 | 1220 | }, |
| 1107 | 1221 | .positionals = .{ |
| 1108 | .symbol_name = symbol_name, | |
| 1109 | .value = declval, | |
| 1222 | .symbol_name = symbol_name.inst, | |
| 1223 | .decl_name = mem.spanZ(module_export.exported_decl.name), | |
| 1110 | 1224 | }, |
| 1111 | 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 | 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 | 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 | 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 | 1244 | const owner_decl = func_pl.func.owner_decl; |
| 1126 | 1245 | break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); |
| 1127 | 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 | 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); | |
| 1133 | return new_decl; | |
| 1253 | try new_body.inst_table.putNoClobber(inst, new_inst); | |
| 1254 | return new_inst; | |
| 1134 | 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 | 1261 | const declval = try self.arena.allocator.create(Inst.DeclVal); |
| 1141 | 1262 | declval.* = .{ |
| 1142 | 1263 | .base = .{ |
| 1143 | .name = try self.autoName(), | |
| 1144 | 1264 | .src = src, |
| 1145 | 1265 | .tag = Inst.DeclVal.base_tag, |
| 1146 | 1266 | }, |
| ... | ... | @@ -1150,12 +1270,11 @@ const EmitZIR = struct { |
| 1150 | 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 | 1274 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); |
| 1155 | 1275 | const int_inst = try self.arena.allocator.create(Inst.Int); |
| 1156 | 1276 | int_inst.* = .{ |
| 1157 | 1277 | .base = .{ |
| 1158 | .name = try self.autoName(), | |
| 1159 | 1278 | .src = src, |
| 1160 | 1279 | .tag = Inst.Int.base_tag, |
| 1161 | 1280 | }, |
| ... | ... | @@ -1164,34 +1283,29 @@ const EmitZIR = struct { |
| 1164 | 1283 | }, |
| 1165 | 1284 | .kw_args = .{}, |
| 1166 | 1285 | }; |
| 1167 | try self.decls.append(self.allocator, &int_inst.base); | |
| 1168 | return &int_inst.base; | |
| 1286 | return self.emitUnnamedDecl(&int_inst.base); | |
| 1169 | 1287 | } |
| 1170 | 1288 | |
| 1171 | fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst { | |
| 1172 | const declval = try self.emitDeclVal(src, mem.spanZ(decl.name)); | |
| 1173 | const ref_inst = try self.arena.allocator.create(Inst.Ref); | |
| 1174 | ref_inst.* = .{ | |
| 1289 | fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst { | |
| 1290 | const declref_inst = try self.arena.allocator.create(Inst.DeclRef); | |
| 1291 | declref_inst.* = .{ | |
| 1175 | 1292 | .base = .{ |
| 1176 | .name = try self.autoName(), | |
| 1177 | 1293 | .src = src, |
| 1178 | .tag = Inst.Ref.base_tag, | |
| 1294 | .tag = Inst.DeclRef.base_tag, | |
| 1179 | 1295 | }, |
| 1180 | 1296 | .positionals = .{ |
| 1181 | .operand = declval, | |
| 1297 | .name = mem.spanZ(module_decl.name), | |
| 1182 | 1298 | }, |
| 1183 | 1299 | .kw_args = .{}, |
| 1184 | 1300 | }; |
| 1185 | try self.decls.append(self.allocator, &ref_inst.base); | |
| 1186 | ||
| 1187 | return &ref_inst.base; | |
| 1301 | return &declref_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 | 1305 | const allocator = &self.arena.allocator; |
| 1192 | 1306 | if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| { |
| 1193 | 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 | 1310 | switch (typed_value.ty.zigTypeTag()) { |
| 1197 | 1311 | .Pointer => { |
| ... | ... | @@ -1218,18 +1332,16 @@ const EmitZIR = struct { |
| 1218 | 1332 | const as_inst = try self.arena.allocator.create(Inst.As); |
| 1219 | 1333 | as_inst.* = .{ |
| 1220 | 1334 | .base = .{ |
| 1221 | .name = try self.autoName(), | |
| 1222 | 1335 | .src = src, |
| 1223 | 1336 | .tag = Inst.As.base_tag, |
| 1224 | 1337 | }, |
| 1225 | 1338 | .positionals = .{ |
| 1226 | .dest_type = try self.emitType(src, typed_value.ty), | |
| 1227 | .value = try self.emitComptimeIntVal(src, typed_value.val), | |
| 1339 | .dest_type = (try self.emitType(src, typed_value.ty)).inst, | |
| 1340 | .value = (try self.emitComptimeIntVal(src, typed_value.val)).inst, | |
| 1228 | 1341 | }, |
| 1229 | 1342 | .kw_args = .{}, |
| 1230 | 1343 | }; |
| 1231 | ||
| 1232 | return &as_inst.base; | |
| 1344 | return self.emitUnnamedDecl(&as_inst.base); | |
| 1233 | 1345 | }, |
| 1234 | 1346 | .Type => { |
| 1235 | 1347 | const ty = typed_value.val.toType(); |
| ... | ... | @@ -1255,7 +1367,6 @@ const EmitZIR = struct { |
| 1255 | 1367 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1256 | 1368 | fail_inst.* = .{ |
| 1257 | 1369 | .base = .{ |
| 1258 | .name = try self.autoName(), | |
| 1259 | 1370 | .src = src, |
| 1260 | 1371 | .tag = Inst.CompileError.base_tag, |
| 1261 | 1372 | }, |
| ... | ... | @@ -1270,7 +1381,6 @@ const EmitZIR = struct { |
| 1270 | 1381 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1271 | 1382 | fail_inst.* = .{ |
| 1272 | 1383 | .base = .{ |
| 1273 | .name = try self.autoName(), | |
| 1274 | 1384 | .src = src, |
| 1275 | 1385 | .tag = Inst.CompileError.base_tag, |
| 1276 | 1386 | }, |
| ... | ... | @@ -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 | 1398 | const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); |
| 1289 | 1399 | mem.copy(*Inst, arena_instrs, instructions.items); |
| ... | ... | @@ -1291,18 +1401,16 @@ const EmitZIR = struct { |
| 1291 | 1401 | const fn_inst = try self.arena.allocator.create(Inst.Fn); |
| 1292 | 1402 | fn_inst.* = .{ |
| 1293 | 1403 | .base = .{ |
| 1294 | .name = try self.autoName(), | |
| 1295 | 1404 | .src = src, |
| 1296 | 1405 | .tag = Inst.Fn.base_tag, |
| 1297 | 1406 | }, |
| 1298 | 1407 | .positionals = .{ |
| 1299 | .fn_type = fn_type, | |
| 1408 | .fn_type = fn_type.inst, | |
| 1300 | 1409 | .body = .{ .instructions = arena_instrs }, |
| 1301 | 1410 | }, |
| 1302 | 1411 | .kw_args = .{}, |
| 1303 | 1412 | }; |
| 1304 | try self.decls.append(self.allocator, &fn_inst.base); | |
| 1305 | return &fn_inst.base; | |
| 1413 | return self.emitUnnamedDecl(&fn_inst.base); | |
| 1306 | 1414 | }, |
| 1307 | 1415 | .Array => { |
| 1308 | 1416 | // TODO more checks to make sure this can be emitted as a string literal |
| ... | ... | @@ -1318,7 +1426,6 @@ const EmitZIR = struct { |
| 1318 | 1426 | const str_inst = try self.arena.allocator.create(Inst.Str); |
| 1319 | 1427 | str_inst.* = .{ |
| 1320 | 1428 | .base = .{ |
| 1321 | .name = try self.autoName(), | |
| 1322 | 1429 | .src = src, |
| 1323 | 1430 | .tag = Inst.Str.base_tag, |
| 1324 | 1431 | }, |
| ... | ... | @@ -1327,8 +1434,7 @@ const EmitZIR = struct { |
| 1327 | 1434 | }, |
| 1328 | 1435 | .kw_args = .{}, |
| 1329 | 1436 | }; |
| 1330 | try self.decls.append(self.allocator, &str_inst.base); | |
| 1331 | return &str_inst.base; | |
| 1437 | return self.emitUnnamedDecl(&str_inst.base); | |
| 1332 | 1438 | }, |
| 1333 | 1439 | .Void => return self.emitPrimitive(src, .void_value), |
| 1334 | 1440 | else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), |
| ... | ... | @@ -1339,7 +1445,6 @@ const EmitZIR = struct { |
| 1339 | 1445 | const new_inst = try self.arena.allocator.create(T); |
| 1340 | 1446 | new_inst.* = .{ |
| 1341 | 1447 | .base = .{ |
| 1342 | .name = try self.autoName(), | |
| 1343 | 1448 | .src = src, |
| 1344 | 1449 | .tag = T.base_tag, |
| 1345 | 1450 | }, |
| ... | ... | @@ -1355,6 +1460,10 @@ const EmitZIR = struct { |
| 1355 | 1460 | inst_table: *std.AutoHashMap(*ir.Inst, *Inst), |
| 1356 | 1461 | instructions: *std.ArrayList(*Inst), |
| 1357 | 1462 | ) Allocator.Error!void { |
| 1463 | const new_body = ZirBody{ | |
| 1464 | .inst_table = inst_table, | |
| 1465 | .instructions = instructions, | |
| 1466 | }; | |
| 1358 | 1467 | for (body.instructions) |inst| { |
| 1359 | 1468 | const new_inst = switch (inst.tag) { |
| 1360 | 1469 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), |
| ... | ... | @@ -1364,16 +1473,15 @@ const EmitZIR = struct { |
| 1364 | 1473 | |
| 1365 | 1474 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); |
| 1366 | 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 | 1478 | new_inst.* = .{ |
| 1370 | 1479 | .base = .{ |
| 1371 | .name = try self.autoName(), | |
| 1372 | 1480 | .src = inst.src, |
| 1373 | 1481 | .tag = Inst.Call.base_tag, |
| 1374 | 1482 | }, |
| 1375 | 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 | 1485 | .args = args, |
| 1378 | 1486 | }, |
| 1379 | 1487 | .kw_args = .{}, |
| ... | ... | @@ -1381,7 +1489,22 @@ const EmitZIR = struct { |
| 1381 | 1489 | break :blk &new_inst.base; |
| 1382 | 1490 | }, |
| 1383 | 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 | 1508 | .constant => unreachable, // excluded from function bodies |
| 1386 | 1509 | .assembly => blk: { |
| 1387 | 1510 | const old_inst = inst.cast(ir.Inst.Assembly).?; |
| ... | ... | @@ -1389,33 +1512,32 @@ const EmitZIR = struct { |
| 1389 | 1512 | |
| 1390 | 1513 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); |
| 1391 | 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 | 1518 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); |
| 1396 | 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 | 1523 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); |
| 1401 | 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 | 1528 | new_inst.* = .{ |
| 1406 | 1529 | .base = .{ |
| 1407 | .name = try self.autoName(), | |
| 1408 | 1530 | .src = inst.src, |
| 1409 | 1531 | .tag = Inst.Asm.base_tag, |
| 1410 | 1532 | }, |
| 1411 | 1533 | .positionals = .{ |
| 1412 | .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source), | |
| 1413 | .return_type = try self.emitType(inst.src, inst.ty), | |
| 1534 | .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst, | |
| 1535 | .return_type = (try self.emitType(inst.src, inst.ty)).inst, | |
| 1414 | 1536 | }, |
| 1415 | 1537 | .kw_args = .{ |
| 1416 | 1538 | .@"volatile" = old_inst.args.is_volatile, |
| 1417 | 1539 | .output = if (old_inst.args.output) |o| |
| 1418 | try self.emitStringLiteral(inst.src, o) | |
| 1540 | (try self.emitStringLiteral(inst.src, o)).inst | |
| 1419 | 1541 | else |
| 1420 | 1542 | null, |
| 1421 | 1543 | .inputs = inputs, |
| ... | ... | @@ -1430,12 +1552,11 @@ const EmitZIR = struct { |
| 1430 | 1552 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); |
| 1431 | 1553 | new_inst.* = .{ |
| 1432 | 1554 | .base = .{ |
| 1433 | .name = try self.autoName(), | |
| 1434 | 1555 | .src = inst.src, |
| 1435 | 1556 | .tag = Inst.PtrToInt.base_tag, |
| 1436 | 1557 | }, |
| 1437 | 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 | 1561 | .kw_args = .{}, |
| 1441 | 1562 | }; |
| ... | ... | @@ -1446,13 +1567,12 @@ const EmitZIR = struct { |
| 1446 | 1567 | const new_inst = try self.arena.allocator.create(Inst.BitCast); |
| 1447 | 1568 | new_inst.* = .{ |
| 1448 | 1569 | .base = .{ |
| 1449 | .name = try self.autoName(), | |
| 1450 | 1570 | .src = inst.src, |
| 1451 | 1571 | .tag = Inst.BitCast.base_tag, |
| 1452 | 1572 | }, |
| 1453 | 1573 | .positionals = .{ |
| 1454 | .dest_type = try self.emitType(inst.src, inst.ty), | |
| 1455 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | |
| 1574 | .dest_type = (try self.emitType(inst.src, inst.ty)).inst, | |
| 1575 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1456 | 1576 | }, |
| 1457 | 1577 | .kw_args = .{}, |
| 1458 | 1578 | }; |
| ... | ... | @@ -1463,13 +1583,12 @@ const EmitZIR = struct { |
| 1463 | 1583 | const new_inst = try self.arena.allocator.create(Inst.Cmp); |
| 1464 | 1584 | new_inst.* = .{ |
| 1465 | 1585 | .base = .{ |
| 1466 | .name = try self.autoName(), | |
| 1467 | 1586 | .src = inst.src, |
| 1468 | 1587 | .tag = Inst.Cmp.base_tag, |
| 1469 | 1588 | }, |
| 1470 | 1589 | .positionals = .{ |
| 1471 | .lhs = try self.resolveInst(inst_table, old_inst.args.lhs), | |
| 1472 | .rhs = try self.resolveInst(inst_table, old_inst.args.rhs), | |
| 1590 | .lhs = try self.resolveInst(new_body, old_inst.args.lhs), | |
| 1591 | .rhs = try self.resolveInst(new_body, old_inst.args.rhs), | |
| 1473 | 1592 | .op = old_inst.args.op, |
| 1474 | 1593 | }, |
| 1475 | 1594 | .kw_args = .{}, |
| ... | ... | @@ -1491,12 +1610,11 @@ const EmitZIR = struct { |
| 1491 | 1610 | const new_inst = try self.arena.allocator.create(Inst.CondBr); |
| 1492 | 1611 | new_inst.* = .{ |
| 1493 | 1612 | .base = .{ |
| 1494 | .name = try self.autoName(), | |
| 1495 | 1613 | .src = inst.src, |
| 1496 | 1614 | .tag = Inst.CondBr.base_tag, |
| 1497 | 1615 | }, |
| 1498 | 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 | 1618 | .true_body = .{ .instructions = true_body.toOwnedSlice() }, |
| 1501 | 1619 | .false_body = .{ .instructions = false_body.toOwnedSlice() }, |
| 1502 | 1620 | }, |
| ... | ... | @@ -1509,12 +1627,11 @@ const EmitZIR = struct { |
| 1509 | 1627 | const new_inst = try self.arena.allocator.create(Inst.IsNull); |
| 1510 | 1628 | new_inst.* = .{ |
| 1511 | 1629 | .base = .{ |
| 1512 | .name = try self.autoName(), | |
| 1513 | 1630 | .src = inst.src, |
| 1514 | 1631 | .tag = Inst.IsNull.base_tag, |
| 1515 | 1632 | }, |
| 1516 | 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 | 1636 | .kw_args = .{}, |
| 1520 | 1637 | }; |
| ... | ... | @@ -1525,12 +1642,11 @@ const EmitZIR = struct { |
| 1525 | 1642 | const new_inst = try self.arena.allocator.create(Inst.IsNonNull); |
| 1526 | 1643 | new_inst.* = .{ |
| 1527 | 1644 | .base = .{ |
| 1528 | .name = try self.autoName(), | |
| 1529 | 1645 | .src = inst.src, |
| 1530 | 1646 | .tag = Inst.IsNonNull.base_tag, |
| 1531 | 1647 | }, |
| 1532 | 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 | 1651 | .kw_args = .{}, |
| 1536 | 1652 | }; |
| ... | ... | @@ -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 | 1662 | switch (ty.tag()) { |
| 1547 | 1663 | .isize => return self.emitPrimitive(src, .isize), |
| 1548 | 1664 | .usize => return self.emitPrimitive(src, .usize), |
| ... | ... | @@ -1575,26 +1691,24 @@ const EmitZIR = struct { |
| 1575 | 1691 | ty.fnParamTypes(param_types); |
| 1576 | 1692 | const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len); |
| 1577 | 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 | 1697 | const fntype_inst = try self.arena.allocator.create(Inst.FnType); |
| 1582 | 1698 | fntype_inst.* = .{ |
| 1583 | 1699 | .base = .{ |
| 1584 | .name = try self.autoName(), | |
| 1585 | 1700 | .src = src, |
| 1586 | 1701 | .tag = Inst.FnType.base_tag, |
| 1587 | 1702 | }, |
| 1588 | 1703 | .positionals = .{ |
| 1589 | 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 | 1707 | .kw_args = .{ |
| 1593 | 1708 | .cc = ty.fnCallingConvention(), |
| 1594 | 1709 | }, |
| 1595 | 1710 | }; |
| 1596 | try self.decls.append(self.allocator, &fntype_inst.base); | |
| 1597 | return &fntype_inst.base; | |
| 1711 | return self.emitUnnamedDecl(&fntype_inst.base); | |
| 1598 | 1712 | }, |
| 1599 | 1713 | else => std.debug.panic("TODO implement emitType for {}", .{ty}), |
| 1600 | 1714 | }, |
| ... | ... | @@ -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 | 1731 | const gop = try self.primitive_table.getOrPut(tag); |
| 1618 | 1732 | if (!gop.found_existing) { |
| 1619 | 1733 | const primitive_inst = try self.arena.allocator.create(Inst.Primitive); |
| 1620 | 1734 | primitive_inst.* = .{ |
| 1621 | 1735 | .base = .{ |
| 1622 | .name = try self.autoName(), | |
| 1623 | 1736 | .src = src, |
| 1624 | 1737 | .tag = Inst.Primitive.base_tag, |
| 1625 | 1738 | }, |
| ... | ... | @@ -1628,17 +1741,15 @@ const EmitZIR = struct { |
| 1628 | 1741 | }, |
| 1629 | 1742 | .kw_args = .{}, |
| 1630 | 1743 | }; |
| 1631 | try self.decls.append(self.allocator, &primitive_inst.base); | |
| 1632 | gop.kv.value = &primitive_inst.base; | |
| 1744 | gop.kv.value = try self.emitUnnamedDecl(&primitive_inst.base); | |
| 1633 | 1745 | } |
| 1634 | 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 | 1750 | const str_inst = try self.arena.allocator.create(Inst.Str); |
| 1639 | 1751 | str_inst.* = .{ |
| 1640 | 1752 | .base = .{ |
| 1641 | .name = try self.autoName(), | |
| 1642 | 1753 | .src = src, |
| 1643 | 1754 | .tag = Inst.Str.base_tag, |
| 1644 | 1755 | }, |
| ... | ... | @@ -1647,22 +1758,17 @@ const EmitZIR = struct { |
| 1647 | 1758 | }, |
| 1648 | 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); | |
| 1653 | ref_inst.* = .{ | |
| 1654 | .base = .{ | |
| 1655 | .name = try self.autoName(), | |
| 1656 | .src = src, | |
| 1657 | .tag = Inst.Ref.base_tag, | |
| 1658 | }, | |
| 1659 | .positionals = .{ | |
| 1660 | .operand = &str_inst.base, | |
| 1661 | }, | |
| 1662 | .kw_args = .{}, | |
| 1764 | fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl { | |
| 1765 | const decl = try self.arena.allocator.create(Decl); | |
| 1766 | decl.* = .{ | |
| 1767 | .name = try self.autoName(), | |
| 1768 | .contents_hash = undefined, | |
| 1769 | .inst = inst, | |
| 1663 | 1770 | }; |
| 1664 | try self.decls.append(self.allocator, &ref_inst.base); | |
| 1665 | ||
| 1666 | return &ref_inst.base; | |
| 1771 | try self.decls.append(self.allocator, decl); | |
| 1772 | return decl; | |
| 1667 | 1773 | } |
| 1668 | 1774 | }; |
src/codegen.cpp+18-2| ... | ... | @@ -5583,8 +5583,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir |
| 5583 | 5583 | |
| 5584 | 5584 | bool val_is_undef = value_is_all_undef(g, instruction->byte->value); |
| 5585 | 5585 | LLVMValueRef fill_char; |
| 5586 | if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.base.scope)) { | |
| 5587 | fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); | |
| 5586 | if (val_is_undef) { | |
| 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 | 5592 | } else { |
| 5589 | 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 | 7477 | continue; |
| 7474 | 7478 | } |
| 7475 | 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 | 7486 | ZigType *field_type = field_val->type; |
| 7477 | 7487 | assert(field_type != nullptr); |
| 7478 | 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 | 9475 | const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", |
| 9466 | 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 | 9481 | args.append("-isystem"); |
| 9469 | 9482 | args.append(libcxx_include_path); |
| 9470 | 9483 | |
| 9484 | args.append("-isystem"); | |
| 9485 | args.append(libcxxabi_include_path); | |
| 9486 | ||
| 9471 | 9487 | if (target_abi_is_musl(g->zig_target->abi)) { |
| 9472 | 9488 | args.append("-D_LIBCPP_HAS_MUSL_LIBC"); |
| 9473 | 9489 | } |
test/stage2/compare_output.zig+115-22| ... | ... | @@ -1,28 +1,121 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 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 | 10 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // TODO: re-enable these tests. | |
| 6 | // https://github.com/ziglang/zig/issues/1364 | |
| 11 | if (std.Target.current.os.tag != .linux or | |
| 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 | |
| 9 | //try ctx.testCompareOutputLibC( | |
| 10 | // \\extern fn puts([*]const u8) void; | |
| 11 | // \\pub export fn main() c_int { | |
| 12 | // \\ puts("Hello, world!"); | |
| 13 | // \\ return 0; | |
| 14 | // \\} | |
| 15 | //, "Hello, world!" ++ std.cstr.line_sep); | |
| 16 | ||
| 17 | //// function calling another function | |
| 18 | //try ctx.testCompareOutputLibC( | |
| 19 | // \\extern fn puts(s: [*]const u8) void; | |
| 20 | // \\pub export fn main() c_int { | |
| 21 | // \\ return foo("OK"); | |
| 22 | // \\} | |
| 23 | // \\fn foo(s: [*]const u8) c_int { | |
| 24 | // \\ puts(s); | |
| 25 | // \\ return 0; | |
| 26 | // \\} | |
| 27 | //, "OK" ++ std.cstr.line_sep); | |
| 19 | { | |
| 20 | var case = ctx.addExe("hello world with updates", linux_x64); | |
| 21 | // Regular old hello world | |
| 22 | case.addCompareOutput( | |
| 23 | \\export fn _start() noreturn { | |
| 24 | \\ print(); | |
| 25 | \\ | |
| 26 | \\ exit(); | |
| 27 | \\} | |
| 28 | \\ | |
| 29 | \\fn print() void { | |
| 30 | \\ asm volatile ("syscall" | |
| 31 | \\ : | |
| 32 | \\ : [number] "{rax}" (1), | |
| 33 | \\ [arg1] "{rdi}" (1), | |
| 34 | \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), | |
| 35 | \\ [arg3] "{rdx}" (14) | |
| 36 | \\ : "rcx", "r11", "memory" | |
| 37 | \\ ); | |
| 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 | 27 | \\ %0 = call(@notafunc, []) |
| 28 | 28 | \\}) |
| 29 | 29 | \\@0 = str("_start") |
| 30 | \\@1 = ref(@0) | |
| 31 | \\@2 = export(@1, @start) | |
| 32 | , &[_][]const u8{":5:13: error: use of undeclared identifier 'notafunc'"}); | |
| 30 | \\@1 = export(@0, "start") | |
| 31 | , &[_][]const u8{":5:13: error: decl 'notafunc' not found"}); | |
| 33 | 32 | |
| 34 | 33 | // TODO: this error should occur at the call site, not the fntype decl |
| 35 | 34 | ctx.addZIRError("call naked function", linux_x64, |
| ... | ... | @@ -41,8 +40,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 41 | 40 | \\ %0 = call(@s, []) |
| 42 | 41 | \\}) |
| 43 | 42 | \\@0 = str("_start") |
| 44 | \\@1 = ref(@0) | |
| 45 | \\@2 = export(@1, @start) | |
| 43 | \\@1 = export(@0, "start") | |
| 46 | 44 | , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"}); |
| 47 | 45 | |
| 48 | 46 | // TODO: re-enable these tests. |
test/stage2/zir.zig+152-333| ... | ... | @@ -14,23 +14,21 @@ pub fn addCases(ctx: *TestContext) void { |
| 14 | 14 | \\@fnty = fntype([], @void, cc=C) |
| 15 | 15 | \\ |
| 16 | 16 | \\@9 = str("entry") |
| 17 | \\@10 = ref(@9) | |
| 18 | \\@11 = export(@10, @entry) | |
| 17 | \\@11 = export(@9, "entry") | |
| 19 | 18 | \\ |
| 20 | 19 | \\@entry = fn(@fnty, { |
| 21 | \\ %11 = return() | |
| 20 | \\ %11 = returnvoid() | |
| 22 | 21 | \\}) |
| 23 | 22 | , |
| 24 | 23 | \\@void = primitive(void) |
| 25 | 24 | \\@fnty = fntype([], @void, cc=C) |
| 26 | \\@9 = str("entry") | |
| 27 | \\@10 = ref(@9) | |
| 28 | \\@unnamed$6 = str("entry") | |
| 29 | \\@unnamed$7 = ref(@unnamed$6) | |
| 30 | \\@unnamed$8 = export(@unnamed$7, @entry) | |
| 31 | \\@unnamed$10 = fntype([], @void, cc=C) | |
| 32 | \\@entry = fn(@unnamed$10, { | |
| 33 | \\ %0 = return() | |
| 25 | \\@9 = declref("9$0") | |
| 26 | \\@9$0 = str("entry") | |
| 27 | \\@unnamed$4 = str("entry") | |
| 28 | \\@unnamed$5 = export(@unnamed$4, "entry") | |
| 29 | \\@unnamed$6 = fntype([], @void, cc=C) | |
| 30 | \\@entry = fn(@unnamed$6, { | |
| 31 | \\ %0 = returnvoid() | |
| 34 | 32 | \\}) |
| 35 | 33 | \\ |
| 36 | 34 | ); |
| ... | ... | @@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void { |
| 45 | 43 | \\ |
| 46 | 44 | \\@entry = fn(@fnty, { |
| 47 | 45 | \\ %a = str("\x32\x08\x01\x0a") |
| 48 | \\ %aref = ref(%a) | |
| 49 | \\ %eptr0 = elemptr(%aref, @0) | |
| 50 | \\ %eptr1 = elemptr(%aref, @1) | |
| 51 | \\ %eptr2 = elemptr(%aref, @2) | |
| 52 | \\ %eptr3 = elemptr(%aref, @3) | |
| 46 | \\ %eptr0 = elemptr(%a, @0) | |
| 47 | \\ %eptr1 = elemptr(%a, @1) | |
| 48 | \\ %eptr2 = elemptr(%a, @2) | |
| 49 | \\ %eptr3 = elemptr(%a, @3) | |
| 53 | 50 | \\ %v0 = deref(%eptr0) |
| 54 | 51 | \\ %v1 = deref(%eptr1) |
| 55 | 52 | \\ %v2 = deref(%eptr2) |
| ... | ... | @@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void { |
| 61 | 58 | \\ %expected = int(69) |
| 62 | 59 | \\ %ok = cmp(%result, eq, %expected) |
| 63 | 60 | \\ %10 = condbr(%ok, { |
| 64 | \\ %11 = return() | |
| 61 | \\ %11 = returnvoid() | |
| 65 | 62 | \\ }, { |
| 66 | 63 | \\ %12 = breakpoint() |
| 67 | 64 | \\ }) |
| 68 | 65 | \\}) |
| 69 | 66 | \\ |
| 70 | 67 | \\@9 = str("entry") |
| 71 | \\@10 = ref(@9) | |
| 72 | \\@11 = export(@10, @entry) | |
| 68 | \\@11 = export(@9, "entry") | |
| 73 | 69 | , |
| 74 | 70 | \\@void = primitive(void) |
| 75 | 71 | \\@fnty = fntype([], @void, cc=C) |
| ... | ... | @@ -77,65 +73,62 @@ pub fn addCases(ctx: *TestContext) void { |
| 77 | 73 | \\@1 = int(1) |
| 78 | 74 | \\@2 = int(2) |
| 79 | 75 | \\@3 = int(3) |
| 80 | \\@unnamed$7 = fntype([], @void, cc=C) | |
| 81 | \\@entry = fn(@unnamed$7, { | |
| 82 | \\ %0 = return() | |
| 76 | \\@unnamed$6 = fntype([], @void, cc=C) | |
| 77 | \\@entry = fn(@unnamed$6, { | |
| 78 | \\ %0 = returnvoid() | |
| 83 | 79 | \\}) |
| 84 | \\@a = str("2\x08\x01\n") | |
| 85 | \\@9 = str("entry") | |
| 86 | \\@10 = ref(@9) | |
| 87 | \\@unnamed$14 = str("entry") | |
| 88 | \\@unnamed$15 = ref(@unnamed$14) | |
| 89 | \\@unnamed$16 = export(@unnamed$15, @entry) | |
| 80 | \\@entry$1 = str("2\x08\x01\n") | |
| 81 | \\@9 = declref("9$0") | |
| 82 | \\@9$0 = str("entry") | |
| 83 | \\@unnamed$11 = str("entry") | |
| 84 | \\@unnamed$12 = export(@unnamed$11, "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 | 90 | case.addTransform( |
| 96 | 91 | \\@void = primitive(void) |
| 97 | 92 | \\@fnty = fntype([], @void, cc=C) |
| 98 | 93 | \\ |
| 99 | 94 | \\@9 = str("entry") |
| 100 | \\@10 = ref(@9) | |
| 101 | \\@11 = export(@10, @entry) | |
| 95 | \\@11 = export(@9, "entry") | |
| 102 | 96 | \\ |
| 103 | 97 | \\@entry = fn(@fnty, { |
| 104 | 98 | \\ %0 = call(@a, []) |
| 105 | \\ %1 = return() | |
| 99 | \\ %1 = returnvoid() | |
| 106 | 100 | \\}) |
| 107 | 101 | \\ |
| 108 | 102 | \\@a = fn(@fnty, { |
| 109 | 103 | \\ %0 = call(@b, []) |
| 110 | \\ %1 = return() | |
| 104 | \\ %1 = returnvoid() | |
| 111 | 105 | \\}) |
| 112 | 106 | \\ |
| 113 | 107 | \\@b = fn(@fnty, { |
| 114 | 108 | \\ %0 = call(@a, []) |
| 115 | \\ %1 = return() | |
| 109 | \\ %1 = returnvoid() | |
| 116 | 110 | \\}) |
| 117 | 111 | , |
| 118 | 112 | \\@void = primitive(void) |
| 119 | 113 | \\@fnty = fntype([], @void, cc=C) |
| 120 | \\@9 = str("entry") | |
| 121 | \\@10 = ref(@9) | |
| 122 | \\@unnamed$6 = str("entry") | |
| 123 | \\@unnamed$7 = ref(@unnamed$6) | |
| 124 | \\@unnamed$8 = export(@unnamed$7, @entry) | |
| 125 | \\@unnamed$12 = fntype([], @void, cc=C) | |
| 126 | \\@entry = fn(@unnamed$12, { | |
| 114 | \\@9 = declref("9$0") | |
| 115 | \\@9$0 = str("entry") | |
| 116 | \\@unnamed$4 = str("entry") | |
| 117 | \\@unnamed$5 = export(@unnamed$4, "entry") | |
| 118 | \\@unnamed$6 = fntype([], @void, cc=C) | |
| 119 | \\@entry = fn(@unnamed$6, { | |
| 127 | 120 | \\ %0 = call(@a, [], modifier=auto) |
| 128 | \\ %1 = return() | |
| 121 | \\ %1 = returnvoid() | |
| 129 | 122 | \\}) |
| 130 | \\@unnamed$17 = fntype([], @void, cc=C) | |
| 131 | \\@a = fn(@unnamed$17, { | |
| 123 | \\@unnamed$8 = fntype([], @void, cc=C) | |
| 124 | \\@a = fn(@unnamed$8, { | |
| 132 | 125 | \\ %0 = call(@b, [], modifier=auto) |
| 133 | \\ %1 = return() | |
| 126 | \\ %1 = returnvoid() | |
| 134 | 127 | \\}) |
| 135 | \\@unnamed$22 = fntype([], @void, cc=C) | |
| 136 | \\@b = fn(@unnamed$22, { | |
| 128 | \\@unnamed$10 = fntype([], @void, cc=C) | |
| 129 | \\@b = fn(@unnamed$10, { | |
| 137 | 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 | 138 | \\@fnty = fntype([], @void, cc=C) |
| 146 | 139 | \\ |
| 147 | 140 | \\@9 = str("entry") |
| 148 | \\@10 = ref(@9) | |
| 149 | \\@11 = export(@10, @entry) | |
| 141 | \\@11 = export(@9, "entry") | |
| 150 | 142 | \\ |
| 151 | 143 | \\@entry = fn(@fnty, { |
| 152 | 144 | \\ %0 = call(@a, []) |
| 153 | \\ %1 = return() | |
| 145 | \\ %1 = returnvoid() | |
| 154 | 146 | \\}) |
| 155 | 147 | \\ |
| 156 | 148 | \\@a = fn(@fnty, { |
| 157 | 149 | \\ %0 = call(@b, []) |
| 158 | \\ %1 = return() | |
| 150 | \\ %1 = returnvoid() | |
| 159 | 151 | \\}) |
| 160 | 152 | \\ |
| 161 | 153 | \\@b = fn(@fnty, { |
| 162 | 154 | \\ %9 = compileerror("message") |
| 163 | 155 | \\ %0 = call(@a, []) |
| 164 | \\ %1 = return() | |
| 156 | \\ %1 = returnvoid() | |
| 165 | 157 | \\}) |
| 166 | 158 | , |
| 167 | 159 | &[_][]const u8{ |
| 168 | ":19:21: error: message", | |
| 160 | ":18:21: error: message", | |
| 169 | 161 | }, |
| 170 | 162 | ); |
| 171 | 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 | 168 | \\@fnty = fntype([], @void, cc=C) |
| 177 | 169 | \\ |
| 178 | 170 | \\@9 = str("entry") |
| 179 | \\@10 = ref(@9) | |
| 180 | \\@11 = export(@10, @entry) | |
| 171 | \\@11 = export(@9, "entry") | |
| 181 | 172 | \\ |
| 182 | 173 | \\@entry = fn(@fnty, { |
| 183 | \\ %1 = return() | |
| 174 | \\ %0 = returnvoid() | |
| 184 | 175 | \\}) |
| 185 | 176 | \\ |
| 186 | 177 | \\@a = fn(@fnty, { |
| 187 | 178 | \\ %0 = call(@b, []) |
| 188 | \\ %1 = return() | |
| 179 | \\ %1 = returnvoid() | |
| 189 | 180 | \\}) |
| 190 | 181 | \\ |
| 191 | 182 | \\@b = fn(@fnty, { |
| 192 | 183 | \\ %9 = compileerror("message") |
| 193 | 184 | \\ %0 = call(@a, []) |
| 194 | \\ %1 = return() | |
| 185 | \\ %1 = returnvoid() | |
| 195 | 186 | \\}) |
| 196 | 187 | , |
| 197 | 188 | \\@void = primitive(void) |
| 198 | 189 | \\@fnty = fntype([], @void, cc=C) |
| 199 | \\@9 = str("entry") | |
| 200 | \\@10 = ref(@9) | |
| 201 | \\@unnamed$6 = str("entry") | |
| 202 | \\@unnamed$7 = ref(@unnamed$6) | |
| 203 | \\@unnamed$8 = export(@unnamed$7, @entry) | |
| 204 | \\@unnamed$10 = fntype([], @void, cc=C) | |
| 205 | \\@entry = fn(@unnamed$10, { | |
| 206 | \\ %0 = return() | |
| 190 | \\@9 = declref("9$2") | |
| 191 | \\@9$2 = str("entry") | |
| 192 | \\@unnamed$4 = str("entry") | |
| 193 | \\@unnamed$5 = export(@unnamed$4, "entry") | |
| 194 | \\@unnamed$6 = fntype([], @void, cc=C) | |
| 195 | \\@entry = fn(@unnamed$6, { | |
| 196 | \\ %0 = returnvoid() | |
| 207 | 197 | \\}) |
| 208 | 198 | \\ |
| 209 | 199 | ); |
| ... | ... | @@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void { |
| 217 | 207 | return; |
| 218 | 208 | } |
| 219 | 209 | |
| 220 | ctx.addZIRCompareOutput( | |
| 221 | "hello world ZIR, update msg", | |
| 222 | &[_][]const u8{ | |
| 223 | \\@noreturn = primitive(noreturn) | |
| 224 | \\@void = primitive(void) | |
| 225 | \\@usize = primitive(usize) | |
| 226 | \\@0 = int(0) | |
| 227 | \\@1 = int(1) | |
| 228 | \\@2 = int(2) | |
| 229 | \\@3 = int(3) | |
| 230 | \\ | |
| 231 | \\@syscall_array = str("syscall") | |
| 232 | \\@sysoutreg_array = str("={rax}") | |
| 233 | \\@rax_array = str("{rax}") | |
| 234 | \\@rdi_array = str("{rdi}") | |
| 235 | \\@rcx_array = str("rcx") | |
| 236 | \\@r11_array = str("r11") | |
| 237 | \\@rdx_array = str("{rdx}") | |
| 238 | \\@rsi_array = str("{rsi}") | |
| 239 | \\@memory_array = str("memory") | |
| 240 | \\@len_array = str("len") | |
| 241 | \\ | |
| 242 | \\@msg = str("Hello, world!\n") | |
| 243 | \\ | |
| 244 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | |
| 245 | \\@start = fn(@start_fnty, { | |
| 246 | \\ %SYS_exit_group = int(231) | |
| 247 | \\ %exit_code = as(@usize, @0) | |
| 248 | \\ | |
| 249 | \\ %syscall = ref(@syscall_array) | |
| 250 | \\ %sysoutreg = ref(@sysoutreg_array) | |
| 251 | \\ %rax = ref(@rax_array) | |
| 252 | \\ %rdi = ref(@rdi_array) | |
| 253 | \\ %rcx = ref(@rcx_array) | |
| 254 | \\ %rdx = ref(@rdx_array) | |
| 255 | \\ %rsi = ref(@rsi_array) | |
| 256 | \\ %r11 = ref(@r11_array) | |
| 257 | \\ %memory = ref(@memory_array) | |
| 258 | \\ | |
| 259 | \\ %SYS_write = as(@usize, @1) | |
| 260 | \\ %STDOUT_FILENO = as(@usize, @1) | |
| 261 | \\ | |
| 262 | \\ %msg_ptr = ref(@msg) | |
| 263 | \\ %msg_addr = ptrtoint(%msg_ptr) | |
| 264 | \\ | |
| 265 | \\ %len_name = ref(@len_array) | |
| 266 | \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name) | |
| 267 | \\ %msg_len = deref(%msg_len_ptr) | |
| 268 | \\ %rc_write = asm(%syscall, @usize, | |
| 269 | \\ volatile=1, | |
| 270 | \\ output=%sysoutreg, | |
| 271 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | |
| 272 | \\ clobbers=[%rcx, %r11, %memory], | |
| 273 | \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) | |
| 274 | \\ | |
| 275 | \\ %rc_exit = asm(%syscall, @usize, | |
| 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 | }, | |
| 210 | ctx.addZIRCompareOutput("hello world ZIR", | |
| 211 | \\@noreturn = primitive(noreturn) | |
| 212 | \\@void = primitive(void) | |
| 213 | \\@usize = primitive(usize) | |
| 214 | \\@0 = int(0) | |
| 215 | \\@1 = int(1) | |
| 216 | \\@2 = int(2) | |
| 217 | \\@3 = int(3) | |
| 218 | \\ | |
| 219 | \\@msg = str("Hello, world!\n") | |
| 220 | \\ | |
| 221 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | |
| 222 | \\@start = fn(@start_fnty, { | |
| 223 | \\ %SYS_exit_group = int(231) | |
| 224 | \\ %exit_code = as(@usize, @0) | |
| 225 | \\ | |
| 226 | \\ %syscall = str("syscall") | |
| 227 | \\ %sysoutreg = str("={rax}") | |
| 228 | \\ %rax = str("{rax}") | |
| 229 | \\ %rdi = str("{rdi}") | |
| 230 | \\ %rcx = str("rcx") | |
| 231 | \\ %rdx = str("{rdx}") | |
| 232 | \\ %rsi = str("{rsi}") | |
| 233 | \\ %r11 = str("r11") | |
| 234 | \\ %memory = str("memory") | |
| 235 | \\ | |
| 236 | \\ %SYS_write = as(@usize, @1) | |
| 237 | \\ %STDOUT_FILENO = as(@usize, @1) | |
| 238 | \\ | |
| 239 | \\ %msg_addr = ptrtoint(@msg) | |
| 240 | \\ | |
| 241 | \\ %len_name = str("len") | |
| 242 | \\ %msg_len_ptr = fieldptr(@msg, %len_name) | |
| 243 | \\ %msg_len = deref(%msg_len_ptr) | |
| 244 | \\ %rc_write = asm(%syscall, @usize, | |
| 245 | \\ volatile=1, | |
| 246 | \\ output=%sysoutreg, | |
| 247 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | |
| 248 | \\ clobbers=[%rcx, %r11, %memory], | |
| 249 | \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) | |
| 250 | \\ | |
| 251 | \\ %rc_exit = asm(%syscall, @usize, | |
| 252 | \\ volatile=1, | |
| 253 | \\ output=%sysoutreg, | |
| 254 | \\ inputs=[%rax, %rdi], | |
| 255 | \\ clobbers=[%rcx, %r11, %memory], | |
| 256 | \\ args=[%SYS_exit_group, %exit_code]) | |
| 257 | \\ | |
| 258 | \\ %99 = unreachable() | |
| 259 | \\}); | |
| 260 | \\ | |
| 261 | \\@9 = str("_start") | |
| 262 | \\@11 = export(@9, "start") | |
| 263 | , | |
| 264 | \\Hello, world! | |
| 265 | \\ | |
| 434 | 266 | ); |
| 435 | 267 | |
| 436 | ctx.addZIRCompareOutput( | |
| 437 | "function call with no args no return value", | |
| 438 | &[_][]const u8{ | |
| 439 | \\@noreturn = primitive(noreturn) | |
| 440 | \\@void = primitive(void) | |
| 441 | \\@usize = primitive(usize) | |
| 442 | \\@0 = int(0) | |
| 443 | \\@1 = int(1) | |
| 444 | \\@2 = int(2) | |
| 445 | \\@3 = int(3) | |
| 446 | \\ | |
| 447 | \\@syscall_array = str("syscall") | |
| 448 | \\@sysoutreg_array = str("={rax}") | |
| 449 | \\@rax_array = str("{rax}") | |
| 450 | \\@rdi_array = str("{rdi}") | |
| 451 | \\@rcx_array = str("rcx") | |
| 452 | \\@r11_array = str("r11") | |
| 453 | \\@memory_array = str("memory") | |
| 454 | \\ | |
| 455 | \\@exit0_fnty = fntype([], @noreturn) | |
| 456 | \\@exit0 = fn(@exit0_fnty, { | |
| 457 | \\ %SYS_exit_group = int(231) | |
| 458 | \\ %exit_code = as(@usize, @0) | |
| 459 | \\ | |
| 460 | \\ %syscall = ref(@syscall_array) | |
| 461 | \\ %sysoutreg = ref(@sysoutreg_array) | |
| 462 | \\ %rax = ref(@rax_array) | |
| 463 | \\ %rdi = ref(@rdi_array) | |
| 464 | \\ %rcx = ref(@rcx_array) | |
| 465 | \\ %r11 = ref(@r11_array) | |
| 466 | \\ %memory = ref(@memory_array) | |
| 467 | \\ | |
| 468 | \\ %rc = asm(%syscall, @usize, | |
| 469 | \\ volatile=1, | |
| 470 | \\ output=%sysoutreg, | |
| 471 | \\ inputs=[%rax, %rdi], | |
| 472 | \\ clobbers=[%rcx, %r11, %memory], | |
| 473 | \\ args=[%SYS_exit_group, %exit_code]) | |
| 474 | \\ | |
| 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 | ); | |
| 268 | ctx.addZIRCompareOutput("function call with no args no return value", | |
| 269 | \\@noreturn = primitive(noreturn) | |
| 270 | \\@void = primitive(void) | |
| 271 | \\@usize = primitive(usize) | |
| 272 | \\@0 = int(0) | |
| 273 | \\@1 = int(1) | |
| 274 | \\@2 = int(2) | |
| 275 | \\@3 = int(3) | |
| 276 | \\ | |
| 277 | \\@exit0_fnty = fntype([], @noreturn) | |
| 278 | \\@exit0 = fn(@exit0_fnty, { | |
| 279 | \\ %SYS_exit_group = int(231) | |
| 280 | \\ %exit_code = as(@usize, @0) | |
| 281 | \\ | |
| 282 | \\ %syscall = str("syscall") | |
| 283 | \\ %sysoutreg = str("={rax}") | |
| 284 | \\ %rax = str("{rax}") | |
| 285 | \\ %rdi = str("{rdi}") | |
| 286 | \\ %rcx = str("rcx") | |
| 287 | \\ %r11 = str("r11") | |
| 288 | \\ %memory = str("memory") | |
| 289 | \\ | |
| 290 | \\ %rc = asm(%syscall, @usize, | |
| 291 | \\ volatile=1, | |
| 292 | \\ output=%sysoutreg, | |
| 293 | \\ inputs=[%rax, %rdi], | |
| 294 | \\ clobbers=[%rcx, %r11, %memory], | |
| 295 | \\ args=[%SYS_exit_group, %exit_code]) | |
| 296 | \\ | |
| 297 | \\ %99 = unreachable() | |
| 298 | \\}); | |
| 299 | \\ | |
| 300 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | |
| 301 | \\@start = fn(@start_fnty, { | |
| 302 | \\ %0 = call(@exit0, []) | |
| 303 | \\}) | |
| 304 | \\@9 = str("_start") | |
| 305 | \\@11 = export(@9, "start") | |
| 306 | , ""); | |
| 488 | 307 | } |