Skip to content

Commit 4ab88d1

Browse files
committed
Add clang atomic control options and attribute
Add option and statement attribute for controlling emitting of target-specific metadata to atomicrmw instructions in IR. The RFC for this attribute and option is https://discourse.llvm.org/t/rfc-add-clang-atomic-control-options-and-pragmas/80641, Originally a pragma was proposed, then it was changed to clang attribute. This attribute allows users to specify one, two, or all three options and must be applied to a compound statement. The attribute can also be nested, with inner attributes overriding the options specified by outer attributes or the target's default options. These options will then determine the target-specific metadata added to atomic instructions in the IR. In addition to the attribute, a new compiler option is introduced: -fatomic=no_remote_memory:{on|off},no_fine_grained_memory:{on|off},ignore_denormal_mode{on|off}. This compiler option allows users to override the target's default options through the Clang driver and front end. In terms of implementation, the atomic attribute is represented in the AST by the existing AttributedStmt, with minimal changes to AST and Sema. During code generation in Clang, the CodeGenModule maintains the current atomic options, which are used to emit the relevant metadata for atomic instructions. RAII is used to manage the saving and restoring of atomic options when entering and exiting nested AttributedStmt.
1 parent 4ac891c commit 4ab88d1

28 files changed

+1458
-390
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===--- AtomicOptions.def - Atomic Options database -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// This file defines the Atomic language options. Users of this file
9+
// must define the OPTION macro to make use of this information.
10+
#ifndef OPTION
11+
# error Define the OPTION macro to handle atomic language options
12+
#endif
13+
14+
// OPTION(name, type, width, previousName)
15+
OPTION(NoRemoteMemory, bool, 1, First)
16+
OPTION(NoFineGrainedMemory, bool, 1, NoRemoteMemory)
17+
OPTION(IgnoreDenormalMode, bool, 1, NoFineGrainedMemory)
18+
19+
#undef OPTION

clang/include/clang/Basic/Attr.td

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4855,3 +4855,59 @@ def ClspvLibclcBuiltin: InheritableAttr {
48554855
let Documentation = [ClspvLibclcBuiltinDoc];
48564856
let SimpleHandler = 1;
48574857
}
4858+
4859+
def Atomic : StmtAttr {
4860+
let Spellings = [Clang<"atomic">];
4861+
let Args = [
4862+
EnumArgument<"NoRemoteMemory", "NoRemoteMemoryTy", /*IsString*/ false,
4863+
["no_remote_memory", "!no_remote_memory", ""],
4864+
["NoRemoteMemoryOn", "NoRemoteMemoryOff", "NoRemoteMemoryUnset"]>,
4865+
EnumArgument<"NoFineGrainedMemory", "NoFineGrainedMemoryTy", /*IsString*/ false,
4866+
["no_fine_grained_memory", "!no_fine_grained_memory", ""],
4867+
["NoFineGrainedMemoryOn", "NoFineGrainedMemoryOff", "NoFineGrainedMemoryUnset"]>,
4868+
EnumArgument<"IgnoreDenormalMode", "IgnoreDenormalModeTy", /*IsString*/ false,
4869+
["ignore_denormal_mode", "!ignore_denormal_mode", ""],
4870+
["IgnoreDenormalModeOn", "IgnoreDenormalModeOff", "IgnoreDenormalModeUnset"]>
4871+
];
4872+
let Subjects = SubjectList<[CompoundStmt], ErrorDiag, "compound statements">;
4873+
let HasCustomParsing = 1;
4874+
let Documentation = [Undocumented];
4875+
let AdditionalMembers = [{
4876+
AtomicOptionsOverride AOO;
4877+
AtomicOptionsOverride getAtomicOptionsOverride() const { return AOO; }
4878+
void updateAtomicOptionsOverride() {
4879+
switch (getNoRemoteMemory()) {
4880+
case NoRemoteMemoryOn:
4881+
AOO.setNoRemoteMemoryOverride(true);
4882+
break;
4883+
case NoRemoteMemoryOff:
4884+
AOO.setNoRemoteMemoryOverride(false);
4885+
break;
4886+
case NoRemoteMemoryUnset:
4887+
AOO.clearNoRemoteMemoryOverride();
4888+
}
4889+
4890+
switch (getNoFineGrainedMemory()) {
4891+
case NoFineGrainedMemoryOn:
4892+
AOO.setNoFineGrainedMemoryOverride(true);
4893+
break;
4894+
case NoFineGrainedMemoryOff:
4895+
AOO.setNoFineGrainedMemoryOverride(false);
4896+
break;
4897+
case NoFineGrainedMemoryUnset:
4898+
AOO.clearNoFineGrainedMemoryOverride();
4899+
}
4900+
4901+
switch (getIgnoreDenormalMode()) {
4902+
case IgnoreDenormalModeOn:
4903+
AOO.setIgnoreDenormalModeOverride(true);
4904+
break;
4905+
case IgnoreDenormalModeOff:
4906+
AOO.setIgnoreDenormalModeOverride(false);
4907+
break;
4908+
case IgnoreDenormalModeUnset:
4909+
AOO.clearIgnoreDenormalModeOverride();
4910+
}
4911+
}
4912+
}];
4913+
}

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,13 @@ def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">;
305305
def err_drv_invalid_value_with_suggestion : Error<
306306
"invalid value '%1' in '%0', expected one of: %2">;
307307
def err_drv_alignment_not_power_of_two : Error<"alignment is not a power of 2 in '%0'">;
308+
309+
def err_drv_invalid_atomic_option : Error<
310+
"invalid argument '%0' to -fatomic=; must be a "
311+
"comma-separated list of key:value pairs, where allowed keys are "
312+
"'no_fine_grained_memory', 'no_remote_memory', 'ignore_denormal_mode', "
313+
"and values are 'on' or 'off', and each key must be unique">;
314+
308315
def err_drv_invalid_remap_file : Error<
309316
"invalid option '%0' not of the form <from-file>;<to-file>">;
310317
def err_drv_invalid_gcc_install_dir : Error<"'%0' does not contain a GCC installation">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3275,6 +3275,8 @@ def err_invalid_branch_protection_spec : Error<
32753275
"invalid or misplaced branch protection specification '%0'">;
32763276
def warn_unsupported_branch_protection_spec : Warning<
32773277
"unsupported branch protection specification '%0'">, InGroup<BranchProtection>;
3278+
def err_attribute_invalid_atomic_argument : Error<
3279+
"invalid argument '%0' to atomic attribute; valid options are: 'no_remote_memory', 'no_fine_grained_memory', 'ignore_denormal_mode' (optionally prefixed with '!')">;
32783280

32793281
def warn_unsupported_target_attribute
32803282
: Warning<"%select{unsupported|duplicate|unknown}0%select{| CPU|"

clang/include/clang/Basic/LangOptions.h

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,10 @@ class LangOptions : public LangOptionsBase {
622622
// WebAssembly target.
623623
bool NoWasmOpt = false;
624624

625+
/// The default atomic codegen options specified by command line in the
626+
/// format of key:{on|off}.
627+
std::vector<std::string> AtomicOptionsAsWritten;
628+
625629
LangOptions();
626630

627631
/// Set language defaults for the given input language and
@@ -1093,6 +1097,157 @@ inline void FPOptions::applyChanges(FPOptionsOverride FPO) {
10931097
*this = FPO.applyOverrides(*this);
10941098
}
10951099

1100+
/// Atomic control options
1101+
class AtomicOptionsOverride;
1102+
class AtomicOptions {
1103+
public:
1104+
using storage_type = uint16_t;
1105+
1106+
static constexpr unsigned StorageBitSize = 8 * sizeof(storage_type);
1107+
1108+
static constexpr storage_type FirstShift = 0, FirstWidth = 0;
1109+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
1110+
static constexpr storage_type NAME##Shift = \
1111+
PREVIOUS##Shift + PREVIOUS##Width; \
1112+
static constexpr storage_type NAME##Width = WIDTH; \
1113+
static constexpr storage_type NAME##Mask = ((1 << NAME##Width) - 1) \
1114+
<< NAME##Shift;
1115+
#include "clang/Basic/AtomicOptions.def"
1116+
1117+
static constexpr storage_type TotalWidth = 0
1118+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) +WIDTH
1119+
#include "clang/Basic/AtomicOptions.def"
1120+
;
1121+
static_assert(TotalWidth <= StorageBitSize,
1122+
"Too short type for AtomicOptions");
1123+
1124+
private:
1125+
storage_type Value;
1126+
1127+
AtomicOptionsOverride getChangesSlow(const AtomicOptions &Base) const;
1128+
1129+
public:
1130+
AtomicOptions() : Value(0) {
1131+
setNoRemoteMemory(false);
1132+
setNoFineGrainedMemory(false);
1133+
setIgnoreDenormalMode(false);
1134+
}
1135+
1136+
bool operator==(AtomicOptions other) const { return Value == other.Value; }
1137+
1138+
/// Return the default value of AtomicOptions that's used when trailing
1139+
/// storage isn't required.
1140+
static AtomicOptions defaultWithoutTrailingStorage(const LangOptions &LO);
1141+
1142+
storage_type getAsOpaqueInt() const { return Value; }
1143+
static AtomicOptions getFromOpaqueInt(storage_type Value) {
1144+
AtomicOptions Opts;
1145+
Opts.Value = Value;
1146+
return Opts;
1147+
}
1148+
1149+
/// Return difference with the given option set.
1150+
AtomicOptionsOverride getChangesFrom(const AtomicOptions &Base) const;
1151+
1152+
void applyChanges(AtomicOptionsOverride AO);
1153+
1154+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
1155+
TYPE get##NAME() const { \
1156+
return static_cast<TYPE>((Value & NAME##Mask) >> NAME##Shift); \
1157+
} \
1158+
void set##NAME(TYPE value) { \
1159+
Value = (Value & ~NAME##Mask) | (storage_type(value) << NAME##Shift); \
1160+
}
1161+
#include "clang/Basic/AtomicOptions.def"
1162+
LLVM_DUMP_METHOD void dump();
1163+
};
1164+
1165+
/// Represents difference between two AtomicOptions values.
1166+
class AtomicOptionsOverride {
1167+
AtomicOptions Options = AtomicOptions::getFromOpaqueInt(0);
1168+
AtomicOptions::storage_type OverrideMask = 0;
1169+
1170+
public:
1171+
/// The type suitable for storing values of AtomicOptionsOverride. Must be
1172+
/// twice as wide as bit size of AtomicOption.
1173+
using storage_type = uint32_t;
1174+
static_assert(sizeof(storage_type) >= 2 * sizeof(AtomicOptions::storage_type),
1175+
"Too short type for AtomicOptionsOverride");
1176+
1177+
/// Bit mask selecting bits of OverrideMask in serialized representation of
1178+
/// AtomicOptionsOverride.
1179+
static constexpr storage_type OverrideMaskBits =
1180+
(static_cast<storage_type>(1) << AtomicOptions::StorageBitSize) - 1;
1181+
1182+
AtomicOptionsOverride() {}
1183+
AtomicOptionsOverride(const LangOptions &LO);
1184+
AtomicOptionsOverride(AtomicOptions AO)
1185+
: Options(AO), OverrideMask(OverrideMaskBits) {}
1186+
AtomicOptionsOverride(AtomicOptions AO, AtomicOptions::storage_type Mask)
1187+
: Options(AO), OverrideMask(Mask) {}
1188+
1189+
bool requiresTrailingStorage() const { return OverrideMask != 0; }
1190+
1191+
storage_type getAsOpaqueInt() const {
1192+
return (static_cast<storage_type>(Options.getAsOpaqueInt())
1193+
<< AtomicOptions::StorageBitSize) |
1194+
OverrideMask;
1195+
}
1196+
1197+
static AtomicOptionsOverride getFromOpaqueInt(storage_type I) {
1198+
AtomicOptionsOverride Opts;
1199+
Opts.OverrideMask = I & OverrideMaskBits;
1200+
Opts.Options =
1201+
AtomicOptions::getFromOpaqueInt(I >> AtomicOptions::StorageBitSize);
1202+
return Opts;
1203+
}
1204+
1205+
AtomicOptions applyOverrides(AtomicOptions Base) {
1206+
AtomicOptions Result = AtomicOptions::getFromOpaqueInt(
1207+
(Base.getAsOpaqueInt() & ~OverrideMask) |
1208+
(Options.getAsOpaqueInt() & OverrideMask));
1209+
return Result;
1210+
}
1211+
1212+
bool operator==(AtomicOptionsOverride other) const {
1213+
return Options == other.Options && OverrideMask == other.OverrideMask;
1214+
}
1215+
bool operator!=(AtomicOptionsOverride other) const {
1216+
return !(*this == other);
1217+
}
1218+
1219+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
1220+
bool has##NAME##Override() const { \
1221+
return OverrideMask & AtomicOptions::NAME##Mask; \
1222+
} \
1223+
TYPE get##NAME##Override() const { \
1224+
assert(has##NAME##Override()); \
1225+
return Options.get##NAME(); \
1226+
} \
1227+
void clear##NAME##Override() { \
1228+
Options.set##NAME(TYPE(0)); \
1229+
OverrideMask &= ~AtomicOptions::NAME##Mask; \
1230+
} \
1231+
void set##NAME##Override(TYPE value) { \
1232+
Options.set##NAME(value); \
1233+
OverrideMask |= AtomicOptions::NAME##Mask; \
1234+
}
1235+
#include "clang/Basic/AtomicOptions.def"
1236+
1237+
LLVM_DUMP_METHOD void dump();
1238+
};
1239+
1240+
inline AtomicOptionsOverride
1241+
AtomicOptions::getChangesFrom(const AtomicOptions &Base) const {
1242+
if (Value == Base.Value)
1243+
return AtomicOptionsOverride();
1244+
return getChangesSlow(Base);
1245+
}
1246+
1247+
inline void AtomicOptions::applyChanges(AtomicOptionsOverride AO) {
1248+
*this = AO.applyOverrides(*this);
1249+
}
1250+
10961251
/// Describes the kind of translation unit being processed.
10971252
enum TranslationUnitKind {
10981253
/// The translation unit is a complete translation unit.

clang/include/clang/Basic/TargetInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ class TargetInfo : public TransferrableTargetInfo,
295295
// in function attributes in IR.
296296
llvm::StringSet<> ReadOnlyFeatures;
297297

298+
// Default atomic options
299+
AtomicOptions AtomicOpts;
300+
298301
public:
299302
/// Construct a target for the given options.
300303
///
@@ -1674,6 +1677,9 @@ class TargetInfo : public TransferrableTargetInfo,
16741677
return CC_C;
16751678
}
16761679

1680+
/// Get the default atomic options.
1681+
AtomicOptions getAtomicOpts() const { return AtomicOpts; }
1682+
16771683
enum CallingConvCheckResult {
16781684
CCCR_OK,
16791685
CCCR_Warning,

clang/include/clang/Driver/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,6 +2346,14 @@ def fsymbol_partition_EQ : Joined<["-"], "fsymbol-partition=">, Group<f_Group>,
23462346
Visibility<[ClangOption, CC1Option]>,
23472347
MarshallingInfoString<CodeGenOpts<"SymbolPartition">>;
23482348

2349+
def fatomic_EQ : CommaJoined<["-"], "fatomic=">, Group<f_Group>,
2350+
Visibility<[ClangOption, CC1Option]>,
2351+
HelpText<"Specify atomic codegen options as a comma-separated list of "
2352+
"key:value pairs, allowed keys and values are "
2353+
"no_fine_grained_memory:on|off, no_remote_memory:on|off, "
2354+
"ignore_denormal_mode:on|off">,
2355+
MarshallingInfoStringVector<LangOpts<"AtomicOptionsAsWritten">>;
2356+
23492357
defm memory_profile : OptInCC1FFlag<"memory-profile", "Enable", "Disable", " heap memory profiling">;
23502358
def fmemory_profile_EQ : Joined<["-"], "fmemory-profile=">,
23512359
Group<f_Group>, Visibility<[ClangOption, CC1Option]>,

clang/include/clang/Parse/Parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3100,6 +3100,11 @@ class Parser : public CodeCompletionHandler {
31003100
std::optional<AvailabilitySpec> ParseAvailabilitySpec();
31013101
ExprResult ParseAvailabilityCheckExpr(SourceLocation StartLoc);
31023102

3103+
void ParseAtomicAttribute(IdentifierInfo &AttrName,
3104+
SourceLocation AttrNameLoc, ParsedAttributes &Attrs,
3105+
SourceLocation *EndLoc, IdentifierInfo *ScopeName,
3106+
SourceLocation ScopeLoc, ParsedAttr::Form Form);
3107+
31033108
void ParseExternalSourceSymbolAttribute(IdentifierInfo &ExternalSourceSymbol,
31043109
SourceLocation Loc,
31053110
ParsedAttributes &Attrs,

clang/lib/Basic/LangOptions.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,49 @@ LLVM_DUMP_METHOD void FPOptionsOverride::dump() {
238238
#include "clang/Basic/FPOptions.def"
239239
llvm::errs() << "\n";
240240
}
241+
242+
AtomicOptionsOverride
243+
AtomicOptions::getChangesSlow(const AtomicOptions &Base) const {
244+
AtomicOptions::storage_type OverrideMask = 0;
245+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
246+
if (get##NAME() != Base.get##NAME()) \
247+
OverrideMask |= NAME##Mask;
248+
#include "clang/Basic/AtomicOptions.def"
249+
return AtomicOptionsOverride(*this, OverrideMask);
250+
}
251+
252+
LLVM_DUMP_METHOD void AtomicOptions::dump() {
253+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
254+
llvm::errs() << "\n " #NAME " " << get##NAME();
255+
#include "clang/Basic/AtomicOptions.def"
256+
llvm::errs() << "\n";
257+
}
258+
259+
LLVM_DUMP_METHOD void AtomicOptionsOverride::dump() {
260+
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
261+
if (has##NAME##Override()) \
262+
llvm::errs() << "\n " #NAME " Override is " << get##NAME##Override();
263+
#include "clang/Basic/AtomicOptions.def"
264+
llvm::errs() << "\n";
265+
}
266+
267+
AtomicOptionsOverride::AtomicOptionsOverride(const LangOptions &LO) {
268+
for (const auto &Setting : LO.AtomicOptionsAsWritten) {
269+
SmallVector<StringRef, 2> KeyValue;
270+
StringRef(Setting).split(KeyValue, ":");
271+
// Assuming option string has been checked elsewhere and is valid.
272+
assert(KeyValue.size() == 2 && "Invalid atomic option format");
273+
StringRef Key = KeyValue[0];
274+
StringRef Val = KeyValue[1];
275+
bool IsEnabled = (Val == "on");
276+
277+
if (Key == "no_fine_grained_memory")
278+
setNoFineGrainedMemoryOverride(IsEnabled);
279+
else if (Key == "no_remote_memory")
280+
setNoRemoteMemoryOverride(IsEnabled);
281+
else if (Key == "ignore_denormal_mode")
282+
setIgnoreDenormalModeOverride(IsEnabled);
283+
else
284+
assert(false && "Unknown atomic option key");
285+
}
286+
}

clang/lib/Basic/Targets/AMDGPU.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ AMDGPUTargetInfo::AMDGPUTargetInfo(const llvm::Triple &Triple,
241241
WavefrontSize = (GPUFeatures & llvm::AMDGPU::FEATURE_WAVE32) ? 32 : 64;
242242
AllowAMDGPUUnsafeFPAtomics = Opts.AllowAMDGPUUnsafeFPAtomics;
243243

244+
// Set the default atomic options
245+
AtomicOpts.setNoRemoteMemory(true);
246+
AtomicOpts.setNoFineGrainedMemory(true);
247+
AtomicOpts.setIgnoreDenormalMode(Opts.AllowAMDGPUUnsafeFPAtomics);
248+
244249
// Set pointer width and alignment for the generic address space.
245250
PointerWidth = PointerAlign = getPointerWidthV(LangAS::Default);
246251
if (getMaxPointerWidth() == 64) {
@@ -264,6 +269,8 @@ void AMDGPUTargetInfo::adjust(DiagnosticsEngine &Diags, LangOptions &Opts) {
264269
// to OpenCL can be removed from the following line.
265270
setAddressSpaceMap((Opts.OpenCL && !Opts.OpenCLGenericAddressSpace) ||
266271
!isAMDGCN(getTriple()));
272+
273+
AtomicOpts.applyChanges(AtomicOptionsOverride(Opts));
267274
}
268275

269276
ArrayRef<Builtin::Info> AMDGPUTargetInfo::getTargetBuiltins() const {

0 commit comments

Comments
 (0)