Skip to content

[flang] Lower BIND(C) assumed length to CFI descriptor #65950

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 6 additions & 3 deletions flang/lib/Lower/CallInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ class Fortran::lower::CallInterfaceImpl {
[&](const Fortran::evaluate::characteristics::DummyDataObject
&dummy) {
const auto &entity = getDataObjectEntity(std::get<1>(pair));
if (dummy.CanBePassedViaImplicitInterface())
if (!isBindC && dummy.CanBePassedViaImplicitInterface())
handleImplicitDummy(&argCharacteristics, dummy, entity);
else
handleExplicitDummy(&argCharacteristics, dummy, entity,
Expand Down Expand Up @@ -871,7 +871,8 @@ class Fortran::lower::CallInterfaceImpl {

// Define when an explicit argument must be passed in a fir.box.
bool dummyRequiresBox(
const Fortran::evaluate::characteristics::DummyDataObject &obj) {
const Fortran::evaluate::characteristics::DummyDataObject &obj,
bool isBindC) {
using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attrs;
constexpr ShapeAttrs shapeRequiringBox = {
Expand All @@ -888,6 +889,8 @@ class Fortran::lower::CallInterfaceImpl {
if (const Fortran::semantics::Scope *scope = derived->scope())
// Need to pass length type parameters in fir.box if any.
return scope->IsDerivedTypeWithLengthParameter();
if (isBindC && obj.type.type().IsAssumedLengthCharacter())
return true; // Fortran 2018 18.3.6 point 2 (5)
return false;
}

Expand Down Expand Up @@ -973,7 +976,7 @@ class Fortran::lower::CallInterfaceImpl {
addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox,
attrs);
addPassedArg(PassEntityBy::MutableBox, entity, characteristics);
} else if (dummyRequiresBox(obj)) {
} else if (dummyRequiresBox(obj, isBindC)) {
// Pass as fir.box or fir.class
if (isValueAttr)
TODO(loc, "assumed shape dummy argument with VALUE attribute");
Expand Down
54 changes: 50 additions & 4 deletions flang/lib/Lower/ConvertVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1275,12 +1275,14 @@ static void instantiateCommon(Fortran::lower::AbstractConverter &converter,

/// Helper to decide if a dummy argument must be tracked in an BoxValue.
static bool lowerToBoxValue(const Fortran::semantics::Symbol &sym,
mlir::Value dummyArg) {
mlir::Value dummyArg,
Fortran::lower::AbstractConverter &converter) {
// Only dummy arguments coming as fir.box can be tracked in an BoxValue.
if (!dummyArg || !dummyArg.getType().isa<fir::BaseBoxType>())
return false;
// Non contiguous arrays must be tracked in an BoxValue.
if (sym.Rank() > 0 && !sym.attrs().test(Fortran::semantics::Attr::CONTIGUOUS))
if (sym.Rank() > 0 && !Fortran::evaluate::IsSimplyContiguous(
sym, converter.getFoldingContext()))
return true;
// Assumed rank and optional fir.box cannot yet be read while lowering the
// specifications.
Expand Down Expand Up @@ -1713,16 +1715,60 @@ void Fortran::lower::mapSymbolAttributes(

if (isDummy) {
mlir::Value dummyArg = symMap.lookupSymbol(sym).getAddr();
if (lowerToBoxValue(sym, dummyArg)) {
if (lowerToBoxValue(sym, dummyArg, converter)) {
llvm::SmallVector<mlir::Value> lbounds;
llvm::SmallVector<mlir::Value> explicitExtents;
llvm::SmallVector<mlir::Value> explicitParams;
// Lower lower bounds, explicit type parameters and explicit
// extents if any.
if (ba.isChar())
if (ba.isChar()) {
if (mlir::Value len =
lowerExplicitCharLen(converter, loc, ba, symMap, stmtCtx))
explicitParams.push_back(len);
if (sym.Rank() == 0) {
// Do not keep scalar characters as fir.box (even when optional).
// Lowering and FIR is not meant to deal with scalar characters as
// fir.box outside of calls.
auto boxTy = dummyArg.getType().dyn_cast<fir::BaseBoxType>();
mlir::Type refTy = builder.getRefType(boxTy.getEleTy());
mlir::Type lenType = builder.getCharacterLengthType();
mlir::Value addr, len;
if (Fortran::semantics::IsOptional(sym)) {
auto isPresent = builder.create<fir::IsPresentOp>(
loc, builder.getI1Type(), dummyArg);
auto addrAndLen =
builder
.genIfOp(loc, {refTy, lenType}, isPresent,
/*withElseRegion=*/true)
.genThen([&]() {
mlir::Value readAddr =
builder.create<fir::BoxAddrOp>(loc, refTy, dummyArg);
mlir::Value readLength =
charHelp.readLengthFromBox(dummyArg);
builder.create<fir::ResultOp>(
loc, mlir::ValueRange{readAddr, readLength});
})
.genElse([&] {
mlir::Value readAddr = builder.genAbsentOp(loc, refTy);
mlir::Value readLength =
fir::factory::createZeroValue(builder, loc, lenType);
builder.create<fir::ResultOp>(
loc, mlir::ValueRange{readAddr, readLength});
})
.getResults();
addr = addrAndLen[0];
len = addrAndLen[1];
} else {
addr = builder.create<fir::BoxAddrOp>(loc, refTy, dummyArg);
len = charHelp.readLengthFromBox(dummyArg);
}
if (!explicitParams.empty())
len = explicitParams[0];
::genDeclareSymbol(converter, symMap, sym, addr, len, /*extents=*/{},
/*lbounds=*/{}, replace);
return;
}
}
// TODO: derived type length parameters.
lowerExplicitLowerBounds(converter, loc, ba, lbounds, symMap, stmtCtx);
lowerExplicitExtents(converter, loc, ba, lbounds, explicitExtents, symMap,
Expand Down
5 changes: 0 additions & 5 deletions flang/lib/Optimizer/Builder/BoxValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,6 @@ bool fir::BoxValue::verify() const {
return false;
if (!lbounds.empty() && lbounds.size() != rank())
return false;
// Explicit extents are here to cover cases where an explicit-shape dummy
// argument comes as a fir.box. This can only happen with derived types and
// unlimited polymorphic.
if (!extents.empty() && !(isDerived() || isUnlimitedPolymorphic()))
return false;
if (!extents.empty() && extents.size() != rank())
return false;
if (isCharacter() && explicitParams.size() > 1)
Expand Down
41 changes: 41 additions & 0 deletions flang/test/Lower/HLFIR/bindc-assumed-length.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
! Test that assumed length character scalars and explicit shape arrays are passed via
! CFI descriptor (fir.box) in BIND(C) procedures. They are passed only by address
! and length in non BIND(C) procedures. See Fortran 2018 standard 18.3.6 point 2(5).
! RUN: bbc -hlfir -emit-fir -o - %s 2>&1 | FileCheck %s

module bindcchar
contains
! CHECK-LABEL: func.func @bindc(
! CHECK-SAME: %{{[^:]*}}: !fir.box<!fir.char<1,?>>
! CHECK-SAME: %{{[^:]*}}: !fir.box<!fir.array<100x!fir.char<1,?>>>
subroutine bindc(c1, c3) bind(c)
character(*) :: c1, c3(100)
print *, c1(1:3), c3(5)(1:3)
end subroutine

! CHECK-LABEL: func.func @bindc_optional(
! CHECK-SAME: %{{[^:]*}}: !fir.box<!fir.char<1,?>>
! CHECK-SAME: %{{[^:]*}}: !fir.box<!fir.array<100x!fir.char<1,?>>>
subroutine bindc_optional(c1, c3) bind(c)
character(*), optional :: c1, c3(100)
print *, c1(1:3), c3(5)(1:3)
end subroutine

! CHECK-LABEL: func.func @_QMbindccharPnot_bindc(
! CHECK-SAME: %{{[^:]*}}: !fir.boxchar<1>
! CHECK-SAME: %{{[^:]*}}: !fir.boxchar<1>
subroutine not_bindc(c1, c3)
character(*) :: c1, c3(100)
call bindc(c1, c3)
call bindc_optional(c1, c3)
end subroutine

! CHECK-LABEL: func.func @_QMbindccharPnot_bindc_optional(
! CHECK-SAME: %{{[^:]*}}: !fir.boxchar<1>
! CHECK-SAME: %{{[^:]*}}: !fir.boxchar<1>
subroutine not_bindc_optional(c1, c3)
character(*), optional :: c1, c3(100)
call bindc(c1, c3)
call bindc_optional(c1, c3)
end subroutine
end module