@@ -2103,7 +2103,7 @@ LoadModuleMethodCache::lookupSlowPath(BorrowedRef<> obj, BorrowedRef<> name) {
21032103 return {nullptr , nullptr };
21042104}
21052105
2106- // Single source of truth for BinaryOpCache's add specializations, in priority
2106+ // Single source of truth for BinaryOpCache's specializations, in priority
21072107// order. Each row is X(Name, Lhs, Rhs, Ret, Op, Fallback):
21082108// - Name unique identifier; becomes Specialization::k<Name>.
21092109// - Lhs, Rhs, Ret operand and result types (SpecializedType values); their
@@ -2114,9 +2114,15 @@ LoadModuleMethodCache::lookupSlowPath(BorrowedRef<> obj, BorrowedRef<> name) {
21142114// compact/compact/long when a result overflows the compact
21152115// range, which in turn steps down to long/long/long when the
21162116// operands stop being compact.
2117- // - Op fast-path operation (e.g. longAdd, defined below).
2118- // - Fallback the Specialization to step down to once this row stops
2119- // matching.
2117+ // - Op fast-path operation (e.g. longAdd, defined in
2118+ // inline_cache.cpp).
2119+ // - Fallback the per-op Specialization to step down to once this row
2120+ // stops matching.
2121+ //
2122+ // These macros are defined here (rather than in inline_cache.cpp) so the
2123+ // Specialization enum below can be generated from the same lists that drive the
2124+ // dispatch switches. The Op/Fallback columns are only expanded inside
2125+ // inline_cache.cpp, so naming file-local op helpers here is fine.
21202126#define FOREACH_ADD_SPECIALIZATION (X ) \
21212127 X (AddCompactCompactCompact, \
21222128 CompactLong, \
@@ -2137,12 +2143,45 @@ LoadModuleMethodCache::lookupSlowPath(BorrowedRef<> obj, BorrowedRef<> name) {
21372143 X (AddTuple, Tuple, Tuple, Tuple, tupleAdd, kAddGeneric ) \
21382144 X (AddComplex, Complex, Complex, Complex, complexAdd, kAddGeneric )
21392145
2146+ // Specializations for the multiply op. Note the (sequence, long) rows have
2147+ // distinct lhs/rhs/result types: list/str/tuple repeated by an integer count.
2148+ #define FOREACH_MULTIPLY_SPECIALIZATION (X ) \
2149+ X (MulCompactCompactCompact, \
2150+ CompactLong, \
2151+ CompactLong, \
2152+ CompactLong, \
2153+ compactLongMul, \
2154+ kMulCompactCompactLong ) \
2155+ X (MulCompactCompactLong, \
2156+ CompactLong, \
2157+ CompactLong, \
2158+ Long, \
2159+ compactLongMul, \
2160+ kMulLongLongLong ) \
2161+ X (MulLongLongLong, Long, Long, Long, longMul, kMultiplyGeneric ) \
2162+ X (MulFloat, Float, Float, Float, floatMul, kMultiplyGeneric ) \
2163+ X (MulList, List, Long, List, listMul, kMultiplyGeneric ) \
2164+ X (MulUnicode, Unicode, Long, Unicode, strMul, kMultiplyGeneric ) \
2165+ X (MulTuple, Tuple, Long, Tuple, tupleMul, kMultiplyGeneric ) \
2166+ X (MulComplex, Complex, Long, Complex, complexMul, kMultiplyGeneric )
2167+
2168+ // The full specialization list (add followed by multiply), used to generate the
2169+ // single Specialization enum and the specializedTypes() switch that covers all
2170+ // values.
2171+ #define FOREACH_BINARY_OP_SPECIALIZATION (X ) \
2172+ FOREACH_ADD_SPECIALIZATION (X) \
2173+ FOREACH_MULTIPLY_SPECIALIZATION (X)
2174+
21402175enum class BinaryOpCache ::Specialization : uint8_t {
2141- #define DECLARE_ADD_SPECIALIZATION (NAME, LHS, RHS, RET, OP, FALLBACK ) k##NAME ,
2176+ #define DECLARE_BINARY_OP_SPECIALIZATION (NAME, LHS, RHS, RET, OP, FALLBACK ) \
2177+ k##NAME ,
21422178 kUninitializedAdd ,
21432179 kAddGeneric ,
2144- FOREACH_ADD_SPECIALIZATION (DECLARE_ADD_SPECIALIZATION )
2145- #undef DECLARE_ADD_SPECIALIZATION
2180+ FOREACH_ADD_SPECIALIZATION (DECLARE_BINARY_OP_SPECIALIZATION )
2181+ kUninitializedMultiply ,
2182+ kMultiplyGeneric ,
2183+ FOREACH_MULTIPLY_SPECIALIZATION (DECLARE_BINARY_OP_SPECIALIZATION )
2184+ #undef DECLARE_BINARY_OP_SPECIALIZATION
21462185};
21472186
21482187BinaryOpCache::BinaryOpCache (cinderx::jit::hir::BinaryOpKind op)
@@ -2153,6 +2192,8 @@ BinaryOpCache::Specialization BinaryOpCache::selectInitialSpecialization(
21532192 switch (op) {
21542193 case cinderx::jit::hir::BinaryOpKind::kAdd :
21552194 return Specialization::kUninitializedAdd ;
2195+ case cinderx::jit::hir::BinaryOpKind::kMultiply :
2196+ return Specialization::kUninitializedMultiply ;
21562197 default :
21572198 throw std::runtime_error (
21582199 fmt::format (
@@ -2291,6 +2332,8 @@ PyObject* BinaryOpCache::invokeSpecialized(
22912332 }
22922333#define POPULATE_ADD_SPECIALIZATION (...) \
22932334 POPULATE_BINARY_SPECIALIZATION (add, __VA_ARGS__)
2335+ #define POPULATE_MULTIPLY_SPECIALIZATION (...) \
2336+ POPULATE_BINARY_SPECIALIZATION (multiply, __VA_ARGS__)
22942337
22952338// Emits one dispatch-switch arm that runs the specialization directly via
22962339// invokeSpecialized<>, threading the Fallback value and the matching
@@ -2307,6 +2350,8 @@ PyObject* BinaryOpCache::invokeSpecialized(
23072350 &BinaryOpCache::DISPATCH >(lhs, rhs, cache);
23082351#define DISPATCH_ADD_SPECIALIZATION (...) \
23092352 DISPATCH_BINARY_SPECIALIZATION (add, __VA_ARGS__)
2353+ #define DISPATCH_MULTIPLY_SPECIALIZATION (...) \
2354+ DISPATCH_BINARY_SPECIALIZATION (multiply, __VA_ARGS__)
23102355
23112356// Emits one specializedTypes() switch arm mapping a specialization to its
23122357// (lhs, rhs, return) operand types. A single enum lets one switch cover both
@@ -2353,13 +2398,78 @@ static inline PyObject* tupleAdd(PyObject* lhs, PyObject* rhs) {
23532398 return PyTuple_Type.tp_as_sequence ->sq_concat (lhs, rhs);
23542399}
23552400
2401+ static inline PyObject* longMul (PyObject* lhs, PyObject* rhs) {
2402+ #if PY_VERSION_HEX >= 0x030F0000
2403+ // _PyLong_Multiply was removed in 3.15. Both operands are exact ints here,
2404+ // so the public number slot is equivalent and returns a new reference.
2405+ return PyLong_Type.tp_as_number ->nb_multiply (lhs, rhs);
2406+ #else
2407+ return _PyLong_Multiply (
2408+ reinterpret_cast <PyLongObject*>(lhs),
2409+ reinterpret_cast <PyLongObject*>(rhs));
2410+ #endif
2411+ }
2412+
2413+ // Fast path for two compact ints: multiply their machine-word values
2414+ // directly. Both operands are single-digit (guaranteed by _PyLong_IsCompact),
2415+ // so the product cannot overflow Py_ssize_t. The result may itself be
2416+ // non-compact; the compact/compact/compact specialization detects that via
2417+ // its return-type check and steps down to compact/compact/long.
2418+ static inline PyObject* compactLongMul (PyObject* lhs, PyObject* rhs) {
2419+ Py_ssize_t a = _PyLong_CompactValue (reinterpret_cast <PyLongObject*>(lhs));
2420+ Py_ssize_t b = _PyLong_CompactValue (reinterpret_cast <PyLongObject*>(rhs));
2421+ return PyLong_FromSsize_t (a * b);
2422+ }
2423+
2424+ static inline PyObject* floatMul (PyObject* lhs, PyObject* rhs) {
2425+ double a = reinterpret_cast <PyFloatObject*>(lhs)->ob_fval ;
2426+ double b = reinterpret_cast <PyFloatObject*>(rhs)->ob_fval ;
2427+ return PyFloat_FromDouble (a * b);
2428+ }
2429+
2430+ static inline PyObject* complexMul (PyObject* lhs, PyObject* rhs) {
2431+ // complex * long: the complex nb_multiply slot coerces the integer operand.
2432+ return PyComplex_Type.tp_as_number ->nb_multiply (lhs, rhs);
2433+ }
2434+
2435+ // Sequence-repeat helpers for (sequence, long) multiplication. The repeat
2436+ // count is the integer rhs; an out-of-range count surfaces as an error from
2437+ // PyLong_AsSsize_t, matching the generic path.
2438+ static inline PyObject*
2439+ sequenceRepeat (PySequenceMethods* methods, PyObject* seq, PyObject* count) {
2440+ Py_ssize_t n = PyLong_AsSsize_t (count);
2441+ if (n == -1 && PyErr_Occurred ()) {
2442+ return nullptr ;
2443+ }
2444+ return methods->sq_repeat (seq, n);
2445+ }
2446+
2447+ static inline PyObject* listMul (PyObject* lhs, PyObject* rhs) {
2448+ return sequenceRepeat (PyList_Type.tp_as_sequence , lhs, rhs);
2449+ }
2450+
2451+ static inline PyObject* strMul (PyObject* lhs, PyObject* rhs) {
2452+ return sequenceRepeat (PyUnicode_Type.tp_as_sequence , lhs, rhs);
2453+ }
2454+
2455+ static inline PyObject* tupleMul (PyObject* lhs, PyObject* rhs) {
2456+ return sequenceRepeat (PyTuple_Type.tp_as_sequence , lhs, rhs);
2457+ }
2458+
23562459PyObject* BinaryOpCache::addGeneric (
23572460 PyObject* lhs,
23582461 PyObject* rhs,
23592462 BinaryOpCache* /* cache */ ) {
23602463 return PyNumber_Add (lhs, rhs);
23612464}
23622465
2466+ PyObject* BinaryOpCache::multiplyGeneric (
2467+ PyObject* lhs,
2468+ PyObject* rhs,
2469+ BinaryOpCache* /* cache */ ) {
2470+ return PyNumber_Multiply (lhs, rhs);
2471+ }
2472+
23632473PyObject* BinaryOpCache::populateAndInvokeAdd (
23642474 PyObject* lhs,
23652475 PyObject* rhs,
@@ -2370,10 +2480,20 @@ PyObject* BinaryOpCache::populateAndInvokeAdd(
23702480 return addGeneric (lhs, rhs, cache);
23712481}
23722482
2373- // Dispatch on the cache's current specialization and run the corresponding add
2374- // operation directly. The specialized arms are generated from
2375- // FOREACH_ADD_SPECIALIZATION so this stays in sync with the Specialization
2376- // enum. There is no indirect call through a function pointer.
2483+ PyObject* BinaryOpCache::populateAndInvokeMultiply (
2484+ PyObject* lhs,
2485+ PyObject* rhs,
2486+ BinaryOpCache* cache) {
2487+ FOREACH_MULTIPLY_SPECIALIZATION (POPULATE_MULTIPLY_SPECIALIZATION )
2488+
2489+ cache->specialization_ = Specialization::kMultiplyGeneric ;
2490+ return multiplyGeneric (lhs, rhs, cache);
2491+ }
2492+
2493+ // Dispatch on the cache's current specialization and run the corresponding
2494+ // add operation directly. The arms cover only the add subset of the single
2495+ // Specialization enum (generated from FOREACH_ADD_SPECIALIZATION); multiply
2496+ // states never reach here because codegen calls add() only for kAdd caches.
23772497PyObject*
23782498BinaryOpCache::add (PyObject* lhs, PyObject* rhs, BinaryOpCache* cache) {
23792499 switch (cache->specialization_ ) {
@@ -2382,23 +2502,41 @@ BinaryOpCache::add(PyObject* lhs, PyObject* rhs, BinaryOpCache* cache) {
23822502 case Specialization::kAddGeneric :
23832503 return addGeneric (lhs, rhs, cache);
23842504 FOREACH_ADD_SPECIALIZATION (DISPATCH_ADD_SPECIALIZATION )
2505+ default :
2506+ JIT_ABORT (" Unexpected specialization in BinaryOpCache::add" );
2507+ }
2508+ }
2509+
2510+ // Dispatch on the cache's current specialization. Mirrors add() but over the
2511+ // multiply subset of the enum (FOREACH_MULTIPLY_SPECIALIZATION).
2512+ PyObject*
2513+ BinaryOpCache::multiply (PyObject* lhs, PyObject* rhs, BinaryOpCache* cache) {
2514+ switch (cache->specialization_ ) {
2515+ case Specialization::kUninitializedMultiply :
2516+ return populateAndInvokeMultiply (lhs, rhs, cache);
2517+ case Specialization::kMultiplyGeneric :
2518+ return multiplyGeneric (lhs, rhs, cache);
2519+ FOREACH_MULTIPLY_SPECIALIZATION (DISPATCH_MULTIPLY_SPECIALIZATION )
2520+ default :
2521+ JIT_ABORT (" Unexpected specialization in BinaryOpCache::multiply" );
23852522 }
2386- JIT_ABORT (" Unknown BinaryOpCache specialization" );
23872523}
23882524
23892525BinaryOpCache::BinarySpecialization BinaryOpCache::specializedTypes () const {
23902526 switch (specialization_) {
23912527 case Specialization::kUninitializedAdd :
2528+ case Specialization::kUninitializedMultiply :
23922529 return BinarySpecialization{
23932530 SpecializedType::kUninitialized ,
23942531 SpecializedType::kUninitialized ,
23952532 SpecializedType::kUninitialized };
23962533 case Specialization::kAddGeneric :
2534+ case Specialization::kMultiplyGeneric :
23972535 return BinarySpecialization{
23982536 SpecializedType::kGeneric ,
23992537 SpecializedType::kGeneric ,
24002538 SpecializedType::kGeneric };
2401- FOREACH_ADD_SPECIALIZATION (SPECIALIZATION_TYPES_ENTRY )
2539+ FOREACH_BINARY_OP_SPECIALIZATION (SPECIALIZATION_TYPES_ENTRY )
24022540 }
24032541 JIT_ABORT (" Unknown BinaryOpCache specialization" );
24042542}
0 commit comments