Skip to content

Conversation

ceseo
Copy link
Contributor

@ceseo ceseo commented Sep 5, 2025

Internal procedures expect reference parameters. However, when %VAL is used, the argument should be passed by value. Create a temporary variable in call generation and pass the address to avoid a type conversion error.

Fixes #118239

Internal procedures expect reference parameters. However, when  %VAL is used,
the argument should be passed by value. Create a temporary variable in call
generation and pass the address to avoid a type conversion error.

Fixes llvm#118239
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir labels Sep 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 5, 2025

@llvm/pr-subscribers-flang-fir-hlfir

Author: Carlos Seo (ceseo)

Changes

Internal procedures expect reference parameters. However, when %VAL is used, the argument should be passed by value. Create a temporary variable in call generation and pass the address to avoid a type conversion error.

Fixes #118239


Full diff: https://github.com/llvm/llvm-project/pull/157186.diff

2 Files Affected:

  • (modified) flang/lib/Lower/ConvertCall.cpp (+28-5)
  • (added) flang/test/Lower/percent-val-actual-argument.f90 (+14)
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 04dcc9250be61..9f71f7417005f 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -494,10 +494,20 @@ Fortran::lower::genCallOpAndResult(
     // arguments of any type and vice versa.
     mlir::Value cast;
     auto *context = builder.getContext();
-    if (mlir::isa<fir::BoxProcType>(snd) &&
-        mlir::isa<mlir::FunctionType>(fst.getType())) {
-      auto funcTy = mlir::FunctionType::get(context, {}, {});
-      auto boxProcTy = builder.getBoxProcType(funcTy);
+
+    // Special handling for %VAL arguments: internal procedures expect
+    // reference parameters. When %VAL is used, the argument should be
+    // passed by value. So we need to create a temporary variable and
+    // pass its address to avoid a type conversion error.
+    if (fir::isa_ref_type(snd) && !fir::isa_ref_type(fst.getType()) &&
+        fir::dyn_cast_ptrEleTy(snd) == fst.getType()) {
+      mlir::Value temp = builder.createTemporary(loc, fst.getType());
+      builder.create<fir::StoreOp>(loc, fst, temp);
+      cast = temp;
+    } else if (mlir::isa<fir::BoxProcType>(snd) &&
+               mlir::isa<mlir::FunctionType>(fst.getType())) {
+      mlir::FunctionType funcTy = mlir::FunctionType::get(context, {}, {});
+      fir::BoxProcType boxProcTy = builder.getBoxProcType(funcTy);
       if (mlir::Value host = argumentHostAssocs(converter, fst)) {
         cast = fir::EmboxProcOp::create(builder, loc, boxProcTy,
                                         llvm::ArrayRef<mlir::Value>{fst, host});
@@ -1637,7 +1647,20 @@ void prepareUserCallArguments(
           (*cleanup)();
         break;
       }
-      caller.placeInput(arg, builder.createConvert(loc, argTy, value));
+      // For %VAL arguments, we should pass the value directly without
+      // conversion to reference types. If argTy is different from value type,
+      // it might be due to signature mismatch with internal procedures.
+      if (argTy == value.getType())
+        caller.placeInput(arg, value);
+      else if (fir::isa_ref_type(argTy) &&
+               fir::dyn_cast_ptrEleTy(argTy) == value.getType()) {
+        // We're trying to convert value to reference - create temporary
+        mlir::Value temp = builder.createTemporary(loc, value.getType());
+        builder.create<fir::StoreOp>(loc, value, temp);
+        caller.placeInput(arg, temp);
+      } else
+        caller.placeInput(arg, builder.createConvert(loc, argTy, value));
+
     } break;
     case PassBy::BaseAddressValueAttribute:
     case PassBy::CharBoxValueAttribute:
diff --git a/flang/test/Lower/percent-val-actual-argument.f90 b/flang/test/Lower/percent-val-actual-argument.f90
new file mode 100644
index 0000000000000..736baa0eca21d
--- /dev/null
+++ b/flang/test/Lower/percent-val-actual-argument.f90
@@ -0,0 +1,14 @@
+! RUN: bbc %s -emit-fir -o - | FileCheck %s
+
+program main
+  logical::a1
+  data a1/.true./
+  call sa(%val(a1))
+! CHECK: fir.load %3 : !fir.ref<!fir.logical<4>>
+! CHECK: fir.convert %13 : (!fir.logical<4>) -> i1
+  write(6,*) "a1 = ", a1
+end program main
+
+subroutine sa(x1)
+  logical::x1
+end subroutine sa

@ceseo ceseo requested a review from tblah September 5, 2025 22:12
Copy link
Contributor

@tblah tblah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the bug fix

 * Fix test to reflect the passing by loaded value change.
 * Check the actual call.
 * Use flang -fc1 -emit-hlfir instead of bbc.
mlir::Value temp = builder.createTemporary(loc, fst.getType());
builder.create<fir::StoreOp>(loc, fst, temp);
cast = temp;
auto loadOp = mlir::cast<fir::LoadOp>(fst.getDefiningOp());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. Please could you keep the old code creating the temporary as a fallback. I'm not 100% certain that there will always be a load here. If the cast failed that would be a compiler crash. I'd rather have 2 lines of dead code than a compiler crash for some obscure untested case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's the case because hlfir::loadTrivialScalar always creates fir::LoadOp for scalar trivial variables coming from hlfir.declare.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking I understand the reasoning here: do you mean it will have always taken this path, and that means it must be a load op?

hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, correct.

Copy link
Contributor

@tblah tblah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the bug fix

@ceseo ceseo merged commit 225f181 into llvm:main Sep 9, 2025
9 checks passed
@ceseo
Copy link
Contributor Author

ceseo commented Sep 9, 2025

@tblah I noticed this is breaking the gfortran testsuite: https://lab.llvm.org/buildbot/#/builders/4/builds/9037

The test is:

! { dg-do run }                                                                 
! { dg-options "-fcheck=all" }                                                  
!                                                                               
! { dg-shouldfail "Pointer check" }                                             
! { dg-output "Fortran runtime error: Pointer actual argument 'p' is not associated" }
!                                                                               
! PR fortran/50718                                                              
!                                                                               
! Was failing with -fcheck=pointer: Segfault at run time                        
                                                                                
integer, pointer :: p => null()                                                 
                                                                                
call sub2(%val(p)) ! Invalid: Nonassociated pointer                             
end                                                                             
                                                                                
! Not quite correct dummy, but if one uses VALUE, gfortran                      
! complains about a missing interface - which we cannot use                     
! if we want to use %VAL().                                                     
                                                                                
subroutine sub2(p)                                                              
  integer :: p                                                                  
end subroutine sub2

I don't think this applies to Flang. What do you think?

@tblah
Copy link
Contributor

tblah commented Sep 10, 2025

Agreed. This seems to be testing for a compile error which we don't implement. Previously this passed because flang crashed (so it looked to the test suite like the compile didn't succeed). Now that you've fixed %VAL this will compile correctly (and segfault at runtime). I think this test should be disabled.

@jeanPerier
Copy link
Contributor

jeanPerier commented Sep 10, 2025

Hi @ceseo, thanks for working on the %VAL fix. I agree a segfault is the expectation for the gfortran test, however, I do not think people will get it with the fix you did [edit: because it removed the load from the pointer target data].

I think removing the load is not the correct approach because this will cause the compilation of the caller to be lowered differently if the implicit procedure is or not defined in the same file, while implicit calls should not be impacted by the existence of the definition of the definition in the same file. This is because the "compilation unit" concept is not really a Fortran concept, it is an implementation detail. Code move from one compilation unit to another should not affect the semantic of a program.

Basically, we should lower test1 and test2 the same way in this example (just like gfortran) and I do not think it is the case with your fix (see godbolt link).

subroutine sa(x1)
  logical::x1
end

subroutine test1(a1)
  logical::a1
  ! Implicit call. sa1 defined in the current compilation unit.
  call sa(%val(a1))
end

subroutine test2(a2)
  logical::a2
  ! Implicit call. sa2 not defined in compilation unit.
  call sa2(%val(a2))
end

I think the correct fix is to force an indirect call to the implicit signature just like mustCastFuncOpToCopeWithImplicitInterfaceMismatch is doing for cases without %VAL. There may be a %VAL issue around that logic.

@ceseo
Copy link
Contributor Author

ceseo commented Sep 10, 2025

I think the correct fix is to force an indirect call to the implicit signature just like mustCastFuncOpToCopeWithImplicitInterfaceMismatch is doing for cases without %VAL. There may be a %VAL issue around that logic.

Ah, I see. I'll take a look at this. Thanks!

@ceseo
Copy link
Contributor Author

ceseo commented Sep 10, 2025

New PR submitted: #157873

Thanks for your input!

@eugeneepshteyn
Copy link
Contributor

@ceseo , this is probably related to Jean's comment above, this Fujitsu test now fails: https://github.com/fujitsu/compiler-test-suite/blob/main/Fortran/0977/0977_0061.f90

It prints "NG" for calls to s() and ssss(). When succeeding, it should only print "pass".

@sscalpone
Copy link
Contributor

%VAL is an extension. Intel and gfortran have different behavior. What behavior do we want in flang?

@ceseo
Copy link
Contributor Author

ceseo commented Sep 10, 2025

@ceseo , this is probably related to Jean's comment above, this Fujitsu test now fails: https://github.com/fujitsu/compiler-test-suite/blob/main/Fortran/0977/0977_0061.f90

It prints "NG" for calls to s() and ssss(). When succeeding, it should only print "pass".

Yes, it is. I just confirmed the PR I submitted above fixes it too.

@klausler
Copy link
Contributor

what is the difference between %VAL(x) and (x) as an actual argument?

@eugeneepshteyn
Copy link
Contributor

what is the difference between %VAL(x) and (x) as an actual argument?

Wouldn't (x) require a temporary? %VAL(x) should just load the value and pass it as is.

@eugeneepshteyn
Copy link
Contributor

%VAL is an extension. Intel and gfortran have different behavior. What behavior do we want in flang?

I believe Intel places some restrictions on what could be done with %VAL: https://www.intel.com/content/www/us/en/docs/fortran-compiler/developer-guide-reference/2025-2/val.html

How does gfortran behave?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:fir-hlfir flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Flang] Compilation error of using %VAL for an actual argument
7 participants