| ... | @@ -535,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -535,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 535 | .mod => try self.airMod(inst), | 535 | .mod => try self.airMod(inst), |
| 536 | .shl, .shl_exact => try self.airBinOp(inst), | 536 | .shl, .shl_exact => try self.airBinOp(inst), |
| 537 | .shl_sat => try self.airShlSat(inst), | 537 | .shl_sat => try self.airShlSat(inst), |
| 538 | .min => try self.airMin(inst), | 538 | .min => try self.airMinMax(inst), |
| 539 | .max => try self.airMax(inst), | 539 | .max => try self.airMinMax(inst), |
| 540 | .slice => try self.airSlice(inst), | 540 | .slice => try self.airSlice(inst), |
| 541 | | 541 | |
| 542 | .sqrt, | 542 | .sqrt, |
| ... | @@ -1140,15 +1140,119 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1140,15 +1140,119 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1140 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 { | 1143 | fn minMax( |
| 1144 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1144 | self: *Self, |
| 1145 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); | 1145 | tag: Air.Inst.Tag, |
| 1146 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1256 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1153 | } | 1257 | } |
| 1154 | | 1258 | |