Skip to content

Commit 2b5f68a

Browse files
authored
[Clang][C++23] Implement P1774R8: Portable assumptions (#81014)
This implements the C++23 `[[assume]]` attribute. Assumption information is lowered to a call to `@llvm.assume`, unless the expression has side-effects, in which case it is discarded and a warning is issued to tell the user that the assumption doesn’t do anything. A failed assumption at compile time is an error (unless we are in `MSVCCompat` mode, in which case we don’t check assumptions at compile time). Due to performance regressions in LLVM, assumptions can be disabled with the `-fno-assumptions` flag. With it, assumptions will still be parsed and checked, but no calls to `@llvm.assume` will be emitted and assumptions will not be checked at compile time.
1 parent 9df7194 commit 2b5f68a

28 files changed

+502
-29
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ C++23 Feature Support
100100

101101
- Implemented `P2718R0: Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_. Also
102102
materialize temporary object which is a prvalue in discarded-value expression.
103+
- Implemented `P1774R8: Portable assumptions <https://wg21.link/P1774R8>`_.
103104

104105
- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.
105106

clang/include/clang/Basic/Attr.td

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,13 @@ def Unlikely : StmtAttr {
15801580
}
15811581
def : MutualExclusions<[Likely, Unlikely]>;
15821582

1583+
def CXXAssume : StmtAttr {
1584+
let Spellings = [CXX11<"", "assume", 202207>];
1585+
let Subjects = SubjectList<[NullStmt], ErrorDiag, "empty statements">;
1586+
let Args = [ExprArgument<"Assumption">];
1587+
let Documentation = [CXXAssumeDocs];
1588+
}
1589+
15831590
def NoMerge : DeclOrStmtAttr {
15841591
let Spellings = [Clang<"nomerge">];
15851592
let Documentation = [NoMergeDocs];
@@ -4151,11 +4158,11 @@ def OMPDeclareVariant : InheritableAttr {
41514158
}];
41524159
}
41534160

4154-
def Assumption : InheritableAttr {
4161+
def OMPAssume : InheritableAttr {
41554162
let Spellings = [Clang<"assume">];
41564163
let Subjects = SubjectList<[Function, ObjCMethod]>;
41574164
let InheritEvenIfAlreadyPresent = 1;
4158-
let Documentation = [AssumptionDocs];
4165+
let Documentation = [OMPAssumeDocs];
41594166
let Args = [StringArgument<"Assumption">];
41604167
}
41614168

clang/include/clang/Basic/AttrDocs.td

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1996,6 +1996,34 @@ Here is an example:
19961996
}];
19971997
}
19981998

1999+
def CXXAssumeDocs : Documentation {
2000+
let Category = DocCatStmt;
2001+
let Heading = "assume";
2002+
let Content = [{
2003+
The ``assume`` attribute is used to indicate to the optimizer that a
2004+
certain condition is assumed to be true at a certain point in the
2005+
program. If this condition is violated at runtime, the behavior is
2006+
undefined. ``assume`` can only be applied to a null statement.
2007+
2008+
Different optimisers are likely to react differently to the presence of
2009+
this attribute; in some cases, adding ``assume`` may affect performance
2010+
negatively. It should be used with parsimony and care.
2011+
2012+
Note that `clang::assume` is a different attribute. Always write ``assume``
2013+
without a namespace if you intend to use the standard C++ attribute.
2014+
2015+
Example:
2016+
2017+
.. code-block:: c++
2018+
2019+
int f(int x, int y) {
2020+
[[assume(x == 27)]];
2021+
[[assume(x == y)]];
2022+
return y + 1; // May be optimised to `return 28`.
2023+
}
2024+
}];
2025+
}
2026+
19992027
def LikelihoodDocs : Documentation {
20002028
let Category = DocCatStmt;
20012029
let Heading = "likely and unlikely";
@@ -4629,7 +4657,7 @@ For more information see
46294657
}];
46304658
}
46314659

4632-
def AssumptionDocs : Documentation {
4660+
def OMPAssumeDocs : Documentation {
46334661
let Category = DocCatFunction;
46344662
let Heading = "assume";
46354663
let Content = [{

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ def note_constexpr_unsupported_flexible_array : Note<
399399
"flexible array initialization is not yet supported">;
400400
def note_constexpr_non_const_vectorelements : Note<
401401
"cannot determine number of elements for sizeless vectors in a constant expression">;
402+
def note_constexpr_assumption_failed : Note<
403+
"assumption evaluated to false">;
402404
def err_experimental_clang_interp_failed : Error<
403405
"the experimental clang interpreter failed to evaluate an expression">;
404406

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1133,9 +1133,11 @@ def NonGCC : DiagGroup<"non-gcc",
11331133
def CXX14Attrs : DiagGroup<"c++14-attribute-extensions">;
11341134
def CXX17Attrs : DiagGroup<"c++17-attribute-extensions">;
11351135
def CXX20Attrs : DiagGroup<"c++20-attribute-extensions">;
1136+
def CXX23Attrs : DiagGroup<"c++23-attribute-extensions">;
11361137
def FutureAttrs : DiagGroup<"future-attribute-extensions", [CXX14Attrs,
11371138
CXX17Attrs,
1138-
CXX20Attrs]>;
1139+
CXX20Attrs,
1140+
CXX23Attrs]>;
11391141

11401142
def CXX23AttrsOnLambda : DiagGroup<"c++23-lambda-attributes">;
11411143

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,9 @@ def err_ms_property_expected_comma_or_rparen : Error<
786786
def err_ms_property_initializer : Error<
787787
"property declaration cannot have a default member initializer">;
788788

789+
def err_assume_attr_expects_cond_expr : Error<
790+
"use of this expression in an %0 attribute requires parentheses">;
791+
789792
def warn_cxx20_compat_explicit_bool : Warning<
790793
"this expression will be parsed as explicit(bool) in C++20">,
791794
InGroup<CXX20Compat>, DefaultIgnore;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -855,10 +855,10 @@ def note_strncat_wrong_size : Note<
855855
def warn_assume_side_effects : Warning<
856856
"the argument to %0 has side effects that will be discarded">,
857857
InGroup<DiagGroup<"assume">>;
858-
def warn_assume_attribute_string_unknown : Warning<
858+
def warn_omp_assume_attribute_string_unknown : Warning<
859859
"unknown assumption string '%0'; attribute is potentially ignored">,
860860
InGroup<UnknownAssumption>;
861-
def warn_assume_attribute_string_unknown_suggested : Warning<
861+
def warn_omp_assume_attribute_string_unknown_suggested : Warning<
862862
"unknown assumption string '%0' may be misspelled; attribute is potentially "
863863
"ignored, did you mean '%1'?">,
864864
InGroup<MisspelledAssumption>;
@@ -9115,6 +9115,8 @@ def ext_cxx17_attr : Extension<
91159115
"use of the %0 attribute is a C++17 extension">, InGroup<CXX17Attrs>;
91169116
def ext_cxx20_attr : Extension<
91179117
"use of the %0 attribute is a C++20 extension">, InGroup<CXX20Attrs>;
9118+
def ext_cxx23_attr : Extension<
9119+
"use of the %0 attribute is a C++23 extension">, InGroup<CXX23Attrs>;
91189120

91199121
def warn_unused_comparison : Warning<
91209122
"%select{equality|inequality|relational|three-way}0 comparison result unused">,
@@ -10169,6 +10171,9 @@ def err_fallthrough_attr_outside_switch : Error<
1016910171
def err_fallthrough_attr_invalid_placement : Error<
1017010172
"fallthrough annotation does not directly precede switch label">;
1017110173

10174+
def err_assume_attr_args : Error<
10175+
"attribute '%0' requires a single expression argument">;
10176+
1017210177
def warn_unreachable_default : Warning<
1017310178
"default label in switch which covers all enumeration values">,
1017410179
InGroup<CoveredSwitchDefault>, DefaultIgnore;

clang/include/clang/Basic/LangOptions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ LANGOPT(RegCall4, 1, 0, "Set __regcall4 as a default calling convention to respe
450450

451451
LANGOPT(MatrixTypes, 1, 0, "Enable or disable the builtin matrix type")
452452

453+
LANGOPT(CXXAssumptions, 1, 1, "Enable or disable codegen and compile-time checks for C++23's [[assume]] attribute")
454+
453455
ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2,
454456
StrictFlexArraysLevelKind::Default,
455457
"Rely on strict definition of flexible arrays")

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3789,6 +3789,12 @@ def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=
37893789
HelpText<"Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)">,
37903790
MetaVarName<"<regex>">;
37913791

3792+
defm assumptions : BoolFOption<"assumptions",
3793+
LangOpts<"CXXAssumptions">, DefaultTrue,
3794+
NegFlag<SetFalse, [], [ClangOption, CC1Option],
3795+
"Disable codegen and compile-time checks for C++23's [[assume]] attribute">,
3796+
PosFlag<SetTrue>>;
3797+
37923798
def fvectorize : Flag<["-"], "fvectorize">, Group<f_Group>,
37933799
HelpText<"Enable the loop vectorization passes">;
37943800
def fno_vectorize : Flag<["-"], "fno-vectorize">, Group<f_Group>;

clang/include/clang/Parse/Parser.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,7 @@ class Parser : public CodeCompletionHandler {
18031803
ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause);
18041804
// Expr that doesn't include commas.
18051805
ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
1806+
ExprResult ParseConditionalExpression();
18061807

18071808
ExprResult ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
18081809
unsigned &NumLineToksConsumed,
@@ -2955,6 +2956,12 @@ class Parser : public CodeCompletionHandler {
29552956
SourceLocation ScopeLoc,
29562957
CachedTokens &OpenMPTokens);
29572958

2959+
/// Parse a C++23 assume() attribute. Returns true on error.
2960+
bool ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
2961+
IdentifierInfo *AttrName,
2962+
SourceLocation AttrNameLoc,
2963+
SourceLocation *EndLoc);
2964+
29582965
IdentifierInfo *TryParseCXX11AttributeIdentifier(
29592966
SourceLocation &Loc,
29602967
Sema::AttributeCompletion Completion = Sema::AttributeCompletion::None,

0 commit comments

Comments
 (0)