Commit 845531dde1

joachimschmidt557 <joachim.schmidt557@outlook.com>
2021-12-31 17:33:08
stage2 ARM: implement airUnwrapErrErr + airCmp for error sets
1 parent f8163f7
Changed files (2)
src
arch
test
stage2
src/arch/arm/CodeGen.zig
@@ -1099,15 +1099,22 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
-    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const error_union_ty = self.air.typeOf(ty_op.operand);
+        const payload_ty = error_union_ty.errorUnionPayload();
+        const mcv = try self.resolveInst(ty_op.operand);
+        if (!payload_ty.hasCodeGenBits()) break :result mcv;
+
+        return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
+    };
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
-        const err_ty = self.air.typeOf(ty_op.operand);
-        const payload_ty = err_ty.errorUnionPayload();
+        const error_union_ty = self.air.typeOf(ty_op.operand);
+        const payload_ty = error_union_ty.errorUnionPayload();
         if (!payload_ty.hasCodeGenBits()) break :result MCValue.none;
 
         return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
@@ -2411,19 +2418,26 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
-    if (self.liveness.isUnused(inst))
-        return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
-    const ty = self.air.typeOf(bin_op.lhs);
-    assert(ty.eql(self.air.typeOf(bin_op.rhs)));
-    if (ty.zigTypeTag() == .ErrorSet)
-        return self.fail("TODO implement cmp for errors", .{});
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const lhs = try self.resolveInst(bin_op.lhs);
+        const rhs = try self.resolveInst(bin_op.rhs);
+        const lhs_ty = self.air.typeOf(bin_op.lhs);
 
-    try self.spillCompareFlagsIfOccupied();
-    self.compare_flags_inst = inst;
+        if (lhs_ty.abiSize(self.target.*) > 4) {
+            return self.fail("TODO cmp for types with size > 4", .{});
+        }
+
+        const signedness: std.builtin.Signedness = blk: {
+            // by default we tell the operand type is unsigned (i.e. bools and enum values)
+            if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
+
+            // incase of an actual integer, we emit the correct signedness
+            break :blk lhs_ty.intInfo(self.target.*).signedness;
+        };
+
+        try self.spillCompareFlagsIfOccupied();
+        self.compare_flags_inst = inst;
 
-    const lhs = try self.resolveInst(bin_op.lhs);
-    const rhs = try self.resolveInst(bin_op.rhs);
-    const result: MCValue = result: {
         const lhs_is_register = lhs == .register;
         const rhs_is_register = rhs == .register;
         // lhs should always be a register
@@ -2457,11 +2471,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
         // Move the operands to the newly allocated registers
         const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
         if (lhs_mcv == .register and !lhs_is_register) {
-            try self.genSetReg(ty, lhs_mcv.register, lhs);
+            try self.genSetReg(lhs_ty, lhs_mcv.register, lhs);
             branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs);
         }
         if (rhs_mcv == .register and !rhs_is_register) {
-            try self.genSetReg(ty, rhs_mcv.register, rhs);
+            try self.genSetReg(lhs_ty, rhs_mcv.register, rhs);
             branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs);
         }
 
@@ -2469,9 +2483,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
         // The signedness of the integer does not matter for the cmp instruction
         try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined);
 
-        break :result switch (ty.isSignedInt()) {
-            true => MCValue{ .compare_flags_signed = op },
-            false => MCValue{ .compare_flags_unsigned = op },
+        break :result switch (signedness) {
+            .signed => MCValue{ .compare_flags_signed = op },
+            .unsigned => MCValue{ .compare_flags_unsigned = op },
         };
     };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
test/stage2/arm.zig
@@ -636,6 +636,35 @@ pub fn addCases(ctx: *TestContext) !void {
         ,
             "Hello, World!\n",
         );
+
+        case.addCompareOutput(
+            \\pub fn main() void {
+            \\    foo() catch |err| {
+            \\        assert(err == error.Foo);
+            \\        assert(err != error.Bar);
+            \\        assert(err != error.Baz);
+            \\    };
+            \\    bar() catch |err| {
+            \\        assert(err != error.Foo);
+            \\        assert(err == error.Bar);
+            \\        assert(err != error.Baz);
+            \\    };
+            \\}
+            \\
+            \\fn assert(ok: bool) void {
+            \\    if (!ok) unreachable;
+            \\}
+            \\
+            \\fn foo() anyerror!void {
+            \\    return error.Foo;
+            \\}
+            \\
+            \\fn bar() anyerror!void {
+            \\    return error.Bar;
+            \\}
+        ,
+            "",
+        );
     }
 
     {