blob: e0f9516eaa4319fa0611bf4c8124ed820e79e713 [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.
#include <sstream>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include <llvm/Transforms/Utils/UnrollLoop.h>
#include <thrift/protocol/TDebugProtocol.h>
#include "codegen/codegen-anyval.h"
#include "codegen/llvm-codegen.h"
#include "common/object-pool.h"
#include "common/status.h"
#include "exprs/aggregate-functions.h"
#include "exprs/anyval-util.h"
#include "exprs/bit-byte-functions.h"
#include "exprs/case-expr.h"
#include "exprs/cast-functions.h"
#include "exprs/compound-predicates.h"
#include "exprs/conditional-functions.h"
#include "exprs/decimal-functions.h"
#include "exprs/decimal-operators.h"
#include "exprs/expr-context.h"
#include "exprs/expr.h"
#include "exprs/hive-udf-call.h"
#include "exprs/in-predicate.h"
#include "exprs/is-not-empty-predicate.h"
#include "exprs/is-null-predicate.h"
#include "exprs/like-predicate.h"
#include "exprs/literal.h"
#include "exprs/math-functions.h"
#include "exprs/null-literal.h"
#include "exprs/operators.h"
#include "exprs/scalar-fn-call.h"
#include "exprs/slot-ref.h"
#include "exprs/string-functions.h"
#include "exprs/timestamp-functions.h"
#include "exprs/tuple-is-null-predicate.h"
#include "exprs/udf-builtins.h"
#include "exprs/utility-functions.h"
#include "gen-cpp/Data_types.h"
#include "gen-cpp/Exprs_types.h"
#include "runtime/lib-cache.h"
#include "runtime/mem-tracker.h"
#include "runtime/raw-value.h"
#include "runtime/runtime-state.h"
#include "runtime/tuple-row.h"
#include "runtime/tuple.h"
#include "udf/udf-internal.h"
#include "udf/udf.h"
#include "gen-cpp/Exprs_types.h"
#include "gen-cpp/ImpalaService_types.h"
#include "common/names.h"
using namespace impala_udf;
using namespace llvm;
namespace impala {
const char* Expr::LLVM_CLASS_NAME = "class.impala::Expr";
const char* Expr::GET_CONSTANT_INT_SYMBOL_PREFIX = "_ZN6impala4Expr14GetConstantInt";
template<class T>
bool ParseString(const string& str, T* val) {
istringstream stream(str);
stream >> *val;
return !stream.fail();
}
FunctionContext* Expr::RegisterFunctionContext(ExprContext* ctx, RuntimeState* state,
int varargs_buffer_size) {
FunctionContext::TypeDesc return_type = AnyValUtil::ColumnTypeToTypeDesc(type_);
vector<FunctionContext::TypeDesc> arg_types;
for (int i = 0; i < children_.size(); ++i) {
arg_types.push_back(AnyValUtil::ColumnTypeToTypeDesc(children_[i]->type_));
}
fn_context_index_ = ctx->Register(state, return_type, arg_types, varargs_buffer_size);
return ctx->fn_context(fn_context_index_);
}
Expr::Expr(const ColumnType& type, bool is_slotref)
: cache_entry_(NULL),
is_slotref_(is_slotref),
type_(type),
output_scale_(-1),
fn_context_index_(-1),
ir_compute_fn_(NULL) {
}
Expr::Expr(const TExprNode& node, bool is_slotref)
: cache_entry_(NULL),
is_slotref_(is_slotref),
type_(ColumnType::FromThrift(node.type)),
output_scale_(-1),
fn_context_index_(-1),
ir_compute_fn_(NULL) {
if (node.__isset.fn) fn_ = node.fn;
}
Expr::~Expr() {
DCHECK(cache_entry_ == NULL);
}
void Expr::Close(RuntimeState* state, ExprContext* context,
FunctionContext::FunctionStateScope scope) {
for (int i = 0; i < children_.size(); ++i) {
children_[i]->Close(state, context, scope);
}
if (scope == FunctionContext::FRAGMENT_LOCAL) {
// This is the final, non-cloned context to close. Clean up the whole Expr.
if (cache_entry_ != NULL) {
LibCache::instance()->DecrementUseCount(cache_entry_);
cache_entry_ = NULL;
}
}
}
Status Expr::CreateExprTree(ObjectPool* pool, const TExpr& texpr, ExprContext** ctx) {
// input is empty
if (texpr.nodes.size() == 0) {
*ctx = NULL;
return Status::OK();
}
int node_idx = 0;
Expr* e;
Status status = CreateTreeFromThrift(pool, texpr.nodes, NULL, &node_idx, &e, ctx);
if (status.ok() && node_idx + 1 != texpr.nodes.size()) {
status = Status(
"Expression tree only partially reconstructed. Not all thrift nodes were used.");
}
if (!status.ok()) {
LOG(ERROR) << "Could not construct expr tree.\n" << status.GetDetail() << "\n"
<< apache::thrift::ThriftDebugString(texpr);
}
return status;
}
Status Expr::CreateExprTrees(ObjectPool* pool, const vector<TExpr>& texprs,
vector<ExprContext*>* ctxs) {
ctxs->clear();
for (int i = 0; i < texprs.size(); ++i) {
ExprContext* ctx;
RETURN_IF_ERROR(CreateExprTree(pool, texprs[i], &ctx));
ctxs->push_back(ctx);
}
return Status::OK();
}
Status Expr::CreateTreeFromThrift(ObjectPool* pool, const vector<TExprNode>& nodes,
Expr* parent, int* node_idx, Expr** root_expr, ExprContext** ctx) {
// propagate error case
if (*node_idx >= nodes.size()) {
return Status("Failed to reconstruct expression tree from thrift.");
}
int num_children = nodes[*node_idx].num_children;
Expr* expr = NULL;
RETURN_IF_ERROR(CreateExpr(pool, nodes[*node_idx], &expr));
DCHECK(expr != NULL);
if (parent != NULL) {
parent->AddChild(expr);
} else {
DCHECK(root_expr != NULL);
DCHECK(ctx != NULL);
*root_expr = expr;
*ctx = pool->Add(new ExprContext(expr));
}
for (int i = 0; i < num_children; i++) {
*node_idx += 1;
RETURN_IF_ERROR(CreateTreeFromThrift(pool, nodes, expr, node_idx, NULL, NULL));
// we are expecting a child, but have used all nodes
// this means we have been given a bad tree and must fail
if (*node_idx >= nodes.size()) {
return Status("Failed to reconstruct expression tree from thrift.");
}
}
return Status::OK();
}
Status Expr::CreateExpr(ObjectPool* pool, const TExprNode& texpr_node, Expr** expr) {
switch (texpr_node.node_type) {
case TExprNodeType::BOOL_LITERAL:
case TExprNodeType::FLOAT_LITERAL:
case TExprNodeType::INT_LITERAL:
case TExprNodeType::STRING_LITERAL:
case TExprNodeType::DECIMAL_LITERAL:
case TExprNodeType::TIMESTAMP_LITERAL:
*expr = pool->Add(new Literal(texpr_node));
return Status::OK();
case TExprNodeType::CASE_EXPR:
if (!texpr_node.__isset.case_expr) {
return Status("Case expression not set in thrift node");
}
*expr = pool->Add(new CaseExpr(texpr_node));
return Status::OK();
case TExprNodeType::COMPOUND_PRED:
if (texpr_node.fn.name.function_name == "and") {
*expr = pool->Add(new AndPredicate(texpr_node));
} else if (texpr_node.fn.name.function_name == "or") {
*expr = pool->Add(new OrPredicate(texpr_node));
} else {
DCHECK_EQ(texpr_node.fn.name.function_name, "not");
*expr = pool->Add(new ScalarFnCall(texpr_node));
}
return Status::OK();
case TExprNodeType::NULL_LITERAL:
*expr = pool->Add(new NullLiteral(texpr_node));
return Status::OK();
case TExprNodeType::SLOT_REF:
if (!texpr_node.__isset.slot_ref) {
return Status("Slot reference not set in thrift node");
}
*expr = pool->Add(new SlotRef(texpr_node));
return Status::OK();
case TExprNodeType::TUPLE_IS_NULL_PRED:
*expr = pool->Add(new TupleIsNullPredicate(texpr_node));
return Status::OK();
case TExprNodeType::FUNCTION_CALL:
if (!texpr_node.__isset.fn) {
return Status("Function not set in thrift node");
}
// Special-case functions that have their own Expr classes
// TODO: is there a better way to do this?
if (texpr_node.fn.name.function_name == "if") {
*expr = pool->Add(new IfExpr(texpr_node));
} else if (texpr_node.fn.name.function_name == "nullif") {
*expr = pool->Add(new NullIfExpr(texpr_node));
} else if (texpr_node.fn.name.function_name == "isnull" ||
texpr_node.fn.name.function_name == "ifnull" ||
texpr_node.fn.name.function_name == "nvl") {
*expr = pool->Add(new IsNullExpr(texpr_node));
} else if (texpr_node.fn.name.function_name == "coalesce") {
*expr = pool->Add(new CoalesceExpr(texpr_node));
} else if (texpr_node.fn.binary_type == TFunctionBinaryType::JAVA) {
*expr = pool->Add(new HiveUdfCall(texpr_node));
} else {
*expr = pool->Add(new ScalarFnCall(texpr_node));
}
return Status::OK();
case TExprNodeType::IS_NOT_EMPTY_PRED:
*expr = pool->Add(new IsNotEmptyPredicate(texpr_node));
return Status::OK();
default:
stringstream os;
os << "Unknown expr node type: " << texpr_node.node_type;
return Status(os.str());
}
}
bool Expr::NeedCodegen(const TExpr& texpr) {
for (const TExprNode& texpr_node : texpr.nodes) {
if (texpr_node.node_type == TExprNodeType::FUNCTION_CALL && texpr_node.__isset.fn &&
texpr_node.fn.binary_type == TFunctionBinaryType::IR) {
return true;
}
}
return false;
}
struct MemLayoutData {
int expr_idx;
int byte_size;
bool variable_length;
int alignment;
// TODO: sort by type as well? Any reason to do this?
// TODO: would sorting in reverse order of size be faster due to better packing?
// TODO: why put var-len at end?
bool operator<(const MemLayoutData& rhs) const {
// variable_len go at end
if (this->variable_length && !rhs.variable_length) return false;
if (!this->variable_length && rhs.variable_length) return true;
return this->byte_size < rhs.byte_size;
}
};
int Expr::ComputeResultsLayout(const vector<Expr*>& exprs, vector<int>* offsets,
int* var_result_begin) {
if (exprs.size() == 0) {
*var_result_begin = -1;
return 0;
}
// Don't align more than word (8-byte) size. There's no performance gain beyond 8-byte
// alignment, and there is a performance gain to keeping the results buffer small. This
// is consistent with what compilers do.
int MAX_ALIGNMENT = sizeof(int64_t);
vector<MemLayoutData> data;
data.resize(exprs.size());
// Collect all the byte sizes and sort them
for (int i = 0; i < exprs.size(); ++i) {
DCHECK(!exprs[i]->type().IsComplexType()) << "NYI";
data[i].expr_idx = i;
data[i].byte_size = exprs[i]->type().GetSlotSize();
DCHECK_GT(data[i].byte_size, 0);
data[i].variable_length = exprs[i]->type().IsVarLenStringType();
bool fixed_len_char = exprs[i]->type().type == TYPE_CHAR && !data[i].variable_length;
// Compute the alignment of this value. Values should be self-aligned for optimal
// memory access speed, up to the max alignment (e.g., if this value is an int32_t,
// its offset in the buffer should be divisible by sizeof(int32_t)).
// TODO: is self-alignment really necessary for perf?
if (!fixed_len_char) {
data[i].alignment = min(data[i].byte_size, MAX_ALIGNMENT);
} else {
// Fixed-len chars are aligned to a one-byte boundary, as if they were char[],
// leaving no padding between them and the previous value.
data[i].alignment = 1;
}
}
sort(data.begin(), data.end());
// Walk the types and store in a packed aligned layout
int byte_offset = 0;
offsets->resize(exprs.size());
*var_result_begin = -1;
for (int i = 0; i < data.size(); ++i) {
// Increase byte_offset so data[i] is at the right alignment (i.e. add padding between
// this value and the previous).
byte_offset = BitUtil::RoundUp(byte_offset, data[i].alignment);
(*offsets)[data[i].expr_idx] = byte_offset;
if (data[i].variable_length && *var_result_begin == -1) {
*var_result_begin = byte_offset;
}
DCHECK(!(i == 0 && byte_offset > 0)) << "first value should be at start of layout";
byte_offset += data[i].byte_size;
}
return byte_offset;
}
int Expr::ComputeResultsLayout(const vector<ExprContext*>& ctxs, vector<int>* offsets,
int* var_result_begin) {
vector<Expr*> exprs;
for (int i = 0; i < ctxs.size(); ++i) exprs.push_back(ctxs[i]->root());
return ComputeResultsLayout(exprs, offsets, var_result_begin);
}
void Expr::Close(const vector<ExprContext*>& ctxs, RuntimeState* state) {
for (int i = 0; i < ctxs.size(); ++i) {
ctxs[i]->Close(state);
}
}
Status Expr::Prepare(const vector<ExprContext*>& ctxs, RuntimeState* state,
const RowDescriptor& row_desc, MemTracker* tracker) {
for (int i = 0; i < ctxs.size(); ++i) {
RETURN_IF_ERROR(ctxs[i]->Prepare(state, row_desc, tracker));
}
return Status::OK();
}
Status Expr::Prepare(RuntimeState* state, const RowDescriptor& row_desc,
ExprContext* context) {
DCHECK(type_.type != INVALID_TYPE);
for (int i = 0; i < children_.size(); ++i) {
RETURN_IF_ERROR(children_[i]->Prepare(state, row_desc, context));
}
return Status::OK();
}
Status Expr::Open(const vector<ExprContext*>& ctxs, RuntimeState* state) {
for (int i = 0; i < ctxs.size(); ++i) {
RETURN_IF_ERROR(ctxs[i]->Open(state));
}
return Status::OK();
}
Status Expr::Open(RuntimeState* state, ExprContext* context,
FunctionContext::FunctionStateScope scope) {
for (int i = 0; i < children_.size(); ++i) {
RETURN_IF_ERROR(children_[i]->Open(state, context, scope));
}
return Status::OK();
}
Status Expr::CloneIfNotExists(const vector<ExprContext*>& ctxs, RuntimeState* state,
vector<ExprContext*>* new_ctxs) {
DCHECK(new_ctxs != NULL);
if (!new_ctxs->empty()) {
// 'ctxs' was already cloned into '*new_ctxs', nothing to do.
DCHECK_EQ(new_ctxs->size(), ctxs.size());
for (int i = 0; i < new_ctxs->size(); ++i) DCHECK((*new_ctxs)[i]->is_clone_);
return Status::OK();
}
new_ctxs->resize(ctxs.size());
for (int i = 0; i < ctxs.size(); ++i) {
RETURN_IF_ERROR(ctxs[i]->Clone(state, &(*new_ctxs)[i]));
}
return Status::OK();
}
string Expr::DebugString() const {
// TODO: implement partial debug string for member vars
stringstream out;
out << " type=" << type_.DebugString();
if (!children_.empty()) {
out << " children=" << DebugString(children_);
}
return out.str();
}
string Expr::DebugString(const vector<Expr*>& exprs) {
stringstream out;
out << "[";
for (int i = 0; i < exprs.size(); ++i) {
out << (i == 0 ? "" : " ") << exprs[i]->DebugString();
}
out << "]";
return out.str();
}
string Expr::DebugString(const vector<ExprContext*>& ctxs) {
vector<Expr*> exprs;
for (int i = 0; i < ctxs.size(); ++i) exprs.push_back(ctxs[i]->root());
return DebugString(exprs);
}
bool Expr::IsConstant() const {
for (int i = 0; i < children_.size(); ++i) {
if (!children_[i]->IsConstant()) return false;
}
return true;
}
bool Expr::IsLiteral() const {
return false;
}
int Expr::GetSlotIds(vector<SlotId>* slot_ids) const {
int n = 0;
for (int i = 0; i < children_.size(); ++i) {
n += children_[i]->GetSlotIds(slot_ids);
}
return n;
}
Function* Expr::GetStaticGetValWrapper(ColumnType type, LlvmCodeGen* codegen) {
switch (type.type) {
case TYPE_BOOLEAN:
return codegen->GetFunction(IRFunction::EXPR_GET_BOOLEAN_VAL, false);
case TYPE_TINYINT:
return codegen->GetFunction(IRFunction::EXPR_GET_TINYINT_VAL, false);
case TYPE_SMALLINT:
return codegen->GetFunction(IRFunction::EXPR_GET_SMALLINT_VAL, false);
case TYPE_INT:
return codegen->GetFunction(IRFunction::EXPR_GET_INT_VAL, false);
case TYPE_BIGINT:
return codegen->GetFunction(IRFunction::EXPR_GET_BIGINT_VAL, false);
case TYPE_FLOAT:
return codegen->GetFunction(IRFunction::EXPR_GET_FLOAT_VAL, false);
case TYPE_DOUBLE:
return codegen->GetFunction(IRFunction::EXPR_GET_DOUBLE_VAL, false);
case TYPE_STRING:
case TYPE_CHAR:
case TYPE_VARCHAR:
return codegen->GetFunction(IRFunction::EXPR_GET_STRING_VAL, false);
case TYPE_TIMESTAMP:
return codegen->GetFunction(IRFunction::EXPR_GET_TIMESTAMP_VAL, false);
case TYPE_DECIMAL:
return codegen->GetFunction(IRFunction::EXPR_GET_DECIMAL_VAL, false);
default:
DCHECK(false) << "Invalid type: " << type.DebugString();
return NULL;
}
}
Function* Expr::CreateIrFunctionPrototype(LlvmCodeGen* codegen, const string& name,
Value* (*args)[2]) {
Type* return_type = CodegenAnyVal::GetLoweredType(codegen, type());
LlvmCodeGen::FnPrototype prototype(codegen, name, return_type);
prototype.AddArgument(
LlvmCodeGen::NamedVariable(
"context", codegen->GetPtrType(ExprContext::LLVM_CLASS_NAME)));
prototype.AddArgument(
LlvmCodeGen::NamedVariable("row", codegen->GetPtrType(TupleRow::LLVM_CLASS_NAME)));
Function* function = prototype.GeneratePrototype(NULL, args[0]);
DCHECK(function != NULL);
return function;
}
void Expr::InitBuiltinsDummy() {
// Call one function from each of the classes to pull all the symbols
// from that class in.
// TODO: is there a better way to do this?
AggregateFunctions::InitNull(NULL, NULL);
BitByteFunctions::CountSet(NULL, TinyIntVal::null());
CastFunctions::CastToBooleanVal(NULL, TinyIntVal::null());
CompoundPredicate::Not(NULL, BooleanVal::null());
ConditionalFunctions::NullIfZero(NULL, TinyIntVal::null());
DecimalFunctions::Precision(NULL, DecimalVal::null());
DecimalOperators::CastToDecimalVal(NULL, DecimalVal::null());
InPredicate::InIterate(NULL, BigIntVal::null(), 0, NULL);
IsNullPredicate::IsNull(NULL, BooleanVal::null());
LikePredicate::Like(NULL, StringVal::null(), StringVal::null());
Operators::Add_IntVal_IntVal(NULL, IntVal::null(), IntVal::null());
MathFunctions::Pi(NULL);
StringFunctions::Length(NULL, StringVal::null());
TimestampFunctions::Year(NULL, TimestampVal::null());
TimestampFunctions::UnixAndFromUnixPrepare(NULL, FunctionContext::FRAGMENT_LOCAL);
UdfBuiltins::Pi(NULL);
UtilityFunctions::Pid(NULL);
}
Status Expr::GetConstVal(RuntimeState* state, ExprContext* context, AnyVal** const_val) {
DCHECK(context->opened_);
if (!IsConstant()) {
*const_val = NULL;
return Status::OK();
}
RETURN_IF_ERROR(AllocateAnyVal(state, context->pool_.get(), type_,
"Could not allocate constant expression value", const_val));
switch (type_.type) {
case TYPE_BOOLEAN:
*reinterpret_cast<BooleanVal*>(*const_val) = GetBooleanVal(context, NULL);
break;
case TYPE_TINYINT:
*reinterpret_cast<TinyIntVal*>(*const_val) = GetTinyIntVal(context, NULL);
break;
case TYPE_SMALLINT:
*reinterpret_cast<SmallIntVal*>(*const_val) = GetSmallIntVal(context, NULL);
break;
case TYPE_INT:
*reinterpret_cast<IntVal*>(*const_val) = GetIntVal(context, NULL);
break;
case TYPE_BIGINT:
*reinterpret_cast<BigIntVal*>(*const_val) = GetBigIntVal(context, NULL);
break;
case TYPE_FLOAT:
*reinterpret_cast<FloatVal*>(*const_val) = GetFloatVal(context, NULL);
break;
case TYPE_DOUBLE:
*reinterpret_cast<DoubleVal*>(*const_val) = GetDoubleVal(context, NULL);
break;
case TYPE_STRING:
case TYPE_CHAR:
case TYPE_VARCHAR: {
StringVal* sv = reinterpret_cast<StringVal*>(*const_val);
*sv = GetStringVal(context, NULL);
if (sv->len > 0) {
// Make sure the memory is owned by 'context'.
uint8_t* ptr_copy = context->pool_->TryAllocate(sv->len);
if (ptr_copy == NULL) {
return context->pool_->mem_tracker()->MemLimitExceeded(
state, "Could not allocate constant string value", sv->len);
}
memcpy(ptr_copy, sv->ptr, sv->len);
sv->ptr = ptr_copy;
}
break;
}
case TYPE_TIMESTAMP:
*reinterpret_cast<TimestampVal*>(*const_val) = GetTimestampVal(context, NULL);
break;
case TYPE_DECIMAL:
*reinterpret_cast<DecimalVal*>(*const_val) = GetDecimalVal(context, NULL);
break;
default:
DCHECK(false) << "Type not implemented: " << type();
}
// Errors may have been set during the GetConstVal() call.
return GetFnContextError(context);
}
int Expr::GetConstantInt(const FunctionContext::TypeDesc& return_type,
const std::vector<FunctionContext::TypeDesc>& arg_types, ExprConstant c, int i) {
switch (c) {
case RETURN_TYPE_SIZE:
DCHECK_EQ(i, -1);
return AnyValUtil::TypeDescToColumnType(return_type).GetByteSize();
case RETURN_TYPE_PRECISION:
DCHECK_EQ(i, -1);
DCHECK_EQ(return_type.type, FunctionContext::TYPE_DECIMAL);
return return_type.precision;
case RETURN_TYPE_SCALE:
DCHECK_EQ(i, -1);
DCHECK_EQ(return_type.type, FunctionContext::TYPE_DECIMAL);
return return_type.scale;
case ARG_TYPE_SIZE:
DCHECK_GE(i, 0);
DCHECK_LT(i, arg_types.size());
return AnyValUtil::TypeDescToColumnType(arg_types[i]).GetByteSize();
case ARG_TYPE_PRECISION:
DCHECK_GE(i, 0);
DCHECK_LT(i, arg_types.size());
DCHECK_EQ(arg_types[i].type, FunctionContext::TYPE_DECIMAL);
return arg_types[i].precision;
case ARG_TYPE_SCALE:
DCHECK_GE(i, 0);
DCHECK_LT(i, arg_types.size());
DCHECK_EQ(arg_types[i].type, FunctionContext::TYPE_DECIMAL);
return arg_types[i].scale;
default:
CHECK(false) << "NYI";
return -1;
}
}
int Expr::GetConstantInt(const FunctionContext& ctx, ExprConstant c, int i) {
return GetConstantInt(ctx.GetReturnType(), ctx.impl()->arg_types(), c, i);
}
int Expr::InlineConstants(LlvmCodeGen* codegen, Function* fn) {
FunctionContext::TypeDesc return_type = AnyValUtil::ColumnTypeToTypeDesc(type_);
vector<FunctionContext::TypeDesc> arg_types;
for (int i = 0; i < children_.size(); ++i) {
arg_types.push_back(AnyValUtil::ColumnTypeToTypeDesc(children_[i]->type_));
}
return InlineConstants(return_type, arg_types, codegen, fn);
}
int Expr::InlineConstants(const FunctionContext::TypeDesc& return_type,
const std::vector<FunctionContext::TypeDesc>& arg_types, LlvmCodeGen* codegen,
Function* fn) {
int replaced = 0;
for (inst_iterator iter = inst_begin(fn), end = inst_end(fn); iter != end; ) {
// Increment iter now so we don't mess it up modifying the instruction below
Instruction* instr = &*(iter++);
// Look for call instructions
if (!isa<CallInst>(instr)) continue;
CallInst* call_instr = cast<CallInst>(instr);
Function* called_fn = call_instr->getCalledFunction();
// Look for call to Expr::GetConstant*()
if (called_fn == NULL ||
called_fn->getName().find(GET_CONSTANT_INT_SYMBOL_PREFIX) == string::npos) {
continue;
}
// 'c' and 'i' arguments must be constant
ConstantInt* c_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(1));
ConstantInt* i_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(2));
DCHECK(c_arg != NULL) << "Non-constant 'c' argument to Expr::GetConstant*()";
DCHECK(i_arg != NULL) << "Non-constant 'i' argument to Expr::GetConstant*()";
// Replace the called function with the appropriate constant
ExprConstant c_val = static_cast<ExprConstant>(c_arg->getSExtValue());
int i_val = static_cast<int>(i_arg->getSExtValue());
// All supported constants are currently integers.
call_instr->replaceAllUsesWith(ConstantInt::get(codegen->GetType(TYPE_INT),
GetConstantInt(return_type, arg_types, c_val, i_val)));
call_instr->eraseFromParent();
++replaced;
}
return replaced;
}
Status Expr::GetCodegendComputeFnWrapper(LlvmCodeGen* codegen, Function** fn) {
if (ir_compute_fn_ != NULL) {
*fn = ir_compute_fn_;
return Status::OK();
}
Function* static_getval_fn = GetStaticGetValWrapper(type(), codegen);
// Call it passing this as the additional first argument.
Value* args[2];
ir_compute_fn_ = CreateIrFunctionPrototype(codegen, "CodegenComputeFnWrapper", &args);
BasicBlock* entry_block =
BasicBlock::Create(codegen->context(), "entry", ir_compute_fn_);
LlvmBuilder builder(entry_block);
Value* this_ptr =
codegen->CastPtrToLlvmPtr(codegen->GetPtrType(Expr::LLVM_CLASS_NAME), this);
Value* compute_fn_args[] = {this_ptr, args[0], args[1]};
Value* ret = CodegenAnyVal::CreateCall(
codegen, &builder, static_getval_fn, compute_fn_args, "ret");
builder.CreateRet(ret);
ir_compute_fn_ = codegen->FinalizeFunction(ir_compute_fn_);
*fn = ir_compute_fn_;
return Status::OK();
}
// At least one of these should always be subclassed.
BooleanVal Expr::GetBooleanVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return BooleanVal::null();
}
TinyIntVal Expr::GetTinyIntVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return TinyIntVal::null();
}
SmallIntVal Expr::GetSmallIntVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return SmallIntVal::null();
}
IntVal Expr::GetIntVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return IntVal::null();
}
BigIntVal Expr::GetBigIntVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return BigIntVal::null();
}
FloatVal Expr::GetFloatVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return FloatVal::null();
}
DoubleVal Expr::GetDoubleVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return DoubleVal::null();
}
StringVal Expr::GetStringVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return StringVal::null();
}
CollectionVal Expr::GetCollectionVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return CollectionVal::null();
}
TimestampVal Expr::GetTimestampVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return TimestampVal::null();
}
DecimalVal Expr::GetDecimalVal(ExprContext* context, const TupleRow* row) {
DCHECK(false) << DebugString();
return DecimalVal::null();
}
Status Expr::GetFnContextError(ExprContext* ctx) {
if (fn_context_index_ != -1) {
FunctionContext* fn_ctx = ctx->fn_context(fn_context_index_);
if (fn_ctx->has_error()) return Status(fn_ctx->error_msg());
}
return Status::OK();
}
string Expr::DebugString(const string& expr_name) const {
stringstream out;
out << expr_name << "(" << Expr::DebugString() << ")";
return out.str();
}
}