diff options
author | Rafael Espindola <rafael.espindola@gmail.com> | 2012-07-31 02:44:24 +0000 |
---|---|---|
committer | Rafael Espindola <rafael.espindola@gmail.com> | 2012-07-31 02:44:24 +0000 |
commit | b48280ba1790122cd3fa6e17c88ecd6a4571a4eb (patch) | |
tree | f9542b6b14db62a150a7af7d2cfb0b0d7d3b859f | |
parent | d72f56de7c79828928147389aed2c6c46f331031 (diff) |
Handle functions with struct arguments or return types and the regparm
attribute. It is a variation of the x86_64 ABI:
* A struct returned indirectly uses the first register argument to pass the
pointer.
* Floats, Doubles and structs containing only one of them are not passed in
registers.
* Other structs are split into registers if they fit on the remaining ones.
Otherwise they are passed in memory.
* When a struct doesn't fit it still consumes the registers.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161022 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/CodeGen/ABIInfo.h | 30 | ||||
-rw-r--r-- | lib/CodeGen/CGCall.cpp | 36 | ||||
-rw-r--r-- | lib/CodeGen/TargetInfo.cpp | 104 | ||||
-rw-r--r-- | test/CodeGen/regparm-struct.c | 177 | ||||
-rw-r--r-- | test/CodeGenCXX/pr13396.cpp | 2 |
5 files changed, 308 insertions, 41 deletions
diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index 838cb2989a..86f53803d5 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -74,31 +74,42 @@ namespace clang { unsigned UIntData; bool BoolData0; bool BoolData1; + bool InReg; - ABIArgInfo(Kind K, llvm::Type *TD, unsigned UI, bool B0, bool B1, + ABIArgInfo(Kind K, llvm::Type *TD, unsigned UI, bool B0, bool B1, bool IR, llvm::Type* P) : TheKind(K), TypeData(TD), PaddingType(P), UIntData(UI), BoolData0(B0), - BoolData1(B1) {} + BoolData1(B1), InReg(IR) {} public: ABIArgInfo() : TheKind(Direct), TypeData(0), UIntData(0) {} static ABIArgInfo getDirect(llvm::Type *T = 0, unsigned Offset = 0, llvm::Type *Padding = 0) { - return ABIArgInfo(Direct, T, Offset, false, false, Padding); + return ABIArgInfo(Direct, T, Offset, false, false, false, Padding); + } + static ABIArgInfo getDirectInReg(llvm::Type *T) { + return ABIArgInfo(Direct, T, 0, false, false, true, 0); } static ABIArgInfo getExtend(llvm::Type *T = 0) { - return ABIArgInfo(Extend, T, 0, false, false, 0); + return ABIArgInfo(Extend, T, 0, false, false, false, 0); + } + static ABIArgInfo getExtendInReg(llvm::Type *T = 0) { + return ABIArgInfo(Extend, T, 0, false, false, true, 0); } static ABIArgInfo getIgnore() { - return ABIArgInfo(Ignore, 0, 0, false, false, 0); + return ABIArgInfo(Ignore, 0, 0, false, false, false, 0); } static ABIArgInfo getIndirect(unsigned Alignment, bool ByVal = true , bool Realign = false) { - return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, 0); + return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, false, 0); + } + static ABIArgInfo getIndirectInReg(unsigned Alignment, bool ByVal = true + , bool Realign = false) { + return ABIArgInfo(Indirect, 0, Alignment, ByVal, Realign, true, 0); } static ABIArgInfo getExpand() { - return ABIArgInfo(Expand, 0, 0, false, false, 0); + return ABIArgInfo(Expand, 0, 0, false, false, false, 0); } Kind getKind() const { return TheKind; } @@ -132,6 +143,11 @@ namespace clang { TypeData = T; } + bool getInReg() const { + assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!"); + return InReg; + } + // Indirect accessors unsigned getIndirectAlign() const { assert(TheKind == Indirect && "Invalid kind!"); diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 9deec19bf8..7d2b9d355e 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -983,14 +983,18 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, case ABIArgInfo::Ignore: break; - case ABIArgInfo::Indirect: - PAL.push_back(llvm::AttributeWithIndex::get(Index, - llvm::Attribute::StructRet)); + case ABIArgInfo::Indirect: { + llvm::Attributes SRETAttrs = llvm::Attribute::StructRet; + if (RetAI.getInReg()) + SRETAttrs |= llvm::Attribute::InReg; + PAL.push_back(llvm::AttributeWithIndex::get(Index, SRETAttrs)); + ++Index; // sret disables readnone and readonly FuncAttrs &= ~(llvm::Attribute::ReadOnly | llvm::Attribute::ReadNone); break; + } case ABIArgInfo::Expand: llvm_unreachable("Invalid ABI kind for return argument"); @@ -999,14 +1003,6 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, if (RetAttrs) PAL.push_back(llvm::AttributeWithIndex::get(0, RetAttrs)); - // FIXME: RegParm should be reduced in case of global register variable. - signed RegParm; - if (FI.getHasRegParm()) - RegParm = FI.getRegParm(); - else - RegParm = CodeGenOpts.NumRegisterParameters; - - unsigned PointerWidth = getContext().getTargetInfo().getPointerWidth(0); for (CGFunctionInfo::const_arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) { QualType ParamType = it->type; @@ -1024,22 +1020,22 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, Attrs |= llvm::Attribute::ZExt; // FALL THROUGH case ABIArgInfo::Direct: - if (RegParm > 0 && - (ParamType->isIntegerType() || ParamType->isPointerType() || - ParamType->isReferenceType())) { - RegParm -= - (Context.getTypeSize(ParamType) + PointerWidth - 1) / PointerWidth; - if (RegParm >= 0) + if (AI.getInReg()) Attrs |= llvm::Attribute::InReg; - } + // FIXME: handle sseregparm someday... // Increment Index if there is padding. Index += (AI.getPaddingType() != 0); if (llvm::StructType *STy = - dyn_cast<llvm::StructType>(AI.getCoerceToType())) - Index += STy->getNumElements()-1; // 1 will be added below. + dyn_cast<llvm::StructType>(AI.getCoerceToType())) { + unsigned Extra = STy->getNumElements()-1; // 1 will be added below. + if (Attrs != llvm::Attribute::None) + for (unsigned I = 0; I < Extra; ++I) + PAL.push_back(llvm::AttributeWithIndex::get(Index + I, Attrs)); + Index += Extra; + } break; case ABIArgInfo::Indirect: diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 06e1a3e42b..2095cb9bf8 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -413,12 +413,18 @@ static llvm::Type* X86AdjustInlineAsmType(CodeGen::CodeGenFunction &CGF, /// X86_32ABIInfo - The X86-32 ABI information. class X86_32ABIInfo : public ABIInfo { + enum Class { + Integer, + Float + }; + static const unsigned MinABIStackAlignInBytes = 4; bool IsDarwinVectorABI; bool IsSmallStructInRegABI; bool IsMMXDisabled; bool IsWin32FloatStructABI; + unsigned DefaultNumRegisterParameters; static bool isRegisterSize(unsigned Size) { return (Size == 8 || Size == 16 || Size == 32 || Size == 64); @@ -434,8 +440,11 @@ class X86_32ABIInfo : public ABIInfo { /// \brief Return the alignment to use for the given type on the stack. unsigned getTypeStackAlignInBytes(QualType Ty, unsigned Align) const; + Class classify(QualType Ty) const; ABIArgInfo classifyReturnType(QualType RetTy, unsigned callingConvention) const; + ABIArgInfo classifyArgumentTypeWithReg(QualType RetTy, + unsigned &FreeRegs) const; ABIArgInfo classifyArgumentType(QualType RetTy) const; public: @@ -444,16 +453,18 @@ public: virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, CodeGenFunction &CGF) const; - X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m, bool w) + X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m, bool w, + unsigned r) : ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p), - IsMMXDisabled(m), IsWin32FloatStructABI(w) {} + IsMMXDisabled(m), IsWin32FloatStructABI(w), + DefaultNumRegisterParameters(r) {} }; class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { public: X86_32TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, - bool d, bool p, bool m, bool w) - :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m, w)) {} + bool d, bool p, bool m, bool w, unsigned r) + :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m, w, r)) {} void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const; @@ -690,6 +701,57 @@ ABIArgInfo X86_32ABIInfo::getIndirectResult(QualType Ty, bool ByVal) const { return ABIArgInfo::getIndirect(StackAlign); } +X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const { + const Type *T = isSingleElementStruct(Ty, getContext()); + if (!T) + T = Ty.getTypePtr(); + + if (const BuiltinType *BT = T->getAs<BuiltinType>()) { + BuiltinType::Kind K = BT->getKind(); + if (K == BuiltinType::Float || K == BuiltinType::Double) + return Float; + } + return Integer; +} + +ABIArgInfo +X86_32ABIInfo::classifyArgumentTypeWithReg(QualType Ty, + unsigned &FreeRegs) const { + // Common case first. + if (FreeRegs == 0) + return classifyArgumentType(Ty); + + Class C = classify(Ty); + if (C == Float) + return classifyArgumentType(Ty); + + unsigned SizeInRegs = (getContext().getTypeSize(Ty) + 31) / 32; + if (SizeInRegs == 0) + return classifyArgumentType(Ty); + + if (SizeInRegs > FreeRegs) { + FreeRegs = 0; + return classifyArgumentType(Ty); + } + assert(SizeInRegs >= 1 && SizeInRegs <= 3); + FreeRegs -= SizeInRegs; + + // If it is a simple scalar, keep the type so that we produce a cleaner IR. + ABIArgInfo Foo = classifyArgumentType(Ty); + if (Foo.isDirect() && !Foo.getDirectOffset() && !Foo.getPaddingType()) + return ABIArgInfo::getDirectInReg(Foo.getCoerceToType()); + if (Foo.isExtend()) + return ABIArgInfo::getExtendInReg(Foo.getCoerceToType()); + + llvm::LLVMContext &LLVMContext = getVMContext(); + llvm::Type *Int32 = llvm::Type::getInt32Ty(LLVMContext); + SmallVector<llvm::Type*, 3> Elements; + for (unsigned I = 0; I < SizeInRegs; ++I) + Elements.push_back(Int32); + llvm::Type *Result = llvm::StructType::get(LLVMContext, Elements); + return ABIArgInfo::getDirectInReg(Result); +} + ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const { // FIXME: Set alignment on indirect arguments. if (isAggregateTypeForABI(Ty)) { @@ -754,9 +816,23 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const { void X86_32ABIInfo::computeInfo(CGFunctionInfo &FI) const { FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), FI.getCallingConvention()); + + unsigned FreeRegs = FI.getHasRegParm() ? FI.getRegParm() : + DefaultNumRegisterParameters; + + // If the return value is indirect, then the hidden argument is consuming one + // integer register. + if (FI.getReturnInfo().isIndirect() && FreeRegs) { + --FreeRegs; + ABIArgInfo &Old = FI.getReturnInfo(); + Old = ABIArgInfo::getIndirectInReg(Old.getIndirectAlign(), + Old.getIndirectByVal(), + Old.getIndirectRealign()); + } + for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) - it->info = classifyArgumentType(it->type); + it->info = classifyArgumentTypeWithReg(it->type, FreeRegs); } llvm::Value *X86_32ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, @@ -3735,8 +3811,8 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { if (Triple.isOSDarwin()) return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, true, true, DisableMMX, false)); + new X86_32TargetCodeGenInfo(Types, true, true, DisableMMX, false, + CodeGenOpts.NumRegisterParameters)); switch (Triple.getOS()) { case llvm::Triple::Cygwin: @@ -3746,18 +3822,20 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::FreeBSD: case llvm::Triple::OpenBSD: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, false, true, DisableMMX, false)); + new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX, + false, + CodeGenOpts.NumRegisterParameters)); case llvm::Triple::Win32: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, false, true, DisableMMX, true)); + new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX, true, + CodeGenOpts.NumRegisterParameters)); default: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo( - Types, false, false, DisableMMX, false)); + new X86_32TargetCodeGenInfo(Types, false, false, DisableMMX, + false, + CodeGenOpts.NumRegisterParameters)); } } diff --git a/test/CodeGen/regparm-struct.c b/test/CodeGen/regparm-struct.c new file mode 100644 index 0000000000..b31901266e --- /dev/null +++ b/test/CodeGen/regparm-struct.c @@ -0,0 +1,177 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s + +__attribute__((regparm(3))) void f1(int a, int b, int c, int d); +// CHECK: declare void @f1(i32 inreg, i32 inreg, i32 inreg, i32) +void g1() { + f1(41, 42, 43, 44); +} + +struct s1 { + int x1; +}; +__attribute__((regparm(3))) void f2(int a, int b, struct s1 c, int d); +// CHECK: declare void @f2(i32 inreg, i32 inreg, i32 inreg, i32) +void g2() { + struct s1 x = {43}; + f2(41, 42, x, 44); +} + +struct s2 { + int x1; + int x2; +}; +__attribute__((regparm(3))) void f3(int a, int b, struct s2 c, int d); +// CHECK: declare void @f3(i32 inreg, i32 inreg, i32, i32, i32) +void g3() { + struct s2 x = {43, 44}; + f3(41, 42, x, 45); +} +__attribute__((regparm(3))) void f4(int a, struct s2 b, int c); +// CHECK: declare void @f4(i32 inreg, i32 inreg, i32 inreg, i32) +void g4() { + struct s2 x = {42, 43}; + f4(41, x, 44); +} + +struct s3 { + int x1; + int x2; + int x3; +}; +__attribute__((regparm(3))) void f5(int a, struct s3 b, int c); +// CHECK: declare void @f5(i32 inreg, i32, i32, i32, i32) +void g5() { + struct s3 x = {42, 43, 44}; + f5(41, x, 45); +} +__attribute__((regparm(3))) void f6(struct s3 a, int b); +// CHECK: declare void @f6(i32 inreg, i32 inreg, i32 inreg, i32) +void g6() { + struct s3 x = {41, 42, 43}; + f6(x, 44); +} + +struct s4 { + int x1; + int x2; + int x3; + int x4; +}; +__attribute__((regparm(3))) void f7(struct s4 a, int b); +// CHECK: declare void @f7(i32, i32, i32, i32, i32) +void g7() { + struct s4 x = {41, 42, 43, 44}; + f7(x, 45); +} + +__attribute__((regparm(3))) void f8(float a, int b); +// CHECK: declare void @f8(float, i32 inreg) +void g8(void) { + f8(41, 42); +} + +struct s5 { + float x1; +}; +__attribute__((regparm(3))) void f9(struct s5 a, int b); +// CHECK: declare void @f9(float, i32 inreg) +void g9(void) { + struct s5 x = {41}; + f9(x, 42); +} + +struct s6 { + float x1; + int x2; +}; +__attribute__((regparm(3))) void f10(struct s6 a, int b); +// CHECK: declare void @f10(i32 inreg, i32 inreg, i32 inreg) +void g10(void) { + struct s6 x = {41, 42}; + f10(x, 43); +} + +struct s7 { + float x1; + int x2; + float x3; +}; +__attribute__((regparm(3))) void f11(struct s7 a, int b); +// CHECK: declare void @f11(i32 inreg, i32 inreg, i32 inreg, i32) +void g11(void) { + struct s7 x = {41, 42, 43}; + f11(x, 44); +} + +struct s8 { + float x1; + float x2; +}; +__attribute__((regparm(3))) void f12(struct s8 a, int b); +// CHECK: declare void @f12(i32 inreg, i32 inreg, i32 inreg) +void g12(void) { + struct s8 x = {41, 42}; + f12(x, 43); +} + +struct s9 { + float x1; + float x2; + float x3; +}; +__attribute__((regparm(3))) void f13(struct s9 a, int b); +// CHECK: declare void @f13(i32 inreg, i32 inreg, i32 inreg, i32) +void g13(void) { + struct s9 x = {41, 42, 43}; + f13(x, 44); +} + +struct s10 { + double x1; +}; +__attribute__((regparm(3))) void f14(struct s10 a, int b, int c); +// CHECK: declare void @f14(double, i32 inreg, i32 inreg) +void g14(void) { + struct s10 x = { 41 }; + f14(x, 42, 43); +} + +struct s11 { + double x1; + double x2; +}; +__attribute__((regparm(3))) void f15(struct s11 a, int b); +// CHECK: declare void @f15(double, double, i32) +void g15(void) { + struct s11 x = { 41, 42 }; + f15(x, 43); +} + +struct s12 { + double x1; + float x2; +}; +__attribute__((regparm(3))) void f16(struct s12 a, int b); +// CHECK: declare void @f16(i32 inreg, i32 inreg, i32 inreg, i32) +void g16(void) { + struct s12 x = { 41, 42 }; + f16(x, 43); +} + +__attribute__((regparm(3))) struct s12 f17(int a, int b, int c); +// CHECK: declare void @f17(%struct.s12* inreg sret, i32 inreg, i32 inreg, i32) +void g17(void) { + f17(41, 42, 43); +} + +struct s13 { + struct inner { + float x; + } y; +}; +__attribute__((regparm(3))) void f18(struct s13 a, int b, int c, int d); +// CHECK: declare void @f18(%struct.s13* byval align 4, i32 inreg, i32 inreg, i32 inreg) +void g18(void) { + struct s13 x = {{41}}; + f18(x, 42, 43, 44); +} diff --git a/test/CodeGenCXX/pr13396.cpp b/test/CodeGenCXX/pr13396.cpp index b390eea009..7d4e2ce74f 100644 --- a/test/CodeGenCXX/pr13396.cpp +++ b/test/CodeGenCXX/pr13396.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple i686-pc-linux-gnu %s -emit-llvm -o - | FileCheck %s struct foo { template<typename T> __attribute__ ((regparm (3))) foo(T x) {} |