authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-12 01:02:48-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-12 01:02:48-04:00
log619159cf48e953ca65933391313a72c392007710
tree315cb12cff807374cd8fbbad3c06107241633565
parenta32d3a85d21d614e5960b9eadcd85374954b910f

self-hosted: rework the memory layout of ir.Module and related types

* add TypedValue.Managed which represents a Type, a Value, and some kind of memory management strategy. * introduce an analysis queue * flesh out how incremental compilation works with respect to exports * ir.text.Module is only capable of one error message during parsing * link.zig no longer has a decl table map and instead has structs that exist directly on ir.Module.Decl and ir.Module.Export * implement primitive .text block allocation * implement linker code for updating Decls and Exports * implement null Type Some supporting std lib changes: * add std.ArrayList.appendSliceAssumeCapacity * add std.fs.File.copyRange and copyRangeAll * fix std.HashMap having modification safety on in ReleaseSmall builds * add std.HashMap.putAssumeCapacityNoClobber

9 files changed, 651 insertions(+), 276 deletions(-)

lib/std/array_list.zig+14-3
...@@ -149,10 +149,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -149,10 +149,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
149 /// Append the slice of items to the list. Allocates more149 /// Append the slice of items to the list. Allocates more
150 /// memory as necessary.150 /// memory as necessary.
151 pub fn appendSlice(self: *Self, items: SliceConst) !void {151 pub fn appendSlice(self: *Self, items: SliceConst) !void {
152 try self.ensureCapacity(self.items.len + items.len);
153 self.appendSliceAssumeCapacity(items);
154 }
155
156 /// Append the slice of items to the list, asserting the capacity is already
157 /// enough to store the new items.
158 pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void {
152 const oldlen = self.items.len;159 const oldlen = self.items.len;
153 const newlen = self.items.len + items.len;160 const newlen = self.items.len + items.len;
154
155 try self.ensureCapacity(newlen);
156 self.items.len = newlen;161 self.items.len = newlen;
157 mem.copy(T, self.items[oldlen..], items);162 mem.copy(T, self.items[oldlen..], items);
158 }163 }
...@@ -378,10 +383,16 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -378,10 +383,16 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
378 /// Append the slice of items to the list. Allocates more383 /// Append the slice of items to the list. Allocates more
379 /// memory as necessary.384 /// memory as necessary.
380 pub fn appendSlice(self: *Self, allocator: *Allocator, items: SliceConst) !void {385 pub fn appendSlice(self: *Self, allocator: *Allocator, items: SliceConst) !void {
386 try self.ensureCapacity(allocator, self.items.len + items.len);
387 self.appendSliceAssumeCapacity(items);
388 }
389
390 /// Append the slice of items to the list, asserting the capacity is enough
391 /// to store the new items.
392 pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void {
381 const oldlen = self.items.len;393 const oldlen = self.items.len;
382 const newlen = self.items.len + items.len;394 const newlen = self.items.len + items.len;
383395
384 try self.ensureCapacity(allocator, newlen);
385 self.items.len = newlen;396 self.items.len = newlen;
386 mem.copy(T, self.items[oldlen..], items);397 mem.copy(T, self.items[oldlen..], items);
387 }398 }
lib/std/fs/file.zig+24
...@@ -527,6 +527,30 @@ pub const File = struct {...@@ -527,6 +527,30 @@ pub const File = struct {
527 }527 }
528 }528 }
529529
530 pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) PWriteError!usize {
531 // TODO take advantage of copy_file_range OS APIs
532 var buf: [8 * 4096]u8 = undefined;
533 const adjusted_count = math.min(buf.len, len);
534 const amt_read = try in.pread(buf[0..adjusted_count], in_offset);
535 if (amt_read == 0) return 0;
536 return out.pwrite(buf[0..amt_read], out_offset);
537 }
538
539 /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it
540 /// means the in file reached the end. Reaching the end of a file is not an error condition.
541 pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) PWriteError!usize {
542 var total_bytes_copied = 0;
543 var in_off = in_offset;
544 var out_off = out_offset;
545 while (total_bytes_copied < len) {
546 const amt_copied = try copyRange(in, in_off, out, out_off, len - total_bytes_copied);
547 if (amt_copied == 0) return total_bytes_copied;
548 total_bytes_copied += amt_copied;
549 in_off += amt_copied;
550 out_off += amt_copied;
551 }
552 }
553
530 pub const WriteFileOptions = struct {554 pub const WriteFileOptions = struct {
531 in_offset: u64 = 0,555 in_offset: u64 = 0,
532556
lib/std/hash_map.zig+5-1
...@@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash;...@@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash;
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
11const builtin = @import("builtin");11const builtin = @import("builtin");
1212
13const want_modification_safety = builtin.mode != .ReleaseFast;13const want_modification_safety = std.debug.runtime_safety;
14const debug_u32 = if (want_modification_safety) u32 else void;14const debug_u32 = if (want_modification_safety) u32 else void;
1515
16pub fn AutoHashMap(comptime K: type, comptime V: type) type {16pub fn AutoHashMap(comptime K: type, comptime V: type) type {
...@@ -219,6 +219,10 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -219,6 +219,10 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
219 return put_result.old_kv;219 return put_result.old_kv;
220 }220 }
221221
222 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
223 assert(self.putAssumeCapacity(key, value) == null);
224 }
225
222 pub fn get(hm: *const Self, key: K) ?*KV {226 pub fn get(hm: *const Self, key: K) ?*KV {
223 if (hm.entries.len == 0) {227 if (hm.entries.len == 0) {
224 return null;228 return null;
src-self-hosted/TypedValue.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2const Type = @import("type.zig").Type;
3const Value = @import("value.zig").Value;
4const Allocator = std.mem.Allocator;
5const TypedValue = @This();
6
7ty: Type,
8val: Value,
9
10/// Memory management for TypedValue. The main purpose of this type
11/// is to be small and have a deinit() function to free associated resources.
12pub const Managed = struct {
13 /// If the tag value is less than Tag.no_payload_count, then no pointer
14 /// dereference is needed.
15 typed_value: TypedValue,
16 /// If this is `null` then there is no memory management needed.
17 arena: ?*std.heap.ArenaAllocator.State = null,
18
19 pub fn deinit(self: *ManagedTypedValue, allocator: *Allocator) void {
20 if (self.arena) |a| a.promote(allocator).deinit();
21 self.* = undefined;
22 }
23};
src-self-hosted/ir.zig+320-132
...@@ -5,6 +5,7 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;...@@ -5,6 +5,7 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;
5const LinkedList = std.TailQueue;5const LinkedList = std.TailQueue;
6const Value = @import("value.zig").Value;6const Value = @import("value.zig").Value;
7const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
8const TypedValue = @import("TypedValue.zig");
8const assert = std.debug.assert;9const assert = std.debug.assert;
9const BigIntConst = std.math.big.int.Const;10const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;11const BigIntMutable = std.math.big.int.Mutable;
...@@ -167,11 +168,6 @@ pub const Inst = struct {...@@ -167,11 +168,6 @@ pub const Inst = struct {
167 };168 };
168};169};
169170
170pub const TypedValue = struct {
171 ty: Type,
172 val: Value,
173};
174
175fn swapRemoveElem(allocator: *Allocator, comptime T: type, item: T, list: *ArrayListUnmanaged(T)) void {171fn swapRemoveElem(allocator: *Allocator, comptime T: type, item: T, list: *ArrayListUnmanaged(T)) void {
176 var i: usize = 0;172 var i: usize = 0;
177 while (i < list.items.len) {173 while (i < list.items.len) {
...@@ -192,46 +188,125 @@ pub const Module = struct {...@@ -192,46 +188,125 @@ pub const Module = struct {
192 root_scope: *Scope.ZIRModule,188 root_scope: *Scope.ZIRModule,
193 /// Pointer to externally managed resource.189 /// Pointer to externally managed resource.
194 bin_file: *link.ElfFile,190 bin_file: *link.ElfFile,
195 failed_decls: ArrayListUnmanaged(*Decl) = .{},191 /// It's rare for a decl to be exported, so we save memory by having a sparse map of
196 failed_fns: ArrayListUnmanaged(*Fn) = .{},192 /// Decl pointers to details about them being exported.
197 failed_files: ArrayListUnmanaged(*Scope.ZIRModule) = .{},193 /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
194 decl_exports: std.AutoHashMap(*Decl, []*Export),
195 /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
196 /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
197 /// is performing the export of another Decl.
198 /// This table owns the Export memory.
199 export_owners: std.AutoHashMap(*Decl, []*Export),
200 /// Maps fully qualified namespaced names to the Decl struct for them.
198 decl_table: std.AutoHashMap(Decl.Hash, *Decl),201 decl_table: std.AutoHashMap(Decl.Hash, *Decl),
202
199 optimize_mode: std.builtin.Mode,203 optimize_mode: std.builtin.Mode,
200 link_error_flags: link.ElfFile.ErrorFlags = .{},204 link_error_flags: link.ElfFile.ErrorFlags = .{},
201205
206 /// We optimize memory usage for a compilation with no compile errors by storing the
207 /// error messages and mapping outside of `Decl`.
208 /// The ErrorMsg memory is owned by the decl, using Module's allocator.
209 failed_decls: std.AutoHashMap(*Decl, *ErrorMsg),
210 /// We optimize memory usage for a compilation with no compile errors by storing the
211 /// error messages and mapping outside of `Fn`.
212 /// The ErrorMsg memory is owned by the `Fn`, using Module's allocator.
213 failed_fns: std.AutoHashMap(*Fn, *ErrorMsg),
214 /// Using a map here for consistency with the other fields here.
215 /// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator.
216 failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
217 /// Using a map here for consistency with the other fields here.
218 /// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
219 failed_exports: std.AutoHashMap(*Export, *ErrorMsg),
220
221 pub const Export = struct {
222 options: std.builtin.ExportOptions,
223 /// Byte offset into the file that contains the export directive.
224 src: usize,
225 /// Represents the position of the export, if any, in the output file.
226 link: link.ElfFile.Export,
227 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
228 owner_decl: *Decl,
229 status: enum { in_progress, failed, complete },
230 };
231
202 pub const Decl = struct {232 pub const Decl = struct {
203 /// Contains the memory for `typed_value` and this `Decl` itself.
204 /// If the Decl is a function, also contains that memory.
205 /// If the decl has any export nodes, also contains that memory.
206 /// TODO look into using a more memory efficient arena that will cost less bytes per decl.
207 /// This one has a minimum allocation of 4096 bytes.
208 arena: std.heap.ArenaAllocator.State,
209 /// This name is relative to the containing namespace of the decl. It uses a null-termination233 /// This name is relative to the containing namespace of the decl. It uses a null-termination
210 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed234 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
211 /// in symbol names, because executable file formats use null-terminated strings for symbol names.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.
212 name: [*:0]const u8,239 name: [*:0]const u8,
213 /// It's rare for a decl to be exported, and it's even rarer for a decl to be mapped to more240 /// The direct parent container of the Decl. This field will need to get more fleshed out when
214 /// than one export, so we use a linked list to save memory.241 /// self-hosted supports proper struct types and Zig AST => ZIR.
215 export_node: ?*LinkedList(std.builtin.ExportOptions).Node = null,242 /// Reference to externally owned memory.
243 scope: *Scope.ZIRModule,
216 /// Byte offset into the source file that contains this declaration.244 /// Byte offset into the source file that contains this declaration.
217 /// This is the base offset that src offsets within this Decl are relative to.245 /// This is the base offset that src offsets within this Decl are relative to.
218 src: usize,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,
251 most_recent: TypedValue.Managed,
252 },
219 /// Represents the "shallow" analysis status. For example, for decls that are functions,253 /// Represents the "shallow" analysis status. For example, for decls that are functions,
220 /// the function type is analyzed with this set to `in_progress`, however, the semantic254 /// the function type is analyzed with this set to `in_progress`, however, the semantic
221 /// analysis of the function body is performed with this value set to `success`. Functions255 /// analysis of the function body is performed with this value set to `success`. Functions
222 /// have their own analysis status field.256 /// have their own analysis status field.
223 analysis: union(enum) {257 analysis: enum {
224 in_progress,258 initial_in_progress,
225 failure: ErrorMsg,259 /// This Decl might be OK but it depends on another one which did not successfully complete
226 success: TypedValue,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 /// This Decl might be OK but it depends on another one which did not successfully complete
269 /// semantic analysis. There is a most recent value available.
270 repeat_dependency_failure,
271 /// Semantic anlaysis failure, but the `typed_value.most_recent` can be accessed.
272 /// There will be a corresponding ErrorMsg in Module.failed_decls.
273 repeat_sema_failure,
274 /// Completed successfully before; the `typed_value.most_recent` can be accessed, and
275 /// new semantic analysis is in progress.
276 repeat_in_progress,
277 /// Everything is done and updated.
278 complete,
227 },279 },
228 /// The direct container of the Decl. This field will need to get more fleshed out when280
229 /// self-hosted supports proper struct types and Zig AST => ZIR.281 /// Represents the position of the code, if any, in the output file.
230 scope: *Scope.ZIRModule,282 /// This is populated regardless of semantic analysis and code generation.
283 /// This value is `undefined` if the type has no runtime bits.
284 link: link.ElfFile.Decl,
285
286 /// The set of other decls whose typed_value could possibly change if this Decl's
287 /// typed_value is modified.
288 /// TODO look into using a lightweight map/set data structure rather than a linear array.
289 dependants: ArrayListUnmanaged(*Decl) = .{},
290
291 pub fn typedValue(self: Decl) ?TypedValue {
292 switch (self.analysis) {
293 .initial_in_progress,
294 .initial_dependency_failure,
295 .initial_sema_failure,
296 => return null,
297 .codegen_failure,
298 .repeat_dependency_failure,
299 .repeat_sema_failure,
300 .repeat_in_progress,
301 .complete,
302 => return self.typed_value.most_recent,
303 }
304 }
231305
232 pub fn destroy(self: *Decl, allocator: *Allocator) void {306 pub fn destroy(self: *Decl, allocator: *Allocator) void {
233 var arena = self.arena.promote(allocator);307 allocator.free(mem.spanZ(u8, self.name));
234 arena.deinit();308 if (self.typedValue()) |tv| tv.deinit(allocator);
309 allocator.destroy(self);
235 }310 }
236311
237 pub const Hash = [16]u8;312 pub const Hash = [16]u8;
...@@ -252,8 +327,10 @@ pub const Module = struct {...@@ -252,8 +327,10 @@ pub const Module = struct {
252 pub const Fn = struct {327 pub const Fn = struct {
253 fn_type: Type,328 fn_type: Type,
254 analysis: union(enum) {329 analysis: union(enum) {
330 queued,
255 in_progress: *Analysis,331 in_progress: *Analysis,
256 failure: ErrorMsg,332 /// There will be a corresponding ErrorMsg in Module.failed_fns
333 failure,
257 success: Body,334 success: Body,
258 },335 },
259 /// The direct container of the Fn. This field will need to get more fleshed out when336 /// The direct container of the Fn. This field will need to get more fleshed out when
...@@ -290,68 +367,36 @@ pub const Module = struct {...@@ -290,68 +367,36 @@ pub const Module = struct {
290 /// Relative to the owning package's root_src_dir.367 /// Relative to the owning package's root_src_dir.
291 /// Reference to external memory, not owned by ZIRModule.368 /// Reference to external memory, not owned by ZIRModule.
292 sub_file_path: []const u8,369 sub_file_path: []const u8,
293 contents: union(enum) {370 source: union {
294 unloaded,371 unloaded,
295 parse_failure: ParseFailure,372 bytes: [:0]const u8,
296 success: Contents,
297 },373 },
298 pub const ParseFailure = struct {374 contents: union {
299 source: [:0]const u8,375 not_available,
300 errors: []ErrorMsg,
301
302 pub fn deinit(self: *ParseFailure, allocator: *Allocator) void {
303 allocator.free(self.errors);
304 allocator.free(source);
305 }
306 };
307 pub const Contents = struct {
308 source: [:0]const u8,
309 module: *text.Module,376 module: *text.Module,
310 };377 },
378 status: enum {
379 unloaded,
380 unloaded_parse_failure,
381 loaded_parse_failure,
382 loaded_success,
383 },
311384
312 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {385 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
313 switch (self.contents) {386 switch (self.status) {
314 .unloaded => {},387 .unloaded,
315 .parse_failure => |pf| pd.deinit(allocator),388 .unloaded_parse_failure,
316 .success => |contents| {389 => {},
390 .loaded_success => {
391 allocator.free(contents.source);
392 self.contents.module.deinit(allocator);
393 },
394 .loaded_parse_failure => {
317 allocator.free(contents.source);395 allocator.free(contents.source);
318 contents.src_zir_module.deinit(allocator);
319 },396 },
320 }397 }
321 self.* = undefined;398 self.* = undefined;
322 }399 }
323
324 pub fn loadContents(self: *ZIRModule, allocator: *Allocator) !*Contents {
325 if (self.contents) |contents| return contents;
326
327 const max_size = std.math.maxInt(u32);
328 const source = try self.root_pkg_dir.readFileAllocOptions(allocator, self.root_src_path, max_size, 1, 0);
329 errdefer allocator.free(source);
330
331 var errors = std.ArrayList(ErrorMsg).init(allocator);
332 defer errors.deinit();
333
334 var src_zir_module = try text.parse(allocator, source, &errors);
335 errdefer src_zir_module.deinit(allocator);
336
337 switch (self.contents) {
338 .parse_failure => |pf| pf.deinit(allocator),
339 .unloaded => {},
340 .success => unreachable,
341 }
342
343 if (errors.items.len != 0) {
344 self.contents = .{ .parse_failure = errors.toOwnedSlice() };
345 return error.ParseFailure;
346 }
347 self.contents = .{
348 .success = .{
349 .source = source,
350 .module = src_zir_module,
351 },
352 };
353 return &self.contents.success;
354 }
355 };400 };
356401
357 /// This is a temporary structure, references to it are valid only402 /// This is a temporary structure, references to it are valid only
...@@ -436,7 +481,7 @@ pub const Module = struct {...@@ -436,7 +481,7 @@ pub const Module = struct {
436 // Analyze the root source file now.481 // Analyze the root source file now.
437 self.analyzeRoot(self.root_scope) catch |err| switch (err) {482 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
438 error.AnalysisFail => {483 error.AnalysisFail => {
439 assert(self.totalErrorCount() != 0);484 assert(self.failed_files.size != 0);
440 },485 },
441 else => |e| return e,486 else => |e| return e,
442 };487 };
...@@ -446,9 +491,10 @@ pub const Module = struct {...@@ -446,9 +491,10 @@ pub const Module = struct {
446 }491 }
447492
448 pub fn totalErrorCount(self: *Module) usize {493 pub fn totalErrorCount(self: *Module) usize {
449 return self.failed_decls.items.len +494 return self.failed_decls.size +
450 self.failed_fns.items.len +495 self.failed_fns.size +
451 self.failed_decls.items.len +496 self.failed_decls.size +
497 self.failed_exports.size +
452 @boolToInt(self.link_error_flags.no_entry_point_found);498 @boolToInt(self.link_error_flags.no_entry_point_found);
453 }499 }
454500
...@@ -459,26 +505,42 @@ pub const Module = struct {...@@ -459,26 +505,42 @@ pub const Module = struct {
459 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);505 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
460 defer errors.deinit();506 defer errors.deinit();
461507
462 for (self.failed_files.items) |scope| {508 {
463 const source = scope.parse_failure.source;509 var it = self.failed_files.iterator();
464 for (scope.parse_failure.errors) |parse_error| {510 while (it.next()) |kv| {
465 AllErrors.add(&arena, &errors, scope.sub_file_path, source, parse_error);511 const scope = kv.key;
512 const err_msg = kv.value;
513 const source = scope.parse_failure.source;
514 AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg);
466 }515 }
467 }516 }
468517 {
469 for (self.failed_fns.items) |func| {518 var it = self.failed_fns.iterator();
470 const source = func.scope.success.source;519 while (it.next()) |kv| {
471 for (func.analysis.failure) |err_msg| {520 const func = kv.key;
521 const err_msg = kv.value;
522 const source = func.scope.success.source;
472 AllErrors.add(&arena, &errors, func.scope.sub_file_path, source, err_msg);523 AllErrors.add(&arena, &errors, func.scope.sub_file_path, source, err_msg);
473 }524 }
474 }525 }
475526 {
476 for (self.failed_decls.items) |decl| {527 var it = self.failed_decls.iterator();
477 const source = decl.scope.success.source;528 while (it.next()) |kv| {
478 for (decl.analysis.failure) |err_msg| {529 const decl = kv.key;
530 const err_msg = kv.value;
531 const source = decl.scope.success.source;
479 AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg);532 AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg);
480 }533 }
481 }534 }
535 {
536 var it = self.failed_exports.iterator();
537 while (it.next()) |kv| {
538 const decl = kv.key.owner_decl;
539 const err_msg = kv.value;
540 const source = decl.scope.success.source;
541 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg);
542 }
543 }
482544
483 if (self.link_error_flags.no_entry_point_found) {545 if (self.link_error_flags.no_entry_point_found) {
484 try errors.append(.{546 try errors.append(.{
...@@ -508,23 +570,81 @@ pub const Module = struct {...@@ -508,23 +570,81 @@ pub const Module = struct {
508 // Here we simulate adding a source file which was previously not part of the compilation,570 // Here we simulate adding a source file which was previously not part of the compilation,
509 // which means scanning the decls looking for exports.571 // which means scanning the decls looking for exports.
510 // TODO also identify decls that need to be deleted.572 // TODO also identify decls that need to be deleted.
511 const contents = blk: {573 const src_module = switch (root_scope.status) {
512 // Clear parse errors.574 .unloaded => blk: {
513 swapRemoveElem(self.allocator, *Scope.ZIRModule, root_scope, self.failed_files);575 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
514 try self.failed_files.ensureCapacity(self.allocator, self.failed_files.items.len + 1);576
515 break :blk root_scope.loadContents(self.allocator) catch |err| switch (err) {577 var keep_source = false;
516 error.ParseFailure => {578 const source = try self.root_pkg_dir.readFileAllocOptions(
517 self.failed_files.appendAssumeCapacity(root_scope);579 self.allocator,
580 self.root_src_path,
581 std.math.maxInt(u32),
582 1,
583 0,
584 );
585 defer if (!keep_source) self.allocator.free(source);
586
587 var keep_zir_module = false;
588 const zir_module = try self.allocator.create(text.Module);
589 defer if (!keep_zir_module) self.allocator.destroy(zir_module);
590
591 zir_module.* = try text.parse(self.allocator, source);
592 defer if (!keep_zir_module) zir_module.deinit(self.allocator);
593
594 if (zir_module.error_msg) |src_err_msg| {
595 self.failed_files.putAssumeCapacityNoClobber(
596 root_scope,
597 try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
598 );
599 root_scope.status = .loaded_parse_failure;
600 root_scope.source = .{ .bytes = source };
601 keep_source = true;
518 return error.AnalysisFail;602 return error.AnalysisFail;
519 },603 }
520 else => |e| return e,604
521 };605 root_scope.status = .loaded_success;
606 root_scope.source = .{ .bytes = source };
607 keep_source = true;
608 root_scope.contents = .{ .module = zir_module };
609 keep_zir_module = true;
610
611 break :blk zir_module;
612 },
613
614 .unloaded_parse_failure, .loaded_parse_failure => return error.AnalysisFail,
615 .loaded_success => root_scope.contents.module,
522 };616 };
617
618 // Here we ensure enough queue capacity to store all the decls, so that later we can use
619 // appendAssumeCapacity.
620 try self.analysis_queue.ensureCapacity(self.analysis_queue.items.len + contents.module.decls.len);
621
523 for (contents.module.decls) |decl| {622 for (contents.module.decls) |decl| {
524 if (decl.cast(text.Inst.Export)) |export_inst| {623 if (decl.cast(text.Inst.Export)) |export_inst| {
525 try analyzeExport(self, &root_scope.base, export_inst);624 try analyzeExport(self, &root_scope.base, export_inst);
526 }625 }
527 }626 }
627
628 while (self.analysis_queue.popOrNull()) |work_item| {
629 switch (work_item) {
630 .decl => |decl| switch (decl.analysis) {
631 .success => |typed_value| {
632 var arena = decl.arena.promote(self.allocator);
633 const update_result = self.bin_file.updateDecl(
634 self.*,
635 typed_value,
636 decl.export_node,
637 decl.fullyQualifiedNameHash(),
638 &arena.allocator,
639 );
640 decl.arena = arena.state;
641 if (try update_result) |err_msg| {
642 decl.analysis = .{ .codegen_failure = err_msg };
643 }
644 },
645 },
646 }
647 }
528 }648 }
529649
530 fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl {650 fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl {
...@@ -548,21 +668,41 @@ pub const Module = struct {...@@ -548,21 +668,41 @@ pub const Module = struct {
548 break :blk new_decl;668 break :blk new_decl;
549 };669 };
550670
551 var decl_scope: Scope.DeclAnalysis = .{ .decl = new_decl };671 swapRemoveElem(self.allocator, *Scope.ZIRModule, root_scope, self.failed_decls);
672 var decl_scope: Scope.DeclAnalysis = .{
673 .base = .{ .parent = scope },
674 .decl = new_decl,
675 };
552 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {676 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
553 error.AnalysisFail => return error.AnalysisFail,677 error.AnalysisFail => {
678 assert(new_decl.analysis == .failure);
679 return error.AnalysisFail;
680 },
554 else => |e| return e,681 else => |e| return e,
555 };682 };
556 new_decl.analysis = .{ .success = typed_value };683 new_decl.analysis = .{ .success = typed_value };
557 if (try self.bin_file.updateDecl(self.*, typed_value, new_decl.export_node, hash)) |err_msg| {684 // We ensureCapacity when scanning for decls.
558 new_decl.analysis = .{ .success = typed_value };685 self.analysis_queue.appendAssumeCapacity(.{ .decl = new_decl });
559 } else |err| {
560 return err;
561 }
562 return new_decl;686 return new_decl;
563 }687 }
564 }688 }
565689
690 fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl {
691 const decl = try self.resolveDecl(scope, old_inst);
692 switch (decl.analysis) {
693 .initial_in_progress => unreachable,
694 .repeat_in_progress => unreachable,
695 .initial_dependency_failure,
696 .repeat_dependency_failure,
697 .initial_sema_failure,
698 .repeat_sema_failure,
699 .codegen_failure,
700 => return error.AnalysisFail,
701
702 .complete => return decl,
703 }
704 }
705
566 fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {706 fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {
567 if (scope.cast(Scope.Block)) |block| {707 if (scope.cast(Scope.Block)) |block| {
568 if (block.func.inst_table.get(old_inst)) |kv| {708 if (block.func.inst_table.get(old_inst)) |kv| {
...@@ -570,7 +710,7 @@ pub const Module = struct {...@@ -570,7 +710,7 @@ pub const Module = struct {
570 }710 }
571 }711 }
572712
573 const decl = try self.resolveDecl(scope, old_inst);713 const decl = try self.resolveCompleteDecl(scope, old_inst);
574 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);714 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
575 return self.analyzeDeref(scope, old_inst.src, decl_ref);715 return self.analyzeDeref(scope, old_inst.src, decl_ref);
576 }716 }
...@@ -621,29 +761,52 @@ pub const Module = struct {...@@ -621,29 +761,52 @@ pub const Module = struct {
621 }761 }
622762
623 fn analyzeExport(self: *Module, scope: *Scope, export_inst: *text.Inst.Export) !void {763 fn analyzeExport(self: *Module, scope: *Scope, export_inst: *text.Inst.Export) !void {
764 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
765 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
624 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);766 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
625 const decl = try self.resolveDecl(scope, export_inst.positionals.value);767 const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
768 const typed_value = exported_decl.typed_value.most_recent.typed_value;
769 switch (typed_value.ty.zigTypeTag()) {
770 .Fn => {},
771 else => return self.fail(
772 scope,
773 export_inst.positionals.value.src,
774 "unable to export type '{}'",
775 .{typed_value.ty},
776 ),
777 }
778 const new_export = try self.allocator.create(Export);
779 errdefer self.allocator.destroy(new_export);
626780
627 switch (decl.analysis) {781 const owner_decl = scope.getDecl();
628 .in_progress => unreachable,782
629 .failure => return error.AnalysisFail,783 new_export.* = .{
630 .success => |typed_value| switch (typed_value.ty.zigTypeTag()) {784 .options = .{ .data = .{ .name = symbol_name } },
631 .Fn => {},785 .src = export_inst.base.src,
632 else => return self.fail(786 .link = .{},
633 scope,787 .owner_decl = owner_decl,
634 export_inst.positionals.value.src,788 .status = .in_progress,
635 "unable to export type '{}'",789 };
636 .{typed_value.ty},790
637 ),791 // Add to export_owners table.
638 },792 const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable;
793 if (!eo_gop.found_existing) {
794 eo_gop.kv.value = &[0]*Export{};
795 }
796 eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1);
797 eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export;
798 errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1);
799
800 // Add to exported_decl table.
801 const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable;
802 if (!de_gop.found_existing) {
803 de_gop.kv.value = &[0]*Export{};
639 }804 }
640 const Node = LinkedList(std.builtin.ExportOptions).Node;805 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);
641 export_node = try decl.arena.promote(self.allocator).allocator.create(Node);806 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
642 export_node.* = .{ .data = .{ .name = symbol_name } };807 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
643 decl.export_node = export_node;
644808
645 // TODO Avoid double update in the case of exporting a decl that we just created.809 try self.bin_file.updateDeclExports(self, decl, de_gop.kv.value);
646 self.bin_file.updateDeclExports();
647 }810 }
648811
649 /// TODO should not need the cast on the last parameter at the callsites812 /// TODO should not need the cast on the last parameter at the callsites
...@@ -1636,6 +1799,31 @@ pub const Module = struct {...@@ -1636,6 +1799,31 @@ pub const Module = struct {
1636pub const ErrorMsg = struct {1799pub const ErrorMsg = struct {
1637 byte_offset: usize,1800 byte_offset: usize,
1638 msg: []const u8,1801 msg: []const u8,
1802
1803 pub fn create(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !*ErrorMsg {
1804 const self = try allocator.create(ErrorMsg);
1805 errdefer allocator.destroy(ErrorMsg);
1806 self.* = init(allocator, byte_offset, format, args);
1807 return self;
1808 }
1809
1810 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
1811 pub fn destroy(self: *ErrorMsg, allocator: *Allocator) void {
1812 self.deinit(allocator);
1813 allocator.destroy(self);
1814 }
1815
1816 pub fn init(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !ErrorMsg {
1817 return ErrorMsg{
1818 .byte_offset = byte_offset,
1819 .msg = try std.fmt.allocPrint(allocator, format, args),
1820 };
1821 }
1822
1823 pub fn deinit(self: *ErrorMsg, allocator: *Allocator) void {
1824 allocator.free(err_msg.msg);
1825 self.* = undefined;
1826 }
1639};1827};
16401828
1641pub fn main() anyerror!void {1829pub fn main() anyerror!void {
src-self-hosted/ir/text.zig+6-10
...@@ -406,8 +406,8 @@ pub const ErrorMsg = struct {...@@ -406,8 +406,8 @@ pub const ErrorMsg = struct {
406406
407pub const Module = struct {407pub const Module = struct {
408 decls: []*Inst,408 decls: []*Inst,
409 errors: []ErrorMsg,
410 arena: std.heap.ArenaAllocator.State,409 arena: std.heap.ArenaAllocator.State,
410 error_msg: ?ErrorMsg = null,
411411
412 pub const Body = struct {412 pub const Body = struct {
413 instructions: []*Inst,413 instructions: []*Inst,
...@@ -415,7 +415,6 @@ pub const Module = struct {...@@ -415,7 +415,6 @@ pub const Module = struct {
415415
416 pub fn deinit(self: *Module, allocator: *Allocator) void {416 pub fn deinit(self: *Module, allocator: *Allocator) void {
417 allocator.free(self.decls);417 allocator.free(self.decls);
418 allocator.free(self.errors);
419 self.arena.promote(allocator).deinit();418 self.arena.promote(allocator).deinit();
420 self.* = undefined;419 self.* = undefined;
421 }420 }
...@@ -576,22 +575,21 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module...@@ -576,22 +575,21 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
576 .i = 0,575 .i = 0,
577 .source = source,576 .source = source,
578 .global_name_map = &global_name_map,577 .global_name_map = &global_name_map,
579 .errors = .{},
580 .decls = .{},578 .decls = .{},
581 };579 };
582 errdefer parser.arena.deinit();580 errdefer parser.arena.deinit();
583581
584 parser.parseRoot() catch |err| switch (err) {582 parser.parseRoot() catch |err| switch (err) {
585 error.ParseFailure => {583 error.ParseFailure => {
586 assert(parser.errors.items.len != 0);584 assert(parser.error_msg != null);
587 },585 },
588 else => |e| return e,586 else => |e| return e,
589 };587 };
590588
591 return Module{589 return Module{
592 .decls = parser.decls.toOwnedSlice(allocator),590 .decls = parser.decls.toOwnedSlice(allocator),
593 .errors = parser.errors.toOwnedSlice(allocator),
594 .arena = parser.arena.state,591 .arena = parser.arena.state,
592 .error_msg = parser.error_msg,
595 };593 };
596}594}
597595
...@@ -600,9 +598,9 @@ const Parser = struct {...@@ -600,9 +598,9 @@ const Parser = struct {
600 arena: std.heap.ArenaAllocator,598 arena: std.heap.ArenaAllocator,
601 i: usize,599 i: usize,
602 source: [:0]const u8,600 source: [:0]const u8,
603 errors: std.ArrayListUnmanaged(ErrorMsg),
604 decls: std.ArrayListUnmanaged(*Inst),601 decls: std.ArrayListUnmanaged(*Inst),
605 global_name_map: *std.StringHashMap(usize),602 global_name_map: *std.StringHashMap(usize),
603 error_msg: ?ErrorMsg = null,
606604
607 const Body = struct {605 const Body = struct {
608 instructions: std.ArrayList(*Inst),606 instructions: std.ArrayList(*Inst),
...@@ -776,10 +774,9 @@ const Parser = struct {...@@ -776,10 +774,9 @@ const Parser = struct {
776774
777 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {775 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
778 @setCold(true);776 @setCold(true);
779 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);777 self.error_msg = ErrorMsg{
780 (try self.errors.addOne()).* = .{
781 .byte_offset = self.i,778 .byte_offset = self.i,
782 .msg = msg,779 .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
783 };780 };
784 return error.ParseFailure;781 return error.ParseFailure;
785 }782 }
...@@ -971,7 +968,6 @@ pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module {...@@ -971,7 +968,6 @@ pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module {
971 return Module{968 return Module{
972 .decls = ctx.decls.toOwnedSlice(),969 .decls = ctx.decls.toOwnedSlice(),
973 .arena = ctx.arena,970 .arena = ctx.arena,
974 .errors = &[0]ErrorMsg{},
975 };971 };
976}972}
977973
src-self-hosted/link.zig+225-127
...@@ -130,6 +130,20 @@ pub const ElfFile = struct {...@@ -130,6 +130,20 @@ pub const ElfFile = struct {
130 no_entry_point_found: bool = false,130 no_entry_point_found: bool = false,
131 };131 };
132132
133 /// TODO it's too bad this optional takes up double the memory it should
134 pub const Decl = struct {
135 /// Each decl always gets a local symbol with the fully qualified name.
136 /// The vaddr and size are found here directly.
137 /// The file offset is found by computing the vaddr offset from the section vaddr
138 /// the symbol references, and adding that to the file offset of the section.
139 local_sym_index: ?usize = null,
140 };
141
142 /// TODO it's too bad this optional takes up double the memory it should
143 pub const Export = struct {
144 sym_index: ?usize = null,
145 };
146
133 pub fn deinit(self: *ElfFile) void {147 pub fn deinit(self: *ElfFile) void {
134 self.sections.deinit(self.allocator);148 self.sections.deinit(self.allocator);
135 self.program_headers.deinit(self.allocator);149 self.program_headers.deinit(self.allocator);
...@@ -138,7 +152,7 @@ pub const ElfFile = struct {...@@ -138,7 +152,7 @@ pub const ElfFile = struct {
138 self.offset_table.deinit(self.allocator);152 self.offset_table.deinit(self.allocator);
139 }153 }
140154
141 // `expand_num / expand_den` is the factor of padding when allocation155 // `alloc_num / alloc_den` is the factor of padding when allocation
142 const alloc_num = 4;156 const alloc_num = 4;
143 const alloc_den = 3;157 const alloc_den = 3;
144158
...@@ -216,12 +230,21 @@ pub const ElfFile = struct {...@@ -216,12 +230,21 @@ pub const ElfFile = struct {
216 }230 }
217231
218 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {232 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {
233 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
219 const result = self.shstrtab.items.len;234 const result = self.shstrtab.items.len;
220 try self.shstrtab.appendSlice(bytes);235 self.shstrtab.appendSliceAssumeCapacity(bytes);
221 try self.shstrtab.append(0);236 self.shstrtab.appendAssumeCapacity(0);
222 return @intCast(u32, result);237 return @intCast(u32, result);
223 }238 }
224239
240 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {
241 const existing_name = self.getString(old_str_off);
242 if (mem.eql(u8, existing_name, new_name)) {
243 return old_str_off;
244 }
245 return self.makeString(new_name);
246 }
247
225 pub fn populateMissingMetadata(self: *ElfFile) !void {248 pub fn populateMissingMetadata(self: *ElfFile) !void {
226 const small_ptr = switch (self.ptr_width) {249 const small_ptr = switch (self.ptr_width) {
227 .p32 => true,250 .p32 => true,
...@@ -575,166 +598,200 @@ pub const ElfFile = struct {...@@ -575,166 +598,200 @@ pub const ElfFile = struct {
575 try self.file.pwriteAll(hdr_buf[0..index], 0);598 try self.file.pwriteAll(hdr_buf[0..index], 0);
576 }599 }
577600
578 /// TODO Look into making this smaller to save memory.
579 /// Lots of redundant info here with the data stored in symbol structs.
580 const DeclSymbol = struct {
581 symbol_indexes: []usize,
582 vaddr: u64,
583 file_offset: u64,
584 size: u64,
585 };
586
587 const AllocatedBlock = struct {601 const AllocatedBlock = struct {
588 vaddr: u64,602 vaddr: u64,
589 file_offset: u64,603 file_offset: u64,
590 size_capacity: u64,604 size_capacity: u64,
591 };605 };
592606
593 fn allocateDeclSymbol(self: *ElfFile, size: u64) AllocatedBlock {607 fn allocateTextBlock(self: *ElfFile, new_block_size: u64) !AllocatedBlock {
594 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];608 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
595 todo();609 const shdr = &self.sections.items[self.text_section_index.?];
596 //{610
597 // // Now that we know the code size, we need to update the program header for executable code611 const text_capacity = self.allocatedSize(shdr.sh_offset);
598 // phdr.p_memsz = vaddr - phdr.p_vaddr;612 // TODO instead of looping here, maintain a free list and a pointer to the end.
599 // phdr.p_filesz = phdr.p_memsz;613 const end_vaddr = blk: {
600614 var start: u64 = 0;
601 // const shdr = &self.sections.items[self.text_section_index.?];615 var size: u64 = 0;
602 // shdr.sh_size = phdr.p_filesz;616 for (self.symbols.items) |sym| {
617 if (sym.st_value > start) {
618 start = sm.st_value;
619 size = sym.st_size;
620 }
621 }
622 break :blk start + (size * alloc_num / alloc_den);
623 };
603624
604 // self.phdr_table_dirty = true; // TODO look into making only the one program header dirty625 const text_size = end_vaddr - phdr.p_vaddr;
605 // self.shdr_table_dirty = true; // TODO look into making only the one section dirty626 const needed_size = text_size + new_block_size;
606 //}627 if (needed_size > text_capacity) {
628 // Must move the entire text section.
629 const new_offset = self.findFreeSpace(needed_size, 0x1000);
630 const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, text_size);
631 if (amt != text_size) return error.InputOutput;
632 shdr.sh_offset = new_offset;
633 }
634 // Now that we know the code size, we need to update the program header for executable code
635 shdr.sh_size = needed_size;
636 phdr.p_memsz = needed_size;
637 phdr.p_filesz = needed_size;
607638
608 //return self.writeSymbols();639 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
640 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
609 }641 }
610642
611 fn findAllocatedBlock(self: *ElfFile, vaddr: u64) AllocatedBlock {643 fn findAllocatedTextBlock(self: *ElfFile, sym: elf.Elf64_Sym) AllocatedBlock {
612 todo();644 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
645 const shdr = &self.sections.items[self.text_section_index.?];
646
647 // Find the next sym after this one.
648 // TODO look into using a hash map to speed up perf.
649 const text_capacity = self.allocatedSize(shdr.sh_offset);
650 var next_vaddr_start = phdr.p_vaddr + text_capacity;
651 for (self.symbols.items) |elem| {
652 if (elem.st_value < sym.st_value) continue;
653 if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value;
654 }
655 return .{
656 .vaddr = sym.st_value,
657 .file_offset = shdr.sh_offset + (sym.st_value - phdr.p_vaddr),
658 .size_capacity = next_vaddr_start - sym.st_value,
659 };
613 }660 }
614661
615 pub fn updateDecl(662 pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void {
616 self: *ElfFile,
617 module: ir.Module,
618 typed_value: ir.TypedValue,
619 decl_export_node: ?*std.LinkedList(std.builtin.ExportOptions).Node,
620 hash: ir.Module.Decl.Hash,
621 err_msg_allocator: *Allocator,
622 ) !?ir.ErrorMsg {
623 var code = std.ArrayList(u8).init(self.allocator);663 var code = std.ArrayList(u8).init(self.allocator);
624 defer code.deinit();664 defer code.deinit();
625665
626 const err_msg = try codegen.generateSymbol(typed_value, module, &code, err_msg_allocator);666 const typed_value = decl.typed_value.most_recent.typed_value;
627 if (err_msg != null) |em| return em;667 const err_msg = try codegen.generateSymbol(typed_value, module, &code, module.allocator);
668 if (err_msg != null) |em| {
669 decl.analysis = .codegen_failure;
670 _ = try module.failed_decls.put(decl, em);
671 return;
672 }
628673
629 const export_count = blk: {674 const file_offset = blk: {
630 var export_node = decl_export_node;675 const code_size = code.items.len;
631 var i: usize = 0;676 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
632 while (export_node) |node| : (export_node = node.next) i += 1;677 .Fn => elf.STT_FUNC,
633 break :blk i;678 else => elf.STT_OBJECT,
634 };679 };
635680
636 // Find or create a symbol from the decl681 if (decl.link.local_sym_index) |local_sym_index| {
637 var valid_sym_index_len: usize = 0;682 const local_sym = &self.symbols.items[local_sym_index];
638 const decl_symbol = blk: {683 const existing_block = self.findAllocatedTextBlock(local_sym);
639 if (self.decl_table.getValue(hash)) |decl_symbol| {684 const file_offset = if (code_size > existing_block.size_capacity) fo: {
640 valid_sym_index_len = decl_symbol.symbol_indexes.len;685 const new_block = self.allocateTextBlock(code_size);
641 decl_symbol.symbol_indexes = try self.allocator.realloc(usize, export_count);686 local_sym.st_value = new_block.vaddr;
642687 local_sym.st_size = code_size;
643 const existing_block = self.findAllocatedBlock(decl_symbol.vaddr);688 break :fo new_block.file_offset;
644 if (code.items.len > existing_block.size_capacity) {689 } else existing_block.file_offset;
645 const new_block = self.allocateDeclSymbol(code.items.len);690 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name));
646 decl_symbol.vaddr = new_block.vaddr;691 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
647 decl_symbol.file_offset = new_block.file_offset;692 // TODO this write could be avoided if no fields of the symbol were changed.
648 decl_symbol.size = code.items.len;693 try self.writeSymbol(local_sym_index);
649 }694 break :blk file_offset;
650 break :blk decl_symbol;
651 } else {695 } else {
652 const new_block = self.allocateDeclSymbol(code.items.len);696 try self.symbols.ensureCapacity(self.symbols.items.len + 1);
653697 const decl_name = mem.spanZ(u8, decl.name);
654 const decl_symbol = try self.allocator.create(DeclSymbol);698 const name_str_index = try self.makeString(decl_name);
655 errdefer self.allocator.destroy(decl_symbol);699 const new_block = self.allocateTextBlock(code_size);
656700 const local_sym_index = self.symbols.items.len;
657 decl_symbol.* = .{701
658 .symbol_indexes = try self.allocator.alloc(usize, export_count),702 self.symbols.appendAssumeCapacity(self.allocator, .{
659 .vaddr = new_block.vaddr,703 .st_name = name_str_index,
660 .file_offset = new_block.file_offset,704 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
661 .size = code.items.len,705 .st_other = 0,
662 };706 .st_shndx = self.text_section_index.?,
663 errdefer self.allocator.free(decl_symbol.symbol_indexes);707 .st_value = new_block.vaddr,
664708 .st_size = code_size,
665 try self.decl_table.put(hash, decl_symbol);709 });
666 break :blk decl_symbol;710 errdefer self.symbols.shrink(self.symbols.items.len - 1);
711 try self.writeSymbol(local_sym_index);
712
713 self.symbol_count_dirty = true;
714 decl.link.local_sym_index = local_sym_index;
715
716 break :blk new_block.file_offset;
667 }717 }
668 };718 };
669719
670 // Allocate new symbols.720 try self.file.pwriteAll(code.items, file_offset);
671 {
672 var i: usize = valid_sym_index_len;
673 const old_len = self.symbols.items.len;
674 try self.symbols.resize(old_len + (decl_symbol.symbol_indexes.len - i));
675 while (i < decl_symbol.symbol_indexes) : (i += 1) {
676 decl_symbol.symbol_indexes[i] = old_len + i;
677 }
678 }
679721
680 var export_node = decl_export_node;722 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
681 var export_index: usize = 0;723 const decl_exports = module.decl_exports.get(decl) orelse &[0]*ir.Module.Export{};
682 while (export_node) |node| : ({724 return self.updateDeclExports(module, decl, decl_exports);
683 export_node = node.next;725 }
684 export_index += 1;726
685 }) {727 /// Must be called only after a successful call to `updateDecl`.
686 if (node.data.section) |section_name| {728 pub fn updateDeclExports(
729 self: *ElfFile,
730 module: *ir.Module,
731 decl: *const ir.Module.Decl,
732 exports: []const *const Export,
733 ) !void {
734 try self.symbols.ensureCapacity(self.symbols.items.len + exports.len);
735 const typed_value = decl.typed_value.most_recent.typed_value;
736 const decl_sym = self.symbols.items[decl.link.local_sym_index.?];
737
738 for (exports) |exp| {
739 if (exp.options.section) |section_name| {
687 if (!mem.eql(u8, section_name, ".text")) {740 if (!mem.eql(u8, section_name, ".text")) {
688 try errors.ensureCapacity(errors.items.len + 1);741 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
689 errors.appendAssumeCapacity(.{742 module.failed_exports.putAssumeCapacityNoClobber(
690 .byte_offset = 0,743 exp,
691 .msg = try std.fmt.allocPrint(errors.allocator, "Unimplemented: ExportOptions.section", .{}),744 try ir.ErrorMsg.create(0, "Unimplemented: ExportOptions.section", .{}),
692 });745 );
693 }746 }
694 }747 }
695 const stb_bits = switch (node.data.linkage) {748 const stb_bits = switch (exp.options.linkage) {
696 .Internal => elf.STB_LOCAL,749 .Internal => elf.STB_LOCAL,
697 .Strong => blk: {750 .Strong => blk: {
698 if (mem.eql(u8, node.data.name, "_start")) {751 if (mem.eql(u8, exp.options.name, "_start")) {
699 self.entry_addr = decl_symbol.vaddr;752 self.entry_addr = decl_symbol.vaddr;
700 }753 }
701 break :blk elf.STB_GLOBAL;754 break :blk elf.STB_GLOBAL;
702 },755 },
703 .Weak => elf.STB_WEAK,756 .Weak => elf.STB_WEAK,
704 .LinkOnce => {757 .LinkOnce => {
705 try errors.ensureCapacity(errors.items.len + 1);758 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
706 errors.appendAssumeCapacity(.{759 module.failed_exports.putAssumeCapacityNoClobber(
707 .byte_offset = 0,760 exp,
708 .msg = try std.fmt.allocPrint(errors.allocator, "Unimplemented: GlobalLinkage.LinkOnce", .{}),761 try ir.ErrorMsg.create(0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
709 });762 );
710 },763 },
711 };764 };
712 const stt_bits = switch (typed_value.ty.zigTypeTag()) {765 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
713 .Fn => elf.STT_FUNC,766 if (exp.link.sym_index) |i| {
714 else => elf.STT_OBJECT,767 const sym = &self.symbols.items[i];
715 };768 sym.* = .{
716 const sym_index = decl_symbol.symbol_indexes[export_index];769 .st_name = try self.updateString(sym.st_name, exp.options.name),
717 const name = blk: {770 .st_info = (stb_bits << 4) | stt_bits,
718 if (i < valid_sym_index_len) {771 .st_other = 0,
719 const name_stroff = self.symbols.items[sym_index].st_name;772 .st_shndx = self.text_section_index.?,
720 const existing_name = self.getString(name_stroff);773 .st_value = decl_sym.st_value,
721 if (mem.eql(u8, existing_name, node.data.name)) {774 .st_size = decl_sym.st_size,
722 break :blk name_stroff;775 };
723 }776 try self.writeSymbol(i);
724 }777 } else {
725 break :blk try self.makeString(node.data.name);778 const name = try self.makeString(exp.options.name);
726 };779 const i = self.symbols.items.len;
727 self.symbols.items[sym_index] = .{780 self.symbols.appendAssumeCapacity(self.allocator, .{
728 .st_name = name,781 .st_name = sn.name,
729 .st_info = (stb_bits << 4) | stt_bits,782 .st_info = (stb_bits << 4) | stt_bits,
730 .st_other = 0,783 .st_other = 0,
731 .st_shndx = self.text_section_index.?,784 .st_shndx = self.text_section_index.?,
732 .st_value = decl_symbol.vaddr,785 .st_value = decl_sym.st_value,
733 .st_size = code.items.len,786 .st_size = decl_sym.st_size,
734 };787 });
788 errdefer self.symbols.shrink(self.symbols.items.len - 1);
789 try self.writeSymbol(i);
790
791 self.symbol_count_dirty = true;
792 exp.link.sym_index = i;
793 }
735 }794 }
736
737 try self.file.pwriteAll(code.items, decl_symbol.file_offset);
738 }795 }
739796
740 fn writeProgHeader(self: *ElfFile, index: usize) !void {797 fn writeProgHeader(self: *ElfFile, index: usize) !void {
...@@ -782,7 +839,48 @@ pub const ElfFile = struct {...@@ -782,7 +839,48 @@ pub const ElfFile = struct {
782 }839 }
783 }840 }
784841
785 fn writeSymbols(self: *ElfFile) !void {842 fn writeSymbol(self: *ElfFile, index: usize) !void {
843 const syms_sect = &self.sections.items[self.symtab_section_index.?];
844 // Make sure we are not pointlessly writing symbol data that will have to get relocated
845 // due to running out of space.
846 if (self.symbol_count_dirty) {
847 const allocated_size = self.allocatedSize(syms_sect.sh_offset);
848 const needed_size = self.symbols.items.len * sym_size;
849 if (needed_size > allocated_size) {
850 return self.writeAllSymbols();
851 }
852 }
853 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
854 switch (self.ptr_width) {
855 .p32 => {
856 var sym = [1]elf.Elf32_Sym{
857 .{
858 .st_name = self.symbols.items[index].st_name,
859 .st_value = @intCast(u32, self.symbols.items[index].st_value),
860 .st_size = @intCast(u32, self.symbols.items[index].st_size),
861 .st_info = self.symbols.items[index].st_info,
862 .st_other = self.symbols.items[index].st_other,
863 .st_shndx = self.symbols.items[index].st_shndx,
864 },
865 };
866 if (foreign_endian) {
867 bswapAllFields(elf.Elf32_Sym, &sym[0]);
868 }
869 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
870 try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
871 },
872 .p64 => {
873 var sym = [1]elf.Elf64_Sym{self.symbols.items[index]};
874 if (foreign_endian) {
875 bswapAllFields(elf.Elf64_Sym, &sym[0]);
876 }
877 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
878 try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
879 },
880 }
881 }
882
883 fn writeAllSymbols(self: *ElfFile) !void {
786 const small_ptr = self.ptr_width == .p32;884 const small_ptr = self.ptr_width == .p32;
787 const syms_sect = &self.sections.items[self.symtab_section_index.?];885 const syms_sect = &self.sections.items[self.symtab_section_index.?];
788 const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);886 const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
src-self-hosted/type.zig+21-2
...@@ -5,8 +5,7 @@ const Allocator = std.mem.Allocator;...@@ -5,8 +5,7 @@ const Allocator = std.mem.Allocator;
5const Target = std.Target;5const Target = std.Target;
66
7/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.7/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
8/// It's important for this struct to be small.8/// It's important for this type to be small.
9/// It is not copyable since it may contain references to its inner data.
10/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement9/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
11/// of obtaining a lock on a global type table, as well as making the10/// of obtaining a lock on a global type table, as well as making the
12/// garbage collection bookkeeping simpler.11/// garbage collection bookkeeping simpler.
...@@ -51,6 +50,7 @@ pub const Type = extern union {...@@ -51,6 +50,7 @@ pub const Type = extern union {
51 .comptime_int => return .ComptimeInt,50 .comptime_int => return .ComptimeInt,
52 .comptime_float => return .ComptimeFloat,51 .comptime_float => return .ComptimeFloat,
53 .noreturn => return .NoReturn,52 .noreturn => return .NoReturn,
53 .@"null" => return .Null,
5454
55 .fn_noreturn_no_args => return .Fn,55 .fn_noreturn_no_args => return .Fn,
56 .fn_naked_noreturn_no_args => return .Fn,56 .fn_naked_noreturn_no_args => return .Fn,
...@@ -184,6 +184,8 @@ pub const Type = extern union {...@@ -184,6 +184,8 @@ pub const Type = extern union {
184 .noreturn,184 .noreturn,
185 => return out_stream.writeAll(@tagName(t)),185 => return out_stream.writeAll(@tagName(t)),
186186
187 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
188
187 .const_slice_u8 => return out_stream.writeAll("[]const u8"),189 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
188 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),190 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
189 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),191 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
...@@ -246,6 +248,7 @@ pub const Type = extern union {...@@ -246,6 +248,7 @@ pub const Type = extern union {
246 .comptime_int => return Value.initTag(.comptime_int_type),248 .comptime_int => return Value.initTag(.comptime_int_type),
247 .comptime_float => return Value.initTag(.comptime_float_type),249 .comptime_float => return Value.initTag(.comptime_float_type),
248 .noreturn => return Value.initTag(.noreturn_type),250 .noreturn => return Value.initTag(.noreturn_type),
251 .@"null" => return Value.initTag(.null_type),
249 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),252 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
250 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),253 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
251 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),254 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
...@@ -286,6 +289,7 @@ pub const Type = extern union {...@@ -286,6 +289,7 @@ pub const Type = extern union {
286 .comptime_int,289 .comptime_int,
287 .comptime_float,290 .comptime_float,
288 .noreturn,291 .noreturn,
292 .@"null",
289 .array,293 .array,
290 .array_u8_sentinel_0,294 .array_u8_sentinel_0,
291 .const_slice_u8,295 .const_slice_u8,
...@@ -329,6 +333,7 @@ pub const Type = extern union {...@@ -329,6 +333,7 @@ pub const Type = extern union {
329 .comptime_int,333 .comptime_int,
330 .comptime_float,334 .comptime_float,
331 .noreturn,335 .noreturn,
336 .@"null",
332 .array,337 .array,
333 .array_u8_sentinel_0,338 .array_u8_sentinel_0,
334 .single_const_pointer,339 .single_const_pointer,
...@@ -372,6 +377,7 @@ pub const Type = extern union {...@@ -372,6 +377,7 @@ pub const Type = extern union {
372 .comptime_int,377 .comptime_int,
373 .comptime_float,378 .comptime_float,
374 .noreturn,379 .noreturn,
380 .@"null",
375 .array,381 .array,
376 .array_u8_sentinel_0,382 .array_u8_sentinel_0,
377 .fn_noreturn_no_args,383 .fn_noreturn_no_args,
...@@ -416,6 +422,7 @@ pub const Type = extern union {...@@ -416,6 +422,7 @@ pub const Type = extern union {
416 .comptime_int,422 .comptime_int,
417 .comptime_float,423 .comptime_float,
418 .noreturn,424 .noreturn,
425 .@"null",
419 .fn_noreturn_no_args,426 .fn_noreturn_no_args,
420 .fn_naked_noreturn_no_args,427 .fn_naked_noreturn_no_args,
421 .fn_ccc_void_no_args,428 .fn_ccc_void_no_args,
...@@ -458,6 +465,7 @@ pub const Type = extern union {...@@ -458,6 +465,7 @@ pub const Type = extern union {
458 .comptime_int,465 .comptime_int,
459 .comptime_float,466 .comptime_float,
460 .noreturn,467 .noreturn,
468 .@"null",
461 .fn_noreturn_no_args,469 .fn_noreturn_no_args,
462 .fn_naked_noreturn_no_args,470 .fn_naked_noreturn_no_args,
463 .fn_ccc_void_no_args,471 .fn_ccc_void_no_args,
...@@ -489,6 +497,7 @@ pub const Type = extern union {...@@ -489,6 +497,7 @@ pub const Type = extern union {
489 .comptime_int,497 .comptime_int,
490 .comptime_float,498 .comptime_float,
491 .noreturn,499 .noreturn,
500 .@"null",
492 .fn_noreturn_no_args,501 .fn_noreturn_no_args,
493 .fn_naked_noreturn_no_args,502 .fn_naked_noreturn_no_args,
494 .fn_ccc_void_no_args,503 .fn_ccc_void_no_args,
...@@ -533,6 +542,7 @@ pub const Type = extern union {...@@ -533,6 +542,7 @@ pub const Type = extern union {
533 .comptime_int,542 .comptime_int,
534 .comptime_float,543 .comptime_float,
535 .noreturn,544 .noreturn,
545 .@"null",
536 .fn_noreturn_no_args,546 .fn_noreturn_no_args,
537 .fn_naked_noreturn_no_args,547 .fn_naked_noreturn_no_args,
538 .fn_ccc_void_no_args,548 .fn_ccc_void_no_args,
...@@ -606,6 +616,7 @@ pub const Type = extern union {...@@ -606,6 +616,7 @@ pub const Type = extern union {
606 .comptime_int,616 .comptime_int,
607 .comptime_float,617 .comptime_float,
608 .noreturn,618 .noreturn,
619 .@"null",
609 .array,620 .array,
610 .single_const_pointer,621 .single_const_pointer,
611 .single_const_pointer_to_comptime_int,622 .single_const_pointer_to_comptime_int,
...@@ -650,6 +661,7 @@ pub const Type = extern union {...@@ -650,6 +661,7 @@ pub const Type = extern union {
650 .comptime_int,661 .comptime_int,
651 .comptime_float,662 .comptime_float,
652 .noreturn,663 .noreturn,
664 .@"null",
653 .array,665 .array,
654 .single_const_pointer,666 .single_const_pointer,
655 .single_const_pointer_to_comptime_int,667 .single_const_pointer_to_comptime_int,
...@@ -693,6 +705,7 @@ pub const Type = extern union {...@@ -693,6 +705,7 @@ pub const Type = extern union {
693 .comptime_int,705 .comptime_int,
694 .comptime_float,706 .comptime_float,
695 .noreturn,707 .noreturn,
708 .@"null",
696 .array,709 .array,
697 .single_const_pointer,710 .single_const_pointer,
698 .single_const_pointer_to_comptime_int,711 .single_const_pointer_to_comptime_int,
...@@ -736,6 +749,7 @@ pub const Type = extern union {...@@ -736,6 +749,7 @@ pub const Type = extern union {
736 .comptime_int,749 .comptime_int,
737 .comptime_float,750 .comptime_float,
738 .noreturn,751 .noreturn,
752 .@"null",
739 .array,753 .array,
740 .single_const_pointer,754 .single_const_pointer,
741 .single_const_pointer_to_comptime_int,755 .single_const_pointer_to_comptime_int,
...@@ -779,6 +793,7 @@ pub const Type = extern union {...@@ -779,6 +793,7 @@ pub const Type = extern union {
779 .comptime_int,793 .comptime_int,
780 .comptime_float,794 .comptime_float,
781 .noreturn,795 .noreturn,
796 .@"null",
782 .array,797 .array,
783 .single_const_pointer,798 .single_const_pointer,
784 .single_const_pointer_to_comptime_int,799 .single_const_pointer_to_comptime_int,
...@@ -833,6 +848,7 @@ pub const Type = extern union {...@@ -833,6 +848,7 @@ pub const Type = extern union {
833 .type,848 .type,
834 .anyerror,849 .anyerror,
835 .noreturn,850 .noreturn,
851 .@"null",
836 .fn_noreturn_no_args,852 .fn_noreturn_no_args,
837 .fn_naked_noreturn_no_args,853 .fn_naked_noreturn_no_args,
838 .fn_ccc_void_no_args,854 .fn_ccc_void_no_args,
...@@ -881,6 +897,7 @@ pub const Type = extern union {...@@ -881,6 +897,7 @@ pub const Type = extern union {
881 .c_void,897 .c_void,
882 .void,898 .void,
883 .noreturn,899 .noreturn,
900 .@"null",
884 => return true,901 => return true,
885902
886 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,903 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
...@@ -933,6 +950,7 @@ pub const Type = extern union {...@@ -933,6 +950,7 @@ pub const Type = extern union {
933 .c_void,950 .c_void,
934 .void,951 .void,
935 .noreturn,952 .noreturn,
953 .@"null",
936 .int_unsigned,954 .int_unsigned,
937 .int_signed,955 .int_signed,
938 .array,956 .array,
...@@ -974,6 +992,7 @@ pub const Type = extern union {...@@ -974,6 +992,7 @@ pub const Type = extern union {
974 comptime_int,992 comptime_int,
975 comptime_float,993 comptime_float,
976 noreturn,994 noreturn,
995 @"null",
977 fn_noreturn_no_args,996 fn_noreturn_no_args,
978 fn_naked_noreturn_no_args,997 fn_naked_noreturn_no_args,
979 fn_ccc_void_no_args,998 fn_ccc_void_no_args,
src-self-hosted/value.zig+13-1
...@@ -10,7 +10,7 @@ const ir = @import("ir.zig");...@@ -10,7 +10,7 @@ const ir = @import("ir.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.
13/// It's important for this struct to be small.13/// It's important for this type to be small.
14/// This union takes advantage of the fact that the first page of memory14/// This union takes advantage of the fact that the first page of memory
15/// is unmapped, giving us 4096 possible enum tags that have no payload.15/// is unmapped, giving us 4096 possible enum tags that have no payload.
16pub const Value = extern union {16pub const Value = extern union {
...@@ -46,6 +46,7 @@ pub const Value = extern union {...@@ -46,6 +46,7 @@ pub const Value = extern union {
46 comptime_int_type,46 comptime_int_type,
47 comptime_float_type,47 comptime_float_type,
48 noreturn_type,48 noreturn_type,
49 null_type,
49 fn_noreturn_no_args_type,50 fn_noreturn_no_args_type,
50 fn_naked_noreturn_no_args_type,51 fn_naked_noreturn_no_args_type,
51 fn_ccc_void_no_args_type,52 fn_ccc_void_no_args_type,
...@@ -138,6 +139,7 @@ pub const Value = extern union {...@@ -138,6 +139,7 @@ pub const Value = extern union {
138 .comptime_int_type => return out_stream.writeAll("comptime_int"),139 .comptime_int_type => return out_stream.writeAll("comptime_int"),
139 .comptime_float_type => return out_stream.writeAll("comptime_float"),140 .comptime_float_type => return out_stream.writeAll("comptime_float"),
140 .noreturn_type => return out_stream.writeAll("noreturn"),141 .noreturn_type => return out_stream.writeAll("noreturn"),
142 .null_type => return out_stream.writeAll("@TypeOf(null)"),
141 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),143 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
142 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),144 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
143 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),145 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
...@@ -209,6 +211,7 @@ pub const Value = extern union {...@@ -209,6 +211,7 @@ pub const Value = extern union {
209 .comptime_int_type => Type.initTag(.comptime_int),211 .comptime_int_type => Type.initTag(.comptime_int),
210 .comptime_float_type => Type.initTag(.comptime_float),212 .comptime_float_type => Type.initTag(.comptime_float),
211 .noreturn_type => Type.initTag(.noreturn),213 .noreturn_type => Type.initTag(.noreturn),
214 .null_type => Type.initTag(.@"null"),
212 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),215 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
213 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),216 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
214 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),217 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
...@@ -263,6 +266,7 @@ pub const Value = extern union {...@@ -263,6 +266,7 @@ pub const Value = extern union {
263 .comptime_int_type,266 .comptime_int_type,
264 .comptime_float_type,267 .comptime_float_type,
265 .noreturn_type,268 .noreturn_type,
269 .null_type,
266 .fn_noreturn_no_args_type,270 .fn_noreturn_no_args_type,
267 .fn_naked_noreturn_no_args_type,271 .fn_naked_noreturn_no_args_type,
268 .fn_ccc_void_no_args_type,272 .fn_ccc_void_no_args_type,
...@@ -319,6 +323,7 @@ pub const Value = extern union {...@@ -319,6 +323,7 @@ pub const Value = extern union {
319 .comptime_int_type,323 .comptime_int_type,
320 .comptime_float_type,324 .comptime_float_type,
321 .noreturn_type,325 .noreturn_type,
326 .null_type,
322 .fn_noreturn_no_args_type,327 .fn_noreturn_no_args_type,
323 .fn_naked_noreturn_no_args_type,328 .fn_naked_noreturn_no_args_type,
324 .fn_ccc_void_no_args_type,329 .fn_ccc_void_no_args_type,
...@@ -376,6 +381,7 @@ pub const Value = extern union {...@@ -376,6 +381,7 @@ pub const Value = extern union {
376 .comptime_int_type,381 .comptime_int_type,
377 .comptime_float_type,382 .comptime_float_type,
378 .noreturn_type,383 .noreturn_type,
384 .null_type,
379 .fn_noreturn_no_args_type,385 .fn_noreturn_no_args_type,
380 .fn_naked_noreturn_no_args_type,386 .fn_naked_noreturn_no_args_type,
381 .fn_ccc_void_no_args_type,387 .fn_ccc_void_no_args_type,
...@@ -438,6 +444,7 @@ pub const Value = extern union {...@@ -438,6 +444,7 @@ pub const Value = extern union {
438 .comptime_int_type,444 .comptime_int_type,
439 .comptime_float_type,445 .comptime_float_type,
440 .noreturn_type,446 .noreturn_type,
447 .null_type,
441 .fn_noreturn_no_args_type,448 .fn_noreturn_no_args_type,
442 .fn_naked_noreturn_no_args_type,449 .fn_naked_noreturn_no_args_type,
443 .fn_ccc_void_no_args_type,450 .fn_ccc_void_no_args_type,
...@@ -529,6 +536,7 @@ pub const Value = extern union {...@@ -529,6 +536,7 @@ pub const Value = extern union {
529 .comptime_int_type,536 .comptime_int_type,
530 .comptime_float_type,537 .comptime_float_type,
531 .noreturn_type,538 .noreturn_type,
539 .null_type,
532 .fn_noreturn_no_args_type,540 .fn_noreturn_no_args_type,
533 .fn_naked_noreturn_no_args_type,541 .fn_naked_noreturn_no_args_type,
534 .fn_ccc_void_no_args_type,542 .fn_ccc_void_no_args_type,
...@@ -582,6 +590,7 @@ pub const Value = extern union {...@@ -582,6 +590,7 @@ pub const Value = extern union {
582 .comptime_int_type,590 .comptime_int_type,
583 .comptime_float_type,591 .comptime_float_type,
584 .noreturn_type,592 .noreturn_type,
593 .null_type,
585 .fn_noreturn_no_args_type,594 .fn_noreturn_no_args_type,
586 .fn_naked_noreturn_no_args_type,595 .fn_naked_noreturn_no_args_type,
587 .fn_ccc_void_no_args_type,596 .fn_ccc_void_no_args_type,
...@@ -674,6 +683,7 @@ pub const Value = extern union {...@@ -674,6 +683,7 @@ pub const Value = extern union {
674 .comptime_int_type,683 .comptime_int_type,
675 .comptime_float_type,684 .comptime_float_type,
676 .noreturn_type,685 .noreturn_type,
686 .null_type,
677 .fn_noreturn_no_args_type,687 .fn_noreturn_no_args_type,
678 .fn_naked_noreturn_no_args_type,688 .fn_naked_noreturn_no_args_type,
679 .fn_ccc_void_no_args_type,689 .fn_ccc_void_no_args_type,
...@@ -736,6 +746,7 @@ pub const Value = extern union {...@@ -736,6 +746,7 @@ pub const Value = extern union {
736 .comptime_int_type,746 .comptime_int_type,
737 .comptime_float_type,747 .comptime_float_type,
738 .noreturn_type,748 .noreturn_type,
749 .null_type,
739 .fn_noreturn_no_args_type,750 .fn_noreturn_no_args_type,
740 .fn_naked_noreturn_no_args_type,751 .fn_naked_noreturn_no_args_type,
741 .fn_ccc_void_no_args_type,752 .fn_ccc_void_no_args_type,
...@@ -812,6 +823,7 @@ pub const Value = extern union {...@@ -812,6 +823,7 @@ pub const Value = extern union {
812 .comptime_int_type,823 .comptime_int_type,
813 .comptime_float_type,824 .comptime_float_type,
814 .noreturn_type,825 .noreturn_type,
826 .null_type,
815 .fn_noreturn_no_args_type,827 .fn_noreturn_no_args_type,
816 .fn_naked_noreturn_no_args_type,828 .fn_naked_noreturn_no_args_type,
817 .fn_ccc_void_no_args_type,829 .fn_ccc_void_no_args_type,