text: // 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, RuntimeState* state) {
    RETURN_IF_ERROR(ScalarExpr::Init(row_desc, 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::GetIntVal(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::GetCodegendComputeFn(LlvmCodeGen* codegen, llvm::Function** fn) {
    if (ir_compute_fn_ != nullptr) {
      *fn = ir_compute_fn_;
      return Status::OK();
    }
  
    // 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");
    }
  
    ir_compute_fn_ = *fn;
  
    return Status::OK();
  }
  
  string ValidTupleIdExpr::DebugString() const {
    return "ValidTupleId()";
  }
  
  } // namespace impala
  