Skip to content

[Clang][C++23] Implement P1774R8: Portable assumptions #81014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5ba6690
[Clang] Parsing and Sema for C++23's `assume` attribute
Sirraide Feb 7, 2024
df08a14
[Clang] Make argument of assume() potentially evaluated
Sirraide Feb 7, 2024
64345e2
[Clang] Move most [[assume()]] diags to test/SemaCXX
Sirraide Feb 7, 2024
6c44d20
[Clang] Codegen for [[assume()]]
Sirraide Feb 7, 2024
dd8febd
[Clang] Check that __has_cpp_attribute returns 1 for assume
Sirraide Feb 7, 2024
b498be8
[NFC] Add newline at end of file
Sirraide Feb 7, 2024
36fc751
[Clang] Update documentation
Sirraide Feb 7, 2024
fc72029
[Clang][NFC] Remove outdated comment
Sirraide Feb 7, 2024
0d869a5
[Clang][NFC] Fix formatting
Sirraide Feb 7, 2024
5928582
[Clang] Do not hard-code attribute name in diagnostics
Sirraide Feb 7, 2024
f286e2e
[Clang] Diagnose failed assumptions during constant expression evalua…
Sirraide Feb 7, 2024
2fb786c
[Clang][NFC] Fix formatting
Sirraide Feb 7, 2024
dc23ed5
[Clang] Handle assume attribute in template instantiation
Sirraide Feb 8, 2024
0725f04
[Clang] Add more tests for assume involving concepts
Sirraide Feb 8, 2024
a46edf9
[Clang] Add -ast-print test for assume
Sirraide Feb 8, 2024
646b32d
[Clang][NFC] Add newline at end of file
Sirraide Feb 8, 2024
c7515d9
[Clang] Actually include CHECK directives in comments
Sirraide Feb 8, 2024
6069ef3
Merge branch 'main' into assume
Sirraide Feb 27, 2024
338b76e
Merge branch 'main' into assume
Sirraide Mar 6, 2024
2f998a4
[Clang] Readd sema functions after merge
Sirraide Mar 6, 2024
98ed8f3
[NFC] Rename AssumeAttr -> CXXAssumeAttr, AssumptionAttr -> OMPAssume…
Sirraide Mar 6, 2024
6b30e99
[Clang] Ignore failed assumptions in constant expressions in MSVCComp…
Sirraide Mar 6, 2024
c41566b
[Clang] Add assume lang option and command-line flag
Sirraide Mar 7, 2024
d85d46b
[Clang] Ensure __has_cpp_attribute(assume) returns the correct value
Sirraide Mar 7, 2024
33bbd8d
[NFC] Add newline at end of file
Sirraide Mar 7, 2024
7aa1a95
[Clang] Update cxx_status
Sirraide Mar 7, 2024
61230cb
[Clang] Rename last occurrence of Assumption -> OMPAssume
Sirraide Mar 7, 2024
df227c4
[Clang] [Parser] Better diagnostics for [[assume]] syntax errors
Sirraide Mar 7, 2024
0d20580
[Clang] [Parser] Remove superfluous check for ()
Sirraide Mar 7, 2024
61f92cf
[Clang] Warn about potential pessimisation in assume docs
Sirraide Mar 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ C++23 Feature Support

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

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down
11 changes: 9 additions & 2 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,13 @@ def Unlikely : StmtAttr {
}
def : MutualExclusions<[Likely, Unlikely]>;

def CXXAssume : StmtAttr {
let Spellings = [CXX11<"", "assume", 202207>];
let Subjects = SubjectList<[NullStmt], ErrorDiag, "empty statements">;
let Args = [ExprArgument<"Assumption">];
let Documentation = [CXXAssumeDocs];
}

def NoMerge : DeclOrStmtAttr {
let Spellings = [Clang<"nomerge">];
let Documentation = [NoMergeDocs];
Expand Down Expand Up @@ -4143,11 +4150,11 @@ def OMPDeclareVariant : InheritableAttr {
}];
}

def Assumption : InheritableAttr {
def OMPAssume : InheritableAttr {
let Spellings = [Clang<"assume">];
let Subjects = SubjectList<[Function, ObjCMethod]>;
let InheritEvenIfAlreadyPresent = 1;
let Documentation = [AssumptionDocs];
let Documentation = [OMPAssumeDocs];
let Args = [StringArgument<"Assumption">];
}

Expand Down
30 changes: 29 additions & 1 deletion clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1996,6 +1996,34 @@ Here is an example:
}];
}

def CXXAssumeDocs : Documentation {
let Category = DocCatStmt;
let Heading = "assume";
let Content = [{
The ``assume`` attribute is used to indicate to the optimizer that a
certain condition is assumed to be true at a certain point in the
program. If this condition is violated at runtime, the behavior is
undefined. ``assume`` can only be applied to a null statement.

Different optimisers are likely to react differently to the presence of
this attribute; in some cases, adding ``assume`` may affect performance
negatively. It should be used with parsimony and care.

Note that `clang::assume` is a different attribute. Always write ``assume``
without a namespace if you intend to use the standard C++ attribute.

Example:

.. code-block:: c++

int f(int x, int y) {
[[assume(x == 27)]];
[[assume(x == y)]];
return y + 1; // May be optimised to `return 28`.
}
}];
}

def LikelihoodDocs : Documentation {
let Category = DocCatStmt;
let Heading = "likely and unlikely";
Expand Down Expand Up @@ -4629,7 +4657,7 @@ For more information see
}];
}

def AssumptionDocs : Documentation {
def OMPAssumeDocs : Documentation {
let Category = DocCatFunction;
let Heading = "assume";
let Content = [{
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ def note_constexpr_unsupported_flexible_array : Note<
"flexible array initialization is not yet supported">;
def note_constexpr_non_const_vectorelements : Note<
"cannot determine number of elements for sizeless vectors in a constant expression">;
def note_constexpr_assumption_failed : Note<
"assumption evaluated to false">;
def err_experimental_clang_interp_failed : Error<
"the experimental clang interpreter failed to evaluate an expression">;

Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -1133,9 +1133,11 @@ def NonGCC : DiagGroup<"non-gcc",
def CXX14Attrs : DiagGroup<"c++14-attribute-extensions">;
def CXX17Attrs : DiagGroup<"c++17-attribute-extensions">;
def CXX20Attrs : DiagGroup<"c++20-attribute-extensions">;
def CXX23Attrs : DiagGroup<"c++23-attribute-extensions">;
def FutureAttrs : DiagGroup<"future-attribute-extensions", [CXX14Attrs,
CXX17Attrs,
CXX20Attrs]>;
CXX20Attrs,
CXX23Attrs]>;

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

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,9 @@ def err_ms_property_expected_comma_or_rparen : Error<
def err_ms_property_initializer : Error<
"property declaration cannot have a default member initializer">;

def err_assume_attr_expects_cond_expr : Error<
"use of this expression in an %0 attribute requires parentheses">;

def warn_cxx20_compat_explicit_bool : Warning<
"this expression will be parsed as explicit(bool) in C++20">,
InGroup<CXX20Compat>, DefaultIgnore;
Expand Down
9 changes: 7 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -855,10 +855,10 @@ def note_strncat_wrong_size : Note<
def warn_assume_side_effects : Warning<
"the argument to %0 has side effects that will be discarded">,
InGroup<DiagGroup<"assume">>;
def warn_assume_attribute_string_unknown : Warning<
def warn_omp_assume_attribute_string_unknown : Warning<
"unknown assumption string '%0'; attribute is potentially ignored">,
InGroup<UnknownAssumption>;
def warn_assume_attribute_string_unknown_suggested : Warning<
def warn_omp_assume_attribute_string_unknown_suggested : Warning<
"unknown assumption string '%0' may be misspelled; attribute is potentially "
"ignored, did you mean '%1'?">,
InGroup<MisspelledAssumption>;
Expand Down Expand Up @@ -9107,6 +9107,8 @@ def ext_cxx17_attr : Extension<
"use of the %0 attribute is a C++17 extension">, InGroup<CXX17Attrs>;
def ext_cxx20_attr : Extension<
"use of the %0 attribute is a C++20 extension">, InGroup<CXX20Attrs>;
def ext_cxx23_attr : Extension<
"use of the %0 attribute is a C++23 extension">, InGroup<CXX23Attrs>;

def warn_unused_comparison : Warning<
"%select{equality|inequality|relational|three-way}0 comparison result unused">,
Expand Down Expand Up @@ -10173,6 +10175,9 @@ def err_fallthrough_attr_outside_switch : Error<
def err_fallthrough_attr_invalid_placement : Error<
"fallthrough annotation does not directly precede switch label">;

def err_assume_attr_args : Error<
"attribute '%0' requires a single expression argument">;

def warn_unreachable_default : Warning<
"default label in switch which covers all enumeration values">,
InGroup<CoveredSwitchDefault>, DefaultIgnore;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ LANGOPT(RegCall4, 1, 0, "Set __regcall4 as a default calling convention to respe

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

LANGOPT(CXXAssumptions, 1, 1, "Enable or disable codegen and compile-time checks for C++23's [[assume]] attribute")

ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2,
StrictFlexArraysLevelKind::Default,
"Rely on strict definition of flexible arrays")
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3789,6 +3789,12 @@ def foptimization_record_passes_EQ : Joined<["-"], "foptimization-record-passes=
HelpText<"Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)">,
MetaVarName<"<regex>">;

defm assumptions : BoolFOption<"assumptions",
LangOpts<"CXXAssumptions">, DefaultTrue,
NegFlag<SetFalse, [], [ClangOption, CC1Option],
"Disable codegen and compile-time checks for C++23's [[assume]] attribute">,
PosFlag<SetTrue>>;

def fvectorize : Flag<["-"], "fvectorize">, Group<f_Group>,
HelpText<"Enable the loop vectorization passes">;
def fno_vectorize : Flag<["-"], "fno-vectorize">, Group<f_Group>;
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,7 @@ class Parser : public CodeCompletionHandler {
ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause);
// Expr that doesn't include commas.
ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
ExprResult ParseConditionalExpression();

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

/// Parse a C++23 assume() attribute. Returns true on error.
bool ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
IdentifierInfo *AttrName,
SourceLocation AttrNameLoc,
SourceLocation *EndLoc);

IdentifierInfo *TryParseCXX11AttributeIdentifier(
SourceLocation &Loc,
Sema::AttributeCompletion Completion = Sema::AttributeCompletion::None,
Expand Down
10 changes: 8 additions & 2 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -9002,6 +9002,12 @@ class Sema final {
void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs,
SmallVectorImpl<const Attr *> &OutAttrs);

ExprResult ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
SourceRange Range);
ExprResult BuildCXXAssumeExpr(Expr *Assumption,
const IdentifierInfo *AttrName,
SourceRange Range);

///@}

//
Expand Down Expand Up @@ -14693,10 +14699,10 @@ class Sema final {
SmallVector<OMPDeclareVariantScope, 4> OMPDeclareVariantScopes;

/// The current `omp begin/end assumes` scopes.
SmallVector<AssumptionAttr *, 4> OMPAssumeScoped;
SmallVector<OMPAssumeAttr *, 4> OMPAssumeScoped;

/// All `omp assumes` we encountered so far.
SmallVector<AssumptionAttr *, 4> OMPAssumeGlobal;
SmallVector<OMPAssumeAttr *, 4> OMPAssumeGlobal;

/// OMPD_loop is mapped to OMPD_for, OMPD_distribute or OMPD_simd depending
/// on the parameter of the bind clause. In the methods for the
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5582,6 +5582,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
MSConstexprContextRAII ConstexprContext(
*Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
isa<ReturnStmt>(SS));

auto LO = Info.getCtx().getLangOpts();
if (LO.CXXAssumptions && !LO.MSVCCompat) {
for (auto *Attr : AS->getAttrs()) {
auto *AA = dyn_cast<CXXAssumeAttr>(Attr);
if (!AA)
continue;

auto *Assumption = AA->getAssumption();
if (Assumption->isValueDependent())
return ESR_Failed;

bool Value;
if (!EvaluateAsBooleanCondition(Assumption, Value, Info))
return ESR_Failed;
if (!Value) {
Info.CCEDiag(Assumption->getExprLoc(),
diag::note_constexpr_assumption_failed);
return ESR_Failed;
}
}
}

return EvaluateStmt(Result, Info, SS, Case);
}

Expand Down
8 changes: 4 additions & 4 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1796,14 +1796,14 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
FuncAttrs.addAttribute("aarch64_inout_zt0");
}

static void AddAttributesFromAssumes(llvm::AttrBuilder &FuncAttrs,
const Decl *Callee) {
static void AddAttributesFromOMPAssumes(llvm::AttrBuilder &FuncAttrs,
const Decl *Callee) {
if (!Callee)
return;

SmallVector<StringRef, 4> Attrs;

for (const AssumptionAttr *AA : Callee->specific_attrs<AssumptionAttr>())
for (const OMPAssumeAttr *AA : Callee->specific_attrs<OMPAssumeAttr>())
AA->getAssumption().split(Attrs, ",");

if (!Attrs.empty())
Expand Down Expand Up @@ -2344,7 +2344,7 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,

// Attach assumption attributes to the declaration. If this is a call
// site, attach assumptions from the caller to the call as well.
AddAttributesFromAssumes(FuncAttrs, TargetDecl);
AddAttributesFromOMPAssumes(FuncAttrs, TargetDecl);

bool HasOptnone = false;
// The NoBuiltinAttr attached to the target FunctionDecl.
Expand Down
12 changes: 10 additions & 2 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,11 +728,19 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
case attr::AlwaysInline:
alwaysinline = true;
break;
case attr::MustTail:
case attr::MustTail: {
const Stmt *Sub = S.getSubStmt();
const ReturnStmt *R = cast<ReturnStmt>(Sub);
musttail = cast<CallExpr>(R->getRetValue()->IgnoreParens());
break;
} break;
case attr::CXXAssume: {
const Expr *Assumption = cast<CXXAssumeAttr>(A)->getAssumption();
if (getLangOpts().CXXAssumptions &&
!Assumption->HasSideEffects(getContext())) {
llvm::Value *AssumptionVal = EvaluateExprAsBool(Assumption);
Builder.CreateAssumption(AssumptionVal);
}
} break;
}
}
SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge);
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6976,6 +6976,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
(!IsWindowsMSVC || IsMSVC2015Compatible)))
CmdArgs.push_back("-fno-threadsafe-statics");

// Add -fno-assumptions, if it was specified.
if (!Args.hasFlag(options::OPT_fassumptions, options::OPT_fno_assumptions,
true))
CmdArgs.push_back("-fno-assumptions");

// -fgnu-keywords default varies depending on language; only pass if
// specified.
Args.AddLastArg(CmdArgs, options::OPT_fgnu_keywords,
Expand Down
62 changes: 61 additions & 1 deletion clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4528,6 +4528,61 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
}
}

/// Parse the argument to C++23's [[assume()]] attribute.
bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
IdentifierInfo *AttrName,
SourceLocation AttrNameLoc,
SourceLocation *EndLoc) {
assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();

// [dcl.attr.assume]: The expression is potentially evaluated.
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);

TentativeParsingAction TPA(*this);
ExprResult Res(
Actions.CorrectDelayedTyposInExpr(ParseConditionalExpression()));
if (Res.isInvalid()) {
TPA.Commit();
SkipUntil(tok::r_paren, tok::r_square, StopAtSemi | StopBeforeMatch);
if (Tok.is(tok::r_paren))
T.consumeClose();
return true;
}

if (!Tok.isOneOf(tok::r_paren, tok::r_square)) {
// Emit a better diagnostic if this is an otherwise valid expression that
// is not allowed here.
TPA.Revert();
Res = ParseExpression();
if (!Res.isInvalid()) {
auto *E = Res.get();
Diag(E->getExprLoc(), diag::err_assume_attr_expects_cond_expr)
<< AttrName << FixItHint::CreateInsertion(E->getBeginLoc(), "(")
<< FixItHint::CreateInsertion(PP.getLocForEndOfToken(E->getEndLoc()),
")")
<< E->getSourceRange();
}

T.consumeClose();
return true;
}

TPA.Commit();
ArgsUnion Assumption = Res.get();
auto RParen = Tok.getLocation();
T.consumeClose();
Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), nullptr,
SourceLocation(), &Assumption, 1, ParsedAttr::Form::CXX11());

if (EndLoc)
*EndLoc = RParen;

return false;
}

/// ParseCXX11AttributeArgs -- Parse a C++11 attribute-argument-clause.
///
/// [C++11] attribute-argument-clause:
Expand Down Expand Up @@ -4596,7 +4651,12 @@ bool Parser::ParseCXX11AttributeArgs(
if (ScopeName && (ScopeName->isStr("clang") || ScopeName->isStr("_Clang")))
NumArgs = ParseClangAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc,
ScopeName, ScopeLoc, Form);
else
// So does C++23's assume() attribute.
else if (!ScopeName && AttrName->isStr("assume")) {
if (ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc))
return true;
NumArgs = 1;
} else
NumArgs = ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc,
ScopeName, ScopeLoc, Form);

Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,19 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
return ParseRHSOfBinaryExpression(LHS, prec::Assignment);
}

ExprResult Parser::ParseConditionalExpression() {
if (Tok.is(tok::code_completion)) {
cutOffParsing();
Actions.CodeCompleteExpression(getCurScope(),
PreferredType.get(Tok.getLocation()));
return ExprError();
}

ExprResult LHS = ParseCastExpression(
AnyCastExpr, /*isAddressOfOperand=*/false, NotTypeCast);
return ParseRHSOfBinaryExpression(LHS, prec::Conditional);
}

/// Parse an assignment expression where part of an Objective-C message
/// send has already been parsed.
///
Expand Down
Loading