@@ -271,25 +271,6 @@ static fir::ExtendedValue arrayLoadExtValue(fir::FirOpBuilder &builder,
271
271
" properties are explicit, assumed, deferred, or ?" );
272
272
}
273
273
274
- // / Convert the result of a fir.array_modify to an ExtendedValue given the
275
- // / related fir.array_load.
276
- static fir::ExtendedValue arrayModifyToExv (fir::FirOpBuilder &builder,
277
- mlir::Location loc,
278
- fir::ArrayLoadOp load,
279
- mlir::Value elementAddr) {
280
- auto eleTy = fir::unwrapPassByRefType (elementAddr.getType ());
281
- if (fir::isa_char (eleTy)) {
282
- auto len = fir::factory::CharacterExprHelper{builder, loc}.getLength (
283
- load.memref ());
284
- if (!len) {
285
- assert (load.typeparams ().size () == 1 && " length must be in array_load" );
286
- len = load.typeparams ()[0 ];
287
- }
288
- return fir::CharBoxValue{elementAddr, len};
289
- }
290
- return elementAddr;
291
- }
292
-
293
274
// / Is this a call to an elemental procedure with at least one array argument ?
294
275
static bool
295
276
isElementalProcWithArrayArgs (const Fortran::evaluate::ProcedureRef &procRef) {
@@ -2488,6 +2469,25 @@ static void genScalarUserDefinedAssignmentCall(fir::FirOpBuilder &builder,
2488
2469
builder.create <fir::CallOp>(loc, func, mlir::ValueRange{lhsArg, rhsArg});
2489
2470
}
2490
2471
2472
+ // / Convert the result of a fir.array_modify to an ExtendedValue given the
2473
+ // / related fir.array_load.
2474
+ static fir::ExtendedValue arrayModifyToExv (fir::FirOpBuilder &builder,
2475
+ mlir::Location loc,
2476
+ fir::ArrayLoadOp load,
2477
+ mlir::Value elementAddr) {
2478
+ auto eleTy = fir::unwrapPassByRefType (elementAddr.getType ());
2479
+ if (fir::isa_char (eleTy)) {
2480
+ auto len = fir::factory::CharacterExprHelper{builder, loc}.getLength (
2481
+ load.memref ());
2482
+ if (!len) {
2483
+ assert (load.typeparams ().size () == 1 && " length must be in array_load" );
2484
+ len = load.typeparams ()[0 ];
2485
+ }
2486
+ return fir::CharBoxValue{elementAddr, len};
2487
+ }
2488
+ return elementAddr;
2489
+ }
2490
+
2491
2491
// ===----------------------------------------------------------------------===//
2492
2492
//
2493
2493
// Lowering of scalar expressions in an explicit iteration space context.
@@ -2540,7 +2540,8 @@ class ScalarArrayExprLowering {
2540
2540
// 3) Finalize the inner context.
2541
2541
expSpace.finalizeContext ();
2542
2542
// 4) Thread the array value updated forward. Note: the lhs might be
2543
- // ill-formed, in which case there is no array to thread.
2543
+ // ill-formed (performing scalar assignment in an array context),
2544
+ // in which case there is no array to thread.
2544
2545
if (auto updateOp = mlir::dyn_cast<fir::ArrayUpdateOp>(
2545
2546
fir::getBase (lexv).getDefiningOp ())) {
2546
2547
auto oldInnerArg = updateOp.sequence ();
@@ -2557,6 +2558,45 @@ class ScalarArrayExprLowering {
2557
2558
return lexv;
2558
2559
}
2559
2560
2561
+ ExtValue userAssign (mlir::FuncOp userAssignment,
2562
+ const Fortran::lower::SomeExpr &lhs,
2563
+ const Fortran::lower::SomeExpr &rhs) {
2564
+ auto loc = getLoc ();
2565
+ semant = ConstituentSemantics::RefTransparent;
2566
+ // 1) Lower the rhs expression with array_fetch op(s).
2567
+ auto rexv = lower (rhs);
2568
+ // 2) Lower the lhs expression to an array_modify.
2569
+ semant = ConstituentSemantics::CustomCopyInCopyOut;
2570
+ auto lexv = lower (lhs);
2571
+ bool isIllFormedLHS = false ;
2572
+ // 3) Insert the call
2573
+ if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
2574
+ fir::getBase (lexv).getDefiningOp ())) {
2575
+ auto oldInnerArg = modifyOp.sequence ();
2576
+ auto offset = expSpace.argPosition (oldInnerArg);
2577
+ expSpace.setInnerArg (offset, fir::getBase (lexv));
2578
+ auto exv =
2579
+ arrayModifyToExv (builder, loc, expSpace.getLhsLoad (0 ).getValue (),
2580
+ modifyOp.getResult (0 ));
2581
+ genScalarUserDefinedAssignmentCall (builder, loc, userAssignment, exv,
2582
+ rexv);
2583
+ } else {
2584
+ // LHS is ill formed, it is a scalar with no references to FORALL
2585
+ // subscripts, so there is actually no array assignment here. The user
2586
+ // code is probably bad, but still insert user assignment call since it
2587
+ // was not rejected by semantics (a warning was emitted).
2588
+ isIllFormedLHS = true ;
2589
+ genScalarUserDefinedAssignmentCall (builder, getLoc (), userAssignment,
2590
+ lexv, rexv);
2591
+ }
2592
+ // 4) Finalize the inner context.
2593
+ expSpace.finalizeContext ();
2594
+ // 5). Thread the array value updated forward.
2595
+ if (!isIllFormedLHS)
2596
+ builder.create <fir::ResultOp>(getLoc (), fir::getBase (lexv));
2597
+ return lexv;
2598
+ }
2599
+
2560
2600
private:
2561
2601
bool pathIsEmpty () { return reversePath.empty (); }
2562
2602
@@ -2605,9 +2645,9 @@ class ScalarArrayExprLowering {
2605
2645
ExtValue applyPathToArrayLoad (fir::ArrayLoadOp load) {
2606
2646
auto loc = getLoc ();
2607
2647
ExtValue result;
2648
+ auto path = lowerPath (load.getType ());
2608
2649
if (semant == ConstituentSemantics::ProjectedCopyInCopyOut) {
2609
2650
auto innerArg = expSpace.findArgumentOfLoad (load);
2610
- auto path = lowerPath (load.getType ());
2611
2651
auto eleTy = fir::applyPathToType (innerArg.getType (), path);
2612
2652
auto toTy = adjustedArrayElementType (eleTy);
2613
2653
auto castedElement = builder.createConvert (loc, toTy, elementalValue);
@@ -2618,8 +2658,22 @@ class ScalarArrayExprLowering {
2618
2658
update->setAttr (fir::factory::attrFortranArrayOffsets (),
2619
2659
builder.getUnitAttr ());
2620
2660
result = arrayLoadExtValue (builder, loc, load, {}, update);
2661
+ } else if (semant == ConstituentSemantics::CustomCopyInCopyOut) {
2662
+ // Create an array_modify to get the LHS element address and indicate
2663
+ // the assignment, and create the call to the user defined assignment.
2664
+ auto innerArg = expSpace.findArgumentOfLoad (load);
2665
+ auto eleTy = fir::applyPathToType (innerArg.getType (), path);
2666
+ auto refEleTy =
2667
+ fir::isa_ref_type (eleTy) ? eleTy : builder.getRefType (eleTy);
2668
+ auto arrModify = builder.create <fir::ArrayModifyOp>(
2669
+ loc, mlir::TypeRange{refEleTy, innerArg.getType ()}, innerArg, path,
2670
+ load.typeparams ());
2671
+ // Flag the offsets as "Fortran" as they are not zero-origin.
2672
+ arrModify->setAttr (fir::factory::attrFortranArrayOffsets (),
2673
+ builder.getUnitAttr ());
2674
+ result =
2675
+ arrayLoadExtValue (builder, loc, load, {}, arrModify.getResult (1 ));
2621
2676
} else {
2622
- auto path = lowerPath (load.getType ());
2623
2677
auto eleTy = fir::applyPathToType (load.getType (), path);
2624
2678
assert (eleTy && " path did not apply to type" );
2625
2679
auto resTy = adjustedArrayElementType (eleTy);
@@ -3715,13 +3769,16 @@ class ArrayExprLowering {
3715
3769
builder.restoreInsertionPoint (insPt);
3716
3770
}
3717
3771
3718
- static void lowerElementalUserAssignment (
3719
- Fortran::lower::AbstractConverter &converter,
3720
- Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3721
- const Fortran::evaluate::Expr<Fortran::evaluate::SomeType> &call) {
3772
+ static void
3773
+ lowerElementalUserAssignment (Fortran::lower::AbstractConverter &converter,
3774
+ Fortran::lower::SymMap &symMap,
3775
+ Fortran::lower::StatementContext &stmtCtx,
3776
+ Fortran::lower::ExplicitIterSpace &explicitSpace,
3777
+ Fortran::lower::ImplicitIterSpace &implicitSpace,
3778
+ const Fortran::evaluate::ProcedureRef &procRef) {
3722
3779
ArrayExprLowering ael (converter, stmtCtx, symMap,
3723
- ConstituentSemantics::CustomCopyInCopyOut);
3724
- auto procRef = std::get<Fortran::evaluate::ProcedureRef>(call. u );
3780
+ ConstituentSemantics::CustomCopyInCopyOut,
3781
+ &explicitSpace, &implicitSpace );
3725
3782
assert (procRef.arguments ().size () == 2 );
3726
3783
const auto *lhs = procRef.arguments ()[0 ].value ().UnwrapExpr ();
3727
3784
const auto *rhs = procRef.arguments ()[1 ].value ().UnwrapExpr ();
@@ -3752,9 +3809,12 @@ class ArrayExprLowering {
3752
3809
determineShapeOfDest (lhs);
3753
3810
semant = ConstituentSemantics::RefTransparent;
3754
3811
auto exv = lowerArrayExpression (rhs);
3755
- builder.create <fir::ArrayMergeStoreOp>(
3756
- loc, destination, fir::getBase (exv), destination.memref (),
3757
- destination.slice (), destination.typeparams ());
3812
+ if (explicitSpaceIsActive ())
3813
+ builder.create <fir::ResultOp>(loc, fir::getBase (exv));
3814
+ else
3815
+ builder.create <fir::ArrayMergeStoreOp>(
3816
+ loc, destination, fir::getBase (exv), destination.memref (),
3817
+ destination.slice (), destination.typeparams ());
3758
3818
}
3759
3819
3760
3820
// / Compute the shape of a slice.
@@ -5914,6 +5974,25 @@ class ArrayExprLowering {
5914
5974
return arrayLoadExtValue (builder, loc, load, {}, update);
5915
5975
};
5916
5976
}
5977
+ if (semant == ConstituentSemantics::CustomCopyInCopyOut) {
5978
+ // Create an array_modify to get the LHS element address and indicate
5979
+ // the assignment, and create the call to the user defined assignment.
5980
+ destination = load;
5981
+ auto innerArg = explicitSpace->findArgumentOfLoad (load);
5982
+ return [=](IterSpace iters) mutable {
5983
+ auto [path, eleTy] = lowerPath (loc, revPath, load.getType (), iters);
5984
+ auto refEleTy =
5985
+ fir::isa_ref_type (eleTy) ? eleTy : builder.getRefType (eleTy);
5986
+ auto arrModify = builder.create <fir::ArrayModifyOp>(
5987
+ loc, mlir::TypeRange{refEleTy, innerArg.getType ()}, innerArg, path,
5988
+ load.typeparams ());
5989
+ // Flag the offsets as "Fortran" as they are not zero-origin.
5990
+ arrModify->setAttr (fir::factory::attrFortranArrayOffsets (),
5991
+ builder.getUnitAttr ());
5992
+ return arrayLoadExtValue (builder, loc, load, {},
5993
+ arrModify.getResult (1 ));
5994
+ };
5995
+ }
5917
5996
return [=](IterSpace iters) mutable {
5918
5997
auto [path, eleTy] = lowerPath (loc, revPath, load.getType (), iters);
5919
5998
auto resTy = adjustedArrayElementType (eleTy);
@@ -6232,26 +6311,61 @@ fir::MutableBoxValue Fortran::lower::createMutableBox(
6232
6311
}
6233
6312
6234
6313
mlir::Value Fortran::lower::createSubroutineCall (
6235
- AbstractConverter &converter,
6236
- const evaluate::Expr<evaluate::SomeType> &call, SymMap &symMap ,
6237
- StatementContext &stmtCtx, bool isUserDefAssignment) {
6314
+ AbstractConverter &converter, const evaluate::ProcedureRef &call,
6315
+ ExplicitIterSpace &explicitIterSpace, ImplicitIterSpace &implicitIterSpace ,
6316
+ SymMap &symMap, StatementContext &stmtCtx, bool isUserDefAssignment) {
6238
6317
auto loc = converter.getCurrentLocation ();
6318
+
6319
+ if (isUserDefAssignment) {
6320
+ assert (call.arguments ().size () == 2 );
6321
+ const auto *lhs = call.arguments ()[0 ].value ().UnwrapExpr ();
6322
+ const auto *rhs = call.arguments ()[1 ].value ().UnwrapExpr ();
6323
+ assert (lhs && rhs &&
6324
+ " user defined assignment arguments must be expressions" );
6325
+ if (call.IsElemental () && lhs->Rank () > 0 ) {
6326
+ // Elemental user defined assignment has special requirements to deal with
6327
+ // LHS/RHS overlaps. See 10.2.1.5 p2.
6328
+ ArrayExprLowering::lowerElementalUserAssignment (
6329
+ converter, symMap, stmtCtx, explicitIterSpace, implicitIterSpace,
6330
+ call);
6331
+ } else if (explicitIterSpace.isActive () && lhs->Rank () == 0 ) {
6332
+ // Scalar defined assignment (elemental or not) in a FORALL context.
6333
+ auto func = Fortran::lower::CallerInterface (call, converter).getFuncOp ();
6334
+ ScalarArrayExprLowering sael (converter, symMap, explicitIterSpace,
6335
+ stmtCtx);
6336
+ sael.userAssign (func, *lhs, *rhs);
6337
+ } else if (explicitIterSpace.isActive ()) {
6338
+ // TODO: need to array fetch/modify sub-arrays ?
6339
+ TODO (loc, " non elemental user defined array assignment inside FORALL" );
6340
+ } else {
6341
+ if (!implicitIterSpace.empty ())
6342
+ fir::emitFatalError (
6343
+ loc,
6344
+ " C1032: user defined assignment inside WHERE must be elemental" );
6345
+ // Non elemental user defined assignment outside of FORALL and WHERE.
6346
+ // FIXME: The non elemental user defined assignment case with array
6347
+ // arguments must be take into account potential overlap. So far the front
6348
+ // end does not add parentheses around the RHS argument in the call as it
6349
+ // should according to 15.4.3.4.3 p2.
6350
+ Fortran::semantics::SomeExpr expr{call};
6351
+ Fortran::lower::createSomeExtendedExpression (loc, converter, expr, symMap,
6352
+ stmtCtx);
6353
+ }
6354
+ return {};
6355
+ }
6356
+
6357
+ assert (implicitIterSpace.empty () && !explicitIterSpace.isActive () &&
6358
+ " subroutine calls are not allowed inside WHERE and FORALL" );
6359
+
6239
6360
if (isElementalProcWithArrayArgs (call)) {
6240
- // Elemental user defined assignment has special requirements to deal with
6241
- // LHS/RHS overlaps. See 10.2.1.5 p2.
6242
- if (isUserDefAssignment)
6243
- ArrayExprLowering::lowerElementalUserAssignment (converter, symMap,
6244
- stmtCtx, call);
6245
- else
6246
- ArrayExprLowering::lowerArrayElementalSubroutine (converter, symMap,
6247
- stmtCtx, call);
6248
- return mlir::Value{};
6361
+ Fortran::semantics::SomeExpr expr{call};
6362
+ ArrayExprLowering::lowerArrayElementalSubroutine (converter, symMap, stmtCtx,
6363
+ expr);
6364
+ return {};
6249
6365
}
6250
- // FIXME: The non elemental user defined assignment case with array arguments
6251
- // must be take into account potential overlap. So far the front end does not
6252
- // add parentheses around the RHS argument in the call as it should according
6253
- // to 15.4.3.4.3 p2.
6254
- auto res = Fortran::lower::createSomeExtendedExpression (loc, converter, call,
6366
+ // Simple subroutine call, with potential alternate return.
6367
+ Fortran::semantics::SomeExpr expr{call};
6368
+ auto res = Fortran::lower::createSomeExtendedExpression (loc, converter, expr,
6255
6369
symMap, stmtCtx);
6256
6370
return fir::getBase (res);
6257
6371
}
0 commit comments