Skip to content

Commit dc100af

Browse files
Alisha Nayakmeta-codesync[bot]
authored andcommitted
Back out "Implement float comparisons with primitive operations"
Summary: Original commit changeset: 24952346336c Original Phabricator Diff: D110238069 Root cause for S683263 Reviewed By: alexmalyshev Differential Revision: D110895061 fbshipit-source-id: eb0661e30b6ca0251ac7b57a5139cc90b9e6b966
1 parent e2187de commit dc100af

12 files changed

Lines changed: 75 additions & 242 deletions

File tree

cinderx/Jit/codegen/autogen.cpp

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -531,13 +531,6 @@ void TranslateCompare(Environ* env, const Instruction* instr) {
531531
} else if (!inp1->isVecD()) {
532532
as->cmp(AutoTranslator::getGp(inp0), AutoTranslator::getGp(inp1));
533533
} else {
534-
// Floating-point comparison; both operands are in XMM registers. `comisd`
535-
// sets the flags in the unsigned sense (CF/ZF) and reports unordered (NaN)
536-
// operands as CF=ZF=PF=1; the shared condition switch below then reads
537-
// those flags. NaN-correctness and the comparison direction are chosen
538-
// when the compare is lowered to LIR, so a compare fused into a branch,
539-
// which reuses these flags via compareToBranchCC on the LIR opcode, stays
540-
// consistent with the standalone setcc emitted here.
541534
as->comisd(AutoTranslator::getVecD(inp0), AutoTranslator::getVecD(inp1));
542535
}
543536
auto output = AutoTranslator::getGp(instr->output());
@@ -573,7 +566,7 @@ void TranslateCompare(Environ* env, const Instruction* instr) {
573566
as->setbe(output);
574567
break;
575568
default:
576-
JIT_ABORT("Bad instruction for TranslateCompare {}", instr->opname());
569+
JIT_ABORT("bad instruction for TranslateCompare");
577570
}
578571
if (instr->output()->dataType() != lir::Operand::k8bit) {
579572
as->movzx(
@@ -600,11 +593,6 @@ void TranslateCompare(Environ* env, const Instruction* instr) {
600593
} else if (!inp1->isVecD()) {
601594
as->cmp(AutoTranslator::getGpWiden(inp0), AutoTranslator::getGpWiden(inp1));
602595
} else {
603-
// Floating-point comparison, see the note in the x86-64 path. `fcmp` sets
604-
// NZCV (unordered/NaN operands set C=1, V=1 while leaving Z=0), the shared
605-
// condition switch below picks the cset. NaN-correctness and the comparison
606-
// direction are chosen when the compare is lowered to LIR, keeping the
607-
// standalone cset and any fused b.cc consistent.
608596
as->fcmp(AutoTranslator::getVecD(inp0), AutoTranslator::getVecD(inp1));
609597
}
610598

@@ -641,7 +629,7 @@ void TranslateCompare(Environ* env, const Instruction* instr) {
641629
as->cset(output, arm::CondCode::kLS);
642630
break;
643631
default:
644-
JIT_ABORT("Bad instruction for TranslateCompare {}", instr->opname());
632+
JIT_ABORT("bad instruction for TranslateCompare");
645633
}
646634
#else
647635
CINDER_UNSUPPORTED

cinderx/Jit/hir/hir.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ bool Instr::isReplayable() const {
394394
case Opcode::kCIntToCBool:
395395
case Opcode::kCompactLongUnbox:
396396
case Opcode::kDoubleBinaryOp:
397+
case Opcode::kFloatCompare:
397398
case Opcode::kFormatValue:
398399
case Opcode::kFormatWithSpec:
399400
case Opcode::kGetSecondOutput:
@@ -761,6 +762,7 @@ bool isPassthrough(const Instr& instr) {
761762
case Opcode::kFillTypeAttrCache:
762763
case Opcode::kFillTypeMethodCache:
763764
case Opcode::kFloatBinaryOp:
765+
case Opcode::kFloatCompare:
764766
case Opcode::kFormatValue:
765767
case Opcode::kFormatWithSpec:
766768
case Opcode::kGetAIter:

cinderx/Jit/hir/hir.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,32 @@ class INSTR_CLASS(
18191819
CompareOp op_;
18201820
};
18211821

1822+
// Perform the comparison indicated by op between two floats
1823+
class INSTR_CLASS(
1824+
FloatCompare,
1825+
(TFloatExact, TFloatExact),
1826+
HasOutput,
1827+
Operands<2>) {
1828+
public:
1829+
FloatCompare(Register* dst, CompareOp op, Register* left, Register* right)
1830+
: InstrT(dst, left, right), op_(op) {}
1831+
1832+
CompareOp op() const {
1833+
return op_;
1834+
}
1835+
1836+
Register* left() const {
1837+
return getOperand(0);
1838+
}
1839+
1840+
Register* right() const {
1841+
return getOperand(1);
1842+
}
1843+
1844+
private:
1845+
CompareOp op_;
1846+
};
1847+
18221848
// Perform the comparison indicated by op between two longs
18231849
class INSTR_CLASS(
18241850
LongCompare,

cinderx/Jit/hir/instr_effects.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ MemoryEffects memoryEffects(const Instr& inst) {
4141
case Opcode::kDeopt:
4242
case Opcode::kDeoptPatchpoint:
4343
case Opcode::kDoubleBinaryOp:
44+
case Opcode::kFloatCompare:
4445
case Opcode::kGetSecondOutput:
4546
case Opcode::kHintType:
4647
case Opcode::kIndexUnbox:
@@ -400,6 +401,7 @@ bool hasArbitraryExecution(const Instr& inst) {
400401
case Opcode::kDeoptPatchpoint:
401402
case Opcode::kDoubleBinaryOp:
402403
case Opcode::kEndInlinedFunction:
404+
case Opcode::kFloatCompare:
403405
case Opcode::kGetSecondOutput:
404406
case Opcode::kGuardIs:
405407
case Opcode::kHintType:

cinderx/Jit/hir/ops.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ namespace cinderx::jit::hir {
5858
V(FillTypeAttrCache) \
5959
V(FillTypeMethodCache) \
6060
V(FloatBinaryOp) \
61+
V(FloatCompare) \
6162
V(FormatValue) \
6263
V(FormatWithSpec) \
6364
V(GetAIter) \

cinderx/Jit/hir/parser.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,15 @@ HIRParser::parseInstr(std::string_view opcode, Register* dst, int bb_index) {
597597
instruction = newInstr<Compare>(dst, op, left, right);
598598
break;
599599
}
600+
case Opcode::kFloatCompare: {
601+
expect("<");
602+
CompareOp op = ParseCompareOpName(getNextToken());
603+
expect(">");
604+
auto left = parseRegister();
605+
auto right = parseRegister();
606+
NEW_INSTR(FloatCompare, dst, op, left, right);
607+
break;
608+
}
600609
case Opcode::kLongCompare: {
601610
expect("<");
602611
CompareOp op = ParseCompareOpName(getNextToken());

cinderx/Jit/hir/pass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ Type outputType(
355355
}
356356
case Opcode::kFloatBinaryOp:
357357
return TFloatExact;
358+
case Opcode::kFloatCompare:
358359
case Opcode::kLongCompare:
359360
case Opcode::kUnicodeCompare:
360361
return TImmortalBool;

cinderx/Jit/hir/printer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,10 @@ static std::string format_immediates(const Function* func, const Instr& instr) {
478478
const auto& cmp = static_cast<const Compare&>(instr);
479479
return std::string{GetCompareOpName(cmp.op())};
480480
}
481+
case Opcode::kFloatCompare: {
482+
const auto& cmp = static_cast<const FloatCompare&>(instr);
483+
return std::string{GetCompareOpName(cmp.op())};
484+
}
481485
case Opcode::kLongCompare: {
482486
const auto& cmp = static_cast<const LongCompare&>(instr);
483487
return std::string{GetCompareOpName(cmp.op())};

cinderx/Jit/hir/simplify.cpp

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -430,22 +430,12 @@ Register* simplifyCompare(Env& env, const Compare* instr) {
430430
}
431431
}
432432

433-
// Emit primitive comparisons for floats: unbox and compare as CDouble. The
434-
// op is emitted naturally (PrimitiveCompare<LessThan> and friends). NaNs are
435-
// handled using Python's rules (`NaN == NaN` is false, `NaN != NaN` is true,
436-
// all other comparison types with NaN are false).
433+
// Emit FloatCompare if both args are FloatExact and the op is supported
434+
// between two longs.
437435
if (left->isA(TFloatExact) && right->isA(TFloatExact) &&
438-
(op == CompareOp::kLessThan || op == CompareOp::kLessThanEqual ||
439-
op == CompareOp::kGreaterThan || op == CompareOp::kGreaterThanEqual ||
440-
op == CompareOp::kEqual || op == CompareOp::kNotEqual)) {
441-
std::optional<PrimitiveCompareOp> prim_op = toPrimitiveCompareOp(op);
442-
env.emit<UseType>(left, TFloatExact);
443-
env.emit<UseType>(right, TFloatExact);
444-
Register* unboxed_left = env.emit<PrimitiveUnbox>(left, TCDouble);
445-
Register* unboxed_right = env.emit<PrimitiveUnbox>(right, TCDouble);
446-
Register* result =
447-
env.emit<PrimitiveCompare>(*prim_op, unboxed_left, unboxed_right);
448-
return env.emit<PrimitiveBoxBool>(result);
436+
!(op == CompareOp::kIn || op == CompareOp::kNotIn ||
437+
op == CompareOp::kExcMatch)) {
438+
return env.emit<FloatCompare>(instr->op(), left, right);
449439
}
450440

451441
// Emit LongCompare if both args are LongExact and the op is supported between

cinderx/Jit/lir/generator.cpp

Lines changed: 11 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,126 +2592,6 @@ LIRGenerator::TranslatedBlock LIRGenerator::translateOneBasicBlock(
25922592
}
25932593
case Opcode::kPrimitiveCompare: {
25942594
auto instr = static_cast<const PrimitiveCompare*>(&i);
2595-
// Float comparisons need NaN-correct condition codes, and the choice is
2596-
// architecture-specific because comisd (x86-64) and fcmp (ARM64) expose
2597-
// different flags for unordered (NaN) operands. Pick the LIR opcode
2598-
// (and operand order) so the opcode's standard condition, used both by
2599-
// the standalone setcc/cset and by a compare fused into a branch
2600-
// through compareToBranchCC, yields Python's result: every ordering
2601-
// comparison involving a NaN is false, `NaN == NaN` is False, `Nan !=
2602-
// NaN` is True.
2603-
if (instr->left()->type() <= TCDouble) {
2604-
Register* lhs = instr->left();
2605-
Register* rhs = instr->right();
2606-
#if defined(CINDER_X86_64)
2607-
// comisd sets unsigned-sense flags and reports unordered as CF=1, so
2608-
// only the above / above-equal conditions are false for NaN. Express
2609-
// every ordering as > / >=, swapping operands for < / <=. == / !=
2610-
// have no NaN-correct single-instruction form (comisd folds unordered
2611-
// into ZF), so build them from ordering comparisons:
2612-
// a == b == (a <= b) && (a >= b); a != b == !(a == b)
2613-
// Floats are neither signed nor unsigned, so the signed and unsigned
2614-
// PrimitiveCompareOp variants denote the same ordering (Static Python
2615-
// `double` emits the unsigned variants; the Python-float path emits
2616-
// the natural ones).
2617-
switch (instr->op()) {
2618-
case PrimitiveCompareOp::kGreaterThan:
2619-
case PrimitiveCompareOp::kGreaterThanUnsigned:
2620-
bbb.appendInstr(
2621-
instr->output(), Instruction::kGreaterThanUnsigned, lhs, rhs);
2622-
break;
2623-
case PrimitiveCompareOp::kGreaterThanEqual:
2624-
case PrimitiveCompareOp::kGreaterThanEqualUnsigned:
2625-
bbb.appendInstr(
2626-
instr->output(),
2627-
Instruction::kGreaterThanEqualUnsigned,
2628-
lhs,
2629-
rhs);
2630-
break;
2631-
case PrimitiveCompareOp::kLessThan: // a < b == b > a
2632-
case PrimitiveCompareOp::kLessThanUnsigned:
2633-
bbb.appendInstr(
2634-
instr->output(), Instruction::kGreaterThanUnsigned, rhs, lhs);
2635-
break;
2636-
case PrimitiveCompareOp::kLessThanEqual: // a <= b == b >= a
2637-
case PrimitiveCompareOp::kLessThanEqualUnsigned:
2638-
bbb.appendInstr(
2639-
instr->output(),
2640-
Instruction::kGreaterThanEqualUnsigned,
2641-
rhs,
2642-
lhs);
2643-
break;
2644-
case PrimitiveCompareOp::kEqual:
2645-
case PrimitiveCompareOp::kNotEqual: {
2646-
Instruction* le = bbb.appendInstr(
2647-
OutVReg{Operand::k8bit},
2648-
Instruction::kGreaterThanEqualUnsigned,
2649-
rhs,
2650-
lhs); // a <= b
2651-
Instruction* ge = bbb.appendInstr(
2652-
OutVReg{Operand::k8bit},
2653-
Instruction::kGreaterThanEqualUnsigned,
2654-
lhs,
2655-
rhs); // a >= b
2656-
if (instr->op() == PrimitiveCompareOp::kEqual) {
2657-
bbb.appendInstr(instr->output(), Instruction::kAnd, le, ge);
2658-
} else {
2659-
Instruction* eq = bbb.appendInstr(
2660-
OutVReg{Operand::k8bit}, Instruction::kAnd, le, ge);
2661-
bbb.appendInstr(
2662-
instr->output(),
2663-
Instruction::kXor,
2664-
eq,
2665-
Imm{1, DataType::k8bit});
2666-
}
2667-
break;
2668-
}
2669-
default:
2670-
JIT_ABORT(
2671-
"Not a float comparison {}", static_cast<int>(instr->op()));
2672-
}
2673-
#elif defined(CINDER_AARCH64)
2674-
// fcmp leaves Z=0 for unordered operands and sets C=1, V=1. Pick the
2675-
// condition that is false for NaN on each ordering: GT/GE for > / >=
2676-
// (signed opcodes), LO/LS for < / <= (unsigned opcodes). EQ/NE key
2677-
// off Z alone and are already NaN-correct. Floats are neither signed
2678-
// nor unsigned, so the signed and unsigned PrimitiveCompareOp
2679-
// variants denote the same ordering (Static Python `double` emits the
2680-
// unsigned variants; the Python-float path emits the natural ones).
2681-
Instruction::Opcode op;
2682-
switch (instr->op()) {
2683-
case PrimitiveCompareOp::kEqual:
2684-
op = Instruction::kEqual;
2685-
break;
2686-
case PrimitiveCompareOp::kNotEqual:
2687-
op = Instruction::kNotEqual;
2688-
break;
2689-
case PrimitiveCompareOp::kGreaterThan:
2690-
case PrimitiveCompareOp::kGreaterThanUnsigned:
2691-
op = Instruction::kGreaterThanSigned;
2692-
break;
2693-
case PrimitiveCompareOp::kGreaterThanEqual:
2694-
case PrimitiveCompareOp::kGreaterThanEqualUnsigned:
2695-
op = Instruction::kGreaterThanEqualSigned;
2696-
break;
2697-
case PrimitiveCompareOp::kLessThan:
2698-
case PrimitiveCompareOp::kLessThanUnsigned:
2699-
op = Instruction::kLessThanUnsigned;
2700-
break;
2701-
case PrimitiveCompareOp::kLessThanEqual:
2702-
case PrimitiveCompareOp::kLessThanEqualUnsigned:
2703-
op = Instruction::kLessThanEqualUnsigned;
2704-
break;
2705-
default:
2706-
JIT_ABORT(
2707-
"Not a float comparison {}", static_cast<int>(instr->op()));
2708-
}
2709-
bbb.appendInstr(instr->output(), op, lhs, rhs);
2710-
#else
2711-
CINDER_UNSUPPORTED
2712-
#endif
2713-
break;
2714-
}
27152595
Instruction::Opcode op;
27162596
switch (instr->op()) {
27172597
case PrimitiveCompareOp::kEqual:
@@ -3488,6 +3368,17 @@ LIRGenerator::TranslatedBlock LIRGenerator::translateOneBasicBlock(
34883368
op);
34893369
break;
34903370
}
3371+
case Opcode::kFloatCompare: {
3372+
auto instr = static_cast<const FloatCompare*>(&i);
3373+
3374+
bbb.appendCallInstruction(
3375+
instr->output(),
3376+
PyFloat_Type.tp_richcompare,
3377+
instr->left(),
3378+
instr->right(),
3379+
static_cast<int>(instr->op()));
3380+
break;
3381+
}
34913382
case Opcode::kLongCompare: {
34923383
auto instr = static_cast<const LongCompare*>(&i);
34933384

0 commit comments

Comments
 (0)