@@ -221,6 +221,25 @@ bool hlfir::Entity::mayHaveNonDefaultLowerBounds() const {
221221 return true ;
222222}
223223
224+ mlir::Operation *traverseConverts (mlir::Operation *op) {
225+ while (auto convert = llvm::dyn_cast_or_null<fir::ConvertOp>(op))
226+ op = convert.getValue ().getDefiningOp ();
227+ return op;
228+ }
229+
230+ bool hlfir::Entity::mayBeOptional () const {
231+ if (!isVariable ())
232+ return false ;
233+ // TODO: introduce a fir type to better identify optionals.
234+ if (mlir::Operation *op = traverseConverts (getDefiningOp ())) {
235+ if (auto varIface = llvm::dyn_cast<fir::FortranVariableOpInterface>(op))
236+ return varIface.isOptional ();
237+ return !llvm::isa<fir::AllocaOp, fir::AllocMemOp, fir::ReboxOp,
238+ fir::EmboxOp, fir::LoadOp>(op);
239+ }
240+ return true ;
241+ }
242+
224243fir::FortranVariableOpInterface
225244hlfir::genDeclare (mlir::Location loc, fir::FirOpBuilder &builder,
226245 const fir::ExtendedValue &exv, llvm::StringRef name,
@@ -963,9 +982,69 @@ llvm::SmallVector<mlir::Value> hlfir::genLoopNestWithReductions(
963982 return outerLoop->getResults ();
964983}
965984
985+ template <typename Lambda>
986+ static fir::ExtendedValue
987+ conditionallyEvaluate (mlir::Location loc, fir::FirOpBuilder &builder,
988+ mlir::Value condition, const Lambda &genIfTrue) {
989+ mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint ();
990+
991+ // Evaluate in some region that will be moved into the actual ifOp (the actual
992+ // ifOp can only be created when the result types are known).
993+ auto badIfOp = builder.create <fir::IfOp>(loc, condition.getType (), condition,
994+ /* withElseRegion=*/ false );
995+ mlir::Block *preparationBlock = &badIfOp.getThenRegion ().front ();
996+ builder.setInsertionPointToStart (preparationBlock);
997+ fir::ExtendedValue result = genIfTrue ();
998+ fir::ResultOp resultOp = result.match (
999+ [&](const fir::CharBoxValue &box) -> fir::ResultOp {
1000+ return builder.create <fir::ResultOp>(
1001+ loc, mlir::ValueRange{box.getAddr (), box.getLen ()});
1002+ },
1003+ [&](const mlir::Value &addr) -> fir::ResultOp {
1004+ return builder.create <fir::ResultOp>(loc, addr);
1005+ },
1006+ [&](const auto &) -> fir::ResultOp {
1007+ TODO (loc, " unboxing non scalar optional fir.box" );
1008+ });
1009+ builder.restoreInsertionPoint (insertPt);
1010+
1011+ // Create actual fir.if operation.
1012+ auto ifOp =
1013+ builder.create <fir::IfOp>(loc, resultOp->getOperandTypes (), condition,
1014+ /* withElseRegion=*/ true );
1015+ // Move evaluation into Then block,
1016+ preparationBlock->moveBefore (&ifOp.getThenRegion ().back ());
1017+ ifOp.getThenRegion ().back ().erase ();
1018+ // Create absent result in the Else block.
1019+ builder.setInsertionPointToStart (&ifOp.getElseRegion ().front ());
1020+ llvm::SmallVector<mlir::Value> absentValues;
1021+ for (mlir::Type resTy : ifOp->getResultTypes ()) {
1022+ if (fir::isa_ref_type (resTy) || fir::isa_box_type (resTy))
1023+ absentValues.emplace_back (builder.create <fir::AbsentOp>(loc, resTy));
1024+ else
1025+ absentValues.emplace_back (builder.create <fir::ZeroOp>(loc, resTy));
1026+ }
1027+ builder.create <fir::ResultOp>(loc, absentValues);
1028+ badIfOp->erase ();
1029+
1030+ // Build fir::ExtendedValue from the result values.
1031+ builder.setInsertionPointAfter (ifOp);
1032+ return result.match (
1033+ [&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
1034+ return fir::CharBoxValue{ifOp.getResult (0 ), ifOp.getResult (1 )};
1035+ },
1036+ [&](const mlir::Value &) -> fir::ExtendedValue {
1037+ return ifOp.getResult (0 );
1038+ },
1039+ [&](const auto &) -> fir::ExtendedValue {
1040+ TODO (loc, " unboxing non scalar optional fir.box" );
1041+ });
1042+ }
1043+
9661044static fir::ExtendedValue translateVariableToExtendedValue (
9671045 mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity variable,
968- bool forceHlfirBase = false , bool contiguousHint = false ) {
1046+ bool forceHlfirBase = false , bool contiguousHint = false ,
1047+ bool keepScalarOptionalBoxed = false ) {
9691048 assert (variable.isVariable () && " must be a variable" );
9701049 // When going towards FIR, use the original base value to avoid
9711050 // introducing descriptors at runtime when they are not required.
@@ -984,14 +1063,33 @@ static fir::ExtendedValue translateVariableToExtendedValue(
9841063 const bool contiguous = variable.isSimplyContiguous () || contiguousHint;
9851064 const bool isAssumedRank = variable.isAssumedRank ();
9861065 if (!contiguous || variable.isPolymorphic () ||
987- variable.isDerivedWithLengthParameters () || variable.isOptional () ||
988- isAssumedRank) {
1066+ variable.isDerivedWithLengthParameters () || isAssumedRank) {
9891067 llvm::SmallVector<mlir::Value> nonDefaultLbounds;
9901068 if (!isAssumedRank)
9911069 nonDefaultLbounds = getNonDefaultLowerBounds (loc, builder, variable);
9921070 return fir::BoxValue (base, nonDefaultLbounds,
9931071 getExplicitTypeParams (variable));
9941072 }
1073+ if (variable.mayBeOptional ()) {
1074+ if (!keepScalarOptionalBoxed && variable.isScalar ()) {
1075+ mlir::Value isPresent = builder.create <fir::IsPresentOp>(
1076+ loc, builder.getI1Type (), variable);
1077+ return conditionallyEvaluate (
1078+ loc, builder, isPresent, [&]() -> fir::ExtendedValue {
1079+ mlir::Value base = genVariableRawAddress (loc, builder, variable);
1080+ if (variable.isCharacter ()) {
1081+ mlir::Value len =
1082+ genCharacterVariableLength (loc, builder, variable);
1083+ return fir::CharBoxValue{base, len};
1084+ }
1085+ return base;
1086+ });
1087+ }
1088+ llvm::SmallVector<mlir::Value> nonDefaultLbounds =
1089+ getNonDefaultLowerBounds (loc, builder, variable);
1090+ return fir::BoxValue (base, nonDefaultLbounds,
1091+ getExplicitTypeParams (variable));
1092+ }
9951093 // Otherwise, the variable can be represented in a fir::ExtendedValue
9961094 // without the overhead of a fir.box.
9971095 base = genVariableRawAddress (loc, builder, variable);
@@ -1035,10 +1133,12 @@ hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
10351133
10361134std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
10371135hlfir::translateToExtendedValue (mlir::Location loc, fir::FirOpBuilder &builder,
1038- hlfir::Entity entity, bool contiguousHint) {
1136+ hlfir::Entity entity, bool contiguousHint,
1137+ bool keepScalarOptionalBoxed) {
10391138 if (entity.isVariable ())
10401139 return {translateVariableToExtendedValue (loc, builder, entity, false ,
1041- contiguousHint),
1140+ contiguousHint,
1141+ keepScalarOptionalBoxed),
10421142 std::nullopt };
10431143
10441144 if (entity.isProcedure ()) {
@@ -1094,7 +1194,9 @@ hlfir::convertToBox(mlir::Location loc, fir::FirOpBuilder &builder,
10941194 if (entity.isProcedurePointer ())
10951195 entity = hlfir::derefPointersAndAllocatables (loc, builder, entity);
10961196
1097- auto [exv, cleanup] = translateToExtendedValue (loc, builder, entity);
1197+ auto [exv, cleanup] =
1198+ translateToExtendedValue (loc, builder, entity, /* contiguousHint=*/ false ,
1199+ /* keepScalarOptionalBoxed=*/ true );
10981200 // Procedure entities should not go through createBoxValue that embox
10991201 // object entities. Return the fir.boxproc directly.
11001202 if (entity.isProcedure ())
0 commit comments