blob: 1eee39600a1c8d17a8ae7577cf2e3a1fdc848453 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
/// --- Terminology:
//
/// Compute function: The function that, given a row, performs the computation of an expr
/// and produces a scalar result. This function evaluates the necessary child arguments by
/// calling their compute functions, then performs whatever computation is necessary on
/// the arguments (e.g. calling a UDF with the child arguments). All compute functions
/// take arguments (ExprContext*, const TupleRow*). The return type is a *Val (i.e. a subclass
/// of AnyVal). Thus, a single expression will implement a compute function for every
/// return type it supports.
///
/// UDX: user-defined X. E.g., user-defined function, user-defined aggregate. Something
/// that is written by an external user.
///
/// Scalar function call: An expr that returns a single scalar value and can be
/// implemented using the UDF interface. Note that this includes builtins, which although
/// not being user-defined still use the same interface as UDFs (i.e., they are
/// implemented as functions with signature "*Val (FunctionContext*, *Val, *Val...)").
///
/// Aggregate function call: a UDA or builtin aggregate function.
///
/// --- Expr overview:
///
/// The Expr superclass defines a virtual Get*Val() compute function for each possible
/// return type (GetBooleanVal(), GetStringVal(), etc). Expr subclasses implement the
/// Get*Val() functions associated with their possible return types; for many Exprs this
/// will be a single function. These functions are generally cross-compiled to both native
/// and IR libraries. In the interpreted path, the native compute functions are run as-is.
///
/// For the codegen path, Expr defines a virtual method GetCodegendComputeFn() that
/// returns the Function* of the expr's compute function. Note that we do not need a
/// separate GetCodegendComputeFn() for each type.
///
/// Only short-circuited operators (e.g. &&, ||) and other special functions like literals
/// must implement custom Get*Val() compute functions. Scalar function calls use the
/// generic compute functions implemented by ScalarFnCall(). For cross-compiled compute
/// functions, GetCodegendComputeFn() can use ReplaceChildCallsComputeFn(), which takes a
/// cross-compiled IR Get*Val() function, pulls out any calls to the children's Get*Val()
/// functions (which we identify via the Get*Val() static wrappers), and replaces them
/// with the codegen'd version of that function. This allows us to write a single function
/// for both the interpreted and codegen paths.
///
/// Only short-circuited operators (e.g. &&, ||) and other special functions like
/// literals must implement custom Get*Val() compute functions. Scalar function calls
/// use the generic compute functions implemented by ScalarFnCall(). For cross-compiled
/// compute functions, GetCodegendComputeFn() can use ReplaceChildCallsComputeFn(), which
/// takes a cross-compiled IR Get*Val() function, pulls out any calls to the children's
/// Get*Val() functions (which we identify via the Get*Val() static wrappers), and
/// replaces them with the codegen'd version of that function. This allows us to write a
/// single function for both the interpreted and codegen paths.
///
/// --- Expr users (e.g. exec nodes):
///
/// A typical usage pattern will look something like:
/// 1. Expr::CreateExprTrees()
/// 2. Expr::Prepare()
/// 3. Expr::Open()
/// 4. Expr::CloneIfNotExists() [for multi-threaded execution]
/// 5. Evaluate exprs via Get*Val() calls
/// 6. Expr::Close() [called once per ExprContext, including clones]
///
/// Expr users should use the static Get*Val() wrapper functions to evaluate exprs,
/// cross-compile the resulting function, and use ReplaceGetValCalls() to create the
/// codegen'd function. See the comments on these functions for more details. This is a
/// similar pattern to that used by the cross-compiled compute functions.
///
/// TODO:
/// - Fix codegen compile time
/// - Fix perf regressions via extra optimization passes + patching LLVM
#ifndef IMPALA_EXPRS_EXPR_H
#define IMPALA_EXPRS_EXPR_H
#include <memory>
#include <string>
#include <vector>
#include <boost/scoped_ptr.hpp>
#include "common/global-types.h"
#include "common/status.h"
#include "impala-ir/impala-ir-functions.h"
#include "runtime/types.h"
#include "udf/udf-internal.h" // for CollectionVal
#include "udf/udf.h"
using namespace impala_udf;
namespace llvm {
class BasicBlock;
class Function;
class Type;
class Value;
};
namespace impala {
class ExprContext;
class IsNullExpr;
class LibCacheEntry;
class LlvmCodeGen;
class MemTracker;
class ObjectPool;
class RowDescriptor;
class RuntimeState;
class TColumnValue;
class TExpr;
class TExprNode;
class Tuple;
class TupleRow;
/// This is the superclass of all expr evaluation nodes.
class Expr {
public:
virtual ~Expr();
/// Virtual compute functions for each *Val type. Each Expr subclass should implement
/// the functions for the return type(s) it supports. For example, a boolean function
/// will only implement GetBooleanVal(). Some Exprs, like Literal, have many possible
/// return types and will implement multiple Get*Val() functions.
virtual BooleanVal GetBooleanVal(ExprContext* context, const TupleRow*);
virtual TinyIntVal GetTinyIntVal(ExprContext* context, const TupleRow*);
virtual SmallIntVal GetSmallIntVal(ExprContext* context, const TupleRow*);
virtual IntVal GetIntVal(ExprContext* context, const TupleRow*);
virtual BigIntVal GetBigIntVal(ExprContext* context, const TupleRow*);
virtual FloatVal GetFloatVal(ExprContext* context, const TupleRow*);
virtual DoubleVal GetDoubleVal(ExprContext* context, const TupleRow*);
virtual StringVal GetStringVal(ExprContext* context, const TupleRow*);
virtual CollectionVal GetCollectionVal(ExprContext* context, const TupleRow*);
virtual TimestampVal GetTimestampVal(ExprContext* context, const TupleRow*);
virtual DecimalVal GetDecimalVal(ExprContext* context, const TupleRow*);
/// Get the number of digits after the decimal that should be displayed for this value.
/// Returns -1 if no scale has been specified (currently the scale is only set for
/// doubles set by RoundUpTo). GetValue() must have already been called.
/// TODO: is this still necessary?
int output_scale() const { return output_scale_; }
void AddChild(Expr* expr) { children_.push_back(expr); }
Expr* GetChild(int i) const { return children_[i]; }
int GetNumChildren() const { return children_.size(); }
const ColumnType& type() const { return type_; }
bool is_slotref() const { return is_slotref_; }
const std::vector<Expr*>& children() const { return children_; }
/// Returns an error status if the function context associated with the
/// expr has an error set.
Status GetFnContextError(ExprContext* ctx);
/// Returns true if the expression is considered constant. This must match the
/// definition of Expr.isConstant() in the frontend. The default implementation returns
/// true if all children are constant.
/// TODO: IMPALA-4617 - plumb through the value from the frontend and remove duplicate
/// logic.
virtual bool IsConstant() const;
/// Returns true if this is a literal expression.
virtual bool IsLiteral() const;
/// Returns the number of SlotRef nodes in the expr tree. If this returns 0, it means it
/// is valid to call GetValue(nullptr) on the expr tree.
/// If 'slot_ids' is non-null, add the slot ids to it.
virtual int GetSlotIds(std::vector<SlotId>* slot_ids = nullptr) const;
/// Returns true iff the expression 'texpr' contains UDF available only as LLVM IR. In
/// which case, it's impossible to interpret this expression and codegen must be used.
static bool NeedCodegen(const TExpr& texpr);
/// Create expression tree from the list of nodes contained in texpr within 'pool'.
/// Returns the root of expression tree in 'expr' and the corresponding ExprContext in
/// 'ctx'.
static Status CreateExprTree(ObjectPool* pool, const TExpr& texpr, ExprContext** ctx);
/// Creates vector of ExprContexts containing exprs from the given vector of
/// TExprs within 'pool'. Returns an error if any of the individual conversions caused
/// an error, otherwise OK.
static Status CreateExprTrees(ObjectPool* pool, const std::vector<TExpr>& texprs,
std::vector<ExprContext*>* ctxs);
/// Convenience function for preparing multiple expr trees.
/// Allocations from 'ctxs' will be counted against 'tracker'.
static Status Prepare(const std::vector<ExprContext*>& ctxs, RuntimeState* state,
const RowDescriptor& row_desc, MemTracker* tracker);
/// Convenience function for opening multiple expr trees.
static Status Open(const std::vector<ExprContext*>& ctxs, RuntimeState* state);
/// Clones each ExprContext for multiple expr trees. 'new_ctxs' must be non-NULL.
/// Idempotent: if '*new_ctxs' is empty, a clone of each context in 'ctxs' will be added
/// to it, and if non-empty, it is assumed CloneIfNotExists() was already called and the
/// call is a no-op. The new ExprContexts are created in state->obj_pool().
static Status CloneIfNotExists(const std::vector<ExprContext*>& ctxs,
RuntimeState* state, std::vector<ExprContext*>* new_ctxs);
/// Convenience function for closing multiple expr trees.
static void Close(const std::vector<ExprContext*>& ctxs, RuntimeState* state);
/// Create a new literal expr of 'type' with initial 'data'.
/// data should match the ColumnType (i.e. type == TYPE_INT, data is a int*)
/// The new Expr will be allocated from the pool.
static Expr* CreateLiteral(ObjectPool* pool, const ColumnType& type, void* data);
/// Create a new literal expr of 'type' by parsing the string.
/// NULL will be returned if the string and type are not compatible.
/// The new Expr will be allocated from the pool.
static Expr* CreateLiteral(ObjectPool* pool, const ColumnType& type,
const std::string&);
/// Computes a memory efficient layout for storing the results of evaluating
/// 'exprs'. The results are assumed to be void* slot types (vs AnyVal types). Varlen
/// data is not included (e.g. there will be space for a StringValue, but not the data
/// referenced by it).
///
/// Returns the number of bytes necessary to store all the results and offsets
/// where the result for each expr should be stored.
///
/// Variable length types are guaranteed to be at the end and 'var_result_begin'
/// will be set the beginning byte offset where variable length results begin.
/// 'var_result_begin' will be set to -1 if there are no variable len types.
static int ComputeResultsLayout(const std::vector<Expr*>& exprs,
std::vector<int>* offsets, int* var_result_begin);
static int ComputeResultsLayout(const std::vector<ExprContext*>& ctxs,
std::vector<int>* offsets, int* var_result_begin);
/// Returns an llvm::Function* with signature:
/// <subclass of AnyVal> ComputeFn(ExprContext* context, const TupleRow* row)
//
/// The function should evaluate this expr over 'row' and return the result as the
/// appropriate type of AnyVal.
virtual Status GetCodegendComputeFn(LlvmCodeGen* codegen, llvm::Function** fn) = 0;
/// If this expr is constant, evaluates the expr with no input row argument and returns
/// the result in 'const_val'. Sets 'const_val' to NULL if the argument is not constant.
/// The returned AnyVal and associated varlen data is owned by 'context'. This should
/// only be called after Open() has been called on this expr. Returns an error if there
/// was an error evaluating the expression or if memory could not be allocated for the
/// expression result.
virtual Status GetConstVal(
RuntimeState* state, ExprContext* context, AnyVal** const_val);
virtual std::string DebugString() const;
static std::string DebugString(const std::vector<Expr*>& exprs);
static std::string DebugString(const std::vector<ExprContext*>& ctxs);
/// The builtin functions are not called from anywhere in the code and the
/// symbols are therefore not included in the binary. We call these functions
/// by using dlsym. The compiler must think this function is callable to
/// not strip these symbols.
static void InitBuiltinsDummy();
// Any additions to this enum must be reflected in both GetConstant*() and
// GetIrConstant().
enum ExprConstant {
RETURN_TYPE_SIZE, // int
RETURN_TYPE_PRECISION, // int
RETURN_TYPE_SCALE, // int
ARG_TYPE_SIZE, // int[]
ARG_TYPE_PRECISION, // int[]
ARG_TYPE_SCALE, // int[]
};
// Static function for obtaining a runtime constant. Expr compute functions and
// builtins implementing the UDF interface should use this function, rather than
// accessing runtime constants directly, so any recognized constants can be inlined via
// InlineConstants() in the codegen path. In the interpreted path, this function will
// work as-is.
//
// 'c' determines which constant is returned. The type of the constant is annotated in
// the ExprConstant enum above. If the constant is an array, 'i' must be specified and
// indicates which element to return. 'i' must always be an immediate integer value so
// InlineConstants() can resolve the index, e.g., it cannot be a variable or an
// expression like "1 + 1". For example, if 'c' = ARG_TYPE_SIZE, then 'T' = int and
// 0 <= i < children_.size().
//
// InlineConstants() can be run on the function to replace recognized constants. The
// constants are only replaced in the function itself, so any callee functions with
// constants to be replaced must be inlined into the function that InlineConstants()
// is run on (e.g. by annotating them with IR_ALWAYS_INLINE).
//
// TODO: implement a loop unroller (or use LLVM's) so we can use GetConstantInt() in loops
static int GetConstantInt(const FunctionContext& ctx, ExprConstant c, int i = -1);
/// Finds all calls to Expr::GetConstantInt() in 'fn' and replaces them with the
/// appropriate runtime constants based on the arguments. 'return_type' is the
/// return type of the UDF or UDAF, i.e. the value of FunctionContext::GetReturnType().
/// 'arg_types' are the argument types of the UDF or UDAF, i.e. the values of
/// FunctionContext::GetArgType().
static int InlineConstants(const FunctionContext::TypeDesc& return_type,
const std::vector<FunctionContext::TypeDesc>& arg_types,
LlvmCodeGen* codegen, llvm::Function* fn);
static const char* LLVM_CLASS_NAME;
// Expr::GetConstantInt() symbol prefix.
static const char* GET_CONSTANT_INT_SYMBOL_PREFIX;
protected:
friend class AggFnEvaluator;
friend class CastExpr;
friend class ComputeFunctions;
friend class DecimalFunctions;
friend class DecimalLliteral;
friend class DecimalOperators;
friend class MathFunctions;
friend class StringFunctions;
friend class TimestampFunctions;
friend class ConditionalFunctions;
friend class UtilityFunctions;
friend class CaseExpr;
friend class InPredicate;
friend class FunctionCall;
friend class ScalarFnCall;
Expr(const ColumnType& type, bool is_slotref = false);
Expr(const TExprNode& node, bool is_slotref = false);
/// Initializes this expr instance for execution. This does not include initializing
/// state in the ExprContext; 'context' should only be used to register a
/// FunctionContext via RegisterFunctionContext(). Any IR functions must be generated
/// here.
///
/// Subclasses overriding this function should call Expr::Prepare() to recursively call
/// Prepare() on the expr tree.
virtual Status Prepare(RuntimeState* state, const RowDescriptor& row_desc,
ExprContext* context);
/// Initializes 'context' for execution. If scope if FRAGMENT_LOCAL, both fragment- and
/// thread-local state should be initialized. Otherwise, if scope is THREAD_LOCAL, only
/// thread-local state should be initialized.
//
/// Subclasses overriding this function should call Expr::Open() to recursively call
/// Open() on the expr tree.
virtual Status Open(RuntimeState* state, ExprContext* context,
FunctionContext::FunctionStateScope scope = FunctionContext::FRAGMENT_LOCAL);
/// Subclasses overriding this function should call Expr::Close().
//
/// If scope if FRAGMENT_LOCAL, both fragment- and thread-local state should be torn
/// down. Otherwise, if scope is THREAD_LOCAL, only thread-local state should be torn
/// down.
virtual void Close(RuntimeState* state, ExprContext* context,
FunctionContext::FunctionStateScope scope = FunctionContext::FRAGMENT_LOCAL);
/// Cache entry for the library implementing this function.
LibCacheEntry* cache_entry_;
/// Function description.
TFunction fn_;
/// recognize if this node is a slotref in order to speed up GetValue()
const bool is_slotref_;
/// analysis is done, types are fixed at this point
const ColumnType type_;
std::vector<Expr*> children_;
int output_scale_;
/// Index to pass to ExprContext::fn_context() to retrieve this expr's FunctionContext.
/// Set in RegisterFunctionContext(). -1 if this expr does not need a FunctionContext and
/// doesn't call RegisterFunctionContext().
int fn_context_index_;
/// Cached codegened compute function. Exprs should set this in GetCodegendComputeFn().
llvm::Function* ir_compute_fn_;
/// Helper function that calls ctx->Register(), sets fn_context_index_, and returns the
/// registered FunctionContext.
FunctionContext* RegisterFunctionContext(
ExprContext* ctx, RuntimeState* state, int varargs_buffer_size = 0);
/// Helper function to create an empty Function* with the appropriate signature to be
/// returned by GetCodegendComputeFn(). 'name' is the name of the returned Function*.
/// The arguments to the function are returned in 'args'.
llvm::Function* CreateIrFunctionPrototype(LlvmCodeGen* codegen, const std::string& name,
llvm::Value* (*args)[2]);
/// Generates an IR compute function that calls the appropriate interpreted Get*Val()
/// compute function.
//
/// This is useful for builtins that can't be implemented with the UDF interface
/// (e.g. functions that need short-circuiting) and that don't have custom codegen
/// functions that use the IRBuilder. It doesn't provide any performance benefit over
/// the interpreted path.
/// TODO: this should be replaced with fancier xcompiling infrastructure
Status GetCodegendComputeFnWrapper(LlvmCodeGen* codegen, llvm::Function** fn);
/// Returns the IR version of the static Get*Val() wrapper function corresponding to
/// 'type'. This is used for calling interpreted Get*Val() functions from codegen'd
/// functions (e.g. in ScalarFnCall() when codegen is disabled).
llvm::Function* GetStaticGetValWrapper(ColumnType type, LlvmCodeGen* codegen);
/// Replace all calls to Expr::GetConstant() in 'fn' based on the types of the
/// expr and its children. This is a convenience method that invokes the static
/// InlineConstants() function with the correct arguments for the expr.
int InlineConstants(LlvmCodeGen* codegen, llvm::Function* fn);
/// Simple debug string that provides no expr subclass-specific information
std::string DebugString(const std::string& expr_name) const;
private:
friend class ExprContext;
friend class ExprTest;
friend class ExprCodegenTest;
/// Create a new Expr based on texpr_node.node_type within 'pool'.
static Status CreateExpr(ObjectPool* pool, const TExprNode& texpr_node, Expr** expr);
/// Creates an expr tree for the node rooted at 'node_idx' via depth-first traversal.
/// parameters
/// nodes: vector of thrift expression nodes to be translated
/// parent: parent of node at node_idx (or NULL for node_idx == 0)
/// node_idx:
/// in: root of TExprNode tree
/// out: next node in 'nodes' that isn't part of tree
/// root_expr: out: root of constructed expr tree
/// ctx: out: context of constructed expr tree
/// return
/// status.ok() if successful
/// !status.ok() if tree is inconsistent or corrupt
static Status CreateTreeFromThrift(ObjectPool* pool,
const std::vector<TExprNode>& nodes, Expr* parent, int* node_idx,
Expr** root_expr, ExprContext** ctx);
/// Static wrappers around the virtual Get*Val() functions. Calls the appropriate
/// Get*Val() function on expr, passing it the context and row arguments.
//
/// These are used to call Get*Val() functions from generated functions, since I don't
/// know how to call virtual functions directly. GetStaticGetValWrapper() returns the
/// IR function of the appropriate wrapper function.
static BooleanVal GetBooleanVal(Expr* expr, ExprContext* context, const TupleRow* row);
static TinyIntVal GetTinyIntVal(Expr* expr, ExprContext* context, const TupleRow* row);
static SmallIntVal GetSmallIntVal(Expr* expr, ExprContext* context, const TupleRow* row);
static IntVal GetIntVal(Expr* expr, ExprContext* context, const TupleRow* row);
static BigIntVal GetBigIntVal(Expr* expr, ExprContext* context, const TupleRow* row);
static FloatVal GetFloatVal(Expr* expr, ExprContext* context, const TupleRow* row);
static DoubleVal GetDoubleVal(Expr* expr, ExprContext* context, const TupleRow* row);
static StringVal GetStringVal(Expr* expr, ExprContext* context, const TupleRow* row);
static TimestampVal GetTimestampVal(Expr* expr, ExprContext* context, const TupleRow* row);
static DecimalVal GetDecimalVal(Expr* expr, ExprContext* context, const TupleRow* row);
// Helper function for GetConstantInt() and InlineConstants(): return the constant value
// given the specific argument and return types.
static int GetConstantInt(const FunctionContext::TypeDesc& return_type,
const std::vector<FunctionContext::TypeDesc>& arg_types, ExprConstant c,
int i = -1);
};
}
#endif