Skip to content

Commit 594b892

Browse files
DinoVmeta-codesync[bot]
authored andcommitted
Add inline cache for binary add
Summary: This adds the infrastructure for inline caching of binary ops and adds support for inline caches of +. The goal here is to get more precise information about the inline caching then we get from the interpreter before we recompile a function. The cache is represented by a single 1 byte value which follows transitions of initializing -> known types -> degraded types. The types tracked are lhs/rhs/return type. So we may start in initializing, go to compact + compact == compact, degrade to long + long == long, and then degrade to fully generic. Right now we support compact longs, longs, floats, strs, lists, tuples, and complex numbers. There's a lot of template and macro machinery here but the goal is to make adding new ops easy. Reviewed By: mpage Differential Revision: D109088703 fbshipit-source-id: da6fd74d1af882f6dc1f419d74bf35358e74d02f
1 parent 5cb79c0 commit 594b892

18 files changed

Lines changed: 941 additions & 0 deletions

cinderx/Jit/config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ struct Config {
150150
bool attr_caches{!kFreeThreadedBuild};
151151
// Collect stats information about attribute caches.
152152
bool collect_attr_cache_stats{false};
153+
// Use inline caches for binary operations (currently the add variant only),
154+
// dispatching through a self-modifying function pointer that specializes on
155+
// the operand types observed at runtime. Opt-in / off by default.
156+
bool binary_op_caches{false};
153157
// Use type annotations to create runtime checks.
154158
bool emit_type_annotation_guards{false};
155159
// Whether or not to JIT specialized opcodes or to fall back to their generic

cinderx/Jit/context.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,10 @@ LoadTypeMethodCache* Context::allocateLoadTypeMethodCache() {
373373
return load_type_method_caches_.allocate();
374374
}
375375

376+
BinaryOpCache* Context::allocateBinaryOpCache(hir::BinaryOpKind op) {
377+
return binary_op_caches_.allocate(op);
378+
}
379+
376380
StoreAttrCache* Context::allocateStoreAttrCache() {
377381
return store_attr_caches_.allocate();
378382
}

cinderx/Jit/context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ class Context : public IJitContext, public CompiledFunctionOwner {
398398
LoadModuleMethodCache* allocateLoadModuleMethodCache();
399399
LoadTypeMethodCache* allocateLoadTypeMethodCache();
400400
StoreAttrCache* allocateStoreAttrCache();
401+
BinaryOpCache* allocateBinaryOpCache(hir::BinaryOpKind op);
401402

402403
const Builtins& builtins();
403404

@@ -498,6 +499,7 @@ class Context : public IJitContext, public CompiledFunctionOwner {
498499
SlabArena<LoadModuleMethodCache> load_module_method_caches_;
499500
SlabArena<LoadTypeMethodCache> load_type_method_caches_;
500501
SlabArena<StoreAttrCache, AttributeCacheSizeTrait> store_attr_caches_;
502+
SlabArena<BinaryOpCache> binary_op_caches_;
501503
SlabArena<void*> pointer_caches_;
502504

503505
FunctionEntryCacheMap function_entry_caches_;

cinderx/Jit/hir/builder.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,16 @@ void HIRBuilder::emitBinaryOp(
20272027
if (getConfig().specialized_opcodes) {
20282028
switch (bc_instr.specializedOpcode()) {
20292029
case BINARY_OP_ADD_INT:
2030+
// If we have inline caches for binary ops enabled then we don't want
2031+
// to specialize on the last seen interpreter type. The binary cache
2032+
// ops perform no backoff so once a cache is installed it persists.
2033+
// We want more accurate tracking of types to not perform deopts when
2034+
// the caches are enabled.
2035+
if (!getConfig().binary_op_caches) {
2036+
tc.emit<GuardType>(left, TLongExact, left, tc.frame);
2037+
tc.emit<GuardType>(right, TLongExact, right, tc.frame);
2038+
}
2039+
break;
20302040
case BINARY_OP_MULTIPLY_INT:
20312041
case BINARY_OP_SUBTRACT_INT:
20322042
tc.emit<GuardType>(left, TLongExact, left, tc.frame);

cinderx/Jit/hir/hir.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ bool Instr::isReplayable() const {
449449
case Opcode::kBatchDecref:
450450
case Opcode::kBeginInlinedFunction:
451451
case Opcode::kBinaryOp:
452+
case Opcode::kBinaryOpCached:
452453
case Opcode::kBranch:
453454
case Opcode::kBuildSlice:
454455
case Opcode::kBuildInterpolation:
@@ -733,6 +734,7 @@ bool isPassthrough(const Instr& instr) {
733734
return (static_cast<const Cast*>(&instr))->pytype() != &PyFloat_Type;
734735

735736
case Opcode::kBinaryOp:
737+
case Opcode::kBinaryOpCached:
736738
case Opcode::kBuildSlice:
737739
case Opcode::kBuildString:
738740
case Opcode::kBuildInterpolation:

cinderx/Jit/hir/hir.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,40 @@ class INSTR_CLASS(
659659
BinaryOpKind op_;
660660
};
661661

662+
// Variant of BinaryOp that dispatches through a per-instruction inline cache
663+
// (BinaryOpCache). Used to specialize the operation based on the operand types
664+
// observed at runtime. Currently only emitted for the add (kAdd) variant.
665+
class INSTR_CLASS(
666+
BinaryOpCached,
667+
(TObject, TObject),
668+
HasOutput,
669+
Operands<2>,
670+
DeoptBase) {
671+
public:
672+
BinaryOpCached(
673+
Register* dst,
674+
BinaryOpKind op,
675+
Register* left,
676+
Register* right,
677+
const FrameState& frame)
678+
: InstrT(dst, left, right, frame), op_(op) {}
679+
680+
BinaryOpKind op() const {
681+
return op_;
682+
}
683+
684+
Register* left() const {
685+
return getOperand(0);
686+
}
687+
688+
Register* right() const {
689+
return getOperand(1);
690+
}
691+
692+
private:
693+
BinaryOpKind op_;
694+
};
695+
662696
#define FOREACH_UNARY_OP_KIND(V) \
663697
V(Not) \
664698
V(Negate) \

cinderx/Jit/hir/instr_effects.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ MemoryEffects memoryEffects(const Instr& inst) {
111111
// inputs, and may write all memory locations (usually from invoking
112112
// arbitrary user code).
113113
case Opcode::kBinaryOp:
114+
case Opcode::kBinaryOpCached:
114115
case Opcode::kCallEx:
115116
case Opcode::kCallInd:
116117
case Opcode::kCallIntrinsic:
@@ -526,6 +527,7 @@ bool hasArbitraryExecution(const Instr& inst) {
526527
case Opcode::kLoadSpecial:
527528
case Opcode::kLongBinaryOp:
528529
case Opcode::kLongInPlaceOp:
530+
case Opcode::kBinaryOpCached:
529531
case Opcode::kMakeFunction:
530532
case Opcode::kMergeSetUnpack:
531533
case Opcode::kMatchClass:

cinderx/Jit/hir/ops.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace cinderx::jit::hir {
1313
V(BatchDecref) \
1414
V(BeginInlinedFunction) \
1515
V(BinaryOp) \
16+
V(BinaryOpCached) \
1617
V(BitCast) \
1718
V(Branch) \
1819
V(BuildSlice) \

cinderx/Jit/hir/parser.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,15 @@ HIRParser::parseInstr(std::string_view opcode, Register* dst, int bb_index) {
543543
instruction = newInstr<BinaryOp>(dst, op, left, right);
544544
break;
545545
}
546+
case Opcode::kBinaryOpCached: {
547+
expect("<");
548+
BinaryOpKind op = ParseBinaryOpName(getNextToken());
549+
expect(">");
550+
auto left = parseRegister();
551+
auto right = parseRegister();
552+
instruction = newInstr<BinaryOpCached>(dst, op, left, right);
553+
break;
554+
}
546555
case Opcode::kLongBinaryOp: {
547556
expect("<");
548557
BinaryOpKind op = ParseBinaryOpName(getNextToken());

cinderx/Jit/hir/pass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ Type outputType(
188188
return TObject;
189189
}
190190

191+
case Opcode::kBinaryOpCached:
191192
case Opcode::kBuildInterpolation:
192193
case Opcode::kBuildTemplate:
193194
case Opcode::kCallIntrinsic:

0 commit comments

Comments
 (0)