authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-15 21:44:33-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-15 21:44:33-04:00
logf2feb4e47aa7d74f26f5bda1f8383ccd0f54026a
treed76a29e9fe29bb6e717b7c07dd4cb74f4190ce3f
parent64f4ef75566ef34289e9e6a455b0173e4e58df47

move Module to its own file


8 files changed, 3534 insertions(+), 3525 deletions(-)

src-self-hosted/Module.zig created+2018
...@@ -0,0 +1,2018 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const ArrayListUnmanaged = std.ArrayListUnmanaged;
5const Value = @import("value.zig").Value;
6const Type = @import("type.zig").Type;
7const TypedValue = @import("TypedValue.zig");
8const assert = std.debug.assert;
9const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;
11const Target = std.Target;
12const Package = @import("Package.zig");
13const link = @import("link.zig");
14const ir = @import("ir.zig");
15const zir = @import("zir.zig");
16const Module = @This();
17const Inst = ir.Inst;
18
19/// General-purpose allocator.
20allocator: *Allocator,
21/// Module owns this resource.
22root_pkg: *Package,
23/// Module owns this resource.
24root_scope: *Scope.ZIRModule,
25/// Pointer to externally managed resource.
26bin_file: *link.ElfFile,
27/// It's rare for a decl to be exported, so we save memory by having a sparse map of
28/// Decl pointers to details about them being exported.
29/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
30decl_exports: std.AutoHashMap(*Decl, []*Export),
31/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
32/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
33/// is performing the export of another Decl.
34/// This table owns the Export memory.
35export_owners: std.AutoHashMap(*Decl, []*Export),
36/// Maps fully qualified namespaced names to the Decl struct for them.
37decl_table: std.AutoHashMap(Decl.Hash, *Decl),
38
39optimize_mode: std.builtin.Mode,
40link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
41
42work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
43
44/// We optimize memory usage for a compilation with no compile errors by storing the
45/// error messages and mapping outside of `Decl`.
46/// The ErrorMsg memory is owned by the decl, using Module's allocator.
47/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
48/// a Decl can have a failed_decls entry but have analysis status of success.
49failed_decls: std.AutoHashMap(*Decl, *ErrorMsg),
50/// Using a map here for consistency with the other fields here.
51/// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator.
52failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
53/// Using a map here for consistency with the other fields here.
54/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
55failed_exports: std.AutoHashMap(*Export, *ErrorMsg),
56
57pub const WorkItem = union(enum) {
58 /// Write the machine code for a Decl to the output file.
59 codegen_decl: *Decl,
60};
61
62pub const Export = struct {
63 options: std.builtin.ExportOptions,
64 /// Byte offset into the file that contains the export directive.
65 src: usize,
66 /// Represents the position of the export, if any, in the output file.
67 link: link.ElfFile.Export,
68 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
69 owner_decl: *Decl,
70 status: enum {
71 in_progress,
72 failed,
73 /// Indicates that the failure was due to a temporary issue, such as an I/O error
74 /// when writing to the output file. Retrying the export may succeed.
75 failed_retryable,
76 complete,
77 },
78};
79
80pub const Decl = struct {
81 /// This name is relative to the containing namespace of the decl. It uses a null-termination
82 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
83 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
84 /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
85 /// mapping them to an address in the output file.
86 /// Memory owned by this decl, using Module's allocator.
87 name: [*:0]const u8,
88 /// The direct parent container of the Decl. This field will need to get more fleshed out when
89 /// self-hosted supports proper struct types and Zig AST => ZIR.
90 /// Reference to externally owned memory.
91 scope: *Scope.ZIRModule,
92 /// Byte offset into the source file that contains this declaration.
93 /// This is the base offset that src offsets within this Decl are relative to.
94 src: usize,
95 /// The most recent value of the Decl after a successful semantic analysis.
96 /// The tag for this union is determined by the tag value of the analysis field.
97 typed_value: union {
98 never_succeeded: void,
99 most_recent: TypedValue.Managed,
100 },
101 /// Represents the "shallow" analysis status. For example, for decls that are functions,
102 /// the function type is analyzed with this set to `in_progress`, however, the semantic
103 /// analysis of the function body is performed with this value set to `success`. Functions
104 /// have their own analysis status field.
105 analysis: enum {
106 initial_in_progress,
107 /// This Decl might be OK but it depends on another one which did not successfully complete
108 /// semantic analysis. This Decl never had a value computed.
109 initial_dependency_failure,
110 /// Semantic analysis failure. This Decl never had a value computed.
111 /// There will be a corresponding ErrorMsg in Module.failed_decls.
112 initial_sema_failure,
113 /// In this case the `typed_value.most_recent` can still be accessed.
114 /// There will be a corresponding ErrorMsg in Module.failed_decls.
115 codegen_failure,
116 /// In this case the `typed_value.most_recent` can still be accessed.
117 /// There will be a corresponding ErrorMsg in Module.failed_decls.
118 /// This indicates the failure was something like running out of disk space,
119 /// and attempting codegen again may succeed.
120 codegen_failure_retryable,
121 /// This Decl might be OK but it depends on another one which did not successfully complete
122 /// semantic analysis. There is a most recent value available.
123 repeat_dependency_failure,
124 /// Semantic anlaysis failure, but the `typed_value.most_recent` can be accessed.
125 /// There will be a corresponding ErrorMsg in Module.failed_decls.
126 repeat_sema_failure,
127 /// Completed successfully before; the `typed_value.most_recent` can be accessed, and
128 /// new semantic analysis is in progress.
129 repeat_in_progress,
130 /// Everything is done and updated.
131 complete,
132 },
133
134 /// Represents the position of the code in the output file.
135 /// This is populated regardless of semantic analysis and code generation.
136 link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
137
138 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
139 /// typed_value is modified.
140 /// TODO look into using a lightweight map/set data structure rather than a linear array.
141 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
142
143 contents_hash: Hash,
144
145 pub fn destroy(self: *Decl, allocator: *Allocator) void {
146 allocator.free(mem.spanZ(self.name));
147 if (self.typedValueManaged()) |tvm| {
148 tvm.deinit(allocator);
149 }
150 allocator.destroy(self);
151 }
152
153 pub const Hash = [16]u8;
154
155 /// If the name is small enough, it is used directly as the hash.
156 /// If it is long, blake3 hash is computed.
157 pub fn hashSimpleName(name: []const u8) Hash {
158 var out: Hash = undefined;
159 if (name.len <= Hash.len) {
160 mem.copy(u8, &out, name);
161 mem.set(u8, out[name.len..], 0);
162 } else {
163 std.crypto.Blake3.hash(name, &out);
164 }
165 return out;
166 }
167
168 /// Must generate unique bytes with no collisions with other decls.
169 /// The point of hashing here is only to limit the number of bytes of
170 /// the unique identifier to a fixed size (16 bytes).
171 pub fn fullyQualifiedNameHash(self: Decl) Hash {
172 // Right now we only have ZIRModule as the source. So this is simply the
173 // relative name of the decl.
174 return hashSimpleName(mem.spanZ(u8, self.name));
175 }
176
177 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
178 const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
179 return tvm.typed_value;
180 }
181
182 pub fn value(self: *Decl) error{AnalysisFail}!Value {
183 return (try self.typedValue()).val;
184 }
185
186 pub fn dump(self: *Decl) void {
187 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
188 std.debug.warn("{}:{}:{} name={} status={}", .{
189 self.scope.sub_file_path,
190 loc.line + 1,
191 loc.column + 1,
192 mem.spanZ(self.name),
193 @tagName(self.analysis),
194 });
195 if (self.typedValueManaged()) |tvm| {
196 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
197 }
198 std.debug.warn("\n", .{});
199 }
200
201 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
202 switch (self.analysis) {
203 .initial_in_progress,
204 .initial_dependency_failure,
205 .initial_sema_failure,
206 => return null,
207 .codegen_failure,
208 .codegen_failure_retryable,
209 .repeat_dependency_failure,
210 .repeat_sema_failure,
211 .repeat_in_progress,
212 .complete,
213 => return &self.typed_value.most_recent,
214 }
215 }
216};
217
218/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
219pub const Fn = struct {
220 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
221 fn_type: Type,
222 analysis: union(enum) {
223 /// The value is the source instruction.
224 queued: *zir.Inst.Fn,
225 in_progress: *Analysis,
226 /// There will be a corresponding ErrorMsg in Module.failed_decls
227 sema_failure,
228 /// This Fn might be OK but it depends on another Decl which did not successfully complete
229 /// semantic analysis.
230 dependency_failure,
231 success: Body,
232 },
233
234 /// This memory is temporary and points to stack memory for the duration
235 /// of Fn analysis.
236 pub const Analysis = struct {
237 inner_block: Scope.Block,
238 /// TODO Performance optimization idea: instead of this inst_table,
239 /// use a field in the zir.Inst instead to track corresponding instructions
240 inst_table: std.AutoHashMap(*zir.Inst, *Inst),
241 needed_inst_capacity: usize,
242 };
243};
244
245pub const Scope = struct {
246 tag: Tag,
247
248 pub fn cast(base: *Scope, comptime T: type) ?*T {
249 if (base.tag != T.base_tag)
250 return null;
251
252 return @fieldParentPtr(T, "base", base);
253 }
254
255 /// Asserts the scope has a parent which is a DeclAnalysis and
256 /// returns the arena Allocator.
257 pub fn arena(self: *Scope) *Allocator {
258 switch (self.tag) {
259 .block => return self.cast(Block).?.arena,
260 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
261 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
262 }
263 }
264
265 /// Asserts the scope has a parent which is a DeclAnalysis and
266 /// returns the Decl.
267 pub fn decl(self: *Scope) *Decl {
268 switch (self.tag) {
269 .block => return self.cast(Block).?.decl,
270 .decl => return self.cast(DeclAnalysis).?.decl,
271 .zir_module => unreachable,
272 }
273 }
274
275 /// Asserts the scope has a parent which is a ZIRModule and
276 /// returns it.
277 pub fn namespace(self: *Scope) *ZIRModule {
278 switch (self.tag) {
279 .block => return self.cast(Block).?.decl.scope,
280 .decl => return self.cast(DeclAnalysis).?.decl.scope,
281 .zir_module => return self.cast(ZIRModule).?,
282 }
283 }
284
285 pub fn dumpInst(self: *Scope, inst: *Inst) void {
286 const zir_module = self.namespace();
287 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
288 std.debug.warn("{}:{}:{}: {}: ty={}\n", .{
289 zir_module.sub_file_path,
290 loc.line + 1,
291 loc.column + 1,
292 @tagName(inst.tag),
293 inst.ty,
294 });
295 }
296
297 pub const Tag = enum {
298 zir_module,
299 block,
300 decl,
301 };
302
303 pub const ZIRModule = struct {
304 pub const base_tag: Tag = .zir_module;
305 base: Scope = Scope{ .tag = base_tag },
306 /// Relative to the owning package's root_src_dir.
307 /// Reference to external memory, not owned by ZIRModule.
308 sub_file_path: []const u8,
309 source: union {
310 unloaded: void,
311 bytes: [:0]const u8,
312 },
313 contents: union {
314 not_available: void,
315 module: *zir.Module,
316 },
317 status: enum {
318 never_loaded,
319 unloaded_success,
320 unloaded_parse_failure,
321 unloaded_sema_failure,
322 loaded_parse_failure,
323 loaded_sema_failure,
324 loaded_success,
325 },
326
327 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {
328 switch (self.status) {
329 .never_loaded,
330 .unloaded_parse_failure,
331 .unloaded_sema_failure,
332 .unloaded_success,
333 => {},
334
335 .loaded_success => {
336 allocator.free(self.source.bytes);
337 self.contents.module.deinit(allocator);
338 allocator.destroy(self.contents.module);
339 self.status = .unloaded_success;
340 },
341 .loaded_sema_failure => {
342 allocator.free(self.source.bytes);
343 self.contents.module.deinit(allocator);
344 allocator.destroy(self.contents.module);
345 self.status = .unloaded_sema_failure;
346 },
347 .loaded_parse_failure => {
348 allocator.free(self.source.bytes);
349 self.status = .unloaded_parse_failure;
350 },
351 }
352 }
353
354 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
355 self.unload(allocator);
356 self.* = undefined;
357 }
358
359 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
360 const loc = std.zig.findLineColumn(self.source.bytes, src);
361 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
362 }
363 };
364
365 /// This is a temporary structure, references to it are valid only
366 /// during semantic analysis of the block.
367 pub const Block = struct {
368 pub const base_tag: Tag = .block;
369 base: Scope = Scope{ .tag = base_tag },
370 func: *Fn,
371 decl: *Decl,
372 instructions: ArrayListUnmanaged(*Inst),
373 /// Points to the arena allocator of DeclAnalysis
374 arena: *Allocator,
375 };
376
377 /// This is a temporary structure, references to it are valid only
378 /// during semantic analysis of the decl.
379 pub const DeclAnalysis = struct {
380 pub const base_tag: Tag = .decl;
381 base: Scope = Scope{ .tag = base_tag },
382 decl: *Decl,
383 arena: std.heap.ArenaAllocator,
384 };
385};
386
387pub const Body = struct {
388 instructions: []*Inst,
389};
390
391pub const AllErrors = struct {
392 arena: std.heap.ArenaAllocator.State,
393 list: []const Message,
394
395 pub const Message = struct {
396 src_path: []const u8,
397 line: usize,
398 column: usize,
399 byte_offset: usize,
400 msg: []const u8,
401 };
402
403 pub fn deinit(self: *AllErrors, allocator: *Allocator) void {
404 self.arena.promote(allocator).deinit();
405 }
406
407 fn add(
408 arena: *std.heap.ArenaAllocator,
409 errors: *std.ArrayList(Message),
410 sub_file_path: []const u8,
411 source: []const u8,
412 simple_err_msg: ErrorMsg,
413 ) !void {
414 const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
415 try errors.append(.{
416 .src_path = try arena.allocator.dupe(u8, sub_file_path),
417 .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
418 .byte_offset = simple_err_msg.byte_offset,
419 .line = loc.line,
420 .column = loc.column,
421 });
422 }
423};
424
425pub fn deinit(self: *Module) void {
426 const allocator = self.allocator;
427 self.work_queue.deinit();
428 {
429 var it = self.decl_table.iterator();
430 while (it.next()) |kv| {
431 kv.value.destroy(allocator);
432 }
433 self.decl_table.deinit();
434 }
435 {
436 var it = self.failed_decls.iterator();
437 while (it.next()) |kv| {
438 kv.value.destroy(allocator);
439 }
440 self.failed_decls.deinit();
441 }
442 {
443 var it = self.failed_files.iterator();
444 while (it.next()) |kv| {
445 kv.value.destroy(allocator);
446 }
447 self.failed_files.deinit();
448 }
449 {
450 var it = self.failed_exports.iterator();
451 while (it.next()) |kv| {
452 kv.value.destroy(allocator);
453 }
454 self.failed_exports.deinit();
455 }
456 {
457 var it = self.decl_exports.iterator();
458 while (it.next()) |kv| {
459 const export_list = kv.value;
460 allocator.free(export_list);
461 }
462 self.decl_exports.deinit();
463 }
464 {
465 var it = self.export_owners.iterator();
466 while (it.next()) |kv| {
467 const export_list = kv.value;
468 for (export_list) |exp| {
469 allocator.destroy(exp);
470 }
471 allocator.free(export_list);
472 }
473 self.export_owners.deinit();
474 }
475 self.root_pkg.destroy();
476 {
477 self.root_scope.deinit(allocator);
478 allocator.destroy(self.root_scope);
479 }
480 self.* = undefined;
481}
482
483pub fn target(self: Module) std.Target {
484 return self.bin_file.options.target;
485}
486
487/// Detect changes to source files, perform semantic analysis, and update the output files.
488pub fn update(self: *Module) !void {
489 // TODO Use the cache hash file system to detect which source files changed.
490 // Here we simulate a full cache miss.
491 // Analyze the root source file now.
492 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
493 error.AnalysisFail => {
494 assert(self.totalErrorCount() != 0);
495 },
496 else => |e| return e,
497 };
498
499 try self.performAllTheWork();
500
501 // Unload all the source files from memory.
502 self.root_scope.unload(self.allocator);
503
504 try self.bin_file.flush();
505 self.link_error_flags = self.bin_file.error_flags;
506}
507
508pub fn totalErrorCount(self: *Module) usize {
509 return self.failed_decls.size +
510 self.failed_files.size +
511 self.failed_exports.size +
512 @boolToInt(self.link_error_flags.no_entry_point_found);
513}
514
515pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
516 var arena = std.heap.ArenaAllocator.init(self.allocator);
517 errdefer arena.deinit();
518
519 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
520 defer errors.deinit();
521
522 {
523 var it = self.failed_files.iterator();
524 while (it.next()) |kv| {
525 const scope = kv.key;
526 const err_msg = kv.value;
527 const source = scope.source.bytes;
528 try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*);
529 }
530 }
531 {
532 var it = self.failed_decls.iterator();
533 while (it.next()) |kv| {
534 const decl = kv.key;
535 const err_msg = kv.value;
536 const source = decl.scope.source.bytes;
537 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
538 }
539 }
540 {
541 var it = self.failed_exports.iterator();
542 while (it.next()) |kv| {
543 const decl = kv.key.owner_decl;
544 const err_msg = kv.value;
545 const source = decl.scope.source.bytes;
546 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
547 }
548 }
549
550 if (self.link_error_flags.no_entry_point_found) {
551 try errors.append(.{
552 .src_path = self.root_pkg.root_src_path,
553 .line = 0,
554 .column = 0,
555 .byte_offset = 0,
556 .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
557 });
558 }
559
560 assert(errors.items.len == self.totalErrorCount());
561
562 return AllErrors{
563 .arena = arena.state,
564 .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
565 };
566}
567
568const InnerError = error{ OutOfMemory, AnalysisFail };
569
570pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
571 while (self.work_queue.readItem()) |work_item| switch (work_item) {
572 .codegen_decl => |decl| switch (decl.analysis) {
573 .initial_in_progress,
574 .repeat_in_progress,
575 => unreachable,
576
577 .initial_sema_failure,
578 .repeat_sema_failure,
579 .codegen_failure,
580 .initial_dependency_failure,
581 .repeat_dependency_failure,
582 => continue,
583
584 .complete, .codegen_failure_retryable => {
585 if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
586 switch (payload.func.analysis) {
587 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
588 error.AnalysisFail => {
589 if (payload.func.analysis == .queued) {
590 payload.func.analysis = .dependency_failure;
591 }
592 continue;
593 },
594 else => |e| return e,
595 },
596 .in_progress => unreachable,
597 .sema_failure, .dependency_failure => continue,
598 .success => {},
599 }
600 }
601
602 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
603
604 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
605 error.OutOfMemory => return error.OutOfMemory,
606 error.AnalysisFail => {
607 decl.analysis = .repeat_dependency_failure;
608 },
609 else => {
610 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
611 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
612 self.allocator,
613 decl.src,
614 "unable to codegen: {}",
615 .{@errorName(err)},
616 ));
617 decl.analysis = .codegen_failure_retryable;
618 },
619 };
620 },
621 },
622 };
623}
624
625fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
626 switch (root_scope.status) {
627 .never_loaded, .unloaded_success => {
628 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
629
630 var keep_source = false;
631 const source = try self.root_pkg.root_src_dir.readFileAllocOptions(
632 self.allocator,
633 self.root_pkg.root_src_path,
634 std.math.maxInt(u32),
635 1,
636 0,
637 );
638 defer if (!keep_source) self.allocator.free(source);
639
640 var keep_zir_module = false;
641 const zir_module = try self.allocator.create(zir.Module);
642 defer if (!keep_zir_module) self.allocator.destroy(zir_module);
643
644 zir_module.* = try zir.parse(self.allocator, source);
645 defer if (!keep_zir_module) zir_module.deinit(self.allocator);
646
647 if (zir_module.error_msg) |src_err_msg| {
648 self.failed_files.putAssumeCapacityNoClobber(
649 root_scope,
650 try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
651 );
652 root_scope.status = .loaded_parse_failure;
653 root_scope.source = .{ .bytes = source };
654 keep_source = true;
655 return error.AnalysisFail;
656 }
657
658 root_scope.status = .loaded_success;
659 root_scope.source = .{ .bytes = source };
660 keep_source = true;
661 root_scope.contents = .{ .module = zir_module };
662 keep_zir_module = true;
663
664 return zir_module;
665 },
666
667 .unloaded_parse_failure,
668 .unloaded_sema_failure,
669 .loaded_parse_failure,
670 .loaded_sema_failure,
671 => return error.AnalysisFail,
672 .loaded_success => return root_scope.contents.module,
673 }
674}
675
676fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
677 // TODO use the cache to identify, from the modified source files, the decls which have
678 // changed based on the span of memory that represents the decl in the re-parsed source file.
679 // Use the cached dependency graph to recursively determine the set of decls which need
680 // regeneration.
681 // Here we simulate adding a source file which was previously not part of the compilation,
682 // which means scanning the decls looking for exports.
683 // TODO also identify decls that need to be deleted.
684 switch (root_scope.status) {
685 .never_loaded => {
686 const src_module = try self.getSrcModule(root_scope);
687
688 // Here we ensure enough queue capacity to store all the decls, so that later we can use
689 // appendAssumeCapacity.
690 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
691
692 for (src_module.decls) |decl| {
693 if (decl.cast(zir.Inst.Export)) |export_inst| {
694 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
695 }
696 }
697 },
698
699 .unloaded_parse_failure,
700 .unloaded_sema_failure,
701 .loaded_parse_failure,
702 .loaded_sema_failure,
703 .loaded_success,
704 .unloaded_success,
705 => {
706 const src_module = try self.getSrcModule(root_scope);
707
708 // Look for changed decls.
709 for (src_module.decls) |src_decl| {
710 const name_hash = Decl.hashSimpleName(src_decl.name);
711 if (self.decl_table.get(name_hash)) |kv| {
712 const decl = kv.value;
713 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
714 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
715 // TODO recursive dependency management
716 std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});
717 self.decl_table.removeAssertDiscard(name_hash);
718 const saved_link = decl.link;
719 decl.destroy(self.allocator);
720 if (self.export_owners.getValue(decl)) |exports| {
721 @panic("TODO handle updating a decl that does an export");
722 }
723 const new_decl = self.resolveDecl(
724 &root_scope.base,
725 src_decl,
726 saved_link,
727 ) catch |err| switch (err) {
728 error.OutOfMemory => return error.OutOfMemory,
729 error.AnalysisFail => continue,
730 };
731 if (self.decl_exports.remove(decl)) |entry| {
732 self.decl_exports.putAssumeCapacityNoClobber(new_decl, entry.value);
733 }
734 }
735 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
736 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
737 }
738 }
739 },
740 }
741}
742
743fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
744 // Use the Decl's arena for function memory.
745 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
746 defer decl.typed_value.most_recent.arena.?.* = arena.state;
747 var analysis: Fn.Analysis = .{
748 .inner_block = .{
749 .func = func,
750 .decl = decl,
751 .instructions = .{},
752 .arena = &arena.allocator,
753 },
754 .needed_inst_capacity = 0,
755 .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator),
756 };
757 defer analysis.inner_block.instructions.deinit(self.allocator);
758 defer analysis.inst_table.deinit();
759
760 const fn_inst = func.analysis.queued;
761 func.analysis = .{ .in_progress = &analysis };
762
763 try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body);
764
765 func.analysis = .{
766 .success = .{
767 .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items),
768 },
769 };
770}
771
772fn resolveDecl(
773 self: *Module,
774 scope: *Scope,
775 old_inst: *zir.Inst,
776 bin_file_link: link.ElfFile.Decl,
777) InnerError!*Decl {
778 const hash = Decl.hashSimpleName(old_inst.name);
779 if (self.decl_table.get(hash)) |kv| {
780 return kv.value;
781 } else {
782 const new_decl = blk: {
783 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
784 const new_decl = try self.allocator.create(Decl);
785 errdefer self.allocator.destroy(new_decl);
786 const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
787 errdefer self.allocator.free(name);
788 new_decl.* = .{
789 .name = name,
790 .scope = scope.namespace(),
791 .src = old_inst.src,
792 .typed_value = .{ .never_succeeded = {} },
793 .analysis = .initial_in_progress,
794 .contents_hash = Decl.hashSimpleName(old_inst.contents),
795 .link = bin_file_link,
796 };
797 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
798 break :blk new_decl;
799 };
800
801 var decl_scope: Scope.DeclAnalysis = .{
802 .decl = new_decl,
803 .arena = std.heap.ArenaAllocator.init(self.allocator),
804 };
805 errdefer decl_scope.arena.deinit();
806
807 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
808 error.OutOfMemory => return error.OutOfMemory,
809 error.AnalysisFail => {
810 switch (new_decl.analysis) {
811 .initial_in_progress => new_decl.analysis = .initial_dependency_failure,
812 .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure,
813 else => {},
814 }
815 return error.AnalysisFail;
816 },
817 };
818 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
819
820 const has_codegen_bits = typed_value.ty.hasCodeGenBits();
821 if (has_codegen_bits) {
822 // We don't fully codegen the decl until later, but we do need to reserve a global
823 // offset table index for it. This allows us to codegen decls out of dependency order,
824 // increasing how many computations can be done in parallel.
825 try self.bin_file.allocateDeclIndexes(new_decl);
826 }
827
828 arena_state.* = decl_scope.arena.state;
829
830 new_decl.typed_value = .{
831 .most_recent = .{
832 .typed_value = typed_value,
833 .arena = arena_state,
834 },
835 };
836 new_decl.analysis = .complete;
837 if (has_codegen_bits) {
838 // We ensureCapacity when scanning for decls.
839 self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl });
840 }
841 return new_decl;
842 }
843}
844
845fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
846 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.Decl.empty);
847 switch (decl.analysis) {
848 .initial_in_progress => unreachable,
849 .repeat_in_progress => unreachable,
850 .initial_dependency_failure,
851 .repeat_dependency_failure,
852 .initial_sema_failure,
853 .repeat_sema_failure,
854 .codegen_failure,
855 .codegen_failure_retryable,
856 => return error.AnalysisFail,
857
858 .complete => return decl,
859 }
860}
861
862fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
863 if (scope.cast(Scope.Block)) |block| {
864 if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| {
865 return kv.value;
866 }
867 }
868
869 const decl = try self.resolveCompleteDecl(scope, old_inst);
870 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
871 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
872}
873
874fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
875 return scope.cast(Scope.Block) orelse
876 return self.fail(scope, src, "instruction illegal outside function body", .{});
877}
878
879fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
880 const new_inst = try self.resolveInst(scope, old_inst);
881 const val = try self.resolveConstValue(scope, new_inst);
882 return TypedValue{
883 .ty = new_inst.ty,
884 .val = val,
885 };
886}
887
888fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
889 return (try self.resolveDefinedValue(scope, base)) orelse
890 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
891}
892
893fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
894 if (base.value()) |val| {
895 if (val.isUndef()) {
896 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
897 }
898 return val;
899 }
900 return null;
901}
902
903fn resolveConstString(self: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
904 const new_inst = try self.resolveInst(scope, old_inst);
905 const wanted_type = Type.initTag(.const_slice_u8);
906 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
907 const val = try self.resolveConstValue(scope, coerced_inst);
908 return val.toAllocatedBytes(scope.arena());
909}
910
911fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
912 const new_inst = try self.resolveInst(scope, old_inst);
913 const wanted_type = Type.initTag(.@"type");
914 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
915 const val = try self.resolveConstValue(scope, coerced_inst);
916 return val.toType();
917}
918
919fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void {
920 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
921 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
922 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
923 const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
924 const typed_value = exported_decl.typed_value.most_recent.typed_value;
925 switch (typed_value.ty.zigTypeTag()) {
926 .Fn => {},
927 else => return self.fail(
928 scope,
929 export_inst.positionals.value.src,
930 "unable to export type '{}'",
931 .{typed_value.ty},
932 ),
933 }
934 const new_export = try self.allocator.create(Export);
935 errdefer self.allocator.destroy(new_export);
936
937 const owner_decl = scope.decl();
938
939 new_export.* = .{
940 .options = .{ .name = symbol_name },
941 .src = export_inst.base.src,
942 .link = .{},
943 .owner_decl = owner_decl,
944 .status = .in_progress,
945 };
946
947 // Add to export_owners table.
948 const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable;
949 if (!eo_gop.found_existing) {
950 eo_gop.kv.value = &[0]*Export{};
951 }
952 eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1);
953 eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export;
954 errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1);
955
956 // Add to exported_decl table.
957 const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable;
958 if (!de_gop.found_existing) {
959 de_gop.kv.value = &[0]*Export{};
960 }
961 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);
962 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
963 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
964
965 self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) {
966 error.OutOfMemory => return error.OutOfMemory,
967 else => {
968 try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);
969 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
970 self.allocator,
971 export_inst.base.src,
972 "unable to export: {}",
973 .{@errorName(err)},
974 ));
975 new_export.status = .failed_retryable;
976 },
977 };
978}
979
980/// TODO should not need the cast on the last parameter at the callsites
981fn addNewInstArgs(
982 self: *Module,
983 block: *Scope.Block,
984 src: usize,
985 ty: Type,
986 comptime T: type,
987 args: Inst.Args(T),
988) !*Inst {
989 const inst = try self.addNewInst(block, src, ty, T);
990 inst.args = args;
991 return &inst.base;
992}
993
994fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
995 const inst = try block.arena.create(T);
996 inst.* = .{
997 .base = .{
998 .tag = T.base_tag,
999 .ty = ty,
1000 .src = src,
1001 },
1002 .args = undefined,
1003 };
1004 try block.instructions.append(self.allocator, &inst.base);
1005 return inst;
1006}
1007
1008fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
1009 const const_inst = try scope.arena().create(Inst.Constant);
1010 const_inst.* = .{
1011 .base = .{
1012 .tag = Inst.Constant.base_tag,
1013 .ty = typed_value.ty,
1014 .src = src,
1015 },
1016 .val = typed_value.val,
1017 };
1018 return &const_inst.base;
1019}
1020
1021fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst {
1022 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
1023 ty_payload.* = .{ .len = str.len };
1024
1025 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
1026 bytes_payload.* = .{ .data = str };
1027
1028 return self.constInst(scope, src, .{
1029 .ty = Type.initPayload(&ty_payload.base),
1030 .val = Value.initPayload(&bytes_payload.base),
1031 });
1032}
1033
1034fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1035 return self.constInst(scope, src, .{
1036 .ty = Type.initTag(.type),
1037 .val = try ty.toValue(scope.arena()),
1038 });
1039}
1040
1041fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
1042 return self.constInst(scope, src, .{
1043 .ty = Type.initTag(.void),
1044 .val = Value.initTag(.the_one_possible_value),
1045 });
1046}
1047
1048fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1049 return self.constInst(scope, src, .{
1050 .ty = ty,
1051 .val = Value.initTag(.undef),
1052 });
1053}
1054
1055fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
1056 return self.constInst(scope, src, .{
1057 .ty = Type.initTag(.bool),
1058 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
1059 });
1060}
1061
1062fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
1063 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
1064 int_payload.* = .{ .int = int };
1065
1066 return self.constInst(scope, src, .{
1067 .ty = ty,
1068 .val = Value.initPayload(&int_payload.base),
1069 });
1070}
1071
1072fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
1073 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
1074 int_payload.* = .{ .int = int };
1075
1076 return self.constInst(scope, src, .{
1077 .ty = ty,
1078 .val = Value.initPayload(&int_payload.base),
1079 });
1080}
1081
1082fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
1083 const val_payload = if (big_int.positive) blk: {
1084 if (big_int.to(u64)) |x| {
1085 return self.constIntUnsigned(scope, src, ty, x);
1086 } else |err| switch (err) {
1087 error.NegativeIntoUnsigned => unreachable,
1088 error.TargetTooSmall => {}, // handled below
1089 }
1090 const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1091 big_int_payload.* = .{ .limbs = big_int.limbs };
1092 break :blk &big_int_payload.base;
1093 } else blk: {
1094 if (big_int.to(i64)) |x| {
1095 return self.constIntSigned(scope, src, ty, x);
1096 } else |err| switch (err) {
1097 error.NegativeIntoUnsigned => unreachable,
1098 error.TargetTooSmall => {}, // handled below
1099 }
1100 const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
1101 big_int_payload.* = .{ .limbs = big_int.limbs };
1102 break :blk &big_int_payload.base;
1103 };
1104
1105 return self.constInst(scope, src, .{
1106 .ty = ty,
1107 .val = Value.initPayload(val_payload),
1108 });
1109}
1110
1111fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
1112 const new_inst = try self.analyzeInst(scope, old_inst);
1113 return TypedValue{
1114 .ty = new_inst.ty,
1115 .val = try self.resolveConstValue(scope, new_inst),
1116 };
1117}
1118
1119fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
1120 switch (old_inst.tag) {
1121 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
1122 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
1123 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
1124 .str => {
1125 const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;
1126 // The bytes references memory inside the ZIR module, which can get deallocated
1127 // after semantic analysis is complete. We need the memory to be in the Decl's arena.
1128 const arena_bytes = try scope.arena().dupe(u8, bytes);
1129 return self.constStr(scope, old_inst.src, arena_bytes);
1130 },
1131 .int => {
1132 const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int;
1133 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
1134 },
1135 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(zir.Inst.PtrToInt).?),
1136 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(zir.Inst.FieldPtr).?),
1137 .deref => return self.analyzeInstDeref(scope, old_inst.cast(zir.Inst.Deref).?),
1138 .as => return self.analyzeInstAs(scope, old_inst.cast(zir.Inst.As).?),
1139 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?),
1140 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?),
1141 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?),
1142 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?),
1143 .@"export" => {
1144 try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?);
1145 return self.constVoid(scope, old_inst.src);
1146 },
1147 .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?),
1148 .ref => return self.analyzeInstRef(scope, old_inst.cast(zir.Inst.Ref).?),
1149 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?),
1150 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?),
1151 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),
1152 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),
1153 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),
1154 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),
1155 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
1156 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
1157 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),
1158 }
1159}
1160
1161fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
1162 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1163 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
1164}
1165
1166fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst {
1167 const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand);
1168 return self.analyzeDeclRef(scope, inst.base.src, decl);
1169}
1170
1171fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
1172 const decl_name = try self.resolveConstString(scope, inst.positionals.name);
1173 // This will need to get more fleshed out when there are proper structs & namespaces.
1174 const zir_module = scope.namespace();
1175 for (zir_module.contents.module.decls) |src_decl| {
1176 if (mem.eql(u8, src_decl.name, decl_name)) {
1177 const decl = try self.resolveCompleteDecl(scope, src_decl);
1178 return self.analyzeDeclRef(scope, inst.base.src, decl);
1179 }
1180 }
1181 return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name});
1182}
1183
1184fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
1185 const decl_tv = try decl.typedValue();
1186 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1187 ty_payload.* = .{ .pointee_type = decl_tv.ty };
1188 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
1189 val_payload.* = .{ .decl = decl };
1190 return self.constInst(scope, src, .{
1191 .ty = Type.initPayload(&ty_payload.base),
1192 .val = Value.initPayload(&val_payload.base),
1193 });
1194}
1195
1196fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
1197 const func = try self.resolveInst(scope, inst.positionals.func);
1198 if (func.ty.zigTypeTag() != .Fn)
1199 return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
1200
1201 const cc = func.ty.fnCallingConvention();
1202 if (cc == .Naked) {
1203 // TODO add error note: declared here
1204 return self.fail(
1205 scope,
1206 inst.positionals.func.src,
1207 "unable to call function with naked calling convention",
1208 .{},
1209 );
1210 }
1211 const call_params_len = inst.positionals.args.len;
1212 const fn_params_len = func.ty.fnParamLen();
1213 if (func.ty.fnIsVarArgs()) {
1214 if (call_params_len < fn_params_len) {
1215 // TODO add error note: declared here
1216 return self.fail(
1217 scope,
1218 inst.positionals.func.src,
1219 "expected at least {} arguments, found {}",
1220 .{ fn_params_len, call_params_len },
1221 );
1222 }
1223 return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
1224 } else if (fn_params_len != call_params_len) {
1225 // TODO add error note: declared here
1226 return self.fail(
1227 scope,
1228 inst.positionals.func.src,
1229 "expected {} arguments, found {}",
1230 .{ fn_params_len, call_params_len },
1231 );
1232 }
1233
1234 if (inst.kw_args.modifier == .compile_time) {
1235 return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
1236 }
1237 if (inst.kw_args.modifier != .auto) {
1238 return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
1239 }
1240
1241 // TODO handle function calls of generic functions
1242
1243 const fn_param_types = try self.allocator.alloc(Type, fn_params_len);
1244 defer self.allocator.free(fn_param_types);
1245 func.ty.fnParamTypes(fn_param_types);
1246
1247 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
1248 for (inst.positionals.args) |src_arg, i| {
1249 const uncasted_arg = try self.resolveInst(scope, src_arg);
1250 casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg);
1251 }
1252
1253 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1254 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){
1255 .func = func,
1256 .args = casted_args,
1257 });
1258}
1259
1260fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
1261 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
1262 const new_func = try scope.arena().create(Fn);
1263 new_func.* = .{
1264 .fn_type = fn_type,
1265 .analysis = .{ .queued = fn_inst },
1266 };
1267 const fn_payload = try scope.arena().create(Value.Payload.Function);
1268 fn_payload.* = .{ .func = new_func };
1269 return self.constInst(scope, fn_inst.base.src, .{
1270 .ty = fn_type,
1271 .val = Value.initPayload(&fn_payload.base),
1272 });
1273}
1274
1275fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
1276 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
1277
1278 if (return_type.zigTypeTag() == .NoReturn and
1279 fntype.positionals.param_types.len == 0 and
1280 fntype.kw_args.cc == .Unspecified)
1281 {
1282 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
1283 }
1284
1285 if (return_type.zigTypeTag() == .NoReturn and
1286 fntype.positionals.param_types.len == 0 and
1287 fntype.kw_args.cc == .Naked)
1288 {
1289 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
1290 }
1291
1292 if (return_type.zigTypeTag() == .Void and
1293 fntype.positionals.param_types.len == 0 and
1294 fntype.kw_args.cc == .C)
1295 {
1296 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
1297 }
1298
1299 return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{});
1300}
1301
1302fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
1303 return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType());
1304}
1305
1306fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst {
1307 const dest_type = try self.resolveType(scope, as.positionals.dest_type);
1308 const new_inst = try self.resolveInst(scope, as.positionals.value);
1309 return self.coerce(scope, dest_type, new_inst);
1310}
1311
1312fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {
1313 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);
1314 if (ptr.ty.zigTypeTag() != .Pointer) {
1315 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
1316 }
1317 // TODO handle known-pointer-address
1318 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
1319 const ty = Type.initTag(.usize);
1320 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
1321}
1322
1323fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
1324 const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr);
1325 const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name);
1326
1327 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
1328 .Pointer => object_ptr.ty.elemType(),
1329 else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
1330 };
1331 switch (elem_ty.zigTypeTag()) {
1332 .Array => {
1333 if (mem.eql(u8, field_name, "len")) {
1334 const len_payload = try scope.arena().create(Value.Payload.Int_u64);
1335 len_payload.* = .{ .int = elem_ty.arrayLen() };
1336
1337 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
1338 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
1339
1340 return self.constInst(scope, fieldptr.base.src, .{
1341 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
1342 .val = Value.initPayload(&ref_payload.base),
1343 });
1344 } else {
1345 return self.fail(
1346 scope,
1347 fieldptr.positionals.field_name.src,
1348 "no member named '{}' in '{}'",
1349 .{ field_name, elem_ty },
1350 );
1351 }
1352 },
1353 else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
1354 }
1355}
1356
1357fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *zir.Inst.IntCast) InnerError!*Inst {
1358 const dest_type = try self.resolveType(scope, intcast.positionals.dest_type);
1359 const new_inst = try self.resolveInst(scope, intcast.positionals.value);
1360
1361 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
1362 .ComptimeInt => true,
1363 .Int => false,
1364 else => return self.fail(
1365 scope,
1366 intcast.positionals.dest_type.src,
1367 "expected integer type, found '{}'",
1368 .{
1369 dest_type,
1370 },
1371 ),
1372 };
1373
1374 switch (new_inst.ty.zigTypeTag()) {
1375 .ComptimeInt, .Int => {},
1376 else => return self.fail(
1377 scope,
1378 intcast.positionals.value.src,
1379 "expected integer type, found '{}'",
1380 .{new_inst.ty},
1381 ),
1382 }
1383
1384 if (dest_is_comptime_int or new_inst.value() != null) {
1385 return self.coerce(scope, dest_type, new_inst);
1386 }
1387
1388 return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{});
1389}
1390
1391fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst {
1392 const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
1393 const operand = try self.resolveInst(scope, inst.positionals.operand);
1394 return self.bitcast(scope, dest_type, operand);
1395}
1396
1397fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
1398 const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
1399 const uncasted_index = try self.resolveInst(scope, inst.positionals.index);
1400 const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index);
1401
1402 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
1403 if (array_ptr.value()) |array_ptr_val| {
1404 if (elem_index.value()) |index_val| {
1405 // Both array pointer and index are compile-time known.
1406 const index_u64 = index_val.toUnsignedInt();
1407 // @intCast here because it would have been impossible to construct a value that
1408 // required a larger index.
1409 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
1410
1411 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1412 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
1413
1414 return self.constInst(scope, inst.base.src, .{
1415 .ty = Type.initPayload(&type_payload.base),
1416 .val = elem_ptr,
1417 });
1418 }
1419 }
1420 }
1421
1422 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
1423}
1424
1425fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {
1426 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1427 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
1428
1429 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
1430 if (lhs.value()) |lhs_val| {
1431 if (rhs.value()) |rhs_val| {
1432 // TODO is this a performance issue? maybe we should try the operation without
1433 // resorting to BigInt first.
1434 var lhs_space: Value.BigIntSpace = undefined;
1435 var rhs_space: Value.BigIntSpace = undefined;
1436 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
1437 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
1438 const limbs = try scope.arena().alloc(
1439 std.math.big.Limb,
1440 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1441 );
1442 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1443 result_bigint.add(lhs_bigint, rhs_bigint);
1444 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1445
1446 if (!lhs.ty.eql(rhs.ty)) {
1447 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
1448 }
1449
1450 const val_payload = if (result_bigint.positive) blk: {
1451 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1452 val_payload.* = .{ .limbs = result_limbs };
1453 break :blk &val_payload.base;
1454 } else blk: {
1455 const val_payload = try scope.arena().create(Value.Payload.IntBigNegative);
1456 val_payload.* = .{ .limbs = result_limbs };
1457 break :blk &val_payload.base;
1458 };
1459
1460 return self.constInst(scope, inst.base.src, .{
1461 .ty = lhs.ty,
1462 .val = Value.initPayload(val_payload),
1463 });
1464 }
1465 }
1466 }
1467
1468 return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{});
1469}
1470
1471fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.Deref) InnerError!*Inst {
1472 const ptr = try self.resolveInst(scope, deref.positionals.ptr);
1473 return self.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.ptr.src);
1474}
1475
1476fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
1477 const elem_ty = switch (ptr.ty.zigTypeTag()) {
1478 .Pointer => ptr.ty.elemType(),
1479 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
1480 };
1481 if (ptr.value()) |val| {
1482 return self.constInst(scope, src, .{
1483 .ty = elem_ty,
1484 .val = try val.pointerDeref(scope.arena()),
1485 });
1486 }
1487
1488 return self.fail(scope, src, "TODO implement runtime deref", .{});
1489}
1490
1491fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
1492 const return_type = try self.resolveType(scope, assembly.positionals.return_type);
1493 const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source);
1494 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null;
1495
1496 const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
1497 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
1498 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
1499
1500 for (inputs) |*elem, i| {
1501 elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]);
1502 }
1503 for (clobbers) |*elem, i| {
1504 elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]);
1505 }
1506 for (args) |*elem, i| {
1507 const arg = try self.resolveInst(scope, assembly.kw_args.args[i]);
1508 elem.* = try self.coerce(scope, Type.initTag(.usize), arg);
1509 }
1510
1511 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
1512 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
1513 .asm_source = asm_source,
1514 .is_volatile = assembly.kw_args.@"volatile",
1515 .output = output,
1516 .inputs = inputs,
1517 .clobbers = clobbers,
1518 .args = args,
1519 });
1520}
1521
1522fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {
1523 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1524 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
1525 const op = inst.positionals.op;
1526
1527 const is_equality_cmp = switch (op) {
1528 .eq, .neq => true,
1529 else => false,
1530 };
1531 const lhs_ty_tag = lhs.ty.zigTypeTag();
1532 const rhs_ty_tag = rhs.ty.zigTypeTag();
1533 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
1534 // null == null, null != null
1535 return self.constBool(scope, inst.base.src, op == .eq);
1536 } else if (is_equality_cmp and
1537 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
1538 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
1539 {
1540 // comparing null with optionals
1541 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
1542 if (opt_operand.value()) |opt_val| {
1543 const is_null = opt_val.isNull();
1544 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
1545 }
1546 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1547 switch (op) {
1548 .eq => return self.addNewInstArgs(
1549 b,
1550 inst.base.src,
1551 Type.initTag(.bool),
1552 Inst.IsNull,
1553 Inst.Args(Inst.IsNull){ .operand = opt_operand },
1554 ),
1555 .neq => return self.addNewInstArgs(
1556 b,
1557 inst.base.src,
1558 Type.initTag(.bool),
1559 Inst.IsNonNull,
1560 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
1561 ),
1562 else => unreachable,
1563 }
1564 } else if (is_equality_cmp and
1565 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
1566 {
1567 return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
1568 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
1569 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
1570 return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
1571 } else if (is_equality_cmp and
1572 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
1573 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
1574 {
1575 return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
1576 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
1577 if (!is_equality_cmp) {
1578 return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
1579 }
1580 return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
1581 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
1582 // This operation allows any combination of integer and float types, regardless of the
1583 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
1584 // numeric types.
1585 return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
1586 }
1587 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
1588}
1589
1590fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
1591 const operand = try self.resolveInst(scope, inst.positionals.operand);
1592 return self.analyzeIsNull(scope, inst.base.src, operand, true);
1593}
1594
1595fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNonNull) InnerError!*Inst {
1596 const operand = try self.resolveInst(scope, inst.positionals.operand);
1597 return self.analyzeIsNull(scope, inst.base.src, operand, false);
1598}
1599
1600fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
1601 const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition);
1602 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
1603
1604 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
1605 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
1606 try self.analyzeBody(scope, body.*);
1607 return self.constVoid(scope, inst.base.src);
1608 }
1609
1610 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
1611
1612 var true_block: Scope.Block = .{
1613 .func = parent_block.func,
1614 .decl = parent_block.decl,
1615 .instructions = .{},
1616 .arena = parent_block.arena,
1617 };
1618 defer true_block.instructions.deinit(self.allocator);
1619 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
1620
1621 var false_block: Scope.Block = .{
1622 .func = parent_block.func,
1623 .decl = parent_block.decl,
1624 .instructions = .{},
1625 .arena = parent_block.arena,
1626 };
1627 defer false_block.instructions.deinit(self.allocator);
1628 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
1629
1630 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
1631 .condition = cond,
1632 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
1633 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
1634 });
1635}
1636
1637fn wantSafety(self: *Module, scope: *Scope) bool {
1638 return switch (self.optimize_mode) {
1639 .Debug => true,
1640 .ReleaseSafe => true,
1641 .ReleaseFast => false,
1642 .ReleaseSmall => false,
1643 };
1644}
1645
1646fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unreachable) InnerError!*Inst {
1647 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
1648 if (self.wantSafety(scope)) {
1649 // TODO Once we have a panic function to call, call it here instead of this.
1650 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
1651 }
1652 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
1653}
1654
1655fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
1656 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1657 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
1658}
1659
1660fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
1661 if (scope.cast(Scope.Block)) |b| {
1662 const analysis = b.func.analysis.in_progress;
1663 analysis.needed_inst_capacity += body.instructions.len;
1664 try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity);
1665 for (body.instructions) |src_inst| {
1666 const new_inst = try self.analyzeInst(scope, src_inst);
1667 analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst);
1668 }
1669 } else {
1670 for (body.instructions) |src_inst| {
1671 _ = try self.analyzeInst(scope, src_inst);
1672 }
1673 }
1674}
1675
1676fn analyzeIsNull(
1677 self: *Module,
1678 scope: *Scope,
1679 src: usize,
1680 operand: *Inst,
1681 invert_logic: bool,
1682) InnerError!*Inst {
1683 return self.fail(scope, src, "TODO implement analysis of isnull and isnotnull", .{});
1684}
1685
1686/// Asserts that lhs and rhs types are both numeric.
1687fn cmpNumeric(
1688 self: *Module,
1689 scope: *Scope,
1690 src: usize,
1691 lhs: *Inst,
1692 rhs: *Inst,
1693 op: std.math.CompareOperator,
1694) !*Inst {
1695 assert(lhs.ty.isNumeric());
1696 assert(rhs.ty.isNumeric());
1697
1698 const lhs_ty_tag = lhs.ty.zigTypeTag();
1699 const rhs_ty_tag = rhs.ty.zigTypeTag();
1700
1701 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
1702 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
1703 return self.fail(scope, src, "vector length mismatch: {} and {}", .{
1704 lhs.ty.arrayLen(),
1705 rhs.ty.arrayLen(),
1706 });
1707 }
1708 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
1709 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
1710 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
1711 lhs.ty,
1712 rhs.ty,
1713 });
1714 }
1715
1716 if (lhs.value()) |lhs_val| {
1717 if (rhs.value()) |rhs_val| {
1718 return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
1719 }
1720 }
1721
1722 // TODO handle comparisons against lazy zero values
1723 // Some values can be compared against zero without being runtime known or without forcing
1724 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
1725 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
1726 // of this function if we don't need to.
1727
1728 // It must be a runtime comparison.
1729 const b = try self.requireRuntimeBlock(scope, src);
1730 // For floats, emit a float comparison instruction.
1731 const lhs_is_float = switch (lhs_ty_tag) {
1732 .Float, .ComptimeFloat => true,
1733 else => false,
1734 };
1735 const rhs_is_float = switch (rhs_ty_tag) {
1736 .Float, .ComptimeFloat => true,
1737 else => false,
1738 };
1739 if (lhs_is_float and rhs_is_float) {
1740 // Implicit cast the smaller one to the larger one.
1741 const dest_type = x: {
1742 if (lhs_ty_tag == .ComptimeFloat) {
1743 break :x rhs.ty;
1744 } else if (rhs_ty_tag == .ComptimeFloat) {
1745 break :x lhs.ty;
1746 }
1747 if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
1748 break :x lhs.ty;
1749 } else {
1750 break :x rhs.ty;
1751 }
1752 };
1753 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1754 const casted_rhs = try self.coerce(scope, dest_type, rhs);
1755 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1756 .lhs = casted_lhs,
1757 .rhs = casted_rhs,
1758 .op = op,
1759 });
1760 }
1761 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
1762 // For mixed signed and unsigned integers, implicit cast both operands to a signed
1763 // integer with + 1 bit.
1764 // For mixed floats and integers, extract the integer part from the float, cast that to
1765 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
1766 // add/subtract 1.
1767 const lhs_is_signed = if (lhs.value()) |lhs_val|
1768 lhs_val.compareWithZero(.lt)
1769 else
1770 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
1771 const rhs_is_signed = if (rhs.value()) |rhs_val|
1772 rhs_val.compareWithZero(.lt)
1773 else
1774 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
1775 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
1776
1777 var dest_float_type: ?Type = null;
1778
1779 var lhs_bits: usize = undefined;
1780 if (lhs.value()) |lhs_val| {
1781 if (lhs_val.isUndef())
1782 return self.constUndef(scope, src, Type.initTag(.bool));
1783 const is_unsigned = if (lhs_is_float) x: {
1784 var bigint_space: Value.BigIntSpace = undefined;
1785 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1786 defer bigint.deinit();
1787 const zcmp = lhs_val.orderAgainstZero();
1788 if (lhs_val.floatHasFraction()) {
1789 switch (op) {
1790 .eq => return self.constBool(scope, src, false),
1791 .neq => return self.constBool(scope, src, true),
1792 else => {},
1793 }
1794 if (zcmp == .lt) {
1795 try bigint.addScalar(bigint.toConst(), -1);
1796 } else {
1797 try bigint.addScalar(bigint.toConst(), 1);
1798 }
1799 }
1800 lhs_bits = bigint.toConst().bitCountTwosComp();
1801 break :x (zcmp != .lt);
1802 } else x: {
1803 lhs_bits = lhs_val.intBitCountTwosComp();
1804 break :x (lhs_val.orderAgainstZero() != .lt);
1805 };
1806 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1807 } else if (lhs_is_float) {
1808 dest_float_type = lhs.ty;
1809 } else {
1810 const int_info = lhs.ty.intInfo(self.target());
1811 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1812 }
1813
1814 var rhs_bits: usize = undefined;
1815 if (rhs.value()) |rhs_val| {
1816 if (rhs_val.isUndef())
1817 return self.constUndef(scope, src, Type.initTag(.bool));
1818 const is_unsigned = if (rhs_is_float) x: {
1819 var bigint_space: Value.BigIntSpace = undefined;
1820 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1821 defer bigint.deinit();
1822 const zcmp = rhs_val.orderAgainstZero();
1823 if (rhs_val.floatHasFraction()) {
1824 switch (op) {
1825 .eq => return self.constBool(scope, src, false),
1826 .neq => return self.constBool(scope, src, true),
1827 else => {},
1828 }
1829 if (zcmp == .lt) {
1830 try bigint.addScalar(bigint.toConst(), -1);
1831 } else {
1832 try bigint.addScalar(bigint.toConst(), 1);
1833 }
1834 }
1835 rhs_bits = bigint.toConst().bitCountTwosComp();
1836 break :x (zcmp != .lt);
1837 } else x: {
1838 rhs_bits = rhs_val.intBitCountTwosComp();
1839 break :x (rhs_val.orderAgainstZero() != .lt);
1840 };
1841 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1842 } else if (rhs_is_float) {
1843 dest_float_type = rhs.ty;
1844 } else {
1845 const int_info = rhs.ty.intInfo(self.target());
1846 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1847 }
1848
1849 const dest_type = if (dest_float_type) |ft| ft else blk: {
1850 const max_bits = std.math.max(lhs_bits, rhs_bits);
1851 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
1852 error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
1853 };
1854 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
1855 };
1856 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1857 const casted_rhs = try self.coerce(scope, dest_type, lhs);
1858
1859 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1860 .lhs = casted_lhs,
1861 .rhs = casted_rhs,
1862 .op = op,
1863 });
1864}
1865
1866fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
1867 if (signed) {
1868 const int_payload = try scope.arena().create(Type.Payload.IntSigned);
1869 int_payload.* = .{ .bits = bits };
1870 return Type.initPayload(&int_payload.base);
1871 } else {
1872 const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
1873 int_payload.* = .{ .bits = bits };
1874 return Type.initPayload(&int_payload.base);
1875 }
1876}
1877
1878fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
1879 // If the types are the same, we can return the operand.
1880 if (dest_type.eql(inst.ty))
1881 return inst;
1882
1883 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
1884 if (in_memory_result == .ok) {
1885 return self.bitcast(scope, dest_type, inst);
1886 }
1887
1888 // *[N]T to []T
1889 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
1890 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
1891 {
1892 const array_type = inst.ty.elemType();
1893 const dst_elem_type = dest_type.elemType();
1894 if (array_type.zigTypeTag() == .Array and
1895 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
1896 {
1897 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
1898 }
1899 }
1900
1901 // comptime_int to fixed-width integer
1902 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
1903 // The representation is already correct; we only need to make sure it fits in the destination type.
1904 const val = inst.value().?; // comptime_int always has comptime known value
1905 if (!val.intFitsInType(dest_type, self.target())) {
1906 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
1907 }
1908 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1909 }
1910
1911 // integer widening
1912 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
1913 const src_info = inst.ty.intInfo(self.target());
1914 const dst_info = dest_type.intInfo(self.target());
1915 if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
1916 if (inst.value()) |val| {
1917 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1918 } else {
1919 return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{});
1920 }
1921 } else {
1922 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
1923 }
1924 }
1925
1926 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
1927}
1928
1929fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
1930 if (inst.value()) |val| {
1931 // Keep the comptime Value representation; take the new type.
1932 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1933 }
1934 // TODO validate the type size and other compile errors
1935 const b = try self.requireRuntimeBlock(scope, inst.src);
1936 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
1937}
1938
1939fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
1940 if (inst.value()) |val| {
1941 // The comptime Value representation is compatible with both types.
1942 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
1943 }
1944 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
1945}
1946
1947fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError {
1948 @setCold(true);
1949 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
1950 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
1951 const err_msg = try ErrorMsg.create(self.allocator, src, format, args);
1952 switch (scope.tag) {
1953 .decl => {
1954 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
1955 switch (decl.analysis) {
1956 .initial_in_progress => decl.analysis = .initial_sema_failure,
1957 .repeat_in_progress => decl.analysis = .repeat_sema_failure,
1958 else => unreachable,
1959 }
1960 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
1961 },
1962 .block => {
1963 const block = scope.cast(Scope.Block).?;
1964 block.func.analysis = .sema_failure;
1965 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
1966 },
1967 .zir_module => {
1968 const zir_module = scope.cast(Scope.ZIRModule).?;
1969 zir_module.status = .loaded_sema_failure;
1970 self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg);
1971 },
1972 }
1973 return error.AnalysisFail;
1974}
1975
1976const InMemoryCoercionResult = enum {
1977 ok,
1978 no_match,
1979};
1980
1981fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
1982 if (dest_type.eql(src_type))
1983 return .ok;
1984
1985 // TODO: implement more of this function
1986
1987 return .no_match;
1988}
1989
1990pub const ErrorMsg = struct {
1991 byte_offset: usize,
1992 msg: []const u8,
1993
1994 pub fn create(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !*ErrorMsg {
1995 const self = try allocator.create(ErrorMsg);
1996 errdefer allocator.destroy(self);
1997 self.* = try init(allocator, byte_offset, format, args);
1998 return self;
1999 }
2000
2001 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
2002 pub fn destroy(self: *ErrorMsg, allocator: *Allocator) void {
2003 self.deinit(allocator);
2004 allocator.destroy(self);
2005 }
2006
2007 pub fn init(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !ErrorMsg {
2008 return ErrorMsg{
2009 .byte_offset = byte_offset,
2010 .msg = try std.fmt.allocPrint(allocator, format, args),
2011 };
2012 }
2013
2014 pub fn deinit(self: *ErrorMsg, allocator: *Allocator) void {
2015 allocator.free(self.msg);
2016 self.* = undefined;
2017 }
2018};
src-self-hosted/codegen.zig+10-8
...@@ -6,6 +6,8 @@ const Type = @import("type.zig").Type;...@@ -6,6 +6,8 @@ const Type = @import("type.zig").Type;
6const Value = @import("value.zig").Value;6const Value = @import("value.zig").Value;
7const TypedValue = @import("TypedValue.zig");7const TypedValue = @import("TypedValue.zig");
8const link = @import("link.zig");8const link = @import("link.zig");
9const Module = @import("Module.zig");
10const ErrorMsg = Module.ErrorMsg;
9const Target = std.Target;11const Target = std.Target;
10const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
1113
...@@ -14,7 +16,7 @@ pub const Result = union(enum) {...@@ -14,7 +16,7 @@ pub const Result = union(enum) {
14 appended: void,16 appended: void,
15 /// The value is available externally, `code` is unused.17 /// The value is available externally, `code` is unused.
16 externally_managed: []const u8,18 externally_managed: []const u8,
17 fail: *ir.ErrorMsg,19 fail: *Module.ErrorMsg,
18};20};
1921
20pub fn generateSymbol(22pub fn generateSymbol(
...@@ -77,7 +79,7 @@ pub fn generateSymbol(...@@ -77,7 +79,7 @@ pub fn generateSymbol(
77 }79 }
78 }80 }
79 return Result{81 return Result{
80 .fail = try ir.ErrorMsg.create(82 .fail = try ErrorMsg.create(
81 bin_file.allocator,83 bin_file.allocator,
82 src,84 src,
83 "TODO implement generateSymbol for more kinds of arrays",85 "TODO implement generateSymbol for more kinds of arrays",
...@@ -107,7 +109,7 @@ pub fn generateSymbol(...@@ -107,7 +109,7 @@ pub fn generateSymbol(
107 return Result{ .appended = {} };109 return Result{ .appended = {} };
108 }110 }
109 return Result{111 return Result{
110 .fail = try ir.ErrorMsg.create(112 .fail = try ErrorMsg.create(
111 bin_file.allocator,113 bin_file.allocator,
112 src,114 src,
113 "TODO implement generateSymbol for pointer {}",115 "TODO implement generateSymbol for pointer {}",
...@@ -123,7 +125,7 @@ pub fn generateSymbol(...@@ -123,7 +125,7 @@ pub fn generateSymbol(
123 return Result{ .appended = {} };125 return Result{ .appended = {} };
124 }126 }
125 return Result{127 return Result{
126 .fail = try ir.ErrorMsg.create(128 .fail = try ErrorMsg.create(
127 bin_file.allocator,129 bin_file.allocator,
128 src,130 src,
129 "TODO implement generateSymbol for int type '{}'",131 "TODO implement generateSymbol for int type '{}'",
...@@ -133,7 +135,7 @@ pub fn generateSymbol(...@@ -133,7 +135,7 @@ pub fn generateSymbol(
133 },135 },
134 else => |t| {136 else => |t| {
135 return Result{137 return Result{
136 .fail = try ir.ErrorMsg.create(138 .fail = try ErrorMsg.create(
137 bin_file.allocator,139 bin_file.allocator,
138 src,140 src,
139 "TODO implement generateSymbol for type '{}'",141 "TODO implement generateSymbol for type '{}'",
...@@ -147,10 +149,10 @@ pub fn generateSymbol(...@@ -147,10 +149,10 @@ pub fn generateSymbol(
147const Function = struct {149const Function = struct {
148 bin_file: *link.ElfFile,150 bin_file: *link.ElfFile,
149 target: *const std.Target,151 target: *const std.Target,
150 mod_fn: *const ir.Module.Fn,152 mod_fn: *const Module.Fn,
151 code: *std.ArrayList(u8),153 code: *std.ArrayList(u8),
152 inst_table: std.AutoHashMap(*ir.Inst, MCValue),154 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
153 err_msg: ?*ir.ErrorMsg,155 err_msg: ?*ErrorMsg,
154156
155 const MCValue = union(enum) {157 const MCValue = union(enum) {
156 none,158 none,
...@@ -570,7 +572,7 @@ const Function = struct {...@@ -570,7 +572,7 @@ const Function = struct {
570 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {572 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
571 @setCold(true);573 @setCold(true);
572 assert(self.err_msg == null);574 assert(self.err_msg == null);
573 self.err_msg = try ir.ErrorMsg.create(self.code.allocator, src, format, args);575 self.err_msg = try ErrorMsg.create(self.code.allocator, src, format, args);
574 return error.CodegenFail;576 return error.CodegenFail;
575 }577 }
576};578};
src-self-hosted/ir.zig+2-2016
...@@ -1,20 +1,9 @@...@@ -1,20 +1,9 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const ArrayListUnmanaged = std.ArrayListUnmanaged;
5const Value = @import("value.zig").Value;2const Value = @import("value.zig").Value;
6const Type = @import("type.zig").Type;3const Type = @import("type.zig").Type;
7const TypedValue = @import("TypedValue.zig");4const Module = @import("Module.zig");
8const assert = std.debug.assert;
9const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;
11const Target = std.Target;
12const Package = @import("Package.zig");
13const link = @import("link.zig");
145
15pub const text = @import("ir/text.zig");6/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
16
17/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
18/// of instructions that correspond to the ZIR text format.7/// of instructions that correspond to the ZIR text format.
19/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,8/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
20/// so are the `Value` and `Type`. The value of a constant must be copied into9/// so are the `Value` and `Type`. The value of a constant must be copied into
...@@ -166,2006 +155,3 @@ pub const Inst = struct {...@@ -166,2006 +155,3 @@ pub const Inst = struct {
166 args: void,155 args: void,
167 };156 };
168};157};
169
170pub const Module = struct {
171 /// General-purpose allocator.
172 allocator: *Allocator,
173 /// Module owns this resource.
174 root_pkg: *Package,
175 /// Module owns this resource.
176 root_scope: *Scope.ZIRModule,
177 /// Pointer to externally managed resource.
178 bin_file: *link.ElfFile,
179 /// It's rare for a decl to be exported, so we save memory by having a sparse map of
180 /// Decl pointers to details about them being exported.
181 /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
182 decl_exports: std.AutoHashMap(*Decl, []*Export),
183 /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
184 /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
185 /// is performing the export of another Decl.
186 /// This table owns the Export memory.
187 export_owners: std.AutoHashMap(*Decl, []*Export),
188 /// Maps fully qualified namespaced names to the Decl struct for them.
189 decl_table: std.AutoHashMap(Decl.Hash, *Decl),
190
191 optimize_mode: std.builtin.Mode,
192 link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
193
194 work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
195
196 /// We optimize memory usage for a compilation with no compile errors by storing the
197 /// error messages and mapping outside of `Decl`.
198 /// The ErrorMsg memory is owned by the decl, using Module's allocator.
199 /// Note that a Decl can succeed but the Fn it represents can fail. In this case,
200 /// a Decl can have a failed_decls entry but have analysis status of success.
201 failed_decls: std.AutoHashMap(*Decl, *ErrorMsg),
202 /// Using a map here for consistency with the other fields here.
203 /// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator.
204 failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
205 /// Using a map here for consistency with the other fields here.
206 /// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
207 failed_exports: std.AutoHashMap(*Export, *ErrorMsg),
208
209 pub const WorkItem = union(enum) {
210 /// Write the machine code for a Decl to the output file.
211 codegen_decl: *Decl,
212 };
213
214 pub const Export = struct {
215 options: std.builtin.ExportOptions,
216 /// Byte offset into the file that contains the export directive.
217 src: usize,
218 /// Represents the position of the export, if any, in the output file.
219 link: link.ElfFile.Export,
220 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
221 owner_decl: *Decl,
222 status: enum {
223 in_progress,
224 failed,
225 /// Indicates that the failure was due to a temporary issue, such as an I/O error
226 /// when writing to the output file. Retrying the export may succeed.
227 failed_retryable,
228 complete,
229 },
230 };
231
232 pub const Decl = struct {
233 /// This name is relative to the containing namespace of the decl. It uses a null-termination
234 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
235 /// in symbol names, because executable file formats use null-terminated strings for symbol names.
236 /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
237 /// mapping them to an address in the output file.
238 /// Memory owned by this decl, using Module's allocator.
239 name: [*:0]const u8,
240 /// The direct parent container of the Decl. This field will need to get more fleshed out when
241 /// self-hosted supports proper struct types and Zig AST => ZIR.
242 /// Reference to externally owned memory.
243 scope: *Scope.ZIRModule,
244 /// Byte offset into the source file that contains this declaration.
245 /// This is the base offset that src offsets within this Decl are relative to.
246 src: usize,
247 /// The most recent value of the Decl after a successful semantic analysis.
248 /// The tag for this union is determined by the tag value of the analysis field.
249 typed_value: union {
250 never_succeeded: void,
251 most_recent: TypedValue.Managed,
252 },
253 /// Represents the "shallow" analysis status. For example, for decls that are functions,
254 /// the function type is analyzed with this set to `in_progress`, however, the semantic
255 /// analysis of the function body is performed with this value set to `success`. Functions
256 /// have their own analysis status field.
257 analysis: enum {
258 initial_in_progress,
259 /// This Decl might be OK but it depends on another one which did not successfully complete
260 /// semantic analysis. This Decl never had a value computed.
261 initial_dependency_failure,
262 /// Semantic analysis failure. This Decl never had a value computed.
263 /// There will be a corresponding ErrorMsg in Module.failed_decls.
264 initial_sema_failure,
265 /// In this case the `typed_value.most_recent` can still be accessed.
266 /// There will be a corresponding ErrorMsg in Module.failed_decls.
267 codegen_failure,
268 /// In this case the `typed_value.most_recent` can still be accessed.
269 /// There will be a corresponding ErrorMsg in Module.failed_decls.
270 /// This indicates the failure was something like running out of disk space,
271 /// and attempting codegen again may succeed.
272 codegen_failure_retryable,
273 /// This Decl might be OK but it depends on another one which did not successfully complete
274 /// semantic analysis. There is a most recent value available.
275 repeat_dependency_failure,
276 /// Semantic anlaysis failure, but the `typed_value.most_recent` can be accessed.
277 /// There will be a corresponding ErrorMsg in Module.failed_decls.
278 repeat_sema_failure,
279 /// Completed successfully before; the `typed_value.most_recent` can be accessed, and
280 /// new semantic analysis is in progress.
281 repeat_in_progress,
282 /// Everything is done and updated.
283 complete,
284 },
285
286 /// Represents the position of the code in the output file.
287 /// This is populated regardless of semantic analysis and code generation.
288 link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
289
290 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
291 /// typed_value is modified.
292 /// TODO look into using a lightweight map/set data structure rather than a linear array.
293 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
294
295 contents_hash: Hash,
296
297 pub fn destroy(self: *Decl, allocator: *Allocator) void {
298 allocator.free(mem.spanZ(self.name));
299 if (self.typedValueManaged()) |tvm| {
300 tvm.deinit(allocator);
301 }
302 allocator.destroy(self);
303 }
304
305 pub const Hash = [16]u8;
306
307 /// If the name is small enough, it is used directly as the hash.
308 /// If it is long, blake3 hash is computed.
309 pub fn hashSimpleName(name: []const u8) Hash {
310 var out: Hash = undefined;
311 if (name.len <= Hash.len) {
312 mem.copy(u8, &out, name);
313 mem.set(u8, out[name.len..], 0);
314 } else {
315 std.crypto.Blake3.hash(name, &out);
316 }
317 return out;
318 }
319
320 /// Must generate unique bytes with no collisions with other decls.
321 /// The point of hashing here is only to limit the number of bytes of
322 /// the unique identifier to a fixed size (16 bytes).
323 pub fn fullyQualifiedNameHash(self: Decl) Hash {
324 // Right now we only have ZIRModule as the source. So this is simply the
325 // relative name of the decl.
326 return hashSimpleName(mem.spanZ(u8, self.name));
327 }
328
329 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
330 const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
331 return tvm.typed_value;
332 }
333
334 pub fn value(self: *Decl) error{AnalysisFail}!Value {
335 return (try self.typedValue()).val;
336 }
337
338 pub fn dump(self: *Decl) void {
339 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
340 std.debug.warn("{}:{}:{} name={} status={}", .{
341 self.scope.sub_file_path,
342 loc.line + 1,
343 loc.column + 1,
344 mem.spanZ(self.name),
345 @tagName(self.analysis),
346 });
347 if (self.typedValueManaged()) |tvm| {
348 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
349 }
350 std.debug.warn("\n", .{});
351 }
352
353 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
354 switch (self.analysis) {
355 .initial_in_progress,
356 .initial_dependency_failure,
357 .initial_sema_failure,
358 => return null,
359 .codegen_failure,
360 .codegen_failure_retryable,
361 .repeat_dependency_failure,
362 .repeat_sema_failure,
363 .repeat_in_progress,
364 .complete,
365 => return &self.typed_value.most_recent,
366 }
367 }
368 };
369
370 /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
371 pub const Fn = struct {
372 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
373 fn_type: Type,
374 analysis: union(enum) {
375 /// The value is the source instruction.
376 queued: *text.Inst.Fn,
377 in_progress: *Analysis,
378 /// There will be a corresponding ErrorMsg in Module.failed_decls
379 sema_failure,
380 /// This Fn might be OK but it depends on another Decl which did not successfully complete
381 /// semantic analysis.
382 dependency_failure,
383 success: Body,
384 },
385
386 /// This memory is temporary and points to stack memory for the duration
387 /// of Fn analysis.
388 pub const Analysis = struct {
389 inner_block: Scope.Block,
390 /// TODO Performance optimization idea: instead of this inst_table,
391 /// use a field in the text.Inst instead to track corresponding instructions
392 inst_table: std.AutoHashMap(*text.Inst, *Inst),
393 needed_inst_capacity: usize,
394 };
395 };
396
397 pub const Scope = struct {
398 tag: Tag,
399
400 pub fn cast(base: *Scope, comptime T: type) ?*T {
401 if (base.tag != T.base_tag)
402 return null;
403
404 return @fieldParentPtr(T, "base", base);
405 }
406
407 /// Asserts the scope has a parent which is a DeclAnalysis and
408 /// returns the arena Allocator.
409 pub fn arena(self: *Scope) *Allocator {
410 switch (self.tag) {
411 .block => return self.cast(Block).?.arena,
412 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
413 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
414 }
415 }
416
417 /// Asserts the scope has a parent which is a DeclAnalysis and
418 /// returns the Decl.
419 pub fn decl(self: *Scope) *Decl {
420 switch (self.tag) {
421 .block => return self.cast(Block).?.decl,
422 .decl => return self.cast(DeclAnalysis).?.decl,
423 .zir_module => unreachable,
424 }
425 }
426
427 /// Asserts the scope has a parent which is a ZIRModule and
428 /// returns it.
429 pub fn namespace(self: *Scope) *ZIRModule {
430 switch (self.tag) {
431 .block => return self.cast(Block).?.decl.scope,
432 .decl => return self.cast(DeclAnalysis).?.decl.scope,
433 .zir_module => return self.cast(ZIRModule).?,
434 }
435 }
436
437 pub fn dumpInst(self: *Scope, inst: *Inst) void {
438 const zir_module = self.namespace();
439 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
440 std.debug.warn("{}:{}:{}: {}: ty={}\n", .{
441 zir_module.sub_file_path,
442 loc.line + 1,
443 loc.column + 1,
444 @tagName(inst.tag),
445 inst.ty,
446 });
447 }
448
449 pub const Tag = enum {
450 zir_module,
451 block,
452 decl,
453 };
454
455 pub const ZIRModule = struct {
456 pub const base_tag: Tag = .zir_module;
457 base: Scope = Scope{ .tag = base_tag },
458 /// Relative to the owning package's root_src_dir.
459 /// Reference to external memory, not owned by ZIRModule.
460 sub_file_path: []const u8,
461 source: union {
462 unloaded: void,
463 bytes: [:0]const u8,
464 },
465 contents: union {
466 not_available: void,
467 module: *text.Module,
468 },
469 status: enum {
470 never_loaded,
471 unloaded_success,
472 unloaded_parse_failure,
473 unloaded_sema_failure,
474 loaded_parse_failure,
475 loaded_sema_failure,
476 loaded_success,
477 },
478
479 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {
480 switch (self.status) {
481 .never_loaded,
482 .unloaded_parse_failure,
483 .unloaded_sema_failure,
484 .unloaded_success,
485 => {},
486
487 .loaded_success => {
488 allocator.free(self.source.bytes);
489 self.contents.module.deinit(allocator);
490 allocator.destroy(self.contents.module);
491 self.status = .unloaded_success;
492 },
493 .loaded_sema_failure => {
494 allocator.free(self.source.bytes);
495 self.contents.module.deinit(allocator);
496 allocator.destroy(self.contents.module);
497 self.status = .unloaded_sema_failure;
498 },
499 .loaded_parse_failure => {
500 allocator.free(self.source.bytes);
501 self.status = .unloaded_parse_failure;
502 },
503 }
504 }
505
506 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
507 self.unload(allocator);
508 self.* = undefined;
509 }
510
511 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
512 const loc = std.zig.findLineColumn(self.source.bytes, src);
513 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
514 }
515 };
516
517 /// This is a temporary structure, references to it are valid only
518 /// during semantic analysis of the block.
519 pub const Block = struct {
520 pub const base_tag: Tag = .block;
521 base: Scope = Scope{ .tag = base_tag },
522 func: *Fn,
523 decl: *Decl,
524 instructions: ArrayListUnmanaged(*Inst),
525 /// Points to the arena allocator of DeclAnalysis
526 arena: *Allocator,
527 };
528
529 /// This is a temporary structure, references to it are valid only
530 /// during semantic analysis of the decl.
531 pub const DeclAnalysis = struct {
532 pub const base_tag: Tag = .decl;
533 base: Scope = Scope{ .tag = base_tag },
534 decl: *Decl,
535 arena: std.heap.ArenaAllocator,
536 };
537 };
538
539 pub const Body = struct {
540 instructions: []*Inst,
541 };
542
543 pub const AllErrors = struct {
544 arena: std.heap.ArenaAllocator.State,
545 list: []const Message,
546
547 pub const Message = struct {
548 src_path: []const u8,
549 line: usize,
550 column: usize,
551 byte_offset: usize,
552 msg: []const u8,
553 };
554
555 pub fn deinit(self: *AllErrors, allocator: *Allocator) void {
556 self.arena.promote(allocator).deinit();
557 }
558
559 fn add(
560 arena: *std.heap.ArenaAllocator,
561 errors: *std.ArrayList(Message),
562 sub_file_path: []const u8,
563 source: []const u8,
564 simple_err_msg: ErrorMsg,
565 ) !void {
566 const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
567 try errors.append(.{
568 .src_path = try arena.allocator.dupe(u8, sub_file_path),
569 .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
570 .byte_offset = simple_err_msg.byte_offset,
571 .line = loc.line,
572 .column = loc.column,
573 });
574 }
575 };
576
577 pub fn deinit(self: *Module) void {
578 const allocator = self.allocator;
579 self.work_queue.deinit();
580 {
581 var it = self.decl_table.iterator();
582 while (it.next()) |kv| {
583 kv.value.destroy(allocator);
584 }
585 self.decl_table.deinit();
586 }
587 {
588 var it = self.failed_decls.iterator();
589 while (it.next()) |kv| {
590 kv.value.destroy(allocator);
591 }
592 self.failed_decls.deinit();
593 }
594 {
595 var it = self.failed_files.iterator();
596 while (it.next()) |kv| {
597 kv.value.destroy(allocator);
598 }
599 self.failed_files.deinit();
600 }
601 {
602 var it = self.failed_exports.iterator();
603 while (it.next()) |kv| {
604 kv.value.destroy(allocator);
605 }
606 self.failed_exports.deinit();
607 }
608 {
609 var it = self.decl_exports.iterator();
610 while (it.next()) |kv| {
611 const export_list = kv.value;
612 allocator.free(export_list);
613 }
614 self.decl_exports.deinit();
615 }
616 {
617 var it = self.export_owners.iterator();
618 while (it.next()) |kv| {
619 const export_list = kv.value;
620 for (export_list) |exp| {
621 allocator.destroy(exp);
622 }
623 allocator.free(export_list);
624 }
625 self.export_owners.deinit();
626 }
627 self.root_pkg.destroy();
628 {
629 self.root_scope.deinit(allocator);
630 allocator.destroy(self.root_scope);
631 }
632 self.* = undefined;
633 }
634
635 pub fn target(self: Module) std.Target {
636 return self.bin_file.options.target;
637 }
638
639 /// Detect changes to source files, perform semantic analysis, and update the output files.
640 pub fn update(self: *Module) !void {
641 // TODO Use the cache hash file system to detect which source files changed.
642 // Here we simulate a full cache miss.
643 // Analyze the root source file now.
644 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
645 error.AnalysisFail => {
646 assert(self.totalErrorCount() != 0);
647 },
648 else => |e| return e,
649 };
650
651 try self.performAllTheWork();
652
653 // Unload all the source files from memory.
654 self.root_scope.unload(self.allocator);
655
656 try self.bin_file.flush();
657 self.link_error_flags = self.bin_file.error_flags;
658 }
659
660 pub fn totalErrorCount(self: *Module) usize {
661 return self.failed_decls.size +
662 self.failed_files.size +
663 self.failed_exports.size +
664 @boolToInt(self.link_error_flags.no_entry_point_found);
665 }
666
667 pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
668 var arena = std.heap.ArenaAllocator.init(self.allocator);
669 errdefer arena.deinit();
670
671 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
672 defer errors.deinit();
673
674 {
675 var it = self.failed_files.iterator();
676 while (it.next()) |kv| {
677 const scope = kv.key;
678 const err_msg = kv.value;
679 const source = scope.source.bytes;
680 try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*);
681 }
682 }
683 {
684 var it = self.failed_decls.iterator();
685 while (it.next()) |kv| {
686 const decl = kv.key;
687 const err_msg = kv.value;
688 const source = decl.scope.source.bytes;
689 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
690 }
691 }
692 {
693 var it = self.failed_exports.iterator();
694 while (it.next()) |kv| {
695 const decl = kv.key.owner_decl;
696 const err_msg = kv.value;
697 const source = decl.scope.source.bytes;
698 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
699 }
700 }
701
702 if (self.link_error_flags.no_entry_point_found) {
703 try errors.append(.{
704 .src_path = self.root_pkg.root_src_path,
705 .line = 0,
706 .column = 0,
707 .byte_offset = 0,
708 .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
709 });
710 }
711
712 assert(errors.items.len == self.totalErrorCount());
713
714 return AllErrors{
715 .arena = arena.state,
716 .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
717 };
718 }
719
720 const InnerError = error{ OutOfMemory, AnalysisFail };
721
722 pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
723 while (self.work_queue.readItem()) |work_item| switch (work_item) {
724 .codegen_decl => |decl| switch (decl.analysis) {
725 .initial_in_progress,
726 .repeat_in_progress,
727 => unreachable,
728
729 .initial_sema_failure,
730 .repeat_sema_failure,
731 .codegen_failure,
732 .initial_dependency_failure,
733 .repeat_dependency_failure,
734 => continue,
735
736 .complete, .codegen_failure_retryable => {
737 if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
738 switch (payload.func.analysis) {
739 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
740 error.AnalysisFail => {
741 if (payload.func.analysis == .queued) {
742 payload.func.analysis = .dependency_failure;
743 }
744 continue;
745 },
746 else => |e| return e,
747 },
748 .in_progress => unreachable,
749 .sema_failure, .dependency_failure => continue,
750 .success => {},
751 }
752 }
753
754 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
755
756 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
757 error.OutOfMemory => return error.OutOfMemory,
758 error.AnalysisFail => {
759 decl.analysis = .repeat_dependency_failure;
760 },
761 else => {
762 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
763 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
764 self.allocator,
765 decl.src,
766 "unable to codegen: {}",
767 .{@errorName(err)},
768 ));
769 decl.analysis = .codegen_failure_retryable;
770 },
771 };
772 },
773 },
774 };
775 }
776
777 fn getTextModule(self: *Module, root_scope: *Scope.ZIRModule) !*text.Module {
778 switch (root_scope.status) {
779 .never_loaded, .unloaded_success => {
780 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
781
782 var keep_source = false;
783 const source = try self.root_pkg.root_src_dir.readFileAllocOptions(
784 self.allocator,
785 self.root_pkg.root_src_path,
786 std.math.maxInt(u32),
787 1,
788 0,
789 );
790 defer if (!keep_source) self.allocator.free(source);
791
792 var keep_zir_module = false;
793 const zir_module = try self.allocator.create(text.Module);
794 defer if (!keep_zir_module) self.allocator.destroy(zir_module);
795
796 zir_module.* = try text.parse(self.allocator, source);
797 defer if (!keep_zir_module) zir_module.deinit(self.allocator);
798
799 if (zir_module.error_msg) |src_err_msg| {
800 self.failed_files.putAssumeCapacityNoClobber(
801 root_scope,
802 try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
803 );
804 root_scope.status = .loaded_parse_failure;
805 root_scope.source = .{ .bytes = source };
806 keep_source = true;
807 return error.AnalysisFail;
808 }
809
810 root_scope.status = .loaded_success;
811 root_scope.source = .{ .bytes = source };
812 keep_source = true;
813 root_scope.contents = .{ .module = zir_module };
814 keep_zir_module = true;
815
816 return zir_module;
817 },
818
819 .unloaded_parse_failure,
820 .unloaded_sema_failure,
821 .loaded_parse_failure,
822 .loaded_sema_failure,
823 => return error.AnalysisFail,
824 .loaded_success => return root_scope.contents.module,
825 }
826 }
827
828 fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
829 // TODO use the cache to identify, from the modified source files, the decls which have
830 // changed based on the span of memory that represents the decl in the re-parsed source file.
831 // Use the cached dependency graph to recursively determine the set of decls which need
832 // regeneration.
833 // Here we simulate adding a source file which was previously not part of the compilation,
834 // which means scanning the decls looking for exports.
835 // TODO also identify decls that need to be deleted.
836 switch (root_scope.status) {
837 .never_loaded => {
838 const src_module = try self.getTextModule(root_scope);
839
840 // Here we ensure enough queue capacity to store all the decls, so that later we can use
841 // appendAssumeCapacity.
842 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
843
844 for (src_module.decls) |decl| {
845 if (decl.cast(text.Inst.Export)) |export_inst| {
846 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
847 }
848 }
849 },
850
851 .unloaded_parse_failure,
852 .unloaded_sema_failure,
853 .loaded_parse_failure,
854 .loaded_sema_failure,
855 .loaded_success,
856 .unloaded_success,
857 => {
858 const src_module = try self.getTextModule(root_scope);
859
860 // Look for changed decls.
861 for (src_module.decls) |src_decl| {
862 const name_hash = Decl.hashSimpleName(src_decl.name);
863 if (self.decl_table.get(name_hash)) |kv| {
864 const decl = kv.value;
865 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
866 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
867 // TODO recursive dependency management
868 std.debug.warn("noticed that '{}' changed\n", .{src_decl.name});
869 self.decl_table.removeAssertDiscard(name_hash);
870 const saved_link = decl.link;
871 decl.destroy(self.allocator);
872 if (self.export_owners.getValue(decl)) |exports| {
873 @panic("TODO handle updating a decl that does an export");
874 }
875 const new_decl = self.resolveDecl(
876 &root_scope.base,
877 src_decl,
878 saved_link,
879 ) catch |err| switch (err) {
880 error.OutOfMemory => return error.OutOfMemory,
881 error.AnalysisFail => continue,
882 };
883 if (self.decl_exports.remove(decl)) |entry| {
884 self.decl_exports.putAssumeCapacityNoClobber(new_decl, entry.value);
885 }
886 }
887 } else if (src_decl.cast(text.Inst.Export)) |export_inst| {
888 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
889 }
890 }
891 },
892 }
893 }
894
895 fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
896 // Use the Decl's arena for function memory.
897 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
898 defer decl.typed_value.most_recent.arena.?.* = arena.state;
899 var analysis: Fn.Analysis = .{
900 .inner_block = .{
901 .func = func,
902 .decl = decl,
903 .instructions = .{},
904 .arena = &arena.allocator,
905 },
906 .needed_inst_capacity = 0,
907 .inst_table = std.AutoHashMap(*text.Inst, *Inst).init(self.allocator),
908 };
909 defer analysis.inner_block.instructions.deinit(self.allocator);
910 defer analysis.inst_table.deinit();
911
912 const fn_inst = func.analysis.queued;
913 func.analysis = .{ .in_progress = &analysis };
914
915 try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body);
916
917 func.analysis = .{
918 .success = .{
919 .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items),
920 },
921 };
922 }
923
924 fn resolveDecl(
925 self: *Module,
926 scope: *Scope,
927 old_inst: *text.Inst,
928 bin_file_link: link.ElfFile.Decl,
929 ) InnerError!*Decl {
930 const hash = Decl.hashSimpleName(old_inst.name);
931 if (self.decl_table.get(hash)) |kv| {
932 return kv.value;
933 } else {
934 const new_decl = blk: {
935 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
936 const new_decl = try self.allocator.create(Decl);
937 errdefer self.allocator.destroy(new_decl);
938 const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
939 errdefer self.allocator.free(name);
940 new_decl.* = .{
941 .name = name,
942 .scope = scope.namespace(),
943 .src = old_inst.src,
944 .typed_value = .{ .never_succeeded = {} },
945 .analysis = .initial_in_progress,
946 .contents_hash = Decl.hashSimpleName(old_inst.contents),
947 .link = bin_file_link,
948 };
949 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
950 break :blk new_decl;
951 };
952
953 var decl_scope: Scope.DeclAnalysis = .{
954 .decl = new_decl,
955 .arena = std.heap.ArenaAllocator.init(self.allocator),
956 };
957 errdefer decl_scope.arena.deinit();
958
959 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
960 error.OutOfMemory => return error.OutOfMemory,
961 error.AnalysisFail => {
962 switch (new_decl.analysis) {
963 .initial_in_progress => new_decl.analysis = .initial_dependency_failure,
964 .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure,
965 else => {},
966 }
967 return error.AnalysisFail;
968 },
969 };
970 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
971
972 const has_codegen_bits = typed_value.ty.hasCodeGenBits();
973 if (has_codegen_bits) {
974 // We don't fully codegen the decl until later, but we do need to reserve a global
975 // offset table index for it. This allows us to codegen decls out of dependency order,
976 // increasing how many computations can be done in parallel.
977 try self.bin_file.allocateDeclIndexes(new_decl);
978 }
979
980 arena_state.* = decl_scope.arena.state;
981
982 new_decl.typed_value = .{
983 .most_recent = .{
984 .typed_value = typed_value,
985 .arena = arena_state,
986 },
987 };
988 new_decl.analysis = .complete;
989 if (has_codegen_bits) {
990 // We ensureCapacity when scanning for decls.
991 self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl });
992 }
993 return new_decl;
994 }
995 }
996
997 fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl {
998 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.Decl.empty);
999 switch (decl.analysis) {
1000 .initial_in_progress => unreachable,
1001 .repeat_in_progress => unreachable,
1002 .initial_dependency_failure,
1003 .repeat_dependency_failure,
1004 .initial_sema_failure,
1005 .repeat_sema_failure,
1006 .codegen_failure,
1007 .codegen_failure_retryable,
1008 => return error.AnalysisFail,
1009
1010 .complete => return decl,
1011 }
1012 }
1013
1014 fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {
1015 if (scope.cast(Scope.Block)) |block| {
1016 if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| {
1017 return kv.value;
1018 }
1019 }
1020
1021 const decl = try self.resolveCompleteDecl(scope, old_inst);
1022 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
1023 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
1024 }
1025
1026 fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
1027 return scope.cast(Scope.Block) orelse
1028 return self.fail(scope, src, "instruction illegal outside function body", .{});
1029 }
1030
1031 fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!TypedValue {
1032 const new_inst = try self.resolveInst(scope, old_inst);
1033 const val = try self.resolveConstValue(scope, new_inst);
1034 return TypedValue{
1035 .ty = new_inst.ty,
1036 .val = val,
1037 };
1038 }
1039
1040 fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
1041 return (try self.resolveDefinedValue(scope, base)) orelse
1042 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
1043 }
1044
1045 fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1046 if (base.value()) |val| {
1047 if (val.isUndef()) {
1048 return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
1049 }
1050 return val;
1051 }
1052 return null;
1053 }
1054
1055 fn resolveConstString(self: *Module, scope: *Scope, old_inst: *text.Inst) ![]u8 {
1056 const new_inst = try self.resolveInst(scope, old_inst);
1057 const wanted_type = Type.initTag(.const_slice_u8);
1058 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
1059 const val = try self.resolveConstValue(scope, coerced_inst);
1060 return val.toAllocatedBytes(scope.arena());
1061 }
1062
1063 fn resolveType(self: *Module, scope: *Scope, old_inst: *text.Inst) !Type {
1064 const new_inst = try self.resolveInst(scope, old_inst);
1065 const wanted_type = Type.initTag(.@"type");
1066 const coerced_inst = try self.coerce(scope, wanted_type, new_inst);
1067 const val = try self.resolveConstValue(scope, coerced_inst);
1068 return val.toType();
1069 }
1070
1071 fn analyzeExport(self: *Module, scope: *Scope, export_inst: *text.Inst.Export) InnerError!void {
1072 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
1073 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
1074 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
1075 const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
1076 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1077 switch (typed_value.ty.zigTypeTag()) {
1078 .Fn => {},
1079 else => return self.fail(
1080 scope,
1081 export_inst.positionals.value.src,
1082 "unable to export type '{}'",
1083 .{typed_value.ty},
1084 ),
1085 }
1086 const new_export = try self.allocator.create(Export);
1087 errdefer self.allocator.destroy(new_export);
1088
1089 const owner_decl = scope.decl();
1090
1091 new_export.* = .{
1092 .options = .{ .name = symbol_name },
1093 .src = export_inst.base.src,
1094 .link = .{},
1095 .owner_decl = owner_decl,
1096 .status = .in_progress,
1097 };
1098
1099 // Add to export_owners table.
1100 const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable;
1101 if (!eo_gop.found_existing) {
1102 eo_gop.kv.value = &[0]*Export{};
1103 }
1104 eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1);
1105 eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export;
1106 errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1);
1107
1108 // Add to exported_decl table.
1109 const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable;
1110 if (!de_gop.found_existing) {
1111 de_gop.kv.value = &[0]*Export{};
1112 }
1113 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);
1114 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
1115 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
1116
1117 self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) {
1118 error.OutOfMemory => return error.OutOfMemory,
1119 else => {
1120 try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);
1121 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
1122 self.allocator,
1123 export_inst.base.src,
1124 "unable to export: {}",
1125 .{@errorName(err)},
1126 ));
1127 new_export.status = .failed_retryable;
1128 },
1129 };
1130 }
1131
1132 /// TODO should not need the cast on the last parameter at the callsites
1133 fn addNewInstArgs(
1134 self: *Module,
1135 block: *Scope.Block,
1136 src: usize,
1137 ty: Type,
1138 comptime T: type,
1139 args: Inst.Args(T),
1140 ) !*Inst {
1141 const inst = try self.addNewInst(block, src, ty, T);
1142 inst.args = args;
1143 return &inst.base;
1144 }
1145
1146 fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
1147 const inst = try block.arena.create(T);
1148 inst.* = .{
1149 .base = .{
1150 .tag = T.base_tag,
1151 .ty = ty,
1152 .src = src,
1153 },
1154 .args = undefined,
1155 };
1156 try block.instructions.append(self.allocator, &inst.base);
1157 return inst;
1158 }
1159
1160 fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
1161 const const_inst = try scope.arena().create(Inst.Constant);
1162 const_inst.* = .{
1163 .base = .{
1164 .tag = Inst.Constant.base_tag,
1165 .ty = typed_value.ty,
1166 .src = src,
1167 },
1168 .val = typed_value.val,
1169 };
1170 return &const_inst.base;
1171 }
1172
1173 fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst {
1174 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
1175 ty_payload.* = .{ .len = str.len };
1176
1177 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
1178 bytes_payload.* = .{ .data = str };
1179
1180 return self.constInst(scope, src, .{
1181 .ty = Type.initPayload(&ty_payload.base),
1182 .val = Value.initPayload(&bytes_payload.base),
1183 });
1184 }
1185
1186 fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1187 return self.constInst(scope, src, .{
1188 .ty = Type.initTag(.type),
1189 .val = try ty.toValue(scope.arena()),
1190 });
1191 }
1192
1193 fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
1194 return self.constInst(scope, src, .{
1195 .ty = Type.initTag(.void),
1196 .val = Value.initTag(.the_one_possible_value),
1197 });
1198 }
1199
1200 fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1201 return self.constInst(scope, src, .{
1202 .ty = ty,
1203 .val = Value.initTag(.undef),
1204 });
1205 }
1206
1207 fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
1208 return self.constInst(scope, src, .{
1209 .ty = Type.initTag(.bool),
1210 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
1211 });
1212 }
1213
1214 fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
1215 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
1216 int_payload.* = .{ .int = int };
1217
1218 return self.constInst(scope, src, .{
1219 .ty = ty,
1220 .val = Value.initPayload(&int_payload.base),
1221 });
1222 }
1223
1224 fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
1225 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
1226 int_payload.* = .{ .int = int };
1227
1228 return self.constInst(scope, src, .{
1229 .ty = ty,
1230 .val = Value.initPayload(&int_payload.base),
1231 });
1232 }
1233
1234 fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
1235 const val_payload = if (big_int.positive) blk: {
1236 if (big_int.to(u64)) |x| {
1237 return self.constIntUnsigned(scope, src, ty, x);
1238 } else |err| switch (err) {
1239 error.NegativeIntoUnsigned => unreachable,
1240 error.TargetTooSmall => {}, // handled below
1241 }
1242 const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1243 big_int_payload.* = .{ .limbs = big_int.limbs };
1244 break :blk &big_int_payload.base;
1245 } else blk: {
1246 if (big_int.to(i64)) |x| {
1247 return self.constIntSigned(scope, src, ty, x);
1248 } else |err| switch (err) {
1249 error.NegativeIntoUnsigned => unreachable,
1250 error.TargetTooSmall => {}, // handled below
1251 }
1252 const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
1253 big_int_payload.* = .{ .limbs = big_int.limbs };
1254 break :blk &big_int_payload.base;
1255 };
1256
1257 return self.constInst(scope, src, .{
1258 .ty = ty,
1259 .val = Value.initPayload(val_payload),
1260 });
1261 }
1262
1263 fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!TypedValue {
1264 const new_inst = try self.analyzeInst(scope, old_inst);
1265 return TypedValue{
1266 .ty = new_inst.ty,
1267 .val = try self.resolveConstValue(scope, new_inst),
1268 };
1269 }
1270
1271 fn analyzeInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {
1272 switch (old_inst.tag) {
1273 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(text.Inst.Breakpoint).?),
1274 .call => return self.analyzeInstCall(scope, old_inst.cast(text.Inst.Call).?),
1275 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(text.Inst.DeclRef).?),
1276 .str => {
1277 const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;
1278 // The bytes references memory inside the ZIR text module, which can get deallocated
1279 // after semantic analysis is complete. We need the memory to be in the Decl's arena.
1280 const arena_bytes = try scope.arena().dupe(u8, bytes);
1281 return self.constStr(scope, old_inst.src, arena_bytes);
1282 },
1283 .int => {
1284 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
1285 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
1286 },
1287 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(text.Inst.PtrToInt).?),
1288 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(text.Inst.FieldPtr).?),
1289 .deref => return self.analyzeInstDeref(scope, old_inst.cast(text.Inst.Deref).?),
1290 .as => return self.analyzeInstAs(scope, old_inst.cast(text.Inst.As).?),
1291 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(text.Inst.Asm).?),
1292 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(text.Inst.Unreachable).?),
1293 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(text.Inst.Return).?),
1294 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(text.Inst.Fn).?),
1295 .@"export" => {
1296 try self.analyzeExport(scope, old_inst.cast(text.Inst.Export).?);
1297 return self.constVoid(scope, old_inst.src);
1298 },
1299 .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(text.Inst.Primitive).?),
1300 .ref => return self.analyzeInstRef(scope, old_inst.cast(text.Inst.Ref).?),
1301 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(text.Inst.FnType).?),
1302 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(text.Inst.IntCast).?),
1303 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(text.Inst.BitCast).?),
1304 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(text.Inst.ElemPtr).?),
1305 .add => return self.analyzeInstAdd(scope, old_inst.cast(text.Inst.Add).?),
1306 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(text.Inst.Cmp).?),
1307 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(text.Inst.CondBr).?),
1308 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(text.Inst.IsNull).?),
1309 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(text.Inst.IsNonNull).?),
1310 }
1311 }
1312
1313 fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *text.Inst.Breakpoint) InnerError!*Inst {
1314 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1315 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
1316 }
1317
1318 fn analyzeInstRef(self: *Module, scope: *Scope, inst: *text.Inst.Ref) InnerError!*Inst {
1319 const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand);
1320 return self.analyzeDeclRef(scope, inst.base.src, decl);
1321 }
1322
1323 fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *text.Inst.DeclRef) InnerError!*Inst {
1324 const decl_name = try self.resolveConstString(scope, inst.positionals.name);
1325 // This will need to get more fleshed out when there are proper structs & namespaces.
1326 const zir_module = scope.namespace();
1327 for (zir_module.contents.module.decls) |src_decl| {
1328 if (mem.eql(u8, src_decl.name, decl_name)) {
1329 const decl = try self.resolveCompleteDecl(scope, src_decl);
1330 return self.analyzeDeclRef(scope, inst.base.src, decl);
1331 }
1332 }
1333 return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name});
1334 }
1335
1336 fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
1337 const decl_tv = try decl.typedValue();
1338 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1339 ty_payload.* = .{ .pointee_type = decl_tv.ty };
1340 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
1341 val_payload.* = .{ .decl = decl };
1342 return self.constInst(scope, src, .{
1343 .ty = Type.initPayload(&ty_payload.base),
1344 .val = Value.initPayload(&val_payload.base),
1345 });
1346 }
1347
1348 fn analyzeInstCall(self: *Module, scope: *Scope, inst: *text.Inst.Call) InnerError!*Inst {
1349 const func = try self.resolveInst(scope, inst.positionals.func);
1350 if (func.ty.zigTypeTag() != .Fn)
1351 return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
1352
1353 const cc = func.ty.fnCallingConvention();
1354 if (cc == .Naked) {
1355 // TODO add error note: declared here
1356 return self.fail(
1357 scope,
1358 inst.positionals.func.src,
1359 "unable to call function with naked calling convention",
1360 .{},
1361 );
1362 }
1363 const call_params_len = inst.positionals.args.len;
1364 const fn_params_len = func.ty.fnParamLen();
1365 if (func.ty.fnIsVarArgs()) {
1366 if (call_params_len < fn_params_len) {
1367 // TODO add error note: declared here
1368 return self.fail(
1369 scope,
1370 inst.positionals.func.src,
1371 "expected at least {} arguments, found {}",
1372 .{ fn_params_len, call_params_len },
1373 );
1374 }
1375 return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
1376 } else if (fn_params_len != call_params_len) {
1377 // TODO add error note: declared here
1378 return self.fail(
1379 scope,
1380 inst.positionals.func.src,
1381 "expected {} arguments, found {}",
1382 .{ fn_params_len, call_params_len },
1383 );
1384 }
1385
1386 if (inst.kw_args.modifier == .compile_time) {
1387 return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
1388 }
1389 if (inst.kw_args.modifier != .auto) {
1390 return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
1391 }
1392
1393 // TODO handle function calls of generic functions
1394
1395 const fn_param_types = try self.allocator.alloc(Type, fn_params_len);
1396 defer self.allocator.free(fn_param_types);
1397 func.ty.fnParamTypes(fn_param_types);
1398
1399 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
1400 for (inst.positionals.args) |src_arg, i| {
1401 const uncasted_arg = try self.resolveInst(scope, src_arg);
1402 casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg);
1403 }
1404
1405 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1406 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){
1407 .func = func,
1408 .args = casted_args,
1409 });
1410 }
1411
1412 fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *text.Inst.Fn) InnerError!*Inst {
1413 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
1414 const new_func = try scope.arena().create(Fn);
1415 new_func.* = .{
1416 .fn_type = fn_type,
1417 .analysis = .{ .queued = fn_inst },
1418 };
1419 const fn_payload = try scope.arena().create(Value.Payload.Function);
1420 fn_payload.* = .{ .func = new_func };
1421 return self.constInst(scope, fn_inst.base.src, .{
1422 .ty = fn_type,
1423 .val = Value.initPayload(&fn_payload.base),
1424 });
1425 }
1426
1427 fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *text.Inst.FnType) InnerError!*Inst {
1428 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
1429
1430 if (return_type.zigTypeTag() == .NoReturn and
1431 fntype.positionals.param_types.len == 0 and
1432 fntype.kw_args.cc == .Unspecified)
1433 {
1434 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
1435 }
1436
1437 if (return_type.zigTypeTag() == .NoReturn and
1438 fntype.positionals.param_types.len == 0 and
1439 fntype.kw_args.cc == .Naked)
1440 {
1441 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
1442 }
1443
1444 if (return_type.zigTypeTag() == .Void and
1445 fntype.positionals.param_types.len == 0 and
1446 fntype.kw_args.cc == .C)
1447 {
1448 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
1449 }
1450
1451 return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{});
1452 }
1453
1454 fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *text.Inst.Primitive) InnerError!*Inst {
1455 return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType());
1456 }
1457
1458 fn analyzeInstAs(self: *Module, scope: *Scope, as: *text.Inst.As) InnerError!*Inst {
1459 const dest_type = try self.resolveType(scope, as.positionals.dest_type);
1460 const new_inst = try self.resolveInst(scope, as.positionals.value);
1461 return self.coerce(scope, dest_type, new_inst);
1462 }
1463
1464 fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
1465 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);
1466 if (ptr.ty.zigTypeTag() != .Pointer) {
1467 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
1468 }
1469 // TODO handle known-pointer-address
1470 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
1471 const ty = Type.initTag(.usize);
1472 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
1473 }
1474
1475 fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
1476 const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr);
1477 const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name);
1478
1479 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
1480 .Pointer => object_ptr.ty.elemType(),
1481 else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
1482 };
1483 switch (elem_ty.zigTypeTag()) {
1484 .Array => {
1485 if (mem.eql(u8, field_name, "len")) {
1486 const len_payload = try scope.arena().create(Value.Payload.Int_u64);
1487 len_payload.* = .{ .int = elem_ty.arrayLen() };
1488
1489 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
1490 ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
1491
1492 return self.constInst(scope, fieldptr.base.src, .{
1493 .ty = Type.initTag(.single_const_pointer_to_comptime_int),
1494 .val = Value.initPayload(&ref_payload.base),
1495 });
1496 } else {
1497 return self.fail(
1498 scope,
1499 fieldptr.positionals.field_name.src,
1500 "no member named '{}' in '{}'",
1501 .{ field_name, elem_ty },
1502 );
1503 }
1504 },
1505 else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
1506 }
1507 }
1508
1509 fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *text.Inst.IntCast) InnerError!*Inst {
1510 const dest_type = try self.resolveType(scope, intcast.positionals.dest_type);
1511 const new_inst = try self.resolveInst(scope, intcast.positionals.value);
1512
1513 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
1514 .ComptimeInt => true,
1515 .Int => false,
1516 else => return self.fail(
1517 scope,
1518 intcast.positionals.dest_type.src,
1519 "expected integer type, found '{}'",
1520 .{
1521 dest_type,
1522 },
1523 ),
1524 };
1525
1526 switch (new_inst.ty.zigTypeTag()) {
1527 .ComptimeInt, .Int => {},
1528 else => return self.fail(
1529 scope,
1530 intcast.positionals.value.src,
1531 "expected integer type, found '{}'",
1532 .{new_inst.ty},
1533 ),
1534 }
1535
1536 if (dest_is_comptime_int or new_inst.value() != null) {
1537 return self.coerce(scope, dest_type, new_inst);
1538 }
1539
1540 return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{});
1541 }
1542
1543 fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *text.Inst.BitCast) InnerError!*Inst {
1544 const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
1545 const operand = try self.resolveInst(scope, inst.positionals.operand);
1546 return self.bitcast(scope, dest_type, operand);
1547 }
1548
1549 fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *text.Inst.ElemPtr) InnerError!*Inst {
1550 const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr);
1551 const uncasted_index = try self.resolveInst(scope, inst.positionals.index);
1552 const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index);
1553
1554 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
1555 if (array_ptr.value()) |array_ptr_val| {
1556 if (elem_index.value()) |index_val| {
1557 // Both array pointer and index are compile-time known.
1558 const index_u64 = index_val.toUnsignedInt();
1559 // @intCast here because it would have been impossible to construct a value that
1560 // required a larger index.
1561 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
1562
1563 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1564 type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() };
1565
1566 return self.constInst(scope, inst.base.src, .{
1567 .ty = Type.initPayload(&type_payload.base),
1568 .val = elem_ptr,
1569 });
1570 }
1571 }
1572 }
1573
1574 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
1575 }
1576
1577 fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *text.Inst.Add) InnerError!*Inst {
1578 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1579 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
1580
1581 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
1582 if (lhs.value()) |lhs_val| {
1583 if (rhs.value()) |rhs_val| {
1584 // TODO is this a performance issue? maybe we should try the operation without
1585 // resorting to BigInt first.
1586 var lhs_space: Value.BigIntSpace = undefined;
1587 var rhs_space: Value.BigIntSpace = undefined;
1588 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
1589 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
1590 const limbs = try scope.arena().alloc(
1591 std.math.big.Limb,
1592 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1593 );
1594 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1595 result_bigint.add(lhs_bigint, rhs_bigint);
1596 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1597
1598 if (!lhs.ty.eql(rhs.ty)) {
1599 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
1600 }
1601
1602 const val_payload = if (result_bigint.positive) blk: {
1603 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1604 val_payload.* = .{ .limbs = result_limbs };
1605 break :blk &val_payload.base;
1606 } else blk: {
1607 const val_payload = try scope.arena().create(Value.Payload.IntBigNegative);
1608 val_payload.* = .{ .limbs = result_limbs };
1609 break :blk &val_payload.base;
1610 };
1611
1612 return self.constInst(scope, inst.base.src, .{
1613 .ty = lhs.ty,
1614 .val = Value.initPayload(val_payload),
1615 });
1616 }
1617 }
1618 }
1619
1620 return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{});
1621 }
1622
1623 fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *text.Inst.Deref) InnerError!*Inst {
1624 const ptr = try self.resolveInst(scope, deref.positionals.ptr);
1625 return self.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.ptr.src);
1626 }
1627
1628 fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
1629 const elem_ty = switch (ptr.ty.zigTypeTag()) {
1630 .Pointer => ptr.ty.elemType(),
1631 else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
1632 };
1633 if (ptr.value()) |val| {
1634 return self.constInst(scope, src, .{
1635 .ty = elem_ty,
1636 .val = try val.pointerDeref(scope.arena()),
1637 });
1638 }
1639
1640 return self.fail(scope, src, "TODO implement runtime deref", .{});
1641 }
1642
1643 fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *text.Inst.Asm) InnerError!*Inst {
1644 const return_type = try self.resolveType(scope, assembly.positionals.return_type);
1645 const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source);
1646 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null;
1647
1648 const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
1649 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
1650 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
1651
1652 for (inputs) |*elem, i| {
1653 elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]);
1654 }
1655 for (clobbers) |*elem, i| {
1656 elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]);
1657 }
1658 for (args) |*elem, i| {
1659 const arg = try self.resolveInst(scope, assembly.kw_args.args[i]);
1660 elem.* = try self.coerce(scope, Type.initTag(.usize), arg);
1661 }
1662
1663 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
1664 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
1665 .asm_source = asm_source,
1666 .is_volatile = assembly.kw_args.@"volatile",
1667 .output = output,
1668 .inputs = inputs,
1669 .clobbers = clobbers,
1670 .args = args,
1671 });
1672 }
1673
1674 fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *text.Inst.Cmp) InnerError!*Inst {
1675 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1676 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
1677 const op = inst.positionals.op;
1678
1679 const is_equality_cmp = switch (op) {
1680 .eq, .neq => true,
1681 else => false,
1682 };
1683 const lhs_ty_tag = lhs.ty.zigTypeTag();
1684 const rhs_ty_tag = rhs.ty.zigTypeTag();
1685 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
1686 // null == null, null != null
1687 return self.constBool(scope, inst.base.src, op == .eq);
1688 } else if (is_equality_cmp and
1689 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
1690 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
1691 {
1692 // comparing null with optionals
1693 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
1694 if (opt_operand.value()) |opt_val| {
1695 const is_null = opt_val.isNull();
1696 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
1697 }
1698 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1699 switch (op) {
1700 .eq => return self.addNewInstArgs(
1701 b,
1702 inst.base.src,
1703 Type.initTag(.bool),
1704 Inst.IsNull,
1705 Inst.Args(Inst.IsNull){ .operand = opt_operand },
1706 ),
1707 .neq => return self.addNewInstArgs(
1708 b,
1709 inst.base.src,
1710 Type.initTag(.bool),
1711 Inst.IsNonNull,
1712 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
1713 ),
1714 else => unreachable,
1715 }
1716 } else if (is_equality_cmp and
1717 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
1718 {
1719 return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
1720 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
1721 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
1722 return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
1723 } else if (is_equality_cmp and
1724 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
1725 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
1726 {
1727 return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
1728 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
1729 if (!is_equality_cmp) {
1730 return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
1731 }
1732 return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
1733 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
1734 // This operation allows any combination of integer and float types, regardless of the
1735 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
1736 // numeric types.
1737 return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
1738 }
1739 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
1740 }
1741
1742 fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *text.Inst.IsNull) InnerError!*Inst {
1743 const operand = try self.resolveInst(scope, inst.positionals.operand);
1744 return self.analyzeIsNull(scope, inst.base.src, operand, true);
1745 }
1746
1747 fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *text.Inst.IsNonNull) InnerError!*Inst {
1748 const operand = try self.resolveInst(scope, inst.positionals.operand);
1749 return self.analyzeIsNull(scope, inst.base.src, operand, false);
1750 }
1751
1752 fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *text.Inst.CondBr) InnerError!*Inst {
1753 const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition);
1754 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
1755
1756 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
1757 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
1758 try self.analyzeBody(scope, body.*);
1759 return self.constVoid(scope, inst.base.src);
1760 }
1761
1762 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
1763
1764 var true_block: Scope.Block = .{
1765 .func = parent_block.func,
1766 .decl = parent_block.decl,
1767 .instructions = .{},
1768 .arena = parent_block.arena,
1769 };
1770 defer true_block.instructions.deinit(self.allocator);
1771 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
1772
1773 var false_block: Scope.Block = .{
1774 .func = parent_block.func,
1775 .decl = parent_block.decl,
1776 .instructions = .{},
1777 .arena = parent_block.arena,
1778 };
1779 defer false_block.instructions.deinit(self.allocator);
1780 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
1781
1782 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
1783 .condition = cond,
1784 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
1785 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
1786 });
1787 }
1788
1789 fn wantSafety(self: *Module, scope: *Scope) bool {
1790 return switch (self.optimize_mode) {
1791 .Debug => true,
1792 .ReleaseSafe => true,
1793 .ReleaseFast => false,
1794 .ReleaseSmall => false,
1795 };
1796 }
1797
1798 fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *text.Inst.Unreachable) InnerError!*Inst {
1799 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
1800 if (self.wantSafety(scope)) {
1801 // TODO Once we have a panic function to call, call it here instead of this.
1802 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
1803 }
1804 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
1805 }
1806
1807 fn analyzeInstRet(self: *Module, scope: *Scope, inst: *text.Inst.Return) InnerError!*Inst {
1808 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1809 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
1810 }
1811
1812 fn analyzeBody(self: *Module, scope: *Scope, body: text.Module.Body) !void {
1813 if (scope.cast(Scope.Block)) |b| {
1814 const analysis = b.func.analysis.in_progress;
1815 analysis.needed_inst_capacity += body.instructions.len;
1816 try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity);
1817 for (body.instructions) |src_inst| {
1818 const new_inst = try self.analyzeInst(scope, src_inst);
1819 analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst);
1820 }
1821 } else {
1822 for (body.instructions) |src_inst| {
1823 _ = try self.analyzeInst(scope, src_inst);
1824 }
1825 }
1826 }
1827
1828 fn analyzeIsNull(
1829 self: *Module,
1830 scope: *Scope,
1831 src: usize,
1832 operand: *Inst,
1833 invert_logic: bool,
1834 ) InnerError!*Inst {
1835 return self.fail(scope, src, "TODO implement analysis of isnull and isnotnull", .{});
1836 }
1837
1838 /// Asserts that lhs and rhs types are both numeric.
1839 fn cmpNumeric(
1840 self: *Module,
1841 scope: *Scope,
1842 src: usize,
1843 lhs: *Inst,
1844 rhs: *Inst,
1845 op: std.math.CompareOperator,
1846 ) !*Inst {
1847 assert(lhs.ty.isNumeric());
1848 assert(rhs.ty.isNumeric());
1849
1850 const lhs_ty_tag = lhs.ty.zigTypeTag();
1851 const rhs_ty_tag = rhs.ty.zigTypeTag();
1852
1853 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
1854 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
1855 return self.fail(scope, src, "vector length mismatch: {} and {}", .{
1856 lhs.ty.arrayLen(),
1857 rhs.ty.arrayLen(),
1858 });
1859 }
1860 return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
1861 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
1862 return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
1863 lhs.ty,
1864 rhs.ty,
1865 });
1866 }
1867
1868 if (lhs.value()) |lhs_val| {
1869 if (rhs.value()) |rhs_val| {
1870 return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
1871 }
1872 }
1873
1874 // TODO handle comparisons against lazy zero values
1875 // Some values can be compared against zero without being runtime known or without forcing
1876 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
1877 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
1878 // of this function if we don't need to.
1879
1880 // It must be a runtime comparison.
1881 const b = try self.requireRuntimeBlock(scope, src);
1882 // For floats, emit a float comparison instruction.
1883 const lhs_is_float = switch (lhs_ty_tag) {
1884 .Float, .ComptimeFloat => true,
1885 else => false,
1886 };
1887 const rhs_is_float = switch (rhs_ty_tag) {
1888 .Float, .ComptimeFloat => true,
1889 else => false,
1890 };
1891 if (lhs_is_float and rhs_is_float) {
1892 // Implicit cast the smaller one to the larger one.
1893 const dest_type = x: {
1894 if (lhs_ty_tag == .ComptimeFloat) {
1895 break :x rhs.ty;
1896 } else if (rhs_ty_tag == .ComptimeFloat) {
1897 break :x lhs.ty;
1898 }
1899 if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) {
1900 break :x lhs.ty;
1901 } else {
1902 break :x rhs.ty;
1903 }
1904 };
1905 const casted_lhs = try self.coerce(scope, dest_type, lhs);
1906 const casted_rhs = try self.coerce(scope, dest_type, rhs);
1907 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1908 .lhs = casted_lhs,
1909 .rhs = casted_rhs,
1910 .op = op,
1911 });
1912 }
1913 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
1914 // For mixed signed and unsigned integers, implicit cast both operands to a signed
1915 // integer with + 1 bit.
1916 // For mixed floats and integers, extract the integer part from the float, cast that to
1917 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
1918 // add/subtract 1.
1919 const lhs_is_signed = if (lhs.value()) |lhs_val|
1920 lhs_val.compareWithZero(.lt)
1921 else
1922 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
1923 const rhs_is_signed = if (rhs.value()) |rhs_val|
1924 rhs_val.compareWithZero(.lt)
1925 else
1926 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
1927 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
1928
1929 var dest_float_type: ?Type = null;
1930
1931 var lhs_bits: usize = undefined;
1932 if (lhs.value()) |lhs_val| {
1933 if (lhs_val.isUndef())
1934 return self.constUndef(scope, src, Type.initTag(.bool));
1935 const is_unsigned = if (lhs_is_float) x: {
1936 var bigint_space: Value.BigIntSpace = undefined;
1937 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1938 defer bigint.deinit();
1939 const zcmp = lhs_val.orderAgainstZero();
1940 if (lhs_val.floatHasFraction()) {
1941 switch (op) {
1942 .eq => return self.constBool(scope, src, false),
1943 .neq => return self.constBool(scope, src, true),
1944 else => {},
1945 }
1946 if (zcmp == .lt) {
1947 try bigint.addScalar(bigint.toConst(), -1);
1948 } else {
1949 try bigint.addScalar(bigint.toConst(), 1);
1950 }
1951 }
1952 lhs_bits = bigint.toConst().bitCountTwosComp();
1953 break :x (zcmp != .lt);
1954 } else x: {
1955 lhs_bits = lhs_val.intBitCountTwosComp();
1956 break :x (lhs_val.orderAgainstZero() != .lt);
1957 };
1958 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1959 } else if (lhs_is_float) {
1960 dest_float_type = lhs.ty;
1961 } else {
1962 const int_info = lhs.ty.intInfo(self.target());
1963 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1964 }
1965
1966 var rhs_bits: usize = undefined;
1967 if (rhs.value()) |rhs_val| {
1968 if (rhs_val.isUndef())
1969 return self.constUndef(scope, src, Type.initTag(.bool));
1970 const is_unsigned = if (rhs_is_float) x: {
1971 var bigint_space: Value.BigIntSpace = undefined;
1972 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1973 defer bigint.deinit();
1974 const zcmp = rhs_val.orderAgainstZero();
1975 if (rhs_val.floatHasFraction()) {
1976 switch (op) {
1977 .eq => return self.constBool(scope, src, false),
1978 .neq => return self.constBool(scope, src, true),
1979 else => {},
1980 }
1981 if (zcmp == .lt) {
1982 try bigint.addScalar(bigint.toConst(), -1);
1983 } else {
1984 try bigint.addScalar(bigint.toConst(), 1);
1985 }
1986 }
1987 rhs_bits = bigint.toConst().bitCountTwosComp();
1988 break :x (zcmp != .lt);
1989 } else x: {
1990 rhs_bits = rhs_val.intBitCountTwosComp();
1991 break :x (rhs_val.orderAgainstZero() != .lt);
1992 };
1993 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1994 } else if (rhs_is_float) {
1995 dest_float_type = rhs.ty;
1996 } else {
1997 const int_info = rhs.ty.intInfo(self.target());
1998 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1999 }
2000
2001 const dest_type = if (dest_float_type) |ft| ft else blk: {
2002 const max_bits = std.math.max(lhs_bits, rhs_bits);
2003 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
2004 error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
2005 };
2006 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
2007 };
2008 const casted_lhs = try self.coerce(scope, dest_type, lhs);
2009 const casted_rhs = try self.coerce(scope, dest_type, lhs);
2010
2011 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
2012 .lhs = casted_lhs,
2013 .rhs = casted_rhs,
2014 .op = op,
2015 });
2016 }
2017
2018 fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
2019 if (signed) {
2020 const int_payload = try scope.arena().create(Type.Payload.IntSigned);
2021 int_payload.* = .{ .bits = bits };
2022 return Type.initPayload(&int_payload.base);
2023 } else {
2024 const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
2025 int_payload.* = .{ .bits = bits };
2026 return Type.initPayload(&int_payload.base);
2027 }
2028 }
2029
2030 fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2031 // If the types are the same, we can return the operand.
2032 if (dest_type.eql(inst.ty))
2033 return inst;
2034
2035 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
2036 if (in_memory_result == .ok) {
2037 return self.bitcast(scope, dest_type, inst);
2038 }
2039
2040 // *[N]T to []T
2041 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
2042 (!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
2043 {
2044 const array_type = inst.ty.elemType();
2045 const dst_elem_type = dest_type.elemType();
2046 if (array_type.zigTypeTag() == .Array and
2047 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
2048 {
2049 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
2050 }
2051 }
2052
2053 // comptime_int to fixed-width integer
2054 if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) {
2055 // The representation is already correct; we only need to make sure it fits in the destination type.
2056 const val = inst.value().?; // comptime_int always has comptime known value
2057 if (!val.intFitsInType(dest_type, self.target())) {
2058 return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
2059 }
2060 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2061 }
2062
2063 // integer widening
2064 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
2065 const src_info = inst.ty.intInfo(self.target());
2066 const dst_info = dest_type.intInfo(self.target());
2067 if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) {
2068 if (inst.value()) |val| {
2069 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2070 } else {
2071 return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{});
2072 }
2073 } else {
2074 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
2075 }
2076 }
2077
2078 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
2079 }
2080
2081 fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2082 if (inst.value()) |val| {
2083 // Keep the comptime Value representation; take the new type.
2084 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2085 }
2086 // TODO validate the type size and other compile errors
2087 const b = try self.requireRuntimeBlock(scope, inst.src);
2088 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
2089 }
2090
2091 fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2092 if (inst.value()) |val| {
2093 // The comptime Value representation is compatible with both types.
2094 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2095 }
2096 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2097 }
2098
2099 fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError {
2100 @setCold(true);
2101 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
2102 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
2103 const err_msg = try ErrorMsg.create(self.allocator, src, format, args);
2104 switch (scope.tag) {
2105 .decl => {
2106 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
2107 switch (decl.analysis) {
2108 .initial_in_progress => decl.analysis = .initial_sema_failure,
2109 .repeat_in_progress => decl.analysis = .repeat_sema_failure,
2110 else => unreachable,
2111 }
2112 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
2113 },
2114 .block => {
2115 const block = scope.cast(Scope.Block).?;
2116 block.func.analysis = .sema_failure;
2117 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
2118 },
2119 .zir_module => {
2120 const zir_module = scope.cast(Scope.ZIRModule).?;
2121 zir_module.status = .loaded_sema_failure;
2122 self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg);
2123 },
2124 }
2125 return error.AnalysisFail;
2126 }
2127
2128 const InMemoryCoercionResult = enum {
2129 ok,
2130 no_match,
2131 };
2132
2133 fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
2134 if (dest_type.eql(src_type))
2135 return .ok;
2136
2137 // TODO: implement more of this function
2138
2139 return .no_match;
2140 }
2141};
2142
2143pub const ErrorMsg = struct {
2144 byte_offset: usize,
2145 msg: []const u8,
2146
2147 pub fn create(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !*ErrorMsg {
2148 const self = try allocator.create(ErrorMsg);
2149 errdefer allocator.destroy(self);
2150 self.* = try init(allocator, byte_offset, format, args);
2151 return self;
2152 }
2153
2154 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
2155 pub fn destroy(self: *ErrorMsg, allocator: *Allocator) void {
2156 self.deinit(allocator);
2157 allocator.destroy(self);
2158 }
2159
2160 pub fn init(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !ErrorMsg {
2161 return ErrorMsg{
2162 .byte_offset = byte_offset,
2163 .msg = try std.fmt.allocPrint(allocator, format, args),
2164 };
2165 }
2166
2167 pub fn deinit(self: *ErrorMsg, allocator: *Allocator) void {
2168 allocator.free(self.msg);
2169 self.* = undefined;
2170 }
2171};
src-self-hosted/ir/text.zig deleted-1476
...@@ -1,1476 +0,0 @@
1//! This file has to do with parsing and rendering the ZIR text format.
2
3const std = @import("std");
4const mem = std.mem;
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
9const Type = @import("../type.zig").Type;
10const Value = @import("../value.zig").Value;
11const TypedValue = @import("../TypedValue.zig");
12const ir = @import("../ir.zig");
13
14/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
15/// in-memory, analyzed instructions with types and values.
16pub const Inst = struct {
17 tag: Tag,
18 /// Byte offset into the source.
19 src: usize,
20 name: []const u8,
21
22 /// Slice into the source of the part after the = and before the next instruction.
23 contents: []const u8 = &[0]u8{},
24
25 /// These names are used directly as the instruction names in the text format.
26 pub const Tag = enum {
27 breakpoint,
28 call,
29 /// Represents a reference to a global decl by name.
30 /// The syntax `@foo` is equivalent to `declref("foo")`.
31 declref,
32 str,
33 int,
34 ptrtoint,
35 fieldptr,
36 deref,
37 as,
38 @"asm",
39 @"unreachable",
40 @"return",
41 @"fn",
42 @"export",
43 primitive,
44 ref,
45 fntype,
46 intcast,
47 bitcast,
48 elemptr,
49 add,
50 cmp,
51 condbr,
52 isnull,
53 isnonnull,
54 };
55
56 pub fn TagToType(tag: Tag) type {
57 return switch (tag) {
58 .breakpoint => Breakpoint,
59 .call => Call,
60 .declref => DeclRef,
61 .str => Str,
62 .int => Int,
63 .ptrtoint => PtrToInt,
64 .fieldptr => FieldPtr,
65 .deref => Deref,
66 .as => As,
67 .@"asm" => Asm,
68 .@"unreachable" => Unreachable,
69 .@"return" => Return,
70 .@"fn" => Fn,
71 .@"export" => Export,
72 .primitive => Primitive,
73 .ref => Ref,
74 .fntype => FnType,
75 .intcast => IntCast,
76 .bitcast => BitCast,
77 .elemptr => ElemPtr,
78 .add => Add,
79 .cmp => Cmp,
80 .condbr => CondBr,
81 .isnull => IsNull,
82 .isnonnull => IsNonNull,
83 };
84 }
85
86 pub fn cast(base: *Inst, comptime T: type) ?*T {
87 if (base.tag != T.base_tag)
88 return null;
89
90 return @fieldParentPtr(T, "base", base);
91 }
92
93 pub const Breakpoint = struct {
94 pub const base_tag = Tag.breakpoint;
95 base: Inst,
96
97 positionals: struct {},
98 kw_args: struct {},
99 };
100
101 pub const Call = struct {
102 pub const base_tag = Tag.call;
103 base: Inst,
104
105 positionals: struct {
106 func: *Inst,
107 args: []*Inst,
108 },
109 kw_args: struct {
110 modifier: std.builtin.CallOptions.Modifier = .auto,
111 },
112 };
113
114 pub const DeclRef = struct {
115 pub const base_tag = Tag.declref;
116 base: Inst,
117
118 positionals: struct {
119 name: *Inst,
120 },
121 kw_args: struct {},
122 };
123
124 pub const Str = struct {
125 pub const base_tag = Tag.str;
126 base: Inst,
127
128 positionals: struct {
129 bytes: []const u8,
130 },
131 kw_args: struct {},
132 };
133
134 pub const Int = struct {
135 pub const base_tag = Tag.int;
136 base: Inst,
137
138 positionals: struct {
139 int: BigIntConst,
140 },
141 kw_args: struct {},
142 };
143
144 pub const PtrToInt = struct {
145 pub const base_tag = Tag.ptrtoint;
146 base: Inst,
147
148 positionals: struct {
149 ptr: *Inst,
150 },
151 kw_args: struct {},
152 };
153
154 pub const FieldPtr = struct {
155 pub const base_tag = Tag.fieldptr;
156 base: Inst,
157
158 positionals: struct {
159 object_ptr: *Inst,
160 field_name: *Inst,
161 },
162 kw_args: struct {},
163 };
164
165 pub const Deref = struct {
166 pub const base_tag = Tag.deref;
167 base: Inst,
168
169 positionals: struct {
170 ptr: *Inst,
171 },
172 kw_args: struct {},
173 };
174
175 pub const As = struct {
176 pub const base_tag = Tag.as;
177 base: Inst,
178
179 positionals: struct {
180 dest_type: *Inst,
181 value: *Inst,
182 },
183 kw_args: struct {},
184 };
185
186 pub const Asm = struct {
187 pub const base_tag = Tag.@"asm";
188 base: Inst,
189
190 positionals: struct {
191 asm_source: *Inst,
192 return_type: *Inst,
193 },
194 kw_args: struct {
195 @"volatile": bool = false,
196 output: ?*Inst = null,
197 inputs: []*Inst = &[0]*Inst{},
198 clobbers: []*Inst = &[0]*Inst{},
199 args: []*Inst = &[0]*Inst{},
200 },
201 };
202
203 pub const Unreachable = struct {
204 pub const base_tag = Tag.@"unreachable";
205 base: Inst,
206
207 positionals: struct {},
208 kw_args: struct {},
209 };
210
211 pub const Return = struct {
212 pub const base_tag = Tag.@"return";
213 base: Inst,
214
215 positionals: struct {},
216 kw_args: struct {},
217 };
218
219 pub const Fn = struct {
220 pub const base_tag = Tag.@"fn";
221 base: Inst,
222
223 positionals: struct {
224 fn_type: *Inst,
225 body: Module.Body,
226 },
227 kw_args: struct {},
228 };
229
230 pub const Export = struct {
231 pub const base_tag = Tag.@"export";
232 base: Inst,
233
234 positionals: struct {
235 symbol_name: *Inst,
236 value: *Inst,
237 },
238 kw_args: struct {},
239 };
240
241 pub const Ref = struct {
242 pub const base_tag = Tag.ref;
243 base: Inst,
244
245 positionals: struct {
246 operand: *Inst,
247 },
248 kw_args: struct {},
249 };
250
251 pub const Primitive = struct {
252 pub const base_tag = Tag.primitive;
253 base: Inst,
254
255 positionals: struct {
256 tag: BuiltinType,
257 },
258 kw_args: struct {},
259
260 pub const BuiltinType = enum {
261 isize,
262 usize,
263 c_short,
264 c_ushort,
265 c_int,
266 c_uint,
267 c_long,
268 c_ulong,
269 c_longlong,
270 c_ulonglong,
271 c_longdouble,
272 c_void,
273 f16,
274 f32,
275 f64,
276 f128,
277 bool,
278 void,
279 noreturn,
280 type,
281 anyerror,
282 comptime_int,
283 comptime_float,
284
285 fn toType(self: BuiltinType) Type {
286 return switch (self) {
287 .isize => Type.initTag(.isize),
288 .usize => Type.initTag(.usize),
289 .c_short => Type.initTag(.c_short),
290 .c_ushort => Type.initTag(.c_ushort),
291 .c_int => Type.initTag(.c_int),
292 .c_uint => Type.initTag(.c_uint),
293 .c_long => Type.initTag(.c_long),
294 .c_ulong => Type.initTag(.c_ulong),
295 .c_longlong => Type.initTag(.c_longlong),
296 .c_ulonglong => Type.initTag(.c_ulonglong),
297 .c_longdouble => Type.initTag(.c_longdouble),
298 .c_void => Type.initTag(.c_void),
299 .f16 => Type.initTag(.f16),
300 .f32 => Type.initTag(.f32),
301 .f64 => Type.initTag(.f64),
302 .f128 => Type.initTag(.f128),
303 .bool => Type.initTag(.bool),
304 .void => Type.initTag(.void),
305 .noreturn => Type.initTag(.noreturn),
306 .type => Type.initTag(.type),
307 .anyerror => Type.initTag(.anyerror),
308 .comptime_int => Type.initTag(.comptime_int),
309 .comptime_float => Type.initTag(.comptime_float),
310 };
311 }
312 };
313 };
314
315 pub const FnType = struct {
316 pub const base_tag = Tag.fntype;
317 base: Inst,
318
319 positionals: struct {
320 param_types: []*Inst,
321 return_type: *Inst,
322 },
323 kw_args: struct {
324 cc: std.builtin.CallingConvention = .Unspecified,
325 },
326 };
327
328 pub const IntCast = struct {
329 pub const base_tag = Tag.intcast;
330 base: Inst,
331
332 positionals: struct {
333 dest_type: *Inst,
334 value: *Inst,
335 },
336 kw_args: struct {},
337 };
338
339 pub const BitCast = struct {
340 pub const base_tag = Tag.bitcast;
341 base: Inst,
342
343 positionals: struct {
344 dest_type: *Inst,
345 operand: *Inst,
346 },
347 kw_args: struct {},
348 };
349
350 pub const ElemPtr = struct {
351 pub const base_tag = Tag.elemptr;
352 base: Inst,
353
354 positionals: struct {
355 array_ptr: *Inst,
356 index: *Inst,
357 },
358 kw_args: struct {},
359 };
360
361 pub const Add = struct {
362 pub const base_tag = Tag.add;
363 base: Inst,
364
365 positionals: struct {
366 lhs: *Inst,
367 rhs: *Inst,
368 },
369 kw_args: struct {},
370 };
371
372 pub const Cmp = struct {
373 pub const base_tag = Tag.cmp;
374 base: Inst,
375
376 positionals: struct {
377 lhs: *Inst,
378 op: std.math.CompareOperator,
379 rhs: *Inst,
380 },
381 kw_args: struct {},
382 };
383
384 pub const CondBr = struct {
385 pub const base_tag = Tag.condbr;
386 base: Inst,
387
388 positionals: struct {
389 condition: *Inst,
390 true_body: Module.Body,
391 false_body: Module.Body,
392 },
393 kw_args: struct {},
394 };
395
396 pub const IsNull = struct {
397 pub const base_tag = Tag.isnull;
398 base: Inst,
399
400 positionals: struct {
401 operand: *Inst,
402 },
403 kw_args: struct {},
404 };
405
406 pub const IsNonNull = struct {
407 pub const base_tag = Tag.isnonnull;
408 base: Inst,
409
410 positionals: struct {
411 operand: *Inst,
412 },
413 kw_args: struct {},
414 };
415};
416
417pub const ErrorMsg = struct {
418 byte_offset: usize,
419 msg: []const u8,
420};
421
422pub const Module = struct {
423 decls: []*Inst,
424 arena: std.heap.ArenaAllocator,
425 error_msg: ?ErrorMsg = null,
426
427 pub const Body = struct {
428 instructions: []*Inst,
429 };
430
431 pub fn deinit(self: *Module, allocator: *Allocator) void {
432 allocator.free(self.decls);
433 self.arena.deinit();
434 self.* = undefined;
435 }
436
437 /// This is a debugging utility for rendering the tree to stderr.
438 pub fn dump(self: Module) void {
439 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
440 }
441
442 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
443
444 /// The allocator is used for temporary storage, but this function always returns
445 /// with no resources allocated.
446 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
447 // First, build a map of *Inst to @ or % indexes
448 var inst_table = InstPtrTable.init(allocator);
449 defer inst_table.deinit();
450
451 try inst_table.ensureCapacity(self.decls.len);
452
453 for (self.decls) |decl, decl_i| {
454 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
455
456 if (decl.cast(Inst.Fn)) |fn_inst| {
457 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
458 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
459 }
460 }
461 }
462
463 for (self.decls) |decl, i| {
464 try stream.print("@{} ", .{i});
465 try self.writeInstToStream(stream, decl, &inst_table);
466 try stream.writeByte('\n');
467 }
468 }
469
470 fn writeInstToStream(
471 self: Module,
472 stream: var,
473 decl: *Inst,
474 inst_table: *const InstPtrTable,
475 ) @TypeOf(stream).Error!void {
476 // TODO I tried implementing this with an inline for loop and hit a compiler bug
477 switch (decl.tag) {
478 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
479 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
480 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
481 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
482 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
483 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
484 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
485 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
486 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
487 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
488 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
489 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
490 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
491 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
492 .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
493 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
494 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
495 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
496 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
497 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
498 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
499 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
500 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
501 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
502 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
503 }
504 }
505
506 fn writeInstToStreamGeneric(
507 self: Module,
508 stream: var,
509 comptime inst_tag: Inst.Tag,
510 base: *Inst,
511 inst_table: *const InstPtrTable,
512 ) !void {
513 const SpecificInst = Inst.TagToType(inst_tag);
514 const inst = @fieldParentPtr(SpecificInst, "base", base);
515 const Positionals = @TypeOf(inst.positionals);
516 try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
517 const pos_fields = @typeInfo(Positionals).Struct.fields;
518 inline for (pos_fields) |arg_field, i| {
519 if (i != 0) {
520 try stream.writeAll(", ");
521 }
522 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
523 }
524
525 comptime var need_comma = pos_fields.len != 0;
526 const KW_Args = @TypeOf(inst.kw_args);
527 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
528 if (@typeInfo(arg_field.field_type) == .Optional) {
529 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
530 if (need_comma) try stream.writeAll(", ");
531 try stream.print("{}=", .{arg_field.name});
532 try self.writeParamToStream(stream, non_optional, inst_table);
533 need_comma = true;
534 }
535 } else {
536 if (need_comma) try stream.writeAll(", ");
537 try stream.print("{}=", .{arg_field.name});
538 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
539 need_comma = true;
540 }
541 }
542
543 try stream.writeByte(')');
544 }
545
546 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {
547 if (@typeInfo(@TypeOf(param)) == .Enum) {
548 return stream.writeAll(@tagName(param));
549 }
550 switch (@TypeOf(param)) {
551 *Inst => return self.writeInstParamToStream(stream, param, inst_table),
552 []*Inst => {
553 try stream.writeByte('[');
554 for (param) |inst, i| {
555 if (i != 0) {
556 try stream.writeAll(", ");
557 }
558 try self.writeInstParamToStream(stream, inst, inst_table);
559 }
560 try stream.writeByte(']');
561 },
562 Module.Body => {
563 try stream.writeAll("{\n");
564 for (param.instructions) |inst, i| {
565 try stream.print(" %{} ", .{i});
566 try self.writeInstToStream(stream, inst, inst_table);
567 try stream.writeByte('\n');
568 }
569 try stream.writeByte('}');
570 },
571 bool => return stream.writeByte("01"[@boolToInt(param)]),
572 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
573 BigIntConst => return stream.print("{}", .{param}),
574 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
575 }
576 }
577
578 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
579 const info = inst_table.getValue(inst).?;
580 const prefix = if (info.fn_body == null) "@" else "%";
581 try stream.print("{}{}", .{ prefix, info.index });
582 }
583};
584
585pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
586 var global_name_map = std.StringHashMap(usize).init(allocator);
587 defer global_name_map.deinit();
588
589 var parser: Parser = .{
590 .allocator = allocator,
591 .arena = std.heap.ArenaAllocator.init(allocator),
592 .i = 0,
593 .source = source,
594 .global_name_map = &global_name_map,
595 .decls = .{},
596 .unnamed_index = 0,
597 };
598 errdefer parser.arena.deinit();
599
600 parser.parseRoot() catch |err| switch (err) {
601 error.ParseFailure => {
602 assert(parser.error_msg != null);
603 },
604 else => |e| return e,
605 };
606
607 return Module{
608 .decls = parser.decls.toOwnedSlice(allocator),
609 .arena = parser.arena,
610 .error_msg = parser.error_msg,
611 };
612}
613
614const Parser = struct {
615 allocator: *Allocator,
616 arena: std.heap.ArenaAllocator,
617 i: usize,
618 source: [:0]const u8,
619 decls: std.ArrayListUnmanaged(*Inst),
620 global_name_map: *std.StringHashMap(usize),
621 error_msg: ?ErrorMsg = null,
622 unnamed_index: usize,
623
624 const Body = struct {
625 instructions: std.ArrayList(*Inst),
626 name_map: std.StringHashMap(usize),
627 };
628
629 fn parseBody(self: *Parser) !Module.Body {
630 var body_context = Body{
631 .instructions = std.ArrayList(*Inst).init(self.allocator),
632 .name_map = std.StringHashMap(usize).init(self.allocator),
633 };
634 defer body_context.instructions.deinit();
635 defer body_context.name_map.deinit();
636
637 try requireEatBytes(self, "{");
638 skipSpace(self);
639
640 while (true) : (self.i += 1) switch (self.source[self.i]) {
641 ';' => _ = try skipToAndOver(self, '\n'),
642 '%' => {
643 self.i += 1;
644 const ident = try skipToAndOver(self, ' ');
645 skipSpace(self);
646 try requireEatBytes(self, "=");
647 skipSpace(self);
648 const inst = try parseInstruction(self, &body_context, ident);
649 const ident_index = body_context.instructions.items.len;
650 if (try body_context.name_map.put(ident, ident_index)) |_| {
651 return self.fail("redefinition of identifier '{}'", .{ident});
652 }
653 try body_context.instructions.append(inst);
654 continue;
655 },
656 ' ', '\n' => continue,
657 '}' => {
658 self.i += 1;
659 break;
660 },
661 else => |byte| return self.failByte(byte),
662 };
663
664 // Move the instructions to the arena
665 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
666 mem.copy(*Inst, instrs, body_context.instructions.items);
667 return Module.Body{ .instructions = instrs };
668 }
669
670 fn parseStringLiteral(self: *Parser) ![]u8 {
671 const start = self.i;
672 try self.requireEatBytes("\"");
673
674 while (true) : (self.i += 1) switch (self.source[self.i]) {
675 '"' => {
676 self.i += 1;
677 const span = self.source[start..self.i];
678 var bad_index: usize = undefined;
679 const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
680 error.InvalidCharacter => {
681 self.i = start + bad_index;
682 const bad_byte = self.source[self.i];
683 return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
684 },
685 else => |e| return e,
686 };
687 return parsed;
688 },
689 '\\' => {
690 self.i += 1;
691 continue;
692 },
693 0 => return self.failByte(0),
694 else => continue,
695 };
696 }
697
698 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
699 const start = self.i;
700 if (self.source[self.i] == '-') self.i += 1;
701 while (true) : (self.i += 1) switch (self.source[self.i]) {
702 '0'...'9' => continue,
703 else => break,
704 };
705 const number_text = self.source[start..self.i];
706 const base = 10;
707 // TODO reuse the same array list for this
708 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
709 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
710 defer self.allocator.free(limbs_buffer);
711 const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
712 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
713 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
714 result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
715 error.InvalidCharacter => {
716 self.i = start;
717 return self.fail("invalid digit in integer literal", .{});
718 },
719 };
720 return result.toConst();
721 }
722
723 fn parseRoot(self: *Parser) !void {
724 // The IR format is designed so that it can be tokenized and parsed at the same time.
725 while (true) {
726 switch (self.source[self.i]) {
727 ';' => _ = try skipToAndOver(self, '\n'),
728 '@' => {
729 self.i += 1;
730 const ident = try skipToAndOver(self, ' ');
731 skipSpace(self);
732 try requireEatBytes(self, "=");
733 skipSpace(self);
734 const inst = try parseInstruction(self, null, ident);
735 const ident_index = self.decls.items.len;
736 if (try self.global_name_map.put(ident, ident_index)) |_| {
737 return self.fail("redefinition of identifier '{}'", .{ident});
738 }
739 try self.decls.append(self.allocator, inst);
740 },
741 ' ', '\n' => self.i += 1,
742 0 => break,
743 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
744 }
745 }
746 }
747
748 fn eatByte(self: *Parser, byte: u8) bool {
749 if (self.source[self.i] != byte) return false;
750 self.i += 1;
751 return true;
752 }
753
754 fn skipSpace(self: *Parser) void {
755 while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
756 self.i += 1;
757 }
758 }
759
760 fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
761 const start = self.i;
762 for (bytes) |byte| {
763 if (self.source[self.i] != byte) {
764 self.i = start;
765 return self.fail("expected '{}'", .{bytes});
766 }
767 self.i += 1;
768 }
769 }
770
771 fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
772 const start_i = self.i;
773 while (self.source[self.i] != 0) : (self.i += 1) {
774 if (self.source[self.i] == byte) {
775 const result = self.source[start_i..self.i];
776 self.i += 1;
777 return result;
778 }
779 }
780 return self.fail("unexpected EOF", .{});
781 }
782
783 /// ParseFailure is an internal error code; handled in `parse`.
784 const InnerError = error{ ParseFailure, OutOfMemory };
785
786 fn failByte(self: *Parser, byte: u8) InnerError {
787 if (byte == 0) {
788 return self.fail("unexpected EOF", .{});
789 } else {
790 return self.fail("unexpected byte: '{c}'", .{byte});
791 }
792 }
793
794 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
795 @setCold(true);
796 self.error_msg = ErrorMsg{
797 .byte_offset = self.i,
798 .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
799 };
800 return error.ParseFailure;
801 }
802
803 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst {
804 const contents_start = self.i;
805 const fn_name = try skipToAndOver(self, '(');
806 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
807 if (mem.eql(u8, field.name, fn_name)) {
808 const tag = @field(Inst.Tag, field.name);
809 return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx, name, contents_start);
810 }
811 }
812 return self.fail("unknown instruction '{}'", .{fn_name});
813 }
814
815 fn parseInstructionGeneric(
816 self: *Parser,
817 comptime fn_name: []const u8,
818 comptime InstType: type,
819 body_ctx: ?*Body,
820 inst_name: []const u8,
821 contents_start: usize,
822 ) InnerError!*Inst {
823 const inst_specific = try self.arena.allocator.create(InstType);
824 inst_specific.base = .{
825 .name = inst_name,
826 .src = self.i,
827 .tag = InstType.base_tag,
828 };
829
830 if (@hasField(InstType, "ty")) {
831 inst_specific.ty = opt_type orelse {
832 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
833 };
834 }
835
836 const Positionals = @TypeOf(inst_specific.positionals);
837 inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
838 if (self.source[self.i] == ',') {
839 self.i += 1;
840 skipSpace(self);
841 } else if (self.source[self.i] == ')') {
842 return self.fail("expected positional parameter '{}'", .{arg_field.name});
843 }
844 @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
845 self,
846 arg_field.field_type,
847 body_ctx,
848 );
849 skipSpace(self);
850 }
851
852 const KW_Args = @TypeOf(inst_specific.kw_args);
853 inst_specific.kw_args = .{}; // assign defaults
854 skipSpace(self);
855 while (eatByte(self, ',')) {
856 skipSpace(self);
857 const name = try skipToAndOver(self, '=');
858 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
859 const field_name = arg_field.name;
860 if (mem.eql(u8, name, field_name)) {
861 const NonOptional = switch (@typeInfo(arg_field.field_type)) {
862 .Optional => |info| info.child,
863 else => arg_field.field_type,
864 };
865 @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
866 break;
867 }
868 } else {
869 return self.fail("unrecognized keyword parameter: '{}'", .{name});
870 }
871 skipSpace(self);
872 }
873 try requireEatBytes(self, ")");
874
875 inst_specific.base.contents = self.source[contents_start..self.i];
876
877 return &inst_specific.base;
878 }
879
880 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
881 if (@typeInfo(T) == .Enum) {
882 const start = self.i;
883 while (true) : (self.i += 1) switch (self.source[self.i]) {
884 ' ', '\n', ',', ')' => {
885 const enum_name = self.source[start..self.i];
886 return std.meta.stringToEnum(T, enum_name) orelse {
887 return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
888 };
889 },
890 0 => return self.failByte(0),
891 else => continue,
892 };
893 }
894 switch (T) {
895 Module.Body => return parseBody(self),
896 bool => {
897 const bool_value = switch (self.source[self.i]) {
898 '0' => false,
899 '1' => true,
900 else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
901 };
902 self.i += 1;
903 return bool_value;
904 },
905 []*Inst => {
906 try requireEatBytes(self, "[");
907 skipSpace(self);
908 if (eatByte(self, ']')) return &[0]*Inst{};
909
910 var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
911 while (true) {
912 skipSpace(self);
913 try instructions.append(try parseParameterInst(self, body_ctx));
914 skipSpace(self);
915 if (!eatByte(self, ',')) break;
916 }
917 try requireEatBytes(self, "]");
918 return instructions.toOwnedSlice();
919 },
920 *Inst => return parseParameterInst(self, body_ctx),
921 []u8, []const u8 => return self.parseStringLiteral(),
922 BigIntConst => return self.parseIntegerLiteral(),
923 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
924 }
925 return self.fail("TODO parse parameter {}", .{@typeName(T)});
926 }
927
928 fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
929 const local_ref = switch (self.source[self.i]) {
930 '@' => false,
931 '%' => true,
932 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
933 };
934 const map = if (local_ref)
935 if (body_ctx) |bc|
936 &bc.name_map
937 else
938 return self.fail("referencing a % instruction in global scope", .{})
939 else
940 self.global_name_map;
941
942 self.i += 1;
943 const name_start = self.i;
944 while (true) : (self.i += 1) switch (self.source[self.i]) {
945 0, ' ', '\n', ',', ')', ']' => break,
946 else => continue,
947 };
948 const ident = self.source[name_start..self.i];
949 const kv = map.get(ident) orelse {
950 const bad_name = self.source[name_start - 1 .. self.i];
951 const src = name_start - 1;
952 if (local_ref) {
953 self.i = src;
954 return self.fail("unrecognized identifier: {}", .{bad_name});
955 } else {
956 const name = try self.arena.allocator.create(Inst.Str);
957 name.* = .{
958 .base = .{
959 .name = try self.generateName(),
960 .src = src,
961 .tag = Inst.Str.base_tag,
962 },
963 .positionals = .{ .bytes = ident },
964 .kw_args = .{},
965 };
966 const declref = try self.arena.allocator.create(Inst.DeclRef);
967 declref.* = .{
968 .base = .{
969 .name = try self.generateName(),
970 .src = src,
971 .tag = Inst.DeclRef.base_tag,
972 },
973 .positionals = .{ .name = &name.base },
974 .kw_args = .{},
975 };
976 return &declref.base;
977 }
978 };
979 if (local_ref) {
980 return body_ctx.?.instructions.items[kv.value];
981 } else {
982 return self.decls.items[kv.value];
983 }
984 }
985
986 fn generateName(self: *Parser) ![]u8 {
987 const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index});
988 self.unnamed_index += 1;
989 return result;
990 }
991};
992
993pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module {
994 var ctx: EmitZIR = .{
995 .allocator = allocator,
996 .decls = .{},
997 .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
998 .arena = std.heap.ArenaAllocator.init(allocator),
999 .old_module = &old_module,
1000 };
1001 defer ctx.decls.deinit(allocator);
1002 defer ctx.decl_table.deinit();
1003 errdefer ctx.arena.deinit();
1004
1005 try ctx.emit();
1006
1007 return Module{
1008 .decls = ctx.decls.toOwnedSlice(allocator),
1009 .arena = ctx.arena,
1010 };
1011}
1012
1013const EmitZIR = struct {
1014 allocator: *Allocator,
1015 arena: std.heap.ArenaAllocator,
1016 old_module: *const ir.Module,
1017 decls: std.ArrayListUnmanaged(*Inst),
1018 decl_table: std.AutoHashMap(*ir.Inst, *Inst),
1019
1020 fn emit(self: *EmitZIR) !void {
1021 var it = self.old_module.decl_exports.iterator();
1022 while (it.next()) |kv| {
1023 const decl = kv.key;
1024 const exports = kv.value;
1025 const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value);
1026 for (exports) |module_export| {
1027 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
1028 const export_inst = try self.arena.allocator.create(Inst.Export);
1029 export_inst.* = .{
1030 .base = .{
1031 .name = try self.autoName(),
1032 .src = module_export.src,
1033 .tag = Inst.Export.base_tag,
1034 },
1035 .positionals = .{
1036 .symbol_name = symbol_name,
1037 .value = export_value,
1038 },
1039 .kw_args = .{},
1040 };
1041 try self.decls.append(self.allocator, &export_inst.base);
1042 }
1043 }
1044 }
1045
1046 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
1047 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1048 if (self.decl_table.getValue(inst)) |decl| {
1049 return decl;
1050 }
1051 const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1052 try self.decl_table.putNoClobber(inst, new_decl);
1053 return new_decl;
1054 } else {
1055 return inst_table.getValue(inst).?;
1056 }
1057 }
1058
1059 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
1060 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
1061 const int_inst = try self.arena.allocator.create(Inst.Int);
1062 int_inst.* = .{
1063 .base = .{
1064 .name = try self.autoName(),
1065 .src = src,
1066 .tag = Inst.Int.base_tag,
1067 },
1068 .positionals = .{
1069 .int = val.toBigInt(big_int_space),
1070 },
1071 .kw_args = .{},
1072 };
1073 try self.decls.append(self.allocator, &int_inst.base);
1074 return &int_inst.base;
1075 }
1076
1077 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {
1078 const allocator = &self.arena.allocator;
1079 switch (typed_value.ty.zigTypeTag()) {
1080 .Pointer => {
1081 const ptr_elem_type = typed_value.ty.elemType();
1082 switch (ptr_elem_type.zigTypeTag()) {
1083 .Array => {
1084 // TODO more checks to make sure this can be emitted as a string literal
1085 //const array_elem_type = ptr_elem_type.elemType();
1086 //if (array_elem_type.eql(Type.initTag(.u8)) and
1087 // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
1088 //{
1089 //}
1090 const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
1091 error.AnalysisFail => unreachable,
1092 else => |e| return e,
1093 };
1094 return self.emitStringLiteral(src, bytes);
1095 },
1096 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
1097 }
1098 },
1099 .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
1100 .Int => {
1101 const as_inst = try self.arena.allocator.create(Inst.As);
1102 as_inst.* = .{
1103 .base = .{
1104 .name = try self.autoName(),
1105 .src = src,
1106 .tag = Inst.As.base_tag,
1107 },
1108 .positionals = .{
1109 .dest_type = try self.emitType(src, typed_value.ty),
1110 .value = try self.emitComptimeIntVal(src, typed_value.val),
1111 },
1112 .kw_args = .{},
1113 };
1114 try self.decls.append(self.allocator, &as_inst.base);
1115
1116 return &as_inst.base;
1117 },
1118 .Type => {
1119 const ty = typed_value.val.toType();
1120 return self.emitType(src, ty);
1121 },
1122 .Fn => {
1123 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
1124
1125 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
1126 defer inst_table.deinit();
1127
1128 var instructions = std.ArrayList(*Inst).init(self.allocator);
1129 defer instructions.deinit();
1130
1131 try self.emitBody(module_fn.analysis.success, &inst_table, &instructions);
1132
1133 const fn_type = try self.emitType(src, module_fn.fn_type);
1134
1135 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1136 mem.copy(*Inst, arena_instrs, instructions.items);
1137
1138 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1139 fn_inst.* = .{
1140 .base = .{
1141 .name = try self.autoName(),
1142 .src = src,
1143 .tag = Inst.Fn.base_tag,
1144 },
1145 .positionals = .{
1146 .fn_type = fn_type,
1147 .body = .{ .instructions = arena_instrs },
1148 },
1149 .kw_args = .{},
1150 };
1151 try self.decls.append(self.allocator, &fn_inst.base);
1152 return &fn_inst.base;
1153 },
1154 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
1155 }
1156 }
1157
1158 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1159 const new_inst = try self.arena.allocator.create(T);
1160 new_inst.* = .{
1161 .base = .{
1162 .name = try self.autoName(),
1163 .src = src,
1164 .tag = T.base_tag,
1165 },
1166 .positionals = .{},
1167 .kw_args = .{},
1168 };
1169 return &new_inst.base;
1170 }
1171
1172 fn emitBody(
1173 self: *EmitZIR,
1174 body: ir.Module.Body,
1175 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1176 instructions: *std.ArrayList(*Inst),
1177 ) Allocator.Error!void {
1178 for (body.instructions) |inst| {
1179 const new_inst = switch (inst.tag) {
1180 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1181 .call => blk: {
1182 const old_inst = inst.cast(ir.Inst.Call).?;
1183 const new_inst = try self.arena.allocator.create(Inst.Call);
1184
1185 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1186 for (args) |*elem, i| {
1187 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1188 }
1189 new_inst.* = .{
1190 .base = .{
1191 .name = try self.autoName(),
1192 .src = inst.src,
1193 .tag = Inst.Call.base_tag,
1194 },
1195 .positionals = .{
1196 .func = try self.resolveInst(inst_table, old_inst.args.func),
1197 .args = args,
1198 },
1199 .kw_args = .{},
1200 };
1201 break :blk &new_inst.base;
1202 },
1203 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1204 .ret => try self.emitTrivial(inst.src, Inst.Return),
1205 .constant => unreachable, // excluded from function bodies
1206 .assembly => blk: {
1207 const old_inst = inst.cast(ir.Inst.Assembly).?;
1208 const new_inst = try self.arena.allocator.create(Inst.Asm);
1209
1210 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1211 for (inputs) |*elem, i| {
1212 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
1213 }
1214
1215 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1216 for (clobbers) |*elem, i| {
1217 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
1218 }
1219
1220 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1221 for (args) |*elem, i| {
1222 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1223 }
1224
1225 new_inst.* = .{
1226 .base = .{
1227 .name = try self.autoName(),
1228 .src = inst.src,
1229 .tag = Inst.Asm.base_tag,
1230 },
1231 .positionals = .{
1232 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
1233 .return_type = try self.emitType(inst.src, inst.ty),
1234 },
1235 .kw_args = .{
1236 .@"volatile" = old_inst.args.is_volatile,
1237 .output = if (old_inst.args.output) |o|
1238 try self.emitStringLiteral(inst.src, o)
1239 else
1240 null,
1241 .inputs = inputs,
1242 .clobbers = clobbers,
1243 .args = args,
1244 },
1245 };
1246 break :blk &new_inst.base;
1247 },
1248 .ptrtoint => blk: {
1249 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1250 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1251 new_inst.* = .{
1252 .base = .{
1253 .name = try self.autoName(),
1254 .src = inst.src,
1255 .tag = Inst.PtrToInt.base_tag,
1256 },
1257 .positionals = .{
1258 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
1259 },
1260 .kw_args = .{},
1261 };
1262 break :blk &new_inst.base;
1263 },
1264 .bitcast => blk: {
1265 const old_inst = inst.cast(ir.Inst.BitCast).?;
1266 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1267 new_inst.* = .{
1268 .base = .{
1269 .name = try self.autoName(),
1270 .src = inst.src,
1271 .tag = Inst.BitCast.base_tag,
1272 },
1273 .positionals = .{
1274 .dest_type = try self.emitType(inst.src, inst.ty),
1275 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1276 },
1277 .kw_args = .{},
1278 };
1279 break :blk &new_inst.base;
1280 },
1281 .cmp => blk: {
1282 const old_inst = inst.cast(ir.Inst.Cmp).?;
1283 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1284 new_inst.* = .{
1285 .base = .{
1286 .name = try self.autoName(),
1287 .src = inst.src,
1288 .tag = Inst.Cmp.base_tag,
1289 },
1290 .positionals = .{
1291 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
1292 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
1293 .op = old_inst.args.op,
1294 },
1295 .kw_args = .{},
1296 };
1297 break :blk &new_inst.base;
1298 },
1299 .condbr => blk: {
1300 const old_inst = inst.cast(ir.Inst.CondBr).?;
1301
1302 var true_body = std.ArrayList(*Inst).init(self.allocator);
1303 var false_body = std.ArrayList(*Inst).init(self.allocator);
1304
1305 defer true_body.deinit();
1306 defer false_body.deinit();
1307
1308 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1309 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1310
1311 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1312 new_inst.* = .{
1313 .base = .{
1314 .name = try self.autoName(),
1315 .src = inst.src,
1316 .tag = Inst.CondBr.base_tag,
1317 },
1318 .positionals = .{
1319 .condition = try self.resolveInst(inst_table, old_inst.args.condition),
1320 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1321 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1322 },
1323 .kw_args = .{},
1324 };
1325 break :blk &new_inst.base;
1326 },
1327 .isnull => blk: {
1328 const old_inst = inst.cast(ir.Inst.IsNull).?;
1329 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1330 new_inst.* = .{
1331 .base = .{
1332 .name = try self.autoName(),
1333 .src = inst.src,
1334 .tag = Inst.IsNull.base_tag,
1335 },
1336 .positionals = .{
1337 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1338 },
1339 .kw_args = .{},
1340 };
1341 break :blk &new_inst.base;
1342 },
1343 .isnonnull => blk: {
1344 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1345 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1346 new_inst.* = .{
1347 .base = .{
1348 .name = try self.autoName(),
1349 .src = inst.src,
1350 .tag = Inst.IsNonNull.base_tag,
1351 },
1352 .positionals = .{
1353 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1354 },
1355 .kw_args = .{},
1356 };
1357 break :blk &new_inst.base;
1358 },
1359 };
1360 try instructions.append(new_inst);
1361 try inst_table.putNoClobber(inst, new_inst);
1362 }
1363 }
1364
1365 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1366 switch (ty.tag()) {
1367 .isize => return self.emitPrimitiveType(src, .isize),
1368 .usize => return self.emitPrimitiveType(src, .usize),
1369 .c_short => return self.emitPrimitiveType(src, .c_short),
1370 .c_ushort => return self.emitPrimitiveType(src, .c_ushort),
1371 .c_int => return self.emitPrimitiveType(src, .c_int),
1372 .c_uint => return self.emitPrimitiveType(src, .c_uint),
1373 .c_long => return self.emitPrimitiveType(src, .c_long),
1374 .c_ulong => return self.emitPrimitiveType(src, .c_ulong),
1375 .c_longlong => return self.emitPrimitiveType(src, .c_longlong),
1376 .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),
1377 .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),
1378 .c_void => return self.emitPrimitiveType(src, .c_void),
1379 .f16 => return self.emitPrimitiveType(src, .f16),
1380 .f32 => return self.emitPrimitiveType(src, .f32),
1381 .f64 => return self.emitPrimitiveType(src, .f64),
1382 .f128 => return self.emitPrimitiveType(src, .f128),
1383 .anyerror => return self.emitPrimitiveType(src, .anyerror),
1384 else => switch (ty.zigTypeTag()) {
1385 .Bool => return self.emitPrimitiveType(src, .bool),
1386 .Void => return self.emitPrimitiveType(src, .void),
1387 .NoReturn => return self.emitPrimitiveType(src, .noreturn),
1388 .Type => return self.emitPrimitiveType(src, .type),
1389 .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),
1390 .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),
1391 .Fn => {
1392 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
1393 defer self.allocator.free(param_types);
1394
1395 ty.fnParamTypes(param_types);
1396 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
1397 for (param_types) |param_type, i| {
1398 emitted_params[i] = try self.emitType(src, param_type);
1399 }
1400
1401 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
1402 fntype_inst.* = .{
1403 .base = .{
1404 .name = try self.autoName(),
1405 .src = src,
1406 .tag = Inst.FnType.base_tag,
1407 },
1408 .positionals = .{
1409 .param_types = emitted_params,
1410 .return_type = try self.emitType(src, ty.fnReturnType()),
1411 },
1412 .kw_args = .{
1413 .cc = ty.fnCallingConvention(),
1414 },
1415 };
1416 try self.decls.append(self.allocator, &fntype_inst.base);
1417 return &fntype_inst.base;
1418 },
1419 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1420 },
1421 }
1422 }
1423
1424 fn autoName(self: *EmitZIR) ![]u8 {
1425 return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len});
1426 }
1427
1428 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
1429 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1430 primitive_inst.* = .{
1431 .base = .{
1432 .name = try self.autoName(),
1433 .src = src,
1434 .tag = Inst.Primitive.base_tag,
1435 },
1436 .positionals = .{
1437 .tag = tag,
1438 },
1439 .kw_args = .{},
1440 };
1441 try self.decls.append(self.allocator, &primitive_inst.base);
1442 return &primitive_inst.base;
1443 }
1444
1445 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
1446 const str_inst = try self.arena.allocator.create(Inst.Str);
1447 str_inst.* = .{
1448 .base = .{
1449 .name = try self.autoName(),
1450 .src = src,
1451 .tag = Inst.Str.base_tag,
1452 },
1453 .positionals = .{
1454 .bytes = str,
1455 },
1456 .kw_args = .{},
1457 };
1458 try self.decls.append(self.allocator, &str_inst.base);
1459
1460 const ref_inst = try self.arena.allocator.create(Inst.Ref);
1461 ref_inst.* = .{
1462 .base = .{
1463 .name = try self.autoName(),
1464 .src = src,
1465 .tag = Inst.Ref.base_tag,
1466 },
1467 .positionals = .{
1468 .operand = &str_inst.base,
1469 },
1470 .kw_args = .{},
1471 };
1472 try self.decls.append(self.allocator, &ref_inst.base);
1473
1474 return &ref_inst.base;
1475 }
1476};
src-self-hosted/link.zig+11-10
...@@ -3,6 +3,7 @@ const mem = std.mem;...@@ -3,6 +3,7 @@ const mem = std.mem;
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const ir = @import("ir.zig");5const ir = @import("ir.zig");
6const Module = @import("Module.zig");
6const fs = std.fs;7const fs = std.fs;
7const elf = std.elf;8const elf = std.elf;
8const codegen = @import("codegen.zig");9const codegen = @import("codegen.zig");
...@@ -45,8 +46,8 @@ pub fn writeFilePath(...@@ -45,8 +46,8 @@ pub fn writeFilePath(
45 allocator: *Allocator,46 allocator: *Allocator,
46 dir: fs.Dir,47 dir: fs.Dir,
47 sub_path: []const u8,48 sub_path: []const u8,
48 module: ir.Module,49 module: Module,
49 errors: *std.ArrayList(ir.ErrorMsg),50 errors: *std.ArrayList(Module.ErrorMsg),
50) !void {51) !void {
51 const options: Options = .{52 const options: Options = .{
52 .target = module.target,53 .target = module.target,
...@@ -755,7 +756,7 @@ pub const ElfFile = struct {...@@ -755,7 +756,7 @@ pub const ElfFile = struct {
755 };756 };
756 }757 }
757758
758 pub fn allocateDeclIndexes(self: *ElfFile, decl: *ir.Module.Decl) !void {759 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
759 if (decl.link.local_sym_index != 0) return;760 if (decl.link.local_sym_index != 0) return;
760761
761 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);762 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
...@@ -784,7 +785,7 @@ pub const ElfFile = struct {...@@ -784,7 +785,7 @@ pub const ElfFile = struct {
784 };785 };
785 }786 }
786787
787 pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void {788 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
788 var code_buffer = std.ArrayList(u8).init(self.allocator);789 var code_buffer = std.ArrayList(u8).init(self.allocator);
789 defer code_buffer.deinit();790 defer code_buffer.deinit();
790791
...@@ -878,16 +879,16 @@ pub const ElfFile = struct {...@@ -878,16 +879,16 @@ pub const ElfFile = struct {
878 try self.file.pwriteAll(code, file_offset);879 try self.file.pwriteAll(code, file_offset);
879880
880 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.881 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
881 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*ir.Module.Export{};882 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};
882 return self.updateDeclExports(module, decl, decl_exports);883 return self.updateDeclExports(module, decl, decl_exports);
883 }884 }
884885
885 /// Must be called only after a successful call to `updateDecl`.886 /// Must be called only after a successful call to `updateDecl`.
886 pub fn updateDeclExports(887 pub fn updateDeclExports(
887 self: *ElfFile,888 self: *ElfFile,
888 module: *ir.Module,889 module: *Module,
889 decl: *const ir.Module.Decl,890 decl: *const Module.Decl,
890 exports: []const *ir.Module.Export,891 exports: []const *Module.Export,
891 ) !void {892 ) !void {
892 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);893 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
893 const typed_value = decl.typed_value.most_recent.typed_value;894 const typed_value = decl.typed_value.most_recent.typed_value;
...@@ -900,7 +901,7 @@ pub const ElfFile = struct {...@@ -900,7 +901,7 @@ pub const ElfFile = struct {
900 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);901 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
901 module.failed_exports.putAssumeCapacityNoClobber(902 module.failed_exports.putAssumeCapacityNoClobber(
902 exp,903 exp,
903 try ir.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),904 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
904 );905 );
905 continue;906 continue;
906 }907 }
...@@ -918,7 +919,7 @@ pub const ElfFile = struct {...@@ -918,7 +919,7 @@ pub const ElfFile = struct {
918 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);919 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
919 module.failed_exports.putAssumeCapacityNoClobber(920 module.failed_exports.putAssumeCapacityNoClobber(
920 exp,921 exp,
921 try ir.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),922 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
922 );923 );
923 continue;924 continue;
924 },925 },
src-self-hosted/main.zig+13-12
...@@ -6,9 +6,10 @@ const process = std.process;...@@ -6,9 +6,10 @@ const process = std.process;
6const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;7const ArrayList = std.ArrayList;
8const ast = std.zig.ast;8const ast = std.zig.ast;
9const ir = @import("ir.zig");9const Module = @import("Module.zig");
10const link = @import("link.zig");10const link = @import("link.zig");
11const Package = @import("Package.zig");11const Package = @import("Package.zig");
12const zir = @import("zir.zig");
1213
13const LibCInstallation = @import("libc_installation.zig").LibCInstallation;14const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1415
...@@ -438,7 +439,7 @@ fn buildOutputType(...@@ -438,7 +439,7 @@ fn buildOutputType(
438 const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);439 const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path);
439 errdefer root_pkg.destroy();440 errdefer root_pkg.destroy();
440441
441 const root_scope = try gpa.create(ir.Module.Scope.ZIRModule);442 const root_scope = try gpa.create(Module.Scope.ZIRModule);
442 errdefer gpa.destroy(root_scope);443 errdefer gpa.destroy(root_scope);
443 root_scope.* = .{444 root_scope.* = .{
444 .sub_file_path = root_pkg.root_src_path,445 .sub_file_path = root_pkg.root_src_path,
...@@ -447,19 +448,19 @@ fn buildOutputType(...@@ -447,19 +448,19 @@ fn buildOutputType(
447 .status = .never_loaded,448 .status = .never_loaded,
448 };449 };
449450
450 break :blk ir.Module{451 break :blk Module{
451 .allocator = gpa,452 .allocator = gpa,
452 .root_pkg = root_pkg,453 .root_pkg = root_pkg,
453 .root_scope = root_scope,454 .root_scope = root_scope,
454 .bin_file = &bin_file,455 .bin_file = &bin_file,
455 .optimize_mode = .Debug,456 .optimize_mode = .Debug,
456 .decl_table = std.AutoHashMap(ir.Module.Decl.Hash, *ir.Module.Decl).init(gpa),457 .decl_table = std.AutoHashMap(Module.Decl.Hash, *Module.Decl).init(gpa),
457 .decl_exports = std.AutoHashMap(*ir.Module.Decl, []*ir.Module.Export).init(gpa),458 .decl_exports = std.AutoHashMap(*Module.Decl, []*Module.Export).init(gpa),
458 .export_owners = std.AutoHashMap(*ir.Module.Decl, []*ir.Module.Export).init(gpa),459 .export_owners = std.AutoHashMap(*Module.Decl, []*Module.Export).init(gpa),
459 .failed_decls = std.AutoHashMap(*ir.Module.Decl, *ir.ErrorMsg).init(gpa),460 .failed_decls = std.AutoHashMap(*Module.Decl, *Module.ErrorMsg).init(gpa),
460 .failed_files = std.AutoHashMap(*ir.Module.Scope.ZIRModule, *ir.ErrorMsg).init(gpa),461 .failed_files = std.AutoHashMap(*Module.Scope.ZIRModule, *Module.ErrorMsg).init(gpa),
461 .failed_exports = std.AutoHashMap(*ir.Module.Export, *ir.ErrorMsg).init(gpa),462 .failed_exports = std.AutoHashMap(*Module.Export, *Module.ErrorMsg).init(gpa),
462 .work_queue = std.fifo.LinearFifo(ir.Module.WorkItem, .Dynamic).init(gpa),463 .work_queue = std.fifo.LinearFifo(Module.WorkItem, .Dynamic).init(gpa),
463 };464 };
464 };465 };
465 defer module.deinit();466 defer module.deinit();
...@@ -491,7 +492,7 @@ fn buildOutputType(...@@ -491,7 +492,7 @@ fn buildOutputType(
491 }492 }
492}493}
493494
494fn updateModule(gpa: *Allocator, module: *ir.Module, zir_out_path: ?[]const u8) !void {495fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
495 try module.update();496 try module.update();
496497
497 var errors = try module.getAllErrorsAlloc();498 var errors = try module.getAllErrorsAlloc();
...@@ -509,7 +510,7 @@ fn updateModule(gpa: *Allocator, module: *ir.Module, zir_out_path: ?[]const u8)...@@ -509,7 +510,7 @@ fn updateModule(gpa: *Allocator, module: *ir.Module, zir_out_path: ?[]const u8)
509 }510 }
510511
511 if (zir_out_path) |zop| {512 if (zir_out_path) |zop| {
512 var new_zir_module = try ir.text.emit_zir(gpa, module.*);513 var new_zir_module = try zir.emit(gpa, module.*);
513 defer new_zir_module.deinit(gpa);514 defer new_zir_module.deinit(gpa);
514515
515 const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});516 const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
src-self-hosted/value.zig+3-3
...@@ -6,7 +6,7 @@ const BigIntConst = std.math.big.int.Const;...@@ -6,7 +6,7 @@ const BigIntConst = std.math.big.int.Const;
6const BigIntMutable = std.math.big.int.Mutable;6const BigIntMutable = std.math.big.int.Mutable;
7const Target = std.Target;7const Target = std.Target;
8const Allocator = std.mem.Allocator;8const Allocator = std.mem.Allocator;
9const ir = @import("ir.zig");9const Module = @import("Module.zig");
1010
11/// This is the raw data, with no bookkeeping, no memory awareness,11/// This is the raw data, with no bookkeeping, no memory awareness,
12/// no de-duplication, and no type system awareness.12/// no de-duplication, and no type system awareness.
...@@ -904,7 +904,7 @@ pub const Value = extern union {...@@ -904,7 +904,7 @@ pub const Value = extern union {
904904
905 pub const Function = struct {905 pub const Function = struct {
906 base: Payload = Payload{ .tag = .function },906 base: Payload = Payload{ .tag = .function },
907 func: *ir.Module.Fn,907 func: *Module.Fn,
908 };908 };
909909
910 pub const ArraySentinel0_u8_Type = struct {910 pub const ArraySentinel0_u8_Type = struct {
...@@ -926,7 +926,7 @@ pub const Value = extern union {...@@ -926,7 +926,7 @@ pub const Value = extern union {
926 /// Represents a pointer to a decl, not the value of the decl.926 /// Represents a pointer to a decl, not the value of the decl.
927 pub const DeclRef = struct {927 pub const DeclRef = struct {
928 base: Payload = Payload{ .tag = .decl_ref },928 base: Payload = Payload{ .tag = .decl_ref },
929 decl: *ir.Module.Decl,929 decl: *Module.Decl,
930 };930 };
931931
932 pub const ElemPtr = struct {932 pub const ElemPtr = struct {
src-self-hosted/zir.zig created+1477
...@@ -0,0 +1,1477 @@
1//! This file has to do with parsing and rendering the ZIR text format.
2
3const std = @import("std");
4const mem = std.mem;
5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
9const Type = @import("type.zig").Type;
10const Value = @import("value.zig").Value;
11const TypedValue = @import("TypedValue.zig");
12const ir = @import("ir.zig");
13const IrModule = @import("Module.zig");
14
15/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
16/// in-memory, analyzed instructions with types and values.
17pub const Inst = struct {
18 tag: Tag,
19 /// Byte offset into the source.
20 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{},
25
26 /// These names are used directly as the instruction names in the text format.
27 pub const Tag = enum {
28 breakpoint,
29 call,
30 /// Represents a reference to a global decl by name.
31 /// The syntax `@foo` is equivalent to `declref("foo")`.
32 declref,
33 str,
34 int,
35 ptrtoint,
36 fieldptr,
37 deref,
38 as,
39 @"asm",
40 @"unreachable",
41 @"return",
42 @"fn",
43 @"export",
44 primitive,
45 ref,
46 fntype,
47 intcast,
48 bitcast,
49 elemptr,
50 add,
51 cmp,
52 condbr,
53 isnull,
54 isnonnull,
55 };
56
57 pub fn TagToType(tag: Tag) type {
58 return switch (tag) {
59 .breakpoint => Breakpoint,
60 .call => Call,
61 .declref => DeclRef,
62 .str => Str,
63 .int => Int,
64 .ptrtoint => PtrToInt,
65 .fieldptr => FieldPtr,
66 .deref => Deref,
67 .as => As,
68 .@"asm" => Asm,
69 .@"unreachable" => Unreachable,
70 .@"return" => Return,
71 .@"fn" => Fn,
72 .@"export" => Export,
73 .primitive => Primitive,
74 .ref => Ref,
75 .fntype => FnType,
76 .intcast => IntCast,
77 .bitcast => BitCast,
78 .elemptr => ElemPtr,
79 .add => Add,
80 .cmp => Cmp,
81 .condbr => CondBr,
82 .isnull => IsNull,
83 .isnonnull => IsNonNull,
84 };
85 }
86
87 pub fn cast(base: *Inst, comptime T: type) ?*T {
88 if (base.tag != T.base_tag)
89 return null;
90
91 return @fieldParentPtr(T, "base", base);
92 }
93
94 pub const Breakpoint = struct {
95 pub const base_tag = Tag.breakpoint;
96 base: Inst,
97
98 positionals: struct {},
99 kw_args: struct {},
100 };
101
102 pub const Call = struct {
103 pub const base_tag = Tag.call;
104 base: Inst,
105
106 positionals: struct {
107 func: *Inst,
108 args: []*Inst,
109 },
110 kw_args: struct {
111 modifier: std.builtin.CallOptions.Modifier = .auto,
112 },
113 };
114
115 pub const DeclRef = struct {
116 pub const base_tag = Tag.declref;
117 base: Inst,
118
119 positionals: struct {
120 name: *Inst,
121 },
122 kw_args: struct {},
123 };
124
125 pub const Str = struct {
126 pub const base_tag = Tag.str;
127 base: Inst,
128
129 positionals: struct {
130 bytes: []const u8,
131 },
132 kw_args: struct {},
133 };
134
135 pub const Int = struct {
136 pub const base_tag = Tag.int;
137 base: Inst,
138
139 positionals: struct {
140 int: BigIntConst,
141 },
142 kw_args: struct {},
143 };
144
145 pub const PtrToInt = struct {
146 pub const base_tag = Tag.ptrtoint;
147 base: Inst,
148
149 positionals: struct {
150 ptr: *Inst,
151 },
152 kw_args: struct {},
153 };
154
155 pub const FieldPtr = struct {
156 pub const base_tag = Tag.fieldptr;
157 base: Inst,
158
159 positionals: struct {
160 object_ptr: *Inst,
161 field_name: *Inst,
162 },
163 kw_args: struct {},
164 };
165
166 pub const Deref = struct {
167 pub const base_tag = Tag.deref;
168 base: Inst,
169
170 positionals: struct {
171 ptr: *Inst,
172 },
173 kw_args: struct {},
174 };
175
176 pub const As = struct {
177 pub const base_tag = Tag.as;
178 base: Inst,
179
180 positionals: struct {
181 dest_type: *Inst,
182 value: *Inst,
183 },
184 kw_args: struct {},
185 };
186
187 pub const Asm = struct {
188 pub const base_tag = Tag.@"asm";
189 base: Inst,
190
191 positionals: struct {
192 asm_source: *Inst,
193 return_type: *Inst,
194 },
195 kw_args: struct {
196 @"volatile": bool = false,
197 output: ?*Inst = null,
198 inputs: []*Inst = &[0]*Inst{},
199 clobbers: []*Inst = &[0]*Inst{},
200 args: []*Inst = &[0]*Inst{},
201 },
202 };
203
204 pub const Unreachable = struct {
205 pub const base_tag = Tag.@"unreachable";
206 base: Inst,
207
208 positionals: struct {},
209 kw_args: struct {},
210 };
211
212 pub const Return = struct {
213 pub const base_tag = Tag.@"return";
214 base: Inst,
215
216 positionals: struct {},
217 kw_args: struct {},
218 };
219
220 pub const Fn = struct {
221 pub const base_tag = Tag.@"fn";
222 base: Inst,
223
224 positionals: struct {
225 fn_type: *Inst,
226 body: Module.Body,
227 },
228 kw_args: struct {},
229 };
230
231 pub const Export = struct {
232 pub const base_tag = Tag.@"export";
233 base: Inst,
234
235 positionals: struct {
236 symbol_name: *Inst,
237 value: *Inst,
238 },
239 kw_args: struct {},
240 };
241
242 pub const Ref = struct {
243 pub const base_tag = Tag.ref;
244 base: Inst,
245
246 positionals: struct {
247 operand: *Inst,
248 },
249 kw_args: struct {},
250 };
251
252 pub const Primitive = struct {
253 pub const base_tag = Tag.primitive;
254 base: Inst,
255
256 positionals: struct {
257 tag: BuiltinType,
258 },
259 kw_args: struct {},
260
261 pub const BuiltinType = enum {
262 isize,
263 usize,
264 c_short,
265 c_ushort,
266 c_int,
267 c_uint,
268 c_long,
269 c_ulong,
270 c_longlong,
271 c_ulonglong,
272 c_longdouble,
273 c_void,
274 f16,
275 f32,
276 f64,
277 f128,
278 bool,
279 void,
280 noreturn,
281 type,
282 anyerror,
283 comptime_int,
284 comptime_float,
285
286 fn toType(self: BuiltinType) Type {
287 return switch (self) {
288 .isize => Type.initTag(.isize),
289 .usize => Type.initTag(.usize),
290 .c_short => Type.initTag(.c_short),
291 .c_ushort => Type.initTag(.c_ushort),
292 .c_int => Type.initTag(.c_int),
293 .c_uint => Type.initTag(.c_uint),
294 .c_long => Type.initTag(.c_long),
295 .c_ulong => Type.initTag(.c_ulong),
296 .c_longlong => Type.initTag(.c_longlong),
297 .c_ulonglong => Type.initTag(.c_ulonglong),
298 .c_longdouble => Type.initTag(.c_longdouble),
299 .c_void => Type.initTag(.c_void),
300 .f16 => Type.initTag(.f16),
301 .f32 => Type.initTag(.f32),
302 .f64 => Type.initTag(.f64),
303 .f128 => Type.initTag(.f128),
304 .bool => Type.initTag(.bool),
305 .void => Type.initTag(.void),
306 .noreturn => Type.initTag(.noreturn),
307 .type => Type.initTag(.type),
308 .anyerror => Type.initTag(.anyerror),
309 .comptime_int => Type.initTag(.comptime_int),
310 .comptime_float => Type.initTag(.comptime_float),
311 };
312 }
313 };
314 };
315
316 pub const FnType = struct {
317 pub const base_tag = Tag.fntype;
318 base: Inst,
319
320 positionals: struct {
321 param_types: []*Inst,
322 return_type: *Inst,
323 },
324 kw_args: struct {
325 cc: std.builtin.CallingConvention = .Unspecified,
326 },
327 };
328
329 pub const IntCast = struct {
330 pub const base_tag = Tag.intcast;
331 base: Inst,
332
333 positionals: struct {
334 dest_type: *Inst,
335 value: *Inst,
336 },
337 kw_args: struct {},
338 };
339
340 pub const BitCast = struct {
341 pub const base_tag = Tag.bitcast;
342 base: Inst,
343
344 positionals: struct {
345 dest_type: *Inst,
346 operand: *Inst,
347 },
348 kw_args: struct {},
349 };
350
351 pub const ElemPtr = struct {
352 pub const base_tag = Tag.elemptr;
353 base: Inst,
354
355 positionals: struct {
356 array_ptr: *Inst,
357 index: *Inst,
358 },
359 kw_args: struct {},
360 };
361
362 pub const Add = struct {
363 pub const base_tag = Tag.add;
364 base: Inst,
365
366 positionals: struct {
367 lhs: *Inst,
368 rhs: *Inst,
369 },
370 kw_args: struct {},
371 };
372
373 pub const Cmp = struct {
374 pub const base_tag = Tag.cmp;
375 base: Inst,
376
377 positionals: struct {
378 lhs: *Inst,
379 op: std.math.CompareOperator,
380 rhs: *Inst,
381 },
382 kw_args: struct {},
383 };
384
385 pub const CondBr = struct {
386 pub const base_tag = Tag.condbr;
387 base: Inst,
388
389 positionals: struct {
390 condition: *Inst,
391 true_body: Module.Body,
392 false_body: Module.Body,
393 },
394 kw_args: struct {},
395 };
396
397 pub const IsNull = struct {
398 pub const base_tag = Tag.isnull;
399 base: Inst,
400
401 positionals: struct {
402 operand: *Inst,
403 },
404 kw_args: struct {},
405 };
406
407 pub const IsNonNull = struct {
408 pub const base_tag = Tag.isnonnull;
409 base: Inst,
410
411 positionals: struct {
412 operand: *Inst,
413 },
414 kw_args: struct {},
415 };
416};
417
418pub const ErrorMsg = struct {
419 byte_offset: usize,
420 msg: []const u8,
421};
422
423pub const Module = struct {
424 decls: []*Inst,
425 arena: std.heap.ArenaAllocator,
426 error_msg: ?ErrorMsg = null,
427
428 pub const Body = struct {
429 instructions: []*Inst,
430 };
431
432 pub fn deinit(self: *Module, allocator: *Allocator) void {
433 allocator.free(self.decls);
434 self.arena.deinit();
435 self.* = undefined;
436 }
437
438 /// This is a debugging utility for rendering the tree to stderr.
439 pub fn dump(self: Module) void {
440 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
441 }
442
443 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
444
445 /// The allocator is used for temporary storage, but this function always returns
446 /// with no resources allocated.
447 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
448 // First, build a map of *Inst to @ or % indexes
449 var inst_table = InstPtrTable.init(allocator);
450 defer inst_table.deinit();
451
452 try inst_table.ensureCapacity(self.decls.len);
453
454 for (self.decls) |decl, decl_i| {
455 try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null });
456
457 if (decl.cast(Inst.Fn)) |fn_inst| {
458 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
459 try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body });
460 }
461 }
462 }
463
464 for (self.decls) |decl, i| {
465 try stream.print("@{} ", .{i});
466 try self.writeInstToStream(stream, decl, &inst_table);
467 try stream.writeByte('\n');
468 }
469 }
470
471 fn writeInstToStream(
472 self: Module,
473 stream: var,
474 decl: *Inst,
475 inst_table: *const InstPtrTable,
476 ) @TypeOf(stream).Error!void {
477 // TODO I tried implementing this with an inline for loop and hit a compiler bug
478 switch (decl.tag) {
479 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
480 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
481 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
482 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
483 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
484 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
485 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
486 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
487 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
488 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
489 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
490 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
491 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
492 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
493 .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
494 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
495 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
496 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
497 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
498 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
499 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
500 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
501 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
502 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
503 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
504 }
505 }
506
507 fn writeInstToStreamGeneric(
508 self: Module,
509 stream: var,
510 comptime inst_tag: Inst.Tag,
511 base: *Inst,
512 inst_table: *const InstPtrTable,
513 ) !void {
514 const SpecificInst = Inst.TagToType(inst_tag);
515 const inst = @fieldParentPtr(SpecificInst, "base", base);
516 const Positionals = @TypeOf(inst.positionals);
517 try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
518 const pos_fields = @typeInfo(Positionals).Struct.fields;
519 inline for (pos_fields) |arg_field, i| {
520 if (i != 0) {
521 try stream.writeAll(", ");
522 }
523 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
524 }
525
526 comptime var need_comma = pos_fields.len != 0;
527 const KW_Args = @TypeOf(inst.kw_args);
528 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
529 if (@typeInfo(arg_field.field_type) == .Optional) {
530 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
531 if (need_comma) try stream.writeAll(", ");
532 try stream.print("{}=", .{arg_field.name});
533 try self.writeParamToStream(stream, non_optional, inst_table);
534 need_comma = true;
535 }
536 } else {
537 if (need_comma) try stream.writeAll(", ");
538 try stream.print("{}=", .{arg_field.name});
539 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
540 need_comma = true;
541 }
542 }
543
544 try stream.writeByte(')');
545 }
546
547 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {
548 if (@typeInfo(@TypeOf(param)) == .Enum) {
549 return stream.writeAll(@tagName(param));
550 }
551 switch (@TypeOf(param)) {
552 *Inst => return self.writeInstParamToStream(stream, param, inst_table),
553 []*Inst => {
554 try stream.writeByte('[');
555 for (param) |inst, i| {
556 if (i != 0) {
557 try stream.writeAll(", ");
558 }
559 try self.writeInstParamToStream(stream, inst, inst_table);
560 }
561 try stream.writeByte(']');
562 },
563 Module.Body => {
564 try stream.writeAll("{\n");
565 for (param.instructions) |inst, i| {
566 try stream.print(" %{} ", .{i});
567 try self.writeInstToStream(stream, inst, inst_table);
568 try stream.writeByte('\n');
569 }
570 try stream.writeByte('}');
571 },
572 bool => return stream.writeByte("01"[@boolToInt(param)]),
573 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
574 BigIntConst => return stream.print("{}", .{param}),
575 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
576 }
577 }
578
579 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
580 const info = inst_table.getValue(inst).?;
581 const prefix = if (info.fn_body == null) "@" else "%";
582 try stream.print("{}{}", .{ prefix, info.index });
583 }
584};
585
586pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
587 var global_name_map = std.StringHashMap(usize).init(allocator);
588 defer global_name_map.deinit();
589
590 var parser: Parser = .{
591 .allocator = allocator,
592 .arena = std.heap.ArenaAllocator.init(allocator),
593 .i = 0,
594 .source = source,
595 .global_name_map = &global_name_map,
596 .decls = .{},
597 .unnamed_index = 0,
598 };
599 errdefer parser.arena.deinit();
600
601 parser.parseRoot() catch |err| switch (err) {
602 error.ParseFailure => {
603 assert(parser.error_msg != null);
604 },
605 else => |e| return e,
606 };
607
608 return Module{
609 .decls = parser.decls.toOwnedSlice(allocator),
610 .arena = parser.arena,
611 .error_msg = parser.error_msg,
612 };
613}
614
615const Parser = struct {
616 allocator: *Allocator,
617 arena: std.heap.ArenaAllocator,
618 i: usize,
619 source: [:0]const u8,
620 decls: std.ArrayListUnmanaged(*Inst),
621 global_name_map: *std.StringHashMap(usize),
622 error_msg: ?ErrorMsg = null,
623 unnamed_index: usize,
624
625 const Body = struct {
626 instructions: std.ArrayList(*Inst),
627 name_map: std.StringHashMap(usize),
628 };
629
630 fn parseBody(self: *Parser) !Module.Body {
631 var body_context = Body{
632 .instructions = std.ArrayList(*Inst).init(self.allocator),
633 .name_map = std.StringHashMap(usize).init(self.allocator),
634 };
635 defer body_context.instructions.deinit();
636 defer body_context.name_map.deinit();
637
638 try requireEatBytes(self, "{");
639 skipSpace(self);
640
641 while (true) : (self.i += 1) switch (self.source[self.i]) {
642 ';' => _ = try skipToAndOver(self, '\n'),
643 '%' => {
644 self.i += 1;
645 const ident = try skipToAndOver(self, ' ');
646 skipSpace(self);
647 try requireEatBytes(self, "=");
648 skipSpace(self);
649 const inst = try parseInstruction(self, &body_context, ident);
650 const ident_index = body_context.instructions.items.len;
651 if (try body_context.name_map.put(ident, ident_index)) |_| {
652 return self.fail("redefinition of identifier '{}'", .{ident});
653 }
654 try body_context.instructions.append(inst);
655 continue;
656 },
657 ' ', '\n' => continue,
658 '}' => {
659 self.i += 1;
660 break;
661 },
662 else => |byte| return self.failByte(byte),
663 };
664
665 // Move the instructions to the arena
666 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
667 mem.copy(*Inst, instrs, body_context.instructions.items);
668 return Module.Body{ .instructions = instrs };
669 }
670
671 fn parseStringLiteral(self: *Parser) ![]u8 {
672 const start = self.i;
673 try self.requireEatBytes("\"");
674
675 while (true) : (self.i += 1) switch (self.source[self.i]) {
676 '"' => {
677 self.i += 1;
678 const span = self.source[start..self.i];
679 var bad_index: usize = undefined;
680 const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
681 error.InvalidCharacter => {
682 self.i = start + bad_index;
683 const bad_byte = self.source[self.i];
684 return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
685 },
686 else => |e| return e,
687 };
688 return parsed;
689 },
690 '\\' => {
691 self.i += 1;
692 continue;
693 },
694 0 => return self.failByte(0),
695 else => continue,
696 };
697 }
698
699 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
700 const start = self.i;
701 if (self.source[self.i] == '-') self.i += 1;
702 while (true) : (self.i += 1) switch (self.source[self.i]) {
703 '0'...'9' => continue,
704 else => break,
705 };
706 const number_text = self.source[start..self.i];
707 const base = 10;
708 // TODO reuse the same array list for this
709 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
710 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
711 defer self.allocator.free(limbs_buffer);
712 const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
713 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
714 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
715 result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
716 error.InvalidCharacter => {
717 self.i = start;
718 return self.fail("invalid digit in integer literal", .{});
719 },
720 };
721 return result.toConst();
722 }
723
724 fn parseRoot(self: *Parser) !void {
725 // The IR format is designed so that it can be tokenized and parsed at the same time.
726 while (true) {
727 switch (self.source[self.i]) {
728 ';' => _ = try skipToAndOver(self, '\n'),
729 '@' => {
730 self.i += 1;
731 const ident = try skipToAndOver(self, ' ');
732 skipSpace(self);
733 try requireEatBytes(self, "=");
734 skipSpace(self);
735 const inst = try parseInstruction(self, null, ident);
736 const ident_index = self.decls.items.len;
737 if (try self.global_name_map.put(ident, ident_index)) |_| {
738 return self.fail("redefinition of identifier '{}'", .{ident});
739 }
740 try self.decls.append(self.allocator, inst);
741 },
742 ' ', '\n' => self.i += 1,
743 0 => break,
744 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
745 }
746 }
747 }
748
749 fn eatByte(self: *Parser, byte: u8) bool {
750 if (self.source[self.i] != byte) return false;
751 self.i += 1;
752 return true;
753 }
754
755 fn skipSpace(self: *Parser) void {
756 while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
757 self.i += 1;
758 }
759 }
760
761 fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
762 const start = self.i;
763 for (bytes) |byte| {
764 if (self.source[self.i] != byte) {
765 self.i = start;
766 return self.fail("expected '{}'", .{bytes});
767 }
768 self.i += 1;
769 }
770 }
771
772 fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
773 const start_i = self.i;
774 while (self.source[self.i] != 0) : (self.i += 1) {
775 if (self.source[self.i] == byte) {
776 const result = self.source[start_i..self.i];
777 self.i += 1;
778 return result;
779 }
780 }
781 return self.fail("unexpected EOF", .{});
782 }
783
784 /// ParseFailure is an internal error code; handled in `parse`.
785 const InnerError = error{ ParseFailure, OutOfMemory };
786
787 fn failByte(self: *Parser, byte: u8) InnerError {
788 if (byte == 0) {
789 return self.fail("unexpected EOF", .{});
790 } else {
791 return self.fail("unexpected byte: '{c}'", .{byte});
792 }
793 }
794
795 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
796 @setCold(true);
797 self.error_msg = ErrorMsg{
798 .byte_offset = self.i,
799 .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
800 };
801 return error.ParseFailure;
802 }
803
804 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst {
805 const contents_start = self.i;
806 const fn_name = try skipToAndOver(self, '(');
807 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
808 if (mem.eql(u8, field.name, fn_name)) {
809 const tag = @field(Inst.Tag, field.name);
810 return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx, name, contents_start);
811 }
812 }
813 return self.fail("unknown instruction '{}'", .{fn_name});
814 }
815
816 fn parseInstructionGeneric(
817 self: *Parser,
818 comptime fn_name: []const u8,
819 comptime InstType: type,
820 body_ctx: ?*Body,
821 inst_name: []const u8,
822 contents_start: usize,
823 ) InnerError!*Inst {
824 const inst_specific = try self.arena.allocator.create(InstType);
825 inst_specific.base = .{
826 .name = inst_name,
827 .src = self.i,
828 .tag = InstType.base_tag,
829 };
830
831 if (@hasField(InstType, "ty")) {
832 inst_specific.ty = opt_type orelse {
833 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
834 };
835 }
836
837 const Positionals = @TypeOf(inst_specific.positionals);
838 inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
839 if (self.source[self.i] == ',') {
840 self.i += 1;
841 skipSpace(self);
842 } else if (self.source[self.i] == ')') {
843 return self.fail("expected positional parameter '{}'", .{arg_field.name});
844 }
845 @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
846 self,
847 arg_field.field_type,
848 body_ctx,
849 );
850 skipSpace(self);
851 }
852
853 const KW_Args = @TypeOf(inst_specific.kw_args);
854 inst_specific.kw_args = .{}; // assign defaults
855 skipSpace(self);
856 while (eatByte(self, ',')) {
857 skipSpace(self);
858 const name = try skipToAndOver(self, '=');
859 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
860 const field_name = arg_field.name;
861 if (mem.eql(u8, name, field_name)) {
862 const NonOptional = switch (@typeInfo(arg_field.field_type)) {
863 .Optional => |info| info.child,
864 else => arg_field.field_type,
865 };
866 @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
867 break;
868 }
869 } else {
870 return self.fail("unrecognized keyword parameter: '{}'", .{name});
871 }
872 skipSpace(self);
873 }
874 try requireEatBytes(self, ")");
875
876 inst_specific.base.contents = self.source[contents_start..self.i];
877
878 return &inst_specific.base;
879 }
880
881 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
882 if (@typeInfo(T) == .Enum) {
883 const start = self.i;
884 while (true) : (self.i += 1) switch (self.source[self.i]) {
885 ' ', '\n', ',', ')' => {
886 const enum_name = self.source[start..self.i];
887 return std.meta.stringToEnum(T, enum_name) orelse {
888 return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
889 };
890 },
891 0 => return self.failByte(0),
892 else => continue,
893 };
894 }
895 switch (T) {
896 Module.Body => return parseBody(self),
897 bool => {
898 const bool_value = switch (self.source[self.i]) {
899 '0' => false,
900 '1' => true,
901 else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
902 };
903 self.i += 1;
904 return bool_value;
905 },
906 []*Inst => {
907 try requireEatBytes(self, "[");
908 skipSpace(self);
909 if (eatByte(self, ']')) return &[0]*Inst{};
910
911 var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
912 while (true) {
913 skipSpace(self);
914 try instructions.append(try parseParameterInst(self, body_ctx));
915 skipSpace(self);
916 if (!eatByte(self, ',')) break;
917 }
918 try requireEatBytes(self, "]");
919 return instructions.toOwnedSlice();
920 },
921 *Inst => return parseParameterInst(self, body_ctx),
922 []u8, []const u8 => return self.parseStringLiteral(),
923 BigIntConst => return self.parseIntegerLiteral(),
924 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
925 }
926 return self.fail("TODO parse parameter {}", .{@typeName(T)});
927 }
928
929 fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
930 const local_ref = switch (self.source[self.i]) {
931 '@' => false,
932 '%' => true,
933 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
934 };
935 const map = if (local_ref)
936 if (body_ctx) |bc|
937 &bc.name_map
938 else
939 return self.fail("referencing a % instruction in global scope", .{})
940 else
941 self.global_name_map;
942
943 self.i += 1;
944 const name_start = self.i;
945 while (true) : (self.i += 1) switch (self.source[self.i]) {
946 0, ' ', '\n', ',', ')', ']' => break,
947 else => continue,
948 };
949 const ident = self.source[name_start..self.i];
950 const kv = map.get(ident) orelse {
951 const bad_name = self.source[name_start - 1 .. self.i];
952 const src = name_start - 1;
953 if (local_ref) {
954 self.i = src;
955 return self.fail("unrecognized identifier: {}", .{bad_name});
956 } else {
957 const name = try self.arena.allocator.create(Inst.Str);
958 name.* = .{
959 .base = .{
960 .name = try self.generateName(),
961 .src = src,
962 .tag = Inst.Str.base_tag,
963 },
964 .positionals = .{ .bytes = ident },
965 .kw_args = .{},
966 };
967 const declref = try self.arena.allocator.create(Inst.DeclRef);
968 declref.* = .{
969 .base = .{
970 .name = try self.generateName(),
971 .src = src,
972 .tag = Inst.DeclRef.base_tag,
973 },
974 .positionals = .{ .name = &name.base },
975 .kw_args = .{},
976 };
977 return &declref.base;
978 }
979 };
980 if (local_ref) {
981 return body_ctx.?.instructions.items[kv.value];
982 } else {
983 return self.decls.items[kv.value];
984 }
985 }
986
987 fn generateName(self: *Parser) ![]u8 {
988 const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index});
989 self.unnamed_index += 1;
990 return result;
991 }
992};
993
994pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
995 var ctx: EmitZIR = .{
996 .allocator = allocator,
997 .decls = .{},
998 .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator),
999 .arena = std.heap.ArenaAllocator.init(allocator),
1000 .old_module = &old_module,
1001 };
1002 defer ctx.decls.deinit(allocator);
1003 defer ctx.decl_table.deinit();
1004 errdefer ctx.arena.deinit();
1005
1006 try ctx.emit();
1007
1008 return Module{
1009 .decls = ctx.decls.toOwnedSlice(allocator),
1010 .arena = ctx.arena,
1011 };
1012}
1013
1014const EmitZIR = struct {
1015 allocator: *Allocator,
1016 arena: std.heap.ArenaAllocator,
1017 old_module: *const IrModule,
1018 decls: std.ArrayListUnmanaged(*Inst),
1019 decl_table: std.AutoHashMap(*ir.Inst, *Inst),
1020
1021 fn emit(self: *EmitZIR) !void {
1022 var it = self.old_module.decl_exports.iterator();
1023 while (it.next()) |kv| {
1024 const decl = kv.key;
1025 const exports = kv.value;
1026 const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value);
1027 for (exports) |module_export| {
1028 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
1029 const export_inst = try self.arena.allocator.create(Inst.Export);
1030 export_inst.* = .{
1031 .base = .{
1032 .name = try self.autoName(),
1033 .src = module_export.src,
1034 .tag = Inst.Export.base_tag,
1035 },
1036 .positionals = .{
1037 .symbol_name = symbol_name,
1038 .value = export_value,
1039 },
1040 .kw_args = .{},
1041 };
1042 try self.decls.append(self.allocator, &export_inst.base);
1043 }
1044 }
1045 }
1046
1047 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
1048 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1049 if (self.decl_table.getValue(inst)) |decl| {
1050 return decl;
1051 }
1052 const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1053 try self.decl_table.putNoClobber(inst, new_decl);
1054 return new_decl;
1055 } else {
1056 return inst_table.getValue(inst).?;
1057 }
1058 }
1059
1060 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
1061 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
1062 const int_inst = try self.arena.allocator.create(Inst.Int);
1063 int_inst.* = .{
1064 .base = .{
1065 .name = try self.autoName(),
1066 .src = src,
1067 .tag = Inst.Int.base_tag,
1068 },
1069 .positionals = .{
1070 .int = val.toBigInt(big_int_space),
1071 },
1072 .kw_args = .{},
1073 };
1074 try self.decls.append(self.allocator, &int_inst.base);
1075 return &int_inst.base;
1076 }
1077
1078 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {
1079 const allocator = &self.arena.allocator;
1080 switch (typed_value.ty.zigTypeTag()) {
1081 .Pointer => {
1082 const ptr_elem_type = typed_value.ty.elemType();
1083 switch (ptr_elem_type.zigTypeTag()) {
1084 .Array => {
1085 // TODO more checks to make sure this can be emitted as a string literal
1086 //const array_elem_type = ptr_elem_type.elemType();
1087 //if (array_elem_type.eql(Type.initTag(.u8)) and
1088 // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
1089 //{
1090 //}
1091 const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
1092 error.AnalysisFail => unreachable,
1093 else => |e| return e,
1094 };
1095 return self.emitStringLiteral(src, bytes);
1096 },
1097 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
1098 }
1099 },
1100 .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
1101 .Int => {
1102 const as_inst = try self.arena.allocator.create(Inst.As);
1103 as_inst.* = .{
1104 .base = .{
1105 .name = try self.autoName(),
1106 .src = src,
1107 .tag = Inst.As.base_tag,
1108 },
1109 .positionals = .{
1110 .dest_type = try self.emitType(src, typed_value.ty),
1111 .value = try self.emitComptimeIntVal(src, typed_value.val),
1112 },
1113 .kw_args = .{},
1114 };
1115 try self.decls.append(self.allocator, &as_inst.base);
1116
1117 return &as_inst.base;
1118 },
1119 .Type => {
1120 const ty = typed_value.val.toType();
1121 return self.emitType(src, ty);
1122 },
1123 .Fn => {
1124 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
1125
1126 var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
1127 defer inst_table.deinit();
1128
1129 var instructions = std.ArrayList(*Inst).init(self.allocator);
1130 defer instructions.deinit();
1131
1132 try self.emitBody(module_fn.analysis.success, &inst_table, &instructions);
1133
1134 const fn_type = try self.emitType(src, module_fn.fn_type);
1135
1136 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1137 mem.copy(*Inst, arena_instrs, instructions.items);
1138
1139 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1140 fn_inst.* = .{
1141 .base = .{
1142 .name = try self.autoName(),
1143 .src = src,
1144 .tag = Inst.Fn.base_tag,
1145 },
1146 .positionals = .{
1147 .fn_type = fn_type,
1148 .body = .{ .instructions = arena_instrs },
1149 },
1150 .kw_args = .{},
1151 };
1152 try self.decls.append(self.allocator, &fn_inst.base);
1153 return &fn_inst.base;
1154 },
1155 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
1156 }
1157 }
1158
1159 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1160 const new_inst = try self.arena.allocator.create(T);
1161 new_inst.* = .{
1162 .base = .{
1163 .name = try self.autoName(),
1164 .src = src,
1165 .tag = T.base_tag,
1166 },
1167 .positionals = .{},
1168 .kw_args = .{},
1169 };
1170 return &new_inst.base;
1171 }
1172
1173 fn emitBody(
1174 self: *EmitZIR,
1175 body: IrModule.Body,
1176 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1177 instructions: *std.ArrayList(*Inst),
1178 ) Allocator.Error!void {
1179 for (body.instructions) |inst| {
1180 const new_inst = switch (inst.tag) {
1181 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1182 .call => blk: {
1183 const old_inst = inst.cast(ir.Inst.Call).?;
1184 const new_inst = try self.arena.allocator.create(Inst.Call);
1185
1186 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1187 for (args) |*elem, i| {
1188 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1189 }
1190 new_inst.* = .{
1191 .base = .{
1192 .name = try self.autoName(),
1193 .src = inst.src,
1194 .tag = Inst.Call.base_tag,
1195 },
1196 .positionals = .{
1197 .func = try self.resolveInst(inst_table, old_inst.args.func),
1198 .args = args,
1199 },
1200 .kw_args = .{},
1201 };
1202 break :blk &new_inst.base;
1203 },
1204 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1205 .ret => try self.emitTrivial(inst.src, Inst.Return),
1206 .constant => unreachable, // excluded from function bodies
1207 .assembly => blk: {
1208 const old_inst = inst.cast(ir.Inst.Assembly).?;
1209 const new_inst = try self.arena.allocator.create(Inst.Asm);
1210
1211 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1212 for (inputs) |*elem, i| {
1213 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
1214 }
1215
1216 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1217 for (clobbers) |*elem, i| {
1218 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
1219 }
1220
1221 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1222 for (args) |*elem, i| {
1223 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1224 }
1225
1226 new_inst.* = .{
1227 .base = .{
1228 .name = try self.autoName(),
1229 .src = inst.src,
1230 .tag = Inst.Asm.base_tag,
1231 },
1232 .positionals = .{
1233 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
1234 .return_type = try self.emitType(inst.src, inst.ty),
1235 },
1236 .kw_args = .{
1237 .@"volatile" = old_inst.args.is_volatile,
1238 .output = if (old_inst.args.output) |o|
1239 try self.emitStringLiteral(inst.src, o)
1240 else
1241 null,
1242 .inputs = inputs,
1243 .clobbers = clobbers,
1244 .args = args,
1245 },
1246 };
1247 break :blk &new_inst.base;
1248 },
1249 .ptrtoint => blk: {
1250 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1251 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1252 new_inst.* = .{
1253 .base = .{
1254 .name = try self.autoName(),
1255 .src = inst.src,
1256 .tag = Inst.PtrToInt.base_tag,
1257 },
1258 .positionals = .{
1259 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
1260 },
1261 .kw_args = .{},
1262 };
1263 break :blk &new_inst.base;
1264 },
1265 .bitcast => blk: {
1266 const old_inst = inst.cast(ir.Inst.BitCast).?;
1267 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1268 new_inst.* = .{
1269 .base = .{
1270 .name = try self.autoName(),
1271 .src = inst.src,
1272 .tag = Inst.BitCast.base_tag,
1273 },
1274 .positionals = .{
1275 .dest_type = try self.emitType(inst.src, inst.ty),
1276 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1277 },
1278 .kw_args = .{},
1279 };
1280 break :blk &new_inst.base;
1281 },
1282 .cmp => blk: {
1283 const old_inst = inst.cast(ir.Inst.Cmp).?;
1284 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1285 new_inst.* = .{
1286 .base = .{
1287 .name = try self.autoName(),
1288 .src = inst.src,
1289 .tag = Inst.Cmp.base_tag,
1290 },
1291 .positionals = .{
1292 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
1293 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
1294 .op = old_inst.args.op,
1295 },
1296 .kw_args = .{},
1297 };
1298 break :blk &new_inst.base;
1299 },
1300 .condbr => blk: {
1301 const old_inst = inst.cast(ir.Inst.CondBr).?;
1302
1303 var true_body = std.ArrayList(*Inst).init(self.allocator);
1304 var false_body = std.ArrayList(*Inst).init(self.allocator);
1305
1306 defer true_body.deinit();
1307 defer false_body.deinit();
1308
1309 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1310 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1311
1312 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1313 new_inst.* = .{
1314 .base = .{
1315 .name = try self.autoName(),
1316 .src = inst.src,
1317 .tag = Inst.CondBr.base_tag,
1318 },
1319 .positionals = .{
1320 .condition = try self.resolveInst(inst_table, old_inst.args.condition),
1321 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1322 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1323 },
1324 .kw_args = .{},
1325 };
1326 break :blk &new_inst.base;
1327 },
1328 .isnull => blk: {
1329 const old_inst = inst.cast(ir.Inst.IsNull).?;
1330 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1331 new_inst.* = .{
1332 .base = .{
1333 .name = try self.autoName(),
1334 .src = inst.src,
1335 .tag = Inst.IsNull.base_tag,
1336 },
1337 .positionals = .{
1338 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1339 },
1340 .kw_args = .{},
1341 };
1342 break :blk &new_inst.base;
1343 },
1344 .isnonnull => blk: {
1345 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1346 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1347 new_inst.* = .{
1348 .base = .{
1349 .name = try self.autoName(),
1350 .src = inst.src,
1351 .tag = Inst.IsNonNull.base_tag,
1352 },
1353 .positionals = .{
1354 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1355 },
1356 .kw_args = .{},
1357 };
1358 break :blk &new_inst.base;
1359 },
1360 };
1361 try instructions.append(new_inst);
1362 try inst_table.putNoClobber(inst, new_inst);
1363 }
1364 }
1365
1366 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1367 switch (ty.tag()) {
1368 .isize => return self.emitPrimitiveType(src, .isize),
1369 .usize => return self.emitPrimitiveType(src, .usize),
1370 .c_short => return self.emitPrimitiveType(src, .c_short),
1371 .c_ushort => return self.emitPrimitiveType(src, .c_ushort),
1372 .c_int => return self.emitPrimitiveType(src, .c_int),
1373 .c_uint => return self.emitPrimitiveType(src, .c_uint),
1374 .c_long => return self.emitPrimitiveType(src, .c_long),
1375 .c_ulong => return self.emitPrimitiveType(src, .c_ulong),
1376 .c_longlong => return self.emitPrimitiveType(src, .c_longlong),
1377 .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong),
1378 .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble),
1379 .c_void => return self.emitPrimitiveType(src, .c_void),
1380 .f16 => return self.emitPrimitiveType(src, .f16),
1381 .f32 => return self.emitPrimitiveType(src, .f32),
1382 .f64 => return self.emitPrimitiveType(src, .f64),
1383 .f128 => return self.emitPrimitiveType(src, .f128),
1384 .anyerror => return self.emitPrimitiveType(src, .anyerror),
1385 else => switch (ty.zigTypeTag()) {
1386 .Bool => return self.emitPrimitiveType(src, .bool),
1387 .Void => return self.emitPrimitiveType(src, .void),
1388 .NoReturn => return self.emitPrimitiveType(src, .noreturn),
1389 .Type => return self.emitPrimitiveType(src, .type),
1390 .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int),
1391 .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float),
1392 .Fn => {
1393 const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
1394 defer self.allocator.free(param_types);
1395
1396 ty.fnParamTypes(param_types);
1397 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
1398 for (param_types) |param_type, i| {
1399 emitted_params[i] = try self.emitType(src, param_type);
1400 }
1401
1402 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
1403 fntype_inst.* = .{
1404 .base = .{
1405 .name = try self.autoName(),
1406 .src = src,
1407 .tag = Inst.FnType.base_tag,
1408 },
1409 .positionals = .{
1410 .param_types = emitted_params,
1411 .return_type = try self.emitType(src, ty.fnReturnType()),
1412 },
1413 .kw_args = .{
1414 .cc = ty.fnCallingConvention(),
1415 },
1416 };
1417 try self.decls.append(self.allocator, &fntype_inst.base);
1418 return &fntype_inst.base;
1419 },
1420 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1421 },
1422 }
1423 }
1424
1425 fn autoName(self: *EmitZIR) ![]u8 {
1426 return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len});
1427 }
1428
1429 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
1430 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1431 primitive_inst.* = .{
1432 .base = .{
1433 .name = try self.autoName(),
1434 .src = src,
1435 .tag = Inst.Primitive.base_tag,
1436 },
1437 .positionals = .{
1438 .tag = tag,
1439 },
1440 .kw_args = .{},
1441 };
1442 try self.decls.append(self.allocator, &primitive_inst.base);
1443 return &primitive_inst.base;
1444 }
1445
1446 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
1447 const str_inst = try self.arena.allocator.create(Inst.Str);
1448 str_inst.* = .{
1449 .base = .{
1450 .name = try self.autoName(),
1451 .src = src,
1452 .tag = Inst.Str.base_tag,
1453 },
1454 .positionals = .{
1455 .bytes = str,
1456 },
1457 .kw_args = .{},
1458 };
1459 try self.decls.append(self.allocator, &str_inst.base);
1460
1461 const ref_inst = try self.arena.allocator.create(Inst.Ref);
1462 ref_inst.* = .{
1463 .base = .{
1464 .name = try self.autoName(),
1465 .src = src,
1466 .tag = Inst.Ref.base_tag,
1467 },
1468 .positionals = .{
1469 .operand = &str_inst.base,
1470 },
1471 .kw_args = .{},
1472 };
1473 try self.decls.append(self.allocator, &ref_inst.base);
1474
1475 return &ref_inst.base;
1476 }
1477};