authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-23 19:49:26+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-08-11 11:07:56+02:00
log7e6dbd63989ed4f62b9d12fb03119b0bec1bda84
tree34879076ce73fcaa855d990a33498e8aca1c5250
parent9f1f60fd4311b37493a5297ccaac167d0270f521
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Use free-lists for unused locals

When a local is no longer needed (for instance, it was used as a temporary during arithmetic), it can be appended to one of the typed freelists. This allows us to re-use locals and therefore require less locals, reducing the binary size, as well as runtime initialization.

1 files changed, 47 insertions(+), 2 deletions(-)

src/arch/wasm/CodeGen.zig+47-2
......@@ -601,6 +601,21 @@ stack_size: u32 = 0,
601601/// However, local variables or the usage of `@setAlignStack` can overwrite this default.
602602stack_alignment: u32 = 16,
603603
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.
608free_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.
611free_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.
614free_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.
617free_locals_f64: std.ArrayListUnmanaged(u32) = .{},
618
604619const InnerError = error{
605620 OutOfMemory,
606621 /// An error occurred when trying to lower AIR to MIR.
......@@ -781,13 +796,43 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {
781796/// Creates one locals for a given `Type`.
782797/// Returns a corresponding `Wvalue` with `local` as active tag
783798fn 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));
784816 const initial_index = self.local_index;
785 const valtype = genValtype(ty, self.target);
786 try self.locals.append(self.gpa, valtype);
787817 self.local_index += 1;
788818 return WValue{ .local = initial_index };
789819}
790820
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`.
825fn 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
791836/// Generates a `wasm.Type` from a given function type.
792837/// Memory is owned by the caller.
793838fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type {