| author | |
| committer | |
| log | 619159cf48e953ca65933391313a72c392007710 |
| tree | 315cb12cff807374cd8fbbad3c06107241633565 |
| parent | a32d3a85d21d614e5960b9eadcd85374954b910f |
* 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.putAssumeCapacityNoClobber9 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 | 149 | /// Append the slice of items to the list. Allocates more |
| 150 | 150 | /// memory as necessary. |
| 151 | 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 | 159 | const oldlen = self.items.len; |
| 153 | 160 | const newlen = self.items.len + items.len; |
| 154 | ||
| 155 | try self.ensureCapacity(newlen); | |
| 156 | 161 | self.items.len = newlen; |
| 157 | 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 | 383 | /// Append the slice of items to the list. Allocates more |
| 379 | 384 | /// memory as necessary. |
| 380 | 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 | 393 | const oldlen = self.items.len; |
| 382 | 394 | const newlen = self.items.len + items.len; |
| 383 | 395 | |
| 384 | try self.ensureCapacity(allocator, newlen); | |
| 385 | 396 | self.items.len = newlen; |
| 386 | 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 | 527 | } |
| 528 | 528 | } |
| 529 | 529 | |
| 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 | 554 | pub const WriteFileOptions = struct { |
| 531 | 555 | in_offset: u64 = 0, |
| 532 | 556 |
lib/std/hash_map.zig+5-1| ... | ... | @@ -10,7 +10,7 @@ const Wyhash = std.hash.Wyhash; |
| 10 | 10 | const Allocator = mem.Allocator; |
| 11 | 11 | const builtin = @import("builtin"); |
| 12 | 12 | |
| 13 | const want_modification_safety = builtin.mode != .ReleaseFast; | |
| 13 | const want_modification_safety = std.debug.runtime_safety; | |
| 14 | 14 | const debug_u32 = if (want_modification_safety) u32 else void; |
| 15 | 15 | |
| 16 | 16 | pub 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 | 219 | return put_result.old_kv; |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void { | |
| 223 | assert(self.putAssumeCapacity(key, value) == null); | |
| 224 | } | |
| 225 | ||
| 222 | 226 | pub fn get(hm: *const Self, key: K) ?*KV { |
| 223 | 227 | if (hm.entries.len == 0) { |
| 224 | 228 | return null; |
src-self-hosted/TypedValue.zig created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Type = @import("type.zig").Type; | |
| 3 | const Value = @import("value.zig").Value; | |
| 4 | const Allocator = std.mem.Allocator; | |
| 5 | const TypedValue = @This(); | |
| 6 | ||
| 7 | ty: Type, | |
| 8 | val: 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. | |
| 12 | pub 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 | 5 | const LinkedList = std.TailQueue; |
| 6 | 6 | const Value = @import("value.zig").Value; |
| 7 | 7 | const Type = @import("type.zig").Type; |
| 8 | const TypedValue = @import("TypedValue.zig"); | |
| 8 | 9 | const assert = std.debug.assert; |
| 9 | 10 | const BigIntConst = std.math.big.int.Const; |
| 10 | 11 | const BigIntMutable = std.math.big.int.Mutable; |
| ... | ... | @@ -167,11 +168,6 @@ pub const Inst = struct { |
| 167 | 168 | }; |
| 168 | 169 | }; |
| 169 | 170 | |
| 170 | pub const TypedValue = struct { | |
| 171 | ty: Type, | |
| 172 | val: Value, | |
| 173 | }; | |
| 174 | ||
| 175 | 171 | fn swapRemoveElem(allocator: *Allocator, comptime T: type, item: T, list: *ArrayListUnmanaged(T)) void { |
| 176 | 172 | var i: usize = 0; |
| 177 | 173 | while (i < list.items.len) { |
| ... | ... | @@ -192,46 +188,125 @@ pub const Module = struct { |
| 192 | 188 | root_scope: *Scope.ZIRModule, |
| 193 | 189 | /// Pointer to externally managed resource. |
| 194 | 190 | 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. | |
| 198 | 201 | decl_table: std.AutoHashMap(Decl.Hash, *Decl), |
| 202 | ||
| 199 | 203 | optimize_mode: std.builtin.Mode, |
| 200 | 204 | link_error_flags: link.ElfFile.ErrorFlags = .{}, |
| 201 | 205 | |
| 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 | 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 | 233 | /// This name is relative to the containing namespace of the decl. It uses a null-termination |
| 210 | 234 | /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed |
| 211 | 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 | 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 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, | |
| 216 | 244 | /// Byte offset into the source file that contains this declaration. |
| 217 | 245 | /// This is the base offset that src offsets within this Decl are relative to. |
| 218 | 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 | 253 | /// Represents the "shallow" analysis status. For example, for decls that are functions, |
| 220 | 254 | /// the function type is analyzed with this set to `in_progress`, however, the semantic |
| 221 | 255 | /// analysis of the function body is performed with this value set to `success`. Functions |
| 222 | 256 | /// 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, | |
| 227 | 279 | }, |
| 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 | } | |
| 231 | 305 | |
| 232 | 306 | 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); | |
| 235 | 310 | } |
| 236 | 311 | |
| 237 | 312 | pub const Hash = [16]u8; |
| ... | ... | @@ -252,8 +327,10 @@ pub const Module = struct { |
| 252 | 327 | pub const Fn = struct { |
| 253 | 328 | fn_type: Type, |
| 254 | 329 | analysis: union(enum) { |
| 330 | queued, | |
| 255 | 331 | in_progress: *Analysis, |
| 256 | failure: ErrorMsg, | |
| 332 | /// There will be a corresponding ErrorMsg in Module.failed_fns | |
| 333 | failure, | |
| 257 | 334 | success: Body, |
| 258 | 335 | }, |
| 259 | 336 | /// 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 | 367 | /// Relative to the owning package's root_src_dir. |
| 291 | 368 | /// Reference to external memory, not owned by ZIRModule. |
| 292 | 369 | sub_file_path: []const u8, |
| 293 | contents: union(enum) { | |
| 370 | source: union { | |
| 294 | 371 | unloaded, |
| 295 | parse_failure: ParseFailure, | |
| 296 | success: Contents, | |
| 372 | bytes: [:0]const u8, | |
| 297 | 373 | }, |
| 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, | |
| 309 | 376 | module: *text.Module, |
| 310 | }; | |
| 377 | }, | |
| 378 | status: enum { | |
| 379 | unloaded, | |
| 380 | unloaded_parse_failure, | |
| 381 | loaded_parse_failure, | |
| 382 | loaded_success, | |
| 383 | }, | |
| 311 | 384 | |
| 312 | 385 | 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 => { | |
| 317 | 395 | allocator.free(contents.source); |
| 318 | contents.src_zir_module.deinit(allocator); | |
| 319 | 396 | }, |
| 320 | 397 | } |
| 321 | 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 | }; |
| 356 | 401 | |
| 357 | 402 | /// This is a temporary structure, references to it are valid only |
| ... | ... | @@ -436,7 +481,7 @@ pub const Module = struct { |
| 436 | 481 | // Analyze the root source file now. |
| 437 | 482 | self.analyzeRoot(self.root_scope) catch |err| switch (err) { |
| 438 | 483 | error.AnalysisFail => { |
| 439 | assert(self.totalErrorCount() != 0); | |
| 484 | assert(self.failed_files.size != 0); | |
| 440 | 485 | }, |
| 441 | 486 | else => |e| return e, |
| 442 | 487 | }; |
| ... | ... | @@ -446,9 +491,10 @@ pub const Module = struct { |
| 446 | 491 | } |
| 447 | 492 | |
| 448 | 493 | 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 + | |
| 452 | 498 | @boolToInt(self.link_error_flags.no_entry_point_found); |
| 453 | 499 | } |
| 454 | 500 | |
| ... | ... | @@ -459,26 +505,42 @@ pub const Module = struct { |
| 459 | 505 | var errors = std.ArrayList(AllErrors.Message).init(self.allocator); |
| 460 | 506 | defer errors.deinit(); |
| 461 | 507 | |
| 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); | |
| 466 | 515 | } |
| 467 | 516 | } |
| 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; | |
| 472 | 523 | AllErrors.add(&arena, &errors, func.scope.sub_file_path, source, err_msg); |
| 473 | 524 | } |
| 474 | 525 | } |
| 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; | |
| 479 | 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 | } | |
| 482 | 544 | |
| 483 | 545 | if (self.link_error_flags.no_entry_point_found) { |
| 484 | 546 | try errors.append(.{ |
| ... | ... | @@ -508,23 +570,81 @@ pub const Module = struct { |
| 508 | 570 | // Here we simulate adding a source file which was previously not part of the compilation, |
| 509 | 571 | // which means scanning the decls looking for exports. |
| 510 | 572 | // 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; | |
| 518 | 602 | 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, | |
| 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 | 622 | for (contents.module.decls) |decl| { |
| 524 | 623 | if (decl.cast(text.Inst.Export)) |export_inst| { |
| 525 | 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 | } |
| 529 | 649 | |
| 530 | 650 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl { |
| ... | ... | @@ -548,21 +668,41 @@ pub const Module = struct { |
| 548 | 668 | break :blk new_decl; |
| 549 | 669 | }; |
| 550 | 670 | |
| 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 | 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 | 681 | else => |e| return e, |
| 555 | 682 | }; |
| 556 | 683 | 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 }); | |
| 562 | 686 | return new_decl; |
| 563 | 687 | } |
| 564 | 688 | } |
| 565 | 689 | |
| 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 | 706 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst { |
| 567 | 707 | if (scope.cast(Scope.Block)) |block| { |
| 568 | 708 | if (block.func.inst_table.get(old_inst)) |kv| { |
| ... | ... | @@ -570,7 +710,7 @@ pub const Module = struct { |
| 570 | 710 | } |
| 571 | 711 | } |
| 572 | 712 | |
| 573 | const decl = try self.resolveDecl(scope, old_inst); | |
| 713 | const decl = try self.resolveCompleteDecl(scope, old_inst); | |
| 574 | 714 | const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl); |
| 575 | 715 | return self.analyzeDeref(scope, old_inst.src, decl_ref); |
| 576 | 716 | } |
| ... | ... | @@ -621,29 +761,52 @@ pub const Module = struct { |
| 621 | 761 | } |
| 622 | 762 | |
| 623 | 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 | 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); | |
| 626 | 780 | |
| 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{}; | |
| 639 | 804 | } |
| 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); | |
| 644 | 808 | |
| 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); | |
| 647 | 810 | } |
| 648 | 811 | |
| 649 | 812 | /// TODO should not need the cast on the last parameter at the callsites |
| ... | ... | @@ -1636,6 +1799,31 @@ pub const Module = struct { |
| 1636 | 1799 | pub const ErrorMsg = struct { |
| 1637 | 1800 | byte_offset: usize, |
| 1638 | 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 | }; |
| 1640 | 1828 | |
| 1641 | 1829 | pub fn main() anyerror!void { |
src-self-hosted/ir/text.zig+6-10| ... | ... | @@ -406,8 +406,8 @@ pub const ErrorMsg = struct { |
| 406 | 406 | |
| 407 | 407 | pub const Module = struct { |
| 408 | 408 | decls: []*Inst, |
| 409 | errors: []ErrorMsg, | |
| 410 | 409 | arena: std.heap.ArenaAllocator.State, |
| 410 | error_msg: ?ErrorMsg = null, | |
| 411 | 411 | |
| 412 | 412 | pub const Body = struct { |
| 413 | 413 | instructions: []*Inst, |
| ... | ... | @@ -415,7 +415,6 @@ pub const Module = struct { |
| 415 | 415 | |
| 416 | 416 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 417 | 417 | allocator.free(self.decls); |
| 418 | allocator.free(self.errors); | |
| 419 | 418 | self.arena.promote(allocator).deinit(); |
| 420 | 419 | self.* = undefined; |
| 421 | 420 | } |
| ... | ... | @@ -576,22 +575,21 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module |
| 576 | 575 | .i = 0, |
| 577 | 576 | .source = source, |
| 578 | 577 | .global_name_map = &global_name_map, |
| 579 | .errors = .{}, | |
| 580 | 578 | .decls = .{}, |
| 581 | 579 | }; |
| 582 | 580 | errdefer parser.arena.deinit(); |
| 583 | 581 | |
| 584 | 582 | parser.parseRoot() catch |err| switch (err) { |
| 585 | 583 | error.ParseFailure => { |
| 586 | assert(parser.errors.items.len != 0); | |
| 584 | assert(parser.error_msg != null); | |
| 587 | 585 | }, |
| 588 | 586 | else => |e| return e, |
| 589 | 587 | }; |
| 590 | 588 | |
| 591 | 589 | return Module{ |
| 592 | 590 | .decls = parser.decls.toOwnedSlice(allocator), |
| 593 | .errors = parser.errors.toOwnedSlice(allocator), | |
| 594 | 591 | .arena = parser.arena.state, |
| 592 | .error_msg = parser.error_msg, | |
| 595 | 593 | }; |
| 596 | 594 | } |
| 597 | 595 | |
| ... | ... | @@ -600,9 +598,9 @@ const Parser = struct { |
| 600 | 598 | arena: std.heap.ArenaAllocator, |
| 601 | 599 | i: usize, |
| 602 | 600 | source: [:0]const u8, |
| 603 | errors: std.ArrayListUnmanaged(ErrorMsg), | |
| 604 | 601 | decls: std.ArrayListUnmanaged(*Inst), |
| 605 | 602 | global_name_map: *std.StringHashMap(usize), |
| 603 | error_msg: ?ErrorMsg = null, | |
| 606 | 604 | |
| 607 | 605 | const Body = struct { |
| 608 | 606 | instructions: std.ArrayList(*Inst), |
| ... | ... | @@ -776,10 +774,9 @@ const Parser = struct { |
| 776 | 774 | |
| 777 | 775 | fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError { |
| 778 | 776 | @setCold(true); |
| 779 | const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args); | |
| 780 | (try self.errors.addOne()).* = .{ | |
| 777 | self.error_msg = ErrorMsg{ | |
| 781 | 778 | .byte_offset = self.i, |
| 782 | .msg = msg, | |
| 779 | .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args), | |
| 783 | 780 | }; |
| 784 | 781 | return error.ParseFailure; |
| 785 | 782 | } |
| ... | ... | @@ -971,7 +968,6 @@ pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module { |
| 971 | 968 | return Module{ |
| 972 | 969 | .decls = ctx.decls.toOwnedSlice(), |
| 973 | 970 | .arena = ctx.arena, |
| 974 | .errors = &[0]ErrorMsg{}, | |
| 975 | 971 | }; |
| 976 | 972 | } |
| 977 | 973 |
src-self-hosted/link.zig+225-127| ... | ... | @@ -130,6 +130,20 @@ pub const ElfFile = struct { |
| 130 | 130 | no_entry_point_found: bool = false, |
| 131 | 131 | }; |
| 132 | 132 | |
| 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 | 147 | pub fn deinit(self: *ElfFile) void { |
| 134 | 148 | self.sections.deinit(self.allocator); |
| 135 | 149 | self.program_headers.deinit(self.allocator); |
| ... | ... | @@ -138,7 +152,7 @@ pub const ElfFile = struct { |
| 138 | 152 | self.offset_table.deinit(self.allocator); |
| 139 | 153 | } |
| 140 | 154 | |
| 141 | // `expand_num / expand_den` is the factor of padding when allocation | |
| 155 | // `alloc_num / alloc_den` is the factor of padding when allocation | |
| 142 | 156 | const alloc_num = 4; |
| 143 | 157 | const alloc_den = 3; |
| 144 | 158 | |
| ... | ... | @@ -216,12 +230,21 @@ pub const ElfFile = struct { |
| 216 | 230 | } |
| 217 | 231 | |
| 218 | 232 | fn makeString(self: *ElfFile, bytes: []const u8) !u32 { |
| 233 | try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1); | |
| 219 | 234 | 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); | |
| 222 | 237 | return @intCast(u32, result); |
| 223 | 238 | } |
| 224 | 239 | |
| 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 | 248 | pub fn populateMissingMetadata(self: *ElfFile) !void { |
| 226 | 249 | const small_ptr = switch (self.ptr_width) { |
| 227 | 250 | .p32 => true, |
| ... | ... | @@ -575,166 +598,200 @@ pub const ElfFile = struct { |
| 575 | 598 | try self.file.pwriteAll(hdr_buf[0..index], 0); |
| 576 | 599 | } |
| 577 | 600 | |
| 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 | 601 | const AllocatedBlock = struct { |
| 588 | 602 | vaddr: u64, |
| 589 | 603 | file_offset: u64, |
| 590 | 604 | size_capacity: u64, |
| 591 | 605 | }; |
| 592 | 606 | |
| 593 | fn allocateDeclSymbol(self: *ElfFile, size: u64) AllocatedBlock { | |
| 607 | fn allocateTextBlock(self: *ElfFile, new_block_size: u64) !AllocatedBlock { | |
| 594 | 608 | 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 | }; | |
| 603 | 624 | |
| 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; | |
| 607 | 638 | |
| 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 | } |
| 610 | 642 | |
| 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 | }; | |
| 613 | 660 | } |
| 614 | 661 | |
| 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 { | |
| 623 | 663 | var code = std.ArrayList(u8).init(self.allocator); |
| 624 | 664 | defer code.deinit(); |
| 625 | 665 | |
| 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 | } | |
| 628 | 673 | |
| 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 | }; | |
| 635 | 680 | |
| 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; | |
| 651 | 695 | } 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; | |
| 667 | 717 | } |
| 668 | 718 | }; |
| 669 | 719 | |
| 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); | |
| 679 | 721 | |
| 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| { | |
| 687 | 740 | 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 | ); | |
| 693 | 746 | } |
| 694 | 747 | } |
| 695 | const stb_bits = switch (node.data.linkage) { | |
| 748 | const stb_bits = switch (exp.options.linkage) { | |
| 696 | 749 | .Internal => elf.STB_LOCAL, |
| 697 | 750 | .Strong => blk: { |
| 698 | if (mem.eql(u8, node.data.name, "_start")) { | |
| 751 | if (mem.eql(u8, exp.options.name, "_start")) { | |
| 699 | 752 | self.entry_addr = decl_symbol.vaddr; |
| 700 | 753 | } |
| 701 | 754 | break :blk elf.STB_GLOBAL; |
| 702 | 755 | }, |
| 703 | 756 | .Weak => elf.STB_WEAK, |
| 704 | 757 | .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 | ); | |
| 710 | 763 | }, |
| 711 | 764 | }; |
| 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 | } | |
| 735 | 794 | } |
| 736 | ||
| 737 | try self.file.pwriteAll(code.items, decl_symbol.file_offset); | |
| 738 | 795 | } |
| 739 | 796 | |
| 740 | 797 | fn writeProgHeader(self: *ElfFile, index: usize) !void { |
| ... | ... | @@ -782,7 +839,48 @@ pub const ElfFile = struct { |
| 782 | 839 | } |
| 783 | 840 | } |
| 784 | 841 | |
| 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 | 884 | const small_ptr = self.ptr_width == .p32; |
| 787 | 885 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 788 | 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 | 5 | const Target = std.Target; |
| 6 | 6 | |
| 7 | 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. | |
| 9 | /// It is not copyable since it may contain references to its inner data. | |
| 8 | /// It's important for this type to be small. | |
| 10 | 9 | /// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement |
| 11 | 10 | /// of obtaining a lock on a global type table, as well as making the |
| 12 | 11 | /// garbage collection bookkeeping simpler. |
| ... | ... | @@ -51,6 +50,7 @@ pub const Type = extern union { |
| 51 | 50 | .comptime_int => return .ComptimeInt, |
| 52 | 51 | .comptime_float => return .ComptimeFloat, |
| 53 | 52 | .noreturn => return .NoReturn, |
| 53 | .@"null" => return .Null, | |
| 54 | 54 | |
| 55 | 55 | .fn_noreturn_no_args => return .Fn, |
| 56 | 56 | .fn_naked_noreturn_no_args => return .Fn, |
| ... | ... | @@ -184,6 +184,8 @@ pub const Type = extern union { |
| 184 | 184 | .noreturn, |
| 185 | 185 | => return out_stream.writeAll(@tagName(t)), |
| 186 | 186 | |
| 187 | .@"null" => return out_stream.writeAll("@TypeOf(null)"), | |
| 188 | ||
| 187 | 189 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 188 | 190 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), |
| 189 | 191 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| ... | ... | @@ -246,6 +248,7 @@ pub const Type = extern union { |
| 246 | 248 | .comptime_int => return Value.initTag(.comptime_int_type), |
| 247 | 249 | .comptime_float => return Value.initTag(.comptime_float_type), |
| 248 | 250 | .noreturn => return Value.initTag(.noreturn_type), |
| 251 | .@"null" => return Value.initTag(.null_type), | |
| 249 | 252 | .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type), |
| 250 | 253 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 251 | 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 | 289 | .comptime_int, |
| 287 | 290 | .comptime_float, |
| 288 | 291 | .noreturn, |
| 292 | .@"null", | |
| 289 | 293 | .array, |
| 290 | 294 | .array_u8_sentinel_0, |
| 291 | 295 | .const_slice_u8, |
| ... | ... | @@ -329,6 +333,7 @@ pub const Type = extern union { |
| 329 | 333 | .comptime_int, |
| 330 | 334 | .comptime_float, |
| 331 | 335 | .noreturn, |
| 336 | .@"null", | |
| 332 | 337 | .array, |
| 333 | 338 | .array_u8_sentinel_0, |
| 334 | 339 | .single_const_pointer, |
| ... | ... | @@ -372,6 +377,7 @@ pub const Type = extern union { |
| 372 | 377 | .comptime_int, |
| 373 | 378 | .comptime_float, |
| 374 | 379 | .noreturn, |
| 380 | .@"null", | |
| 375 | 381 | .array, |
| 376 | 382 | .array_u8_sentinel_0, |
| 377 | 383 | .fn_noreturn_no_args, |
| ... | ... | @@ -416,6 +422,7 @@ pub const Type = extern union { |
| 416 | 422 | .comptime_int, |
| 417 | 423 | .comptime_float, |
| 418 | 424 | .noreturn, |
| 425 | .@"null", | |
| 419 | 426 | .fn_noreturn_no_args, |
| 420 | 427 | .fn_naked_noreturn_no_args, |
| 421 | 428 | .fn_ccc_void_no_args, |
| ... | ... | @@ -458,6 +465,7 @@ pub const Type = extern union { |
| 458 | 465 | .comptime_int, |
| 459 | 466 | .comptime_float, |
| 460 | 467 | .noreturn, |
| 468 | .@"null", | |
| 461 | 469 | .fn_noreturn_no_args, |
| 462 | 470 | .fn_naked_noreturn_no_args, |
| 463 | 471 | .fn_ccc_void_no_args, |
| ... | ... | @@ -489,6 +497,7 @@ pub const Type = extern union { |
| 489 | 497 | .comptime_int, |
| 490 | 498 | .comptime_float, |
| 491 | 499 | .noreturn, |
| 500 | .@"null", | |
| 492 | 501 | .fn_noreturn_no_args, |
| 493 | 502 | .fn_naked_noreturn_no_args, |
| 494 | 503 | .fn_ccc_void_no_args, |
| ... | ... | @@ -533,6 +542,7 @@ pub const Type = extern union { |
| 533 | 542 | .comptime_int, |
| 534 | 543 | .comptime_float, |
| 535 | 544 | .noreturn, |
| 545 | .@"null", | |
| 536 | 546 | .fn_noreturn_no_args, |
| 537 | 547 | .fn_naked_noreturn_no_args, |
| 538 | 548 | .fn_ccc_void_no_args, |
| ... | ... | @@ -606,6 +616,7 @@ pub const Type = extern union { |
| 606 | 616 | .comptime_int, |
| 607 | 617 | .comptime_float, |
| 608 | 618 | .noreturn, |
| 619 | .@"null", | |
| 609 | 620 | .array, |
| 610 | 621 | .single_const_pointer, |
| 611 | 622 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -650,6 +661,7 @@ pub const Type = extern union { |
| 650 | 661 | .comptime_int, |
| 651 | 662 | .comptime_float, |
| 652 | 663 | .noreturn, |
| 664 | .@"null", | |
| 653 | 665 | .array, |
| 654 | 666 | .single_const_pointer, |
| 655 | 667 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -693,6 +705,7 @@ pub const Type = extern union { |
| 693 | 705 | .comptime_int, |
| 694 | 706 | .comptime_float, |
| 695 | 707 | .noreturn, |
| 708 | .@"null", | |
| 696 | 709 | .array, |
| 697 | 710 | .single_const_pointer, |
| 698 | 711 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -736,6 +749,7 @@ pub const Type = extern union { |
| 736 | 749 | .comptime_int, |
| 737 | 750 | .comptime_float, |
| 738 | 751 | .noreturn, |
| 752 | .@"null", | |
| 739 | 753 | .array, |
| 740 | 754 | .single_const_pointer, |
| 741 | 755 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -779,6 +793,7 @@ pub const Type = extern union { |
| 779 | 793 | .comptime_int, |
| 780 | 794 | .comptime_float, |
| 781 | 795 | .noreturn, |
| 796 | .@"null", | |
| 782 | 797 | .array, |
| 783 | 798 | .single_const_pointer, |
| 784 | 799 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -833,6 +848,7 @@ pub const Type = extern union { |
| 833 | 848 | .type, |
| 834 | 849 | .anyerror, |
| 835 | 850 | .noreturn, |
| 851 | .@"null", | |
| 836 | 852 | .fn_noreturn_no_args, |
| 837 | 853 | .fn_naked_noreturn_no_args, |
| 838 | 854 | .fn_ccc_void_no_args, |
| ... | ... | @@ -881,6 +897,7 @@ pub const Type = extern union { |
| 881 | 897 | .c_void, |
| 882 | 898 | .void, |
| 883 | 899 | .noreturn, |
| 900 | .@"null", | |
| 884 | 901 | => return true, |
| 885 | 902 | |
| 886 | 903 | .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0, |
| ... | ... | @@ -933,6 +950,7 @@ pub const Type = extern union { |
| 933 | 950 | .c_void, |
| 934 | 951 | .void, |
| 935 | 952 | .noreturn, |
| 953 | .@"null", | |
| 936 | 954 | .int_unsigned, |
| 937 | 955 | .int_signed, |
| 938 | 956 | .array, |
| ... | ... | @@ -974,6 +992,7 @@ pub const Type = extern union { |
| 974 | 992 | comptime_int, |
| 975 | 993 | comptime_float, |
| 976 | 994 | noreturn, |
| 995 | @"null", | |
| 977 | 996 | fn_noreturn_no_args, |
| 978 | 997 | fn_naked_noreturn_no_args, |
| 979 | 998 | fn_ccc_void_no_args, |
src-self-hosted/value.zig+13-1| ... | ... | @@ -10,7 +10,7 @@ const ir = @import("ir.zig"); |
| 10 | 10 | |
| 11 | 11 | /// This is the raw data, with no bookkeeping, no memory awareness, |
| 12 | 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 | 14 | /// This union takes advantage of the fact that the first page of memory |
| 15 | 15 | /// is unmapped, giving us 4096 possible enum tags that have no payload. |
| 16 | 16 | pub const Value = extern union { |
| ... | ... | @@ -46,6 +46,7 @@ pub const Value = extern union { |
| 46 | 46 | comptime_int_type, |
| 47 | 47 | comptime_float_type, |
| 48 | 48 | noreturn_type, |
| 49 | null_type, | |
| 49 | 50 | fn_noreturn_no_args_type, |
| 50 | 51 | fn_naked_noreturn_no_args_type, |
| 51 | 52 | fn_ccc_void_no_args_type, |
| ... | ... | @@ -138,6 +139,7 @@ pub const Value = extern union { |
| 138 | 139 | .comptime_int_type => return out_stream.writeAll("comptime_int"), |
| 139 | 140 | .comptime_float_type => return out_stream.writeAll("comptime_float"), |
| 140 | 141 | .noreturn_type => return out_stream.writeAll("noreturn"), |
| 142 | .null_type => return out_stream.writeAll("@TypeOf(null)"), | |
| 141 | 143 | .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"), |
| 142 | 144 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 143 | 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 | 211 | .comptime_int_type => Type.initTag(.comptime_int), |
| 210 | 212 | .comptime_float_type => Type.initTag(.comptime_float), |
| 211 | 213 | .noreturn_type => Type.initTag(.noreturn), |
| 214 | .null_type => Type.initTag(.@"null"), | |
| 212 | 215 | .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args), |
| 213 | 216 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), |
| 214 | 217 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), |
| ... | ... | @@ -263,6 +266,7 @@ pub const Value = extern union { |
| 263 | 266 | .comptime_int_type, |
| 264 | 267 | .comptime_float_type, |
| 265 | 268 | .noreturn_type, |
| 269 | .null_type, | |
| 266 | 270 | .fn_noreturn_no_args_type, |
| 267 | 271 | .fn_naked_noreturn_no_args_type, |
| 268 | 272 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -319,6 +323,7 @@ pub const Value = extern union { |
| 319 | 323 | .comptime_int_type, |
| 320 | 324 | .comptime_float_type, |
| 321 | 325 | .noreturn_type, |
| 326 | .null_type, | |
| 322 | 327 | .fn_noreturn_no_args_type, |
| 323 | 328 | .fn_naked_noreturn_no_args_type, |
| 324 | 329 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -376,6 +381,7 @@ pub const Value = extern union { |
| 376 | 381 | .comptime_int_type, |
| 377 | 382 | .comptime_float_type, |
| 378 | 383 | .noreturn_type, |
| 384 | .null_type, | |
| 379 | 385 | .fn_noreturn_no_args_type, |
| 380 | 386 | .fn_naked_noreturn_no_args_type, |
| 381 | 387 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -438,6 +444,7 @@ pub const Value = extern union { |
| 438 | 444 | .comptime_int_type, |
| 439 | 445 | .comptime_float_type, |
| 440 | 446 | .noreturn_type, |
| 447 | .null_type, | |
| 441 | 448 | .fn_noreturn_no_args_type, |
| 442 | 449 | .fn_naked_noreturn_no_args_type, |
| 443 | 450 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -529,6 +536,7 @@ pub const Value = extern union { |
| 529 | 536 | .comptime_int_type, |
| 530 | 537 | .comptime_float_type, |
| 531 | 538 | .noreturn_type, |
| 539 | .null_type, | |
| 532 | 540 | .fn_noreturn_no_args_type, |
| 533 | 541 | .fn_naked_noreturn_no_args_type, |
| 534 | 542 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -582,6 +590,7 @@ pub const Value = extern union { |
| 582 | 590 | .comptime_int_type, |
| 583 | 591 | .comptime_float_type, |
| 584 | 592 | .noreturn_type, |
| 593 | .null_type, | |
| 585 | 594 | .fn_noreturn_no_args_type, |
| 586 | 595 | .fn_naked_noreturn_no_args_type, |
| 587 | 596 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -674,6 +683,7 @@ pub const Value = extern union { |
| 674 | 683 | .comptime_int_type, |
| 675 | 684 | .comptime_float_type, |
| 676 | 685 | .noreturn_type, |
| 686 | .null_type, | |
| 677 | 687 | .fn_noreturn_no_args_type, |
| 678 | 688 | .fn_naked_noreturn_no_args_type, |
| 679 | 689 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -736,6 +746,7 @@ pub const Value = extern union { |
| 736 | 746 | .comptime_int_type, |
| 737 | 747 | .comptime_float_type, |
| 738 | 748 | .noreturn_type, |
| 749 | .null_type, | |
| 739 | 750 | .fn_noreturn_no_args_type, |
| 740 | 751 | .fn_naked_noreturn_no_args_type, |
| 741 | 752 | .fn_ccc_void_no_args_type, |
| ... | ... | @@ -812,6 +823,7 @@ pub const Value = extern union { |
| 812 | 823 | .comptime_int_type, |
| 813 | 824 | .comptime_float_type, |
| 814 | 825 | .noreturn_type, |
| 826 | .null_type, | |
| 815 | 827 | .fn_noreturn_no_args_type, |
| 816 | 828 | .fn_naked_noreturn_no_args_type, |
| 817 | 829 | .fn_ccc_void_no_args_type, |