| //===-- 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> ®ions() { 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(®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); |
| } |
| |
| //...................................................................... |
| |
| 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 ®ions = 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 ®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); |
| } |
| |
| //...................................................................... |