mirror of https://github.com/mongodb/mongo
SERVER-102171 Extend NaryOp to include Operations::Mult (#34583)
GitOrigin-RevId: da5bbacda11953f1a4a9cc7963cd1acb900930fb
This commit is contained in:
parent
3fa256f424
commit
7d3e912236
|
|
@ -294,6 +294,9 @@ vm::CodeFragment EPrimNary::compileDirect(CompileCtx& ctx) const {
|
|||
case EPrimNary::add:
|
||||
code.appendAdd(lhsParam, rhsParam);
|
||||
break;
|
||||
case EPrimNary::mul:
|
||||
code.appendMul(lhsParam, rhsParam);
|
||||
break;
|
||||
default:
|
||||
MONGO_UNREACHABLE;
|
||||
}
|
||||
|
|
@ -325,6 +328,9 @@ std::vector<DebugPrinter::Block> EPrimNary::debugPrint() const {
|
|||
case EPrimNary::add:
|
||||
ret.emplace_back("+");
|
||||
break;
|
||||
case EPrimNary::mul:
|
||||
ret.emplace_back("*");
|
||||
break;
|
||||
default:
|
||||
MONGO_UNREACHABLE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,9 +337,11 @@ public:
|
|||
|
||||
// Math operations.
|
||||
add,
|
||||
mul,
|
||||
};
|
||||
|
||||
EPrimNary(Op op, std::vector<std::unique_ptr<EExpression>> args) : _op(op) {
|
||||
tassert(10217100, "Expected at least two operands", args.size() >= 2);
|
||||
_nodes.reserve(args.size());
|
||||
for (auto&& arg : args) {
|
||||
_nodes.emplace_back(std::move(arg));
|
||||
|
|
@ -376,7 +378,7 @@ public:
|
|||
// Math operations.
|
||||
add, // TODO: remove with SERVER-100579
|
||||
sub,
|
||||
mul,
|
||||
mul, // TODO: remove with SERVER-100579
|
||||
div,
|
||||
|
||||
// Comparison operations. These operations support taking a third "collator" arg.
|
||||
|
|
|
|||
|
|
@ -279,4 +279,56 @@ TEST_F(SBEPrimNaryTest, NaryAdd) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(SBEPrimNaryTest, NaryMult) {
|
||||
auto& os = gctx->outStream();
|
||||
|
||||
std::vector<std::unique_ptr<value::ViewOfValueAccessor>> accessors;
|
||||
value::SlotVector slotIds;
|
||||
|
||||
int depth = 3;
|
||||
int numSlots = 1 << depth;
|
||||
for (int i = 0; i < numSlots; i++) {
|
||||
accessors.emplace_back(std::make_unique<value::ViewOfValueAccessor>());
|
||||
accessors.back()->reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(i + 1));
|
||||
slotIds.push_back(bindAccessor(accessors.back().get()));
|
||||
}
|
||||
|
||||
auto expr = makeNaryExpr(EPrimNary::Op::mul, slotIds);
|
||||
printInputExpression(os, *expr);
|
||||
|
||||
auto compiledExpr = compileExpression(*expr);
|
||||
printCompiledExpression(os, *compiledExpr);
|
||||
|
||||
{
|
||||
auto [tag, val] = runCompiledExpression(compiledExpr.get());
|
||||
value::ValueGuard guard(tag, val);
|
||||
|
||||
TypedValue expected = makeInt64(40320);
|
||||
ASSERT_THAT(std::make_pair(tag, val), ValueEq(expected));
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < numSlots; ++idx) {
|
||||
accessors[idx]->reset(value::TypeTags::Nothing, 0);
|
||||
|
||||
auto [tag, val] = runCompiledExpression(compiledExpr.get());
|
||||
value::ValueGuard guard(tag, val);
|
||||
|
||||
TypedValue expected = makeNothing();
|
||||
ASSERT_THAT(std::make_pair(tag, val), ValueEq(expected));
|
||||
accessors[idx]->reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(idx + 1));
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < numSlots; ++idx) {
|
||||
accessors[idx]->reset(value::TypeTags::NumberDouble,
|
||||
value::bitcastFrom<double>(static_cast<double>(idx + 1)));
|
||||
|
||||
auto [tag, val] = runCompiledExpression(compiledExpr.get());
|
||||
value::ValueGuard guard(tag, val);
|
||||
|
||||
TypedValue expected = makeDouble(40320.0);
|
||||
ASSERT_THAT(std::make_pair(tag, val), ValueEq(expected));
|
||||
accessors[idx]->reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(idx + 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mongo::sbe
|
||||
|
|
|
|||
|
|
@ -248,8 +248,9 @@ public:
|
|||
NaryOp(Operations inOp, ABTVector exprs) : Base(std::move(exprs)), _op(inOp) {
|
||||
tassert(10199600,
|
||||
"operation doesn't allow multiple operands",
|
||||
_op == Operations::And || _op == Operations::Or || _op == Operations::Add);
|
||||
tassert(10199601, "operation needs at least two operands", nodes().size() >= 2);
|
||||
_op == Operations::And || _op == Operations::Or || _op == Operations::Add ||
|
||||
_op == Operations::Mult);
|
||||
tassert(10199601, "operation needs at least one operand", nodes().size() >= 1);
|
||||
for (auto&& expr : nodes()) {
|
||||
assertExprSort(expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,6 +265,9 @@ std::unique_ptr<sbe::EExpression> SBEExpressionLowering::transport(
|
|||
case Operations::Add:
|
||||
sbeOp = sbe::EPrimNary::add;
|
||||
break;
|
||||
case Operations::Mult:
|
||||
sbeOp = sbe::EPrimNary::mul;
|
||||
break;
|
||||
default:
|
||||
MONGO_UNREACHABLE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -463,29 +463,44 @@ void ExpressionConstEval::transport(optimizer::ABT& n,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case optimizer::Operations::Add: {
|
||||
case optimizer::Operations::Add:
|
||||
case optimizer::Operations::Mult: {
|
||||
auto it = args.begin();
|
||||
if (!it->cast<optimizer::Constant>()) {
|
||||
return;
|
||||
}
|
||||
it++;
|
||||
for (; it < args.end(); it++) {
|
||||
optimizer::ABT& rhs = *it;
|
||||
auto rhsConst = rhs.cast<optimizer::Constant>();
|
||||
if (!rhsConst) {
|
||||
break;
|
||||
if (it->cast<optimizer::Constant>()) {
|
||||
it++;
|
||||
for (; it < args.end(); it++) {
|
||||
optimizer::ABT& rhs = *it;
|
||||
auto rhsConst = rhs.cast<optimizer::Constant>();
|
||||
if (!rhsConst) {
|
||||
break;
|
||||
}
|
||||
optimizer::ABT& lhs = *(it - 1);
|
||||
auto lhsConst = lhs.cast<optimizer::Constant>();
|
||||
|
||||
auto [lhsTag, lhsValue] = lhsConst->get();
|
||||
auto [rhsTag, rhsValue] = rhsConst->get();
|
||||
|
||||
auto performOp = [&](sbe::value::TypeTags lhsTag,
|
||||
sbe::value::Value lhsValue,
|
||||
sbe::value::TypeTags rhsTag,
|
||||
sbe::value::Value rhsValue) {
|
||||
switch (op.op()) {
|
||||
case optimizer::Operations::Add:
|
||||
return sbe::value::genericAdd(lhsTag, lhsValue, rhsTag, rhsValue);
|
||||
case optimizer::Operations::Mult:
|
||||
return sbe::value::genericMul(lhsTag, lhsValue, rhsTag, rhsValue);
|
||||
default:
|
||||
MONGO_UNREACHABLE;
|
||||
}
|
||||
};
|
||||
|
||||
auto [_, resultType, resultValue] =
|
||||
performOp(lhsTag, lhsValue, rhsTag, rhsValue);
|
||||
swapAndUpdate(rhs,
|
||||
optimizer::make<optimizer::Constant>(resultType, resultValue));
|
||||
}
|
||||
optimizer::ABT& lhs = *(it - 1);
|
||||
auto lhsConst = lhs.cast<optimizer::Constant>();
|
||||
|
||||
auto [lhsTag, lhsValue] = lhsConst->get();
|
||||
auto [rhsTag, rhsValue] = rhsConst->get();
|
||||
|
||||
auto [_, resultType, resultValue] =
|
||||
sbe::value::genericAdd(lhsTag, lhsValue, rhsTag, rhsValue);
|
||||
swapAndUpdate(rhs, optimizer::make<optimizer::Constant>(resultType, resultValue));
|
||||
args.erase(args.begin(), it - 1);
|
||||
}
|
||||
args.erase(args.begin(), it - 1);
|
||||
invariant(args.size() > 0);
|
||||
if (args.size() == 1) {
|
||||
swapAndUpdate(n, std::exchange(args[0], optimizer::make<optimizer::Blackhole>()));
|
||||
|
|
|
|||
|
|
@ -118,9 +118,6 @@ optimizer::ABT makeBinaryOp(optimizer::Operations binaryOp,
|
|||
optimizer::ABT makeNaryOp(optimizer::Operations op, optimizer::ABTVector args) {
|
||||
tassert(10199700, "Expected at least one argument", !args.empty());
|
||||
if (feature_flags::gFeatureFlagSbeUpgradeBinaryTrees.isEnabled()) {
|
||||
if (args.size() == 1) {
|
||||
return std::move(args[0]);
|
||||
}
|
||||
return optimizer::make<optimizer::NaryOp>(op, std::move(args));
|
||||
} else {
|
||||
return std::accumulate(
|
||||
|
|
|
|||
|
|
@ -2361,7 +2361,8 @@ public:
|
|||
auto arity = expr->getChildren().size();
|
||||
_context->ensureArity(arity);
|
||||
|
||||
if (arity < kArgumentCountForBinaryTree) {
|
||||
if (arity < kArgumentCountForBinaryTree ||
|
||||
feature_flags::gFeatureFlagSbeUpgradeBinaryTrees.isEnabled()) {
|
||||
visitFast(expr);
|
||||
return;
|
||||
}
|
||||
|
|
@ -2446,11 +2447,7 @@ public:
|
|||
makeBooleanOpTree(optimizer::Operations::Or, std::move(checkExprsNull));
|
||||
auto checkNumberAllArguments =
|
||||
makeBooleanOpTree(optimizer::Operations::And, std::move(checkExprsNumber));
|
||||
auto multiplication = std::accumulate(
|
||||
names.begin() + 1, names.end(), makeVariable(names.front()), [](auto&& acc, auto&& ex) {
|
||||
return optimizer::make<optimizer::BinaryOp>(
|
||||
optimizer::Operations::Mult, std::move(acc), makeVariable(ex));
|
||||
});
|
||||
auto multiplication = makeNaryOp(optimizer::Operations::Mult, std::move(variables));
|
||||
|
||||
auto multiplyExpr = buildABTMultiBranchConditionalFromCaseValuePairs(
|
||||
{ABTCaseValuePair{std::move(checkNullAnyArgument), optimizer::Constant::null()},
|
||||
|
|
|
|||
|
|
@ -121,6 +121,11 @@ TEST(SbeStageBuilderConstEvalTest, ConstEval) {
|
|||
tree = _nary("Add", "1"_cint64, "2"_cint64, "3"_cint64)._n;
|
||||
result = constEval(tree);
|
||||
ASSERT_EQ(result->getValueInt64(), 6);
|
||||
|
||||
// 2 * 3 * 4
|
||||
tree = _nary("Mult", "2"_cint64, "3"_cint64, "4"_cint64)._n;
|
||||
result = constEval(tree);
|
||||
ASSERT_EQ(result->getValueInt64(), 24);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -149,6 +154,11 @@ TEST(SbeStageBuilderConstEvalTest, ConstEval3) {
|
|||
tree = _nary("Add", "1.5"_cdouble, "0.5"_cdouble, "1.0"_cdouble)._n;
|
||||
result = constEval(tree);
|
||||
ASSERT_EQ(result->getValueDouble(), 3.0);
|
||||
|
||||
// 1.5 * 0.5 * 1.0
|
||||
tree = _nary("Mult", "1.5"_cdouble, "0.5"_cdouble, "1.0"_cdouble)._n;
|
||||
result = constEval(tree);
|
||||
ASSERT_EQ(result->getValueDouble(), 0.75);
|
||||
}
|
||||
|
||||
TEST(SbeStageBuilderConstEvalTest, ConstEval4) {
|
||||
|
|
@ -477,6 +487,61 @@ TEST(ConstEvalTest, NaryAddMultFold) {
|
|||
"| Variable [x]\n"
|
||||
"Const [3]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Add", "x"_var)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"Variable [x]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Mult", "1"_cint64, "x"_var, "y"_var, "z"_var)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"NaryOp [Mult]\n"
|
||||
"| | | Variable [z]\n"
|
||||
"| | Variable [y]\n"
|
||||
"| Variable [x]\n"
|
||||
"Const [1]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Mult", "1"_cint64, "2"_cint64, "y"_var, "z"_var)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"NaryOp [Mult]\n"
|
||||
"| | Variable [z]\n"
|
||||
"| Variable [y]\n"
|
||||
"Const [2]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Mult", "1"_cint64, "2"_cint64, "3"_cint64, "z"_var)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"NaryOp [Mult]\n"
|
||||
"| Variable [z]\n"
|
||||
"Const [6]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Mult", "1"_cint64, "2"_cint64, "3"_cint64, "4"_cint64)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"Const [24]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Mult", "1"_cint64, "2"_cint64, "x"_var, "3"_cint64, "4"_cint64)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"NaryOp [Mult]\n"
|
||||
"| | | Const [4]\n"
|
||||
"| | Const [3]\n"
|
||||
"| Variable [x]\n"
|
||||
"Const [2]\n",
|
||||
abt);
|
||||
|
||||
abt = _nary("Mult", "2"_cint64)._n;
|
||||
evaluator.optimize(abt);
|
||||
ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
"Const [2]\n",
|
||||
abt);
|
||||
}
|
||||
|
||||
TEST(ConstEvalTest, ConstantEquality) {
|
||||
|
|
|
|||
|
|
@ -445,5 +445,37 @@ TEST_F(AbtToSbeExpression, LowerNaryAdd) {
|
|||
ASSERT_EQ(sbe::value::bitcastTo<int64_t>(resultVal), 125);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AbtToSbeExpression, LowerNaryMult) {
|
||||
{
|
||||
auto tree = make<NaryOp>(Operations::Mult,
|
||||
ABTVector{Constant::int64(1),
|
||||
Constant::int64(5),
|
||||
Constant::int64(10),
|
||||
Constant::int64(20),
|
||||
Constant::int64(100)});
|
||||
|
||||
auto [resultTag, resultVal] = evalExpr(tree, boost::none);
|
||||
|
||||
ASSERT_EQ(sbe::value::TypeTags::NumberInt64, resultTag);
|
||||
ASSERT_EQ(sbe::value::bitcastTo<int64_t>(resultVal), 100000);
|
||||
}
|
||||
{
|
||||
auto tree = make<NaryOp>(
|
||||
Operations::Mult,
|
||||
ABTVector{make<BinaryOp>(Operations::Sub, Constant::int64(10), make<Variable>("var")),
|
||||
make<BinaryOp>(Operations::Sub, Constant::int64(20), make<Variable>("var")),
|
||||
make<BinaryOp>(Operations::Sub, Constant::int64(30), make<Variable>("var")),
|
||||
make<BinaryOp>(Operations::Sub, Constant::int64(40), make<Variable>("var")),
|
||||
make<BinaryOp>(Operations::Sub, Constant::int64(50), make<Variable>("var"))});
|
||||
|
||||
auto [resultTag, resultVal] =
|
||||
evalExpr(tree, std::pair{ProjectionName{"var"}, sbe::value::makeIntOrLong(5)});
|
||||
|
||||
ASSERT_EQ(sbe::value::TypeTags::NumberInt64, resultTag);
|
||||
ASSERT_EQ(sbe::value::bitcastTo<int64_t>(resultVal), 2953125);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo::stage_builder::abt
|
||||
|
|
|
|||
|
|
@ -526,5 +526,34 @@ TEST(TypeCheckerTest, TypeCheckNaryAdd) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(TypeCheckerTest, TypeCheckNaryMult) {
|
||||
{
|
||||
auto tree = make<NaryOp>(
|
||||
Operations::Mult,
|
||||
ABTVector{
|
||||
Constant::int32(1), Constant::int32(2), Constant::int32(3), Constant::int32(4)});
|
||||
auto signature = TypeChecker{}.typeCheck(tree);
|
||||
ASSERT_EQ(signature.typesMask, TypeSignature::kNumericType.typesMask);
|
||||
}
|
||||
{
|
||||
auto tree = make<NaryOp>(
|
||||
Operations::Mult,
|
||||
ABTVector{
|
||||
Constant::int32(1), Constant::int32(2), Constant::int32(3), Constant::nothing()});
|
||||
auto signature = TypeChecker{}.typeCheck(tree);
|
||||
ASSERT_EQ(signature.typesMask,
|
||||
(TypeSignature::kNothingType.include(TypeSignature::kNumericType)).typesMask);
|
||||
}
|
||||
{
|
||||
auto tree = make<NaryOp>(
|
||||
Operations::Mult,
|
||||
ABTVector{
|
||||
Constant::int32(1), Constant::int32(2), Constant::int32(3), make<Variable>("var")});
|
||||
auto signature = TypeChecker{}.typeCheck(tree);
|
||||
ASSERT_EQ(signature.typesMask,
|
||||
(TypeSignature::kNumericType.include(TypeSignature::kNothingType)).typesMask);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo::stage_builder
|
||||
|
|
|
|||
|
|
@ -4335,6 +4335,166 @@ TEST(VectorizerTest, ConvertMult) {
|
|||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationScalarScalar(opStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeBlocks = make<NaryOp>(
|
||||
op, ABTVector{make<Variable>("var1"), make<Variable>("var2"), make<Variable>("var3")});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var1"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
bindings.emplace(
|
||||
"var2"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
bindings.emplace(
|
||||
"var3"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeBlocks, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationBlockBlockBlock(fnStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeBlockScalarScalar = make<NaryOp>(
|
||||
op, ABTVector{make<Variable>("var"), Constant::int32(9), Constant::int32(20)});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeBlockScalarScalar, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationBlockScalarScalar(fnStr, opStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeScalarBlockScalar = make<NaryOp>(
|
||||
op, ABTVector{Constant::int32(9), make<Variable>("var"), Constant::int32(20)});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeScalarBlockScalar, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationScalarBlockScalar(fnStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeScalarScalarBlock = make<NaryOp>(
|
||||
op, ABTVector{Constant::int32(9), Constant::int32(20), make<Variable>("var")});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeScalarScalarBlock, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationScalarScalarBlock(fnStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeScalarScalarScalar = make<NaryOp>(
|
||||
op, ABTVector{Constant::int32(9), Constant::int32(20), Constant::int32(100)});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeScalarScalarScalar, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationScalarScalarScalar(opStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeBlockBlockScalar = make<NaryOp>(
|
||||
op, ABTVector{make<Variable>("var1"), make<Variable>("var2"), Constant::int32(9)});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
bindings.emplace(
|
||||
"var2"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeBlockBlockScalar, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationBlockBlockScalar(fnStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeScalarBlockBlock = make<NaryOp>(
|
||||
op, ABTVector{Constant::int32(9), make<Variable>("var1"), make<Variable>("var2")});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
bindings.emplace(
|
||||
"var2"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeScalarBlockBlock, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationScalarBlockBlock(fnStr, processed);
|
||||
}
|
||||
|
||||
{
|
||||
auto treeBlockScalarBlock = make<NaryOp>(
|
||||
op, ABTVector{make<Variable>("var1"), Constant::int32(9), make<Variable>("var2")});
|
||||
|
||||
Vectorizer::VariableTypes bindings;
|
||||
bindings.emplace(
|
||||
"var"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
bindings.emplace(
|
||||
"var2"_sd,
|
||||
std::make_pair(TypeSignature::kBlockType.include(TypeSignature::kAnyScalarType),
|
||||
boost::none));
|
||||
|
||||
sbe::value::FrameIdGenerator generator;
|
||||
auto processed = Vectorizer{&generator, Vectorizer::Purpose::Project}.vectorize(
|
||||
treeBlockScalarBlock, bindings, boost::none);
|
||||
|
||||
ASSERT_TRUE(processed.expr.has_value());
|
||||
assertArithmeticOperationBlockScalarBlock(fnStr, processed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(VectorizerTest, ConvertDiv) {
|
||||
|
|
|
|||
|
|
@ -397,6 +397,15 @@ TypeSignature TypeChecker::operator()(optimizer::ABT& n,
|
|||
}
|
||||
return TypeSignature::kNumericType.include(sig.intersect(TypeSignature::kDateTimeType))
|
||||
.include(sig.intersect(TypeSignature::kNothingType));
|
||||
} else if (op.op() == optimizer::Operations::Mult) {
|
||||
// The signature of the Mult is numeric plus Nothing.
|
||||
|
||||
TypeSignature sig = {};
|
||||
for (auto& node : op.nodes()) {
|
||||
TypeSignature nodeType = node.visit(*this, false);
|
||||
sig = sig.include(nodeType);
|
||||
}
|
||||
return TypeSignature::kNumericType.include(sig.intersect(TypeSignature::kNothingType));
|
||||
}
|
||||
return TypeSignature::kAnyScalarType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -521,6 +521,7 @@ Vectorizer::Tree Vectorizer::vectorizeNaryHelper(const optimizer::NaryOp& op, si
|
|||
case optimizer::Operations::Or:
|
||||
return vectorizeLogicalOp(op.op(), lhsNode, rhsNode);
|
||||
case optimizer::Operations::Add:
|
||||
case optimizer::Operations::Mult:
|
||||
return vectorizeArithmeticOp(op.op(), lhsNode, rhsNode);
|
||||
default:
|
||||
MONGO_UNREACHABLE;
|
||||
|
|
@ -531,7 +532,8 @@ Vectorizer::Tree Vectorizer::operator()(const optimizer::ABT& n, const optimizer
|
|||
switch (op.op()) {
|
||||
case optimizer::Operations::And:
|
||||
case optimizer::Operations::Or:
|
||||
case optimizer::Operations::Add: {
|
||||
case optimizer::Operations::Add:
|
||||
case optimizer::Operations::Mult: {
|
||||
return vectorizeNaryHelper(op, 0);
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# Golden test output of SBEPrimNaryTest/NaryMult
|
||||
-- INPUT EXPRESSION:
|
||||
(s1 * s2 * s3 * s4 * s5 * s6 * s7 * s8)
|
||||
|
||||
-- COMPILED EXPRESSION:
|
||||
[0x0000-0x005d] stackSize: 1, maxStackSize: 2
|
||||
0x0000: pushAccessVal(accessor: <accessor>);
|
||||
0x0009: pushAccessVal(accessor: <accessor>);
|
||||
0x0012: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
0x0015: pushAccessVal(accessor: <accessor>);
|
||||
0x001e: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
0x0021: pushAccessVal(accessor: <accessor>);
|
||||
0x002a: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
0x002d: pushAccessVal(accessor: <accessor>);
|
||||
0x0036: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
0x0039: pushAccessVal(accessor: <accessor>);
|
||||
0x0042: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
0x0045: pushAccessVal(accessor: <accessor>);
|
||||
0x004e: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
0x0051: pushAccessVal(accessor: <accessor>);
|
||||
0x005a: mul(popLhs: 1, moveFromLhs: 1, offsetLhs: 0, popRhs: 1, moveFromRhs: 1, offsetRhs: 0);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue