[flang-commits] [flang] [llvm] [flang-rt] - Lightweight runtime assignment function (AssignSimple) for intrinsic-type assignments (PR #213704)

Pranav Bhandarkar via flang-commits flang-commits at lists.llvm.org
Wed Aug 19 10:55:01 PDT 2026


================
@@ -53,3 +53,154 @@ TEST(Assign, RTNAME(CopyInAssign)) {
 
   intResultStrided.Destroy();
 }
+
+TEST(AssignSimple, AliasedReverseStride) {
+  // Test aliasing detection with reverse-stride copy: a(5:1:-1) = a(1:5)
+  // This exercises the MayAlias() detection and temporary buffer path.
+  // Without temp buffer, the element-wise copy would corrupt data by
+  // overwriting source elements before they're read.
+
+  // Create backing storage as a C++ array
+  int data[5] = {1, 2, 3, 4, 5};
+  constexpr int elementBytes = sizeof(int);
+  TypeCode intType{TypeCategory::Integer, 4};
+
+  // Create source descriptor: forward view (1:5)
+  StaticDescriptor<1> staticSource;
+  Descriptor &source{staticSource.descriptor()};
+  SubscriptValue extent[1]{5};
+  source.Establish(intType, elementBytes, data, 1, extent);
+  source.GetDimension(0).SetLowerBound(1);
+
+  // Create dest descriptor: reverse view (5:1:-1) of same memory
+  StaticDescriptor<1> staticDest;
+  Descriptor &dest{staticDest.descriptor()};
+  dest.Establish(
+      intType, elementBytes, &data[4], 1, extent); // Start at last element
+  dest.GetDimension(0).SetLowerBound(1);
+  dest.GetDimension(0).SetByteStride(-elementBytes); // Negative stride
+
+  RTNAME(AssignSimple)(dest, source, __FILE__, __LINE__);
+
+  // Verify reverse copy succeeded.
+  // The backing array should now be [5,4,3,2,1] (reversed from [1,2,3,4,5])
+  int expected[5] = {5, 4, 3, 2, 1};
+  EXPECT_EQ(std::memcmp(data, expected, 5 * sizeof(int)), 0);
+}
+
+TEST(AssignSimple, ReallocateUnallocated) {
+  // Test allocatable reallocation from unallocated state
+  StaticDescriptor<1> staticDest;
+  Descriptor &dest{staticDest.descriptor()};
+  dest.Establish(TypeCode{TypeCategory::Integer, 4}, sizeof(int), nullptr, 1,
+      nullptr, CFI_attribute_allocatable);
+  dest.GetDimension(0).SetBounds(1, 0);
+  // dest is now unallocated
+
+  auto source{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{4}, std::vector<int>{10, 20, 30, 40}, sizeof(int))};
+
+  EXPECT_FALSE(dest.IsAllocated());
+
+  RTNAME(AssignSimple)(dest, *source, __FILE__, __LINE__);
+
+  // Verify dest is now allocated with correct shape and data
+  EXPECT_TRUE(dest.IsAllocated());
+  EXPECT_EQ(dest.rank(), 1);
+  EXPECT_EQ(dest.GetDimension(0).LowerBound(), 1);
+  EXPECT_EQ(dest.GetDimension(0).Extent(), 4);
+  EXPECT_EQ(dest.Elements(), 4);
+
+  int expected[4] = {10, 20, 30, 40};
+  EXPECT_EQ(
+      std::memcmp(dest.OffsetElement<int>(0), expected, 4 * sizeof(int)), 0);
+
+  // Verify source unchanged
+  EXPECT_EQ(
+      std::memcmp(source->OffsetElement<int>(0), expected, 4 * sizeof(int)), 0);
+
+  dest.Destroy();
+  source->Destroy();
+}
+
+TEST(AssignSimple, ReallocateShapeMismatch) {
+  // Test allocatable reallocation when shape (extent) differs
+  auto dest{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{3}, std::vector<int>{1, 2, 3}, sizeof(int))};
+
+  auto source{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{5}, std::vector<int>{10, 20, 30, 40, 50}, sizeof(int))};
+
+  EXPECT_TRUE(dest->IsAllocated());
+  EXPECT_EQ(dest->GetDimension(0).Extent(), 3);
+
+  RTNAME(AssignSimple)(*dest, *source, __FILE__, __LINE__);
+
+  // Verify dest was reallocated with new extent matching source
+  EXPECT_TRUE(dest->IsAllocated());
+  EXPECT_EQ(dest->rank(), 1);
+  EXPECT_EQ(dest->GetDimension(0).LowerBound(), 1);
+  EXPECT_EQ(dest->GetDimension(0).Extent(), 5);
+  EXPECT_EQ(dest->Elements(), 5);
+
+  int expected[5] = {10, 20, 30, 40, 50};
+  EXPECT_EQ(
+      std::memcmp(dest->OffsetElement<int>(0), expected, 5 * sizeof(int)), 0);
+
+  // Verify source unchanged
+  EXPECT_EQ(
+      std::memcmp(source->OffsetElement<int>(0), expected, 5 * sizeof(int)), 0);
+
+  dest->Destroy();
+  source->Destroy();
+}
+
+TEST(AssignSimple, NonContiguousToContiguous) {
+  // Test non-contiguous source (strided) to contiguous destination
+  // Pattern: take every other element from an 8-element array
+  auto source{MakeArray<TypeCategory::Integer, 4>(std::vector<int>{8},
+      std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8}, sizeof(int))};
+
+  // Make source non-contiguous: stride=2*sizeof(int), extent=4
+  // This gives us elements [1, 3, 5, 7] from the backing array
+  source->GetDimension(0).SetByteStride(sizeof(int) * 2);
+  source->GetDimension(0).SetExtent(4);
+  EXPECT_FALSE(source->IsContiguous());
+
+  auto dest{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{4}, std::vector<int>{0, 0, 0, 0}, sizeof(int))};
+  EXPECT_TRUE(dest->IsContiguous());
+
+  RTNAME(AssignSimple)(*dest, *source, __FILE__, __LINE__);
+
+  // Verify dest has strided elements from source
+  int expected[4] = {1, 3, 5, 7};
+  EXPECT_EQ(
+      std::memcmp(dest->OffsetElement<int>(0), expected, 4 * sizeof(int)), 0);
+  EXPECT_TRUE(dest->IsContiguous());
+
+  dest->Destroy();
+  source->Destroy();
+}
+
+TEST(AssignSimple, ZeroSizeArray) {
+  // Test zero-size array edge case
+  auto source{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{0}, std::vector<int>{}, sizeof(int))};
+
+  auto dest{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{0}, std::vector<int>{}, sizeof(int))};
+
+  EXPECT_EQ(source->Elements(), 0);
+  EXPECT_EQ(dest->Elements(), 0);
+
+  // Should not crash with zero-size arrays
+  RTNAME(AssignSimple)(*dest, *source, __FILE__, __LINE__);
+
+  // Verify both still have 0 elements
+  EXPECT_EQ(dest->Elements(), 0);
+  EXPECT_EQ(source->Elements(), 0);
+
+  dest->Destroy();
+  source->Destroy();
+}
----------------
bhandarkar-pranav wrote:

I have added more tests that now exercise, more comprehensively, the execution paths throuhg `AssignSimple`. We now cover all code paths that require a temporary buffer inside `AssignSimple`. Here's a code coverage matrix claude generated for me.

| Code Section | AliasedReverseStride | New Tests |
|--------------|---------------------|-----------|
| **Aliasing Detection** | ✅ | ✅ |
| **Temp Buffer Allocation** | ✅ | ✅ |
| **Contiguous Source → Temp** (line 965) | ✅ | ✅ |
| **Non-Contiguous Source → Temp** (lines 967-973) | ❌ | ✅ (AliasedNonContiguousToNonContiguous) |
| **Reallocation with Aliasing** (lines 1003-1021) | ❌ | ✅ (AliasedReallocatableSelfAssign) |
| **Temp → Contiguous Dest** (line 1033) | ❌ | ✅ (AliasedOverlappingSection) |
| **Temp → Non-Contiguous Dest** (lines 1037-1043) | ✅ | ✅ |
| **Multi-Dimensional** | ❌ | ✅ (AliasedTwoDimensionalReverse) |
| **Partial Overlap** | ❌ | ✅ (AliasedOverlappingSection) |



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


More information about the flang-commits mailing list