[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