Skip to content

Commit d70dcb5

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Unbox float TrueDivide and mixed float/int ops to DoubleBinaryOp
Summary: Two related optimizations in the Simplify pass: 1. When both operands of a FloatBinaryOp<TrueDivide> are FloatExact, lower to unboxed DoubleBinaryOp<TrueDivide> (a single divsd instruction) with a guard for divide-by-zero. Previously only Add/Subtract/Multiply were unboxed; TrueDivide fell through to the boxed CPython float_div slot. 2. When a BinaryOp has one FloatExact operand and one LongExact operand with a known constant value (object specialization), convert the int constant to a double at compile time and emit an unboxed DoubleBinaryOp directly. This handles patterns like `x * 2`, `1.0 / y`, `dt / 3` where one side is a float and the other is a small constant integer. Supported for Add, Subtract, Multiply, TrueDivide, and Power. Reviewed By: jbower-fb Differential Revision: D96742433 fbshipit-source-id: 6ac1f11a69d829a1f24280dceccd81f948365ed6
1 parent 8ecb2a1 commit d70dcb5

File tree

3 files changed

+360
-2
lines changed

3 files changed

+360
-2
lines changed

cinderx/Jit/hir/simplify.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,55 @@ Register* simplifyBinaryOp(Env& env, const BinaryOp* instr) {
744744
return env.emit<FloatBinaryOp>(instr->op(), lhs, rhs, *instr->frameState());
745745
}
746746

747+
// Mixed float/int binary ops where the int is a known constant: convert the
748+
// int to a double at compile time and emit an unboxed DoubleBinaryOp. This
749+
// avoids going through CPython's generic binary op dispatch which would do
750+
// the int-to-float conversion at runtime.
751+
if ((op == BinaryOpKind::kAdd || op == BinaryOpKind::kSubtract ||
752+
op == BinaryOpKind::kMultiply || op == BinaryOpKind::kTrueDivide ||
753+
op == BinaryOpKind::kPower)) {
754+
Register* float_reg = nullptr;
755+
Register* int_reg = nullptr;
756+
bool int_on_right = false;
757+
if (lhs->isA(TFloatExact) && rhs->isA(TLongExact) &&
758+
rhs->type().hasObjectSpec()) {
759+
float_reg = lhs;
760+
int_reg = rhs;
761+
int_on_right = true;
762+
} else if (
763+
lhs->isA(TLongExact) && rhs->isA(TFloatExact) &&
764+
lhs->type().hasObjectSpec()) {
765+
float_reg = rhs;
766+
int_reg = lhs;
767+
int_on_right = false;
768+
}
769+
if (float_reg != nullptr) {
770+
int overflow;
771+
long long_val =
772+
PyLong_AsLongAndOverflow(int_reg->type().objectSpec(), &overflow);
773+
if (!overflow) {
774+
auto double_val = static_cast<double>(long_val);
775+
env.emit<UseType>(float_reg, TFloatExact);
776+
env.emit<UseType>(int_reg, int_reg->type());
777+
Register* unbox_float = env.emit<PrimitiveUnbox>(float_reg, TCDouble);
778+
Register* const_double =
779+
env.emit<LoadConst>(Type::fromCDouble(double_val));
780+
Register* unbox_left = int_on_right ? unbox_float : const_double;
781+
Register* unbox_right = int_on_right ? const_double : unbox_float;
782+
// Need to guard against division by zero.
783+
if (op == BinaryOpKind::kTrueDivide) {
784+
Register* zero = env.emit<LoadConst>(Type::fromCDouble(0.0));
785+
Register* is_nonzero = env.emit<PrimitiveCompare>(
786+
PrimitiveCompareOp::kNotEqual, unbox_right, zero);
787+
env.emitInstr<Guard>(is_nonzero);
788+
}
789+
Register* result =
790+
env.emit<DoubleBinaryOp>(op, unbox_left, unbox_right);
791+
return env.emit<PrimitiveBox>(result, TCDouble, *instr->frameState());
792+
}
793+
}
794+
}
795+
747796
if ((lhs->isA(TUnicodeExact) && rhs->isA(TLongExact)) &&
748797
(op == BinaryOpKind::kMultiply)) {
749798
Register* unboxed_rhs = env.emit<IndexUnbox>(rhs, PyExc_OverflowError);
@@ -870,6 +919,19 @@ Register* simplifyFloatBinaryOp(Env& env, const FloatBinaryOp* instr) {
870919
return env.emit<PrimitiveBox>(result, TCDouble, *instr->frameState());
871920
}
872921

922+
// True-divide is similar to add/sub/mul, but needs to guard against division
923+
// by zero as that would raise a ZeroDivisionError in the interpreter.
924+
if (op == BinaryOpKind::kTrueDivide) {
925+
Register* unbox_left = env.emit<PrimitiveUnbox>(instr->left(), TCDouble);
926+
Register* unbox_right = env.emit<PrimitiveUnbox>(instr->right(), TCDouble);
927+
Register* zero = env.emit<LoadConst>(Type::fromCDouble(0.0));
928+
Register* is_nonzero = env.emit<PrimitiveCompare>(
929+
PrimitiveCompareOp::kNotEqual, unbox_right, zero);
930+
env.emitInstr<Guard>(is_nonzero);
931+
Register* result = env.emit<DoubleBinaryOp>(op, unbox_left, unbox_right);
932+
return env.emit<PrimitiveBox>(result, TCDouble, *instr->frameState());
933+
}
934+
873935
// `x ** 0.5`, convert to the unboxed path. The LIR generator can lower this
874936
// into a call to sqrt().
875937
if (op == BinaryOpKind::kPower) {

cinderx/RuntimeTests/hir_tests/simplify_test.txt

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6155,6 +6155,200 @@ fun test {
61556155
}
61566156
}
61576157
--- Test Name ---
6158+
BinaryOpMulFloatExactAndConstIntToUnboxed
6159+
--- Input ---
6160+
# HIR
6161+
fun test {
6162+
bb 0 {
6163+
v1 = LoadArg<0>
6164+
v2:ImmortalLongExact[3] = LoadConst<ImmortalLongExact[3]>
6165+
v3 = RefineType<FloatExact> v1
6166+
v4 = BinaryOp<Multiply> v3 v2
6167+
Return v4
6168+
}
6169+
}
6170+
--- Expected 3.10 ---
6171+
fun test {
6172+
bb 0 {
6173+
v1:Object = LoadArg<0>
6174+
v2:ImmortalLongExact[3] = LoadConst<ImmortalLongExact[3]>
6175+
v3:FloatExact = RefineType<FloatExact> v1
6176+
UseType<FloatExact> v3
6177+
UseType<ImmortalLongExact[3]> v2
6178+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6179+
v6:CDouble[3] = LoadConst<CDouble[3]>
6180+
v7:CDouble = DoubleBinaryOp<Multiply> v5 v6
6181+
v8:FloatExact = PrimitiveBox<CDouble> v7 {
6182+
FrameState {
6183+
CurInstrOffset -2
6184+
}
6185+
}
6186+
Return v8
6187+
}
6188+
}
6189+
--- Expected 3.12 ---
6190+
fun test {
6191+
bb 0 {
6192+
v1:Object = LoadArg<0>
6193+
v2:ImmortalLongExact[3] = LoadConst<ImmortalLongExact[3]>
6194+
v3:FloatExact = RefineType<FloatExact> v1
6195+
UseType<FloatExact> v3
6196+
UseType<ImmortalLongExact[3]> v2
6197+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6198+
v6:CDouble[3] = LoadConst<CDouble[3]>
6199+
v7:CDouble = DoubleBinaryOp<Multiply> v5 v6
6200+
v8:FloatExact = PrimitiveBox<CDouble> v7 {
6201+
FrameState {
6202+
CurInstrOffset -2
6203+
}
6204+
}
6205+
Return v8
6206+
}
6207+
}
6208+
--- Expected 3.14 ---
6209+
fun test {
6210+
bb 0 {
6211+
v1:Object = LoadArg<0>
6212+
v2:ImmortalLongExact[3] = LoadConst<ImmortalLongExact[3]>
6213+
v3:FloatExact = RefineType<FloatExact> v1
6214+
UseType<FloatExact> v3
6215+
UseType<ImmortalLongExact[3]> v2
6216+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6217+
v6:CDouble[3] = LoadConst<CDouble[3]>
6218+
v7:CDouble = DoubleBinaryOp<Multiply> v5 v6
6219+
v8:FloatExact = PrimitiveBox<CDouble> v7 {
6220+
FrameState {
6221+
CurInstrOffset -2
6222+
}
6223+
}
6224+
Return v8
6225+
}
6226+
}
6227+
--- Expected 3.15 ---
6228+
fun test {
6229+
bb 0 {
6230+
v1:Object = LoadArg<0>
6231+
v2:ImmortalLongExact[3] = LoadConst<ImmortalLongExact[3]>
6232+
v3:FloatExact = RefineType<FloatExact> v1
6233+
UseType<FloatExact> v3
6234+
UseType<ImmortalLongExact[3]> v2
6235+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6236+
v6:CDouble[3] = LoadConst<CDouble[3]>
6237+
v7:CDouble = DoubleBinaryOp<Multiply> v5 v6
6238+
v8:FloatExact = PrimitiveBox<CDouble> v7 {
6239+
FrameState {
6240+
CurInstrOffset -2
6241+
}
6242+
}
6243+
Return v8
6244+
}
6245+
}
6246+
--- Test Name ---
6247+
BinaryOpTrueDivideConstIntAndFloatExactToUnboxed
6248+
--- Input ---
6249+
# HIR
6250+
fun test {
6251+
bb 0 {
6252+
v2 = LoadArg<0>
6253+
v1:MortalLongExact[1] = LoadConst<MortalLongExact[1]>
6254+
v3 = RefineType<FloatExact> v2
6255+
v4 = BinaryOp<TrueDivide> v1 v3
6256+
Return v4
6257+
}
6258+
}
6259+
--- Expected 3.10 ---
6260+
fun test {
6261+
bb 0 {
6262+
v2:Object = LoadArg<0>
6263+
v1:ImmortalLongExact[1] = LoadConst<ImmortalLongExact[1]>
6264+
v3:FloatExact = RefineType<FloatExact> v2
6265+
UseType<FloatExact> v3
6266+
UseType<ImmortalLongExact[1]> v1
6267+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6268+
v6:CDouble[1] = LoadConst<CDouble[1]>
6269+
v7:CDouble[0] = LoadConst<CDouble[0]>
6270+
v8:CBool = PrimitiveCompare<NotEqual> v5 v7
6271+
Guard v8 {
6272+
}
6273+
v9:CDouble = DoubleBinaryOp<TrueDivide> v6 v5
6274+
v10:FloatExact = PrimitiveBox<CDouble> v9 {
6275+
FrameState {
6276+
CurInstrOffset -2
6277+
}
6278+
}
6279+
Return v10
6280+
}
6281+
}
6282+
--- Expected 3.12 ---
6283+
fun test {
6284+
bb 0 {
6285+
v2:Object = LoadArg<0>
6286+
v1:ImmortalLongExact[1] = LoadConst<ImmortalLongExact[1]>
6287+
v3:FloatExact = RefineType<FloatExact> v2
6288+
UseType<FloatExact> v3
6289+
UseType<ImmortalLongExact[1]> v1
6290+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6291+
v6:CDouble[1] = LoadConst<CDouble[1]>
6292+
v7:CDouble[0] = LoadConst<CDouble[0]>
6293+
v8:CBool = PrimitiveCompare<NotEqual> v5 v7
6294+
Guard v8 {
6295+
}
6296+
v9:CDouble = DoubleBinaryOp<TrueDivide> v6 v5
6297+
v10:FloatExact = PrimitiveBox<CDouble> v9 {
6298+
FrameState {
6299+
CurInstrOffset -2
6300+
}
6301+
}
6302+
Return v10
6303+
}
6304+
}
6305+
--- Expected 3.14 ---
6306+
fun test {
6307+
bb 0 {
6308+
v2:Object = LoadArg<0>
6309+
v1:ImmortalLongExact[1] = LoadConst<ImmortalLongExact[1]>
6310+
v3:FloatExact = RefineType<FloatExact> v2
6311+
UseType<FloatExact> v3
6312+
UseType<ImmortalLongExact[1]> v1
6313+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6314+
v6:CDouble[1] = LoadConst<CDouble[1]>
6315+
v7:CDouble[0] = LoadConst<CDouble[0]>
6316+
v8:CBool = PrimitiveCompare<NotEqual> v5 v7
6317+
Guard v8 {
6318+
}
6319+
v9:CDouble = DoubleBinaryOp<TrueDivide> v6 v5
6320+
v10:FloatExact = PrimitiveBox<CDouble> v9 {
6321+
FrameState {
6322+
CurInstrOffset -2
6323+
}
6324+
}
6325+
Return v10
6326+
}
6327+
}
6328+
--- Expected 3.15 ---
6329+
fun test {
6330+
bb 0 {
6331+
v2:Object = LoadArg<0>
6332+
v1:ImmortalLongExact[1] = LoadConst<ImmortalLongExact[1]>
6333+
v3:FloatExact = RefineType<FloatExact> v2
6334+
UseType<FloatExact> v3
6335+
UseType<ImmortalLongExact[1]> v1
6336+
v5:CDouble = PrimitiveUnbox<CDouble> v3
6337+
v6:CDouble[1] = LoadConst<CDouble[1]>
6338+
v7:CDouble[0] = LoadConst<CDouble[0]>
6339+
v8:CBool = PrimitiveCompare<NotEqual> v5 v7
6340+
Guard v8 {
6341+
}
6342+
v9:CDouble = DoubleBinaryOp<TrueDivide> v6 v5
6343+
v10:FloatExact = PrimitiveBox<CDouble> v9 {
6344+
FrameState {
6345+
CurInstrOffset -2
6346+
}
6347+
}
6348+
Return v10
6349+
}
6350+
}
6351+
--- Test Name ---
61586352
BinaryOpWithObjSpecLeftAndRightLongExactTurnsIntoLoadConst
61596353
--- Input ---
61606354
# HIR
@@ -14135,6 +14329,108 @@ fun test {
1413514329
}
1413614330
}
1413714331
--- Test Name ---
14332+
FloatBinaryOpTrueDivideToUnboxed
14333+
--- Input ---
14334+
# HIR
14335+
fun test {
14336+
bb 0 {
14337+
v1 = LoadArg<0>
14338+
v2 = LoadArg<1>
14339+
v3 = RefineType<FloatExact> v1
14340+
v4 = RefineType<FloatExact> v2
14341+
v5 = FloatBinaryOp<TrueDivide> v3 v4
14342+
Return v5
14343+
}
14344+
}
14345+
--- Expected 3.10 ---
14346+
fun test {
14347+
bb 0 {
14348+
v1:Object = LoadArg<0>
14349+
v2:Object = LoadArg<1>
14350+
v3:FloatExact = RefineType<FloatExact> v1
14351+
v4:FloatExact = RefineType<FloatExact> v2
14352+
v6:CDouble = PrimitiveUnbox<CDouble> v3
14353+
v7:CDouble = PrimitiveUnbox<CDouble> v4
14354+
v8:CDouble[0] = LoadConst<CDouble[0]>
14355+
v9:CBool = PrimitiveCompare<NotEqual> v7 v8
14356+
Guard v9 {
14357+
}
14358+
v10:CDouble = DoubleBinaryOp<TrueDivide> v6 v7
14359+
v11:FloatExact = PrimitiveBox<CDouble> v10 {
14360+
FrameState {
14361+
CurInstrOffset -2
14362+
}
14363+
}
14364+
Return v11
14365+
}
14366+
}
14367+
--- Expected 3.12 ---
14368+
fun test {
14369+
bb 0 {
14370+
v1:Object = LoadArg<0>
14371+
v2:Object = LoadArg<1>
14372+
v3:FloatExact = RefineType<FloatExact> v1
14373+
v4:FloatExact = RefineType<FloatExact> v2
14374+
v6:CDouble = PrimitiveUnbox<CDouble> v3
14375+
v7:CDouble = PrimitiveUnbox<CDouble> v4
14376+
v8:CDouble[0] = LoadConst<CDouble[0]>
14377+
v9:CBool = PrimitiveCompare<NotEqual> v7 v8
14378+
Guard v9 {
14379+
}
14380+
v10:CDouble = DoubleBinaryOp<TrueDivide> v6 v7
14381+
v11:FloatExact = PrimitiveBox<CDouble> v10 {
14382+
FrameState {
14383+
CurInstrOffset -2
14384+
}
14385+
}
14386+
Return v11
14387+
}
14388+
}
14389+
--- Expected 3.14 ---
14390+
fun test {
14391+
bb 0 {
14392+
v1:Object = LoadArg<0>
14393+
v2:Object = LoadArg<1>
14394+
v3:FloatExact = RefineType<FloatExact> v1
14395+
v4:FloatExact = RefineType<FloatExact> v2
14396+
v6:CDouble = PrimitiveUnbox<CDouble> v3
14397+
v7:CDouble = PrimitiveUnbox<CDouble> v4
14398+
v8:CDouble[0] = LoadConst<CDouble[0]>
14399+
v9:CBool = PrimitiveCompare<NotEqual> v7 v8
14400+
Guard v9 {
14401+
}
14402+
v10:CDouble = DoubleBinaryOp<TrueDivide> v6 v7
14403+
v11:FloatExact = PrimitiveBox<CDouble> v10 {
14404+
FrameState {
14405+
CurInstrOffset -2
14406+
}
14407+
}
14408+
Return v11
14409+
}
14410+
}
14411+
--- Expected 3.15 ---
14412+
fun test {
14413+
bb 0 {
14414+
v1:Object = LoadArg<0>
14415+
v2:Object = LoadArg<1>
14416+
v3:FloatExact = RefineType<FloatExact> v1
14417+
v4:FloatExact = RefineType<FloatExact> v2
14418+
v6:CDouble = PrimitiveUnbox<CDouble> v3
14419+
v7:CDouble = PrimitiveUnbox<CDouble> v4
14420+
v8:CDouble[0] = LoadConst<CDouble[0]>
14421+
v9:CBool = PrimitiveCompare<NotEqual> v7 v8
14422+
Guard v9 {
14423+
}
14424+
v10:CDouble = DoubleBinaryOp<TrueDivide> v6 v7
14425+
v11:FloatExact = PrimitiveBox<CDouble> v10 {
14426+
FrameState {
14427+
CurInstrOffset -2
14428+
}
14429+
}
14430+
Return v11
14431+
}
14432+
}
14433+
--- Test Name ---
1413814434
InPlaceOpFloatAddToUnboxed
1413914435
--- Input ---
1414014436
# HIR

0 commit comments

Comments
 (0)