From: Jakob Stoklund Olesen Date: Tue, 28 May 2013 04:57:37 +0000 (+0000) Subject: Build correct coercion types in SparcV9ABIInfo. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fc782fbeb25ad880ec667f34997bd45d530aef86;p=clang Build correct coercion types in SparcV9ABIInfo. The coercion type serves two purposes: 1. Pad structs to a multiple of 64 bits, so they are passed 'left-aligned' in registers. 2. Expose aligned floating point elements as first-level elements, so the code generator knows to pass them in floating point registers. We also compute the InReg flag which indicates that the struct contains aligned 32-bit floats. This flag is used by the code generator to pick the right registers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@182753 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 222ef1429d..4fa0c3bbdf 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -5161,6 +5161,117 @@ private: virtual void computeInfo(CGFunctionInfo &FI) const; virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, CodeGenFunction &CGF) const; + + // Coercion type builder for structs passed in registers. The coercion type + // serves two purposes: + // + // 1. Pad structs to a multiple of 64 bits, so they are passed 'left-aligned' + // in registers. + // 2. Expose aligned floating point elements as first-level elements, so the + // code generator knows to pass them in floating point registers. + // + // We also compute the InReg flag which indicates that the struct contains + // aligned 32-bit floats. + // + struct CoerceBuilder { + llvm::LLVMContext &Context; + const llvm::DataLayout &DL; + SmallVector Elems; + uint64_t Size; + bool InReg; + + CoerceBuilder(llvm::LLVMContext &c, const llvm::DataLayout &dl) + : Context(c), DL(dl), Size(0), InReg(false) {} + + // Pad Elems with integers until Size is ToSize. + void pad(uint64_t ToSize) { + assert(ToSize >= Size && "Cannot remove elements"); + if (ToSize == Size) + return; + + // Finish the current 64-bit word. + uint64_t Aligned = llvm::RoundUpToAlignment(Size, 64); + if (Aligned > Size && Aligned <= ToSize) { + Elems.push_back(llvm::IntegerType::get(Context, Aligned - Size)); + Size = Aligned; + } + + // Add whole 64-bit words. + while (Size + 64 <= ToSize) { + Elems.push_back(llvm::Type::getInt64Ty(Context)); + Size += 64; + } + + // Final in-word padding. + if (Size < ToSize) { + Elems.push_back(llvm::IntegerType::get(Context, ToSize - Size)); + Size = ToSize; + } + } + + // Add a floating point element at Offset. + void addFloat(uint64_t Offset, llvm::Type *Ty, unsigned Bits) { + // Unaligned floats are treated as integers. + if (Offset % Bits) + return; + // The InReg flag is only required if there are any floats < 64 bits. + if (Bits < 64) + InReg = true; + pad(Offset); + Elems.push_back(Ty); + Size = Offset + Bits; + } + + // Add a struct type to the coercion type, starting at Offset (in bits). + void addStruct(uint64_t Offset, llvm::StructType *StrTy) { + const llvm::StructLayout *Layout = DL.getStructLayout(StrTy); + for (unsigned i = 0, e = StrTy->getNumElements(); i != e; ++i) { + llvm::Type *ElemTy = StrTy->getElementType(i); + uint64_t ElemOffset = Offset + Layout->getElementOffsetInBits(i); + switch (ElemTy->getTypeID()) { + case llvm::Type::StructTyID: + addStruct(ElemOffset, cast(ElemTy)); + break; + case llvm::Type::FloatTyID: + addFloat(ElemOffset, ElemTy, 32); + break; + case llvm::Type::DoubleTyID: + addFloat(ElemOffset, ElemTy, 64); + break; + case llvm::Type::FP128TyID: + addFloat(ElemOffset, ElemTy, 128); + break; + case llvm::Type::PointerTyID: + if (ElemOffset % 64 == 0) { + pad(ElemOffset); + Elems.push_back(ElemTy); + Size += 64; + } + break; + default: + break; + } + } + } + + // Check if Ty is a usable substitute for the coercion type. + bool isUsableType(llvm::StructType *Ty) const { + if (Ty->getNumElements() != Elems.size()) + return false; + for (unsigned i = 0, e = Elems.size(); i != e; ++i) + if (Elems[i] != Ty->getElementType(i)) + return false; + return true; + } + + // Get the coercion type as a literal struct type. + llvm::Type *getType() const { + if (Elems.size() == 1) + return Elems.front(); + else + return llvm::StructType::get(Context, Elems); + } + }; }; } // end anonymous namespace @@ -5189,9 +5300,22 @@ SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit) const { return ABIArgInfo::getDirect(); // This is a small aggregate type that should be passed in registers. - // FIXME: Compute the correct coersion type. - // FIXME: Ensure any float members are passed in float registers. - return ABIArgInfo::getDirect(); + // Build a coercion type from the LLVM struct type. + llvm::StructType *StrTy = dyn_cast(CGT.ConvertType(Ty)); + if (!StrTy) + return ABIArgInfo::getDirect(); + + CoerceBuilder CB(getVMContext(), getDataLayout()); + CB.addStruct(0, StrTy); + CB.pad(llvm::RoundUpToAlignment(CB.DL.getTypeSizeInBits(StrTy), 64)); + + // Try to use the original type for coercion. + llvm::Type *CoerceTy = CB.isUsableType(StrTy) ? StrTy : CB.getType(); + + if (CB.InReg) + return ABIArgInfo::getDirectInReg(CoerceTy); + else + return ABIArgInfo::getDirect(CoerceTy); } llvm::Value *SparcV9ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, diff --git a/test/CodeGen/sparcv9-abi.c b/test/CodeGen/sparcv9-abi.c index eb515e09e7..41b06dd01d 100644 --- a/test/CodeGen/sparcv9-abi.c +++ b/test/CodeGen/sparcv9-abi.c @@ -56,3 +56,37 @@ struct large f_large(struct large x) { return x; } +// A 64-bit struct fits in a register. +struct reg { + int a, b; +}; + +// CHECK: define i64 @f_reg(i64 %x.coerce) +struct reg f_reg(struct reg x) { + x.a += x.b; + return x; +} + +// Structs with mixed int and float parts require the inreg attribute. +struct mixed { + int a; + float b; +}; + +// CHECK: @f_mixed(i32 inreg %x.coerce0, float inreg %x.coerce1) +// FIXME: The return value should also be 'inreg'. +struct mixed f_mixed(struct mixed x) { + x.a += 1; + return x; +} + +// Struct with padding. +struct mixed2 { + int a; + double b; +}; + +struct mixed2 f_mixed2(struct mixed2 x) { + x.a += 1; + return x; +}