| // 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/valid-tuple-id.h" |
| |
| #include <sstream> |
| |
| #include <codegen/codegen-anyval.h> |
| #include "codegen/llvm-codegen.h" |
| #include "common/names.h" |
| #include "runtime/descriptors.h" |
| #include "runtime/tuple-row.h" |
| |
| namespace impala { |
| |
| const char* ValidTupleIdExpr::LLVM_CLASS_NAME = "class.impala::ValidTupleIdExpr"; |
| |
| ValidTupleIdExpr::ValidTupleIdExpr(const TExprNode& node) : ScalarExpr(node) {} |
| |
| Status ValidTupleIdExpr::Init( |
| const RowDescriptor& row_desc, bool is_entry_point, RuntimeState* state) { |
| RETURN_IF_ERROR(ScalarExpr::Init(row_desc, is_entry_point, state)); |
| DCHECK_EQ(0, children_.size()); |
| tuple_ids_.reserve(row_desc.tuple_descriptors().size()); |
| for (TupleDescriptor* tuple_desc : row_desc.tuple_descriptors()) { |
| tuple_ids_.push_back(tuple_desc->id()); |
| } |
| return Status::OK(); |
| } |
| |
| int ValidTupleIdExpr::ComputeNonNullCount(const TupleRow* row, int num_tuples) { |
| int non_null_count = 0; |
| for (int i = 0; i < num_tuples; ++i) non_null_count += (row->GetTuple(i) != nullptr); |
| return non_null_count; |
| } |
| |
| IntVal ValidTupleIdExpr::GetIntValInterpreted( |
| ScalarExprEvaluator* eval, const TupleRow* row) const { |
| // Validate that exactly one tuple is non-NULL. |
| int num_tuples = tuple_ids_.size(); |
| DCHECK_EQ(1, ComputeNonNullCount(row, num_tuples)); |
| for (int i = 0; i < num_tuples; ++i) { |
| if (row->GetTuple(i) != nullptr) return IntVal(tuple_ids_[i]); |
| } |
| return IntVal::null(); |
| } |
| |
| // Sample IR output (with 3 tuple ids in the vector): |
| // |
| // define i64 @ValidTupleId(%"class.impala::ScalarExprEvaluator"* %eval, |
| // %"class.impala::TupleRow"* %row) #48 { |
| // entry: |
| // %0 = alloca i64 |
| // %1 = alloca i64 |
| // %2 = alloca i64 |
| // %tuple_row_ptr = getelementptr inbounds %"class.impala::TupleRow", |
| // %"class.impala::TupleRow"* %row, i32 0 |
| // %tuple_ptr = bitcast %"class.impala::TupleRow"* %tuple_row_ptr to |
| // %"class.impala::Tuple"** |
| // %tuple_val = load %"class.impala::Tuple"*, %"class.impala::Tuple"** %tuple_ptr |
| // %is_not_null = icmp ne %"class.impala::Tuple"* %tuple_val, null |
| // br i1 %is_not_null, label %return, label %next |
| // |
| // next: ; preds = %entry |
| // %tuple_row_ptr2 = getelementptr inbounds %"class.impala::TupleRow", |
| // %"class.impala::TupleRow"* %row, i32 1 |
| // %tuple_ptr3 = bitcast %"class.impala::TupleRow"* %tuple_row_ptr2 to |
| // %"class.impala::Tuple"** |
| // %tuple_val4 = load %"class.impala::Tuple"*, %"class.impala::Tuple"** %tuple_ptr3 |
| // %is_not_null5 = icmp ne %"class.impala::Tuple"* %tuple_val4, null |
| // br i1 %is_not_null5, label %return7, label %next6 |
| // |
| // return: ; preds = %entry |
| // %ret = load i64, i64* %2 |
| // %3 = and i64 %ret, 4294967295 |
| // %ret1 = or i64 %3, 4294967296 -- 1 |
| // ret i64 %ret1 |
| // |
| // next6: ; preds = %next |
| // %tuple_row_ptr10 = getelementptr inbounds %"class.impala::TupleRow", |
| // %"class.impala::TupleRow"* %row, i32 2 |
| // %tuple_ptr11 = bitcast %"class.impala::TupleRow"* %tuple_row_ptr10 to |
| // %"class.impala::Tuple"** |
| // %tuple_val12 = load %"class.impala::Tuple"*, %"class.impala::Tuple"** %tuple_ptr11 |
| // %is_not_null13 = icmp ne %"class.impala::Tuple"* %tuple_val12, null |
| // br i1 %is_not_null13, label %return15, label %next14 |
| // |
| // return7: ; preds = %next |
| // %ret8 = load i64, i64* %1 |
| // %4 = and i64 %ret8, 4294967295 |
| // %ret9 = or i64 %4, 12884901888 -- 3 |
| // ret i64 %ret9 |
| // |
| // next14: ; preds = %next6 |
| // ret i64 1 |
| // |
| // return15: ; preds = %next6 |
| // %ret16 = load i64, i64* %0 |
| // %5 = and i64 %ret16, 4294967295 |
| // %ret17 = or i64 %5, 21474836480 -- 5 |
| // ret i64 %ret17 |
| // } |
| // |
| Status ValidTupleIdExpr::GetCodegendComputeFnImpl( |
| LlvmCodeGen* codegen, llvm::Function** fn) { |
| // Create a method with the expected signature. |
| llvm::Value* args[2]; |
| llvm::Function* new_fn = CreateIrFunctionPrototype("ValidTupleId", codegen, &args); |
| llvm::LLVMContext& context = codegen->context(); |
| llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(context, "entry", new_fn); |
| LlvmBuilder builder(entry_block); |
| llvm::Value* row_ptr = args[1]; |
| |
| // Unroll the loop. |
| for (uint32_t i = 0; i < tuple_ids_.size(); i++) { |
| // Get the i'th Tuple* from the row. |
| llvm::Value* tuple_row_ptr = |
| builder.CreateInBoundsGEP(row_ptr, codegen->GetI32Constant(i), "tuple_row_ptr"); |
| // Cast to Tuple** |
| llvm::Type* tuple_ptr_ptr_type = |
| codegen->GetPtrType(codegen->GetNamedPtrType(Tuple::LLVM_CLASS_NAME)); |
| llvm::Value* tuple_ptr_ptr = |
| builder.CreateBitCast(tuple_row_ptr, tuple_ptr_ptr_type, "tuple_ptr_ptr"); |
| // Get the Tuple* and compare to nullptr. |
| llvm::Value* tuple_ptr = builder.CreateLoad(tuple_ptr_ptr, "tuple_ptr"); |
| llvm::Value* is_not_null = builder.CreateIsNotNull(tuple_ptr, "is_not_null"); |
| |
| // Create a conditional on the result. |
| llvm::BasicBlock* next = llvm::BasicBlock::Create(context, "next", new_fn); |
| llvm::BasicBlock* ret_block = llvm::BasicBlock::Create(context, "return", new_fn); |
| builder.CreateCondBr(is_not_null, ret_block, next); |
| |
| // Add code to the block that is executed if the Tuple* was not a nullptr. |
| builder.SetInsertPoint(ret_block); |
| // Generate code returning an IntVal containing the tuple id. |
| CodegenAnyVal result(codegen, &builder, TYPE_INT, nullptr, "ret"); |
| llvm::Constant* tuple_id = |
| codegen->GetI32Constant(static_cast<uint64_t>(tuple_ids_[i])); |
| result.SetVal(tuple_id); |
| builder.CreateRet(result.GetLoweredValue()); |
| |
| // Add code to the block that is executed if the Tuple* was null. |
| builder.SetInsertPoint(next); |
| } |
| // We fell out of the loop, return null. |
| builder.CreateRet(CodegenAnyVal::GetNullVal(codegen, type())); |
| |
| *fn = codegen->FinalizeFunction(new_fn); |
| if (UNLIKELY(*fn == nullptr)) { |
| return Status(TErrorCode::IR_VERIFY_FAILED, "ValidTupleId"); |
| } |
| return Status::OK(); |
| } |
| |
| string ValidTupleIdExpr::DebugString() const { |
| return "ValidTupleId()"; |
| } |
| |
| } // namespace impala |