Skip to content

Commit 22a10c8

Browse files
authored
[OpenMP][Flang] Fix atomic operations on complex types (#165366)
Fixes #165184 In OMPIRBuilder::createAtomicRead() and createAtomicWrite(), the size parameter for __atomic_load/__atomic_store was incorrectly computed from the pointer type instead of the pointee (element) type. On 64-bit systems, this resulted in only 8 bytes being transferred regardless of the actual struct size. Changed both functions to use XElemTy (element type) instead of the pointer type when computing LoadSize. This ensures the full struct is transferred.
1 parent ff23ddc commit 22a10c8

File tree

4 files changed

+155
-10
lines changed

4 files changed

+155
-10
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
! Test lowering of atomic read to LLVM IR for complex types.
2+
! This is a regression test for issue #165184.
3+
4+
! RUN: %flang_fc1 -emit-llvm -fopenmp -o - %s | FileCheck %s
5+
6+
! Test that atomic read operations with complex types emit the correct
7+
! size parameter to __atomic_load:
8+
! - complex(4) (8 bytes total): should call __atomic_load(i64 8, ...)
9+
! - complex(8) (16 bytes total): should call __atomic_load(i64 16, ...)
10+
11+
program atomic_read_complex
12+
implicit none
13+
14+
! Test complex(4) - single precision (8 bytes)
15+
complex(4) :: c41, c42
16+
! Test complex(8) - double precision (16 bytes)
17+
complex(8) :: c81, c82
18+
19+
c42 = (1.0_4, 1.0_4)
20+
c82 = (1.0_8, 1.0_8)
21+
22+
! CHECK-LABEL: define {{.*}} @_QQmain
23+
24+
! Single precision complex: 8 bytes
25+
! CHECK: call void @__atomic_load(i64 8, ptr {{.*}}, ptr {{.*}}, i32 {{.*}})
26+
!$omp atomic read
27+
c41 = c42
28+
29+
! Double precision complex: 16 bytes (this was broken before the fix)
30+
! CHECK: call void @__atomic_load(i64 16, ptr {{.*}}, ptr {{.*}}, i32 {{.*}})
31+
!$omp atomic read
32+
c81 = c82
33+
34+
end program atomic_read_complex
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
! Test lowering of atomic write to LLVM IR for complex types.
2+
! This is a regression test for issue #165184.
3+
4+
! RUN: %flang_fc1 -emit-llvm -fopenmp -o - %s | FileCheck %s
5+
6+
! Test that atomic write operations with complex types emit the correct
7+
! size parameter to __atomic_store:
8+
! - complex(4) (8 bytes total): should call __atomic_store(i64 8, ...)
9+
! - complex(8) (16 bytes total): should call __atomic_store(i64 16, ...)
10+
11+
program atomic_write_complex
12+
implicit none
13+
14+
! Test complex(4) - single precision (8 bytes)
15+
complex(4) :: c41, c42
16+
! Test complex(8) - double precision (16 bytes)
17+
complex(8) :: c81, c82
18+
19+
c42 = (1.0_4, 1.0_4)
20+
c82 = (1.0_8, 1.0_8)
21+
22+
! CHECK-LABEL: define {{.*}} @_QQmain
23+
24+
! Single precision complex: 8 bytes
25+
! CHECK: call void @__atomic_store(i64 8, ptr {{.*}}, ptr {{.*}}, i32 {{.*}})
26+
!$omp atomic write
27+
c41 = c42
28+
29+
! Double precision complex: 16 bytes (this was broken before the fix)
30+
! CHECK: call void @__atomic_store(i64 16, ptr {{.*}}, ptr {{.*}}, i32 {{.*}})
31+
!$omp atomic write
32+
c81 = c82
33+
34+
end program atomic_write_complex

llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5473,7 +5473,8 @@ OpenMPIRBuilder::collapseLoops(DebugLoc DL, ArrayRef<CanonicalLoopInfo *> Loops,
54735473
}
54745474

54755475
// TODO: Enable UndefinedSanitizer to diagnose an overflow here.
5476-
CollapsedTripCount = Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
5476+
CollapsedTripCount =
5477+
Builder.CreateNUWMul(CollapsedTripCount, OrigTripCount);
54775478
}
54785479

54795480
// Create the collapsed loop control flow.
@@ -9338,9 +9339,8 @@ OpenMPIRBuilder::createAtomicRead(const LocationDescription &Loc,
93389339
// target does not support `atomicrmw` of the size of the struct
93399340
LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
93409341
OldVal->setAtomic(AO);
9341-
const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
9342-
unsigned LoadSize =
9343-
LoadDL.getTypeStoreSize(OldVal->getPointerOperand()->getType());
9342+
const DataLayout &DL = OldVal->getModule()->getDataLayout();
9343+
unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
93449344
OpenMPIRBuilder::AtomicInfo atomicInfo(
93459345
&Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
93469346
OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
@@ -9384,9 +9384,8 @@ OpenMPIRBuilder::createAtomicWrite(const LocationDescription &Loc,
93849384
XSt->setAtomic(AO);
93859385
} else if (XElemTy->isStructTy()) {
93869386
LoadInst *OldVal = Builder.CreateLoad(XElemTy, X.Var, "omp.atomic.read");
9387-
const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
9388-
unsigned LoadSize =
9389-
LoadDL.getTypeStoreSize(OldVal->getPointerOperand()->getType());
9387+
const DataLayout &DL = OldVal->getModule()->getDataLayout();
9388+
unsigned LoadSize = DL.getTypeStoreSize(XElemTy);
93909389
OpenMPIRBuilder::AtomicInfo atomicInfo(
93919390
&Builder, XElemTy, LoadSize * 8, LoadSize * 8, OldVal->getAlign(),
93929391
OldVal->getAlign(), true /* UseLibcall */, AllocaIP, X.Var);
@@ -9581,7 +9580,7 @@ Expected<std::pair<Value *, Value *>> OpenMPIRBuilder::emitAtomicUpdate(
95819580
OldVal->setAtomic(AO);
95829581
// CurBB
95839582
// | /---\
9584-
// ContBB |
9583+
// ContBB |
95859584
// | \---/
95869585
// ExitBB
95879586
BasicBlock *CurBB = Builder.GetInsertBlock();

llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4534,6 +4534,85 @@ TEST_F(OpenMPIRBuilderTest, OMPAtomicCompareCapture) {
45344534
EXPECT_FALSE(verifyModule(*M, &errs()));
45354535
}
45364536

4537+
TEST_F(OpenMPIRBuilderTest, OMPAtomicRWStructType) {
4538+
// Test for issue #165184: atomic read/write on struct types should use
4539+
// element type size, not pointer size.
4540+
OpenMPIRBuilder OMPBuilder(*M);
4541+
OMPBuilder.initialize();
4542+
F->setName("func");
4543+
IRBuilder<> Builder(BB);
4544+
4545+
OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});
4546+
BasicBlock *EntryBB = BB;
4547+
OpenMPIRBuilder::InsertPointTy AllocaIP(EntryBB,
4548+
EntryBB->getFirstInsertionPt());
4549+
4550+
LLVMContext &Ctx = M->getContext();
4551+
4552+
// Create a struct type {double, double} to simulate complex(8) - 16 bytes
4553+
StructType *Complex8Ty = StructType::create(
4554+
Ctx, {Type::getDoubleTy(Ctx), Type::getDoubleTy(Ctx)}, "complex");
4555+
4556+
AllocaInst *XVal = Builder.CreateAlloca(Complex8Ty);
4557+
XVal->setName("AtomicVar");
4558+
OpenMPIRBuilder::AtomicOpValue X = {XVal, Complex8Ty, false, false};
4559+
AtomicOrdering AO = AtomicOrdering::SequentiallyConsistent;
4560+
4561+
// Create value to write: {1.0, 1.0}
4562+
Constant *Real = ConstantFP::get(Type::getDoubleTy(Ctx), 1.0);
4563+
Constant *Imag = ConstantFP::get(Type::getDoubleTy(Ctx), 1.0);
4564+
Constant *ValToWrite = ConstantStruct::get(Complex8Ty, {Real, Imag});
4565+
4566+
// Test atomic write
4567+
Builder.restoreIP(
4568+
OMPBuilder.createAtomicWrite(Loc, X, ValToWrite, AO, AllocaIP));
4569+
4570+
// Test atomic read
4571+
AllocaInst *VVal = Builder.CreateAlloca(Complex8Ty);
4572+
VVal->setName("ReadDest");
4573+
OpenMPIRBuilder::AtomicOpValue V = {VVal, Complex8Ty, false, false};
4574+
4575+
Builder.restoreIP(OMPBuilder.createAtomicRead(Loc, X, V, AO, AllocaIP));
4576+
4577+
Builder.CreateRetVoid();
4578+
OMPBuilder.finalize();
4579+
EXPECT_FALSE(verifyModule(*M, &errs()));
4580+
4581+
// Verify that __atomic_store and __atomic_load are called with size 16
4582+
bool FoundAtomicStore = false;
4583+
bool FoundAtomicLoad = false;
4584+
4585+
for (Function &Fn : *M) {
4586+
if (Fn.getName().starts_with("__atomic_store")) {
4587+
// Check that first call to __atomic_store has size argument = 16
4588+
for (User *U : Fn.users()) {
4589+
if (auto *CB = dyn_cast<CallBase>(U)) {
4590+
if (auto *SizeArg = dyn_cast<ConstantInt>(CB->getArgOperand(0))) {
4591+
EXPECT_EQ(SizeArg->getZExtValue(), 16U);
4592+
FoundAtomicStore = true;
4593+
break;
4594+
}
4595+
}
4596+
}
4597+
}
4598+
if (Fn.getName().starts_with("__atomic_load")) {
4599+
// Check that first call to __atomic_load has size argument = 16
4600+
for (User *U : Fn.users()) {
4601+
if (auto *CB = dyn_cast<CallBase>(U)) {
4602+
if (auto *SizeArg = dyn_cast<ConstantInt>(CB->getArgOperand(0))) {
4603+
EXPECT_EQ(SizeArg->getZExtValue(), 16U);
4604+
FoundAtomicLoad = true;
4605+
break;
4606+
}
4607+
}
4608+
}
4609+
}
4610+
}
4611+
4612+
EXPECT_TRUE(FoundAtomicStore) << "Did not find __atomic_store call";
4613+
EXPECT_TRUE(FoundAtomicLoad) << "Did not find __atomic_load call";
4614+
}
4615+
45374616
TEST_F(OpenMPIRBuilderTest, CreateTeams) {
45384617
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
45394618
OpenMPIRBuilder OMPBuilder(*M);
@@ -7576,8 +7655,7 @@ TEST_F(OpenMPIRBuilderTest, CreateTaskgroup) {
75767655
// Checking the general structure of the IR generated is same as expected.
75777656
Instruction *GeneratedStoreInst = TaskgroupCall->getNextNode();
75787657
EXPECT_EQ(GeneratedStoreInst, InternalStoreInst);
7579-
Instruction *GeneratedLoad32 =
7580-
GeneratedStoreInst->getNextNode();
7658+
Instruction *GeneratedLoad32 = GeneratedStoreInst->getNextNode();
75817659
EXPECT_EQ(GeneratedLoad32, InternalLoad32);
75827660
Instruction *GeneratedLoad128 = GeneratedLoad32->getNextNode();
75837661
EXPECT_EQ(GeneratedLoad128, InternalLoad128);

0 commit comments

Comments
 (0)