Commit deb8d0765b
Changed files (2)
src
arch
wasm
test
stage2
src/arch/wasm/CodeGen.zig
@@ -187,7 +187,13 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
32 => switch (args.valtype1.?) {
.i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
.i32 => return .i32_load,
- .f32, .f64 => unreachable,
+ .f32 => return .f32_load,
+ .f64 => unreachable,
+ },
+ 64 => switch (args.valtype1.?) {
+ .i64 => return .i64_load,
+ .f64 => return .f64_load,
+ else => unreachable,
},
else => unreachable,
} else switch (args.valtype1.?) {
@@ -216,6 +222,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
},
64 => switch (args.valtype1.?) {
.i64 => return .i64_store,
+ .f64 => return .f64_store,
else => unreachable,
},
else => unreachable,
@@ -505,7 +512,10 @@ gpa: *mem.Allocator,
/// Table to save `WValue`'s generated by an `Air.Inst`
values: ValueTable,
/// Mapping from Air.Inst.Index to block ids
-blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
+blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
+ label: u32,
+ value: WValue,
+}) = .{},
/// `bytes` contains the wasm bytecode belonging to the 'code' section.
code: ArrayList(u8),
/// Contains the generated function type bytecode for the current function
@@ -984,8 +994,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.dbg_stmt => WValue.none,
.intcast => self.airIntcast(inst),
- .is_err => self.airIsErr(inst, .i32_eq),
- .is_non_err => self.airIsErr(inst, .i32_ne),
+ .is_err => self.airIsErr(inst, .i32_ne),
+ .is_non_err => self.airIsErr(inst, .i32_eq),
.is_null => self.airIsNull(inst, .i32_ne),
.is_non_null => self.airIsNull(inst, .i32_eq),
@@ -1065,12 +1075,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const ret_ty = target.ty.fnReturnType();
switch (ret_ty.zigTypeTag()) {
- .ErrorUnion, .Optional => {
+ .Void, .NoReturn => return WValue.none,
+ else => {
const result_local = try self.allocLocal(ret_ty);
try self.addLabel(.local_set, result_local.local);
return result_local;
},
- else => return WValue.none,
}
}
@@ -1086,7 +1096,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (abi_size == 0) return WValue{ .none = {} };
// local, containing the offset to the stack position
- const local = try self.allocLocal(child_type);
+ const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32
try self.moveStack(@intCast(u32, abi_size), local.local);
return local;
@@ -1114,29 +1124,64 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
var buf: Type.Payload.ElemType = undefined;
const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);
const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);
- const payload_offset = @intCast(u32, tag_ty.abiSize(self.target));
-
- if (rhs == .constant) {
- // constant will contain both tag and payload,
- // so save those in 2 temporary locals before storing them
- // in memory
- try self.emitWValue(rhs);
- const tag_local = try self.allocLocal(tag_ty);
- const payload_local = try self.allocLocal(payload_ty);
-
- try self.addLabel(.local_set, payload_local.local);
- try self.addLabel(.local_set, tag_local.local);
-
- try self.store(lhs, tag_local, tag_ty, 0);
- return try self.store(lhs, payload_local, payload_ty, payload_offset);
- } else {
- // Load values from `rhs` stack position and store in `lhs` instead
- const tag_local = try self.load(rhs, tag_ty, 0);
- const payload_local = try self.load(rhs, payload_ty, payload_offset);
+ const payload_offset = if (ty.zigTypeTag() == .ErrorUnion)
+ @intCast(u32, tag_ty.abiSize(self.target))
+ else
+ @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target));
+
+ switch (rhs) {
+ .constant => {
+ // constant will contain both tag and payload,
+ // so save those in 2 temporary locals before storing them
+ // in memory
+ try self.emitWValue(rhs);
+ const tag_local = try self.allocLocal(tag_ty);
+ const payload_local = try self.allocLocal(payload_ty);
+
+ try self.addLabel(.local_set, payload_local.local);
+ try self.addLabel(.local_set, tag_local.local);
+
+ try self.store(lhs, tag_local, tag_ty, 0);
+ return try self.store(lhs, payload_local, payload_ty, payload_offset);
+ },
+ .local => {
+ // Load values from `rhs` stack position and store in `lhs` instead
+ const tag_local = try self.load(rhs, tag_ty, 0);
+ const payload_local = try self.load(rhs, payload_ty, payload_offset);
- try self.store(lhs, tag_local, tag_ty, 0);
- return try self.store(lhs, payload_local, payload_ty, payload_offset);
+ try self.store(lhs, tag_local, tag_ty, 0);
+ return try self.store(lhs, payload_local, payload_ty, payload_offset);
+ },
+ .local_with_offset => |with_offset| {
+ const tag_local = try self.allocLocal(tag_ty);
+ try self.addImm32(0);
+ try self.store(lhs, tag_local, tag_ty, 0);
+
+ return try self.store(
+ lhs,
+ .{ .local = with_offset.local },
+ payload_ty,
+ with_offset.offset,
+ );
+ },
+ else => unreachable,
+ }
+ },
+ .Struct => {
+ // we are copying a struct with its fields.
+ // Replace this with a wasm memcpy instruction once we support that feature.
+ const fields_len = ty.structFieldCount();
+ var index: usize = 0;
+ while (index < fields_len) : (index += 1) {
+ const field_ty = ty.structFieldType(index);
+ if (!field_ty.hasCodeGenBits()) continue;
+ const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch {
+ return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
+ };
+ const field_local = try self.load(rhs, field_ty, field_offset);
+ try self.store(lhs, field_local, field_ty, field_offset);
}
+ return;
},
else => {},
}
@@ -1429,21 +1474,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const extra = self.air.extraData(Air.Block, ty_pl.payload);
const body = self.air.extra[extra.end..][0..extra.data.body_len];
- try self.startBlock(.block, block_ty, null);
+ // if block_ty is non-empty, we create a register to store the temporary value
+ const block_result: WValue = if (block_ty != wasm.block_empty)
+ try self.allocLocal(self.air.getRefType(ty_pl.ty))
+ else
+ WValue.none;
+
+ try self.startBlock(.block, wasm.block_empty);
// Here we set the current block idx, so breaks know the depth to jump
// to when breaking out.
- try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
+ try self.blocks.putNoClobber(self.gpa, inst, .{
+ .label = self.block_depth,
+ .value = block_result,
+ });
try self.genBody(body);
try self.endBlock();
- return .none;
+ return block_result;
}
/// appends a new wasm block to the code section and increases the `block_depth` by 1
-fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8, with_offset: ?usize) !void {
+fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void {
self.block_depth += 1;
- const offset = with_offset orelse self.mir_instructions.len;
- try self.addInstAt(offset, .{
+ try self.addInst(.{
.tag = Mir.Inst.Tag.fromOpcode(block_tag),
.data = .{ .block_type = valtype },
});
@@ -1462,7 +1515,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
// result type of loop is always 'noreturn', meaning we can always
// emit the wasm type 'block_empty'.
- try self.startBlock(.loop, wasm.block_empty, null);
+ try self.startBlock(.loop, wasm.block_empty);
try self.genBody(body);
// breaking to the index of a loop block will continue the loop instead
@@ -1480,13 +1533,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
// TODO: Handle death instructions for then and else body
- // insert blocks at the position of `offset` so
- // the condition can jump to it
- const offset = self.mir_instructions.len;
- try self.emitWValue(condition);
-
// result type is always noreturn, so use `block_empty` as type.
- try self.startBlock(.block, wasm.block_empty, offset);
+ try self.startBlock(.block, wasm.block_empty);
+ // emit the conditional value
+ try self.emitWValue(condition);
// we inserted the block in front of the condition
// so now check if condition matches. If not, break outside this block
@@ -1539,15 +1589,20 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const br = self.air.instructions.items(.data)[inst].br;
+ const block = self.blocks.get(br.block_inst).?;
// if operand has codegen bits we should break with a value
if (self.air.typeOf(br.operand).hasCodeGenBits()) {
try self.emitWValue(self.resolveInst(br.operand));
+
+ if (block.value != .none) {
+ try self.addLabel(.local_set, block.value.local);
+ }
}
// We map every block to its block index.
// We then determine how far we have to jump to it by subtracting it from current block depth
- const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
+ const idx: u32 = self.block_depth - block.label;
try self.addLabel(.br, idx);
return .none;
@@ -1677,7 +1732,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
}
case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
- try self.startBlock(.block, blocktype, null);
+ try self.startBlock(.block, blocktype);
}
// When the highest and lowest values are seperated by '50',
@@ -1690,7 +1745,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
const has_else_body = else_body.len != 0;
if (has_else_body) {
- try self.startBlock(.block, blocktype, null);
+ try self.startBlock(.block, blocktype);
}
if (!is_sparse) {
@@ -1698,7 +1753,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
// The value 'target' represents the index into the table.
// Each index in the table represents a label to the branch
// to jump to.
- try self.startBlock(.block, blocktype, null);
+ try self.startBlock(.block, blocktype);
try self.emitWValue(target);
if (lowest < 0) {
// since br_table works using indexes, starting from '0', we must ensure all values
@@ -1754,7 +1809,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
try self.addLabel(.br_if, 0);
} else {
// in multi-value prongs we must check if any prongs match the target value.
- try self.startBlock(.block, blocktype, null);
+ try self.startBlock(.block, blocktype);
for (case.values) |value| {
try self.emitWValue(target);
try self.emitConstant(value.value, target_ty);
@@ -1794,7 +1849,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
.alignment = err_ty.abiAlignment(self.target),
});
try self.addInst(.{
- .tag = .i32_load,
+ .tag = .i32_load16_u,
.data = .{ .payload = mem_arg_index },
});
@@ -1811,14 +1866,14 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = self.resolveInst(ty_op.operand);
const err_ty = self.air.typeOf(ty_op.operand);
- const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8);
-
- return self.load(operand, self.air.getRefType(ty_op.ty), offset);
+ const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
+ return self.load(operand, err_ty.errorUnionPayload(), offset);
}
fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
- return self.resolveInst(ty_op.operand);
+ _ = ty_op;
+ return self.fail("TODO: wasm airWrapErrUnionPayload", .{});
}
fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
@@ -1880,8 +1935,9 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
var buf: Type.Payload.ElemType = undefined;
const child_ty = opt_ty.optionalChild(&buf);
+ const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target);
- return self.load(operand, child_ty, @as(u32, 1)); // null tag is 1 byte
+ return self.load(operand, child_ty, @intCast(u32, offset));
}
fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
@@ -1893,5 +1949,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
- return self.resolveInst(ty_op.operand);
+ const operand = self.resolveInst(ty_op.operand);
+
+ const op_ty = self.air.typeOf(ty_op.operand);
+ const optional_ty = self.air.getRefType(ty_op.ty);
+ const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target);
+
+ return WValue{ .local_with_offset = .{
+ .local = operand.local,
+ .offset = @intCast(u32, offset),
+ } };
}
test/stage2/wasm.zig
@@ -740,4 +740,39 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, "0\n");
}
+
+ {
+ var case = ctx.exe("wasm pointers", wasi);
+
+ case.addCompareOutput(
+ \\pub export fn _start() u32 {
+ \\ var x: u32 = 0;
+ \\
+ \\ foo(&x);
+ \\ return x;
+ \\}
+ \\
+ \\fn foo(x: *u32)void {
+ \\ x.* = 2;
+ \\}
+ , "2\n");
+
+ case.addCompareOutput(
+ \\pub export fn _start() u32 {
+ \\ var x: u32 = 0;
+ \\
+ \\ foo(&x);
+ \\ bar(&x);
+ \\ return x;
+ \\}
+ \\
+ \\fn foo(x: *u32)void {
+ \\ x.* = 2;
+ \\}
+ \\
+ \\fn bar(x: *u32) void {
+ \\ x.* += 2;
+ \\}
+ , "4\n");
+ }
}