authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-26 22:29:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-26 22:41:19-07:00
log3af973160031fd573f46489bee519217e635839a
tree0e48a3002f16340e95186cdf53d595e60f5051b7
parentc1fd459f14d9e89c0b38b1afc531a7b67e3a2759

stage2: implement runtime pointer access to global constants

The main problem that motivated these changes is that global constants which are referenced by pointer would not be emitted into the binary. This happened because `semaDecl` did not add `codegen_decl` tasks for global constants, instead relying on the constant values being copied as necessary. However when the global constants are referenced by pointer, they need to be sent to the linker to be emitted. After making global const arrays, structs, and unions get emitted, this uncovered a latent issue: the anonymous decls that they referenced would get garbage collected (via `deleteUnusedDecl`) even though they would later be referenced by the global const. In order to solve this problem, I introduced `anon_work_queue` which is the same as `work_queue` except a lower priority. The `codegen_decl` task for anon decls goes into the `anon_work_queue` ensuring that the owner decl gets a chance to mark its anon decls as alive before they are possibly deleted. This caused a few regressions, which I made the judgement call to add workarounds for. Two steps forward, one step back, is still progress. The regressions were: * Two behavior tests having to do with unions. These tests were intentionally exercising the LLVM constant value lowering, however, due to the bug with garbage collection that was fixed in this commit, the LLVM code was not getting exercised, and union types/values were not implemented correctly, due to me forgetting that LLVM does not allow bitcasting aggregate values. - This is worked around by allowing those 2 test cases to regress, moving them to the "passing for stage1 only" section. * The test-stage2 test cases (in test/cases/*) for non-LLVM backends previously did not have any calls to lower struct values, but now they do. The code that was there was just `@panic("TODO")`. I replaced that code with a stub that generates the wrong value. This is an intentional miscompilation that will obviously need to get fixed before any struct behavior tests pass. None of the current tests we have exercise loading any values from these global const structs, so there is not a problem until we try to improve these backends.

9 files changed, 202 insertions(+), 137 deletions(-)

src/Compilation.zig+88-65
...@@ -46,6 +46,7 @@ stage1_cache_manifest: *Cache.Manifest = undefined,...@@ -46,6 +46,7 @@ stage1_cache_manifest: *Cache.Manifest = undefined,
46link_error_flags: link.File.ErrorFlags = .{},46link_error_flags: link.File.ErrorFlags = .{},
4747
48work_queue: std.fifo.LinearFifo(Job, .Dynamic),48work_queue: std.fifo.LinearFifo(Job, .Dynamic),
49anon_work_queue: std.fifo.LinearFifo(Job, .Dynamic),
4950
50/// These jobs are to invoke the Clang compiler to create an object file, which51/// These jobs are to invoke the Clang compiler to create an object file, which
51/// gets linked with the Compilation.52/// gets linked with the Compilation.
...@@ -1460,6 +1461,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1460,6 +1461,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1460 .emit_analysis = options.emit_analysis,1461 .emit_analysis = options.emit_analysis,
1461 .emit_docs = options.emit_docs,1462 .emit_docs = options.emit_docs,
1462 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),1463 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
1464 .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
1463 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),1465 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
1464 .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),1466 .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),
1465 .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa),1467 .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa),
...@@ -1646,6 +1648,7 @@ pub fn destroy(self: *Compilation) void {...@@ -1646,6 +1648,7 @@ pub fn destroy(self: *Compilation) void {
16461648
1647 const gpa = self.gpa;1649 const gpa = self.gpa;
1648 self.work_queue.deinit();1650 self.work_queue.deinit();
1651 self.anon_work_queue.deinit();
1649 self.c_object_work_queue.deinit();1652 self.c_object_work_queue.deinit();
1650 self.astgen_work_queue.deinit();1653 self.astgen_work_queue.deinit();
1651 self.embed_file_work_queue.deinit();1654 self.embed_file_work_queue.deinit();
...@@ -2072,7 +2075,6 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {...@@ -2072,7 +2075,6 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
2072}2075}
20732076
2074pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {2077pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
2075 const gpa = self.gpa;
2076 // If the terminal is dumb, we dont want to show the user all the2078 // If the terminal is dumb, we dont want to show the user all the
2077 // output.2079 // output.
2078 var progress: std.Progress = .{ .dont_print_on_dumb = true };2080 var progress: std.Progress = .{ .dont_print_on_dumb = true };
...@@ -2146,7 +2148,24 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2146,7 +2148,24 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2146 }2148 }
2147 }2149 }
21482150
2149 while (self.work_queue.readItem()) |work_item| switch (work_item) {2151 // In this main loop we give priority to non-anonymous Decls in the work queue, so
2152 // that they can establish references to anonymous Decls, setting alive=true in the
2153 // backend, preventing anonymous Decls from being prematurely destroyed.
2154 while (true) {
2155 if (self.work_queue.readItem()) |work_item| {
2156 try processOneJob(self, work_item, main_progress_node);
2157 continue;
2158 }
2159 if (self.anon_work_queue.readItem()) |work_item| {
2160 try processOneJob(self, work_item, main_progress_node);
2161 continue;
2162 }
2163 break;
2164 }
2165}
2166
2167fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress.Node) !void {
2168 switch (job) {
2150 .codegen_decl => |decl| switch (decl.analysis) {2169 .codegen_decl => |decl| switch (decl.analysis) {
2151 .unreferenced => unreachable,2170 .unreferenced => unreachable,
2152 .in_progress => unreachable,2171 .in_progress => unreachable,
...@@ -2157,24 +2176,25 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2157,24 +2176,25 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2157 .codegen_failure,2176 .codegen_failure,
2158 .dependency_failure,2177 .dependency_failure,
2159 .sema_failure_retryable,2178 .sema_failure_retryable,
2160 => continue,2179 => return,
21612180
2162 .complete, .codegen_failure_retryable => {2181 .complete, .codegen_failure_retryable => {
2163 if (build_options.omit_stage2)2182 if (build_options.omit_stage2)
2164 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2183 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
21652184
2166 const module = self.bin_file.options.module.?;2185 const module = comp.bin_file.options.module.?;
2167 assert(decl.has_tv);2186 assert(decl.has_tv);
2168 assert(decl.ty.hasCodeGenBits());2187 assert(decl.ty.hasCodeGenBits());
21692188
2170 if (decl.alive) {2189 if (decl.alive) {
2171 try module.linkerUpdateDecl(decl);2190 try module.linkerUpdateDecl(decl);
2172 continue;2191 return;
2173 }2192 }
21742193
2175 // Instead of sending this decl to the linker, we actually will delete it2194 // Instead of sending this decl to the linker, we actually will delete it
2176 // because we found out that it in fact was never referenced.2195 // because we found out that it in fact was never referenced.
2177 module.deleteUnusedDecl(decl);2196 module.deleteUnusedDecl(decl);
2197 return;
2178 },2198 },
2179 },2199 },
2180 .codegen_func => |func| switch (func.owner_decl.analysis) {2200 .codegen_func => |func| switch (func.owner_decl.analysis) {
...@@ -2187,20 +2207,21 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2187,20 +2207,21 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2187 .codegen_failure,2207 .codegen_failure,
2188 .dependency_failure,2208 .dependency_failure,
2189 .sema_failure_retryable,2209 .sema_failure_retryable,
2190 => continue,2210 => return,
21912211
2192 .complete, .codegen_failure_retryable => {2212 .complete, .codegen_failure_retryable => {
2193 if (build_options.omit_stage2)2213 if (build_options.omit_stage2)
2194 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2214 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2195 switch (func.state) {2215 switch (func.state) {
2196 .sema_failure, .dependency_failure => continue,2216 .sema_failure, .dependency_failure => return,
2197 .queued => {},2217 .queued => {},
2198 .in_progress => unreachable,2218 .in_progress => unreachable,
2199 .inline_only => unreachable, // don't queue work for this2219 .inline_only => unreachable, // don't queue work for this
2200 .success => unreachable, // don't queue it twice2220 .success => unreachable, // don't queue it twice
2201 }2221 }
22022222
2203 const module = self.bin_file.options.module.?;2223 const gpa = comp.gpa;
2224 const module = comp.bin_file.options.module.?;
2204 const decl = func.owner_decl;2225 const decl = func.owner_decl;
22052226
2206 var tmp_arena = std.heap.ArenaAllocator.init(gpa);2227 var tmp_arena = std.heap.ArenaAllocator.init(gpa);
...@@ -2210,7 +2231,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2210,7 +2231,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2210 var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {2231 var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {
2211 error.AnalysisFail => {2232 error.AnalysisFail => {
2212 assert(func.state != .in_progress);2233 assert(func.state != .in_progress);
2213 continue;2234 return;
2214 },2235 },
2215 error.OutOfMemory => return error.OutOfMemory,2236 error.OutOfMemory => return error.OutOfMemory,
2216 };2237 };
...@@ -2220,17 +2241,17 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2220,17 +2241,17 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2220 var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir);2241 var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir);
2221 defer liveness.deinit(gpa);2242 defer liveness.deinit(gpa);
22222243
2223 if (builtin.mode == .Debug and self.verbose_air) {2244 if (builtin.mode == .Debug and comp.verbose_air) {
2224 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});2245 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2225 @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness);2246 @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness);
2226 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});2247 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
2227 }2248 }
22282249
2229 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {2250 comp.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2230 error.OutOfMemory => return error.OutOfMemory,2251 error.OutOfMemory => return error.OutOfMemory,
2231 error.AnalysisFail => {2252 error.AnalysisFail => {
2232 decl.analysis = .codegen_failure;2253 decl.analysis = .codegen_failure;
2233 continue;2254 return;
2234 },2255 },
2235 else => {2256 else => {
2236 try module.failed_decls.ensureUnusedCapacity(gpa, 1);2257 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
...@@ -2241,10 +2262,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2241,10 +2262,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2241 .{@errorName(err)},2262 .{@errorName(err)},
2242 ));2263 ));
2243 decl.analysis = .codegen_failure_retryable;2264 decl.analysis = .codegen_failure_retryable;
2244 continue;2265 return;
2245 },2266 },
2246 };2267 };
2247 continue;2268 return;
2248 },2269 },
2249 },2270 },
2250 .emit_h_decl => |decl| switch (decl.analysis) {2271 .emit_h_decl => |decl| switch (decl.analysis) {
...@@ -2256,14 +2277,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2256,14 +2277,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2256 .sema_failure,2277 .sema_failure,
2257 .dependency_failure,2278 .dependency_failure,
2258 .sema_failure_retryable,2279 .sema_failure_retryable,
2259 => continue,2280 => return,
22602281
2261 // emit-h only requires semantic analysis of the Decl to be complete,2282 // emit-h only requires semantic analysis of the Decl to be complete,
2262 // it does not depend on machine code generation to succeed.2283 // it does not depend on machine code generation to succeed.
2263 .codegen_failure, .codegen_failure_retryable, .complete => {2284 .codegen_failure, .codegen_failure_retryable, .complete => {
2264 if (build_options.omit_stage2)2285 if (build_options.omit_stage2)
2265 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2286 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2266 const module = self.bin_file.options.module.?;2287 const gpa = comp.gpa;
2288 const module = comp.bin_file.options.module.?;
2267 const emit_h = module.emit_h.?;2289 const emit_h = module.emit_h.?;
2268 _ = try emit_h.decl_table.getOrPut(gpa, decl);2290 _ = try emit_h.decl_table.getOrPut(gpa, decl);
2269 const decl_emit_h = decl.getEmitH(module);2291 const decl_emit_h = decl.getEmitH(module);
...@@ -2287,7 +2309,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2287,7 +2309,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2287 c_codegen.genHeader(&dg) catch |err| switch (err) {2309 c_codegen.genHeader(&dg) catch |err| switch (err) {
2288 error.AnalysisFail => {2310 error.AnalysisFail => {
2289 try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?);2311 try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?);
2290 continue;2312 return;
2291 },2313 },
2292 else => |e| return e,2314 else => |e| return e,
2293 };2315 };
...@@ -2299,26 +2321,27 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2299,26 +2321,27 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2299 .analyze_decl => |decl| {2321 .analyze_decl => |decl| {
2300 if (build_options.omit_stage2)2322 if (build_options.omit_stage2)
2301 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2323 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2302 const module = self.bin_file.options.module.?;2324 const module = comp.bin_file.options.module.?;
2303 module.ensureDeclAnalyzed(decl) catch |err| switch (err) {2325 module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
2304 error.OutOfMemory => return error.OutOfMemory,2326 error.OutOfMemory => return error.OutOfMemory,
2305 error.AnalysisFail => continue,2327 error.AnalysisFail => return,
2306 };2328 };
2307 },2329 },
2308 .update_embed_file => |embed_file| {2330 .update_embed_file => |embed_file| {
2309 if (build_options.omit_stage2)2331 if (build_options.omit_stage2)
2310 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2332 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2311 const module = self.bin_file.options.module.?;2333 const module = comp.bin_file.options.module.?;
2312 module.updateEmbedFile(embed_file) catch |err| switch (err) {2334 module.updateEmbedFile(embed_file) catch |err| switch (err) {
2313 error.OutOfMemory => return error.OutOfMemory,2335 error.OutOfMemory => return error.OutOfMemory,
2314 error.AnalysisFail => continue,2336 error.AnalysisFail => return,
2315 };2337 };
2316 },2338 },
2317 .update_line_number => |decl| {2339 .update_line_number => |decl| {
2318 if (build_options.omit_stage2)2340 if (build_options.omit_stage2)
2319 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2341 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2320 const module = self.bin_file.options.module.?;2342 const gpa = comp.gpa;
2321 self.bin_file.updateDeclLineNumber(module, decl) catch |err| {2343 const module = comp.bin_file.options.module.?;
2344 comp.bin_file.updateDeclLineNumber(module, decl) catch |err| {
2322 try module.failed_decls.ensureUnusedCapacity(gpa, 1);2345 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2323 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(2346 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2324 gpa,2347 gpa,
...@@ -2332,31 +2355,31 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2332,31 +2355,31 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2332 .analyze_pkg => |pkg| {2355 .analyze_pkg => |pkg| {
2333 if (build_options.omit_stage2)2356 if (build_options.omit_stage2)
2334 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2357 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2335 const module = self.bin_file.options.module.?;2358 const module = comp.bin_file.options.module.?;
2336 module.semaPkg(pkg) catch |err| switch (err) {2359 module.semaPkg(pkg) catch |err| switch (err) {
2337 error.CurrentWorkingDirectoryUnlinked,2360 error.CurrentWorkingDirectoryUnlinked,
2338 error.Unexpected,2361 error.Unexpected,
2339 => try self.setMiscFailure(2362 => try comp.setMiscFailure(
2340 .analyze_pkg,2363 .analyze_pkg,
2341 "unexpected problem analyzing package '{s}'",2364 "unexpected problem analyzing package '{s}'",
2342 .{pkg.root_src_path},2365 .{pkg.root_src_path},
2343 ),2366 ),
2344 error.OutOfMemory => return error.OutOfMemory,2367 error.OutOfMemory => return error.OutOfMemory,
2345 error.AnalysisFail => continue,2368 error.AnalysisFail => return,
2346 };2369 };
2347 },2370 },
2348 .glibc_crt_file => |crt_file| {2371 .glibc_crt_file => |crt_file| {
2349 glibc.buildCRTFile(self, crt_file) catch |err| {2372 glibc.buildCRTFile(comp, crt_file) catch |err| {
2350 // TODO Surface more error details.2373 // TODO Surface more error details.
2351 try self.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{2374 try comp.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{
2352 @errorName(err),2375 @errorName(err),
2353 });2376 });
2354 };2377 };
2355 },2378 },
2356 .glibc_shared_objects => {2379 .glibc_shared_objects => {
2357 glibc.buildSharedObjects(self) catch |err| {2380 glibc.buildSharedObjects(comp) catch |err| {
2358 // TODO Surface more error details.2381 // TODO Surface more error details.
2359 try self.setMiscFailure(2382 try comp.setMiscFailure(
2360 .glibc_shared_objects,2383 .glibc_shared_objects,
2361 "unable to build glibc shared objects: {s}",2384 "unable to build glibc shared objects: {s}",
2362 .{@errorName(err)},2385 .{@errorName(err)},
...@@ -2364,9 +2387,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2364,9 +2387,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2364 };2387 };
2365 },2388 },
2366 .musl_crt_file => |crt_file| {2389 .musl_crt_file => |crt_file| {
2367 musl.buildCRTFile(self, crt_file) catch |err| {2390 musl.buildCRTFile(comp, crt_file) catch |err| {
2368 // TODO Surface more error details.2391 // TODO Surface more error details.
2369 try self.setMiscFailure(2392 try comp.setMiscFailure(
2370 .musl_crt_file,2393 .musl_crt_file,
2371 "unable to build musl CRT file: {s}",2394 "unable to build musl CRT file: {s}",
2372 .{@errorName(err)},2395 .{@errorName(err)},
...@@ -2374,9 +2397,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2374,9 +2397,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2374 };2397 };
2375 },2398 },
2376 .mingw_crt_file => |crt_file| {2399 .mingw_crt_file => |crt_file| {
2377 mingw.buildCRTFile(self, crt_file) catch |err| {2400 mingw.buildCRTFile(comp, crt_file) catch |err| {
2378 // TODO Surface more error details.2401 // TODO Surface more error details.
2379 try self.setMiscFailure(2402 try comp.setMiscFailure(
2380 .mingw_crt_file,2403 .mingw_crt_file,
2381 "unable to build mingw-w64 CRT file: {s}",2404 "unable to build mingw-w64 CRT file: {s}",
2382 .{@errorName(err)},2405 .{@errorName(err)},
...@@ -2384,10 +2407,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2384,10 +2407,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2384 };2407 };
2385 },2408 },
2386 .windows_import_lib => |index| {2409 .windows_import_lib => |index| {
2387 const link_lib = self.bin_file.options.system_libs.keys()[index];2410 const link_lib = comp.bin_file.options.system_libs.keys()[index];
2388 mingw.buildImportLib(self, link_lib) catch |err| {2411 mingw.buildImportLib(comp, link_lib) catch |err| {
2389 // TODO Surface more error details.2412 // TODO Surface more error details.
2390 try self.setMiscFailure(2413 try comp.setMiscFailure(
2391 .windows_import_lib,2414 .windows_import_lib,
2392 "unable to generate DLL import .lib file: {s}",2415 "unable to generate DLL import .lib file: {s}",
2393 .{@errorName(err)},2416 .{@errorName(err)},
...@@ -2395,9 +2418,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2395,9 +2418,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2395 };2418 };
2396 },2419 },
2397 .libunwind => {2420 .libunwind => {
2398 libunwind.buildStaticLib(self) catch |err| {2421 libunwind.buildStaticLib(comp) catch |err| {
2399 // TODO Surface more error details.2422 // TODO Surface more error details.
2400 try self.setMiscFailure(2423 try comp.setMiscFailure(
2401 .libunwind,2424 .libunwind,
2402 "unable to build libunwind: {s}",2425 "unable to build libunwind: {s}",
2403 .{@errorName(err)},2426 .{@errorName(err)},
...@@ -2405,9 +2428,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2405,9 +2428,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2405 };2428 };
2406 },2429 },
2407 .libcxx => {2430 .libcxx => {
2408 libcxx.buildLibCXX(self) catch |err| {2431 libcxx.buildLibCXX(comp) catch |err| {
2409 // TODO Surface more error details.2432 // TODO Surface more error details.
2410 try self.setMiscFailure(2433 try comp.setMiscFailure(
2411 .libcxx,2434 .libcxx,
2412 "unable to build libcxx: {s}",2435 "unable to build libcxx: {s}",
2413 .{@errorName(err)},2436 .{@errorName(err)},
...@@ -2415,9 +2438,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2415,9 +2438,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2415 };2438 };
2416 },2439 },
2417 .libcxxabi => {2440 .libcxxabi => {
2418 libcxx.buildLibCXXABI(self) catch |err| {2441 libcxx.buildLibCXXABI(comp) catch |err| {
2419 // TODO Surface more error details.2442 // TODO Surface more error details.
2420 try self.setMiscFailure(2443 try comp.setMiscFailure(
2421 .libcxxabi,2444 .libcxxabi,
2422 "unable to build libcxxabi: {s}",2445 "unable to build libcxxabi: {s}",
2423 .{@errorName(err)},2446 .{@errorName(err)},
...@@ -2425,9 +2448,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2425,9 +2448,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2425 };2448 };
2426 },2449 },
2427 .libtsan => {2450 .libtsan => {
2428 libtsan.buildTsan(self) catch |err| {2451 libtsan.buildTsan(comp) catch |err| {
2429 // TODO Surface more error details.2452 // TODO Surface more error details.
2430 try self.setMiscFailure(2453 try comp.setMiscFailure(
2431 .libtsan,2454 .libtsan,
2432 "unable to build TSAN library: {s}",2455 "unable to build TSAN library: {s}",
2433 .{@errorName(err)},2456 .{@errorName(err)},
...@@ -2435,9 +2458,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2435,9 +2458,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2435 };2458 };
2436 },2459 },
2437 .wasi_libc_crt_file => |crt_file| {2460 .wasi_libc_crt_file => |crt_file| {
2438 wasi_libc.buildCRTFile(self, crt_file) catch |err| {2461 wasi_libc.buildCRTFile(comp, crt_file) catch |err| {
2439 // TODO Surface more error details.2462 // TODO Surface more error details.
2440 try self.setMiscFailure(2463 try comp.setMiscFailure(
2441 .wasi_libc_crt_file,2464 .wasi_libc_crt_file,
2442 "unable to build WASI libc CRT file: {s}",2465 "unable to build WASI libc CRT file: {s}",
2443 .{@errorName(err)},2466 .{@errorName(err)},
...@@ -2445,15 +2468,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2445,15 +2468,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2445 };2468 };
2446 },2469 },
2447 .compiler_rt_lib => {2470 .compiler_rt_lib => {
2448 self.buildOutputFromZig(2471 comp.buildOutputFromZig(
2449 "compiler_rt.zig",2472 "compiler_rt.zig",
2450 .Lib,2473 .Lib,
2451 &self.compiler_rt_static_lib,2474 &comp.compiler_rt_static_lib,
2452 .compiler_rt,2475 .compiler_rt,
2453 ) catch |err| switch (err) {2476 ) catch |err| switch (err) {
2454 error.OutOfMemory => return error.OutOfMemory,2477 error.OutOfMemory => return error.OutOfMemory,
2455 error.SubCompilationFailed => continue, // error reported already2478 error.SubCompilationFailed => return, // error reported already
2456 else => try self.setMiscFailure(2479 else => try comp.setMiscFailure(
2457 .compiler_rt,2480 .compiler_rt,
2458 "unable to build compiler_rt: {s}",2481 "unable to build compiler_rt: {s}",
2459 .{@errorName(err)},2482 .{@errorName(err)},
...@@ -2461,15 +2484,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2461,15 +2484,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2461 };2484 };
2462 },2485 },
2463 .compiler_rt_obj => {2486 .compiler_rt_obj => {
2464 self.buildOutputFromZig(2487 comp.buildOutputFromZig(
2465 "compiler_rt.zig",2488 "compiler_rt.zig",
2466 .Obj,2489 .Obj,
2467 &self.compiler_rt_obj,2490 &comp.compiler_rt_obj,
2468 .compiler_rt,2491 .compiler_rt,
2469 ) catch |err| switch (err) {2492 ) catch |err| switch (err) {
2470 error.OutOfMemory => return error.OutOfMemory,2493 error.OutOfMemory => return error.OutOfMemory,
2471 error.SubCompilationFailed => continue, // error reported already2494 error.SubCompilationFailed => return, // error reported already
2472 else => try self.setMiscFailure(2495 else => try comp.setMiscFailure(
2473 .compiler_rt,2496 .compiler_rt,
2474 "unable to build compiler_rt: {s}",2497 "unable to build compiler_rt: {s}",
2475 .{@errorName(err)},2498 .{@errorName(err)},
...@@ -2477,15 +2500,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2477,15 +2500,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2477 };2500 };
2478 },2501 },
2479 .libssp => {2502 .libssp => {
2480 self.buildOutputFromZig(2503 comp.buildOutputFromZig(
2481 "ssp.zig",2504 "ssp.zig",
2482 .Lib,2505 .Lib,
2483 &self.libssp_static_lib,2506 &comp.libssp_static_lib,
2484 .libssp,2507 .libssp,
2485 ) catch |err| switch (err) {2508 ) catch |err| switch (err) {
2486 error.OutOfMemory => return error.OutOfMemory,2509 error.OutOfMemory => return error.OutOfMemory,
2487 error.SubCompilationFailed => continue, // error reported already2510 error.SubCompilationFailed => return, // error reported already
2488 else => try self.setMiscFailure(2511 else => try comp.setMiscFailure(
2489 .libssp,2512 .libssp,
2490 "unable to build libssp: {s}",2513 "unable to build libssp: {s}",
2491 .{@errorName(err)},2514 .{@errorName(err)},
...@@ -2493,15 +2516,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2493,15 +2516,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2493 };2516 };
2494 },2517 },
2495 .zig_libc => {2518 .zig_libc => {
2496 self.buildOutputFromZig(2519 comp.buildOutputFromZig(
2497 "c.zig",2520 "c.zig",
2498 .Lib,2521 .Lib,
2499 &self.libc_static_lib,2522 &comp.libc_static_lib,
2500 .zig_libc,2523 .zig_libc,
2501 ) catch |err| switch (err) {2524 ) catch |err| switch (err) {
2502 error.OutOfMemory => return error.OutOfMemory,2525 error.OutOfMemory => return error.OutOfMemory,
2503 error.SubCompilationFailed => continue, // error reported already2526 error.SubCompilationFailed => return, // error reported already
2504 else => try self.setMiscFailure(2527 else => try comp.setMiscFailure(
2505 .zig_libc,2528 .zig_libc,
2506 "unable to build zig's multitarget libc: {s}",2529 "unable to build zig's multitarget libc: {s}",
2507 .{@errorName(err)},2530 .{@errorName(err)},
...@@ -2512,11 +2535,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2512,11 +2535,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2512 if (!build_options.is_stage1)2535 if (!build_options.is_stage1)
2513 unreachable;2536 unreachable;
25142537
2515 self.updateStage1Module(main_progress_node) catch |err| {2538 comp.updateStage1Module(main_progress_node) catch |err| {
2516 fatal("unable to build stage1 zig object: {s}", .{@errorName(err)});2539 fatal("unable to build stage1 zig object: {s}", .{@errorName(err)});
2517 };2540 };
2518 },2541 },
2519 };2542 }
2520}2543}
25212544
2522const AstGenSrc = union(enum) {2545const AstGenSrc = union(enum) {
src/Module.zig+38-22
...@@ -3039,10 +3039,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {...@@ -3039,10 +3039,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
3039 log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{3039 log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{
3040 decl, decl.name, dep, dep.name,3040 decl, decl.name, dep, dep.name,
3041 });3041 });
3042 // We don't perform a deletion here, because this Decl or another one3042 try mod.markDeclForDeletion(dep);
3043 // may end up referencing it before the update is complete.
3044 dep.deletion_flag = true;
3045 try mod.deletion_set.put(mod.gpa, dep, {});
3046 }3043 }
3047 }3044 }
3048 decl.dependencies.clearRetainingCapacity();3045 decl.dependencies.clearRetainingCapacity();
...@@ -3433,21 +3430,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3433,21 +3430,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
34333430
3434 decl.owns_tv = false;3431 decl.owns_tv = false;
3435 var queue_linker_work = false;3432 var queue_linker_work = false;
3436 if (decl_tv.val.castTag(.variable)) |payload| {3433 switch (decl_tv.val.tag()) {
3437 const variable = payload.data;3434 .variable => {
3438 if (variable.owner_decl == decl) {3435 const variable = decl_tv.val.castTag(.variable).?.data;
3439 decl.owns_tv = true;3436 if (variable.owner_decl == decl) {
3440 queue_linker_work = true;3437 decl.owns_tv = true;
34413438 queue_linker_work = true;
3442 const copied_init = try variable.init.copy(&decl_arena.allocator);3439
3443 variable.init = copied_init;3440 const copied_init = try variable.init.copy(&decl_arena.allocator);
3444 }3441 variable.init = copied_init;
3445 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {3442 }
3446 const owner_decl = payload.data;3443 },
3447 if (decl == owner_decl) {3444 .extern_fn => {
3448 decl.owns_tv = true;3445 const owner_decl = decl_tv.val.castTag(.extern_fn).?.data;
3446 if (decl == owner_decl) {
3447 decl.owns_tv = true;
3448 queue_linker_work = true;
3449 }
3450 },
3451 .array, .@"struct", .@"union" => {
3452 log.debug("send global const to linker: {*} ({s})", .{ decl, decl.name });
3449 queue_linker_work = true;3453 queue_linker_work = true;
3450 }3454 },
3455 else => {},
3451 }3456 }
34523457
3453 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);3458 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
...@@ -3462,6 +3467,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3462,6 +3467,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3462 decl.generation = mod.generation;3467 decl.generation = mod.generation;
34633468
3464 if (queue_linker_work and decl.ty.hasCodeGenBits()) {3469 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3470 log.debug("queue linker work for {*} ({s})", .{ decl, decl.name });
3471
3465 try mod.comp.bin_file.allocateDeclIndexes(decl);3472 try mod.comp.bin_file.allocateDeclIndexes(decl);
3466 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });3473 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
34673474
...@@ -3985,6 +3992,7 @@ pub fn clearDecl(...@@ -3985,6 +3992,7 @@ pub fn clearDecl(
3985 decl.analysis = .unreferenced;3992 decl.analysis = .unreferenced;
3986}3993}
39873994
3995/// This function is exclusively called for anonymous decls.
3988pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {3996pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
3989 log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name });3997 log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name });
39903998
...@@ -4019,6 +4027,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {...@@ -4019,6 +4027,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
4019 decl.destroy(mod);4027 decl.destroy(mod);
4020}4028}
40214029
4030/// We don't perform a deletion here, because this Decl or another one
4031/// may end up referencing it before the update is complete.
4032fn markDeclForDeletion(mod: *Module, decl: *Decl) !void {
4033 decl.deletion_flag = true;
4034 try mod.deletion_set.put(mod.gpa, decl, {});
4035}
4036
4022/// Cancel the creation of an anon decl and delete any references to it.4037/// Cancel the creation of an anon decl and delete any references to it.
4023/// If other decls depend on this decl, they must be aborted first.4038/// If other decls depend on this decl, they must be aborted first.
4024pub fn abortAnonDecl(mod: *Module, decl: *Decl) void {4039pub fn abortAnonDecl(mod: *Module, decl: *Decl) void {
...@@ -4369,12 +4384,13 @@ pub fn createAnonymousDeclFromDeclNamed(...@@ -4369,12 +4384,13 @@ pub fn createAnonymousDeclFromDeclNamed(
43694384
4370 namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});4385 namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
43714386
4372 // TODO: This generates the Decl into the machine code file if it is of a4387 // The Decl starts off with alive=false and the codegen backend will set alive=true
4373 // type that is non-zero size. We should be able to further improve the4388 // if the Decl is referenced by an instruction or another constant. Otherwise,
4374 // compiler to omit Decls which are only referenced at compile-time and not runtime.4389 // the Decl will be garbage collected by the `codegen_decl` task instead of sent
4390 // to the linker.
4375 if (typed_value.ty.hasCodeGenBits()) {4391 if (typed_value.ty.hasCodeGenBits()) {
4376 try mod.comp.bin_file.allocateDeclIndexes(new_decl);4392 try mod.comp.bin_file.allocateDeclIndexes(new_decl);
4377 try mod.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });4393 try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl });
4378 }4394 }
43794395
4380 return new_decl;4396 return new_decl;
src/codegen.zig+7
...@@ -285,6 +285,13 @@ pub fn generateSymbol(...@@ -285,6 +285,13 @@ pub fn generateSymbol(
285 }285 }
286 return Result{ .appended = {} };286 return Result{ .appended = {} };
287 },287 },
288 .Struct => {
289 const field_vals = typed_value.val.castTag(.@"struct").?.data;
290 _ = field_vals; // TODO write the fields for real
291 const target = bin_file.options.target;
292 try code.writer().writeByteNTimes(0xaa, typed_value.ty.abiSize(target));
293 return Result{ .appended = {} };
294 },
288 else => |t| {295 else => |t| {
289 return Result{296 return Result{
290 .fail = try ErrorMsg.create(297 .fail = try ErrorMsg.create(
src/codegen/wasm.zig+5
...@@ -809,6 +809,11 @@ pub const Context = struct {...@@ -809,6 +809,11 @@ pub const Context = struct {
809 try self.emitConstant(val, ty);809 try self.emitConstant(val, ty);
810 return Result.appended;810 return Result.appended;
811 },811 },
812 .Struct => {
813 // TODO write the fields for real
814 try self.code.writer().writeByteNTimes(0xaa, ty.abiSize(self.target));
815 return Result{ .appended = {} };
816 },
812 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),817 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
813 }818 }
814 }819 }
test/behavior/basic.zig+15
...@@ -459,3 +459,18 @@ var gdt = [_]GDTEntry{...@@ -459,3 +459,18 @@ var gdt = [_]GDTEntry{
459 GDTEntry{ .field = 2 },459 GDTEntry{ .field = 2 },
460};460};
461var global_ptr = &gdt[0];461var global_ptr = &gdt[0];
462
463test "global constant is loaded with a runtime-known index" {
464 const S = struct {
465 fn doTheTest() !void {
466 var index: usize = 1;
467 const ptr = &pieces[index].field;
468 try expect(ptr.* == 2);
469 }
470 const Piece = struct {
471 field: i32,
472 };
473 const pieces = [_]Piece{ Piece{ .field = 1 }, Piece{ .field = 2 }, Piece{ .field = 3 } };
474 };
475 try S.doTheTest();
476}
test/behavior/switch.zig-19
...@@ -90,25 +90,6 @@ fn returnsFive() i32 {...@@ -90,25 +90,6 @@ fn returnsFive() i32 {
90 return 5;90 return 5;
91}91}
9292
93const Number = union(enum) {
94 One: u64,
95 Two: u8,
96 Three: f32,
97};
98
99const number = Number{ .Three = 1.23 };
100
101fn returnsFalse() bool {
102 switch (number) {
103 Number.One => |x| return x > 1234,
104 Number.Two => |x| return x == 'a',
105 Number.Three => |x| return x > 12.34,
106 }
107}
108test "switch on const enum with var" {
109 try expect(!returnsFalse());
110}
111
112test "switch on type" {93test "switch on type" {
113 try expect(trueIfBoolFalseOtherwise(bool));94 try expect(trueIfBoolFalseOtherwise(bool));
114 try expect(!trueIfBoolFalseOtherwise(i32));95 try expect(!trueIfBoolFalseOtherwise(i32));
test/behavior/switch_stage1.zig+18
...@@ -3,6 +3,24 @@ const expect = std.testing.expect;...@@ -3,6 +3,24 @@ const expect = std.testing.expect;
3const expectError = std.testing.expectError;3const expectError = std.testing.expectError;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
55
6const Number = union(enum) {
7 One: u64,
8 Two: u8,
9 Three: f32,
10};
11
12const number = Number{ .Three = 1.23 };
13
14fn returnsFalse() bool {
15 switch (number) {
16 Number.One => |x| return x > 1234,
17 Number.Two => |x| return x == 'a',
18 Number.Three => |x| return x > 12.34,
19 }
20}
21test "switch on const enum with var" {
22 try expect(!returnsFalse());
23}
6test "switch all prongs unreachable" {24test "switch all prongs unreachable" {
7 try testAllProngsUnreachable();25 try testAllProngsUnreachable();
8 comptime try testAllProngsUnreachable();26 comptime try testAllProngsUnreachable();
test/behavior/union.zig-31
...@@ -71,34 +71,3 @@ test "0-sized extern union definition" {...@@ -71,34 +71,3 @@ test "0-sized extern union definition" {
7171
72 try expect(U.f == 1);72 try expect(U.f == 1);
73}73}
74
75const Value = union(enum) {
76 Int: u64,
77 Array: [9]u8,
78};
79
80const Agg = struct {
81 val1: Value,
82 val2: Value,
83};
84
85const v1 = Value{ .Int = 1234 };
86const v2 = Value{ .Array = [_]u8{3} ** 9 };
87
88const err = @as(anyerror!Agg, Agg{
89 .val1 = v1,
90 .val2 = v2,
91});
92
93const array = [_]Value{ v1, v2, v1, v2 };
94
95test "unions embedded in aggregate types" {
96 switch (array[1]) {
97 Value.Array => |arr| try expect(arr[4] == 3),
98 else => unreachable,
99 }
100 switch ((err catch unreachable).val1) {
101 Value.Int => |x| try expect(x == 1234),
102 else => unreachable,
103 }
104}
test/behavior/union_stage1.zig+31
...@@ -3,6 +3,37 @@ const expect = std.testing.expect;...@@ -3,6 +3,37 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;4const Tag = std.meta.Tag;
55
6const Value = union(enum) {
7 Int: u64,
8 Array: [9]u8,
9};
10
11const Agg = struct {
12 val1: Value,
13 val2: Value,
14};
15
16const v1 = Value{ .Int = 1234 };
17const v2 = Value{ .Array = [_]u8{3} ** 9 };
18
19const err = @as(anyerror!Agg, Agg{
20 .val1 = v1,
21 .val2 = v2,
22});
23
24const array = [_]Value{ v1, v2, v1, v2 };
25
26test "unions embedded in aggregate types" {
27 switch (array[1]) {
28 Value.Array => |arr| try expect(arr[4] == 3),
29 else => unreachable,
30 }
31 switch ((err catch unreachable).val1) {
32 Value.Int => |x| try expect(x == 1234),
33 else => unreachable,
34 }
35}
36
6const Letter = enum { A, B, C };37const Letter = enum { A, B, C };
7const Payload = union(Letter) {38const Payload = union(Letter) {
8 A: i32,39 A: i32,