| ... | ... | @@ -535,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 535 | 535 | .mod => try self.airMod(inst), |
| 536 | 536 | .shl, .shl_exact => try self.airBinOp(inst), |
| 537 | 537 | .shl_sat => try self.airShlSat(inst), |
| 538 | | .min => try self.airMin(inst), |
| 539 | | .max => try self.airMax(inst), |
| 538 | .min => try self.airMinMax(inst), |
| 539 | .max => try self.airMinMax(inst), |
| 540 | 540 | .slice => try self.airSlice(inst), |
| 541 | 541 | |
| 542 | 542 | .sqrt, |
| ... | ... | @@ -1140,15 +1140,119 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1140 | 1140 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1141 | 1141 | } |
| 1142 | 1142 | |
| 1143 | | fn airMin(self: *Self, inst: Air.Inst.Index) !void { |
| 1144 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1145 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); |
| 1146 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1143 | fn minMax( |
| 1144 | self: *Self, |
| 1145 | tag: Air.Inst.Tag, |
| 1146 | maybe_inst: ?Air.Inst.Index, |
| 1147 | lhs: MCValue, |
| 1148 | rhs: MCValue, |
| 1149 | lhs_ty: Type, |
| 1150 | rhs_ty: Type, |
| 1151 | ) !MCValue { |
| 1152 | switch (lhs_ty.zigTypeTag()) { |
| 1153 | .Float => return self.fail("TODO ARM min/max on floats", .{}), |
| 1154 | .Vector => return self.fail("TODO ARM min/max on vectors", .{}), |
| 1155 | .Int => { |
| 1156 | assert(lhs_ty.eql(rhs_ty)); |
| 1157 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1158 | if (int_info.bits <= 32) { |
| 1159 | const lhs_is_register = lhs == .register; |
| 1160 | const rhs_is_register = rhs == .register; |
| 1161 | |
| 1162 | const lhs_reg = switch (lhs) { |
| 1163 | .register => |r| r, |
| 1164 | else => try self.copyToTmpRegister(lhs_ty, lhs), |
| 1165 | }; |
| 1166 | self.register_manager.freezeRegs(&.{lhs_reg}); |
| 1167 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1168 | |
| 1169 | const rhs_reg = switch (rhs) { |
| 1170 | .register => |r| r, |
| 1171 | else => try self.copyToTmpRegister(rhs_ty, rhs), |
| 1172 | }; |
| 1173 | self.register_manager.freezeRegs(&.{rhs_reg}); |
| 1174 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1175 | |
| 1176 | const dest_reg = if (maybe_inst) |inst| blk: { |
| 1177 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1178 | |
| 1179 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 1180 | break :blk lhs_reg; |
| 1181 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 1182 | break :blk rhs_reg; |
| 1183 | } else { |
| 1184 | break :blk try self.register_manager.allocReg(inst); |
| 1185 | } |
| 1186 | } else try self.register_manager.allocReg(null); |
| 1187 | |
| 1188 | // lhs == reg should have been checked by airMinMax |
| 1189 | // |
| 1190 | // By guaranteeing lhs != rhs, we guarantee (dst != |
| 1191 | // lhs) or (dst != rhs), which is a property we use to |
| 1192 | // omit generating one instruction when we reuse a |
| 1193 | // register. |
| 1194 | assert(lhs_reg != rhs_reg); // see note above |
| 1195 | |
| 1196 | _ = try self.binOpRegister(.cmp_eq, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); |
| 1197 | |
| 1198 | const cond_choose_lhs: Condition = switch (tag) { |
| 1199 | .max => switch (int_info.signedness) { |
| 1200 | .signed => Condition.gt, |
| 1201 | .unsigned => Condition.hi, |
| 1202 | }, |
| 1203 | .min => switch (int_info.signedness) { |
| 1204 | .signed => Condition.lt, |
| 1205 | .unsigned => Condition.cc, |
| 1206 | }, |
| 1207 | else => unreachable, |
| 1208 | }; |
| 1209 | const cond_choose_rhs = cond_choose_lhs.negate(); |
| 1210 | |
| 1211 | if (dest_reg != lhs_reg) { |
| 1212 | _ = try self.addInst(.{ |
| 1213 | .tag = .mov, |
| 1214 | .cond = cond_choose_lhs, |
| 1215 | .data = .{ .rr_op = .{ |
| 1216 | .rd = dest_reg, |
| 1217 | .rn = .r0, |
| 1218 | .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none), |
| 1219 | } }, |
| 1220 | }); |
| 1221 | } |
| 1222 | if (dest_reg != rhs_reg) { |
| 1223 | _ = try self.addInst(.{ |
| 1224 | .tag = .mov, |
| 1225 | .cond = cond_choose_rhs, |
| 1226 | .data = .{ .rr_op = .{ |
| 1227 | .rd = dest_reg, |
| 1228 | .rn = .r0, |
| 1229 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), |
| 1230 | } }, |
| 1231 | }); |
| 1232 | } |
| 1233 | |
| 1234 | return MCValue{ .register = dest_reg }; |
| 1235 | } else { |
| 1236 | return self.fail("TODO ARM min/max on integers > u32/i32", .{}); |
| 1237 | } |
| 1238 | }, |
| 1239 | else => unreachable, |
| 1240 | } |
| 1147 | 1241 | } |
| 1148 | 1242 | |
| 1149 | | fn airMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1243 | fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1244 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1150 | 1245 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1151 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch}); |
| 1246 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1247 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1248 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1249 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1250 | |
| 1251 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1252 | if (bin_op.lhs == bin_op.rhs) break :result lhs; |
| 1253 | |
| 1254 | break :result try self.minMax(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1255 | }; |
| 1152 | 1256 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1153 | 1257 | } |
| 1154 | 1258 | |