| ... | @@ -936,35 +936,34 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { | ... | @@ -936,35 +936,34 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 936 | }; | 936 | }; |
| 937 | // TODO swap this for inst.ty.ptrAlign | 937 | // TODO swap this for inst.ty.ptrAlign |
| 938 | const abi_align = elem_ty.abiAlignment(self.target.*); | 938 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| | 939 | |
| 939 | return self.allocMem(abi_size, abi_align, inst); | 940 | return self.allocMem(abi_size, abi_align, inst); |
| 940 | } | 941 | } |
| 941 | | 942 | |
| 942 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | 943 | fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { |
| 943 | const elem_ty = self.air.typeOfIndex(inst); | | |
| 944 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { | 944 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { |
| 945 | const mod = self.bin_file.options.module.?; | 945 | const mod = self.bin_file.options.module.?; |
| 946 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); | 946 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| 947 | }; | 947 | }; |
| 948 | const abi_align = elem_ty.abiAlignment(self.target.*); | 948 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 949 | if (abi_align > self.stack_align) | | |
| 950 | self.stack_align = abi_align; | | |
| 951 | | 949 | |
| 952 | if (reg_ok) { | 950 | if (reg_ok) { |
| 953 | // Make sure the type can fit in a register before we try to allocate one. | 951 | // Make sure the type can fit in a register before we try to allocate one. |
| 954 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 952 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 955 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 953 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 956 | if (abi_size <= ptr_bytes) { | 954 | if (abi_size <= ptr_bytes) { |
| 957 | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { | 955 | if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { |
| 958 | return MCValue{ .register = reg }; | 956 | return MCValue{ .register = reg }; |
| 959 | } | 957 | } |
| 960 | } | 958 | } |
| 961 | } | 959 | } |
| 962 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); | 960 | |
| | 961 | const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst); |
| 963 | return MCValue{ .stack_offset = stack_offset }; | 962 | return MCValue{ .stack_offset = stack_offset }; |
| 964 | } | 963 | } |
| 965 | | 964 | |
| 966 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 965 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 967 | const stack_mcv = try self.allocRegOrMem(inst, false); | 966 | const stack_mcv = try self.allocRegOrMem(self.air.typeOfIndex(inst), false, inst); |
| 968 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); | 967 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); |
| 969 | | 968 | |
| 970 | const reg_mcv = self.getResolvedInstValue(inst); | 969 | const reg_mcv = self.getResolvedInstValue(inst); |
| ... | @@ -985,12 +984,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -985,12 +984,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 985 | /// occupied | 984 | /// occupied |
| 986 | fn spillCompareFlagsIfOccupied(self: *Self) !void { | 985 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 987 | if (self.cpsr_flags_inst) |inst_to_save| { | 986 | if (self.cpsr_flags_inst) |inst_to_save| { |
| | 987 | const ty = self.air.typeOfIndex(inst_to_save); |
| 988 | const mcv = self.getResolvedInstValue(inst_to_save); | 988 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 989 | const new_mcv = switch (mcv) { | 989 | const new_mcv = switch (mcv) { |
| 990 | .cpsr_flags => try self.allocRegOrMem(inst_to_save, true), | 990 | .cpsr_flags => try self.allocRegOrMem(ty, true, inst_to_save), |
| 991 | .register_c_flag, | 991 | .register_c_flag, |
| 992 | .register_v_flag, | 992 | .register_v_flag, |
| 993 | => try self.allocRegOrMem(inst_to_save, false), | 993 | => try self.allocRegOrMem(ty, false, inst_to_save), |
| 994 | else => unreachable, // mcv doesn't occupy the compare flags | 994 | else => unreachable, // mcv doesn't occupy the compare flags |
| 995 | }; | 995 | }; |
| 996 | | 996 | |
| ... | @@ -1121,10 +1121,11 @@ fn truncRegister( | ... | @@ -1121,10 +1121,11 @@ fn truncRegister( |
| 1121 | }); | 1121 | }); |
| 1122 | } | 1122 | } |
| 1123 | | 1123 | |
| | 1124 | /// Asserts that both operand_ty and dest_ty are integer types |
| 1124 | fn trunc( | 1125 | fn trunc( |
| 1125 | self: *Self, | 1126 | self: *Self, |
| 1126 | maybe_inst: ?Air.Inst.Index, | 1127 | maybe_inst: ?Air.Inst.Index, |
| 1127 | operand: MCValue, | 1128 | operand_bind: ReadArg.Bind, |
| 1128 | operand_ty: Type, | 1129 | operand_ty: Type, |
| 1129 | dest_ty: Type, | 1130 | dest_ty: Type, |
| 1130 | ) !MCValue { | 1131 | ) !MCValue { |
| ... | @@ -1132,39 +1133,38 @@ fn trunc( | ... | @@ -1132,39 +1133,38 @@ fn trunc( |
| 1132 | const info_b = dest_ty.intInfo(self.target.*); | 1133 | const info_b = dest_ty.intInfo(self.target.*); |
| 1133 | | 1134 | |
| 1134 | if (info_b.bits <= 32) { | 1135 | if (info_b.bits <= 32) { |
| 1135 | const operand_reg = switch (operand) { | 1136 | if (info_a.bits > 32) { |
| 1136 | .register => |r| r, | 1137 | return self.fail("TODO load least significant word into register", .{}); |
| 1137 | else => operand_reg: { | 1138 | } |
| 1138 | if (info_a.bits <= 32) { | | |
| 1139 | break :operand_reg try self.copyToTmpRegister(operand_ty, operand); | | |
| 1140 | } else { | | |
| 1141 | return self.fail("TODO load least significant word into register", .{}); | | |
| 1142 | } | | |
| 1143 | }, | | |
| 1144 | }; | | |
| 1145 | const operand_reg_lock = self.register_manager.lockReg(operand_reg); | | |
| 1146 | defer if (operand_reg_lock) |reg| self.register_manager.unlockReg(reg); | | |
| 1147 | | 1139 | |
| 1148 | const dest_reg = if (maybe_inst) |inst| blk: { | 1140 | var operand_reg: Register = undefined; |
| 1149 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1141 | var dest_reg: Register = undefined; |
| 1150 | | 1142 | |
| 1151 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | 1143 | const read_args = [_]ReadArg{ |
| 1152 | break :blk operand_reg; | 1144 | .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg }, |
| 1153 | } else { | 1145 | }; |
| 1154 | break :blk try self.register_manager.allocReg(inst, gp); | 1146 | const write_args = [_]WriteArg{ |
| 1155 | } | 1147 | .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 1156 | } else try self.register_manager.allocReg(null, gp); | 1148 | }; |
| | 1149 | try self.allocRegs( |
| | 1150 | &read_args, |
| | 1151 | &write_args, |
| | 1152 | if (maybe_inst) |inst| .{ |
| | 1153 | .corresponding_inst = inst, |
| | 1154 | .operand_mapping = &.{0}, |
| | 1155 | } else null, |
| | 1156 | ); |
| 1157 | | 1157 | |
| 1158 | switch (info_b.bits) { | 1158 | switch (info_b.bits) { |
| 1159 | 32 => { | 1159 | 32 => { |
| 1160 | try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); | 1160 | try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); |
| 1161 | return MCValue{ .register = dest_reg }; | | |
| 1162 | }, | 1161 | }, |
| 1163 | else => { | 1162 | else => { |
| 1164 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); | 1163 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); |
| 1165 | return MCValue{ .register = dest_reg }; | | |
| 1166 | }, | 1164 | }, |
| 1167 | } | 1165 | } |
| | 1166 | |
| | 1167 | return MCValue{ .register = dest_reg }; |
| 1168 | } else { | 1168 | } else { |
| 1169 | return self.fail("TODO: truncate to ints > 32 bits", .{}); | 1169 | return self.fail("TODO: truncate to ints > 32 bits", .{}); |
| 1170 | } | 1170 | } |
| ... | @@ -1172,12 +1172,12 @@ fn trunc( | ... | @@ -1172,12 +1172,12 @@ fn trunc( |
| 1172 | | 1172 | |
| 1173 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { | 1173 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1174 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1174 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1175 | const operand = try self.resolveInst(ty_op.operand); | 1175 | const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; |
| 1176 | const operand_ty = self.air.typeOf(ty_op.operand); | 1176 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 1177 | const dest_ty = self.air.typeOfIndex(inst); | 1177 | const dest_ty = self.air.typeOfIndex(inst); |
| 1178 | | 1178 | |
| 1179 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { | 1179 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 1180 | break :blk try self.trunc(inst, operand, operand_ty, dest_ty); | 1180 | break :blk try self.trunc(inst, operand_bind, operand_ty, dest_ty); |
| 1181 | }; | 1181 | }; |
| 1182 | | 1182 | |
| 1183 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1183 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | @@ -2334,7 +2334,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2334,7 +2334,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2334 | break :result dst_mcv; | 2334 | break :result dst_mcv; |
| 2335 | }, | 2335 | }, |
| 2336 | else => { | 2336 | else => { |
| 2337 | const dest = try self.allocRegOrMem(inst, true); | 2337 | const dest = try self.allocRegOrMem(self.air.typeOfIndex(inst), true, inst); |
| 2338 | | 2338 | |
| 2339 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; | 2339 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; |
| 2340 | const index_bind: ReadArg.Bind = .{ .mcv = index_mcv }; | 2340 | const index_bind: ReadArg.Bind = .{ .mcv = index_mcv }; |
| ... | @@ -2583,16 +2583,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2583,16 +2583,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2583 | if (self.liveness.isUnused(inst) and !is_volatile) | 2583 | if (self.liveness.isUnused(inst) and !is_volatile) |
| 2584 | break :result MCValue.dead; | 2584 | break :result MCValue.dead; |
| 2585 | | 2585 | |
| 2586 | const dst_mcv: MCValue = blk: { | 2586 | const dest_mcv: MCValue = blk: { |
| 2587 | if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { | 2587 | const ptr_fits_dest = elem_ty.abiSize(self.target.*) <= 4; |
| | 2588 | if (ptr_fits_dest and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| 2588 | // The MCValue that holds the pointer can be re-used as the value. | 2589 | // The MCValue that holds the pointer can be re-used as the value. |
| 2589 | break :blk ptr; | 2590 | break :blk ptr; |
| 2590 | } else { | 2591 | } else { |
| 2591 | break :blk try self.allocRegOrMem(inst, true); | 2592 | break :blk try self.allocRegOrMem(elem_ty, true, inst); |
| 2592 | } | 2593 | } |
| 2593 | }; | 2594 | }; |
| 2594 | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); | 2595 | try self.load(dest_mcv, ptr, self.air.typeOf(ty_op.operand)); |
| 2595 | break :result dst_mcv; | 2596 | |
| | 2597 | break :result dest_mcv; |
| 2596 | }; | 2598 | }; |
| 2597 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2599 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2598 | } | 2600 | } |
| ... | @@ -4615,81 +4617,39 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4615,81 +4617,39 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4615 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | 4617 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 4616 | } | 4618 | } |
| 4617 | | 4619 | |
| 4618 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | 4620 | fn isNull( |
| 4619 | if (ty.isPtrLikeOptional()) { | 4621 | self: *Self, |
| 4620 | assert(ty.abiSize(self.target.*) == 4); | 4622 | operand_bind: ReadArg.Bind, |
| 4621 | | 4623 | operand_ty: Type, |
| 4622 | const reg_mcv: MCValue = switch (operand) { | 4624 | ) !MCValue { |
| 4623 | .register => operand, | 4625 | if (operand_ty.isPtrLikeOptional()) { |
| 4624 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, | 4626 | assert(operand_ty.abiSize(self.target.*) == 4); |
| 4625 | }; | | |
| 4626 | | | |
| 4627 | _ = try self.addInst(.{ | | |
| 4628 | .tag = .cmp, | | |
| 4629 | .data = .{ .r_op_cmp = .{ | | |
| 4630 | .rn = reg_mcv.register, | | |
| 4631 | .op = Instruction.Operand.fromU32(0).?, | | |
| 4632 | } }, | | |
| 4633 | }); | | |
| 4634 | | 4627 | |
| 4635 | return MCValue{ .cpsr_flags = .eq }; | 4628 | const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } }; |
| | 4629 | return self.cmp(operand_bind, imm_bind, Type.usize, .eq); |
| 4636 | } else { | 4630 | } else { |
| 4637 | return self.fail("TODO implement non-pointer optionals", .{}); | 4631 | return self.fail("TODO implement non-pointer optionals", .{}); |
| 4638 | } | 4632 | } |
| 4639 | } | 4633 | } |
| 4640 | | 4634 | |
| 4641 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | 4635 | fn isNonNull( |
| 4642 | const is_null_result = try self.isNull(ty, operand); | | |
| 4643 | assert(is_null_result.cpsr_flags == .eq); | | |
| 4644 | | | |
| 4645 | return MCValue{ .cpsr_flags = .ne }; | | |
| 4646 | } | | |
| 4647 | | | |
| 4648 | fn isErr( | | |
| 4649 | self: *Self, | 4636 | self: *Self, |
| 4650 | error_union_bind: ReadArg.Bind, | 4637 | operand_bind: ReadArg.Bind, |
| 4651 | error_union_ty: Type, | 4638 | operand_ty: Type, |
| 4652 | ) !MCValue { | 4639 | ) !MCValue { |
| 4653 | const error_type = error_union_ty.errorUnionSet(); | 4640 | const is_null_result = try self.isNull(operand_bind, operand_ty); |
| 4654 | | 4641 | assert(is_null_result.cpsr_flags == .eq); |
| 4655 | if (error_type.errorSetIsEmpty()) { | | |
| 4656 | return MCValue{ .immediate = 0 }; // always false | | |
| 4657 | } | | |
| 4658 | | | |
| 4659 | const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); | | |
| 4660 | _ = try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .neq); | | |
| 4661 | return MCValue{ .cpsr_flags = .hi }; | | |
| 4662 | } | | |
| 4663 | | 4642 | |
| 4664 | fn isNonErr( | 4643 | return MCValue{ .cpsr_flags = .ne }; |
| 4665 | self: *Self, | | |
| 4666 | error_union_bind: ReadArg.Bind, | | |
| 4667 | error_union_ty: Type, | | |
| 4668 | ) !MCValue { | | |
| 4669 | const is_err_result = try self.isErr(error_union_bind, error_union_ty); | | |
| 4670 | switch (is_err_result) { | | |
| 4671 | .cpsr_flags => |cond| { | | |
| 4672 | assert(cond == .hi); | | |
| 4673 | return MCValue{ .cpsr_flags = cond.negate() }; | | |
| 4674 | }, | | |
| 4675 | .immediate => |imm| { | | |
| 4676 | assert(imm == 0); | | |
| 4677 | return MCValue{ .immediate = 1 }; | | |
| 4678 | }, | | |
| 4679 | else => unreachable, | | |
| 4680 | } | | |
| 4681 | } | 4644 | } |
| 4682 | | 4645 | |
| 4683 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | 4646 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4684 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4647 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4685 | | | |
| 4686 | try self.spillCompareFlagsIfOccupied(); | | |
| 4687 | self.cpsr_flags_inst = inst; | | |
| 4688 | | | |
| 4689 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4648 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4690 | const operand = try self.resolveInst(un_op); | 4649 | const operand_bind: ReadArg.Bind = .{ .inst = un_op }; |
| 4691 | const ty = self.air.typeOf(un_op); | 4650 | const operand_ty = self.air.typeOf(un_op); |
| 4692 | break :result try self.isNull(ty, operand); | 4651 | |
| | 4652 | break :result try self.isNull(operand_bind, operand_ty); |
| 4693 | }; | 4653 | }; |
| 4694 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4654 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4695 | } | 4655 | } |
| ... | @@ -4699,16 +4659,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4699,16 +4659,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4699 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4659 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4700 | const operand_ptr = try self.resolveInst(un_op); | 4660 | const operand_ptr = try self.resolveInst(un_op); |
| 4701 | const ptr_ty = self.air.typeOf(un_op); | 4661 | const ptr_ty = self.air.typeOf(un_op); |
| 4702 | const operand: MCValue = blk: { | 4662 | const elem_ty = ptr_ty.elemType(); |
| 4703 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4663 | |
| 4704 | // The MCValue that holds the pointer can be re-used as the value. | 4664 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4705 | break :blk operand_ptr; | | |
| 4706 | } else { | | |
| 4707 | break :blk try self.allocRegOrMem(inst, true); | | |
| 4708 | } | | |
| 4709 | }; | | |
| 4710 | try self.load(operand, operand_ptr, ptr_ty); | 4665 | try self.load(operand, operand_ptr, ptr_ty); |
| 4711 | break :result try self.isNull(ptr_ty.elemType(), operand); | 4666 | |
| | 4667 | break :result try self.isNull(.{ .mcv = operand }, elem_ty); |
| 4712 | }; | 4668 | }; |
| 4713 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4669 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4714 | } | 4670 | } |
| ... | @@ -4716,9 +4672,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4716,9 +4672,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4716 | fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { | 4672 | fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4717 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4673 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4718 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4674 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4719 | const operand = try self.resolveInst(un_op); | 4675 | const operand_bind: ReadArg.Bind = .{ .inst = un_op }; |
| 4720 | const ty = self.air.typeOf(un_op); | 4676 | const operand_ty = self.air.typeOf(un_op); |
| 4721 | break :result try self.isNonNull(ty, operand); | 4677 | |
| | 4678 | break :result try self.isNonNull(operand_bind, operand_ty); |
| 4722 | }; | 4679 | }; |
| 4723 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4680 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4724 | } | 4681 | } |
| ... | @@ -4728,20 +4685,50 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4728,20 +4685,50 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4728 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4685 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4729 | const operand_ptr = try self.resolveInst(un_op); | 4686 | const operand_ptr = try self.resolveInst(un_op); |
| 4730 | const ptr_ty = self.air.typeOf(un_op); | 4687 | const ptr_ty = self.air.typeOf(un_op); |
| 4731 | const operand: MCValue = blk: { | 4688 | const elem_ty = ptr_ty.elemType(); |
| 4732 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4689 | |
| 4733 | // The MCValue that holds the pointer can be re-used as the value. | 4690 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4734 | break :blk operand_ptr; | | |
| 4735 | } else { | | |
| 4736 | break :blk try self.allocRegOrMem(inst, true); | | |
| 4737 | } | | |
| 4738 | }; | | |
| 4739 | try self.load(operand, operand_ptr, ptr_ty); | 4691 | try self.load(operand, operand_ptr, ptr_ty); |
| 4740 | break :result try self.isNonNull(ptr_ty.elemType(), operand); | 4692 | |
| | 4693 | break :result try self.isNonNull(.{ .mcv = operand }, elem_ty); |
| 4741 | }; | 4694 | }; |
| 4742 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4695 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4743 | } | 4696 | } |
| 4744 | | 4697 | |
| | 4698 | fn isErr( |
| | 4699 | self: *Self, |
| | 4700 | error_union_bind: ReadArg.Bind, |
| | 4701 | error_union_ty: Type, |
| | 4702 | ) !MCValue { |
| | 4703 | const error_type = error_union_ty.errorUnionSet(); |
| | 4704 | |
| | 4705 | if (error_type.errorSetIsEmpty()) { |
| | 4706 | return MCValue{ .immediate = 0 }; // always false |
| | 4707 | } |
| | 4708 | |
| | 4709 | const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); |
| | 4710 | return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); |
| | 4711 | } |
| | 4712 | |
| | 4713 | fn isNonErr( |
| | 4714 | self: *Self, |
| | 4715 | error_union_bind: ReadArg.Bind, |
| | 4716 | error_union_ty: Type, |
| | 4717 | ) !MCValue { |
| | 4718 | const is_err_result = try self.isErr(error_union_bind, error_union_ty); |
| | 4719 | switch (is_err_result) { |
| | 4720 | .cpsr_flags => |cond| { |
| | 4721 | assert(cond == .hi); |
| | 4722 | return MCValue{ .cpsr_flags = cond.negate() }; |
| | 4723 | }, |
| | 4724 | .immediate => |imm| { |
| | 4725 | assert(imm == 0); |
| | 4726 | return MCValue{ .immediate = 1 }; |
| | 4727 | }, |
| | 4728 | else => unreachable, |
| | 4729 | } |
| | 4730 | } |
| | 4731 | |
| 4745 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | 4732 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 4746 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4733 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4747 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4734 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | @@ -4758,16 +4745,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4758,16 +4745,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4758 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4745 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4759 | const operand_ptr = try self.resolveInst(un_op); | 4746 | const operand_ptr = try self.resolveInst(un_op); |
| 4760 | const ptr_ty = self.air.typeOf(un_op); | 4747 | const ptr_ty = self.air.typeOf(un_op); |
| 4761 | const operand: MCValue = blk: { | 4748 | const elem_ty = ptr_ty.elemType(); |
| 4762 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4749 | |
| 4763 | // The MCValue that holds the pointer can be re-used as the value. | 4750 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4764 | break :blk operand_ptr; | | |
| 4765 | } else { | | |
| 4766 | break :blk try self.allocRegOrMem(inst, true); | | |
| 4767 | } | | |
| 4768 | }; | | |
| 4769 | try self.load(operand, operand_ptr, ptr_ty); | 4751 | try self.load(operand, operand_ptr, ptr_ty); |
| 4770 | break :result try self.isErr(.{ .mcv = operand }, ptr_ty.elemType()); | 4752 | |
| | 4753 | break :result try self.isErr(.{ .mcv = operand }, elem_ty); |
| 4771 | }; | 4754 | }; |
| 4772 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4755 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4773 | } | 4756 | } |
| ... | @@ -4788,16 +4771,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4788,16 +4771,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4788 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4771 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4789 | const operand_ptr = try self.resolveInst(un_op); | 4772 | const operand_ptr = try self.resolveInst(un_op); |
| 4790 | const ptr_ty = self.air.typeOf(un_op); | 4773 | const ptr_ty = self.air.typeOf(un_op); |
| 4791 | const operand: MCValue = blk: { | 4774 | const elem_ty = ptr_ty.elemType(); |
| 4792 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4775 | |
| 4793 | // The MCValue that holds the pointer can be re-used as the value. | 4776 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4794 | break :blk operand_ptr; | | |
| 4795 | } else { | | |
| 4796 | break :blk try self.allocRegOrMem(inst, true); | | |
| 4797 | } | | |
| 4798 | }; | | |
| 4799 | try self.load(operand, operand_ptr, ptr_ty); | 4777 | try self.load(operand, operand_ptr, ptr_ty); |
| 4800 | break :result try self.isNonErr(.{ .mcv = operand }, ptr_ty.elemType()); | 4778 | |
| | 4779 | break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); |
| 4801 | }; | 4780 | }; |
| 4802 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4781 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4803 | } | 4782 | } |
| ... | @@ -5010,7 +4989,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { | ... | @@ -5010,7 +4989,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5010 | .none, .dead, .unreach => unreachable, | 4989 | .none, .dead, .unreach => unreachable, |
| 5011 | .register, .stack_offset, .memory => operand_mcv, | 4990 | .register, .stack_offset, .memory => operand_mcv, |
| 5012 | .immediate, .stack_argument_offset, .cpsr_flags => blk: { | 4991 | .immediate, .stack_argument_offset, .cpsr_flags => blk: { |
| 5013 | const new_mcv = try self.allocRegOrMem(block, true); | 4992 | const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block); |
| 5014 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); | 4993 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 5015 | break :blk new_mcv; | 4994 | break :blk new_mcv; |
| 5016 | }, | 4995 | }, |