authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-08 20:22:55+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-19 20:19:00+02:00
log99422cb5284f3e15c1b5a8598a6b1622c0e7b6ca
treecf712b929c5f3fd1128cd83a97863203c762d8f4
parentf2860bb4f40565e43f51757e6cb604bb2df16ae0
signaturelock-open Commit is signed but in an unrecognized format.

wasm: add `dead` tag to `WValue`

This new tag is used for freed locals that are not allowed to have any remaining references pointing to it. This new tag allows us to easily identify liveness bugs. Previously we would set the entire region to `undefined` which would incorrectly set the tag to `function_index`, making codegen think it was a valid `WValue` while it wasn't.

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

src/arch/wasm/CodeGen.zig+9-2
...@@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;...@@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;
2929
30/// Wasm Value, created when generating an instruction30/// Wasm Value, created when generating an instruction
31const WValue = union(enum) {31const WValue = union(enum) {
32 /// `WValue` which has been freed and may no longer hold
33 /// any references.
34 dead: void,
32 /// May be referenced but is unused35 /// May be referenced but is unused
33 none: void,36 none: void,
34 /// The value lives on top of the stack37 /// The value lives on top of the stack
...@@ -86,6 +89,7 @@ const WValue = union(enum) {...@@ -86,6 +89,7 @@ const WValue = union(enum) {
86 fn offset(value: WValue) u32 {89 fn offset(value: WValue) u32 {
87 switch (value) {90 switch (value) {
88 .stack_offset => |stack_offset| return stack_offset.value,91 .stack_offset => |stack_offset| return stack_offset.value,
92 .dead => unreachable,
89 else => return 0,93 else => return 0,
90 }94 }
91 }95 }
...@@ -123,7 +127,7 @@ const WValue = union(enum) {...@@ -123,7 +127,7 @@ const WValue = union(enum) {
123 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,127 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
124 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,128 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
125 }129 }
126 value.* = undefined;130 value.* = .dead;
127 }131 }
128};132};
129133
...@@ -832,6 +836,7 @@ const Branch = struct {...@@ -832,6 +836,7 @@ const Branch = struct {
832836
833 fn deinit(branch: *Branch, gpa: Allocator) void {837 fn deinit(branch: *Branch, gpa: Allocator) void {
834 branch.values.deinit(gpa);838 branch.values.deinit(gpa);
839 branch.* = undefined;
835 }840 }
836};841};
837842
...@@ -884,7 +889,7 @@ fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {...@@ -884,7 +889,7 @@ fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {
884 if (value.local.value < reserved_indexes) {889 if (value.local.value < reserved_indexes) {
885 return; // function arguments can never be re-used890 return; // function arguments can never be re-used
886 }891 }
887 log.debug("Decreasing reference for ref: %{?d}, using local '{d}'\n", .{ Air.refToIndex(ref), value.local.value });892 log.debug("Decreasing reference for ref: %{?d}, using local '{d}'", .{ Air.refToIndex(ref), value.local.value });
888 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer893 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
889 if (value.local.references == 0) {894 if (value.local.references == 0) {
890 value.free(func);895 value.free(func);
...@@ -1030,6 +1035,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {...@@ -1030,6 +1035,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
1030/// Writes the bytecode depending on the given `WValue` in `val`1035/// Writes the bytecode depending on the given `WValue` in `val`
1031fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {1036fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
1032 switch (value) {1037 switch (value) {
1038 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
1033 .none, .stack => {}, // no-op1039 .none, .stack => {}, // no-op
1034 .local => |idx| try func.addLabel(.local_get, idx.value),1040 .local => |idx| try func.addLabel(.local_get, idx.value),
1035 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),1041 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
...@@ -1226,6 +1232,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1226,6 +1232,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
1226 defer {1232 defer {
1227 var outer_branch = func.branches.pop();1233 var outer_branch = func.branches.pop();
1228 outer_branch.deinit(func.gpa);1234 outer_branch.deinit(func.gpa);
1235 assert(func.branches.items.len == 0); // missing branch merge
1229 }1236 }
1230 // Generate MIR for function body1237 // Generate MIR for function body
1231 try func.genBody(func.air.getMainBody());1238 try func.genBody(func.air.getMainBody());