diff --git a/flang/include/flang/Lower/CustomIntrinsicCall.h b/flang/include/flang/Lower/CustomIntrinsicCall.h --- a/flang/include/flang/Lower/CustomIntrinsicCall.h +++ b/flang/include/flang/Lower/CustomIntrinsicCall.h @@ -25,6 +25,7 @@ #define FORTRAN_LOWER_CUSTOMINTRINSICCALL_H #include "flang/Lower/AbstractConverter.h" +#include "flang/Optimizer/Builder/IntrinsicCall.h" #include #include @@ -48,6 +49,8 @@ /// Type of callback to be provided to prepare the arguments fetching from an /// actual argument expression. using OperandPrepare = std::function; +using OperandPrepareAs = std::function; /// Type of the callback to inquire about an argument presence, once the call /// preparation was done. An absent optional means the argument is statically @@ -61,8 +64,9 @@ /// verified. This means the getter callback can dereference the argument /// without any special care. /// For elemental intrinsics, the getter must provide the current iteration -/// element value. -using OperandGetter = std::function; +/// element value. If the boolean argument is true, the callback must load the +/// argument before returning it. +using OperandGetter = std::function; /// Given a callback \p prepareOptionalArgument to prepare optional /// arguments and a callback \p prepareOtherArgument to prepare non-optional @@ -78,7 +82,7 @@ const Fortran::evaluate::SpecificIntrinsic &intrinsic, std::optional retTy, const OperandPrepare &prepareOptionalArgument, - const OperandPrepare &prepareOtherArgument, AbstractConverter &converter); + const OperandPrepareAs &prepareOtherArgument, AbstractConverter &converter); /// Given a callback \p getOperand to generate a reference to the i-th argument, /// and a callback \p isPresentCheck to test if an argument is present, this diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp --- a/flang/lib/Lower/ConvertExpr.cpp +++ b/flang/lib/Lower/ConvertExpr.cpp @@ -1795,16 +1795,32 @@ genActualIsPresentTest(builder, loc, optionalArg); operands.emplace_back(optionalArg, isPresent); }; - auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) { - operands.emplace_back(genval(expr), std::nullopt); + auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr, + fir::LowerIntrinsicArgAs lowerAs) { + switch (lowerAs) { + case fir::LowerIntrinsicArgAs::Value: + operands.emplace_back(genval(expr), std::nullopt); + return; + case fir::LowerIntrinsicArgAs::Addr: + operands.emplace_back(gen(expr), std::nullopt); + return; + case fir::LowerIntrinsicArgAs::Box: + operands.emplace_back(lowerIntrinsicArgumentAsBox(expr), + std::nullopt); + return; + case fir::LowerIntrinsicArgAs::Inquired: + operands.emplace_back(lowerIntrinsicArgumentAsInquired(expr), + std::nullopt); + return; + } }; Fortran::lower::prepareCustomIntrinsicArgument( procRef, *intrinsic, resultType, prepareOptionalArg, prepareOtherArg, converter); - auto getArgument = [&](std::size_t i) -> ExtValue { - if (fir::conformsWithPassByRef( - fir::getBase(operands[i].first).getType())) + auto getArgument = [&](std::size_t i, bool loadArg) -> ExtValue { + if (loadArg && fir::conformsWithPassByRef( + fir::getBase(operands[i].first).getType())) return genLoad(operands[i].first); return operands[i].first; }; @@ -4503,7 +4519,10 @@ operands.emplace_back(cc, isPresent); } }; - auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) { + auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr, + fir::LowerIntrinsicArgAs lowerAs) { + assert(lowerAs == fir::LowerIntrinsicArgAs::Value && + "expect value arguments for elemental intrinsic"); PushSemantics(ConstituentSemantics::RefTransparent); operands.emplace_back(genElementalArgument(expr), std::nullopt); }; @@ -4513,7 +4532,7 @@ fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); return [=](IterSpace iters) -> ExtValue { - auto getArgument = [&](std::size_t i) -> ExtValue { + auto getArgument = [&](std::size_t i, bool) -> ExtValue { return operands[i].first(iters); }; auto isPresent = [&](std::size_t i) -> std::optional { diff --git a/flang/lib/Lower/CustomIntrinsicCall.cpp b/flang/lib/Lower/CustomIntrinsicCall.cpp --- a/flang/lib/Lower/CustomIntrinsicCall.cpp +++ b/flang/lib/Lower/CustomIntrinsicCall.cpp @@ -17,6 +17,7 @@ #include "flang/Lower/StatementContext.h" #include "flang/Optimizer/Builder/IntrinsicCall.h" #include "flang/Optimizer/Builder/Todo.h" +#include "flang/Semantics/tools.h" #include /// Is this a call to MIN or MAX intrinsic with arguments that may be absent at @@ -54,6 +55,30 @@ Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContext); } +/// Is this a call to ASSOCIATED where the TARGET is an OPTIONAL (but not a +/// deallocated allocatable or disassociated pointer)? +/// Subtle: contrary to other intrinsic optional arguments, disassociated +/// POINTER and unallocated ALLOCATABLE actual argument are not considered +/// absent here. This is because ASSOCIATED has special requirements for TARGET +/// actual arguments that are POINTERs. There is no precise requirements for +/// ALLOCATABLEs, but all existing Fortran compilers treat them similarly to +/// POINTERs. That is: unallocated TARGETs cause ASSOCIATED to rerun false. The +/// runtime deals with the disassociated/unallocated case. Simply ensures that +/// TARGET that are OPTIONAL get conditionally emboxed here to convey the +/// optional aspect to the runtime. +static bool isAssociatedWithDynamicallyOptionalArg( + llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef, + Fortran::evaluate::FoldingContext &foldingContext) { + if (name != "associated" || procRef.arguments().size() < 2) + return false; + auto *expr = Fortran::evaluate::UnwrapExpr( + procRef.arguments()[1]); + const Fortran::semantics::Symbol *sym{ + expr ? Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr) + : nullptr}; + return (sym && Fortran::semantics::IsOptional(*sym)); +} + bool Fortran::lower::intrinsicRequiresCustomOptionalHandling( const Fortran::evaluate::ProcedureRef &procRef, const Fortran::evaluate::SpecificIntrinsic &intrinsic, @@ -61,7 +86,8 @@ llvm::StringRef name = intrinsic.name; Fortran::evaluate::FoldingContext &fldCtx = converter.getFoldingContext(); return isMinOrMaxWithDynamicallyOptionalArg(name, procRef, fldCtx) || - isIshftcWithDynamicallyOptionalArg(name, procRef, fldCtx); + isIshftcWithDynamicallyOptionalArg(name, procRef, fldCtx) || + isAssociatedWithDynamicallyOptionalArg(name, procRef, fldCtx); } /// Generate the FIR+MLIR operations for the generic intrinsic \p name @@ -91,7 +117,7 @@ const Fortran::evaluate::SpecificIntrinsic &intrinsic, std::optional retTy, const Fortran::lower::OperandPrepare &prepareOptionalArgument, - const Fortran::lower::OperandPrepare &prepareOtherArgument, + const Fortran::lower::OperandPrepareAs &prepareOtherArgument, Fortran::lower::AbstractConverter &converter) { assert(retTy && "MIN and MAX must have a return type"); mlir::Type resultType = *retTy; @@ -106,7 +132,7 @@ if (arg.index() <= 1 || !Fortran::evaluate::MayBePassedAsAbsentOptional( *expr, converter.getFoldingContext())) { // Non optional arguments. - prepareOtherArgument(*expr); + prepareOtherArgument(*expr, fir::LowerIntrinsicArgAs::Value); } else { // Dynamically optional arguments. // Subtle: even for scalar the if-then-else will be generated in the loop @@ -129,8 +155,9 @@ assert(retTy && "MIN and MAX must have a return type"); mlir::Type resultType = *retTy; llvm::SmallVector args; - args.push_back(getOperand(0)); - args.push_back(getOperand(1)); + const bool loadOperand = true; + args.push_back(getOperand(0, loadOperand)); + args.push_back(getOperand(1, loadOperand)); mlir::Value extremum = fir::getBase( genIntrinsicCall(builder, loc, name, resultType, args, stmtCtx)); @@ -145,7 +172,7 @@ .genThen([&]() { llvm::SmallVector args; args.emplace_back(extremum); - args.emplace_back(getOperand(opIndex)); + args.emplace_back(getOperand(opIndex, loadOperand)); fir::ExtendedValue newExtremum = genIntrinsicCall( builder, loc, name, resultType, args, stmtCtx); builder.create(loc, fir::getBase(newExtremum)); @@ -156,7 +183,7 @@ // Argument is know to be present at compile time. llvm::SmallVector args; args.emplace_back(extremum); - args.emplace_back(getOperand(opIndex)); + args.emplace_back(getOperand(opIndex, loadOperand)); extremum = fir::getBase( genIntrinsicCall(builder, loc, name, resultType, args, stmtCtx)); } @@ -169,7 +196,7 @@ const Fortran::evaluate::SpecificIntrinsic &intrinsic, std::optional retTy, const Fortran::lower::OperandPrepare &prepareOptionalArgument, - const Fortran::lower::OperandPrepare &prepareOtherArgument, + const Fortran::lower::OperandPrepareAs &prepareOtherArgument, Fortran::lower::AbstractConverter &converter) { for (auto arg : llvm::enumerate(procRef.arguments())) { const auto *expr = @@ -182,7 +209,7 @@ prepareOptionalArgument(*expr); } else { // Non optional arguments. - prepareOtherArgument(*expr); + prepareOtherArgument(*expr, fir::LowerIntrinsicArgAs::Value); } } } @@ -200,15 +227,16 @@ assert(retTy && "ISFHTC must have a return type"); mlir::Type resultType = *retTy; llvm::SmallVector args; - args.push_back(getOperand(0)); - args.push_back(getOperand(1)); + const bool loadOperand = true; + args.push_back(getOperand(0, loadOperand)); + args.push_back(getOperand(1, loadOperand)); auto iPC = isPresentCheck(2); assert(iPC.has_value()); args.push_back(builder .genIfOp(loc, {resultType}, *iPC, /*withElseRegion=*/true) .genThen([&]() { - fir::ExtendedValue sizeExv = getOperand(2); + fir::ExtendedValue sizeExv = getOperand(2, loadOperand); mlir::Value size = builder.createConvert( loc, resultType, fir::getBase(sizeExv)); builder.create(loc, size); @@ -223,17 +251,79 @@ return genIntrinsicCall(builder, loc, name, resultType, args, stmtCtx); } +static void prepareAssociatedArguments( + const Fortran::evaluate::ProcedureRef &procRef, + const Fortran::evaluate::SpecificIntrinsic &intrinsic, + std::optional retTy, + const Fortran::lower::OperandPrepare &prepareOptionalArgument, + const Fortran::lower::OperandPrepareAs &prepareOtherArgument, + Fortran::lower::AbstractConverter &converter) { + const auto *pointer = procRef.UnwrapArgExpr(0); + const auto *optionalTarget = procRef.UnwrapArgExpr(1); + assert(pointer && optionalTarget && + "expected call to associated with a target"); + prepareOtherArgument(*pointer, fir::LowerIntrinsicArgAs::Inquired); + prepareOptionalArgument(*optionalTarget); +} + +static fir::ExtendedValue +lowerAssociated(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef name, std::optional resultType, + const Fortran::lower::OperandPresent &isPresentCheck, + const Fortran::lower::OperandGetter &getOperand, + std::size_t numOperands, + Fortran::lower::StatementContext &stmtCtx) { + assert(numOperands == 2 && "expect two arguments when TARGET is OPTIONAL"); + llvm::SmallVector args; + args.push_back(getOperand(0, /*loadOperand=*/false)); + // Ensure a null descriptor is passed to the code lowering Associated if + // TARGET is absent. + fir::ExtendedValue targetExv = getOperand(1, /*loadOperand=*/false); + mlir::Value targetBase = fir::getBase(targetExv); + // subtle: isPresentCheck would test for an unallocated/disassociated target, + // while the optionality of the target pointer/allocatable is what must be + // checked here. + mlir::Value isPresent = + builder.create(loc, builder.getI1Type(), targetBase); + mlir::Type targetType = fir::unwrapRefType(targetBase.getType()); + mlir::Type targetValueType = fir::unwrapPassByRefType(targetType); + mlir::Type boxType = targetType.isa() + ? targetType + : fir::BoxType::get(targetValueType); + fir::BoxValue targetBox = + builder + .genIfOp(loc, {boxType}, isPresent, + /*withElseRegion=*/true) + .genThen([&]() { + mlir::Value box = builder.createBox(loc, targetExv); + mlir::Value cast = builder.createConvert(loc, boxType, box); + builder.create(loc, cast); + }) + .genElse([&]() { + mlir::Value absentBox = builder.create(loc, boxType); + builder.create(loc, absentBox); + }) + .getResults()[0]; + args.emplace_back(std::move(targetBox)); + return genIntrinsicCall(builder, loc, name, resultType, args, stmtCtx); +} + void Fortran::lower::prepareCustomIntrinsicArgument( const Fortran::evaluate::ProcedureRef &procRef, const Fortran::evaluate::SpecificIntrinsic &intrinsic, std::optional retTy, const OperandPrepare &prepareOptionalArgument, - const OperandPrepare &prepareOtherArgument, AbstractConverter &converter) { + const OperandPrepareAs &prepareOtherArgument, + AbstractConverter &converter) { llvm::StringRef name = intrinsic.name; if (name == "min" || name == "max") return prepareMinOrMaxArguments(procRef, intrinsic, retTy, prepareOptionalArgument, prepareOtherArgument, converter); + if (name == "associated") + return prepareAssociatedArguments(procRef, intrinsic, retTy, + prepareOptionalArgument, + prepareOtherArgument, converter); assert(name == "ishftc" && "unexpected custom intrinsic argument call"); return prepareIshftcArguments(procRef, intrinsic, retTy, prepareOptionalArgument, prepareOtherArgument, @@ -248,6 +338,9 @@ if (name == "min" || name == "max") return lowerMinOrMax(builder, loc, name, retTy, isPresentCheck, getOperand, numOperands, stmtCtx); + if (name == "associated") + return lowerAssociated(builder, loc, name, retTy, isPresentCheck, + getOperand, numOperands, stmtCtx); assert(name == "ishftc" && "unexpected custom intrinsic call"); return lowerIshftc(builder, loc, name, retTy, isPresentCheck, getOperand, numOperands, stmtCtx); diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -2418,40 +2418,7 @@ const fir::ExtendedValue &target = args[1]; if (isStaticallyAbsent(target)) return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer); - - mlir::Value targetBox; - if (fir::valueHasFirAttribute(fir::getBase(target), - fir::getOptionalAttrName())) { - // Subtle: contrary to other intrinsic optional arguments, disassociated - // POINTER and unallocated ALLOCATABLE actual argument are not considered - // absent here. This is because ASSOCIATED has special requirements for - // TARGET actual arguments that are POINTERs. There is no precise - // requirements for ALLOCATABLEs, but all existing Fortran compilers treat - // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED - // to rerun false. The runtime deals with the disassociated/unallocated - // case. Simply ensures that TARGET that are OPTIONAL get conditionally - // emboxed here to convey the optional aspect to the runtime. - mlir::Type boxType = fir::BoxType::get(builder.getNoneType()); - auto isPresent = builder.create(loc, builder.getI1Type(), - fir::getBase(target)); - targetBox = builder - .genIfOp(loc, {boxType}, isPresent, - /*withElseRegion=*/true) - .genThen([&]() { - mlir::Value box = builder.createBox(loc, target); - mlir::Value cast = - builder.createConvert(loc, boxType, box); - builder.create(loc, cast); - }) - .genElse([&]() { - mlir::Value absentBox = - builder.create(loc, boxType); - builder.create(loc, absentBox); - }) - .getResults()[0]; - } else { - targetBox = builder.createBox(loc, target); - } + mlir::Value targetBox = builder.createBox(loc, target); mlir::Value pointerBoxRef = fir::factory::getMutableIRBox(builder, loc, *pointer); auto pointerBox = builder.create(loc, pointerBoxRef); diff --git a/flang/test/Lower/Intrinsics/associated.f90 b/flang/test/Lower/Intrinsics/associated.f90 --- a/flang/test/Lower/Intrinsics/associated.f90 +++ b/flang/test/Lower/Intrinsics/associated.f90 @@ -48,18 +48,18 @@ print *, associated(p, optionales_ziel) ! CHECK: %[[VAL_2:.*]] = arith.constant 10 : index ! CHECK: %[[VAL_3:.*]] = fir.is_present %[[VAL_1]] : (!fir.ref>) -> i1 - ! CHECK: %[[VAL_4:.*]] = fir.if %[[VAL_3]] -> (!fir.box) { + ! CHECK: %[[VAL_4:.*]] = fir.if %[[VAL_3]] -> (!fir.box>) { ! CHECK: %[[VAL_5:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1> ! CHECK: %[[VAL_6:.*]] = fir.embox %[[VAL_1]](%[[VAL_5]]) : (!fir.ref>, !fir.shape<1>) -> !fir.box> - ! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (!fir.box>) -> !fir.box - ! CHECK: fir.result %[[VAL_7]] : !fir.box + ! CHECK: fir.result %[[VAL_6]] : !fir.box> ! CHECK: } else { - ! CHECK: %[[VAL_8:.*]] = fir.absent !fir.box - ! CHECK: fir.result %[[VAL_8]] : !fir.box + ! CHECK: %[[VAL_8:.*]] = fir.absent !fir.box> + ! CHECK: fir.result %[[VAL_8]] : !fir.box> ! CHECK: } ! CHECK: %[[VAL_13:.*]] = fir.load %[[VAL_0]] : !fir.ref>>> ! CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_13]] : (!fir.box>>) -> !fir.box - ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_14]], %[[VAL_4]]) {{.*}}: (!fir.box, !fir.box) -> i1 + ! CHECK: %[[VAL_15:.*]] = fir.convert %[[VAL_4]] : (!fir.box>) -> !fir.box + ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_14]], %[[VAL_15]]) {{.*}}: (!fir.box, !fir.box) -> i1 end subroutine ! CHECK-LABEL: func @_QPtest_optional_target_2( @@ -70,16 +70,16 @@ real, optional, target :: optionales_ziel(:) print *, associated(p, optionales_ziel) ! CHECK: %[[VAL_7:.*]] = fir.is_present %[[VAL_1]] : (!fir.box>) -> i1 - ! CHECK: %[[VAL_8:.*]] = fir.if %[[VAL_7]] -> (!fir.box) { - ! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_1]] : (!fir.box>) -> !fir.box - ! CHECK: fir.result %[[VAL_9]] : !fir.box + ! CHECK: %[[VAL_8:.*]] = fir.if %[[VAL_7]] -> (!fir.box>) { + ! CHECK: fir.result %[[VAL_1]] : !fir.box> ! CHECK: } else { - ! CHECK: %[[VAL_10:.*]] = fir.absent !fir.box - ! CHECK: fir.result %[[VAL_10]] : !fir.box + ! CHECK: %[[VAL_10:.*]] = fir.absent !fir.box> + ! CHECK: fir.result %[[VAL_10]] : !fir.box> ! CHECK: } ! CHECK: %[[VAL_10:.*]] = fir.load %[[VAL_0]] : !fir.ref>>> ! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.box>>) -> !fir.box - ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_11]], %[[VAL_8]]) {{.*}}: (!fir.box, !fir.box) -> i1 + ! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_8]] : (!fir.box>) -> !fir.box + ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_11]], %[[VAL_12]]) {{.*}}: (!fir.box, !fir.box) -> i1 end subroutine ! CHECK-LABEL: func @_QPtest_optional_target_3( @@ -90,17 +90,17 @@ real, optional, pointer :: optionales_ziel(:) print *, associated(p, optionales_ziel) ! CHECK: %[[VAL_8:.*]] = fir.is_present %[[VAL_1]] : (!fir.ref>>>) -> i1 - ! CHECK: %[[VAL_9:.*]] = fir.if %[[VAL_8]] -> (!fir.box) { + ! CHECK: %[[VAL_9:.*]] = fir.if %[[VAL_8]] -> (!fir.box>>) { ! CHECK: %[[VAL_10:.*]] = fir.load %[[VAL_1]] : !fir.ref>>> - ! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.box>>) -> !fir.box - ! CHECK: fir.result %[[VAL_11]] : !fir.box + ! CHECK: fir.result %[[VAL_10]] : !fir.box>> ! CHECK: } else { - ! CHECK: %[[VAL_12:.*]] = fir.absent !fir.box - ! CHECK: fir.result %[[VAL_12]] : !fir.box + ! CHECK: %[[VAL_12:.*]] = fir.absent !fir.box>> + ! CHECK: fir.result %[[VAL_12]] : !fir.box>> ! CHECK: } ! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_0]] : !fir.ref>>> ! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_11]] : (!fir.box>>) -> !fir.box - ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_12]], %[[VAL_9]]) {{.*}}: (!fir.box, !fir.box) -> i1 + ! CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_9]] : (!fir.box>>) -> !fir.box + ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_12]], %[[VAL_13]]) {{.*}}: (!fir.box, !fir.box) -> i1 end subroutine ! CHECK-LABEL: func @_QPtest_optional_target_4( @@ -111,17 +111,17 @@ real, optional, allocatable, target :: optionales_ziel(:) print *, associated(p, optionales_ziel) ! CHECK: %[[VAL_8:.*]] = fir.is_present %[[VAL_1]] : (!fir.ref>>>) -> i1 - ! CHECK: %[[VAL_9:.*]] = fir.if %[[VAL_8]] -> (!fir.box) { + ! CHECK: %[[VAL_9:.*]] = fir.if %[[VAL_8]] -> (!fir.box>>) { ! CHECK: %[[VAL_10:.*]] = fir.load %[[VAL_1]] : !fir.ref>>> - ! CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.box>>) -> !fir.box - ! CHECK: fir.result %[[VAL_11]] : !fir.box + ! CHECK: fir.result %[[VAL_10]] : !fir.box>> ! CHECK: } else { - ! CHECK: %[[VAL_12:.*]] = fir.absent !fir.box - ! CHECK: fir.result %[[VAL_12]] : !fir.box + ! CHECK: %[[VAL_12:.*]] = fir.absent !fir.box>> + ! CHECK: fir.result %[[VAL_12]] : !fir.box>> ! CHECK: } ! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_0]] : !fir.ref>>> ! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_11]] : (!fir.box>>) -> !fir.box - ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_12]], %[[VAL_9]]) {{.*}}: (!fir.box, !fir.box) -> i1 + ! CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_9]] : (!fir.box>>) -> !fir.box + ! CHECK: fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_12]], %[[VAL_13]]) {{.*}}: (!fir.box, !fir.box) -> i1 end subroutine ! CHECK-LABEL: func @_QPtest_pointer_target(