[Mlir-commits] [flang] [llvm] [mlir] [flang] Fix REAL(10)/COMPLEX(10) component sizes in runtime type info (PR #192049)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Tue Apr 14 05:54:11 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Sairudra More (Saieiei)

<details>
<summary>Changes</summary>

This fixes a crash caused by incorrect component sizes in runtime type info.

For REAL(10) and COMPLEX(10) components, `Component::GetElementByteSize()` was using the Fortran kind value as the byte size. On x86-64 that underestimates the actual storage size, so strided componentwise assignment can corrupt adjacent data and crash.

This patch routes REAL and COMPLEX component sizes through `Descriptor::BytesFor()`, which matches the runtime's existing storage-size handling. I also added a regression test covering strided assignment of a SEQUENCE type with REAL(10)/COMPLEX(10) components.

Locally, the original reproducer now passes, the new regression test passes, and `check-flang-rt` / `check-flang` are clean.

This is a narrow runtime-side fix only. It does not change compiler-side type info encoding or the runtime type info ABI.


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


5 Files Affected:

- (modified) flang-rt/lib/runtime/type-info.cpp (+3-2) 
- (added) flang-rt/test/Driver/derived-type-real10-assign.f90 (+60) 
- (added) flang/test/Integration/OpenMP/taskloop-bounds-cast.f90 (+33) 
- (modified) llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp (-2) 
- (added) mlir/test/Target/LLVMIR/openmp-taskloop-bounds-cast.mlir (+27) 


``````````diff
diff --git a/flang-rt/lib/runtime/type-info.cpp b/flang-rt/lib/runtime/type-info.cpp
index cb8e894bd3922..c5acfad3a98e3 100644
--- a/flang-rt/lib/runtime/type-info.cpp
+++ b/flang-rt/lib/runtime/type-info.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang-rt/runtime/type-info.h"
+#include "flang-rt/runtime/descriptor.h"
 #include "flang-rt/runtime/terminator.h"
 #include "flang-rt/runtime/tools.h"
 #include <cstdio>
@@ -37,11 +38,11 @@ RT_API_ATTRS std::size_t Component::GetElementByteSize(
   switch (category()) {
   case TypeCategory::Integer:
   case TypeCategory::Unsigned:
-  case TypeCategory::Real:
   case TypeCategory::Logical:
     return kind_;
+  case TypeCategory::Real:
   case TypeCategory::Complex:
-    return 2 * kind_;
+    return Descriptor::BytesFor(category(), kind_);
   case TypeCategory::Character:
     if (auto value{characterLen_.GetValue(&instance)}) {
       return kind_ * *value;
diff --git a/flang-rt/test/Driver/derived-type-real10-assign.f90 b/flang-rt/test/Driver/derived-type-real10-assign.f90
new file mode 100644
index 0000000000000..d71ea8568f3b0
--- /dev/null
+++ b/flang-rt/test/Driver/derived-type-real10-assign.f90
@@ -0,0 +1,60 @@
+! Test that componentwise assignment of SEQUENCE types with REAL(10) and
+! COMPLEX(10) components copies the correct number of bytes. On x86-64,
+! REAL(10) occupies 16 bytes (not 10) and COMPLEX(10) occupies 32 bytes
+! (not 20). A bug in Component::GetElementByteSize() previously returned
+! the Fortran kind value instead of the actual storage size, corrupting
+! subsequent fields during strided derived-type assignment.
+
+! UNSUPPORTED: system-windows
+! UNSUPPORTED: offload-cuda
+
+! RUN: %flang -L"%libdir" %s -o %t
+! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t | FileCheck %s
+
+! CHECK: PASS
+
+program derived_type_real10_assign
+  implicit none
+
+  type :: seq_t
+    sequence
+    real(10)    :: r
+    complex(10) :: z
+    integer     :: tag
+  end type
+
+  type(seq_t) :: a(4), b(4)
+  integer :: i
+
+  ! Initialize source array
+  do i = 1, 4
+    a(i)%r   = real(i * 100, 10)
+    a(i)%z   = cmplx(real(i, 10), real(i * 10, 10))
+    a(i)%tag = i
+  end do
+
+  ! Initialize destination with different values
+  do i = 1, 4
+    b(i)%r   = 0.0_10
+    b(i)%z   = (0.0_10, 0.0_10)
+    b(i)%tag = 0
+  end do
+
+  ! Strided section assignment — forces componentwise copy in the runtime
+  b(1:3:2) = a(2:4:2)
+
+  ! b(1) should equal a(2), b(3) should equal a(4)
+  ! b(2) and b(4) should remain zero
+  if (b(1)%r   /= a(2)%r)   stop 1
+  if (b(1)%z   /= a(2)%z)   stop 2
+  if (b(1)%tag /= a(2)%tag)  stop 3
+  if (b(3)%r   /= a(4)%r)   stop 4
+  if (b(3)%z   /= a(4)%z)   stop 5
+  if (b(3)%tag /= a(4)%tag)  stop 6
+
+  ! Verify untouched elements
+  if (b(2)%tag /= 0) stop 7
+  if (b(4)%tag /= 0) stop 8
+
+  print *, 'PASS'
+end program
diff --git a/flang/test/Integration/OpenMP/taskloop-bounds-cast.f90 b/flang/test/Integration/OpenMP/taskloop-bounds-cast.f90
new file mode 100644
index 0000000000000..9397b98b9ecd6
--- /dev/null
+++ b/flang/test/Integration/OpenMP/taskloop-bounds-cast.f90
@@ -0,0 +1,33 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
+! Regression test for an intermittent crash in taskloop LLVM IR codegen.
+! A dead saveIP/restoreIP pair in OpenMPIRBuilder::createTaskloop introduced
+! stale IRBuilder/debug-location state on the restore path before an
+! immediately following SetInsertPoint override. Removing the dead restore
+! avoids that restore path entirely.
+
+! RUN: %flang_fc1 -emit-llvm -fopenmp -o - %s | FileCheck %s
+
+! Verify that the taskloop runtime call is present in the generated LLVM IR
+! and that the i64 bounds casts required by the ABI are emitted.
+
+! CHECK-LABEL: define void @test_taskloop_(
+
+! CHECK: sext i32 {{.*}} to i64
+! CHECK: call void @__kmpc_taskloop(
+
+subroutine test_taskloop(n)
+  integer, intent(in) :: n
+  integer :: i
+
+  !$omp taskloop
+  do i = 1, n
+  end do
+  !$omp end taskloop
+end subroutine test_taskloop
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 2a0a017fbb0f3..2e73c1791992f 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -2185,7 +2185,6 @@ OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
     /* Create the casting for the Bounds Values that can be used when outlining
      * to replace the uses of the fakes with real values */
     BasicBlock *CodeReplBB = StaleCI->getParent();
-    IRBuilderBase::InsertPoint CurrentIp = Builder.saveIP();
     Builder.SetInsertPoint(CodeReplBB->getFirstInsertionPt());
     Value *CastedLBVal =
         Builder.CreateIntCast(LBVal, Builder.getInt64Ty(), true, "lb64");
@@ -2193,7 +2192,6 @@ OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createTaskloop(
         Builder.CreateIntCast(UBVal, Builder.getInt64Ty(), true, "ub64");
     Value *CastedStepVal =
         Builder.CreateIntCast(StepVal, Builder.getInt64Ty(), true, "step64");
-    Builder.restoreIP(CurrentIp);
 
     Builder.SetInsertPoint(StaleCI);
 
diff --git a/mlir/test/Target/LLVMIR/openmp-taskloop-bounds-cast.mlir b/mlir/test/Target/LLVMIR/openmp-taskloop-bounds-cast.mlir
new file mode 100644
index 0000000000000..41121705aa2c1
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-taskloop-bounds-cast.mlir
@@ -0,0 +1,27 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+// Regression test: a dead saveIP/restoreIP pair in
+// OpenMPIRBuilder::createTaskloop PostOutlineCB could introduce stale
+// IRBuilder debug-location state and cause an intermittent crash during
+// finalize(). Removing the dead pair avoids that restore path entirely.
+// Verifies that an i32 loop is lowered correctly with a __kmpc_taskloop call.
+
+omp.private {type = private} @_QPtest_taskloop_boundsEi_private_i32 : i32
+
+llvm.func @_QPtest_taskloop_bounds() {
+  %0 = llvm.mlir.constant(1 : i64) : i64
+  %1 = llvm.alloca %0 x i32 {bindc_name = "i"} : (i64) -> !llvm.ptr
+  %lb = llvm.mlir.constant(1 : i32) : i32
+  %ub = llvm.mlir.constant(10 : i32) : i32
+  %step = llvm.mlir.constant(1 : i32) : i32
+  omp.taskloop private(@_QPtest_taskloop_boundsEi_private_i32 %1 -> %arg0 : !llvm.ptr) {
+    omp.loop_nest (%arg1) : i32 = (%lb) to (%ub) inclusive step (%step) {
+      llvm.store %arg1, %arg0 : i32, !llvm.ptr
+      omp.yield
+    }
+  }
+  llvm.return
+}
+
+// CHECK-LABEL: define void @_QPtest_taskloop_bounds(
+// CHECK:         call void @__kmpc_taskloop(

``````````

</details>


https://github.com/llvm/llvm-project/pull/192049


More information about the Mlir-commits mailing list