Commit 29164a31cc
Changed files (2)
src
arch
wasm
test
src/arch/wasm/CodeGen.zig
@@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {
try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
}
+/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
+fn addMemArg(self: *Self, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
+ const extra_index = try self.addExtra(mem_arg);
+ try self.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
+}
+
/// Appends entries to `mir_extra` based on the type of `extra`.
/// Returns the index into `mir_extra`
fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
@@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
.ErrorUnion,
.Optional,
.Fn,
+ .Array,
=> wasm.Valtype.i32,
- else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),
+ else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}),
};
}
@@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
switch (return_type.zigTypeTag()) {
.Void, .NoReturn => {},
.Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
- .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
else => try returns.append(try self.typeToValtype(return_type)),
}
@@ -1146,6 +1152,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.cmp_lt => self.airCmp(inst, .lt),
.cmp_neq => self.airCmp(inst, .neq),
+ .array_to_slice => self.airArrayToSlice(inst),
.alloc => self.airAlloc(inst),
.arg => self.airArg(inst),
.bitcast => self.airBitcast(inst),
@@ -1178,6 +1185,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.ret_load => self.airRetLoad(inst),
.slice_len => self.airSliceLen(inst),
.slice_elem_val => self.airSliceElemVal(inst),
+ .slice_elem_ptr => self.airSliceElemPtr(inst),
.slice_ptr => self.airSlicePtr(inst),
.store => self.airStore(inst),
.struct_field_ptr => self.airStructFieldPtr(inst),
@@ -1283,6 +1291,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
.Struct, .Pointer, .Optional, .ErrorUnion => {
// single pointer can be passed directly
if (arg_ty.isSinglePointer() or arg_val != .constant) {
+ if (arg_val == .none) {
+ // when the argument is a 0-sized value, but the function
+ // expects a non-zero typed value (such as a slice), we must emit an argument
+ // as function calls are verified with the function signature in wasm.
+ // In those cases we will emit a '0xaa' as address, meaning invalid memory.
+ try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa)));
+ continue;
+ }
try self.emitWValue(arg_val);
continue;
}
@@ -1467,14 +1483,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
});
// store rhs value at stack pointer's location in memory
- const mem_arg_index = try self.addExtra(Mir.MemArg{
- .offset = offset,
- .alignment = ty.abiAlignment(self.target),
- });
- try self.addInst(.{
- .tag = Mir.Inst.Tag.fromOpcode(opcode),
- .data = .{ .payload = mem_arg_index },
- });
+ try self.addMemArg(
+ Mir.Inst.Tag.fromOpcode(opcode),
+ .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
+ );
}
fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
@@ -1518,14 +1530,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
.signedness = signedness,
});
- const mem_arg_index = try self.addExtra(Mir.MemArg{
- .offset = offset,
- .alignment = ty.abiAlignment(self.target),
- });
- try self.addInst(.{
- .tag = Mir.Inst.Tag.fromOpcode(opcode),
- .data = .{ .payload = mem_arg_index },
- });
+ try self.addMemArg(
+ Mir.Inst.Tag.fromOpcode(opcode),
+ .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
+ );
// store the result in a local
const result = try self.allocLocal(ty);
@@ -1713,10 +1721,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
// When constant has value 'null', set is_null local to '1'
// and payload to '0'
- if (val.castTag(.opt_payload)) |pl| {
- const payload_val = pl.data;
+ if (val.castTag(.opt_payload)) |payload| {
try self.addImm32(0);
- try self.emitConstant(payload_val, payload_type);
+ if (payload_type.hasCodeGenBits())
+ try self.emitConstant(payload.data, payload_type);
} else {
// set null-tag
try self.addImm32(1);
@@ -1740,6 +1748,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {
33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
},
+ .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
}
}
@@ -1953,7 +1962,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
fn airBitcast(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);
+ if (operand == .constant) {
+ const result = try self.allocLocal(self.air.typeOfIndex(inst));
+ try self.emitWValue(operand);
+ try self.addLabel(.local_set, result.local);
+ return result;
+ }
+ return operand;
}
fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
@@ -1973,12 +1989,21 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const struct_ptr = self.resolveInst(ty_op.operand);
const struct_ty = self.air.typeOf(ty_op.operand).childType();
+ const field_ty = struct_ty.structFieldType(index);
const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
return self.fail("Field type '{}' too big to fit into stack frame", .{
- struct_ty.structFieldType(index),
+ field_ty,
});
};
- return structFieldPtr(struct_ptr, offset);
+ // field points to another struct, so retrieve that struct first
+ switch (struct_ptr) {
+ .local => return structFieldPtr(struct_ptr, offset),
+ .local_with_offset => |with_offset| {
+ const result = try self.load(struct_ptr, field_ty, with_offset.offset);
+ return structFieldPtr(result, offset);
+ },
+ else => unreachable,
+ }
}
fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {
@@ -2156,14 +2181,10 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
// load the error tag value
try self.emitWValue(operand);
- const mem_arg_index = try self.addExtra(Mir.MemArg{
- .offset = 0,
- .alignment = err_ty.abiAlignment(self.target),
- });
- try self.addInst(.{
- .tag = .i32_load16_u,
- .data = .{ .payload = mem_arg_index },
- });
+ try self.addMemArg(
+ .i32_load16_u,
+ .{ .offset = 0, .alignment = err_ty.abiAlignment(self.target) },
+ );
// Compare the error value with '0'
try self.addImm32(0);
@@ -2257,11 +2278,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
// load the null tag value
try self.emitWValue(operand);
- const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 });
- try self.addInst(.{
- .tag = .i32_load8_u,
- .data = .{ .payload = mem_arg_index },
- });
+ try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });
// Compare the error value with '0'
try self.addImm32(0);
@@ -2353,6 +2370,31 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
};
}
+fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue.none;
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
+ const slice_ty = self.air.typeOf(bin_op.lhs);
+ const elem_ty = self.air.getRefType(ty_pl.ty).childType();
+ const elem_size = elem_ty.abiSize(self.target);
+
+ const slice = self.resolveInst(bin_op.lhs);
+ const index = self.resolveInst(bin_op.rhs);
+
+ const slice_ptr = try self.load(slice, slice_ty, 0);
+ try self.addLabel(.local_get, slice_ptr.local);
+
+ // calculate index into slice
+ try self.emitWValue(index);
+ try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
+ try self.addTag(.i32_mul);
+ try self.addTag(.i32_add);
+
+ const result = try self.allocLocal(Type.initTag(.i32));
+ try self.addLabel(.local_set, result.local);
+ return result;
+}
+
fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue.none;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
@@ -2424,3 +2466,8 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const un_op = self.air.instructions.items(.data)[inst].un_op;
return self.resolveInst(un_op);
}
+
+fn airArrayToSlice(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);
+}
test/behavior.zig
@@ -8,9 +8,18 @@ test {
_ = @import("behavior/bugs/624.zig");
_ = @import("behavior/bugs/655.zig");
_ = @import("behavior/bugs/679.zig");
+ _ = @import("behavior/bugs/704.zig");
_ = @import("behavior/bugs/1111.zig");
_ = @import("behavior/bugs/1486.zig");
_ = @import("behavior/bugs/2346.zig");
+ _ = @import("behavior/bugs/2692.zig");
+ _ = @import("behavior/bugs/2889.zig");
+ _ = @import("behavior/bugs/3046.zig");
+ _ = @import("behavior/bugs/3586.zig");
+ _ = @import("behavior/bugs/4560.zig");
+ _ = @import("behavior/bugs/4769_a.zig");
+ _ = @import("behavior/bugs/4769_b.zig");
+ _ = @import("behavior/bugs/4954.zig");
_ = @import("behavior/bugs/6850.zig");
_ = @import("behavior/enum.zig");
_ = @import("behavior/hasdecl.zig");