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,
4646link_error_flags: link.File.ErrorFlags = .{},
4747
4848work_queue: std.fifo.LinearFifo(Job, .Dynamic),
49anon_work_queue: std.fifo.LinearFifo(Job, .Dynamic),
4950
5051/// These jobs are to invoke the Clang compiler to create an object file, which
5152/// gets linked with the Compilation.
......@@ -1460,6 +1461,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
14601461 .emit_analysis = options.emit_analysis,
14611462 .emit_docs = options.emit_docs,
14621463 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
1464 .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
14631465 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
14641466 .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),
14651467 .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa),
......@@ -1646,6 +1648,7 @@ pub fn destroy(self: *Compilation) void {
16461648
16471649 const gpa = self.gpa;
16481650 self.work_queue.deinit();
1651 self.anon_work_queue.deinit();
16491652 self.c_object_work_queue.deinit();
16501653 self.astgen_work_queue.deinit();
16511654 self.embed_file_work_queue.deinit();
......@@ -2072,7 +2075,6 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
20722075}
20732076
20742077pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
2075 const gpa = self.gpa;
20762078 // If the terminal is dumb, we dont want to show the user all the
20772079 // output.
20782080 var progress: std.Progress = .{ .dont_print_on_dumb = true };
......@@ -2146,7 +2148,24 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21462148 }
21472149 }
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) {
21502169 .codegen_decl => |decl| switch (decl.analysis) {
21512170 .unreferenced => unreachable,
21522171 .in_progress => unreachable,
......@@ -2157,24 +2176,25 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21572176 .codegen_failure,
21582177 .dependency_failure,
21592178 .sema_failure_retryable,
2160 => continue,
2179 => return,
21612180
21622181 .complete, .codegen_failure_retryable => {
21632182 if (build_options.omit_stage2)
21642183 @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.?;
21672186 assert(decl.has_tv);
21682187 assert(decl.ty.hasCodeGenBits());
21692188
21702189 if (decl.alive) {
21712190 try module.linkerUpdateDecl(decl);
2172 continue;
2191 return;
21732192 }
21742193
21752194 // Instead of sending this decl to the linker, we actually will delete it
21762195 // because we found out that it in fact was never referenced.
21772196 module.deleteUnusedDecl(decl);
2197 return;
21782198 },
21792199 },
21802200 .codegen_func => |func| switch (func.owner_decl.analysis) {
......@@ -2187,20 +2207,21 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21872207 .codegen_failure,
21882208 .dependency_failure,
21892209 .sema_failure_retryable,
2190 => continue,
2210 => return,
21912211
21922212 .complete, .codegen_failure_retryable => {
21932213 if (build_options.omit_stage2)
21942214 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
21952215 switch (func.state) {
2196 .sema_failure, .dependency_failure => continue,
2216 .sema_failure, .dependency_failure => return,
21972217 .queued => {},
21982218 .in_progress => unreachable,
21992219 .inline_only => unreachable, // don't queue work for this
22002220 .success => unreachable, // don't queue it twice
22012221 }
22022222
2203 const module = self.bin_file.options.module.?;
2223 const gpa = comp.gpa;
2224 const module = comp.bin_file.options.module.?;
22042225 const decl = func.owner_decl;
22052226
22062227 var tmp_arena = std.heap.ArenaAllocator.init(gpa);
......@@ -2210,7 +2231,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22102231 var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {
22112232 error.AnalysisFail => {
22122233 assert(func.state != .in_progress);
2213 continue;
2234 return;
22142235 },
22152236 error.OutOfMemory => return error.OutOfMemory,
22162237 };
......@@ -2220,17 +2241,17 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22202241 var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir);
22212242 defer liveness.deinit(gpa);
22222243
2223 if (builtin.mode == .Debug and self.verbose_air) {
2244 if (builtin.mode == .Debug and comp.verbose_air) {
22242245 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
22252246 @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness);
22262247 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
22272248 }
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) {
22302251 error.OutOfMemory => return error.OutOfMemory,
22312252 error.AnalysisFail => {
22322253 decl.analysis = .codegen_failure;
2233 continue;
2254 return;
22342255 },
22352256 else => {
22362257 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
......@@ -2241,10 +2262,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22412262 .{@errorName(err)},
22422263 ));
22432264 decl.analysis = .codegen_failure_retryable;
2244 continue;
2265 return;
22452266 },
22462267 };
2247 continue;
2268 return;
22482269 },
22492270 },
22502271 .emit_h_decl => |decl| switch (decl.analysis) {
......@@ -2256,14 +2277,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22562277 .sema_failure,
22572278 .dependency_failure,
22582279 .sema_failure_retryable,
2259 => continue,
2280 => return,
22602281
22612282 // emit-h only requires semantic analysis of the Decl to be complete,
22622283 // it does not depend on machine code generation to succeed.
22632284 .codegen_failure, .codegen_failure_retryable, .complete => {
22642285 if (build_options.omit_stage2)
22652286 @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.?;
22672289 const emit_h = module.emit_h.?;
22682290 _ = try emit_h.decl_table.getOrPut(gpa, decl);
22692291 const decl_emit_h = decl.getEmitH(module);
......@@ -2287,7 +2309,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22872309 c_codegen.genHeader(&dg) catch |err| switch (err) {
22882310 error.AnalysisFail => {
22892311 try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?);
2290 continue;
2312 return;
22912313 },
22922314 else => |e| return e,
22932315 };
......@@ -2299,26 +2321,27 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
22992321 .analyze_decl => |decl| {
23002322 if (build_options.omit_stage2)
23012323 @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.?;
23032325 module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
23042326 error.OutOfMemory => return error.OutOfMemory,
2305 error.AnalysisFail => continue,
2327 error.AnalysisFail => return,
23062328 };
23072329 },
23082330 .update_embed_file => |embed_file| {
23092331 if (build_options.omit_stage2)
23102332 @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.?;
23122334 module.updateEmbedFile(embed_file) catch |err| switch (err) {
23132335 error.OutOfMemory => return error.OutOfMemory,
2314 error.AnalysisFail => continue,
2336 error.AnalysisFail => return,
23152337 };
23162338 },
23172339 .update_line_number => |decl| {
23182340 if (build_options.omit_stage2)
23192341 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2320 const module = self.bin_file.options.module.?;
2321 self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
2342 const gpa = comp.gpa;
2343 const module = comp.bin_file.options.module.?;
2344 comp.bin_file.updateDeclLineNumber(module, decl) catch |err| {
23222345 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
23232346 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
23242347 gpa,
......@@ -2332,31 +2355,31 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
23322355 .analyze_pkg => |pkg| {
23332356 if (build_options.omit_stage2)
23342357 @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.?;
23362359 module.semaPkg(pkg) catch |err| switch (err) {
23372360 error.CurrentWorkingDirectoryUnlinked,
23382361 error.Unexpected,
2339 => try self.setMiscFailure(
2362 => try comp.setMiscFailure(
23402363 .analyze_pkg,
23412364 "unexpected problem analyzing package '{s}'",
23422365 .{pkg.root_src_path},
23432366 ),
23442367 error.OutOfMemory => return error.OutOfMemory,
2345 error.AnalysisFail => continue,
2368 error.AnalysisFail => return,
23462369 };
23472370 },
23482371 .glibc_crt_file => |crt_file| {
2349 glibc.buildCRTFile(self, crt_file) catch |err| {
2372 glibc.buildCRTFile(comp, crt_file) catch |err| {
23502373 // 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}", .{
23522375 @errorName(err),
23532376 });
23542377 };
23552378 },
23562379 .glibc_shared_objects => {
2357 glibc.buildSharedObjects(self) catch |err| {
2380 glibc.buildSharedObjects(comp) catch |err| {
23582381 // TODO Surface more error details.
2359 try self.setMiscFailure(
2382 try comp.setMiscFailure(
23602383 .glibc_shared_objects,
23612384 "unable to build glibc shared objects: {s}",
23622385 .{@errorName(err)},
......@@ -2364,9 +2387,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
23642387 };
23652388 },
23662389 .musl_crt_file => |crt_file| {
2367 musl.buildCRTFile(self, crt_file) catch |err| {
2390 musl.buildCRTFile(comp, crt_file) catch |err| {
23682391 // TODO Surface more error details.
2369 try self.setMiscFailure(
2392 try comp.setMiscFailure(
23702393 .musl_crt_file,
23712394 "unable to build musl CRT file: {s}",
23722395 .{@errorName(err)},
......@@ -2374,9 +2397,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
23742397 };
23752398 },
23762399 .mingw_crt_file => |crt_file| {
2377 mingw.buildCRTFile(self, crt_file) catch |err| {
2400 mingw.buildCRTFile(comp, crt_file) catch |err| {
23782401 // TODO Surface more error details.
2379 try self.setMiscFailure(
2402 try comp.setMiscFailure(
23802403 .mingw_crt_file,
23812404 "unable to build mingw-w64 CRT file: {s}",
23822405 .{@errorName(err)},
......@@ -2384,10 +2407,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
23842407 };
23852408 },
23862409 .windows_import_lib => |index| {
2387 const link_lib = self.bin_file.options.system_libs.keys()[index];
2388 mingw.buildImportLib(self, link_lib) catch |err| {
2410 const link_lib = comp.bin_file.options.system_libs.keys()[index];
2411 mingw.buildImportLib(comp, link_lib) catch |err| {
23892412 // TODO Surface more error details.
2390 try self.setMiscFailure(
2413 try comp.setMiscFailure(
23912414 .windows_import_lib,
23922415 "unable to generate DLL import .lib file: {s}",
23932416 .{@errorName(err)},
......@@ -2395,9 +2418,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
23952418 };
23962419 },
23972420 .libunwind => {
2398 libunwind.buildStaticLib(self) catch |err| {
2421 libunwind.buildStaticLib(comp) catch |err| {
23992422 // TODO Surface more error details.
2400 try self.setMiscFailure(
2423 try comp.setMiscFailure(
24012424 .libunwind,
24022425 "unable to build libunwind: {s}",
24032426 .{@errorName(err)},
......@@ -2405,9 +2428,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24052428 };
24062429 },
24072430 .libcxx => {
2408 libcxx.buildLibCXX(self) catch |err| {
2431 libcxx.buildLibCXX(comp) catch |err| {
24092432 // TODO Surface more error details.
2410 try self.setMiscFailure(
2433 try comp.setMiscFailure(
24112434 .libcxx,
24122435 "unable to build libcxx: {s}",
24132436 .{@errorName(err)},
......@@ -2415,9 +2438,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24152438 };
24162439 },
24172440 .libcxxabi => {
2418 libcxx.buildLibCXXABI(self) catch |err| {
2441 libcxx.buildLibCXXABI(comp) catch |err| {
24192442 // TODO Surface more error details.
2420 try self.setMiscFailure(
2443 try comp.setMiscFailure(
24212444 .libcxxabi,
24222445 "unable to build libcxxabi: {s}",
24232446 .{@errorName(err)},
......@@ -2425,9 +2448,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24252448 };
24262449 },
24272450 .libtsan => {
2428 libtsan.buildTsan(self) catch |err| {
2451 libtsan.buildTsan(comp) catch |err| {
24292452 // TODO Surface more error details.
2430 try self.setMiscFailure(
2453 try comp.setMiscFailure(
24312454 .libtsan,
24322455 "unable to build TSAN library: {s}",
24332456 .{@errorName(err)},
......@@ -2435,9 +2458,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24352458 };
24362459 },
24372460 .wasi_libc_crt_file => |crt_file| {
2438 wasi_libc.buildCRTFile(self, crt_file) catch |err| {
2461 wasi_libc.buildCRTFile(comp, crt_file) catch |err| {
24392462 // TODO Surface more error details.
2440 try self.setMiscFailure(
2463 try comp.setMiscFailure(
24412464 .wasi_libc_crt_file,
24422465 "unable to build WASI libc CRT file: {s}",
24432466 .{@errorName(err)},
......@@ -2445,15 +2468,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24452468 };
24462469 },
24472470 .compiler_rt_lib => {
2448 self.buildOutputFromZig(
2471 comp.buildOutputFromZig(
24492472 "compiler_rt.zig",
24502473 .Lib,
2451 &self.compiler_rt_static_lib,
2474 &comp.compiler_rt_static_lib,
24522475 .compiler_rt,
24532476 ) catch |err| switch (err) {
24542477 error.OutOfMemory => return error.OutOfMemory,
2455 error.SubCompilationFailed => continue, // error reported already
2456 else => try self.setMiscFailure(
2478 error.SubCompilationFailed => return, // error reported already
2479 else => try comp.setMiscFailure(
24572480 .compiler_rt,
24582481 "unable to build compiler_rt: {s}",
24592482 .{@errorName(err)},
......@@ -2461,15 +2484,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24612484 };
24622485 },
24632486 .compiler_rt_obj => {
2464 self.buildOutputFromZig(
2487 comp.buildOutputFromZig(
24652488 "compiler_rt.zig",
24662489 .Obj,
2467 &self.compiler_rt_obj,
2490 &comp.compiler_rt_obj,
24682491 .compiler_rt,
24692492 ) catch |err| switch (err) {
24702493 error.OutOfMemory => return error.OutOfMemory,
2471 error.SubCompilationFailed => continue, // error reported already
2472 else => try self.setMiscFailure(
2494 error.SubCompilationFailed => return, // error reported already
2495 else => try comp.setMiscFailure(
24732496 .compiler_rt,
24742497 "unable to build compiler_rt: {s}",
24752498 .{@errorName(err)},
......@@ -2477,15 +2500,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24772500 };
24782501 },
24792502 .libssp => {
2480 self.buildOutputFromZig(
2503 comp.buildOutputFromZig(
24812504 "ssp.zig",
24822505 .Lib,
2483 &self.libssp_static_lib,
2506 &comp.libssp_static_lib,
24842507 .libssp,
24852508 ) catch |err| switch (err) {
24862509 error.OutOfMemory => return error.OutOfMemory,
2487 error.SubCompilationFailed => continue, // error reported already
2488 else => try self.setMiscFailure(
2510 error.SubCompilationFailed => return, // error reported already
2511 else => try comp.setMiscFailure(
24892512 .libssp,
24902513 "unable to build libssp: {s}",
24912514 .{@errorName(err)},
......@@ -2493,15 +2516,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
24932516 };
24942517 },
24952518 .zig_libc => {
2496 self.buildOutputFromZig(
2519 comp.buildOutputFromZig(
24972520 "c.zig",
24982521 .Lib,
2499 &self.libc_static_lib,
2522 &comp.libc_static_lib,
25002523 .zig_libc,
25012524 ) catch |err| switch (err) {
25022525 error.OutOfMemory => return error.OutOfMemory,
2503 error.SubCompilationFailed => continue, // error reported already
2504 else => try self.setMiscFailure(
2526 error.SubCompilationFailed => return, // error reported already
2527 else => try comp.setMiscFailure(
25052528 .zig_libc,
25062529 "unable to build zig's multitarget libc: {s}",
25072530 .{@errorName(err)},
......@@ -2512,11 +2535,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
25122535 if (!build_options.is_stage1)
25132536 unreachable;
25142537
2515 self.updateStage1Module(main_progress_node) catch |err| {
2538 comp.updateStage1Module(main_progress_node) catch |err| {
25162539 fatal("unable to build stage1 zig object: {s}", .{@errorName(err)});
25172540 };
25182541 },
2519 };
2542 }
25202543}
25212544
25222545const AstGenSrc = union(enum) {
src/Module.zig+38-22
......@@ -3039,10 +3039,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
30393039 log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{
30403040 decl, decl.name, dep, dep.name,
30413041 });
3042 // We don't perform a deletion here, because this Decl or another one
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, {});
3042 try mod.markDeclForDeletion(dep);
30463043 }
30473044 }
30483045 decl.dependencies.clearRetainingCapacity();
......@@ -3433,21 +3430,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
34333430
34343431 decl.owns_tv = false;
34353432 var queue_linker_work = false;
3436 if (decl_tv.val.castTag(.variable)) |payload| {
3437 const variable = payload.data;
3438 if (variable.owner_decl == decl) {
3439 decl.owns_tv = true;
3440 queue_linker_work = true;
3441
3442 const copied_init = try variable.init.copy(&decl_arena.allocator);
3443 variable.init = copied_init;
3444 }
3445 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
3446 const owner_decl = payload.data;
3447 if (decl == owner_decl) {
3448 decl.owns_tv = true;
3433 switch (decl_tv.val.tag()) {
3434 .variable => {
3435 const variable = decl_tv.val.castTag(.variable).?.data;
3436 if (variable.owner_decl == decl) {
3437 decl.owns_tv = true;
3438 queue_linker_work = true;
3439
3440 const copied_init = try variable.init.copy(&decl_arena.allocator);
3441 variable.init = copied_init;
3442 }
3443 },
3444 .extern_fn => {
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 });
34493453 queue_linker_work = true;
3450 }
3454 },
3455 else => {},
34513456 }
34523457
34533458 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
......@@ -3462,6 +3467,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
34623467 decl.generation = mod.generation;
34633468
34643469 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3470 log.debug("queue linker work for {*} ({s})", .{ decl, decl.name });
3471
34653472 try mod.comp.bin_file.allocateDeclIndexes(decl);
34663473 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
34673474
......@@ -3985,6 +3992,7 @@ pub fn clearDecl(
39853992 decl.analysis = .unreferenced;
39863993}
39873994
3995/// This function is exclusively called for anonymous decls.
39883996pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
39893997 log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name });
39903998
......@@ -4019,6 +4027,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
40194027 decl.destroy(mod);
40204028}
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
40224037/// Cancel the creation of an anon decl and delete any references to it.
40234038/// If other decls depend on this decl, they must be aborted first.
40244039pub fn abortAnonDecl(mod: *Module, decl: *Decl) void {
......@@ -4369,12 +4384,13 @@ pub fn createAnonymousDeclFromDeclNamed(
43694384
43704385 namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
43714386
4372 // TODO: This generates the Decl into the machine code file if it is of a
4373 // type that is non-zero size. We should be able to further improve the
4374 // compiler to omit Decls which are only referenced at compile-time and not runtime.
4387 // The Decl starts off with alive=false and the codegen backend will set alive=true
4388 // if the Decl is referenced by an instruction or another constant. Otherwise,
4389 // the Decl will be garbage collected by the `codegen_decl` task instead of sent
4390 // to the linker.
43754391 if (typed_value.ty.hasCodeGenBits()) {
43764392 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 });
43784394 }
43794395
43804396 return new_decl;
src/codegen.zig+7
......@@ -285,6 +285,13 @@ pub fn generateSymbol(
285285 }
286286 return Result{ .appended = {} };
287287 },
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 },
288295 else => |t| {
289296 return Result{
290297 .fail = try ErrorMsg.create(
src/codegen/wasm.zig+5
......@@ -809,6 +809,11 @@ pub const Context = struct {
809809 try self.emitConstant(val, ty);
810810 return Result.appended;
811811 },
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 },
812817 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
813818 }
814819 }
test/behavior/basic.zig+15
......@@ -459,3 +459,18 @@ var gdt = [_]GDTEntry{
459459 GDTEntry{ .field = 2 },
460460};
461461var 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 {
9090 return 5;
9191}
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
11293test "switch on type" {
11394 try expect(trueIfBoolFalseOtherwise(bool));
11495 try expect(!trueIfBoolFalseOtherwise(i32));
test/behavior/switch_stage1.zig+18
......@@ -3,6 +3,24 @@ const expect = std.testing.expect;
33const expectError = std.testing.expectError;
44const 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}
624test "switch all prongs unreachable" {
725 try testAllProngsUnreachable();
826 comptime try testAllProngsUnreachable();
test/behavior/union.zig-31
......@@ -71,34 +71,3 @@ test "0-sized extern union definition" {
7171
7272 try expect(U.f == 1);
7373}
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;
33const expectEqual = std.testing.expectEqual;
44const 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
637const Letter = enum { A, B, C };
738const Payload = union(Letter) {
839 A: i32,