gollvm: refactor cabiOracle and Llvm_backend class
Currently, some code of gollvm is written specifically for amd64, such as classes
cabiOracle and Llvm_backend. To make it easier for gollvm to extend to multiple
architectures, this patch mainly makes the following modifications:
1, Adds a llvm::CallingConv::ID type parameter to the constructor of class Llvm_backend,
so that we can generate different backend according to this parameter.
2, Refactors class cabiOracle with class inheritance. Firstly we add a pure virtual
class CABIOracleArgumentAnalyzer, which contains the common architecture independent
interfaces. Then implemente a sub-class CABIOracleX86_64_SysV for amd64, it inherits
class CABIOracleArgumentAnalyzer. Finally add a pointer data member of class
CABIOracleArgumentAnalyzer into class CABIOracle, and rewrite part of the public
functions of class CABIOracle with this pointer. The pointer will be assigned to
different sub-class instance object according to the calling convention value of type
manager, so we can have different ABI implementation with class cabiOracle.
With this change, we can easily port gollvm to another architecture by implementing
a sub-class of class CABIOracleArgumentAnalyzer.
This CL doesn't modify the unit test cases, we'll do this work in the follow up patches.
Change-Id: I40ab0cfc0012f404f3d237844d4731fc5623b686
Reviewed-on: https://go-review.googlesource.com/c/gollvm/+/211837
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/bridge/go-llvm-bfunction.cpp b/bridge/go-llvm-bfunction.cpp
index 7109ccb..5afdcaa 100644
--- a/bridge/go-llvm-bfunction.cpp
+++ b/bridge/go-llvm-bfunction.cpp
@@ -187,11 +187,11 @@
if (paramInfo.abiTypes().size() == 1) {
arguments_[soff]->setName(name);
} else {
- assert(paramInfo.abiTypes().size() == 2);
- std::string argp1(name); argp1 += ".chunk0";
- std::string argp2(name); argp2 += ".chunk1";
- arguments_[soff]->setName(argp1);
- arguments_[soff+1]->setName(argp2);
+ assert(paramInfo.abiTypes().size() <= CABIParamInfo::ABI_TYPES_MAX_SIZE);
+ for (unsigned i = 0; i < paramInfo.abiTypes().size(); ++i) {
+ std::string argp = name + ".chunk" + std::to_string(i);
+ arguments_[soff+i]->setName(argp);
+ }
}
std::string aname(name);
aname += ".addr";
@@ -372,36 +372,31 @@
paramVar->setInitializer(si);
return 1;
}
- assert(paramInfo.abiTypes().size() == 2);
- // More complex case: param arrives in two registers.
+ assert(paramInfo.abiTypes().size() <= CABIParamInfo::ABI_TYPES_MAX_SIZE);
+ // More complex case: param arrives in multiple registers.
- // Create struct type corresponding to first and second params
+ // Create struct type corresponding to multiple params.
llvm::Type *llst = paramInfo.computeABIStructType(tm);
llvm::Type *ptst = llvm::PointerType::get(llst, 0);
// Cast the spill location to a pointer to the struct created above.
std::string tag(namegen("cast"));
llvm::Value *bitcast = builder.CreateBitCast(sploc, ptst, tag);
+ llvm::Instruction *stinst = nullptr;
- // Generate a store to the first field
- std::string tag0(namegen("field0"));
- llvm::Value *field0gep =
- builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, 0, tag0);
- llvm::Value *argChunk0 = arguments_[paramInfo.sigOffset()];
- builder.CreateStore(argChunk0, field0gep);
-
- // Generate a store to the second field
- std::string tag1(namegen("field1"));
- llvm::Value *field1gep =
- builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, 1, tag0);
- llvm::Value *argChunk1 = arguments_[paramInfo.sigOffset()+1];
- llvm::Instruction *stinst = builder.CreateStore(argChunk1, field1gep);
-
+ // Generate a store to each field.
+ for (unsigned i = 0; i < paramInfo.abiTypes().size(); ++i) {
+ std::string tag(namegen("field" + std::to_string(i)));
+ llvm::Value *fieldgep =
+ builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, i, tag);
+ llvm::Value *argChunk = arguments_[paramInfo.sigOffset() + i];
+ stinst = builder.CreateStore(argChunk, fieldgep);
+ }
paramVar->setInitializer(stinst);
// All done.
- return 2;
+ return paramInfo.abiTypes().size();
}
void Bfunction::genProlog(llvm::BasicBlock *entry)
diff --git a/bridge/go-llvm-cabi-oracle.cpp b/bridge/go-llvm-cabi-oracle.cpp
index 95b9b10..b5d870b 100644
--- a/bridge/go-llvm-cabi-oracle.cpp
+++ b/bridge/go-llvm-cabi-oracle.cpp
@@ -42,17 +42,6 @@
static TypDisp getTypDisp(llvm::Type *typ) {
if (typ->isFloatTy() || typ->isDoubleTy())
return FlavSSE;
- if (typ->isArrayTy()) {
- llvm::ArrayType *at = llvm::cast<llvm::ArrayType>(typ);
- return getTypDisp(at->getTypeAtIndex(0u));
- }
- if (typ->isStructTy()) {
- llvm::StructType *st = llvm::cast<llvm::StructType>(typ);
- TypDisp disp = FlavEmpty;
- for (unsigned idx = 0; idx < st->getNumElements(); idx++)
- disp = dispMeet(getTypDisp(st->getElementType(idx)), disp);
- return disp;
- }
return FlavInt;
}
@@ -129,30 +118,28 @@
TypeManager *typeManager_;
typedef std::pair<Btype *, unsigned> typAndOffset;
- void addLeafTypes(Btype *bt, unsigned off,
- std::vector<typAndOffset> *leaves);
+ void addLeafTypes(Btype *bt, unsigned off, std::vector<typAndOffset> *leaves);
+ void explode(Btype *bt);
void explodeStruct(Btype *bst);
void explodeArray(BArrayType *bat);
void incorporateScalar(Btype *bt);
- void determineABITypes();
+ void determineABITypesForX86_64_SysV();
TypeManager *tm() const { return typeManager_; }
};
EightByteInfo::EightByteInfo(Btype *bt, TypeManager *tmgr)
: typeManager_(tmgr)
{
- BStructType *bst = bt->castToBStructType();
- BComplexType *bct = bt->castToBComplexType();
- BArrayType *bat = bt->castToBArrayType();
- if (bst || bct) {
- explodeStruct(bt);
- } else if (bat) {
- explodeArray(bat);
- } else {
- incorporateScalar(bt);
+ explode(bt);
+ llvm::CallingConv::ID cconv = tmgr->callingConv();
+ switch (cconv) {
+ case llvm::CallingConv::X86_64_SysV:
+ determineABITypesForX86_64_SysV();
+ break;
+ default:
+ llvm::errs() << "unsupported llvm::CallingConv::ID " << cconv << "\n";
+ break;
}
- assert(ebrs_.size() <= 2);
- determineABITypes();
}
// Perform a pre-order walk of a type, collecting the various leaf
@@ -233,26 +220,25 @@
// type bar struct {
// f1 double; [0] double 0
// f2 uint8; [1] unsigned char 64
-// f3 int16; short 70
+// f3 int16; short 80
// }
//
void EightByteInfo::explodeStruct(Btype *bst)
{
- assert(tm()->typeSize(bst) <= 16);
-
std::vector<typAndOffset> leafTypes;
addLeafTypes(bst, 0, &leafTypes);
// collect offsets and field types
EightByteRegion *cur8 = nullptr;
-
+ unsigned curOffset = 0;
for (auto &pair : leafTypes) {
Btype *lt = pair.first;
unsigned offset = pair.second;
- if (cur8 == nullptr || (offset >= 8 && ebrs_.size() == 1)) {
+ if (cur8 == nullptr || (offset - curOffset >= 8)) {
ebrs_.push_back(EightByteRegion());
cur8 = &ebrs_.back();
+ curOffset = offset;
}
cur8->types.push_back(lt->type());
cur8->offsets.push_back(offset);
@@ -279,24 +265,23 @@
// unsigned short 32
// unsigned short 48
// [1] unsigned short 64
-// unsigned short 70
+// unsigned short 80
void EightByteInfo::explodeArray(BArrayType *bat)
{
- assert(tm()->typeSize(bat) <= 16);
EightByteRegion *cur8 = nullptr;
unsigned curOffset = 0;
unsigned elSize = tm()->typeSize(bat->elemType());
for (unsigned elidx = 0; elidx < bat->nelSize(); ++elidx) {
unsigned offset = elidx * elSize;
- if (cur8 == nullptr || (offset >= 8 && curOffset < 8)) {
+ if (cur8 == nullptr || (offset - curOffset >= 8)) {
ebrs_.push_back(EightByteRegion());
cur8 = &ebrs_.back();
+ curOffset = offset;
}
// note that elem type here may be composite
cur8->types.push_back(bat->elemType()->type());
cur8->offsets.push_back(offset);
- curOffset = offset;
}
}
@@ -313,6 +298,20 @@
ebrs_.push_back(ebr);
}
+// Convert a Btype into EightByteRegions.
+void EightByteInfo::explode(Btype *bt)
+{
+ BStructType *bst = bt->castToBStructType();
+ BComplexType *bct = bt->castToBComplexType();
+ BArrayType *bat = bt->castToBArrayType();
+ if (bst || bct || bat) {
+ // Flatten all composite types.
+ explodeStruct(bt);
+ } else {
+ incorporateScalar(bt);
+ }
+}
+
void EightByteInfo::getRegisterRequirements(unsigned *numInt, unsigned *numSSE)
{
*numInt = 0;
@@ -347,8 +346,11 @@
// the correct alignment. Work around this by checking for such situations
// and promoting the type of the first EBR to 64 bits.
//
-void EightByteInfo::determineABITypes()
+void EightByteInfo::determineABITypesForX86_64_SysV()
{
+ // In the direct case, ebrs_.size() cannot be greater than 2 because parameters
+ // larger than 16 bytes are passed indirectly.
+ assert(ebrs_.size() <= 2);
unsigned intRegions = 0;
unsigned floatRegions = 0;
for (auto &ebr : ebrs_) {
@@ -401,10 +403,9 @@
assert(abiTypes_[0]->isStructTy());
return abiTypes_[0];
}
- assert(abiTypes_.size() == 2);
- llvm::Type *ft0 = abiTypes_[0];
- llvm::Type *ft1 = abiTypes_[1];
- llvm::Type *llst = tm->makeLLVMTwoElementStructType(ft0, ft1);
+
+ assert(abiTypes_.size() <= CABIParamInfo::ABI_TYPES_MAX_SIZE);
+ llvm::Type *llst = tm->makeLLVMStructType(abiTypes_);
return llst;
}
@@ -444,8 +445,20 @@
// signature) along with available int/sse regs.
class ABIState {
- public:
- ABIState() : availIntRegs_(6), availSSERegs_(8), argCount_(0) { }
+public:
+ ABIState(TypeManager *typm) : argCount_(0) {
+ assert(typm != nullptr);
+ llvm::CallingConv::ID cconv = typm->callingConv();
+ switch (cconv) {
+ case llvm::CallingConv::X86_64_SysV:
+ availIntRegs_ = 6;
+ availSSERegs_ = 8;
+ break;
+ default:
+ llvm::errs() << "unsupported llvm::CallingConv::ID " << cconv << "\n";
+ break;
+ }
+ }
void addDirectIntArg() {
if (availIntRegs_)
availIntRegs_ -= 1;
@@ -455,23 +468,19 @@
if (availSSERegs_)
availSSERegs_ -= 1;
argCount_ += 1;
- }
- void addIndirectArg() {
- argCount_ += 1;
}
+ void addIndirectArg() { argCount_ += 1; }
void addIndirectReturn() {
if (availIntRegs_)
availIntRegs_ -= 1;
argCount_ += 1;
}
- void addChainArg() {
- argCount_ += 1;
- }
+ void addChainArg() { argCount_ += 1; }
unsigned argCount() const { return argCount_; }
unsigned availIntRegs() const { return availIntRegs_; }
unsigned availSSERegs() const { return availSSERegs_; }
- private:
+private:
unsigned availIntRegs_;
unsigned availSSERegs_;
unsigned argCount_;
@@ -488,7 +497,10 @@
, fcnTypeForABI_(nullptr)
, typeManager_(typeManager)
, followsCabi_(followsCabi)
+ , ccID_(llvm::CallingConv::MaxID)
+ , cc_(nullptr)
{
+ setCC();
analyze();
}
@@ -499,29 +511,47 @@
, fcnTypeForABI_(nullptr)
, typeManager_(typeManager)
, followsCabi_(ft->followsCabi())
+ , ccID_(llvm::CallingConv::MaxID)
+ , cc_(nullptr)
{
+ setCC();
analyze();
}
-bool CABIOracle::supported() const
+void CABIOracle::setCC()
{
- return tm()->callingConv() == llvm::CallingConv::X86_64_SysV;
+ assert(typeManager_ != nullptr);
+ ccID_ = typeManager_->callingConv();
+ // Supported architectures at present.
+ assert(ccID_ == llvm::CallingConv::X86_64_SysV);
+
+ if (cc_ != nullptr) {
+ return;
+ }
+ switch (ccID_) {
+ case llvm::CallingConv::X86_64_SysV:
+ cc_ = std::unique_ptr<CABIOracleArgumentAnalyzer>(new CABIOracleX86_64_SysV(typeManager_));
+ break;
+ default:
+ llvm::errs() << "unsupported llvm::CallingConv::ID " << ccID_ << "\n";
+ break;
+ }
}
-const llvm::DataLayout *CABIOracle::datalayout() const
+TypeManager *CABIOracle::tm() const
{
- return typeManager_->datalayout();
+ return typeManager_;
}
llvm::FunctionType *CABIOracle::getFunctionTypeForABI()
{
- assert(supported());
+ assert(cc_ != nullptr);
return fcnTypeForABI_;
}
const CABIParamInfo &CABIOracle::paramInfo(unsigned idx)
{
- assert(supported());
+ assert(cc_ != nullptr);
// Slot 0: return info
// Slot 1: static chain param
// Slot 2: first argument / parameter
@@ -532,7 +562,7 @@
const CABIParamInfo &CABIOracle::returnInfo()
{
- assert(supported());
+ assert(cc_ != nullptr);
unsigned ridx = 0;
assert(ridx < infov_.size());
return infov_[ridx];
@@ -540,7 +570,7 @@
const CABIParamInfo &CABIOracle::chainInfo()
{
- assert(supported());
+ assert(cc_ != nullptr);
unsigned ridx = 1;
assert(ridx < infov_.size());
return infov_[ridx];
@@ -569,151 +599,6 @@
}
}
-// For full C++ (with long double, unions, vector types) the
-// rules here are a good deal more complicated, but for Go
-// it all boils down to the size of the type.
-
-CABIParamDisp CABIOracle::classifyArgType(Btype *btype)
-{
- int64_t sz = tm()->typeSize(btype);
- return (sz == 0 ? ParmIgnore : ((sz <= 16) ? ParmDirect : ParmIndirect));
-}
-
-CABIParamInfo CABIOracle::analyzeABIReturn(Btype *resultType, ABIState &state)
-{
- llvm::Type *rtyp = resultType->type();
- CABIParamDisp rdisp = (rtyp == tm()->llvmVoidType() ?
- ParmIgnore : classifyArgType(resultType));
-
- if (rdisp == ParmIgnore) {
- // This corresponds to a function with no returns or
- // returning an empty composite.
- llvm::Type *voidType = tm()->llvmVoidType();
- return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
- }
-
- if (rdisp == ParmIndirect) {
- // Return value will be passed in memory, via a hidden
- // struct return param.
- // It is on stack, therefore address space 0.
- llvm::Type *ptrTyp = llvm::PointerType::get(rtyp, 0);
- state.addIndirectReturn();
- return CABIParamInfo(ptrTyp, ParmIndirect, AttrStructReturn, 0);
- }
-
- // Figure out what to do in the direct case
- assert(rdisp == ParmDirect);
- EightByteInfo ebi(resultType, tm());
- auto ®ions = ebi.regions();
- if (regions.size() == 1) {
- // Single value
- return CABIParamInfo(regions[0].abiDirectType,
- ParmDirect, regions[0].attr, -1);
- }
-
- // Two-element struct
- assert(regions.size() == 2);
- llvm::Type *abiTyp =
- tm()->makeLLVMTwoElementStructType(regions[0].abiDirectType,
- regions[1].abiDirectType);
- return CABIParamInfo(abiTyp, ParmDirect, AttrNone, -1);
-}
-
-// Given the number of registers that we think a param is going to consume, and
-// a state object storing the registers used so far, canPassDirectly() makes a
-// decision as to whether a given param can be passed directly in registers vs
-// in memory.
-//
-// Note the first clause, "if (regsInt + regsSSE == 1) return true". This may
-// seem counter-intuitive (why no check against the state object?), but this way
-// of doing things is the convention used by other front ends (e.g. clang). What
-// is happening here is that for larger aggregate/array params (things that
-// don't fit into a single register), we'll make the pass-through-memory
-// semantics explicit in the function signature and generate the explict code to
-// copy things into memory. For params that do fit into a single register,
-// however, we just leave them all as by-value parameters and then assume that
-// the back end will do the right thing (e.g. pass the first few in registers
-// and then the remaining ones in memory).
-//
-// Doing things this way has performance advantages in that the middle-end
-// (all of the machine-independent LLVM optimization passes) won't have
-// to deal with the additional chunks of stack memory and code to copy
-// things onto and off of the stack (not to mention the aliasing concerns
-// when a local variable's address is taken and then passed in a function
-// call).
-
-bool CABIOracle::canPassDirectly(unsigned regsInt,
- unsigned regsSSE,
- ABIState &state)
-{
- if (regsInt + regsSSE == 1) // see comment above
- return true;
- if (regsInt <= state.availIntRegs() && regsSSE <= state.availSSERegs())
- return true;
- return false;
-}
-
-CABIParamInfo CABIOracle::analyzeABIParam(Btype *paramType, ABIState &state)
-{
- llvm::Type *ptyp = paramType->type();
-
- // The only situations in which we should be seeing AuxT types here is
- // in cases where we're analyzing the signatures of builtin functions,
- // meaning that there should be no structures or arrays.
- assert(paramType->flavor() != Btype::AuxT || ptyp->isVoidTy() ||
- !(ptyp->isStructTy() || ptyp->isArrayTy() ||
- ptyp->isVectorTy() || ptyp->isEmptyTy() ||
- ptyp->isIntegerTy(8) || ptyp->isIntegerTy(16)));
-
- CABIParamDisp pdisp = classifyArgType(paramType);
-
- if (pdisp == ParmIgnore) {
- // Empty struct or array
- llvm::Type *voidType = tm()->llvmVoidType();
- return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
- }
-
- int sigOff = state.argCount();
-
- if (pdisp == ParmIndirect) {
- // Value will be passed in memory on stack.
- // Stack is always in address space 0.
- llvm::Type *ptrTyp = llvm::PointerType::get(ptyp, 0);
- state.addIndirectArg();
- return CABIParamInfo(ptrTyp, ParmIndirect, AttrByVal, sigOff);
- }
-
- // Figure out what to do in the direct case
- assert(pdisp == ParmDirect);
- EightByteInfo ebi(paramType, tm());
-
- // Figure out how many registers it would take to pass this parm directly
- unsigned regsInt = 0, regsSSE = 0;
- ebi.getRegisterRequirements(®sInt, ®sSSE);
-
- // Make direct/indirect decision
- CABIParamAttr attr = AttrNone;
- if (canPassDirectly(regsInt, regsSSE, state)) {
- std::vector<llvm::Type *> abiTypes;
- for (auto &ebr : ebi.regions()) {
- abiTypes.push_back(ebr.abiDirectType);
- if (ebr.attr != AttrNone) {
- assert(attr == AttrNone || attr == ebr.attr);
- attr = ebr.attr;
- }
- if (ebr.getRegionTypDisp() == FlavSSE)
- state.addDirectSSEArg();
- else
- state.addDirectIntArg();
- }
- return CABIParamInfo(abiTypes, ParmDirect, attr, sigOff);
- } else {
- state.addIndirectArg();
- llvm::Type *ptrTyp = llvm::PointerType::get(ptyp, 0);
- return CABIParamInfo(ptrTyp, ParmIndirect, AttrByVal, sigOff);
- }
-}
-
// Fill in parameter / return / type information for a builtin function,
// e.g. all values passed + returned directly, no static chain param.
@@ -746,10 +631,6 @@
fcnTypeForABI_ = llvm::FunctionType::get(rtyp, elems, isVarargs);
}
-// This driver function carries out the various classification steps
-// described in the AMD64 ABI Draft 0.99.8 document, section 3.2.3,
-// 4th page and thereabouts.
-
void CABIOracle::analyze()
{
if (fcnTypeForABI_)
@@ -759,10 +640,11 @@
return;
}
- ABIState state;
+ ABIState state(tm());
+ assert(cc_ != nullptr);
// First slot in the info vector will be for the return.
- infov_.push_back(analyzeABIReturn(fcnResultType_, state));
+ infov_.push_back(cc_->analyzeABIReturn(fcnResultType_, state));
// Static chain parameter
int sigOff = state.argCount();
@@ -773,7 +655,7 @@
// Now process the params.
for (unsigned idx = 0; idx < fcnParamTypes_.size(); ++idx) {
Btype *pType = fcnParamTypes_[idx];
- auto d = analyzeABIParam(pType, state);
+ auto d = cc_->analyzeABIParam(pType, state);
infov_.push_back(d);
}
@@ -794,3 +676,156 @@
const bool isVarargs = false;
fcnTypeForABI_ = llvm::FunctionType::get(rtyp, elems, isVarargs);
}
+
+//......................................................................
+
+CABIOracleX86_64_SysV::CABIOracleX86_64_SysV(TypeManager *typeManager)
+ : CABIOracleArgumentAnalyzer(typeManager) {}
+
+// For full C++ (with long double, unions, vector types) the
+// rules here are a good deal more complicated, but for Go
+// it all boils down to the size of the type.
+
+CABIParamDisp CABIOracleX86_64_SysV::classifyArgType(Btype *btype)
+{
+ int64_t sz = tm_->typeSize(btype);
+ return (sz == 0 ? ParmIgnore : ((sz <= 16) ? ParmDirect : ParmIndirect));
+}
+
+// Given the number of registers that we think a param is going to consume, and
+// a state object storing the registers used so far, canPassDirectly() makes a
+// decision as to whether a given param can be passed directly in registers vs
+// in memory.
+//
+// Note the first clause, "if (regsInt + regsSSE == 1) return true". This may
+// seem counter-intuitive (why no check against the state object?), but this way
+// of doing things is the convention used by other front ends (e.g. clang). What
+// is happening here is that for larger aggregate/array params (things that
+// don't fit into a single register), we'll make the pass-through-memory
+// semantics explicit in the function signature and generate the explict code to
+// copy things into memory. For params that do fit into a single register,
+// however, we just leave them all as by-value parameters and then assume that
+// the back end will do the right thing (e.g. pass the first few in registers
+// and then the remaining ones in memory).
+//
+// Doing things this way has performance advantages in that the middle-end
+// (all of the machine-independent LLVM optimization passes) won't have
+// to deal with the additional chunks of stack memory and code to copy
+// things onto and off of the stack (not to mention the aliasing concerns
+// when a local variable's address is taken and then passed in a function
+// call).
+
+bool CABIOracleX86_64_SysV::canPassDirectly(unsigned regsInt,
+ unsigned regsSSE,
+ ABIState &state)
+{
+ if (regsInt + regsSSE == 1) // see comment above
+ return true;
+ if (regsInt <= state.availIntRegs() && regsSSE <= state.availSSERegs())
+ return true;
+ return false;
+}
+
+CABIParamInfo CABIOracleX86_64_SysV::analyzeABIParam(Btype *paramType,
+ ABIState &state)
+{
+ llvm::Type *ptyp = paramType->type();
+
+ // The only situations in which we should be seeing AuxT types here is
+ // in cases where we're analyzing the signatures of builtin functions,
+ // meaning that there should be no structures or arrays.
+ assert(paramType->flavor() != Btype::AuxT || ptyp->isVoidTy() ||
+ !(ptyp->isStructTy() || ptyp->isArrayTy() || ptyp->isVectorTy() ||
+ ptyp->isEmptyTy() || ptyp->isIntegerTy(8) || ptyp->isIntegerTy(16)));
+
+ CABIParamDisp pdisp = classifyArgType(paramType);
+
+ if (pdisp == ParmIgnore) {
+ // Empty struct or array
+ llvm::Type *voidType = tm_->llvmVoidType();
+ return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
+ }
+
+ int sigOff = state.argCount();
+
+ if (pdisp == ParmIndirect) {
+ // Value will be passed in memory on stack.
+ // Stack is always in address space 0.
+ llvm::Type *ptrTyp = llvm::PointerType::get(ptyp, 0);
+ state.addIndirectArg();
+ return CABIParamInfo(ptrTyp, ParmIndirect, AttrByVal, sigOff);
+ }
+
+ // Figure out what to do in the direct case
+ assert(pdisp == ParmDirect);
+ EightByteInfo ebi(paramType, tm_);
+
+ // Figure out how many registers it would take to pass this parm directly
+ unsigned regsInt = 0, regsSSE = 0;
+ ebi.getRegisterRequirements(®sInt, ®sSSE);
+
+ // Make direct/indirect decision
+ CABIParamAttr attr = AttrNone;
+ if (canPassDirectly(regsInt, regsSSE, state)) {
+ std::vector<llvm::Type *> abiTypes;
+ for (auto &ebr : ebi.regions()) {
+ abiTypes.push_back(ebr.abiDirectType);
+ if (ebr.attr != AttrNone) {
+ assert(attr == AttrNone || attr == ebr.attr);
+ attr = ebr.attr;
+ }
+ if (ebr.getRegionTypDisp() == FlavSSE)
+ state.addDirectSSEArg();
+ else
+ state.addDirectIntArg();
+ }
+ return CABIParamInfo(abiTypes, ParmDirect, attr, sigOff);
+ } else {
+ state.addIndirectArg();
+ llvm::Type *ptrTyp = llvm::PointerType::get(ptyp, 0);
+ return CABIParamInfo(ptrTyp, ParmIndirect, AttrByVal, sigOff);
+ }
+}
+
+CABIParamInfo CABIOracleX86_64_SysV::analyzeABIReturn(Btype *resultType,
+ ABIState &state) {
+ llvm::Type *rtyp = resultType->type();
+ CABIParamDisp rdisp =
+ (rtyp == tm_->llvmVoidType() ? ParmIgnore
+ : classifyArgType(resultType));
+
+ if (rdisp == ParmIgnore) {
+ // This corresponds to a function with no returns or
+ // returning an empty composite.
+ llvm::Type *voidType = tm_->llvmVoidType();
+ return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
+ }
+
+ if (rdisp == ParmIndirect) {
+ // Return value will be passed in memory, via a hidden
+ // struct return param.
+ // It is on stack, therefore address space 0.
+ llvm::Type *ptrTyp = llvm::PointerType::get(rtyp, 0);
+ state.addIndirectReturn();
+ return CABIParamInfo(ptrTyp, ParmIndirect, AttrStructReturn, 0);
+ }
+
+ // Figure out what to do in the direct case
+ assert(rdisp == ParmDirect);
+ EightByteInfo ebi(resultType, tm_);
+ auto ®ions = ebi.regions();
+ if (regions.size() == 1) {
+ // Single value
+ return CABIParamInfo(regions[0].abiDirectType,
+ ParmDirect, regions[0].attr, -1);
+ }
+
+ // Two-element struct
+ assert(regions.size() == 2);
+ llvm::Type *abiTyp =
+ tm_->makeLLVMTwoElementStructType(regions[0].abiDirectType,
+ regions[1].abiDirectType);
+ return CABIParamInfo(abiTyp, ParmDirect, AttrNone, -1);
+}
+
+//......................................................................
diff --git a/bridge/go-llvm-cabi-oracle.h b/bridge/go-llvm-cabi-oracle.h
index c995e6e..f146522 100644
--- a/bridge/go-llvm-cabi-oracle.h
+++ b/bridge/go-llvm-cabi-oracle.h
@@ -21,10 +21,12 @@
#define LLVMGOFRONTEND_GO_LLVM_CABI_ORACLE_H
#include "go-llvm-btype.h"
+#include "llvm/IR/CallingConv.h"
class TypeManager;
class EightByteInfo;
class ABIState;
+class CABIOracleArgumentAnalyzer;
namespace llvm {
class DataLayout;
@@ -34,7 +36,7 @@
// Disposition of a specific function argument or function return value.
-enum CABIParamDisp : uint8_t {
+enum CABIParamDisp : uint8_t {
// Pass argument directly (not in memory).
ParmDirect,
@@ -50,7 +52,7 @@
// Attributes on parameters. These correspond directly to the LLVM attrs
// of the same name.
-enum CABIParamAttr : uint8_t {
+enum CABIParamAttr : uint8_t {
AttrNone=0,
AttrStructReturn,
AttrByVal,
@@ -128,6 +130,12 @@
void dump();
void osdump(llvm::raw_ostream &os);
+ // This constant specifies the maximum possible size of vectors abiTypes_
+ // in the direct parameter passing case.
+ // For X86_64_SysV, the size of paramInfo.abiTypes() can't be larger than 2,
+ // because parameters that are larger than 16 bytes are passed indirectly.
+ static const unsigned int ABI_TYPES_MAX_SIZE = 2;
+
private:
std::vector<llvm::Type *> abiTypes_;
CABIParamDisp disp_;
@@ -143,7 +151,6 @@
class CABIOracle {
public:
-
// Given information on the param types and result type for a
// function, create an oracle object that can answer C ABI
// queries about the function.
@@ -156,9 +163,6 @@
CABIOracle(BFunctionType *ft,
TypeManager *typeManager);
- // Returns TRUE if this cc supported, FALSE otherwise.
- bool supported() const;
-
// Return the appropriate "cooked" LLVM function type for this
// abstract function type.
llvm::FunctionType *getFunctionTypeForABI();
@@ -174,7 +178,7 @@
const CABIParamInfo &chainInfo();
// Type manager used with this oracle.
- TypeManager *tm() const { return typeManager_; }
+ TypeManager *tm() const;
// Various dump methods.
void dump();
@@ -188,13 +192,39 @@
TypeManager *typeManager_;
std::vector<CABIParamInfo> infov_;
bool followsCabi_;
+ llvm::CallingConv::ID ccID_;
+ std::unique_ptr<CABIOracleArgumentAnalyzer> cc_;
+ // The main entry for cabi analysis.
void analyze();
void analyzeRaw();
- CABIParamInfo analyzeABIReturn(Btype *resultType, ABIState &state);
+ // Set calling convention.
+ void setCC();
+};
+
+// This is a pure virtual class for architecture-independent interfaces.
+class CABIOracleArgumentAnalyzer {
+ public:
+ CABIOracleArgumentAnalyzer(TypeManager *tm) : tm_(tm) {}
+ virtual ~CABIOracleArgumentAnalyzer() {}
+ virtual CABIParamInfo analyzeABIReturn(Btype *resultType, ABIState &state) = 0;
+ virtual CABIParamInfo analyzeABIParam(Btype *pType, ABIState &state) = 0;
+ protected:
+ TypeManager *tm_;
+};
+
+// This class implements x86_64 SysV calling convention.
+class CABIOracleX86_64_SysV : public CABIOracleArgumentAnalyzer {
+ public:
+ // Given information on the param types and result type for a
+ // function, create an oracle object that can answer C ABI
+ // queries about the function.
+ CABIOracleX86_64_SysV(TypeManager *typeManager);
CABIParamInfo analyzeABIParam(Btype *pType, ABIState &state);
+ CABIParamInfo analyzeABIReturn(Btype *resultType, ABIState &state);
+
+ private:
bool canPassDirectly(unsigned regsInt, unsigned regsSSE, ABIState &state);
- const llvm::DataLayout *datalayout() const;
CABIParamDisp classifyArgType(Btype *btype);
};
diff --git a/bridge/go-llvm-materialize.cpp b/bridge/go-llvm-materialize.cpp
index 4ed16ed..9147ab6 100644
--- a/bridge/go-llvm-materialize.cpp
+++ b/bridge/go-llvm-materialize.cpp
@@ -1259,8 +1259,9 @@
}
// This now corresponds to the case of passing the contents of
- // a small structure via two pieces / params.
- assert(paramInfo.abiTypes().size() == 2);
+ // a small structure via no more than CABIParamInfo::ABI_TYPES_MAX_SIZE
+ // pieces / params.
+ assert(paramInfo.abiTypes().size() <= CABIParamInfo::ABI_TYPES_MAX_SIZE);
assert(paramInfo.attr() == AttrNone);
assert(ctx == VE_lvalue);
@@ -1288,19 +1289,14 @@
builder.CreatePointerBitCastOrAddrSpaceCast(val, ptst, tag);
// Load up each field
- std::string ftag0(namegen("field0"));
- llvm::Value *field0gep =
- builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, 0, ftag0);
- std::string ltag0(namegen("ld"));
- llvm::Value *ld0 = builder.CreateLoad(field0gep, ltag0);
- state.llargs.push_back(ld0);
-
- std::string ftag1(namegen("field1"));
- llvm::Value *field1gep =
- builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, 1, ftag1);
- std::string ltag1(namegen("ld"));
- llvm::Value *ld1 = builder.CreateLoad(field1gep, ltag1);
- state.llargs.push_back(ld1);
+ for ( unsigned i = 0; i < paramInfo.abiTypes().size(); ++i) {
+ std::string ftag(namegen("field"+std::to_string(i)));
+ llvm::Value *fieldgep =
+ builder.CreateConstInBoundsGEP2_32(llst, bitcast, 0, i, ftag);
+ std::string ltag(namegen("ld"));
+ llvm::Value *ld = builder.CreateLoad(fieldgep, ltag);
+ state.llargs.push_back(ld);
+ }
}
}
diff --git a/bridge/go-llvm-typemanager.cpp b/bridge/go-llvm-typemanager.cpp
index 5ba97f3..a74ff99 100644
--- a/bridge/go-llvm-typemanager.cpp
+++ b/bridge/go-llvm-typemanager.cpp
@@ -331,6 +331,12 @@
return lst;
}
+llvm::Type *
+TypeManager::makeLLVMStructType(const std::vector<llvm::Type *> &fields) {
+ llvm::Type *lst = llvm::StructType::get(context_, fields);
+ return lst;
+}
+
bool TypeManager::addPlaceholderRefs(Btype *btype)
{
bool rval = false;
diff --git a/bridge/go-llvm-typemanager.h b/bridge/go-llvm-typemanager.h
index 8eca95e..57d391f 100644
--- a/bridge/go-llvm-typemanager.h
+++ b/bridge/go-llvm-typemanager.h
@@ -165,6 +165,7 @@
llvm::Type *makeLLVMTwoElementStructType(llvm::Type *f1, llvm::Type *f2);
llvm::Type *makeLLVMPointerType(llvm::Type *toTy);
llvm::Type *makeLLVMStructType(const std::vector<Btyped_identifier> &fields);
+ llvm::Type *makeLLVMStructType(const std::vector<llvm::Type *> &fields);
llvm::Type *makeLLVMFunctionType(const std::vector<Btype *> ¶mTypes,
Btype *rbtype, bool followsCabi);
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index ee1c747..df92d6c 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -43,8 +43,9 @@
Llvm_backend::Llvm_backend(llvm::LLVMContext &context,
llvm::Module *module,
Llvm_linemap *linemap,
- unsigned addrspace)
- : TypeManager(context, llvm::CallingConv::X86_64_SysV, addrspace)
+ unsigned addrspace,
+ llvm::CallingConv::ID cconv)
+ : TypeManager(context, cconv, addrspace)
, context_(context)
, module_(module)
, datalayout_(module ? &module->getDataLayout() : nullptr)
diff --git a/bridge/go-llvm.h b/bridge/go-llvm.h
index 0d4824b..947a13a 100644
--- a/bridge/go-llvm.h
+++ b/bridge/go-llvm.h
@@ -79,7 +79,9 @@
Llvm_backend(llvm::LLVMContext &context,
llvm::Module *module,
Llvm_linemap *linemap,
- unsigned addrspace);
+ unsigned addrspace,
+ /* Temporarily set the parameter as optional to workaround the unit tests. */
+ llvm::CallingConv::ID cconv=llvm::CallingConv::X86_64_SysV);
~Llvm_backend();
// Types.
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index 5c64b4b..6875878 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -102,6 +102,7 @@
Triple triple_;
const ToolChain &toolchain_;
Driver &driver_;
+ CallingConv::ID cconv_;
LLVMContext context_;
const char *progname_;
std::string executablePath_;
@@ -127,6 +128,7 @@
void createPasses(legacy::PassManager &MPM,
legacy::FunctionPassManager &FPM);
void setupGoSearchPath();
+ void setCConv();
// This routine emits output for -### and/or -v, then returns TRUE
// of the compilation should be stubbed out (-###) or FALSE otherwise.
@@ -154,6 +156,7 @@
: triple_(tc.driver().triple()),
toolchain_(tc),
driver_(tc.driver()),
+ cconv_(CallingConv::MaxID),
progname_(tc.driver().progname()),
executablePath_(executablePath),
args_(tc.driver().args()),
@@ -346,6 +349,9 @@
// Set triple.
triple_ = driver_.triple();
+ // Set calling convention.
+ setCConv();
+
// Get the target specific parser.
std::string Error;
const Target *TheTarget =
@@ -607,7 +613,7 @@
// Now construct Llvm_backend helper.
unsigned addrspace = enable_gc_ ? 1 : 0;
- bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get(), addrspace));
+ bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get(), addrspace, cconv_));
// Honor inline, tracelevel cmd line options
llvm::Optional<unsigned> tl =
@@ -628,7 +634,6 @@
true);
bridge_->setNoFpElim(!omitFp);
- // -f[no-]split-stack
bool useSplitStack =
driver_.reconcileOptionPair(gollvm::options::OPT_fsplit_stack,
gollvm::options::OPT_fno_split_stack,
@@ -797,6 +802,21 @@
go_add_search_path(dir.c_str());
}
+// Set cconv according to the triple_ value.
+void CompileGoImpl::setCConv()
+{
+ assert(triple_.getArch() != Triple::UnknownArch);
+ switch (triple_.getArch()) {
+ case Triple::x86_64:
+ cconv_ = CallingConv::X86_64_SysV;
+ break;
+ default:
+ errs() << "currently Gollvm is not supported on architecture "
+ << triple_.getArchName().str()<< "\n";
+ break;
+ }
+}
+
bool CompileGoImpl::invokeFrontEnd()
{
// Collect the input files and kick off the front end