| // 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 "exprs/conditional-functions.h" |
| |
| #include "codegen/codegen-anyval.h" |
| #include "codegen/llvm-codegen.h" |
| #include "exprs/scalar-expr.inline.h" |
| |
| using namespace impala; |
| |
| /// Sample IR output: |
| /// define i16 @IfExpr(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) #47 { |
| /// is_condition_null: |
| /// %condition = call i16 @"impala::Operators::Gt_BigIntVal_BigIntValWrapper"( |
| /// %"class.impala::ScalarExprEvaluator"* %eval, %"class.impala::TupleRow"* %row) |
| /// %is_null = trunc i16 %condition to i1 |
| /// br i1 %is_null, label %return_else, label %eval_condition |
| /// |
| /// eval_condition: ; preds = %is_condition_null |
| /// %0 = ashr i16 %condition, 8 |
| /// %1 = trunc i16 %0 to i8 |
| /// %val = trunc i8 %1 to i1 |
| /// br i1 %val, label %return_then, label %return_else |
| /// |
| /// return_then: ; preds = %eval_condition |
| /// %then_val = call i16 @Literal.4(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) |
| /// ret i16 %then_val |
| /// |
| /// return_else: ; preds = %eval_condition, %is_condition_null |
| /// %else_val = call i16 @NullLiteral(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) |
| /// ret i16 %else_val |
| /// } |
| Status IfExpr::GetCodegendComputeFnImpl(LlvmCodeGen* codegen, llvm::Function** fn) { |
| constexpr int IF_NUM_CHILDREN = 3; |
| DCHECK_EQ(IF_NUM_CHILDREN, GetNumChildren()); |
| |
| llvm::Function* child_fns[IF_NUM_CHILDREN]; |
| for (int i = 0; i < IF_NUM_CHILDREN; ++i) { |
| RETURN_IF_ERROR(GetChild(i)->GetCodegendComputeFn(codegen, false, &child_fns[i])); |
| } |
| |
| llvm::LLVMContext& context = codegen->context(); |
| LlvmBuilder builder(context); |
| |
| llvm::Value* args[2]; |
| llvm::Function* function = CreateIrFunctionPrototype("IfExpr", codegen, &args); |
| |
| llvm::BasicBlock* is_condition_null_block = llvm::BasicBlock::Create( |
| context, "is_condition_null", function); |
| llvm::BasicBlock* eval_condition_block = llvm::BasicBlock::Create( |
| context, "eval_condition", function); |
| llvm::BasicBlock* return_then_block = llvm::BasicBlock::Create( |
| context, "return_then", function); |
| llvm::BasicBlock* return_else_block = llvm::BasicBlock::Create( |
| context, "return_else", function); |
| |
| // Check if the condition is a null value. |
| builder.SetInsertPoint(is_condition_null_block); |
| CodegenAnyVal condition = CodegenAnyVal::CreateCallWrapped( |
| codegen, &builder, children()[0]->type(), child_fns[0], args, "condition"); |
| builder.CreateCondBr( |
| condition.GetIsNull(), return_else_block, eval_condition_block); |
| |
| // Condition is non-null, branch according to its value. |
| builder.SetInsertPoint(eval_condition_block); |
| builder.CreateCondBr(condition.GetVal(), return_then_block, return_else_block); |
| |
| // Eval and return then value. |
| builder.SetInsertPoint(return_then_block); |
| llvm::Value* then_val = |
| CodegenAnyVal::CreateCall(codegen, &builder, child_fns[1], args, "then_val"); |
| builder.CreateRet(then_val); |
| |
| // Eval and return else value. |
| builder.SetInsertPoint(return_else_block); |
| llvm::Value* else_val = |
| CodegenAnyVal::CreateCall(codegen, &builder, child_fns[2], args, "else_val"); |
| builder.CreateRet(else_val); |
| |
| *fn = codegen->FinalizeFunction(function); |
| if (UNLIKELY(*fn == nullptr)) return Status(TErrorCode::IR_VERIFY_FAILED, "IfExpr"); |
| return Status::OK(); |
| } |
| |
| /// Sample IR output for three columns: |
| /// define i64 @CoalesceExpr(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) #47 { |
| /// is_child_null: |
| /// %child = call i64 @GetSlotRef(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) |
| /// %is_null = trunc i64 %child to i1 |
| /// br i1 %is_null, label %is_child_null1, label %return_val |
| /// |
| /// is_child_null1: ; preds = %is_child_null |
| /// %child4 = call i64 @GetSlotRef.4(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) |
| /// %is_null5 = trunc i64 %child4 to i1 |
| /// br i1 %is_null5, label %return_last_value, label %return_val3 |
| /// |
| /// return_val: ; preds = %is_child_null |
| /// ret i64 %child |
| /// |
| /// return_last_value: ; preds = %is_child_null1 |
| /// %last_child = call i64 @GetSlotRef.5(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) |
| /// ret i64 %last_child |
| /// |
| /// return_val3: ; preds = %is_child_null1 |
| /// ret i64 %child4 |
| /// } |
| Status CoalesceExpr::GetCodegendComputeFnImpl(LlvmCodeGen* codegen, llvm::Function** fn) { |
| const int num_children = GetNumChildren(); |
| DCHECK_GE(num_children, 1); |
| vector<llvm::Function*> child_fns(num_children, nullptr); |
| for (int i = 0; i < num_children; ++i) { |
| RETURN_IF_ERROR(GetChild(i)->GetCodegendComputeFn(codegen, false, &child_fns[i])); |
| } |
| |
| llvm::LLVMContext& context = codegen->context(); |
| LlvmBuilder builder(context); |
| llvm::Value* args[2]; |
| llvm::Function* function = CreateIrFunctionPrototype("CoalesceExpr", codegen, &args); |
| |
| // The entry block to the function, Checks whether the first child is null. |
| // To be filled later in the loop. |
| llvm::BasicBlock* is_child_null_block = llvm::BasicBlock::Create( |
| context, "is_child_null", function); |
| |
| llvm::BasicBlock* current_null_check_block = is_child_null_block; |
| llvm::BasicBlock* next_null_check_block = nullptr; |
| |
| // Loops through all children except the last. The next block in case of a null value is |
| // always a new "is_child_null" block. |
| for (int i = 0; i < num_children - 1; ++i) { |
| next_null_check_block = llvm::BasicBlock::Create(context, "is_child_null", function); |
| |
| // The block that returns the value of the child if it is not null. |
| llvm::BasicBlock* return_val_block = |
| llvm::BasicBlock::Create(context, "return_val", function); |
| |
| builder.SetInsertPoint(current_null_check_block); |
| CodegenAnyVal child = CodegenAnyVal::CreateCallWrapped( |
| codegen, &builder, children()[i]->type(), child_fns[i], args, "child"); |
| builder.CreateCondBr( |
| child.GetIsNull(), next_null_check_block, return_val_block); |
| |
| builder.SetInsertPoint(return_val_block); |
| builder.CreateRet(child.GetLoweredValue()); |
| |
| current_null_check_block = next_null_check_block; |
| } |
| |
| // Handle the last child. We can return it directly because if it is null, we need to |
| // return null anyway. |
| llvm::BasicBlock* return_last_value_block = current_null_check_block; |
| return_last_value_block->setName("return_last_value"); |
| builder.SetInsertPoint(return_last_value_block); |
| llvm::Value* last_child = CodegenAnyVal::CreateCall( |
| codegen, &builder, child_fns[num_children - 1], args, "last_child"); |
| builder.CreateRet(last_child); |
| |
| *fn = codegen->FinalizeFunction(function); |
| if (UNLIKELY(*fn == nullptr)) { |
| return Status(TErrorCode::IR_VERIFY_FAILED, "CoalesceExpr"); |
| } |
| return Status::OK(); |
| } |
| |
| /// Sample IR output: |
| /// define { i8, i64 } @IsNullExpr(%"class.impala::ScalarExprEvaluator"* %eval, |
| /// %"class.impala::TupleRow"* %row) #47 { |
| /// is_first_value_null: |
| /// %first_value = call { i8, i64 } @GetSlotRef( |
| /// %"class.impala::ScalarExprEvaluator"* %eval, %"class.impala::TupleRow"* %row) |
| /// %0 = extractvalue { i8, i64 } %first_value, 0 |
| /// %is_null = trunc i8 %0 to i1 |
| /// br i1 %is_null, label %return_second_value, label %return_first_value |
| /// |
| /// return_first_value: ; preds = %is_first_value_null |
| /// ret { i8, i64 } %first_value |
| /// |
| /// return_second_value: ; preds = %is_first_value_null |
| /// %second_value = call { i8, i64 } @Literal( |
| /// %"class.impala::ScalarExprEvaluator"* %eval, %"class.impala::TupleRow"* %row) |
| /// ret { i8, i64 } %second_value |
| /// } |
| Status IsNullExpr::GetCodegendComputeFnImpl(LlvmCodeGen* codegen, llvm::Function** fn) { |
| constexpr int NUM_CHILDREN = 2; |
| DCHECK_EQ(NUM_CHILDREN, GetNumChildren()); |
| |
| llvm::Function* child_fns[NUM_CHILDREN]; |
| for (int i = 0; i < NUM_CHILDREN; ++i) { |
| RETURN_IF_ERROR(GetChild(i)->GetCodegendComputeFn(codegen, false, &child_fns[i])); |
| } |
| |
| llvm::LLVMContext& context = codegen->context(); |
| LlvmBuilder builder(context); |
| |
| llvm::Value* args[2]; |
| llvm::Function* function = CreateIrFunctionPrototype("IsNullExpr", codegen, &args); |
| |
| llvm::BasicBlock* is_first_value_null_block = llvm::BasicBlock::Create( |
| context, "is_first_value_null", function); |
| llvm::BasicBlock* return_first_value_block = llvm::BasicBlock::Create( |
| context, "return_first_value", function); |
| llvm::BasicBlock* return_second_value_block = llvm::BasicBlock::Create( |
| context, "return_second_value", function); |
| |
| // Check if the first child is null. |
| builder.SetInsertPoint(is_first_value_null_block); |
| CodegenAnyVal first_value = CodegenAnyVal::CreateCallWrapped( |
| codegen, &builder, children()[0]->type(), child_fns[0], args, "first_value"); |
| builder.CreateCondBr( |
| first_value.GetIsNull(), return_second_value_block, return_first_value_block); |
| |
| builder.SetInsertPoint(return_first_value_block); |
| builder.CreateRet(first_value.GetLoweredValue()); |
| |
| builder.SetInsertPoint(return_second_value_block); |
| llvm::Value* second_value = CodegenAnyVal::CreateCall( |
| codegen, &builder, child_fns[1], args, "second_value"); |
| builder.CreateRet(second_value); |
| |
| *fn = codegen->FinalizeFunction(function); |
| if (UNLIKELY(*fn == nullptr)) return Status(TErrorCode::IR_VERIFY_FAILED, "IsNullExpr"); |
| return Status::OK(); |
| } |
| |
| /// Definitions of Get*ValInterpreted functions. |
| #define IS_NULL_COMPUTE_FUNCTION(type) \ |
| type IsNullExpr::Get##type##Interpreted( \ |
| ScalarExprEvaluator* eval, const TupleRow* row) const { \ |
| DCHECK_EQ(children_.size(), 2); \ |
| type val = GetChild(0)->Get##type(eval, row); \ |
| if (!val.is_null) return val; /* short-circuit */ \ |
| return GetChild(1)->Get##type(eval, row); \ |
| } |
| |
| IS_NULL_COMPUTE_FUNCTION(BooleanVal); |
| IS_NULL_COMPUTE_FUNCTION(TinyIntVal); |
| IS_NULL_COMPUTE_FUNCTION(SmallIntVal); |
| IS_NULL_COMPUTE_FUNCTION(IntVal); |
| IS_NULL_COMPUTE_FUNCTION(BigIntVal); |
| IS_NULL_COMPUTE_FUNCTION(FloatVal); |
| IS_NULL_COMPUTE_FUNCTION(DoubleVal); |
| IS_NULL_COMPUTE_FUNCTION(StringVal); |
| IS_NULL_COMPUTE_FUNCTION(TimestampVal); |
| IS_NULL_COMPUTE_FUNCTION(DecimalVal); |
| IS_NULL_COMPUTE_FUNCTION(DateVal); |
| |
| #define IF_COMPUTE_FUNCTION(type) \ |
| type IfExpr::Get##type##Interpreted( \ |
| ScalarExprEvaluator* eval, const TupleRow* row) const { \ |
| DCHECK_EQ(children_.size(), 3); \ |
| BooleanVal cond = GetChild(0)->GetBooleanVal(eval, row); \ |
| if (cond.is_null || !cond.val) { \ |
| return GetChild(2)->Get##type(eval, row); \ |
| } \ |
| return GetChild(1)->Get##type(eval, row); \ |
| } |
| |
| IF_COMPUTE_FUNCTION(BooleanVal); |
| IF_COMPUTE_FUNCTION(TinyIntVal); |
| IF_COMPUTE_FUNCTION(SmallIntVal); |
| IF_COMPUTE_FUNCTION(IntVal); |
| IF_COMPUTE_FUNCTION(BigIntVal); |
| IF_COMPUTE_FUNCTION(FloatVal); |
| IF_COMPUTE_FUNCTION(DoubleVal); |
| IF_COMPUTE_FUNCTION(StringVal); |
| IF_COMPUTE_FUNCTION(TimestampVal); |
| IF_COMPUTE_FUNCTION(DecimalVal); |
| IF_COMPUTE_FUNCTION(DateVal); |
| |
| #define COALESCE_COMPUTE_FUNCTION(type) \ |
| type CoalesceExpr::Get##type##Interpreted( \ |
| ScalarExprEvaluator* eval, const TupleRow* row) const { \ |
| DCHECK_GE(children_.size(), 1); \ |
| for (int i = 0; i < children_.size(); ++i) { \ |
| type val = GetChild(i)->Get##type(eval, row); \ |
| if (!val.is_null) return val; \ |
| } \ |
| return type::null(); \ |
| } |
| |
| COALESCE_COMPUTE_FUNCTION(BooleanVal); |
| COALESCE_COMPUTE_FUNCTION(TinyIntVal); |
| COALESCE_COMPUTE_FUNCTION(SmallIntVal); |
| COALESCE_COMPUTE_FUNCTION(IntVal); |
| COALESCE_COMPUTE_FUNCTION(BigIntVal); |
| COALESCE_COMPUTE_FUNCTION(FloatVal); |
| COALESCE_COMPUTE_FUNCTION(DoubleVal); |
| COALESCE_COMPUTE_FUNCTION(StringVal); |
| COALESCE_COMPUTE_FUNCTION(TimestampVal); |
| COALESCE_COMPUTE_FUNCTION(DecimalVal); |
| COALESCE_COMPUTE_FUNCTION(DateVal); |