Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 161 additions & 1 deletion flang/lib/Optimizer/CodeGen/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,29 @@ struct TargetPPC64 : public GenericTarget<TargetPPC64> {
AT{});
return marshal;
}

CodeGenSpecifics::Marshalling
structType(mlir::Location loc, fir::RecordType ty, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign{
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
unsigned short align{
std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
marshal.emplace_back(fir::ReferenceType::get(ty),
AT{align, /*byvale*/ !isResult, /*sret*/ isResult});
return marshal;
}

CodeGenSpecifics::Marshalling
structArgumentType(mlir::Location loc, fir::RecordType ty,
const Marshalling &previousArguments) const override {
return structType(loc, ty, false);
}

CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType ty) const override {
return structType(loc, ty, true);
}
};
} // namespace

Expand All @@ -1060,7 +1083,7 @@ namespace {
struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
using GenericTarget::GenericTarget;

static constexpr int defaultWidth = 64;
static constexpr int defaultWidth{64};

CodeGenSpecifics::Marshalling
complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
Expand All @@ -1081,6 +1104,143 @@ struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
AT{});
return marshal;
}

unsigned getElemWidth(mlir::Type ty) const {
unsigned width{};
llvm::TypeSwitch<mlir::Type>(ty)
.template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
auto elemType{
mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};
width = elemType.getWidth();
})
.template Case<mlir::FloatType>(
[&](mlir::FloatType real) { width = real.getWidth(); });
return width;
}

// Determine if all derived types components are of the same float type with
// the same width. Complex(4) is considered 2 floats and complex(8) 2 doubles.
bool hasSameFloatAndWidth(
fir::RecordType recTy,
std::pair<mlir::Type, unsigned> &firstTypeAndWidth) const {
for (auto comp : recTy.getTypeList()) {
mlir::Type compType{comp.second};
if (mlir::isa<fir::RecordType>(compType)) {
auto rc{hasSameFloatAndWidth(mlir::cast<fir::RecordType>(compType),
firstTypeAndWidth)};
if (!rc)
return false;
} else {
mlir::Type ty;
bool isFloatType{false};
if (mlir::isa<mlir::FloatType, mlir::ComplexType>(compType)) {
ty = compType;
isFloatType = true;
} else if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(compType)) {
ty = seqTy.getEleTy();
isFloatType = mlir::isa<mlir::FloatType, mlir::ComplexType>(ty);
}

if (!isFloatType) {
return false;
}
auto width{getElemWidth(ty)};
if (firstTypeAndWidth.first == nullptr) {
firstTypeAndWidth.first = ty;
firstTypeAndWidth.second = width;
} else if (width != firstTypeAndWidth.second) {
return false;
}
}
}
return true;
}

CodeGenSpecifics::Marshalling
passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign{
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
unsigned short align{
std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
marshal.emplace_back(fir::ReferenceType::get(ty),
AT{align, /*byval=*/!isResult, /*sret=*/isResult});
return marshal;
}

CodeGenSpecifics::Marshalling
structType(mlir::Location loc, fir::RecordType recTy, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
loc, recTy, getDataLayout(), kindMap)};
auto recordTypeSize{sizeAndAlign.first};
mlir::Type seqTy;
std::pair<mlir::Type, unsigned> firstTyAndWidth{nullptr, 0};

// If there are less than or equal to 8 floats, the structure is flatten as
// an array of floats.
constexpr uint64_t maxNoOfFloats{8};

// i64 type
mlir::Type elemTy{mlir::IntegerType::get(recTy.getContext(), defaultWidth)};
uint64_t nElem{static_cast<uint64_t>(
std::ceil(static_cast<float>(recordTypeSize * 8) / defaultWidth))};

// If the derived type components contains are all floats with the same
// width, the argument is passed as an array of floats.
if (hasSameFloatAndWidth(recTy, firstTyAndWidth)) {
uint64_t n{};
auto firstType{firstTyAndWidth.first};

// Type is either float or complex
if (auto cmplx = mlir::dyn_cast<mlir::ComplexType>(firstType)) {
auto fltType{mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};
n = static_cast<uint64_t>(8 * recordTypeSize / fltType.getWidth());
if (n <= maxNoOfFloats) {
nElem = n;
elemTy = fltType;
}
} else if (mlir::isa<mlir::FloatType>(firstType)) {
auto elemSizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
loc, firstType, getDataLayout(), kindMap)};
n = static_cast<uint64_t>(recordTypeSize / elemSizeAndAlign.first);
if (n <= maxNoOfFloats) {
nElem = n;
elemTy = firstType;
}
}
// Neither float nor complex
assert(n > 0 && "unexpected type");
}

// For function returns, only flattened if there are less than 8
// floats in total.
if (isResult &&
((mlir::isa<mlir::FloatType>(elemTy) && nElem > maxNoOfFloats) ||
!mlir::isa<mlir::FloatType>(elemTy))) {
return passOnTheStack(loc, recTy, isResult);
}

seqTy = fir::SequenceType::get(nElem, elemTy);
marshal.emplace_back(seqTy, AT{});
return marshal;
}

CodeGenSpecifics::Marshalling
structArgumentType(mlir::Location loc, fir::RecordType recType,
const Marshalling &previousArguments) const override {
auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
loc, recType, getDataLayout(), kindMap)};
if (sizeAndAlign.first > 64) {
return passOnTheStack(loc, recType, false);
}
return structType(loc, recType, false);
}

CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType recType) const override {
return structType(loc, recType, true);
}
};
} // namespace

Expand Down
25 changes: 25 additions & 0 deletions flang/test/Fir/struct-passing-powerpc64-aix-byval.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Test powerpc64 rewrite of struct passed by value (BIND(C), VALUE derived types).
//
// RUN: fir-opt --target-rewrite="target=powerpc64-ibm-aix" %s | FileCheck %s
// REQUIRES: powerpc-registered-target

// character type
func.func @csubch(%arg0: !fir.type<_QFcsubchTdt<{c:!fir.char<1>}>> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubch"} { return }
//CHECK-LABEL: func.func @csubch(%arg0: !fir.ref<!fir.type<_QFcsubchTdt<{c:!fir.char<1>}>>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubchTdt<{c:!fir.char<1>}>>}) attributes {fir.bindc_name = "csubch"}

// integer type
func.func @csubi1(%arg0: !fir.type<_QFcsubi1Tdt<{i:i32}>> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubi1"} { return }
//CHECK-LABEL: func.func @csubi1(%arg0: !fir.ref<!fir.type<_QFcsubi1Tdt<{i:i32}>>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubi1Tdt<{i:i32}>>}) attributes {fir.bindc_name = "csubi1"}

// real type (scalar)
func.func @csubr1(%arg0: !fir.type<_QFcsubr1Tdt<{r1:f32,r2:f32,r3:f32}>> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr1"} { return }
//CHECK-LABEL: func.func @csubr1(%arg0: !fir.ref<!fir.type<_QFcsubr1Tdt<{r1:f32,r2:f32,r3:f32}>>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubr1Tdt<{r1:f32,r2:f32,r3:f32}>>}) attributes {fir.bindc_name = "csubr1"}

// real type (array)
func.func @csubr5(%arg0: !fir.type<_QFcsubr5Tdt<{r:!fir.array<8xf32>}>> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr5"} { return }
//CHECK-LABEL: func.func @csubr5(%arg0: !fir.ref<!fir.type<_QFcsubr5Tdt<{r:!fir.array<8xf32>}>>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubr5Tdt<{r:!fir.array<8xf32>}>>}) attributes {fir.bindc_name = "csubr5"}

// mixed types
func.func @csub1(%arg0: !fir.type<_QFcsub1Tdt<{c:!fir.char<1>,r:f32,i:i64}>> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub1"} { return }
//CHECK-LABEL: func.func @csub1(%arg0: !fir.ref<!fir.type<_QFcsub1Tdt<{c:!fir.char<1>,r:f32,i:i64}>>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsub1Tdt<{c:!fir.char<1>,r:f32,i:i64}>>}) attributes {fir.bindc_name = "csub1"}

79 changes: 79 additions & 0 deletions flang/test/Fir/struct-passing-ppc64le-byval.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Test ppc64le rewrite of struct passed by value (BIND(C), VALUE derived types).
//
// RUN: fir-opt --target-rewrite="target=ppc64le-unknown-linux" %s | FileCheck %s
// REQUIRES: powerpc-registered-target

// character type
func.func @csubch(%arg0: !fir.type<_QFcsubchTdt{c:!fir.char<1>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubch"} { return }
//CHECK-LABEL: func.func @csubch(%arg0: !fir.array<1xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubch"}

// integer type
func.func @csubi1(%arg0: !fir.type<_QFcsubi1Tdt{i:i32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubi1"} { return }
//CHECK-LABEL: func.func @csubi1(%arg0: !fir.array<1xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubi1"}

// integer type with size > 64 bytes
func.func @csubi2(%arg0: !fir.type<_QFcsubi2Tdt{i:!fir.array<17xi32>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubi2"} { return }
//CHECK-LABEL: func.func @csubi2(%arg0: !fir.ref<!fir.type<_QFcsubi2Tdt{i:!fir.array<17xi32>}>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubi2Tdt{i:!fir.array<17xi32>}>}) attributes {fir.bindc_name = "csubi2"}

// real type (scalar)
func.func @csubr1(%arg0: !fir.type<_QFcsubr1Tdt{r1:f32,r2:f32,r3:f32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr1"} { return }
//CHECK-LABEL: func.func @csubr1(%arg0: !fir.array<3xf32> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr1"}

// real type (< 8 floats and <= 64 bytes)
func.func @csubr2(%arg0: !fir.type<_QFcsubr2Tdt{r1:f64,r2:f64,r3:f64,r4:f64,r5:f64,r6:f64,r7:f64,r8:f64}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr2"} { return }
//CHECK-LABEL: func.func @csubr2(%arg0: !fir.array<8xf64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr2"}

// real type (> 8 floats and <= 64 bytes)
func.func @csubr3(%arg0: !fir.type<_QFcsubr3Tdt{r1:f32,r2:f32,r3:f32,r4:f32,r5:f32,r6:f32,r7:f32,r8:f32,r9:f32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr3"} { return }
//CHECK-LABEL: func.func @csubr3(%arg0: !fir.array<5xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr3"}

// real type (> 8 floats and > 64 bytes)
func.func @csubr4(%arg0: !fir.type<_QFcsubr4Tdt{r1:f32,r2:f32,r3:f32,r4:f32,r5:f32,r6:f32,r7:f32,r8:f32,r9:f32,r10:f32,r11:f32,r12:f32,r13:f32,r14:f32,r15:f32,r16:f32,r17:f32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr4"} { return }
//CHECK-LABEL: func.func @csubr4(%arg0: !fir.ref<!fir.type<_QFcsubr4Tdt{r1:f32,r2:f32,r3:f32,r4:f32,r5:f32,r6:f32,r7:f32,r8:f32,r9:f32,r10:f32,r11:f32,r12:f32,r13:f32,r14:f32,r15:f32,r16:f32,r17:f32}>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubr4Tdt{r1:f32,r2:f32,r3:f32,r4:f32,r5:f32,r6:f32,r7:f32,r8:f32,r9:f32,r10:f32,r11:f32,r12:f32,r13:f32,r14:f32,r15:f32,r16:f32,r17:f32}>}) attributes {fir.bindc_name = "csubr4"}

// real type (array)
func.func @csubr5(%arg0: !fir.type<_QFcsubr5Tdt{r:!fir.array<8xf32>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr5"} { return }
//CHECK-LABEL: func.func @csubr5(%arg0: !fir.array<8xf32> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr5"}

// real type (array componets and > 64 bytes)
func.func @csubr6(%arg0: !fir.type<_QFcsubr6Tdt{r:!fir.array<9xf64>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr6"} { return }
//CHECK-LABEL: func.func @csubr6(%arg0: !fir.ref<!fir.type<_QFcsubr6Tdt{r:!fir.array<9xf64>}>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubr6Tdt{r:!fir.array<9xf64>}>}) attributes {fir.bindc_name = "csubr6"}

// real type with different kinds
func.func @csubr7(%arg0: !fir.type<_QFcsubr7Tdt{r1:f32,r2:f64,r3:f64}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr7"} { return }
//CHECK-LABEL: func.func @csubr7(%arg0: !fir.array<3xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubr7"}

// complex type
func.func @csubc1(%arg0: !fir.type<_QFcsubc1Tdt{r1:complex<f32>,r2:complex<f32>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc1"} { return }
//CHECK-LABEL: func.func @csubc1(%arg0: !fir.array<4xf32> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc1"}

func.func @csubc2(%arg0: !fir.type<_QFcsubc2Tdt{r1:complex<f64>,r2:complex<f64>,r3:complex<f64>,r4:complex<f64>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc2"} { return }
//CHECK-LABEL: func.func @csubc2(%arg0: !fir.array<8xf64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc2"}

// complex type (> 8 floats and size < 64 bytes)
func.func @csubc3(%arg0: !fir.type<_QFcsubc3Tdt{r:!fir.array<5xcomplex<f32>>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc3"} { return }
//CHECK-LABEL: func.func @csubc3(%arg0: !fir.array<5xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc3"}

// complex type (size > 64 bytes)
func.func @csubc4(%arg0: !fir.type<_QFcsubc4Tdt{r:!fir.array<9xcomplex<f32>>}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csubc4"} { return }
//CHECK-LABEL: func.func @csubc4(%arg0: !fir.ref<!fir.type<_QFcsubc4Tdt{r:!fir.array<9xcomplex<f32>>}>> {fir.bindc_name = "arg", llvm.align = 8 : i32, llvm.byval = !fir.type<_QFcsubc4Tdt{r:!fir.array<9xcomplex<f32>>}>}) attributes {fir.bindc_name = "csubc4"}

// mixed type
func.func @csub1(%arg0: !fir.type<_QFcsub1Tdt{c:!fir.char<1>,r:f32,i:i64}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub1"} { return }
//CHECK-LABEL: func.func @csub1(%arg0: !fir.array<2xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub1"}

// nested derived types
func.func @csub2(%arg0: !fir.type<_QFcsub2Tdt1{xdt0:!fir.type<_QFcsub2Tdt0{f1:f32,f2:f32,f3:f32}>,x1:f32,x2:f32,x3:f32,x4:f32,x5:f32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub2"} { return }
//CHECK-LABEL: func.func @csub2(%arg0: !fir.array<8xf32> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub2"}

func.func @csub3(%arg0: !fir.type<_QFcsub3Tdt1{xdt0:!fir.type<_QFcsub3Tdt0{f1:f32,f2:f32,f3:f32}>,x1:f32,x2:f32,i:i32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub3"} { return }
//CHECK-LABEL: func.func @csub3(%arg0: !fir.array<3xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub3"}

func.func @csub4(%arg0: !fir.type<_QFcsub4Tdt1{xdt0:!fir.type<_QFcsub4Tdt0{f1:f32}>,x1:f64}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub4"} { return }
//CHECK-LABEL: func.func @csub4(%arg0: !fir.array<2xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub4"}

func.func @csub5(%arg0: !fir.type<_QFcsub5Tdt1{xdt0:!fir.type<_QFcsub5Tdt0{f1:complex<f32>}>,x1:f32}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub5"} { return }
//CHECK-LABEL: func.func @csub5(%arg0: !fir.array<3xf32> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub5"}

func.func @csub6(%arg0: !fir.type<_QFcsub6Tdt1{xdt0:!fir.type<_QFcsub6Tdt0{f1:complex<f32>}>,x1:f64}> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub6"} { return }
//CHECK-LABEL: func.func @csub6(%arg0: !fir.array<2xi64> {fir.bindc_name = "arg"}) attributes {fir.bindc_name = "csub6"}
Loading
Loading