[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
Wed Sep 16 20:37:06 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
----------------
MattPD wrote:
The title and description incorrectly say the parent zeroes the padding and the patch preserves the destination's padding.
The parent leaves the padding unwritten rather than zeroing it. With `rawx` set to `achar(170)` instead of `achar(0)` before `x = transfer(raw, x)`, the parent prints `6 AA 7 AA 8 AA`. The zeros in the description's example come from `rawx = repeat(achar(0), n)`.
The copy preserves the source's padding, not the destination's. For a TRANSFER result the source padding is the transferred bytes. For a structure constructor the source is a temporary. On this branch `x = t(1, 2_c_int8_t)` writes `01 00 00 00 02 00 00 00` over the `AA` padding left in place by the parent, and `x = t(k, int(k, c_int8_t))` writes `02 00 00 00 02 00 FF FF`, the temporary's padding.
Both behaviors conform. F2023 10.2.1.3 p15 defines derived-type intrinsic assignment component by component, and p16 allows any means with the same effect, so padding is outside the defined effect on either side. F2023 16.9.212 p5 constrains `TRANSFER(TRANSFER(raw, x), raw)`, and the parent already preserves this round trip. The patch is a compatibility choice, not a correctness fix, since gfortran preserves the bytes. The description could state the gfortran compatibility goal and mention the constructor case.
`-pedantic` reports the EQUIVALENCE in the example as nonstandard. `x = transfer(raw, x); raw = transfer(x, raw)` shows the difference without EQUIVALENCE.
https://github.com/llvm/llvm-project/pull/223814
More information about the flang-commits
mailing list