blob: 2884dcb4075226e4fa0cc9d4875cd91f47a3d697 [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 "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