| ... | ... | @@ -601,6 +601,21 @@ stack_size: u32 = 0, |
| 601 | 601 | /// However, local variables or the usage of `@setAlignStack` can overwrite this default. |
| 602 | 602 | stack_alignment: u32 = 16, |
| 603 | 603 | |
| 604 | // For each individual Wasm valtype we store a seperate free list which |
| 605 | // allows us to re-use locals that are no longer used. e.g. a temporary local. |
| 606 | /// A list of indexes which represents a local of valtype `i32`. |
| 607 | /// It is illegal to store a non-i32 valtype in this list. |
| 608 | free_locals_i32: std.ArrayListUnmanaged(u32) = .{}, |
| 609 | /// A list of indexes which represents a local of valtype `i64`. |
| 610 | /// It is illegal to store a non-i32 valtype in this list. |
| 611 | free_locals_i64: std.ArrayListUnmanaged(u32) = .{}, |
| 612 | /// A list of indexes which represents a local of valtype `f32`. |
| 613 | /// It is illegal to store a non-i32 valtype in this list. |
| 614 | free_locals_f32: std.ArrayListUnmanaged(u32) = .{}, |
| 615 | /// A list of indexes which represents a local of valtype `f64`. |
| 616 | /// It is illegal to store a non-i32 valtype in this list. |
| 617 | free_locals_f64: std.ArrayListUnmanaged(u32) = .{}, |
| 618 | |
| 604 | 619 | const InnerError = error{ |
| 605 | 620 | OutOfMemory, |
| 606 | 621 | /// An error occurred when trying to lower AIR to MIR. |
| ... | ... | @@ -781,13 +796,43 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { |
| 781 | 796 | /// Creates one locals for a given `Type`. |
| 782 | 797 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 783 | 798 | fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 799 | const valtype = typeToValtype(ty, self.target); |
| 800 | switch (valtype) { |
| 801 | .i32 => if (self.free_locals_i32.popOrNull()) |index| { |
| 802 | return WValue{ .local = index }; |
| 803 | }, |
| 804 | .i64 => if (self.free_locals_i64.popOrNull()) |index| { |
| 805 | return WValue{ .local = index }; |
| 806 | }, |
| 807 | .f32 => if (self.free_locals_f32.popOrNull()) |index| { |
| 808 | return WValue{ .local = index }; |
| 809 | }, |
| 810 | .f64 => if (self.free_locals_f64.popOrNull()) |index| { |
| 811 | return WValue{ .local = index }; |
| 812 | }, |
| 813 | } |
| 814 | // no local was free to be re-used, so allocate a new local instead |
| 815 | try self.locals.append(self.gpa, wasm.valtype(valtype)); |
| 784 | 816 | const initial_index = self.local_index; |
| 785 | | const valtype = genValtype(ty, self.target); |
| 786 | | try self.locals.append(self.gpa, valtype); |
| 787 | 817 | self.local_index += 1; |
| 788 | 818 | return WValue{ .local = initial_index }; |
| 789 | 819 | } |
| 790 | 820 | |
| 821 | /// Marks a local as no longer being referenced and essentially allows |
| 822 | /// us to re-use it somewhere else within the function. |
| 823 | /// The valtype of the local is deducted by using the index of the given. |
| 824 | /// Asserts given `WValue` is a `local`. |
| 825 | fn freeLocal(self: *Self, value: WValue) InnerError!WValue { |
| 826 | const index = value.local; |
| 827 | const valtype = wasm.valtype(self.locals.items[index]); |
| 828 | switch (valtype) { |
| 829 | .i32 => self.free_locals_i32.append(index) catch {}, // It's ok to fail any of those, a new local can be allocated instead |
| 830 | .i64 => self.free_locals_i64.append(index) catch {}, |
| 831 | .f32 => self.free_locals_f32.append(index) catch {}, |
| 832 | .f64 => self.free_locals_f64.append(index) catch {}, |
| 833 | } |
| 834 | } |
| 835 | |
| 791 | 836 | /// Generates a `wasm.Type` from a given function type. |
| 792 | 837 | /// Memory is owned by the caller. |
| 793 | 838 | fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type { |