// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "codegen/llvm_codegen.h"

#include <fstream>
#include <mutex>
#include <iostream>
#include <sstream>
#include <boost/thread/mutex.hpp>

#include <llvm/IR/DataLayout.h>
#include <llvm/Analysis/Passes.h>
#include <llvm/Analysis/InstructionSimplify.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/JIT.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/PassManager.h>
#include <llvm/Support/DynamicLibrary.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/NoFolder.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/system_error.h>
#include <llvm/Support/InstIterator.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils/Cloning.h>

#include "common/logging.h"
#include "codegen/subexpr_elimination.h"
#include "codegen/doris_ir_data.h"
#include "doris_ir/doris_ir_names.h"
#include "util/cpu_info.h"
#include "util/path_builder.h"

using llvm::Value;
using llvm::Function;
using llvm::Module;
using llvm::PassManager;
using llvm::PassManagerBuilder;
using llvm::DataLayout;
using llvm::FunctionPassManager;

namespace doris {

static std::mutex s_llvm_initialization_lock;
static bool s_llvm_initialized = false;

void LlvmCodeGen::initialize_llvm(bool load_backend) {
    std::unique_lock<std::mutex> initialization_lock(s_llvm_initialization_lock);
    if (s_llvm_initialized) {
        return;
    }

    // This allocates a global llvm struct and enables multithreading.
    // There is no real good time to clean this up but we only make it once.
    bool result = llvm::llvm_start_multithreaded();
    DCHECK(result);
    // This can *only* be called once per process and is used to setup
    // dynamically linking jitted code.
    llvm::InitializeNativeTarget();
    s_llvm_initialized = true;

    if (load_backend) {
        std::string path;
        // For test env, we have to load libfesupport.so to provide sym for LLVM.
        PathBuilder::get_full_build_path("service/libfesupport.so", &path);
        bool failed = llvm::sys::DynamicLibrary::LoadLibraryPermanently(path.c_str());
        DCHECK_EQ(failed, 0);
    }
}

LlvmCodeGen::LlvmCodeGen(ObjectPool* pool, const std::string& name) :
        _name(name),
        _profile(pool, "CodeGen"),
        _optimizations_enabled(false),
        _is_corrupt(false),
        _is_compiled(false),
        _context(new llvm::LLVMContext()),
        _module(NULL),
        _execution_engine(NULL),
        _scratch_buffer_offset(0),
        _debug_trace_fn(NULL) {
    DCHECK(s_llvm_initialized) << "Must call LlvmCodeGen::initialize_llvm first.";

    _load_module_timer = ADD_TIMER(&_profile, "LoadTime");
    _prepare_module_timer = ADD_TIMER(&_profile, "PrepareTime");
    _module_file_size = ADD_COUNTER(&_profile, "ModuleFileSize", TUnit::BYTES);
    _codegen_timer = ADD_TIMER(&_profile, "CodegenTime");
    _optimization_timer = ADD_TIMER(&_profile, "OptimizationTime");
    _compile_timer = ADD_TIMER(&_profile, "CompileTime");

    _loaded_functions.resize(IRFunction::FN_END);
}

Status LlvmCodeGen::load_from_file(
        ObjectPool* pool,
        const std::string& file,
        boost::scoped_ptr<LlvmCodeGen>* codegen) {
    codegen->reset(new LlvmCodeGen(pool, ""));
    SCOPED_TIMER((*codegen)->_profile.total_time_counter());
    SCOPED_TIMER((*codegen)->_load_module_timer);
    llvm::OwningPtr<llvm::MemoryBuffer> file_buffer;
    llvm::error_code err = llvm::MemoryBuffer::getFile(file, file_buffer);

    if (err.value() != 0) {
        std::stringstream ss;
        ss << "Could not load module " << file << ": " << err.message();
        return Status(ss.str());
    }

    COUNTER_UPDATE((*codegen)->_module_file_size, file_buffer->getBufferSize());
    std::string error;
    llvm::Module* loaded_module = NULL;
    // llvm::ParseBitcodeFile(file_buffer.get(),
    //                        (*codegen)->context(), &error);

    if (loaded_module == NULL) {
        std::stringstream ss;
        ss << "Could not parse module " << file << ": " << error;
        return Status(ss.str());
    }

    (*codegen)->_module = loaded_module;

    return (*codegen)->init();
}

Status LlvmCodeGen::load_from_memory(
        ObjectPool* pool, llvm::MemoryBuffer* module_ir,
        const std::string& module_name, const std::string& id, 
        boost::scoped_ptr<LlvmCodeGen>* codegen) {
    codegen->reset(new LlvmCodeGen(pool, id));
    SCOPED_TIMER((*codegen)->_profile.total_time_counter());

    llvm::Module* loaded_module = NULL;
    RETURN_IF_ERROR(load_module_from_memory(
            codegen->get(), module_ir, module_name, &loaded_module));
    (*codegen)->_module = loaded_module;

    return (*codegen)->init();
}

Status LlvmCodeGen::load_module_from_memory(
        LlvmCodeGen* codegen, llvm::MemoryBuffer* module_ir,
        const std::string& module_name, llvm::Module** module) {
    SCOPED_TIMER(codegen->_prepare_module_timer);
    std::string error;
    *module = llvm::ParseBitcodeFile(module_ir, codegen->context(), &error);
    if (*module == NULL) {
        std::stringstream ss;
        ss << "Could not parse module " << module_name << ": " << error;
        return Status(ss.str());
    }
    return Status::OK;
}

Status LlvmCodeGen::load_doris_ir(
        ObjectPool* pool, 
        const std::string& id,
        boost::scoped_ptr<LlvmCodeGen>* codegen_ret) {
    // Select the appropriate IR version.  We cannot use LLVM IR with sse instructions on
    // a machine without sse support (loading the module will fail regardless of whether
    // those instructions are run or not).
    llvm::StringRef module_ir;
    std::string module_name;
    if (CpuInfo::is_supported(CpuInfo::SSE4_2)) {
        module_ir = llvm::StringRef(reinterpret_cast<const char*>(doris_sse_llvm_ir),
                              doris_sse_llvm_ir_len);
        module_name = "Doris IR with SSE support";
    } else {
        module_ir = llvm::StringRef(reinterpret_cast<const char*>(doris_no_sse_llvm_ir),
                              doris_no_sse_llvm_ir_len);
        module_name = "Doris IR with no SSE support";
    }
    boost::scoped_ptr<llvm::MemoryBuffer> module_ir_buf(
        llvm::MemoryBuffer::getMemBuffer(module_ir, "", false));
    RETURN_IF_ERROR(load_from_memory(pool, module_ir_buf.get(), module_name, id,
                                   codegen_ret));
    LlvmCodeGen* codegen = codegen_ret->get();

    // Parse module for cross compiled functions and types
    SCOPED_TIMER(codegen->_profile.total_time_counter());
    SCOPED_TIMER(codegen->_load_module_timer);

    // Get type for StringValue
    codegen->_string_val_type = codegen->get_type(StringValue::s_llvm_class_name);
    codegen->_decimal_val_type = codegen->get_type(DecimalValue::_s_llvm_class_name);
    // Get type for DateTimeValue
    codegen->_datetime_val_type = codegen->get_type(DateTimeValue::_s_llvm_class_name);

    // Verify size is correct
    const llvm::DataLayout* data_layout = codegen->execution_engine()->getDataLayout();
    const llvm::StructLayout* layout =
        data_layout->getStructLayout(static_cast<llvm::StructType*>(codegen->_string_val_type));

    if (layout->getSizeInBytes() != sizeof(StringValue)) {
        DCHECK_EQ(layout->getSizeInBytes(), sizeof(StringValue));
        return Status("Could not create llvm struct type for StringVal");
    }

    // Parse functions from module
    std::vector<llvm::Function*> functions;
    codegen->get_functions(&functions);
    int parsed_functions = 0;

    for (int i = 0; i < functions.size(); ++i) {
        std::string fn_name = functions[i]->getName();

        for (int j = IRFunction::FN_START; j < IRFunction::FN_END; ++j) {
            // Substring match to match precompiled functions.  The compiled function names
            // will be mangled.
            // TODO: reconsider this.  Substring match is probably not strict enough but
            // undoing the mangling is no fun either.
            if (fn_name.find(FN_MAPPINGS[j].fn_name) != std::string::npos) {
                if (codegen->_loaded_functions[FN_MAPPINGS[j].fn] != NULL) {
                    return Status("Duplicate definition found for function: " + fn_name);
                }

                codegen->_loaded_functions[FN_MAPPINGS[j].fn] = functions[i];
                ++parsed_functions;
            }
        }
    }

    if (parsed_functions != IRFunction::FN_END) {
        std::stringstream ss;
        ss << "Unable to find these precompiled functions: ";
        bool first = true;

        for (int i = IRFunction::FN_START; i != IRFunction::FN_END; ++i) {
            if (codegen->_loaded_functions[i] == NULL) {
                if (!first) {
                    ss << ", ";
                }

                ss << FN_MAPPINGS[i].fn_name;
                first = false;
            }
        }

        return Status(ss.str());
    }

    return Status::OK;
}

Status LlvmCodeGen::init() {
    if (_module == NULL) {
        _module = new llvm::Module(_name, context());
    }

    llvm::CodeGenOpt::Level opt_level = llvm::CodeGenOpt::Aggressive;
#ifndef NDEBUG
    // For debug builds, don't generate JIT compiled optimized assembly.
    // This takes a non-neglible amount of time (~.5 ms per function) and
    // blows up the fe tests (which take ~10-20 ms each).
    opt_level = llvm::CodeGenOpt::None;
#endif
    llvm::EngineBuilder builder = llvm::EngineBuilder(_module).setOptLevel(opt_level);
    // TODO Uncomment the below line as soon as we upgrade to LLVM 3.5 to enable SSE, if
    // available. In LLVM 3.3 this is done automatically and cannot be enabled because
    // for some reason SSE4 intrinsics selection will not work.
    // builder.setMCPU(llvm::sys::getHostCPUName());
    builder.setErrorStr(&_error_string);
    _execution_engine.reset(builder.create());
    if (_execution_engine == NULL) {
        // _execution_engine will take ownership of the module if it is created
        delete _module;
        std::stringstream ss;
        ss << "Could not create ExecutionEngine: " << _error_string;
        return Status(ss.str());
    }
    _void_type = llvm::Type::getVoidTy(context());
    _ptr_type = llvm::PointerType::get(get_type(TYPE_TINYINT), 0);
    _true_value = llvm::ConstantInt::get(context(), llvm::APInt(1, true, true));
    _false_value = llvm::ConstantInt::get(context(), llvm::APInt(1, false, true));

    RETURN_IF_ERROR(load_intrinsics());

    return Status::OK;
}

LlvmCodeGen::~LlvmCodeGen() {
    for (auto& it : _jitted_functions) {
        _execution_engine->freeMachineCodeForFunction(it.first);
    }
}

void LlvmCodeGen::enable_optimizations(bool enable) {
    _optimizations_enabled = enable;
}

std::string LlvmCodeGen::get_ir(bool full_module) const {
    std::string str;
    llvm::raw_string_ostream stream(str);
    if (full_module) {
        _module->print(stream, NULL);
    } else {
        for (int i = 0; i < _codegend_functions.size(); ++i) {
            _codegend_functions[i]->print(stream, NULL);
        }
    }
    return str;
}

llvm::PointerType* LlvmCodeGen::get_ptr_type(llvm::Type* type) {
    return llvm::PointerType::get(type, 0);
}

llvm::Type* LlvmCodeGen::get_type(const PrimitiveType& type) {
    switch (type) {
    case TYPE_NULL:
        return llvm::Type::getInt1Ty(context());
    case TYPE_BOOLEAN:
        return llvm::Type::getInt1Ty(context());
    case TYPE_TINYINT:
        return llvm::Type::getInt8Ty(context());
    case TYPE_SMALLINT:
        return llvm::Type::getInt16Ty(context());
    case TYPE_INT:
        return llvm::Type::getInt32Ty(context());
    case TYPE_BIGINT:
        return llvm::Type::getInt64Ty(context());
    case TYPE_LARGEINT:
         return llvm::Type::getIntNTy(context(), 128);
    case TYPE_FLOAT:
        return llvm::Type::getFloatTy(context());
    case TYPE_DOUBLE:
        return llvm::Type::getDoubleTy(context());
    case TYPE_CHAR:
    case TYPE_VARCHAR:
    case TYPE_HLL:
        return _string_val_type;
    case TYPE_DECIMAL:
        return _decimal_val_type;
    case TYPE_DATE:
    case TYPE_DATETIME:
        return _datetime_val_type;
    default:
        DCHECK(false) << "Invalid type.";
        return NULL;
    }
}

llvm::Type* LlvmCodeGen::get_type(const TypeDescriptor& type) {
    return get_type(type.type);
}

llvm::PointerType* LlvmCodeGen::get_ptr_type(const TypeDescriptor& type) {
    return llvm::PointerType::get(get_type(type.type), 0);
}

llvm::PointerType* LlvmCodeGen::get_ptr_type(const PrimitiveType& type) {
    return llvm::PointerType::get(get_type(type), 0);
}


llvm::Type* LlvmCodeGen::get_type(const std::string& name) {
    return _module->getTypeByName(name);
}

llvm::PointerType* LlvmCodeGen::get_ptr_type(const std::string& name) {
    llvm::Type* type = get_type(name);
    DCHECK(type != NULL) << name;
    return llvm::PointerType::get(type, 0);
}

// Llvm doesn't let you create a PointerValue from a c-side ptr.  Instead
// cast it to an int and then to 'type'.
llvm::Value* LlvmCodeGen::cast_ptr_to_llvm_ptr(llvm::Type* type, void* ptr) {
    llvm::Constant* const_int = llvm::ConstantInt::get(
            llvm::Type::getInt64Ty(context()), (int64_t)ptr);
    return llvm::ConstantExpr::getIntToPtr(const_int, type);
}

llvm::Value* LlvmCodeGen::get_int_constant(PrimitiveType type, int64_t val) {
    switch (type) {
    case TYPE_NULL:
        return llvm::ConstantInt::get(context(), llvm::APInt(8, val));
    case TYPE_TINYINT:
        return llvm::ConstantInt::get(context(), llvm::APInt(8, val));
    case TYPE_SMALLINT:
        return llvm::ConstantInt::get(context(), llvm::APInt(16, val));
    case TYPE_INT:
        return llvm::ConstantInt::get(context(), llvm::APInt(32, val));
    case TYPE_BIGINT:
        return llvm::ConstantInt::get(context(), llvm::APInt(64, val));
    case TYPE_LARGEINT:
        return llvm::ConstantInt::get(context(), llvm::APInt(128, val));
    default:
        DCHECK(false);
        return NULL;
    }
}

llvm::AllocaInst* LlvmCodeGen::create_entry_block_alloca(
        llvm::Function* f,
        const NamedVariable& var) {
    llvm::IRBuilder<> tmp(&f->getEntryBlock(), f->getEntryBlock().begin());
    return tmp.CreateAlloca(var.type, 0, var.name.c_str());
}

llvm::AllocaInst* LlvmCodeGen::create_entry_block_alloca(
        const LlvmBuilder& builder, llvm::Type* type, const char* name) {
    return create_entry_block_alloca(
        builder.GetInsertBlock()->getParent(), NamedVariable(name, type));
}

void LlvmCodeGen::create_if_else_blocks(
        llvm::Function* fn, const std::string& if_name,
        const std::string& else_name, llvm::BasicBlock** if_block, llvm::BasicBlock** else_block,
        llvm::BasicBlock* insert_before) {
    *if_block = llvm::BasicBlock::Create(context(), if_name, fn, insert_before);
    *else_block = llvm::BasicBlock::Create(context(), else_name, fn, insert_before);
}

llvm::Function* LlvmCodeGen::get_lib_c_function(FnPrototype* prototype) {
    if (_external_functions.find(prototype->name()) != _external_functions.end()) {
        return _external_functions[prototype->name()];
    }

    llvm::Function* func = prototype->generate_prototype();
    _external_functions[prototype->name()] = func;
    return func;
}

llvm::Function* LlvmCodeGen::get_function(IRFunction::Type function) {
    DCHECK(_loaded_functions[function] != NULL);
    return _loaded_functions[function];
}

// There is an llvm bug (#10957) that causes the first step of the verifier to always
// abort the process if it runs into an issue and ignores ReturnStatusAction.  This
// would cause Doris to go down if one query has a problem.
// To work around this, we will copy that step here and not abort on error.
// TODO: doesn't seem there is much traction in getting this fixed but we'll see
bool LlvmCodeGen::verify_function(llvm::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().
    for (llvm::inst_iterator iter = inst_begin(fn); iter != inst_end(fn); ++iter) {
        llvm::Instruction* instr = &*iter;
        if (!llvm::isa<llvm::CallInst>(instr)) {
            continue;
        }
        llvm::CallInst* call_instr = reinterpret_cast<llvm::CallInst*>(instr);
        llvm::Function* called_fn = call_instr->getCalledFunction();
        // look for call to Expr::GetConstant()
        if (called_fn != NULL && called_fn->getName().find(
                    Expr::_s_get_constant_symbol_prefix) != std::string::npos) {
            LOG(ERROR) << "Found call to Expr::GetConstant(): " << print(call_instr);
            _is_corrupt = true;
            break;
        }
    }

    // There is an llvm bug (#10957) that causes the first step of the verifier to always
    // abort the process if it runs into an issue and ignores ReturnStatusAction.  This
    // would cause impalad to go down if one query has a problem.  To work around this, we
    // will copy that step here and not abort on error. Adapted from the pre-verifier
    // function pass.
    // TODO: doesn't seem there is much traction in getting this fixed but we'll see
    for (llvm::Function::iterator i = fn->begin(), e = fn->end(); i != e; ++i) {
        if (i->empty() || !i->back().isTerminator()) {
            LOG(ERROR) << "Basic block must end with terminator: \n" << print(&(*i));
            _is_corrupt = true;
            break;
        }
    }

    if (!_is_corrupt) {
        _is_corrupt = llvm::verifyFunction(*fn, llvm::PrintMessageAction);
    }

    if (_is_corrupt) {
        std::string fn_name = fn->getName(); // llvm has some fancy operator overloading
        LOG(ERROR) << "Function corrupt: " << fn_name;
        return false;
    }
    return true;
}

LlvmCodeGen::FnPrototype::FnPrototype(
    LlvmCodeGen* gen, const std::string& name, llvm::Type* ret_type) :
    _codegen(gen), _name(name), _ret_type(ret_type) {
    DCHECK(!_codegen->_is_compiled) << "Not valid to add additional functions";
}

llvm::Function* LlvmCodeGen::FnPrototype::generate_prototype(
        LlvmBuilder* builder, llvm::Value** params) {
    std::vector<llvm::Type*> arguments;
    for (int i = 0; i < _args.size(); ++i) {
        arguments.push_back(_args[i].type);
    }
    llvm::FunctionType* prototype = llvm::FunctionType::get(_ret_type, arguments, false);
    llvm::Function* fn = llvm::Function::Create(
        prototype, llvm::Function::ExternalLinkage, _name, _codegen->_module);
    DCHECK(fn != NULL);

    // Name the arguments
    int idx = 0;
    for (llvm::Function::arg_iterator iter = fn->arg_begin();
            iter != fn->arg_end(); ++iter, ++idx) {
        iter->setName(_args[idx].name);
        if (params != NULL) {
            params[idx] = iter;
        }
    }

    if (builder != NULL) {
        llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(_codegen->context(), "entry", fn);
        builder->SetInsertPoint(entry_block);
    }

    _codegen->_codegend_functions.push_back(fn);
    return fn;
}

llvm::Function* LlvmCodeGen::replace_call_sites(
        llvm::Function* caller, bool update_in_place,
        llvm::Function* new_fn, const std::string& replacee_name, int* replaced) {
    DCHECK(caller->getParent() == _module);

    if (!update_in_place) {
        // Clone the function and add it to the module
        llvm::ValueToValueMapTy dummy_vmap;
        llvm::Function* new_caller = llvm::CloneFunction(caller, dummy_vmap, false);
        new_caller->copyAttributesFrom(caller);
        _module->getFunctionList().push_back(new_caller);
        caller = new_caller;
    } else if (_jitted_functions.find(caller) != _jitted_functions.end()) {
        // This function is already dynamically linked, unlink it.
        _execution_engine->freeMachineCodeForFunction(caller);
        _jitted_functions.erase(caller);
    }

    *replaced = 0;
    // loop over all blocks
    llvm::Function::iterator block_iter = caller->begin();

    while (block_iter != caller->end()) {
        llvm::BasicBlock* block = block_iter++;
        // loop over instructions in the block
        llvm::BasicBlock::iterator instr_iter = block->begin();

        while (instr_iter != block->end()) {
            llvm::Instruction* instr = instr_iter++;

            // look for call instructions
            if (llvm::CallInst::classof(instr)) {
                llvm::CallInst* call_instr = reinterpret_cast<llvm::CallInst*>(instr);
                llvm::Function* old_fn = call_instr->getCalledFunction();

                // look for call instruction that matches the name
                if (old_fn->getName().find(replacee_name) != std::string::npos) {
                    // Replace the called function
                    call_instr->setCalledFunction(new_fn);
                    ++*replaced;
                }
            }
        }
    }

    return caller;
}

Function* LlvmCodeGen::clone_function(Function* fn) {
    llvm::ValueToValueMapTy dummy_vmap;
    // CloneFunction() automatically gives the new function a unique name
    Function* fn_clone = llvm::CloneFunction(fn, dummy_vmap, false);
    fn_clone->copyAttributesFrom(fn);
    _module->getFunctionList().push_back(fn_clone);
    return fn_clone;
}

// TODO: revisit this.  Inlining all call sites might not be the right call.  We
// probably need to make this more complicated and somewhat cost based or write
// our own optimization passes.
int LlvmCodeGen::inline_call_sites(llvm::Function* fn, bool skip_registered_fns) {
    int functions_inlined = 0;
    // Collect all call sites
    std::vector<llvm::CallInst*> call_sites;

    // loop over all blocks
    llvm::Function::iterator block_iter = fn->begin();

    while (block_iter != fn->end()) {
        llvm::BasicBlock* block = block_iter++;
        // loop over instructions in the block
        llvm::BasicBlock::iterator instr_iter = block->begin();

        while (instr_iter != block->end()) {
            llvm::Instruction* instr = instr_iter++;

            // look for call instructions
            if (llvm::CallInst::classof(instr)) {
                llvm::CallInst* call_instr = reinterpret_cast<llvm::CallInst*>(instr);
                llvm::Function* called_fn = call_instr->getCalledFunction();

                if (skip_registered_fns) {
                    if (_registered_exprs.find(called_fn) != _registered_exprs.end()) {
                        continue;
                    }
                }

                call_sites.push_back(call_instr);
            }
        }
    }

    // Inline all call sites.  InlineFunction can still fail (function is recursive, etc)
    // but that always leaves the original function in a consistent state
    for (int i = 0; i < call_sites.size(); ++i) {
        llvm::InlineFunctionInfo info;

        if (llvm::InlineFunction(call_sites[i], info)) {
            ++functions_inlined;
        }
    }

    return functions_inlined;
}

llvm::Function* LlvmCodeGen::optimize_function_with_exprs(llvm::Function* fn) {
    int num_inlined = 0;
    do {
        // This assumes that all redundant exprs have been registered.
        num_inlined = inline_call_sites(fn, false);
    } while (num_inlined > 0);
    // TODO(zc): fix
    // SubExprElimination subexpr_elim(this);
    // subexpr_elim.run(fn);
    return finalize_function(fn);
}

llvm::Function* LlvmCodeGen::finalize_function(llvm::Function* function) {
    if (!verify_function(function)) {
        return NULL;
    }

    return function;
}

Status LlvmCodeGen::finalize_module() {
    DCHECK(!_is_compiled);
    _is_compiled = true;

    // TODO(zc)
#if 0
    if (FLAGS_unopt_module_dir.size() != 0) {
        string path = FLAGS_unopt_module_dir + "/" + id_ + "_unopt.ll";
        fstream f(path.c_str(), fstream::out | fstream::trunc);
        if (f.fail()) {
            LOG(ERROR) << "Could not save IR to: " << path;
        } else {
            f << GetIR(true);
            f.close();
        }
    }
#endif

    if (_is_corrupt) {
        return Status("Module is corrupt.");
    }
    SCOPED_TIMER(_profile.total_time_counter());

    // Don't waste time optimizing module if there are no functions to JIT. This can happen
    // if the codegen object is created but no functions are successfully codegen'd.
    if (_optimizations_enabled // TODO(zc): && !FLAGS_disable_optimization_passes 
            && !_fns_to_jit_compile.empty()) {
        optimize_module();
    }

    SCOPED_TIMER(_compile_timer);
    // JIT compile all codegen'd functions
    for (int i = 0; i < _fns_to_jit_compile.size(); ++i) {
        *_fns_to_jit_compile[i].second = jit_function(_fns_to_jit_compile[i].first);
    }
#if 0
    if (FLAGS_opt_module_dir.size() != 0) {
        string path = FLAGS_opt_module_dir + "/" + id_ + "_opt.ll";
        fstream f(path.c_str(), fstream::out | fstream::trunc);
        if (f.fail()) {
            LOG(ERROR) << "Could not save IR to: " << path;
        } else {
            f << GetIR(true);
            f.close();
        }
    }
#endif

    return Status::OK;
}

void LlvmCodeGen::optimize_module() {
    SCOPED_TIMER(_optimization_timer);

    // This pass manager will construct optimizations passes that are "typical" for
    // c/c++ programs.  We're relying on llvm to pick the best passes for us.
    // TODO: we can likely muck with this to get better compile speeds or write
    // our own passes.  Our subexpression elimination optimization can be rolled into
    // a pass.
    PassManagerBuilder pass_builder;
    // 2 maps to -O2
    // TODO: should we switch to 3? (3 may not produce different IR than 2 while taking
    // longer, but we should check)
    pass_builder.OptLevel = 2;
    // Don't optimize for code size (this corresponds to -O2/-O3)
    pass_builder.SizeLevel = 0;
    pass_builder.Inliner = llvm::createFunctionInliningPass() ;

    // Specifying the data layout is necessary for some optimizations (e.g. removing many
    // of the loads/stores produced by structs).
    const std::string& data_layout_str = _module->getDataLayout();
    DCHECK(!data_layout_str.empty());

    // Before running any other optimization passes, run the internalize pass, giving it
    // the names of all functions registered by AddFunctionToJit(), followed by the
    // global dead code elimination pass. This causes all functions not registered to be
    // JIT'd to be marked as internal, and any internal functions that are not used are
    // deleted by DCE pass. This greatly decreases compile time by removing unused code.
    std::vector<const char*> exported_fn_names;
    for (int i = 0; i < _fns_to_jit_compile.size(); ++i) {
        exported_fn_names.push_back(_fns_to_jit_compile[i].first->getName().data());
    }
    boost::scoped_ptr<PassManager> module_pass_manager(new PassManager());
    module_pass_manager->add(new DataLayout(data_layout_str));
    module_pass_manager->add(llvm::createInternalizePass(exported_fn_names));
    module_pass_manager->add(llvm::createGlobalDCEPass());
    module_pass_manager->run(*_module);

    // Create and run function pass manager
    boost::scoped_ptr<FunctionPassManager> fn_pass_manager(new FunctionPassManager(_module));
    fn_pass_manager->add(new DataLayout(data_layout_str));
    pass_builder.populateFunctionPassManager(*fn_pass_manager);
    fn_pass_manager->doInitialization();
    for (Module::iterator it = _module->begin(), end = _module->end(); it != end; ++it) {
        if (!it->isDeclaration()) fn_pass_manager->run(*it);
    }
    fn_pass_manager->doFinalization();

    // Create and run module pass manager
    module_pass_manager.reset(new PassManager());
    module_pass_manager->add(new DataLayout(data_layout_str));
    pass_builder.populateModulePassManager(*module_pass_manager);
    module_pass_manager->run(*_module);

    // if (FLAGS_print_llvm_ir_instruction_count) {
    //     for (int i = 0; i < _fns_to_jit_compile.size(); ++i) {
    //         InstructionCounter counter;
    //         counter.visit(*_fns_to_jit_compile[i].first);
    //         VLOG(1) << _fns_to_jit_compile[i].first->getName().str();
    //         VLOG(1) << counter.PrintCounters();
    //     }
    // }
}

void LlvmCodeGen::add_function_to_jit(llvm::Function* fn, void** fn_ptr) {
#if 0
    llvm::Type* decimal_val_type = get_type(CodegenAnyVal::LLVM_DECIMALVAL_NAME);
    if (fn->getReturnType() == decimal_val_type) {
        // Per the x86 calling convention ABI, DecimalVals should be returned via an extra
        // first DecimalVal* argument. We generate non-compliant functions that return the
        // DecimalVal directly, which we can call from generated code, but not from compiled
        // native code.  To avoid accidentally calling a non-compliant function from native
        // code, call 'function' from an ABI-compliant wrapper.
        stringstream name;
        name << fn->getName().str() << "ABIWrapper";
        LlvmCodeGen::FnPrototype prototype(this, name.str(), void_type_);
        // Add return argument
        prototype.AddArgument(NamedVariable("result", decimal_val_type->getPointerTo()));
        // Add regular arguments
        for (Function::arg_iterator arg = fn->arg_begin(); arg != fn->arg_end(); ++arg) {
            prototype.AddArgument(NamedVariable(arg->getName(), arg->getType()));
        }
        LlvmBuilder builder(context());
        Value* args[fn->arg_size() + 1];
        Function* fn_wrapper = prototype.GeneratePrototype(&builder, &args[0]);
        fn_wrapper->addFnAttr(llvm::Attribute::AlwaysInline);
        // Mark first argument as sret (not sure if this is necessary but it can't hurt)
        fn_wrapper->addAttribute(1, Attribute::StructRet);
        // Call 'fn' and store the result in the result argument
        Value* result =
            builder.CreateCall(fn, ArrayRef<Value*>(&args[1], fn->arg_size()), "result");
        builder.CreateStore(result, args[0]);
        builder.CreateRetVoid();
        fn = FinalizeFunction(fn_wrapper);
        DCHECK(fn != NULL);
    }
#endif
    _fns_to_jit_compile.push_back(std::make_pair(fn, fn_ptr));
}


void* LlvmCodeGen::jit_function(llvm::Function* function, int* scratch_size) {
    if (_is_corrupt) {
        return NULL;
    }

    if (scratch_size == NULL) {
        DCHECK_EQ(_scratch_buffer_offset, 0);
    } else {
        *scratch_size = _scratch_buffer_offset;
    }

    // TODO: log a warning if the jitted function is too big (larger than I cache)
    void* jitted_function = _execution_engine->getPointerToFunction(function);
    boost::lock_guard<boost::mutex> l(_jitted_functions_lock);

    if (jitted_function != NULL) {
        _jitted_functions[function] = true;
    }

    return jitted_function;
}

int LlvmCodeGen::get_scratch_buffer(int byte_size) {
    // TODO: this is not yet implemented/tested
    DCHECK(false);
    int result = _scratch_buffer_offset;
    // TODO: alignment?
    result += byte_size;
    return result;
}

// Wrapper around printf to make it easier to call from IR
extern "C" void debug_trace(const char* str) {
    printf("LLVM Trace: %s\n", str);
}

void LlvmCodeGen::codegen_debug_trace(LlvmBuilder* builder, const char* str) {
    LOG(ERROR) << "Remove IR codegen debug traces before checking in.";

    // Lazily link in debug function to the module
    if (_debug_trace_fn == NULL) {
        std::vector<llvm::Type*> args;
        args.push_back(_ptr_type);
        llvm::FunctionType* fn_type = llvm::FunctionType::get(_void_type, args, false);
        _debug_trace_fn = llvm::Function::Create(fn_type, llvm::GlobalValue::ExternalLinkage,
                                           "debug_trace", _module);

        DCHECK(_debug_trace_fn != NULL);
        // debug_trace shouldn't already exist (llvm mangles function names if there
        // are duplicates)
        DCHECK(_debug_trace_fn->getName() ==  "debug_trace");

        _debug_trace_fn->setCallingConv(llvm::CallingConv::C);

        // Add a mapping to the execution engine so it can link the debug_trace function
        _execution_engine->addGlobalMapping(_debug_trace_fn,
                                            reinterpret_cast<void*>(&debug_trace));
    }

    // Make a copy of str into memory owned by this object.  This is no guarantee that str is
    // still around when the debug printf is executed.
    _debug_strings.push_back(str);
    str = _debug_strings[_debug_strings.size() - 1].c_str();

    // Call the function by turning 'str' into a constant ptr value
    llvm::Value* str_ptr = cast_ptr_to_llvm_ptr(_ptr_type, const_cast<char*>(str));
    std::vector<llvm::Value*> calling_args;
    calling_args.push_back(str_ptr);
    builder->CreateCall(_debug_trace_fn, calling_args);
}

void LlvmCodeGen::get_functions(std::vector<llvm::Function*>* functions) {
    llvm::Module::iterator fn_iter = _module->begin();

    while (fn_iter != _module->end()) {
        llvm::Function* fn = fn_iter++;

        if (!fn->empty()) {
            functions->push_back(fn);
        }
    }
}

// TODO: cache this function (e.g. all min(int, int) are identical).
// we probably want some more global IR function cache, or, implement this
// in c and precompile it with clang.
// define i32 @Min(i32 %v1, i32 %v2) {
// entry:
//   %0 = icmp slt i32 %v1, %v2
//   br i1 %0, label %ret_v1, label %ret_v2
//
// ret_v1:                                           ; preds = %entry
//   ret i32 %v1
//
// ret_v2:                                           ; preds = %entry
//   ret i32 %v2
// }
llvm::Function* LlvmCodeGen::codegen_min_max(const TypeDescriptor& type, bool min) {
    LlvmCodeGen::FnPrototype prototype(this, min ? "Min" : "Max", get_type(type));
    prototype.add_argument(LlvmCodeGen::NamedVariable("v1", get_type(type)));
    prototype.add_argument(LlvmCodeGen::NamedVariable("v2", get_type(type)));

    llvm::Value* params[2];
    LlvmBuilder builder(context());
    llvm::Function* fn = prototype.generate_prototype(&builder, &params[0]);

    llvm::Value* compare = NULL;

    switch (type.type) {
    case TYPE_NULL:
        compare = false_value();
        break;

    case TYPE_BOOLEAN:
        if (min) {
            // For min, return x && y
            compare = builder.CreateAnd(params[0], params[1]);
        } else {
            // For max, return x || y
            compare = builder.CreateOr(params[0], params[1]);
        }

        break;

    case TYPE_TINYINT:
    case TYPE_SMALLINT:
    case TYPE_INT:
    case TYPE_BIGINT:
        if (min) {
            compare = builder.CreateICmpSLT(params[0], params[1]);
        } else {
            compare = builder.CreateICmpSGT(params[0], params[1]);
        }

        break;

    case TYPE_FLOAT:
    case TYPE_DOUBLE:
        if (min) {
            compare = builder.CreateFCmpULT(params[0], params[1]);
        } else {
            compare = builder.CreateFCmpUGT(params[0], params[1]);
        }

        break;

    default:
        DCHECK(false);
    }

    if (type.type == TYPE_BOOLEAN) {
        builder.CreateRet(compare);
    } else {
        llvm::BasicBlock* ret_v1 = NULL;
        llvm::BasicBlock* ret_v2 = NULL;
        create_if_else_blocks(fn, "ret_v1", "ret_v2", &ret_v1, &ret_v2);

        builder.CreateCondBr(compare, ret_v1, ret_v2);
        builder.SetInsertPoint(ret_v1);
        builder.CreateRet(params[0]);
        builder.SetInsertPoint(ret_v2);
        builder.CreateRet(params[1]);
    }

    if (!verify_function(fn)) {
        return NULL;
    }

    return fn;
}

// Intrinsics are loaded one by one.  Some are overloaded (e.g. memcpy) and the types must
// be specified.
// TODO: is there a better way to do this?
Status LlvmCodeGen::load_intrinsics() {
    // Load memcpy
    {
        llvm::Type* types[] = { ptr_type(), ptr_type(), get_type(TYPE_INT) };
        llvm::Function* fn = llvm::Intrinsic::getDeclaration(
                module(), llvm::Intrinsic::memcpy, types);

        if (fn == NULL) {
            return Status("Could not find memcpy intrinsic.");
        }

        _llvm_intrinsics[llvm::Intrinsic::memcpy] = fn;
    }

    // TODO: where is the best place to put this?
    struct {
        llvm::Intrinsic::ID id;
        const char* error;
    } non_overloaded_intrinsics[] = {
        { llvm::Intrinsic::x86_sse42_crc32_32_8, "sse4.2 crc32_u8" },
        { llvm::Intrinsic::x86_sse42_crc32_32_16, "sse4.2 crc32_u16" },
        { llvm::Intrinsic::x86_sse42_crc32_32_32, "sse4.2 crc32_u32" },
        { llvm::Intrinsic::x86_sse42_crc32_64_64, "sse4.2 crc32_u64" },
    };
    const int num_intrinsics =
        sizeof(non_overloaded_intrinsics) / sizeof(non_overloaded_intrinsics[0]);

    for (int i = 0; i < num_intrinsics; ++i) {
        llvm::Intrinsic::ID id = non_overloaded_intrinsics[i].id;
        llvm::Function* fn = llvm::Intrinsic::getDeclaration(module(), id);

        if (fn == NULL) {
            std::stringstream ss;
            ss << "Could not find " << non_overloaded_intrinsics[i].error << " intrinsic";
            return Status(ss.str());
        }

        _llvm_intrinsics[id] = fn;
    }

    return Status::OK;
}

void LlvmCodeGen::codegen_memcpy(LlvmBuilder* builder, llvm::Value* dst, llvm::Value* src, int size) {
    // Cast src/dst to int8_t*.  If they already are, this will get optimized away
    DCHECK(llvm::PointerType::classof(dst->getType()));
    DCHECK(llvm::PointerType::classof(src->getType()));
    dst = builder->CreateBitCast(dst, ptr_type());
    src = builder->CreateBitCast(src, ptr_type());

    // Get intrinsic function.
    llvm::Function* memcpy_fn = _llvm_intrinsics[llvm::Intrinsic::memcpy];
    DCHECK(memcpy_fn != NULL);

    // The fourth argument is the alignment.  For non-zero values, the caller
    // must guarantee that the src and dst values are aligned to that byte boundary.
    // TODO: We should try to take advantage of this since our tuples are well aligned.
    llvm::Value* args[] = {
        dst, src, get_int_constant(TYPE_INT, size),
        get_int_constant(TYPE_INT, 0),
        false_value()                       // is_volatile.
    };
    builder->CreateCall(memcpy_fn, args);
}

Value* LlvmCodeGen::codegen_array_at(
        LlvmBuilder* builder, Value* array, int idx, const char* name) {
    DCHECK(array->getType()->isPointerTy() || array->getType()->isArrayTy())
        << print(array->getType());
    Value* ptr = builder->CreateConstGEP1_32(array, idx);
    return builder->CreateLoad(ptr, name);
}

void LlvmCodeGen::codegen_assign(LlvmBuilder* builder,
                                llvm::Value* dst, llvm::Value* src, PrimitiveType type) {
    switch (type) {
    case TYPE_CHAR:
    case TYPE_VARCHAR: 
    case TYPE_HLL:  {
        codegen_memcpy(builder, dst, src, sizeof(StringValue));
        break;
    }

    case TYPE_DATETIME:
        DCHECK(false) << "Timestamp NYI"; // TODO
        break;

    default:
        builder->CreateStore(src, dst);
        break;
    }
}

void LlvmCodeGen::clear_hash_fns() {
    _hash_fns.clear();
}

// Codegen to compute hash for a particular byte size.  Loops are unrolled in this
// process.  For the case where num_bytes == 11, we'd do this by calling
//   1. crc64 (for first 8 bytes)
//   2. crc16 (for bytes 9, 10)
//   3. crc8 (for byte 11)
// The resulting IR looks like:
// define i32 @CrcHash11(i8* %data, i32 %len, i32 %seed) {
// entry:
//   %0 = zext i32 %seed to i64
//   %1 = bitcast i8* %data to i64*
//   %2 = getelementptr i64* %1, i32 0
//   %3 = load i64* %2
//   %4 = call i64 @llvm.x86.sse42.crc32.64.64(i64 %0, i64 %3)
//   %5 = trunc i64 %4 to i32
//   %6 = getelementptr i8* %data, i32 8
//   %7 = bitcast i8* %6 to i16*
//   %8 = load i16* %7
//   %9 = call i32 @llvm.x86.sse42.crc32.32.16(i32 %5, i16 %8)
//   %10 = getelementptr i8* %6, i32 2
//   %11 = load i8* %10
//   %12 = call i32 @llvm.x86.sse42.crc32.32.8(i32 %9, i8 %11)
//   ret i32 %12
// }
llvm::Function* LlvmCodeGen::get_hash_function(int num_bytes) {
    if (CpuInfo::is_supported(CpuInfo::SSE4_2)) {
        if (num_bytes == -1) {
            // -1 indicates variable length, just return the generic loop based
            // hash fn.
            return get_function(IRFunction::HASH_CRC);
            return NULL;
        }

        std::map<int, llvm::Function*>::iterator cached_fn = _hash_fns.find(num_bytes);
        if (cached_fn != _hash_fns.end()) {
            return cached_fn->second;
        }

        // Generate a function to hash these bytes
        std::stringstream ss;
        ss << "CrcHash" << num_bytes;
        FnPrototype prototype(this, ss.str(), get_type(TYPE_INT));
        prototype.add_argument(LlvmCodeGen::NamedVariable("data", ptr_type()));
        prototype.add_argument(LlvmCodeGen::NamedVariable("len", get_type(TYPE_INT)));
        prototype.add_argument(LlvmCodeGen::NamedVariable("seed", get_type(TYPE_INT)));

        llvm::Value* args[3];
        LlvmBuilder builder(context());
        llvm::Function* fn = prototype.generate_prototype(&builder, &args[0]);
        llvm::Value* data = args[0];
        llvm::Value* result = args[2];

        llvm::Function* crc8_fn = _llvm_intrinsics[llvm::Intrinsic::x86_sse42_crc32_32_8];
        llvm::Function* crc16_fn = _llvm_intrinsics[llvm::Intrinsic::x86_sse42_crc32_32_16];
        llvm::Function* crc32_fn = _llvm_intrinsics[llvm::Intrinsic::x86_sse42_crc32_32_32];
        llvm::Function* crc64_fn = _llvm_intrinsics[llvm::Intrinsic::x86_sse42_crc32_64_64];

        // Generate the crc instructions starting with the highest number of bytes
        if (num_bytes >= 8) {
            llvm::Value* result_64 = builder.CreateZExt(result, get_type(TYPE_BIGINT));
            llvm::Value* ptr = builder.CreateBitCast(data, get_ptr_type(TYPE_BIGINT));
            int i = 0;

            while (num_bytes >= 8) {
                llvm::Value* index[] = { get_int_constant(TYPE_INT, i++) };
                llvm::Value* d = builder.CreateLoad(builder.CreateGEP(ptr, index));
                result_64 = builder.CreateCall2(crc64_fn, result_64, d);
                num_bytes -= 8;
            }

            result = builder.CreateTrunc(result_64, get_type(TYPE_INT));
            llvm::Value* index[] = { get_int_constant(TYPE_INT, i * 8) };
            // Update data to past the 8-byte chunks
            data = builder.CreateGEP(data, index);
        }

        if (num_bytes >= 4) {
            DCHECK_LT(num_bytes, 8);
            llvm::Value* ptr = builder.CreateBitCast(data, get_ptr_type(TYPE_INT));
            llvm::Value* d = builder.CreateLoad(ptr);
            result = builder.CreateCall2(crc32_fn, result, d);
            llvm::Value* index[] = { get_int_constant(TYPE_INT, 4) };
            data = builder.CreateGEP(data, index);
            num_bytes -= 4;
        }

        if (num_bytes >= 2) {
            DCHECK_LT(num_bytes, 4);
            llvm::Value* ptr = builder.CreateBitCast(data, get_ptr_type(TYPE_SMALLINT));
            llvm::Value* d = builder.CreateLoad(ptr);
            result = builder.CreateCall2(crc16_fn, result, d);
            llvm::Value* index[] = { get_int_constant(TYPE_INT, 2) };
            data = builder.CreateGEP(data, index);
            num_bytes -= 2;
        }

        if (num_bytes > 0) {
            DCHECK_EQ(num_bytes, 1);
            llvm::Value* d = builder.CreateLoad(data);
            result = builder.CreateCall2(crc8_fn, result, d);
            --num_bytes;
        }
        DCHECK_EQ(num_bytes, 0);
        Value* shift_16 = get_int_constant(TYPE_INT, 16);
        Value* upper_bits = builder.CreateShl(result, shift_16);
        Value* lower_bits = builder.CreateLShr(result, shift_16);
        result = builder.CreateOr(upper_bits, lower_bits);
        builder.CreateRet(result);

        fn = finalize_function(fn);
        if (fn != NULL) {
            _hash_fns[num_bytes] = fn;
        }
        return fn;
    } else {
        // Don't bother with optimizations without crc hash instruction
        return get_function(IRFunction::HASH_FNV);
        return NULL;
    }
}

llvm::Value* LlvmCodeGen::get_ptr_to(LlvmBuilder* builder, llvm::Value* v, const char* name) {
    llvm::Value* ptr = create_entry_block_alloca(*builder, v->getType(), name);
    builder->CreateStore(v, ptr);
    return ptr;
}

llvm::Instruction::CastOps LlvmCodeGen::get_cast_op(
        const TypeDescriptor& from_type, const TypeDescriptor& to_type) {
    switch (from_type.type) {
    case TYPE_BOOLEAN: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
            return llvm::Instruction::Trunc;
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
        case TYPE_INT:
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::ZExt;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::SIToFP;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_TINYINT: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
            return llvm::Instruction::Trunc;
        case TYPE_SMALLINT:
        case TYPE_INT:
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::SExt;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::SIToFP;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_SMALLINT: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
            return llvm::Instruction::Trunc;
        case TYPE_INT:
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::SExt;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::SIToFP;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_INT: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
        case TYPE_INT:
            return llvm::Instruction::Trunc;
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::SExt;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::SIToFP;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_BIGINT: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
        case TYPE_INT:
        case TYPE_BIGINT:
            return llvm::Instruction::Trunc;
        case TYPE_LARGEINT:
            return llvm::Instruction::SExt;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::SIToFP;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_LARGEINT: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
        case TYPE_INT:
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::Trunc;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::SIToFP;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_FLOAT: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
        case TYPE_INT:
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::FPToSI;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::FPExt;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    case TYPE_DOUBLE: {
        switch (to_type.type) {
        case TYPE_BOOLEAN:
        case TYPE_TINYINT:
        case TYPE_SMALLINT:
        case TYPE_INT:
        case TYPE_BIGINT:
        case TYPE_LARGEINT:
            return llvm::Instruction::FPToSI;
        case TYPE_FLOAT:
        case TYPE_DOUBLE:
            return llvm::Instruction::FPTrunc;
        default:
            return llvm::Instruction::CastOpsEnd;
        }
    }
    default:
        return llvm::Instruction::CastOpsEnd;
    }
    return llvm::Instruction::CastOpsEnd;
}

}
