Skip to content

Commit 2a69ca9

Browse files
committed
[Clang] Re-write codegen for atomic_test_and_set and atomic_clear (llvm#120449)
Re-write the sema and codegen for the atomic_test_and_set and atomic_clear builtin functions to go via AtomicExpr, like the other atomic builtins do. This simplifies the code, because AtomicExpr already handles things like generating code for to dynamically select the memory ordering, which was duplicated for these builtins. This also fixes a few crash bugs, one when passing an integer to the pointer argument, and one when using an array. This also adds diagnostics for the memory orderings which are not valid for atomic_clear according to https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html, which were missing before. Fixes llvm#111293.
1 parent 1332db3 commit 2a69ca9

File tree

7 files changed

+316
-157
lines changed

7 files changed

+316
-157
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,16 +1977,16 @@ def AtomicNandFetch : AtomicBuiltin {
19771977
let Prototype = "void(...)";
19781978
}
19791979

1980-
def AtomicTestAndSet : Builtin {
1980+
def AtomicTestAndSet : AtomicBuiltin {
19811981
let Spellings = ["__atomic_test_and_set"];
1982-
let Attributes = [NoThrow];
1983-
let Prototype = "bool(void volatile*, int)";
1982+
let Attributes = [NoThrow, CustomTypeChecking];
1983+
let Prototype = "void(...)";
19841984
}
19851985

1986-
def AtomicClear : Builtin {
1986+
def AtomicClear : AtomicBuiltin {
19871987
let Spellings = ["__atomic_clear"];
1988-
let Attributes = [NoThrow];
1989-
let Prototype = "void(void volatile*, int)";
1988+
let Attributes = [NoThrow, CustomTypeChecking];
1989+
let Prototype = "void(...)";
19901990
}
19911991

19921992
def AtomicThreadFence : Builtin {

clang/lib/AST/Expr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5070,6 +5070,8 @@ unsigned AtomicExpr::getNumSubExprs(AtomicOp Op) {
50705070
case AO__opencl_atomic_init:
50715071
case AO__c11_atomic_load:
50725072
case AO__atomic_load_n:
5073+
case AO__atomic_test_and_set:
5074+
case AO__atomic_clear:
50735075
return 2;
50745076

50755077
case AO__scoped_atomic_load_n:

clang/lib/CodeGen/CGAtomic.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,24 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
723723
case AtomicExpr::AO__scoped_atomic_fetch_nand:
724724
Op = llvm::AtomicRMWInst::Nand;
725725
break;
726+
727+
case AtomicExpr::AO__atomic_test_and_set: {
728+
llvm::AtomicRMWInst *RMWI =
729+
CGF.emitAtomicRMWInst(llvm::AtomicRMWInst::Xchg, Ptr,
730+
CGF.Builder.getInt8(1), Order, Scope, E);
731+
RMWI->setVolatile(E->isVolatile());
732+
llvm::Value *Result = CGF.Builder.CreateIsNotNull(RMWI, "tobool");
733+
CGF.Builder.CreateStore(Result, Dest);
734+
return;
735+
}
736+
737+
case AtomicExpr::AO__atomic_clear: {
738+
llvm::StoreInst *Store =
739+
CGF.Builder.CreateStore(CGF.Builder.getInt8(0), Ptr);
740+
Store->setAtomic(Order, Scope);
741+
Store->setVolatile(E->isVolatile());
742+
return;
743+
}
726744
}
727745

728746
llvm::Value *LoadVal1 = CGF.Builder.CreateLoad(Val1);
@@ -878,6 +896,8 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
878896
case AtomicExpr::AO__c11_atomic_load:
879897
case AtomicExpr::AO__opencl_atomic_load:
880898
case AtomicExpr::AO__hip_atomic_load:
899+
case AtomicExpr::AO__atomic_test_and_set:
900+
case AtomicExpr::AO__atomic_clear:
881901
break;
882902

883903
case AtomicExpr::AO__atomic_load:
@@ -1200,6 +1220,8 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
12001220
case AtomicExpr::AO__opencl_atomic_fetch_max:
12011221
case AtomicExpr::AO__scoped_atomic_fetch_max:
12021222
case AtomicExpr::AO__scoped_atomic_max_fetch:
1223+
case AtomicExpr::AO__atomic_test_and_set:
1224+
case AtomicExpr::AO__atomic_clear:
12031225
llvm_unreachable("Integral atomic operations always become atomicrmw!");
12041226
}
12051227

@@ -1239,7 +1261,8 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
12391261
E->getOp() == AtomicExpr::AO__atomic_store ||
12401262
E->getOp() == AtomicExpr::AO__atomic_store_n ||
12411263
E->getOp() == AtomicExpr::AO__scoped_atomic_store ||
1242-
E->getOp() == AtomicExpr::AO__scoped_atomic_store_n;
1264+
E->getOp() == AtomicExpr::AO__scoped_atomic_store_n ||
1265+
E->getOp() == AtomicExpr::AO__atomic_clear;
12431266
bool IsLoad = E->getOp() == AtomicExpr::AO__c11_atomic_load ||
12441267
E->getOp() == AtomicExpr::AO__opencl_atomic_load ||
12451268
E->getOp() == AtomicExpr::AO__hip_atomic_load ||

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 0 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -5139,147 +5139,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
51395139
ReturnValueSlot(), Args);
51405140
}
51415141

5142-
case Builtin::BI__atomic_test_and_set: {
5143-
// Look at the argument type to determine whether this is a volatile
5144-
// operation. The parameter type is always volatile.
5145-
QualType PtrTy = E->getArg(0)->IgnoreImpCasts()->getType();
5146-
bool Volatile =
5147-
PtrTy->castAs<PointerType>()->getPointeeType().isVolatileQualified();
5148-
5149-
Address Ptr =
5150-
EmitPointerWithAlignment(E->getArg(0)).withElementType(Int8Ty);
5151-
5152-
Value *NewVal = Builder.getInt8(1);
5153-
Value *Order = EmitScalarExpr(E->getArg(1));
5154-
if (isa<llvm::ConstantInt>(Order)) {
5155-
int ord = cast<llvm::ConstantInt>(Order)->getZExtValue();
5156-
AtomicRMWInst *Result = nullptr;
5157-
switch (ord) {
5158-
case 0: // memory_order_relaxed
5159-
default: // invalid order
5160-
Result = Builder.CreateAtomicRMW(llvm::AtomicRMWInst::Xchg, Ptr, NewVal,
5161-
llvm::AtomicOrdering::Monotonic);
5162-
break;
5163-
case 1: // memory_order_consume
5164-
case 2: // memory_order_acquire
5165-
Result = Builder.CreateAtomicRMW(llvm::AtomicRMWInst::Xchg, Ptr, NewVal,
5166-
llvm::AtomicOrdering::Acquire);
5167-
break;
5168-
case 3: // memory_order_release
5169-
Result = Builder.CreateAtomicRMW(llvm::AtomicRMWInst::Xchg, Ptr, NewVal,
5170-
llvm::AtomicOrdering::Release);
5171-
break;
5172-
case 4: // memory_order_acq_rel
5173-
5174-
Result = Builder.CreateAtomicRMW(llvm::AtomicRMWInst::Xchg, Ptr, NewVal,
5175-
llvm::AtomicOrdering::AcquireRelease);
5176-
break;
5177-
case 5: // memory_order_seq_cst
5178-
Result = Builder.CreateAtomicRMW(
5179-
llvm::AtomicRMWInst::Xchg, Ptr, NewVal,
5180-
llvm::AtomicOrdering::SequentiallyConsistent);
5181-
break;
5182-
}
5183-
Result->setVolatile(Volatile);
5184-
return RValue::get(Builder.CreateIsNotNull(Result, "tobool"));
5185-
}
5186-
5187-
llvm::BasicBlock *ContBB = createBasicBlock("atomic.continue", CurFn);
5188-
5189-
llvm::BasicBlock *BBs[5] = {
5190-
createBasicBlock("monotonic", CurFn),
5191-
createBasicBlock("acquire", CurFn),
5192-
createBasicBlock("release", CurFn),
5193-
createBasicBlock("acqrel", CurFn),
5194-
createBasicBlock("seqcst", CurFn)
5195-
};
5196-
llvm::AtomicOrdering Orders[5] = {
5197-
llvm::AtomicOrdering::Monotonic, llvm::AtomicOrdering::Acquire,
5198-
llvm::AtomicOrdering::Release, llvm::AtomicOrdering::AcquireRelease,
5199-
llvm::AtomicOrdering::SequentiallyConsistent};
5200-
5201-
Order = Builder.CreateIntCast(Order, Builder.getInt32Ty(), false);
5202-
llvm::SwitchInst *SI = Builder.CreateSwitch(Order, BBs[0]);
5203-
5204-
Builder.SetInsertPoint(ContBB);
5205-
PHINode *Result = Builder.CreatePHI(Int8Ty, 5, "was_set");
5206-
5207-
for (unsigned i = 0; i < 5; ++i) {
5208-
Builder.SetInsertPoint(BBs[i]);
5209-
AtomicRMWInst *RMW = Builder.CreateAtomicRMW(llvm::AtomicRMWInst::Xchg,
5210-
Ptr, NewVal, Orders[i]);
5211-
RMW->setVolatile(Volatile);
5212-
Result->addIncoming(RMW, BBs[i]);
5213-
Builder.CreateBr(ContBB);
5214-
}
5215-
5216-
SI->addCase(Builder.getInt32(0), BBs[0]);
5217-
SI->addCase(Builder.getInt32(1), BBs[1]);
5218-
SI->addCase(Builder.getInt32(2), BBs[1]);
5219-
SI->addCase(Builder.getInt32(3), BBs[2]);
5220-
SI->addCase(Builder.getInt32(4), BBs[3]);
5221-
SI->addCase(Builder.getInt32(5), BBs[4]);
5222-
5223-
Builder.SetInsertPoint(ContBB);
5224-
return RValue::get(Builder.CreateIsNotNull(Result, "tobool"));
5225-
}
5226-
5227-
case Builtin::BI__atomic_clear: {
5228-
QualType PtrTy = E->getArg(0)->IgnoreImpCasts()->getType();
5229-
bool Volatile =
5230-
PtrTy->castAs<PointerType>()->getPointeeType().isVolatileQualified();
5231-
5232-
Address Ptr = EmitPointerWithAlignment(E->getArg(0));
5233-
Ptr = Ptr.withElementType(Int8Ty);
5234-
Value *NewVal = Builder.getInt8(0);
5235-
Value *Order = EmitScalarExpr(E->getArg(1));
5236-
if (isa<llvm::ConstantInt>(Order)) {
5237-
int ord = cast<llvm::ConstantInt>(Order)->getZExtValue();
5238-
StoreInst *Store = Builder.CreateStore(NewVal, Ptr, Volatile);
5239-
switch (ord) {
5240-
case 0: // memory_order_relaxed
5241-
default: // invalid order
5242-
Store->setOrdering(llvm::AtomicOrdering::Monotonic);
5243-
break;
5244-
case 3: // memory_order_release
5245-
Store->setOrdering(llvm::AtomicOrdering::Release);
5246-
break;
5247-
case 5: // memory_order_seq_cst
5248-
Store->setOrdering(llvm::AtomicOrdering::SequentiallyConsistent);
5249-
break;
5250-
}
5251-
return RValue::get(nullptr);
5252-
}
5253-
5254-
llvm::BasicBlock *ContBB = createBasicBlock("atomic.continue", CurFn);
5255-
5256-
llvm::BasicBlock *BBs[3] = {
5257-
createBasicBlock("monotonic", CurFn),
5258-
createBasicBlock("release", CurFn),
5259-
createBasicBlock("seqcst", CurFn)
5260-
};
5261-
llvm::AtomicOrdering Orders[3] = {
5262-
llvm::AtomicOrdering::Monotonic, llvm::AtomicOrdering::Release,
5263-
llvm::AtomicOrdering::SequentiallyConsistent};
5264-
5265-
Order = Builder.CreateIntCast(Order, Builder.getInt32Ty(), false);
5266-
llvm::SwitchInst *SI = Builder.CreateSwitch(Order, BBs[0]);
5267-
5268-
for (unsigned i = 0; i < 3; ++i) {
5269-
Builder.SetInsertPoint(BBs[i]);
5270-
StoreInst *Store = Builder.CreateStore(NewVal, Ptr, Volatile);
5271-
Store->setOrdering(Orders[i]);
5272-
Builder.CreateBr(ContBB);
5273-
}
5274-
5275-
SI->addCase(Builder.getInt32(0), BBs[0]);
5276-
SI->addCase(Builder.getInt32(3), BBs[1]);
5277-
SI->addCase(Builder.getInt32(5), BBs[2]);
5278-
5279-
Builder.SetInsertPoint(ContBB);
5280-
return RValue::get(nullptr);
5281-
}
5282-
52835142
case Builtin::BI__atomic_thread_fence:
52845143
case Builtin::BI__atomic_signal_fence:
52855144
case Builtin::BI__c11_atomic_thread_fence:

clang/lib/Sema/SemaChecking.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3634,6 +3634,7 @@ static bool isValidOrderingForOp(int64_t Ordering, AtomicExpr::AtomicOp Op) {
36343634
case AtomicExpr::AO__atomic_store_n:
36353635
case AtomicExpr::AO__scoped_atomic_store:
36363636
case AtomicExpr::AO__scoped_atomic_store_n:
3637+
case AtomicExpr::AO__atomic_clear:
36373638
return OrderingCABI != llvm::AtomicOrderingCABI::consume &&
36383639
OrderingCABI != llvm::AtomicOrderingCABI::acquire &&
36393640
OrderingCABI != llvm::AtomicOrderingCABI::acq_rel;
@@ -3686,12 +3687,18 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
36863687
C11CmpXchg,
36873688

36883689
// bool __atomic_compare_exchange(A *, C *, CP, bool, int, int)
3689-
GNUCmpXchg
3690+
GNUCmpXchg,
3691+
3692+
// bool __atomic_test_and_set(A *, int)
3693+
TestAndSet,
3694+
3695+
// void __atomic_clear(A *, int)
3696+
Clear,
36903697
} Form = Init;
36913698

3692-
const unsigned NumForm = GNUCmpXchg + 1;
3693-
const unsigned NumArgs[] = { 2, 2, 3, 3, 3, 3, 4, 5, 6 };
3694-
const unsigned NumVals[] = { 1, 0, 1, 1, 1, 1, 2, 2, 3 };
3699+
const unsigned NumForm = Clear + 1;
3700+
const unsigned NumArgs[] = {2, 2, 3, 3, 3, 3, 4, 5, 6, 2, 2};
3701+
const unsigned NumVals[] = {1, 0, 1, 1, 1, 1, 2, 2, 3, 0, 0};
36953702
// where:
36963703
// C is an appropriate type,
36973704
// A is volatile _Atomic(C) for __c11 builtins and is C for GNU builtins,
@@ -3852,6 +3859,14 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
38523859
case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
38533860
Form = GNUCmpXchg;
38543861
break;
3862+
3863+
case AtomicExpr::AO__atomic_test_and_set:
3864+
Form = TestAndSet;
3865+
break;
3866+
3867+
case AtomicExpr::AO__atomic_clear:
3868+
Form = Clear;
3869+
break;
38553870
}
38563871

38573872
unsigned AdjustedNumArgs = NumArgs[Form];
@@ -3997,10 +4012,10 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
39974012
ValType.removeLocalVolatile();
39984013
ValType.removeLocalConst();
39994014
QualType ResultType = ValType;
4000-
if (Form == Copy || Form == LoadCopy || Form == GNUXchg ||
4001-
Form == Init)
4015+
if (Form == Copy || Form == LoadCopy || Form == GNUXchg || Form == Init ||
4016+
Form == Clear)
40024017
ResultType = Context.VoidTy;
4003-
else if (Form == C11CmpXchg || Form == GNUCmpXchg)
4018+
else if (Form == C11CmpXchg || Form == GNUCmpXchg || Form == TestAndSet)
40044019
ResultType = Context.BoolTy;
40054020

40064021
// The type of a parameter passed 'by value'. In the GNU atomics, such
@@ -4045,6 +4060,10 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
40454060
APIOrderedArgs.push_back(Args[1]); // Order
40464061
APIOrderedArgs.push_back(Args[3]); // OrderFail
40474062
break;
4063+
case TestAndSet:
4064+
case Clear:
4065+
APIOrderedArgs.push_back(Args[1]); // Order
4066+
break;
40484067
}
40494068
} else
40504069
APIOrderedArgs.append(Args.begin(), Args.end());
@@ -4130,6 +4149,8 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
41304149
SubExprs.push_back(APIOrderedArgs[1]); // Val1
41314150
break;
41324151
case Load:
4152+
case TestAndSet:
4153+
case Clear:
41334154
SubExprs.push_back(APIOrderedArgs[1]); // Order
41344155
break;
41354156
case LoadCopy:

0 commit comments

Comments
 (0)