Add a coerce-and-expand ABIArgInfo as a generalization of some

of the things we do with Expand / Direct.

NFC for now, but this will be used by swiftcall expansion.

llvm-svn: 263192
This commit is contained in:
John McCall 2016-03-11 04:30:43 +00:00
parent c56a8b3284
commit f26e73df75
4 changed files with 243 additions and 19 deletions

View File

@ -19,15 +19,11 @@
#include "clang/AST/CanonicalType.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Type.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/TrailingObjects.h"
#include <cassert>
namespace llvm {
class Type;
class StructType;
}
namespace clang {
class Decl;
@ -64,6 +60,12 @@ public:
/// are all scalar types or are themselves expandable types.
Expand,
/// CoerceAndExpand - Only valid for aggregate argument types. The
/// structure should be expanded into consecutive arguments corresponding
/// to the non-array elements of the type stored in CoerceToType.
/// Array elements in the type are assumed to be padding and skipped.
CoerceAndExpand,
/// InAlloca - Pass the argument directly using the LLVM inalloca attribute.
/// This is similar to indirect with byval, except it only applies to
/// arguments stored in memory and forbids any implicit copies. When
@ -75,8 +77,11 @@ public:
};
private:
llvm::Type *TypeData; // isDirect() || isExtend()
llvm::Type *PaddingType;
llvm::Type *TypeData; // canHaveCoerceToType()
union {
llvm::Type *PaddingType; // canHavePaddingType()
llvm::Type *UnpaddedCoerceAndExpandType; // isCoerceAndExpand()
};
union {
unsigned DirectOffset; // isDirect() || isExtend()
unsigned IndirectAlign; // isIndirect()
@ -91,8 +96,22 @@ private:
bool InReg : 1; // isDirect() || isExtend() || isIndirect()
bool CanBeFlattened: 1; // isDirect()
bool canHavePaddingType() const {
return isDirect() || isExtend() || isIndirect() || isExpand();
}
void setPaddingType(llvm::Type *T) {
assert(canHavePaddingType());
PaddingType = T;
}
void setUnpaddedCoerceToType(llvm::Type *T) {
assert(isCoerceAndExpand());
UnpaddedCoerceAndExpandType = T;
}
ABIArgInfo(Kind K)
: PaddingType(nullptr), TheKind(K), PaddingInReg(false), InReg(false) {}
: TheKind(K), PaddingInReg(false), InReg(false) {
}
public:
ABIArgInfo()
@ -104,8 +123,8 @@ public:
bool CanBeFlattened = true) {
auto AI = ABIArgInfo(Direct);
AI.setCoerceToType(T);
AI.setDirectOffset(Offset);
AI.setPaddingType(Padding);
AI.setDirectOffset(Offset);
AI.setCanBeFlattened(CanBeFlattened);
return AI;
}
@ -117,6 +136,7 @@ public:
static ABIArgInfo getExtend(llvm::Type *T = nullptr) {
auto AI = ABIArgInfo(Extend);
AI.setCoerceToType(T);
AI.setPaddingType(nullptr);
AI.setDirectOffset(0);
return AI;
}
@ -151,7 +171,9 @@ public:
return AI;
}
static ABIArgInfo getExpand() {
return ABIArgInfo(Expand);
auto AI = ABIArgInfo(Expand);
AI.setPaddingType(nullptr);
return AI;
}
static ABIArgInfo getExpandWithPadding(bool PaddingInReg,
llvm::Type *Padding) {
@ -161,6 +183,54 @@ public:
return AI;
}
/// \param unpaddedCoerceToType The coerce-to type with padding elements
/// removed, canonicalized to a single element if it would otherwise
/// have exactly one element.
static ABIArgInfo getCoerceAndExpand(llvm::StructType *coerceToType,
llvm::Type *unpaddedCoerceToType) {
#ifndef NDEBUG
// Sanity checks on unpaddedCoerceToType.
// Assert that we only have a struct type if there are multiple elements.
auto unpaddedStruct = dyn_cast<llvm::StructType>(unpaddedCoerceToType);
assert(!unpaddedStruct || unpaddedStruct->getNumElements() != 1);
// Assert that all the non-padding elements have a corresponding element
// in the unpadded type.
unsigned unpaddedIndex = 0;
for (auto eltType : coerceToType->elements()) {
if (isPaddingForCoerceAndExpand(eltType)) continue;
if (unpaddedStruct) {
assert(unpaddedStruct->getElementType(unpaddedIndex) == eltType);
} else {
assert(unpaddedIndex == 0 && unpaddedCoerceToType == eltType);
}
unpaddedIndex++;
}
// Assert that there aren't extra elements in the unpadded type.
if (unpaddedStruct) {
assert(unpaddedStruct->getNumElements() == unpaddedIndex);
} else {
assert(unpaddedIndex == 1);
}
#endif
auto AI = ABIArgInfo(CoerceAndExpand);
AI.setCoerceToType(coerceToType);
AI.setUnpaddedCoerceToType(unpaddedCoerceToType);
return AI;
}
static bool isPaddingForCoerceAndExpand(llvm::Type *eltType) {
if (eltType->isArrayTy()) {
assert(eltType->getArrayElementType()->isIntegerTy(8));
return true;
} else {
return false;
}
}
Kind getKind() const { return TheKind; }
bool isDirect() const { return TheKind == Direct; }
bool isInAlloca() const { return TheKind == InAlloca; }
@ -168,8 +238,11 @@ public:
bool isIgnore() const { return TheKind == Ignore; }
bool isIndirect() const { return TheKind == Indirect; }
bool isExpand() const { return TheKind == Expand; }
bool isCoerceAndExpand() const { return TheKind == CoerceAndExpand; }
bool canHaveCoerceToType() const { return isDirect() || isExtend(); }
bool canHaveCoerceToType() const {
return isDirect() || isExtend() || isCoerceAndExpand();
}
// Direct/Extend accessors
unsigned getDirectOffset() const {
@ -181,9 +254,9 @@ public:
DirectOffset = Offset;
}
llvm::Type *getPaddingType() const { return PaddingType; }
void setPaddingType(llvm::Type *T) { PaddingType = T; }
llvm::Type *getPaddingType() const {
return (canHavePaddingType() ? PaddingType : nullptr);
}
bool getPaddingInReg() const {
return PaddingInReg;
@ -202,6 +275,26 @@ public:
TypeData = T;
}
llvm::StructType *getCoerceAndExpandType() const {
assert(isCoerceAndExpand());
return cast<llvm::StructType>(TypeData);
}
llvm::Type *getUnpaddedCoerceAndExpandType() const {
assert(isCoerceAndExpand());
return UnpaddedCoerceAndExpandType;
}
ArrayRef<llvm::Type *>getCoerceAndExpandTypeSequence() const {
assert(isCoerceAndExpand());
if (auto structTy =
dyn_cast<llvm::StructType>(UnpaddedCoerceAndExpandType)) {
return structTy->elements();
} else {
return llvm::makeArrayRef(&UnpaddedCoerceAndExpandType, 1);
}
}
bool getInReg() const {
assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!");
return InReg;

View File

@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H
#define LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/IRBuilder.h"
#include "Address.h"
#include "CodeGenTypeCache.h"
@ -194,6 +195,12 @@ public:
Addr.getPointer(), Index, Name),
Addr.getAlignment().alignmentAtOffset(Offset));
}
Address CreateStructGEP(Address Addr, unsigned Index,
const llvm::StructLayout *Layout,
const llvm::Twine &Name = "") {
auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index));
return CreateStructGEP(Addr, Index, Offset, Name);
}
/// Given
/// %addr = [n x T]* ...

View File

@ -1357,11 +1357,13 @@ void ClangToLLVMArgMapping::construct(const ASTContext &Context,
// ignore and inalloca doesn't have matching LLVM parameters.
IRArgs.NumberOfArgs = 0;
break;
case ABIArgInfo::Expand: {
case ABIArgInfo::CoerceAndExpand:
IRArgs.NumberOfArgs = AI.getCoerceAndExpandTypeSequence().size();
break;
case ABIArgInfo::Expand:
IRArgs.NumberOfArgs = getExpansionSize(ArgType, Context);
break;
}
}
if (IRArgs.NumberOfArgs > 0) {
IRArgs.FirstArgIndex = IRArgNo;
@ -1460,6 +1462,10 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) {
case ABIArgInfo::Ignore:
resultType = llvm::Type::getVoidTy(getLLVMContext());
break;
case ABIArgInfo::CoerceAndExpand:
resultType = retAI.getUnpaddedCoerceAndExpandType();
break;
}
ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI, true);
@ -1527,6 +1533,15 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) {
break;
}
case ABIArgInfo::CoerceAndExpand: {
auto ArgTypesIter = ArgTypes.begin() + FirstIRArg;
for (auto EltTy : ArgInfo.getCoerceAndExpandTypeSequence()) {
*ArgTypesIter++ = EltTy;
}
assert(ArgTypesIter == ArgTypes.begin() + FirstIRArg + NumIRArgs);
break;
}
case ABIArgInfo::Expand:
auto ArgTypesIter = ArgTypes.begin() + FirstIRArg;
getExpandedTypes(it->type, ArgTypesIter);
@ -1768,6 +1783,9 @@ void CodeGenModule::ConstructAttributeList(
break;
}
case ABIArgInfo::CoerceAndExpand:
break;
case ABIArgInfo::Expand:
llvm_unreachable("Invalid ABI kind for return argument");
}
@ -1875,7 +1893,8 @@ void CodeGenModule::ConstructAttributeList(
}
case ABIArgInfo::Ignore:
case ABIArgInfo::Expand:
continue;
case ABIArgInfo::CoerceAndExpand:
break;
case ABIArgInfo::InAlloca:
// inalloca disables readnone and readonly.
@ -2248,6 +2267,29 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
break;
}
case ABIArgInfo::CoerceAndExpand: {
// Reconstruct into a temporary.
Address alloca = CreateMemTemp(Ty, getContext().getDeclAlign(Arg));
ArgVals.push_back(ParamValue::forIndirect(alloca));
auto coercionType = ArgI.getCoerceAndExpandType();
alloca = Builder.CreateElementBitCast(alloca, coercionType);
auto layout = CGM.getDataLayout().getStructLayout(coercionType);
unsigned argIndex = FirstIRArg;
for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) {
llvm::Type *eltType = coercionType->getElementType(i);
if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType))
continue;
auto eltAddr = Builder.CreateStructGEP(alloca, i, layout);
auto elt = FnArgs[argIndex++];
Builder.CreateStore(elt, eltAddr);
}
assert(argIndex == FirstIRArg + NumIRArgs);
break;
}
case ABIArgInfo::Expand: {
// If this structure was expanded into multiple arguments then
// we need to create a temporary and reconstruct it from the
@ -2638,6 +2680,40 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
case ABIArgInfo::Ignore:
break;
case ABIArgInfo::CoerceAndExpand: {
auto coercionType = RetAI.getCoerceAndExpandType();
auto layout = CGM.getDataLayout().getStructLayout(coercionType);
// Load all of the coerced elements out into results.
llvm::SmallVector<llvm::Value*, 4> results;
Address addr = Builder.CreateElementBitCast(ReturnValue, coercionType);
for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) {
auto coercedEltType = coercionType->getElementType(i);
if (ABIArgInfo::isPaddingForCoerceAndExpand(coercedEltType))
continue;
auto eltAddr = Builder.CreateStructGEP(addr, i, layout);
auto elt = Builder.CreateLoad(eltAddr);
results.push_back(elt);
}
// If we have one result, it's the single direct result type.
if (results.size() == 1) {
RV = results[0];
// Otherwise, we need to make a first-class aggregate.
} else {
// Construct a return type that lacks padding elements.
llvm::Type *returnType = RetAI.getUnpaddedCoerceAndExpandType();
RV = llvm::UndefValue::get(returnType);
for (unsigned i = 0, e = results.size(); i != e; ++i) {
RV = Builder.CreateInsertValue(RV, results[i], i);
}
}
break;
}
case ABIArgInfo::Expand:
llvm_unreachable("Invalid ABI kind for return argument");
}
@ -3377,7 +3453,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
// alloca to hold the result, unless one is given to us.
Address SRetPtr = Address::invalid();
size_t UnusedReturnSize = 0;
if (RetAI.isIndirect() || RetAI.isInAlloca()) {
if (RetAI.isIndirect() || RetAI.isInAlloca() || RetAI.isCoerceAndExpand()) {
if (!ReturnValue.isNull()) {
SRetPtr = ReturnValue.getValue();
} else {
@ -3391,7 +3467,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
}
if (IRFunctionArgs.hasSRetArg()) {
IRCallArgs[IRFunctionArgs.getSRetArgNo()] = SRetPtr.getPointer();
} else {
} else if (RetAI.isInAlloca()) {
Address Addr = createInAllocaStructGEP(RetAI.getInAllocaFieldIndex());
Builder.CreateStore(SRetPtr.getPointer(), Addr);
}
@ -3571,6 +3647,29 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
break;
}
case ABIArgInfo::CoerceAndExpand: {
assert(RV.isAggregate() &&
"CoerceAndExpand does not support non-aggregate types yet");
auto coercionType = ArgInfo.getCoerceAndExpandType();
auto layout = CGM.getDataLayout().getStructLayout(coercionType);
Address addr = RV.getAggregateAddress();
addr = Builder.CreateElementBitCast(addr, coercionType);
unsigned IRArgPos = FirstIRArg;
for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) {
llvm::Type *eltType = coercionType->getElementType(i);
if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue;
Address eltAddr = Builder.CreateStructGEP(addr, i, layout);
llvm::Value *elt = Builder.CreateLoad(eltAddr);
IRCallArgs[IRArgPos++] = elt;
}
assert(IRArgPos == FirstIRArg + NumIRArgs);
break;
}
case ABIArgInfo::Expand:
unsigned IRArgPos = FirstIRArg;
ExpandTypeToArgs(I->Ty, RV, IRFuncTy, IRCallArgs, IRArgPos);
@ -3770,6 +3869,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
return ret;
}
case ABIArgInfo::CoerceAndExpand: {
auto coercionType = RetAI.getCoerceAndExpandType();
auto layout = CGM.getDataLayout().getStructLayout(coercionType);
Address addr = SRetPtr;
addr = Builder.CreateElementBitCast(addr, coercionType);
unsigned unpaddedIndex = 0;
for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) {
llvm::Type *eltType = coercionType->getElementType(i);
if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue;
Address eltAddr = Builder.CreateStructGEP(addr, i, layout);
llvm::Value *elt = Builder.CreateExtractValue(CI, unpaddedIndex++);
Builder.CreateStore(elt, eltAddr);
}
break;
}
case ABIArgInfo::Ignore:
// If we are ignoring an argument that had a result, make sure to
// construct the appropriate return value for our caller.

View File

@ -160,6 +160,10 @@ LLVM_DUMP_METHOD void ABIArgInfo::dump() const {
case Expand:
OS << "Expand";
break;
case CoerceAndExpand:
OS << "CoerceAndExpand Type=";
getCoerceAndExpandType()->print(OS);
break;
}
OS << ")\n";
}
@ -1570,6 +1574,7 @@ static bool isArgInAlloca(const ABIArgInfo &Info) {
case ABIArgInfo::Direct:
case ABIArgInfo::Extend:
case ABIArgInfo::Expand:
case ABIArgInfo::CoerceAndExpand:
if (Info.getInReg())
return false;
return true;
@ -6829,6 +6834,7 @@ Address SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
CharUnits Stride;
switch (AI.getKind()) {
case ABIArgInfo::Expand:
case ABIArgInfo::CoerceAndExpand:
case ABIArgInfo::InAlloca:
llvm_unreachable("Unsupported ABI kind for va_arg");
@ -7059,6 +7065,7 @@ Address XCoreABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
CharUnits ArgSize = CharUnits::Zero();
switch (AI.getKind()) {
case ABIArgInfo::Expand:
case ABIArgInfo::CoerceAndExpand:
case ABIArgInfo::InAlloca:
llvm_unreachable("Unsupported ABI kind for va_arg");
case ABIArgInfo::Ignore: