| ... | ... | @@ -1641,12 +1641,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1641 | 1641 | } |
| 1642 | 1642 | }, |
| 1643 | 1643 | .Struct, .Array => { |
| 1644 | | if (rhs == .constant) { |
| 1644 | const final_rhs = if (rhs == .constant) blk: { |
| 1645 | const tmp = try self.allocLocal(Type.usize); |
| 1645 | 1646 | try self.emitWValue(rhs); |
| 1646 | | try self.addLabel(.local_set, lhs.local); |
| 1647 | | return; |
| 1648 | | } |
| 1649 | | return try self.memCopy(ty, lhs, rhs); |
| 1647 | try self.addLabel(.local_set, tmp.local); |
| 1648 | break :blk tmp; |
| 1649 | } else rhs; |
| 1650 | return try self.memCopy(ty, lhs, final_rhs); |
| 1650 | 1651 | }, |
| 1651 | 1652 | .Pointer => { |
| 1652 | 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 | 2268 | const rhs = self.resolveInst(bin_op.rhs); |
| 2268 | 2269 | const operand_ty = self.air.typeOf(bin_op.lhs); |
| 2269 | 2270 | |
| 2270 | | try self.emitWValue(lhs); |
| 2271 | | try self.emitWValue(rhs); |
| 2272 | | |
| 2273 | 2271 | if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { |
| 2274 | 2272 | var buf: Type.Payload.ElemType = undefined; |
| 2275 | 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 | 2275 | // When we hit this case, we must check the value of optionals |
| 2278 | 2276 | // that are not pointers. This means first checking against non-null for |
| 2279 | 2277 | // 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 | } |
| 2283 | 2281 | |
| 2282 | try self.emitWValue(lhs); |
| 2283 | try self.emitWValue(rhs); |
| 2284 | |
| 2284 | 2285 | const signedness: std.builtin.Signedness = blk: { |
| 2285 | 2286 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2286 | 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 | 2706 | |
| 2706 | 2707 | const op_ty = self.air.typeOf(un_op); |
| 2707 | 2708 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; |
| 2709 | return self.isNull(operand, optional_ty, opcode); |
| 2710 | } |
| 2711 | |
| 2712 | fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 2708 | 2713 | try self.emitWValue(operand); |
| 2709 | 2714 | if (!optional_ty.isPtrLikeOptional()) { |
| 2710 | 2715 | var buf: Type.Payload.ElemType = undefined; |
| 2711 | 2716 | const payload_ty = optional_ty.optionalChild(&buf); |
| 2712 | | // When payload is zero-bits, we can treat operand as a value, rather than a |
| 2713 | | // stack value |
| 2717 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 2718 | // a pointer to the stack value |
| 2714 | 2719 | if (payload_ty.hasCodeGenBits()) { |
| 2715 | 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 | 3210 | try self.addLabel(.local_set, result.local); |
| 3206 | 3211 | return result; |
| 3207 | 3212 | } |
| 3213 | |
| 3214 | fn 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 | } |