[flang-commits] [flang] [flang] Fix TRANSFER into derived type with tail padding zeroing pad bytes (PR #223814)
via flang-commits
flang-commits at lists.llvm.org
Thu Sep 17 19:52:47 PDT 2026
================
@@ -1552,15 +1472,24 @@ void fir::factory::genRecordAssignment(fir::FirOpBuilder &builder,
return;
}
- // Otherwise, the derived type has compile time constant size and for which
- // the component by component assignment can be replaced by a memory copy.
- // Since we do not know the size of the derived type in lowering, do a
- // component by component assignment. Note that a single fir.load/fir.store
- // could be used on "small" record types, but as the type size grows, this
- // leads to issues in LLVM (long compile times, long IR files, and even
- // asserts at some point). Since there is no good size boundary, just always
- // use component by component assignment here.
- genComponentByComponentAssignment(builder, loc, lhs, rhs, isTemporaryLHS);
+ // Otherwise, the derived type has compile time constant size, no
+ // allocatable components, and no user-defined assignment. The size of the
+ // type is not known at this point in lowering, but fir.copy defers the size
+ // computation to codegen where the LLVM data layout is available. That allows
+ // it to copy the full allocated storage including any ABI tail-padding bytes,
+ // which a field-by-field copy would silently skip. Preserving those bytes
+ // matters for SEQUENCE types whose storage is reinterpreted via EQUIVALENCE
+ // or TRANSFER.
+ mlir::Value fromAddr = fir::getBase(rhs);
+ mlir::Value toAddr = fir::getBase(lhs);
+ // Ensure we have raw ref<RecordType> pointers for fir.copy.
+ auto refTy = builder.getRefType(recTy);
+ if (fromAddr.getType() != refTy)
+ fromAddr = builder.createConvert(loc, refTy, fromAddr);
+ if (toAddr.getType() != refTy)
+ toAddr = builder.createConvert(loc, refTy, toAddr);
+ // disjoint == true at this point (guaranteed by the condition above).
+ fir::CopyOp::create(builder, loc, fromAddr, toAddr, /*noOverlap=*/true);
----------------
MattPD wrote:
My earlier suggestion to skip unsupported uses was incomplete. The guard avoids the crash, but `idx = threadIdx` still reads an external global instead of GPU registers.
Save this as `repro.cuf`:
```fortran
attributes(global) subroutine read_thread(out)
integer, device :: out
type(dim3) :: idx
idx = threadIdx
out = idx%x
end subroutine
```
With `MODS` set to the intrinsic-module directory of the build providing `bbc`, run:
```sh
bbc -fcuda -J "${MODS?}" -emit-fir repro.cuf -o repro.fir
fir-opt --cuf-predefined-var-to-gpu repro.fir
```
With the tools built at [the PR's original base](https://github.com/llvm/llvm-project/commit/de9ad513460a1a5439b0b8d6d5c7a51a3634bc20), the pass produces three NVVM thread-ID reads with one-based adjustments. This revision retains a copy from `__builtin_threadidx` instead. Could whole-record uses receive those register values too? The new test currently requires the global-backed copy to remain.
https://github.com/llvm/llvm-project/pull/223814
More information about the flang-commits
mailing list