| // 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/is-not-empty-predicate.h" |
| |
| #include <sstream> |
| #include <codegen/codegen-anyval.h> |
| #include <codegen/llvm-codegen.h> |
| #include <llvm/IR/BasicBlock.h> |
| |
| #include "gen-cpp/Exprs_types.h" |
| |
| #include "common/names.h" |
| #include "exprs/null-literal.h" |
| #include "exprs/scalar-expr.inline.h" |
| #include "exprs/slot-ref.h" |
| |
| namespace impala { |
| |
| const char* IsNotEmptyPredicate::LLVM_CLASS_NAME = "class.impala::IsNotEmptyPredicate"; |
| |
| IsNotEmptyPredicate::IsNotEmptyPredicate(const TExprNode& node) : Predicate(node) {} |
| |
| BooleanVal IsNotEmptyPredicate::GetBooleanValInterpreted( |
| ScalarExprEvaluator* eval, const TupleRow* row) const { |
| CollectionVal coll = children_[0]->GetCollectionVal(eval, row); |
| if (coll.is_null) return BooleanVal::null(); |
| return BooleanVal(coll.num_tuples != 0); |
| } |
| |
| Status IsNotEmptyPredicate::Init( |
| const RowDescriptor& row_desc, bool is_entry_point, RuntimeState* state) { |
| RETURN_IF_ERROR(ScalarExpr::Init(row_desc, is_entry_point, state)); |
| DCHECK_EQ(children_.size(), 1); |
| return Status::OK(); |
| } |
| |
| // Sample IR output (when child is a SlotRef): |
| // define i16 @IsNotEmptyPredicate(%"class.impala::ScalarExprEvaluator"* %eval, |
| // %"class.impala::TupleRow"* %row) #37 { |
| // entry: |
| // %0 = alloca i16 |
| // %1 = alloca i16 |
| // %coll_val = call { i64, i8* } @GetSlotRef( |
| // %"class.impala::ScalarExprEvaluator"* %eval, %"class.impala::TupleRow"* %row) |
| // %2 = extractvalue { i64, i8* } %coll_val, 0 |
| // %coll_is_null = trunc i64 %2 to i1 |
| // br i1 %coll_is_null, label %ret_null, label %check_count |
| // |
| // check_count: ; preds = %entry |
| // %3 = extractvalue { i64, i8* } %coll_val, 0 |
| // %4 = ashr i64 %3, 32 |
| // %5 = trunc i64 %4 to i32 |
| // %6 = icmp ne i32 %5, 0 |
| // %has_values_result = load i16, i16* %0 |
| // %7 = zext i1 %6 to i16 |
| // %8 = shl i16 %7, 8 |
| // %9 = and i16 %has_values_result, 255 |
| // %has_values_result1 = or i16 %9, %8 |
| // ret i16 %has_values_result1 |
| // |
| // ret_null: ; preds = %entry |
| // %null_result = load i16, i16* %1 |
| // ret i16 1 |
| // } |
| Status IsNotEmptyPredicate::GetCodegendComputeFnImpl( |
| LlvmCodeGen* codegen, llvm::Function** fn) { |
| // Create a method with the expected signature. |
| llvm::LLVMContext& context = codegen->context(); |
| llvm::Value* args[2]; |
| llvm::Function* new_fn = |
| CreateIrFunctionPrototype("IsNotEmptyPredicate", codegen, &args); |
| llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(context, "entry", new_fn); |
| LlvmBuilder builder(entry_block); |
| |
| ScalarExpr* child = children_[0]; // The child node, on which to call GetCollectionVal. |
| llvm::Function* get_collection_val_fn; |
| RETURN_IF_ERROR(child->GetCodegendComputeFn(codegen, false, &get_collection_val_fn)); |
| DCHECK(get_collection_val_fn != nullptr); |
| |
| // Find type for the CollectionVal struct. |
| llvm::Type* collection_type = codegen->GetNamedType("struct.impala_udf::CollectionVal"); |
| DCHECK(collection_type->isStructTy()); |
| |
| // Construct the call to the evaluation method, and return the result. |
| CodegenAnyVal coll_val = CodegenAnyVal::CreateCallWrapped(codegen, &builder, |
| child->type(), get_collection_val_fn, args, "coll_val"); |
| |
| // Find the 'is_null' field of the CollectionVal. |
| llvm::Value* is_null = coll_val.GetIsNull("coll_is_null"); |
| |
| llvm::BasicBlock* check_count = |
| llvm::BasicBlock::Create(context, "check_count", new_fn); |
| llvm::BasicBlock* ret_null = llvm::BasicBlock::Create(context, "ret_null", new_fn); |
| builder.CreateCondBr(is_null, ret_null, check_count); |
| |
| // Add code to the block that is executed if is_null was true. |
| builder.SetInsertPoint(ret_null); |
| // allocate a BooleanVal, set null in it, and return it. |
| CodegenAnyVal null_result(codegen, &builder, TYPE_BOOLEAN, nullptr, "null_result"); |
| builder.CreateRet(null_result.GetNullVal(codegen, TYPE_BOOLEAN)); |
| |
| // Back to the branch where 'is_null' is false. |
| builder.SetInsertPoint(check_count); |
| // Load the value of 'num_tuples'. |
| llvm::Value* num_tuples = coll_val.GetLen(); |
| |
| llvm::Value* has_values = builder.CreateICmpNE(num_tuples, codegen->GetI32Constant(0)); |
| CodegenAnyVal has_values_result( |
| codegen, &builder, TYPE_BOOLEAN, nullptr, "has_values_result"); |
| has_values_result.SetVal(has_values); |
| builder.CreateRet(has_values_result.GetLoweredValue()); |
| |
| *fn = codegen->FinalizeFunction(new_fn); |
| if (UNLIKELY(*fn == nullptr)) { |
| return Status(TErrorCode::IR_VERIFY_FAILED, "IsNotEmptyPredicate"); |
| } |
| return Status::OK(); |
| } |
| |
| string IsNotEmptyPredicate::DebugString() const { |
| return ScalarExpr::DebugString("IsNotEmptyPredicate"); |
| } |
| |
| } // namespace impala |