authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-10 18:41:44+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-10 21:01:12+01:00
log6a9ddf244a1a6641991ed45b7ff7cacf7c789bfb
treec1074ec4d51341b43c58beec5229d8843c17b772
parente7b7088056d55744694c304f95308ba6e1965455
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement optional compare

We now pass all optionals.zig behavior tests

1 files changed, 57 insertions(+), 11 deletions(-)

src/arch/wasm/CodeGen.zig+57-11
...@@ -1641,12 +1641,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1641,12 +1641,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1641 }1641 }
1642 },1642 },
1643 .Struct, .Array => {1643 .Struct, .Array => {
1644 if (rhs == .constant) {1644 const final_rhs = if (rhs == .constant) blk: {
1645 const tmp = try self.allocLocal(Type.usize);
1645 try self.emitWValue(rhs);1646 try self.emitWValue(rhs);
1646 try self.addLabel(.local_set, lhs.local);1647 try self.addLabel(.local_set, tmp.local);
1647 return;1648 break :blk tmp;
1648 }1649 } else rhs;
1649 return try self.memCopy(ty, lhs, rhs);1650 return try self.memCopy(ty, lhs, final_rhs);
1650 },1651 },
1651 .Pointer => {1652 .Pointer => {
1652 if (ty.isSlice() and rhs == .constant) {1653 if (ty.isSlice() and rhs == .constant) {
...@@ -2267,9 +2268,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -2267,9 +2268,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
2267 const rhs = self.resolveInst(bin_op.rhs);2268 const rhs = self.resolveInst(bin_op.rhs);
2268 const operand_ty = self.air.typeOf(bin_op.lhs);2269 const operand_ty = self.air.typeOf(bin_op.lhs);
22692270
2270 try self.emitWValue(lhs);
2271 try self.emitWValue(rhs);
2272
2273 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {2271 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {
2274 var buf: Type.Payload.ElemType = undefined;2272 var buf: Type.Payload.ElemType = undefined;
2275 const payload_ty = operand_ty.optionalChild(&buf);2273 const payload_ty = operand_ty.optionalChild(&buf);
...@@ -2277,10 +2275,13 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -2277,10 +2275,13 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
2277 // When we hit this case, we must check the value of optionals2275 // When we hit this case, we must check the value of optionals
2278 // that are not pointers. This means first checking against non-null for2276 // that are not pointers. This means first checking against non-null for
2279 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs2277 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
2280 return self.fail("TODO: Implement airCmp for comparing optionals", .{});2278 return self.cmpOptionals(lhs, rhs, operand_ty, op);
2281 }2279 }
2282 }2280 }
22832281
2282 try self.emitWValue(lhs);
2283 try self.emitWValue(rhs);
2284
2284 const signedness: std.builtin.Signedness = blk: {2285 const signedness: std.builtin.Signedness = blk: {
2285 // by default we tell the operand type is unsigned (i.e. bools and enum values)2286 // by default we tell the operand type is unsigned (i.e. bools and enum values)
2286 if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned;2287 if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned;
...@@ -2705,12 +2706,16 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en...@@ -2705,12 +2706,16 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en
27052706
2706 const op_ty = self.air.typeOf(un_op);2707 const op_ty = self.air.typeOf(un_op);
2707 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;2708 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
2709 return self.isNull(operand, optional_ty, opcode);
2710}
2711
2712fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
2708 try self.emitWValue(operand);2713 try self.emitWValue(operand);
2709 if (!optional_ty.isPtrLikeOptional()) {2714 if (!optional_ty.isPtrLikeOptional()) {
2710 var buf: Type.Payload.ElemType = undefined;2715 var buf: Type.Payload.ElemType = undefined;
2711 const payload_ty = optional_ty.optionalChild(&buf);2716 const payload_ty = optional_ty.optionalChild(&buf);
2712 // When payload is zero-bits, we can treat operand as a value, rather than a2717 // When payload is zero-bits, we can treat operand as a value, rather than
2713 // stack value2718 // a pointer to the stack value
2714 if (payload_ty.hasCodeGenBits()) {2719 if (payload_ty.hasCodeGenBits()) {
2715 try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });2720 try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });
2716 }2721 }
...@@ -3205,3 +3210,44 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3205,3 +3210,44 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3205 try self.addLabel(.local_set, result.local);3210 try self.addLabel(.local_set, result.local);
3206 return result;3211 return result;
3207}3212}
3213
3214fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
3215 assert(operand_ty.hasCodeGenBits());
3216 assert(op == .eq or op == .neq);
3217 var buf: Type.Payload.ElemType = undefined;
3218 const payload_ty = operand_ty.optionalChild(&buf);
3219 const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target));
3220
3221 const lhs_is_null = try self.isNull(lhs, operand_ty, .i32_eq);
3222 const rhs_is_null = try self.isNull(rhs, operand_ty, .i32_eq);
3223
3224 // We store the final result in here that will be validated
3225 // if the optional is truly equal.
3226 const result = try self.allocLocal(Type.initTag(.i32));
3227
3228 try self.startBlock(.block, wasm.block_empty);
3229 try self.emitWValue(lhs_is_null);
3230 try self.emitWValue(rhs_is_null);
3231 try self.addTag(.i32_ne); // inverse so we can exit early
3232 try self.addLabel(.br_if, 0);
3233
3234 const lhs_pl = try self.load(lhs, payload_ty, offset);
3235 const rhs_pl = try self.load(rhs, payload_ty, offset);
3236
3237 try self.emitWValue(lhs_pl);
3238 try self.emitWValue(rhs_pl);
3239 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = try self.typeToValtype(payload_ty) });
3240 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3241 try self.addLabel(.br_if, 0);
3242
3243 try self.addImm32(1);
3244 try self.addLabel(.local_set, result.local);
3245 try self.endBlock();
3246
3247 const is_equal = try self.allocLocal(Type.initTag(.i32));
3248 try self.emitWValue(result);
3249 try self.addImm32(0);
3250 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
3251 try self.addLabel(.local_set, is_equal.local);
3252 return is_equal;
3253}