/** * Copyright (C) 2019-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include #include #include "mongo/base/compare_numbers.h" #include "mongo/config.h" #include "mongo/db/exec/sbe/makeobj_spec.h" #include "mongo/db/exec/sbe/values/slot.h" #include "mongo/db/exec/sbe/values/sort_spec.h" #include "mongo/db/exec/sbe/values/value.h" #include "mongo/db/exec/sbe/vm/datetime.h" #include "mongo/db/exec/sbe/vm/label.h" #include "mongo/db/query/collation/collator_interface.h" #include "mongo/db/query/datetime/date_time_support.h" #include #if !defined(MONGO_CONFIG_DEBUG_BUILD) #define MONGO_COMPILER_ALWAYS_INLINE_OPT MONGO_COMPILER_ALWAYS_INLINE #else #define MONGO_COMPILER_ALWAYS_INLINE_OPT #endif namespace mongo { namespace sbe { namespace vm { template std::pair genericCompare( value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue, const StringData::ComparatorInterface* comparator = nullptr, Op op = {}) { if (value::isNumber(lhsTag) && value::isNumber(rhsTag)) { switch (getWidestNumericalType(lhsTag, rhsTag)) { case value::TypeTags::NumberInt32: { auto result = op(value::numericCast(lhsTag, lhsValue), value::numericCast(rhsTag, rhsValue)); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } case value::TypeTags::NumberInt64: { auto result = op(value::numericCast(lhsTag, lhsValue), value::numericCast(rhsTag, rhsValue)); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } case value::TypeTags::NumberDouble: { auto result = [&]() { if (lhsTag == value::TypeTags::NumberInt64) { auto rhs = value::bitcastTo(rhsValue); if (std::isnan(rhs)) { return false; } return op(compareLongToDouble(value::bitcastTo(lhsValue), rhs), 0); } else if (rhsTag == value::TypeTags::NumberInt64) { auto lhs = value::bitcastTo(lhsValue); if (std::isnan(lhs)) { return false; } return op(compareDoubleToLong(lhs, value::bitcastTo(rhsValue)), 0); } else { return op(value::numericCast(lhsTag, lhsValue), value::numericCast(rhsTag, rhsValue)); } }(); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } case value::TypeTags::NumberDecimal: { auto result = [&]() { if (lhsTag == value::TypeTags::NumberDouble) { if (value::isNaN(lhsTag, lhsValue) || value::isNaN(rhsTag, rhsValue)) { return false; } return op(compareDoubleToDecimal(value::bitcastTo(lhsValue), value::bitcastTo(rhsValue)), 0); } else if (rhsTag == value::TypeTags::NumberDouble) { if (value::isNaN(lhsTag, lhsValue) || value::isNaN(rhsTag, rhsValue)) { return false; } return op(compareDecimalToDouble(value::bitcastTo(lhsValue), value::bitcastTo(rhsValue)), 0); } else { return op(value::numericCast(lhsTag, lhsValue), value::numericCast(rhsTag, rhsValue)); } }(); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } default: MONGO_UNREACHABLE; } } else if (isStringOrSymbol(lhsTag) && isStringOrSymbol(rhsTag)) { auto lhsStr = value::getStringOrSymbolView(lhsTag, lhsValue); auto rhsStr = value::getStringOrSymbolView(rhsTag, rhsValue); auto result = op(comparator ? comparator->compare(lhsStr, rhsStr) : lhsStr.compare(rhsStr), 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::Date && rhsTag == value::TypeTags::Date) { auto result = op(value::bitcastTo(lhsValue), value::bitcastTo(rhsValue)); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::Timestamp && rhsTag == value::TypeTags::Timestamp) { auto result = op(value::bitcastTo(lhsValue), value::bitcastTo(rhsValue)); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::Boolean && rhsTag == value::TypeTags::Boolean) { auto result = op(value::bitcastTo(lhsValue), value::bitcastTo(rhsValue)); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::Null && rhsTag == value::TypeTags::Null) { // This is where Mongo differs from SQL. auto result = op(0, 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::MinKey && rhsTag == value::TypeTags::MinKey) { auto result = op(0, 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::MaxKey && rhsTag == value::TypeTags::MaxKey) { auto result = op(0, 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if (lhsTag == value::TypeTags::bsonUndefined && rhsTag == value::TypeTags::bsonUndefined) { auto result = op(0, 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } else if ((value::isArray(lhsTag) && value::isArray(rhsTag)) || (value::isObject(lhsTag) && value::isObject(rhsTag)) || (value::isBinData(lhsTag) && value::isBinData(rhsTag))) { auto [tag, val] = value::compareValue(lhsTag, lhsValue, rhsTag, rhsValue, comparator); if (tag == value::TypeTags::NumberInt32) { auto result = op(value::bitcastTo(val), 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } } else if (isObjectId(lhsTag) && isObjectId(rhsTag)) { auto lhsObjId = lhsTag == value::TypeTags::ObjectId ? value::getObjectIdView(lhsValue)->data() : value::bitcastTo(lhsValue); auto rhsObjId = rhsTag == value::TypeTags::ObjectId ? value::getObjectIdView(rhsValue)->data() : value::bitcastTo(rhsValue); auto threeWayResult = memcmp(lhsObjId, rhsObjId, sizeof(value::ObjectIdType)); return {value::TypeTags::Boolean, value::bitcastFrom(op(threeWayResult, 0))}; } else if (lhsTag == value::TypeTags::bsonRegex && rhsTag == value::TypeTags::bsonRegex) { auto lhsRegex = value::getBsonRegexView(lhsValue); auto rhsRegex = value::getBsonRegexView(rhsValue); if (auto threeWayResult = lhsRegex.pattern.compare(rhsRegex.pattern); threeWayResult != 0) { return {value::TypeTags::Boolean, value::bitcastFrom(op(threeWayResult, 0))}; } auto threeWayResult = lhsRegex.flags.compare(rhsRegex.flags); return {value::TypeTags::Boolean, value::bitcastFrom(op(threeWayResult, 0))}; } else if (lhsTag == value::TypeTags::bsonDBPointer && rhsTag == value::TypeTags::bsonDBPointer) { auto lhsDBPtr = value::getBsonDBPointerView(lhsValue); auto rhsDBPtr = value::getBsonDBPointerView(rhsValue); if (lhsDBPtr.ns.size() != rhsDBPtr.ns.size()) { return {value::TypeTags::Boolean, value::bitcastFrom(op(lhsDBPtr.ns.size(), rhsDBPtr.ns.size()))}; } if (auto threeWayResult = lhsDBPtr.ns.compare(rhsDBPtr.ns); threeWayResult != 0) { return {value::TypeTags::Boolean, value::bitcastFrom(op(threeWayResult, 0))}; } auto threeWayResult = memcmp(lhsDBPtr.id, rhsDBPtr.id, sizeof(value::ObjectIdType)); return {value::TypeTags::Boolean, value::bitcastFrom(op(threeWayResult, 0))}; } else if (lhsTag == value::TypeTags::bsonJavascript && rhsTag == value::TypeTags::bsonJavascript) { auto lhsCode = value::getBsonJavascriptView(lhsValue); auto rhsCode = value::getBsonJavascriptView(rhsValue); return {value::TypeTags::Boolean, value::bitcastFrom(op(lhsCode.compare(rhsCode), 0))}; } else if (lhsTag == value::TypeTags::bsonCodeWScope && rhsTag == value::TypeTags::bsonCodeWScope) { auto lhsCws = value::getBsonCodeWScopeView(lhsValue); auto rhsCws = value::getBsonCodeWScopeView(rhsValue); if (auto threeWayResult = lhsCws.code.compare(rhsCws.code); threeWayResult != 0) { return {value::TypeTags::Boolean, value::bitcastFrom(op(threeWayResult, 0))}; } // Special string comparison semantics do not apply to strings nested inside the // CodeWScope scope object, so we do not pass through the string comparator. auto [tag, val] = value::compareValue(value::TypeTags::bsonObject, value::bitcastFrom(lhsCws.scope), value::TypeTags::bsonObject, value::bitcastFrom(rhsCws.scope)); if (tag == value::TypeTags::NumberInt32) { auto result = op(value::bitcastTo(val), 0); return {value::TypeTags::Boolean, value::bitcastFrom(result)}; } } return {value::TypeTags::Nothing, 0}; } template std::pair genericCompare(value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue, value::TypeTags collTag, value::Value collValue, Op op = {}) { if (collTag != value::TypeTags::collator) { return {value::TypeTags::Nothing, 0}; } auto comparator = static_cast(value::getCollatorView(collValue)); return genericCompare(lhsTag, lhsValue, rhsTag, rhsValue, comparator, op); } namespace { template T readFromMemory(const uint8_t* ptr) noexcept { static_assert(!IsEndian::value); T val; memcpy(&val, ptr, sizeof(T)); return val; } template size_t writeToMemory(uint8_t* ptr, const T val) noexcept { static_assert(!IsEndian::value); memcpy(ptr, &val, sizeof(T)); return sizeof(T); } } // namespace struct Instruction { enum Tags { pushConstVal, pushAccessVal, pushOwnedAccessorVal, pushEnvAccessorVal, pushMoveVal, pushLocalVal, pushMoveLocalVal, pushLocalLambda, pop, swap, add, sub, mul, div, idiv, mod, negate, numConvert, logicNot, less, lessEq, greater, greaterEq, eq, neq, // 3 way comparison (spaceship) with bson woCompare semantics. cmp3w, // collation-aware comparison instructions collLess, collLessEq, collGreater, collGreaterEq, collEq, collNeq, collCmp3w, fillEmpty, fillEmptyImm, getField, getFieldImm, getElement, collComparisonKey, getFieldOrElement, traverseP, // traverse projection paths traversePImm, traverseF, // traverse filter paths traverseFImm, // Iterates over values in column index cells. Skips values from nested arrays. traverseCsiCellValues, // Iterates the column index cell and returns values representing the types of cell's // content, including arrays and nested objects. Skips contents of nested arrays. traverseCsiCellTypes, setField, getArraySize, // number of elements aggSum, aggMin, aggMax, aggFirst, aggLast, aggCollMin, aggCollMax, exists, isNull, isObject, isArray, isString, isNumber, isBinData, isDate, isNaN, isInfinity, isRecordId, isMinKey, isMaxKey, isTimestamp, typeMatchImm, function, functionSmall, jmp, // offset is calculated from the end of instruction jmpTrue, jmpFalse, jmpNothing, jmpNotNothing, ret, // used only by simple local lambdas allocStack, fail, dateTruncImm, lastInstruction // this is just a marker used to calculate number of instructions }; enum Constants : uint8_t { Nothing, Null, False, True, Int32One, }; constexpr static size_t kMaxInlineStringSize = 256; /** * An instruction parameter descriptor. Values (instruction arguments) live on the VM stack and * the descriptor tells where to find it. The position on the stack is expressed as an offset * from the top of stack. * Optionally, an instruction can "consume" the value by popping the stack. All non-named * temporaries are popped after the use. Naturally, only the top of stack (offset 0) can be * popped. We do not support an arbitrary erasure from the middle of stack. */ struct Parameter { int variable{0}; boost::optional frameId; // Get the size in bytes of an instruction parameter encoded in byte code. size_t size() const noexcept { return sizeof(bool) + (frameId ? sizeof(int) : 0); } MONGO_COMPILER_ALWAYS_INLINE_OPT static std::pair decodeParam(const uint8_t*& pcPointer) noexcept { auto pop = readFromMemory(pcPointer); pcPointer += sizeof(pop); int offset = 0; if (!pop) { offset = readFromMemory(pcPointer); pcPointer += sizeof(offset); } return {pop, offset}; } }; static const char* toStringConstants(Constants k) { switch (k) { case Nothing: return "Nothing"; case Null: return "Null"; case True: return "True"; case False: return "False"; case Int32One: return "1"; default: return "unknown"; } } // Make sure that values in this arrays are always in-sync with the enum. static int stackOffset[]; uint8_t tag; const char* toString() const { switch (tag) { case pushConstVal: return "pushConstVal"; case pushAccessVal: return "pushAccessVal"; case pushOwnedAccessorVal: return "pushOwnedAccessorVal"; case pushEnvAccessorVal: return "pushEnvAccessorVal"; case pushMoveVal: return "pushMoveVal"; case pushLocalVal: return "pushLocalVal"; case pushMoveLocalVal: return "pushMoveLocalVal"; case pushLocalLambda: return "pushLocalLambda"; case pop: return "pop"; case swap: return "swap"; case add: return "add"; case sub: return "sub"; case mul: return "mul"; case div: return "div"; case idiv: return "idiv"; case mod: return "mod"; case negate: return "negate"; case numConvert: return "numConvert"; case logicNot: return "logicNot"; case less: return "less"; case lessEq: return "lessEq"; case greater: return "greater"; case greaterEq: return "greaterEq"; case eq: return "eq"; case neq: return "neq"; case cmp3w: return "cmp3w"; case collLess: return "collLess"; case collLessEq: return "collLessEq"; case collGreater: return "collGreater"; case collGreaterEq: return "collGreaterEq"; case collEq: return "collEq"; case collNeq: return "collNeq"; case collCmp3w: return "collCmp3w"; case fillEmpty: return "fillEmpty"; case fillEmptyImm: return "fillEmptyImm"; case getField: return "getField"; case getFieldImm: return "getFieldImm"; case getElement: return "getElement"; case collComparisonKey: return "collComparisonKey"; case getFieldOrElement: return "getFieldOrElement"; case traverseP: return "traverseP"; case traversePImm: return "traversePImm"; case traverseF: return "traverseF"; case traverseFImm: return "traverseFImm"; case traverseCsiCellValues: return "traverseCsiCellValues"; case traverseCsiCellTypes: return "traverseCsiCellTypes"; case setField: return "setField"; case getArraySize: return "getArraySize"; case aggSum: return "aggSum"; case aggMin: return "aggMin"; case aggMax: return "aggMax"; case aggFirst: return "aggFirst"; case aggLast: return "aggLast"; case aggCollMin: return "aggCollMin"; case aggCollMax: return "aggCollMax"; case exists: return "exists"; case isNull: return "isNull"; case isObject: return "isObject"; case isArray: return "isArray"; case isString: return "isString"; case isNumber: return "isNumber"; case isBinData: return "isBinData"; case isDate: return "isDate"; case isNaN: return "isNaN"; case isInfinity: return "isInfinity"; case isRecordId: return "isRecordId"; case isMinKey: return "isMinKey"; case isMaxKey: return "isMaxKey"; case isTimestamp: return "isTimestamp"; case typeMatchImm: return "typeMatchImm"; case function: return "function"; case functionSmall: return "functionSmall"; case jmp: return "jmp"; case jmpTrue: return "jmpTrue"; case jmpFalse: return "jmpFalse"; case jmpNothing: return "jmpNothing"; case jmpNotNothing: return "jmpNotNothing"; case ret: return "ret"; case allocStack: return "allocStack"; case fail: return "fail"; case dateTruncImm: return "dateTruncImm"; default: return "unrecognized"; } } }; static_assert(sizeof(Instruction) == sizeof(uint8_t)); enum class Builtin : uint8_t { split, regexMatch, replaceOne, dateDiff, dateParts, dateToParts, isoDateToParts, dayOfYear, dayOfMonth, dayOfWeek, datePartsWeekYear, dateToString, dateFromString, dateFromStringNoThrow, dropFields, newArray, keepFields, newArrayFromRange, newObj, ksToString, // KeyString to string newKs, // new KeyString collNewKs, // new KeyString (with collation) abs, // absolute value ceil, floor, trunc, exp, ln, log10, sqrt, addToArray, // agg function to append to an array addToArrayCapped, // agg function to append to an array, fails when the array reaches specified // size mergeObjects, // agg function to merge BSON documents addToSet, // agg function to append to a set addToSetCapped, // agg function to append to a set, fails when the set reaches specified size collAddToSet, // agg function to append to a set (with collation) collAddToSetCapped, // agg function to append to a set (with collation), fails when the set // reaches specified size // Special double summation. doubleDoubleSum, // A variant of the standard sum aggregate function which maintains a DoubleDouble as the // accumulator's underlying state. aggDoubleDoubleSum, // Converts a DoubleDouble sum into a single numeric scalar for use once the summation is // complete. doubleDoubleSumFinalize, // Converts a partial sum into a format suitable for serialization over the wire to the merging // node. The merging node expects the internal state of the DoubleDouble summation to be // serialized in a particular format. doubleDoublePartialSumFinalize, // An agg function which can be used to sum a sequence of DoubleDouble inputs, producing the // resulting total as a DoubleDouble. aggMergeDoubleDoubleSums, // Implements Welford's online algorithm for computing sample or population standard deviation // in a single pass. aggStdDev, // Combines standard deviations that have been partially computed on a subset of the data // using Welford's online algorithm. aggMergeStdDevs, stdDevPopFinalize, stdDevSampFinalize, bitTestZero, // test bitwise mask & value is zero bitTestMask, // test bitwise mask & value is mask bitTestPosition, // test BinData with a bit position list bsonSize, // implements $bsonSize toUpper, toLower, coerceToBool, coerceToString, concat, concatArrays, // Agg function to concatenate arrays, failing when the accumulator reaches a specified size. aggConcatArraysCapped, // Agg functions to compute the set union of two arrays, failing when the accumulator reaches a // specified size. aggSetUnionCapped, aggCollSetUnionCapped, // Agg function for a simple set union (with no size cap or collation). aggSetUnion, acos, acosh, asin, asinh, atan, atanh, atan2, cos, cosh, degreesToRadians, radiansToDegrees, sin, sinh, tan, tanh, round, isMember, collIsMember, indexOfBytes, indexOfCP, isDayOfWeek, isTimeUnit, isTimezone, isValidToStringFormat, validateFromStringFormat, setUnion, setIntersection, setDifference, setEquals, collSetUnion, collSetIntersection, collSetDifference, collSetEquals, runJsPredicate, regexCompile, // compile into value::pcreRegex regexFind, regexFindAll, shardFilter, shardHash, extractSubArray, isArrayEmpty, reverseArray, sortArray, dateAdd, hasNullBytes, getRegexPattern, getRegexFlags, hash, ftsMatch, generateSortKey, generateCheapSortKey, sortKeyComponentVectorGetElement, sortKeyComponentVectorToArray, makeBsonObj, tsSecond, tsIncrement, typeMatch, dateTrunc, internalLeast, // helper functions for computation of sort keys internalGreatest, // helper functions for computation of sort keys year, month, hour, minute, second, millisecond, week, isoWeekYear, isoDayOfWeek, isoWeek, objectToArray, arrayToObject, aggFirstN, aggFirstNMerge, aggFirstNFinalize, aggLastN, aggLastNMerge, aggLastNFinalize, aggTopN, aggTopNMerge, aggTopNFinalize, aggBottomN, aggBottomNMerge, aggBottomNFinalize, aggMaxN, aggMaxNMerge, aggMaxNFinalize, aggMinN, aggMinNMerge, aggMinNFinalize, }; std::string builtinToString(Builtin b); /** * This enum defines indices into an 'Array' that store state for $AccumulatorN expressions. * * The array might contain up to four elements: * - The element at index `kInternalArr` is the array that holds the values. * - The element at index `kStartIdx` is the logical start index in the internal array. This is * used for emulating queue behaviour. * - The element at index `kMaxSize` is the maximum number entries the data structure holds. * - The element at index `kMemUsage` holds the current memory usage * - The element at index `kMemLimit` holds the max memory limit allowed */ enum class AggMultiElems { kInternalArr, kStartIdx, kMaxSize, kMemUsage, kMemLimit, kSizeOfArray }; /** * Less than comparison based on a sort pattern. */ struct SortPatternLess { SortPatternLess(const value::SortSpec* sortSpec) : _sortSpec(sortSpec) {} bool operator()(const std::pair& lhs, const std::pair& rhs) const { auto [cmpTag, cmpVal] = _sortSpec->compare(lhs.first, lhs.second, rhs.first, rhs.second); uassert(5807000, "Invalid comparison result", cmpTag == value::TypeTags::NumberInt32); return value::bitcastTo(cmpVal) < 0; } private: const value::SortSpec* _sortSpec; }; /** * Greater than comparison based on a sort pattern. */ struct SortPatternGreater { SortPatternGreater(const value::SortSpec* sortSpec) : _sortSpec(sortSpec) {} bool operator()(const std::pair& lhs, const std::pair& rhs) const { auto [cmpTag, cmpVal] = _sortSpec->compare(lhs.first, lhs.second, rhs.first, rhs.second); uassert(5807001, "Invalid comparison result", cmpTag == value::TypeTags::NumberInt32); return value::bitcastTo(cmpVal) > 0; } private: const value::SortSpec* _sortSpec; }; /** * Comparison based on the key of a pair of elements. */ template struct PairKeyComp { PairKeyComp(const Comp& comp) : _comp(comp) {} bool operator()(const std::pair& lhs, const std::pair& rhs) const { auto [lPairTag, lPairVal] = lhs; auto lPair = value::getArrayView(lPairVal); auto lKey = lPair->getAt(0); auto [rPairTag, rPairVal] = rhs; auto rPair = value::getArrayView(rPairVal); auto rKey = rPair->getAt(0); return _comp(lKey, rKey); } private: const Comp _comp; }; template struct ValueCompare { ValueCompare(const CollatorInterface* collator) : _collator(collator) {} bool operator()(const std::pair& lhs, const std::pair& rhs) const { auto [tag, val] = value::compareValue(lhs.first, lhs.second, rhs.first, rhs.second, _collator); uassert(7548805, "Invalid comparison result", tag == value::TypeTags::NumberInt32); if constexpr (less) { return value::bitcastTo(val) < 0; } else { return value::bitcastTo(val) > 0; } } private: const CollatorInterface* _collator; }; /** * This enum defines indices into an 'Array' that returns the partial sum result when 'needsMerge' * is requested. * * See 'builtinDoubleDoubleSumFinalize()' for more details. */ enum class AggPartialSumElems { kTotal, kError, kSizeOfArray }; /** * This enum defines indices into an 'Array' that accumulates $stdDevPop and $stdDevSamp results. * * The array contains 3 elements: * - The element at index `kCount` keeps track of the total number of values processd * - The elements at index `kRunningMean` keeps track of the mean of all the values that have been * processed. * - The elements at index `kRunningM2` keeps track of running M2 value (defined within: * https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) * for all the values that have been processed. * * See 'aggStdDevImpl()'/'aggStdDev()'/'stdDevPopFinalize() / stdDevSampFinalize()' for more * details. */ enum AggStdDevValueElems { kCount, kRunningMean, kRunningM2, // This is actually not an index but represents the number of elements stored kSizeOfArray }; /** * This enum defines indices into an 'Array' that returns the result of accumulators that track the * size of accumulated values, such as 'addToArrayCapped' and 'addToSetCapped'. */ enum class AggArrayWithSize { kValues = 0, kSizeOfValues, kLast = kSizeOfValues + 1 }; using SmallArityType = uint8_t; using ArityType = uint32_t; class CodeFragment { public: const auto& frames() const { return _frames; } auto& instrs() { return _instrs; } const auto& instrs() const { return _instrs; } auto stackSize() const { return _stackSize; } auto maxStackSize() const { return _maxStackSize; } void append(CodeFragment&& code); void appendNoStack(CodeFragment&& code); void append(CodeFragment&& lhs, CodeFragment&& rhs); void appendConstVal(value::TypeTags tag, value::Value val); void appendAccessVal(value::SlotAccessor* accessor); void appendMoveVal(value::SlotAccessor* accessor); void appendLocalVal(FrameId frameId, int variable, bool moveFrom); void appendLocalLambda(int codePosition); void appendPop(); void appendSwap(); void appendAdd(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendSub(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendMul(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendDiv(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendIDiv(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendMod(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendNegate(Instruction::Parameter input); void appendNot(Instruction::Parameter input); void appendLess(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendLessEq(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendGreater(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendGreaterEq(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendEq(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendNeq(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendCmp3w(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendCollLess(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendCollLessEq(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendCollGreater(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendCollGreaterEq(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendCollEq(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendCollNeq(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendCollCmp3w(Instruction::Parameter lhs, Instruction::Parameter rhs, Instruction::Parameter collator); void appendFillEmpty(); void appendFillEmpty(Instruction::Constants k); void appendGetField(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendGetField(Instruction::Parameter input, StringData fieldName); void appendGetElement(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendCollComparisonKey(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendGetFieldOrElement(Instruction::Parameter lhs, Instruction::Parameter rhs); void appendTraverseP(); void appendTraverseP(int codePosition, Instruction::Constants k); void appendTraverseF(); void appendTraverseF(int codePosition, Instruction::Constants k); void appendTraverseCellValues(); void appendTraverseCellValues(int codePosition); void appendTraverseCellTypes(); void appendTraverseCellTypes(int codePosition); void appendSetField(); void appendGetArraySize(Instruction::Parameter input); void appendDateTrunc(TimeUnit unit, int64_t binSize, TimeZone timezone, DayOfWeek startOfWeek); void appendSum(); void appendMin(); void appendMax(); void appendFirst(); void appendLast(); void appendCollMin(); void appendCollMax(); void appendExists(Instruction::Parameter input); void appendIsNull(Instruction::Parameter input); void appendIsObject(Instruction::Parameter input); void appendIsArray(Instruction::Parameter input); void appendIsString(Instruction::Parameter input); void appendIsNumber(Instruction::Parameter input); void appendIsBinData(Instruction::Parameter input); void appendIsDate(Instruction::Parameter input); void appendIsNaN(Instruction::Parameter input); void appendIsInfinity(Instruction::Parameter input); void appendIsRecordId(Instruction::Parameter input); void appendIsMinKey(Instruction::Parameter input); void appendIsMaxKey(Instruction::Parameter input); void appendIsTimestamp(Instruction::Parameter input); void appendTypeMatch(Instruction::Parameter input, uint32_t mask); void appendFunction(Builtin f, ArityType arity); void appendLabelJump(LabelId labelId); void appendLabelJumpTrue(LabelId labelId); void appendLabelJumpFalse(LabelId labelId); void appendLabelJumpNothing(LabelId labelId); void appendLabelJumpNotNothing(LabelId labelId); void appendRet(); void appendAllocStack(uint32_t size); void appendFail(); void appendNumericConvert(value::TypeTags targetTag); // For printing from an interactive debugger. std::string toString() const; // Declares and defines a local variable frame at the current depth. // Local frame declaration is used to resolve the stack offsets of local variable access. // All references local variables must have matching frame declaration. The // variable reference and frame declaration is allowed to happen in any order. void declareFrame(FrameId frameId); // Declares and defines a local variable frame at the current stack depth modifies by the given // offset. void declareFrame(FrameId frameId, int stackOffset); // Removes the frame from scope. The frame must have no outstanding fixups. // That is: must be declared or never referenced. void removeFrame(FrameId frameId); // Returns whether the are any frames currently in scope. bool hasFrames() const; // Associates the current code position with a label. void appendLabel(LabelId labelId); // Removes the label from scope. The label must have no outstanding fixups. // That is: must be associated with code position or never referenced. void removeLabel(LabelId labelId); void validate(); private: // Adjusts all the stack offsets in the outstanding fixups by the provided delta as follows: for // a given 'stackOffsetDelta' of frames in this CodeFragment: // 1. Adds this delta to the 'stackPosition' of all frames having a defined stack position. // 2. Adds this delta to all uses of frame stack posn's in code (located at 'fixupOffset's). // The net effect is to change the stack offsets of all frames with defined stack positions and // all code references to frame offsets in this CodeFragment by 'stackOffsetDelta'. void fixupStackOffsets(int stackOffsetDelta); // Stores the fixup information for stack frames. // fixupOffsets - byte offsets in the code where the stack depth of the frame was used and need // fixup. // stackPosition - stack depth in elements of where the frame was declared, or kPositionNotSet // if not known yet. struct FrameInfo { static constexpr int64_t kPositionNotSet = std::numeric_limits::min(); absl::InlinedVector fixupOffsets; int64_t stackPosition{kPositionNotSet}; }; // Stores the fixup information for labels. // fixupOffsets - offsets in the code where the label was used and need fixup. // definitionOffset - offset in the code where label was defined. struct LabelInfo { static constexpr int64_t kOffsetNotSet = std::numeric_limits::min(); absl::InlinedVector fixupOffsets; int64_t definitionOffset{kOffsetNotSet}; }; template void appendSimpleInstruction(Instruction::Tags tag, Ts&&... params); void appendLabelJumpInstruction(LabelId labelId, Instruction::Tags tag); auto allocateSpace(size_t size) { auto oldSize = _instrs.size(); _instrs.resize(oldSize + size); return _instrs.data() + oldSize; } template void adjustStackSimple(const Instruction& i, Ts&&... params); void copyCodeAndFixup(CodeFragment&& from); template size_t appendParameters(uint8_t* ptr, Ts&&... params); size_t appendParameter(uint8_t* ptr, Instruction::Parameter param, int& popCompensation); // Convert a variable index to a stack offset. constexpr int varToOffset(int var) const { return -var - 1; } // Returns the frame with ID 'frameId' if it already exists, else creates and returns it. FrameInfo& getOrDeclareFrame(FrameId frameId); // For a given 'frame' in this CodeFragment, subtracts the frame's 'stackPosition' from all the // refs to this frame in code (located at 'fixupOffset's). This is done once the true stack // position of the frame is known, so code refs point to the correct location in the frame. void fixupFrame(FrameInfo& frame); LabelInfo& getOrDeclareLabel(LabelId labelId); void fixupLabel(LabelInfo& label); // The sequence of byte code instructions this CodeFragment represents. absl::InlinedVector _instrs; // A collection of frame information for local variables. // Variables can be declared or referenced out of order and at the time of variable reference // it may not be known the relative stack offset of variable declaration w.r.t to its use. // This tracks both declaration info (stack depth) and use info (code offset). // When code is concatenated the offsets are adjusted if needed and when declaration stack depth // becomes known all fixups are resolved. absl::flat_hash_map _frames; // A collection of label information for labels that are currently in scope. // Labels can be defined or referenced out of order and at at time of label reference (e.g: // jumps or lambda creation), the exact relative offset may not be yet known. // This tracks both label definition (code offset where label is defined) and use info for jumps // or lambdas (code offset). When code is concatenated the offsets are adjusted, if needed, and // when label definition offset becomes known all fixups are resolved. absl::flat_hash_map _labels; // Delta number of '_argStack' entries effect of this CodeFragment; may be negative. int64_t _stackSize{0}; // Maximum absolute number of entries in '_argStack' from this CodeFragment. int64_t _maxStackSize{0}; }; class ByteCode { // The number of bytes per stack entry. static constexpr size_t sizeOfElement = sizeof(bool) + sizeof(value::TypeTags) + sizeof(value::Value); static_assert(sizeOfElement == 10); static_assert(std::is_trivially_copyable_v>); public: ByteCode() { _argStack = reinterpret_cast(mongoMalloc(sizeOfElement * 4)); _argStackEnd = _argStack + sizeOfElement * 4; _argStackTop = _argStack - sizeOfElement; } ~ByteCode() { std::free(_argStack); } ByteCode(const ByteCode&) = delete; ByteCode& operator=(const ByteCode&) = delete; FastTuple run(const CodeFragment* code); bool runPredicate(const CodeFragment* code); private: void runInternal(const CodeFragment* code, int64_t position); void runLambdaInternal(const CodeFragment* code, int64_t position); MONGO_COMPILER_NORETURN void runFailInstruction(); template void runTagCheck(const uint8_t*& pcPointer, T&& predicate); void runTagCheck(const uint8_t*& pcPointer, value::TypeTags tagRhs); MONGO_COMPILER_ALWAYS_INLINE static std::pair decodeParam(const uint8_t*& pcPointer) noexcept { return Instruction::Parameter::decodeParam(pcPointer); } FastTuple genericDiv(value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue); FastTuple genericIDiv(value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue); FastTuple genericMod(value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue); FastTuple genericAbs(value::TypeTags operandTag, value::Value operandValue); FastTuple genericCeil(value::TypeTags operandTag, value::Value operandValue); FastTuple genericFloor(value::TypeTags operandTag, value::Value operandValue); FastTuple genericExp(value::TypeTags operandTag, value::Value operandValue); FastTuple genericLn(value::TypeTags operandTag, value::Value operandValue); FastTuple genericLog10(value::TypeTags operandTag, value::Value operandValue); FastTuple genericSqrt(value::TypeTags operandTag, value::Value operandValue); FastTuple genericRoundTrunc( std::string funcName, Decimal128::RoundingMode roundingMode, ArityType arity); std::pair genericNot(value::TypeTags tag, value::Value value); std::pair genericIsMember(value::TypeTags lhsTag, value::Value lhsVal, value::TypeTags rhsTag, value::Value rhsVal, CollatorInterface* collator = nullptr); std::pair genericIsMember(value::TypeTags lhsTag, value::Value lhsVal, value::TypeTags rhsTag, value::Value rhsVal, value::TypeTags collTag, value::Value collVal); std::pair compare3way( value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue, const StringData::ComparatorInterface* comparator = nullptr); std::pair compare3way(value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue, value::TypeTags collTag, value::Value collValue); FastTuple getField(value::TypeTags objTag, value::Value objValue, value::TypeTags fieldTag, value::Value fieldValue); FastTuple getField(value::TypeTags objTag, value::Value objValue, StringData fieldStr); FastTuple getElement(value::TypeTags objTag, value::Value objValue, value::TypeTags fieldTag, value::Value fieldValue); FastTuple getFieldOrElement(value::TypeTags objTag, value::Value objValue, value::TypeTags fieldTag, value::Value fieldValue); void traverseP(const CodeFragment* code); void traverseP(const CodeFragment* code, int64_t position, int64_t maxDepth); void traverseP_nested(const CodeFragment* code, int64_t position, value::TypeTags tag, value::Value val, int64_t maxDepth); void traverseF(const CodeFragment* code); void traverseF(const CodeFragment* code, int64_t position, bool compareArray); void traverseFInArray(const CodeFragment* code, int64_t position, bool compareArray); bool runLambdaPredicate(const CodeFragment* code, int64_t position); void traverseCsiCellValues(const CodeFragment* code, int64_t position); void traverseCsiCellTypes(const CodeFragment* code, int64_t position); FastTuple setField(); FastTuple getArraySize(value::TypeTags tag, value::Value val); FastTuple aggSum(value::TypeTags accTag, value::Value accValue, value::TypeTags fieldTag, value::Value fieldValue); void aggDoubleDoubleSumImpl(value::Array* accumulator, value::TypeTags rhsTag, value::Value rhsValue); void aggMergeDoubleDoubleSumsImpl(value::Array* accumulator, value::TypeTags rhsTag, value::Value rhsValue); // This is an implementation of the following algorithm: // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm void aggStdDevImpl(value::Array* accumulator, value::TypeTags rhsTag, value::Value rhsValue); void aggMergeStdDevsImpl(value::Array* accumulator, value::TypeTags rhsTag, value::Value rhsValue); FastTuple aggStdDevFinalizeImpl(value::Value fieldValue, bool isSamp); FastTuple aggMin(value::TypeTags accTag, value::Value accValue, value::TypeTags fieldTag, value::Value fieldValue, CollatorInterface* collator = nullptr); FastTuple aggMax(value::TypeTags accTag, value::Value accValue, value::TypeTags fieldTag, value::Value fieldValue, CollatorInterface* collator = nullptr); FastTuple aggFirst(value::TypeTags accTag, value::Value accValue, value::TypeTags fieldTag, value::Value fieldValue); FastTuple aggLast(value::TypeTags accTag, value::Value accValue, value::TypeTags fieldTag, value::Value fieldValue); FastTuple genericAcos(value::TypeTags operandTag, value::Value operandValue); FastTuple genericAcosh(value::TypeTags operandTag, value::Value operandValue); FastTuple genericAsin(value::TypeTags operandTag, value::Value operandValue); FastTuple genericAsinh(value::TypeTags operandTag, value::Value operandValue); FastTuple genericAtan(value::TypeTags operandTag, value::Value operandValue); FastTuple genericAtanh(value::TypeTags operandTag, value::Value operandValue); FastTuple genericAtan2(value::TypeTags operandTag1, value::Value operandValue1, value::TypeTags operandTag2, value::Value operandValue2); FastTuple genericCos(value::TypeTags operandTag, value::Value operandValue); FastTuple genericCosh(value::TypeTags operandTag, value::Value operandValue); FastTuple genericDegreesToRadians( value::TypeTags operandTag, value::Value operandValue); FastTuple genericRadiansToDegrees( value::TypeTags operandTag, value::Value operandValue); FastTuple genericSin(value::TypeTags operandTag, value::Value operandValue); FastTuple genericSinh(value::TypeTags operandTag, value::Value operandValue); FastTuple genericTan(value::TypeTags operandTag, value::Value operandValue); FastTuple genericTanh(value::TypeTags operandTag, value::Value operandValue); FastTuple genericDayOfYear(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericDayOfYear(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericDayOfMonth(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericDayOfMonth(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericDayOfWeek(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericDayOfWeek(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericYear(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericYear(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericMonth(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericMonth(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericHour(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericHour(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericMinute(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericMinute(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericSecond(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericSecond(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericMillisecond(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericMillisecond(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericWeek(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericWeek(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericISOWeekYear(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericISOWeekYear(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericISODayOfWeek( value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericISODayOfWeek(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericISOWeek(value::TypeTags timezoneDBTag, value::Value timezoneDBValue, value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericISOWeek(value::TypeTags dateTag, value::Value dateValue, value::TypeTags timezoneTag, value::Value timezoneValue); FastTuple genericNewKeyString( ArityType arity, CollatorInterface* collator = nullptr); FastTuple dateTrunc(value::TypeTags dateTag, value::Value dateValue, TimeUnit unit, int64_t binSize, TimeZone timezone, DayOfWeek startOfWeek); /** * produceBsonObject() takes a MakeObjSpec ('spec'), a root value ('rootTag' and 'rootVal'), * and 0 or more "computed" values as inputs, it builds an output BSON object based on the * instructions provided by 'spec' and based on the contents of 'root' and the computed input * values, and then it returns the output object. (Note the computed input values are not * directly passed in as C++ parameters -- instead the computed input values are passed via * the VM's stack.) * * 'spec' provides two lists of field names: "keepOrDrop" fields and "computed" fields. These * lists are disjoint and do not contain duplicates. The number of computed input values passed * in by the caller on the VM stack must match the number of fields in the "computed" list. * * For each field F in the "computed" list, this method will retrieve the corresponding computed * input value V from the VM stack and add {F,V} to the output object. * * If 'root' is not an object, it is ignored. Otherwise, for each field F in 'root' with value V * that does not appear in the "computed" list, this method will copy {F,V} to the output object * if either: (1) field F appears in the "keepOrDrop" list and 'spec->fieldBehavior == keep'; or * (2) field F does _not_ appear in the "keepOrDrop" list and 'spec->fieldBehavior == drop'. If * neither of these conditions are met, field F in 'root' will be ignored. * * For any two distinct fields F1 and F2 in the output object, if F1 is in 'root' and F2 does * not appear before F1 in 'root', -OR- if both F1 and F2 are not in 'root' and F2 does not * appear before F1 in the "computed" list, then F2 will appear after F1 in the output object. */ std::pair produceBsonObject(const MakeObjSpec* mos, value::TypeTags rootTag, value::Value rootVal, int stackOffset); FastTuple builtinSplit(ArityType arity); FastTuple builtinDate(ArityType arity); FastTuple builtinDateWeekYear(ArityType arity); FastTuple builtinDateDiff(ArityType arity); FastTuple builtinDateToParts(ArityType arity); FastTuple builtinIsoDateToParts(ArityType arity); FastTuple builtinDayOfYear(ArityType arity); FastTuple builtinDayOfMonth(ArityType arity); FastTuple builtinDayOfWeek(ArityType arity); FastTuple builtinRegexMatch(ArityType arity); FastTuple builtinKeepFields(ArityType arity); FastTuple builtinReplaceOne(ArityType arity); FastTuple builtinDropFields(ArityType arity); FastTuple builtinNewArray(ArityType arity); FastTuple builtinNewArrayFromRange(ArityType arity); FastTuple builtinNewObj(ArityType arity); FastTuple builtinKeyStringToString(ArityType arity); FastTuple builtinNewKeyString(ArityType arity); FastTuple builtinCollNewKeyString(ArityType arity); FastTuple builtinAbs(ArityType arity); FastTuple builtinCeil(ArityType arity); FastTuple builtinFloor(ArityType arity); FastTuple builtinTrunc(ArityType arity); FastTuple builtinExp(ArityType arity); FastTuple builtinLn(ArityType arity); FastTuple builtinLog10(ArityType arity); FastTuple builtinSqrt(ArityType arity); FastTuple builtinAddToArray(ArityType arity); FastTuple builtinAddToArrayCapped(ArityType arity); FastTuple builtinMergeObjects(ArityType arity); FastTuple builtinAddToSet(ArityType arity); FastTuple builtinCollAddToSet(ArityType arity); FastTuple addToSetCappedImpl(value::TypeTags tagNewElem, value::Value valNewElem, int32_t sizeCap, CollatorInterface* collator); FastTuple builtinAddToSetCapped(ArityType arity); FastTuple builtinCollAddToSetCapped(ArityType arity); FastTuple builtinDoubleDoubleSum(ArityType arity); // The template parameter is false for a regular DoubleDouble summation and true if merging // partially computed DoubleDouble sums. template FastTuple builtinAggDoubleDoubleSum(ArityType arity); FastTuple builtinDoubleDoubleSumFinalize(ArityType arity); FastTuple builtinDoubleDoublePartialSumFinalize( ArityType arity); // The template parameter is false for a regular std dev and true if merging partially computed // standard devations. template FastTuple builtinAggStdDev(ArityType arity); FastTuple builtinStdDevPopFinalize(ArityType arity); FastTuple builtinStdDevSampFinalize(ArityType arity); FastTuple builtinBitTestZero(ArityType arity); FastTuple builtinBitTestMask(ArityType arity); FastTuple builtinBitTestPosition(ArityType arity); FastTuple builtinBsonSize(ArityType arity); FastTuple builtinToUpper(ArityType arity); FastTuple builtinToLower(ArityType arity); FastTuple builtinCoerceToBool(ArityType arity); FastTuple builtinCoerceToString(ArityType arity); FastTuple builtinAcos(ArityType arity); FastTuple builtinAcosh(ArityType arity); FastTuple builtinAsin(ArityType arity); FastTuple builtinAsinh(ArityType arity); FastTuple builtinAtan(ArityType arity); FastTuple builtinAtanh(ArityType arity); FastTuple builtinAtan2(ArityType arity); FastTuple builtinCos(ArityType arity); FastTuple builtinCosh(ArityType arity); FastTuple builtinDegreesToRadians(ArityType arity); FastTuple builtinRadiansToDegrees(ArityType arity); FastTuple builtinSin(ArityType arity); FastTuple builtinSinh(ArityType arity); FastTuple builtinTan(ArityType arity); FastTuple builtinTanh(ArityType arity); FastTuple builtinRound(ArityType arity); FastTuple builtinConcat(ArityType arity); FastTuple builtinConcatArrays(ArityType arity); FastTuple builtinAggConcatArraysCapped(ArityType arity); FastTuple builtinAggSetUnion(ArityType arity); FastTuple builtinAggSetUnionCapped(ArityType arity); FastTuple builtinAggCollSetUnionCapped(ArityType arity); FastTuple aggSetUnionCappedImpl( value::TypeTags tagNewElem, value::Value valNewElem, int32_t sizeCap, CollatorInterface* collator); FastTuple builtinIsMember(ArityType arity); FastTuple builtinCollIsMember(ArityType arity); FastTuple builtinIndexOfBytes(ArityType arity); FastTuple builtinIndexOfCP(ArityType arity); FastTuple builtinIsDayOfWeek(ArityType arity); FastTuple builtinIsTimeUnit(ArityType arity); FastTuple builtinIsTimezone(ArityType arity); FastTuple builtinIsValidToStringFormat(ArityType arity); FastTuple builtinValidateFromStringFormat(ArityType arity); FastTuple builtinSetUnion(ArityType arity); FastTuple builtinSetIntersection(ArityType arity); FastTuple builtinSetDifference(ArityType arity); FastTuple builtinSetEquals(ArityType arity); FastTuple builtinCollSetUnion(ArityType arity); FastTuple builtinCollSetIntersection(ArityType arity); FastTuple builtinCollSetDifference(ArityType arity); FastTuple builtinCollSetEquals(ArityType arity); FastTuple builtinRunJsPredicate(ArityType arity); FastTuple builtinRegexCompile(ArityType arity); FastTuple builtinRegexFind(ArityType arity); FastTuple builtinRegexFindAll(ArityType arity); FastTuple builtinShardFilter(ArityType arity); FastTuple builtinShardHash(ArityType arity); FastTuple builtinExtractSubArray(ArityType arity); FastTuple builtinIsArrayEmpty(ArityType arity); FastTuple builtinReverseArray(ArityType arity); FastTuple builtinSortArray(ArityType arity); FastTuple builtinDateAdd(ArityType arity); FastTuple builtinHasNullBytes(ArityType arity); FastTuple builtinGetRegexPattern(ArityType arity); FastTuple builtinGetRegexFlags(ArityType arity); FastTuple builtinHash(ArityType arity); FastTuple builtinFtsMatch(ArityType arity); std::pair generateSortKeyHelper(ArityType arity); FastTuple builtinGenerateSortKey(ArityType arity); FastTuple builtinGenerateCheapSortKey(ArityType arity); FastTuple builtinSortKeyComponentVectorGetElement( ArityType arity); FastTuple builtinSortKeyComponentVectorToArray( ArityType arity); FastTuple builtinMakeBsonObj(ArityType arity); FastTuple builtinTsSecond(ArityType arity); FastTuple builtinTsIncrement(ArityType arity); FastTuple builtinTypeMatch(ArityType arity); FastTuple builtinDateToString(ArityType arity); FastTuple builtinDateFromString(ArityType arity); FastTuple builtinDateFromStringNoThrow(ArityType arity); FastTuple builtinDateTrunc(ArityType arity); FastTuple builtinMinMaxFromArray(ArityType arity, Builtin f); FastTuple builtinYear(ArityType arity); FastTuple builtinMonth(ArityType arity); FastTuple builtinHour(ArityType arity); FastTuple builtinMinute(ArityType arity); FastTuple builtinSecond(ArityType arity); FastTuple builtinMillisecond(ArityType arity); FastTuple builtinWeek(ArityType arity); FastTuple builtinISOWeekYear(ArityType arity); FastTuple builtinISODayOfWeek(ArityType arity); FastTuple builtinISOWeek(ArityType arity); FastTuple builtinObjectToArray(ArityType arity); FastTuple builtinArrayToObject(ArityType arity); FastTuple builtinAggFirstN(ArityType arity); FastTuple builtinAggFirstNMerge(ArityType arity); FastTuple builtinAggFirstNFinalize(ArityType arity); FastTuple builtinAggLastN(ArityType arity); FastTuple builtinAggLastNMerge(ArityType arity); FastTuple builtinAggLastNFinalize(ArityType arity); template FastTuple builtinAggTopBottomN(ArityType arity); template FastTuple builtinAggTopBottomNMerge(ArityType arity); FastTuple builtinAggTopBottomNFinalize(ArityType arity); template FastTuple builtinAggMinMaxN(ArityType arity); template FastTuple builtinAggMinMaxNMerge(ArityType arity); template FastTuple builtinAggMinMaxNFinalize(ArityType arity); FastTuple dispatchBuiltin(Builtin f, ArityType arity); static constexpr size_t offsetOwned = 0; static constexpr size_t offsetTag = 1; static constexpr size_t offsetVal = 2; MONGO_COMPILER_ALWAYS_INLINE_OPT FastTuple readTuple(uint8_t* ptr) noexcept { auto owned = readFromMemory(ptr + offsetOwned); auto tag = readFromMemory(ptr + offsetTag); auto val = readFromMemory(ptr + offsetVal); return {owned, tag, val}; } MONGO_COMPILER_ALWAYS_INLINE_OPT void writeTuple(uint8_t* ptr, bool owned, value::TypeTags tag, value::Value val) noexcept { writeToMemory(ptr + offsetOwned, owned); writeToMemory(ptr + offsetTag, tag); writeToMemory(ptr + offsetVal, val); } MONGO_COMPILER_ALWAYS_INLINE_OPT FastTuple getFromStack(size_t offset, bool pop = false) noexcept { auto ret = readTuple(_argStackTop - offset * sizeOfElement); if (pop) { popStack(); } return ret; } MONGO_COMPILER_ALWAYS_INLINE_OPT FastTuple moveFromStack(size_t offset) noexcept { if (MONGO_likely(offset == 0)) { auto [owned, tag, val] = readTuple(_argStackTop); writeToMemory(_argStackTop + offsetOwned, false); return {owned, tag, val}; } else { auto ptr = _argStackTop - offset * sizeOfElement; auto [owned, tag, val] = readTuple(ptr); writeToMemory(ptr + offsetOwned, false); return {owned, tag, val}; } } MONGO_COMPILER_ALWAYS_INLINE_OPT std::pair moveOwnedFromStack(size_t offset) { auto [owned, tag, val] = moveFromStack(offset); if (!owned) { std::tie(tag, val) = value::copyValue(tag, val); } return {tag, val}; } MONGO_COMPILER_ALWAYS_INLINE_OPT void setStack(size_t offset, bool owned, value::TypeTags tag, value::Value val) noexcept { if (MONGO_likely(offset == 0)) { topStack(owned, tag, val); } else { writeTuple(_argStackTop - offset * sizeOfElement, owned, tag, val); } } MONGO_COMPILER_ALWAYS_INLINE_OPT void pushStack(bool owned, value::TypeTags tag, value::Value val) noexcept { auto localPtr = _argStackTop += sizeOfElement; if constexpr (kDebugBuild) { invariant(localPtr != _argStackEnd); } writeTuple(localPtr, owned, tag, val); } MONGO_COMPILER_ALWAYS_INLINE void topStack(bool owned, value::TypeTags tag, value::Value val) noexcept { writeTuple(_argStackTop, owned, tag, val); } MONGO_COMPILER_ALWAYS_INLINE void popStack() noexcept { _argStackTop -= sizeOfElement; } MONGO_COMPILER_ALWAYS_INLINE_OPT void popAndReleaseStack() noexcept { auto [owned, tag, val] = getFromStack(0); if (owned) { value::releaseValue(tag, val); } popStack(); } void stackReset() noexcept { _argStackTop = _argStack - sizeOfElement; } void allocStack(size_t size) noexcept; void swapStack(); // The top entry in '_argStack', or one element before the stack when empty. uint8_t* _argStackTop{nullptr}; // The byte following '_argStack's current memory block. uint8_t* _argStackEnd{nullptr}; // Expression execution stack of (owned, tag, value) tuples each of 'sizeOfElement' bytes. uint8_t* _argStack{nullptr}; }; } // namespace vm } // namespace sbe } // namespace mongo