[flang-commits] [flang] [llvm] [flang] To fix LIT test that AIX does not emit comdat. (PR #214950)

Daniel Chen via flang-commits flang-commits at lists.llvm.org
Sat Aug 8 16:17:46 PDT 2026


https://github.com/DanielCChen updated https://github.com/llvm/llvm-project/pull/214950

>From 12faecad00a218d650b4a534817368ebbb6e779f Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Wed, 5 Aug 2026 08:28:11 -0400
Subject: [PATCH 1/3] [flang] Fix ICE when lowering IBM vector() array through
 fir.box

---
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       |  2 +-
 flang/lib/Optimizer/Dialect/FIRType.cpp       |  5 ++-
 flang/test/Fir/embox-ppc-vector.fir           | 40 +++++++++++++++++++
 .../test/Lower/PowerPC/ppc-vec-array-box.f90  | 39 ++++++++++++++++++
 4 files changed, 84 insertions(+), 2 deletions(-)
 create mode 100644 flang/test/Fir/embox-ppc-vector.fir
 create mode 100644 flang/test/Lower/PowerPC/ppc-vec-array-box.f90

diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 4b7891bd46df1..8a9b511a35420 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -1820,7 +1820,7 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
       return {genTypeStrideInBytes(loc, i64Ty, rewriter, ptrTy, dataLayout),
               typeCodeVal};
     }
-    if (mlir::isa<fir::RecordType>(boxEleTy))
+    if (mlir::isa<fir::RecordType, fir::VectorType>(boxEleTy))
       return {genTypeStrideInBytes(loc, i64Ty, rewriter,
                                    this->convertType(boxEleTy), dataLayout),
               typeCodeVal};
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 178860239e17d..8b80f1d60146a 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -584,7 +584,10 @@ int getTypeCode(mlir::Type ty, const fir::KindMapping &kindMap) {
   }
   if (fir::isa_ref_type(ty))
     return CFI_type_cptr;
-  if (mlir::isa<fir::RecordType>(ty))
+  // fir::VectorType is the IBM Fortran vector() extension type used as a
+  // fir.box element type. Use CFI_type_struct as the accepted fallback
+  // for opaque aggregate types in internal descriptors
+  if (mlir::isa<fir::RecordType, fir::VectorType>(ty))
     return CFI_type_struct;
   llvm_unreachable("unsupported type");
 }
diff --git a/flang/test/Fir/embox-ppc-vector.fir b/flang/test/Fir/embox-ppc-vector.fir
new file mode 100644
index 0000000000000..14f163e8b970e
--- /dev/null
+++ b/flang/test/Fir/embox-ppc-vector.fir
@@ -0,0 +1,40 @@
+// Test emboxing PPC vector type to a descriptor
+//RUN: fir-opt -cg-rewrite --fir-to-llvm-ir %s | FileCheck %s
+
+func.func @test1() {
+  %0 = fir.alloca !fir.box<!fir.heap<!fir.vector<4:i32>>>
+  %1 = fir.zero_bits !fir.heap<!fir.vector<4:i32>>
+  %2 = fir.embox %1 : (!fir.heap<!fir.vector<4:i32>>) -> !fir.box<!fir.heap<!fir.vector<4:i32>>>
+  fir.store %2 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.vector<4:i32>>>>
+  return
+}
+
+//CHECK-LABEL: llvm.func @test1()
+//CHECK: %[[TYPE_VAL:.*]] = llvm.mlir.constant(42 : i32) : i32
+//CHECK: %[[ELEM_LEN_VAL:.*]] = llvm.mlir.constant(16 : i64) : i64
+//CHECK: %[[DSC_1:.*]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+//CHECK: %[[DSC_2:.*]] = llvm.insertvalue %[[ELEM_LEN_VAL]], %[[DSC_1]][1] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+//CHECK: %[[RANK_VAL:.*]] = llvm.mlir.constant(0 : i32) : i32
+//CHECK: %[[DSC_3:.*]] = llvm.trunc %[[RANK_VAL]] : i32 to i8
+//CHECK: %[[DSC_4:.*]] = llvm.insertvalue %[[DSC_3]], %11[3] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+//CHECK: %[[DSC_5:.*]] = llvm.trunc %[[TYPE_VAL]] : i32 to i8
+//CHECK: %[[DSC_6:.*]] = llvm.insertvalue %[[DSC_5]], %[[DSC_4]][4] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+
+func.func @test2() {
+  %0 = fir.alloca !fir.box<!fir.heap<!fir.vector<2:f64>>>
+  %1 = fir.zero_bits !fir.heap<!fir.vector<2:f64>>
+  %2 = fir.embox %1 : (!fir.heap<!fir.vector<2:f64>>) -> !fir.box<!fir.heap<!fir.vector<2:f64>>>
+  fir.store %2 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.vector<2:f64>>>>
+  return
+}
+
+//CHECK-LABEL: llvm.func @test2()
+//CHECK: %[[TYPE_VAL:.*]] = llvm.mlir.constant(42 : i32) : i32
+//CHECK: %[[ELEM_LEN_VAL:.*]] = llvm.mlir.constant(16 : i64) : i64
+//CHECK: %[[DSC_1:.*]] = llvm.mlir.undef : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+//CHECK: %[[DSC_2:.*]] = llvm.insertvalue %[[ELEM_LEN_VAL]], %[[DSC_1]][1] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+//CHECK: %[[RANK_VAL:.*]] = llvm.mlir.constant(0 : i32) : i32
+//CHECK: %[[DSC_3:.*]] = llvm.trunc %[[RANK_VAL]] : i32 to i8
+//CHECK: %[[DSC_4:.*]] = llvm.insertvalue %[[DSC_3]], %11[3] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
+//CHECK: %[[DSC_5:.*]] = llvm.trunc %[[TYPE_VAL]] : i32 to i8
+//CHECK: %[[DSC_6:.*]] = llvm.insertvalue %[[DSC_5]], %[[DSC_4]][4] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
diff --git a/flang/test/Lower/PowerPC/ppc-vec-array-box.f90 b/flang/test/Lower/PowerPC/ppc-vec-array-box.f90
new file mode 100644
index 0000000000000..fcd824704e501
--- /dev/null
+++ b/flang/test/Lower/PowerPC/ppc-vec-array-box.f90
@@ -0,0 +1,39 @@
+! RUN: %flang_fc1 -triple powerpc64-ibm-aix7.2.0.0 -emit-fir -o - %s | FileCheck %s --check-prefix=FIR
+! RUN: %flang_fc1 -triple powerpc64-ibm-aix7.2.0.0 -emit-llvm -O0 -o - %s | FileCheck %s --check-prefix=LLVMIR
+! REQUIRES: target=powerpc{{.*}}
+
+! Test that merge() on an array of IBM vector() type lowers correctly through
+! a fir.box descriptor at -O0.
+!
+! At -O0, HLFIR bufferization creates a temporary array and wraps it in a
+! fir.box<!fir.array<Nx!fir.vector<...>>>.  Before the fix, both:
+!   - getTypeCode()        in FIRType.cpp  hit llvm_unreachable("unsupported type")
+!   - getSizeAndTypeCode() in CodeGen.cpp  hit fir::emitFatalError(...)
+! because neither handled fir::VectorType as a fir.box element type.
+!
+! Additionally, getTypeCode() must return CFI_type_struct (not CFI_type_other)
+! for fir::VectorType. CFI_type_other is rejected at runtime by
+! VerifyEstablishParameters for internal (compiler-generated) descriptors,
+! causing a fatal error: "CFI_establish returned 15 for CFI_type_t(-1)".
+!
+! At -O3, hlfir::createInlineHLFIRCopy inlines the copy element-by-element,
+! avoiding the fir.box entirely, which is why -O3 always passed.
+
+subroutine vec_merge_array(va, vb, mask, res)
+  vector(integer(4)), intent(in)  :: va(2), vb(2)
+  logical,            intent(in)  :: mask(2)
+  vector(integer(4)), intent(out) :: res(2)
+  res = merge(va, vb, mask)
+end subroutine
+
+! FIR-LABEL: func.func @_QPvec_merge_array
+
+! Verify the temporary array and its fir.embox are generated with
+! fir.vector<4:i32> as the element type — this is the type that previously
+! triggered the ICE in getTypeCode() and getSizeAndTypeCode().
+! FIR: fir.allocmem !fir.array<2x!fir.vector<4:i32>>
+! FIR: fir.embox {{.*}} : ({{.*}}!fir.array<2x!fir.vector<4:i32>>{{.*}}) -> !fir.box<!fir.array<2x!fir.vector<4:i32>>>
+
+! LLVMIR-LABEL: define void @vec_merge_array_
+! Verify CodeGen completes and produces correct vector stores.
+! LLVMIR: store <4 x i32>

>From a5dc9547ea151f8a0f714bd5fa59ad435bbadded Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Fri, 7 Aug 2026 22:37:55 -0400
Subject: [PATCH 2/3] [flang] Fix same_type_as for IBM vector() types in
 class(*) descriptors

When a class(*) allocatable holds an IBM vector extension type (e.g.
vector(integer(4)) or vector(real(4))), same_type_as produced wrong
results:

  - same_type_as(x, x) after allocate(x, source=vi) returned .false.
    (derivedType_ was null in the descriptor addendum)
  - same_type_as(xi, xr) where xi/xr hold different vector KINDs
    returned .true. (uninstantiatedType() compared equal for all
    vector() variants)

Root cause and fixes (7 files):

1. flang/lib/Lower/ConvertType.cpp
   genDerivedType() returned early for IsVectorType() before calling
   registerTypeInfo(), so no fir.type_info stub was ever queued.
   Fix: call registerTypeInfo with an empty fir.RecordType before
   the early return.

2. flang/lib/Lower/Bridge.cpp
   createTypeInfoOp() would crash on vector types because the normal
   path iterates DerivedTypeDetails::componentNames(), which is invalid
   for vector types.
   Fix: early return for IsVectorType() that emits a minimal
   fir.type_info stub with no-init/no-destroy/no-final attributes.

3. flang/lib/Lower/Allocatable.cpp
   The mold value for allocate(source=vi) was a plain fir.box with no
   addendum, so AllocatableApplyMold copied a null derivedType_ into
   the LHS descriptor.
   Fix: new genVectorMoldBox() helper that re-boxes vector molds as
   fir.class<fir.type<K0K4>> so the derivedType_ addendum is set;
   called in genRuntimeAllocateApplyMold().

4. flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
   The polymorphic assignment RHS for a vector type was a plain fir.box
   with no addendum, leaving derivedType_ null after assignment.
   Fix: when LHS is polymorphic and RHS element type is VectorType,
   re-box as fir.class<fir.type<K0K4>> before passing to the runtime.

5. flang/lib/Optimizer/CodeGen/CodeGen.cpp (populateDescriptor)
   When useInputType=true and the input element type is VectorType
   (not RecordType), the RecordType branch was skipped and the code fell
   through to the zero-pointer branch, leaving the type descriptor null.
   Fix: add an else-if that uses fir::unwrapIfDerived(boxTy) to obtain
   the RecordType from the box's declared type instead.

6. flang/lib/Optimizer/Dialect/FIRType.cpp
   Remove a stale comment; no functional change (VectorType and
   RecordType remain combined in the CFI_type_struct branch, which is
   required to avoid a CFI_establish runtime error).

7. flang-rt/lib/runtime/derived-api.cpp (SameTypeAs)
   After uninstantiatedType() pointer equality, all vector() KINDs were
   considered the same type because they share a single PDT template.
   Fix: after confirming the uninstantiated template pointers match,
   check whether the type name starts with __builtin_ppc_intrinsic_vector
   and return false if so -- each vector KIND is a distinct user type.

Test: flang/test/Lower/PowerPC/ppc-vec-same-type-as.f90
  - allocate(class(*), source=vector(integer(4))): same_type_as .true.
  - allocate(class(*), source=vector(real(4))):    same_type_as .true.
  - assign vector(real(4)) to class(*):            same_type_as .true.
  - cross-kind: same_type_as(xi, xr) .false., same-kind still .true.
---
 flang-rt/lib/runtime/derived-api.cpp          | 20 ++++-
 flang/lib/Lower/Allocatable.cpp               | 76 ++++++++++++++++++-
 flang/lib/Lower/Bridge.cpp                    | 23 +++++-
 flang/lib/Lower/ConvertType.cpp               | 14 ++++
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       |  5 ++
 flang/lib/Optimizer/Dialect/FIRType.cpp       |  3 -
 .../HLFIR/Transforms/ConvertToFIR.cpp         | 42 ++++++++++
 .../Lower/PowerPC/ppc-vec-same-type-as.f90    | 63 +++++++++++++++
 8 files changed, 235 insertions(+), 11 deletions(-)
 create mode 100644 flang/test/Lower/PowerPC/ppc-vec-same-type-as.f90

diff --git a/flang-rt/lib/runtime/derived-api.cpp b/flang-rt/lib/runtime/derived-api.cpp
index fe6868292f019..4655022953cf8 100644
--- a/flang-rt/lib/runtime/derived-api.cpp
+++ b/flang-rt/lib/runtime/derived-api.cpp
@@ -106,11 +106,25 @@ bool RTDEF(SameTypeAs)(const Descriptor &a, const Descriptor &b) {
         return true;
       } else if (const typeInfo::DerivedType *
           uninstDerivedTypeA{derivedTypeA->uninstantiatedType()}) {
-        // There are KIND type parameters, are these the same type if those
-        // are ignored?
+        // IBM vector types share an uninstantiated PDT template but are
+        // distinct to the user.  Guard against them below.
         const typeInfo::DerivedType *uninstDerivedTypeB{
             derivedTypeB->uninstantiatedType()};
-        return uninstDerivedTypeA == uninstDerivedTypeB;
+        if (uninstDerivedTypeA != uninstDerivedTypeB)
+          return false;
+        // Same uninstantiated template -- reject if it is an IBM vector type.
+        static constexpr char vecPrefix[] =
+            "__builtin_ppc_intrinsic_vector";
+        static constexpr std::size_t vecPrefixLen{sizeof(vecPrefix) - 1};
+        const Descriptor &nameDesc{uninstDerivedTypeA->name()};
+        const char *typeName{nameDesc.OffsetElement<const char>()};
+        std::size_t nameLen{nameDesc.ElementBytes()};
+        if (nameLen >= vecPrefixLen &&
+            Fortran::runtime::memcmp(
+                typeName, vecPrefix, vecPrefixLen) == 0)
+          // IBM vector types: same template but different KIND -- distinct.
+          return false;
+        return true;
       }
     }
   }
diff --git a/flang/lib/Lower/Allocatable.cpp b/flang/lib/Lower/Allocatable.cpp
index f51342b27a19d..ecc965720c468 100644
--- a/flang/lib/Lower/Allocatable.cpp
+++ b/flang/lib/Lower/Allocatable.cpp
@@ -207,7 +207,74 @@ static mlir::Value genRuntimeAllocateSource(fir::FirOpBuilder &builder,
   return fir::CallOp::create(builder, loc, callee, args).getResult(0);
 }
 
-/// Generate runtime call to apply mold to the descriptor.
+/// Re-box \p mold as fir.class<fir.type<K0K4>> when it holds an IBM vector
+/// type so AllocatableApplyMold copies a valid derivedType_ into the LHS.
+/// Returns {} if \p mold is not a vector type.
+static mlir::Value genVectorMoldBox(fir::FirOpBuilder &builder,
+                                    mlir::Location loc,
+                                    const fir::ExtendedValue &mold) {
+  // The mold is always a BoxValue; getBase returns a fir.box.
+  // unwrapSeqOrBoxedSeqType (not getFortranElementType) is needed to unwrap
+  // through the box to reach the vector element type.
+  mlir::Value baseVal = fir::getBase(mold);
+  mlir::Type elemTy = fir::unwrapSeqOrBoxedSeqType(baseVal.getType());
+  if (!mlir::isa<fir::VectorType>(elemTy))
+    return {};
+  // K-suffix: K0K<bytes> for integer/unsigned, K2K<bytes> for real.
+  mlir::Type eleTy = mlir::cast<fir::VectorType>(elemTy).getEleTy();
+  std::string kSuffix;
+  if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(eleTy))
+    kSuffix = "K0K" + std::to_string(intTy.getWidth() / 8);
+  else if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(eleTy))
+    kSuffix = "K2K" + std::to_string(floatTy.getWidth() / 8);
+  if (kSuffix.empty())
+    return {};
+  // Look up by MLIR-uniqued name; do not walk the module -- fir.type_info ops
+  // are emitted after the function body and not yet present.
+  std::string recName =
+      "_QM__ppc_typesT__builtin_ppc_intrinsic_vector" + kSuffix;
+  fir::RecordType recTy =
+      fir::RecordType::get(builder.getContext(), recName);
+  if (!recTy)
+    return {};
+  // fir.embox requires a data pointer, not a descriptor value.
+  mlir::Value dataAddr = fir::BoxAddrOp::create(builder, loc, baseVal);
+  // Build seqTy from the unwrapped address (known or unknown extents).
+  mlir::Type seqTy;
+  if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(
+          fir::unwrapPassByRefType(dataAddr.getType())))
+    seqTy = fir::SequenceType::get(arrTy.getShape(), recTy);
+  else
+    seqTy = fir::SequenceType::get({fir::SequenceType::getUnknownExtent()},
+                                   recTy);
+  // createShape returns {} for a BoxValue with no explicit lower bounds;
+  // fall back to reading extents from the box (constants or fir.box_dims).
+  mlir::Value shape = builder.createShape(loc, mold);
+  if (!shape) {
+    mlir::Type innerTy = fir::dyn_cast_ptrOrBoxEleTy(baseVal.getType());
+    if (auto seqArrTy = mlir::dyn_cast<fir::SequenceType>(innerTy)) {
+      mlir::Type idxTy = builder.getIndexType();
+      llvm::SmallVector<mlir::Value> extents;
+      for (auto [d, ext] : llvm::enumerate(seqArrTy.getShape())) {
+        if (ext != fir::SequenceType::getUnknownExtent())
+          extents.push_back(builder.createIntegerConstant(loc, idxTy, ext));
+        else {
+          mlir::Value dimIdx = builder.createIntegerConstant(loc, idxTy, d);
+          auto dimInfo = fir::BoxDimsOp::create(builder, loc, idxTy, idxTy,
+                                                idxTy, baseVal, dimIdx);
+          extents.push_back(dimInfo.getResult(1));
+        }
+      }
+      if (!extents.empty())
+        shape = builder.genShape(loc, extents);
+    }
+  }
+  mlir::Value empty;
+  mlir::ValueRange emptyRange;
+  return fir::EmboxOp::create(builder, loc, fir::ClassType::get(seqTy),
+                              dataAddr, shape, empty, emptyRange);
+}
+
 static void genRuntimeAllocateApplyMold(fir::FirOpBuilder &builder,
                                         mlir::Location loc,
                                         const fir::MutableBoxValue &box,
@@ -218,9 +285,14 @@ static void genRuntimeAllocateApplyMold(fir::FirOpBuilder &builder,
                                                                     builder)
           : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableApplyMold)>(
                 loc, builder);
+  // For IBM vector molds, re-box as fir.class so the descriptor addendum
+  // carries derivedType_, which AllocatableApplyMold copies to the LHS.
+  mlir::Value moldBase = genVectorMoldBox(builder, loc, mold);
+  if (!moldBase)
+    moldBase = fir::getBase(mold);
   const auto args = fir::runtime::createArguments(
       builder, loc, callee.getFunctionType(),
-      fir::factory::getMutableIRBox(builder, loc, box), fir::getBase(mold),
+      fir::factory::getMutableIRBox(builder, loc, box), moldBase,
       builder.createIntegerConstant(
           loc, callee.getFunctionType().getInputs()[2], rank));
   fir::CallOp::create(builder, loc, callee, args);
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index a8e3e4a0aea1a..38eabf6085d4a 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -402,14 +402,31 @@ class TypeInfoConverter {
 
   void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
                         const TypeInfo &info) {
+    fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+    fir::TypeInfoOp dt;
+    mlir::OpBuilder::InsertPoint insertPointIfCreated;
+
+    // IBM vector types have no components or bindings; emit a minimal
+    // fir.type_info stub so CodeGen can find the .dt. global when lowering
+    // a fir.class<fir.type<K0K4>> descriptor for same_type_as support.
+    if (info.typeSpec.IsVectorType()) {
+      std::tie(dt, insertPointIfCreated) =
+          builder.createTypeInfoOp(info.loc, info.type,
+                                   /*parentType=*/fir::RecordType{});
+      if (!insertPointIfCreated.isSet())
+        return;
+      dt->setAttr(dt.getNoInitAttrName(), builder.getUnitAttr());
+      dt->setAttr(dt.getNoDestroyAttrName(), builder.getUnitAttr());
+      dt->setAttr(dt.getNoFinalAttrName(), builder.getUnitAttr());
+      builder.restoreInsertionPoint(insertPointIfCreated);
+      return;
+    }
+
     fir::RecordType parentType{};
     if (const Fortran::semantics::DerivedTypeSpec *parent =
             Fortran::evaluate::GetParentTypeSpec(info.typeSpec))
       parentType = mlir::cast<fir::RecordType>(converter.genType(*parent));
 
-    fir::FirOpBuilder &builder = converter.getFirOpBuilder();
-    fir::TypeInfoOp dt;
-    mlir::OpBuilder::InsertPoint insertPointIfCreated;
     std::tie(dt, insertPointIfCreated) =
         builder.createTypeInfoOp(info.loc, info.type, parentType);
     if (!insertPointIfCreated.isSet())
diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp
index 0fdbdfcc74424..dd4e8485d48e4 100644
--- a/flang/lib/Lower/ConvertType.cpp
+++ b/flang/lib/Lower/ConvertType.cpp
@@ -383,6 +383,20 @@ struct TypeBuilderImpl {
     std::vector<std::pair<std::string, mlir::Type>> ps;
     std::vector<std::pair<std::string, mlir::Type>> cs;
     if (tySpec.IsVectorType()) {
+      // Register an empty fir.type<> so TypeInfoConverter emits a fir.type_info
+      // stub (see Bridge.cpp).  This lets CodeGen resolve the .dt. global when
+      // a vector type appears in a fir.class<> descriptor.
+      if (const Fortran::semantics::Scope *scope = tySpec.GetScope()) {
+        if (const Fortran::semantics::Symbol *typeInfoSym =
+                scope->runtimeDerivedTypeDescription()) {
+          mlir::Location loc =
+              converter.genLocation(tySpec.typeSymbol().name());
+          auto rec = fir::RecordType::get(context, converter.mangleName(tySpec));
+          if (!rec.isFinalized())
+            rec.finalize({}, {}); // empty: no Fortran components, no len params
+          converter.registerTypeInfo(loc, *typeInfoSym, tySpec, rec);
+        }
+      }
       return genVectorType(tySpec);
     }
 
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 8a9b511a35420..c3d1e3ad4db55 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -1936,6 +1936,11 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
             auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
             typeDesc =
                 getTypeDescriptor(mod, rewriter, loc, recTy, this->options);
+          } else if (auto recTy = fir::unwrapIfDerived(boxTy)) {
+            // inputType is not a RecordType (e.g. IBM vector re-boxed as
+            // fir.class<fir.type<K0K4>>); use the record type from boxTy.
+            typeDesc =
+                getTypeDescriptor(mod, rewriter, loc, recTy, this->options);
           } else {
             // Unlimited polymorphic type descriptor with no record type. Set
             // type descriptor address to a clean state.
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 8b80f1d60146a..a1426d1fff666 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -584,9 +584,6 @@ int getTypeCode(mlir::Type ty, const fir::KindMapping &kindMap) {
   }
   if (fir::isa_ref_type(ty))
     return CFI_type_cptr;
-  // fir::VectorType is the IBM Fortran vector() extension type used as a
-  // fir.box element type. Use CFI_type_struct as the accepted fallback
-  // for opaque aggregate types in internal descriptors
   if (mlir::isa<fir::RecordType, fir::VectorType>(ty))
     return CFI_type_struct;
   llvm_unreachable("unsupported type");
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index 29c209f4c338e..7ad22838e2d89 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -87,6 +87,48 @@ class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {
         fir::StoreOp::create(builder, loc, rhsVal, temp);
         rhsExv = temp;
       }
+      // For polymorphic LHS with an IBM vector RHS, re-box as
+      // fir.class<fir.type<K0K4>> so CodeGen stores the .dt. global address
+      // into the descriptor addendum (derivedType_).  RecordType is looked up
+      // by MLIR-uniqued name (K0K<bytes> for integer, K2K<bytes> for real);
+      // a module walk is not possible as fir.type_info ops are emitted after
+      // the function body.
+      if (lhs.isPolymorphic()) {
+        mlir::Type rhsElemTy = rhs.getFortranElementType();
+        if (auto vecTy = mlir::dyn_cast<fir::VectorType>(rhsElemTy)) {
+          // K-suffix: K0K<bytes> for integer/unsigned, K2K<bytes> for real.
+          mlir::Type eleTy = vecTy.getEleTy();
+          std::string kSuffix;
+          if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(eleTy))
+            kSuffix = "K0K" + std::to_string(intTy.getWidth() / 8);
+          else if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(eleTy))
+            kSuffix = "K2K" + std::to_string(floatTy.getWidth() / 8);
+          if (!kSuffix.empty()) {
+            // RecordType looked up by MLIR-uniqued name.
+            std::string recName =
+                "_QM__ppc_typesT__builtin_ppc_intrinsic_vector" + kSuffix;
+            fir::RecordType recTy =
+                fir::RecordType::get(builder.getContext(), recName);
+            if (recTy) {
+              mlir::Value baseAddr = fir::getBase(rhsExv);
+              mlir::Type rhsSeqTy =
+                  fir::unwrapPassByRefType(baseAddr.getType());
+              mlir::Type recSeqTy;
+              if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(rhsSeqTy))
+                recSeqTy = fir::SequenceType::get(seqTy.getShape(), recTy);
+              else
+                recSeqTy = recTy;
+              mlir::Type classTy = fir::ClassType::get(recSeqTy);
+              mlir::Value shape = builder.createShape(loc, rhsExv);
+              mlir::Value empty;
+              mlir::ValueRange emptyRange;
+              return fir::EmboxOp::create(builder, loc, classTy, baseAddr,
+                                          shape, /*slice=*/empty,
+                                          /*typeParams=*/emptyRange);
+            }
+          }
+        }
+      }
       return fir::getBase(builder.createBox(loc, rhsExv));
     };
 
diff --git a/flang/test/Lower/PowerPC/ppc-vec-same-type-as.f90 b/flang/test/Lower/PowerPC/ppc-vec-same-type-as.f90
new file mode 100644
index 0000000000000..bd47f4d9d7824
--- /dev/null
+++ b/flang/test/Lower/PowerPC/ppc-vec-same-type-as.f90
@@ -0,0 +1,63 @@
+! RUN: %flang -o %t %s && %t
+! REQUIRES: target=powerpc{{.*}}-{{.*}}-aix{{.*}}
+!
+! Test that same_type_as works correctly when a class(*) allocatable holds an
+! IBM vector extension type:
+!   - same_type_as(x, x) must be .true. when x is vector(integer(4)) or
+!     vector(real(4)) (derivedType_ must be non-null in the descriptor)
+!   - same_type_as(x, y) must be .false. when x is vector(integer(4)) and y is
+!     vector(real(4)) (distinct types must not compare equal via
+!     uninstantiatedType())
+
+subroutine test_allocate_source()
+  vector(integer(4)) :: vi(2)
+  vector(real(4))    :: vr(2)
+  class(*), allocatable :: x(:)
+
+  ! allocate from vector(integer(4))
+  allocate(x(2), source=vi)
+  if (.not. same_type_as(x, x)) error stop "FAIL: allocate vector(integer(4))"
+  deallocate(x)
+
+  ! allocate from vector(real(4))
+  allocate(x(2), source=vr)
+  if (.not. same_type_as(x, x)) error stop "FAIL: allocate vector(real(4))"
+  deallocate(x)
+end subroutine
+
+subroutine test_assign()
+  vector(integer(4)) :: vi(2)
+  vector(real(4))    :: vr(2)
+  class(*), allocatable :: x(:)
+
+  ! allocate first, then assign a different vector type
+  allocate(x(2), source=vi)
+  x = vr
+  if (.not. same_type_as(x, x)) error stop "FAIL: assign vector(real(4))"
+end subroutine
+
+subroutine test_cross_type()
+  vector(integer(4)) :: vi(2)
+  vector(real(4))    :: vr(2)
+  class(*), allocatable :: xi(:), xr(:)
+
+  ! xi holds vector(integer(4)), xr holds vector(real(4)) -- must be .false.
+  allocate(xi(2), source=vi)
+  allocate(xr(2), source=vr)
+  if (same_type_as(xi, xr)) &
+      error stop "FAIL: vector(integer(4)) same_type_as vector(real(4))"
+  if (same_type_as(xr, xi)) &
+      error stop "FAIL: vector(real(4)) same_type_as vector(integer(4))"
+
+  ! Same-type checks must still pass
+  if (.not. same_type_as(xi, xi)) &
+      error stop "FAIL: xi same_type_as xi"
+  if (.not. same_type_as(xr, xr)) &
+      error stop "FAIL: xr same_type_as xr"
+end subroutine
+
+call test_allocate_source()
+call test_assign()
+call test_cross_type()
+print *, "PASS"
+end

>From cbbe3e2fd0fce125958f02fbaaff3253e48a0eda Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Sat, 8 Aug 2026 06:27:23 -0400
Subject: [PATCH 3/3] To fix LIT test that AIX does not emit comdat.

---
 flang/test/Lower/MIF/coarray_dealloc_not_alloc.f90 | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/flang/test/Lower/MIF/coarray_dealloc_not_alloc.f90 b/flang/test/Lower/MIF/coarray_dealloc_not_alloc.f90
index 5575a411dae14..44e31704c0055 100644
--- a/flang/test/Lower/MIF/coarray_dealloc_not_alloc.f90
+++ b/flang/test/Lower/MIF/coarray_dealloc_not_alloc.f90
@@ -1,6 +1,6 @@
 ! RUN: %flang_fc1 -emit-llvm -fcoarray %s -o - 2>&1 | FileCheck %s --check-prefix=LLVM
 
-! LLVM: @_QFB1Ekk_coarray_handle = linkonce global %_QM__fortran_builtinsT__builtin_prif_coarray_handle_type zeroinitializer, comdat
+! LLVM: @_QFB1Ekk_coarray_handle = linkonce global %_QM__fortran_builtinsT__builtin_prif_coarray_handle_type zeroinitializer{{.*}}
 
 ! LLVM-LABEL:  @_QQmain()
 ! LLVM:  call void @_QMprifPprif_deallocate_coarray(ptr @_QFB1Ekk_coarray_handle, ptr null, ptr null, ptr null)
@@ -11,4 +11,3 @@
 allocatable :: kk(:)[:]
 endblock
 end
-



More information about the flang-commits mailing list