[flang-commits] [flang] [flang][MIF] Fix COSHAPE and THIS_IMAGE(coarray) type mismatch for non-i64 kinds (PR #208429)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Thu Jul 9 04:32:00 PDT 2026


https://github.com/ergawy updated https://github.com/llvm/llvm-project/pull/208429

>From 62a6f96a24e66aa7211433cb9a79c2b0cb5feaad Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Tue, 7 Jul 2026 07:41:24 -0700
Subject: [PATCH] [flang][MIF] Fix COSHAPE and THIS_IMAGE(coarray) type
 mismatch for non-i64 kinds

Both COSHAPE and THIS_IMAGE(coarray [,team]) return integer arrays whose
element type is determined by the Fortran KIND argument (default: i32).
The lowering code was ignoring resultType in both cases and hardcoding i64,
which caused downstream type mismatches:

- COSHAPE: SimplifyHLFIRIntrinsics assertion in PRODUCT(COSHAPE(y)) when
  the product input type (i64) did not match the declared result type (i32).
- THIS_IMAGE(coarray): MLIR verifier error on arith.cmpi with mismatched
  operand types (i64 vs i32) in comparisons like THIS_IMAGE(a) /= [5,0,-7].

Fix: derive eleTy from resultType in both genCoshape and genThisImage and
propagate it into the mif.* op's result type. In MIFOpConversion, extract
a shared convertI64SeqToEleTy helper that post-converts the i64 scratch
buffer written by the PRIF runtime into the declared-element-type buffer
when the two types differ. Both MIFCoshapeOpConversion and
MIFThisImageOpConversion call this helper.

Co-authored-by: Claude Opus 4.7 <noreply at anthropic.com>
---
 flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 21 +++++---
 .../Optimizer/Transforms/MIFOpConversion.cpp  | 46 ++++++++++++++++
 flang/test/Fir/MIF/coshape.mlir               | 54 +++++++++++++++++++
 flang/test/Fir/MIF/this_image.mlir            | 54 +++++++++++++++++++
 flang/test/Lower/MIF/coshape.f90              | 11 +++-
 flang/test/Lower/MIF/this_image.f90           |  2 +-
 6 files changed, 179 insertions(+), 9 deletions(-)

diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index bff561064bc70..2fe81d63f8e49 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -3602,13 +3602,19 @@ mlir::Value IntrinsicLibrary::genCospi(mlir::Type resultType,
 
 // COSHAPE
 fir::ExtendedValue
-IntrinsicLibrary::genCoshape(mlir::Type,
+IntrinsicLibrary::genCoshape(mlir::Type resultType,
                              llvm::ArrayRef<fir::ExtendedValue> args) {
   checkCoarrayEnabled(loc, options);
   assert(args.size() == 2);
 
-  return mif::CoshapeOp::create(builder, loc,
-                                /*coarray*/ fir::getBase(args[0]));
+  // Use the declared Fortran element type (e.g. i32 for default integer kind)
+  // rather than hardcoding i64. MIFCoshapeOpConversion converts the i64 values
+  // written by the prif_coshape runtime to the declared type.
+  mlir::Type eleTy = hlfir::getFortranElementType(resultType);
+  mlir::Type coshapeResultTy = fir::BoxType::get(
+      fir::SequenceType::get({fir::SequenceType::getUnknownExtent()}, eleTy));
+  return mif::CoshapeOp::create(builder, loc, coshapeResultTy,
+                                fir::getBase(args[0]));
 }
 
 // COUNT
@@ -8349,9 +8355,12 @@ IntrinsicLibrary::genThisImage(mlir::Type resultType,
   mlir::Value team = fir::getBase(args[args.size() - 1]);
 
   if (!coarrayIsAbsent && dimIsAbsent) {
-    mlir::Value res =
-        mif::ThisImageOp::create(builder, loc, fir::getBase(args[0]), team);
-    return res;
+    mlir::Type eleTy = hlfir::getFortranElementType(resultType);
+    mlir::Type thisImageResultTy = fir::BoxType::get(
+        fir::SequenceType::get({fir::SequenceType::getUnknownExtent()}, eleTy));
+    return mif::ThisImageOp::create(builder, loc, thisImageResultTy,
+                                    fir::getBase(args[0]),
+                                    /*dim=*/mlir::Value{}, team);
   }
   mlir::Value res;
   if (!dimIsAbsent) {
diff --git a/flang/lib/Optimizer/Transforms/MIFOpConversion.cpp b/flang/lib/Optimizer/Transforms/MIFOpConversion.cpp
index 24dfbf4a9ef02..1ceac427818df 100644
--- a/flang/lib/Optimizer/Transforms/MIFOpConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/MIFOpConversion.cpp
@@ -419,6 +419,43 @@ struct MIFInitOpConversion : public mlir::OpRewritePattern<mif::InitOp> {
   }
 };
 
+/// After a PRIF call that always writes i64 cosubscripts into a sequence
+/// buffer, convert the buffer to the declared Fortran element type when it
+/// is narrower than i64. Returns \p i64Result unchanged when eleTy == i64Ty;
+/// otherwise allocates a typed buffer, copies with element-wise conversion,
+/// and returns the result widened to \p declaredBoxTy.
+static mlir::Value convertI64SeqToEleTy(fir::FirOpBuilder &builder,
+                                        mlir::Location loc,
+                                        mlir::Value i64Result, mlir::Type i64Ty,
+                                        mlir::Type eleTy, std::int64_t corank,
+                                        mlir::Type declaredBoxTy) {
+  if (eleTy == i64Ty)
+    return i64Result;
+  mlir::Value i64Buf = fir::BoxAddrOp::create(
+      builder, loc,
+      builder.getRefType(
+          mlir::cast<fir::BoxType>(i64Result.getType()).getEleTy()),
+      i64Result);
+  mlir::Type declaredSeqType = fir::SequenceType::get(
+      static_cast<fir::SequenceType::Extent>(corank), eleTy);
+  mlir::Value declaredBuf = builder.createTemporary(loc, declaredSeqType);
+  mlir::Type indexTy = builder.getIndexType();
+  mlir::Type i64RefTy = builder.getRefType(i64Ty);
+  mlir::Type eleRefTy = builder.getRefType(eleTy);
+  for (std::int64_t i = 0; i < corank; ++i) {
+    mlir::Value idx = builder.createIntegerConstant(loc, indexTy, i);
+    mlir::Value srcAddr =
+        fir::CoordinateOp::create(builder, loc, i64RefTy, i64Buf, idx);
+    mlir::Value i64Val = fir::LoadOp::create(builder, loc, srcAddr);
+    mlir::Value eleVal = builder.createConvert(loc, eleTy, i64Val);
+    mlir::Value dstAddr =
+        fir::CoordinateOp::create(builder, loc, eleRefTy, declaredBuf, idx);
+    fir::StoreOp::create(builder, loc, eleVal, dstAddr);
+  }
+  mlir::Value result = builder.createBox(loc, declaredBuf);
+  return fir::ConvertOp::create(builder, loc, declaredBoxTy, result);
+}
+
 /// Convert mif.this_image operation to PRIF runtime call
 struct MIFThisImageOpConversion
     : public mlir::OpRewritePattern<mif::ThisImageOp> {
@@ -481,6 +518,10 @@ struct MIFThisImageOpConversion
         fir::CallOp::create(builder, loc, funcOp, args);
         result = fir::ConvertOp::create(builder, loc,
                                         genBoxedSequenceType(i64Ty), result);
+        auto boxResultTy = mlir::cast<fir::BoxType>(op.getResult().getType());
+        mlir::Type eleTy = fir::unwrapSequenceType(boxResultTy.getEleTy());
+        result = convertI64SeqToEleTy(builder, loc, result, i64Ty, eleTy,
+                                      corank, op.getResult().getType());
       }
       rewriter.replaceOp(op, result);
     } else {
@@ -1190,6 +1231,11 @@ struct MIFCoshapeOpConversion : public mlir::OpRewritePattern<mif::CoshapeOp> {
     fir::CallOp::create(builder, loc, funcOp, args);
     result = fir::ConvertOp::create(builder, loc, genBoxedSequenceType(i64Ty),
                                     result);
+
+    auto boxResultTy = mlir::cast<fir::BoxType>(op.getResult().getType());
+    mlir::Type eleTy = fir::unwrapSequenceType(boxResultTy.getEleTy());
+    result = convertI64SeqToEleTy(builder, loc, result, i64Ty, eleTy, corank,
+                                  op.getResult().getType());
     rewriter.replaceOp(op, result);
     return mlir::success();
   }
diff --git a/flang/test/Fir/MIF/coshape.mlir b/flang/test/Fir/MIF/coshape.mlir
index 849a3048d8810..47d46ddb44566 100644
--- a/flang/test/Fir/MIF/coshape.mlir
+++ b/flang/test/Fir/MIF/coshape.mlir
@@ -73,8 +73,62 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr<270> = dense<32> : vec
     hlfir.destroy %28 : !hlfir.expr<?xi64>
     return
   }
+
+  // Test that MIFCoshapeOpConversion emits an element-wise i64→i32 conversion
+  // loop when the declared element type differs from the i64 written by
+  // prif_coshape. Models `integer :: c[5,*]` (corank:2, default kind = i32).
+  func.func @test_coshape_i32() {
+    %c1_i64 = arith.constant 1 : i64
+    %c5_i64 = arith.constant 5 : i64
+    %c0 = arith.constant 0 : index
+    %c1 = arith.constant 1 : index
+    %lc = fir.alloca !fir.array<2xi64>
+    %uc = fir.alloca !fir.array<1xi64>
+    %lc0 = fir.coordinate_of %lc, %c0 : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
+    fir.store %c1_i64 to %lc0 : !fir.ref<i64>
+    %lc1 = fir.coordinate_of %lc, %c1 : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
+    fir.store %c1_i64 to %lc1 : !fir.ref<i64>
+    %uc0 = fir.coordinate_of %uc, %c0 : (!fir.ref<!fir.array<1xi64>>, index) -> !fir.ref<i64>
+    fir.store %c5_i64 to %uc0 : !fir.ref<i64>
+    %lb = fir.embox %lc : (!fir.ref<!fir.array<2xi64>>) -> !fir.box<!fir.array<2xi64>>
+    %ub = fir.embox %uc : (!fir.ref<!fir.array<1xi64>>) -> !fir.box<!fir.array<1xi64>>
+    %c_ref = fir.address_of(@_QFEc) : !fir.ref<i32>
+    mif.alloc_coarray %c_ref lcobounds %lb ucobounds %ub {uniq_name = "_QFEc"} : (!fir.ref<i32>, !fir.box<!fir.array<2xi64>>, !fir.box<!fir.array<1xi64>>) -> ()
+    %c_decl:2 = hlfir.declare %c_ref {uniq_name = "_QFEc"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+    %cobox = fir.embox %c_decl#0 : (!fir.ref<i32>) -> !fir.box<i32, corank:2>
+    %res = mif.coshape coarray %cobox : (!fir.box<i32, corank:2>) -> !fir.box<!fir.array<?xi32>>
+    return
+  }
 }
 
 // CHECK-LABEL: func.func @_QQmain
 // CHECK: fir.call @_QMprifPprif_coshape
 // CHECK: fir.call @_QMprifPprif_coshape
+
+// CHECK-LABEL: func.func @test_coshape_i32
+// i32 and i64 scratch buffers are hoisted to the function entry block.
+// CHECK-DAG:     %[[I32BUF:.*]] = fir.alloca !fir.array<2xi32>
+// CHECK-DAG:     %[[I64BUF:.*]] = fir.alloca !fir.array<2xi64>
+// prif_coshape writes i64 values into the i64 buffer.
+// CHECK:         fir.call @_QMprifPprif_coshape
+// After the call, `result` is converted to a dynamic-extent i64 box and
+// then its base pointer is extracted for the per-element copy loop.
+// CHECK:         %[[I64BOX:.*]] = fir.convert {{.*}} : (!fir.box<!fir.array<2xi64>>) -> !fir.box<!fir.array<?xi64>>
+// CHECK:         %[[I64BUFREF:.*]] = fir.box_addr %[[I64BOX]] : (!fir.box<!fir.array<?xi64>>) -> !fir.ref<!fir.array<?xi64>>
+// Loop iteration 0: load i64, truncate to i32, store into i32 buffer.
+// CHECK:         %[[C0:.*]] = arith.constant 0 : index
+// CHECK:         %[[SRC0:.*]] = fir.coordinate_of %[[I64BUFREF]], %[[C0]] : (!fir.ref<!fir.array<?xi64>>, index) -> !fir.ref<i64>
+// CHECK:         %[[VAL0:.*]] = fir.load %[[SRC0]] : !fir.ref<i64>
+// CHECK:         %[[CONV0:.*]] = fir.convert %[[VAL0]] : (i64) -> i32
+// CHECK:         %[[DST0:.*]] = fir.coordinate_of %[[I32BUF]], %[[C0]] : (!fir.ref<!fir.array<2xi32>>, index) -> !fir.ref<i32>
+// CHECK:         fir.store %[[CONV0]] to %[[DST0]] : !fir.ref<i32>
+// Loop iteration 1: same pattern for the second codimension.
+// CHECK:         %[[C1:.*]] = arith.constant 1 : index
+// CHECK:         %[[SRC1:.*]] = fir.coordinate_of %[[I64BUFREF]], %[[C1]] : (!fir.ref<!fir.array<?xi64>>, index) -> !fir.ref<i64>
+// CHECK:         %[[VAL1:.*]] = fir.load %[[SRC1]] : !fir.ref<i64>
+// CHECK:         %[[CONV1:.*]] = fir.convert %[[VAL1]] : (i64) -> i32
+// CHECK:         %[[DST1:.*]] = fir.coordinate_of %[[I32BUF]], %[[C1]] : (!fir.ref<!fir.array<2xi32>>, index) -> !fir.ref<i32>
+// CHECK:         fir.store %[[CONV1]] to %[[DST1]] : !fir.ref<i32>
+// The i32 buffer is boxed and widened to a dynamic-extent result.
+// CHECK:         %[[BOX_I32:.*]] = fir.embox %[[I32BUF]] : (!fir.ref<!fir.array<2xi32>>) -> !fir.box<!fir.array<2xi32>>
+// CHECK:         fir.convert %[[BOX_I32]] : (!fir.box<!fir.array<2xi32>>) -> !fir.box<!fir.array<?xi32>>
diff --git a/flang/test/Fir/MIF/this_image.mlir b/flang/test/Fir/MIF/this_image.mlir
index d97e2466ce54a..4af6d6cc0d983 100644
--- a/flang/test/Fir/MIF/this_image.mlir
+++ b/flang/test/Fir/MIF/this_image.mlir
@@ -61,7 +61,61 @@
     return
   }
 
+  // Test that MIFThisImageOpConversion emits an element-wise i64→i32
+  // conversion loop when the declared element type differs from the i64
+  // written by prif_this_image_with_coarray. Models `integer :: a[2,*]`
+  // (corank:2, default kind = i32).
+  func.func @test_this_image_i32() {
+    %c1_i64 = arith.constant 1 : i64
+    %c2_i64 = arith.constant 2 : i64
+    %c0 = arith.constant 0 : index
+    %c1 = arith.constant 1 : index
+    %lc = fir.alloca !fir.array<2xi64>
+    %uc = fir.alloca !fir.array<1xi64>
+    %lc0 = fir.coordinate_of %lc, %c0 : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
+    fir.store %c1_i64 to %lc0 : !fir.ref<i64>
+    %lc1 = fir.coordinate_of %lc, %c1 : (!fir.ref<!fir.array<2xi64>>, index) -> !fir.ref<i64>
+    fir.store %c1_i64 to %lc1 : !fir.ref<i64>
+    %uc0 = fir.coordinate_of %uc, %c0 : (!fir.ref<!fir.array<1xi64>>, index) -> !fir.ref<i64>
+    fir.store %c2_i64 to %uc0 : !fir.ref<i64>
+    %lb = fir.embox %lc : (!fir.ref<!fir.array<2xi64>>) -> !fir.box<!fir.array<2xi64>>
+    %ub = fir.embox %uc : (!fir.ref<!fir.array<1xi64>>) -> !fir.box<!fir.array<1xi64>>
+    %a_ref = fir.address_of(@_QFEa2) : !fir.ref<i32>
+    mif.alloc_coarray %a_ref lcobounds %lb ucobounds %ub {uniq_name = "_QFEa2"} : (!fir.ref<i32>, !fir.box<!fir.array<2xi64>>, !fir.box<!fir.array<1xi64>>) -> ()
+    %a_decl:2 = hlfir.declare %a_ref {uniq_name = "_QFEa2"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+    %cobox = fir.embox %a_decl#0 : (!fir.ref<i32>) -> !fir.box<i32, corank:2>
+    %res = mif.this_image coarray %cobox : (!fir.box<i32, corank:2>) -> !fir.box<!fir.array<?xi32>>
+    return
+  }
+
 // CHECK-LABEL: func.func @_QQmain
 // CHECK: fir.call @_QMprifPprif_this_image_no_coarray
 // CHECK: fir.call @_QMprifPprif_this_image_with_coarray
 // CHECK: fir.call @_QMprifPprif_this_image_with_dim
+
+// CHECK-LABEL: func.func @test_this_image_i32
+// i32 and i64 scratch buffers are hoisted to the function entry block.
+// CHECK-DAG:     %[[I32BUF:.*]] = fir.alloca !fir.array<2xi32>
+// CHECK-DAG:     %[[I64BUF:.*]] = fir.alloca !fir.array<2xi64>
+// prif_this_image_with_coarray writes i64 values into the i64 buffer.
+// CHECK:         fir.call @_QMprifPprif_this_image_with_coarray
+// After the call the i64 box is widened and its base pointer extracted.
+// CHECK:         %[[I64BOX:.*]] = fir.convert {{.*}} : (!fir.box<!fir.array<2xi64>>) -> !fir.box<!fir.array<?xi64>>
+// CHECK:         %[[I64BUFREF:.*]] = fir.box_addr %[[I64BOX]] : (!fir.box<!fir.array<?xi64>>) -> !fir.ref<!fir.array<?xi64>>
+// Loop iteration 0: load i64, truncate to i32, store into i32 buffer.
+// CHECK:         %[[C0:.*]] = arith.constant 0 : index
+// CHECK:         %[[SRC0:.*]] = fir.coordinate_of %[[I64BUFREF]], %[[C0]] : (!fir.ref<!fir.array<?xi64>>, index) -> !fir.ref<i64>
+// CHECK:         %[[VAL0:.*]] = fir.load %[[SRC0]] : !fir.ref<i64>
+// CHECK:         %[[CONV0:.*]] = fir.convert %[[VAL0]] : (i64) -> i32
+// CHECK:         %[[DST0:.*]] = fir.coordinate_of %[[I32BUF]], %[[C0]] : (!fir.ref<!fir.array<2xi32>>, index) -> !fir.ref<i32>
+// CHECK:         fir.store %[[CONV0]] to %[[DST0]] : !fir.ref<i32>
+// Loop iteration 1: same pattern for the second codimension.
+// CHECK:         %[[C1:.*]] = arith.constant 1 : index
+// CHECK:         %[[SRC1:.*]] = fir.coordinate_of %[[I64BUFREF]], %[[C1]] : (!fir.ref<!fir.array<?xi64>>, index) -> !fir.ref<i64>
+// CHECK:         %[[VAL1:.*]] = fir.load %[[SRC1]] : !fir.ref<i64>
+// CHECK:         %[[CONV1:.*]] = fir.convert %[[VAL1]] : (i64) -> i32
+// CHECK:         %[[DST1:.*]] = fir.coordinate_of %[[I32BUF]], %[[C1]] : (!fir.ref<!fir.array<2xi32>>, index) -> !fir.ref<i32>
+// CHECK:         fir.store %[[CONV1]] to %[[DST1]] : !fir.ref<i32>
+// The i32 buffer is boxed and widened to a dynamic-extent result.
+// CHECK:         %[[BOX_I32:.*]] = fir.embox %[[I32BUF]] : (!fir.ref<!fir.array<2xi32>>) -> !fir.box<!fir.array<2xi32>>
+// CHECK:         fir.convert %[[BOX_I32]] : (!fir.box<!fir.array<2xi32>>) -> !fir.box<!fir.array<?xi32>>
diff --git a/flang/test/Lower/MIF/coshape.f90 b/flang/test/Lower/MIF/coshape.f90
index 2cf6d50f084a0..c7ba488bf70dd 100644
--- a/flang/test/Lower/MIF/coshape.f90
+++ b/flang/test/Lower/MIF/coshape.f90
@@ -3,12 +3,19 @@
 program test
   integer :: res(3)
   integer(kind=8) :: res2(3)
+  integer :: res3(3)
   integer :: a[2,3:5,*]
 
-  ! CHECK: mif.coshape coarray %[[COARRAY:.*]] : (!fir.box<i32, corank:3>) -> !fir.box<!fir.array<?xi64>>
+  ! COSHAPE without KIND returns default integer kind (4 = i32).
+  ! CHECK: mif.coshape coarray %[[COARRAY:.*]] : (!fir.box<i32, corank:3>) -> !fir.box<!fir.array<?xi32>>
   res = coshape(a)
 
-  ! CHECK: mif.coshape coarray %[[COARRAY:.*]] : (!fir.box<i32, corank:3>) -> !fir.box<!fir.array<?xi64>>
+  ! Assignment to integer(kind=8) widens; COSHAPE result type is still i32.
+  ! CHECK: mif.coshape coarray %[[COARRAY:.*]] : (!fir.box<i32, corank:3>) -> !fir.box<!fir.array<?xi32>>
   res2 = coshape(a)
 
+  ! Explicit KIND=8 yields i64 elements.
+  ! CHECK: mif.coshape coarray %[[COARRAY:.*]] : (!fir.box<i32, corank:3>) -> !fir.box<!fir.array<?xi64>>
+  res3 = coshape(a, kind=8)
+
 end program
diff --git a/flang/test/Lower/MIF/this_image.f90 b/flang/test/Lower/MIF/this_image.f90
index 95d9ab1dedd39..b026300876b7b 100644
--- a/flang/test/Lower/MIF/this_image.f90
+++ b/flang/test/Lower/MIF/this_image.f90
@@ -12,7 +12,7 @@ program test
   ! CHECK: mif.this_image team %[[TEAM:.*]] : ({{.*}}) -> i32
   i = this_image(TEAM=team)
 
-  ! CHECK: mif.this_image coarray %[[A:.*]] : (!fir.box<i32, corank:2>) -> !fir.box<!fir.array<?xi64>>
+  ! CHECK: mif.this_image coarray %[[A:.*]] : (!fir.box<i32, corank:2>) -> !fir.box<!fir.array<?xi32>>
   j = this_image(COARRAY=a)
   
   ! CHECK: mif.this_image coarray %[[A:.*]] dim %[[DIM:.*]] : (!fir.box<i32, corank:2>, i32) -> i64



More information about the flang-commits mailing list