| author | |
| committer | |
| log | a32d3a85d21d614e5960b9eadcd85374954b910f |
| tree | 8712bfc619205eaf23201215a4a19f7c0c108cfa |
| parent | ae080b5c217fbcfd350a5d52b8b4626a95540ab3 |
* introduce std.ArrayListUnmanaged for when you have the allocator
stored elsewhere
* move std.heap.ArenaAllocator implementation to its own file. extract
the main state into std.heap.ArenaAllocator.State, which can be
stored as an alternative to storing the entire ArenaAllocator, saving
24 bytes per ArenaAllocator on 64 bit targets.
* std.LinkedList.Node pointer field now defaults to being null
initialized.
* Rework self-hosted compiler Package API
* Delete almost all the bitrotted self-hosted compiler code. The only bit
rotted code left is in main.zig and compilation.zig
* Add call instruction to ZIR
* self-hosted compiler ir API and link API are reworked to support
a long-running compiler that incrementally updates declarations
* Introduce the concept of scopes to ZIR semantic analysis
* ZIR text format supports referencing named decls that are declared
later in the file
* Figure out how memory management works for the long-running compiler
and incremental compilation. The main roots are top level
declarations. There is a table of decls. The key is a cryptographic
hash of the fully qualified decl name. Each decl has an arena
allocator where all of the memory related to that decl is stored.
Each code block has its own arena allocator for the lifetime of
the block. Values that want to survive when going out of scope in
a block must get copied into the outer block. Finally, values must
get copied into the Decl arena to be long-lived.
* Delete the unused MemoryCell struct. Instead, comptime pointers are
based on references to Decl structs.
* Figure out how caching works. Each Decl will store a set of other
Decls which must be recompiled when it changes.
This branch is still work-in-progress; this commit breaks the build.21 files changed, 1836 insertions(+), 1504 deletions(-)
lib/std/array_list.zig+236-7| ... | @@ -8,13 +8,13 @@ const Allocator = mem.Allocator; | ... | @@ -8,13 +8,13 @@ const Allocator = mem.Allocator; |
| 8 | /// A contiguous, growable list of items in memory. | 8 | /// A contiguous, growable list of items in memory. |
| 9 | /// This is a wrapper around an array of T values. Initialize with `init`. | 9 | /// This is a wrapper around an array of T values. Initialize with `init`. |
| 10 | pub fn ArrayList(comptime T: type) type { | 10 | pub fn ArrayList(comptime T: type) type { |
| 11 | return AlignedArrayList(T, null); | 11 | return ArrayListAligned(T, null); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { | 14 | pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 15 | if (alignment) |a| { | 15 | if (alignment) |a| { |
| 16 | if (a == @alignOf(T)) { | 16 | if (a == @alignOf(T)) { |
| 17 | return AlignedArrayList(T, null); | 17 | return ArrayListAligned(T, null); |
| 18 | } | 18 | } |
| 19 | } | 19 | } |
| 20 | return struct { | 20 | return struct { |
| ... | @@ -76,6 +76,10 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -76,6 +76,10 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { |
| 76 | }; | 76 | }; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) { | ||
| 80 | return .{ .items = self.items, .capacity = self.capacity }; | ||
| 81 | } | ||
| 82 | |||
| 79 | /// The caller owns the returned memory. ArrayList becomes empty. | 83 | /// The caller owns the returned memory. ArrayList becomes empty. |
| 80 | pub fn toOwnedSlice(self: *Self) Slice { | 84 | pub fn toOwnedSlice(self: *Self) Slice { |
| 81 | const allocator = self.allocator; | 85 | const allocator = self.allocator; |
| ... | @@ -84,8 +88,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -84,8 +88,8 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { |
| 84 | return result; | 88 | return result; |
| 85 | } | 89 | } |
| 86 | 90 | ||
| 87 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` | 91 | /// Insert `item` at index `n` by moving `list[n .. list.len]` to make room. |
| 88 | /// to make room. | 92 | /// This operation is O(N). |
| 89 | pub fn insert(self: *Self, n: usize, item: T) !void { | 93 | pub fn insert(self: *Self, n: usize, item: T) !void { |
| 90 | try self.ensureCapacity(self.items.len + 1); | 94 | try self.ensureCapacity(self.items.len + 1); |
| 91 | self.items.len += 1; | 95 | self.items.len += 1; |
| ... | @@ -94,8 +98,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -94,8 +98,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { |
| 94 | self.items[n] = item; | 98 | self.items[n] = item; |
| 95 | } | 99 | } |
| 96 | 100 | ||
| 97 | /// Insert slice `items` at index `i`. Moves | 101 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 98 | /// `list[i .. list.len]` to make room. | ||
| 99 | /// This operation is O(N). | 102 | /// This operation is O(N). |
| 100 | pub fn insertSlice(self: *Self, i: usize, items: SliceConst) !void { | 103 | pub fn insertSlice(self: *Self, i: usize, items: SliceConst) !void { |
| 101 | try self.ensureCapacity(self.items.len + items.len); | 104 | try self.ensureCapacity(self.items.len + items.len); |
| ... | @@ -259,6 +262,232 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -259,6 +262,232 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { |
| 259 | }; | 262 | }; |
| 260 | } | 263 | } |
| 261 | 264 | ||
| 265 | /// Bring-your-own allocator with every function call. | ||
| 266 | /// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`. | ||
| 267 | pub fn init() Self { | ||
| 268 | return .{ | ||
| 269 | .items = &[_]T{}, | ||
| 270 | .capacity = 0, | ||
| 271 | }; | ||
| 272 | } | ||
| 273 | |||
| 274 | pub fn ArrayListUnmanaged(comptime T: type) type { | ||
| 275 | return ArrayListAlignedUnmanaged(T, null); | ||
| 276 | } | ||
| 277 | |||
| 278 | pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type { | ||
| 279 | if (alignment) |a| { | ||
| 280 | if (a == @alignOf(T)) { | ||
| 281 | return ArrayListAlignedUnmanaged(T, null); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | return struct { | ||
| 285 | const Self = @This(); | ||
| 286 | |||
| 287 | /// Content of the ArrayList. | ||
| 288 | items: Slice = &[_]T{}, | ||
| 289 | capacity: usize = 0, | ||
| 290 | |||
| 291 | pub const Slice = if (alignment) |a| ([]align(a) T) else []T; | ||
| 292 | pub const SliceConst = if (alignment) |a| ([]align(a) const T) else []const T; | ||
| 293 | |||
| 294 | /// Initialize with capacity to hold at least num elements. | ||
| 295 | /// Deinitialize with `deinit` or use `toOwnedSlice`. | ||
| 296 | pub fn initCapacity(allocator: *Allocator, num: usize) !Self { | ||
| 297 | var self = Self.init(allocator); | ||
| 298 | try self.ensureCapacity(allocator, num); | ||
| 299 | return self; | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Release all allocated memory. | ||
| 303 | pub fn deinit(self: *Self, allocator: *Allocator) void { | ||
| 304 | allocator.free(self.allocatedSlice()); | ||
| 305 | self.* = undefined; | ||
| 306 | } | ||
| 307 | |||
| 308 | pub fn toManaged(self: *Self, allocator: *Allocator) ArrayListAligned(T, alignment) { | ||
| 309 | return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator }; | ||
| 310 | } | ||
| 311 | |||
| 312 | /// The caller owns the returned memory. ArrayList becomes empty. | ||
| 313 | pub fn toOwnedSlice(self: *Self, allocator: *Allocator) Slice { | ||
| 314 | const result = allocator.shrink(self.allocatedSlice(), self.items.len); | ||
| 315 | self.* = init(allocator); | ||
| 316 | return result; | ||
| 317 | } | ||
| 318 | |||
| 319 | /// Insert `item` at index `n`. Moves `list[n .. list.len]` | ||
| 320 | /// to make room. | ||
| 321 | pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void { | ||
| 322 | try self.ensureCapacity(allocator, self.items.len + 1); | ||
| 323 | self.items.len += 1; | ||
| 324 | |||
| 325 | mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]); | ||
| 326 | self.items[n] = item; | ||
| 327 | } | ||
| 328 | |||
| 329 | /// Insert slice `items` at index `i`. Moves | ||
| 330 | /// `list[i .. list.len]` to make room. | ||
| 331 | /// This operation is O(N). | ||
| 332 | pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void { | ||
| 333 | try self.ensureCapacity(allocator, self.items.len + items.len); | ||
| 334 | self.items.len += items.len; | ||
| 335 | |||
| 336 | mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]); | ||
| 337 | mem.copy(T, self.items[i .. i + items.len], items); | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Extend the list by 1 element. Allocates more memory as necessary. | ||
| 341 | pub fn append(self: *Self, allocator: *Allocator, item: T) !void { | ||
| 342 | const new_item_ptr = try self.addOne(allocator); | ||
| 343 | new_item_ptr.* = item; | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Extend the list by 1 element, but asserting `self.capacity` | ||
| 347 | /// is sufficient to hold an additional item. | ||
| 348 | pub fn appendAssumeCapacity(self: *Self, item: T) void { | ||
| 349 | const new_item_ptr = self.addOneAssumeCapacity(); | ||
| 350 | new_item_ptr.* = item; | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Remove the element at index `i` from the list and return its value. | ||
| 354 | /// Asserts the array has at least one item. | ||
| 355 | /// This operation is O(N). | ||
| 356 | pub fn orderedRemove(self: *Self, i: usize) T { | ||
| 357 | const newlen = self.items.len - 1; | ||
| 358 | if (newlen == i) return self.pop(); | ||
| 359 | |||
| 360 | const old_item = self.items[i]; | ||
| 361 | for (self.items[i..newlen]) |*b, j| b.* = self.items[i + 1 + j]; | ||
| 362 | self.items[newlen] = undefined; | ||
| 363 | self.items.len = newlen; | ||
| 364 | return old_item; | ||
| 365 | } | ||
| 366 | |||
| 367 | /// Removes the element at the specified index and returns it. | ||
| 368 | /// The empty slot is filled from the end of the list. | ||
| 369 | /// This operation is O(1). | ||
| 370 | pub fn swapRemove(self: *Self, i: usize) T { | ||
| 371 | if (self.items.len - 1 == i) return self.pop(); | ||
| 372 | |||
| 373 | const old_item = self.items[i]; | ||
| 374 | self.items[i] = self.pop(); | ||
| 375 | return old_item; | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Append the slice of items to the list. Allocates more | ||
| 379 | /// memory as necessary. | ||
| 380 | pub fn appendSlice(self: *Self, allocator: *Allocator, items: SliceConst) !void { | ||
| 381 | const oldlen = self.items.len; | ||
| 382 | const newlen = self.items.len + items.len; | ||
| 383 | |||
| 384 | try self.ensureCapacity(allocator, newlen); | ||
| 385 | self.items.len = newlen; | ||
| 386 | mem.copy(T, self.items[oldlen..], items); | ||
| 387 | } | ||
| 388 | |||
| 389 | /// Same as `append` except it returns the number of bytes written, which is always the same | ||
| 390 | /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API. | ||
| 391 | /// This function may be called only when `T` is `u8`. | ||
| 392 | fn appendWrite(self: *Self, allocator: *Allocator, m: []const u8) !usize { | ||
| 393 | try self.appendSlice(allocator, m); | ||
| 394 | return m.len; | ||
| 395 | } | ||
| 396 | |||
| 397 | /// Append a value to the list `n` times. | ||
| 398 | /// Allocates more memory as necessary. | ||
| 399 | pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void { | ||
| 400 | const old_len = self.items.len; | ||
| 401 | try self.resize(self.items.len + n); | ||
| 402 | mem.set(T, self.items[old_len..self.items.len], value); | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Adjust the list's length to `new_len`. | ||
| 406 | /// Does not initialize added items if any. | ||
| 407 | pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void { | ||
| 408 | try self.ensureCapacity(allocator, new_len); | ||
| 409 | self.items.len = new_len; | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Reduce allocated capacity to `new_len`. | ||
| 413 | /// Invalidates element pointers. | ||
| 414 | pub fn shrink(self: *Self, allocator: *Allocator, new_len: usize) void { | ||
| 415 | assert(new_len <= self.items.len); | ||
| 416 | |||
| 417 | self.items = allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) { | ||
| 418 | error.OutOfMemory => { // no problem, capacity is still correct then. | ||
| 419 | self.items.len = new_len; | ||
| 420 | return; | ||
| 421 | }, | ||
| 422 | }; | ||
| 423 | self.capacity = new_len; | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { | ||
| 427 | var better_capacity = self.capacity; | ||
| 428 | if (better_capacity >= new_capacity) return; | ||
| 429 | |||
| 430 | while (true) { | ||
| 431 | better_capacity += better_capacity / 2 + 8; | ||
| 432 | if (better_capacity >= new_capacity) break; | ||
| 433 | } | ||
| 434 | |||
| 435 | const new_memory = try allocator.realloc(self.allocatedSlice(), better_capacity); | ||
| 436 | self.items.ptr = new_memory.ptr; | ||
| 437 | self.capacity = new_memory.len; | ||
| 438 | } | ||
| 439 | |||
| 440 | /// Increases the array's length to match the full capacity that is already allocated. | ||
| 441 | /// The new elements have `undefined` values. | ||
| 442 | /// This operation does not invalidate any element pointers. | ||
| 443 | pub fn expandToCapacity(self: *Self) void { | ||
| 444 | self.items.len = self.capacity; | ||
| 445 | } | ||
| 446 | |||
| 447 | /// Increase length by 1, returning pointer to the new item. | ||
| 448 | /// The returned pointer becomes invalid when the list is resized. | ||
| 449 | pub fn addOne(self: *Self, allocator: *Allocator) !*T { | ||
| 450 | const newlen = self.items.len + 1; | ||
| 451 | try self.ensureCapacity(allocator, newlen); | ||
| 452 | return self.addOneAssumeCapacity(); | ||
| 453 | } | ||
| 454 | |||
| 455 | /// Increase length by 1, returning pointer to the new item. | ||
| 456 | /// Asserts that there is already space for the new item without allocating more. | ||
| 457 | /// The returned pointer becomes invalid when the list is resized. | ||
| 458 | /// This operation does not invalidate any element pointers. | ||
| 459 | pub fn addOneAssumeCapacity(self: *Self) *T { | ||
| 460 | assert(self.items.len < self.capacity); | ||
| 461 | |||
| 462 | self.items.len += 1; | ||
| 463 | return &self.items[self.items.len - 1]; | ||
| 464 | } | ||
| 465 | |||
| 466 | /// Remove and return the last element from the list. | ||
| 467 | /// Asserts the list has at least one item. | ||
| 468 | /// This operation does not invalidate any element pointers. | ||
| 469 | pub fn pop(self: *Self) T { | ||
| 470 | const val = self.items[self.items.len - 1]; | ||
| 471 | self.items.len -= 1; | ||
| 472 | return val; | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Remove and return the last element from the list. | ||
| 476 | /// If the list is empty, returns `null`. | ||
| 477 | /// This operation does not invalidate any element pointers. | ||
| 478 | pub fn popOrNull(self: *Self) ?T { | ||
| 479 | if (self.items.len == 0) return null; | ||
| 480 | return self.pop(); | ||
| 481 | } | ||
| 482 | |||
| 483 | /// For a nicer API, `items.len` is the length, not the capacity. | ||
| 484 | /// This requires "unsafe" slicing. | ||
| 485 | fn allocatedSlice(self: Self) Slice { | ||
| 486 | return self.items.ptr[0..self.capacity]; | ||
| 487 | } | ||
| 488 | }; | ||
| 489 | } | ||
| 490 | |||
| 262 | test "std.ArrayList.init" { | 491 | test "std.ArrayList.init" { |
| 263 | var list = ArrayList(i32).init(testing.allocator); | 492 | var list = ArrayList(i32).init(testing.allocator); |
| 264 | defer list.deinit(); | 493 | defer list.deinit(); |
lib/std/heap.zig+1-89| ... | @@ -11,6 +11,7 @@ const maxInt = std.math.maxInt; | ... | @@ -11,6 +11,7 @@ const maxInt = std.math.maxInt; |
| 11 | 11 | ||
| 12 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; | 12 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; |
| 13 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; | 13 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; |
| 14 | pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; | ||
| 14 | 15 | ||
| 15 | const Allocator = mem.Allocator; | 16 | const Allocator = mem.Allocator; |
| 16 | 17 | ||
| ... | @@ -510,95 +511,6 @@ pub const HeapAllocator = switch (builtin.os.tag) { | ... | @@ -510,95 +511,6 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 510 | else => @compileError("Unsupported OS"), | 511 | else => @compileError("Unsupported OS"), |
| 511 | }; | 512 | }; |
| 512 | 513 | ||
| 513 | /// This allocator takes an existing allocator, wraps it, and provides an interface | ||
| 514 | /// where you can allocate without freeing, and then free it all together. | ||
| 515 | pub const ArenaAllocator = struct { | ||
| 516 | allocator: Allocator, | ||
| 517 | |||
| 518 | child_allocator: *Allocator, | ||
| 519 | buffer_list: std.SinglyLinkedList([]u8), | ||
| 520 | end_index: usize, | ||
| 521 | |||
| 522 | const BufNode = std.SinglyLinkedList([]u8).Node; | ||
| 523 | |||
| 524 | pub fn init(child_allocator: *Allocator) ArenaAllocator { | ||
| 525 | return ArenaAllocator{ | ||
| 526 | .allocator = Allocator{ | ||
| 527 | .reallocFn = realloc, | ||
| 528 | .shrinkFn = shrink, | ||
| 529 | }, | ||
| 530 | .child_allocator = child_allocator, | ||
| 531 | .buffer_list = std.SinglyLinkedList([]u8).init(), | ||
| 532 | .end_index = 0, | ||
| 533 | }; | ||
| 534 | } | ||
| 535 | |||
| 536 | pub fn deinit(self: ArenaAllocator) void { | ||
| 537 | var it = self.buffer_list.first; | ||
| 538 | while (it) |node| { | ||
| 539 | // this has to occur before the free because the free frees node | ||
| 540 | const next_it = node.next; | ||
| 541 | self.child_allocator.free(node.data); | ||
| 542 | it = next_it; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode { | ||
| 547 | const actual_min_size = minimum_size + @sizeOf(BufNode); | ||
| 548 | var len = prev_len; | ||
| 549 | while (true) { | ||
| 550 | len += len / 2; | ||
| 551 | len += mem.page_size - @rem(len, mem.page_size); | ||
| 552 | if (len >= actual_min_size) break; | ||
| 553 | } | ||
| 554 | const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); | ||
| 555 | const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]); | ||
| 556 | const buf_node = &buf_node_slice[0]; | ||
| 557 | buf_node.* = BufNode{ | ||
| 558 | .data = buf, | ||
| 559 | .next = null, | ||
| 560 | }; | ||
| 561 | self.buffer_list.prepend(buf_node); | ||
| 562 | self.end_index = 0; | ||
| 563 | return buf_node; | ||
| 564 | } | ||
| 565 | |||
| 566 | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { | ||
| 567 | const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator); | ||
| 568 | |||
| 569 | var cur_node = if (self.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment); | ||
| 570 | while (true) { | ||
| 571 | const cur_buf = cur_node.data[@sizeOf(BufNode)..]; | ||
| 572 | const addr = @ptrToInt(cur_buf.ptr) + self.end_index; | ||
| 573 | const adjusted_addr = mem.alignForward(addr, alignment); | ||
| 574 | const adjusted_index = self.end_index + (adjusted_addr - addr); | ||
| 575 | const new_end_index = adjusted_index + n; | ||
| 576 | if (new_end_index > cur_buf.len) { | ||
| 577 | cur_node = try self.createNode(cur_buf.len, n + alignment); | ||
| 578 | continue; | ||
| 579 | } | ||
| 580 | const result = cur_buf[adjusted_index..new_end_index]; | ||
| 581 | self.end_index = new_end_index; | ||
| 582 | return result; | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | ||
| 587 | if (new_size <= old_mem.len and new_align <= new_size) { | ||
| 588 | // We can't do anything with the memory, so tell the client to keep it. | ||
| 589 | return error.OutOfMemory; | ||
| 590 | } else { | ||
| 591 | const result = try alloc(allocator, new_size, new_align); | ||
| 592 | @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); | ||
| 593 | return result; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { | ||
| 598 | return old_mem[0..new_size]; | ||
| 599 | } | ||
| 600 | }; | ||
| 601 | |||
| 602 | pub const FixedBufferAllocator = struct { | 514 | pub const FixedBufferAllocator = struct { |
| 603 | allocator: Allocator, | 515 | allocator: Allocator, |
| 604 | end_index: usize, | 516 | end_index: usize, |
lib/std/heap/arena_allocator.zig created+102| ... | @@ -0,0 +1,102 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | |||
| 6 | /// This allocator takes an existing allocator, wraps it, and provides an interface | ||
| 7 | /// where you can allocate without freeing, and then free it all together. | ||
| 8 | pub const ArenaAllocator = struct { | ||
| 9 | allocator: Allocator, | ||
| 10 | |||
| 11 | child_allocator: *Allocator, | ||
| 12 | state: State, | ||
| 13 | |||
| 14 | /// Inner state of ArenaAllocator. Can be stored rather than the entire ArenaAllocator | ||
| 15 | /// as a memory-saving optimization. | ||
| 16 | pub const State = struct { | ||
| 17 | buffer_list: std.SinglyLinkedList([]u8) = @as(std.SinglyLinkedList([]u8), .{}), | ||
| 18 | end_index: usize = 0, | ||
| 19 | |||
| 20 | pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator { | ||
| 21 | return .{ | ||
| 22 | .allocator = Allocator{ | ||
| 23 | .reallocFn = realloc, | ||
| 24 | .shrinkFn = shrink, | ||
| 25 | }, | ||
| 26 | .child_allocator = child_allocator, | ||
| 27 | .state = self, | ||
| 28 | }; | ||
| 29 | } | ||
| 30 | }; | ||
| 31 | |||
| 32 | const BufNode = std.SinglyLinkedList([]u8).Node; | ||
| 33 | |||
| 34 | pub fn init(child_allocator: *Allocator) ArenaAllocator { | ||
| 35 | return (State{}).promote(child_allocator); | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn deinit(self: ArenaAllocator) void { | ||
| 39 | var it = self.state.buffer_list.first; | ||
| 40 | while (it) |node| { | ||
| 41 | // this has to occur before the free because the free frees node | ||
| 42 | const next_it = node.next; | ||
| 43 | self.child_allocator.free(node.data); | ||
| 44 | it = next_it; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode { | ||
| 49 | const actual_min_size = minimum_size + @sizeOf(BufNode); | ||
| 50 | var len = prev_len; | ||
| 51 | while (true) { | ||
| 52 | len += len / 2; | ||
| 53 | len += mem.page_size - @rem(len, mem.page_size); | ||
| 54 | if (len >= actual_min_size) break; | ||
| 55 | } | ||
| 56 | const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); | ||
| 57 | const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]); | ||
| 58 | const buf_node = &buf_node_slice[0]; | ||
| 59 | buf_node.* = BufNode{ | ||
| 60 | .data = buf, | ||
| 61 | .next = null, | ||
| 62 | }; | ||
| 63 | self.state.buffer_list.prepend(buf_node); | ||
| 64 | self.state.end_index = 0; | ||
| 65 | return buf_node; | ||
| 66 | } | ||
| 67 | |||
| 68 | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { | ||
| 69 | const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator); | ||
| 70 | |||
| 71 | var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment); | ||
| 72 | while (true) { | ||
| 73 | const cur_buf = cur_node.data[@sizeOf(BufNode)..]; | ||
| 74 | const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index; | ||
| 75 | const adjusted_addr = mem.alignForward(addr, alignment); | ||
| 76 | const adjusted_index = self.state.end_index + (adjusted_addr - addr); | ||
| 77 | const new_end_index = adjusted_index + n; | ||
| 78 | if (new_end_index > cur_buf.len) { | ||
| 79 | cur_node = try self.createNode(cur_buf.len, n + alignment); | ||
| 80 | continue; | ||
| 81 | } | ||
| 82 | const result = cur_buf[adjusted_index..new_end_index]; | ||
| 83 | self.state.end_index = new_end_index; | ||
| 84 | return result; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | ||
| 89 | if (new_size <= old_mem.len and new_align <= new_size) { | ||
| 90 | // We can't do anything with the memory, so tell the client to keep it. | ||
| 91 | return error.OutOfMemory; | ||
| 92 | } else { | ||
| 93 | const result = try alloc(allocator, new_size, new_align); | ||
| 94 | @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); | ||
| 95 | return result; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { | ||
| 100 | return old_mem[0..new_size]; | ||
| 101 | } | ||
| 102 | }; | ||
lib/std/linked_list.zig+1-1| ... | @@ -49,7 +49,7 @@ pub fn SinglyLinkedList(comptime T: type) type { | ... | @@ -49,7 +49,7 @@ pub fn SinglyLinkedList(comptime T: type) type { |
| 49 | } | 49 | } |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | first: ?*Node, | 52 | first: ?*Node = null, |
| 53 | 53 | ||
| 54 | /// Initialize a linked list. | 54 | /// Initialize a linked list. |
| 55 | /// | 55 | /// |
lib/std/std.zig+3-1| ... | @@ -1,6 +1,8 @@ | ... | @@ -1,6 +1,8 @@ |
| 1 | pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList; | ||
| 2 | pub const ArrayList = @import("array_list.zig").ArrayList; | 1 | pub const ArrayList = @import("array_list.zig").ArrayList; |
| 2 | pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned; | ||
| 3 | pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged; | ||
| 3 | pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled; | 4 | pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled; |
| 5 | pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged; | ||
| 4 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; | 6 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; |
| 5 | pub const BloomFilter = @import("bloom_filter.zig").BloomFilter; | 7 | pub const BloomFilter = @import("bloom_filter.zig").BloomFilter; |
| 6 | pub const BufMap = @import("buf_map.zig").BufMap; | 8 | pub const BufMap = @import("buf_map.zig").BufMap; |
src-self-hosted/Package.zig created+52| ... | @@ -0,0 +1,52 @@ | ||
| 1 | pub const Table = std.StringHashMap(*Package); | ||
| 2 | |||
| 3 | root_src_dir: std.fs.Dir, | ||
| 4 | /// Relative to `root_src_dir`. | ||
| 5 | root_src_path: []const u8, | ||
| 6 | table: Table, | ||
| 7 | |||
| 8 | /// No references to `root_src_dir` and `root_src_path` are kept. | ||
| 9 | pub fn create( | ||
| 10 | allocator: *mem.Allocator, | ||
| 11 | base_dir: std.fs.Dir, | ||
| 12 | /// Relative to `base_dir`. | ||
| 13 | root_src_dir: []const u8, | ||
| 14 | /// Relative to `root_src_dir`. | ||
| 15 | root_src_path: []const u8, | ||
| 16 | ) !*Package { | ||
| 17 | const ptr = try allocator.create(Package); | ||
| 18 | errdefer allocator.destroy(ptr); | ||
| 19 | const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path); | ||
| 20 | errdefer allocator.free(root_src_path_dupe); | ||
| 21 | ptr.* = .{ | ||
| 22 | .root_src_dir = try base_dir.openDir(root_src_dir, .{}), | ||
| 23 | .root_src_path = root_src_path_dupe, | ||
| 24 | .table = Table.init(allocator), | ||
| 25 | }; | ||
| 26 | return ptr; | ||
| 27 | } | ||
| 28 | |||
| 29 | pub fn destroy(self: *Package) void { | ||
| 30 | const allocator = self.table.allocator; | ||
| 31 | self.root_src_dir.close(); | ||
| 32 | allocator.free(self.root_src_path); | ||
| 33 | { | ||
| 34 | var it = self.table.iterator(); | ||
| 35 | while (it.next()) |kv| { | ||
| 36 | allocator.free(kv.key); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | self.table.deinit(); | ||
| 40 | allocator.destroy(self); | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn add(self: *Package, name: []const u8, package: *Package) !void { | ||
| 44 | const name_dupe = try mem.dupe(self.table.allocator, u8, name); | ||
| 45 | errdefer self.table.allocator.deinit(name_dupe); | ||
| 46 | const entry = try self.table.put(name_dupe, package); | ||
| 47 | assert(entry == null); | ||
| 48 | } | ||
| 49 | |||
| 50 | const std = @import("std"); | ||
| 51 | const mem = std.mem; | ||
| 52 | const assert = std.debug.assert; | ||
src-self-hosted/c.zig deleted-7| ... | @@ -1,7 +0,0 @@ | ||
| 1 | pub usingnamespace @cImport({ | ||
| 2 | @cDefine("__STDC_CONSTANT_MACROS", ""); | ||
| 3 | @cDefine("__STDC_LIMIT_MACROS", ""); | ||
| 4 | @cInclude("inttypes.h"); | ||
| 5 | @cInclude("config.h"); | ||
| 6 | @cInclude("zig_llvm.h"); | ||
| 7 | }); | ||
src-self-hosted/codegen.zig+25-33| ... | @@ -6,38 +6,24 @@ const Type = @import("type.zig").Type; | ... | @@ -6,38 +6,24 @@ const Type = @import("type.zig").Type; |
| 6 | const Value = @import("value.zig").Value; | 6 | const Value = @import("value.zig").Value; |
| 7 | const Target = std.Target; | 7 | const Target = std.Target; |
| 8 | 8 | ||
| 9 | pub const ErrorMsg = struct { | 9 | pub fn generateSymbol( |
| 10 | byte_offset: usize, | 10 | typed_value: ir.TypedValue, |
| 11 | msg: []const u8, | 11 | module: ir.Module, |
| 12 | }; | 12 | code: *std.ArrayList(u8), |
| 13 | 13 | errors: *std.ArrayList(ir.ErrorMsg), | |
| 14 | pub const Symbol = struct { | 14 | ) !void { |
| 15 | errors: []ErrorMsg, | ||
| 16 | |||
| 17 | pub fn deinit(self: *Symbol, allocator: *mem.Allocator) void { | ||
| 18 | for (self.errors) |err| { | ||
| 19 | allocator.free(err.msg); | ||
| 20 | } | ||
| 21 | allocator.free(self.errors); | ||
| 22 | self.* = undefined; | ||
| 23 | } | ||
| 24 | }; | ||
| 25 | |||
| 26 | pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !Symbol { | ||
| 27 | switch (typed_value.ty.zigTypeTag()) { | 15 | switch (typed_value.ty.zigTypeTag()) { |
| 28 | .Fn => { | 16 | .Fn => { |
| 29 | const index = typed_value.val.cast(Value.Payload.Function).?.index; | 17 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; |
| 30 | const module_fn = module.fns[index]; | ||
| 31 | 18 | ||
| 32 | var function = Function{ | 19 | var function = Function{ |
| 33 | .module = &module, | 20 | .module = &module, |
| 34 | .mod_fn = &module_fn, | 21 | .mod_fn = module_fn, |
| 35 | .code = code, | 22 | .code = code, |
| 36 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), | 23 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), |
| 37 | .errors = std.ArrayList(ErrorMsg).init(code.allocator), | 24 | .errors = errors, |
| 38 | }; | 25 | }; |
| 39 | defer function.inst_table.deinit(); | 26 | defer function.inst_table.deinit(); |
| 40 | defer function.errors.deinit(); | ||
| 41 | 27 | ||
| 42 | for (module_fn.body.instructions) |inst| { | 28 | for (module_fn.body.instructions) |inst| { |
| 43 | const new_inst = function.genFuncInst(inst) catch |err| switch (err) { | 29 | const new_inst = function.genFuncInst(inst) catch |err| switch (err) { |
| ... | @@ -52,7 +38,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. | ... | @@ -52,7 +38,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. |
| 52 | 38 | ||
| 53 | return Symbol{ .errors = function.errors.toOwnedSlice() }; | 39 | return Symbol{ .errors = function.errors.toOwnedSlice() }; |
| 54 | }, | 40 | }, |
| 55 | else => @panic("TODO implement generateSymbol for non-function types"), | 41 | else => @panic("TODO implement generateSymbol for non-function decls"), |
| 56 | } | 42 | } |
| 57 | } | 43 | } |
| 58 | 44 | ||
| ... | @@ -61,7 +47,7 @@ const Function = struct { | ... | @@ -61,7 +47,7 @@ const Function = struct { |
| 61 | mod_fn: *const ir.Module.Fn, | 47 | mod_fn: *const ir.Module.Fn, |
| 62 | code: *std.ArrayList(u8), | 48 | code: *std.ArrayList(u8), |
| 63 | inst_table: std.AutoHashMap(*ir.Inst, MCValue), | 49 | inst_table: std.AutoHashMap(*ir.Inst, MCValue), |
| 64 | errors: std.ArrayList(ErrorMsg), | 50 | errors: *std.ArrayList(ir.ErrorMsg), |
| 65 | 51 | ||
| 66 | const MCValue = union(enum) { | 52 | const MCValue = union(enum) { |
| 67 | none, | 53 | none, |
| ... | @@ -78,6 +64,7 @@ const Function = struct { | ... | @@ -78,6 +64,7 @@ const Function = struct { |
| 78 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { | 64 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { |
| 79 | switch (inst.tag) { | 65 | switch (inst.tag) { |
| 80 | .breakpoint => return self.genBreakpoint(inst.src), | 66 | .breakpoint => return self.genBreakpoint(inst.src), |
| 67 | .call => return self.genCall(inst.cast(ir.Inst.Call).?), | ||
| 81 | .unreach => return MCValue{ .unreach = {} }, | 68 | .unreach => return MCValue{ .unreach = {} }, |
| 82 | .constant => unreachable, // excluded from function bodies | 69 | .constant => unreachable, // excluded from function bodies |
| 83 | .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), | 70 | .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), |
| ... | @@ -101,6 +88,13 @@ const Function = struct { | ... | @@ -101,6 +88,13 @@ const Function = struct { |
| 101 | return .unreach; | 88 | return .unreach; |
| 102 | } | 89 | } |
| 103 | 90 | ||
| 91 | fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue { | ||
| 92 | switch (self.module.target.cpu.arch) { | ||
| 93 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.module.target.cpu.arch}), | ||
| 94 | } | ||
| 95 | return .unreach; | ||
| 96 | } | ||
| 97 | |||
| 104 | fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { | 98 | fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { |
| 105 | switch (self.module.target.cpu.arch) { | 99 | switch (self.module.target.cpu.arch) { |
| 106 | .i386, .x86_64 => { | 100 | .i386, .x86_64 => { |
| ... | @@ -140,6 +134,7 @@ const Function = struct { | ... | @@ -140,6 +134,7 @@ const Function = struct { |
| 140 | fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void { | 134 | fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void { |
| 141 | switch (self.module.target.cpu.arch) { | 135 | switch (self.module.target.cpu.arch) { |
| 142 | .i386, .x86_64 => { | 136 | .i386, .x86_64 => { |
| 137 | // TODO x86 treats the operands as signed | ||
| 143 | if (amount <= std.math.maxInt(u8)) { | 138 | if (amount <= std.math.maxInt(u8)) { |
| 144 | try self.code.resize(self.code.items.len + 2); | 139 | try self.code.resize(self.code.items.len + 2); |
| 145 | self.code.items[self.code.items.len - 2] = 0xeb; | 140 | self.code.items[self.code.items.len - 2] = 0xeb; |
| ... | @@ -433,14 +428,11 @@ const Function = struct { | ... | @@ -433,14 +428,11 @@ const Function = struct { |
| 433 | 428 | ||
| 434 | fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } { | 429 | fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } { |
| 435 | @setCold(true); | 430 | @setCold(true); |
| 436 | const msg = try std.fmt.allocPrint(self.errors.allocator, format, args); | 431 | try self.errors.ensureCapacity(self.errors.items.len + 1); |
| 437 | { | 432 | self.errors.appendAssumeCapacity(.{ |
| 438 | errdefer self.errors.allocator.free(msg); | 433 | .byte_offset = src, |
| 439 | (try self.errors.addOne()).* = .{ | 434 | .msg = try std.fmt.allocPrint(self.errors.allocator, format, args), |
| 440 | .byte_offset = src, | 435 | }); |
| 441 | .msg = msg, | ||
| 442 | }; | ||
| 443 | } | ||
| 444 | return error.CodegenFail; | 436 | return error.CodegenFail; |
| 445 | } | 437 | } |
| 446 | }; | 438 | }; |
src-self-hosted/compilation.zig+56-33| ... | @@ -19,7 +19,6 @@ const AtomicOrder = builtin.AtomicOrder; | ... | @@ -19,7 +19,6 @@ const AtomicOrder = builtin.AtomicOrder; |
| 19 | const Scope = @import("scope.zig").Scope; | 19 | const Scope = @import("scope.zig").Scope; |
| 20 | const Decl = @import("decl.zig").Decl; | 20 | const Decl = @import("decl.zig").Decl; |
| 21 | const ir = @import("ir.zig"); | 21 | const ir = @import("ir.zig"); |
| 22 | const Visib = @import("visib.zig").Visib; | ||
| 23 | const Value = @import("value.zig").Value; | 22 | const Value = @import("value.zig").Value; |
| 24 | const Type = Value.Type; | 23 | const Type = Value.Type; |
| 25 | const Span = errmsg.Span; | 24 | const Span = errmsg.Span; |
| ... | @@ -30,7 +29,11 @@ const link = @import("link.zig").link; | ... | @@ -30,7 +29,11 @@ const link = @import("link.zig").link; |
| 30 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 29 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 31 | const CInt = @import("c_int.zig").CInt; | 30 | const CInt = @import("c_int.zig").CInt; |
| 32 | const fs = std.fs; | 31 | const fs = std.fs; |
| 33 | const util = @import("util.zig"); | 32 | |
| 33 | pub const Visib = enum { | ||
| 34 | Private, | ||
| 35 | Pub, | ||
| 36 | }; | ||
| 34 | 37 | ||
| 35 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB | 38 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB |
| 36 | 39 | ||
| ... | @@ -45,7 +48,7 @@ pub const ZigCompiler = struct { | ... | @@ -45,7 +48,7 @@ pub const ZigCompiler = struct { |
| 45 | 48 | ||
| 46 | native_libc: event.Future(LibCInstallation), | 49 | native_libc: event.Future(LibCInstallation), |
| 47 | 50 | ||
| 48 | var lazy_init_targets = std.once(util.initializeAllTargets); | 51 | var lazy_init_targets = std.once(initializeAllTargets); |
| 49 | 52 | ||
| 50 | pub fn init(allocator: *Allocator) !ZigCompiler { | 53 | pub fn init(allocator: *Allocator) !ZigCompiler { |
| 51 | lazy_init_targets.call(); | 54 | lazy_init_targets.call(); |
| ... | @@ -119,6 +122,8 @@ pub const LlvmHandle = struct { | ... | @@ -119,6 +122,8 @@ pub const LlvmHandle = struct { |
| 119 | }; | 122 | }; |
| 120 | 123 | ||
| 121 | pub const Compilation = struct { | 124 | pub const Compilation = struct { |
| 125 | pub const FnLinkSet = std.TailQueue(?*Value.Fn); | ||
| 126 | |||
| 122 | zig_compiler: *ZigCompiler, | 127 | zig_compiler: *ZigCompiler, |
| 123 | name: ArrayListSentineled(u8, 0), | 128 | name: ArrayListSentineled(u8, 0), |
| 124 | llvm_triple: ArrayListSentineled(u8, 0), | 129 | llvm_triple: ArrayListSentineled(u8, 0), |
| ... | @@ -152,8 +157,6 @@ pub const Compilation = struct { | ... | @@ -152,8 +157,6 @@ pub const Compilation = struct { |
| 152 | /// it uses an optional pointer so that tombstone removals are possible | 157 | /// it uses an optional pointer so that tombstone removals are possible |
| 153 | fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()), | 158 | fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()), |
| 154 | 159 | ||
| 155 | pub const FnLinkSet = std.TailQueue(?*Value.Fn); | ||
| 156 | |||
| 157 | link_libs_list: ArrayList(*LinkLib), | 160 | link_libs_list: ArrayList(*LinkLib), |
| 158 | libc_link_lib: ?*LinkLib = null, | 161 | libc_link_lib: ?*LinkLib = null, |
| 159 | 162 | ||
| ... | @@ -361,8 +364,7 @@ pub const Compilation = struct { | ... | @@ -361,8 +364,7 @@ pub const Compilation = struct { |
| 361 | return comp; | 364 | return comp; |
| 362 | } else if (await frame) |_| unreachable else |err| return err; | 365 | } else if (await frame) |_| unreachable else |err| return err; |
| 363 | } | 366 | } |
| 364 | 367 | fn createAsync( | |
| 365 | async fn createAsync( | ||
| 366 | out_comp: *?*Compilation, | 368 | out_comp: *?*Compilation, |
| 367 | zig_compiler: *ZigCompiler, | 369 | zig_compiler: *ZigCompiler, |
| 368 | name: []const u8, | 370 | name: []const u8, |
| ... | @@ -372,7 +374,7 @@ pub const Compilation = struct { | ... | @@ -372,7 +374,7 @@ pub const Compilation = struct { |
| 372 | build_mode: builtin.Mode, | 374 | build_mode: builtin.Mode, |
| 373 | is_static: bool, | 375 | is_static: bool, |
| 374 | zig_lib_dir: []const u8, | 376 | zig_lib_dir: []const u8, |
| 375 | ) !void { | 377 | ) callconv(.Async) !void { |
| 376 | const allocator = zig_compiler.allocator; | 378 | const allocator = zig_compiler.allocator; |
| 377 | 379 | ||
| 378 | // TODO merge this line with stage2.zig crossTargetToTarget | 380 | // TODO merge this line with stage2.zig crossTargetToTarget |
| ... | @@ -442,8 +444,8 @@ pub const Compilation = struct { | ... | @@ -442,8 +444,8 @@ pub const Compilation = struct { |
| 442 | } | 444 | } |
| 443 | 445 | ||
| 444 | comp.name = try ArrayListSentineled(u8, 0).init(comp.arena(), name); | 446 | comp.name = try ArrayListSentineled(u8, 0).init(comp.arena(), name); |
| 445 | comp.llvm_triple = try util.getLLVMTriple(comp.arena(), target); | 447 | comp.llvm_triple = try getLLVMTriple(comp.arena(), target); |
| 446 | comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple); | 448 | comp.llvm_target = try llvmTargetFromTriple(comp.llvm_triple); |
| 447 | comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" }); | 449 | comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" }); |
| 448 | 450 | ||
| 449 | const opt_level = switch (build_mode) { | 451 | const opt_level = switch (build_mode) { |
| ... | @@ -726,8 +728,7 @@ pub const Compilation = struct { | ... | @@ -726,8 +728,7 @@ pub const Compilation = struct { |
| 726 | fn start(self: *Compilation) void { | 728 | fn start(self: *Compilation) void { |
| 727 | self.main_loop_future.resolve(); | 729 | self.main_loop_future.resolve(); |
| 728 | } | 730 | } |
| 729 | 731 | fn mainLoop(self: *Compilation) callconv(.Async) void { | |
| 730 | async fn mainLoop(self: *Compilation) void { | ||
| 731 | // wait until start() is called | 732 | // wait until start() is called |
| 732 | _ = self.main_loop_future.get(); | 733 | _ = self.main_loop_future.get(); |
| 733 | 734 | ||
| ... | @@ -790,8 +791,7 @@ pub const Compilation = struct { | ... | @@ -790,8 +791,7 @@ pub const Compilation = struct { |
| 790 | build_result = group.wait(); | 791 | build_result = group.wait(); |
| 791 | } | 792 | } |
| 792 | } | 793 | } |
| 793 | 794 | fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) callconv(.Async) BuildError!void { | |
| 794 | async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void { | ||
| 795 | const tree_scope = blk: { | 795 | const tree_scope = blk: { |
| 796 | const source_code = fs.cwd().readFileAlloc( | 796 | const source_code = fs.cwd().readFileAlloc( |
| 797 | self.gpa(), | 797 | self.gpa(), |
| ... | @@ -964,15 +964,14 @@ pub const Compilation = struct { | ... | @@ -964,15 +964,14 @@ pub const Compilation = struct { |
| 964 | try link(self); | 964 | try link(self); |
| 965 | } | 965 | } |
| 966 | } | 966 | } |
| 967 | |||
| 968 | /// caller takes ownership of resulting Code | 967 | /// caller takes ownership of resulting Code |
| 969 | async fn genAndAnalyzeCode( | 968 | fn genAndAnalyzeCode( |
| 970 | comp: *Compilation, | 969 | comp: *Compilation, |
| 971 | tree_scope: *Scope.AstTree, | 970 | tree_scope: *Scope.AstTree, |
| 972 | scope: *Scope, | 971 | scope: *Scope, |
| 973 | node: *ast.Node, | 972 | node: *ast.Node, |
| 974 | expected_type: ?*Type, | 973 | expected_type: ?*Type, |
| 975 | ) !*ir.Code { | 974 | ) callconv(.Async) !*ir.Code { |
| 976 | const unanalyzed_code = try ir.gen( | 975 | const unanalyzed_code = try ir.gen( |
| 977 | comp, | 976 | comp, |
| 978 | node, | 977 | node, |
| ... | @@ -1000,13 +999,12 @@ pub const Compilation = struct { | ... | @@ -1000,13 +999,12 @@ pub const Compilation = struct { |
| 1000 | 999 | ||
| 1001 | return analyzed_code; | 1000 | return analyzed_code; |
| 1002 | } | 1001 | } |
| 1003 | 1002 | fn addCompTimeBlock( | |
| 1004 | async fn addCompTimeBlock( | ||
| 1005 | comp: *Compilation, | 1003 | comp: *Compilation, |
| 1006 | tree_scope: *Scope.AstTree, | 1004 | tree_scope: *Scope.AstTree, |
| 1007 | scope: *Scope, | 1005 | scope: *Scope, |
| 1008 | comptime_node: *ast.Node.Comptime, | 1006 | comptime_node: *ast.Node.Comptime, |
| 1009 | ) BuildError!void { | 1007 | ) callconv(.Async) BuildError!void { |
| 1010 | const void_type = Type.Void.get(comp); | 1008 | const void_type = Type.Void.get(comp); |
| 1011 | defer void_type.base.base.deref(comp); | 1009 | defer void_type.base.base.deref(comp); |
| 1012 | 1010 | ||
| ... | @@ -1024,12 +1022,11 @@ pub const Compilation = struct { | ... | @@ -1024,12 +1022,11 @@ pub const Compilation = struct { |
| 1024 | }; | 1022 | }; |
| 1025 | analyzed_code.destroy(comp.gpa()); | 1023 | analyzed_code.destroy(comp.gpa()); |
| 1026 | } | 1024 | } |
| 1027 | 1025 | fn addTopLevelDecl( | |
| 1028 | async fn addTopLevelDecl( | ||
| 1029 | self: *Compilation, | 1026 | self: *Compilation, |
| 1030 | decl: *Decl, | 1027 | decl: *Decl, |
| 1031 | locked_table: *Decl.Table, | 1028 | locked_table: *Decl.Table, |
| 1032 | ) BuildError!void { | 1029 | ) callconv(.Async) BuildError!void { |
| 1033 | const is_export = decl.isExported(decl.tree_scope.tree); | 1030 | const is_export = decl.isExported(decl.tree_scope.tree); |
| 1034 | 1031 | ||
| 1035 | if (is_export) { | 1032 | if (is_export) { |
| ... | @@ -1065,11 +1062,10 @@ pub const Compilation = struct { | ... | @@ -1065,11 +1062,10 @@ pub const Compilation = struct { |
| 1065 | 1062 | ||
| 1066 | try self.prelink_group.call(addCompileErrorAsync, .{ self, msg }); | 1063 | try self.prelink_group.call(addCompileErrorAsync, .{ self, msg }); |
| 1067 | } | 1064 | } |
| 1068 | 1065 | fn addCompileErrorAsync( | |
| 1069 | async fn addCompileErrorAsync( | ||
| 1070 | self: *Compilation, | 1066 | self: *Compilation, |
| 1071 | msg: *Msg, | 1067 | msg: *Msg, |
| 1072 | ) BuildError!void { | 1068 | ) callconv(.Async) BuildError!void { |
| 1073 | errdefer msg.destroy(); | 1069 | errdefer msg.destroy(); |
| 1074 | 1070 | ||
| 1075 | const compile_errors = self.compile_errors.acquire(); | 1071 | const compile_errors = self.compile_errors.acquire(); |
| ... | @@ -1077,8 +1073,7 @@ pub const Compilation = struct { | ... | @@ -1077,8 +1073,7 @@ pub const Compilation = struct { |
| 1077 | 1073 | ||
| 1078 | try compile_errors.value.append(msg); | 1074 | try compile_errors.value.append(msg); |
| 1079 | } | 1075 | } |
| 1080 | 1076 | fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) callconv(.Async) BuildError!void { | |
| 1081 | async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) BuildError!void { | ||
| 1082 | const exported_symbol_names = self.exported_symbol_names.acquire(); | 1077 | const exported_symbol_names = self.exported_symbol_names.acquire(); |
| 1083 | defer exported_symbol_names.release(); | 1078 | defer exported_symbol_names.release(); |
| 1084 | 1079 | ||
| ... | @@ -1129,8 +1124,7 @@ pub const Compilation = struct { | ... | @@ -1129,8 +1124,7 @@ pub const Compilation = struct { |
| 1129 | } | 1124 | } |
| 1130 | return link_lib; | 1125 | return link_lib; |
| 1131 | } | 1126 | } |
| 1132 | 1127 | fn startFindingNativeLibC(self: *Compilation) callconv(.Async) void { | |
| 1133 | async fn startFindingNativeLibC(self: *Compilation) void { | ||
| 1134 | event.Loop.startCpuBoundOperation(); | 1128 | event.Loop.startCpuBoundOperation(); |
| 1135 | // we don't care if it fails, we're just trying to kick off the future resolution | 1129 | // we don't care if it fails, we're just trying to kick off the future resolution |
| 1136 | _ = self.zig_compiler.getNativeLibC() catch return; | 1130 | _ = self.zig_compiler.getNativeLibC() catch return; |
| ... | @@ -1234,7 +1228,7 @@ pub const Compilation = struct { | ... | @@ -1234,7 +1228,7 @@ pub const Compilation = struct { |
| 1234 | } | 1228 | } |
| 1235 | 1229 | ||
| 1236 | /// This declaration has been blessed as going into the final code generation. | 1230 | /// This declaration has been blessed as going into the final code generation. |
| 1237 | pub async fn resolveDecl(comp: *Compilation, decl: *Decl) BuildError!void { | 1231 | pub fn resolveDecl(comp: *Compilation, decl: *Decl) callconv(.Async) BuildError!void { |
| 1238 | if (decl.resolution.start()) |ptr| return ptr.*; | 1232 | if (decl.resolution.start()) |ptr| return ptr.*; |
| 1239 | 1233 | ||
| 1240 | decl.resolution.data = try generateDecl(comp, decl); | 1234 | decl.resolution.data = try generateDecl(comp, decl); |
| ... | @@ -1335,8 +1329,7 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { | ... | @@ -1335,8 +1329,7 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 1335 | try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code }); | 1329 | try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code }); |
| 1336 | try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val }); | 1330 | try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val }); |
| 1337 | } | 1331 | } |
| 1338 | 1332 | fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) callconv(.Async) Compilation.BuildError!void { | |
| 1339 | async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) Compilation.BuildError!void { | ||
| 1340 | fn_val.base.ref(); | 1333 | fn_val.base.ref(); |
| 1341 | defer fn_val.base.deref(comp); | 1334 | defer fn_val.base.deref(comp); |
| 1342 | 1335 | ||
| ... | @@ -1432,3 +1425,33 @@ fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { | ... | @@ -1432,3 +1425,33 @@ fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 1432 | fn_decl.value = .{ .FnProto = fn_proto_val }; | 1425 | fn_decl.value = .{ .FnProto = fn_proto_val }; |
| 1433 | symbol_name_consumed = true; | 1426 | symbol_name_consumed = true; |
| 1434 | } | 1427 | } |
| 1428 | |||
| 1429 | pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target { | ||
| 1430 | var result: *llvm.Target = undefined; | ||
| 1431 | var err_msg: [*:0]u8 = undefined; | ||
| 1432 | if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) { | ||
| 1433 | std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg }); | ||
| 1434 | return error.UnsupportedTarget; | ||
| 1435 | } | ||
| 1436 | return result; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | pub fn initializeAllTargets() void { | ||
| 1440 | llvm.InitializeAllTargets(); | ||
| 1441 | llvm.InitializeAllTargetInfos(); | ||
| 1442 | llvm.InitializeAllTargetMCs(); | ||
| 1443 | llvm.InitializeAllAsmPrinters(); | ||
| 1444 | llvm.InitializeAllAsmParsers(); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 { | ||
| 1448 | var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0); | ||
| 1449 | defer result.deinit(); | ||
| 1450 | |||
| 1451 | try result.outStream().print( | ||
| 1452 | "{}-unknown-{}-{}", | ||
| 1453 | .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) }, | ||
| 1454 | ); | ||
| 1455 | |||
| 1456 | return result.toOwnedSlice(); | ||
| 1457 | } |
src-self-hosted/decl.zig deleted-102| ... | @@ -1,102 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Allocator = mem.Allocator; | ||
| 3 | const mem = std.mem; | ||
| 4 | const ast = std.zig.ast; | ||
| 5 | const Visib = @import("visib.zig").Visib; | ||
| 6 | const event = std.event; | ||
| 7 | const Value = @import("value.zig").Value; | ||
| 8 | const Token = std.zig.Token; | ||
| 9 | const errmsg = @import("errmsg.zig"); | ||
| 10 | const Scope = @import("scope.zig").Scope; | ||
| 11 | const Compilation = @import("compilation.zig").Compilation; | ||
| 12 | |||
| 13 | pub const Decl = struct { | ||
| 14 | id: Id, | ||
| 15 | name: []const u8, | ||
| 16 | visib: Visib, | ||
| 17 | resolution: event.Future(Compilation.BuildError!void), | ||
| 18 | parent_scope: *Scope, | ||
| 19 | |||
| 20 | // TODO when we destroy the decl, deref the tree scope | ||
| 21 | tree_scope: *Scope.AstTree, | ||
| 22 | |||
| 23 | pub const Table = std.StringHashMap(*Decl); | ||
| 24 | |||
| 25 | pub fn cast(base: *Decl, comptime T: type) ?*T { | ||
| 26 | if (base.id != @field(Id, @typeName(T))) return null; | ||
| 27 | return @fieldParentPtr(T, "base", base); | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { | ||
| 31 | switch (base.id) { | ||
| 32 | .Fn => { | ||
| 33 | const fn_decl = @fieldParentPtr(Fn, "base", base); | ||
| 34 | return fn_decl.isExported(tree); | ||
| 35 | }, | ||
| 36 | else => return false, | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn getSpan(base: *const Decl) errmsg.Span { | ||
| 41 | switch (base.id) { | ||
| 42 | .Fn => { | ||
| 43 | const fn_decl = @fieldParentPtr(Fn, "base", base); | ||
| 44 | const fn_proto = fn_decl.fn_proto; | ||
| 45 | const start = fn_proto.fn_token; | ||
| 46 | const end = fn_proto.name_token orelse start; | ||
| 47 | return errmsg.Span{ | ||
| 48 | .first = start, | ||
| 49 | .last = end + 1, | ||
| 50 | }; | ||
| 51 | }, | ||
| 52 | else => @panic("TODO"), | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn findRootScope(base: *const Decl) *Scope.Root { | ||
| 57 | return base.parent_scope.findRoot(); | ||
| 58 | } | ||
| 59 | |||
| 60 | pub const Id = enum { | ||
| 61 | Var, | ||
| 62 | Fn, | ||
| 63 | CompTime, | ||
| 64 | }; | ||
| 65 | |||
| 66 | pub const Var = struct { | ||
| 67 | base: Decl, | ||
| 68 | }; | ||
| 69 | |||
| 70 | pub const Fn = struct { | ||
| 71 | base: Decl, | ||
| 72 | value: union(enum) { | ||
| 73 | Unresolved, | ||
| 74 | Fn: *Value.Fn, | ||
| 75 | FnProto: *Value.FnProto, | ||
| 76 | }, | ||
| 77 | fn_proto: *ast.Node.FnProto, | ||
| 78 | |||
| 79 | pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { | ||
| 80 | return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { | ||
| 81 | const token = tree.tokens.at(tok_index); | ||
| 82 | break :x switch (token.id) { | ||
| 83 | .Extern => tree.tokenSlicePtr(token), | ||
| 84 | else => null, | ||
| 85 | }; | ||
| 86 | } else null; | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn isExported(self: Fn, tree: *ast.Tree) bool { | ||
| 90 | if (self.fn_proto.extern_export_inline_token) |tok_index| { | ||
| 91 | const token = tree.tokens.at(tok_index); | ||
| 92 | return token.id == .Keyword_export; | ||
| 93 | } else { | ||
| 94 | return false; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | }; | ||
| 98 | |||
| 99 | pub const CompTime = struct { | ||
| 100 | base: Decl, | ||
| 101 | }; | ||
| 102 | }; | ||
src-self-hosted/ir.zig+716-352| ... | @@ -1,12 +1,16 @@ | ... | @@ -1,12 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 5 | const LinkedList = std.TailQueue; | ||
| 4 | const Value = @import("value.zig").Value; | 6 | const Value = @import("value.zig").Value; |
| 5 | const Type = @import("type.zig").Type; | 7 | const Type = @import("type.zig").Type; |
| 6 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 7 | const BigIntConst = std.math.big.int.Const; | 9 | const BigIntConst = std.math.big.int.Const; |
| 8 | const BigIntMutable = std.math.big.int.Mutable; | 10 | const BigIntMutable = std.math.big.int.Mutable; |
| 9 | const Target = std.Target; | 11 | const Target = std.Target; |
| 12 | const Package = @import("Package.zig"); | ||
| 13 | const link = @import("link.zig"); | ||
| 10 | 14 | ||
| 11 | pub const text = @import("ir/text.zig"); | 15 | pub const text = @import("ir/text.zig"); |
| 12 | 16 | ||
| ... | @@ -25,6 +29,7 @@ pub const Inst = struct { | ... | @@ -25,6 +29,7 @@ pub const Inst = struct { |
| 25 | assembly, | 29 | assembly, |
| 26 | bitcast, | 30 | bitcast, |
| 27 | breakpoint, | 31 | breakpoint, |
| 32 | call, | ||
| 28 | cmp, | 33 | cmp, |
| 29 | condbr, | 34 | condbr, |
| 30 | constant, | 35 | constant, |
| ... | @@ -84,6 +89,15 @@ pub const Inst = struct { | ... | @@ -84,6 +89,15 @@ pub const Inst = struct { |
| 84 | args: void, | 89 | args: void, |
| 85 | }; | 90 | }; |
| 86 | 91 | ||
| 92 | pub const Call = struct { | ||
| 93 | pub const base_tag = Tag.call; | ||
| 94 | base: Inst, | ||
| 95 | args: struct { | ||
| 96 | func: *Inst, | ||
| 97 | args: []const *Inst, | ||
| 98 | }, | ||
| 99 | }; | ||
| 100 | |||
| 87 | pub const Cmp = struct { | 101 | pub const Cmp = struct { |
| 88 | pub const base_tag = Tag.cmp; | 102 | pub const base_tag = Tag.cmp; |
| 89 | 103 | ||
| ... | @@ -158,170 +172,416 @@ pub const TypedValue = struct { | ... | @@ -158,170 +172,416 @@ pub const TypedValue = struct { |
| 158 | val: Value, | 172 | val: Value, |
| 159 | }; | 173 | }; |
| 160 | 174 | ||
| 175 | fn swapRemoveElem(allocator: *Allocator, comptime T: type, item: T, list: *ArrayListUnmanaged(T)) void { | ||
| 176 | var i: usize = 0; | ||
| 177 | while (i < list.items.len) { | ||
| 178 | if (list.items[i] == item) { | ||
| 179 | list.swapRemove(allocator, i); | ||
| 180 | continue; | ||
| 181 | } | ||
| 182 | i += 1; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 161 | pub const Module = struct { | 186 | pub const Module = struct { |
| 162 | exports: []Export, | 187 | /// General-purpose allocator. |
| 163 | errors: []ErrorMsg, | 188 | allocator: *Allocator, |
| 164 | arena: std.heap.ArenaAllocator, | 189 | /// Module owns this resource. |
| 165 | fns: []Fn, | 190 | root_pkg: *Package, |
| 166 | target: Target, | 191 | /// Module owns this resource. |
| 167 | link_mode: std.builtin.LinkMode, | 192 | root_scope: *Scope.ZIRModule, |
| 168 | output_mode: std.builtin.OutputMode, | 193 | /// Pointer to externally managed resource. |
| 169 | object_format: std.Target.ObjectFormat, | 194 | bin_file: *link.ElfFile, |
| 195 | failed_decls: ArrayListUnmanaged(*Decl) = .{}, | ||
| 196 | failed_fns: ArrayListUnmanaged(*Fn) = .{}, | ||
| 197 | failed_files: ArrayListUnmanaged(*Scope.ZIRModule) = .{}, | ||
| 198 | decl_table: std.AutoHashMap(Decl.Hash, *Decl), | ||
| 170 | optimize_mode: std.builtin.Mode, | 199 | optimize_mode: std.builtin.Mode, |
| 171 | 200 | link_error_flags: link.ElfFile.ErrorFlags = .{}, | |
| 172 | pub const Export = struct { | 201 | |
| 173 | name: []const u8, | 202 | pub const Decl = struct { |
| 174 | typed_value: TypedValue, | 203 | /// Contains the memory for `typed_value` and this `Decl` itself. |
| 204 | /// If the Decl is a function, also contains that memory. | ||
| 205 | /// If the decl has any export nodes, also contains that memory. | ||
| 206 | /// TODO look into using a more memory efficient arena that will cost less bytes per decl. | ||
| 207 | /// This one has a minimum allocation of 4096 bytes. | ||
| 208 | arena: std.heap.ArenaAllocator.State, | ||
| 209 | /// This name is relative to the containing namespace of the decl. It uses a null-termination | ||
| 210 | /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed | ||
| 211 | /// in symbol names, because executable file formats use null-terminated strings for symbol names. | ||
| 212 | 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, | ||
| 216 | /// Byte offset into the source file that contains this declaration. | ||
| 217 | /// This is the base offset that src offsets within this Decl are relative to. | ||
| 175 | src: usize, | 218 | src: usize, |
| 219 | /// Represents the "shallow" analysis status. For example, for decls that are functions, | ||
| 220 | /// the function type is analyzed with this set to `in_progress`, however, the semantic | ||
| 221 | /// analysis of the function body is performed with this value set to `success`. Functions | ||
| 222 | /// have their own analysis status field. | ||
| 223 | analysis: union(enum) { | ||
| 224 | in_progress, | ||
| 225 | failure: ErrorMsg, | ||
| 226 | success: TypedValue, | ||
| 227 | }, | ||
| 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, | ||
| 231 | |||
| 232 | pub fn destroy(self: *Decl, allocator: *Allocator) void { | ||
| 233 | var arena = self.arena.promote(allocator); | ||
| 234 | arena.deinit(); | ||
| 235 | } | ||
| 236 | |||
| 237 | pub const Hash = [16]u8; | ||
| 238 | |||
| 239 | /// Must generate unique bytes with no collisions with other decls. | ||
| 240 | /// The point of hashing here is only to limit the number of bytes of | ||
| 241 | /// the unique identifier to a fixed size (16 bytes). | ||
| 242 | pub fn fullyQualifiedNameHash(self: Decl) Hash { | ||
| 243 | // Right now we only have ZIRModule as the source. So this is simply the | ||
| 244 | // relative name of the decl. | ||
| 245 | var out: Hash = undefined; | ||
| 246 | std.crypto.Blake3.hash(mem.spanZ(u8, self.name), &out); | ||
| 247 | return out; | ||
| 248 | } | ||
| 176 | }; | 249 | }; |
| 177 | 250 | ||
| 251 | /// Memory is managed by the arena of the owning Decl. | ||
| 178 | pub const Fn = struct { | 252 | pub const Fn = struct { |
| 179 | analysis_status: enum { in_progress, failure, success }, | ||
| 180 | body: Body, | ||
| 181 | fn_type: Type, | 253 | fn_type: Type, |
| 254 | analysis: union(enum) { | ||
| 255 | in_progress: *Analysis, | ||
| 256 | failure: ErrorMsg, | ||
| 257 | success: Body, | ||
| 258 | }, | ||
| 259 | /// The direct container of the Fn. This field will need to get more fleshed out when | ||
| 260 | /// self-hosted supports proper struct types and Zig AST => ZIR. | ||
| 261 | scope: *Scope.ZIRModule, | ||
| 262 | |||
| 263 | /// This memory managed by the general purpose allocator. | ||
| 264 | pub const Analysis = struct { | ||
| 265 | inner_block: Scope.Block, | ||
| 266 | /// null value means a semantic analysis error happened. | ||
| 267 | inst_table: std.AutoHashMap(*text.Inst, ?*Inst), | ||
| 268 | }; | ||
| 269 | }; | ||
| 270 | |||
| 271 | pub const Scope = struct { | ||
| 272 | tag: Tag, | ||
| 273 | |||
| 274 | pub fn cast(base: *Scope, comptime T: type) ?*T { | ||
| 275 | if (base.tag != T.base_tag) | ||
| 276 | return null; | ||
| 277 | |||
| 278 | return @fieldParentPtr(T, "base", base); | ||
| 279 | } | ||
| 280 | |||
| 281 | pub const Tag = enum { | ||
| 282 | zir_module, | ||
| 283 | block, | ||
| 284 | decl, | ||
| 285 | }; | ||
| 286 | |||
| 287 | pub const ZIRModule = struct { | ||
| 288 | pub const base_tag: Tag = .zir_module; | ||
| 289 | base: Scope = Scope{ .tag = base_tag }, | ||
| 290 | /// Relative to the owning package's root_src_dir. | ||
| 291 | /// Reference to external memory, not owned by ZIRModule. | ||
| 292 | sub_file_path: []const u8, | ||
| 293 | contents: union(enum) { | ||
| 294 | unloaded, | ||
| 295 | parse_failure: ParseFailure, | ||
| 296 | success: Contents, | ||
| 297 | }, | ||
| 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, | ||
| 309 | module: *text.Module, | ||
| 310 | }; | ||
| 311 | |||
| 312 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { | ||
| 313 | switch (self.contents) { | ||
| 314 | .unloaded => {}, | ||
| 315 | .parse_failure => |pf| pd.deinit(allocator), | ||
| 316 | .success => |contents| { | ||
| 317 | allocator.free(contents.source); | ||
| 318 | contents.src_zir_module.deinit(allocator); | ||
| 319 | }, | ||
| 320 | } | ||
| 321 | self.* = undefined; | ||
| 322 | } | ||
| 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 | }; | ||
| 356 | |||
| 357 | /// This is a temporary structure, references to it are valid only | ||
| 358 | /// during semantic analysis of the block. | ||
| 359 | pub const Block = struct { | ||
| 360 | pub const base_tag: Tag = .block; | ||
| 361 | base: Scope = Scope{ .tag = base_tag }, | ||
| 362 | func: *Fn, | ||
| 363 | instructions: ArrayListUnmanaged(*Inst), | ||
| 364 | }; | ||
| 365 | |||
| 366 | /// This is a temporary structure, references to it are valid only | ||
| 367 | /// during semantic analysis of the decl. | ||
| 368 | pub const DeclAnalysis = struct { | ||
| 369 | pub const base_tag: Tag = .decl; | ||
| 370 | base: Scope = Scope{ .tag = base_tag }, | ||
| 371 | decl: *Decl, | ||
| 372 | }; | ||
| 182 | }; | 373 | }; |
| 183 | 374 | ||
| 184 | pub const Body = struct { | 375 | pub const Body = struct { |
| 185 | instructions: []*Inst, | 376 | instructions: []*Inst, |
| 186 | }; | 377 | }; |
| 187 | 378 | ||
| 188 | pub fn deinit(self: *Module, allocator: *Allocator) void { | 379 | pub const AllErrors = struct { |
| 189 | allocator.free(self.exports); | 380 | arena: std.heap.ArenaAllocator.State, |
| 381 | list: []const Message, | ||
| 382 | |||
| 383 | pub const Message = struct { | ||
| 384 | src_path: []const u8, | ||
| 385 | line: usize, | ||
| 386 | column: usize, | ||
| 387 | byte_offset: usize, | ||
| 388 | msg: []const u8, | ||
| 389 | }; | ||
| 390 | |||
| 391 | pub fn deinit(self: *AllErrors, allocator: *Allocator) void { | ||
| 392 | self.arena.promote(allocator).deinit(); | ||
| 393 | } | ||
| 394 | |||
| 395 | fn add( | ||
| 396 | arena: *std.heap.ArenaAllocator, | ||
| 397 | errors: *std.ArrayList(Message), | ||
| 398 | sub_file_path: []const u8, | ||
| 399 | source: []const u8, | ||
| 400 | simple_err_msg: ErrorMsg, | ||
| 401 | ) !void { | ||
| 402 | const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); | ||
| 403 | try errors.append(.{ | ||
| 404 | .src_path = try mem.dupe(u8, &arena.allocator, sub_file_path), | ||
| 405 | .msg = try mem.dupe(u8, &arena.allocator, simple_err_msg.msg), | ||
| 406 | .byte_offset = simple_err_msg.byte_offset, | ||
| 407 | .line = loc.line, | ||
| 408 | .column = loc.column, | ||
| 409 | }); | ||
| 410 | } | ||
| 411 | }; | ||
| 412 | |||
| 413 | pub fn deinit(self: *Module) void { | ||
| 414 | const allocator = self.allocator; | ||
| 190 | allocator.free(self.errors); | 415 | allocator.free(self.errors); |
| 191 | for (self.fns) |f| { | 416 | { |
| 192 | allocator.free(f.body.instructions); | 417 | var it = self.decl_table.iterator(); |
| 418 | while (it.next()) |kv| { | ||
| 419 | kv.value.destroy(allocator); | ||
| 420 | } | ||
| 421 | self.decl_table.deinit(); | ||
| 193 | } | 422 | } |
| 194 | allocator.free(self.fns); | 423 | self.root_pkg.destroy(); |
| 195 | self.arena.deinit(); | 424 | self.root_scope.deinit(); |
| 196 | self.* = undefined; | 425 | self.* = undefined; |
| 197 | } | 426 | } |
| 198 | }; | ||
| 199 | 427 | ||
| 200 | pub const ErrorMsg = struct { | 428 | pub fn target(self: Module) std.Target { |
| 201 | byte_offset: usize, | 429 | return self.bin_file.options.target; |
| 202 | msg: []const u8, | 430 | } |
| 203 | }; | ||
| 204 | 431 | ||
| 205 | pub const AnalyzeOptions = struct { | 432 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 206 | target: Target, | 433 | pub fn update(self: *Module) !void { |
| 207 | output_mode: std.builtin.OutputMode, | 434 | // TODO Use the cache hash file system to detect which source files changed. |
| 208 | link_mode: std.builtin.LinkMode, | 435 | // Here we simulate a full cache miss. |
| 209 | object_format: ?std.Target.ObjectFormat = null, | 436 | // Analyze the root source file now. |
| 210 | optimize_mode: std.builtin.Mode, | 437 | self.analyzeRoot(self.root_scope) catch |err| switch (err) { |
| 211 | }; | 438 | error.AnalysisFail => { |
| 439 | assert(self.totalErrorCount() != 0); | ||
| 440 | }, | ||
| 441 | else => |e| return e, | ||
| 442 | }; | ||
| 212 | 443 | ||
| 213 | pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module { | 444 | try self.bin_file.flush(); |
| 214 | var ctx = Analyze{ | 445 | self.link_error_flags = self.bin_file.error_flags; |
| 215 | .allocator = allocator, | 446 | } |
| 216 | .arena = std.heap.ArenaAllocator.init(allocator), | ||
| 217 | .old_module = &old_module, | ||
| 218 | .errors = std.ArrayList(ErrorMsg).init(allocator), | ||
| 219 | .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator), | ||
| 220 | .exports = std.ArrayList(Module.Export).init(allocator), | ||
| 221 | .fns = std.ArrayList(Module.Fn).init(allocator), | ||
| 222 | .target = options.target, | ||
| 223 | .optimize_mode = options.optimize_mode, | ||
| 224 | .link_mode = options.link_mode, | ||
| 225 | .output_mode = options.output_mode, | ||
| 226 | }; | ||
| 227 | defer ctx.errors.deinit(); | ||
| 228 | defer ctx.decl_table.deinit(); | ||
| 229 | defer ctx.exports.deinit(); | ||
| 230 | defer ctx.fns.deinit(); | ||
| 231 | errdefer ctx.arena.deinit(); | ||
| 232 | |||
| 233 | ctx.analyzeRoot() catch |err| switch (err) { | ||
| 234 | error.AnalysisFail => { | ||
| 235 | assert(ctx.errors.items.len != 0); | ||
| 236 | }, | ||
| 237 | else => |e| return e, | ||
| 238 | }; | ||
| 239 | return Module{ | ||
| 240 | .exports = ctx.exports.toOwnedSlice(), | ||
| 241 | .errors = ctx.errors.toOwnedSlice(), | ||
| 242 | .fns = ctx.fns.toOwnedSlice(), | ||
| 243 | .arena = ctx.arena, | ||
| 244 | .target = ctx.target, | ||
| 245 | .link_mode = ctx.link_mode, | ||
| 246 | .output_mode = ctx.output_mode, | ||
| 247 | .object_format = options.object_format orelse ctx.target.getObjectFormat(), | ||
| 248 | .optimize_mode = ctx.optimize_mode, | ||
| 249 | }; | ||
| 250 | } | ||
| 251 | 447 | ||
| 252 | const Analyze = struct { | 448 | pub fn totalErrorCount(self: *Module) usize { |
| 253 | allocator: *Allocator, | 449 | return self.failed_decls.items.len + |
| 254 | arena: std.heap.ArenaAllocator, | 450 | self.failed_fns.items.len + |
| 255 | old_module: *const text.Module, | 451 | self.failed_decls.items.len + |
| 256 | errors: std.ArrayList(ErrorMsg), | 452 | @boolToInt(self.link_error_flags.no_entry_point_found); |
| 257 | decl_table: std.AutoHashMap(*text.Inst, NewDecl), | 453 | } |
| 258 | exports: std.ArrayList(Module.Export), | ||
| 259 | fns: std.ArrayList(Module.Fn), | ||
| 260 | target: Target, | ||
| 261 | link_mode: std.builtin.LinkMode, | ||
| 262 | optimize_mode: std.builtin.Mode, | ||
| 263 | output_mode: std.builtin.OutputMode, | ||
| 264 | 454 | ||
| 265 | const NewDecl = struct { | 455 | pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 266 | /// null means a semantic analysis error happened | 456 | var arena = std.heap.ArenaAllocator.init(self.allocator); |
| 267 | ptr: ?*Inst, | 457 | errdefer arena.deinit(); |
| 268 | }; | ||
| 269 | 458 | ||
| 270 | const NewInst = struct { | 459 | var errors = std.ArrayList(AllErrors.Message).init(self.allocator); |
| 271 | /// null means a semantic analysis error happened | 460 | defer errors.deinit(); |
| 272 | ptr: ?*Inst, | ||
| 273 | }; | ||
| 274 | 461 | ||
| 275 | const Fn = struct { | 462 | for (self.failed_files.items) |scope| { |
| 276 | /// Index into Module fns array | 463 | const source = scope.parse_failure.source; |
| 277 | fn_index: usize, | 464 | for (scope.parse_failure.errors) |parse_error| { |
| 278 | inner_block: Block, | 465 | AllErrors.add(&arena, &errors, scope.sub_file_path, source, parse_error); |
| 279 | inst_table: std.AutoHashMap(*text.Inst, NewInst), | 466 | } |
| 280 | }; | 467 | } |
| 281 | 468 | ||
| 282 | const Block = struct { | 469 | for (self.failed_fns.items) |func| { |
| 283 | func: *Fn, | 470 | const source = func.scope.success.source; |
| 284 | instructions: std.ArrayList(*Inst), | 471 | for (func.analysis.failure) |err_msg| { |
| 285 | }; | 472 | AllErrors.add(&arena, &errors, func.scope.sub_file_path, source, err_msg); |
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | for (self.failed_decls.items) |decl| { | ||
| 477 | const source = decl.scope.success.source; | ||
| 478 | for (decl.analysis.failure) |err_msg| { | ||
| 479 | AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | if (self.link_error_flags.no_entry_point_found) { | ||
| 484 | try errors.append(.{ | ||
| 485 | .src_path = self.module.root_src_path, | ||
| 486 | .line = 0, | ||
| 487 | .column = 0, | ||
| 488 | .byte_offset = 0, | ||
| 489 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), | ||
| 490 | }); | ||
| 491 | } | ||
| 492 | |||
| 493 | assert(errors.items.len == self.totalErrorCount()); | ||
| 494 | |||
| 495 | return AllErrors{ | ||
| 496 | .arena = arena.state, | ||
| 497 | .list = try mem.dupe(&arena.allocator, AllErrors.Message, errors.items), | ||
| 498 | }; | ||
| 499 | } | ||
| 286 | 500 | ||
| 287 | const InnerError = error{ OutOfMemory, AnalysisFail }; | 501 | const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 288 | 502 | ||
| 289 | fn analyzeRoot(self: *Analyze) !void { | 503 | fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 290 | for (self.old_module.decls) |decl| { | 504 | // TODO use the cache to identify, from the modified source files, the decls which have |
| 505 | // changed based on the span of memory that represents the decl in the re-parsed source file. | ||
| 506 | // Use the cached dependency graph to recursively determine the set of decls which need | ||
| 507 | // regeneration. | ||
| 508 | // Here we simulate adding a source file which was previously not part of the compilation, | ||
| 509 | // which means scanning the decls looking for exports. | ||
| 510 | // 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); | ||
| 518 | return error.AnalysisFail; | ||
| 519 | }, | ||
| 520 | else => |e| return e, | ||
| 521 | }; | ||
| 522 | }; | ||
| 523 | for (contents.module.decls) |decl| { | ||
| 291 | if (decl.cast(text.Inst.Export)) |export_inst| { | 524 | if (decl.cast(text.Inst.Export)) |export_inst| { |
| 292 | try analyzeExport(self, null, export_inst); | 525 | try analyzeExport(self, &root_scope.base, export_inst); |
| 293 | } | 526 | } |
| 294 | } | 527 | } |
| 295 | } | 528 | } |
| 296 | 529 | ||
| 297 | fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { | 530 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl { |
| 298 | if (opt_block) |block| { | 531 | const hash = old_inst.fullyQualifiedNameHash(); |
| 532 | if (self.decl_table.get(hash)) |kv| { | ||
| 533 | return kv.value; | ||
| 534 | } else { | ||
| 535 | const new_decl = blk: { | ||
| 536 | var decl_arena = std.heap.ArenaAllocator.init(self.allocator); | ||
| 537 | errdefer decl_arena.deinit(); | ||
| 538 | const new_decl = try decl_arena.allocator.create(Decl); | ||
| 539 | const name = try mem.dupeZ(&decl_arena.allocator, u8, old_inst.name); | ||
| 540 | new_decl.* = .{ | ||
| 541 | .arena = decl_arena.state, | ||
| 542 | .name = name, | ||
| 543 | .src = old_inst.src, | ||
| 544 | .analysis = .in_progress, | ||
| 545 | .scope = scope.findZIRModule(), | ||
| 546 | }; | ||
| 547 | try self.decl_table.putNoClobber(hash, new_decl); | ||
| 548 | break :blk new_decl; | ||
| 549 | }; | ||
| 550 | |||
| 551 | var decl_scope: Scope.DeclAnalysis = .{ .decl = new_decl }; | ||
| 552 | const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) { | ||
| 553 | error.AnalysisFail => return error.AnalysisFail, | ||
| 554 | else => |e| return e, | ||
| 555 | }; | ||
| 556 | 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 | } | ||
| 562 | return new_decl; | ||
| 563 | } | ||
| 564 | } | ||
| 565 | |||
| 566 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst { | ||
| 567 | if (scope.cast(Scope.Block)) |block| { | ||
| 299 | if (block.func.inst_table.get(old_inst)) |kv| { | 568 | if (block.func.inst_table.get(old_inst)) |kv| { |
| 300 | return kv.value.ptr orelse return error.AnalysisFail; | 569 | return kv.value.ptr orelse return error.AnalysisFail; |
| 301 | } | 570 | } |
| 302 | } | 571 | } |
| 303 | 572 | ||
| 304 | if (self.decl_table.get(old_inst)) |kv| { | 573 | const decl = try self.resolveDecl(scope, old_inst); |
| 305 | return kv.value.ptr orelse return error.AnalysisFail; | 574 | const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl); |
| 306 | } else { | 575 | return self.analyzeDeref(scope, old_inst.src, decl_ref); |
| 307 | const new_inst = self.analyzeInst(null, old_inst) catch |err| switch (err) { | ||
| 308 | error.AnalysisFail => { | ||
| 309 | try self.decl_table.putNoClobber(old_inst, .{ .ptr = null }); | ||
| 310 | return error.AnalysisFail; | ||
| 311 | }, | ||
| 312 | else => |e| return e, | ||
| 313 | }; | ||
| 314 | try self.decl_table.putNoClobber(old_inst, .{ .ptr = new_inst }); | ||
| 315 | return new_inst; | ||
| 316 | } | ||
| 317 | } | 576 | } |
| 318 | 577 | ||
| 319 | fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block { | 578 | fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { |
| 320 | return block orelse return self.fail(src, "instruction illegal outside function body", .{}); | 579 | return scope.cast(Scope.Block) orelse |
| 580 | return self.fail(scope, src, "instruction illegal outside function body", .{}); | ||
| 321 | } | 581 | } |
| 322 | 582 | ||
| 323 | fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue { | 583 | fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!TypedValue { |
| 324 | const new_inst = try self.resolveInst(block, old_inst); | 584 | const new_inst = try self.resolveInst(scope, old_inst); |
| 325 | const val = try self.resolveConstValue(new_inst); | 585 | const val = try self.resolveConstValue(new_inst); |
| 326 | return TypedValue{ | 586 | return TypedValue{ |
| 327 | .ty = new_inst.ty, | 587 | .ty = new_inst.ty, |
| ... | @@ -329,60 +589,67 @@ const Analyze = struct { | ... | @@ -329,60 +589,67 @@ const Analyze = struct { |
| 329 | }; | 589 | }; |
| 330 | } | 590 | } |
| 331 | 591 | ||
| 332 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { | 592 | fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value { |
| 333 | return (try self.resolveDefinedValue(base)) orelse | 593 | return (try self.resolveDefinedValue(base)) orelse |
| 334 | return self.fail(base.src, "unable to resolve comptime value", .{}); | 594 | return self.fail(scope, base.src, "unable to resolve comptime value", .{}); |
| 335 | } | 595 | } |
| 336 | 596 | ||
| 337 | fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value { | 597 | fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { |
| 338 | if (base.value()) |val| { | 598 | if (base.value()) |val| { |
| 339 | if (val.isUndef()) { | 599 | if (val.isUndef()) { |
| 340 | return self.fail(base.src, "use of undefined value here causes undefined behavior", .{}); | 600 | return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{}); |
| 341 | } | 601 | } |
| 342 | return val; | 602 | return val; |
| 343 | } | 603 | } |
| 344 | return null; | 604 | return null; |
| 345 | } | 605 | } |
| 346 | 606 | ||
| 347 | fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 { | 607 | fn resolveConstString(self: *Module, scope: *Scope, old_inst: *text.Inst) ![]u8 { |
| 348 | const new_inst = try self.resolveInst(block, old_inst); | 608 | const new_inst = try self.resolveInst(scope, old_inst); |
| 349 | const wanted_type = Type.initTag(.const_slice_u8); | 609 | const wanted_type = Type.initTag(.const_slice_u8); |
| 350 | const coerced_inst = try self.coerce(block, wanted_type, new_inst); | 610 | const coerced_inst = try self.coerce(scope, wanted_type, new_inst); |
| 351 | const val = try self.resolveConstValue(coerced_inst); | 611 | const val = try self.resolveConstValue(coerced_inst); |
| 352 | return val.toAllocatedBytes(&self.arena.allocator); | 612 | return val.toAllocatedBytes(&self.arena.allocator); |
| 353 | } | 613 | } |
| 354 | 614 | ||
| 355 | fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type { | 615 | fn resolveType(self: *Module, scope: *Scope, old_inst: *text.Inst) !Type { |
| 356 | const new_inst = try self.resolveInst(block, old_inst); | 616 | const new_inst = try self.resolveInst(scope, old_inst); |
| 357 | const wanted_type = Type.initTag(.@"type"); | 617 | const wanted_type = Type.initTag(.@"type"); |
| 358 | const coerced_inst = try self.coerce(block, wanted_type, new_inst); | 618 | const coerced_inst = try self.coerce(scope, wanted_type, new_inst); |
| 359 | const val = try self.resolveConstValue(coerced_inst); | 619 | const val = try self.resolveConstValue(coerced_inst); |
| 360 | return val.toType(); | 620 | return val.toType(); |
| 361 | } | 621 | } |
| 362 | 622 | ||
| 363 | fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void { | 623 | fn analyzeExport(self: *Module, scope: *Scope, export_inst: *text.Inst.Export) !void { |
| 364 | const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name); | 624 | const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name); |
| 365 | const typed_value = try self.resolveInstConst(block, export_inst.positionals.value); | 625 | const decl = try self.resolveDecl(scope, export_inst.positionals.value); |
| 366 | 626 | ||
| 367 | switch (typed_value.ty.zigTypeTag()) { | 627 | switch (decl.analysis) { |
| 368 | .Fn => {}, | 628 | .in_progress => unreachable, |
| 369 | else => return self.fail( | 629 | .failure => return error.AnalysisFail, |
| 370 | export_inst.positionals.value.src, | 630 | .success => |typed_value| switch (typed_value.ty.zigTypeTag()) { |
| 371 | "unable to export type '{}'", | 631 | .Fn => {}, |
| 372 | .{typed_value.ty}, | 632 | else => return self.fail( |
| 373 | ), | 633 | scope, |
| 634 | export_inst.positionals.value.src, | ||
| 635 | "unable to export type '{}'", | ||
| 636 | .{typed_value.ty}, | ||
| 637 | ), | ||
| 638 | }, | ||
| 374 | } | 639 | } |
| 375 | try self.exports.append(.{ | 640 | const Node = LinkedList(std.builtin.ExportOptions).Node; |
| 376 | .name = symbol_name, | 641 | export_node = try decl.arena.promote(self.allocator).allocator.create(Node); |
| 377 | .typed_value = typed_value, | 642 | export_node.* = .{ .data = .{ .name = symbol_name } }; |
| 378 | .src = export_inst.base.src, | 643 | decl.export_node = export_node; |
| 379 | }); | 644 | |
| 645 | // TODO Avoid double update in the case of exporting a decl that we just created. | ||
| 646 | self.bin_file.updateDeclExports(); | ||
| 380 | } | 647 | } |
| 381 | 648 | ||
| 382 | /// TODO should not need the cast on the last parameter at the callsites | 649 | /// TODO should not need the cast on the last parameter at the callsites |
| 383 | fn addNewInstArgs( | 650 | fn addNewInstArgs( |
| 384 | self: *Analyze, | 651 | self: *Module, |
| 385 | block: *Block, | 652 | block: *Scope.Block, |
| 386 | src: usize, | 653 | src: usize, |
| 387 | ty: Type, | 654 | ty: Type, |
| 388 | comptime T: type, | 655 | comptime T: type, |
| ... | @@ -393,7 +660,7 @@ const Analyze = struct { | ... | @@ -393,7 +660,7 @@ const Analyze = struct { |
| 393 | return &inst.base; | 660 | return &inst.base; |
| 394 | } | 661 | } |
| 395 | 662 | ||
| 396 | fn addNewInst(self: *Analyze, block: *Block, src: usize, ty: Type, comptime T: type) !*T { | 663 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { |
| 397 | const inst = try self.arena.allocator.create(T); | 664 | const inst = try self.arena.allocator.create(T); |
| 398 | inst.* = .{ | 665 | inst.* = .{ |
| 399 | .base = .{ | 666 | .base = .{ |
| ... | @@ -403,11 +670,11 @@ const Analyze = struct { | ... | @@ -403,11 +670,11 @@ const Analyze = struct { |
| 403 | }, | 670 | }, |
| 404 | .args = undefined, | 671 | .args = undefined, |
| 405 | }; | 672 | }; |
| 406 | try block.instructions.append(&inst.base); | 673 | try block.instructions.append(self.allocator, &inst.base); |
| 407 | return inst; | 674 | return inst; |
| 408 | } | 675 | } |
| 409 | 676 | ||
| 410 | fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst { | 677 | fn constInst(self: *Module, src: usize, typed_value: TypedValue) !*Inst { |
| 411 | const const_inst = try self.arena.allocator.create(Inst.Constant); | 678 | const const_inst = try self.arena.allocator.create(Inst.Constant); |
| 412 | const_inst.* = .{ | 679 | const_inst.* = .{ |
| 413 | .base = .{ | 680 | .base = .{ |
| ... | @@ -420,7 +687,7 @@ const Analyze = struct { | ... | @@ -420,7 +687,7 @@ const Analyze = struct { |
| 420 | return &const_inst.base; | 687 | return &const_inst.base; |
| 421 | } | 688 | } |
| 422 | 689 | ||
| 423 | fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst { | 690 | fn constStr(self: *Module, src: usize, str: []const u8) !*Inst { |
| 424 | const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0); | 691 | const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0); |
| 425 | array_payload.* = .{ .len = str.len }; | 692 | array_payload.* = .{ .len = str.len }; |
| 426 | 693 | ||
| ... | @@ -436,35 +703,35 @@ const Analyze = struct { | ... | @@ -436,35 +703,35 @@ const Analyze = struct { |
| 436 | }); | 703 | }); |
| 437 | } | 704 | } |
| 438 | 705 | ||
| 439 | fn constType(self: *Analyze, src: usize, ty: Type) !*Inst { | 706 | fn constType(self: *Module, src: usize, ty: Type) !*Inst { |
| 440 | return self.constInst(src, .{ | 707 | return self.constInst(src, .{ |
| 441 | .ty = Type.initTag(.type), | 708 | .ty = Type.initTag(.type), |
| 442 | .val = try ty.toValue(&self.arena.allocator), | 709 | .val = try ty.toValue(&self.arena.allocator), |
| 443 | }); | 710 | }); |
| 444 | } | 711 | } |
| 445 | 712 | ||
| 446 | fn constVoid(self: *Analyze, src: usize) !*Inst { | 713 | fn constVoid(self: *Module, src: usize) !*Inst { |
| 447 | return self.constInst(src, .{ | 714 | return self.constInst(src, .{ |
| 448 | .ty = Type.initTag(.void), | 715 | .ty = Type.initTag(.void), |
| 449 | .val = Value.initTag(.the_one_possible_value), | 716 | .val = Value.initTag(.the_one_possible_value), |
| 450 | }); | 717 | }); |
| 451 | } | 718 | } |
| 452 | 719 | ||
| 453 | fn constUndef(self: *Analyze, src: usize, ty: Type) !*Inst { | 720 | fn constUndef(self: *Module, src: usize, ty: Type) !*Inst { |
| 454 | return self.constInst(src, .{ | 721 | return self.constInst(src, .{ |
| 455 | .ty = ty, | 722 | .ty = ty, |
| 456 | .val = Value.initTag(.undef), | 723 | .val = Value.initTag(.undef), |
| 457 | }); | 724 | }); |
| 458 | } | 725 | } |
| 459 | 726 | ||
| 460 | fn constBool(self: *Analyze, src: usize, v: bool) !*Inst { | 727 | fn constBool(self: *Module, src: usize, v: bool) !*Inst { |
| 461 | return self.constInst(src, .{ | 728 | return self.constInst(src, .{ |
| 462 | .ty = Type.initTag(.bool), | 729 | .ty = Type.initTag(.bool), |
| 463 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], | 730 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], |
| 464 | }); | 731 | }); |
| 465 | } | 732 | } |
| 466 | 733 | ||
| 467 | fn constIntUnsigned(self: *Analyze, src: usize, ty: Type, int: u64) !*Inst { | 734 | fn constIntUnsigned(self: *Module, src: usize, ty: Type, int: u64) !*Inst { |
| 468 | const int_payload = try self.arena.allocator.create(Value.Payload.Int_u64); | 735 | const int_payload = try self.arena.allocator.create(Value.Payload.Int_u64); |
| 469 | int_payload.* = .{ .int = int }; | 736 | int_payload.* = .{ .int = int }; |
| 470 | 737 | ||
| ... | @@ -474,7 +741,7 @@ const Analyze = struct { | ... | @@ -474,7 +741,7 @@ const Analyze = struct { |
| 474 | }); | 741 | }); |
| 475 | } | 742 | } |
| 476 | 743 | ||
| 477 | fn constIntSigned(self: *Analyze, src: usize, ty: Type, int: i64) !*Inst { | 744 | fn constIntSigned(self: *Module, src: usize, ty: Type, int: i64) !*Inst { |
| 478 | const int_payload = try self.arena.allocator.create(Value.Payload.Int_i64); | 745 | const int_payload = try self.arena.allocator.create(Value.Payload.Int_i64); |
| 479 | int_payload.* = .{ .int = int }; | 746 | int_payload.* = .{ .int = int }; |
| 480 | 747 | ||
| ... | @@ -484,7 +751,7 @@ const Analyze = struct { | ... | @@ -484,7 +751,7 @@ const Analyze = struct { |
| 484 | }); | 751 | }); |
| 485 | } | 752 | } |
| 486 | 753 | ||
| 487 | fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | 754 | fn constIntBig(self: *Module, src: usize, ty: Type, big_int: BigIntConst) !*Inst { |
| 488 | const val_payload = if (big_int.positive) blk: { | 755 | const val_payload = if (big_int.positive) blk: { |
| 489 | if (big_int.to(u64)) |x| { | 756 | if (big_int.to(u64)) |x| { |
| 490 | return self.constIntUnsigned(src, ty, x); | 757 | return self.constIntUnsigned(src, ty, x); |
| ... | @@ -513,9 +780,18 @@ const Analyze = struct { | ... | @@ -513,9 +780,18 @@ const Analyze = struct { |
| 513 | }); | 780 | }); |
| 514 | } | 781 | } |
| 515 | 782 | ||
| 516 | fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { | 783 | fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!TypedValue { |
| 784 | const new_inst = try self.analyzeInst(scope, old_inst); | ||
| 785 | return TypedValue{ | ||
| 786 | .ty = new_inst.ty, | ||
| 787 | .val = try self.resolveConstValue(scope, new_inst), | ||
| 788 | }; | ||
| 789 | } | ||
| 790 | |||
| 791 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Inst { | ||
| 517 | switch (old_inst.tag) { | 792 | switch (old_inst.tag) { |
| 518 | .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?), | 793 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(text.Inst.Breakpoint).?), |
| 794 | .call => return self.analyzeInstCall(scope, old_inst.cast(text.Inst.Call).?), | ||
| 519 | .str => { | 795 | .str => { |
| 520 | // We can use this reference because Inst.Const's Value is arena-allocated. | 796 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| 521 | // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. | 797 | // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. |
| ... | @@ -526,53 +802,118 @@ const Analyze = struct { | ... | @@ -526,53 +802,118 @@ const Analyze = struct { |
| 526 | const big_int = old_inst.cast(text.Inst.Int).?.positionals.int; | 802 | const big_int = old_inst.cast(text.Inst.Int).?.positionals.int; |
| 527 | return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int); | 803 | return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int); |
| 528 | }, | 804 | }, |
| 529 | .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?), | 805 | .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(text.Inst.PtrToInt).?), |
| 530 | .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?), | 806 | .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(text.Inst.FieldPtr).?), |
| 531 | .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?), | 807 | .deref => return self.analyzeInstDeref(scope, old_inst.cast(text.Inst.Deref).?), |
| 532 | .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?), | 808 | .as => return self.analyzeInstAs(scope, old_inst.cast(text.Inst.As).?), |
| 533 | .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?), | 809 | .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(text.Inst.Asm).?), |
| 534 | .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?), | 810 | .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(text.Inst.Unreachable).?), |
| 535 | .@"return" => return self.analyzeInstRet(block, old_inst.cast(text.Inst.Return).?), | 811 | .@"return" => return self.analyzeInstRet(scope, old_inst.cast(text.Inst.Return).?), |
| 536 | .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?), | 812 | // TODO postpone function analysis until later |
| 813 | .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(text.Inst.Fn).?), | ||
| 537 | .@"export" => { | 814 | .@"export" => { |
| 538 | try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?); | 815 | try self.analyzeExport(scope, old_inst.cast(text.Inst.Export).?); |
| 539 | return self.constVoid(old_inst.src); | 816 | return self.constVoid(old_inst.src); |
| 540 | }, | 817 | }, |
| 541 | .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?), | 818 | .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?), |
| 542 | .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?), | 819 | .fntype => return self.analyzeInstFnType(scope, old_inst.cast(text.Inst.FnType).?), |
| 543 | .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?), | 820 | .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(text.Inst.IntCast).?), |
| 544 | .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?), | 821 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(text.Inst.BitCast).?), |
| 545 | .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?), | 822 | .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(text.Inst.ElemPtr).?), |
| 546 | .add => return self.analyzeInstAdd(block, old_inst.cast(text.Inst.Add).?), | 823 | .add => return self.analyzeInstAdd(scope, old_inst.cast(text.Inst.Add).?), |
| 547 | .cmp => return self.analyzeInstCmp(block, old_inst.cast(text.Inst.Cmp).?), | 824 | .cmp => return self.analyzeInstCmp(scope, old_inst.cast(text.Inst.Cmp).?), |
| 548 | .condbr => return self.analyzeInstCondBr(block, old_inst.cast(text.Inst.CondBr).?), | 825 | .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(text.Inst.CondBr).?), |
| 549 | .isnull => return self.analyzeInstIsNull(block, old_inst.cast(text.Inst.IsNull).?), | 826 | .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(text.Inst.IsNull).?), |
| 550 | .isnonnull => return self.analyzeInstIsNonNull(block, old_inst.cast(text.Inst.IsNonNull).?), | 827 | .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(text.Inst.IsNonNull).?), |
| 551 | } | 828 | } |
| 552 | } | 829 | } |
| 553 | 830 | ||
| 554 | fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst { | 831 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *text.Inst.Breakpoint) InnerError!*Inst { |
| 555 | const b = try self.requireRuntimeBlock(block, inst.base.src); | 832 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 556 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); | 833 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); |
| 557 | } | 834 | } |
| 558 | 835 | ||
| 559 | fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst { | 836 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *text.Inst.Call) InnerError!*Inst { |
| 560 | const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type); | 837 | const func = try self.resolveInst(scope, inst.positionals.func); |
| 838 | if (func.ty.zigTypeTag() != .Fn) | ||
| 839 | return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty}); | ||
| 840 | |||
| 841 | const cc = func.ty.fnCallingConvention(); | ||
| 842 | if (cc == .Naked) { | ||
| 843 | // TODO add error note: declared here | ||
| 844 | return self.fail( | ||
| 845 | scope, | ||
| 846 | inst.positionals.func.src, | ||
| 847 | "unable to call function with naked calling convention", | ||
| 848 | .{}, | ||
| 849 | ); | ||
| 850 | } | ||
| 851 | const call_params_len = inst.positionals.args.len; | ||
| 852 | const fn_params_len = func.ty.fnParamLen(); | ||
| 853 | if (func.ty.fnIsVarArgs()) { | ||
| 854 | if (call_params_len < fn_params_len) { | ||
| 855 | // TODO add error note: declared here | ||
| 856 | return self.fail( | ||
| 857 | scope, | ||
| 858 | inst.positionals.func.src, | ||
| 859 | "expected at least {} arguments, found {}", | ||
| 860 | .{ fn_params_len, call_params_len }, | ||
| 861 | ); | ||
| 862 | } | ||
| 863 | return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{}); | ||
| 864 | } else if (fn_params_len != call_params_len) { | ||
| 865 | // TODO add error note: declared here | ||
| 866 | return self.fail( | ||
| 867 | scope, | ||
| 868 | inst.positionals.func.src, | ||
| 869 | "expected {} arguments, found {}", | ||
| 870 | .{ fn_params_len, call_params_len }, | ||
| 871 | ); | ||
| 872 | } | ||
| 873 | |||
| 874 | if (inst.kw_args.modifier == .compile_time) { | ||
| 875 | return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{}); | ||
| 876 | } | ||
| 877 | if (inst.kw_args.modifier != .auto) { | ||
| 878 | return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier}); | ||
| 879 | } | ||
| 880 | |||
| 881 | // TODO handle function calls of generic functions | ||
| 882 | |||
| 883 | const fn_param_types = try self.allocator.alloc(Type, fn_params_len); | ||
| 884 | defer self.allocator.free(fn_param_types); | ||
| 885 | func.ty.fnParamTypes(fn_param_types); | ||
| 886 | |||
| 887 | const casted_args = try self.arena.allocator.alloc(*Inst, fn_params_len); | ||
| 888 | for (inst.positionals.args) |src_arg, i| { | ||
| 889 | const uncasted_arg = try self.resolveInst(scope, src_arg); | ||
| 890 | casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg); | ||
| 891 | } | ||
| 892 | |||
| 893 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | ||
| 894 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){ | ||
| 895 | .func = func, | ||
| 896 | .args = casted_args, | ||
| 897 | }); | ||
| 898 | } | ||
| 899 | |||
| 900 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *text.Inst.Fn) InnerError!*Inst { | ||
| 901 | const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type); | ||
| 561 | 902 | ||
| 562 | var new_func: Fn = .{ | 903 | var new_func: Fn = .{ |
| 563 | .fn_index = self.fns.items.len, | 904 | .fn_index = self.fns.items.len, |
| 564 | .inner_block = .{ | 905 | .inner_block = .{ |
| 565 | .func = undefined, | 906 | .func = undefined, |
| 566 | .instructions = std.ArrayList(*Inst).init(self.allocator), | 907 | .instructions = .{}, |
| 567 | }, | 908 | }, |
| 568 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | 909 | .inst_table = std.AutoHashMap(*text.Inst, ?*Inst).init(self.allocator), |
| 569 | }; | 910 | }; |
| 570 | new_func.inner_block.func = &new_func; | 911 | new_func.inner_block.func = &new_func; |
| 571 | defer new_func.inner_block.instructions.deinit(); | 912 | defer new_func.inner_block.instructions.deinit(); |
| 572 | defer new_func.inst_table.deinit(); | 913 | defer new_func.inst_table.deinit(); |
| 573 | // Don't hang on to a reference to this when analyzing body instructions, since the memory | 914 | // Don't hang on to a reference to this when analyzing body instructions, since the memory |
| 574 | // could become invalid. | 915 | // could become invalid. |
| 575 | (try self.fns.addOne()).* = .{ | 916 | (try self.fns.addOne(self.allocator)).* = .{ |
| 576 | .analysis_status = .in_progress, | 917 | .analysis_status = .in_progress, |
| 577 | .fn_type = fn_type, | 918 | .fn_type = fn_type, |
| 578 | .body = undefined, | 919 | .body = undefined, |
| ... | @@ -593,8 +934,15 @@ const Analyze = struct { | ... | @@ -593,8 +934,15 @@ const Analyze = struct { |
| 593 | }); | 934 | }); |
| 594 | } | 935 | } |
| 595 | 936 | ||
| 596 | fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst { | 937 | fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *text.Inst.FnType) InnerError!*Inst { |
| 597 | const return_type = try self.resolveType(block, fntype.positionals.return_type); | 938 | const return_type = try self.resolveType(scope, fntype.positionals.return_type); |
| 939 | |||
| 940 | if (return_type.zigTypeTag() == .NoReturn and | ||
| 941 | fntype.positionals.param_types.len == 0 and | ||
| 942 | fntype.kw_args.cc == .Unspecified) | ||
| 943 | { | ||
| 944 | return self.constType(fntype.base.src, Type.initTag(.fn_noreturn_no_args)); | ||
| 945 | } | ||
| 598 | 946 | ||
| 599 | if (return_type.zigTypeTag() == .NoReturn and | 947 | if (return_type.zigTypeTag() == .NoReturn and |
| 600 | fntype.positionals.param_types.len == 0 and | 948 | fntype.positionals.param_types.len == 0 and |
| ... | @@ -610,37 +958,37 @@ const Analyze = struct { | ... | @@ -610,37 +958,37 @@ const Analyze = struct { |
| 610 | return self.constType(fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); | 958 | return self.constType(fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); |
| 611 | } | 959 | } |
| 612 | 960 | ||
| 613 | return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{}); | 961 | return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{}); |
| 614 | } | 962 | } |
| 615 | 963 | ||
| 616 | fn analyzeInstPrimitive(self: *Analyze, primitive: *text.Inst.Primitive) InnerError!*Inst { | 964 | fn analyzeInstPrimitive(self: *Module, primitive: *text.Inst.Primitive) InnerError!*Inst { |
| 617 | return self.constType(primitive.base.src, primitive.positionals.tag.toType()); | 965 | return self.constType(primitive.base.src, primitive.positionals.tag.toType()); |
| 618 | } | 966 | } |
| 619 | 967 | ||
| 620 | fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst { | 968 | fn analyzeInstAs(self: *Module, scope: *Scope, as: *text.Inst.As) InnerError!*Inst { |
| 621 | const dest_type = try self.resolveType(block, as.positionals.dest_type); | 969 | const dest_type = try self.resolveType(scope, as.positionals.dest_type); |
| 622 | const new_inst = try self.resolveInst(block, as.positionals.value); | 970 | const new_inst = try self.resolveInst(scope, as.positionals.value); |
| 623 | return self.coerce(block, dest_type, new_inst); | 971 | return self.coerce(scope, dest_type, new_inst); |
| 624 | } | 972 | } |
| 625 | 973 | ||
| 626 | fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { | 974 | fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { |
| 627 | const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr); | 975 | const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr); |
| 628 | if (ptr.ty.zigTypeTag() != .Pointer) { | 976 | if (ptr.ty.zigTypeTag() != .Pointer) { |
| 629 | return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}); | 977 | return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}); |
| 630 | } | 978 | } |
| 631 | // TODO handle known-pointer-address | 979 | // TODO handle known-pointer-address |
| 632 | const b = try self.requireRuntimeBlock(block, ptrtoint.base.src); | 980 | const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src); |
| 633 | const ty = Type.initTag(.usize); | 981 | const ty = Type.initTag(.usize); |
| 634 | return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); | 982 | return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); |
| 635 | } | 983 | } |
| 636 | 984 | ||
| 637 | fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { | 985 | fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { |
| 638 | const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr); | 986 | const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr); |
| 639 | const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name); | 987 | const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name); |
| 640 | 988 | ||
| 641 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | 989 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { |
| 642 | .Pointer => object_ptr.ty.elemType(), | 990 | .Pointer => object_ptr.ty.elemType(), |
| 643 | else => return self.fail(fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | 991 | else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), |
| 644 | }; | 992 | }; |
| 645 | switch (elem_ty.zigTypeTag()) { | 993 | switch (elem_ty.zigTypeTag()) { |
| 646 | .Array => { | 994 | .Array => { |
| ... | @@ -657,24 +1005,26 @@ const Analyze = struct { | ... | @@ -657,24 +1005,26 @@ const Analyze = struct { |
| 657 | }); | 1005 | }); |
| 658 | } else { | 1006 | } else { |
| 659 | return self.fail( | 1007 | return self.fail( |
| 1008 | scope, | ||
| 660 | fieldptr.positionals.field_name.src, | 1009 | fieldptr.positionals.field_name.src, |
| 661 | "no member named '{}' in '{}'", | 1010 | "no member named '{}' in '{}'", |
| 662 | .{ field_name, elem_ty }, | 1011 | .{ field_name, elem_ty }, |
| 663 | ); | 1012 | ); |
| 664 | } | 1013 | } |
| 665 | }, | 1014 | }, |
| 666 | else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), | 1015 | else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), |
| 667 | } | 1016 | } |
| 668 | } | 1017 | } |
| 669 | 1018 | ||
| 670 | fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst { | 1019 | fn analyzeInstIntCast(self: *Module, scope: *Scope, intcast: *text.Inst.IntCast) InnerError!*Inst { |
| 671 | const dest_type = try self.resolveType(block, intcast.positionals.dest_type); | 1020 | const dest_type = try self.resolveType(scope, intcast.positionals.dest_type); |
| 672 | const new_inst = try self.resolveInst(block, intcast.positionals.value); | 1021 | const new_inst = try self.resolveInst(scope, intcast.positionals.value); |
| 673 | 1022 | ||
| 674 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | 1023 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 675 | .ComptimeInt => true, | 1024 | .ComptimeInt => true, |
| 676 | .Int => false, | 1025 | .Int => false, |
| 677 | else => return self.fail( | 1026 | else => return self.fail( |
| 1027 | scope, | ||
| 678 | intcast.positionals.dest_type.src, | 1028 | intcast.positionals.dest_type.src, |
| 679 | "expected integer type, found '{}'", | 1029 | "expected integer type, found '{}'", |
| 680 | .{ | 1030 | .{ |
| ... | @@ -686,6 +1036,7 @@ const Analyze = struct { | ... | @@ -686,6 +1036,7 @@ const Analyze = struct { |
| 686 | switch (new_inst.ty.zigTypeTag()) { | 1036 | switch (new_inst.ty.zigTypeTag()) { |
| 687 | .ComptimeInt, .Int => {}, | 1037 | .ComptimeInt, .Int => {}, |
| 688 | else => return self.fail( | 1038 | else => return self.fail( |
| 1039 | scope, | ||
| 689 | intcast.positionals.value.src, | 1040 | intcast.positionals.value.src, |
| 690 | "expected integer type, found '{}'", | 1041 | "expected integer type, found '{}'", |
| 691 | .{new_inst.ty}, | 1042 | .{new_inst.ty}, |
| ... | @@ -693,22 +1044,22 @@ const Analyze = struct { | ... | @@ -693,22 +1044,22 @@ const Analyze = struct { |
| 693 | } | 1044 | } |
| 694 | 1045 | ||
| 695 | if (dest_is_comptime_int or new_inst.value() != null) { | 1046 | if (dest_is_comptime_int or new_inst.value() != null) { |
| 696 | return self.coerce(block, dest_type, new_inst); | 1047 | return self.coerce(scope, dest_type, new_inst); |
| 697 | } | 1048 | } |
| 698 | 1049 | ||
| 699 | return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{}); | 1050 | return self.fail(scope, intcast.base.src, "TODO implement analyze widen or shorten int", .{}); |
| 700 | } | 1051 | } |
| 701 | 1052 | ||
| 702 | fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst { | 1053 | fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *text.Inst.BitCast) InnerError!*Inst { |
| 703 | const dest_type = try self.resolveType(block, inst.positionals.dest_type); | 1054 | const dest_type = try self.resolveType(scope, inst.positionals.dest_type); |
| 704 | const operand = try self.resolveInst(block, inst.positionals.operand); | 1055 | const operand = try self.resolveInst(scope, inst.positionals.operand); |
| 705 | return self.bitcast(block, dest_type, operand); | 1056 | return self.bitcast(scope, dest_type, operand); |
| 706 | } | 1057 | } |
| 707 | 1058 | ||
| 708 | fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst { | 1059 | fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *text.Inst.ElemPtr) InnerError!*Inst { |
| 709 | const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr); | 1060 | const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr); |
| 710 | const uncasted_index = try self.resolveInst(block, inst.positionals.index); | 1061 | const uncasted_index = try self.resolveInst(scope, inst.positionals.index); |
| 711 | const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index); | 1062 | const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index); |
| 712 | 1063 | ||
| 713 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { | 1064 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { |
| 714 | if (array_ptr.value()) |array_ptr_val| { | 1065 | if (array_ptr.value()) |array_ptr_val| { |
| ... | @@ -717,28 +1068,25 @@ const Analyze = struct { | ... | @@ -717,28 +1068,25 @@ const Analyze = struct { |
| 717 | const index_u64 = index_val.toUnsignedInt(); | 1068 | const index_u64 = index_val.toUnsignedInt(); |
| 718 | // @intCast here because it would have been impossible to construct a value that | 1069 | // @intCast here because it would have been impossible to construct a value that |
| 719 | // required a larger index. | 1070 | // required a larger index. |
| 720 | const elem_val = try array_ptr_val.elemValueAt(&self.arena.allocator, @intCast(usize, index_u64)); | 1071 | const elem_ptr = try array_ptr_val.elemPtr(&self.arena.allocator, @intCast(usize, index_u64)); |
| 721 | |||
| 722 | const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal); | ||
| 723 | ref_payload.* = .{ .val = elem_val }; | ||
| 724 | 1072 | ||
| 725 | const type_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer); | 1073 | const type_payload = try self.arena.allocator.create(Type.Payload.SingleConstPointer); |
| 726 | type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() }; | 1074 | type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() }; |
| 727 | 1075 | ||
| 728 | return self.constInst(inst.base.src, .{ | 1076 | return self.constInst(inst.base.src, .{ |
| 729 | .ty = Type.initPayload(&type_payload.base), | 1077 | .ty = Type.initPayload(&type_payload.base), |
| 730 | .val = Value.initPayload(&ref_payload.base), | 1078 | .val = elem_ptr, |
| 731 | }); | 1079 | }); |
| 732 | } | 1080 | } |
| 733 | } | 1081 | } |
| 734 | } | 1082 | } |
| 735 | 1083 | ||
| 736 | return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{}); | 1084 | return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{}); |
| 737 | } | 1085 | } |
| 738 | 1086 | ||
| 739 | fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst { | 1087 | fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *text.Inst.Add) InnerError!*Inst { |
| 740 | const lhs = try self.resolveInst(block, inst.positionals.lhs); | 1088 | const lhs = try self.resolveInst(scope, inst.positionals.lhs); |
| 741 | const rhs = try self.resolveInst(block, inst.positionals.rhs); | 1089 | const rhs = try self.resolveInst(scope, inst.positionals.rhs); |
| 742 | 1090 | ||
| 743 | if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { | 1091 | if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { |
| 744 | if (lhs.value()) |lhs_val| { | 1092 | if (lhs.value()) |lhs_val| { |
| ... | @@ -758,7 +1106,7 @@ const Analyze = struct { | ... | @@ -758,7 +1106,7 @@ const Analyze = struct { |
| 758 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | 1106 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; |
| 759 | 1107 | ||
| 760 | if (!lhs.ty.eql(rhs.ty)) { | 1108 | if (!lhs.ty.eql(rhs.ty)) { |
| 761 | return self.fail(inst.base.src, "TODO implement peer type resolution", .{}); | 1109 | return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{}); |
| 762 | } | 1110 | } |
| 763 | 1111 | ||
| 764 | const val_payload = if (result_bigint.positive) blk: { | 1112 | const val_payload = if (result_bigint.positive) blk: { |
| ... | @@ -779,14 +1127,14 @@ const Analyze = struct { | ... | @@ -779,14 +1127,14 @@ const Analyze = struct { |
| 779 | } | 1127 | } |
| 780 | } | 1128 | } |
| 781 | 1129 | ||
| 782 | return self.fail(inst.base.src, "TODO implement more analyze add", .{}); | 1130 | return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{}); |
| 783 | } | 1131 | } |
| 784 | 1132 | ||
| 785 | fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst { | 1133 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *text.Inst.Deref) InnerError!*Inst { |
| 786 | const ptr = try self.resolveInst(block, deref.positionals.ptr); | 1134 | const ptr = try self.resolveInst(scope, deref.positionals.ptr); |
| 787 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | 1135 | const elem_ty = switch (ptr.ty.zigTypeTag()) { |
| 788 | .Pointer => ptr.ty.elemType(), | 1136 | .Pointer => ptr.ty.elemType(), |
| 789 | else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}), | 1137 | else => return self.fail(scope, deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}), |
| 790 | }; | 1138 | }; |
| 791 | if (ptr.value()) |val| { | 1139 | if (ptr.value()) |val| { |
| 792 | return self.constInst(deref.base.src, .{ | 1140 | return self.constInst(deref.base.src, .{ |
| ... | @@ -795,30 +1143,30 @@ const Analyze = struct { | ... | @@ -795,30 +1143,30 @@ const Analyze = struct { |
| 795 | }); | 1143 | }); |
| 796 | } | 1144 | } |
| 797 | 1145 | ||
| 798 | return self.fail(deref.base.src, "TODO implement runtime deref", .{}); | 1146 | return self.fail(scope, deref.base.src, "TODO implement runtime deref", .{}); |
| 799 | } | 1147 | } |
| 800 | 1148 | ||
| 801 | fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst { | 1149 | fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *text.Inst.Asm) InnerError!*Inst { |
| 802 | const return_type = try self.resolveType(block, assembly.positionals.return_type); | 1150 | const return_type = try self.resolveType(scope, assembly.positionals.return_type); |
| 803 | const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source); | 1151 | const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source); |
| 804 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null; | 1152 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null; |
| 805 | 1153 | ||
| 806 | const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len); | 1154 | const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len); |
| 807 | const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len); | 1155 | const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len); |
| 808 | const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len); | 1156 | const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len); |
| 809 | 1157 | ||
| 810 | for (inputs) |*elem, i| { | 1158 | for (inputs) |*elem, i| { |
| 811 | elem.* = try self.resolveConstString(block, assembly.kw_args.inputs[i]); | 1159 | elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]); |
| 812 | } | 1160 | } |
| 813 | for (clobbers) |*elem, i| { | 1161 | for (clobbers) |*elem, i| { |
| 814 | elem.* = try self.resolveConstString(block, assembly.kw_args.clobbers[i]); | 1162 | elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]); |
| 815 | } | 1163 | } |
| 816 | for (args) |*elem, i| { | 1164 | for (args) |*elem, i| { |
| 817 | const arg = try self.resolveInst(block, assembly.kw_args.args[i]); | 1165 | const arg = try self.resolveInst(scope, assembly.kw_args.args[i]); |
| 818 | elem.* = try self.coerce(block, Type.initTag(.usize), arg); | 1166 | elem.* = try self.coerce(scope, Type.initTag(.usize), arg); |
| 819 | } | 1167 | } |
| 820 | 1168 | ||
| 821 | const b = try self.requireRuntimeBlock(block, assembly.base.src); | 1169 | const b = try self.requireRuntimeBlock(scope, assembly.base.src); |
| 822 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | 1170 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ |
| 823 | .asm_source = asm_source, | 1171 | .asm_source = asm_source, |
| 824 | .is_volatile = assembly.kw_args.@"volatile", | 1172 | .is_volatile = assembly.kw_args.@"volatile", |
| ... | @@ -829,9 +1177,9 @@ const Analyze = struct { | ... | @@ -829,9 +1177,9 @@ const Analyze = struct { |
| 829 | }); | 1177 | }); |
| 830 | } | 1178 | } |
| 831 | 1179 | ||
| 832 | fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst { | 1180 | fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *text.Inst.Cmp) InnerError!*Inst { |
| 833 | const lhs = try self.resolveInst(block, inst.positionals.lhs); | 1181 | const lhs = try self.resolveInst(scope, inst.positionals.lhs); |
| 834 | const rhs = try self.resolveInst(block, inst.positionals.rhs); | 1182 | const rhs = try self.resolveInst(scope, inst.positionals.rhs); |
| 835 | const op = inst.positionals.op; | 1183 | const op = inst.positionals.op; |
| 836 | 1184 | ||
| 837 | const is_equality_cmp = switch (op) { | 1185 | const is_equality_cmp = switch (op) { |
| ... | @@ -853,7 +1201,7 @@ const Analyze = struct { | ... | @@ -853,7 +1201,7 @@ const Analyze = struct { |
| 853 | const is_null = opt_val.isNull(); | 1201 | const is_null = opt_val.isNull(); |
| 854 | return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null); | 1202 | return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null); |
| 855 | } | 1203 | } |
| 856 | const b = try self.requireRuntimeBlock(block, inst.base.src); | 1204 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 857 | switch (op) { | 1205 | switch (op) { |
| 858 | .eq => return self.addNewInstArgs( | 1206 | .eq => return self.addNewInstArgs( |
| 859 | b, | 1207 | b, |
| ... | @@ -874,64 +1222,64 @@ const Analyze = struct { | ... | @@ -874,64 +1222,64 @@ const Analyze = struct { |
| 874 | } else if (is_equality_cmp and | 1222 | } else if (is_equality_cmp and |
| 875 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | 1223 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) |
| 876 | { | 1224 | { |
| 877 | return self.fail(inst.base.src, "TODO implement C pointer cmp", .{}); | 1225 | return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{}); |
| 878 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | 1226 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { |
| 879 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | 1227 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; |
| 880 | return self.fail(inst.base.src, "comparison of '{}' with null", .{non_null_type}); | 1228 | return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type}); |
| 881 | } else if (is_equality_cmp and | 1229 | } else if (is_equality_cmp and |
| 882 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | 1230 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or |
| 883 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | 1231 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) |
| 884 | { | 1232 | { |
| 885 | return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | 1233 | return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); |
| 886 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | 1234 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { |
| 887 | if (!is_equality_cmp) { | 1235 | if (!is_equality_cmp) { |
| 888 | return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); | 1236 | return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); |
| 889 | } | 1237 | } |
| 890 | return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{}); | 1238 | return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{}); |
| 891 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | 1239 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { |
| 892 | // This operation allows any combination of integer and float types, regardless of the | 1240 | // This operation allows any combination of integer and float types, regardless of the |
| 893 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | 1241 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for |
| 894 | // numeric types. | 1242 | // numeric types. |
| 895 | return self.cmpNumeric(block, inst.base.src, lhs, rhs, op); | 1243 | return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op); |
| 896 | } | 1244 | } |
| 897 | return self.fail(inst.base.src, "TODO implement more cmp analysis", .{}); | 1245 | return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{}); |
| 898 | } | 1246 | } |
| 899 | 1247 | ||
| 900 | fn analyzeInstIsNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNull) InnerError!*Inst { | 1248 | fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *text.Inst.IsNull) InnerError!*Inst { |
| 901 | const operand = try self.resolveInst(block, inst.positionals.operand); | 1249 | const operand = try self.resolveInst(scope, inst.positionals.operand); |
| 902 | return self.analyzeIsNull(block, inst.base.src, operand, true); | 1250 | return self.analyzeIsNull(scope, inst.base.src, operand, true); |
| 903 | } | 1251 | } |
| 904 | 1252 | ||
| 905 | fn analyzeInstIsNonNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNonNull) InnerError!*Inst { | 1253 | fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *text.Inst.IsNonNull) InnerError!*Inst { |
| 906 | const operand = try self.resolveInst(block, inst.positionals.operand); | 1254 | const operand = try self.resolveInst(scope, inst.positionals.operand); |
| 907 | return self.analyzeIsNull(block, inst.base.src, operand, false); | 1255 | return self.analyzeIsNull(scope, inst.base.src, operand, false); |
| 908 | } | 1256 | } |
| 909 | 1257 | ||
| 910 | fn analyzeInstCondBr(self: *Analyze, block: ?*Block, inst: *text.Inst.CondBr) InnerError!*Inst { | 1258 | fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *text.Inst.CondBr) InnerError!*Inst { |
| 911 | const uncasted_cond = try self.resolveInst(block, inst.positionals.condition); | 1259 | const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition); |
| 912 | const cond = try self.coerce(block, Type.initTag(.bool), uncasted_cond); | 1260 | const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond); |
| 913 | 1261 | ||
| 914 | if (try self.resolveDefinedValue(cond)) |cond_val| { | 1262 | if (try self.resolveDefinedValue(cond)) |cond_val| { |
| 915 | const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body; | 1263 | const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body; |
| 916 | try self.analyzeBody(block, body.*); | 1264 | try self.analyzeBody(scope, body.*); |
| 917 | return self.constVoid(inst.base.src); | 1265 | return self.constVoid(inst.base.src); |
| 918 | } | 1266 | } |
| 919 | 1267 | ||
| 920 | const parent_block = try self.requireRuntimeBlock(block, inst.base.src); | 1268 | const parent_block = try self.requireRuntimeBlock(scope, inst.base.src); |
| 921 | 1269 | ||
| 922 | var true_block: Block = .{ | 1270 | var true_block: Scope.Block = .{ |
| 923 | .func = parent_block.func, | 1271 | .func = parent_block.func, |
| 924 | .instructions = std.ArrayList(*Inst).init(self.allocator), | 1272 | .instructions = .{}, |
| 925 | }; | 1273 | }; |
| 926 | defer true_block.instructions.deinit(); | 1274 | defer true_block.instructions.deinit(); |
| 927 | try self.analyzeBody(&true_block, inst.positionals.true_body); | 1275 | try self.analyzeBody(&true_block.base, inst.positionals.true_body); |
| 928 | 1276 | ||
| 929 | var false_block: Block = .{ | 1277 | var false_block: Scope.Block = .{ |
| 930 | .func = parent_block.func, | 1278 | .func = parent_block.func, |
| 931 | .instructions = std.ArrayList(*Inst).init(self.allocator), | 1279 | .instructions = .{}, |
| 932 | }; | 1280 | }; |
| 933 | defer false_block.instructions.deinit(); | 1281 | defer false_block.instructions.deinit(); |
| 934 | try self.analyzeBody(&false_block, inst.positionals.false_body); | 1282 | try self.analyzeBody(&false_block.base, inst.positionals.false_body); |
| 935 | 1283 | ||
| 936 | // Copy the instruction pointers to the arena memory | 1284 | // Copy the instruction pointers to the arena memory |
| 937 | const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len); | 1285 | const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len); |
| ... | @@ -947,7 +1295,7 @@ const Analyze = struct { | ... | @@ -947,7 +1295,7 @@ const Analyze = struct { |
| 947 | }); | 1295 | }); |
| 948 | } | 1296 | } |
| 949 | 1297 | ||
| 950 | fn wantSafety(self: *Analyze, block: ?*Block) bool { | 1298 | fn wantSafety(self: *Module, scope: *Scope) bool { |
| 951 | return switch (self.optimize_mode) { | 1299 | return switch (self.optimize_mode) { |
| 952 | .Debug => true, | 1300 | .Debug => true, |
| 953 | .ReleaseSafe => true, | 1301 | .ReleaseSafe => true, |
| ... | @@ -956,47 +1304,47 @@ const Analyze = struct { | ... | @@ -956,47 +1304,47 @@ const Analyze = struct { |
| 956 | }; | 1304 | }; |
| 957 | } | 1305 | } |
| 958 | 1306 | ||
| 959 | fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst { | 1307 | fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *text.Inst.Unreachable) InnerError!*Inst { |
| 960 | const b = try self.requireRuntimeBlock(block, unreach.base.src); | 1308 | const b = try self.requireRuntimeBlock(scope, unreach.base.src); |
| 961 | if (self.wantSafety(block)) { | 1309 | if (self.wantSafety(scope)) { |
| 962 | // TODO Once we have a panic function to call, call it here instead of this. | 1310 | // TODO Once we have a panic function to call, call it here instead of this. |
| 963 | _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {}); | 1311 | _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {}); |
| 964 | } | 1312 | } |
| 965 | return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); | 1313 | return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); |
| 966 | } | 1314 | } |
| 967 | 1315 | ||
| 968 | fn analyzeInstRet(self: *Analyze, block: ?*Block, inst: *text.Inst.Return) InnerError!*Inst { | 1316 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *text.Inst.Return) InnerError!*Inst { |
| 969 | const b = try self.requireRuntimeBlock(block, inst.base.src); | 1317 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 970 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {}); | 1318 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {}); |
| 971 | } | 1319 | } |
| 972 | 1320 | ||
| 973 | fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void { | 1321 | fn analyzeBody(self: *Module, scope: *Scope, body: text.Module.Body) !void { |
| 974 | for (body.instructions) |src_inst| { | 1322 | for (body.instructions) |src_inst| { |
| 975 | const new_inst = self.analyzeInst(block, src_inst) catch |err| { | 1323 | const new_inst = self.analyzeInst(scope, src_inst) catch |err| { |
| 976 | if (block) |b| { | 1324 | if (scope.cast(Scope.Block)) |b| { |
| 977 | self.fns.items[b.func.fn_index].analysis_status = .failure; | 1325 | self.fns.items[b.func.fn_index].analysis_status = .failure; |
| 978 | try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null }); | 1326 | try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null }); |
| 979 | } | 1327 | } |
| 980 | return err; | 1328 | return err; |
| 981 | }; | 1329 | }; |
| 982 | if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | 1330 | if (scope.cast(Scope.Block)) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); |
| 983 | } | 1331 | } |
| 984 | } | 1332 | } |
| 985 | 1333 | ||
| 986 | fn analyzeIsNull( | 1334 | fn analyzeIsNull( |
| 987 | self: *Analyze, | 1335 | self: *Module, |
| 988 | block: ?*Block, | 1336 | scope: *Scope, |
| 989 | src: usize, | 1337 | src: usize, |
| 990 | operand: *Inst, | 1338 | operand: *Inst, |
| 991 | invert_logic: bool, | 1339 | invert_logic: bool, |
| 992 | ) InnerError!*Inst { | 1340 | ) InnerError!*Inst { |
| 993 | return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{}); | 1341 | return self.fail(scope, src, "TODO implement analysis of isnull and isnotnull", .{}); |
| 994 | } | 1342 | } |
| 995 | 1343 | ||
| 996 | /// Asserts that lhs and rhs types are both numeric. | 1344 | /// Asserts that lhs and rhs types are both numeric. |
| 997 | fn cmpNumeric( | 1345 | fn cmpNumeric( |
| 998 | self: *Analyze, | 1346 | self: *Module, |
| 999 | block: ?*Block, | 1347 | scope: *Scope, |
| 1000 | src: usize, | 1348 | src: usize, |
| 1001 | lhs: *Inst, | 1349 | lhs: *Inst, |
| 1002 | rhs: *Inst, | 1350 | rhs: *Inst, |
| ... | @@ -1010,14 +1358,14 @@ const Analyze = struct { | ... | @@ -1010,14 +1358,14 @@ const Analyze = struct { |
| 1010 | 1358 | ||
| 1011 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | 1359 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { |
| 1012 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | 1360 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { |
| 1013 | return self.fail(src, "vector length mismatch: {} and {}", .{ | 1361 | return self.fail(scope, src, "vector length mismatch: {} and {}", .{ |
| 1014 | lhs.ty.arrayLen(), | 1362 | lhs.ty.arrayLen(), |
| 1015 | rhs.ty.arrayLen(), | 1363 | rhs.ty.arrayLen(), |
| 1016 | }); | 1364 | }); |
| 1017 | } | 1365 | } |
| 1018 | return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{}); | 1366 | return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{}); |
| 1019 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | 1367 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { |
| 1020 | return self.fail(src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | 1368 | return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ |
| 1021 | lhs.ty, | 1369 | lhs.ty, |
| 1022 | rhs.ty, | 1370 | rhs.ty, |
| 1023 | }); | 1371 | }); |
| ... | @@ -1036,7 +1384,7 @@ const Analyze = struct { | ... | @@ -1036,7 +1384,7 @@ const Analyze = struct { |
| 1036 | // of this function if we don't need to. | 1384 | // of this function if we don't need to. |
| 1037 | 1385 | ||
| 1038 | // It must be a runtime comparison. | 1386 | // It must be a runtime comparison. |
| 1039 | const b = try self.requireRuntimeBlock(block, src); | 1387 | const b = try self.requireRuntimeBlock(scope, src); |
| 1040 | // For floats, emit a float comparison instruction. | 1388 | // For floats, emit a float comparison instruction. |
| 1041 | const lhs_is_float = switch (lhs_ty_tag) { | 1389 | const lhs_is_float = switch (lhs_ty_tag) { |
| 1042 | .Float, .ComptimeFloat => true, | 1390 | .Float, .ComptimeFloat => true, |
| ... | @@ -1054,14 +1402,14 @@ const Analyze = struct { | ... | @@ -1054,14 +1402,14 @@ const Analyze = struct { |
| 1054 | } else if (rhs_ty_tag == .ComptimeFloat) { | 1402 | } else if (rhs_ty_tag == .ComptimeFloat) { |
| 1055 | break :x lhs.ty; | 1403 | break :x lhs.ty; |
| 1056 | } | 1404 | } |
| 1057 | if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) { | 1405 | if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) { |
| 1058 | break :x lhs.ty; | 1406 | break :x lhs.ty; |
| 1059 | } else { | 1407 | } else { |
| 1060 | break :x rhs.ty; | 1408 | break :x rhs.ty; |
| 1061 | } | 1409 | } |
| 1062 | }; | 1410 | }; |
| 1063 | const casted_lhs = try self.coerce(block, dest_type, lhs); | 1411 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 1064 | const casted_rhs = try self.coerce(block, dest_type, rhs); | 1412 | const casted_rhs = try self.coerce(scope, dest_type, rhs); |
| 1065 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | 1413 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ |
| 1066 | .lhs = casted_lhs, | 1414 | .lhs = casted_lhs, |
| 1067 | .rhs = casted_rhs, | 1415 | .rhs = casted_rhs, |
| ... | @@ -1117,7 +1465,7 @@ const Analyze = struct { | ... | @@ -1117,7 +1465,7 @@ const Analyze = struct { |
| 1117 | } else if (lhs_is_float) { | 1465 | } else if (lhs_is_float) { |
| 1118 | dest_float_type = lhs.ty; | 1466 | dest_float_type = lhs.ty; |
| 1119 | } else { | 1467 | } else { |
| 1120 | const int_info = lhs.ty.intInfo(self.target); | 1468 | const int_info = lhs.ty.intInfo(self.target()); |
| 1121 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | 1469 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); |
| 1122 | } | 1470 | } |
| 1123 | 1471 | ||
| ... | @@ -1152,19 +1500,19 @@ const Analyze = struct { | ... | @@ -1152,19 +1500,19 @@ const Analyze = struct { |
| 1152 | } else if (rhs_is_float) { | 1500 | } else if (rhs_is_float) { |
| 1153 | dest_float_type = rhs.ty; | 1501 | dest_float_type = rhs.ty; |
| 1154 | } else { | 1502 | } else { |
| 1155 | const int_info = rhs.ty.intInfo(self.target); | 1503 | const int_info = rhs.ty.intInfo(self.target()); |
| 1156 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | 1504 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); |
| 1157 | } | 1505 | } |
| 1158 | 1506 | ||
| 1159 | const dest_type = if (dest_float_type) |ft| ft else blk: { | 1507 | const dest_type = if (dest_float_type) |ft| ft else blk: { |
| 1160 | const max_bits = std.math.max(lhs_bits, rhs_bits); | 1508 | const max_bits = std.math.max(lhs_bits, rhs_bits); |
| 1161 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | 1509 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { |
| 1162 | error.Overflow => return self.fail(src, "{} exceeds maximum integer bit count", .{max_bits}), | 1510 | error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}), |
| 1163 | }; | 1511 | }; |
| 1164 | break :blk try self.makeIntType(dest_int_is_signed, casted_bits); | 1512 | break :blk try self.makeIntType(dest_int_is_signed, casted_bits); |
| 1165 | }; | 1513 | }; |
| 1166 | const casted_lhs = try self.coerce(block, dest_type, lhs); | 1514 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 1167 | const casted_rhs = try self.coerce(block, dest_type, lhs); | 1515 | const casted_rhs = try self.coerce(scope, dest_type, lhs); |
| 1168 | 1516 | ||
| 1169 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | 1517 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ |
| 1170 | .lhs = casted_lhs, | 1518 | .lhs = casted_lhs, |
| ... | @@ -1173,7 +1521,7 @@ const Analyze = struct { | ... | @@ -1173,7 +1521,7 @@ const Analyze = struct { |
| 1173 | }); | 1521 | }); |
| 1174 | } | 1522 | } |
| 1175 | 1523 | ||
| 1176 | fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type { | 1524 | fn makeIntType(self: *Module, signed: bool, bits: u16) !Type { |
| 1177 | if (signed) { | 1525 | if (signed) { |
| 1178 | const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned); | 1526 | const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned); |
| 1179 | int_payload.* = .{ .bits = bits }; | 1527 | int_payload.* = .{ .bits = bits }; |
| ... | @@ -1185,14 +1533,14 @@ const Analyze = struct { | ... | @@ -1185,14 +1533,14 @@ const Analyze = struct { |
| 1185 | } | 1533 | } |
| 1186 | } | 1534 | } |
| 1187 | 1535 | ||
| 1188 | fn coerce(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst { | 1536 | fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 1189 | // If the types are the same, we can return the operand. | 1537 | // If the types are the same, we can return the operand. |
| 1190 | if (dest_type.eql(inst.ty)) | 1538 | if (dest_type.eql(inst.ty)) |
| 1191 | return inst; | 1539 | return inst; |
| 1192 | 1540 | ||
| 1193 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | 1541 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); |
| 1194 | if (in_memory_result == .ok) { | 1542 | if (in_memory_result == .ok) { |
| 1195 | return self.bitcast(block, dest_type, inst); | 1543 | return self.bitcast(scope, dest_type, inst); |
| 1196 | } | 1544 | } |
| 1197 | 1545 | ||
| 1198 | // *[N]T to []T | 1546 | // *[N]T to []T |
| ... | @@ -1212,55 +1560,61 @@ const Analyze = struct { | ... | @@ -1212,55 +1560,61 @@ const Analyze = struct { |
| 1212 | if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) { | 1560 | if (inst.ty.zigTypeTag() == .ComptimeInt and dest_type.zigTypeTag() == .Int) { |
| 1213 | // The representation is already correct; we only need to make sure it fits in the destination type. | 1561 | // The representation is already correct; we only need to make sure it fits in the destination type. |
| 1214 | const val = inst.value().?; // comptime_int always has comptime known value | 1562 | const val = inst.value().?; // comptime_int always has comptime known value |
| 1215 | if (!val.intFitsInType(dest_type, self.target)) { | 1563 | if (!val.intFitsInType(dest_type, self.target())) { |
| 1216 | return self.fail(inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | 1564 | return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); |
| 1217 | } | 1565 | } |
| 1218 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); | 1566 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 1219 | } | 1567 | } |
| 1220 | 1568 | ||
| 1221 | // integer widening | 1569 | // integer widening |
| 1222 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { | 1570 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { |
| 1223 | const src_info = inst.ty.intInfo(self.target); | 1571 | const src_info = inst.ty.intInfo(self.target()); |
| 1224 | const dst_info = dest_type.intInfo(self.target); | 1572 | const dst_info = dest_type.intInfo(self.target()); |
| 1225 | if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) { | 1573 | if (src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) { |
| 1226 | if (inst.value()) |val| { | 1574 | if (inst.value()) |val| { |
| 1227 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); | 1575 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 1228 | } else { | 1576 | } else { |
| 1229 | return self.fail(inst.src, "TODO implement runtime integer widening", .{}); | 1577 | return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{}); |
| 1230 | } | 1578 | } |
| 1231 | } else { | 1579 | } else { |
| 1232 | return self.fail(inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); | 1580 | return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type }); |
| 1233 | } | 1581 | } |
| 1234 | } | 1582 | } |
| 1235 | 1583 | ||
| 1236 | return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); | 1584 | return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); |
| 1237 | } | 1585 | } |
| 1238 | 1586 | ||
| 1239 | fn bitcast(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst { | 1587 | fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 1240 | if (inst.value()) |val| { | 1588 | if (inst.value()) |val| { |
| 1241 | // Keep the comptime Value representation; take the new type. | 1589 | // Keep the comptime Value representation; take the new type. |
| 1242 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); | 1590 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 1243 | } | 1591 | } |
| 1244 | // TODO validate the type size and other compile errors | 1592 | // TODO validate the type size and other compile errors |
| 1245 | const b = try self.requireRuntimeBlock(block, inst.src); | 1593 | const b = try self.requireRuntimeBlock(scope, inst.src); |
| 1246 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | 1594 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); |
| 1247 | } | 1595 | } |
| 1248 | 1596 | ||
| 1249 | fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { | 1597 | fn coerceArrayPtrToSlice(self: *Module, dest_type: Type, inst: *Inst) !*Inst { |
| 1250 | if (inst.value()) |val| { | 1598 | if (inst.value()) |val| { |
| 1251 | // The comptime Value representation is compatible with both types. | 1599 | // The comptime Value representation is compatible with both types. |
| 1252 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); | 1600 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 1253 | } | 1601 | } |
| 1254 | return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | 1602 | return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); |
| 1255 | } | 1603 | } |
| 1256 | 1604 | ||
| 1257 | fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError { | 1605 | fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError { |
| 1258 | @setCold(true); | 1606 | @setCold(true); |
| 1259 | const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args); | 1607 | const err_msg = ErrorMsg{ |
| 1260 | (try self.errors.addOne()).* = .{ | ||
| 1261 | .byte_offset = src, | 1608 | .byte_offset = src, |
| 1262 | .msg = msg, | 1609 | .msg = try std.fmt.allocPrint(self.allocator, format, args), |
| 1263 | }; | 1610 | }; |
| 1611 | if (scope.cast(Scope.Block)) |block| { | ||
| 1612 | block.func.analysis = .{ .failure = err_msg }; | ||
| 1613 | } else if (scope.cast(Scope.Decl)) |scope_decl| { | ||
| 1614 | scope_decl.decl.analysis = .{ .failure = err_msg }; | ||
| 1615 | } else { | ||
| 1616 | unreachable; | ||
| 1617 | } | ||
| 1264 | return error.AnalysisFail; | 1618 | return error.AnalysisFail; |
| 1265 | } | 1619 | } |
| 1266 | 1620 | ||
| ... | @@ -1279,6 +1633,11 @@ const Analyze = struct { | ... | @@ -1279,6 +1633,11 @@ const Analyze = struct { |
| 1279 | } | 1633 | } |
| 1280 | }; | 1634 | }; |
| 1281 | 1635 | ||
| 1636 | pub const ErrorMsg = struct { | ||
| 1637 | byte_offset: usize, | ||
| 1638 | msg: []const u8, | ||
| 1639 | }; | ||
| 1640 | |||
| 1282 | pub fn main() anyerror!void { | 1641 | pub fn main() anyerror!void { |
| 1283 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 1642 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 1284 | defer arena.deinit(); | 1643 | defer arena.deinit(); |
| ... | @@ -1288,63 +1647,68 @@ pub fn main() anyerror!void { | ... | @@ -1288,63 +1647,68 @@ pub fn main() anyerror!void { |
| 1288 | defer std.process.argsFree(allocator, args); | 1647 | defer std.process.argsFree(allocator, args); |
| 1289 | 1648 | ||
| 1290 | const src_path = args[1]; | 1649 | const src_path = args[1]; |
| 1650 | const bin_path = args[2]; | ||
| 1291 | const debug_error_trace = true; | 1651 | const debug_error_trace = true; |
| 1292 | 1652 | const output_zir = true; | |
| 1293 | const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0); | ||
| 1294 | defer allocator.free(source); | ||
| 1295 | |||
| 1296 | var zir_module = try text.parse(allocator, source); | ||
| 1297 | defer zir_module.deinit(allocator); | ||
| 1298 | |||
| 1299 | if (zir_module.errors.len != 0) { | ||
| 1300 | for (zir_module.errors) |err_msg| { | ||
| 1301 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | ||
| 1302 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | ||
| 1303 | } | ||
| 1304 | if (debug_error_trace) return error.ParseFailure; | ||
| 1305 | std.process.exit(1); | ||
| 1306 | } | ||
| 1307 | 1653 | ||
| 1308 | const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); | 1654 | const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); |
| 1309 | 1655 | ||
| 1310 | var analyzed_module = try analyze(allocator, zir_module, .{ | 1656 | var bin_file = try link.openBinFilePath(allocator, std.fs.cwd(), bin_path, .{ |
| 1311 | .target = native_info.target, | 1657 | .target = native_info.target, |
| 1312 | .output_mode = .Obj, | 1658 | .output_mode = .Exe, |
| 1313 | .link_mode = .Static, | 1659 | .link_mode = .Static, |
| 1314 | .optimize_mode = .Debug, | 1660 | .object_format = options.object_format orelse native_info.target.getObjectFormat(), |
| 1315 | }); | 1661 | }); |
| 1316 | defer analyzed_module.deinit(allocator); | 1662 | defer bin_file.deinit(allocator); |
| 1663 | |||
| 1664 | var module = blk: { | ||
| 1665 | const root_pkg = try Package.create(allocator, std.fs.cwd(), ".", src_path); | ||
| 1666 | errdefer root_pkg.destroy(); | ||
| 1667 | |||
| 1668 | const root_scope = try allocator.create(Module.Scope.ZIRModule); | ||
| 1669 | errdefer allocator.destroy(root_scope); | ||
| 1670 | root_scope.* = .{ | ||
| 1671 | .sub_file_path = root_pkg.root_src_path, | ||
| 1672 | .contents = .unloaded, | ||
| 1673 | }; | ||
| 1317 | 1674 | ||
| 1318 | if (analyzed_module.errors.len != 0) { | 1675 | break :blk Module{ |
| 1319 | for (analyzed_module.errors) |err_msg| { | 1676 | .allocator = allocator, |
| 1320 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | 1677 | .root_pkg = root_pkg, |
| 1321 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | 1678 | .root_scope = root_scope, |
| 1679 | .bin_file = &bin_file, | ||
| 1680 | .optimize_mode = .Debug, | ||
| 1681 | .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(allocator), | ||
| 1682 | }; | ||
| 1683 | }; | ||
| 1684 | defer module.deinit(); | ||
| 1685 | |||
| 1686 | try module.update(); | ||
| 1687 | |||
| 1688 | const errors = try module.getAllErrorsAlloc(); | ||
| 1689 | defer errors.deinit(); | ||
| 1690 | |||
| 1691 | if (errors.list.len != 0) { | ||
| 1692 | for (errors.list) |full_err_msg| { | ||
| 1693 | std.debug.warn("{}:{}:{}: error: {}\n", .{ | ||
| 1694 | full_err_msg.src_path, | ||
| 1695 | full_err_msg.line + 1, | ||
| 1696 | full_err_msg.column + 1, | ||
| 1697 | full_err_msg.msg, | ||
| 1698 | }); | ||
| 1322 | } | 1699 | } |
| 1323 | if (debug_error_trace) return error.AnalysisFail; | 1700 | if (debug_error_trace) return error.AnalysisFail; |
| 1324 | std.process.exit(1); | 1701 | std.process.exit(1); |
| 1325 | } | 1702 | } |
| 1326 | 1703 | ||
| 1327 | const output_zir = true; | ||
| 1328 | if (output_zir) { | 1704 | if (output_zir) { |
| 1329 | var new_zir_module = try text.emit_zir(allocator, analyzed_module); | 1705 | var new_zir_module = try text.emit_zir(allocator, module); |
| 1330 | defer new_zir_module.deinit(allocator); | 1706 | defer new_zir_module.deinit(allocator); |
| 1331 | 1707 | ||
| 1332 | var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream()); | 1708 | var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream()); |
| 1333 | try new_zir_module.writeToStream(allocator, bos.outStream()); | 1709 | try new_zir_module.writeToStream(allocator, bos.outStream()); |
| 1334 | try bos.flush(); | 1710 | try bos.flush(); |
| 1335 | } | 1711 | } |
| 1336 | |||
| 1337 | const link = @import("link.zig"); | ||
| 1338 | var result = try link.updateFilePath(allocator, analyzed_module, std.fs.cwd(), "zir.o"); | ||
| 1339 | defer result.deinit(allocator); | ||
| 1340 | if (result.errors.len != 0) { | ||
| 1341 | for (result.errors) |err_msg| { | ||
| 1342 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | ||
| 1343 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | ||
| 1344 | } | ||
| 1345 | if (debug_error_trace) return error.LinkFailure; | ||
| 1346 | std.process.exit(1); | ||
| 1347 | } | ||
| 1348 | } | 1712 | } |
| 1349 | 1713 | ||
| 1350 | // Performance optimization ideas: | 1714 | // Performance optimization ideas: |
src-self-hosted/ir/text.zig+125-57| ... | @@ -16,10 +16,16 @@ pub const Inst = struct { | ... | @@ -16,10 +16,16 @@ pub const Inst = struct { |
| 16 | tag: Tag, | 16 | tag: Tag, |
| 17 | /// Byte offset into the source. | 17 | /// Byte offset into the source. |
| 18 | src: usize, | 18 | src: usize, |
| 19 | name: []const u8, | ||
| 19 | 20 | ||
| 20 | /// These names are used directly as the instruction names in the text format. | 21 | /// These names are used directly as the instruction names in the text format. |
| 21 | pub const Tag = enum { | 22 | pub const Tag = enum { |
| 22 | breakpoint, | 23 | breakpoint, |
| 24 | call, | ||
| 25 | /// Represents a reference to a global decl by name. | ||
| 26 | /// Canonicalized ZIR will not have any of these. The | ||
| 27 | /// syntax `@foo` is equivalent to `declref("foo")`. | ||
| 28 | declref, | ||
| 23 | str, | 29 | str, |
| 24 | int, | 30 | int, |
| 25 | ptrtoint, | 31 | ptrtoint, |
| ... | @@ -46,6 +52,8 @@ pub const Inst = struct { | ... | @@ -46,6 +52,8 @@ pub const Inst = struct { |
| 46 | pub fn TagToType(tag: Tag) type { | 52 | pub fn TagToType(tag: Tag) type { |
| 47 | return switch (tag) { | 53 | return switch (tag) { |
| 48 | .breakpoint => Breakpoint, | 54 | .breakpoint => Breakpoint, |
| 55 | .call => Call, | ||
| 56 | .declref => DeclRef, | ||
| 49 | .str => Str, | 57 | .str => Str, |
| 50 | .int => Int, | 58 | .int => Int, |
| 51 | .ptrtoint => PtrToInt, | 59 | .ptrtoint => PtrToInt, |
| ... | @@ -85,6 +93,29 @@ pub const Inst = struct { | ... | @@ -85,6 +93,29 @@ pub const Inst = struct { |
| 85 | kw_args: struct {}, | 93 | kw_args: struct {}, |
| 86 | }; | 94 | }; |
| 87 | 95 | ||
| 96 | pub const Call = struct { | ||
| 97 | pub const base_tag = Tag.call; | ||
| 98 | base: Inst, | ||
| 99 | |||
| 100 | positionals: struct { | ||
| 101 | func: *Inst, | ||
| 102 | args: []*Inst, | ||
| 103 | }, | ||
| 104 | kw_args: struct { | ||
| 105 | modifier: std.builtin.CallOptions.Modifier = .auto, | ||
| 106 | }, | ||
| 107 | }; | ||
| 108 | |||
| 109 | pub const DeclRef = struct { | ||
| 110 | pub const base_tag = Tag.declref; | ||
| 111 | base: Inst, | ||
| 112 | |||
| 113 | positionals: struct { | ||
| 114 | name: *Inst, | ||
| 115 | }, | ||
| 116 | kw_args: struct {}, | ||
| 117 | }; | ||
| 118 | |||
| 88 | pub const Str = struct { | 119 | pub const Str = struct { |
| 89 | pub const base_tag = Tag.str; | 120 | pub const base_tag = Tag.str; |
| 90 | base: Inst, | 121 | base: Inst, |
| ... | @@ -212,55 +243,55 @@ pub const Inst = struct { | ... | @@ -212,55 +243,55 @@ pub const Inst = struct { |
| 212 | kw_args: struct {}, | 243 | kw_args: struct {}, |
| 213 | 244 | ||
| 214 | pub const BuiltinType = enum { | 245 | pub const BuiltinType = enum { |
| 215 | @"isize", | 246 | isize, |
| 216 | @"usize", | 247 | usize, |
| 217 | @"c_short", | 248 | c_short, |
| 218 | @"c_ushort", | 249 | c_ushort, |
| 219 | @"c_int", | 250 | c_int, |
| 220 | @"c_uint", | 251 | c_uint, |
| 221 | @"c_long", | 252 | c_long, |
| 222 | @"c_ulong", | 253 | c_ulong, |
| 223 | @"c_longlong", | 254 | c_longlong, |
| 224 | @"c_ulonglong", | 255 | c_ulonglong, |
| 225 | @"c_longdouble", | 256 | c_longdouble, |
| 226 | @"c_void", | 257 | c_void, |
| 227 | @"f16", | 258 | f16, |
| 228 | @"f32", | 259 | f32, |
| 229 | @"f64", | 260 | f64, |
| 230 | @"f128", | 261 | f128, |
| 231 | @"bool", | 262 | bool, |
| 232 | @"void", | 263 | void, |
| 233 | @"noreturn", | 264 | noreturn, |
| 234 | @"type", | 265 | type, |
| 235 | @"anyerror", | 266 | anyerror, |
| 236 | @"comptime_int", | 267 | comptime_int, |
| 237 | @"comptime_float", | 268 | comptime_float, |
| 238 | 269 | ||
| 239 | fn toType(self: BuiltinType) Type { | 270 | fn toType(self: BuiltinType) Type { |
| 240 | return switch (self) { | 271 | return switch (self) { |
| 241 | .@"isize" => Type.initTag(.@"isize"), | 272 | .isize => Type.initTag(.isize), |
| 242 | .@"usize" => Type.initTag(.@"usize"), | 273 | .usize => Type.initTag(.usize), |
| 243 | .@"c_short" => Type.initTag(.@"c_short"), | 274 | .c_short => Type.initTag(.c_short), |
| 244 | .@"c_ushort" => Type.initTag(.@"c_ushort"), | 275 | .c_ushort => Type.initTag(.c_ushort), |
| 245 | .@"c_int" => Type.initTag(.@"c_int"), | 276 | .c_int => Type.initTag(.c_int), |
| 246 | .@"c_uint" => Type.initTag(.@"c_uint"), | 277 | .c_uint => Type.initTag(.c_uint), |
| 247 | .@"c_long" => Type.initTag(.@"c_long"), | 278 | .c_long => Type.initTag(.c_long), |
| 248 | .@"c_ulong" => Type.initTag(.@"c_ulong"), | 279 | .c_ulong => Type.initTag(.c_ulong), |
| 249 | .@"c_longlong" => Type.initTag(.@"c_longlong"), | 280 | .c_longlong => Type.initTag(.c_longlong), |
| 250 | .@"c_ulonglong" => Type.initTag(.@"c_ulonglong"), | 281 | .c_ulonglong => Type.initTag(.c_ulonglong), |
| 251 | .@"c_longdouble" => Type.initTag(.@"c_longdouble"), | 282 | .c_longdouble => Type.initTag(.c_longdouble), |
| 252 | .@"c_void" => Type.initTag(.@"c_void"), | 283 | .c_void => Type.initTag(.c_void), |
| 253 | .@"f16" => Type.initTag(.@"f16"), | 284 | .f16 => Type.initTag(.f16), |
| 254 | .@"f32" => Type.initTag(.@"f32"), | 285 | .f32 => Type.initTag(.f32), |
| 255 | .@"f64" => Type.initTag(.@"f64"), | 286 | .f64 => Type.initTag(.f64), |
| 256 | .@"f128" => Type.initTag(.@"f128"), | 287 | .f128 => Type.initTag(.f128), |
| 257 | .@"bool" => Type.initTag(.@"bool"), | 288 | .bool => Type.initTag(.bool), |
| 258 | .@"void" => Type.initTag(.@"void"), | 289 | .void => Type.initTag(.void), |
| 259 | .@"noreturn" => Type.initTag(.@"noreturn"), | 290 | .noreturn => Type.initTag(.noreturn), |
| 260 | .@"type" => Type.initTag(.@"type"), | 291 | .type => Type.initTag(.type), |
| 261 | .@"anyerror" => Type.initTag(.@"anyerror"), | 292 | .anyerror => Type.initTag(.anyerror), |
| 262 | .@"comptime_int" => Type.initTag(.@"comptime_int"), | 293 | .comptime_int => Type.initTag(.comptime_int), |
| 263 | .@"comptime_float" => Type.initTag(.@"comptime_float"), | 294 | .comptime_float => Type.initTag(.comptime_float), |
| 264 | }; | 295 | }; |
| 265 | } | 296 | } |
| 266 | }; | 297 | }; |
| ... | @@ -376,7 +407,7 @@ pub const ErrorMsg = struct { | ... | @@ -376,7 +407,7 @@ pub const ErrorMsg = struct { |
| 376 | pub const Module = struct { | 407 | pub const Module = struct { |
| 377 | decls: []*Inst, | 408 | decls: []*Inst, |
| 378 | errors: []ErrorMsg, | 409 | errors: []ErrorMsg, |
| 379 | arena: std.heap.ArenaAllocator, | 410 | arena: std.heap.ArenaAllocator.State, |
| 380 | 411 | ||
| 381 | pub const Body = struct { | 412 | pub const Body = struct { |
| 382 | instructions: []*Inst, | 413 | instructions: []*Inst, |
| ... | @@ -385,7 +416,7 @@ pub const Module = struct { | ... | @@ -385,7 +416,7 @@ pub const Module = struct { |
| 385 | pub fn deinit(self: *Module, allocator: *Allocator) void { | 416 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 386 | allocator.free(self.decls); | 417 | allocator.free(self.decls); |
| 387 | allocator.free(self.errors); | 418 | allocator.free(self.errors); |
| 388 | self.arena.deinit(); | 419 | self.arena.promote(allocator).deinit(); |
| 389 | self.* = undefined; | 420 | self.* = undefined; |
| 390 | } | 421 | } |
| 391 | 422 | ||
| ... | @@ -431,6 +462,7 @@ pub const Module = struct { | ... | @@ -431,6 +462,7 @@ pub const Module = struct { |
| 431 | // TODO I tried implementing this with an inline for loop and hit a compiler bug | 462 | // TODO I tried implementing this with an inline for loop and hit a compiler bug |
| 432 | switch (decl.tag) { | 463 | switch (decl.tag) { |
| 433 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), | 464 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), |
| 465 | .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table), | ||
| 434 | .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), | 466 | .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), |
| 435 | .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), | 467 | .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), |
| 436 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), | 468 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), |
| ... | @@ -543,9 +575,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module | ... | @@ -543,9 +575,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module |
| 543 | .arena = std.heap.ArenaAllocator.init(allocator), | 575 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 544 | .i = 0, | 576 | .i = 0, |
| 545 | .source = source, | 577 | .source = source, |
| 546 | .decls = std.ArrayList(*Inst).init(allocator), | ||
| 547 | .errors = std.ArrayList(ErrorMsg).init(allocator), | ||
| 548 | .global_name_map = &global_name_map, | 578 | .global_name_map = &global_name_map, |
| 579 | .errors = .{}, | ||
| 580 | .decls = .{}, | ||
| 549 | }; | 581 | }; |
| 550 | errdefer parser.arena.deinit(); | 582 | errdefer parser.arena.deinit(); |
| 551 | 583 | ||
| ... | @@ -555,10 +587,11 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module | ... | @@ -555,10 +587,11 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module |
| 555 | }, | 587 | }, |
| 556 | else => |e| return e, | 588 | else => |e| return e, |
| 557 | }; | 589 | }; |
| 590 | |||
| 558 | return Module{ | 591 | return Module{ |
| 559 | .decls = parser.decls.toOwnedSlice(), | 592 | .decls = parser.decls.toOwnedSlice(allocator), |
| 560 | .errors = parser.errors.toOwnedSlice(), | 593 | .errors = parser.errors.toOwnedSlice(allocator), |
| 561 | .arena = parser.arena, | 594 | .arena = parser.arena.state, |
| 562 | }; | 595 | }; |
| 563 | } | 596 | } |
| 564 | 597 | ||
| ... | @@ -567,8 +600,8 @@ const Parser = struct { | ... | @@ -567,8 +600,8 @@ const Parser = struct { |
| 567 | arena: std.heap.ArenaAllocator, | 600 | arena: std.heap.ArenaAllocator, |
| 568 | i: usize, | 601 | i: usize, |
| 569 | source: [:0]const u8, | 602 | source: [:0]const u8, |
| 570 | errors: std.ArrayList(ErrorMsg), | 603 | errors: std.ArrayListUnmanaged(ErrorMsg), |
| 571 | decls: std.ArrayList(*Inst), | 604 | decls: std.ArrayListUnmanaged(*Inst), |
| 572 | global_name_map: *std.StringHashMap(usize), | 605 | global_name_map: *std.StringHashMap(usize), |
| 573 | 606 | ||
| 574 | const Body = struct { | 607 | const Body = struct { |
| ... | @@ -893,8 +926,25 @@ const Parser = struct { | ... | @@ -893,8 +926,25 @@ const Parser = struct { |
| 893 | const ident = self.source[name_start..self.i]; | 926 | const ident = self.source[name_start..self.i]; |
| 894 | const kv = map.get(ident) orelse { | 927 | const kv = map.get(ident) orelse { |
| 895 | const bad_name = self.source[name_start - 1 .. self.i]; | 928 | const bad_name = self.source[name_start - 1 .. self.i]; |
| 896 | self.i = name_start - 1; | 929 | const src = name_start - 1; |
| 897 | return self.fail("unrecognized identifier: {}", .{bad_name}); | 930 | if (local_ref) { |
| 931 | self.i = src; | ||
| 932 | return self.fail("unrecognized identifier: {}", .{bad_name}); | ||
| 933 | } else { | ||
| 934 | const name = try self.arena.allocator.create(Inst.Str); | ||
| 935 | name.* = .{ | ||
| 936 | .base = .{ .src = src, .tag = Inst.Str.base_tag }, | ||
| 937 | .positionals = .{ .bytes = ident }, | ||
| 938 | .kw_args = .{}, | ||
| 939 | }; | ||
| 940 | const declref = try self.arena.allocator.create(Inst.DeclRef); | ||
| 941 | declref.* = .{ | ||
| 942 | .base = .{ .src = src, .tag = Inst.DeclRef.base_tag }, | ||
| 943 | .positionals = .{ .name = &name.base }, | ||
| 944 | .kw_args = .{}, | ||
| 945 | }; | ||
| 946 | return &declref.base; | ||
| 947 | } | ||
| 898 | }; | 948 | }; |
| 899 | if (local_ref) { | 949 | if (local_ref) { |
| 900 | return body_ctx.?.instructions.items[kv.value]; | 950 | return body_ctx.?.instructions.items[kv.value]; |
| ... | @@ -1065,6 +1115,24 @@ const EmitZIR = struct { | ... | @@ -1065,6 +1115,24 @@ const EmitZIR = struct { |
| 1065 | for (body.instructions) |inst| { | 1115 | for (body.instructions) |inst| { |
| 1066 | const new_inst = switch (inst.tag) { | 1116 | const new_inst = switch (inst.tag) { |
| 1067 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), | 1117 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), |
| 1118 | .call => blk: { | ||
| 1119 | const old_inst = inst.cast(ir.Inst.Call).?; | ||
| 1120 | const new_inst = try self.arena.allocator.create(Inst.Call); | ||
| 1121 | |||
| 1122 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | ||
| 1123 | for (args) |*elem, i| { | ||
| 1124 | elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]); | ||
| 1125 | } | ||
| 1126 | new_inst.* = .{ | ||
| 1127 | .base = .{ .src = inst.src, .tag = Inst.Call.base_tag }, | ||
| 1128 | .positionals = .{ | ||
| 1129 | .func = try self.resolveInst(inst_table, old_inst.args.func), | ||
| 1130 | .args = args, | ||
| 1131 | }, | ||
| 1132 | .kw_args = .{}, | ||
| 1133 | }; | ||
| 1134 | break :blk &new_inst.base; | ||
| 1135 | }, | ||
| 1068 | .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), | 1136 | .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), |
| 1069 | .ret => try self.emitTrivial(inst.src, Inst.Return), | 1137 | .ret => try self.emitTrivial(inst.src, Inst.Return), |
| 1070 | .constant => unreachable, // excluded from function bodies | 1138 | .constant => unreachable, // excluded from function bodies |
src-self-hosted/libc_installation.zig-1| ... | @@ -1,6 +1,5 @@ | ... | @@ -1,6 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const util = @import("util.zig"); | ||
| 4 | const Target = std.Target; | 3 | const Target = std.Target; |
| 5 | const fs = std.fs; | 4 | const fs = std.fs; |
| 6 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
src-self-hosted/link.zig+356-224| ... | @@ -9,50 +9,65 @@ const codegen = @import("codegen.zig"); | ... | @@ -9,50 +9,65 @@ const codegen = @import("codegen.zig"); |
| 9 | 9 | ||
| 10 | const default_entry_addr = 0x8000000; | 10 | const default_entry_addr = 0x8000000; |
| 11 | 11 | ||
| 12 | pub const ErrorMsg = struct { | 12 | pub const Options = struct { |
| 13 | byte_offset: usize, | 13 | target: std.Target, |
| 14 | msg: []const u8, | 14 | output_mode: std.builtin.OutputMode, |
| 15 | }; | 15 | link_mode: std.builtin.LinkMode, |
| 16 | 16 | object_format: std.builtin.ObjectFormat, | |
| 17 | pub const Result = struct { | 17 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 18 | errors: []ErrorMsg, | 18 | /// does not already have a symbol table. |
| 19 | 19 | symbol_count_hint: u64 = 32, | |
| 20 | pub fn deinit(self: *Result, allocator: *mem.Allocator) void { | 20 | /// Used for calculating how much space to reserve for executable program code in case |
| 21 | for (self.errors) |err| { | 21 | /// the binary file deos not already have such a section. |
| 22 | allocator.free(err.msg); | 22 | program_code_size_hint: u64 = 256 * 1024, |
| 23 | } | ||
| 24 | allocator.free(self.errors); | ||
| 25 | self.* = undefined; | ||
| 26 | } | ||
| 27 | }; | 23 | }; |
| 28 | 24 | ||
| 29 | /// Attempts incremental linking, if the file already exists. | 25 | /// Attempts incremental linking, if the file already exists. |
| 30 | /// If incremental linking fails, falls back to truncating the file and rewriting it. | 26 | /// If incremental linking fails, falls back to truncating the file and rewriting it. |
| 31 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. | 27 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. |
| 32 | /// This operation is not atomic. | 28 | /// This operation is not atomic. |
| 33 | pub fn updateFilePath( | 29 | pub fn openBinFilePath( |
| 34 | allocator: *Allocator, | 30 | allocator: *Allocator, |
| 35 | module: ir.Module, | ||
| 36 | dir: fs.Dir, | 31 | dir: fs.Dir, |
| 37 | sub_path: []const u8, | 32 | sub_path: []const u8, |
| 38 | ) !Result { | 33 | options: Options, |
| 39 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) }); | 34 | ) !ElfFile { |
| 35 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) }); | ||
| 40 | defer file.close(); | 36 | defer file.close(); |
| 41 | 37 | ||
| 42 | return updateFile(allocator, module, file); | 38 | return openBinFile(allocator, file, options); |
| 43 | } | 39 | } |
| 44 | 40 | ||
| 45 | /// Atomically overwrites the old file, if present. | 41 | /// Atomically overwrites the old file, if present. |
| 46 | pub fn writeFilePath( | 42 | pub fn writeFilePath( |
| 47 | allocator: *Allocator, | 43 | allocator: *Allocator, |
| 48 | module: ir.Module, | ||
| 49 | dir: fs.Dir, | 44 | dir: fs.Dir, |
| 50 | sub_path: []const u8, | 45 | sub_path: []const u8, |
| 51 | ) !Result { | 46 | module: ir.Module, |
| 52 | const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) }); | 47 | errors: *std.ArrayList(ir.ErrorMsg), |
| 48 | ) !void { | ||
| 49 | const options: Options = .{ | ||
| 50 | .target = module.target, | ||
| 51 | .output_mode = module.output_mode, | ||
| 52 | .link_mode = module.link_mode, | ||
| 53 | .object_format = module.object_format, | ||
| 54 | .symbol_count_hint = module.decls.items.len, | ||
| 55 | }; | ||
| 56 | const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) }); | ||
| 53 | defer af.deinit(); | 57 | defer af.deinit(); |
| 54 | 58 | ||
| 55 | const result = try writeFile(allocator, module, af.file); | 59 | const elf_file = try createElfFile(allocator, af.file, options); |
| 60 | for (module.decls.items) |decl| { | ||
| 61 | try elf_file.updateDecl(module, decl, errors); | ||
| 62 | } | ||
| 63 | try elf_file.flush(); | ||
| 64 | if (elf_file.error_flags.no_entry_point_found) { | ||
| 65 | try errors.ensureCapacity(errors.items.len + 1); | ||
| 66 | errors.appendAssumeCapacity(.{ | ||
| 67 | .byte_offset = 0, | ||
| 68 | .msg = try std.fmt.allocPrint(errors.allocator, "no entry point found", .{}), | ||
| 69 | }); | ||
| 70 | } | ||
| 56 | try af.finish(); | 71 | try af.finish(); |
| 57 | return result; | 72 | return result; |
| 58 | } | 73 | } |
| ... | @@ -62,49 +77,65 @@ pub fn writeFilePath( | ... | @@ -62,49 +77,65 @@ pub fn writeFilePath( |
| 62 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 77 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 63 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. | 78 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. |
| 64 | /// This operation is not atomic. | 79 | /// This operation is not atomic. |
| 65 | pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | 80 | pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile { |
| 66 | return updateFileInner(allocator, module, file) catch |err| switch (err) { | 81 | return openBinFileInner(allocator, file, options) catch |err| switch (err) { |
| 67 | error.IncrFailed => { | 82 | error.IncrFailed => { |
| 68 | return writeFile(allocator, module, file); | 83 | return createElfFile(allocator, file, options); |
| 69 | }, | 84 | }, |
| 70 | else => |e| return e, | 85 | else => |e| return e, |
| 71 | }; | 86 | }; |
| 72 | } | 87 | } |
| 73 | 88 | ||
| 74 | const Update = struct { | 89 | pub const ElfFile = struct { |
| 90 | allocator: *Allocator, | ||
| 75 | file: fs.File, | 91 | file: fs.File, |
| 76 | module: *const ir.Module, | 92 | options: Options, |
| 93 | ptr_width: enum { p32, p64 }, | ||
| 77 | 94 | ||
| 78 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 95 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 79 | /// Same order as in the file. | 96 | /// Same order as in the file. |
| 80 | sections: std.ArrayList(elf.Elf64_Shdr), | 97 | sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, |
| 81 | shdr_table_offset: ?u64, | 98 | shdr_table_offset: ?u64 = null, |
| 82 | 99 | ||
| 83 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 100 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 84 | /// Same order as in the file. | 101 | /// Same order as in the file. |
| 85 | program_headers: std.ArrayList(elf.Elf64_Phdr), | 102 | program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, |
| 86 | phdr_table_offset: ?u64, | 103 | phdr_table_offset: ?u64 = null, |
| 87 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags | 104 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags |
| 88 | phdr_load_re_index: ?u16, | 105 | phdr_load_re_index: ?u16 = null, |
| 89 | entry_addr: ?u64, | 106 | entry_addr: ?u64 = null, |
| 90 | 107 | ||
| 91 | shstrtab: std.ArrayList(u8), | 108 | shstrtab: std.ArrayListUnmanaged(u8) = .{}, |
| 92 | shstrtab_index: ?u16, | 109 | shstrtab_index: ?u16 = null, |
| 93 | 110 | ||
| 94 | text_section_index: ?u16, | 111 | text_section_index: ?u16 = null, |
| 95 | symtab_section_index: ?u16, | 112 | symtab_section_index: ?u16 = null, |
| 96 | 113 | ||
| 97 | /// The same order as in the file | 114 | /// The same order as in the file |
| 98 | symbols: std.ArrayList(elf.Elf64_Sym), | 115 | symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, |
| 99 | 116 | ||
| 100 | errors: std.ArrayList(ErrorMsg), | 117 | /// Same order as in the file. |
| 118 | offset_table: std.ArrayListUnmanaged(aoeu) = .{}, | ||
| 119 | |||
| 120 | /// This means the entire read-only executable program code needs to be rewritten. | ||
| 121 | phdr_load_re_dirty: bool = false, | ||
| 122 | phdr_table_dirty: bool = false, | ||
| 123 | shdr_table_dirty: bool = false, | ||
| 124 | shstrtab_dirty: bool = false, | ||
| 125 | symtab_dirty: bool = false, | ||
| 126 | |||
| 127 | error_flags: ErrorFlags = ErrorFlags{}, | ||
| 101 | 128 | ||
| 102 | fn deinit(self: *Update) void { | 129 | pub const ErrorFlags = struct { |
| 103 | self.sections.deinit(); | 130 | no_entry_point_found: bool = false, |
| 104 | self.program_headers.deinit(); | 131 | }; |
| 105 | self.shstrtab.deinit(); | 132 | |
| 106 | self.symbols.deinit(); | 133 | pub fn deinit(self: *ElfFile) void { |
| 107 | self.errors.deinit(); | 134 | self.sections.deinit(self.allocator); |
| 135 | self.program_headers.deinit(self.allocator); | ||
| 136 | self.shstrtab.deinit(self.allocator); | ||
| 137 | self.symbols.deinit(self.allocator); | ||
| 138 | self.offset_table.deinit(self.allocator); | ||
| 108 | } | 139 | } |
| 109 | 140 | ||
| 110 | // `expand_num / expand_den` is the factor of padding when allocation | 141 | // `expand_num / expand_den` is the factor of padding when allocation |
| ... | @@ -112,8 +143,8 @@ const Update = struct { | ... | @@ -112,8 +143,8 @@ const Update = struct { |
| 112 | const alloc_den = 3; | 143 | const alloc_den = 3; |
| 113 | 144 | ||
| 114 | /// Returns end pos of collision, if any. | 145 | /// Returns end pos of collision, if any. |
| 115 | fn detectAllocCollision(self: *Update, start: u64, size: u64) ?u64 { | 146 | fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 { |
| 116 | const small_ptr = self.module.target.cpu.arch.ptrBitWidth() == 32; | 147 | const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32; |
| 117 | const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); | 148 | const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); |
| 118 | if (start < ehdr_size) | 149 | if (start < ehdr_size) |
| 119 | return ehdr_size; | 150 | return ehdr_size; |
| ... | @@ -157,7 +188,7 @@ const Update = struct { | ... | @@ -157,7 +188,7 @@ const Update = struct { |
| 157 | return null; | 188 | return null; |
| 158 | } | 189 | } |
| 159 | 190 | ||
| 160 | fn allocatedSize(self: *Update, start: u64) u64 { | 191 | fn allocatedSize(self: *ElfFile, start: u64) u64 { |
| 161 | var min_pos: u64 = std.math.maxInt(u64); | 192 | var min_pos: u64 = std.math.maxInt(u64); |
| 162 | if (self.shdr_table_offset) |off| { | 193 | if (self.shdr_table_offset) |off| { |
| 163 | if (off > start and off < min_pos) min_pos = off; | 194 | if (off > start and off < min_pos) min_pos = off; |
| ... | @@ -176,7 +207,7 @@ const Update = struct { | ... | @@ -176,7 +207,7 @@ const Update = struct { |
| 176 | return min_pos - start; | 207 | return min_pos - start; |
| 177 | } | 208 | } |
| 178 | 209 | ||
| 179 | fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 { | 210 | fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 { |
| 180 | var start: u64 = 0; | 211 | var start: u64 = 0; |
| 181 | while (self.detectAllocCollision(start, object_size)) |item_end| { | 212 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 182 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); | 213 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); |
| ... | @@ -184,33 +215,21 @@ const Update = struct { | ... | @@ -184,33 +215,21 @@ const Update = struct { |
| 184 | return start; | 215 | return start; |
| 185 | } | 216 | } |
| 186 | 217 | ||
| 187 | fn makeString(self: *Update, bytes: []const u8) !u32 { | 218 | fn makeString(self: *ElfFile, bytes: []const u8) !u32 { |
| 188 | const result = self.shstrtab.items.len; | 219 | const result = self.shstrtab.items.len; |
| 189 | try self.shstrtab.appendSlice(bytes); | 220 | try self.shstrtab.appendSlice(bytes); |
| 190 | try self.shstrtab.append(0); | 221 | try self.shstrtab.append(0); |
| 191 | return @intCast(u32, result); | 222 | return @intCast(u32, result); |
| 192 | } | 223 | } |
| 193 | 224 | ||
| 194 | fn perform(self: *Update) !void { | 225 | pub fn populateMissingMetadata(self: *ElfFile) !void { |
| 195 | const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) { | 226 | const small_ptr = switch (self.ptr_width) { |
| 196 | 32 => .p32, | ||
| 197 | 64 => .p64, | ||
| 198 | else => return error.UnsupportedArchitecture, | ||
| 199 | }; | ||
| 200 | const small_ptr = switch (ptr_width) { | ||
| 201 | .p32 => true, | 227 | .p32 => true, |
| 202 | .p64 => false, | 228 | .p64 => false, |
| 203 | }; | 229 | }; |
| 204 | // This means the entire read-only executable program code needs to be rewritten. | ||
| 205 | var phdr_load_re_dirty = false; | ||
| 206 | var phdr_table_dirty = false; | ||
| 207 | var shdr_table_dirty = false; | ||
| 208 | var shstrtab_dirty = false; | ||
| 209 | var symtab_dirty = false; | ||
| 210 | |||
| 211 | if (self.phdr_load_re_index == null) { | 230 | if (self.phdr_load_re_index == null) { |
| 212 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); | 231 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); |
| 213 | const file_size = 256 * 1024; | 232 | const file_size = self.options.program_code_size_hint; |
| 214 | const p_align = 0x1000; | 233 | const p_align = 0x1000; |
| 215 | const off = self.findFreeSpace(file_size, p_align); | 234 | const off = self.findFreeSpace(file_size, p_align); |
| 216 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 235 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| ... | @@ -225,24 +244,8 @@ const Update = struct { | ... | @@ -225,24 +244,8 @@ const Update = struct { |
| 225 | .p_flags = elf.PF_X | elf.PF_R, | 244 | .p_flags = elf.PF_X | elf.PF_R, |
| 226 | }); | 245 | }); |
| 227 | self.entry_addr = null; | 246 | self.entry_addr = null; |
| 228 | phdr_load_re_dirty = true; | 247 | self.phdr_load_re_dirty = true; |
| 229 | phdr_table_dirty = true; | 248 | self.phdr_table_dirty = true; |
| 230 | } | ||
| 231 | if (self.sections.items.len == 0) { | ||
| 232 | // There must always be a null section in index 0 | ||
| 233 | try self.sections.append(.{ | ||
| 234 | .sh_name = 0, | ||
| 235 | .sh_type = elf.SHT_NULL, | ||
| 236 | .sh_flags = 0, | ||
| 237 | .sh_addr = 0, | ||
| 238 | .sh_offset = 0, | ||
| 239 | .sh_size = 0, | ||
| 240 | .sh_link = 0, | ||
| 241 | .sh_info = 0, | ||
| 242 | .sh_addralign = 0, | ||
| 243 | .sh_entsize = 0, | ||
| 244 | }); | ||
| 245 | shdr_table_dirty = true; | ||
| 246 | } | 249 | } |
| 247 | if (self.shstrtab_index == null) { | 250 | if (self.shstrtab_index == null) { |
| 248 | self.shstrtab_index = @intCast(u16, self.sections.items.len); | 251 | self.shstrtab_index = @intCast(u16, self.sections.items.len); |
| ... | @@ -262,8 +265,8 @@ const Update = struct { | ... | @@ -262,8 +265,8 @@ const Update = struct { |
| 262 | .sh_addralign = 1, | 265 | .sh_addralign = 1, |
| 263 | .sh_entsize = 0, | 266 | .sh_entsize = 0, |
| 264 | }); | 267 | }); |
| 265 | shstrtab_dirty = true; | 268 | self.shstrtab_dirty = true; |
| 266 | shdr_table_dirty = true; | 269 | self.shdr_table_dirty = true; |
| 267 | } | 270 | } |
| 268 | if (self.text_section_index == null) { | 271 | if (self.text_section_index == null) { |
| 269 | self.text_section_index = @intCast(u16, self.sections.items.len); | 272 | self.text_section_index = @intCast(u16, self.sections.items.len); |
| ... | @@ -281,13 +284,13 @@ const Update = struct { | ... | @@ -281,13 +284,13 @@ const Update = struct { |
| 281 | .sh_addralign = phdr.p_align, | 284 | .sh_addralign = phdr.p_align, |
| 282 | .sh_entsize = 0, | 285 | .sh_entsize = 0, |
| 283 | }); | 286 | }); |
| 284 | shdr_table_dirty = true; | 287 | self.shdr_table_dirty = true; |
| 285 | } | 288 | } |
| 286 | if (self.symtab_section_index == null) { | 289 | if (self.symtab_section_index == null) { |
| 287 | self.symtab_section_index = @intCast(u16, self.sections.items.len); | 290 | self.symtab_section_index = @intCast(u16, self.sections.items.len); |
| 288 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | 291 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| 289 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); | 292 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 290 | const file_size = self.module.exports.len * each_size; | 293 | const file_size = self.options.symbol_count_hint * each_size; |
| 291 | const off = self.findFreeSpace(file_size, min_align); | 294 | const off = self.findFreeSpace(file_size, min_align); |
| 292 | //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 295 | //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 293 | 296 | ||
| ... | @@ -300,12 +303,12 @@ const Update = struct { | ... | @@ -300,12 +303,12 @@ const Update = struct { |
| 300 | .sh_size = file_size, | 303 | .sh_size = file_size, |
| 301 | // The section header index of the associated string table. | 304 | // The section header index of the associated string table. |
| 302 | .sh_link = self.shstrtab_index.?, | 305 | .sh_link = self.shstrtab_index.?, |
| 303 | .sh_info = @intCast(u32, self.module.exports.len), | 306 | .sh_info = @intCast(u32, self.symbols.items.len), |
| 304 | .sh_addralign = min_align, | 307 | .sh_addralign = min_align, |
| 305 | .sh_entsize = each_size, | 308 | .sh_entsize = each_size, |
| 306 | }); | 309 | }); |
| 307 | symtab_dirty = true; | 310 | self.symtab_dirty = true; |
| 308 | shdr_table_dirty = true; | 311 | self.shdr_table_dirty = true; |
| 309 | } | 312 | } |
| 310 | const shsize: u64 = switch (ptr_width) { | 313 | const shsize: u64 = switch (ptr_width) { |
| 311 | .p32 => @sizeOf(elf.Elf32_Shdr), | 314 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| ... | @@ -317,7 +320,7 @@ const Update = struct { | ... | @@ -317,7 +320,7 @@ const Update = struct { |
| 317 | }; | 320 | }; |
| 318 | if (self.shdr_table_offset == null) { | 321 | if (self.shdr_table_offset == null) { |
| 319 | self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); | 322 | self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); |
| 320 | shdr_table_dirty = true; | 323 | self.shdr_table_dirty = true; |
| 321 | } | 324 | } |
| 322 | const phsize: u64 = switch (ptr_width) { | 325 | const phsize: u64 = switch (ptr_width) { |
| 323 | .p32 => @sizeOf(elf.Elf32_Phdr), | 326 | .p32 => @sizeOf(elf.Elf32_Phdr), |
| ... | @@ -329,13 +332,15 @@ const Update = struct { | ... | @@ -329,13 +332,15 @@ const Update = struct { |
| 329 | }; | 332 | }; |
| 330 | if (self.phdr_table_offset == null) { | 333 | if (self.phdr_table_offset == null) { |
| 331 | self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign); | 334 | self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign); |
| 332 | phdr_table_dirty = true; | 335 | self.phdr_table_dirty = true; |
| 333 | } | 336 | } |
| 334 | const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 337 | } |
| 335 | 338 | ||
| 336 | try self.writeCodeAndSymbols(phdr_table_dirty, shdr_table_dirty); | 339 | /// Commit pending changes and write headers. |
| 340 | pub fn flush(self: *ElfFile) !void { | ||
| 341 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | ||
| 337 | 342 | ||
| 338 | if (phdr_table_dirty) { | 343 | if (self.phdr_table_dirty) { |
| 339 | const allocated_size = self.allocatedSize(self.phdr_table_offset.?); | 344 | const allocated_size = self.allocatedSize(self.phdr_table_offset.?); |
| 340 | const needed_size = self.program_headers.items.len * phsize; | 345 | const needed_size = self.program_headers.items.len * phsize; |
| 341 | 346 | ||
| ... | @@ -345,7 +350,7 @@ const Update = struct { | ... | @@ -345,7 +350,7 @@ const Update = struct { |
| 345 | } | 350 | } |
| 346 | 351 | ||
| 347 | const allocator = self.program_headers.allocator; | 352 | const allocator = self.program_headers.allocator; |
| 348 | switch (ptr_width) { | 353 | switch (self.ptr_width) { |
| 349 | .p32 => { | 354 | .p32 => { |
| 350 | const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len); | 355 | const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len); |
| 351 | defer allocator.free(buf); | 356 | defer allocator.free(buf); |
| ... | @@ -371,11 +376,12 @@ const Update = struct { | ... | @@ -371,11 +376,12 @@ const Update = struct { |
| 371 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); | 376 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); |
| 372 | }, | 377 | }, |
| 373 | } | 378 | } |
| 379 | self.phdr_table_offset = false; | ||
| 374 | } | 380 | } |
| 375 | 381 | ||
| 376 | { | 382 | { |
| 377 | const shstrtab_sect = &self.sections.items[self.shstrtab_index.?]; | 383 | const shstrtab_sect = &self.sections.items[self.shstrtab_index.?]; |
| 378 | if (shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) { | 384 | if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) { |
| 379 | const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset); | 385 | const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset); |
| 380 | const needed_size = self.shstrtab.items.len; | 386 | const needed_size = self.shstrtab.items.len; |
| 381 | 387 | ||
| ... | @@ -387,13 +393,14 @@ const Update = struct { | ... | @@ -387,13 +393,14 @@ const Update = struct { |
| 387 | //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size }); | 393 | //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size }); |
| 388 | 394 | ||
| 389 | try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); | 395 | try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); |
| 390 | if (!shdr_table_dirty) { | 396 | if (!self.shdr_table_dirty) { |
| 391 | // Then it won't get written with the others and we need to do it. | 397 | // Then it won't get written with the others and we need to do it. |
| 392 | try self.writeSectHeader(self.shstrtab_index.?); | 398 | try self.writeSectHeader(self.shstrtab_index.?); |
| 393 | } | 399 | } |
| 400 | self.shstrtab_dirty = false; | ||
| 394 | } | 401 | } |
| 395 | } | 402 | } |
| 396 | if (shdr_table_dirty) { | 403 | if (self.shdr_table_dirty) { |
| 397 | const allocated_size = self.allocatedSize(self.shdr_table_offset.?); | 404 | const allocated_size = self.allocatedSize(self.shdr_table_offset.?); |
| 398 | const needed_size = self.sections.items.len * phsize; | 405 | const needed_size = self.sections.items.len * phsize; |
| 399 | 406 | ||
| ... | @@ -403,7 +410,7 @@ const Update = struct { | ... | @@ -403,7 +410,7 @@ const Update = struct { |
| 403 | } | 410 | } |
| 404 | 411 | ||
| 405 | const allocator = self.sections.allocator; | 412 | const allocator = self.sections.allocator; |
| 406 | switch (ptr_width) { | 413 | switch (self.ptr_width) { |
| 407 | .p32 => { | 414 | .p32 => { |
| 408 | const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); | 415 | const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); |
| 409 | defer allocator.free(buf); | 416 | defer allocator.free(buf); |
| ... | @@ -431,38 +438,36 @@ const Update = struct { | ... | @@ -431,38 +438,36 @@ const Update = struct { |
| 431 | }, | 438 | }, |
| 432 | } | 439 | } |
| 433 | } | 440 | } |
| 434 | if (self.entry_addr == null and self.module.output_mode == .Exe) { | 441 | if (self.entry_addr == null and self.options.output_mode == .Exe) { |
| 435 | const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{}); | 442 | self.error_flags.no_entry_point_found = true; |
| 436 | errdefer self.errors.allocator.free(msg); | ||
| 437 | try self.errors.append(.{ | ||
| 438 | .byte_offset = 0, | ||
| 439 | .msg = msg, | ||
| 440 | }); | ||
| 441 | } else { | 443 | } else { |
| 444 | self.error_flags.no_entry_point_found = false; | ||
| 442 | try self.writeElfHeader(); | 445 | try self.writeElfHeader(); |
| 443 | } | 446 | } |
| 444 | // TODO find end pos and truncate | 447 | // TODO find end pos and truncate |
| 448 | |||
| 449 | // The point of flush() is to commit changes, so nothing should be dirty after this. | ||
| 450 | assert(!self.phdr_load_re_dirty); | ||
| 451 | assert(!self.phdr_table_dirty); | ||
| 452 | assert(!self.shdr_table_dirty); | ||
| 453 | assert(!self.shstrtab_dirty); | ||
| 454 | assert(!self.symtab_dirty); | ||
| 445 | } | 455 | } |
| 446 | 456 | ||
| 447 | fn writeElfHeader(self: *Update) !void { | 457 | fn writeElfHeader(self: *ElfFile) !void { |
| 448 | var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; | 458 | var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; |
| 449 | 459 | ||
| 450 | var index: usize = 0; | 460 | var index: usize = 0; |
| 451 | hdr_buf[0..4].* = "\x7fELF".*; | 461 | hdr_buf[0..4].* = "\x7fELF".*; |
| 452 | index += 4; | 462 | index += 4; |
| 453 | 463 | ||
| 454 | const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) { | 464 | hdr_buf[index] = switch (self.ptr_width) { |
| 455 | 32 => .p32, | ||
| 456 | 64 => .p64, | ||
| 457 | else => return error.UnsupportedArchitecture, | ||
| 458 | }; | ||
| 459 | hdr_buf[index] = switch (ptr_width) { | ||
| 460 | .p32 => elf.ELFCLASS32, | 465 | .p32 => elf.ELFCLASS32, |
| 461 | .p64 => elf.ELFCLASS64, | 466 | .p64 => elf.ELFCLASS64, |
| 462 | }; | 467 | }; |
| 463 | index += 1; | 468 | index += 1; |
| 464 | 469 | ||
| 465 | const endian = self.module.target.cpu.arch.endian(); | 470 | const endian = self.options.target.cpu.arch.endian(); |
| 466 | hdr_buf[index] = switch (endian) { | 471 | hdr_buf[index] = switch (endian) { |
| 467 | .Little => elf.ELFDATA2LSB, | 472 | .Little => elf.ELFDATA2LSB, |
| 468 | .Big => elf.ELFDATA2MSB, | 473 | .Big => elf.ELFDATA2MSB, |
| ... | @@ -480,10 +485,10 @@ const Update = struct { | ... | @@ -480,10 +485,10 @@ const Update = struct { |
| 480 | 485 | ||
| 481 | assert(index == 16); | 486 | assert(index == 16); |
| 482 | 487 | ||
| 483 | const elf_type = switch (self.module.output_mode) { | 488 | const elf_type = switch (self.options.output_mode) { |
| 484 | .Exe => elf.ET.EXEC, | 489 | .Exe => elf.ET.EXEC, |
| 485 | .Obj => elf.ET.REL, | 490 | .Obj => elf.ET.REL, |
| 486 | .Lib => switch (self.module.link_mode) { | 491 | .Lib => switch (self.options.link_mode) { |
| 487 | .Static => elf.ET.REL, | 492 | .Static => elf.ET.REL, |
| 488 | .Dynamic => elf.ET.DYN, | 493 | .Dynamic => elf.ET.DYN, |
| 489 | }, | 494 | }, |
| ... | @@ -491,7 +496,7 @@ const Update = struct { | ... | @@ -491,7 +496,7 @@ const Update = struct { |
| 491 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian); | 496 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian); |
| 492 | index += 2; | 497 | index += 2; |
| 493 | 498 | ||
| 494 | const machine = self.module.target.cpu.arch.toElfMachine(); | 499 | const machine = self.options.target.cpu.arch.toElfMachine(); |
| 495 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian); | 500 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian); |
| 496 | index += 2; | 501 | index += 2; |
| 497 | 502 | ||
| ... | @@ -501,7 +506,7 @@ const Update = struct { | ... | @@ -501,7 +506,7 @@ const Update = struct { |
| 501 | 506 | ||
| 502 | const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; | 507 | const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; |
| 503 | 508 | ||
| 504 | switch (ptr_width) { | 509 | switch (self.ptr_width) { |
| 505 | .p32 => { | 510 | .p32 => { |
| 506 | mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian); | 511 | mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian); |
| 507 | index += 4; | 512 | index += 4; |
| ... | @@ -533,14 +538,14 @@ const Update = struct { | ... | @@ -533,14 +538,14 @@ const Update = struct { |
| 533 | mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian); | 538 | mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian); |
| 534 | index += 4; | 539 | index += 4; |
| 535 | 540 | ||
| 536 | const e_ehsize: u16 = switch (ptr_width) { | 541 | const e_ehsize: u16 = switch (self.ptr_width) { |
| 537 | .p32 => @sizeOf(elf.Elf32_Ehdr), | 542 | .p32 => @sizeOf(elf.Elf32_Ehdr), |
| 538 | .p64 => @sizeOf(elf.Elf64_Ehdr), | 543 | .p64 => @sizeOf(elf.Elf64_Ehdr), |
| 539 | }; | 544 | }; |
| 540 | mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian); | 545 | mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian); |
| 541 | index += 2; | 546 | index += 2; |
| 542 | 547 | ||
| 543 | const e_phentsize: u16 = switch (ptr_width) { | 548 | const e_phentsize: u16 = switch (self.ptr_width) { |
| 544 | .p32 => @sizeOf(elf.Elf32_Phdr), | 549 | .p32 => @sizeOf(elf.Elf32_Phdr), |
| 545 | .p64 => @sizeOf(elf.Elf64_Phdr), | 550 | .p64 => @sizeOf(elf.Elf64_Phdr), |
| 546 | }; | 551 | }; |
| ... | @@ -551,7 +556,7 @@ const Update = struct { | ... | @@ -551,7 +556,7 @@ const Update = struct { |
| 551 | mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian); | 556 | mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian); |
| 552 | index += 2; | 557 | index += 2; |
| 553 | 558 | ||
| 554 | const e_shentsize: u16 = switch (ptr_width) { | 559 | const e_shentsize: u16 = switch (self.ptr_width) { |
| 555 | .p32 => @sizeOf(elf.Elf32_Shdr), | 560 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| 556 | .p64 => @sizeOf(elf.Elf64_Shdr), | 561 | .p64 => @sizeOf(elf.Elf64_Shdr), |
| 557 | }; | 562 | }; |
| ... | @@ -570,81 +575,172 @@ const Update = struct { | ... | @@ -570,81 +575,172 @@ const Update = struct { |
| 570 | try self.file.pwriteAll(hdr_buf[0..index], 0); | 575 | try self.file.pwriteAll(hdr_buf[0..index], 0); |
| 571 | } | 576 | } |
| 572 | 577 | ||
| 573 | fn writeCodeAndSymbols(self: *Update, phdr_table_dirty: bool, shdr_table_dirty: bool) !void { | 578 | /// TODO Look into making this smaller to save memory. |
| 574 | // index 0 is always a null symbol | 579 | /// Lots of redundant info here with the data stored in symbol structs. |
| 575 | try self.symbols.resize(1); | 580 | const DeclSymbol = struct { |
| 576 | self.symbols.items[0] = .{ | 581 | symbol_indexes: []usize, |
| 577 | .st_name = 0, | 582 | vaddr: u64, |
| 578 | .st_info = 0, | 583 | file_offset: u64, |
| 579 | .st_other = 0, | 584 | size: u64, |
| 580 | .st_shndx = 0, | 585 | }; |
| 581 | .st_value = 0, | ||
| 582 | .st_size = 0, | ||
| 583 | }; | ||
| 584 | 586 | ||
| 587 | const AllocatedBlock = struct { | ||
| 588 | vaddr: u64, | ||
| 589 | file_offset: u64, | ||
| 590 | size_capacity: u64, | ||
| 591 | }; | ||
| 592 | |||
| 593 | fn allocateDeclSymbol(self: *ElfFile, size: u64) AllocatedBlock { | ||
| 585 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 594 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 586 | var vaddr: u64 = phdr.p_vaddr; | 595 | todo(); |
| 587 | var file_off: u64 = phdr.p_offset; | 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; | ||
| 603 | |||
| 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 | //} | ||
| 607 | |||
| 608 | //return self.writeSymbols(); | ||
| 609 | } | ||
| 610 | |||
| 611 | fn findAllocatedBlock(self: *ElfFile, vaddr: u64) AllocatedBlock { | ||
| 612 | todo(); | ||
| 613 | } | ||
| 588 | 614 | ||
| 589 | var code = std.ArrayList(u8).init(self.sections.allocator); | 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 { | ||
| 623 | var code = std.ArrayList(u8).init(self.allocator); | ||
| 590 | defer code.deinit(); | 624 | defer code.deinit(); |
| 591 | 625 | ||
| 592 | for (self.module.exports) |exp| { | 626 | const err_msg = try codegen.generateSymbol(typed_value, module, &code, err_msg_allocator); |
| 593 | code.shrink(0); | 627 | if (err_msg != null) |em| return em; |
| 594 | var symbol = try codegen.generateSymbol(exp.typed_value, self.module.*, &code); | 628 | |
| 595 | defer symbol.deinit(code.allocator); | 629 | const export_count = blk: { |
| 596 | if (symbol.errors.len != 0) { | 630 | var export_node = decl_export_node; |
| 597 | for (symbol.errors) |err| { | 631 | var i: usize = 0; |
| 598 | const msg = try mem.dupe(self.errors.allocator, u8, err.msg); | 632 | while (export_node) |node| : (export_node = node.next) i += 1; |
| 599 | errdefer self.errors.allocator.free(msg); | 633 | break :blk i; |
| 600 | try self.errors.append(.{ | 634 | }; |
| 601 | .byte_offset = err.byte_offset, | 635 | |
| 602 | .msg = msg, | 636 | // Find or create a symbol from the decl |
| 603 | }); | 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; | ||
| 604 | } | 649 | } |
| 605 | continue; | 650 | break :blk decl_symbol; |
| 651 | } 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; | ||
| 667 | } | ||
| 668 | }; | ||
| 669 | |||
| 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; | ||
| 606 | } | 677 | } |
| 607 | try self.file.pwriteAll(code.items, file_off); | 678 | } |
| 608 | 679 | ||
| 609 | if (mem.eql(u8, exp.name, "_start")) { | 680 | var export_node = decl_export_node; |
| 610 | self.entry_addr = vaddr; | 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| { | ||
| 687 | 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 | }); | ||
| 693 | } | ||
| 611 | } | 694 | } |
| 612 | (try self.symbols.addOne()).* = .{ | 695 | const stb_bits = switch (node.data.linkage) { |
| 613 | .st_name = try self.makeString(exp.name), | 696 | .Internal => elf.STB_LOCAL, |
| 614 | .st_info = (elf.STB_LOCAL << 4) | elf.STT_FUNC, | 697 | .Strong => blk: { |
| 698 | if (mem.eql(u8, node.data.name, "_start")) { | ||
| 699 | self.entry_addr = decl_symbol.vaddr; | ||
| 700 | } | ||
| 701 | break :blk elf.STB_GLOBAL; | ||
| 702 | }, | ||
| 703 | .Weak => elf.STB_WEAK, | ||
| 704 | .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 | }); | ||
| 710 | }, | ||
| 711 | }; | ||
| 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, | ||
| 615 | .st_other = 0, | 730 | .st_other = 0, |
| 616 | .st_shndx = self.text_section_index.?, | 731 | .st_shndx = self.text_section_index.?, |
| 617 | .st_value = vaddr, | 732 | .st_value = decl_symbol.vaddr, |
| 618 | .st_size = code.items.len, | 733 | .st_size = code.items.len, |
| 619 | }; | 734 | }; |
| 620 | vaddr += code.items.len; | ||
| 621 | } | 735 | } |
| 622 | 736 | ||
| 623 | { | 737 | try self.file.pwriteAll(code.items, decl_symbol.file_offset); |
| 624 | // Now that we know the code size, we need to update the program header for executable code | ||
| 625 | phdr.p_memsz = vaddr - phdr.p_vaddr; | ||
| 626 | phdr.p_filesz = phdr.p_memsz; | ||
| 627 | |||
| 628 | const shdr = &self.sections.items[self.text_section_index.?]; | ||
| 629 | shdr.sh_size = phdr.p_filesz; | ||
| 630 | |||
| 631 | if (!phdr_table_dirty) { | ||
| 632 | // Then it won't get written with the others and we need to do it. | ||
| 633 | try self.writeProgHeader(self.phdr_load_re_index.?); | ||
| 634 | } | ||
| 635 | if (!shdr_table_dirty) { | ||
| 636 | // Then it won't get written with the others and we need to do it. | ||
| 637 | try self.writeSectHeader(self.text_section_index.?); | ||
| 638 | } | ||
| 639 | } | ||
| 640 | |||
| 641 | return self.writeSymbols(); | ||
| 642 | } | 738 | } |
| 643 | 739 | ||
| 644 | fn writeProgHeader(self: *Update, index: usize) !void { | 740 | fn writeProgHeader(self: *ElfFile, index: usize) !void { |
| 645 | const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 741 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 646 | const offset = self.program_headers.items[index].p_offset; | 742 | const offset = self.program_headers.items[index].p_offset; |
| 647 | switch (self.module.target.cpu.arch.ptrBitWidth()) { | 743 | switch (self.options.target.cpu.arch.ptrBitWidth()) { |
| 648 | 32 => { | 744 | 32 => { |
| 649 | var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; | 745 | var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; |
| 650 | if (foreign_endian) { | 746 | if (foreign_endian) { |
| ... | @@ -663,10 +759,10 @@ const Update = struct { | ... | @@ -663,10 +759,10 @@ const Update = struct { |
| 663 | } | 759 | } |
| 664 | } | 760 | } |
| 665 | 761 | ||
| 666 | fn writeSectHeader(self: *Update, index: usize) !void { | 762 | fn writeSectHeader(self: *ElfFile, index: usize) !void { |
| 667 | const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 763 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 668 | const offset = self.sections.items[index].sh_offset; | 764 | const offset = self.sections.items[index].sh_offset; |
| 669 | switch (self.module.target.cpu.arch.ptrBitWidth()) { | 765 | switch (self.options.target.cpu.arch.ptrBitWidth()) { |
| 670 | 32 => { | 766 | 32 => { |
| 671 | var shdr: [1]elf.Elf32_Shdr = undefined; | 767 | var shdr: [1]elf.Elf32_Shdr = undefined; |
| 672 | shdr[0] = sectHeaderTo32(self.sections.items[index]); | 768 | shdr[0] = sectHeaderTo32(self.sections.items[index]); |
| ... | @@ -686,13 +782,8 @@ const Update = struct { | ... | @@ -686,13 +782,8 @@ const Update = struct { |
| 686 | } | 782 | } |
| 687 | } | 783 | } |
| 688 | 784 | ||
| 689 | fn writeSymbols(self: *Update) !void { | 785 | fn writeSymbols(self: *ElfFile) !void { |
| 690 | const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) { | 786 | const small_ptr = self.ptr_width == .p32; |
| 691 | 32 => .p32, | ||
| 692 | 64 => .p64, | ||
| 693 | else => return error.UnsupportedArchitecture, | ||
| 694 | }; | ||
| 695 | const small_ptr = ptr_width == .p32; | ||
| 696 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | 787 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 697 | const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | 788 | const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| 698 | const sym_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); | 789 | const sym_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| ... | @@ -708,8 +799,8 @@ const Update = struct { | ... | @@ -708,8 +799,8 @@ const Update = struct { |
| 708 | syms_sect.sh_size = needed_size; | 799 | syms_sect.sh_size = needed_size; |
| 709 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); | 800 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); |
| 710 | const allocator = self.symbols.allocator; | 801 | const allocator = self.symbols.allocator; |
| 711 | const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 802 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 712 | switch (ptr_width) { | 803 | switch (self.ptr_width) { |
| 713 | .p32 => { | 804 | .p32 => { |
| 714 | const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len); | 805 | const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len); |
| 715 | defer allocator.free(buf); | 806 | defer allocator.free(buf); |
| ... | @@ -754,13 +845,13 @@ const Update = struct { | ... | @@ -754,13 +845,13 @@ const Update = struct { |
| 754 | 845 | ||
| 755 | /// Truncates the existing file contents and overwrites the contents. | 846 | /// Truncates the existing file contents and overwrites the contents. |
| 756 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 847 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 757 | pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | 848 | pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile { |
| 758 | switch (module.output_mode) { | 849 | switch (options.output_mode) { |
| 759 | .Exe => {}, | 850 | .Exe => {}, |
| 760 | .Obj => {}, | 851 | .Obj => {}, |
| 761 | .Lib => return error.TODOImplementWritingLibFiles, | 852 | .Lib => return error.TODOImplementWritingLibFiles, |
| 762 | } | 853 | } |
| 763 | switch (module.object_format) { | 854 | switch (options.object_format) { |
| 764 | .unknown => unreachable, // TODO remove this tag from the enum | 855 | .unknown => unreachable, // TODO remove this tag from the enum |
| 765 | .coff => return error.TODOImplementWritingCOFF, | 856 | .coff => return error.TODOImplementWritingCOFF, |
| 766 | .elf => {}, | 857 | .elf => {}, |
| ... | @@ -768,38 +859,79 @@ pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Resul | ... | @@ -768,38 +859,79 @@ pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Resul |
| 768 | .wasm => return error.TODOImplementWritingWasmObjects, | 859 | .wasm => return error.TODOImplementWritingWasmObjects, |
| 769 | } | 860 | } |
| 770 | 861 | ||
| 771 | var update = Update{ | 862 | var self: ElfFile = .{ |
| 863 | .allocator = allocator, | ||
| 772 | .file = file, | 864 | .file = file, |
| 773 | .module = &module, | 865 | .options = options, |
| 774 | .sections = std.ArrayList(elf.Elf64_Shdr).init(allocator), | 866 | .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) { |
| 775 | .shdr_table_offset = null, | 867 | 32 => .p32, |
| 776 | .program_headers = std.ArrayList(elf.Elf64_Phdr).init(allocator), | 868 | 64 => .p64, |
| 777 | .phdr_table_offset = null, | 869 | else => return error.UnsupportedELFArchitecture, |
| 778 | .phdr_load_re_index = null, | 870 | }, |
| 779 | .entry_addr = null, | 871 | .symtab_dirty = true, |
| 780 | .shstrtab = std.ArrayList(u8).init(allocator), | 872 | .shdr_table_dirty = true, |
| 781 | .shstrtab_index = null, | ||
| 782 | .text_section_index = null, | ||
| 783 | .symtab_section_index = null, | ||
| 784 | |||
| 785 | .symbols = std.ArrayList(elf.Elf64_Sym).init(allocator), | ||
| 786 | |||
| 787 | .errors = std.ArrayList(ErrorMsg).init(allocator), | ||
| 788 | }; | ||
| 789 | defer update.deinit(); | ||
| 790 | |||
| 791 | try update.perform(); | ||
| 792 | return Result{ | ||
| 793 | .errors = update.errors.toOwnedSlice(), | ||
| 794 | }; | 873 | }; |
| 874 | errdefer self.deinit(); | ||
| 875 | |||
| 876 | // Index 0 is always a null symbol. | ||
| 877 | try self.symbols.append(allocator, .{ | ||
| 878 | .st_name = 0, | ||
| 879 | .st_info = 0, | ||
| 880 | .st_other = 0, | ||
| 881 | .st_shndx = 0, | ||
| 882 | .st_value = 0, | ||
| 883 | .st_size = 0, | ||
| 884 | }); | ||
| 885 | |||
| 886 | // There must always be a null section in index 0 | ||
| 887 | try self.sections.append(allocator, .{ | ||
| 888 | .sh_name = 0, | ||
| 889 | .sh_type = elf.SHT_NULL, | ||
| 890 | .sh_flags = 0, | ||
| 891 | .sh_addr = 0, | ||
| 892 | .sh_offset = 0, | ||
| 893 | .sh_size = 0, | ||
| 894 | .sh_link = 0, | ||
| 895 | .sh_info = 0, | ||
| 896 | .sh_addralign = 0, | ||
| 897 | .sh_entsize = 0, | ||
| 898 | }); | ||
| 899 | |||
| 900 | try self.populateMissingMetadata(); | ||
| 901 | |||
| 902 | return self; | ||
| 795 | } | 903 | } |
| 796 | 904 | ||
| 797 | /// Returns error.IncrFailed if incremental update could not be performed. | 905 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 798 | fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | 906 | fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile { |
| 799 | //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; | 907 | switch (options.output_mode) { |
| 908 | .Exe => {}, | ||
| 909 | .Obj => {}, | ||
| 910 | .Lib => return error.IncrFailed, | ||
| 911 | } | ||
| 912 | switch (options.object_format) { | ||
| 913 | .unknown => unreachable, // TODO remove this tag from the enum | ||
| 914 | .coff => return error.IncrFailed, | ||
| 915 | .elf => {}, | ||
| 916 | .macho => return error.IncrFailed, | ||
| 917 | .wasm => return error.IncrFailed, | ||
| 918 | } | ||
| 919 | var self: ElfFile = .{ | ||
| 920 | .allocator = allocator, | ||
| 921 | .file = file, | ||
| 922 | .options = options, | ||
| 923 | .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) { | ||
| 924 | 32 => .p32, | ||
| 925 | 64 => .p64, | ||
| 926 | else => return error.UnsupportedELFArchitecture, | ||
| 927 | }, | ||
| 928 | }; | ||
| 929 | errdefer self.deinit(); | ||
| 800 | 930 | ||
| 801 | // TODO implement incremental linking | 931 | // TODO implement reading the elf file |
| 802 | return error.IncrFailed; | 932 | return error.IncrFailed; |
| 933 | //try self.populateMissingMetadata(); | ||
| 934 | //return self; | ||
| 803 | } | 935 | } |
| 804 | 936 | ||
| 805 | /// Saturating multiplication | 937 | /// Saturating multiplication |
| ... | @@ -840,14 +972,14 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { | ... | @@ -840,14 +972,14 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { |
| 840 | }; | 972 | }; |
| 841 | } | 973 | } |
| 842 | 974 | ||
| 843 | fn determineMode(module: ir.Module) fs.File.Mode { | 975 | fn determineMode(options: Options) fs.File.Mode { |
| 844 | // On common systems with a 0o022 umask, 0o777 will still result in a file created | 976 | // On common systems with a 0o022 umask, 0o777 will still result in a file created |
| 845 | // with 0o755 permissions, but it works appropriately if the system is configured | 977 | // with 0o755 permissions, but it works appropriately if the system is configured |
| 846 | // more leniently. As another data point, C's fopen seems to open files with the | 978 | // more leniently. As another data point, C's fopen seems to open files with the |
| 847 | // 666 mode. | 979 | // 666 mode. |
| 848 | const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; | 980 | const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; |
| 849 | switch (module.output_mode) { | 981 | switch (options.output_mode) { |
| 850 | .Lib => return switch (module.link_mode) { | 982 | .Lib => return switch (options.link_mode) { |
| 851 | .Dynamic => executable_mode, | 983 | .Dynamic => executable_mode, |
| 852 | .Static => fs.File.default_mode, | 984 | .Static => fs.File.default_mode, |
| 853 | }, | 985 | }, |
src-self-hosted/package.zig deleted-31| ... | @@ -1,31 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const ArrayListSentineled = std.ArrayListSentineled; | ||
| 5 | |||
| 6 | pub const Package = struct { | ||
| 7 | root_src_dir: ArrayListSentineled(u8, 0), | ||
| 8 | root_src_path: ArrayListSentineled(u8, 0), | ||
| 9 | |||
| 10 | /// relative to root_src_dir | ||
| 11 | table: Table, | ||
| 12 | |||
| 13 | pub const Table = std.StringHashMap(*Package); | ||
| 14 | |||
| 15 | /// makes internal copies of root_src_dir and root_src_path | ||
| 16 | /// allocator should be an arena allocator because Package never frees anything | ||
| 17 | pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { | ||
| 18 | const ptr = try allocator.create(Package); | ||
| 19 | ptr.* = Package{ | ||
| 20 | .root_src_dir = try ArrayListSentineled(u8, 0).init(allocator, root_src_dir), | ||
| 21 | .root_src_path = try ArrayListSentineled(u8, 0).init(allocator, root_src_path), | ||
| 22 | .table = Table.init(allocator), | ||
| 23 | }; | ||
| 24 | return ptr; | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn add(self: *Package, name: []const u8, package: *Package) !void { | ||
| 28 | const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package); | ||
| 29 | assert(entry == null); | ||
| 30 | } | ||
| 31 | }; | ||
src-self-hosted/scope.zig deleted-418| ... | @@ -1,418 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Allocator = mem.Allocator; | ||
| 3 | const Decl = @import("decl.zig").Decl; | ||
| 4 | const Compilation = @import("compilation.zig").Compilation; | ||
| 5 | const mem = std.mem; | ||
| 6 | const ast = std.zig.ast; | ||
| 7 | const Value = @import("value.zig").Value; | ||
| 8 | const Type = @import("type.zig").Type; | ||
| 9 | const ir = @import("ir.zig"); | ||
| 10 | const Span = @import("errmsg.zig").Span; | ||
| 11 | const assert = std.debug.assert; | ||
| 12 | const event = std.event; | ||
| 13 | const llvm = @import("llvm.zig"); | ||
| 14 | |||
| 15 | pub const Scope = struct { | ||
| 16 | id: Id, | ||
| 17 | parent: ?*Scope, | ||
| 18 | ref_count: std.atomic.Int(usize), | ||
| 19 | |||
| 20 | /// Thread-safe | ||
| 21 | pub fn ref(base: *Scope) void { | ||
| 22 | _ = base.ref_count.incr(); | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Thread-safe | ||
| 26 | pub fn deref(base: *Scope, comp: *Compilation) void { | ||
| 27 | if (base.ref_count.decr() == 1) { | ||
| 28 | if (base.parent) |parent| parent.deref(comp); | ||
| 29 | switch (base.id) { | ||
| 30 | .Root => @fieldParentPtr(Root, "base", base).destroy(comp), | ||
| 31 | .Decls => @fieldParentPtr(Decls, "base", base).destroy(comp), | ||
| 32 | .Block => @fieldParentPtr(Block, "base", base).destroy(comp), | ||
| 33 | .FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), | ||
| 34 | .CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), | ||
| 35 | .Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), | ||
| 36 | .DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), | ||
| 37 | .Var => @fieldParentPtr(Var, "base", base).destroy(comp), | ||
| 38 | .AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn findRoot(base: *Scope) *Root { | ||
| 44 | var scope = base; | ||
| 45 | while (scope.parent) |parent| { | ||
| 46 | scope = parent; | ||
| 47 | } | ||
| 48 | assert(scope.id == .Root); | ||
| 49 | return @fieldParentPtr(Root, "base", scope); | ||
| 50 | } | ||
| 51 | |||
| 52 | pub fn findFnDef(base: *Scope) ?*FnDef { | ||
| 53 | var scope = base; | ||
| 54 | while (true) { | ||
| 55 | switch (scope.id) { | ||
| 56 | .FnDef => return @fieldParentPtr(FnDef, "base", scope), | ||
| 57 | .Root, .Decls => return null, | ||
| 58 | |||
| 59 | .Block, | ||
| 60 | .Defer, | ||
| 61 | .DeferExpr, | ||
| 62 | .CompTime, | ||
| 63 | .Var, | ||
| 64 | => scope = scope.parent.?, | ||
| 65 | |||
| 66 | .AstTree => unreachable, | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn findDeferExpr(base: *Scope) ?*DeferExpr { | ||
| 72 | var scope = base; | ||
| 73 | while (true) { | ||
| 74 | switch (scope.id) { | ||
| 75 | .DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope), | ||
| 76 | |||
| 77 | .FnDef, | ||
| 78 | .Decls, | ||
| 79 | => return null, | ||
| 80 | |||
| 81 | .Block, | ||
| 82 | .Defer, | ||
| 83 | .CompTime, | ||
| 84 | .Root, | ||
| 85 | .Var, | ||
| 86 | => scope = scope.parent orelse return null, | ||
| 87 | |||
| 88 | .AstTree => unreachable, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | fn init(base: *Scope, id: Id, parent: *Scope) void { | ||
| 94 | base.* = Scope{ | ||
| 95 | .id = id, | ||
| 96 | .parent = parent, | ||
| 97 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 98 | }; | ||
| 99 | parent.ref(); | ||
| 100 | } | ||
| 101 | |||
| 102 | pub const Id = enum { | ||
| 103 | Root, | ||
| 104 | AstTree, | ||
| 105 | Decls, | ||
| 106 | Block, | ||
| 107 | FnDef, | ||
| 108 | CompTime, | ||
| 109 | Defer, | ||
| 110 | DeferExpr, | ||
| 111 | Var, | ||
| 112 | }; | ||
| 113 | |||
| 114 | pub const Root = struct { | ||
| 115 | base: Scope, | ||
| 116 | realpath: []const u8, | ||
| 117 | decls: *Decls, | ||
| 118 | |||
| 119 | /// Creates a Root scope with 1 reference | ||
| 120 | /// Takes ownership of realpath | ||
| 121 | pub fn create(comp: *Compilation, realpath: []u8) !*Root { | ||
| 122 | const self = try comp.gpa().create(Root); | ||
| 123 | self.* = Root{ | ||
| 124 | .base = Scope{ | ||
| 125 | .id = .Root, | ||
| 126 | .parent = null, | ||
| 127 | .ref_count = std.atomic.Int(usize).init(1), | ||
| 128 | }, | ||
| 129 | .realpath = realpath, | ||
| 130 | .decls = undefined, | ||
| 131 | }; | ||
| 132 | errdefer comp.gpa().destroy(self); | ||
| 133 | self.decls = try Decls.create(comp, &self.base); | ||
| 134 | return self; | ||
| 135 | } | ||
| 136 | |||
| 137 | pub fn destroy(self: *Root, comp: *Compilation) void { | ||
| 138 | // TODO comp.fs_watch.removeFile(self.realpath); | ||
| 139 | self.decls.base.deref(comp); | ||
| 140 | comp.gpa().free(self.realpath); | ||
| 141 | comp.gpa().destroy(self); | ||
| 142 | } | ||
| 143 | }; | ||
| 144 | |||
| 145 | pub const AstTree = struct { | ||
| 146 | base: Scope, | ||
| 147 | tree: *ast.Tree, | ||
| 148 | |||
| 149 | /// Creates a scope with 1 reference | ||
| 150 | /// Takes ownership of tree, will deinit and destroy when done. | ||
| 151 | pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree { | ||
| 152 | const self = try comp.gpa().create(AstTree); | ||
| 153 | self.* = AstTree{ | ||
| 154 | .base = undefined, | ||
| 155 | .tree = tree, | ||
| 156 | }; | ||
| 157 | self.base.init(.AstTree, &root_scope.base); | ||
| 158 | |||
| 159 | return self; | ||
| 160 | } | ||
| 161 | |||
| 162 | pub fn destroy(self: *AstTree, comp: *Compilation) void { | ||
| 163 | comp.gpa().free(self.tree.source); | ||
| 164 | self.tree.deinit(); | ||
| 165 | comp.gpa().destroy(self); | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn root(self: *AstTree) *Root { | ||
| 169 | return self.base.findRoot(); | ||
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | pub const Decls = struct { | ||
| 174 | base: Scope, | ||
| 175 | |||
| 176 | /// This table remains Write Locked when the names are incomplete or possibly outdated. | ||
| 177 | /// So if a reader manages to grab a lock, it can be sure that the set of names is complete | ||
| 178 | /// and correct. | ||
| 179 | table: event.RwLocked(Decl.Table), | ||
| 180 | |||
| 181 | /// Creates a Decls scope with 1 reference | ||
| 182 | pub fn create(comp: *Compilation, parent: *Scope) !*Decls { | ||
| 183 | const self = try comp.gpa().create(Decls); | ||
| 184 | self.* = Decls{ | ||
| 185 | .base = undefined, | ||
| 186 | .table = event.RwLocked(Decl.Table).init(Decl.Table.init(comp.gpa())), | ||
| 187 | }; | ||
| 188 | self.base.init(.Decls, parent); | ||
| 189 | return self; | ||
| 190 | } | ||
| 191 | |||
| 192 | pub fn destroy(self: *Decls, comp: *Compilation) void { | ||
| 193 | self.table.deinit(); | ||
| 194 | comp.gpa().destroy(self); | ||
| 195 | } | ||
| 196 | }; | ||
| 197 | |||
| 198 | pub const Block = struct { | ||
| 199 | base: Scope, | ||
| 200 | incoming_values: std.ArrayList(*ir.Inst), | ||
| 201 | incoming_blocks: std.ArrayList(*ir.BasicBlock), | ||
| 202 | end_block: *ir.BasicBlock, | ||
| 203 | is_comptime: *ir.Inst, | ||
| 204 | |||
| 205 | safety: Safety, | ||
| 206 | |||
| 207 | const Safety = union(enum) { | ||
| 208 | Auto, | ||
| 209 | Manual: Manual, | ||
| 210 | |||
| 211 | const Manual = struct { | ||
| 212 | /// the source span that disabled the safety value | ||
| 213 | span: Span, | ||
| 214 | |||
| 215 | /// whether safety is enabled | ||
| 216 | enabled: bool, | ||
| 217 | }; | ||
| 218 | |||
| 219 | fn get(self: Safety, comp: *Compilation) bool { | ||
| 220 | return switch (self) { | ||
| 221 | .Auto => switch (comp.build_mode) { | ||
| 222 | .Debug, | ||
| 223 | .ReleaseSafe, | ||
| 224 | => true, | ||
| 225 | .ReleaseFast, | ||
| 226 | .ReleaseSmall, | ||
| 227 | => false, | ||
| 228 | }, | ||
| 229 | .Manual => |man| man.enabled, | ||
| 230 | }; | ||
| 231 | } | ||
| 232 | }; | ||
| 233 | |||
| 234 | /// Creates a Block scope with 1 reference | ||
| 235 | pub fn create(comp: *Compilation, parent: *Scope) !*Block { | ||
| 236 | const self = try comp.gpa().create(Block); | ||
| 237 | self.* = Block{ | ||
| 238 | .base = undefined, | ||
| 239 | .incoming_values = undefined, | ||
| 240 | .incoming_blocks = undefined, | ||
| 241 | .end_block = undefined, | ||
| 242 | .is_comptime = undefined, | ||
| 243 | .safety = Safety.Auto, | ||
| 244 | }; | ||
| 245 | self.base.init(.Block, parent); | ||
| 246 | return self; | ||
| 247 | } | ||
| 248 | |||
| 249 | pub fn destroy(self: *Block, comp: *Compilation) void { | ||
| 250 | comp.gpa().destroy(self); | ||
| 251 | } | ||
| 252 | }; | ||
| 253 | |||
| 254 | pub const FnDef = struct { | ||
| 255 | base: Scope, | ||
| 256 | |||
| 257 | /// This reference is not counted so that the scope can get destroyed with the function | ||
| 258 | fn_val: ?*Value.Fn, | ||
| 259 | |||
| 260 | /// Creates a FnDef scope with 1 reference | ||
| 261 | /// Must set the fn_val later | ||
| 262 | pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { | ||
| 263 | const self = try comp.gpa().create(FnDef); | ||
| 264 | self.* = FnDef{ | ||
| 265 | .base = undefined, | ||
| 266 | .fn_val = null, | ||
| 267 | }; | ||
| 268 | self.base.init(.FnDef, parent); | ||
| 269 | return self; | ||
| 270 | } | ||
| 271 | |||
| 272 | pub fn destroy(self: *FnDef, comp: *Compilation) void { | ||
| 273 | comp.gpa().destroy(self); | ||
| 274 | } | ||
| 275 | }; | ||
| 276 | |||
| 277 | pub const CompTime = struct { | ||
| 278 | base: Scope, | ||
| 279 | |||
| 280 | /// Creates a CompTime scope with 1 reference | ||
| 281 | pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { | ||
| 282 | const self = try comp.gpa().create(CompTime); | ||
| 283 | self.* = CompTime{ .base = undefined }; | ||
| 284 | self.base.init(.CompTime, parent); | ||
| 285 | return self; | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn destroy(self: *CompTime, comp: *Compilation) void { | ||
| 289 | comp.gpa().destroy(self); | ||
| 290 | } | ||
| 291 | }; | ||
| 292 | |||
| 293 | pub const Defer = struct { | ||
| 294 | base: Scope, | ||
| 295 | defer_expr_scope: *DeferExpr, | ||
| 296 | kind: Kind, | ||
| 297 | |||
| 298 | pub const Kind = enum { | ||
| 299 | ScopeExit, | ||
| 300 | ErrorExit, | ||
| 301 | }; | ||
| 302 | |||
| 303 | /// Creates a Defer scope with 1 reference | ||
| 304 | pub fn create( | ||
| 305 | comp: *Compilation, | ||
| 306 | parent: *Scope, | ||
| 307 | kind: Kind, | ||
| 308 | defer_expr_scope: *DeferExpr, | ||
| 309 | ) !*Defer { | ||
| 310 | const self = try comp.gpa().create(Defer); | ||
| 311 | self.* = Defer{ | ||
| 312 | .base = undefined, | ||
| 313 | .defer_expr_scope = defer_expr_scope, | ||
| 314 | .kind = kind, | ||
| 315 | }; | ||
| 316 | self.base.init(.Defer, parent); | ||
| 317 | defer_expr_scope.base.ref(); | ||
| 318 | return self; | ||
| 319 | } | ||
| 320 | |||
| 321 | pub fn destroy(self: *Defer, comp: *Compilation) void { | ||
| 322 | self.defer_expr_scope.base.deref(comp); | ||
| 323 | comp.gpa().destroy(self); | ||
| 324 | } | ||
| 325 | }; | ||
| 326 | |||
| 327 | pub const DeferExpr = struct { | ||
| 328 | base: Scope, | ||
| 329 | expr_node: *ast.Node, | ||
| 330 | reported_err: bool, | ||
| 331 | |||
| 332 | /// Creates a DeferExpr scope with 1 reference | ||
| 333 | pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { | ||
| 334 | const self = try comp.gpa().create(DeferExpr); | ||
| 335 | self.* = DeferExpr{ | ||
| 336 | .base = undefined, | ||
| 337 | .expr_node = expr_node, | ||
| 338 | .reported_err = false, | ||
| 339 | }; | ||
| 340 | self.base.init(.DeferExpr, parent); | ||
| 341 | return self; | ||
| 342 | } | ||
| 343 | |||
| 344 | pub fn destroy(self: *DeferExpr, comp: *Compilation) void { | ||
| 345 | comp.gpa().destroy(self); | ||
| 346 | } | ||
| 347 | }; | ||
| 348 | |||
| 349 | pub const Var = struct { | ||
| 350 | base: Scope, | ||
| 351 | name: []const u8, | ||
| 352 | src_node: *ast.Node, | ||
| 353 | data: Data, | ||
| 354 | |||
| 355 | pub const Data = union(enum) { | ||
| 356 | Param: Param, | ||
| 357 | Const: *Value, | ||
| 358 | }; | ||
| 359 | |||
| 360 | pub const Param = struct { | ||
| 361 | index: usize, | ||
| 362 | typ: *Type, | ||
| 363 | llvm_value: *llvm.Value, | ||
| 364 | }; | ||
| 365 | |||
| 366 | pub fn createParam( | ||
| 367 | comp: *Compilation, | ||
| 368 | parent: *Scope, | ||
| 369 | name: []const u8, | ||
| 370 | src_node: *ast.Node, | ||
| 371 | param_index: usize, | ||
| 372 | param_type: *Type, | ||
| 373 | ) !*Var { | ||
| 374 | const self = try create(comp, parent, name, src_node); | ||
| 375 | self.data = Data{ | ||
| 376 | .Param = Param{ | ||
| 377 | .index = param_index, | ||
| 378 | .typ = param_type, | ||
| 379 | .llvm_value = undefined, | ||
| 380 | }, | ||
| 381 | }; | ||
| 382 | return self; | ||
| 383 | } | ||
| 384 | |||
| 385 | pub fn createConst( | ||
| 386 | comp: *Compilation, | ||
| 387 | parent: *Scope, | ||
| 388 | name: []const u8, | ||
| 389 | src_node: *ast.Node, | ||
| 390 | value: *Value, | ||
| 391 | ) !*Var { | ||
| 392 | const self = try create(comp, parent, name, src_node); | ||
| 393 | self.data = Data{ .Const = value }; | ||
| 394 | value.ref(); | ||
| 395 | return self; | ||
| 396 | } | ||
| 397 | |||
| 398 | fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var { | ||
| 399 | const self = try comp.gpa().create(Var); | ||
| 400 | self.* = Var{ | ||
| 401 | .base = undefined, | ||
| 402 | .name = name, | ||
| 403 | .src_node = src_node, | ||
| 404 | .data = undefined, | ||
| 405 | }; | ||
| 406 | self.base.init(.Var, parent); | ||
| 407 | return self; | ||
| 408 | } | ||
| 409 | |||
| 410 | pub fn destroy(self: *Var, comp: *Compilation) void { | ||
| 411 | switch (self.data) { | ||
| 412 | .Param => {}, | ||
| 413 | .Const => |value| value.deref(comp), | ||
| 414 | } | ||
| 415 | comp.gpa().destroy(self); | ||
| 416 | } | ||
| 417 | }; | ||
| 418 | }; | ||
src-self-hosted/test.zig+5-6| ... | @@ -3,15 +3,14 @@ const link = @import("link.zig"); | ... | @@ -3,15 +3,14 @@ const link = @import("link.zig"); |
| 3 | const ir = @import("ir.zig"); | 3 | const ir = @import("ir.zig"); |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | ||
| 6 | var global_ctx: TestContext = undefined; | ||
| 7 | |||
| 8 | test "self-hosted" { | 6 | test "self-hosted" { |
| 9 | try global_ctx.init(); | 7 | var ctx: TestContext = undefined; |
| 10 | defer global_ctx.deinit(); | 8 | try ctx.init(); |
| 9 | defer ctx.deinit(); | ||
| 11 | 10 | ||
| 12 | try @import("stage2_tests").addCases(&global_ctx); | 11 | try @import("stage2_tests").addCases(&ctx); |
| 13 | 12 | ||
| 14 | try global_ctx.run(); | 13 | try ctx.run(); |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 17 | pub const TestContext = struct { | 16 | pub const TestContext = struct { |
src-self-hosted/type.zig+62-1| ... | @@ -52,6 +52,7 @@ pub const Type = extern union { | ... | @@ -52,6 +52,7 @@ pub const Type = extern union { |
| 52 | .comptime_float => return .ComptimeFloat, | 52 | .comptime_float => return .ComptimeFloat, |
| 53 | .noreturn => return .NoReturn, | 53 | .noreturn => return .NoReturn, |
| 54 | 54 | ||
| 55 | .fn_noreturn_no_args => return .Fn, | ||
| 55 | .fn_naked_noreturn_no_args => return .Fn, | 56 | .fn_naked_noreturn_no_args => return .Fn, |
| 56 | .fn_ccc_void_no_args => return .Fn, | 57 | .fn_ccc_void_no_args => return .Fn, |
| 57 | 58 | ||
| ... | @@ -184,6 +185,7 @@ pub const Type = extern union { | ... | @@ -184,6 +185,7 @@ pub const Type = extern union { |
| 184 | => return out_stream.writeAll(@tagName(t)), | 185 | => return out_stream.writeAll(@tagName(t)), |
| 185 | 186 | ||
| 186 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | 187 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 188 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), | ||
| 187 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | 189 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 188 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), | 190 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), |
| 189 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), | 191 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), |
| ... | @@ -244,6 +246,7 @@ pub const Type = extern union { | ... | @@ -244,6 +246,7 @@ pub const Type = extern union { |
| 244 | .comptime_int => return Value.initTag(.comptime_int_type), | 246 | .comptime_int => return Value.initTag(.comptime_int_type), |
| 245 | .comptime_float => return Value.initTag(.comptime_float_type), | 247 | .comptime_float => return Value.initTag(.comptime_float_type), |
| 246 | .noreturn => return Value.initTag(.noreturn_type), | 248 | .noreturn => return Value.initTag(.noreturn_type), |
| 249 | .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type), | ||
| 247 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), | 250 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 248 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), | 251 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), |
| 249 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), | 252 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), |
| ... | @@ -286,6 +289,7 @@ pub const Type = extern union { | ... | @@ -286,6 +289,7 @@ pub const Type = extern union { |
| 286 | .array, | 289 | .array, |
| 287 | .array_u8_sentinel_0, | 290 | .array_u8_sentinel_0, |
| 288 | .const_slice_u8, | 291 | .const_slice_u8, |
| 292 | .fn_noreturn_no_args, | ||
| 289 | .fn_naked_noreturn_no_args, | 293 | .fn_naked_noreturn_no_args, |
| 290 | .fn_ccc_void_no_args, | 294 | .fn_ccc_void_no_args, |
| 291 | .int_unsigned, | 295 | .int_unsigned, |
| ... | @@ -329,6 +333,7 @@ pub const Type = extern union { | ... | @@ -329,6 +333,7 @@ pub const Type = extern union { |
| 329 | .array_u8_sentinel_0, | 333 | .array_u8_sentinel_0, |
| 330 | .single_const_pointer, | 334 | .single_const_pointer, |
| 331 | .single_const_pointer_to_comptime_int, | 335 | .single_const_pointer_to_comptime_int, |
| 336 | .fn_noreturn_no_args, | ||
| 332 | .fn_naked_noreturn_no_args, | 337 | .fn_naked_noreturn_no_args, |
| 333 | .fn_ccc_void_no_args, | 338 | .fn_ccc_void_no_args, |
| 334 | .int_unsigned, | 339 | .int_unsigned, |
| ... | @@ -369,6 +374,7 @@ pub const Type = extern union { | ... | @@ -369,6 +374,7 @@ pub const Type = extern union { |
| 369 | .noreturn, | 374 | .noreturn, |
| 370 | .array, | 375 | .array, |
| 371 | .array_u8_sentinel_0, | 376 | .array_u8_sentinel_0, |
| 377 | .fn_noreturn_no_args, | ||
| 372 | .fn_naked_noreturn_no_args, | 378 | .fn_naked_noreturn_no_args, |
| 373 | .fn_ccc_void_no_args, | 379 | .fn_ccc_void_no_args, |
| 374 | .int_unsigned, | 380 | .int_unsigned, |
| ... | @@ -410,6 +416,7 @@ pub const Type = extern union { | ... | @@ -410,6 +416,7 @@ pub const Type = extern union { |
| 410 | .comptime_int, | 416 | .comptime_int, |
| 411 | .comptime_float, | 417 | .comptime_float, |
| 412 | .noreturn, | 418 | .noreturn, |
| 419 | .fn_noreturn_no_args, | ||
| 413 | .fn_naked_noreturn_no_args, | 420 | .fn_naked_noreturn_no_args, |
| 414 | .fn_ccc_void_no_args, | 421 | .fn_ccc_void_no_args, |
| 415 | .int_unsigned, | 422 | .int_unsigned, |
| ... | @@ -451,6 +458,7 @@ pub const Type = extern union { | ... | @@ -451,6 +458,7 @@ pub const Type = extern union { |
| 451 | .comptime_int, | 458 | .comptime_int, |
| 452 | .comptime_float, | 459 | .comptime_float, |
| 453 | .noreturn, | 460 | .noreturn, |
| 461 | .fn_noreturn_no_args, | ||
| 454 | .fn_naked_noreturn_no_args, | 462 | .fn_naked_noreturn_no_args, |
| 455 | .fn_ccc_void_no_args, | 463 | .fn_ccc_void_no_args, |
| 456 | .single_const_pointer, | 464 | .single_const_pointer, |
| ... | @@ -481,6 +489,7 @@ pub const Type = extern union { | ... | @@ -481,6 +489,7 @@ pub const Type = extern union { |
| 481 | .comptime_int, | 489 | .comptime_int, |
| 482 | .comptime_float, | 490 | .comptime_float, |
| 483 | .noreturn, | 491 | .noreturn, |
| 492 | .fn_noreturn_no_args, | ||
| 484 | .fn_naked_noreturn_no_args, | 493 | .fn_naked_noreturn_no_args, |
| 485 | .fn_ccc_void_no_args, | 494 | .fn_ccc_void_no_args, |
| 486 | .array, | 495 | .array, |
| ... | @@ -524,6 +533,7 @@ pub const Type = extern union { | ... | @@ -524,6 +533,7 @@ pub const Type = extern union { |
| 524 | .comptime_int, | 533 | .comptime_int, |
| 525 | .comptime_float, | 534 | .comptime_float, |
| 526 | .noreturn, | 535 | .noreturn, |
| 536 | .fn_noreturn_no_args, | ||
| 527 | .fn_naked_noreturn_no_args, | 537 | .fn_naked_noreturn_no_args, |
| 528 | .fn_ccc_void_no_args, | 538 | .fn_ccc_void_no_args, |
| 529 | .array, | 539 | .array, |
| ... | @@ -579,6 +589,7 @@ pub const Type = extern union { | ... | @@ -579,6 +589,7 @@ pub const Type = extern union { |
| 579 | /// Asserts the type is a function. | 589 | /// Asserts the type is a function. |
| 580 | pub fn fnParamLen(self: Type) usize { | 590 | pub fn fnParamLen(self: Type) usize { |
| 581 | return switch (self.tag()) { | 591 | return switch (self.tag()) { |
| 592 | .fn_noreturn_no_args => 0, | ||
| 582 | .fn_naked_noreturn_no_args => 0, | 593 | .fn_naked_noreturn_no_args => 0, |
| 583 | .fn_ccc_void_no_args => 0, | 594 | .fn_ccc_void_no_args => 0, |
| 584 | 595 | ||
| ... | @@ -622,6 +633,7 @@ pub const Type = extern union { | ... | @@ -622,6 +633,7 @@ pub const Type = extern union { |
| 622 | /// given by `fnParamLen`. | 633 | /// given by `fnParamLen`. |
| 623 | pub fn fnParamTypes(self: Type, types: []Type) void { | 634 | pub fn fnParamTypes(self: Type, types: []Type) void { |
| 624 | switch (self.tag()) { | 635 | switch (self.tag()) { |
| 636 | .fn_noreturn_no_args => return, | ||
| 625 | .fn_naked_noreturn_no_args => return, | 637 | .fn_naked_noreturn_no_args => return, |
| 626 | .fn_ccc_void_no_args => return, | 638 | .fn_ccc_void_no_args => return, |
| 627 | 639 | ||
| ... | @@ -664,6 +676,7 @@ pub const Type = extern union { | ... | @@ -664,6 +676,7 @@ pub const Type = extern union { |
| 664 | /// Asserts the type is a function. | 676 | /// Asserts the type is a function. |
| 665 | pub fn fnReturnType(self: Type) Type { | 677 | pub fn fnReturnType(self: Type) Type { |
| 666 | return switch (self.tag()) { | 678 | return switch (self.tag()) { |
| 679 | .fn_noreturn_no_args => Type.initTag(.noreturn), | ||
| 667 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), | 680 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), |
| 668 | .fn_ccc_void_no_args => Type.initTag(.void), | 681 | .fn_ccc_void_no_args => Type.initTag(.void), |
| 669 | 682 | ||
| ... | @@ -706,6 +719,7 @@ pub const Type = extern union { | ... | @@ -706,6 +719,7 @@ pub const Type = extern union { |
| 706 | /// Asserts the type is a function. | 719 | /// Asserts the type is a function. |
| 707 | pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { | 720 | pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { |
| 708 | return switch (self.tag()) { | 721 | return switch (self.tag()) { |
| 722 | .fn_noreturn_no_args => .Unspecified, | ||
| 709 | .fn_naked_noreturn_no_args => .Naked, | 723 | .fn_naked_noreturn_no_args => .Naked, |
| 710 | .fn_ccc_void_no_args => .C, | 724 | .fn_ccc_void_no_args => .C, |
| 711 | 725 | ||
| ... | @@ -745,6 +759,49 @@ pub const Type = extern union { | ... | @@ -745,6 +759,49 @@ pub const Type = extern union { |
| 745 | }; | 759 | }; |
| 746 | } | 760 | } |
| 747 | 761 | ||
| 762 | /// Asserts the type is a function. | ||
| 763 | pub fn fnIsVarArgs(self: Type) bool { | ||
| 764 | return switch (self.tag()) { | ||
| 765 | .fn_noreturn_no_args => false, | ||
| 766 | .fn_naked_noreturn_no_args => false, | ||
| 767 | .fn_ccc_void_no_args => false, | ||
| 768 | |||
| 769 | .f16, | ||
| 770 | .f32, | ||
| 771 | .f64, | ||
| 772 | .f128, | ||
| 773 | .c_longdouble, | ||
| 774 | .c_void, | ||
| 775 | .bool, | ||
| 776 | .void, | ||
| 777 | .type, | ||
| 778 | .anyerror, | ||
| 779 | .comptime_int, | ||
| 780 | .comptime_float, | ||
| 781 | .noreturn, | ||
| 782 | .array, | ||
| 783 | .single_const_pointer, | ||
| 784 | .single_const_pointer_to_comptime_int, | ||
| 785 | .array_u8_sentinel_0, | ||
| 786 | .const_slice_u8, | ||
| 787 | .u8, | ||
| 788 | .i8, | ||
| 789 | .usize, | ||
| 790 | .isize, | ||
| 791 | .c_short, | ||
| 792 | .c_ushort, | ||
| 793 | .c_int, | ||
| 794 | .c_uint, | ||
| 795 | .c_long, | ||
| 796 | .c_ulong, | ||
| 797 | .c_longlong, | ||
| 798 | .c_ulonglong, | ||
| 799 | .int_unsigned, | ||
| 800 | .int_signed, | ||
| 801 | => unreachable, | ||
| 802 | }; | ||
| 803 | } | ||
| 804 | |||
| 748 | pub fn isNumeric(self: Type) bool { | 805 | pub fn isNumeric(self: Type) bool { |
| 749 | return switch (self.tag()) { | 806 | return switch (self.tag()) { |
| 750 | .f16, | 807 | .f16, |
| ... | @@ -776,6 +833,7 @@ pub const Type = extern union { | ... | @@ -776,6 +833,7 @@ pub const Type = extern union { |
| 776 | .type, | 833 | .type, |
| 777 | .anyerror, | 834 | .anyerror, |
| 778 | .noreturn, | 835 | .noreturn, |
| 836 | .fn_noreturn_no_args, | ||
| 779 | .fn_naked_noreturn_no_args, | 837 | .fn_naked_noreturn_no_args, |
| 780 | .fn_ccc_void_no_args, | 838 | .fn_ccc_void_no_args, |
| 781 | .array, | 839 | .array, |
| ... | @@ -812,6 +870,7 @@ pub const Type = extern union { | ... | @@ -812,6 +870,7 @@ pub const Type = extern union { |
| 812 | .bool, | 870 | .bool, |
| 813 | .type, | 871 | .type, |
| 814 | .anyerror, | 872 | .anyerror, |
| 873 | .fn_noreturn_no_args, | ||
| 815 | .fn_naked_noreturn_no_args, | 874 | .fn_naked_noreturn_no_args, |
| 816 | .fn_ccc_void_no_args, | 875 | .fn_ccc_void_no_args, |
| 817 | .single_const_pointer_to_comptime_int, | 876 | .single_const_pointer_to_comptime_int, |
| ... | @@ -865,6 +924,7 @@ pub const Type = extern union { | ... | @@ -865,6 +924,7 @@ pub const Type = extern union { |
| 865 | .bool, | 924 | .bool, |
| 866 | .type, | 925 | .type, |
| 867 | .anyerror, | 926 | .anyerror, |
| 927 | .fn_noreturn_no_args, | ||
| 868 | .fn_naked_noreturn_no_args, | 928 | .fn_naked_noreturn_no_args, |
| 869 | .fn_ccc_void_no_args, | 929 | .fn_ccc_void_no_args, |
| 870 | .single_const_pointer_to_comptime_int, | 930 | .single_const_pointer_to_comptime_int, |
| ... | @@ -902,11 +962,11 @@ pub const Type = extern union { | ... | @@ -902,11 +962,11 @@ pub const Type = extern union { |
| 902 | c_longlong, | 962 | c_longlong, |
| 903 | c_ulonglong, | 963 | c_ulonglong, |
| 904 | c_longdouble, | 964 | c_longdouble, |
| 905 | c_void, | ||
| 906 | f16, | 965 | f16, |
| 907 | f32, | 966 | f32, |
| 908 | f64, | 967 | f64, |
| 909 | f128, | 968 | f128, |
| 969 | c_void, | ||
| 910 | bool, | 970 | bool, |
| 911 | void, | 971 | void, |
| 912 | type, | 972 | type, |
| ... | @@ -914,6 +974,7 @@ pub const Type = extern union { | ... | @@ -914,6 +974,7 @@ pub const Type = extern union { |
| 914 | comptime_int, | 974 | comptime_int, |
| 915 | comptime_float, | 975 | comptime_float, |
| 916 | noreturn, | 976 | noreturn, |
| 977 | fn_noreturn_no_args, | ||
| 917 | fn_naked_noreturn_no_args, | 978 | fn_naked_noreturn_no_args, |
| 918 | fn_ccc_void_no_args, | 979 | fn_ccc_void_no_args, |
| 919 | single_const_pointer_to_comptime_int, | 980 | single_const_pointer_to_comptime_int, |
src-self-hosted/util.zig deleted-47| ... | @@ -1,47 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Target = std.Target; | ||
| 3 | const llvm = @import("llvm.zig"); | ||
| 4 | |||
| 5 | pub fn getDarwinArchString(self: Target) [:0]const u8 { | ||
| 6 | switch (self.cpu.arch) { | ||
| 7 | .aarch64 => return "arm64", | ||
| 8 | .thumb, | ||
| 9 | .arm, | ||
| 10 | => return "arm", | ||
| 11 | .powerpc => return "ppc", | ||
| 12 | .powerpc64 => return "ppc64", | ||
| 13 | .powerpc64le => return "ppc64le", | ||
| 14 | // @tagName should be able to return sentinel terminated slice | ||
| 15 | else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch), | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target { | ||
| 20 | var result: *llvm.Target = undefined; | ||
| 21 | var err_msg: [*:0]u8 = undefined; | ||
| 22 | if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) { | ||
| 23 | std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg }); | ||
| 24 | return error.UnsupportedTarget; | ||
| 25 | } | ||
| 26 | return result; | ||
| 27 | } | ||
| 28 | |||
| 29 | pub fn initializeAllTargets() void { | ||
| 30 | llvm.InitializeAllTargets(); | ||
| 31 | llvm.InitializeAllTargetInfos(); | ||
| 32 | llvm.InitializeAllTargetMCs(); | ||
| 33 | llvm.InitializeAllAsmPrinters(); | ||
| 34 | llvm.InitializeAllAsmParsers(); | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 { | ||
| 38 | var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0); | ||
| 39 | defer result.deinit(); | ||
| 40 | |||
| 41 | try result.outStream().print( | ||
| 42 | "{}-unknown-{}-{}", | ||
| 43 | .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) }, | ||
| 44 | ); | ||
| 45 | |||
| 46 | return result.toOwnedSlice(); | ||
| 47 | } | ||
src-self-hosted/value.zig+96-90| ... | @@ -6,6 +6,7 @@ const BigIntConst = std.math.big.int.Const; | ... | @@ -6,6 +6,7 @@ const BigIntConst = std.math.big.int.Const; |
| 6 | const BigIntMutable = std.math.big.int.Mutable; | 6 | const BigIntMutable = std.math.big.int.Mutable; |
| 7 | const Target = std.Target; | 7 | const Target = std.Target; |
| 8 | const Allocator = std.mem.Allocator; | 8 | const Allocator = std.mem.Allocator; |
| 9 | const ir = @import("ir.zig"); | ||
| 9 | 10 | ||
| 10 | /// This is the raw data, with no bookkeeping, no memory awareness, | 11 | /// This is the raw data, with no bookkeeping, no memory awareness, |
| 11 | /// no de-duplication, and no type system awareness. | 12 | /// no de-duplication, and no type system awareness. |
| ... | @@ -45,6 +46,7 @@ pub const Value = extern union { | ... | @@ -45,6 +46,7 @@ pub const Value = extern union { |
| 45 | comptime_int_type, | 46 | comptime_int_type, |
| 46 | comptime_float_type, | 47 | comptime_float_type, |
| 47 | noreturn_type, | 48 | noreturn_type, |
| 49 | fn_noreturn_no_args_type, | ||
| 48 | fn_naked_noreturn_no_args_type, | 50 | fn_naked_noreturn_no_args_type, |
| 49 | fn_ccc_void_no_args_type, | 51 | fn_ccc_void_no_args_type, |
| 50 | single_const_pointer_to_comptime_int_type, | 52 | single_const_pointer_to_comptime_int_type, |
| ... | @@ -64,8 +66,8 @@ pub const Value = extern union { | ... | @@ -64,8 +66,8 @@ pub const Value = extern union { |
| 64 | int_big_positive, | 66 | int_big_positive, |
| 65 | int_big_negative, | 67 | int_big_negative, |
| 66 | function, | 68 | function, |
| 67 | ref, | 69 | decl_ref, |
| 68 | ref_val, | 70 | elem_ptr, |
| 69 | bytes, | 71 | bytes, |
| 70 | repeated, // the value is a value repeated some number of times | 72 | repeated, // the value is a value repeated some number of times |
| 71 | 73 | ||
| ... | @@ -136,6 +138,7 @@ pub const Value = extern union { | ... | @@ -136,6 +138,7 @@ pub const Value = extern union { |
| 136 | .comptime_int_type => return out_stream.writeAll("comptime_int"), | 138 | .comptime_int_type => return out_stream.writeAll("comptime_int"), |
| 137 | .comptime_float_type => return out_stream.writeAll("comptime_float"), | 139 | .comptime_float_type => return out_stream.writeAll("comptime_float"), |
| 138 | .noreturn_type => return out_stream.writeAll("noreturn"), | 140 | .noreturn_type => return out_stream.writeAll("noreturn"), |
| 141 | .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"), | ||
| 139 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | 142 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 140 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), | 143 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), |
| 141 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), | 144 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| ... | @@ -153,11 +156,11 @@ pub const Value = extern union { | ... | @@ -153,11 +156,11 @@ pub const Value = extern union { |
| 153 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), | 156 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), |
| 154 | .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}), | 157 | .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}), |
| 155 | .function => return out_stream.writeAll("(function)"), | 158 | .function => return out_stream.writeAll("(function)"), |
| 156 | .ref => return out_stream.writeAll("(ref)"), | 159 | .decl_ref => return out_stream.writeAll("(decl ref)"), |
| 157 | .ref_val => { | 160 | .elem_ptr => { |
| 158 | try out_stream.writeAll("*const "); | 161 | const elem_ptr = val.cast(Payload.Int_u64).?; |
| 159 | val = val.cast(Payload.RefVal).?.val; | 162 | try out_stream.print("&[{}] ", .{elem_ptr.index}); |
| 160 | continue; | 163 | val = elem_ptr.array_ptr; |
| 161 | }, | 164 | }, |
| 162 | .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), | 165 | .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), |
| 163 | .repeated => { | 166 | .repeated => { |
| ... | @@ -181,31 +184,32 @@ pub const Value = extern union { | ... | @@ -181,31 +184,32 @@ pub const Value = extern union { |
| 181 | return switch (self.tag()) { | 184 | return switch (self.tag()) { |
| 182 | .ty => self.cast(Payload.Ty).?.ty, | 185 | .ty => self.cast(Payload.Ty).?.ty, |
| 183 | 186 | ||
| 184 | .u8_type => Type.initTag(.@"u8"), | 187 | .u8_type => Type.initTag(.u8), |
| 185 | .i8_type => Type.initTag(.@"i8"), | 188 | .i8_type => Type.initTag(.i8), |
| 186 | .isize_type => Type.initTag(.@"isize"), | 189 | .isize_type => Type.initTag(.isize), |
| 187 | .usize_type => Type.initTag(.@"usize"), | 190 | .usize_type => Type.initTag(.usize), |
| 188 | .c_short_type => Type.initTag(.@"c_short"), | 191 | .c_short_type => Type.initTag(.c_short), |
| 189 | .c_ushort_type => Type.initTag(.@"c_ushort"), | 192 | .c_ushort_type => Type.initTag(.c_ushort), |
| 190 | .c_int_type => Type.initTag(.@"c_int"), | 193 | .c_int_type => Type.initTag(.c_int), |
| 191 | .c_uint_type => Type.initTag(.@"c_uint"), | 194 | .c_uint_type => Type.initTag(.c_uint), |
| 192 | .c_long_type => Type.initTag(.@"c_long"), | 195 | .c_long_type => Type.initTag(.c_long), |
| 193 | .c_ulong_type => Type.initTag(.@"c_ulong"), | 196 | .c_ulong_type => Type.initTag(.c_ulong), |
| 194 | .c_longlong_type => Type.initTag(.@"c_longlong"), | 197 | .c_longlong_type => Type.initTag(.c_longlong), |
| 195 | .c_ulonglong_type => Type.initTag(.@"c_ulonglong"), | 198 | .c_ulonglong_type => Type.initTag(.c_ulonglong), |
| 196 | .c_longdouble_type => Type.initTag(.@"c_longdouble"), | 199 | .c_longdouble_type => Type.initTag(.c_longdouble), |
| 197 | .f16_type => Type.initTag(.@"f16"), | 200 | .f16_type => Type.initTag(.f16), |
| 198 | .f32_type => Type.initTag(.@"f32"), | 201 | .f32_type => Type.initTag(.f32), |
| 199 | .f64_type => Type.initTag(.@"f64"), | 202 | .f64_type => Type.initTag(.f64), |
| 200 | .f128_type => Type.initTag(.@"f128"), | 203 | .f128_type => Type.initTag(.f128), |
| 201 | .c_void_type => Type.initTag(.@"c_void"), | 204 | .c_void_type => Type.initTag(.c_void), |
| 202 | .bool_type => Type.initTag(.@"bool"), | 205 | .bool_type => Type.initTag(.bool), |
| 203 | .void_type => Type.initTag(.@"void"), | 206 | .void_type => Type.initTag(.void), |
| 204 | .type_type => Type.initTag(.@"type"), | 207 | .type_type => Type.initTag(.type), |
| 205 | .anyerror_type => Type.initTag(.@"anyerror"), | 208 | .anyerror_type => Type.initTag(.anyerror), |
| 206 | .comptime_int_type => Type.initTag(.@"comptime_int"), | 209 | .comptime_int_type => Type.initTag(.comptime_int), |
| 207 | .comptime_float_type => Type.initTag(.@"comptime_float"), | 210 | .comptime_float_type => Type.initTag(.comptime_float), |
| 208 | .noreturn_type => Type.initTag(.@"noreturn"), | 211 | .noreturn_type => Type.initTag(.noreturn), |
| 212 | .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args), | ||
| 209 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), | 213 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), |
| 210 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), | 214 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), |
| 211 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), | 215 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| ... | @@ -222,8 +226,8 @@ pub const Value = extern union { | ... | @@ -222,8 +226,8 @@ pub const Value = extern union { |
| 222 | .int_big_positive, | 226 | .int_big_positive, |
| 223 | .int_big_negative, | 227 | .int_big_negative, |
| 224 | .function, | 228 | .function, |
| 225 | .ref, | 229 | .decl_ref, |
| 226 | .ref_val, | 230 | .elem_ptr, |
| 227 | .bytes, | 231 | .bytes, |
| 228 | .repeated, | 232 | .repeated, |
| 229 | => unreachable, | 233 | => unreachable, |
| ... | @@ -259,6 +263,7 @@ pub const Value = extern union { | ... | @@ -259,6 +263,7 @@ pub const Value = extern union { |
| 259 | .comptime_int_type, | 263 | .comptime_int_type, |
| 260 | .comptime_float_type, | 264 | .comptime_float_type, |
| 261 | .noreturn_type, | 265 | .noreturn_type, |
| 266 | .fn_noreturn_no_args_type, | ||
| 262 | .fn_naked_noreturn_no_args_type, | 267 | .fn_naked_noreturn_no_args_type, |
| 263 | .fn_ccc_void_no_args_type, | 268 | .fn_ccc_void_no_args_type, |
| 264 | .single_const_pointer_to_comptime_int_type, | 269 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -267,8 +272,8 @@ pub const Value = extern union { | ... | @@ -267,8 +272,8 @@ pub const Value = extern union { |
| 267 | .bool_false, | 272 | .bool_false, |
| 268 | .null_value, | 273 | .null_value, |
| 269 | .function, | 274 | .function, |
| 270 | .ref, | 275 | .decl_ref, |
| 271 | .ref_val, | 276 | .elem_ptr, |
| 272 | .bytes, | 277 | .bytes, |
| 273 | .undef, | 278 | .undef, |
| 274 | .repeated, | 279 | .repeated, |
| ... | @@ -314,6 +319,7 @@ pub const Value = extern union { | ... | @@ -314,6 +319,7 @@ pub const Value = extern union { |
| 314 | .comptime_int_type, | 319 | .comptime_int_type, |
| 315 | .comptime_float_type, | 320 | .comptime_float_type, |
| 316 | .noreturn_type, | 321 | .noreturn_type, |
| 322 | .fn_noreturn_no_args_type, | ||
| 317 | .fn_naked_noreturn_no_args_type, | 323 | .fn_naked_noreturn_no_args_type, |
| 318 | .fn_ccc_void_no_args_type, | 324 | .fn_ccc_void_no_args_type, |
| 319 | .single_const_pointer_to_comptime_int_type, | 325 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -322,8 +328,8 @@ pub const Value = extern union { | ... | @@ -322,8 +328,8 @@ pub const Value = extern union { |
| 322 | .bool_false, | 328 | .bool_false, |
| 323 | .null_value, | 329 | .null_value, |
| 324 | .function, | 330 | .function, |
| 325 | .ref, | 331 | .decl_ref, |
| 326 | .ref_val, | 332 | .elem_ptr, |
| 327 | .bytes, | 333 | .bytes, |
| 328 | .undef, | 334 | .undef, |
| 329 | .repeated, | 335 | .repeated, |
| ... | @@ -370,6 +376,7 @@ pub const Value = extern union { | ... | @@ -370,6 +376,7 @@ pub const Value = extern union { |
| 370 | .comptime_int_type, | 376 | .comptime_int_type, |
| 371 | .comptime_float_type, | 377 | .comptime_float_type, |
| 372 | .noreturn_type, | 378 | .noreturn_type, |
| 379 | .fn_noreturn_no_args_type, | ||
| 373 | .fn_naked_noreturn_no_args_type, | 380 | .fn_naked_noreturn_no_args_type, |
| 374 | .fn_ccc_void_no_args_type, | 381 | .fn_ccc_void_no_args_type, |
| 375 | .single_const_pointer_to_comptime_int_type, | 382 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -378,8 +385,8 @@ pub const Value = extern union { | ... | @@ -378,8 +385,8 @@ pub const Value = extern union { |
| 378 | .bool_false, | 385 | .bool_false, |
| 379 | .null_value, | 386 | .null_value, |
| 380 | .function, | 387 | .function, |
| 381 | .ref, | 388 | .decl_ref, |
| 382 | .ref_val, | 389 | .elem_ptr, |
| 383 | .bytes, | 390 | .bytes, |
| 384 | .undef, | 391 | .undef, |
| 385 | .repeated, | 392 | .repeated, |
| ... | @@ -431,6 +438,7 @@ pub const Value = extern union { | ... | @@ -431,6 +438,7 @@ pub const Value = extern union { |
| 431 | .comptime_int_type, | 438 | .comptime_int_type, |
| 432 | .comptime_float_type, | 439 | .comptime_float_type, |
| 433 | .noreturn_type, | 440 | .noreturn_type, |
| 441 | .fn_noreturn_no_args_type, | ||
| 434 | .fn_naked_noreturn_no_args_type, | 442 | .fn_naked_noreturn_no_args_type, |
| 435 | .fn_ccc_void_no_args_type, | 443 | .fn_ccc_void_no_args_type, |
| 436 | .single_const_pointer_to_comptime_int_type, | 444 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -439,8 +447,8 @@ pub const Value = extern union { | ... | @@ -439,8 +447,8 @@ pub const Value = extern union { |
| 439 | .bool_false, | 447 | .bool_false, |
| 440 | .null_value, | 448 | .null_value, |
| 441 | .function, | 449 | .function, |
| 442 | .ref, | 450 | .decl_ref, |
| 443 | .ref_val, | 451 | .elem_ptr, |
| 444 | .bytes, | 452 | .bytes, |
| 445 | .repeated, | 453 | .repeated, |
| 446 | => unreachable, | 454 | => unreachable, |
| ... | @@ -521,6 +529,7 @@ pub const Value = extern union { | ... | @@ -521,6 +529,7 @@ pub const Value = extern union { |
| 521 | .comptime_int_type, | 529 | .comptime_int_type, |
| 522 | .comptime_float_type, | 530 | .comptime_float_type, |
| 523 | .noreturn_type, | 531 | .noreturn_type, |
| 532 | .fn_noreturn_no_args_type, | ||
| 524 | .fn_naked_noreturn_no_args_type, | 533 | .fn_naked_noreturn_no_args_type, |
| 525 | .fn_ccc_void_no_args_type, | 534 | .fn_ccc_void_no_args_type, |
| 526 | .single_const_pointer_to_comptime_int_type, | 535 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -529,8 +538,8 @@ pub const Value = extern union { | ... | @@ -529,8 +538,8 @@ pub const Value = extern union { |
| 529 | .bool_false, | 538 | .bool_false, |
| 530 | .null_value, | 539 | .null_value, |
| 531 | .function, | 540 | .function, |
| 532 | .ref, | 541 | .decl_ref, |
| 533 | .ref_val, | 542 | .elem_ptr, |
| 534 | .bytes, | 543 | .bytes, |
| 535 | .repeated, | 544 | .repeated, |
| 536 | .undef, | 545 | .undef, |
| ... | @@ -573,6 +582,7 @@ pub const Value = extern union { | ... | @@ -573,6 +582,7 @@ pub const Value = extern union { |
| 573 | .comptime_int_type, | 582 | .comptime_int_type, |
| 574 | .comptime_float_type, | 583 | .comptime_float_type, |
| 575 | .noreturn_type, | 584 | .noreturn_type, |
| 585 | .fn_noreturn_no_args_type, | ||
| 576 | .fn_naked_noreturn_no_args_type, | 586 | .fn_naked_noreturn_no_args_type, |
| 577 | .fn_ccc_void_no_args_type, | 587 | .fn_ccc_void_no_args_type, |
| 578 | .single_const_pointer_to_comptime_int_type, | 588 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -581,8 +591,8 @@ pub const Value = extern union { | ... | @@ -581,8 +591,8 @@ pub const Value = extern union { |
| 581 | .bool_false, | 591 | .bool_false, |
| 582 | .null_value, | 592 | .null_value, |
| 583 | .function, | 593 | .function, |
| 584 | .ref, | 594 | .decl_ref, |
| 585 | .ref_val, | 595 | .elem_ptr, |
| 586 | .bytes, | 596 | .bytes, |
| 587 | .repeated, | 597 | .repeated, |
| 588 | .undef, | 598 | .undef, |
| ... | @@ -636,7 +646,7 @@ pub const Value = extern union { | ... | @@ -636,7 +646,7 @@ pub const Value = extern union { |
| 636 | } | 646 | } |
| 637 | 647 | ||
| 638 | /// Asserts the value is a pointer and dereferences it. | 648 | /// Asserts the value is a pointer and dereferences it. |
| 639 | pub fn pointerDeref(self: Value) Value { | 649 | pub fn pointerDeref(self: Value, module: *ir.Module) !Value { |
| 640 | return switch (self.tag()) { | 650 | return switch (self.tag()) { |
| 641 | .ty, | 651 | .ty, |
| 642 | .u8_type, | 652 | .u8_type, |
| ... | @@ -664,6 +674,7 @@ pub const Value = extern union { | ... | @@ -664,6 +674,7 @@ pub const Value = extern union { |
| 664 | .comptime_int_type, | 674 | .comptime_int_type, |
| 665 | .comptime_float_type, | 675 | .comptime_float_type, |
| 666 | .noreturn_type, | 676 | .noreturn_type, |
| 677 | .fn_noreturn_no_args_type, | ||
| 667 | .fn_naked_noreturn_no_args_type, | 678 | .fn_naked_noreturn_no_args_type, |
| 668 | .fn_ccc_void_no_args_type, | 679 | .fn_ccc_void_no_args_type, |
| 669 | .single_const_pointer_to_comptime_int_type, | 680 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -683,14 +694,21 @@ pub const Value = extern union { | ... | @@ -683,14 +694,21 @@ pub const Value = extern union { |
| 683 | => unreachable, | 694 | => unreachable, |
| 684 | 695 | ||
| 685 | .the_one_possible_value => Value.initTag(.the_one_possible_value), | 696 | .the_one_possible_value => Value.initTag(.the_one_possible_value), |
| 686 | .ref => self.cast(Payload.Ref).?.cell.contents, | 697 | .decl_ref => { |
| 687 | .ref_val => self.cast(Payload.RefVal).?.val, | 698 | const index = self.cast(Payload.DeclRef).?.index; |
| 699 | return module.getDeclValue(index); | ||
| 700 | }, | ||
| 701 | .elem_ptr => { | ||
| 702 | const elem_ptr = self.cast(ElemPtr).?; | ||
| 703 | const array_val = try elem_ptr.array_ptr.pointerDeref(module); | ||
| 704 | return self.elemValue(array_val, elem_ptr.index); | ||
| 705 | }, | ||
| 688 | }; | 706 | }; |
| 689 | } | 707 | } |
| 690 | 708 | ||
| 691 | /// Asserts the value is a single-item pointer to an array, or an array, | 709 | /// Asserts the value is a single-item pointer to an array, or an array, |
| 692 | /// or an unknown-length pointer, and returns the element value at the index. | 710 | /// or an unknown-length pointer, and returns the element value at the index. |
| 693 | pub fn elemValueAt(self: Value, allocator: *Allocator, index: usize) Allocator.Error!Value { | 711 | pub fn elemValue(self: Value, index: usize) Value { |
| 694 | switch (self.tag()) { | 712 | switch (self.tag()) { |
| 695 | .ty, | 713 | .ty, |
| 696 | .u8_type, | 714 | .u8_type, |
| ... | @@ -718,6 +736,7 @@ pub const Value = extern union { | ... | @@ -718,6 +736,7 @@ pub const Value = extern union { |
| 718 | .comptime_int_type, | 736 | .comptime_int_type, |
| 719 | .comptime_float_type, | 737 | .comptime_float_type, |
| 720 | .noreturn_type, | 738 | .noreturn_type, |
| 739 | .fn_noreturn_no_args_type, | ||
| 721 | .fn_naked_noreturn_no_args_type, | 740 | .fn_naked_noreturn_no_args_type, |
| 722 | .fn_ccc_void_no_args_type, | 741 | .fn_ccc_void_no_args_type, |
| 723 | .single_const_pointer_to_comptime_int_type, | 742 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -733,13 +752,12 @@ pub const Value = extern union { | ... | @@ -733,13 +752,12 @@ pub const Value = extern union { |
| 733 | .int_big_positive, | 752 | .int_big_positive, |
| 734 | .int_big_negative, | 753 | .int_big_negative, |
| 735 | .undef, | 754 | .undef, |
| 755 | .elem_ptr, | ||
| 756 | .decl_ref, | ||
| 736 | => unreachable, | 757 | => unreachable, |
| 737 | 758 | ||
| 738 | .ref => @panic("TODO figure out how MemoryCell works"), | ||
| 739 | .ref_val => @panic("TODO figure out how MemoryCell works"), | ||
| 740 | |||
| 741 | .bytes => { | 759 | .bytes => { |
| 742 | const int_payload = try allocator.create(Value.Payload.Int_u64); | 760 | const int_payload = try allocator.create(Payload.Int_u64); |
| 743 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; | 761 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; |
| 744 | return Value.initPayload(&int_payload.base); | 762 | return Value.initPayload(&int_payload.base); |
| 745 | }, | 763 | }, |
| ... | @@ -749,6 +767,17 @@ pub const Value = extern union { | ... | @@ -749,6 +767,17 @@ pub const Value = extern union { |
| 749 | } | 767 | } |
| 750 | } | 768 | } |
| 751 | 769 | ||
| 770 | /// Returns a pointer to the element value at the index. | ||
| 771 | pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value { | ||
| 772 | const payload = try allocator.create(Payload.ElemPtr); | ||
| 773 | if (self.cast(Payload.ElemPtr)) |elem_ptr| { | ||
| 774 | payload.* = .{ .array_ptr = elem_ptr.array_ptr, .index = elem_ptr.index + index }; | ||
| 775 | } else { | ||
| 776 | payload.* = .{ .array_ptr = self, .index = index }; | ||
| 777 | } | ||
| 778 | return Value.initPayload(&payload.base); | ||
| 779 | } | ||
| 780 | |||
| 752 | pub fn isUndef(self: Value) bool { | 781 | pub fn isUndef(self: Value) bool { |
| 753 | return self.tag() == .undef; | 782 | return self.tag() == .undef; |
| 754 | } | 783 | } |
| ... | @@ -783,6 +812,7 @@ pub const Value = extern union { | ... | @@ -783,6 +812,7 @@ pub const Value = extern union { |
| 783 | .comptime_int_type, | 812 | .comptime_int_type, |
| 784 | .comptime_float_type, | 813 | .comptime_float_type, |
| 785 | .noreturn_type, | 814 | .noreturn_type, |
| 815 | .fn_noreturn_no_args_type, | ||
| 786 | .fn_naked_noreturn_no_args_type, | 816 | .fn_naked_noreturn_no_args_type, |
| 787 | .fn_ccc_void_no_args_type, | 817 | .fn_ccc_void_no_args_type, |
| 788 | .single_const_pointer_to_comptime_int_type, | 818 | .single_const_pointer_to_comptime_int_type, |
| ... | @@ -796,8 +826,8 @@ pub const Value = extern union { | ... | @@ -796,8 +826,8 @@ pub const Value = extern union { |
| 796 | .int_i64, | 826 | .int_i64, |
| 797 | .int_big_positive, | 827 | .int_big_positive, |
| 798 | .int_big_negative, | 828 | .int_big_negative, |
| 799 | .ref, | 829 | .decl_ref, |
| 800 | .ref_val, | 830 | .elem_ptr, |
| 801 | .bytes, | 831 | .bytes, |
| 802 | .repeated, | 832 | .repeated, |
| 803 | => false, | 833 | => false, |
| ... | @@ -841,8 +871,7 @@ pub const Value = extern union { | ... | @@ -841,8 +871,7 @@ pub const Value = extern union { |
| 841 | 871 | ||
| 842 | pub const Function = struct { | 872 | pub const Function = struct { |
| 843 | base: Payload = Payload{ .tag = .function }, | 873 | base: Payload = Payload{ .tag = .function }, |
| 844 | /// Index into the `fns` array of the `ir.Module` | 874 | func: *ir.Module.Fn, |
| 845 | index: usize, | ||
| 846 | }; | 875 | }; |
| 847 | 876 | ||
| 848 | pub const ArraySentinel0_u8_Type = struct { | 877 | pub const ArraySentinel0_u8_Type = struct { |
| ... | @@ -855,14 +884,17 @@ pub const Value = extern union { | ... | @@ -855,14 +884,17 @@ pub const Value = extern union { |
| 855 | elem_type: *Type, | 884 | elem_type: *Type, |
| 856 | }; | 885 | }; |
| 857 | 886 | ||
| 858 | pub const Ref = struct { | 887 | /// Represents a pointer to a decl, not the value of the decl. |
| 859 | base: Payload = Payload{ .tag = .ref }, | 888 | pub const DeclRef = struct { |
| 860 | cell: *MemoryCell, | 889 | base: Payload = Payload{ .tag = .decl_ref }, |
| 890 | /// Index into the Module's decls list | ||
| 891 | index: usize, | ||
| 861 | }; | 892 | }; |
| 862 | 893 | ||
| 863 | pub const RefVal = struct { | 894 | pub const ElemPtr = struct { |
| 864 | base: Payload = Payload{ .tag = .ref_val }, | 895 | base: Payload = Payload{ .tag = .elem_ptr }, |
| 865 | val: Value, | 896 | array_ptr: Value, |
| 897 | index: usize, | ||
| 866 | }; | 898 | }; |
| 867 | 899 | ||
| 868 | pub const Bytes = struct { | 900 | pub const Bytes = struct { |
| ... | @@ -890,29 +922,3 @@ pub const Value = extern union { | ... | @@ -890,29 +922,3 @@ pub const Value = extern union { |
| 890 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, | 922 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, |
| 891 | }; | 923 | }; |
| 892 | }; | 924 | }; |
| 893 | |||
| 894 | /// This is the heart of resource management of the Zig compiler. The Zig compiler uses | ||
| 895 | /// stop-the-world mark-and-sweep garbage collection during compilation to manage the resources | ||
| 896 | /// associated with evaluating compile-time code and semantic analysis. Each `MemoryCell` represents | ||
| 897 | /// a root. | ||
| 898 | pub const MemoryCell = struct { | ||
| 899 | parent: Parent, | ||
| 900 | contents: Value, | ||
| 901 | |||
| 902 | pub const Parent = union(enum) { | ||
| 903 | none, | ||
| 904 | struct_field: struct { | ||
| 905 | struct_base: *MemoryCell, | ||
| 906 | field_index: usize, | ||
| 907 | }, | ||
| 908 | array_elem: struct { | ||
| 909 | array_base: *MemoryCell, | ||
| 910 | elem_index: usize, | ||
| 911 | }, | ||
| 912 | union_field: *MemoryCell, | ||
| 913 | err_union_code: *MemoryCell, | ||
| 914 | err_union_payload: *MemoryCell, | ||
| 915 | optional_payload: *MemoryCell, | ||
| 916 | optional_flag: *MemoryCell, | ||
| 917 | }; | ||
| 918 | }; |
src-self-hosted/visib.zig deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | pub const Visib = enum { | ||
| 2 | Private, | ||
| 3 | Pub, | ||
| 4 | }; | ||