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 {
149149 /// Append the slice of items to the list. Allocates more
150150 /// memory as necessary.
151151 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 {
152159 const oldlen = self.items.len;
153160 const newlen = self.items.len + items.len;
154
155 try self.ensureCapacity(newlen);
156161 self.items.len = newlen;
157162 mem.copy(T, self.items[oldlen..], items);
158163 }
......@@ -378,10 +383,16 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
378383 /// Append the slice of items to the list. Allocates more
379384 /// memory as necessary.
380385 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 {
381393 const oldlen = self.items.len;
382394 const newlen = self.items.len + items.len;
383395
384 try self.ensureCapacity(allocator, newlen);
385396 self.items.len = newlen;
386397 mem.copy(T, self.items[oldlen..], items);
387398 }
lib/std/fs/file.zig+24
......@@ -527,6 +527,30 @@ pub const File = struct {
527527 }
528528 }
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
530554 pub const WriteFileOptions = struct {
531555 in_offset: u64 = 0,
532556
lib/std/hash_map.zig+5-1
......@@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash;
1010const Allocator = mem.Allocator;
1111const builtin = @import("builtin");
1212
13const want_modification_safety = builtin.mode != .ReleaseFast;
13const want_modification_safety = std.debug.runtime_safety;
1414const debug_u32 = if (want_modification_safety) u32 else void;
1515
1616pub 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
219219 return put_result.old_kv;
220220 }
221221
222 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
223 assert(self.putAssumeCapacity(key, value) == null);
224 }
225
222226 pub fn get(hm: *const Self, key: K) ?*KV {
223227 if (hm.entries.len == 0) {
224228 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;
55const LinkedList = std.TailQueue;
66const Value = @import("value.zig").Value;
77const Type = @import("type.zig").Type;
8const TypedValue = @import("TypedValue.zig");
89const assert = std.debug.assert;
910const BigIntConst = std.math.big.int.Const;
1011const BigIntMutable = std.math.big.int.Mutable;
......@@ -167,11 +168,6 @@ pub const Inst = struct {
167168 };
168169};
169170
170pub const TypedValue = struct {
171 ty: Type,
172 val: Value,
173};
174
175171fn swapRemoveElem(allocator: *Allocator, comptime T: type, item: T, list: *ArrayListUnmanaged(T)) void {
176172 var i: usize = 0;
177173 while (i < list.items.len) {
......@@ -192,46 +188,125 @@ pub const Module = struct {
192188 root_scope: *Scope.ZIRModule,
193189 /// Pointer to externally managed resource.
194190 bin_file: *link.ElfFile,
195 failed_decls: ArrayListUnmanaged(*Decl) = .{},
196 failed_fns: ArrayListUnmanaged(*Fn) = .{},
197 failed_files: ArrayListUnmanaged(*Scope.ZIRModule) = .{},
191 /// It's rare for a decl to be exported, so we save memory by having a sparse map of
192 /// Decl pointers to details about them being exported.
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.
198201 decl_table: std.AutoHashMap(Decl.Hash, *Decl),
202
199203 optimize_mode: std.builtin.Mode,
200204 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
202232 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,
209233 /// This name is relative to the containing namespace of the decl. It uses a null-termination
210234 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
211235 /// 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.
212239 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 more
214 /// than one export, so we use a linked list to save memory.
215 export_node: ?*LinkedList(std.builtin.ExportOptions).Node = null,
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,
216244 /// Byte offset into the source file that contains this declaration.
217245 /// This is the base offset that src offsets within this Decl are relative to.
218246 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 },
219253 /// Represents the "shallow" analysis status. For example, for decls that are functions,
220254 /// the function type is analyzed with this set to `in_progress`, however, the semantic
221255 /// analysis of the function body is performed with this value set to `success`. Functions
222256 /// have their own analysis status field.
223 analysis: union(enum) {
224 in_progress,
225 failure: ErrorMsg,
226 success: TypedValue,
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 /// 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,
227279 },
228 /// The direct container of the Decl. This field will need to get more fleshed out when
229 /// self-hosted supports proper struct types and Zig AST => ZIR.
230 scope: *Scope.ZIRModule,
280
281 /// Represents the position of the code, if any, in the output file.
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
232306 pub fn destroy(self: *Decl, allocator: *Allocator) void {
233 var arena = self.arena.promote(allocator);
234 arena.deinit();
307 allocator.free(mem.spanZ(u8, self.name));
308 if (self.typedValue()) |tv| tv.deinit(allocator);
309 allocator.destroy(self);
235310 }
236311
237312 pub const Hash = [16]u8;
......@@ -252,8 +327,10 @@ pub const Module = struct {
252327 pub const Fn = struct {
253328 fn_type: Type,
254329 analysis: union(enum) {
330 queued,
255331 in_progress: *Analysis,
256 failure: ErrorMsg,
332 /// There will be a corresponding ErrorMsg in Module.failed_fns
333 failure,
257334 success: Body,
258335 },
259336 /// The direct container of the Fn. This field will need to get more fleshed out when
......@@ -290,68 +367,36 @@ pub const Module = struct {
290367 /// Relative to the owning package's root_src_dir.
291368 /// Reference to external memory, not owned by ZIRModule.
292369 sub_file_path: []const u8,
293 contents: union(enum) {
370 source: union {
294371 unloaded,
295 parse_failure: ParseFailure,
296 success: Contents,
372 bytes: [:0]const u8,
297373 },
298 pub const ParseFailure = struct {
299 source: [:0]const u8,
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,
374 contents: union {
375 not_available,
309376 module: *text.Module,
310 };
377 },
378 status: enum {
379 unloaded,
380 unloaded_parse_failure,
381 loaded_parse_failure,
382 loaded_success,
383 },
311384
312385 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
313 switch (self.contents) {
314 .unloaded => {},
315 .parse_failure => |pf| pd.deinit(allocator),
316 .success => |contents| {
386 switch (self.status) {
387 .unloaded,
388 .unloaded_parse_failure,
389 => {},
390 .loaded_success => {
391 allocator.free(contents.source);
392 self.contents.module.deinit(allocator);
393 },
394 .loaded_parse_failure => {
317395 allocator.free(contents.source);
318 contents.src_zir_module.deinit(allocator);
319396 },
320397 }
321398 self.* = undefined;
322399 }
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 }
355400 };
356401
357402 /// This is a temporary structure, references to it are valid only
......@@ -436,7 +481,7 @@ pub const Module = struct {
436481 // Analyze the root source file now.
437482 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
438483 error.AnalysisFail => {
439 assert(self.totalErrorCount() != 0);
484 assert(self.failed_files.size != 0);
440485 },
441486 else => |e| return e,
442487 };
......@@ -446,9 +491,10 @@ pub const Module = struct {
446491 }
447492
448493 pub fn totalErrorCount(self: *Module) usize {
449 return self.failed_decls.items.len +
450 self.failed_fns.items.len +
451 self.failed_decls.items.len +
494 return self.failed_decls.size +
495 self.failed_fns.size +
496 self.failed_decls.size +
497 self.failed_exports.size +
452498 @boolToInt(self.link_error_flags.no_entry_point_found);
453499 }
454500
......@@ -459,26 +505,42 @@ pub const Module = struct {
459505 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
460506 defer errors.deinit();
461507
462 for (self.failed_files.items) |scope| {
463 const source = scope.parse_failure.source;
464 for (scope.parse_failure.errors) |parse_error| {
465 AllErrors.add(&arena, &errors, scope.sub_file_path, source, parse_error);
508 {
509 var it = self.failed_files.iterator();
510 while (it.next()) |kv| {
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);
466515 }
467516 }
468
469 for (self.failed_fns.items) |func| {
470 const source = func.scope.success.source;
471 for (func.analysis.failure) |err_msg| {
517 {
518 var it = self.failed_fns.iterator();
519 while (it.next()) |kv| {
520 const func = kv.key;
521 const err_msg = kv.value;
522 const source = func.scope.success.source;
472523 AllErrors.add(&arena, &errors, func.scope.sub_file_path, source, err_msg);
473524 }
474525 }
475
476 for (self.failed_decls.items) |decl| {
477 const source = decl.scope.success.source;
478 for (decl.analysis.failure) |err_msg| {
526 {
527 var it = self.failed_decls.iterator();
528 while (it.next()) |kv| {
529 const decl = kv.key;
530 const err_msg = kv.value;
531 const source = decl.scope.success.source;
479532 AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg);
480533 }
481534 }
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
483545 if (self.link_error_flags.no_entry_point_found) {
484546 try errors.append(.{
......@@ -508,23 +570,81 @@ pub const Module = struct {
508570 // Here we simulate adding a source file which was previously not part of the compilation,
509571 // which means scanning the decls looking for exports.
510572 // TODO also identify decls that need to be deleted.
511 const contents = blk: {
512 // Clear parse errors.
513 swapRemoveElem(self.allocator, *Scope.ZIRModule, root_scope, self.failed_files);
514 try self.failed_files.ensureCapacity(self.allocator, self.failed_files.items.len + 1);
515 break :blk root_scope.loadContents(self.allocator) catch |err| switch (err) {
516 error.ParseFailure => {
517 self.failed_files.appendAssumeCapacity(root_scope);
573 const src_module = switch (root_scope.status) {
574 .unloaded => blk: {
575 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
576
577 var keep_source = false;
578 const source = try self.root_pkg_dir.readFileAllocOptions(
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;
518602 return error.AnalysisFail;
519 },
520 else => |e| return e,
521 };
603 }
604
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,
522616 };
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
523622 for (contents.module.decls) |decl| {
524623 if (decl.cast(text.Inst.Export)) |export_inst| {
525624 try analyzeExport(self, &root_scope.base, export_inst);
526625 }
527626 }
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 }
528648 }
529649
530650 fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl {
......@@ -548,21 +668,41 @@ pub const Module = struct {
548668 break :blk new_decl;
549669 };
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 };
552676 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 },
554681 else => |e| return e,
555682 };
556683 new_decl.analysis = .{ .success = typed_value };
557 if (try self.bin_file.updateDecl(self.*, typed_value, new_decl.export_node, hash)) |err_msg| {
558 new_decl.analysis = .{ .success = typed_value };
559 } else |err| {
560 return err;
561 }
684 // We ensureCapacity when scanning for decls.
685 self.analysis_queue.appendAssumeCapacity(.{ .decl = new_decl });
562686 return new_decl;
563687 }
564688 }
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
566706 fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst {
567707 if (scope.cast(Scope.Block)) |block| {
568708 if (block.func.inst_table.get(old_inst)) |kv| {
......@@ -570,7 +710,7 @@ pub const Module = struct {
570710 }
571711 }
572712
573 const decl = try self.resolveDecl(scope, old_inst);
713 const decl = try self.resolveCompleteDecl(scope, old_inst);
574714 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
575715 return self.analyzeDeref(scope, old_inst.src, decl_ref);
576716 }
......@@ -621,29 +761,52 @@ pub const Module = struct {
621761 }
622762
623763 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);
624766 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) {
628 .in_progress => unreachable,
629 .failure => return error.AnalysisFail,
630 .success => |typed_value| switch (typed_value.ty.zigTypeTag()) {
631 .Fn => {},
632 else => return self.fail(
633 scope,
634 export_inst.positionals.value.src,
635 "unable to export type '{}'",
636 .{typed_value.ty},
637 ),
638 },
781 const owner_decl = scope.getDecl();
782
783 new_export.* = .{
784 .options = .{ .data = .{ .name = symbol_name } },
785 .src = export_inst.base.src,
786 .link = .{},
787 .owner_decl = owner_decl,
788 .status = .in_progress,
789 };
790
791 // Add to export_owners table.
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{};
639804 }
640 const Node = LinkedList(std.builtin.ExportOptions).Node;
641 export_node = try decl.arena.promote(self.allocator).allocator.create(Node);
642 export_node.* = .{ .data = .{ .name = symbol_name } };
643 decl.export_node = export_node;
805 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);
806 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
807 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
644808
645 // TODO Avoid double update in the case of exporting a decl that we just created.
646 self.bin_file.updateDeclExports();
809 try self.bin_file.updateDeclExports(self, decl, de_gop.kv.value);
647810 }
648811
649812 /// TODO should not need the cast on the last parameter at the callsites
......@@ -1636,6 +1799,31 @@ pub const Module = struct {
16361799pub const ErrorMsg = struct {
16371800 byte_offset: usize,
16381801 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 }
16391827};
16401828
16411829pub fn main() anyerror!void {
src-self-hosted/ir/text.zig+6-10
......@@ -406,8 +406,8 @@ pub const ErrorMsg = struct {
406406
407407pub const Module = struct {
408408 decls: []*Inst,
409 errors: []ErrorMsg,
410409 arena: std.heap.ArenaAllocator.State,
410 error_msg: ?ErrorMsg = null,
411411
412412 pub const Body = struct {
413413 instructions: []*Inst,
......@@ -415,7 +415,6 @@ pub const Module = struct {
415415
416416 pub fn deinit(self: *Module, allocator: *Allocator) void {
417417 allocator.free(self.decls);
418 allocator.free(self.errors);
419418 self.arena.promote(allocator).deinit();
420419 self.* = undefined;
421420 }
......@@ -576,22 +575,21 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
576575 .i = 0,
577576 .source = source,
578577 .global_name_map = &global_name_map,
579 .errors = .{},
580578 .decls = .{},
581579 };
582580 errdefer parser.arena.deinit();
583581
584582 parser.parseRoot() catch |err| switch (err) {
585583 error.ParseFailure => {
586 assert(parser.errors.items.len != 0);
584 assert(parser.error_msg != null);
587585 },
588586 else => |e| return e,
589587 };
590588
591589 return Module{
592590 .decls = parser.decls.toOwnedSlice(allocator),
593 .errors = parser.errors.toOwnedSlice(allocator),
594591 .arena = parser.arena.state,
592 .error_msg = parser.error_msg,
595593 };
596594}
597595
......@@ -600,9 +598,9 @@ const Parser = struct {
600598 arena: std.heap.ArenaAllocator,
601599 i: usize,
602600 source: [:0]const u8,
603 errors: std.ArrayListUnmanaged(ErrorMsg),
604601 decls: std.ArrayListUnmanaged(*Inst),
605602 global_name_map: *std.StringHashMap(usize),
603 error_msg: ?ErrorMsg = null,
606604
607605 const Body = struct {
608606 instructions: std.ArrayList(*Inst),
......@@ -776,10 +774,9 @@ const Parser = struct {
776774
777775 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
778776 @setCold(true);
779 const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
780 (try self.errors.addOne()).* = .{
777 self.error_msg = ErrorMsg{
781778 .byte_offset = self.i,
782 .msg = msg,
779 .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
783780 };
784781 return error.ParseFailure;
785782 }
......@@ -971,7 +968,6 @@ pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module {
971968 return Module{
972969 .decls = ctx.decls.toOwnedSlice(),
973970 .arena = ctx.arena,
974 .errors = &[0]ErrorMsg{},
975971 };
976972}
977973
src-self-hosted/link.zig+225-127
......@@ -130,6 +130,20 @@ pub const ElfFile = struct {
130130 no_entry_point_found: bool = false,
131131 };
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
133147 pub fn deinit(self: *ElfFile) void {
134148 self.sections.deinit(self.allocator);
135149 self.program_headers.deinit(self.allocator);
......@@ -138,7 +152,7 @@ pub const ElfFile = struct {
138152 self.offset_table.deinit(self.allocator);
139153 }
140154
141 // `expand_num / expand_den` is the factor of padding when allocation
155 // `alloc_num / alloc_den` is the factor of padding when allocation
142156 const alloc_num = 4;
143157 const alloc_den = 3;
144158
......@@ -216,12 +230,21 @@ pub const ElfFile = struct {
216230 }
217231
218232 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {
233 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
219234 const result = self.shstrtab.items.len;
220 try self.shstrtab.appendSlice(bytes);
221 try self.shstrtab.append(0);
235 self.shstrtab.appendSliceAssumeCapacity(bytes);
236 self.shstrtab.appendAssumeCapacity(0);
222237 return @intCast(u32, result);
223238 }
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
225248 pub fn populateMissingMetadata(self: *ElfFile) !void {
226249 const small_ptr = switch (self.ptr_width) {
227250 .p32 => true,
......@@ -575,166 +598,200 @@ pub const ElfFile = struct {
575598 try self.file.pwriteAll(hdr_buf[0..index], 0);
576599 }
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
587601 const AllocatedBlock = struct {
588602 vaddr: u64,
589603 file_offset: u64,
590604 size_capacity: u64,
591605 };
592606
593 fn allocateDeclSymbol(self: *ElfFile, size: u64) AllocatedBlock {
607 fn allocateTextBlock(self: *ElfFile, new_block_size: u64) !AllocatedBlock {
594608 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
595 todo();
596 //{
597 // // Now that we know the code size, we need to update the program header for executable code
598 // phdr.p_memsz = vaddr - phdr.p_vaddr;
599 // phdr.p_filesz = phdr.p_memsz;
600
601 // const shdr = &self.sections.items[self.text_section_index.?];
602 // shdr.sh_size = phdr.p_filesz;
609 const shdr = &self.sections.items[self.text_section_index.?];
610
611 const text_capacity = self.allocatedSize(shdr.sh_offset);
612 // TODO instead of looping here, maintain a free list and a pointer to the end.
613 const end_vaddr = blk: {
614 var start: u64 = 0;
615 var size: u64 = 0;
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 dirty
605 // self.shdr_table_dirty = true; // TODO look into making only the one section dirty
606 //}
625 const text_size = end_vaddr - phdr.p_vaddr;
626 const needed_size = text_size + new_block_size;
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
609641 }
610642
611 fn findAllocatedBlock(self: *ElfFile, vaddr: u64) AllocatedBlock {
612 todo();
643 fn findAllocatedTextBlock(self: *ElfFile, sym: elf.Elf64_Sym) AllocatedBlock {
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 };
613660 }
614661
615 pub fn updateDecl(
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 {
662 pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void {
623663 var code = std.ArrayList(u8).init(self.allocator);
624664 defer code.deinit();
625665
626 const err_msg = try codegen.generateSymbol(typed_value, module, &code, err_msg_allocator);
627 if (err_msg != null) |em| return em;
666 const typed_value = decl.typed_value.most_recent.typed_value;
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: {
630 var export_node = decl_export_node;
631 var i: usize = 0;
632 while (export_node) |node| : (export_node = node.next) i += 1;
633 break :blk i;
634 };
674 const file_offset = blk: {
675 const code_size = code.items.len;
676 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
677 .Fn => elf.STT_FUNC,
678 else => elf.STT_OBJECT,
679 };
635680
636 // Find or create a symbol from the decl
637 var valid_sym_index_len: usize = 0;
638 const decl_symbol = blk: {
639 if (self.decl_table.getValue(hash)) |decl_symbol| {
640 valid_sym_index_len = decl_symbol.symbol_indexes.len;
641 decl_symbol.symbol_indexes = try self.allocator.realloc(usize, export_count);
642
643 const existing_block = self.findAllocatedBlock(decl_symbol.vaddr);
644 if (code.items.len > existing_block.size_capacity) {
645 const new_block = self.allocateDeclSymbol(code.items.len);
646 decl_symbol.vaddr = new_block.vaddr;
647 decl_symbol.file_offset = new_block.file_offset;
648 decl_symbol.size = code.items.len;
649 }
650 break :blk decl_symbol;
681 if (decl.link.local_sym_index) |local_sym_index| {
682 const local_sym = &self.symbols.items[local_sym_index];
683 const existing_block = self.findAllocatedTextBlock(local_sym);
684 const file_offset = if (code_size > existing_block.size_capacity) fo: {
685 const new_block = self.allocateTextBlock(code_size);
686 local_sym.st_value = new_block.vaddr;
687 local_sym.st_size = code_size;
688 break :fo new_block.file_offset;
689 } else existing_block.file_offset;
690 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name));
691 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
692 // TODO this write could be avoided if no fields of the symbol were changed.
693 try self.writeSymbol(local_sym_index);
694 break :blk file_offset;
651695 } else {
652 const new_block = self.allocateDeclSymbol(code.items.len);
653
654 const decl_symbol = try self.allocator.create(DeclSymbol);
655 errdefer self.allocator.destroy(decl_symbol);
656
657 decl_symbol.* = .{
658 .symbol_indexes = try self.allocator.alloc(usize, export_count),
659 .vaddr = new_block.vaddr,
660 .file_offset = new_block.file_offset,
661 .size = code.items.len,
662 };
663 errdefer self.allocator.free(decl_symbol.symbol_indexes);
664
665 try self.decl_table.put(hash, decl_symbol);
666 break :blk decl_symbol;
696 try self.symbols.ensureCapacity(self.symbols.items.len + 1);
697 const decl_name = mem.spanZ(u8, decl.name);
698 const name_str_index = try self.makeString(decl_name);
699 const new_block = self.allocateTextBlock(code_size);
700 const local_sym_index = self.symbols.items.len;
701
702 self.symbols.appendAssumeCapacity(self.allocator, .{
703 .st_name = name_str_index,
704 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
705 .st_other = 0,
706 .st_shndx = self.text_section_index.?,
707 .st_value = new_block.vaddr,
708 .st_size = code_size,
709 });
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;
667717 }
668718 };
669719
670 // Allocate new symbols.
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 }
720 try self.file.pwriteAll(code.items, file_offset);
679721
680 var export_node = decl_export_node;
681 var export_index: usize = 0;
682 while (export_node) |node| : ({
683 export_node = node.next;
684 export_index += 1;
685 }) {
686 if (node.data.section) |section_name| {
722 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
723 const decl_exports = module.decl_exports.get(decl) orelse &[0]*ir.Module.Export{};
724 return self.updateDeclExports(module, decl, decl_exports);
725 }
726
727 /// Must be called only after a successful call to `updateDecl`.
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| {
687740 if (!mem.eql(u8, section_name, ".text")) {
688 try errors.ensureCapacity(errors.items.len + 1);
689 errors.appendAssumeCapacity(.{
690 .byte_offset = 0,
691 .msg = try std.fmt.allocPrint(errors.allocator, "Unimplemented: ExportOptions.section", .{}),
692 });
741 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
742 module.failed_exports.putAssumeCapacityNoClobber(
743 exp,
744 try ir.ErrorMsg.create(0, "Unimplemented: ExportOptions.section", .{}),
745 );
693746 }
694747 }
695 const stb_bits = switch (node.data.linkage) {
748 const stb_bits = switch (exp.options.linkage) {
696749 .Internal => elf.STB_LOCAL,
697750 .Strong => blk: {
698 if (mem.eql(u8, node.data.name, "_start")) {
751 if (mem.eql(u8, exp.options.name, "_start")) {
699752 self.entry_addr = decl_symbol.vaddr;
700753 }
701754 break :blk elf.STB_GLOBAL;
702755 },
703756 .Weak => elf.STB_WEAK,
704757 .LinkOnce => {
705 try errors.ensureCapacity(errors.items.len + 1);
706 errors.appendAssumeCapacity(.{
707 .byte_offset = 0,
708 .msg = try std.fmt.allocPrint(errors.allocator, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
709 });
758 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
759 module.failed_exports.putAssumeCapacityNoClobber(
760 exp,
761 try ir.ErrorMsg.create(0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
762 );
710763 },
711764 };
712 const stt_bits = switch (typed_value.ty.zigTypeTag()) {
713 .Fn => elf.STT_FUNC,
714 else => elf.STT_OBJECT,
715 };
716 const sym_index = decl_symbol.symbol_indexes[export_index];
717 const name = blk: {
718 if (i < valid_sym_index_len) {
719 const name_stroff = self.symbols.items[sym_index].st_name;
720 const existing_name = self.getString(name_stroff);
721 if (mem.eql(u8, existing_name, node.data.name)) {
722 break :blk name_stroff;
723 }
724 }
725 break :blk try self.makeString(node.data.name);
726 };
727 self.symbols.items[sym_index] = .{
728 .st_name = name,
729 .st_info = (stb_bits << 4) | stt_bits,
730 .st_other = 0,
731 .st_shndx = self.text_section_index.?,
732 .st_value = decl_symbol.vaddr,
733 .st_size = code.items.len,
734 };
765 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
766 if (exp.link.sym_index) |i| {
767 const sym = &self.symbols.items[i];
768 sym.* = .{
769 .st_name = try self.updateString(sym.st_name, exp.options.name),
770 .st_info = (stb_bits << 4) | stt_bits,
771 .st_other = 0,
772 .st_shndx = self.text_section_index.?,
773 .st_value = decl_sym.st_value,
774 .st_size = decl_sym.st_size,
775 };
776 try self.writeSymbol(i);
777 } else {
778 const name = try self.makeString(exp.options.name);
779 const i = self.symbols.items.len;
780 self.symbols.appendAssumeCapacity(self.allocator, .{
781 .st_name = sn.name,
782 .st_info = (stb_bits << 4) | stt_bits,
783 .st_other = 0,
784 .st_shndx = self.text_section_index.?,
785 .st_value = decl_sym.st_value,
786 .st_size = decl_sym.st_size,
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 }
735794 }
736
737 try self.file.pwriteAll(code.items, decl_symbol.file_offset);
738795 }
739796
740797 fn writeProgHeader(self: *ElfFile, index: usize) !void {
......@@ -782,7 +839,48 @@ pub const ElfFile = struct {
782839 }
783840 }
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 {
786884 const small_ptr = self.ptr_width == .p32;
787885 const syms_sect = &self.sections.items[self.symtab_section_index.?];
788886 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;
55const Target = std.Target;
66
77/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
8/// It's important for this struct to be small.
9/// It is not copyable since it may contain references to its inner data.
8/// It's important for this type to be small.
109/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
1110/// of obtaining a lock on a global type table, as well as making the
1211/// garbage collection bookkeeping simpler.
......@@ -51,6 +50,7 @@ pub const Type = extern union {
5150 .comptime_int => return .ComptimeInt,
5251 .comptime_float => return .ComptimeFloat,
5352 .noreturn => return .NoReturn,
53 .@"null" => return .Null,
5454
5555 .fn_noreturn_no_args => return .Fn,
5656 .fn_naked_noreturn_no_args => return .Fn,
......@@ -184,6 +184,8 @@ pub const Type = extern union {
184184 .noreturn,
185185 => return out_stream.writeAll(@tagName(t)),
186186
187 .@"null" => return out_stream.writeAll("@TypeOf(null)"),
188
187189 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
188190 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
189191 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
......@@ -246,6 +248,7 @@ pub const Type = extern union {
246248 .comptime_int => return Value.initTag(.comptime_int_type),
247249 .comptime_float => return Value.initTag(.comptime_float_type),
248250 .noreturn => return Value.initTag(.noreturn_type),
251 .@"null" => return Value.initTag(.null_type),
249252 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
250253 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
251254 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
......@@ -286,6 +289,7 @@ pub const Type = extern union {
286289 .comptime_int,
287290 .comptime_float,
288291 .noreturn,
292 .@"null",
289293 .array,
290294 .array_u8_sentinel_0,
291295 .const_slice_u8,
......@@ -329,6 +333,7 @@ pub const Type = extern union {
329333 .comptime_int,
330334 .comptime_float,
331335 .noreturn,
336 .@"null",
332337 .array,
333338 .array_u8_sentinel_0,
334339 .single_const_pointer,
......@@ -372,6 +377,7 @@ pub const Type = extern union {
372377 .comptime_int,
373378 .comptime_float,
374379 .noreturn,
380 .@"null",
375381 .array,
376382 .array_u8_sentinel_0,
377383 .fn_noreturn_no_args,
......@@ -416,6 +422,7 @@ pub const Type = extern union {
416422 .comptime_int,
417423 .comptime_float,
418424 .noreturn,
425 .@"null",
419426 .fn_noreturn_no_args,
420427 .fn_naked_noreturn_no_args,
421428 .fn_ccc_void_no_args,
......@@ -458,6 +465,7 @@ pub const Type = extern union {
458465 .comptime_int,
459466 .comptime_float,
460467 .noreturn,
468 .@"null",
461469 .fn_noreturn_no_args,
462470 .fn_naked_noreturn_no_args,
463471 .fn_ccc_void_no_args,
......@@ -489,6 +497,7 @@ pub const Type = extern union {
489497 .comptime_int,
490498 .comptime_float,
491499 .noreturn,
500 .@"null",
492501 .fn_noreturn_no_args,
493502 .fn_naked_noreturn_no_args,
494503 .fn_ccc_void_no_args,
......@@ -533,6 +542,7 @@ pub const Type = extern union {
533542 .comptime_int,
534543 .comptime_float,
535544 .noreturn,
545 .@"null",
536546 .fn_noreturn_no_args,
537547 .fn_naked_noreturn_no_args,
538548 .fn_ccc_void_no_args,
......@@ -606,6 +616,7 @@ pub const Type = extern union {
606616 .comptime_int,
607617 .comptime_float,
608618 .noreturn,
619 .@"null",
609620 .array,
610621 .single_const_pointer,
611622 .single_const_pointer_to_comptime_int,
......@@ -650,6 +661,7 @@ pub const Type = extern union {
650661 .comptime_int,
651662 .comptime_float,
652663 .noreturn,
664 .@"null",
653665 .array,
654666 .single_const_pointer,
655667 .single_const_pointer_to_comptime_int,
......@@ -693,6 +705,7 @@ pub const Type = extern union {
693705 .comptime_int,
694706 .comptime_float,
695707 .noreturn,
708 .@"null",
696709 .array,
697710 .single_const_pointer,
698711 .single_const_pointer_to_comptime_int,
......@@ -736,6 +749,7 @@ pub const Type = extern union {
736749 .comptime_int,
737750 .comptime_float,
738751 .noreturn,
752 .@"null",
739753 .array,
740754 .single_const_pointer,
741755 .single_const_pointer_to_comptime_int,
......@@ -779,6 +793,7 @@ pub const Type = extern union {
779793 .comptime_int,
780794 .comptime_float,
781795 .noreturn,
796 .@"null",
782797 .array,
783798 .single_const_pointer,
784799 .single_const_pointer_to_comptime_int,
......@@ -833,6 +848,7 @@ pub const Type = extern union {
833848 .type,
834849 .anyerror,
835850 .noreturn,
851 .@"null",
836852 .fn_noreturn_no_args,
837853 .fn_naked_noreturn_no_args,
838854 .fn_ccc_void_no_args,
......@@ -881,6 +897,7 @@ pub const Type = extern union {
881897 .c_void,
882898 .void,
883899 .noreturn,
900 .@"null",
884901 => return true,
885902
886903 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
......@@ -933,6 +950,7 @@ pub const Type = extern union {
933950 .c_void,
934951 .void,
935952 .noreturn,
953 .@"null",
936954 .int_unsigned,
937955 .int_signed,
938956 .array,
......@@ -974,6 +992,7 @@ pub const Type = extern union {
974992 comptime_int,
975993 comptime_float,
976994 noreturn,
995 @"null",
977996 fn_noreturn_no_args,
978997 fn_naked_noreturn_no_args,
979998 fn_ccc_void_no_args,
src-self-hosted/value.zig+13-1
......@@ -10,7 +10,7 @@ const ir = @import("ir.zig");
1010
1111/// This is the raw data, with no bookkeeping, no memory awareness,
1212/// 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.
1414/// This union takes advantage of the fact that the first page of memory
1515/// is unmapped, giving us 4096 possible enum tags that have no payload.
1616pub const Value = extern union {
......@@ -46,6 +46,7 @@ pub const Value = extern union {
4646 comptime_int_type,
4747 comptime_float_type,
4848 noreturn_type,
49 null_type,
4950 fn_noreturn_no_args_type,
5051 fn_naked_noreturn_no_args_type,
5152 fn_ccc_void_no_args_type,
......@@ -138,6 +139,7 @@ pub const Value = extern union {
138139 .comptime_int_type => return out_stream.writeAll("comptime_int"),
139140 .comptime_float_type => return out_stream.writeAll("comptime_float"),
140141 .noreturn_type => return out_stream.writeAll("noreturn"),
142 .null_type => return out_stream.writeAll("@TypeOf(null)"),
141143 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
142144 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
143145 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
......@@ -209,6 +211,7 @@ pub const Value = extern union {
209211 .comptime_int_type => Type.initTag(.comptime_int),
210212 .comptime_float_type => Type.initTag(.comptime_float),
211213 .noreturn_type => Type.initTag(.noreturn),
214 .null_type => Type.initTag(.@"null"),
212215 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
213216 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
214217 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
......@@ -263,6 +266,7 @@ pub const Value = extern union {
263266 .comptime_int_type,
264267 .comptime_float_type,
265268 .noreturn_type,
269 .null_type,
266270 .fn_noreturn_no_args_type,
267271 .fn_naked_noreturn_no_args_type,
268272 .fn_ccc_void_no_args_type,
......@@ -319,6 +323,7 @@ pub const Value = extern union {
319323 .comptime_int_type,
320324 .comptime_float_type,
321325 .noreturn_type,
326 .null_type,
322327 .fn_noreturn_no_args_type,
323328 .fn_naked_noreturn_no_args_type,
324329 .fn_ccc_void_no_args_type,
......@@ -376,6 +381,7 @@ pub const Value = extern union {
376381 .comptime_int_type,
377382 .comptime_float_type,
378383 .noreturn_type,
384 .null_type,
379385 .fn_noreturn_no_args_type,
380386 .fn_naked_noreturn_no_args_type,
381387 .fn_ccc_void_no_args_type,
......@@ -438,6 +444,7 @@ pub const Value = extern union {
438444 .comptime_int_type,
439445 .comptime_float_type,
440446 .noreturn_type,
447 .null_type,
441448 .fn_noreturn_no_args_type,
442449 .fn_naked_noreturn_no_args_type,
443450 .fn_ccc_void_no_args_type,
......@@ -529,6 +536,7 @@ pub const Value = extern union {
529536 .comptime_int_type,
530537 .comptime_float_type,
531538 .noreturn_type,
539 .null_type,
532540 .fn_noreturn_no_args_type,
533541 .fn_naked_noreturn_no_args_type,
534542 .fn_ccc_void_no_args_type,
......@@ -582,6 +590,7 @@ pub const Value = extern union {
582590 .comptime_int_type,
583591 .comptime_float_type,
584592 .noreturn_type,
593 .null_type,
585594 .fn_noreturn_no_args_type,
586595 .fn_naked_noreturn_no_args_type,
587596 .fn_ccc_void_no_args_type,
......@@ -674,6 +683,7 @@ pub const Value = extern union {
674683 .comptime_int_type,
675684 .comptime_float_type,
676685 .noreturn_type,
686 .null_type,
677687 .fn_noreturn_no_args_type,
678688 .fn_naked_noreturn_no_args_type,
679689 .fn_ccc_void_no_args_type,
......@@ -736,6 +746,7 @@ pub const Value = extern union {
736746 .comptime_int_type,
737747 .comptime_float_type,
738748 .noreturn_type,
749 .null_type,
739750 .fn_noreturn_no_args_type,
740751 .fn_naked_noreturn_no_args_type,
741752 .fn_ccc_void_no_args_type,
......@@ -812,6 +823,7 @@ pub const Value = extern union {
812823 .comptime_int_type,
813824 .comptime_float_type,
814825 .noreturn_type,
826 .null_type,
815827 .fn_noreturn_no_args_type,
816828 .fn_naked_noreturn_no_args_type,
817829 .fn_ccc_void_no_args_type,