| ... | @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1104 | | 1104 | |
| 1105 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 1105 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1106 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1106 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1107 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); | 1107 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 1108 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| | 1109 | const payload_ty = error_union_ty.errorUnionPayload(); |
| | 1110 | if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; |
| | 1111 | |
| | 1112 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); |
| | 1113 | }; |
| 1108 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1114 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1109 | } | 1115 | } |
| 1110 | | 1116 | |
| ... | @@ -2008,18 +2014,52 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { | ... | @@ -2008,18 +2014,52 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 2008 | return self.fail("TODO call isNull and invert the result", .{}); | 2014 | return self.fail("TODO call isNull and invert the result", .{}); |
| 2009 | } | 2015 | } |
| 2010 | | 2016 | |
| 2011 | fn isErr(self: *Self, operand: MCValue) !MCValue { | 2017 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2012 | _ = operand; | 2018 | _ = operand; |
| 2013 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2019 | |
| 2014 | // will call isNonNull and invert the result. | 2020 | const error_type = ty.errorUnionSet(); |
| 2015 | return self.fail("TODO call isNonErr and invert the result", .{}); | 2021 | const payload_type = ty.errorUnionPayload(); |
| | 2022 | |
| | 2023 | if (!error_type.hasRuntimeBits()) { |
| | 2024 | return MCValue{ .immediate = 0 }; // always false |
| | 2025 | } else if (!payload_type.hasRuntimeBits()) { |
| | 2026 | if (error_type.abiSize(self.target.*) <= 8) { |
| | 2027 | const reg_mcv: MCValue = switch (operand) { |
| | 2028 | .register => operand, |
| | 2029 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, |
| | 2030 | }; |
| | 2031 | |
| | 2032 | _ = try self.addInst(.{ |
| | 2033 | .tag = .cmp_immediate, |
| | 2034 | .data = .{ .rr_imm12_sh = .{ |
| | 2035 | .rd = .xzr, |
| | 2036 | .rn = reg_mcv.register, |
| | 2037 | .imm12 = 0, |
| | 2038 | } }, |
| | 2039 | }); |
| | 2040 | |
| | 2041 | return MCValue{ .compare_flags_unsigned = .gt }; |
| | 2042 | } else { |
| | 2043 | return self.fail("TODO isErr for errors with size > 8", .{}); |
| | 2044 | } |
| | 2045 | } else { |
| | 2046 | return self.fail("TODO isErr for non-empty payloads", .{}); |
| | 2047 | } |
| 2016 | } | 2048 | } |
| 2017 | | 2049 | |
| 2018 | fn isNonErr(self: *Self, operand: MCValue) !MCValue { | 2050 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2019 | _ = operand; | 2051 | const is_err_result = try self.isErr(ty, operand); |
| 2020 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2052 | switch (is_err_result) { |
| 2021 | // will call isNull and invert the result. | 2053 | .compare_flags_unsigned => |op| { |
| 2022 | return self.fail("TODO call isErr and invert the result", .{}); | 2054 | assert(op == .gt); |
| | 2055 | return MCValue{ .compare_flags_unsigned = .lte }; |
| | 2056 | }, |
| | 2057 | .immediate => |imm| { |
| | 2058 | assert(imm == 0); |
| | 2059 | return MCValue{ .immediate = 1 }; |
| | 2060 | }, |
| | 2061 | else => unreachable, |
| | 2062 | } |
| 2023 | } | 2063 | } |
| 2024 | | 2064 | |
| 2025 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | 2065 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2080,7 +2120,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2080,7 +2120,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2080 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2120 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2081 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2121 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2082 | const operand = try self.resolveInst(un_op); | 2122 | const operand = try self.resolveInst(un_op); |
| 2083 | break :result try self.isErr(operand); | 2123 | const ty = self.air.typeOf(un_op); |
| | 2124 | break :result try self.isErr(ty, operand); |
| 2084 | }; | 2125 | }; |
| 2085 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2126 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2086 | } | 2127 | } |
| ... | @@ -2089,6 +2130,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2089,6 +2130,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2089 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2130 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2090 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2131 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2091 | const operand_ptr = try self.resolveInst(un_op); | 2132 | const operand_ptr = try self.resolveInst(un_op); |
| | 2133 | const ptr_ty = self.air.typeOf(un_op); |
| 2092 | const operand: MCValue = blk: { | 2134 | const operand: MCValue = blk: { |
| 2093 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 2135 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2094 | // The MCValue that holds the pointer can be re-used as the value. | 2136 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | @@ -2098,7 +2140,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2098,7 +2140,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2098 | } | 2140 | } |
| 2099 | }; | 2141 | }; |
| 2100 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2142 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2101 | break :result try self.isErr(operand); | 2143 | break :result try self.isErr(ptr_ty.elemType(), operand); |
| 2102 | }; | 2144 | }; |
| 2103 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2145 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2104 | } | 2146 | } |
| ... | @@ -2107,7 +2149,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2107,7 +2149,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2107 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2149 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2108 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2150 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2109 | const operand = try self.resolveInst(un_op); | 2151 | const operand = try self.resolveInst(un_op); |
| 2110 | break :result try self.isNonErr(operand); | 2152 | const ty = self.air.typeOf(un_op); |
| | 2153 | break :result try self.isNonErr(ty, operand); |
| 2111 | }; | 2154 | }; |
| 2112 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2155 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2113 | } | 2156 | } |
| ... | @@ -2116,6 +2159,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2116,6 +2159,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2116 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2159 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2117 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2160 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2118 | const operand_ptr = try self.resolveInst(un_op); | 2161 | const operand_ptr = try self.resolveInst(un_op); |
| | 2162 | const ptr_ty = self.air.typeOf(un_op); |
| 2119 | const operand: MCValue = blk: { | 2163 | const operand: MCValue = blk: { |
| 2120 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 2164 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2121 | // The MCValue that holds the pointer can be re-used as the value. | 2165 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | @@ -2125,7 +2169,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2125,7 +2169,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2125 | } | 2169 | } |
| 2126 | }; | 2170 | }; |
| 2127 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2171 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2128 | break :result try self.isNonErr(operand); | 2172 | break :result try self.isNonErr(ptr_ty.elemType(), operand); |
| 2129 | }; | 2173 | }; |
| 2130 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2174 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2131 | } | 2175 | } |
| ... | @@ -2864,14 +2908,23 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | ... | @@ -2864,14 +2908,23 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 2864 | .ErrorUnion => { | 2908 | .ErrorUnion => { |
| 2865 | const error_type = typed_value.ty.errorUnionSet(); | 2909 | const error_type = typed_value.ty.errorUnionSet(); |
| 2866 | const payload_type = typed_value.ty.errorUnionPayload(); | 2910 | const payload_type = typed_value.ty.errorUnionPayload(); |
| 2867 | const sub_val = typed_value.val.castTag(.eu_payload).?.data; | | |
| 2868 | | 2911 | |
| 2869 | if (!payload_type.hasRuntimeBits()) { | 2912 | if (typed_value.val.castTag(.eu_payload)) |pl| { |
| 2870 | // We use the error type directly as the type. | 2913 | if (!payload_type.hasRuntimeBits()) { |
| 2871 | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); | 2914 | // We use the error type directly as the type. |
| 2872 | } | 2915 | return MCValue{ .immediate = 0 }; |
| | 2916 | } |
| 2873 | | 2917 | |
| 2874 | return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); | 2918 | _ = pl; |
| | 2919 | return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); |
| | 2920 | } else { |
| | 2921 | if (!payload_type.hasRuntimeBits()) { |
| | 2922 | // We use the error type directly as the type. |
| | 2923 | return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); |
| | 2924 | } |
| | 2925 | |
| | 2926 | return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); |
| | 2927 | } |
| 2875 | }, | 2928 | }, |
| 2876 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), | 2929 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), |
| 2877 | } | 2930 | } |