You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by ta...@apache.org on 2019/02/07 23:05:47 UTC

[impala] 01/04: IMPALA-7657: Codegen IsNotEmptyPredicate and ValidTupleIdExpr.

This is an automated email from the ASF dual-hosted git repository.

tarmstrong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git

commit 7707eb041728456ea5e05ae8acc5ab59c715b98a
Author: Andrew Sherman <as...@cloudera.com>
AuthorDate: Mon Jan 7 17:27:19 2019 -0800

    IMPALA-7657: Codegen IsNotEmptyPredicate and ValidTupleIdExpr.
    
    These two classes evaluate scalar expressions. Previously codegen was
    done by calling ScalarExpr::GetCodegendComputeFnWrapper which generates
    a static method that calls the scalar expression evaluation method. Make
    this more efficient by generating code which is customized using
    information available at codegen time.
    
    Add new cross-compiled files null-literal-ir.cc slot-ref-ir.cc
    
    IsNotEmptyPredicate works by getting a CollectionVal object from the
    single child Expr node, and counting its tuples. At codegen time we know
    the type and value of the child node. Generate a call to a node-specific
    non-virtual cross-compiled method to get the CollectionVal object from
    the child. Then generate a code that examines the CollectionVal and
    returns an IntVal.
    
    A ValidTupleIdExpr node contains a vector of tuple ids. It works by
    probing each row for the tuple ids in the vector to find a non-null
    tuple. At codegen time we know the vector of tuple ids. We unroll the
    loop through the tuple ids, generating code that evaluates if the tuple
    is non-null, and returns the tuple id if/when a non-null tuple is found.
    
    IMPALA-7657 also requests replacing GetCodegendComputeFnWrapper() in
    TupleIsNullPredicate. In the current Impala code this method is never
    called. This is because TupleIsNullPredicate is always wrapped in an
    IfExpr. This is always codegen'd by IfExpr's
    GetCodegendComputeFnWrapper() method. There is a separate Jira
    IMPALA-7655 to improve codegen of IfExpr.
    
    Minor corrections:
      Correct the link to llvm tutorial in LlvmCodegen.
    
    PERFORMANCE:
      I tested performance on a local mini-cluster. I wrote some
      pathological queries to test the new code. The new codegen'd code is
      very similar in performance. Both ValidTupleIdExpr and
      IsNotEmptyPredicate seem very slightly faster than the old code.
      Overall these changes are not purely for performance but to move away
      from GetCodegendComputeFnWrapper.
    
    TESTING:
      The changed scalar expressions are well exercised by current tests.
      Ran exhaustive end-to-end tests.
    
    Change-Id: Ifb87b9e3b879c278ce8638d97bcb320a7555a6b3
    Reviewed-on: http://gerrit.cloudera.org:8080/12068
    Reviewed-by: Impala Public Jenkins <im...@cloudera.com>
    Tested-by: Impala Public Jenkins <im...@cloudera.com>
---
 be/src/codegen/gen_ir_descriptions.py              |   4 +
 be/src/codegen/impala-ir.cc                        |   4 +-
 be/src/codegen/llvm-codegen.h                      |   3 +-
 be/src/exprs/CMakeLists.txt                        |   2 +
 be/src/exprs/is-not-empty-predicate.cc             | 156 ++++++++++++++++++++-
 be/src/exprs/is-not-empty-predicate.h              |   8 +-
 ...is-not-empty-predicate.h => null-literal-ir.cc} |  32 +----
 be/src/exprs/null-literal.cc                       |   8 +-
 be/src/exprs/null-literal.h                        |   2 +
 be/src/exprs/scalar-expr.h                         |   1 -
 .../{is-not-empty-predicate.h => slot-ref-ir.cc}   |  37 ++---
 be/src/exprs/slot-ref.cc                           |  16 +--
 be/src/exprs/slot-ref.h                            |   1 +
 be/src/exprs/valid-tuple-id.cc                     | 123 +++++++++++++++-
 be/src/exprs/valid-tuple-id.h                      |  11 +-
 15 files changed, 317 insertions(+), 91 deletions(-)

diff --git a/be/src/codegen/gen_ir_descriptions.py b/be/src/codegen/gen_ir_descriptions.py
index 57986ed..4829539 100755
--- a/be/src/codegen/gen_ir_descriptions.py
+++ b/be/src/codegen/gen_ir_descriptions.py
@@ -93,6 +93,10 @@ ir_functions = [
    "_ZN6impala10ScalarExpr15GetTimestampValEPS0_PNS_19ScalarExprEvaluatorEPKNS_8TupleRowE"],
   ["SCALAR_EXPR_GET_DECIMAL_VAL",
    "_ZN6impala10ScalarExpr13GetDecimalValEPS0_PNS_19ScalarExprEvaluatorEPKNS_8TupleRowE"],
+  ["SCALAR_EXPR_SLOT_REF_GET_COLLECTION_VAL",
+   "_ZNK6impala7SlotRef16GetCollectionValEPNS_19ScalarExprEvaluatorEPKNS_8TupleRowE"],
+  ["SCALAR_EXPR_NULL_LITERAL_GET_COLLECTION_VAL",
+   "_ZNK6impala11NullLiteral16GetCollectionValEPNS_19ScalarExprEvaluatorEPKNS_8TupleRowE"],
   ["HASH_CRC", "IrCrcHash"],
   ["HASH_MURMUR", "IrMurmurHash"],
   ["PHJ_PROCESS_BUILD_BATCH",
diff --git a/be/src/codegen/impala-ir.cc b/be/src/codegen/impala-ir.cc
index 520c9f3..52586f9 100644
--- a/be/src/codegen/impala-ir.cc
+++ b/be/src/codegen/impala-ir.cc
@@ -37,8 +37,8 @@
 #include "exec/select-node-ir.cc"
 #include "exec/topn-node-ir.cc"
 #include "exec/union-node-ir.cc"
-#include "exprs/aggregate-functions-ir.cc"
 #include "exprs/agg-fn-evaluator-ir.cc"
+#include "exprs/aggregate-functions-ir.cc"
 #include "exprs/bit-byte-functions-ir.cc"
 #include "exprs/cast-functions-ir.cc"
 #include "exprs/compound-predicates-ir.cc"
@@ -49,8 +49,10 @@
 #include "exprs/is-null-predicate-ir.cc"
 #include "exprs/like-predicate-ir.cc"
 #include "exprs/math-functions-ir.cc"
+#include "exprs/null-literal-ir.cc"
 #include "exprs/operators-ir.cc"
 #include "exprs/scalar-expr-ir.cc"
+#include "exprs/slot-ref-ir.cc"
 #include "exprs/string-functions-ir.cc"
 #include "exprs/timestamp-functions-ir.cc"
 #include "exprs/udf-builtins-ir.cc"
diff --git a/be/src/codegen/llvm-codegen.h b/be/src/codegen/llvm-codegen.h
index 33d4b61..97300a0 100644
--- a/be/src/codegen/llvm-codegen.h
+++ b/be/src/codegen/llvm-codegen.h
@@ -94,7 +94,6 @@ class LlvmBuilder : public llvm::IRBuilder<> {
   using llvm::IRBuilder<>::IRBuilder;
 };
 
-
 /// LLVM code generator.  This is the top level object to generate jitted code.
 //
 /// LLVM provides a c++ IR builder interface so IR does not need to be written
@@ -103,7 +102,7 @@ class LlvmBuilder : public llvm::IRBuilder<> {
 /// The llvm documentation is not fantastic and a lot of this was figured out
 /// by experimenting.  Thankfully, their API is pretty well designed so it's
 /// possible to get by without great documentation.  The llvm tutorial is very
-/// helpful, http://llvm.org/docs/tutorial/LangImpl1.html.  In this tutorial, they
+/// helpful, https://llvm.org/docs/tutorial/LangImpl01.html.  In this tutorial, they
 /// go over how to JIT an AST for a toy language they create.
 /// It is also helpful to use their online app that lets you compile c/c++ to IR.
 /// http://llvm.org/demo/index.cgi.
diff --git a/be/src/exprs/CMakeLists.txt b/be/src/exprs/CMakeLists.txt
index 734892d..6158d59 100644
--- a/be/src/exprs/CMakeLists.txt
+++ b/be/src/exprs/CMakeLists.txt
@@ -48,11 +48,13 @@ add_library(Exprs
   literal.cc
   math-functions-ir.cc
   null-literal.cc
+  null-literal-ir.cc
   operators-ir.cc
   scalar-expr.cc
   scalar-expr-evaluator.cc
   scalar-expr-ir.cc
   slot-ref.cc
+  slot-ref-ir.cc
   string-functions.cc
   string-functions-ir.cc
   timestamp-functions.cc
diff --git a/be/src/exprs/is-not-empty-predicate.cc b/be/src/exprs/is-not-empty-predicate.cc
index e0ac41b..9d5e277 100644
--- a/be/src/exprs/is-not-empty-predicate.cc
+++ b/be/src/exprs/is-not-empty-predicate.cc
@@ -18,16 +18,21 @@
 #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/slot-ref.h"
 
 namespace impala {
 
-IsNotEmptyPredicate::IsNotEmptyPredicate(const TExprNode& node)
-  : Predicate(node) {
-}
+const char* IsNotEmptyPredicate::LLVM_CLASS_NAME = "class.impala::IsNotEmptyPredicate";
+
+IsNotEmptyPredicate::IsNotEmptyPredicate(const TExprNode& node) : Predicate(node) {}
 
 BooleanVal IsNotEmptyPredicate::GetBooleanVal(
     ScalarExprEvaluator* eval, const TupleRow* row) const {
@@ -42,13 +47,150 @@ Status IsNotEmptyPredicate::Init(const RowDescriptor& row_desc, RuntimeState* st
   return Status::OK();
 }
 
-Status IsNotEmptyPredicate::GetCodegendComputeFn(LlvmCodeGen* codegen,
-    llvm::Function** fn) {
-  return GetCodegendComputeFnWrapper(codegen, fn);
+// Sample IR output (when child is a SlotRef): // FIXME needs review
+//
+//   define i16 @IsNotEmptyPredicate(%"class.impala::ScalarExprEvaluator"* %eval,
+//     %"class.impala::TupleRow"* %row) #38 {
+//   entry:
+//   %0 = alloca i16
+//   %1 = alloca i16
+//   %collection_val = alloca %"struct.impala_udf::CollectionVal"
+//   call void @_ZNK6impala7SlotRef16GetCollectionValEPNS_
+//     19ScalarExprEvaluatorEPKNS_8TupleRowE(%"struct.impala_udf::CollectionVal"*
+//     %collection_val, %"class.impala::SlotRef"* inttoptr (i64 140731643050560
+//     to %"class.impala::SlotRef"*),
+//     %"class.impala::ScalarExprEvaluator"* %eval, %"class.impala::TupleRow"* %row)
+//   %anyval_ptr = getelementptr inbounds %"struct.impala_udf::CollectionVal",
+//     %"struct.impala_udf::CollectionVal"* %collection_val, i32 0, i32 0
+//   %is_null_ptr = getelementptr inbounds %"struct.impala_udf::AnyVal",
+//     %"struct.impala_udf::AnyVal"* %anyval_ptr, i32 0, i32 0
+//   %is_null = load i8, i8* %is_null_ptr
+//   %is_null_bool = icmp ne i8 %is_null, 0
+//   %num_tuples_ptr = getelementptr inbounds %"struct.impala_udf::CollectionVal",
+//     %"struct.impala_udf::CollectionVal"* %collection_val, i32 0, i32 3
+//   %num_tuples = load i32, i32* %num_tuples_ptr
+//   br i1 %is_null_bool, label %ret_null, label %check_count
+//
+// check_count:                                      ; preds = %entry
+//   %4 = icmp ne i32 %num_tuples, 0
+//   %ret2 = load i16, i16* %0
+//   %5 = zext i1 %4 to i16
+//   %6 = shl i16 %5, 8
+//   %7 = and i16 %ret2, 255
+//   %ret21 = or i16 %7, %6
+//   ret i16 %ret21
+//
+// ret_null:                                         ; preds = %entry
+//   %ret = load i16, i16* %1
+//   ret i16 1
+// }
+//
+Status IsNotEmptyPredicate::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::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::Value* child_expr; //  a Value for 'child' with the correct subtype of ScalarExpr.
+  llvm::Function* get_collection_val_fn; // a type specific GetCollectionVal method
+
+  //  To save a virtual method call, find the type of child and lookup the non-virtual
+  //  GetCollection method to call.
+  if (dynamic_cast<SlotRef*>(child)) {
+    // Lookup SlotRef::GetCollectionVal().
+    get_collection_val_fn =
+        codegen->GetFunction(IRFunction::SCALAR_EXPR_SLOT_REF_GET_COLLECTION_VAL, false);
+    child_expr = codegen->CastPtrToLlvmPtr(
+        codegen->GetNamedPtrType(SlotRef::LLVM_CLASS_NAME), child);
+  } else if (dynamic_cast<NullLiteral*>(child)) {
+    // Lookup NullLiteral::GetCollectionVal().
+    get_collection_val_fn = codegen->GetFunction(
+        IRFunction::SCALAR_EXPR_NULL_LITERAL_GET_COLLECTION_VAL, false);
+    child_expr = codegen->CastPtrToLlvmPtr(
+        codegen->GetNamedPtrType(NullLiteral::LLVM_CLASS_NAME), child);
+  } else {
+    // This may mean someone implemented GetCollectionVal in a new subclass of ScalarExpr.
+    DCHECK(false) << "Unknown GetCollectionVal implementation: " << typeid(*child).name();
+    return Status("Codegen'd IsNotEmptyPredicate function found unknown GetCollectionVal,"
+                  " see log.");
+  }
+  DCHECK(get_collection_val_fn != nullptr);
+  DCHECK(child_expr != nullptr);
+
+  // Find type for the CollectionVal struct.
+  llvm::Type* collection_type = codegen->GetNamedType("struct.impala_udf::CollectionVal");
+  DCHECK(collection_type->isStructTy());
+
+  // Allocate space for the CollectionVal on the stack
+  llvm::Value* collection_val_ptr =
+      codegen->CreateEntryBlockAlloca(builder, collection_type, "collection_val");
+
+  // The get_collection_val_fn returns a CollectionVal by value. In llvm we have to pass a
+  // pointer as the first argument. The second argument is the object on which the call is
+  // made.
+  llvm::Value* get_coll_call_args[] = {collection_val_ptr, child_expr, args[0], args[1]};
+
+  // Construct the call to the evaluation method, and return the result.
+  llvm::Value* collection = builder.CreateCall(get_collection_val_fn, get_coll_call_args);
+  DCHECK(collection != nullptr);
+
+  // Find the 'is_null' field of the CollectionVal.
+  llvm::Value* anyval_ptr =
+      builder.CreateStructGEP(nullptr, collection_val_ptr, 0, "anyval_ptr");
+  llvm::Value* is_null_ptr =
+      builder.CreateStructGEP(nullptr, anyval_ptr, 0, "is_null_ptr");
+  llvm::Value* is_null = builder.CreateLoad(is_null_ptr, "is_null");
+
+  // Check if 'is_null' is true.
+  llvm::Value* is_null_bool =
+      builder.CreateICmpNE(is_null, codegen->GetI8Constant(0), "is_null_bool");
+
+  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_bool, 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_ptr =
+      builder.CreateStructGEP(nullptr, collection_val_ptr, 3, "num_tuples_ptr");
+  llvm::Value* num_tuples = builder.CreateLoad(num_tuples_ptr, "num_tuples");
+
+  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");
+  }
+
+  ir_compute_fn_ = *fn;
+
+  return Status::OK();
 }
 
 string IsNotEmptyPredicate::DebugString() const {
   return ScalarExpr::DebugString("IsNotEmptyPredicate");
 }
 
-}
+} // namespace impala
diff --git a/be/src/exprs/is-not-empty-predicate.h b/be/src/exprs/is-not-empty-predicate.h
index 0747a16..bf20350 100644
--- a/be/src/exprs/is-not-empty-predicate.h
+++ b/be/src/exprs/is-not-empty-predicate.h
@@ -28,8 +28,9 @@ class TExprNode;
 /// Predicate that checks whether a collection is empty or not.
 /// TODO: Implement this predicate via the UDF interface once the
 /// interface supports CollectionVals.
-class IsNotEmptyPredicate: public Predicate {
+class IsNotEmptyPredicate : public Predicate {
  public:
+  static const char* LLVM_CLASS_NAME;
   virtual Status GetCodegendComputeFn(LlvmCodeGen* codegen, llvm::Function** fn) override;
   virtual BooleanVal GetBooleanVal(ScalarExprEvaluator*, const TupleRow*) const override;
   virtual std::string DebugString() const override;
@@ -38,10 +39,9 @@ class IsNotEmptyPredicate: public Predicate {
   friend class ScalarExpr;
 
   virtual Status Init(const RowDescriptor& row_desc, RuntimeState* state) override;
-  IsNotEmptyPredicate(const TExprNode& node);
-
+  explicit IsNotEmptyPredicate(const TExprNode& node);
 };
 
-}
+} // namespace impala
 
 #endif
diff --git a/be/src/exprs/is-not-empty-predicate.h b/be/src/exprs/null-literal-ir.cc
similarity index 50%
copy from be/src/exprs/is-not-empty-predicate.h
copy to be/src/exprs/null-literal-ir.cc
index 0747a16..7c5fd86 100644
--- a/be/src/exprs/is-not-empty-predicate.h
+++ b/be/src/exprs/null-literal-ir.cc
@@ -15,33 +15,15 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#ifndef IMPALA_EXPRS_IS_NOT_EMPTY_PREDICATE_H_
-#define IMPALA_EXPRS_IS_NOT_EMPTY_PREDICATE_H_
-
-#include "exprs/predicate.h"
+#include "null-literal.h"
+#include "udf/udf.h"
 
 namespace impala {
 
-class ScalarExprEvaluator;
-class TExprNode;
-
-/// Predicate that checks whether a collection is empty or not.
-/// TODO: Implement this predicate via the UDF interface once the
-/// interface supports CollectionVals.
-class IsNotEmptyPredicate: public Predicate {
- public:
-  virtual Status GetCodegendComputeFn(LlvmCodeGen* codegen, llvm::Function** fn) override;
-  virtual BooleanVal GetBooleanVal(ScalarExprEvaluator*, const TupleRow*) const override;
-  virtual std::string DebugString() const override;
-
- protected:
-  friend class ScalarExpr;
-
-  virtual Status Init(const RowDescriptor& row_desc, RuntimeState* state) override;
-  IsNotEmptyPredicate(const TExprNode& node);
-
-};
-
+CollectionVal NullLiteral::GetCollectionVal(
+    ScalarExprEvaluator* eval, const TupleRow* row) const {
+  DCHECK(type_.IsCollectionType());
+  return CollectionVal::null();
 }
 
-#endif
+} // namespace impala
\ No newline at end of file
diff --git a/be/src/exprs/null-literal.cc b/be/src/exprs/null-literal.cc
index 8ce3a71..a03631a 100644
--- a/be/src/exprs/null-literal.cc
+++ b/be/src/exprs/null-literal.cc
@@ -29,6 +29,8 @@ using namespace impala_udf;
 
 namespace impala {
 
+const char* NullLiteral::LLVM_CLASS_NAME = "class.impala::NullLiteral";
+
 BooleanVal NullLiteral::GetBooleanVal(
     ScalarExprEvaluator* eval, const TupleRow* row) const {
   DCHECK_EQ(type_.type, TYPE_BOOLEAN) << type_;
@@ -89,12 +91,6 @@ DecimalVal NullLiteral::GetDecimalVal(
   return DecimalVal::null();
 }
 
-CollectionVal NullLiteral::GetCollectionVal(
-    ScalarExprEvaluator* eval, const TupleRow* row) const {
-  DCHECK(type_.IsCollectionType());
-  return CollectionVal::null();
-}
-
 // Generated IR for a bigint NULL literal:
 //
 // define { i8, i64 } @NullLiteral(
diff --git a/be/src/exprs/null-literal.h b/be/src/exprs/null-literal.h
index 91f4ca4..c7d9b10 100644
--- a/be/src/exprs/null-literal.h
+++ b/be/src/exprs/null-literal.h
@@ -47,6 +47,8 @@ class NullLiteral: public ScalarExpr {
   /// Constructor for test.
   NullLiteral(PrimitiveType type) : ScalarExpr(type, true) { }
 
+  static const char* LLVM_CLASS_NAME;
+
  protected:
   friend class ScalarExpr;
   friend class ScalarExprEvaluator;
diff --git a/be/src/exprs/scalar-expr.h b/be/src/exprs/scalar-expr.h
index 6eb221e..54b0d9a 100644
--- a/be/src/exprs/scalar-expr.h
+++ b/be/src/exprs/scalar-expr.h
@@ -208,7 +208,6 @@ class ScalarExpr : public Expr {
   friend class ExprTest;
   friend class ExprCodegenTest;
   friend class HashTableTest;
-  friend class OldHashTableTest;
 
   /// Cached LLVM IR for the compute function. Set this in GetCodegendComputeFn().
   llvm::Function* ir_compute_fn_ = nullptr;
diff --git a/be/src/exprs/is-not-empty-predicate.h b/be/src/exprs/slot-ref-ir.cc
similarity index 50%
copy from be/src/exprs/is-not-empty-predicate.h
copy to be/src/exprs/slot-ref-ir.cc
index 0747a16..c9bdd4f 100644
--- a/be/src/exprs/is-not-empty-predicate.h
+++ b/be/src/exprs/slot-ref-ir.cc
@@ -15,33 +15,20 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#ifndef IMPALA_EXPRS_IS_NOT_EMPTY_PREDICATE_H_
-#define IMPALA_EXPRS_IS_NOT_EMPTY_PREDICATE_H_
-
-#include "exprs/predicate.h"
+#include "exprs/slot-ref.h"
+#include "runtime/collection-value.h"
+#include "runtime/tuple-row.h"
 
 namespace impala {
 
-class ScalarExprEvaluator;
-class TExprNode;
-
-/// Predicate that checks whether a collection is empty or not.
-/// TODO: Implement this predicate via the UDF interface once the
-/// interface supports CollectionVals.
-class IsNotEmptyPredicate: public Predicate {
- public:
-  virtual Status GetCodegendComputeFn(LlvmCodeGen* codegen, llvm::Function** fn) override;
-  virtual BooleanVal GetBooleanVal(ScalarExprEvaluator*, const TupleRow*) const override;
-  virtual std::string DebugString() const override;
-
- protected:
-  friend class ScalarExpr;
-
-  virtual Status Init(const RowDescriptor& row_desc, RuntimeState* state) override;
-  IsNotEmptyPredicate(const TExprNode& node);
-
-};
-
+CollectionVal SlotRef::GetCollectionVal(
+    ScalarExprEvaluator* eval, const TupleRow* row) const {
+  DCHECK(type_.IsCollectionType());
+  Tuple* t = row->GetTuple(tuple_idx_);
+  if (t == NULL || t->IsNull(null_indicator_offset_)) return CollectionVal::null();
+  CollectionValue* coll_value =
+      reinterpret_cast<CollectionValue*>(t->GetSlot(slot_offset_));
+  return CollectionVal(coll_value->ptr, coll_value->num_tuples);
 }
 
-#endif
+} // namespace impala
\ No newline at end of file
diff --git a/be/src/exprs/slot-ref.cc b/be/src/exprs/slot-ref.cc
index 00616b3..62a119a 100644
--- a/be/src/exprs/slot-ref.cc
+++ b/be/src/exprs/slot-ref.cc
@@ -38,6 +38,8 @@ using namespace impala_udf;
 
 namespace impala {
 
+const char* SlotRef::LLVM_CLASS_NAME = "class.impala::SlotRef";
+
 SlotRef::SlotRef(const TExprNode& node)
   : ScalarExpr(node),
     slot_offset_(-1),  // invalid
@@ -155,7 +157,7 @@ string SlotRef::DebugString() const {
 // }
 //
 // TODO: We could generate a typed struct (and not a char*) for Tuple for llvm.  We know
-// the types from the TupleDesc.  It will likey make this code simpler to reason about.
+// the types from the TupleDesc.  It will likely make this code simpler to reason about.
 Status SlotRef::GetCodegendComputeFn(LlvmCodeGen* codegen, llvm::Function** fn) {
   if (type_.type == TYPE_CHAR) {
     *fn = NULL;
@@ -475,14 +477,4 @@ DecimalVal SlotRef::GetDecimalVal(
   }
 }
 
-CollectionVal SlotRef::GetCollectionVal(
-    ScalarExprEvaluator* eval, const TupleRow* row) const {
-  DCHECK(type_.IsCollectionType());
-  Tuple* t = row->GetTuple(tuple_idx_);
-  if (t == NULL || t->IsNull(null_indicator_offset_)) return CollectionVal::null();
-  CollectionValue* coll_value =
-      reinterpret_cast<CollectionValue*>(t->GetSlot(slot_offset_));
-  return CollectionVal(coll_value->ptr, coll_value->num_tuples);
-}
-
-}
+} // namespace impala
diff --git a/be/src/exprs/slot-ref.h b/be/src/exprs/slot-ref.h
index 82d25ef..f26012e 100644
--- a/be/src/exprs/slot-ref.h
+++ b/be/src/exprs/slot-ref.h
@@ -56,6 +56,7 @@ class SlotRef : public ScalarExpr {
   virtual bool IsSlotRef() const override { return true; }
   virtual int GetSlotIds(std::vector<SlotId>* slot_ids) const override;
   const SlotId& slot_id() const { return slot_id_; }
+  static const char* LLVM_CLASS_NAME;
 
  protected:
   friend class ScalarExpr;
diff --git a/be/src/exprs/valid-tuple-id.cc b/be/src/exprs/valid-tuple-id.cc
index 84cdf2f..d450aad 100644
--- a/be/src/exprs/valid-tuple-id.cc
+++ b/be/src/exprs/valid-tuple-id.cc
@@ -19,14 +19,16 @@
 
 #include <sstream>
 
-#include "gen-cpp/Exprs_types.h"
-
+#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) {
@@ -39,8 +41,7 @@ Status ValidTupleIdExpr::Init(const RowDescriptor& row_desc, RuntimeState* state
   return Status::OK();
 }
 
-int ValidTupleIdExpr::ComputeNonNullCount(const TupleRow* row) const {
-  int num_tuples = tuple_ids_.size();
+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;
@@ -48,16 +49,126 @@ int ValidTupleIdExpr::ComputeNonNullCount(const TupleRow* row) const {
 
 IntVal ValidTupleIdExpr::GetIntVal(ScalarExprEvaluator* eval, const TupleRow* row) const {
   // Validate that exactly one tuple is non-NULL.
-  DCHECK_EQ(1, ComputeNonNullCount(row));
   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) {
-  return GetCodegendComputeFnWrapper(codegen, 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 {
diff --git a/be/src/exprs/valid-tuple-id.h b/be/src/exprs/valid-tuple-id.h
index 7eb03d9..7de6af0 100644
--- a/be/src/exprs/valid-tuple-id.h
+++ b/be/src/exprs/valid-tuple-id.h
@@ -27,10 +27,13 @@ class TExprNode;
 /// Returns the tuple id of the single non-NULL tuple in the input row.
 /// Valid input rows must have exactly one non-NULL tuple.
 class ValidTupleIdExpr : public ScalarExpr {
+ public:
+  static const char* LLVM_CLASS_NAME;
+
  protected:
   friend class ScalarExpr;
 
-  ValidTupleIdExpr(const TExprNode& node);
+  explicit ValidTupleIdExpr(const TExprNode& node);
 
   virtual Status Init(const RowDescriptor& row_desc, RuntimeState* state) override;
   virtual Status GetCodegendComputeFn(
@@ -44,7 +47,11 @@ class ValidTupleIdExpr : public ScalarExpr {
   std::vector<TupleId> tuple_ids_;
 
   /// Returns the number of tuples in 'row' that are non-null. Used for debugging.
-  int ComputeNonNullCount(const TupleRow* row) const;
+  static int ComputeNonNullCount(const TupleRow* row, int num_tuples);
+
+  /// Returns true if the tuple of the row at the specified index is non-null.
+  /// Called by Codegen.
+  static bool IsTupleFromRowNonNull(const TupleRow* row, int index);
 };
 
 } // namespace impala