blob: 7ba851f09c49c505dfd79d638802746d25361859 [file] [log] [blame]
//===-- go-llvm-cabi-oracle.cpp - implementation of CABIOracle ------------===//
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//===----------------------------------------------------------------------===//
//
// Methods for CABIOracle class.
//
//===----------------------------------------------------------------------===//
#include "go-llvm-cabi-oracle.h"
#include "go-llvm-typemanager.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/Support/raw_ostream.h"
#include "CallingConv.h"
//......................................................................
// Given an LLVM type, classify it according to whether it would
// need to be passed in an integer or SSE register (or if it is
// some combination of entirely empty structs/arrays).
enum TypDisp { FlavSSE, FlavSIMDFP, FlavInt, FlavEmpty };
// Arm64 ABI AAPCS64 defines HFA as follows:
// An Homogeneous Floating-point Aggregate (HFA) is an Homogeneous Aggregate with
// a Fundamental Data Type that is a Floating-Point type and at most four uniquely
// addressable members.
struct HFAInfo {
unsigned number;
llvm::Type *type;
};
// Here "meet" is in the dataflow anlysis sense (meet operator on lattice
// values).
static TypDisp dispMeet(TypDisp d1, TypDisp d2) {
if (d1 == d2)
return d1;
if (d1 == FlavEmpty)
return d2;
if (d2 == FlavEmpty)
return d1;
if (d1 == FlavSSE && d2 == FlavSSE)
return FlavSSE;
return FlavInt;
}
static TypDisp getTypDisp(llvm::Type *typ) {
if (typ->isFloatTy() || typ->isDoubleTy())
return FlavSSE;
return FlavInt;
}
//......................................................................
// Q: split off this class into a separate file, so as to unit test it?
// The AMD64 ABI classification scheme for aggregates (section 3.2.3
// third page) talks about dividing up the contents of an array or
// struct info 8-byte chunks or regions; this container class holds
// info on a region. A given 8-byte region may contain something
// simple like a field of type "double", a pair of floats, or there
// may be a mix of floating point and integer fields within a struct.
// The "types" and "offsets" hold the LLVM types and offsets of
// elements relative to the start of the object; "abiDirectType" holds
// the type used to pass the elements if we're passing the entire
// object directly (in registers) as opposed in on the stack.
//
// Implementation of Arm64 ABI AAPCS64 reused this struct.
struct EightByteRegion {
EightByteRegion() : abiDirectType(nullptr), attr(AttrNone) { }
TypDisp getRegionTypDisp() const;
std::vector<llvm::Type*> types;
std::vector<uint64_t> offsets;
llvm::Type *abiDirectType;
CABIParamAttr attr;
void dump();
void osdump(llvm::raw_ostream &os);
};
TypDisp EightByteRegion::getRegionTypDisp() const {
TypDisp disp = FlavEmpty;
for (auto &t : types)
disp = dispMeet(getTypDisp(t), disp);
return disp;
}
void EightByteRegion::dump()
{
std::string s;
llvm::raw_string_ostream os(s);
osdump(os);
std::cerr << os.str();
}
void EightByteRegion::osdump(llvm::raw_ostream &os)
{
os << "types:\n";
for (auto &t : types) {
os << " ";
t->print(os);
os << "\n";
}
os << "offsets:\n";
for (auto &o : offsets) {
os << " " << o << "\n";
}
}
// This class is a container for zero or more EightByteRegion objects
// that correspond to the contents of an object of some type that
// we're going to be passing to or returning from a function.
class EightByteInfo {
public:
EightByteInfo(Btype *bt, TypeManager *tm);
std::vector<EightByteRegion> &regions() { return ebrs_; }
HFAInfo &getHFA() { return hfa_; }
void getRegisterRequirements(unsigned *numInt, unsigned *numSSE);
private:
std::vector<EightByteRegion> ebrs_;
HFAInfo hfa_;
TypeManager *typeManager_;
typedef std::pair<Btype *, unsigned> typAndOffset;
void addLeafTypes(Btype *bt, unsigned off, std::vector<typAndOffset> *leaves);
void explode(Btype *bt);
void setHFA();
void explodeStruct(Btype *bst);
void explodeArray(BArrayType *bat);
void incorporateScalar(Btype *bt);
void determineABITypesForARM_AAPCS();
void determineABITypesForX86_64_SysV();
TypeManager *tm() const { return typeManager_; }
};
EightByteInfo::EightByteInfo(Btype *bt, TypeManager *tmgr)
: typeManager_(tmgr)
{
explode(bt);
gollvm::driver::CallingConvId cconv = tmgr->callingConv();
switch (cconv) {
case gollvm::driver::CallingConvId::X86_64_SysV:
determineABITypesForX86_64_SysV();
break;
case gollvm::driver::CallingConvId::ARM_AAPCS:
setHFA();
if (getHFA().number == 0 && tmgr->typeSize(bt) <= 16) {
// For HFA and indirect cases, we don't need do this.
determineABITypesForARM_AAPCS();
}
break;
default:
llvm::errs() << "unsupported gollvm::driver::CallingConvId " << static_cast<int>(cconv) << "\n";
break;
}
}
// Perform a pre-order walk of a type, collecting the various leaf
// elements within the type. The general idea is to flatten out any
// nested structs/arrays and expose each of the underlying scalar
// elements of the type along with their offsets. Return value is a
// list of type/offset pairs, where the offset represents the location
// of the scalar type within the larger aggregate type. Example:
//
// struct {
// f1 int8
// f2 [0]uint64
// struct {
// f1 int8
// }
// }
//
// The return for the struct above in "preserve" mode would be:
//
// { int8, 0 }, { int8, 8 }
//
void EightByteInfo::addLeafTypes(Btype *bt,
unsigned offset,
std::vector<typAndOffset> *leaves)
{
assert(bt && leaves);
BStructType *bst = bt->castToBStructType();
if (bst) {
unsigned numFields = bst->fields().size();
for (unsigned fidx = 0; fidx < numFields; ++fidx) {
unsigned foff = tm()->typeFieldOffset(bst, fidx);
addLeafTypes(bst->fieldType(fidx), offset + foff, leaves);
}
return;
}
BArrayType *bat = bt->castToBArrayType();
if (bat) {
Btype *et = bat->elemType();
for (unsigned elidx = 0; elidx < bat->nelSize(); ++elidx) {
unsigned eloff = elidx * tm()->typeSize(et);
addLeafTypes(et, offset + eloff, leaves);
}
return;
}
BComplexType *bct = bt->castToBComplexType();
if (bct) {
unsigned bits = bct->bits() / 2;
for (unsigned fidx = 0; fidx < 2; ++fidx) {
Btype *leaf = tm()->floatType(bits);
unsigned foff = tm()->typeFieldOffset(bct, fidx);
leaves->push_back(std::make_pair(leaf, foff));
}
return;
}
assert(bt->flavor() != Btype::AuxT && bt->flavor() != Btype::FunctionT);
leaves->push_back(std::make_pair(bt, offset));
}
// Given a struct type, explode it into zero, one or multiple EightByteRegion
// descriptors. Examples of the contents of EightByteInfo structs
// for various Go types follow. The first type (empty struct) results
// in a single EightByteRegion struct with empty vectors. The second
// type results in a single EightByteRegion, and the third yields two
// EightByteRegion.
//
// Go struct type: Computed EightByteRegions:
// C types: offsets:
//
// type empty struct { } [0] <no types> <no offsets>
//
// type foo struct {
// f1 uint8; [0] unsigned char 0
// f2 uint16; unsigned short 16
// f4 float32; float 32
// }
//
// type bar struct {
// f1 double; [0] double 0
// f2 uint8; [1] unsigned char 64
// f3 int16; short 80
// }
//
void EightByteInfo::explodeStruct(Btype *bst)
{
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 - curOffset >= 8)) {
ebrs_.push_back(EightByteRegion());
cur8 = &ebrs_.back();
curOffset = offset;
}
cur8->types.push_back(lt->type());
cur8->offsets.push_back(offset);
}
}
// Given an array type, explode it into zero, one or multiple EightByteInfo
// descriptors. Examples appear below; the first array type results
// in a single EightByteInfo with empty type/offset vectors, then the second
// array type results in a single EightByteInfo, and the thrd array
// type results in two EightByteInfo structs:
//
// Go type: Computed EightByteInfo:
// C types: offsets:
//
// [0]float32 [0] <no types> <no offsets>
//
// [3]uint8 [0] unsigned char 0
// unsigned char 8
// unsigned char 16
//
// [6]uint16 [0] unsigned short 0
// unsigned short 16
// unsigned short 32
// unsigned short 48
// [1] unsigned short 64
// unsigned short 80
void EightByteInfo::explodeArray(BArrayType *bat)
{
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 - 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);
}
}
void EightByteInfo::incorporateScalar(Btype *bt)
{
assert(tm()->typeSize(bt) <= 8);
EightByteRegion ebr;
ebr.types.push_back(bt->type());
ebr.offsets.push_back(0u);
ebr.abiDirectType = bt->type();
BIntegerType *bit = bt->castToBIntegerType();
if (bit && tm()->typeSize(bit) < 4)
ebr.attr = (bit->isUnsigned() ? AttrZext : AttrSext);
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;
*numSSE = 0;
for (auto &ebr : ebrs_)
if (ebr.getRegionTypDisp() == FlavSSE)
*numSSE += 1;
else
*numInt += 1;
}
// Check if the parameter is an Homogeneous Floating-point Aggregates (HFA),
// and set hfa_ according to the result.
void EightByteInfo::setHFA() {
if (ebrs_.empty()) {
hfa_ = HFAInfo{0, nullptr};
return;
}
unsigned num = 0;
llvm::Type *typ = ebrs_[0].types[0];
if (typ != tm()->llvmDoubleType() && typ != tm()->llvmFloatType()) {
hfa_ = HFAInfo{0, nullptr};
return;
}
for (auto &ebr : ebrs_) {
for (auto &t : ebr.types) {
if (t != typ || num > 3) {
hfa_ = HFAInfo{0, nullptr};
return;
}
++num;
}
}
hfa_ = HFAInfo{num, typ};
}
// Select the appropriate abi type for each eight-byte region within
// an EightByteInfo. HFA and arguments larger than 16 bytes have been
// processed, so the arguments processed here can only be integer types,
// pointer types or a mix of integer and non-integer, mapped it onto
// the pointer type or the appropriately sized integer type.
//
// Problems arise in the code below when dealing with structures with
// constructs that inject additional padding. For example, consider
// the following struct passed by value:
//
// struct {
// f1 int8
// f2 [0]uint64
// f3 int8
// }
//
// Without taking into account the over-alignment of field f3, we would
// wind up with two regions, each with type int8. This in itself is not so
// bad, but creating a struct from these two types (via ::computeABIStructType)
// would give us { int8, int8 }, in which the second field doesn't have
// the correct alignment. Work around this by checking for such situations
// and promoting the type of the first EBR to 64 bits.
//
void EightByteInfo::determineABITypesForARM_AAPCS() {
assert(ebrs_.size() <= 2);
for (auto &ebr : ebrs_) {
if (ebr.abiDirectType != nullptr)
continue;
// Preserve pointerness for the use of GC.
// TODO: this assumes pointer is 8 byte, so we never pack pointer
// and other stuff together.
if (ebr.types[0]->isPointerTy()) {
ebr.abiDirectType = tm()->llvmPtrType();
continue;
}
unsigned nel = ebr.offsets.size();
unsigned bytes = ebr.offsets[nel - 1] - ebr.offsets[0] +
tm()->llvmTypeSize(ebr.types[nel - 1]);
assert(bytes && bytes <= 8);
ebr.abiDirectType = tm()->llvmArbitraryIntegerType(bytes);
}
// See the example above for more on why this is needed.
if (ebrs_.size() == 2 && ebrs_[0].abiDirectType->isIntegerTy()) {
ebrs_[0].abiDirectType = tm()->llvmArbitraryIntegerType(8);
}
}
// Select the appropriate abi type for each eight-byte region within
// an EightByteInfo. Pure floating point types are mapped onto float,
// double, or <2 x float> (a vector type), integer types (or something
// that is a mix of integer and non-integer) are mapped onto the
// appropriately sized integer type.
//
// Problems arise in the code below when dealing with structures with
// constructs that inject additional padding. For example, consider
// the following struct passed by value:
//
// struct {
// f1 int8
// f2 [0]uint64
// f3 int8
// }
//
// Without taking into account the over-alignment of field f3, we would
// wind up with two regions, each with type int8. This in itself is not so
// bad, but creating a struct from these two types (via ::computeABIStructType)
// would give us { int8, int8 }, in which the second field doesn't have
// the correct alignment. Work around this by checking for such situations
// and promoting the type of the first EBR to 64 bits.
//
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_) {
if (ebr.abiDirectType != nullptr)
continue;
TypDisp regionDisp = ebr.getRegionTypDisp();
if (regionDisp == FlavSSE) {
// Case 1: two floats -> vector
if (ebr.types.size() == 2)
ebr.abiDirectType = tm()->llvmTwoFloatVecType();
else if (ebr.types.size() == 1) {
assert(ebr.types[0] == tm()->llvmDoubleType() ||
ebr.types[0] == tm()->llvmFloatType());
ebr.abiDirectType = ebr.types[0];
} else {
assert(false && "this should never happen");
}
floatRegions += 1;
} else {
unsigned nel = ebr.offsets.size();
unsigned bytes = ebr.offsets[nel-1] - ebr.offsets[0] +
tm()->llvmTypeSize(ebr.types[nel-1]);
assert(bytes && bytes <= 8);
// Preserve pointerness for the use of GC.
// TODO: this assumes pointer is 8 byte, so we never pack pointer
// and other stuff together.
if (ebr.types[0]->isPointerTy())
ebr.abiDirectType = tm()->llvmPtrType();
else
ebr.abiDirectType = tm()->llvmArbitraryIntegerType(bytes);
intRegions += 1;
}
}
// See the example above for more on why this is needed.
if (intRegions == 2 &&
ebrs_[0].abiDirectType->isIntegerTy())
ebrs_[0].abiDirectType = tm()->llvmArbitraryIntegerType(8);
else if (floatRegions == 2 &&
ebrs_[0].abiDirectType == tm()->llvmFloatType())
ebrs_[0].abiDirectType = tm()->llvmDoubleType();
}
//......................................................................
llvm::Type *CABIParamInfo::computeABIStructType(TypeManager *tm) const
{
assert(tm);
if (abiTypes_.size() == 1) {
assert(abiTypes_[0]->isStructTy());
return abiTypes_[0];
}
assert(abiTypes_.size() <= CABIParamInfo::ABI_TYPES_MAX_SIZE);
llvm::Type *llst = tm->makeLLVMStructType(abiTypes_);
return llst;
}
void CABIParamInfo::dump()
{
std::string s;
llvm::raw_string_ostream os(s);
osdump(os);
std::cerr << os.str();
}
void CABIParamInfo::osdump(llvm::raw_ostream &os)
{
os << (disp() == ParmDirect ? "Direct" :
(disp() == ParmIgnore ? "Ignore" :
(disp() == ParmIndirect ? "Indirect" : "<unknown>")));
if (attr() != AttrNone)
os << (attr() == AttrStructReturn ? " AttrStructReturn" :
(attr() == AttrByVal ? " AttrByVal" :
(attr() == AttrNest ? " AttrNest" :
(attr() == AttrZext ? " AttrZext" :
(attr() == AttrSext ? " AttrSext" :
(attr() == AttrDoCopy ? " AttrDoCopy" : " <unknown>"))))));
os << " { ";
unsigned idx = 0;
for (auto &abit : abiTypes_) {
os << (idx++ != 0 ? ", " : "");
abit->print(os);
}
os << " }";
os << " sigOffset: " << sigOffset() << "\n";
}
//......................................................................
// Helper struct to track state information during ABI param
// classification. Keeps track of arg count (args in final ABI-cooked
// signature) along with available int/sse regs.
class ABIState {
public:
ABIState(TypeManager *typm) : argCount_(0) {
assert(typm != nullptr);
gollvm::driver::CallingConvId cconv = typm->callingConv();
switch (cconv) {
case gollvm::driver::CallingConvId::X86_64_SysV:
availIntRegs_ = 6;
availSSERegs_ = 8;
break;
case gollvm::driver::CallingConvId::ARM_AAPCS:
availIntRegs_ = 8;
availSIMDFPRegs_ = 8;
break;
default:
llvm::errs() << "unsupported gollvm::driver::CallingConvId " << static_cast<int>(cconv) << "\n";
break;
}
}
void addDirectIntArg() {
if (availIntRegs_)
availIntRegs_ -= 1;
argCount_ += 1;
}
void addDirectSSEArg() {
if (availSSERegs_)
availSSERegs_ -= 1;
argCount_ += 1;
}
// For ARM_AAPCS HFA, one argument may takes multiple registers.
void addDirectSIMDFPArg(unsigned sr = 1) {
unsigned t = availSIMDFPRegs_ - sr;
if (availSIMDFPRegs_ > t)
availSIMDFPRegs_ = t;
argCount_ += 1;
}
void addIndirectArg() { argCount_ += 1; }
void addIndirectReturn() {
if (availIntRegs_)
availIntRegs_ -= 1;
argCount_ += 1;
}
// ARM_AAPCS uses separate x8 to store return address.
void addIndirectReturnForARM_AAPCS() { argCount_ += 1; }
void addChainArg() { argCount_ += 1; }
unsigned argCount() const { return argCount_; }
unsigned availIntRegs() const { return availIntRegs_; }
unsigned availSSERegs() const { return availSSERegs_; }
unsigned availSIMDFPRegs() const { return availSIMDFPRegs_; }
void clearAvailIntRegs() { availIntRegs_ = 0; }
void clearAvailSIMDFPRegs() { availSIMDFPRegs_ = 0; }
private:
unsigned availIntRegs_;
unsigned availSSERegs_;
unsigned availSIMDFPRegs_;
unsigned argCount_;
};
//......................................................................
CABIOracle::CABIOracle(const std::vector<Btype *> &fcnParamTypes,
Btype *fcnResultType,
bool followsCabi,
TypeManager *typeManager)
: fcnParamTypes_(fcnParamTypes)
, fcnResultType_(fcnResultType)
, fcnTypeForABI_(nullptr)
, typeManager_(typeManager)
, followsCabi_(followsCabi)
, ccID_(gollvm::driver::CallingConvId::MaxID)
, cc_(nullptr)
{
setCC();
analyze();
}
CABIOracle::CABIOracle(BFunctionType *ft,
TypeManager *typeManager)
: fcnParamTypes_(ft->paramTypes())
, fcnResultType_(ft->resultType())
, fcnTypeForABI_(nullptr)
, typeManager_(typeManager)
, followsCabi_(ft->followsCabi())
, ccID_(gollvm::driver::CallingConvId::MaxID)
, cc_(nullptr)
{
setCC();
analyze();
}
void CABIOracle::setCC()
{
assert(typeManager_ != nullptr);
ccID_ = typeManager_->callingConv();
// Supported architectures at present.
assert(ccID_ == gollvm::driver::CallingConvId::X86_64_SysV ||
ccID_ == gollvm::driver::CallingConvId::ARM_AAPCS ||
ccID_ == gollvm::driver::CallingConvId::RISCV64_C);
if (cc_ != nullptr) {
return;
}
switch (ccID_) {
case gollvm::driver::CallingConvId::X86_64_SysV:
cc_ = std::unique_ptr<CABIOracleArgumentAnalyzer>(new CABIOracleX86_64_SysV(typeManager_));
break;
case gollvm::driver::CallingConvId::ARM_AAPCS:
cc_ = std::unique_ptr<CABIOracleArgumentAnalyzer>(new CABIOracleARM_AAPCS(typeManager_));
break;
default:
llvm::errs() << "unsupported gollvm::driver::CallingConvId " << static_cast<int>(ccID_) << "\n";
break;
}
}
TypeManager *CABIOracle::tm() const
{
return typeManager_;
}
llvm::FunctionType *CABIOracle::getFunctionTypeForABI()
{
assert(cc_ != nullptr);
return fcnTypeForABI_;
}
const CABIParamInfo &CABIOracle::paramInfo(unsigned idx)
{
assert(cc_ != nullptr);
// Slot 0: return info
// Slot 1: static chain param
// Slot 2: first argument / parameter
unsigned pidx = idx + 2;
assert(pidx < infov_.size());
return infov_[pidx];
}
const CABIParamInfo &CABIOracle::returnInfo()
{
assert(cc_ != nullptr);
unsigned ridx = 0;
assert(ridx < infov_.size());
return infov_[ridx];
}
const CABIParamInfo &CABIOracle::chainInfo()
{
assert(cc_ != nullptr);
unsigned ridx = 1;
assert(ridx < infov_.size());
return infov_[ridx];
}
void CABIOracle::dump()
{
std::cerr << toString();
}
std::string CABIOracle::toString()
{
std::string s;
llvm::raw_string_ostream os(s);
osdump(os);
return os.str();
}
void CABIOracle::osdump(llvm::raw_ostream &os)
{
os << "Return: ";
infov_[0].osdump(os);
for (unsigned pidx = 1; pidx < infov_.size(); pidx++) {
os << "Param " << pidx << ": ";
infov_[pidx].osdump(os);
}
}
// Fill in parameter / return / type information for a builtin function,
// e.g. all values passed + returned directly, no static chain param.
void CABIOracle::analyzeRaw()
{
//if (fcnTypeForABI_)
//return;
// First slot in the info vector will be for the return.
llvm::Type *rtyp = fcnResultType_->type();
CABIParamInfo rinfo(rtyp, ParmDirect, AttrNone, -1);
infov_.push_back(rinfo);
// No static chain, but we'll create an entry for the chain marked
// as ignored.
CABIParamInfo cinfo(tm()->llvmPtrType(), ParmIgnore, AttrNest, -1);
infov_.push_back(cinfo);
// Now process the params.
llvm::SmallVector<llvm::Type *, 8> elems(0);
for (unsigned idx = 0; idx < fcnParamTypes_.size(); ++idx) {
Btype *pType = fcnParamTypes_[idx];
CABIParamInfo pinfo(pType->type(), ParmDirect, AttrNone, idx);
infov_.push_back(pinfo);
elems.push_back(pType->type());
}
// Build the proper LLVM function type
const bool isVarargs = false;
fcnTypeForABI_ = llvm::FunctionType::get(rtyp, elems, isVarargs);
}
void CABIOracle::analyze()
{
if (fcnTypeForABI_)
return;
if (! followsCabi_) {
analyzeRaw();
return;
}
ABIState state(tm());
assert(cc_ != nullptr);
// First slot in the info vector will be for the return.
infov_.push_back(cc_->analyzeABIReturn(fcnResultType_, state));
// Static chain parameter
int sigOff = state.argCount();
state.addChainArg();
CABIParamInfo cparm(tm()->llvmPtrType(), ParmDirect, AttrNest, sigOff);
infov_.push_back(cparm);
// Now process the params.
for (unsigned idx = 0; idx < fcnParamTypes_.size(); ++idx) {
Btype *pType = fcnParamTypes_[idx];
auto d = cc_->analyzeABIParam(pType, state);
infov_.push_back(d);
}
llvm::SmallVector<llvm::Type *, 8> elems(0);
llvm::Type *rtyp = nullptr;
if (infov_[0].disp() == ParmIndirect) {
rtyp = tm()->llvmVoidType();
elems.push_back(infov_[0].abiType());
} else {
rtyp = infov_[0].abiType();
}
for (unsigned pidx = 1; pidx < infov_.size(); pidx++) {
if (infov_[pidx].disp() == ParmIgnore)
continue;
for (auto &abit : infov_[pidx].abiTypes())
elems.push_back(abit);
}
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(&regsInt, &regsSSE);
// 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 &regions = 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);
}
//......................................................................
CABIOracleARM_AAPCS::CABIOracleARM_AAPCS(TypeManager *typeManager)
: CABIOracleArgumentAnalyzer(typeManager) {}
// 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 + regsSIMDFP == 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 CABIOracleARM_AAPCS::canPassDirectly(unsigned regsInt,
unsigned regsSIMDFP,
ABIState &state)
{
if (regsInt + regsSIMDFP == 1) // see comment above
return true;
if (regsInt <= state.availIntRegs() && regsSIMDFP <= state.availSIMDFPRegs())
return true;
return false;
}
CABIParamInfo CABIOracleARM_AAPCS::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)));
if (ptyp == tm_->llvmVoidType()) {
// Empty struct or array
llvm::Type *voidType = tm_->llvmVoidType();
return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
}
// If ptyp is llvmVoidType, we may not able to get the size of it,
// so we can't combine the following if statement with the above one.
int64_t sz = tm_->typeSize(paramType);
if (sz == 0) {
// Empty struct or array
llvm::Type *voidType = tm_->llvmVoidType();
return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
}
int sigOff = state.argCount();
// Go has only two floating point types: float32 and float64, so the size of
// an HFA does not exceed 32 bytes.
if (sz > 32) {
// 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, AttrDoCopy, sigOff);
}
EightByteInfo ebi(paramType, tm_);
auto &hfa = ebi.getHFA();
if (hfa.number != 0) {
// Is HFA.
llvm::Type * abiType = hfa.type;
if (hfa.number > 1) {
// If it contains multiple elements, make the param as an Array
// type. This ensures that an HFA is passed as a whole.
abiType = llvm::ArrayType::get(hfa.type, hfa.number);
}
if (canPassDirectly(0, hfa.number, state)) {
state.addDirectSIMDFPArg(hfa.number);
} else {
state.clearAvailSIMDFPRegs();
state.addIndirectArg();
}
// Whether or not an HFA can be passed in registers, we use
// ParmDirect. This is because HFA is passed by value on stack
// in indirect cases, and we happen to be able to reuse the
// processing logic of the direct cases.
return CABIParamInfo(abiType, ParmDirect, AttrNone, sigOff);
}
if (sz > 16) {
// Not an HFA,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, AttrDoCopy, sigOff);
}
// Direct case.
auto &regions = ebi.regions();
// Make direct/indirect decision
CABIParamAttr attr = AttrNone;
if (canPassDirectly(regions.size(), 0, state)) {
std::vector<llvm::Type *> abiTypes;
for (auto &ebr : regions) {
abiTypes.push_back(ebr.abiDirectType);
if (ebr.attr != AttrNone) {
assert(attr == AttrNone || attr == ebr.attr);
attr = ebr.attr;
}
state.addDirectIntArg();
}
return CABIParamInfo(abiTypes, ParmDirect, attr, sigOff);
} else {
state.clearAvailIntRegs();
state.addIndirectArg();
llvm::Type *abiType = regions[0].abiDirectType;
if (regions.size() > 1) {
// Convert the argument to an array type so that the backend considers it as a
// whole whether it can be passed through registers.
abiType = llvm::ArrayType::get(tm_->llvmArbitraryIntegerType(8), regions.size());
}
// Pass by value on stack, so use ParmDirect.
return CABIParamInfo(abiType, ParmDirect, AttrNone, sigOff);
}
}
CABIParamInfo CABIOracleARM_AAPCS::analyzeABIReturn(Btype *resultType,
ABIState &state) {
llvm::Type *rtyp = resultType->type();
if (rtyp == tm_->llvmVoidType()) {
// 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 rtyp is llvmVoidType, we may not able to get the size of it,
// so we can't combine the following if statement with the above one.
int64_t sz = tm_->typeSize(resultType);
if (sz == 0) {
// This corresponds to a function with no returns or
// returning an empty composite.
llvm::Type *voidType = tm_->llvmVoidType();
return CABIParamInfo(voidType, ParmIgnore, AttrNone, -1);
}
// Go has only two floating point types: float32 and float64, so the size of
// an HFA does not exceed 32 bytes.
if (sz > 32) {
// 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);
// Indirect return value is passed by register R8, so doesn't occupy any int
// register.
state.addIndirectReturnForARM_AAPCS();
return CABIParamInfo(ptrTyp, ParmIndirect, AttrStructReturn, 0);
}
EightByteInfo ebi(resultType, tm_);
auto &hfa = ebi.getHFA();
if (hfa.number != 0) {
// Is HFA.
// If only one element, don't bother to make a llvm struct type.
if (hfa.number == 1) {
return CABIParamInfo(hfa.type, ParmDirect, AttrNone, -1);
}
std::vector<llvm::Type *> fields;
for (unsigned i = 0; i < hfa.number; ++i) {
fields.push_back(hfa.type);
}
llvm::Type *abiTyp = tm_->makeLLVMStructType(fields);
return CABIParamInfo(abiTyp, ParmDirect, AttrNone, -1);
}
// The return value is not an HFA and its size exceeds 16 bytes,
// be passed in memory, via a hidden struct return param.
if (sz > 16) {
llvm::Type *ptrTyp = llvm::PointerType::get(rtyp, 0);
state.addIndirectReturnForARM_AAPCS();
return CABIParamInfo(ptrTyp, ParmIndirect, AttrStructReturn, 0);
}
// Direct case
auto &regions = 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);
}
//......................................................................