Skip to content

Commit

Permalink
Align arithmetic slow paths with out-of-line cases
Browse files Browse the repository at this point in the history
Summary:
Out-of-line interpreter cases just take the interpreter state as
parameters, which allows them to minimise the amount of work that has to
be done in the interpreter itself. Apply the same approach to arithmetic
operations in the interpreter, by having their slow paths take the
interpreter state and internally read/write registers.

This diff doesn't really move benchmarks, but together with the later
diffs, it significantly reduces the amount of code in the interpreter.

Reviewed By: tmikov

Differential Revision: D65736831

fbshipit-source-id: fd04e8bfb6c1a8cbba93ef43ff20fcd74d25be18
  • Loading branch information
neildhar authored and facebook-github-bot committed Nov 23, 2024
1 parent 18aa2ff commit ed6695d
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 154 deletions.
41 changes: 27 additions & 14 deletions lib/VM/Interpreter-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,26 +124,39 @@ inline double doDec(double d) {
return d - 1;
}

template <auto Oper>
CallResult<HermesValue>
doOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template <auto Oper, typename InstType>
ExecutionStatus doOperSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const InstType *inst);

template <auto Oper>
CallResult<HermesValue>
doBitOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template <auto Oper, typename InstType>
ExecutionStatus doBitOperSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const InstType *inst);

template <auto Oper>
CallResult<HermesValue>
doShiftOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template <auto Oper, typename InstType>
ExecutionStatus doShiftOperSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const InstType *inst);

template <auto Oper>
CallResult<HermesValue> doIncDecOperSlowPath_RJS(
template <auto Oper, typename InstType>
ExecutionStatus doIncDecOperSlowPath_RJS(
Runtime &runtime,
Handle<> src);
PinnedHermesValue *frameRegs,
const InstType *inst);

CallResult<HermesValue> doBitNotSlowPath_RJS(Runtime &runtime, Handle<> src);
ExecutionStatus doBitNotSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const Inst *ip);

CallResult<HermesValue> doNegateSlowPath_RJS(Runtime &runtime, Handle<> src);
ExecutionStatus doNegateSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const Inst *ip);

} // namespace vm
} // namespace hermes
Expand Down
198 changes: 139 additions & 59 deletions lib/VM/Interpreter-slowpaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,13 @@ constexpr auto &BigIntOper<doDec> = BigIntPrimitive::dec;

} // namespace

template <auto Oper>
CallResult<HermesValue>
doOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs) {
template <auto Oper, typename InstType>
ExecutionStatus doOperSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const InstType *inst) {
Handle<> lhs(&REG(inst->op2));
Handle<> rhs(&REG(inst->op3));
CallResult<HermesValue> res =
toPrimitive_RJS(runtime, lhs, PreferredType::NUMBER);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
Expand All @@ -538,27 +542,46 @@ doOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs) {
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return HermesValue::encodeTrustedNumberValue(Oper(left, res->getDouble()));
REG(inst->op1) =
HermesValue::encodeTrustedNumberValue(Oper(left, res->getDouble()));
return ExecutionStatus::RETURNED;
}
return doBigIntBinOp(
res = doBigIntBinOp(
runtime, BigIntOper<Oper>, runtime.makeHandle(res->getBigInt()), rhs);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
REG(inst->op1) = *res;
return ExecutionStatus::RETURNED;
}

template CallResult<HermesValue>
doOperSlowPath_RJS<doDiv>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doOperSlowPath_RJS<doDiv>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::DivInst *inst);

template CallResult<HermesValue>
doOperSlowPath_RJS<doMod>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doOperSlowPath_RJS<doMod>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::ModInst *inst);

template CallResult<HermesValue>
doOperSlowPath_RJS<doMul>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doOperSlowPath_RJS<doMul>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::MulInst *inst);

template CallResult<HermesValue>
doOperSlowPath_RJS<doSub>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doOperSlowPath_RJS<doSub>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::SubInst *inst);

template <auto Oper>
CallResult<HermesValue>
doBitOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs) {
template <auto Oper, typename InstType>
ExecutionStatus doBitOperSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const InstType *inst) {
Handle<> lhs(&REG(inst->op2));
Handle<> rhs(&REG(inst->op3));
CallResult<HermesValue> res =
toPrimitive_RJS(runtime, lhs, PreferredType::NUMBER);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
Expand All @@ -570,25 +593,40 @@ doBitOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs) {
return ExecutionStatus::EXCEPTION;
}
const int32_t left = res->getNumberAs<int32_t>();
res = toInt32_RJS(runtime, std::move(rhs));
res = toInt32_RJS(runtime, rhs);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return HermesValue::encodeTrustedNumberValue(
REG(inst->op1) = HermesValue::encodeTrustedNumberValue(
Oper(left, res->getNumberAs<int32_t>()));
return ExecutionStatus::RETURNED;
}
return doBigIntBinOp(
runtime, BigIntOper<Oper>, runtime.makeHandle(res->getBigInt()), rhs);
res = doBigIntBinOp(
runtime,
BigIntOper<Oper>,
runtime.makeHandle(res->getBigInt()),
Handle<>(rhs));
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
REG(inst->op1) = *res;
return ExecutionStatus::RETURNED;
}

template CallResult<HermesValue>
doBitOperSlowPath_RJS<doBitAnd>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doBitOperSlowPath_RJS<doBitAnd>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::BitAndInst *ip);

template CallResult<HermesValue>
doBitOperSlowPath_RJS<doBitOr>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doBitOperSlowPath_RJS<doBitOr>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::BitOrInst *ip);

template CallResult<HermesValue>
doBitOperSlowPath_RJS<doBitXor>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doBitOperSlowPath_RJS<doBitXor>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::BitXorInst *ip);

namespace {
/// ToIntegral maps the \param Oper shift operation (on Number) to the function
Expand All @@ -608,9 +646,13 @@ template <>
inline constexpr auto &ToIntegral<doURshift> = toUInt32_RJS;
} // namespace

template <auto Oper>
CallResult<HermesValue>
doShiftOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs) {
template <auto Oper, typename InstType>
ExecutionStatus doShiftOperSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const InstType *inst) {
Handle<> lhs(&REG(inst->op2));
Handle<> rhs(&REG(inst->op3));
CallResult<HermesValue> res =
toPrimitive_RJS(runtime, std::move(lhs), PreferredType::NUMBER);

Expand All @@ -629,32 +671,43 @@ doShiftOperSlowPath_RJS(Runtime &runtime, Handle<> lhs, Handle<> rhs) {
return ExecutionStatus::EXCEPTION;
}
auto rnum = static_cast<uint32_t>(res->getNumber()) & 0x1f;
return HermesValue::encodeTrustedNumberValue((*Oper)(lnum, rnum));
REG(inst->op1) = HermesValue::encodeTrustedNumberValue((*Oper)(lnum, rnum));
return ExecutionStatus::RETURNED;
}
return doBigIntBinOp(
res = doBigIntBinOp(
runtime,
BigIntOper<Oper>,
runtime.makeHandle(res->getBigInt()),
std::move(rhs));
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
REG(inst->op1) = *res;
return ExecutionStatus::RETURNED;
}

template CallResult<HermesValue>
doShiftOperSlowPath_RJS<doLShift>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doShiftOperSlowPath_RJS<doLShift>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::LShiftInst *inst);

template CallResult<HermesValue>
doShiftOperSlowPath_RJS<doRShift>(Runtime &runtime, Handle<> lhs, Handle<> rhs);
template ExecutionStatus doShiftOperSlowPath_RJS<doRShift>(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const inst::RShiftInst *inst);

template CallResult<HermesValue> doShiftOperSlowPath_RJS<doURshift>(
template ExecutionStatus doShiftOperSlowPath_RJS<doURshift>(
Runtime &runtime,
Handle<> lhs,
Handle<> rhs);
PinnedHermesValue *frameRegs,
const inst::URshiftInst *inst);

template <auto Oper>
CallResult<HermesValue> doIncDecOperSlowPath_RJS(
template <auto Oper, typename InstType>
ExecutionStatus doIncDecOperSlowPath_RJS(
Runtime &runtime,
Handle<> src) {
CallResult<HermesValue> res =
toPrimitive_RJS(runtime, std::move(src), PreferredType::NUMBER);
PinnedHermesValue *frameRegs,
const InstType *inst) {
CallResult<HermesValue> res = toPrimitive_RJS(
runtime, Handle<>(&REG(inst->op2)), PreferredType::NUMBER);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Expand All @@ -664,49 +717,76 @@ CallResult<HermesValue> doIncDecOperSlowPath_RJS(
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return HermesValue::encodeTrustedNumberValue(Oper(res->getNumber()));
REG(inst->op1) =
HermesValue::encodeTrustedNumberValue(Oper(res->getNumber()));
return ExecutionStatus::RETURNED;
}

return BigIntOper<Oper>(runtime, runtime.makeHandle(res->getBigInt()));
res = BigIntOper<Oper>(runtime, runtime.makeHandle(res->getBigInt()));
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
REG(inst->op1) = *res;
return ExecutionStatus::RETURNED;
}

template CallResult<HermesValue> doIncDecOperSlowPath_RJS<doInc>(
template ExecutionStatus doIncDecOperSlowPath_RJS<doInc>(
Runtime &runtime,
Handle<> src);
PinnedHermesValue *frameRegs,
const inst::IncInst *inst);

template CallResult<HermesValue> doIncDecOperSlowPath_RJS<doDec>(
template ExecutionStatus doIncDecOperSlowPath_RJS<doDec>(
Runtime &runtime,
Handle<> src);
PinnedHermesValue *frameRegs,
const inst::DecInst *inst);

CallResult<HermesValue> doBitNotSlowPath_RJS(Runtime &runtime, Handle<> src) {
ExecutionStatus doBitNotSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const Inst *ip) {
// Try converting src to a numeric.
auto numRes = toNumeric_RJS(runtime, src);
auto numRes = toNumeric_RJS(runtime, Handle<>(&O2REG(BitNot)));
if (LLVM_UNLIKELY(numRes == ExecutionStatus::EXCEPTION))
return ExecutionStatus::EXCEPTION;
// Test for BigInt since it is cheaper than testing for number. If it is a
// number, truncate it and perform bitwise not.
if (LLVM_LIKELY(!numRes->isBigInt()))
return HermesValue::encodeTrustedNumberValue(
if (LLVM_LIKELY(!numRes->isBigInt())) {
O1REG(BitNot) = HermesValue::encodeTrustedNumberValue(
~hermes::truncateToInt32(numRes->getNumber()));
return ExecutionStatus::RETURNED;
}

// The result is a BigInt, perform a BigInt bitwise not.
auto bigint = runtime.makeHandle(numRes->getBigInt());
return BigIntPrimitive::unaryNOT(runtime, bigint);
auto res = BigIntPrimitive::unaryNOT(runtime, bigint);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION))
return ExecutionStatus::EXCEPTION;
O1REG(BitNot) = *res;
return ExecutionStatus::RETURNED;
}

CallResult<HermesValue> doNegateSlowPath_RJS(Runtime &runtime, Handle<> src) {
ExecutionStatus doNegateSlowPath_RJS(
Runtime &runtime,
PinnedHermesValue *frameRegs,
const Inst *ip) {
// Try converting src to a numeric.
auto numRes = toNumeric_RJS(runtime, src);
auto numRes = toNumeric_RJS(runtime, Handle<>(&O2REG(Negate)));
if (LLVM_UNLIKELY(numRes == ExecutionStatus::EXCEPTION))
return ExecutionStatus::EXCEPTION;
// Test for BigInt since it is cheaper than testing for number. If it is a
// number, negate it and return.
if (LLVM_LIKELY(!numRes->isBigInt()))
return HermesValue::encodeTrustedNumberValue(-numRes->getNumber());
if (LLVM_LIKELY(!numRes->isBigInt())) {
O1REG(Negate) = HermesValue::encodeTrustedNumberValue(-numRes->getNumber());
return ExecutionStatus::RETURNED;
}

// The result is a BigInt, perform a BigInt unary minus.
auto bigint = runtime.makeHandle(numRes->getBigInt());
return BigIntPrimitive::unaryMinus(runtime, bigint);
auto res = BigIntPrimitive::unaryMinus(runtime, bigint);
if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION))
return ExecutionStatus::EXCEPTION;
O1REG(Negate) = *res;
return ExecutionStatus::RETURNED;
}

} // namespace vm
Expand Down
Loading

0 comments on commit ed6695d

Please sign in to comment.