[Mlir-commits] [flang] [llvm] [mlir] [flang] Fix REAL(10)/COMPLEX(10) component sizes in runtime type info (PR #192049)
Sairudra More
llvmlistbot at llvm.org
Tue Apr 14 05:53:34 PDT 2026
https://github.com/Saieiei created https://github.com/llvm/llvm-project/pull/192049
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.
>From d166eb157537bfa9d99e531ea435d50c0269f509 Mon Sep 17 00:00:00 2001
From: Saieiei <sairudra60 at gmail.com>
Date: Wed, 18 Mar 2026 04:23:39 -0500
Subject: [PATCH 1/2] [flang] Remove dead restoreIP in OpenMP taskloop lowering
This fixes an intermittent crash in OpenMP taskloop lowering.
In OMPIRBuilder::createTaskloop, the restoreIP in PostOutlineCB was immediately overwritten by the following Builder.SetInsertPoint(StaleCI) with no instructions created in between, so it was effectively dead. This patch removes that dead restore, which is the smallest change and preserves the intended IR placement.
Adds a regression test that compiles a taskloop to LLVM IR and verifies
the bounds casts and __kmpc_taskloop call are present.
---
.../OpenMP/taskloop-bounds-cast.f90 | 33 +++++++++++++++++++
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 2 --
.../LLVMIR/openmp-taskloop-bounds-cast.mlir | 27 +++++++++++++++
3 files changed, 60 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Integration/OpenMP/taskloop-bounds-cast.f90
create mode 100644 mlir/test/Target/LLVMIR/openmp-taskloop-bounds-cast.mlir
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(
>From aea0fddfb8584377f47b05021478ddd10f40b605 Mon Sep 17 00:00:00 2001
From: Sairudra More <moresair at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 14 Apr 2026 07:46:11 -0500
Subject: [PATCH 2/2] [flang] Fix REAL(10)/COMPLEX(10) 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 (10 vs 16 bytes for REAL(10),
20 vs 32 for COMPLEX(10)), so strided componentwise assignment of
SEQUENCE types can corrupt adjacent data and crash.
Route REAL and COMPLEX component sizes through Descriptor::BytesFor(),
which matches the runtime existing storage-size handling.
Add a regression test covering strided assignment of a SEQUENCE type
with REAL(10)/COMPLEX(10) components.
---
flang-rt/lib/runtime/type-info.cpp | 5 +-
.../Driver/derived-type-real10-assign.f90 | 60 +++++++++++++++++++
2 files changed, 63 insertions(+), 2 deletions(-)
create mode 100644 flang-rt/test/Driver/derived-type-real10-assign.f90
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
More information about the Mlir-commits
mailing list