| ... | ... | @@ -3050,19 +3050,60 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 3050 | 3050 | } |
| 3051 | 3051 | |
| 3052 | 3052 | /// Given an error union, returns the error |
| 3053 | | fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { |
| 3053 | fn errUnionErr( |
| 3054 | self: *Self, |
| 3055 | error_union_bind: ReadArg.Bind, |
| 3056 | error_union_ty: Type, |
| 3057 | maybe_inst: ?Air.Inst.Index, |
| 3058 | ) !MCValue { |
| 3054 | 3059 | const err_ty = error_union_ty.errorUnionSet(); |
| 3055 | 3060 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 3056 | 3061 | if (err_ty.errorSetIsEmpty()) { |
| 3057 | 3062 | return MCValue{ .immediate = 0 }; |
| 3058 | 3063 | } |
| 3059 | 3064 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3060 | | return error_union_mcv; |
| 3065 | return try error_union_bind.resolveToMcv(self); |
| 3061 | 3066 | } |
| 3062 | 3067 | |
| 3063 | 3068 | const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); |
| 3064 | | switch (error_union_mcv) { |
| 3065 | | .register => return self.fail("TODO errUnionErr for registers", .{}), |
| 3069 | switch (try error_union_bind.resolveToMcv(self)) { |
| 3070 | .register => { |
| 3071 | var operand_reg: Register = undefined; |
| 3072 | var dest_reg: Register = undefined; |
| 3073 | |
| 3074 | const read_args = [_]ReadArg{ |
| 3075 | .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, |
| 3076 | }; |
| 3077 | const write_args = [_]WriteArg{ |
| 3078 | .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 3079 | }; |
| 3080 | try self.allocRegs( |
| 3081 | &read_args, |
| 3082 | &write_args, |
| 3083 | if (maybe_inst) |inst| .{ |
| 3084 | .corresponding_inst = inst, |
| 3085 | .operand_mapping = &.{0}, |
| 3086 | } else null, |
| 3087 | ); |
| 3088 | |
| 3089 | const err_bit_offset = err_offset * 8; |
| 3090 | const err_bit_size = @intCast(u32, err_ty.abiSize(self.target.*)) * 8; |
| 3091 | |
| 3092 | _ = try self.addInst(.{ |
| 3093 | .tag = .ubfx, // errors are unsigned integers |
| 3094 | .data = .{ |
| 3095 | .rr_lsb_width = .{ |
| 3096 | // Set both registers to the X variant to get the full width |
| 3097 | .rd = dest_reg.toX(), |
| 3098 | .rn = operand_reg.toX(), |
| 3099 | .lsb = @intCast(u6, err_bit_offset), |
| 3100 | .width = @intCast(u7, err_bit_size), |
| 3101 | }, |
| 3102 | }, |
| 3103 | }); |
| 3104 | |
| 3105 | return MCValue{ .register = dest_reg }; |
| 3106 | }, |
| 3066 | 3107 | .stack_argument_offset => |off| { |
| 3067 | 3108 | return MCValue{ .stack_argument_offset = off + err_offset }; |
| 3068 | 3109 | }, |
| ... | ... | @@ -3079,27 +3120,69 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV |
| 3079 | 3120 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 3080 | 3121 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3081 | 3122 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3123 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| 3082 | 3124 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 3083 | | const mcv = try self.resolveInst(ty_op.operand); |
| 3084 | | break :result try self.errUnionErr(mcv, error_union_ty); |
| 3125 | |
| 3126 | break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); |
| 3085 | 3127 | }; |
| 3086 | 3128 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3087 | 3129 | } |
| 3088 | 3130 | |
| 3089 | 3131 | /// Given an error union, returns the payload |
| 3090 | | fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { |
| 3132 | fn errUnionPayload( |
| 3133 | self: *Self, |
| 3134 | error_union_bind: ReadArg.Bind, |
| 3135 | error_union_ty: Type, |
| 3136 | maybe_inst: ?Air.Inst.Index, |
| 3137 | ) !MCValue { |
| 3091 | 3138 | const err_ty = error_union_ty.errorUnionSet(); |
| 3092 | 3139 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 3093 | 3140 | if (err_ty.errorSetIsEmpty()) { |
| 3094 | | return error_union_mcv; |
| 3141 | return try error_union_bind.resolveToMcv(self); |
| 3095 | 3142 | } |
| 3096 | 3143 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3097 | 3144 | return MCValue.none; |
| 3098 | 3145 | } |
| 3099 | 3146 | |
| 3100 | 3147 | const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); |
| 3101 | | switch (error_union_mcv) { |
| 3102 | | .register => return self.fail("TODO errUnionPayload for registers", .{}), |
| 3148 | switch (try error_union_bind.resolveToMcv(self)) { |
| 3149 | .register => { |
| 3150 | var operand_reg: Register = undefined; |
| 3151 | var dest_reg: Register = undefined; |
| 3152 | |
| 3153 | const read_args = [_]ReadArg{ |
| 3154 | .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, |
| 3155 | }; |
| 3156 | const write_args = [_]WriteArg{ |
| 3157 | .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 3158 | }; |
| 3159 | try self.allocRegs( |
| 3160 | &read_args, |
| 3161 | &write_args, |
| 3162 | if (maybe_inst) |inst| .{ |
| 3163 | .corresponding_inst = inst, |
| 3164 | .operand_mapping = &.{0}, |
| 3165 | } else null, |
| 3166 | ); |
| 3167 | |
| 3168 | const payload_bit_offset = payload_offset * 8; |
| 3169 | const payload_bit_size = @intCast(u32, payload_ty.abiSize(self.target.*)) * 8; |
| 3170 | |
| 3171 | _ = try self.addInst(.{ |
| 3172 | .tag = if (payload_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx, |
| 3173 | .data = .{ |
| 3174 | .rr_lsb_width = .{ |
| 3175 | // Set both registers to the X variant to get the full width |
| 3176 | .rd = dest_reg.toX(), |
| 3177 | .rn = operand_reg.toX(), |
| 3178 | .lsb = @intCast(u5, payload_bit_offset), |
| 3179 | .width = @intCast(u6, payload_bit_size), |
| 3180 | }, |
| 3181 | }, |
| 3182 | }); |
| 3183 | |
| 3184 | return MCValue{ .register = dest_reg }; |
| 3185 | }, |
| 3103 | 3186 | .stack_argument_offset => |off| { |
| 3104 | 3187 | return MCValue{ .stack_argument_offset = off + payload_offset }; |
| 3105 | 3188 | }, |
| ... | ... | @@ -3116,9 +3199,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 3116 | 3199 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 3117 | 3200 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3118 | 3201 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3202 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| 3119 | 3203 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 3120 | | const error_union = try self.resolveInst(ty_op.operand); |
| 3121 | | break :result try self.errUnionPayload(error_union, error_union_ty); |
| 3204 | |
| 3205 | break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); |
| 3122 | 3206 | }; |
| 3123 | 3207 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3124 | 3208 | } |
| ... | ... | @@ -3399,9 +3483,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3399 | 3483 | } |
| 3400 | 3484 | |
| 3401 | 3485 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3402 | | const is_volatile = false; // TODO |
| 3403 | 3486 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3404 | | const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}); |
| 3487 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 3488 | const result: MCValue = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { |
| 3489 | const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; |
| 3490 | const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 3491 | |
| 3492 | break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); |
| 3493 | }; |
| 3405 | 3494 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3406 | 3495 | } |
| 3407 | 3496 | |
| ... | ... | @@ -4792,19 +4881,27 @@ fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue |
| 4792 | 4881 | return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; |
| 4793 | 4882 | } |
| 4794 | 4883 | |
| 4795 | | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 4796 | | const error_type = ty.errorUnionSet(); |
| 4884 | fn isErr( |
| 4885 | self: *Self, |
| 4886 | error_union_bind: ReadArg.Bind, |
| 4887 | error_union_ty: Type, |
| 4888 | ) !MCValue { |
| 4889 | const error_type = error_union_ty.errorUnionSet(); |
| 4797 | 4890 | |
| 4798 | 4891 | if (error_type.errorSetIsEmpty()) { |
| 4799 | 4892 | return MCValue{ .immediate = 0 }; // always false |
| 4800 | 4893 | } |
| 4801 | 4894 | |
| 4802 | | const error_mcv = try self.errUnionErr(operand, ty); |
| 4895 | const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); |
| 4803 | 4896 | return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); |
| 4804 | 4897 | } |
| 4805 | 4898 | |
| 4806 | | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 4807 | | const is_err_result = try self.isErr(ty, operand); |
| 4899 | fn isNonErr( |
| 4900 | self: *Self, |
| 4901 | error_union_bind: ReadArg.Bind, |
| 4902 | error_union_ty: Type, |
| 4903 | ) !MCValue { |
| 4904 | const is_err_result = try self.isErr(error_union_bind, error_union_ty); |
| 4808 | 4905 | switch (is_err_result) { |
| 4809 | 4906 | .compare_flags => |cond| { |
| 4810 | 4907 | assert(cond == .hi); |
| ... | ... | @@ -4873,9 +4970,10 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4873 | 4970 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4874 | 4971 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4875 | 4972 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4876 | | const operand = try self.resolveInst(un_op); |
| 4877 | | const ty = self.air.typeOf(un_op); |
| 4878 | | break :result try self.isErr(ty, operand); |
| 4973 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| 4974 | const error_union_ty = self.air.typeOf(un_op); |
| 4975 | |
| 4976 | break :result try self.isErr(error_union_bind, error_union_ty); |
| 4879 | 4977 | }; |
| 4880 | 4978 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4881 | 4979 | } |
| ... | ... | @@ -4890,7 +4988,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4890 | 4988 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4891 | 4989 | try self.load(operand, operand_ptr, ptr_ty); |
| 4892 | 4990 | |
| 4893 | | break :result try self.isErr(elem_ty, operand); |
| 4991 | break :result try self.isErr(.{ .mcv = operand }, elem_ty); |
| 4894 | 4992 | }; |
| 4895 | 4993 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4896 | 4994 | } |
| ... | ... | @@ -4898,9 +4996,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4898 | 4996 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4899 | 4997 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4900 | 4998 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4901 | | const operand = try self.resolveInst(un_op); |
| 4902 | | const ty = self.air.typeOf(un_op); |
| 4903 | | break :result try self.isNonErr(ty, operand); |
| 4999 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| 5000 | const error_union_ty = self.air.typeOf(un_op); |
| 5001 | |
| 5002 | break :result try self.isNonErr(error_union_bind, error_union_ty); |
| 4904 | 5003 | }; |
| 4905 | 5004 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4906 | 5005 | } |
| ... | ... | @@ -4915,7 +5014,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4915 | 5014 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4916 | 5015 | try self.load(operand, operand_ptr, ptr_ty); |
| 4917 | 5016 | |
| 4918 | | break :result try self.isNonErr(elem_ty, operand); |
| 5017 | break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); |
| 4919 | 5018 | }; |
| 4920 | 5019 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4921 | 5020 | } |
| ... | ... | @@ -5960,15 +6059,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { |
| 5960 | 6059 | const extra = self.air.extraData(Air.Try, pl_op.payload); |
| 5961 | 6060 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 5962 | 6061 | const result: MCValue = result: { |
| 6062 | const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; |
| 5963 | 6063 | const error_union_ty = self.air.typeOf(pl_op.operand); |
| 5964 | | const error_union = try self.resolveInst(pl_op.operand); |
| 5965 | | const is_err_result = try self.isErr(error_union_ty, error_union); |
| 6064 | const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); |
| 6065 | const error_union_align = error_union_ty.abiAlignment(self.target.*); |
| 6066 | |
| 6067 | // The error union will die in the body. However, we need the |
| 6068 | // error union after the body in order to extract the payload |
| 6069 | // of the error union, so we create a copy of it |
| 6070 | const error_union_copy = try self.allocMem(error_union_size, error_union_align, null); |
| 6071 | try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self)); |
| 6072 | |
| 6073 | const is_err_result = try self.isErr(error_union_bind, error_union_ty); |
| 5966 | 6074 | const reloc = try self.condBr(is_err_result); |
| 5967 | 6075 | |
| 5968 | 6076 | try self.genBody(body); |
| 5969 | | |
| 5970 | 6077 | try self.performReloc(reloc); |
| 5971 | | break :result try self.errUnionPayload(error_union, error_union_ty); |
| 6078 | |
| 6079 | break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null); |
| 5972 | 6080 | }; |
| 5973 | 6081 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); |
| 5974 | 6082 | } |