[flang][AIX] BIND(C) derived type alignment for AIX (#121505)

This patch is to handle the alignment requirement for the `bind(c)`
derived type component that is real type and larger than 4 bytes. The
alignment of such component is 4-byte.
This commit is contained in:
Kelvin Li 2025-01-13 10:52:09 -05:00 committed by GitHub
parent e2c49a45da
commit 79e788d02e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 340 additions and 35 deletions

View File

@ -62,7 +62,7 @@ public:
// fir.type<name(p : TY'...){f : TY...}> --> llvm<"%name = { ty... }">
std::optional<llvm::LogicalResult>
convertRecordType(fir::RecordType derived,
llvm::SmallVectorImpl<mlir::Type> &results);
llvm::SmallVectorImpl<mlir::Type> &results, bool isPacked);
// Is an extended descriptor needed given the element type of a fir.box type ?
// Extended descriptors are required for derived types.

View File

@ -346,6 +346,12 @@ def fir_RecordType : FIR_Type<"Record", "type"> {
void finalize(llvm::ArrayRef<TypePair> lenPList,
llvm::ArrayRef<TypePair> typeList);
// fir.type is unpacked by default. If the flag is set, the packed fir.type
// is generated and the alignment is enforced by explicit padding by i8
// array fields.
bool isPacked() const;
void pack(bool);
detail::RecordTypeStorage const *uniqueKey() const;
}];
}

View File

@ -20,6 +20,8 @@
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "llvm/Support/Debug.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#define DEBUG_TYPE "flang-lower-type"
@ -385,9 +387,20 @@ struct TypeBuilderImpl {
// with dozens of components/parents (modern Fortran).
derivedTypeInConstruction.try_emplace(&derivedScope, rec);
auto targetTriple{llvm::Triple(
llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))};
// Always generate packed FIR struct type for bind(c) derived type for AIX
if (targetTriple.getOS() == llvm::Triple::OSType::AIX &&
tySpec.typeSymbol().attrs().test(Fortran::semantics::Attr::BIND_C) &&
!IsIsoCType(&tySpec)) {
rec.pack(true);
}
// Gather the record type fields.
// (1) The data components.
if (converter.getLoweringOptions().getLowerToHighLevelFIR()) {
size_t prev_offset{0};
unsigned padCounter{0};
// In HLFIR the parent component is the first fir.type component.
for (const auto &componentName :
typeSymbol.get<Fortran::semantics::DerivedTypeDetails>()
@ -397,7 +410,38 @@ struct TypeBuilderImpl {
"failed to find derived type component symbol");
const Fortran::semantics::Symbol &component = scopeIter->second.get();
mlir::Type ty = genSymbolType(component);
if (rec.isPacked()) {
auto compSize{component.size()};
auto compOffset{component.offset()};
if (prev_offset < compOffset) {
size_t pad{compOffset - prev_offset};
mlir::Type i8Ty{mlir::IntegerType::get(context, 8)};
fir::SequenceType::Shape shape{static_cast<int64_t>(pad)};
mlir::Type padTy{fir::SequenceType::get(shape, i8Ty)};
prev_offset += pad;
cs.emplace_back("__padding" + std::to_string(padCounter++), padTy);
}
prev_offset += compSize;
}
cs.emplace_back(converter.getRecordTypeFieldName(component), ty);
if (rec.isPacked()) {
// For the last component, determine if any padding is needed.
if (componentName ==
typeSymbol.get<Fortran::semantics::DerivedTypeDetails>()
.componentNames()
.back()) {
auto compEnd{component.offset() + component.size()};
if (compEnd < derivedScope.size()) {
size_t pad{derivedScope.size() - compEnd};
mlir::Type i8Ty{mlir::IntegerType::get(context, 8)};
fir::SequenceType::Shape shape{static_cast<int64_t>(pad)};
mlir::Type padTy{fir::SequenceType::get(shape, i8Ty)};
cs.emplace_back("__padding" + std::to_string(padCounter++),
padTy);
}
}
}
}
} else {
for (const auto &component :

View File

@ -167,6 +167,7 @@ public:
cs.emplace_back(t.first, t.second);
}
rec.finalize(ps, cs);
rec.pack(ty.isPacked());
return rec;
});
addConversion([&](TypeDescType ty) {

View File

@ -82,7 +82,7 @@ LLVMTypeConverter::LLVMTypeConverter(mlir::ModuleOp module, bool applyTBAA,
[&](fir::PointerType pointer) { return convertPointerLike(pointer); });
addConversion(
[&](fir::RecordType derived, llvm::SmallVectorImpl<mlir::Type> &results) {
return convertRecordType(derived, results);
return convertRecordType(derived, results, derived.isPacked());
});
addConversion(
[&](fir::ReferenceType ref) { return convertPointerLike(ref); });
@ -133,8 +133,10 @@ mlir::Type LLVMTypeConverter::indexType() const {
}
// fir.type<name(p : TY'...){f : TY...}> --> llvm<"%name = { ty... }">
std::optional<llvm::LogicalResult> LLVMTypeConverter::convertRecordType(
fir::RecordType derived, llvm::SmallVectorImpl<mlir::Type> &results) {
std::optional<llvm::LogicalResult>
LLVMTypeConverter::convertRecordType(fir::RecordType derived,
llvm::SmallVectorImpl<mlir::Type> &results,
bool isPacked) {
auto name = fir::NameUniquer::dropTypeConversionMarkers(derived.getName());
auto st = mlir::LLVM::LLVMStructType::getIdentified(&getContext(), name);
@ -156,7 +158,7 @@ std::optional<llvm::LogicalResult> LLVMTypeConverter::convertRecordType(
else
members.push_back(mlir::cast<mlir::Type>(convertType(mem.second)));
}
if (mlir::failed(st.setBody(members, /*isPacked=*/false)))
if (mlir::failed(st.setBody(members, isPacked)))
return mlir::failure();
results.push_back(st);
return mlir::success();

View File

@ -165,16 +165,20 @@ struct RecordTypeStorage : public mlir::TypeStorage {
setTypeList(typeList);
}
bool isPacked() const { return packed; }
void pack(bool p) { packed = p; }
protected:
std::string name;
bool finalized;
bool packed;
std::vector<RecordType::TypePair> lens;
std::vector<RecordType::TypePair> types;
private:
RecordTypeStorage() = delete;
explicit RecordTypeStorage(llvm::StringRef name)
: name{name}, finalized{false} {}
: name{name}, finalized{false}, packed{false} {}
};
} // namespace detail
@ -872,9 +876,14 @@ llvm::LogicalResult fir::PointerType::verify(
//===----------------------------------------------------------------------===//
// Fortran derived type
// unpacked:
// `type` `<` name
// (`(` id `:` type (`,` id `:` type)* `)`)?
// (`{` id `:` type (`,` id `:` type)* `}`)? '>'
// packed:
// `type` `<` name
// (`(` id `:` type (`,` id `:` type)* `)`)?
// (`<{` id `:` type (`,` id `:` type)* `}>`)? '>'
mlir::Type fir::RecordType::parse(mlir::AsmParser &parser) {
llvm::StringRef name;
if (parser.parseLess() || parser.parseKeyword(&name))
@ -900,6 +909,10 @@ mlir::Type fir::RecordType::parse(mlir::AsmParser &parser) {
}
RecordType::TypeList typeList;
if (!parser.parseOptionalLess()) {
result.pack(true);
}
if (!parser.parseOptionalLBrace()) {
while (true) {
llvm::StringRef field;
@ -913,8 +926,10 @@ mlir::Type fir::RecordType::parse(mlir::AsmParser &parser) {
if (parser.parseOptionalComma())
break;
}
if (parser.parseRBrace())
return {};
if (parser.parseOptionalGreater()) {
if (parser.parseRBrace())
return {};
}
}
if (parser.parseGreater())
@ -941,6 +956,9 @@ void fir::RecordType::print(mlir::AsmPrinter &printer) const {
printer << ')';
}
if (getTypeList().size()) {
if (isPacked()) {
printer << '<';
}
char ch = '{';
for (auto p : getTypeList()) {
printer << ch << p.first << ':';
@ -948,6 +966,9 @@ void fir::RecordType::print(mlir::AsmPrinter &printer) const {
ch = ',';
}
printer << '}';
if (isPacked()) {
printer << '>';
}
}
recordTypeVisited.erase(uniqueKey());
}
@ -973,6 +994,10 @@ RecordType::TypeList fir::RecordType::getLenParamList() const {
bool fir::RecordType::isFinalized() const { return getImpl()->isFinalized(); }
void fir::RecordType::pack(bool p) { getImpl()->pack(p); }
bool fir::RecordType::isPacked() const { return getImpl()->isPacked(); }
detail::RecordTypeStorage const *fir::RecordType::uniqueKey() const {
return getImpl();
}

View File

@ -17,6 +17,8 @@
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include "flang/Semantics/type.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include <algorithm>
#include <vector>
@ -51,9 +53,12 @@ private:
SymbolAndOffset Resolve(const SymbolAndOffset &);
std::size_t ComputeOffset(const EquivalenceObject &);
// Returns amount of padding that was needed for alignment
std::size_t DoSymbol(Symbol &);
std::size_t DoSymbol(
Symbol &, std::optional<const size_t> newAlign = std::nullopt);
SizeAndAlignment GetSizeAndAlignment(const Symbol &, bool entire);
std::size_t Align(std::size_t, std::size_t);
std::optional<size_t> CompAlignment(const Symbol &);
std::optional<size_t> HasSpecialAlign(const Symbol &, Scope &);
SemanticsContext &context_;
std::size_t offset_{0};
@ -65,6 +70,69 @@ private:
equivalenceBlock_;
};
// This function is only called if the target platform is AIX.
static bool isReal8OrLarger(const Fortran::semantics::DeclTypeSpec *type) {
return ((type->IsNumeric(common::TypeCategory::Real) ||
type->IsNumeric(common::TypeCategory::Complex)) &&
evaluate::ToInt64(type->numericTypeSpec().kind()) > 4);
}
// This function is only called if the target platform is AIX.
// It determines the alignment of a component. If the component is a derived
// type, the alignment is computed accordingly.
std::optional<size_t> ComputeOffsetsHelper::CompAlignment(const Symbol &sym) {
size_t max_align{0};
constexpr size_t fourByteAlign{4};
bool contain_double{false};
auto derivedTypeSpec{sym.GetType()->AsDerived()};
DirectComponentIterator directs{*derivedTypeSpec};
for (auto it{directs.begin()}; it != directs.end(); ++it) {
auto type{it->GetType()};
auto s{GetSizeAndAlignment(*it, true)};
if (isReal8OrLarger(type)) {
max_align = std::max(max_align, fourByteAlign);
contain_double = true;
} else if (type->AsDerived()) {
if (const auto newAlgin{CompAlignment(*it)}) {
max_align = std::max(max_align, s.alignment);
} else {
return std::nullopt;
}
} else {
max_align = std::max(max_align, s.alignment);
}
}
if (contain_double) {
return max_align;
} else {
return std::nullopt;
}
}
// This function is only called if the target platform is AIX.
// Special alignment is needed only if it is a bind(c) derived type
// and contain real type components that have larger than 4 bytes.
std::optional<size_t> ComputeOffsetsHelper::HasSpecialAlign(
const Symbol &sym, Scope &scope) {
// On AIX, if the component that is not the first component and is
// a float of 8 bytes or larger, it has the 4-byte alignment.
// Only set the special alignment for bind(c) derived type on that platform.
if (const auto type{sym.GetType()}) {
auto &symOwner{sym.owner()};
if (symOwner.symbol() && symOwner.IsDerivedType() &&
symOwner.symbol()->attrs().HasAny({semantics::Attr::BIND_C}) &&
&sym != &(*scope.GetSymbols().front())) {
if (isReal8OrLarger(type)) {
return 4UL;
} else if (type->AsDerived()) {
return CompAlignment(sym);
}
}
}
return std::nullopt;
}
void ComputeOffsetsHelper::Compute(Scope &scope) {
for (Scope &child : scope.children()) {
ComputeOffsets(context_, child);
@ -113,7 +181,15 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
if (!FindCommonBlockContaining(*symbol) &&
dependents_.find(symbol) == dependents_.end() &&
equivalenceBlock_.find(symbol) == equivalenceBlock_.end()) {
DoSymbol(*symbol);
std::optional<size_t> newAlign{std::nullopt};
// Handle special alignment requirement for AIX
auto triple{llvm::Triple(
llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))};
if (triple.getOS() == llvm::Triple::OSType::AIX) {
newAlign = HasSpecialAlign(*symbol, scope);
}
DoSymbol(*symbol, newAlign);
if (auto *generic{symbol->detailsIf<GenericDetails>()}) {
if (Symbol * specific{generic->specific()};
specific && !FindCommonBlockContaining(*specific)) {
@ -313,7 +389,8 @@ std::size_t ComputeOffsetsHelper::ComputeOffset(
return result;
}
std::size_t ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
std::size_t ComputeOffsetsHelper::DoSymbol(
Symbol &symbol, std::optional<const size_t> newAlign) {
if (!symbol.has<ObjectEntityDetails>() && !symbol.has<ProcEntityDetails>()) {
return 0;
}
@ -322,12 +399,13 @@ std::size_t ComputeOffsetsHelper::DoSymbol(Symbol &symbol) {
return 0;
}
std::size_t previousOffset{offset_};
offset_ = Align(offset_, s.alignment);
size_t alignVal{newAlign.value_or(s.alignment)};
offset_ = Align(offset_, alignVal);
std::size_t padding{offset_ - previousOffset};
symbol.set_size(s.size);
symbol.set_offset(offset_);
offset_ += s.size;
alignment_ = std::max(alignment_, s.alignment);
alignment_ = std::max(alignment_, alignVal);
return padding;
}

View File

@ -38,8 +38,8 @@ end
! CHECK-LABEL: func.func @_QPsub2()
! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFsub2Ex"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>)
! CHECK: %[[CPTR:.*]] = fir.field_index cptr, !fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>
! CHECK: %[[CPTR_COORD:.*]] = fir.coordinate_of %{{.*}}#1, %[[CPTR]] : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.field) -> !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>
! CHECK: %[[CPTR:.*]] = fir.field_index cptr, !fir.type<_QM__fortran_builtinsT__builtin_c_devptr{{[<]?}}{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}{{[>]?}}>
! CHECK: %[[CPTR_COORD:.*]] = fir.coordinate_of %{{.*}}#1, %[[CPTR]] : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{{[<]?}}{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}{{[>]?}}>>, !fir.field) -> !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>
! CHECK: %[[ADDRESS:.*]] = fir.field_index __address, !fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>
! CHECK: %[[ADDRESS_COORD:.*]] = fir.coordinate_of %[[CPTR_COORD]], %[[ADDRESS]] : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.field) -> !fir.ref<i64>
! CHECK: %[[ADDRESS_LOADED:.*]] = fir.load %[[ADDRESS_COORD]] : !fir.ref<i64>

View File

@ -14,11 +14,11 @@ contains
call use_it(x%i)
end subroutine
! CHECK-LABEL: func.func @test(
! CHECK-SAME: %[[VAL_0:.*]]: !fir.type<_QMbindc_byvalTt{i:i32}>
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.type<_QMbindc_byvalTt{i:i32}>
! CHECK: fir.store %[[VAL_0]] to %[[VAL_1]] : !fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>
! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{[0-9]+}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QMbindc_byvalFtestEx"} : (!fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>, !fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>)
! CHECK: %[[VAL_3:.*]] = hlfir.designate %[[VAL_2]]#0{"i"} : (!fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>) -> !fir.ref<i32>
! CHECK-SAME: %[[VAL_0:.*]]: !fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>
! CHECK: fir.store %[[VAL_0]] to %[[VAL_1]] : !fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>
! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_1]] dummy_scope %{{[0-9]}} {fortran_attrs = #fir.var_attrs<value>, uniq_name = "_QMbindc_byvalFtestEx"} : (!fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>, !fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>)
! CHECK: %[[VAL_3:.*]] = hlfir.designate %[[VAL_2]]#0{"i"} : (!fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>) -> !fir.ref<i32>
! CHECK: fir.call @_QPuse_it(%[[VAL_3]]) fastmath<contract> : (!fir.ref<i32>) -> ()
! CHECK: return
! CHECK: }
@ -28,10 +28,10 @@ contains
call test(x)
end subroutine
! CHECK-LABEL: func.func @_QMbindc_byvalPcall_it(
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %{{[0-9]+}} {uniq_name = "_QMbindc_byvalFcall_itEx"} : (!fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>, !fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>)
! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#1 : !fir.ref<!fir.type<_QMbindc_byvalTt{i:i32}>>
! CHECK: fir.call @test(%[[VAL_2]]) proc_attrs<bind_c> fastmath<contract> : (!fir.type<_QMbindc_byvalTt{i:i32}>) -> ()
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] dummy_scope %{{[0-9]}} {uniq_name = "_QMbindc_byvalFcall_itEx"} : (!fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>, !fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>)
! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#1 : !fir.ref<!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>>
! CHECK: fir.call @test(%[[VAL_2]]) proc_attrs<bind_c> fastmath<contract> : (!fir.type<_QMbindc_byvalTt{{[<]?}}{i:i32}{{[>]?}}>) -> ()
! CHECK: return
! CHECK: }
end module

View File

@ -86,7 +86,7 @@ subroutine copyin_char_chararray()
end
! CHECK-LABEL: func.func @_QPcopyin_derived_type() {
! CHECK: %[[VAL_0:.*]] = fir.address_of(@_QFcopyin_derived_typeE.b.my_type.t_arr) : !fir.ref<!fir.array<2x1x!fir.type<_QM__fortran_type_infoTvalue{genre:i8,__padding0:!fir.array<7xi8>,value:i64}>>>
! CHECK: %[[VAL_0:.*]] = fir.address_of(@_QFcopyin_derived_typeE.b.my_type.t_arr) : !fir.ref<!fir.array<2x1x!fir.type<_QM__fortran_type_infoTvalue{{[<]?}}{genre:i8,__padding0:!fir.array<7xi8>,value:i64}{{[>]?}}>>>
! CHECK: %[[VAL_1:.*]] = arith.constant 0 : index
! CHECK: %[[VAL_2:.*]] = arith.constant 2 : index
! CHECK: %[[VAL_3:.*]] = arith.constant 0 : index

View File

@ -0,0 +1,44 @@
! Test padding for BIND(C) derived types lowering for AIX target
! RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s
! REQUIRES: target={{.+}}-aix{{.*}}
subroutine s1()
use, intrinsic :: iso_c_binding
type, bind(c) :: t0
character(c_char) :: x1
real(c_double) :: x2
end type
type(t0) :: xt0
! CHECK-DAG: %_QFs1Tt0 = type <{ [1 x i8], [3 x i8], double }>
type, bind(c) :: t1
integer(c_short) :: x1
real(c_double) :: x2
end type
type(t1) :: xt1
! CHECK-DAG: %_QFs1Tt1 = type <{ i16, [2 x i8], double }>
type, bind(c) :: t2
integer(c_short) :: x1
real(c_double) :: x2
character(c_char) :: x3
end type
type(t2) :: xt2
! CHECK-DAG: %_QFs1Tt2 = type <{ i16, [2 x i8], double, [1 x i8], [3 x i8] }>
type, bind(c) :: t3
character(c_char) :: x1
complex(c_double_complex) :: x2
end type
type(t3) :: xt3
! CHECK-DAG: %_QFs1Tt3 = type <{ [1 x i8], [3 x i8], { double, double } }>
type, bind(c) :: t4
integer(c_short) :: x1
complex(c_double_complex) :: x2
character(c_char) :: x3
end type
type(t4) :: xt4
! CHECK-DAG: %_QFs1Tt4 = type <{ i16, [2 x i8], { double, double }, [1 x i8], [3 x i8] }>
end subroutine s1

View File

@ -123,24 +123,24 @@ contains
! on the caller side.
! CHECK-LABEL: func.func @_QMmod1Psub4()
! FIR: %[[BOX:.*]] = fir.alloca !fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>> {bindc_name = "t", uniq_name = "_QMmod1Fsub4Et"}
! FIR: %[[BOX:.*]] = fir.alloca !fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>> {bindc_name = "t", uniq_name = "_QMmod1Fsub4Et"}
! HLFIR: %[[BOX:.*]]:2 = hlfir.declare {{.*}}"_QMmod1Fsub4Et"
! CHECK-NOT: fir.call @_FortranAAllocatableDeallocate
! CHECK: fir.call @_QMmod1Psub5(%[[BOX]]{{[#0]*}}) {{.*}}: (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>>) -> ()
! CHECK: fir.call @_QMmod1Psub5(%[[BOX]]{{[#0]*}}) {{.*}}: (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>>) -> ()
! Check deallocation of allocatble intent(out) on the callee side. Deallocation
! is done with a runtime call.
! CHECK-LABEL: func.func @_QMmod1Psub5(
! FIR-SAME: %[[ARG0:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>> {fir.bindc_name = "t"})
! FIR-SAME: %[[ARG0:.*]]: !fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>> {fir.bindc_name = "t"})
! HLFIR: %[[ARG0:.*]]:2 = hlfir.declare {{.*}}"_QMmod1Fsub5Et"
! CHECK: %[[BOX:.*]] = fir.load %[[ARG0]]{{[#1]*}} : !fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>>
! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>) -> !fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>
! CHECK: %[[BOX_ADDR_PTR:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>) -> i64
! CHECK: %[[BOX:.*]] = fir.load %[[ARG0]]{{[#1]*}} : !fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>>
! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>) -> !fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>
! CHECK: %[[BOX_ADDR_PTR:.*]] = fir.convert %[[BOX_ADDR]] : (!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>) -> i64
! CHECK: %[[C0:.*]] = arith.constant 0 : i64
! CHECK: %[[IS_ALLOCATED:.*]] = arith.cmpi ne, %[[BOX_ADDR_PTR]], %[[C0]] : i64
! CHECK: fir.if %[[IS_ALLOCATED]] {
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]]{{[#1]*}} : (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>>) -> !fir.ref<!fir.box<none>>
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]]{{[#1]*}} : (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>>) -> !fir.ref<!fir.box<none>>
! CHECK: %{{.*}} = fir.call @_FortranAAllocatableDeallocate(%[[BOX_NONE]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {{.*}}: (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
subroutine sub6()
@ -152,11 +152,11 @@ contains
! Deallocation is done with a runtime call.
! CHECK-LABEL: func.func @_QMmod1Psub6()
! FIR: %[[BOX:.*]] = fir.alloca !fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>> {bindc_name = "t", uniq_name = "_QMmod1Fsub6Et"}
! FIR: %[[BOX:.*]] = fir.alloca !fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>> {bindc_name = "t", uniq_name = "_QMmod1Fsub6Et"}
! HLFIR: %[[BOX:.*]]:2 = hlfir.declare {{.*}}"_QMmod1Fsub6Et"
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[BOX]]{{[#1]*}} : (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>>) -> !fir.ref<!fir.box<none>>
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[BOX]]{{[#1]*}} : (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>>) -> !fir.ref<!fir.box<none>>
! CHECK: %{{.*}} = fir.call @_FortranAAllocatableDeallocate(%[[BOX_NONE]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {{.*}}: (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK: fir.call @sub7(%[[BOX]]{{[#0]*}}) {{.*}}: (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{i:i32}>>>>) -> ()
! CHECK: fir.call @sub7(%[[BOX]]{{[#0]*}}) {{.*}}: (!fir.ref<!fir.box<!fir.heap<!fir.type<_QMmod1Tt1{{[<]?}}{i:i32}{{[>]?}}>>>>) -> ()
subroutine sub8()
integer, allocatable :: a(:)

View File

@ -0,0 +1,105 @@
!RUN: %flang_fc1 -fdebug-dump-symbols %s | FileCheck %s
!REQUIRES: target={{.+}}-aix{{.*}}
! Size and alignment of bind(c) derived types
subroutine s1()
use, intrinsic :: iso_c_binding
type, bind(c) :: dt1
character(c_char) :: x1 !CHECK: x1 size=1 offset=0:
real(c_double) :: x2 !CHECK: x2 size=8 offset=4:
end type
type, bind(c) :: dt2
character(c_char) :: x1(9) !CHECK: x1 size=9 offset=0:
real(c_double) :: x2 !CHECK: x2 size=8 offset=12:
end type
type, bind(c) :: dt3
integer(c_short) :: x1 !CHECK: x1 size=2 offset=0:
real(c_double) :: x2 !CHECK: x2 size=8 offset=4:
end type
type, bind(c) :: dt4
integer(c_int) :: x1 !CHECK: x1 size=4 offset=0:
real(c_double) :: x2 !CHECK: x2 size=8 offset=4:
end type
type, bind(c) :: dt5
real(c_double) :: x1 !CHECK: x1 size=8 offset=0:
real(c_double) :: x2 !CHECK: x2 size=8 offset=8:
end type
type, bind(c) :: dt6
integer(c_long) :: x1 !CHECK: x1 size=8 offset=0:
character(c_char) :: x2 !CHECK: x2 size=1 offset=8:
real(c_double) :: x3 !CHECK: x3 size=8 offset=12:
end type
type, bind(c) :: dt7
integer(c_long) :: x1 !CHECK: x1 size=8 offset=0:
integer(c_long) :: x2 !CHECK: x2 size=8 offset=8:
character(c_char) :: x3 !CHECK: x3 size=1 offset=16:
real(c_double) :: x4 !CHECK: x4 size=8 offset=20:
end type
type, bind(c) :: dt8
character(c_char) :: x1 !CHECK: x1 size=1 offset=0:
complex(c_double_complex) :: x2 !CHECK: x2 size=16 offset=4:
end type
end subroutine
subroutine s2()
use, intrinsic :: iso_c_binding
type, bind(c) :: dt10
character(c_char) :: x1
real(c_double) :: x2
end type
type, bind(c) :: dt11
type(dt10) :: y1 !CHECK: y1 size=12 offset=0:
real(c_double) :: y2 !CHECK: y2 size=8 offset=12:
end type
type, bind(c) :: dt12
character(c_char) :: y1 !CHECK: y1 size=1 offset=0:
type(dt10) :: y2 !CHECK: y2 size=12 offset=4:
character(c_char) :: y3 !CHECK: y3 size=1 offset=16:
end type
type, bind(c) :: dt13
integer(c_short) :: y1 !CHECK: y1 size=2 offset=0:
type(dt10) :: y2 !CHECK: y2 size=12 offset=4:
character(c_char) :: y3 !CHECK: y3 size=1 offset=16:
end type
type, bind(c) :: dt20
character(c_char) :: x1
integer(c_short) :: x2
end type
type, bind(c) :: dt21
real(c_double) :: y1 !CHECK: y1 size=8 offset=0:
type(dt20) :: y2 !CHECK: y2 size=4 offset=8:
real(c_double) :: y3 !CHECK: y3 size=8 offset=12:
end type
type, bind(c) :: dt30
character(c_char) :: x1
character(c_char) :: x2
end type
type, bind(c) :: dt31
integer(c_long) :: y1 !CHECK: y1 size=8 offset=0:
type(dt30) :: y2 !CHECK: y2 size=2 offset=8:
real(c_double) :: y3 !CHECK: y3 size=8 offset=12:
end type
type, bind(c) :: dt40
integer(c_short) :: x1
real(c_double) :: x2
end type
type, bind(c) :: dt41
real(c_double) :: y1 !CHECK: y1 size=8 offset=0:
type(dt40) :: y2 !CHECK: y2 size=12 offset=8:
real(c_double) :: y3 !CHECK: y3 size=8 offset=20:
end type
type, bind(c) :: dt50
integer(c_short) :: x1
complex(c_double_complex) :: x2
end type
type, bind(c) :: dt51
real(c_double) :: y1 !CHECK: y1 size=8 offset=0:
type(dt50) :: y2 !CHECK: y2 size=20 offset=8:
complex(c_double_complex) :: y3 !CHECK: y3 size=16 offset=28:
end type
end subroutine