| ... | @@ -3050,19 +3050,60 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3050,19 +3050,60 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 3050 | } | 3050 | } |
| 3051 | | 3051 | |
| 3052 | /// Given an error union, returns the error | 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 | const err_ty = error_union_ty.errorUnionSet(); | 3059 | const err_ty = error_union_ty.errorUnionSet(); |
| 3055 | const payload_ty = error_union_ty.errorUnionPayload(); | 3060 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 3056 | if (err_ty.errorSetIsEmpty()) { | 3061 | if (err_ty.errorSetIsEmpty()) { |
| 3057 | return MCValue{ .immediate = 0 }; | 3062 | return MCValue{ .immediate = 0 }; |
| 3058 | } | 3063 | } |
| 3059 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 3064 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3060 | return error_union_mcv; | 3065 | return try error_union_bind.resolveToMcv(self); |
| 3061 | } | 3066 | } |
| 3062 | | 3067 | |
| 3063 | const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); | 3068 | const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); |
| 3064 | switch (error_union_mcv) { | 3069 | switch (try error_union_bind.resolveToMcv(self)) { |
| 3065 | .register => return self.fail("TODO errUnionErr for registers", .{}), | 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 | .stack_argument_offset => |off| { | 3107 | .stack_argument_offset => |off| { |
| 3067 | return MCValue{ .stack_argument_offset = off + err_offset }; | 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,27 +3120,69 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV |
| 3079 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | 3120 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 3080 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 3121 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3081 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3122 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 3123 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| 3082 | const error_union_ty = self.air.typeOf(ty_op.operand); | 3124 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 3083 | const mcv = try self.resolveInst(ty_op.operand); | 3125 | |
| 3084 | break :result try self.errUnionErr(mcv, error_union_ty); | 3126 | break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); |
| 3085 | }; | 3127 | }; |
| 3086 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 3128 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3087 | } | 3129 | } |
| 3088 | | 3130 | |
| 3089 | /// Given an error union, returns the payload | 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 | const err_ty = error_union_ty.errorUnionSet(); | 3138 | const err_ty = error_union_ty.errorUnionSet(); |
| 3092 | const payload_ty = error_union_ty.errorUnionPayload(); | 3139 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 3093 | if (err_ty.errorSetIsEmpty()) { | 3140 | if (err_ty.errorSetIsEmpty()) { |
| 3094 | return error_union_mcv; | 3141 | return try error_union_bind.resolveToMcv(self); |
| 3095 | } | 3142 | } |
| 3096 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 3143 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3097 | return MCValue.none; | 3144 | return MCValue.none; |
| 3098 | } | 3145 | } |
| 3099 | | 3146 | |
| 3100 | const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); | 3147 | const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); |
| 3101 | switch (error_union_mcv) { | 3148 | switch (try error_union_bind.resolveToMcv(self)) { |
| 3102 | .register => return self.fail("TODO errUnionPayload for registers", .{}), | 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 | .stack_argument_offset => |off| { | 3186 | .stack_argument_offset => |off| { |
| 3104 | return MCValue{ .stack_argument_offset = off + payload_offset }; | 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,9 +3199,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 3116 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 3199 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 3117 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 3200 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3118 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3201 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 3202 | const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| 3119 | const error_union_ty = self.air.typeOf(ty_op.operand); | 3203 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 3120 | const error_union = try self.resolveInst(ty_op.operand); | 3204 | |
| 3121 | break :result try self.errUnionPayload(error_union, error_union_ty); | 3205 | break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); |
| 3122 | }; | 3206 | }; |
| 3123 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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,9 +3483,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3399 | } | 3483 | } |
| 3400 | | 3484 | |
| 3401 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | 3485 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3402 | const is_volatile = false; // TODO | | |
| 3403 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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,19 +4881,27 @@ fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue |
| 4792 | return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; | 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 { | 4884 | fn isErr( |
| 4796 | const error_type = ty.errorUnionSet(); | 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 | if (error_type.errorSetIsEmpty()) { | 4891 | if (error_type.errorSetIsEmpty()) { |
| 4799 | return MCValue{ .immediate = 0 }; // always false | 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 | return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); | 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 { | 4899 | fn isNonErr( |
| 4807 | const is_err_result = try self.isErr(ty, operand); | 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 | switch (is_err_result) { | 4905 | switch (is_err_result) { |
| 4809 | .compare_flags => |cond| { | 4906 | .compare_flags => |cond| { |
| 4810 | assert(cond == .hi); | 4907 | assert(cond == .hi); |
| ... | @@ -4873,9 +4970,10 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4873,9 +4970,10 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4873 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | 4970 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4874 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4971 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4875 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4972 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4876 | const operand = try self.resolveInst(un_op); | 4973 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| 4877 | const ty = self.air.typeOf(un_op); | 4974 | const error_union_ty = self.air.typeOf(un_op); |
| 4878 | break :result try self.isErr(ty, operand); | 4975 | |
| | 4976 | break :result try self.isErr(error_union_bind, error_union_ty); |
| 4879 | }; | 4977 | }; |
| 4880 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +4988,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4890 | const operand = try self.allocRegOrMem(elem_ty, true, null); | 4988 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4891 | try self.load(operand, operand_ptr, ptr_ty); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,9 +4996,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4898 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { | 4996 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4899 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4997 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4900 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4998 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4901 | const operand = try self.resolveInst(un_op); | 4999 | const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; |
| 4902 | const ty = self.air.typeOf(un_op); | 5000 | const error_union_ty = self.air.typeOf(un_op); |
| 4903 | break :result try self.isNonErr(ty, operand); | 5001 | |
| | 5002 | break :result try self.isNonErr(error_union_bind, error_union_ty); |
| 4904 | }; | 5003 | }; |
| 4905 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +5014,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4915 | const operand = try self.allocRegOrMem(elem_ty, true, null); | 5014 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4916 | try self.load(operand, operand_ptr, ptr_ty); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,15 +6059,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { |
| 5960 | const extra = self.air.extraData(Air.Try, pl_op.payload); | 6059 | const extra = self.air.extraData(Air.Try, pl_op.payload); |
| 5961 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; | 6060 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 5962 | const result: MCValue = result: { | 6061 | const result: MCValue = result: { |
| | 6062 | const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; |
| 5963 | const error_union_ty = self.air.typeOf(pl_op.operand); | 6063 | const error_union_ty = self.air.typeOf(pl_op.operand); |
| 5964 | const error_union = try self.resolveInst(pl_op.operand); | 6064 | const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); |
| 5965 | const is_err_result = try self.isErr(error_union_ty, error_union); | 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 | const reloc = try self.condBr(is_err_result); | 6074 | const reloc = try self.condBr(is_err_result); |
| 5967 | | 6075 | |
| 5968 | try self.genBody(body); | 6076 | try self.genBody(body); |
| 5969 | | | |
| 5970 | try self.performReloc(reloc); | 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 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); | 6081 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); |
| 5974 | } | 6082 | } |