mirror of
https://github.com/ziglang/zig.git
synced 2025-12-26 16:13:07 +00:00
wasm backend: implement multi_value for WValue
This allows us to differentiate between regular locals and variables that create multiple locals on the stack such as optionals and structs. Now `struct_a = struct_b;` works and only updates a reference, rather than update all local's values. Also created more test cases to test against this.
This commit is contained in:
parent
6962647862
commit
87a9c6946d
@ -31,6 +31,9 @@ const WValue = union(enum) {
|
||||
code_offset: usize,
|
||||
/// The label of the block, used by breaks to find its relative distance
|
||||
block_idx: u32,
|
||||
/// Used for variables that create multiple locals on the stack when allocated
|
||||
/// such as structs and optionals.
|
||||
multi_value: u32,
|
||||
};
|
||||
|
||||
/// Wasm ops, but without input/output/signedness information
|
||||
@ -587,8 +590,9 @@ pub const Context = struct {
|
||||
fn emitWValue(self: *Context, val: WValue) InnerError!void {
|
||||
const writer = self.code.writer();
|
||||
switch (val) {
|
||||
.block_idx => unreachable,
|
||||
.none, .code_offset => {},
|
||||
.block_idx => unreachable, // block_idx cannot be referenced
|
||||
.multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually
|
||||
.none, .code_offset => {}, // no-op
|
||||
.local => |idx| {
|
||||
try writer.writeByte(wasm.opcode(.local_get));
|
||||
try leb.writeULEB128(writer, idx);
|
||||
@ -795,7 +799,7 @@ pub const Context = struct {
|
||||
|
||||
fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {
|
||||
const elem_type = inst.base.ty.elemType();
|
||||
const local_value = WValue{ .local = self.local_index };
|
||||
const initial_index = self.local_index;
|
||||
|
||||
switch (elem_type.zigTypeTag()) {
|
||||
.Struct => {
|
||||
@ -810,16 +814,15 @@ pub const Context = struct {
|
||||
self.locals.appendAssumeCapacity(val_type);
|
||||
self.local_index += 1;
|
||||
}
|
||||
return WValue{ .multi_value = initial_index };
|
||||
},
|
||||
// TODO: Add more types that require extra locals such as optionals
|
||||
else => {
|
||||
const valtype = try self.genValtype(inst.base.src, elem_type);
|
||||
try self.locals.append(self.gpa, valtype);
|
||||
self.local_index += 1;
|
||||
return WValue{ .local = initial_index };
|
||||
},
|
||||
}
|
||||
|
||||
return local_value;
|
||||
}
|
||||
|
||||
fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
|
||||
@ -827,10 +830,20 @@ pub const Context = struct {
|
||||
|
||||
const lhs = self.resolveInst(inst.lhs);
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
try writer.writeByte(wasm.opcode(.local_set));
|
||||
try leb.writeULEB128(writer, lhs.local);
|
||||
switch (lhs) {
|
||||
// When assigning a value to a multi_value such as a struct,
|
||||
// we simply assign the local_index to the rhs one.
|
||||
// This allows us to update struct fields without having to individually
|
||||
// set each local as each field's index will be calculated off the struct's base index
|
||||
.multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
|
||||
.local => |local| {
|
||||
try self.emitWValue(rhs);
|
||||
try writer.writeByte(wasm.opcode(.local_set));
|
||||
try leb.writeULEB128(writer, lhs.local);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return .none;
|
||||
}
|
||||
|
||||
@ -1115,6 +1128,6 @@ pub const Context = struct {
|
||||
fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue {
|
||||
const struct_ptr = self.resolveInst(inst.struct_ptr);
|
||||
|
||||
return WValue{ .local = struct_ptr.local + @intCast(u32, inst.field_index) };
|
||||
return WValue{ .local = struct_ptr.multi_value + @intCast(u32, inst.field_index) };
|
||||
}
|
||||
};
|
||||
|
||||
@ -426,19 +426,52 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
case.addCompareOutput(
|
||||
\\const Example = struct { x: u32 };
|
||||
\\
|
||||
\\export fn _start() u32 {
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var example: Example = .{ .x = 5 };
|
||||
\\ return example.x;
|
||||
\\}
|
||||
, "5\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\const Example = struct { x: u32 };
|
||||
\\
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var example: Example = .{ .x = 5 };
|
||||
\\ example.x = 10;
|
||||
\\ return example.x;
|
||||
\\}
|
||||
, "10\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\const Example = struct { x: u32, y: u32 };
|
||||
\\
|
||||
\\export fn _start() u32 {
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var example: Example = .{ .x = 5, .y = 10 };
|
||||
\\ return example.y + example.x;
|
||||
\\}
|
||||
, "15\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\const Example = struct { x: u32, y: u32 };
|
||||
\\
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var example: Example = .{ .x = 5, .y = 10 };
|
||||
\\ var example2: Example = .{ .x = 10, .y = 20 };
|
||||
\\
|
||||
\\ example = example2;
|
||||
\\ return example.y + example.x;
|
||||
\\}
|
||||
, "30\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\const Example = struct { x: u32, y: u32 };
|
||||
\\
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var example: Example = .{ .x = 5, .y = 10 };
|
||||
\\
|
||||
\\ example = .{ .x = 10, .y = 20 };
|
||||
\\ return example.y + example.x;
|
||||
\\}
|
||||
, "30\n");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user