[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:04 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);
----------------
MattPD wrote:

`getRefType(recTy)` returns a non-volatile reference type. When an operand is `!fir.ref<T, volatile>`, the two `createConvert` calls convert it to `!fir.ref<T>`, and `ConvertOp::verify` rejects the conversion under `--strict-fir-volatile-verifier`. All `flang/test/Lower/volatile*.f90` and `flang/test/HLFIR/volatile*.fir` tests run with this option. A whole-record assignment to or from a `volatile` derived-type variable doesn't lower under it:

```fortran
subroutine vol_assign(y)
  type t
    integer :: i
  end type
  type(t), volatile :: x
  type(t) :: y
  x = y
end subroutine
```

`bbc --strict-fir-volatile-verifier -emit-fir vol_assign.f90` on this branch reports `'fir.convert' op this conversion does not preserve volatility: '!fir.ref<!fir.type<_QFvol_assignTt{i:i32}>, volatile>' / '!fir.ref<!fir.type<_QFvol_assignTt{i:i32}>>'`, then `FATAL: lowering from HLFIR to FIR failed`. The parent commit lowers it. The existing volatile tests don't contain a whole-record derived-type assignment, so `check-flang` still passes.

`fir.copy` accepts `fir.ref`, `fir.ptr`, and `fir.heap` operands of either volatility, and `CopyOpConversion` takes `isVolatile` from the operand types. The convert is only needed when the element types differ. One example assigns a module type `_QMmTt` to a structurally identical local SEQUENCE type `_QFsTt`. Could the convert be limited to element-type mismatches, with `builder.getRefType(recTy, fir::isa_volatile_type(addr.getType()))` as the target type? A `fir.copy` with a volatile destination passes the strict verifier and lowers to `llvm.memcpy` with `isVolatile = true`. The proposed target type would satisfy the strict verifier and make the whole-record copy volatile, unlike the parent's per-component loads and stores.

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


More information about the flang-commits mailing list