You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by kw...@apache.org on 2017/02/13 19:23:27 UTC

[4/4] incubator-impala git commit: IMPALA-2020, IMPALA-4809: Codegen support for DECIMAL_V2

IMPALA-2020, IMPALA-4809: Codegen support for DECIMAL_V2

Currently, codegen supports converting type attributes (e.g.
decimal type's precision and scale, type's size) obtained via
calls to FunctionContextImpl::GetFnAttr() (previously
Expr::GetConstantInt()) in cross-compiled code to runtime constants.
This change extends this support for the query option DECIMAL_V2.

To test this change, this change also handles a subset of IMPALA-2020:
casting between decimal values is  updated to support rounding (instead
of truncation) when decimal_v2 is true.

This change also cleans up the existing code by moving the codegen
logic Expr::InlineConstant() to the codegen module and the type
related logic in Expr::GetConstantInt() to FunctionContextImpl.

Change-Id: I2434d240f65b81389b8a8ba027f980a0e1d1f981
Reviewed-on: http://gerrit.cloudera.org:8080/5950
Reviewed-by: Michael Ho <kw...@cloudera.com>
Tested-by: Impala Public Jenkins


Project: http://git-wip-us.apache.org/repos/asf/incubator-impala/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-impala/commit/f982c3f7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-impala/tree/f982c3f7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-impala/diff/f982c3f7

Branch: refs/heads/master
Commit: f982c3f76e762f646b52a01659796b4edcfbc1ad
Parents: a78726d
Author: Michael Ho <kw...@cloudera.com>
Authored: Fri Feb 3 20:55:18 2017 -0800
Committer: Impala Public Jenkins <im...@gerrit.cloudera.org>
Committed: Sat Feb 11 07:07:45 2017 +0000

----------------------------------------------------------------------
 be/src/benchmarks/hash-benchmark.cc             |  18 +-
 be/src/codegen/llvm-codegen-test.cc             |  60 +++--
 be/src/codegen/llvm-codegen.cc                  |  98 +++++++--
 be/src/codegen/llvm-codegen.h                   |  48 ++--
 be/src/exec/partitioned-aggregation-node.cc     |   8 +-
 be/src/exec/partitioned-aggregation-node.h      |   2 +-
 be/src/exprs/agg-fn-evaluator.cc                |   2 +-
 be/src/exprs/aggregate-functions-ir.cc          |  12 +-
 be/src/exprs/conditional-functions-ir.cc        |  27 ++-
 be/src/exprs/decimal-functions-ir.cc            |  78 +++----
 be/src/exprs/decimal-operators-ir.cc            | 219 ++++++++++---------
 be/src/exprs/decimal-operators.h                |   2 +-
 be/src/exprs/expr-codegen-test.cc               | 152 ++++++++-----
 be/src/exprs/expr-test.cc                       |  12 +-
 be/src/exprs/expr.cc                            |  86 --------
 be/src/exprs/expr.h                             |  59 -----
 be/src/exprs/math-functions-ir.cc               |   4 +-
 be/src/exprs/scalar-fn-call.cc                  |   4 +-
 be/src/runtime/decimal-value.inline.h           |   3 +-
 be/src/runtime/lib-cache.cc                     |   9 +-
 be/src/runtime/runtime-state.cc                 |   2 +-
 be/src/runtime/runtime-state.h                  |   1 +
 be/src/service/frontend.cc                      |   2 +-
 be/src/udf/udf-internal.h                       |  47 +++-
 be/src/udf/udf-test-harness.cc                  |   5 +-
 be/src/udf/udf-test-harness.h                   |   4 +-
 be/src/udf/udf.cc                               |  60 ++++-
 .../queries/QueryTest/decimal.test              |  44 ++++
 tests/query_test/test_decimal_casting.py        |  70 +++---
 29 files changed, 646 insertions(+), 492 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/benchmarks/hash-benchmark.cc
----------------------------------------------------------------------
diff --git a/be/src/benchmarks/hash-benchmark.cc b/be/src/benchmarks/hash-benchmark.cc
index 125310b..fb37bed 100644
--- a/be/src/benchmarks/hash-benchmark.cc
+++ b/be/src/benchmarks/hash-benchmark.cc
@@ -26,6 +26,7 @@
 #include "experiments/data-provider.h"
 #include "runtime/mem-tracker.h"
 #include "runtime/string-value.h"
+#include "runtime/test-env.h"
 #include "util/benchmark.h"
 #include "util/cpu-info.h"
 #include "util/hash-util.h"
@@ -419,17 +420,24 @@ int main(int argc, char **argv) {
 
   const int NUM_ROWS = 1024;
 
-  ObjectPool obj_pool;
+  Status status;
+  RuntimeState* state;
+  TestEnv test_env;
+  status = test_env.CreateQueryState(0, 0, 0, nullptr, &state);
+  if (!status.ok()) {
+    cout << "Could not create RuntimeState";
+    return -1;
+  }
+
   MemTracker tracker;
   MemPool mem_pool(&tracker);
-  RuntimeProfile int_profile(&obj_pool, "IntGen");
-  RuntimeProfile mixed_profile(&obj_pool, "MixedGen");
+  RuntimeProfile int_profile(state->obj_pool(), "IntGen");
+  RuntimeProfile mixed_profile(state->obj_pool(), "MixedGen");
   DataProvider int_provider(&mem_pool, &int_profile);
   DataProvider mixed_provider(&mem_pool, &mixed_profile);
 
-  Status status;
   scoped_ptr<LlvmCodeGen> codegen;
-  status = LlvmCodeGen::CreateImpalaCodegen(&obj_pool, NULL, "test", &codegen);
+  status = LlvmCodeGen::CreateImpalaCodegen(state, NULL, "test", &codegen);
   if (!status.ok()) {
     cout << "Could not start codegen.";
     return -1;

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/codegen/llvm-codegen-test.cc
----------------------------------------------------------------------
diff --git a/be/src/codegen/llvm-codegen-test.cc b/be/src/codegen/llvm-codegen-test.cc
index 412426f..59c66f4 100644
--- a/be/src/codegen/llvm-codegen-test.cc
+++ b/be/src/codegen/llvm-codegen-test.cc
@@ -24,6 +24,8 @@
 #include "common/init.h"
 #include "common/object-pool.h"
 #include "runtime/string-value.h"
+#include "runtime/test-env.h"
+#include "service/fe-support.h"
 #include "util/cpu-info.h"
 #include "util/hash-util.h"
 #include "util/path-builder.h"
@@ -38,13 +40,27 @@ namespace impala {
 
 class LlvmCodeGenTest : public testing:: Test {
  protected:
+  scoped_ptr<TestEnv> test_env_;
+  RuntimeState* runtime_state_;
+
+  virtual void SetUp() {
+    test_env_.reset(new TestEnv());
+    EXPECT_OK(test_env_->CreateQueryState(0, 1, 8 * 1024 * 1024, nullptr,
+        &runtime_state_));
+  }
+
+  virtual void TearDown() {
+    runtime_state_ = NULL;
+    test_env_.reset();
+  }
+
   static void LifetimeTest() {
     ObjectPool pool;
     Status status;
     for (int i = 0; i < 10; ++i) {
-      LlvmCodeGen object1(&pool, NULL, "Test");
-      LlvmCodeGen object2(&pool, NULL, "Test");
-      LlvmCodeGen object3(&pool, NULL, "Test");
+      LlvmCodeGen object1(NULL, &pool, NULL, "Test");
+      LlvmCodeGen object2(NULL, &pool, NULL, "Test");
+      LlvmCodeGen object3(NULL, &pool, NULL, "Test");
 
       ASSERT_OK(object1.Init(unique_ptr<Module>(new Module("Test", object1.context()))));
       ASSERT_OK(object2.Init(unique_ptr<Module>(new Module("Test", object2.context()))));
@@ -53,22 +69,12 @@ class LlvmCodeGenTest : public testing:: Test {
   }
 
   // Wrapper to call private test-only methods on LlvmCodeGen object
-  static Status CreateFromFile(
-      ObjectPool* pool, const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
-    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(pool, NULL, filename, "test", codegen));
+  Status CreateFromFile(const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
+    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(runtime_state_,
+        runtime_state_->obj_pool(), NULL, filename, "test", codegen));
     return (*codegen)->MaterializeModule();
   }
 
-  static LlvmCodeGen* CreateCodegen(ObjectPool* pool) {
-    LlvmCodeGen* codegen = pool->Add(new LlvmCodeGen(pool, NULL, "Test"));
-    if (codegen != NULL) {
-      Status status =
-          codegen->Init(unique_ptr<Module>(new Module("Test", codegen->context())));
-      if (!status.ok()) return NULL;
-    }
-    return codegen;
-  }
-
   static void ClearHashFns(LlvmCodeGen* codegen) {
     codegen->ClearHashFns();
   }
@@ -104,11 +110,9 @@ TEST_F(LlvmCodeGenTest, MultithreadedLifetime) {
 
 // Test loading a non-existent file
 TEST_F(LlvmCodeGenTest, BadIRFile) {
-  ObjectPool pool;
   string module_file = "NonExistentFile.ir";
   scoped_ptr<LlvmCodeGen> codegen;
-  EXPECT_FALSE(
-      LlvmCodeGenTest::CreateFromFile(&pool, module_file.c_str(), &codegen).ok());
+  EXPECT_FALSE(CreateFromFile(module_file.c_str(), &codegen).ok());
 }
 
 // IR for the generated linner loop
@@ -159,7 +163,6 @@ Function* CodegenInnerLoop(LlvmCodeGen* codegen, int64_t* jitted_counter, int de
 //   5. Updated the jitted loop in place with another jitted inner loop function
 //   6. Run the loop and make sure the updated is called.
 TEST_F(LlvmCodeGenTest, ReplaceFnCall) {
-  ObjectPool pool;
   const string loop_call_name("_Z21DefaultImplementationv");
   const string loop_name("_Z8TestLoopi");
   typedef void (*TestLoopFn)(int);
@@ -169,7 +172,7 @@ TEST_F(LlvmCodeGenTest, ReplaceFnCall) {
 
   // Part 1: Load the module and make sure everything is loaded correctly.
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(LlvmCodeGenTest::CreateFromFile(&pool, module_file.c_str(), &codegen));
+  ASSERT_OK(CreateFromFile(module_file.c_str(), &codegen));
   EXPECT_TRUE(codegen.get() != NULL);
 
   Function* loop_call = codegen->GetFunction(loop_call_name, false);
@@ -285,10 +288,8 @@ Function* CodegenStringTest(LlvmCodeGen* codegen) {
 // struct.  Just create a simple StringValue struct and make sure the IR can read it
 // and modify it.
 TEST_F(LlvmCodeGenTest, StringValue) {
-  ObjectPool pool;
-
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen));
+  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(runtime_state_, NULL, "test", &codegen));
   EXPECT_TRUE(codegen.get() != NULL);
 
   string str("Test");
@@ -328,10 +329,8 @@ TEST_F(LlvmCodeGenTest, StringValue) {
 
 // Test calling memcpy intrinsic
 TEST_F(LlvmCodeGenTest, MemcpyTest) {
-  ObjectPool pool;
-
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen));
+  ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(runtime_state_, NULL, "test", &codegen));
   ASSERT_TRUE(codegen.get() != NULL);
 
   LlvmCodeGen::FnPrototype prototype(codegen.get(), "MemcpyTest", codegen->void_type());
@@ -367,8 +366,6 @@ TEST_F(LlvmCodeGenTest, MemcpyTest) {
 
 // Test codegen for hash
 TEST_F(LlvmCodeGenTest, HashTest) {
-  ObjectPool pool;
-
   // Values to compute hash on
   const char* data1 = "test string";
   const char* data2 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -378,7 +375,7 @@ TEST_F(LlvmCodeGenTest, HashTest) {
   // Loop to test both the sse4 on/off paths
   for (int i = 0; i < 2; ++i) {
     scoped_ptr<LlvmCodeGen> codegen;
-    ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen));
+    ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(runtime_state_, NULL, "test", &codegen));
     ASSERT_TRUE(codegen.get() != NULL);
 
     Value* llvm_data1 =
@@ -452,7 +449,8 @@ TEST_F(LlvmCodeGenTest, HashTest) {
 
 int main(int argc, char **argv) {
   ::testing::InitGoogleTest(&argc, argv);
-  impala::InitCommonRuntime(argc, argv, false, impala::TestInfo::BE_TEST);
+  impala::InitCommonRuntime(argc, argv, true, impala::TestInfo::BE_TEST);
+  impala::InitFeSupport(false);
   impala::LlvmCodeGen::InitializeLlvm();
   return RUN_ALL_TESTS();
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/codegen/llvm-codegen.cc
----------------------------------------------------------------------
diff --git a/be/src/codegen/llvm-codegen.cc b/be/src/codegen/llvm-codegen.cc
index 6cf43f6..a548654 100644
--- a/be/src/codegen/llvm-codegen.cc
+++ b/be/src/codegen/llvm-codegen.cc
@@ -58,12 +58,14 @@
 #include "codegen/instruction-counter.h"
 #include "codegen/mcjit-mem-mgr.h"
 #include "common/logging.h"
+#include "exprs/anyval-util.h"
 #include "impala-ir/impala-ir-names.h"
 #include "runtime/descriptors.h"
 #include "runtime/hdfs-fs-cache.h"
 #include "runtime/lib-cache.h"
 #include "runtime/mem-pool.h"
 #include "runtime/mem-tracker.h"
+#include "runtime/runtime-state.h"
 #include "runtime/string-value.h"
 #include "runtime/timestamp-value.h"
 #include "util/cpu-info.h"
@@ -170,7 +172,8 @@ Status LlvmCodeGen::InitializeLlvm(bool load_backend) {
 
   ObjectPool init_pool;
   scoped_ptr<LlvmCodeGen> init_codegen;
-  RETURN_IF_ERROR(LlvmCodeGen::CreateFromMemory(&init_pool, NULL, "init", &init_codegen));
+  RETURN_IF_ERROR(LlvmCodeGen::CreateFromMemory(
+      nullptr, &init_pool, nullptr, "init", &init_codegen));
   // LLVM will construct "use" lists only when the entire module is materialized.
   RETURN_IF_ERROR(init_codegen->MaterializeModule());
 
@@ -209,9 +212,10 @@ Status LlvmCodeGen::InitializeLlvm(bool load_backend) {
   return Status::OK();
 }
 
-LlvmCodeGen::LlvmCodeGen(
-    ObjectPool* pool, MemTracker* parent_mem_tracker, const string& id)
-  : id_(id),
+LlvmCodeGen::LlvmCodeGen(RuntimeState* state, ObjectPool* pool,
+    MemTracker* parent_mem_tracker, const string& id)
+  : state_(state),
+    id_(id),
     profile_(pool, "CodeGen"),
     mem_tracker_(new MemTracker(&profile_, -1, "CodeGen", parent_mem_tracker)),
     optimizations_enabled_(false),
@@ -233,9 +237,10 @@ LlvmCodeGen::LlvmCodeGen(
   num_instructions_ = ADD_COUNTER(&profile_, "NumInstructions", TUnit::UNIT);
 }
 
-Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, MemTracker* parent_mem_tracker,
-    const string& file, const string& id, scoped_ptr<LlvmCodeGen>* codegen) {
-  codegen->reset(new LlvmCodeGen(pool, parent_mem_tracker, id));
+Status LlvmCodeGen::CreateFromFile(RuntimeState* state, ObjectPool* pool,
+    MemTracker* parent_mem_tracker, const string& file, const string& id,
+    scoped_ptr<LlvmCodeGen>* codegen) {
+  codegen->reset(new LlvmCodeGen(state, pool, parent_mem_tracker, id));
   SCOPED_TIMER((*codegen)->profile_.total_time_counter());
 
   unique_ptr<Module> loaded_module;
@@ -244,9 +249,9 @@ Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, MemTracker* parent_mem_trac
   return (*codegen)->Init(std::move(loaded_module));
 }
 
-Status LlvmCodeGen::CreateFromMemory(ObjectPool* pool, MemTracker* parent_mem_tracker,
-    const string& id, scoped_ptr<LlvmCodeGen>* codegen) {
-  codegen->reset(new LlvmCodeGen(pool, parent_mem_tracker, id));
+Status LlvmCodeGen::CreateFromMemory(RuntimeState* state, ObjectPool* pool,
+    MemTracker* parent_mem_tracker, const string& id, scoped_ptr<LlvmCodeGen>* codegen) {
+  codegen->reset(new LlvmCodeGen(state, pool, parent_mem_tracker, id));
   SCOPED_TIMER((*codegen)->profile_.total_time_counter());
 
   // Select the appropriate IR version. We cannot use LLVM IR with SSE4.2 instructions on
@@ -355,9 +360,12 @@ void LlvmCodeGen::StripGlobalCtorsDtors(llvm::Module* module) {
   if (destructors != NULL) destructors->eraseFromParent();
 }
 
-Status LlvmCodeGen::CreateImpalaCodegen(ObjectPool* pool, MemTracker* parent_mem_tracker,
-    const string& id, scoped_ptr<LlvmCodeGen>* codegen_ret) {
-  RETURN_IF_ERROR(CreateFromMemory(pool, parent_mem_tracker, id, codegen_ret));
+Status LlvmCodeGen::CreateImpalaCodegen(RuntimeState* state,
+    MemTracker* parent_mem_tracker, const string& id,
+    scoped_ptr<LlvmCodeGen>* codegen_ret) {
+  DCHECK(state != nullptr);
+  RETURN_IF_ERROR(CreateFromMemory(
+      state, state->obj_pool(), parent_mem_tracker, id, codegen_ret));
   LlvmCodeGen* codegen = codegen_ret->get();
 
   // Parse module for cross compiled functions and types
@@ -681,17 +689,19 @@ Function* LlvmCodeGen::GetFunction(IRFunction::Type ir_type, bool clone) {
 bool LlvmCodeGen::VerifyFunction(Function* fn) {
   if (is_corrupt_) return false;
 
-  // Check that there are no calls to Expr::GetConstant(). These should all have been
-  // inlined via Expr::InlineConstants().
+  // Check that there are no calls to FunctionContextImpl::GetConstFnAttr(). These should all
+  // have been inlined via InlineConstFnAttrs().
   for (inst_iterator iter = inst_begin(fn); iter != inst_end(fn); ++iter) {
     Instruction* instr = &*iter;
     if (!isa<CallInst>(instr)) continue;
     CallInst* call_instr = reinterpret_cast<CallInst*>(instr);
     Function* called_fn = call_instr->getCalledFunction();
-    // look for call to Expr::GetConstant()
-    if (called_fn != NULL &&
-        called_fn->getName().find(Expr::GET_CONSTANT_INT_SYMBOL_PREFIX) != string::npos) {
-      LOG(ERROR) << "Found call to Expr::GetConstant*(): " << Print(call_instr);
+
+    // Look for call to FunctionContextImpl::GetConstFnAttr().
+    if (called_fn != nullptr &&
+        called_fn->getName() == FunctionContextImpl::GET_CONST_FN_ATTR_SYMBOL) {
+      LOG(ERROR) << "Found call to FunctionContextImpl::GetConstFnAttr(): "
+                 << Print(call_instr);
       is_corrupt_ = true;
       break;
     }
@@ -916,6 +926,47 @@ int LlvmCodeGen::ReplaceCallSitesWithBoolConst(llvm::Function* caller, bool cons
   return ReplaceCallSitesWithValue(caller, replacement, target_name);
 }
 
+int LlvmCodeGen::InlineConstFnAttrs(const FunctionContext::TypeDesc& ret_type,
+    const vector<FunctionContext::TypeDesc>& arg_types, Function* fn) {
+  int replaced = 0;
+  for (inst_iterator iter = inst_begin(fn), end = inst_end(fn); iter != end; ) {
+    // Increment iter now so we don't mess it up modifying the instruction below
+    Instruction* instr = &*(iter++);
+
+    // Look for call instructions
+    if (!isa<CallInst>(instr)) continue;
+    CallInst* call_instr = cast<CallInst>(instr);
+    Function* called_fn = call_instr->getCalledFunction();
+
+    // Look for call to FunctionContextImpl::GetConstFnAttr().
+    if (called_fn == nullptr ||
+        called_fn->getName() != FunctionContextImpl::GET_CONST_FN_ATTR_SYMBOL) {
+      continue;
+    }
+
+    // 't' and 'i' arguments must be constant
+    ConstantInt* t_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(1));
+    ConstantInt* i_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(2));
+    // This optimization is only applied to built-ins which should have constant args.
+    DCHECK(t_arg != nullptr)
+        << "Non-constant 't' argument to FunctionContextImpl::GetConstFnAttr()";
+    DCHECK(i_arg != nullptr)
+        << "Non-constant 'i' argument to FunctionContextImpl::GetConstFnAttr";
+
+    // Replace the called function with the appropriate constant
+    FunctionContextImpl::ConstFnAttr t_val =
+        static_cast<FunctionContextImpl::ConstFnAttr>(t_arg->getSExtValue());
+    int i_val = static_cast<int>(i_arg->getSExtValue());
+    DCHECK(state_ != nullptr);
+    // All supported constants are currently integers.
+    call_instr->replaceAllUsesWith(ConstantInt::get(GetType(TYPE_INT),
+        FunctionContextImpl::GetConstFnAttr(state_, ret_type, arg_types, t_val, i_val)));
+    call_instr->eraseFromParent();
+    ++replaced;
+  }
+  return replaced;
+}
+
 void LlvmCodeGen::FindCallSites(Function* caller, const string& target_name,
       vector<CallInst*>* results) {
   for (inst_iterator iter = inst_begin(caller); iter != inst_end(caller); ++iter) {
@@ -1216,10 +1267,15 @@ void LlvmCodeGen::CodegenDebugTrace(LlvmBuilder* builder, const char* str,
   builder->CreateCall(printf, calling_args);
 }
 
-void LlvmCodeGen::GetSymbols(unordered_set<string>* symbols) {
-  for (const Function& fn: module_->functions()) {
+Status LlvmCodeGen::GetSymbols(const string& file, const string& module_id,
+    unordered_set<string>* symbols) {
+  ObjectPool pool;
+  scoped_ptr<LlvmCodeGen> codegen;
+  RETURN_IF_ERROR(CreateFromFile(nullptr, &pool, nullptr, file, module_id, &codegen));
+  for (const Function& fn: codegen->module_->functions()) {
     if (fn.isMaterializable()) symbols->insert(fn.getName());
   }
+  return Status::OK();
 }
 
 // TODO: cache this function (e.g. all min(int, int) are identical).

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/codegen/llvm-codegen.h
----------------------------------------------------------------------
diff --git a/be/src/codegen/llvm-codegen.h b/be/src/codegen/llvm-codegen.h
index d51faab..058403d 100644
--- a/be/src/codegen/llvm-codegen.h
+++ b/be/src/codegen/llvm-codegen.h
@@ -153,17 +153,9 @@ class LlvmCodeGen {
   /// 'codegen' will contain the created object on success.
   /// 'parent_mem_tracker' - if non-NULL, the CodeGen MemTracker is created under this.
   /// 'id' is used for outputting the IR module for debugging.
-  static Status CreateImpalaCodegen(ObjectPool*, MemTracker* parent_mem_tracker,
+  static Status CreateImpalaCodegen(RuntimeState* state, MemTracker* parent_mem_tracker,
       const std::string& id, boost::scoped_ptr<LlvmCodeGen>* codegen);
 
-  /// Creates a LlvmCodeGen instance initialized with the module bitcode from 'file'.
-  /// 'codegen' will contain the created object on success. The functions in the module
-  /// are materialized lazily. Getting a reference to a function via GetFunction() will
-  /// materialize the function and its callees recursively.
-  static Status CreateFromFile(ObjectPool*, MemTracker* parent_mem_tracker,
-      const std::string& file, const std::string& id,
-      boost::scoped_ptr<LlvmCodeGen>* codegen);
-
   /// Removes all jit compiled dynamically linked functions from the process.
   ~LlvmCodeGen();
 
@@ -204,6 +196,7 @@ class LlvmCodeGen {
     void AddArgument(const NamedVariable& var) {
       args_.push_back(var);
     }
+
     void AddArgument(const std::string& name, llvm::Type* type) {
       args_.push_back(NamedVariable(name, type));
     }
@@ -333,6 +326,17 @@ class LlvmCodeGen {
   int ReplaceCallSitesWithValue(llvm::Function* caller, llvm::Value* replacement,
       const std::string& target_name);
 
+  /// This function replaces calls to FunctionContextImpl::GetConstFnAttr() with constants
+  /// derived from 'return_type', 'arg_types' and the runtime state 'state_'. Please note
+  /// that this function only replaces call instructions inside 'fn' so to replace the
+  /// call to FunctionContextImpl::GetConstFnAttr() inside the callee functions, please
+  /// inline the callee functions (by annotating them with IR_ALWAYS_INLINE).
+  ///
+  /// TODO: implement a loop unroller (or use LLVM's) so we can use
+  /// FunctionContextImpl::GetConstFnAttr() in loops
+  int InlineConstFnAttrs(const FunctionContext::TypeDesc& return_type,
+      const std::vector<FunctionContext::TypeDesc>& arg_types, llvm::Function* fn);
+
   /// Returns a copy of fn. The copy is added to the module.
   llvm::Function* CloneFunction(llvm::Function* fn);
 
@@ -471,8 +475,9 @@ class LlvmCodeGen {
   llvm::Type* void_type() { return void_type_; }
   llvm::Type* i128_type() { return llvm::Type::getIntNTy(context(), 128); }
 
-  /// Fils in 'symbols' with all the symbols in the module.
-  void GetSymbols(boost::unordered_set<std::string>* symbols);
+  /// Load the module temporarily and populate 'symbols' with the symbols in the module.
+  static Status GetSymbols(const string& file, const string& module_id,
+      boost::unordered_set<std::string>* symbols);
 
   /// Generates function to return min/max(v1, v2)
   llvm::Function* CodegenMinMax(const ColumnType& type, bool min);
@@ -524,19 +529,28 @@ class LlvmCodeGen {
   static void FindGlobalUsers(llvm::User* val, std::vector<llvm::GlobalObject*>* users);
 
   /// Top level codegen object. 'module_id' is used for debugging when outputting the IR.
-  LlvmCodeGen(
-      ObjectPool* pool, MemTracker* parent_mem_tracker, const std::string& module_id);
+  LlvmCodeGen(RuntimeState* state, ObjectPool* pool, MemTracker* parent_mem_tracker,
+      const std::string& module_id);
 
   /// Initializes the jitter and execution engine with the given module.
   Status Init(std::unique_ptr<llvm::Module> module);
 
-  /// Creates a LlvmCodeGen instance initialized with the module bitcode in memory.
+  /// Creates a LlvmCodeGen instance initialized with the module bitcode from 'file'.
   /// 'codegen' will contain the created object on success. The functions in the module
   /// are materialized lazily. Getting a reference to a function via GetFunction() will
   /// materialize the function and its callees recursively.
-  static Status CreateFromMemory(ObjectPool* pool, MemTracker* parent_mem_tracker,
+  static Status CreateFromFile(RuntimeState* state, ObjectPool* pool,
+      MemTracker* parent_mem_tracker, const std::string& file,
       const std::string& id, boost::scoped_ptr<LlvmCodeGen>* codegen);
 
+  /// Creates a LlvmCodeGen instance initialized with the module bitcode in memory.
+  /// 'codegen' will contain the created object on success. The functions in the module
+  /// are materialized lazily. Getting a reference to a function via GetFunction() will
+  /// materialize the function and its callees recursively.
+  static Status CreateFromMemory(RuntimeState* state, ObjectPool* pool,
+      MemTracker* parent_mem_tracker, const std::string& id,
+      boost::scoped_ptr<LlvmCodeGen>* codegen);
+
   /// Loads an LLVM module from 'file' which is the local path to the LLVM bitcode file.
   /// The functions in the module are materialized lazily. Getting a reference to the
   /// function via GetFunction() will materialize the function and its callees
@@ -639,6 +653,10 @@ class LlvmCodeGen {
   /// when a module is loaded to ensure that LLVM can resolve references to them.
   static boost::unordered_set<std::string> fns_to_always_materialize_;
 
+  /// Pointer to the RuntimeState which owns this codegen object. Needed in
+  /// InlineConstFnAttr() to access the query options.
+  const RuntimeState* state_;
+
   /// ID used for debugging (can be e.g. the fragment instance ID)
   std::string id_;
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exec/partitioned-aggregation-node.cc
----------------------------------------------------------------------
diff --git a/be/src/exec/partitioned-aggregation-node.cc b/be/src/exec/partitioned-aggregation-node.cc
index 7c75d01..83232d2 100644
--- a/be/src/exec/partitioned-aggregation-node.cc
+++ b/be/src/exec/partitioned-aggregation-node.cc
@@ -1681,8 +1681,8 @@ Status PartitionedAggregationNode::CodegenUpdateSlot(LlvmCodeGen* codegen,
     // Call the UDA to update/merge 'src' into 'dst', with the result stored in
     // 'updated_dst_val'.
     CodegenAnyVal updated_dst_val;
-    RETURN_IF_ERROR(CodegenCallUda(
-        codegen, &builder, evaluator, agg_fn_ctx_arg, input_vals, dst, &updated_dst_val));
+    RETURN_IF_ERROR(CodegenCallUda(codegen, &builder, evaluator, agg_fn_ctx_arg,
+        input_vals, dst, &updated_dst_val));
     result = updated_dst_val.ToNativeValue();
 
     if (slot_desc->is_nullable() && !special_null_handling) {
@@ -1717,7 +1717,7 @@ Status PartitionedAggregationNode::CodegenUpdateSlot(LlvmCodeGen* codegen,
 }
 
 Status PartitionedAggregationNode::CodegenCallUda(LlvmCodeGen* codegen,
-    LlvmBuilder* builder, AggFnEvaluator* evaluator, Value* agg_fn_ctx,
+    LlvmBuilder* builder, AggFnEvaluator* evaluator, Value* agg_fn_ctx_arg,
     const vector<CodegenAnyVal>& input_vals, const CodegenAnyVal& dst,
     CodegenAnyVal* updated_dst_val) {
   DCHECK_EQ(evaluator->input_expr_ctxs().size(), input_vals.size());
@@ -1727,7 +1727,7 @@ Status PartitionedAggregationNode::CodegenCallUda(LlvmCodeGen* codegen,
   // Set up arguments for call to UDA, which are the FunctionContext*, followed by
   // pointers to all input values, followed by a pointer to the destination value.
   vector<Value*> uda_fn_args;
-  uda_fn_args.push_back(agg_fn_ctx);
+  uda_fn_args.push_back(agg_fn_ctx_arg);
 
   // Create pointers to input args to pass to uda_fn. We must use the unlowered type,
   // e.g. IntVal, because the UDA interface expects the values to be passed as const

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exec/partitioned-aggregation-node.h
----------------------------------------------------------------------
diff --git a/be/src/exec/partitioned-aggregation-node.h b/be/src/exec/partitioned-aggregation-node.h
index 54840c7..f26a252 100644
--- a/be/src/exec/partitioned-aggregation-node.h
+++ b/be/src/exec/partitioned-aggregation-node.h
@@ -651,7 +651,7 @@ class PartitionedAggregationNode : public ExecNode {
   /// operation is applied. The instruction sequence for the UDA call is inserted at
   /// the insert position of 'builder'.
   Status CodegenCallUda(LlvmCodeGen* codegen, LlvmBuilder* builder,
-      AggFnEvaluator* evaluator, llvm::Value* agg_fn_ctx,
+      AggFnEvaluator* evaluator, llvm::Value* agg_fn_ctx_arg,
       const std::vector<CodegenAnyVal>& input_vals, const CodegenAnyVal& dst_val,
       CodegenAnyVal* updated_dst_val);
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/agg-fn-evaluator.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/agg-fn-evaluator.cc b/be/src/exprs/agg-fn-evaluator.cc
index b623130..81c0277 100644
--- a/be/src/exprs/agg-fn-evaluator.cc
+++ b/be/src/exprs/agg-fn-evaluator.cc
@@ -528,7 +528,7 @@ Status AggFnEvaluator::GetUpdateOrMergeFunction(LlvmCodeGen* codegen, Function**
   if (!(*uda_fn)->isDeclaration()) {
     // TODO: IMPALA-4785: we should also replace references to GetIntermediateType()
     // with constants.
-    Expr::InlineConstants(GetOutputTypeDesc(), arg_type_descs_, codegen, *uda_fn);
+    codegen->InlineConstFnAttrs(GetOutputTypeDesc(), arg_type_descs_, *uda_fn);
     *uda_fn = codegen->FinalizeFunction(*uda_fn);
     if (*uda_fn == NULL) {
       return Status(TErrorCode::UDF_VERIFY_FAILED, symbol, fn_.hdfs_location);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/aggregate-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/aggregate-functions-ir.cc b/be/src/exprs/aggregate-functions-ir.cc
index 8505d43..531598c 100644
--- a/be/src/exprs/aggregate-functions-ir.cc
+++ b/be/src/exprs/aggregate-functions-ir.cc
@@ -401,7 +401,7 @@ IR_ALWAYS_INLINE void AggregateFunctions::DecimalAvgAddOrRemove(FunctionContext*
   // Since the src and dst are guaranteed to be the same scale, we can just
   // do a simple add.
   int m = remove ? -1 : 1;
-  switch (Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0)) {
+  switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) {
     case 4:
       avg->sum.val16 += m * src.val4;
       break;
@@ -511,7 +511,7 @@ IR_ALWAYS_INLINE void AggregateFunctions::SumDecimalAddOrSubtract(FunctionContex
     const DecimalVal& src, DecimalVal* dst, bool subtract) {
   if (src.is_null) return;
   if (dst->is_null) InitZero<DecimalVal>(ctx, dst);
-  int precision = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_PRECISION, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
   // Since the src and dst are guaranteed to be the same scale, we can just
   // do a simple add.
   int m = subtract ? -1 : 1;
@@ -573,7 +573,7 @@ template<>
 void AggregateFunctions::Min(FunctionContext* ctx,
     const DecimalVal& src, DecimalVal* dst) {
   if (src.is_null) return;
-  int precision = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_PRECISION, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
   if (precision <= 9) {
     if (dst->is_null || src.val4 < dst->val4) *dst = src;
   } else if (precision <= 19) {
@@ -587,7 +587,7 @@ template<>
 void AggregateFunctions::Max(FunctionContext* ctx,
     const DecimalVal& src, DecimalVal* dst) {
   if (src.is_null) return;
-  int precision = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_PRECISION, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
   if (precision <= 9) {
     if (dst->is_null || src.val4 > dst->val4) *dst = src;
   } else if (precision <= 19) {
@@ -1200,8 +1200,8 @@ void AggregateFunctions::HllUpdate(
   if (src.is_null) return;
   DCHECK(!dst->is_null);
   DCHECK_EQ(dst->len, HLL_LEN);
-  uint64_t hash_value = AnyValUtil::HashDecimal64(
-      src, Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0), HashUtil::FNV64_SEED);
+  int byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0);
+  uint64_t hash_value = AnyValUtil::HashDecimal64(src, byte_size, HashUtil::FNV64_SEED);
   if (hash_value != 0) {
     // Use the lower bits to index into the number of streams and then
     // find the first 1 bit after the index bits.

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/conditional-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/conditional-functions-ir.cc b/be/src/exprs/conditional-functions-ir.cc
index ed98a87..662f167 100644
--- a/be/src/exprs/conditional-functions-ir.cc
+++ b/be/src/exprs/conditional-functions-ir.cc
@@ -23,11 +23,11 @@ using namespace impala;
 using namespace impala_udf;
 
 #define IS_NULL_COMPUTE_FUNCTION(type) \
-  type IsNullExpr::Get##type(ExprContext* context, const TupleRow* row) { \
+  type IsNullExpr::Get##type(ExprContext* ctx, const TupleRow* row) { \
     DCHECK_EQ(children_.size(), 2); \
-    type val = children_[0]->Get##type(context, row); \
+    type val = children_[0]->Get##type(ctx, row); \
     if (!val.is_null) return val; /* short-circuit */ \
-    return children_[1]->Get##type(context, row); \
+    return children_[1]->Get##type(ctx, row); \
   }
 
 IS_NULL_COMPUTE_FUNCTION(BooleanVal);
@@ -68,15 +68,14 @@ NULL_IF_COMPUTE_FUNCTION(TimestampVal);
 NULL_IF_COMPUTE_FUNCTION(DecimalVal);
 
 #define NULL_IF_ZERO_COMPUTE_FUNCTION(type) \
-  type ConditionalFunctions::NullIfZero(FunctionContext* context, const type& val) { \
+  type ConditionalFunctions::NullIfZero(FunctionContext* ctx, const type& val) { \
     if (val.is_null || val.val == 0) return type::null(); \
     return val; \
   }
 
-DecimalVal ConditionalFunctions::NullIfZero(
-    FunctionContext* context, const DecimalVal& val) {
+DecimalVal ConditionalFunctions::NullIfZero(FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return DecimalVal::null();
-  int type_byte_size = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SIZE);
+  int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE);
   switch (type_byte_size) {
     case 4:
       if (val.val4 == 0) return DecimalVal::null();
@@ -101,7 +100,7 @@ NULL_IF_ZERO_COMPUTE_FUNCTION(FloatVal);
 NULL_IF_ZERO_COMPUTE_FUNCTION(DoubleVal);
 
 #define ZERO_IF_NULL_COMPUTE_FUNCTION(type) \
-  type ConditionalFunctions::ZeroIfNull(FunctionContext* context, const type& val) { \
+  type ConditionalFunctions::ZeroIfNull(FunctionContext* ctx, const type& val) { \
     if (val.is_null) return type(0); \
     return val; \
   }
@@ -115,13 +114,13 @@ ZERO_IF_NULL_COMPUTE_FUNCTION(DoubleVal);
 ZERO_IF_NULL_COMPUTE_FUNCTION(DecimalVal);
 
 #define IF_COMPUTE_FUNCTION(type) \
-  type IfExpr::Get##type(ExprContext* context, const TupleRow* row) { \
+  type IfExpr::Get##type(ExprContext* ctx, const TupleRow* row) { \
     DCHECK_EQ(children_.size(), 3); \
-    BooleanVal cond = children_[0]->GetBooleanVal(context, row); \
+    BooleanVal cond = children_[0]->GetBooleanVal(ctx, row); \
     if (cond.is_null || !cond.val) { \
-      return children_[2]->Get##type(context, row); \
+      return children_[2]->Get##type(ctx, row); \
     } \
-    return children_[1]->Get##type(context, row); \
+    return children_[1]->Get##type(ctx, row); \
   }
 
 IF_COMPUTE_FUNCTION(BooleanVal);
@@ -136,10 +135,10 @@ IF_COMPUTE_FUNCTION(TimestampVal);
 IF_COMPUTE_FUNCTION(DecimalVal);
 
 #define COALESCE_COMPUTE_FUNCTION(type) \
-  type CoalesceExpr::Get##type(ExprContext* context, const TupleRow* row) { \
+  type CoalesceExpr::Get##type(ExprContext* ctx, const TupleRow* row) { \
     DCHECK_GE(children_.size(), 1); \
     for (int i = 0; i < children_.size(); ++i) {                  \
-      type val = children_[i]->Get##type(context, row); \
+      type val = children_[i]->Get##type(ctx, row); \
       if (!val.is_null) return val; \
     } \
     return type::null(); \

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/decimal-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-functions-ir.cc b/be/src/exprs/decimal-functions-ir.cc
index cdbd799..3262e27 100644
--- a/be/src/exprs/decimal-functions-ir.cc
+++ b/be/src/exprs/decimal-functions-ir.cc
@@ -28,17 +28,17 @@
 
 namespace impala {
 
-IntVal DecimalFunctions::Precision(FunctionContext* context, const DecimalVal& val) {
-  return IntVal(Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0));
+IntVal DecimalFunctions::Precision(FunctionContext* ctx, const DecimalVal& val) {
+  return IntVal(ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0));
 }
 
-IntVal DecimalFunctions::Scale(FunctionContext* context, const DecimalVal& val) {
-  return IntVal(Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0));
+IntVal DecimalFunctions::Scale(FunctionContext* ctx, const DecimalVal& val) {
+  return IntVal(ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0));
 }
 
-DecimalVal DecimalFunctions::Abs(FunctionContext* context, const DecimalVal& val) {
+DecimalVal DecimalFunctions::Abs(FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return DecimalVal::null();
-  int type_byte_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0);
+  int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0);
   switch (type_byte_size) {
     case 4:
       return DecimalVal(abs(val.val4));
@@ -52,79 +52,83 @@ DecimalVal DecimalFunctions::Abs(FunctionContext* context, const DecimalVal& val
   }
 }
 
-DecimalVal DecimalFunctions::Ceil(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::CEIL);
+DecimalVal DecimalFunctions::Ceil(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::CEIL);
 }
 
-DecimalVal DecimalFunctions::Floor(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::FLOOR);
+DecimalVal DecimalFunctions::Floor(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::FLOOR);
 }
 
-DecimalVal DecimalFunctions::Round(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::ROUND);
+DecimalVal DecimalFunctions::Round(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::ROUND);
 }
 
 /// Always inline in IR module so that constants can be replaced.
 IR_ALWAYS_INLINE DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, int scale,
+    FunctionContext* ctx, const DecimalVal& val, int scale,
     DecimalOperators::DecimalRoundOp op) {
-  int val_precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int val_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
-  int return_precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION);
-  int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE);
+  int val_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int val_scale =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
+  int return_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION);
+  int return_scale =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE);
   if (scale < 0) {
-    return DecimalOperators::RoundDecimalNegativeScale(context,
+    return DecimalOperators::RoundDecimalNegativeScale(ctx,
         val, val_precision, val_scale, return_precision, return_scale, op, -scale);
   } else {
-    return DecimalOperators::RoundDecimal(context,
+    return DecimalOperators::RoundDecimal(ctx,
         val, val_precision, val_scale, return_precision, return_scale, op);
   }
 }
 
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const TinyIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const TinyIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const SmallIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const SmallIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const IntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const IntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 DecimalVal DecimalFunctions::RoundTo(
-    FunctionContext* context, const DecimalVal& val, const BigIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const BigIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::ROUND);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::ROUND);
 }
 
-DecimalVal DecimalFunctions::Truncate(FunctionContext* context, const DecimalVal& val) {
-  return DecimalOperators::RoundDecimal(context, val, DecimalOperators::TRUNCATE);
+DecimalVal DecimalFunctions::Truncate(FunctionContext* ctx, const DecimalVal& val) {
+  return DecimalOperators::RoundDecimal(ctx, val, DecimalOperators::TRUNCATE);
 }
 
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const TinyIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const TinyIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const SmallIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const SmallIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const IntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const IntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 DecimalVal DecimalFunctions::TruncateTo(
-    FunctionContext* context, const DecimalVal& val, const BigIntVal& scale) {
+    FunctionContext* ctx, const DecimalVal& val, const BigIntVal& scale) {
   DCHECK(!scale.is_null);
-  return RoundTo(context, val, scale.val, DecimalOperators::TRUNCATE);
+  return RoundTo(ctx, val, scale.val, DecimalOperators::TRUNCATE);
 }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/decimal-operators-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-operators-ir.cc b/be/src/exprs/decimal-operators-ir.cc
index 516acbc..4dfc8dc 100644
--- a/be/src/exprs/decimal-operators-ir.cc
+++ b/be/src/exprs/decimal-operators-ir.cc
@@ -34,32 +34,32 @@
 
 namespace impala {
 
-#define RETURN_IF_OVERFLOW(context, overflow) \
+#define RETURN_IF_OVERFLOW(ctx, overflow) \
   do {\
     if (UNLIKELY(overflow)) {\
-      context->AddWarning("Expression overflowed, returning NULL");\
+      ctx->AddWarning("Expression overflowed, returning NULL");\
       return DecimalVal::null();\
     }\
   } while (false)
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::IntToDecimalVal(
-    FunctionContext* context, int precision, int scale, int64_t val) {
+    FunctionContext* ctx, int precision, int scale, int64_t val) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
       Decimal4Value dv = Decimal4Value::FromInt(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 8: {
       Decimal8Value dv = Decimal8Value::FromInt(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 16: {
       Decimal16Value dv = Decimal16Value::FromInt(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     default:
@@ -70,25 +70,25 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::IntToDecimalVal(
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::FloatToDecimalVal(
-    FunctionContext* context, int precision, int scale, double val) {
+    FunctionContext* ctx, int precision, int scale, double val) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
       Decimal4Value dv =
           Decimal4Value::FromDouble(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 8: {
       Decimal8Value dv =
           Decimal8Value::FromDouble(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     case 16: {
       Decimal16Value dv =
           Decimal16Value::FromDouble(precision, scale, val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(dv.value());
     }
     default:
@@ -105,28 +105,28 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::FloatToDecimalVal(
 // When going from a smaller type to a larger type, we convert and then scale.
 // Inline these functions in IR module so branches can be optimised out.
 
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* ctx,
     const Decimal4Value& val, int val_scale, int output_precision, int output_scale) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(output_precision)) {
     case 4: {
       Decimal4Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     case 8: {
       Decimal8Value val8 = ToDecimal8(val, &overflow);
       Decimal8Value scaled_val = val8.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     case 16: {
       Decimal16Value val16 = ToDecimal16(val, &overflow);
       Decimal16Value scaled_val = val16.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     default:
@@ -135,7 +135,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
   }
 }
 
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* ctx,
     const Decimal8Value& val, int val_scale, int output_precision, int output_scale) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(output_precision)) {
@@ -143,20 +143,20 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
       Decimal8Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
       Decimal4Value val4 = ToDecimal4(scaled_val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(val4.value());
     }
     case 8: {
       Decimal8Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     case 16: {
       Decimal16Value val16 = ToDecimal16(val, &overflow);
       Decimal16Value scaled_val = val16.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     default:
@@ -165,7 +165,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
   }
 }
 
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext* ctx,
     const Decimal16Value& val, int val_scale, int output_precision, int output_scale) {
   bool overflow = false;
   switch (ColumnType::GetDecimalByteSize(output_precision)) {
@@ -173,20 +173,20 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::ScaleDecimalValue(FunctionContext*
       Decimal16Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
       Decimal4Value val4 = ToDecimal4(scaled_val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(val4.value());
     }
     case 8: {
       Decimal16Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
       Decimal8Value val8 = ToDecimal8(scaled_val, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(val8.value());
     }
     case 16: {
       Decimal16Value scaled_val = val.ScaleTo(
           val_scale, output_scale, output_precision, &overflow);
-      RETURN_IF_OVERFLOW(context, overflow);
+      RETURN_IF_OVERFLOW(ctx, overflow);
       return DecimalVal(scaled_val.value());
     }
     default:
@@ -268,29 +268,31 @@ static inline Decimal16Value GetDecimal16Value(
 }
 
 #define CAST_INT_TO_DECIMAL(from_type) \
-  DecimalVal DecimalOperators::CastToDecimalVal( \
-      FunctionContext* context, const from_type& val) { \
+  IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal( \
+      FunctionContext* ctx, const from_type& val) { \
     if (val.is_null) return DecimalVal::null(); \
-    int precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    return IntToDecimalVal(context, precision, scale, val.val); \
+    int precision = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    return IntToDecimalVal(ctx, precision, scale, val.val); \
   }
 
 #define CAST_FLOAT_TO_DECIMAL(from_type) \
-  DecimalVal DecimalOperators::CastToDecimalVal( \
-      FunctionContext* context, const from_type& val) { \
+  IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal( \
+      FunctionContext* ctx, const from_type& val) { \
     if (val.is_null) return DecimalVal::null(); \
-    int precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    return FloatToDecimalVal(context, precision, scale, val.val); \
+    int precision = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    return FloatToDecimalVal(ctx, precision, scale, val.val); \
   }
 
 #define CAST_DECIMAL_TO_INT(to_type) \
-  to_type DecimalOperators::CastTo##to_type( \
-      FunctionContext* context, const DecimalVal& val) { \
+  IR_ALWAYS_INLINE to_type DecimalOperators::CastTo##to_type( \
+      FunctionContext* ctx, const DecimalVal& val) { \
     if (val.is_null) return to_type::null(); \
-    int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    switch (Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0)) { \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) { \
       case 4: { \
         Decimal4Value dv(val.val4); \
         return to_type(dv.whole_part(scale)); \
@@ -310,11 +312,11 @@ static inline Decimal16Value GetDecimal16Value(
   }
 
 #define CAST_DECIMAL_TO_FLOAT(to_type) \
-  to_type DecimalOperators::CastTo##to_type( \
-      FunctionContext* context, const DecimalVal& val) { \
+  IR_ALWAYS_INLINE to_type DecimalOperators::CastTo##to_type( \
+      FunctionContext* ctx, const DecimalVal& val) { \
     if (val.is_null) return to_type::null(); \
-    int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    switch (Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0)) { \
+    int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) { \
       case 4: { \
         Decimal4Value dv(val.val4); \
         return to_type(dv.ToDouble(scale)); \
@@ -349,7 +351,7 @@ CAST_DECIMAL_TO_FLOAT(DoubleVal)
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
-    FunctionContext* context, const DecimalVal& val, int val_precision, int val_scale,
+    FunctionContext* ctx, const DecimalVal& val, int val_precision, int val_scale,
     int output_precision, int output_scale, const DecimalRoundOp& op,
     int64_t rounding_scale) {
   DCHECK_GT(rounding_scale, 0);
@@ -360,19 +362,19 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
   switch (ColumnType::GetDecimalByteSize(val_precision)) {
     case 4: {
       Decimal4Value val4(val.val4);
-      result = ScaleDecimalValue(context, val4, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val4, val_scale, output_precision,
           output_scale);
       break;
     }
     case 8: {
       Decimal8Value val8(val.val8);
-      result = ScaleDecimalValue(context, val8, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val8, val_scale, output_precision,
           output_scale);
       break;
     }
     case 16: {
       Decimal16Value val16(val.val16);
-      result = ScaleDecimalValue(context, val16, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val16, val_scale, output_precision,
           output_scale);
       break;
     }
@@ -410,7 +412,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
       // Need to check for overflow. This can't happen in the other cases since the
       // FE should have picked a high enough precision.
       if (DecimalUtil::MAX_UNSCALED_DECIMAL16 - abs(delta) < abs(val16.value())) {
-        context->AddWarning("Expression overflowed, returning NULL");
+        ctx->AddWarning("Expression overflowed, returning NULL");
         return DecimalVal::null();
       }
       result.val16 += delta;
@@ -424,7 +426,7 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimalNegativeScale(
 }
 
 // Inline in IR module so branches can be optimised out.
-IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* context,
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* ctx,
     const DecimalVal& val, int val_precision, int val_scale, int output_precision,
     int output_scale, const DecimalRoundOp& op) {
   if (val.is_null) return DecimalVal::null();
@@ -434,23 +436,22 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* cont
   switch (ColumnType::GetDecimalByteSize(val_precision)) {
     case 4: {
       Decimal4Value val4(val.val4);
-      result = ScaleDecimalValue(context, val4, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val4, val_scale, output_precision,
           output_scale);
       delta = RoundDelta(val4, val_scale, output_scale, op);
       break;
     }
     case 8: {
       Decimal8Value val8(val.val8);
-      result = ScaleDecimalValue(context, val8, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val8, val_scale, output_precision,
           output_scale);
       delta = RoundDelta(val8, val_scale, output_scale, op);
       break;
     }
     case 16: {
       Decimal16Value val16(val.val16);
-      result = ScaleDecimalValue(context, val16, val_scale, output_precision,
+      result = ScaleDecimalValue(ctx, val16, val_scale, output_precision,
           output_scale);
-
       delta = RoundDelta(val16, val_scale, output_scale, op);
       break;
     }
@@ -466,41 +467,49 @@ IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(FunctionContext* cont
   // done the cast.
   if (delta == 0) return result;
 
-
-  // The value in 'result' is before the rounding has occurred.
-  // This can't overflow. Rounding to a non-negative scale means at least one digit is
-  // dropped if rounding occurred and the round can add at most one digit before the
-  // decimal.
+  // The value in 'result' is before any rounding has occurred. If there is any rounding,
+  // the ouput's scale must be less than the input's scale.
+  DCHECK_GT(val_scale, output_scale);
   result.val16 += delta;
+
+  // Rounding to a non-negative scale means at least one digit is dropped if rounding
+  // occurred and the round can add at most one digit before the decimal. This cannot
+  // overflow if output_precision >= val_precision. Otherwise, result can overflow.
+  bool overflow = output_precision < val_precision &&
+      abs(result.val16) >= DecimalUtil::GetScaleMultiplier<int128_t>(output_precision);
+  RETURN_IF_OVERFLOW(ctx, overflow);
   return result;
 }
 
 // Inline in IR module so branches can be optimised out.
 IR_ALWAYS_INLINE DecimalVal DecimalOperators::RoundDecimal(
-    FunctionContext* context, const DecimalVal& val, const DecimalRoundOp& op) {
-  int val_precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int val_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
-  int return_precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION);
-  int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE);
-  return RoundDecimal(context, val, val_precision, val_scale, return_precision,
+    FunctionContext* ctx, const DecimalVal& val, const DecimalRoundOp& op) {
+  int val_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int val_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
+  int return_precision =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION);
+  int return_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE);
+  return RoundDecimal(ctx, val, val_precision, val_scale, return_precision,
       return_scale, op);
 }
 
-// Cast is just RoundDecimal(TRUNCATE).
-// TODO: how we handle cast to a smaller scale is an implementation detail in the spec.
-// We could also choose to cast by doing ROUND.
-DecimalVal DecimalOperators::CastToDecimalVal(
-    FunctionContext* context, const DecimalVal& val) {
-  return RoundDecimal(context, val, TRUNCATE);
+// If query option decimal_v2 is true, cast is RoundDecimal(ROUND).
+// Otherwise, it's RoundDecimal(TRUNCATE).
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal(
+    FunctionContext* ctx, const DecimalVal& val) {
+  int is_decimal_v2 = ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
+  DCHECK(is_decimal_v2 == 0 || is_decimal_v2 == 1);
+  return RoundDecimal(ctx, val, is_decimal_v2 != 0 ? ROUND : TRUNCATE);
 }
 
-DecimalVal DecimalOperators::CastToDecimalVal(
-    FunctionContext* context, const StringVal& val) {
+IR_ALWAYS_INLINE DecimalVal DecimalOperators::CastToDecimalVal(
+    FunctionContext* ctx, const StringVal& val) {
   if (val.is_null) return DecimalVal::null();
   StringParser::ParseResult result;
   DecimalVal dv;
-  int precision = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION);
-  int scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION);
+  int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE);
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
       Decimal4Value dv4 = StringParser::StringToDecimal<int32_t>(
@@ -534,10 +543,10 @@ DecimalVal DecimalOperators::CastToDecimalVal(
 }
 
 StringVal DecimalOperators::CastToStringVal(
-    FunctionContext* context, const DecimalVal& val) {
+    FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return StringVal::null();
-  int precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
   string s;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4:
@@ -553,7 +562,7 @@ StringVal DecimalOperators::CastToStringVal(
       DCHECK(false);
       return StringVal::null();
   }
-  StringVal result(context, s.size());
+  StringVal result(ctx, s.size());
   memcpy(result.ptr, s.c_str(), s.size());
   return result;
 }
@@ -574,10 +583,10 @@ IR_ALWAYS_INLINE T DecimalOperators::ConvertToNanoseconds(T val, int scale) {
 }
 
 TimestampVal DecimalOperators::CastToTimestampVal(
-    FunctionContext* context, const DecimalVal& val) {
+    FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return TimestampVal::null();
-  int precision = Expr::GetConstantInt(*context, Expr::ARG_TYPE_PRECISION, 0);
-  int scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0);
+  int precision = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  int scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
   TimestampVal result;
   switch (ColumnType::GetDecimalByteSize(precision)) {
     case 4: {
@@ -620,9 +629,9 @@ TimestampVal DecimalOperators::CastToTimestampVal(
 }
 
 BooleanVal DecimalOperators::CastToBooleanVal(
-    FunctionContext* context, const DecimalVal& val) {
+    FunctionContext* ctx, const DecimalVal& val) {
   if (val.is_null) return BooleanVal::null();
-  switch (Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0)) {
+  switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) {
     case 4:
       return BooleanVal(val.val4 != 0);
     case 8:
@@ -637,17 +646,18 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_ARITHMETIC_OP(FN_NAME, OP_FN) \
   DecimalVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null || y.is_null) return DecimalVal::null(); \
     bool overflow = false; \
-    int x_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0); \
-    int x_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    int y_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 1); \
-    int y_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 1); \
+    int x_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); \
+    int x_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    int y_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1); \
+    int y_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1); \
     int return_precision = \
-        Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    switch (Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SIZE)) { \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int return_scale = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE)) { \
       case 4: { \
         Decimal4Value x_val = GetDecimal4Value(x, x_size, &overflow); \
         Decimal4Value y_val = GetDecimal4Value(y, y_size, &overflow); \
@@ -669,7 +679,7 @@ BooleanVal DecimalOperators::CastToBooleanVal(
         Decimal16Value y_val = GetDecimal16Value(y, y_size, &overflow); \
         Decimal16Value result = x_val.OP_FN<int128_t>(x_scale, y_val, y_scale, \
             return_precision, return_scale, &overflow); \
-        RETURN_IF_OVERFLOW(context, overflow); \
+        RETURN_IF_OVERFLOW(ctx, overflow); \
         return DecimalVal(result.value()); \
       } \
       default: \
@@ -680,18 +690,19 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_ARITHMETIC_OP_CHECK_NAN(FN_NAME, OP_FN) \
   DecimalVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null || y.is_null) return DecimalVal::null(); \
     bool overflow = false; \
     bool is_nan = false; \
-    int x_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0); \
-    int x_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-    int y_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 1); \
-    int y_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 1); \
+    int x_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); \
+    int x_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+    int y_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1); \
+    int y_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1); \
     int return_precision = \
-        Expr::GetConstantInt(*context, Expr::RETURN_TYPE_PRECISION); \
-    int return_scale = Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SCALE); \
-    switch (Expr::GetConstantInt(*context, Expr::RETURN_TYPE_SIZE)) { \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_PRECISION); \
+    int return_scale = \
+        ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SCALE); \
+    switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE)) { \
       case 4: { \
         Decimal4Value x_val = GetDecimal4Value(x, x_size, &overflow); \
         Decimal4Value y_val = GetDecimal4Value(y, y_size, &overflow); \
@@ -715,7 +726,7 @@ BooleanVal DecimalOperators::CastToBooleanVal(
         Decimal16Value y_val = GetDecimal16Value(y, y_size, &overflow); \
         Decimal16Value result = x_val.OP_FN<int128_t>(x_scale, y_val, y_scale, \
             return_precision, return_scale, &is_nan, &overflow); \
-        RETURN_IF_OVERFLOW(context, overflow); \
+        RETURN_IF_OVERFLOW(ctx, overflow); \
         if (is_nan) return DecimalVal::null(); \
         return DecimalVal(result.value()); \
       } \
@@ -727,10 +738,10 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_BINARY_OP_NONNULL(OP_FN, X, Y) \
   bool dummy = false; \
-  int x_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 0); \
-  int x_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 0); \
-  int y_size = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SIZE, 1); \
-  int y_scale = Expr::GetConstantInt(*context, Expr::ARG_TYPE_SCALE, 1); \
+  int x_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); \
+  int x_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0); \
+  int y_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1); \
+  int y_scale = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 1); \
   int byte_size = ::max(x_size, y_size); \
   switch (byte_size) { \
     case 4: { \
@@ -759,14 +770,14 @@ BooleanVal DecimalOperators::CastToBooleanVal(
 
 #define DECIMAL_BINARY_OP(FN_NAME, OP_FN) \
   BooleanVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null || y.is_null) return BooleanVal::null(); \
     DECIMAL_BINARY_OP_NONNULL(OP_FN, x, y) \
   }
 
 #define NULLSAFE_DECIMAL_BINARY_OP(FN_NAME, OP_FN, IS_EQUAL) \
   BooleanVal DecimalOperators::FN_NAME( \
-      FunctionContext* context, const DecimalVal& x, const DecimalVal& y) { \
+      FunctionContext* ctx, const DecimalVal& x, const DecimalVal& y) { \
     if (x.is_null) return BooleanVal(IS_EQUAL ? y.is_null : !y.is_null); \
     if (y.is_null) return BooleanVal(!IS_EQUAL); \
     DECIMAL_BINARY_OP_NONNULL(OP_FN, x, y) \

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/decimal-operators.h
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-operators.h b/be/src/exprs/decimal-operators.h
index 9f425b0..196ffdb 100644
--- a/be/src/exprs/decimal-operators.h
+++ b/be/src/exprs/decimal-operators.h
@@ -104,7 +104,7 @@ class DecimalOperators {
   };
 
   /// Evaluates a round from 'val' and returns the result, using the rounding rule of
-  /// 'type'.
+  /// 'op. Returns DecimalVal::null() on overflow.
   static DecimalVal RoundDecimal(FunctionContext* context,
       const DecimalVal& val, int val_precision, int val_scale, int output_precision,
       int output_scale, const DecimalRoundOp& op);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr-codegen-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-codegen-test.cc b/be/src/exprs/expr-codegen-test.cc
index 0c484f4..c73c89e 100644
--- a/be/src/exprs/expr-codegen-test.cc
+++ b/be/src/exprs/expr-codegen-test.cc
@@ -16,30 +16,39 @@
 // under the License.
 
 // The following is cross-compiled to native code and IR, and used in the test below
-
+#include "exprs/decimal-operators.h"
 #include "exprs/expr.h"
 #include "udf/udf.h"
 
 using namespace impala;
 using namespace impala_udf;
 
-// TestGetConstant() fills in the following constants
-struct Constants {
+// TestGetTypeAttrs() fills in the following constants
+struct FnAttr {
   int return_type_size;
   int arg0_type_size;
   int arg1_type_size;
   int arg2_type_size;
 };
 
-IntVal TestGetConstant(
+#ifdef IR_COMPILE
+#include "exprs/decimal-operators-ir.cc"
+#endif
+
+DecimalVal TestGetFnAttrs(
     FunctionContext* ctx, const DecimalVal& arg0, StringVal arg1, StringVal arg2) {
-  Constants* state = reinterpret_cast<Constants*>(
+  FnAttr* state = reinterpret_cast<FnAttr*>(
       ctx->GetFunctionState(FunctionContext::THREAD_LOCAL));
-  state->return_type_size = Expr::GetConstantInt(*ctx, Expr::RETURN_TYPE_SIZE);
-  state->arg0_type_size = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0);
-  state->arg1_type_size = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 1);
-  state->arg2_type_size = Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 2);
-  return IntVal(10);
+  state->return_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE);
+  state->arg0_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0);
+  state->arg1_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 1);
+  state->arg2_type_size =
+      ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 2);
+  // This function and its callees call FunctionContextImpl::GetConstFnAttr();
+  return DecimalOperators::CastToDecimalVal(ctx, arg0);
 }
 
 // Don't compile the actual test to IR
@@ -48,10 +57,12 @@ IntVal TestGetConstant(
 #include "testutil/gtest-util.h"
 #include "codegen/llvm-codegen.h"
 #include "common/init.h"
+#include "exprs/anyval-util.h"
 #include "exprs/expr-context.h"
 #include "runtime/exec-env.h"
 #include "runtime/mem-tracker.h"
 #include "runtime/runtime-state.h"
+#include "runtime/test-env.h"
 #include "service/fe-support.h"
 #include "udf/udf-internal.h"
 #include "udf/udf-test-harness.h"
@@ -64,28 +75,48 @@ using namespace llvm;
 
 namespace impala {
 
-const char* TEST_GET_CONSTANT_SYMBOL =
-    "_Z15TestGetConstantPN10impala_udf15FunctionContextERKNS_10DecimalValENS_9StringValES5_";
+const char* TEST_GET_FN_ATTR_SYMBOL =
+    "_Z14TestGetFnAttrsPN10impala_udf15FunctionContextERKNS_10DecimalValENS_9StringValES5_";
 
 const int ARG0_PRECISION = 10;
 const int ARG0_SCALE = 2;
 const int ARG1_LEN = 1;
+const int RET_PRECISION = 10;
+const int RET_SCALE = 1;
 
 class ExprCodegenTest : public ::testing::Test {
  protected:
-  int InlineConstants(Expr* expr, LlvmCodeGen* codegen, llvm::Function* fn) {
-    return expr->InlineConstants(codegen, fn);
+  scoped_ptr<TestEnv> test_env_;
+  RuntimeState* runtime_state_;
+  FunctionContext* fn_ctx_;
+  FnAttr fn_type_attr_;
+
+  int InlineConstFnAttrs(Expr* expr, LlvmCodeGen* codegen, llvm::Function* fn) {
+    FunctionContext::TypeDesc ret_type = AnyValUtil::ColumnTypeToTypeDesc(expr->type());
+    vector<FunctionContext::TypeDesc> arg_types;
+    for (const Expr* child : expr->children()) {
+      arg_types.push_back(AnyValUtil::ColumnTypeToTypeDesc(child->type()));
+    }
+    return codegen->InlineConstFnAttrs(ret_type, arg_types, fn);
   }
 
-  static Status CreateFromFile(
-      ObjectPool* pool, const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
-    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(pool, NULL, filename, "test", codegen));
+  Status CreateFromFile(const string& filename, scoped_ptr<LlvmCodeGen>* codegen) {
+    RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile(runtime_state_,
+        runtime_state_->obj_pool(), NULL, filename, "test", codegen));
     return (*codegen)->MaterializeModule();
   }
 
   virtual void SetUp() {
+    TQueryOptions query_options;
+    query_options.__set_decimal_v2(true);
+    test_env_.reset(new TestEnv());
+    EXPECT_OK(test_env_->CreateQueryState(0, 1, 8 * 1024 * 1024, &query_options,
+        &runtime_state_));
+
     FunctionContext::TypeDesc return_type;
-    return_type.type = FunctionContext::TYPE_INT;
+    return_type.type = FunctionContext::TYPE_DECIMAL;
+    return_type.precision = RET_PRECISION;
+    return_type.scale = RET_SCALE;
 
     FunctionContext::TypeDesc arg0_type;
     arg0_type.type = FunctionContext::TYPE_DECIMAL;
@@ -104,23 +135,25 @@ class ExprCodegenTest : public ::testing::Test {
     arg_types.push_back(arg1_type);
     arg_types.push_back(arg2_type);
 
-    fn_ctx_ = UdfTestHarness::CreateTestContext(return_type, arg_types);
+    fn_ctx_ = UdfTestHarness::CreateTestContext(return_type, arg_types, runtime_state_);
 
     // Initialize fn_ctx_ with constants
-    memset(&constants_, -1, sizeof(Constants));
-    fn_ctx_->SetFunctionState(FunctionContext::THREAD_LOCAL, &constants_);
+    memset(&fn_type_attr_, -1, sizeof(FnAttr));
+    fn_ctx_->SetFunctionState(FunctionContext::THREAD_LOCAL, &fn_type_attr_);
   }
 
   virtual void TearDown() {
     fn_ctx_->impl()->Close();
     delete fn_ctx_;
+    runtime_state_ = NULL;
+    test_env_.reset();
   }
 
-  void CheckConstants() {
-    EXPECT_EQ(constants_.return_type_size, 4);
-    EXPECT_EQ(constants_.arg0_type_size, 8);
-    EXPECT_EQ(constants_.arg1_type_size, ARG1_LEN);
-    EXPECT_EQ(constants_.arg2_type_size, 0); // varlen
+  void CheckFnAttr() {
+    EXPECT_EQ(fn_type_attr_.return_type_size, 8);
+    EXPECT_EQ(fn_type_attr_.arg0_type_size, 8);
+    EXPECT_EQ(fn_type_attr_.arg1_type_size, ARG1_LEN);
+    EXPECT_EQ(fn_type_attr_.arg2_type_size, 0); // varlen
   }
 
   static bool VerifyFunction(LlvmCodeGen* codegen, llvm::Function* fn) {
@@ -130,9 +163,6 @@ class ExprCodegenTest : public ::testing::Test {
   static void ResetVerification(LlvmCodeGen* codegen) {
     codegen->ResetVerification();
   }
-
-  FunctionContext* fn_ctx_;
-  Constants constants_;
 };
 
 TExprNode CreateDecimalLiteral(int precision, int scale) {
@@ -183,10 +213,12 @@ TExprNode CreateStringLiteral(int len = -1) {
   return expr;
 }
 
-// Creates a function call to TestGetConstant() in test-udfs.h
-TExprNode CreateFunctionCall(vector<TExprNode> children) {
+// Creates a function call to TestGetFnAttrs() in test-udfs.h
+TExprNode CreateFunctionCall(vector<TExprNode> children, int precision, int scale) {
   TScalarType scalar_type;
-  scalar_type.type = TPrimitiveType::INT;
+  scalar_type.type = TPrimitiveType::DECIMAL;
+  scalar_type.__set_precision(precision);
+  scalar_type.__set_scale(scale);
 
   TTypeNode type;
   type.type = TTypeNodeType::SCALAR;
@@ -196,10 +228,10 @@ TExprNode CreateFunctionCall(vector<TExprNode> children) {
   col_type.__set_types(vector<TTypeNode>(1, type));
 
   TFunctionName fn_name;
-  fn_name.function_name = "test_get_constant";
+  fn_name.function_name = "test_get_type_attr";
 
   TScalarFunction scalar_fn;
-  scalar_fn.symbol = TEST_GET_CONSTANT_SYMBOL;
+  scalar_fn.symbol = TEST_GET_FN_ATTR_SYMBOL;
 
   TFunction fn;
   fn.name = fn_name;
@@ -219,18 +251,22 @@ TExprNode CreateFunctionCall(vector<TExprNode> children) {
   return expr;
 }
 
-TEST_F(ExprCodegenTest, TestGetConstantInterpreted) {
-  DecimalVal arg0_val;
+TEST_F(ExprCodegenTest, TestGetConstFnAttrsInterpreted) {
+  // Call fn and check results'. The input is of type Decimal(10,2) (i.e. 10000.25) and
+  // the output type is Decimal(10,1) (i.e. 10000.3). The precision and scale of arguments
+  // and return types are encoded above (ARG0_*, RET_*);
+  int64_t v = 1000025;
+  DecimalVal arg0_val(v);
   StringVal arg1_val;
   StringVal arg2_val;
-  IntVal result = TestGetConstant(fn_ctx_, arg0_val, arg1_val, arg2_val);
+  DecimalVal result = TestGetFnAttrs(fn_ctx_, arg0_val, arg1_val, arg2_val);
   // sanity check result
   EXPECT_EQ(result.is_null, false);
-  EXPECT_EQ(result.val, 10);
-  CheckConstants();
+  EXPECT_EQ(result.val8, 100003);
+  CheckFnAttr();
 }
 
-TEST_F(ExprCodegenTest, TestInlineConstants) {
+TEST_F(ExprCodegenTest, TestInlineConstFnAttrs) {
   // Setup thrift descriptors
   TExprNode arg0 = CreateDecimalLiteral(ARG0_PRECISION, ARG0_SCALE);
   TExprNode arg1 = CreateStringLiteral(ARG1_LEN);
@@ -241,7 +277,7 @@ TEST_F(ExprCodegenTest, TestInlineConstants) {
   exprs.push_back(arg1);
   exprs.push_back(arg2);
 
-  TExprNode fn_call = CreateFunctionCall(exprs);
+  TExprNode fn_call = CreateFunctionCall(exprs, RET_PRECISION, RET_SCALE);
   exprs.insert(exprs.begin(), fn_call);
 
   TExpr texpr;
@@ -253,21 +289,21 @@ TEST_F(ExprCodegenTest, TestInlineConstants) {
   ExprContext* ctx;
   ASSERT_OK(Expr::CreateExprTree(&pool, texpr, &ctx));
 
-  // Get TestGetConstant() IR function
+  // Get TestGetFnAttrs() IR function
   stringstream test_udf_file;
   test_udf_file << getenv("IMPALA_HOME") << "/be/build/latest/exprs/expr-codegen-test.ll";
   scoped_ptr<LlvmCodeGen> codegen;
-  ASSERT_OK(ExprCodegenTest::CreateFromFile(&pool, test_udf_file.str(), &codegen));
-  Function* fn = codegen->GetFunction(TEST_GET_CONSTANT_SYMBOL, false);
+  ASSERT_OK(CreateFromFile(test_udf_file.str(), &codegen));
+  Function* fn = codegen->GetFunction(TEST_GET_FN_ATTR_SYMBOL, false);
   ASSERT_TRUE(fn != NULL);
 
-  // Function verification should fail because we haven't inlined GetConstant() calls
+  // Function verification should fail because we haven't inlined GetTypeAttr() calls
   bool verification_succeeded = VerifyFunction(codegen.get(), fn);
   EXPECT_FALSE(verification_succeeded);
 
-  // Call InlineConstants() and rerun verification
-  int replaced = InlineConstants(ctx->root(), codegen.get(), fn);
-  EXPECT_EQ(replaced, 4);
+  // Call InlineConstFnAttrs() and rerun verification
+  int replaced = InlineConstFnAttrs(ctx->root(), codegen.get(), fn);
+  EXPECT_EQ(replaced, 9);
   ResetVerification(codegen.get());
   verification_succeeded = VerifyFunction(codegen.get(), fn);
   EXPECT_TRUE(verification_succeeded) << LlvmCodeGen::Print(fn);
@@ -277,17 +313,19 @@ TEST_F(ExprCodegenTest, TestInlineConstants) {
   ASSERT_TRUE(fn != NULL);
   void* fn_ptr;
   codegen->AddFunctionToJit(fn, &fn_ptr);
-  ASSERT_OK(codegen->FinalizeModule());
-  LOG(ERROR) << "Optimized fn: " << LlvmCodeGen::Print(fn);
-
-  // Call fn and check results
-  DecimalVal arg0_val;
-  typedef IntVal (*TestGetConstantType)(FunctionContext*, const DecimalVal&);
-  IntVal result = reinterpret_cast<TestGetConstantType>(fn_ptr)(fn_ctx_, arg0_val);
+  EXPECT_TRUE(codegen->FinalizeModule().ok()) << LlvmCodeGen::Print(fn);
+
+  // Call fn and check results'. The input is of type Decimal(10,2) (i.e. 10000.25) and
+  // the output type is Decimal(10,1) (i.e. 10000.3). The precision and scale of arguments
+  // and return types are encoded above (ARG0_*, RET_*);
+  int64_t v = 1000025;
+  DecimalVal arg0_val(v);
+  typedef DecimalVal (*TestGetFnAttrs)(FunctionContext*, const DecimalVal&);
+  DecimalVal result = reinterpret_cast<TestGetFnAttrs>(fn_ptr)(fn_ctx_, arg0_val);
   // sanity check result
   EXPECT_EQ(result.is_null, false);
-  EXPECT_EQ(result.val, 10);
-  CheckConstants();
+  EXPECT_EQ(result.val8, 100003);
+  CheckFnAttr();
 }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc
index cad716e..1b2dc51 100644
--- a/be/src/exprs/expr-test.cc
+++ b/be/src/exprs/expr-test.cc
@@ -1335,6 +1335,16 @@ DecimalTestCase decimal_cases[] = {
     {{ false, 1, 32, 1 }, { false, 1, 32, 1 }} },
   { "mod(cast('-1.23' as decimal(32,2)), cast('1.0' as decimal(32,2)))",
     {{ false, -23, 32, 2 }, { false, -23, 32, 2 }} },
+  { "cast(cast(0.12344 as decimal(6,5)) as decimal(6,4))",
+    {{ false, 1234, 6, 4 }, { false, 1234, 6, 4 }} },
+  { "cast(cast(0.12345 as decimal(6,5)) as decimal(6,4))",
+    {{ false, 1234, 6, 4 }, { false, 1235, 6, 4 }} },
+  { "cast(cast('0.999' as decimal(4,3)) as decimal(1,0))",
+    {{ false, 0, 1, 0 }, { false, 1, 1, 0 }} },
+  { "cast(cast(999999999.99 as DECIMAL(11,2)) as DECIMAL(9,0))",
+    {{ false, 999999999, 9, 0 }, { true, 0, 9, 0 }} },
+  { "cast(cast(-999999999.99 as DECIMAL(11,2)) as DECIMAL(9,0))",
+    {{ false, -999999999, 9, 0 }, { true, 0, 9, 0 }} },
   { "mod(cast(NULL as decimal(2,0)), cast('10' as decimal(2,0)))",
     {{ true, 0, 2, 0 }, { true, 0, 2, 0 }} },
   { "mod(cast('10' as decimal(2,0)), cast(NULL as decimal(2,0)))",
@@ -1357,7 +1367,7 @@ TEST_F(ExprTest, DecimalArithmeticExprs) {
     string opt = "DECIMAL_V2=" + lexical_cast<string>(v2);
     executor_->pushExecOption(opt);
     for (const DecimalTestCase& c : decimal_cases) {
-      const DecimalExpectedResult& r = c.expected[0];
+      const DecimalExpectedResult& r = c.expected[v2];
       const ColumnType& type = ColumnType::CreateDecimalType(r.precision, r.scale);
       if (r.null) {
         TestIsNull(c.expr, type);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/f982c3f7/be/src/exprs/expr.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr.cc b/be/src/exprs/expr.cc
index f119a9d..32c16a0 100644
--- a/be/src/exprs/expr.cc
+++ b/be/src/exprs/expr.cc
@@ -79,8 +79,6 @@ namespace impala {
 
 const char* Expr::LLVM_CLASS_NAME = "class.impala::Expr";
 
-const char* Expr::GET_CONSTANT_INT_SYMBOL_PREFIX = "_ZN6impala4Expr14GetConstantInt";
-
 template<class T>
 bool ParseString(const string& str, T* val) {
   istringstream stream(str);
@@ -589,90 +587,6 @@ Status Expr::GetConstVal(
   return GetFnContextError(context);
 }
 
-int Expr::GetConstantInt(const FunctionContext::TypeDesc& return_type,
-    const std::vector<FunctionContext::TypeDesc>& arg_types, ExprConstant c, int i) {
-  switch (c) {
-    case RETURN_TYPE_SIZE:
-      DCHECK_EQ(i, -1);
-      return AnyValUtil::TypeDescToColumnType(return_type).GetByteSize();
-    case RETURN_TYPE_PRECISION:
-      DCHECK_EQ(i, -1);
-      DCHECK_EQ(return_type.type, FunctionContext::TYPE_DECIMAL);
-      return return_type.precision;
-    case RETURN_TYPE_SCALE:
-      DCHECK_EQ(i, -1);
-      DCHECK_EQ(return_type.type, FunctionContext::TYPE_DECIMAL);
-      return return_type.scale;
-    case ARG_TYPE_SIZE:
-      DCHECK_GE(i, 0);
-      DCHECK_LT(i, arg_types.size());
-      return AnyValUtil::TypeDescToColumnType(arg_types[i]).GetByteSize();
-    case ARG_TYPE_PRECISION:
-      DCHECK_GE(i, 0);
-      DCHECK_LT(i, arg_types.size());
-      DCHECK_EQ(arg_types[i].type, FunctionContext::TYPE_DECIMAL);
-      return arg_types[i].precision;
-    case ARG_TYPE_SCALE:
-      DCHECK_GE(i, 0);
-      DCHECK_LT(i, arg_types.size());
-      DCHECK_EQ(arg_types[i].type, FunctionContext::TYPE_DECIMAL);
-      return arg_types[i].scale;
-    default:
-      CHECK(false) << "NYI";
-      return -1;
-  }
-}
-
-int Expr::GetConstantInt(const FunctionContext& ctx, ExprConstant c, int i) {
-  return GetConstantInt(ctx.GetReturnType(), ctx.impl()->arg_types(), c, i);
-}
-
-int Expr::InlineConstants(LlvmCodeGen* codegen, Function* fn) {
-  FunctionContext::TypeDesc return_type = AnyValUtil::ColumnTypeToTypeDesc(type_);
-  vector<FunctionContext::TypeDesc> arg_types;
-  for (int i = 0; i < children_.size(); ++i) {
-    arg_types.push_back(AnyValUtil::ColumnTypeToTypeDesc(children_[i]->type_));
-  }
-  return InlineConstants(return_type, arg_types, codegen, fn);
-}
-
-int Expr::InlineConstants(const FunctionContext::TypeDesc& return_type,
-    const std::vector<FunctionContext::TypeDesc>& arg_types, LlvmCodeGen* codegen,
-    Function* fn) {
-  int replaced = 0;
-  for (inst_iterator iter = inst_begin(fn), end = inst_end(fn); iter != end;) {
-    // Increment iter now so we don't mess it up modifying the instruction below
-    Instruction* instr = &*(iter++);
-
-    // Look for call instructions
-    if (!isa<CallInst>(instr)) continue;
-    CallInst* call_instr = cast<CallInst>(instr);
-    Function* called_fn = call_instr->getCalledFunction();
-
-    // Look for call to Expr::GetConstant*()
-    if (called_fn == NULL ||
-        called_fn->getName().find(GET_CONSTANT_INT_SYMBOL_PREFIX) == string::npos) {
-      continue;
-    }
-
-    // 'c' and 'i' arguments must be constant
-    ConstantInt* c_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(1));
-    ConstantInt* i_arg = dyn_cast<ConstantInt>(call_instr->getArgOperand(2));
-    DCHECK(c_arg != NULL) << "Non-constant 'c' argument to Expr::GetConstant*()";
-    DCHECK(i_arg != NULL) << "Non-constant 'i' argument to Expr::GetConstant*()";
-
-    // Replace the called function with the appropriate constant
-    ExprConstant c_val = static_cast<ExprConstant>(c_arg->getSExtValue());
-    int i_val = static_cast<int>(i_arg->getSExtValue());
-    // All supported constants are currently integers.
-    call_instr->replaceAllUsesWith(ConstantInt::get(codegen->GetType(TYPE_INT),
-        GetConstantInt(return_type, arg_types, c_val, i_val)));
-    call_instr->eraseFromParent();
-    ++replaced;
-  }
-  return replaced;
-}
-
 Status Expr::GetCodegendComputeFnWrapper(LlvmCodeGen* codegen, Function** fn) {
   if (ir_compute_fn_ != NULL) {
     *fn = ir_compute_fn_;