提交 7b75d9ea 编写于 作者: S Shreedhar Hardikar 提交者: Nikos Armenatzoglou

This commit generates code for code path: ExecVariableList > slot_getattr >...

This commit  generates code for code path: ExecVariableList > slot_getattr > _slot_getsomeattrs > slot_deform_tuple. This code path is executed during scan in simple select queries that do not have a where clause (e.g., select bar from foo;).

For each attribute A in the target list, regular implementation of ExecvariableList retrieves the slot that A comes from and calls slot_getattr. slot_get_attr() will eventually call slot_deform_tuple (through _slot_getsomeattrs), which fetches all yet unread attributes of the slot until the given attribute.

This commit generates the code for the case that all the attributes in target list use the same slot (created during a scan i.e, ecxt_scantuple). Moreover, instead of looping over the target list one at a time, it uses slot_getattr only once, with the largest attribute index from the target list.

If during code generation time, the completion is not possible (e.g., attributes use different slots), then function returns false and codegen manager will be responsible to manage the clean up.

This implementation does not support:
* Null attributes
* Variable length attributes
* Fixed length attributes passed by reference (e.g., uuid)
If at execution time, we see any of the above types of attributes, we fall back to the regular function.

Moreover, this commit:
* renames existing "codegen" guc, which is used for initiating llvm libraries, to "init_codegen" (Note: we set this guc in gpconfig),
* adds guc "codegen", which enables code generation and compilation at query execution time,
* enhances the existing code generation utilities by adding a function that creates all IR instructions for falling back to regular functions, and
* removes the existing code that generates the code of a naive slot_deform_tuple, which simply falls back to regular slot_deform_tuple.
Signed-off-by: NNikos Armenatzoglou <nikos.armenatzoglou@gmail.com>
上级 e49c5eb8
......@@ -1127,10 +1127,7 @@ heap_deformtuple(HeapTuple tuple,
* re-computing information about previously extracted attributes.
* slot->tts_nvalid is the number of attributes already extracted.
*/
#ifndef USE_CODEGEN
static
#endif
void
static void
slot_deform_tuple(TupleTableSlot *slot, int natts)
{
HeapTuple tuple = TupGetHeapTuple(slot);
......@@ -1244,11 +1241,7 @@ _slot_getsomeattrs(TupleTableSlot *slot, int attnum)
attno = HeapTupleHeaderGetNatts(tuple->t_data);
attno = Min(attno, attnum);
/*
* This macro will decide whether to inline regular slot_deform_tuple or
* call regular / generated slot_deform_tuple if USE_CODEGEN is defined
*/
call_slot_deform_tuple(slot, attno);
slot_deform_tuple(slot, attno);
/*
* If tuple doesn't have all the atts indicated by tupleDesc, read the
......
......@@ -164,7 +164,7 @@ set(GPCODEGEN_SRC
codegen_interface.cc
codegen_manager.cc
codegen_wrapper.cc
slot_deform_tuple_codegen.cc
ExecVariableList_codegen.cc
)
# Integrate with GPDB build system.
......
//---------------------------------------------------------------------------
// Greenplum Database
// Copyright (C) 2016 Pivotal Software, Inc.
//
// @filename:
// codegen_utils.cpp
//
// @doc:
// Contains different code generators
//
//---------------------------------------------------------------------------
#include <algorithm>
#include <cstdint>
#include <string>
#include "codegen/ExecVariableList_codegen.h"
#include "codegen/utils/clang_compiler.h"
#include "codegen/utils/utility.h"
#include "codegen/utils/instance_method_wrappers.h"
#include "codegen/utils/codegen_utils.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Casting.h"
extern "C" {
#include "postgres.h"
#include "utils/elog.h"
#include "access/htup.h"
#include "nodes/execnodes.h"
#include "executor/tuptable.h"
}
using gpcodegen::ExecVariableListCodegen;
constexpr char ExecVariableListCodegen::kExecVariableListPrefix[];
class ElogWrapper {
public:
ElogWrapper(gpcodegen::CodegenUtils* codegen_utils) :
codegen_utils_(codegen_utils) {
SetupElog();
}
~ElogWrapper() {
TearDownElog();
}
template<typename... V>
void CreateElog(
llvm::Value* llvm_elevel,
llvm::Value* llvm_fmt,
V ... args ) {
assert(NULL != llvm_elevel);
assert(NULL != llvm_fmt);
codegen_utils_->ir_builder()->CreateCall(
llvm_elog_start_, {
codegen_utils_->GetConstant(""), // Filename
codegen_utils_->GetConstant(0), // line number
codegen_utils_->GetConstant("") // function name
});
codegen_utils_->ir_builder()->CreateCall(
llvm_elog_finish_, {
llvm_elevel,
llvm_fmt,
args...
});
}
template<typename... V>
void CreateElog(
int elevel,
const char* fmt,
V ... args ) {
CreateElog(codegen_utils_->GetConstant(elevel),
codegen_utils_->GetConstant(fmt),
args...);
}
private:
llvm::Function* llvm_elog_start_;
llvm::Function* llvm_elog_finish_;
gpcodegen::CodegenUtils* codegen_utils_;
void SetupElog(){
assert(codegen_utils_ != nullptr);
llvm_elog_start_ = codegen_utils_->RegisterExternalFunction(elog_start);
assert(llvm_elog_start_ != nullptr);
llvm_elog_finish_ = codegen_utils_->RegisterExternalFunction(elog_finish);
assert(llvm_elog_finish_ != nullptr);
}
void TearDownElog(){
llvm_elog_start_ = nullptr;
llvm_elog_finish_ = nullptr;
}
};
ExecVariableListCodegen::ExecVariableListCodegen
(
ExecVariableListFn regular_func_ptr,
ExecVariableListFn* ptr_to_regular_func_ptr,
ProjectionInfo* proj_info,
TupleTableSlot* slot) :
BaseCodegen(kExecVariableListPrefix, regular_func_ptr, ptr_to_regular_func_ptr),
proj_info_(proj_info),
slot_(slot) {
}
bool ExecVariableListCodegen::GenerateExecVariableList(
gpcodegen::CodegenUtils* codegen_utils) {
assert(NULL != codegen_utils);
ElogWrapper elogwrapper(codegen_utils);
static_assert(sizeof(Datum) == sizeof(int64));
if ( NULL == proj_info_->pi_varSlotOffsets ) {
elog(DEBUG1, "Cannot codegen ExecVariableList because varSlotOffsets are null");
return false;
}
// Only do codegen if all the elements in the target list are on the same tuple slot
// This is an assumption for scan nodes, but we fall back when joins are involved.
for (int i = list_length(proj_info_->pi_targetlist) - 1; i > 0; i--){
if (proj_info_->pi_varSlotOffsets[i] != proj_info_->pi_varSlotOffsets[i-1]){
elog(DEBUG1, "Cannot codegen ExecVariableList because multiple slots to deform.");
return false;
}
}
// Find the largest attribute index in projInfo->pi_targetlist
int max_attr = *std::max_element(
proj_info_->pi_varNumbers,
proj_info_->pi_varNumbers + list_length(proj_info_->pi_targetlist));
// System attribute
if(max_attr <= 0)
{
elog(DEBUG1, "Cannot generate code for ExecVariableList because max_attr is negative (i.e., system attribute).");
return false;
}else if (max_attr > slot_->tts_tupleDescriptor->natts) {
elog(DEBUG1, "Cannot generate code for ExecVariableList because max_attr is greater than natts.");
return false;
}
// So looks like we're going to generate code
llvm::Function* ExecVariableList_func = codegen_utils->
CreateFunction<ExecVariableListFn>(
GetUniqueFuncName());
auto irb = codegen_utils->ir_builder();
// BasicBlock of function entry.
llvm::BasicBlock* entry_block = codegen_utils->CreateBasicBlock(
"entry", ExecVariableList_func);
// BasicBlock for checking correct slot
llvm::BasicBlock* slot_check_block = codegen_utils->CreateBasicBlock(
"slot_check", ExecVariableList_func);
// BasicBlock for checking tuple type.
llvm::BasicBlock* tuple_type_check_block = codegen_utils->CreateBasicBlock(
"tuple_type_check", ExecVariableList_func);
// BasicBlock for heap tuple check.
llvm::BasicBlock* heap_tuple_check_block = codegen_utils->CreateBasicBlock(
"heap_tuple_check", ExecVariableList_func);
// BasicBlock for null check block.
llvm::BasicBlock* null_check_block = codegen_utils->CreateBasicBlock(
"null_check", ExecVariableList_func);
// BasicBlock for main.
llvm::BasicBlock* main_block = codegen_utils->CreateBasicBlock(
"main", ExecVariableList_func);
// BasicBlock for fall back.
llvm::BasicBlock* fallback_block = codegen_utils->CreateBasicBlock(
"fallback", ExecVariableList_func);
// External functions
llvm::Function* llvm_memset = codegen_utils->RegisterExternalFunction(memset);
// Generation-time constants
llvm::Value* llvm_max_attr = codegen_utils->GetConstant(max_attr);
llvm::Value* llvm_slot = codegen_utils->GetConstant(slot_);
// Function arguments to ExecVariableList
llvm::Value* llvm_projInfo_arg = ArgumentByPosition(ExecVariableList_func, 0);
llvm::Value* llvm_values_arg = ArgumentByPosition(ExecVariableList_func, 1);
llvm::Value* llvm_isnull_arg = ArgumentByPosition(ExecVariableList_func, 2);
// Entry block
// -----------
irb->SetInsertPoint(entry_block);
// We start a sequence of checks to ensure that everything is fine and
// we do not need to fall back.
irb->CreateBr(slot_check_block);
// Slot check block
// ----------------
irb->SetInsertPoint(slot_check_block);
llvm::Value* llvm_econtext =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_projInfo_arg, &ProjectionInfo::pi_exprContext));
llvm::Value* llvm_varSlotOffsets =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_projInfo_arg, &ProjectionInfo::pi_varSlotOffsets));
// We want to fall back when ExecVariableList is called with a slot that's
// different from the one we generated the function (eg HashJoin). We also
// assume only 1 slot and that the slot is in a scan node ie from
// exprContext->ecxt_scantuple or varOffset = 0
llvm::Value* llvm_slot_arg =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_econtext, &ExprContext::ecxt_scantuple));
irb->CreateCondBr(
irb->CreateICmpEQ(llvm_slot, llvm_slot_arg),
tuple_type_check_block /* true */,
fallback_block /* false */
);
// Tuple type check block
// ----------------------
// We fall back if we see a virtual tuple or mem tuple,
// but it's possible to partially handle those cases also
irb->SetInsertPoint(tuple_type_check_block);
llvm::Value* llvm_slot_PRIVATE_tts_flags_ptr =
codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_flags);
llvm::Value* llvm_slot_PRIVATE_tts_memtuple =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_memtuple));
// (slot->PRIVATE_tts_flags & TTS_VIRTUAL) != 0
llvm::Value* llvm_tuple_is_virtual = irb->CreateICmpNE(
irb->CreateAnd(
irb->CreateLoad(llvm_slot_PRIVATE_tts_flags_ptr),
codegen_utils->GetConstant(TTS_VIRTUAL)),
codegen_utils->GetConstant(0));
// slot->PRIVATE_tts_memtuple != NULL
llvm::Value* llvm_tuple_has_memtuple = irb->CreateICmpNE(
llvm_slot_PRIVATE_tts_memtuple, codegen_utils->GetConstant((MemTuple) NULL));
// Fall back if tuple is virtual or memtuple is null
irb->CreateCondBr(
irb->CreateOr(llvm_tuple_is_virtual, llvm_tuple_has_memtuple),
fallback_block /*true*/, heap_tuple_check_block /*false*/);
// HeapTuple check block
// ---------------------
// We fall back if the given tuple is not a heaptuple.
irb->SetInsertPoint(heap_tuple_check_block);
// In _slot_getsomeattrs, check if: TupGetHeapTuple(slot) != NULL
llvm::Value* llvm_slot_PRIVATE_tts_heaptuple =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_heaptuple));
llvm::Value* llvm_tuple_has_heaptuple = irb->CreateICmpNE(
llvm_slot_PRIVATE_tts_heaptuple,
codegen_utils->GetConstant((HeapTuple) NULL));
irb->CreateCondBr(llvm_tuple_has_heaptuple,
null_check_block /*true*/,
fallback_block /*false*/);
// Start of slot_deform_tuple
// Null check block
// -------------------------
// Fall back if the slot has any null attributes
// TODO: What to do if any attribute is null????
irb->SetInsertPoint(null_check_block);
llvm::Value* llvm_heaptuple_t_data =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_slot_PRIVATE_tts_heaptuple,
&HeapTupleData::t_data
));
llvm::Value* llvm_heaptuple_t_data_t_infomask =
irb->CreateLoad( codegen_utils->GetPointerToMember(
llvm_heaptuple_t_data, &HeapTupleHeaderData::t_infomask));
// Check and fall back accordingly
irb->CreateCondBr(
irb->CreateICmpNE(
irb->CreateAnd(
llvm_heaptuple_t_data_t_infomask,
codegen_utils->GetConstant<uint16>(HEAP_HASNULL)),
codegen_utils->GetConstant<uint16>(0)),
fallback_block, /* true */
main_block /* false */
);
// Main block
// ----------
// Finally we generate code for slot_deform_tuple
irb->SetInsertPoint(main_block);
// Implementation for : {{{
// attno = HeapTupleHeaderGetNatts(tuple->t_data);
// attno = Min(attno, attnum);
llvm::Value* llvm_heaptuple_t_data_t_infomask2 =
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_heaptuple_t_data,
&HeapTupleHeaderData::t_infomask2));
llvm::Value* llvm_attno_t0 =
irb->CreateZExt(
irb->CreateAnd(llvm_heaptuple_t_data_t_infomask2,
codegen_utils->GetConstant<int16>(HEAP_NATTS_MASK)),
codegen_utils->GetType<int>());
llvm::Value* llvm_attno =
irb->CreateSelect(
irb->CreateICmpSLT(llvm_attno_t0, llvm_max_attr),
llvm_attno_t0,
llvm_max_attr
);
// }}}
// Retrieve slot's PRIVATE variables
llvm::Value* llvm_slot_PRIVATE_tts_isnull /* bool* */=
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_isnull));
llvm::Value* llvm_slot_PRIVATE_tts_values /* Datum* */=
irb->CreateLoad(codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_values));
llvm::Value* llvm_slot_PRIVATE_tts_nvalid_ptr /* int* */=
codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_nvalid);
llvm::Value* llvm_heaptuple_t_data_t_hoff = irb->CreateLoad(
codegen_utils->GetPointerToMember(llvm_heaptuple_t_data,
&HeapTupleHeaderData::t_hoff));
// Find the start of input data byte array
// same as tp in slot_deform_tuple (pointer to tuple data)
llvm::Value* llvm_tuple_data_ptr = irb->CreateInBoundsGEP(
llvm_heaptuple_t_data, {llvm_heaptuple_t_data_t_hoff});
int off = 0;
TupleDesc tupleDesc = slot_->tts_tupleDescriptor;
Form_pg_attribute* att = tupleDesc->attrs;
for (int attnum = 0; attnum < max_attr; ++attnum) {
Form_pg_attribute thisatt = att[attnum];
if ( thisatt->attcacheoff > 0 ) {
off = thisatt->attcacheoff;
}else {
off = att_align(off, thisatt->attalign);
}
// If any thisatt is varlen
if (thisatt->attlen < 0) {
// We don't support variable length attributes.
return false;
}
// values[attnum] = fetchatt(thisatt, tp + off) {{{
llvm::Value* llvm_next_t_data_ptr =
irb->CreateInBoundsGEP(llvm_tuple_data_ptr,
{codegen_utils->GetConstant(off)});
llvm::Value* llvm_next_values_ptr =
irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_values,
{codegen_utils->GetConstant(attnum)});
llvm::Value* llvm_colVal = nullptr;
if( thisatt->attbyval) {
// Load the value from the calculated input address.
switch(thisatt->attlen)
{
case sizeof(char):
llvm_colVal = irb->CreateLoad(llvm_next_t_data_ptr);
break;
case sizeof(int16):
llvm_colVal = irb->CreateLoad(
codegen_utils->GetType<int16>(),irb->CreateBitCast(
llvm_next_t_data_ptr, codegen_utils->GetType<int16*>()));
break;
case sizeof(int32):
llvm_colVal = irb->CreateLoad(
codegen_utils->GetType<int32>(),
irb->CreateBitCast(llvm_next_t_data_ptr,
codegen_utils->GetType<int32*>()));
break;
case sizeof(Datum):
llvm_colVal = irb->CreateLoad(
codegen_utils->GetType<int64>(), irb->CreateBitCast(
llvm_next_t_data_ptr, codegen_utils->GetType<int64*>()));
break;
default:
// We do not support other data type length, passed by value
return false;
}
} else {
// We do not support attributes by reference
return false;
}
// store colVal into out_values[attnum]
irb->CreateStore(
irb->CreateZExt(llvm_colVal, codegen_utils->GetType<Datum>()),
llvm_next_values_ptr);
// }}} End of values[attnum] = fetchatt(thisatt, tp + off)
// isnull[attnum] = false; {{{
llvm::Value* llvm_next_isnull_ptr =
irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_isnull,
{codegen_utils->GetConstant(attnum)});
irb->CreateStore(
codegen_utils->GetConstant<bool>(false),
llvm_next_isnull_ptr);
// }}} End of isnull[attnum] = false;
off += thisatt->attlen;
} // end for
// slot->PRIVATE_tts_off = off;
llvm::Value* llvm_slot_PRIVATE_tts_off_ptr /* long* */=
codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_off);
irb->CreateStore(codegen_utils->GetConstant<long>(off),
llvm_slot_PRIVATE_tts_off_ptr);
// slot->PRIVATE_tts_nvalid = attnum;
irb->CreateStore(llvm_max_attr, llvm_slot_PRIVATE_tts_nvalid_ptr);
// slot->PRIVATE_tts_slow = slow;
llvm::Value* llvm_slot_PRIVATE_tts_slow_ptr /* bool* */=
codegen_utils->GetPointerToMember(
llvm_slot, &TupleTableSlot::PRIVATE_tts_slow);
irb->CreateStore(codegen_utils->GetConstant<bool>(false),
llvm_slot_PRIVATE_tts_slow_ptr);
// End of slot_deform_tuple
// _slot_getsomeattrs() after calling slot_deform_tuple {{{
// for (; attno < attnum; attno++)
// {
// slot->PRIVATE_tts_values[attno] = (Datum) 0;
// slot->PRIVATE_tts_isnull[attno] = true;
// }
// TODO: Convert these to loops
llvm::Value* llvm_num_bytes = irb->CreateMul(
codegen_utils->GetConstant(sizeof(Datum)),
irb->CreateZExtOrTrunc(
irb->CreateSub(llvm_max_attr, llvm_attno),
codegen_utils->GetType<size_t>()));
codegen_utils->ir_builder()->CreateCall(
llvm_memset, {
irb->CreateBitCast(
irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_values, {llvm_attno}),
codegen_utils->GetType<void*>()),
codegen_utils->GetConstant(0),
llvm_num_bytes});
codegen_utils->ir_builder()->CreateCall(
llvm_memset, {
irb->CreateBitCast(
irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_isnull, {llvm_attno}),
codegen_utils->GetType<void*>()),
codegen_utils->GetConstant((int) true),
llvm_num_bytes});
// TupSetVirtualTuple(slot);
irb->CreateStore(
irb->CreateOr(
irb->CreateLoad(llvm_slot_PRIVATE_tts_flags_ptr),
codegen_utils->GetConstant<int>(TTS_VIRTUAL)),
llvm_slot_PRIVATE_tts_flags_ptr);
// }}}
// slot_getattr() after calling _slot_getsomeattrs() and remainder of ExecVariableList {{{
//
// This code from ExecVariableList copies the contents of the isnull & values arrays
// in the slot to output variable from the function parameters to ExecVariableList
int *varNumbers = proj_info_->pi_varNumbers;
for (int i = list_length(proj_info_->pi_targetlist) - 1; i >= 0; i--) {
// *isnull = slot->PRIVATE_tts_isnull[attnum-1];
llvm::Value* llvm_isnull_from_slot_val =
irb->CreateLoad(irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_isnull,
{codegen_utils->GetConstant(varNumbers[i] - 1)}));
llvm::Value* llvm_isnull_ptr =
irb->CreateInBoundsGEP(llvm_isnull_arg, {codegen_utils->GetConstant(i)});
irb->CreateStore(llvm_isnull_from_slot_val, llvm_isnull_ptr);
// values[i] = slot_getattr(varSlot, varNumber+1, &(isnull[i]));
llvm::Value* llvm_value_from_slot_val =
irb->CreateLoad(irb->CreateInBoundsGEP(llvm_slot_PRIVATE_tts_values,
{codegen_utils->GetConstant(varNumbers[i] - 1)}));
llvm::Value* llvm_values_ptr =
irb->CreateInBoundsGEP(llvm_values_arg, {codegen_utils->GetConstant(i)});
irb->CreateStore(llvm_value_from_slot_val, llvm_values_ptr);
}
// We're all done!
codegen_utils->ir_builder()->CreateRetVoid();
// Fall back block
// ---------------
// Note: We collect error code information, based on the block from which we fall back,
// and log it for debugging purposes.
irb->SetInsertPoint(fallback_block);
llvm::PHINode* llvm_error = irb->CreatePHI(codegen_utils->GetType<int>(), 4);
llvm_error->addIncoming(codegen_utils->GetConstant(0), slot_check_block);
llvm_error->addIncoming(codegen_utils->GetConstant(1), tuple_type_check_block);
llvm_error->addIncoming(codegen_utils->GetConstant(2), heap_tuple_check_block);
llvm_error->addIncoming(codegen_utils->GetConstant(3), null_check_block);
elogwrapper.CreateElog(DEBUG1, "Falling back to regular ExecVariableList, reason = %d", llvm_error);
codegen_utils->CreateFallback<ExecVariableListFn>(
codegen_utils->RegisterExternalFunction(GetRegularFuncPointer()),
ExecVariableList_func);
return true;
}
bool ExecVariableListCodegen::GenerateCodeInternal(CodegenUtils* codegen_utils) {
bool isGenerated = GenerateExecVariableList(codegen_utils);
if (isGenerated) {
elog(DEBUG1, "ExecVariableList was generated successfully!");
return true;
}
else {
elog(DEBUG1, "ExecVariableList generation failed!");
return false;
}
}
......@@ -36,9 +36,14 @@
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Casting.h"
extern "C" {
#include <utils/elog.h>
}
using gpcodegen::CodegenManager;
CodegenManager::CodegenManager(const std::string& module_name) {
module_name_ = module_name;
codegen_utils_.reset(new gpcodegen::CodegenUtils(module_name));
}
......@@ -69,9 +74,10 @@ unsigned int CodegenManager::PrepareGeneratedFunctions() {
return success_count;
}
// Call CodegenUtils to compile entire module
bool compilation_status = codegen_utils_->PrepareForExecution(
gpcodegen::CodegenUtils::OptimizationLevel::kNone, true);
gpcodegen::CodegenUtils::OptimizationLevel::kDefault, true);
if (!compilation_status) {
return success_count;
......
......@@ -12,19 +12,20 @@
#include "codegen/codegen_wrapper.h"
#include "codegen/codegen_manager.h"
#include "codegen/slot_deform_tuple_codegen.h"
#include "codegen/ExecVariableList_codegen.h"
#include "codegen/utils/codegen_utils.h"
using gpcodegen::CodegenManager;
using gpcodegen::BaseCodegen;
using gpcodegen::SlotDeformTupleCodegen;
using gpcodegen::ExecVariableListCodegen;
// Current code generator manager that oversees all code generators
static void* ActiveCodeGeneratorManager = nullptr;
static bool is_codegen_initalized = false;
extern bool codegen; // defined from guc
extern bool init_codegen; // defined from guc
// Perform global set-up tasks for code generation. Returns 0 on
// success, nonzero on error.
......@@ -106,12 +107,12 @@ ClassType* CodegenEnroll(FuncType regular_func_ptr,
return generator;
}
void* SlotDeformTupleCodegenEnroll(
SlotDeformTupleFn regular_func_ptr,
SlotDeformTupleFn* ptr_to_chosen_func_ptr,
TupleTableSlot* slot) {
SlotDeformTupleCodegen* generator = CodegenEnroll<SlotDeformTupleCodegen>(
regular_func_ptr, ptr_to_chosen_func_ptr, slot);
void* ExecVariableListCodegenEnroll(
ExecVariableListFn regular_func_ptr,
ExecVariableListFn* ptr_to_chosen_func_ptr,
ProjectionInfo* proj_info,
TupleTableSlot* slot) {
ExecVariableListCodegen* generator = CodegenEnroll<ExecVariableListCodegen>(
regular_func_ptr, ptr_to_chosen_func_ptr, proj_info, slot);
return generator;
}
......@@ -3,15 +3,15 @@
// Copyright (C) 2016 Pivotal Software, Inc.
//
// @filename:
// slot_deform_tuple_codegen.h
// ExecVariableList_codegen.h
//
// @doc:
// Headers for slot_deform_tuple codegen
//
//---------------------------------------------------------------------------
#ifndef GPCODEGEN_SLOT_DEFORM_TUPLE_CODEGEN_H_ // NOLINT(build/header_guard)
#define GPCODEGEN_SLOT_DEFORM_TUPLE_CODEGEN_H_
#ifndef GPCODEGEN_EXECVARIABLELIST_CODEGEN_H_ // NOLINT(build/header_guard)
#define GPCODEGEN_EXECVARIABLELIST_CODEGEN_H_
#include "codegen/codegen_wrapper.h"
#include "codegen/base_codegen.h"
......@@ -22,7 +22,7 @@ namespace gpcodegen {
* @{
*/
class SlotDeformTupleCodegen: public BaseCodegen<SlotDeformTupleFn> {
class ExecVariableListCodegen: public BaseCodegen<ExecVariableListFn> {
public:
/**
* @brief Constructor
......@@ -35,30 +35,66 @@ class SlotDeformTupleCodegen: public BaseCodegen<SlotDeformTupleFn> {
* corresponding regular version.
*
**/
explicit SlotDeformTupleCodegen(SlotDeformTupleFn regular_func_ptr,
SlotDeformTupleFn* ptr_to_regular_func_ptr,
TupleTableSlot* slot);
explicit ExecVariableListCodegen(ExecVariableListFn regular_func_ptr,
ExecVariableListFn* ptr_to_regular_func_ptr,
ProjectionInfo* proj_info,
TupleTableSlot* slot);
virtual ~SlotDeformTupleCodegen() = default;
virtual ~ExecVariableListCodegen() = default;
protected:
/**
* @brief Generate code for the code path ExecVariableList > slot_getattr >
* _slot_getsomeattrs > slot_deform_tuple.
*
* @param codegen_utils
*
* @return true on successful generation; false otherwise.
*
* @note Generate code for code path ExecVariableList > slot_getattr >
* _slot_getsomeattrs > slot_deform_tuple. The regular implementation of
* ExecvariableList, for each attribute <i>A</i> in the target list, retrieves
* the slot that <i>A</> comes from and calls slot_getattr. slot_get_attr() will
* eventually call slot_deform_tuple (through _slot_getsomeattrs), which fetches
* all yet unread attributes of the slot until the given attribute.
*
* This function generates the code for the case that all the
* attributes in target list use the same slot (created during a
* scan i.e, ecxt_scantuple). Moreover, instead of looping over the target
* list one at a time, this approach uses slot_getattr only once, with the
* largest attribute index from the target list.
*
* If code generation is not possible for any reason (e.g., attributes
* use different slots), then the function returns false and the codegen manager
* will manage the clean up.
*
* This implementation does not support:
* (1) Null attributes
* (2) Variable length attributes
* (3) Attributes passed by reference
*
* If at execution time, we see any of the above types of attributes, we fall backs to
* the regular function.
*/
bool GenerateCodeInternal(gpcodegen::CodegenUtils* codegen_utils) final;
private:
ProjectionInfo* proj_info_;
TupleTableSlot* slot_;
static constexpr char kSlotDeformTupleNamePrefix[] = "slot_deform_tuple";
static constexpr char kExecVariableListPrefix[] = "ExecVariableList";
/**
* @brief Generates runtime code that calls slot_deform_tuple as an external function.
* @brief Generates runtime code that implements ExecVariableList.
*
* @param codegen_utils Utility to ease the code generation process.
* @return true on successful generation.
**/
bool GenerateSimpleSlotDeformTuple(gpcodegen::CodegenUtils* codegen_utils);
bool GenerateExecVariableList(gpcodegen::CodegenUtils* codegen_utils);
};
/** @} */
} // namespace gpcodegen
#endif // GPCODEGEN_SLOT_DEFORM_TUPLE_CODEGEN_H_
#endif // GPCODEGEN_EXECVARIABLELIST_CODEGEN_H_
......@@ -14,7 +14,6 @@
#include <string>
#include <vector>
#include "codegen/utils/codegen_utils.h"
#include "codegen/codegen_interface.h"
......
......@@ -103,6 +103,8 @@ class CodegenManager {
// CodegenUtils provides a facade to LLVM subsystem.
std::unique_ptr<gpcodegen::CodegenUtils> codegen_utils_;
std::string module_name_;
// List of all enrolled code generators.
std::vector<std::unique_ptr<CodegenInterface>> enrolled_code_generators_;
......
......@@ -396,6 +396,20 @@ class CodegenUtils {
template <typename FunctionType>
FunctionType GetFunctionPointer(const std::string& function_name);
/**
* @brief Generate the commonly used "fallback case" that generates a call to
* the regular_function passing all parameters from generated_function.
*
* @tparam FuncType FunctionType. e.g ReturnType (*)(ArgumenTypes)
* @param regular_function LLVM Function pointer to the regular fallback function
* @param generated_function LLVM Function pointer to the function being generated
*
**/
template<typename FunctionType>
void CreateFallback(llvm::Function* regular_function,
llvm::Function* generated_function);
private:
// Give ClangCompiler access to 'context_' add allow it to add compiled C++
// sources to 'auxiliary_modules_'.
......@@ -1060,6 +1074,28 @@ auto CodegenUtils::GetFunctionPointerImpl(const std::string& function_name)
}
}
template <typename FunctionType>
void CodegenUtils::CreateFallback(llvm::Function* regular_function,
llvm::Function* generated_function) {
assert(regular_function != nullptr);
assert(generated_function != nullptr);
std::vector<llvm::Value*> forwarded_args;
for (llvm::Argument& arg : generated_function->args()) {
forwarded_args.push_back(&arg);
}
llvm::CallInst* call_fallback_func = ir_builder()->CreateCall(
regular_function, forwarded_args);
/* Return the result of the call, or void if the function returns void. */
if (std::is_same<typename codegen_utils_detail::FunctionTypeUnpacker<FunctionType>::R, void>::value) {
ir_builder()->CreateRetVoid();
} else {
ir_builder()->CreateRet(call_fallback_func);
}
}
} // namespace gpcodegen
#endif // GPCODEGEN_CODEGEN_UTILS_H_
......
//---------------------------------------------------------------------------
// Greenplum Database
// Copyright (C) 2016 Pivotal Software, Inc.
//
// @filename:
// codegen_utils.cpp
//
// @doc:
// Contains different code generators
//
//---------------------------------------------------------------------------
#include <cstdint>
#include <string>
#include "codegen/slot_deform_tuple_codegen.h"
#include "codegen/utils/clang_compiler.h"
#include "codegen/utils/utility.h"
#include "codegen/utils/instance_method_wrappers.h"
#include "codegen/utils/codegen_utils.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Casting.h"
extern "C" {
#include "utils/elog.h"
}
using gpcodegen::SlotDeformTupleCodegen;
constexpr char SlotDeformTupleCodegen::kSlotDeformTupleNamePrefix[];
SlotDeformTupleCodegen::SlotDeformTupleCodegen(
SlotDeformTupleFn regular_func_ptr,
SlotDeformTupleFn* ptr_to_regular_func_ptr,
TupleTableSlot* slot) :
BaseCodegen(
kSlotDeformTupleNamePrefix,
regular_func_ptr,
ptr_to_regular_func_ptr), slot_(slot) {
}
static void ElogWrapper(const char* func_name) {
elog(INFO, "Calling wrapped function: %s", func_name);
}
bool SlotDeformTupleCodegen::GenerateSimpleSlotDeformTuple(
gpcodegen::CodegenUtils* codegen_utils) {
llvm::Function* llvm_elog_wrapper = codegen_utils->RegisterExternalFunction(
ElogWrapper);
assert(llvm_elog_wrapper != nullptr);
SlotDeformTupleFn regular_func_pointer = GetRegularFuncPointer();
llvm::Function* llvm_regular_function =
codegen_utils->RegisterExternalFunction(regular_func_pointer);
assert(llvm_regular_function != nullptr);
llvm::Function* llvm_function =
codegen_utils->CreateFunction<SlotDeformTupleFn>(
GetUniqueFuncName());
llvm::BasicBlock* function_body = codegen_utils->CreateBasicBlock(
"fn_body", llvm_function);
codegen_utils->ir_builder()->SetInsertPoint(function_body);
llvm::Value* func_name_llvm = codegen_utils->GetConstant(
GetOrigFuncName().c_str());
codegen_utils->ir_builder()->CreateCall(
llvm_elog_wrapper, { func_name_llvm });
std::vector<llvm::Value*> forwarded_args;
for (llvm::Argument& arg : llvm_function->args()) {
forwarded_args.push_back(&arg);
}
llvm::CallInst* call = codegen_utils->ir_builder()->CreateCall(
llvm_regular_function, forwarded_args);
codegen_utils->ir_builder()->CreateRetVoid();
return true;
}
bool SlotDeformTupleCodegen::GenerateCodeInternal(CodegenUtils* codegen_utils) {
GenerateSimpleSlotDeformTuple(codegen_utils);
return true;
}
......@@ -223,7 +223,9 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
MemoryAccount* curMemoryAccount = NULL;
void* CodegenManager = CodeGeneratorManagerCreate("execProcnode");
StringInfo codegenManagerName = makeStringInfo();
appendStringInfo(codegenManagerName, "%s-%d-%d", "execProcnode", node->plan_node_id, node->type);
void* CodegenManager = CodeGeneratorManagerCreate(codegenManagerName->data);
START_CODE_GENERATOR_MANAGER(CodegenManager);
{
......
......@@ -5862,7 +5862,10 @@ ExecTargetList(List *targetlist,
*
* Results are stored into the passed values and isnull arrays.
*/
static void
#ifndef USE_CODEGEN
static
#endif
void
ExecVariableList(ProjectionInfo *projInfo,
Datum *values,
bool *isnull)
......@@ -5931,7 +5934,7 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
if (isDone)
*isDone = ExprSingleResult;
ExecVariableList(projInfo,
call_ExecVariableList(projInfo,
slot_get_values(slot),
slot_get_isnull(slot));
ExecStoreVirtualTuple(slot);
......
......@@ -14,6 +14,7 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "codegen/codegen_wrapper.h"
#include "executor/executor.h"
#include "miscadmin.h"
......@@ -300,6 +301,12 @@ InitScanStateRelationDetails(ScanState *scanState, Plan *plan, EState *estate)
ExecAssignScanType(scanState, RelationGetDescr(currentRelation));
ExecAssignScanProjectionInfo(scanState);
ProjectionInfo *projInfo = scanState->ps.ps_ProjInfo;
if (NULL != projInfo && projInfo->pi_isVarList){
enroll_ExecVariableList_codegen(ExecVariableList,
&projInfo->ExecVariableList_gen_info.ExecVariableList_fn, projInfo, scanState->ss_ScanTupleSlot);
}
scanState->tableType = getTableType(scanState->ss_currentRelation);
}
......
......@@ -103,7 +103,6 @@
#include "utils/typcache.h"
#include "cdb/cdbvars.h" /* Gp_segment */
#include "codegen/codegen_wrapper.h"
static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk);
......@@ -361,11 +360,6 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
MemoryContextSwitchTo(oldcontext);
}
/*
* This will enroll to codegen manager if USE_CODEGEN is enabled
*/
enroll_slot_deform_tuple_codegen(slot_deform_tuple,
&slot->slot_deform_tuple_gen_info.slot_deform_tuple_fn, slot);
}
/* --------------------------------
......
......@@ -689,6 +689,9 @@ ExecGetResultType(PlanState *planstate)
return slot->tts_tupleDescriptor;
}
extern void
ExecVariableList(ProjectionInfo *projInfo, Datum *values, bool *isnull);
/* ----------------
* ExecBuildProjectionInfo
*
......@@ -823,6 +826,10 @@ ExecBuildProjectionInfo(List *targetList,
projInfo->pi_varNumbers = NULL;
}
#ifdef USE_CODEGEN
// Set the default location for ExecVariableList
projInfo->ExecVariableList_gen_info.ExecVariableList_fn = ExecVariableList;
#endif
return projInfo;
}
......
......@@ -575,6 +575,7 @@ bool optimizer_prefer_scalar_dqa_multistage_agg;
/**
* GUCs related to code generation.
**/
bool init_codegen;
bool codegen;
/* Security */
......@@ -3397,11 +3398,21 @@ struct config_bool ConfigureNamesBool_gp[] =
},
{
{"codegen", PGC_POSTMASTER, DEVELOPER_OPTIONS,
{"init_codegen", PGC_POSTMASTER, DEVELOPER_OPTIONS,
gettext_noop("Enable just-in-time code generation."),
NULL,
GUC_NOT_IN_SAMPLE
},
&init_codegen,
false, NULL, NULL
},
{
{"codegen", PGC_USERSET, DEVELOPER_OPTIONS,
gettext_noop("Perform just-in-time code generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
},
&codegen,
false, NULL, NULL
},
......
......@@ -13,12 +13,21 @@
#define CODEGEN_WRAPPER_H_
#include "pg_config.h"
#include "c.h"
#ifndef __cplusplus
#include "postgres.h"
#else
typedef int64 Datum;
#endif
/*
* Code that needs to be shared irrespective of whether USE_CODEGEN is enabled or not.
*/
struct TupleTableSlot;
typedef void (*SlotDeformTupleFn) (struct TupleTableSlot *slot, int natts);
struct ProjectionInfo;
typedef void (*ExecVariableListFn) (struct ProjectionInfo *projInfo, Datum *values, bool *isnull);
#ifndef USE_CODEGEN
......@@ -35,16 +44,11 @@ typedef void (*SlotDeformTupleFn) (struct TupleTableSlot *slot, int natts);
#define END_CODE_GENERATOR_MANAGER()
#define init_codegen()
#define call_slot_deform_tuple(slot, attno) slot_deform_tuple(slot, attno)
#define enroll_slot_deform_tuple_codegen(regular_func, ptr_to_chosen_func, slot)
#define call_ExecVariableList(projInfo, values, isnull) ExecVariableList(projInfo, values, isnull)
#define enroll_ExecVariableList_codegen(regular_func, ptr_to_chosen_func, proj_info, slot)
#else
/*
* Forward extern declaration of slot deform tuple if code gen is enabled
*/
extern void slot_deform_tuple(struct TupleTableSlot *slot, int natts);
/*
* @brief Life span of Code generator instance
*
......@@ -68,6 +72,12 @@ typedef enum CodegenFuncLifespan
extern "C" {
#endif
/*
* Forward extern declaration of code generated functions if code gen is enabled
*/
extern void ExecVariableList(struct ProjectionInfo *projInfo, Datum *values, bool *isnull);
/*
* Do one-time global initialization of LLVM library. Returns 1
* on success, 0 on error.
......@@ -119,14 +129,14 @@ void
SetActiveCodeGeneratorManager(void* manager);
/*
* returns the pointer to the SlotDeformTupleCodegen
* returns the pointer to the ExecVariableList
*/
void*
SlotDeformTupleCodegenEnroll(SlotDeformTupleFn regular_func_ptr,
SlotDeformTupleFn* ptr_to_regular_func_ptr,
ExecVariableListCodegenEnroll(ExecVariableListFn regular_func_ptr,
ExecVariableListFn* ptr_to_regular_func_ptr,
struct ProjectionInfo* proj_info,
struct TupleTableSlot* slot);
#ifdef __cplusplus
} // extern "C"
#endif
......@@ -158,7 +168,7 @@ SlotDeformTupleCodegenEnroll(SlotDeformTupleFn regular_func_ptr,
* Initialize LLVM library
*/
#define init_codegen() \
if (codegen) { \
if (init_codegen) { \
if (InitCodegen() == 0) { \
ereport(FATAL, \
(errcode(ERRCODE_INTERNAL_ERROR), \
......@@ -171,22 +181,20 @@ SlotDeformTupleCodegenEnroll(SlotDeformTupleFn regular_func_ptr,
} \
/*
* Call slot_deform_tuple using function pointer slot_deform_tuple_fn.
* Call ExecVariableList using function pointer ExecVariableList_fn.
* Function pointer may point to regular version or generated function
*/
#define call_slot_deform_tuple(slot, attno) \
slot->slot_deform_tuple_gen_info.slot_deform_tuple_fn(slot, attno)
#define call_ExecVariableList(projInfo, values, isnull) \
projInfo->ExecVariableList_gen_info.ExecVariableList_fn(projInfo, values, isnull)
/*
* Enroll given slot to codegen manager.
* The enrollment process also ensures that the slot_deform_tuple_fn pointer
* Enrollment macros
* The enrollment process also ensures that the generated function pointer
* is set to the regular version initially
*/
#define enroll_slot_deform_tuple_codegen(regular_func, ptr_to_regular_func_ptr, slot) \
slot->slot_deform_tuple_gen_info.code_generator = SlotDeformTupleCodegenEnroll( \
regular_func, ptr_to_regular_func_ptr, slot); \
Assert(slot->slot_deform_tuple_gen_info.slot_deform_tuple_fn == regular_func); \
#define enroll_ExecVariableList_codegen(regular_func, ptr_to_regular_func_ptr, proj_info, slot) \
proj_info->ExecVariableList_gen_info.code_generator = ExecVariableListCodegenEnroll( \
regular_func, ptr_to_regular_func_ptr, proj_info, slot); \
Assert(proj_info->ExecVariableList_gen_info.ExecVariableList_fn == regular_func); \
#endif //USE_CODEGEN
......
......@@ -111,17 +111,6 @@
#define TTS_SHOULDFREE 2
#define TTS_VIRTUAL 4
/*
* Interface to the CodegenManager for slot_deform_tuple code generation
*/
typedef struct SlotDeformTupleCodegenInfo
{
/* Pointer to store SlotDeformTupleCodegen from Codegen */
void* code_generator;
/* Function pointer that points to either regular or generated slot_deform_tuple */
SlotDeformTupleFn slot_deform_tuple_fn;
} SlotDeformTupleCodegenInfo;
typedef struct TupleTableSlot
{
NodeTag type; /* vestigial ... allows IsA tests */
......@@ -152,10 +141,6 @@ typedef struct TupleTableSlot
/* System attributes */
Oid tts_tableOid;
#ifdef USE_CODEGEN
SlotDeformTupleCodegenInfo slot_deform_tuple_gen_info;
#endif
} TupleTableSlot;
static inline bool TupIsNull(TupleTableSlot *slot)
......
......@@ -218,6 +218,14 @@ typedef struct ReturnSetInfo
TupleDesc setDesc; /* actual descriptor for returned tuples */
} ReturnSetInfo;
typedef struct ExecVariableListCodegenInfo
{
/* Pointer to store ExecVariableListCodegen from Codegen */
void* code_generator;
/* Function pointer that points to either regular or generated slot_deform_tuple */
ExecVariableListFn ExecVariableList_fn;
} ExecVariableListCodegenInfo;
/* ----------------
* ProjectionInfo node information
*
......@@ -261,6 +269,10 @@ typedef struct ProjectionInfo
int pi_lastInnerVar;
int pi_lastOuterVar;
int pi_lastScanVar;
#ifdef USE_CODEGEN
ExecVariableListCodegenInfo ExecVariableList_gen_info;
#endif
} ProjectionInfo;
/* ----------------
......
......@@ -454,6 +454,7 @@ extern bool optimizer_prefer_scalar_dqa_multistage_agg;
/**
* GUCs related to code generation.
**/
extern bool init_codegen;
extern bool codegen;
/**
......
......@@ -67,12 +67,13 @@ SetActiveCodeGeneratorManager(void* manager)
// returns the pointer to the SlotDeformTupleCodegen
void*
SlotDeformTupleCodegenEnroll(SlotDeformTupleFn regular_func_ptr,
SlotDeformTupleFn* ptr_to_regular_func_ptr,
struct TupleTableSlot* slot)
ExecVariableListCodegenEnroll(ExecVariableListFn regular_func_ptr,
ExecVariableListFn* ptr_to_regular_func_ptr,
struct ProjectionInfo* proj_info,
struct TupleTableSlot* slot)
{
*ptr_to_regular_func_ptr = regular_func_ptr;
elog(ERROR, "mock implementation of SlotDeformTupleCodegen_Enroll called");
elog(ERROR, "mock implementation of ExecVariableListEnroll called");
return NULL;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册