Skip to content

Commit d9df710

Browse files
authored
[clang][Sema] Fix and reapply 'Declare builtins used in #pragma intrinsic #138205' (#142019)
I had to revert #138205 in #141994 because it broke the Chrome build. The problem came down to the following: ```c++ unsigned __int64 _umul128(unsigned __int64, unsigned __int64, unsigned __int64 *); namespace {} #pragma intrinsic(_umul128) void foo() { unsigned __int64 carry; unsigned __int64 low = _umul128(0, 0, &carry); } ``` When processing the `#pragma intrinsic` line, we do a name lookup to see if the builtin was previously declared. In this case the lookup fails because the current namespace of the parser and sema is the above namespace scope. The processing of the pragma happens as part of the namespace close parsing. This is usually fine because most pragmas don't care about scopes. However, that's not true for this and other MS pragmas. To fix this, we change the `#pragma intrinsic` processing to be the same as other MS pragmas such as "optimize". Those are processed like a declaration, and because of that we have the correct current scope, so the lookup succeeds. I added a test case that locks down the Chrome fix, as well as manually tested the Chrome build and confirmed it passed.
1 parent e29eb66 commit d9df710

File tree

5 files changed

+123
-58
lines changed

5 files changed

+123
-58
lines changed

clang/include/clang/Parse/Parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7074,6 +7074,10 @@ class Parser : public CodeCompletionHandler {
70747074
bool HandlePragmaMSOptimize(StringRef PragmaName,
70757075
SourceLocation PragmaLocation);
70767076

7077+
// #pragma intrinsic("foo")
7078+
bool HandlePragmaMSIntrinsic(StringRef PragmaName,
7079+
SourceLocation PragmaLocation);
7080+
70777081
/// Handle the annotation token produced for
70787082
/// #pragma align...
70797083
void HandlePragmaAlign();

clang/lib/Parse/ParsePragma.cpp

Lines changed: 53 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,6 @@ struct PragmaMSRuntimeChecksHandler : public EmptyPragmaHandler {
300300
PragmaMSRuntimeChecksHandler() : EmptyPragmaHandler("runtime_checks") {}
301301
};
302302

303-
struct PragmaMSIntrinsicHandler : public PragmaHandler {
304-
PragmaMSIntrinsicHandler() : PragmaHandler("intrinsic") {}
305-
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
306-
Token &FirstToken) override;
307-
};
308-
309303
// "\#pragma fenv_access (on)".
310304
struct PragmaMSFenvAccessHandler : public PragmaHandler {
311305
PragmaMSFenvAccessHandler() : PragmaHandler("fenv_access") {}
@@ -517,7 +511,7 @@ void Parser::initializePragmaHandlers() {
517511
PP.AddPragmaHandler(MSOptimize.get());
518512
MSRuntimeChecks = std::make_unique<PragmaMSRuntimeChecksHandler>();
519513
PP.AddPragmaHandler(MSRuntimeChecks.get());
520-
MSIntrinsic = std::make_unique<PragmaMSIntrinsicHandler>();
514+
MSIntrinsic = std::make_unique<PragmaMSPragma>("intrinsic");
521515
PP.AddPragmaHandler(MSIntrinsic.get());
522516
MSFenvAccess = std::make_unique<PragmaMSFenvAccessHandler>();
523517
PP.AddPragmaHandler(MSFenvAccess.get());
@@ -1046,7 +1040,8 @@ void Parser::HandlePragmaMSPragma() {
10461040
.Case("strict_gs_check", &Parser::HandlePragmaMSStrictGuardStackCheck)
10471041
.Case("function", &Parser::HandlePragmaMSFunction)
10481042
.Case("alloc_text", &Parser::HandlePragmaMSAllocText)
1049-
.Case("optimize", &Parser::HandlePragmaMSOptimize);
1043+
.Case("optimize", &Parser::HandlePragmaMSOptimize)
1044+
.Case("intrinsic", &Parser::HandlePragmaMSIntrinsic);
10501045

10511046
if (!(this->*Handler)(PragmaName, PragmaLocation)) {
10521047
// Pragma handling failed, and has been diagnosed. Slurp up the tokens
@@ -3762,56 +3757,6 @@ void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP,
37623757
/*DisableMacroExpansion=*/false, /*IsReinject=*/false);
37633758
}
37643759

3765-
/// Handle the Microsoft \#pragma intrinsic extension.
3766-
///
3767-
/// The syntax is:
3768-
/// \code
3769-
/// #pragma intrinsic(memset)
3770-
/// #pragma intrinsic(strlen, memcpy)
3771-
/// \endcode
3772-
///
3773-
/// Pragma intrisic tells the compiler to use a builtin version of the
3774-
/// function. Clang does it anyway, so the pragma doesn't really do anything.
3775-
/// Anyway, we emit a warning if the function specified in \#pragma intrinsic
3776-
/// isn't an intrinsic in clang and suggest to include intrin.h.
3777-
void PragmaMSIntrinsicHandler::HandlePragma(Preprocessor &PP,
3778-
PragmaIntroducer Introducer,
3779-
Token &Tok) {
3780-
PP.Lex(Tok);
3781-
3782-
if (Tok.isNot(tok::l_paren)) {
3783-
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
3784-
<< "intrinsic";
3785-
return;
3786-
}
3787-
PP.Lex(Tok);
3788-
3789-
bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H");
3790-
3791-
while (Tok.is(tok::identifier)) {
3792-
IdentifierInfo *II = Tok.getIdentifierInfo();
3793-
if (!II->getBuiltinID())
3794-
PP.Diag(Tok.getLocation(), diag::warn_pragma_intrinsic_builtin)
3795-
<< II << SuggestIntrinH;
3796-
3797-
PP.Lex(Tok);
3798-
if (Tok.isNot(tok::comma))
3799-
break;
3800-
PP.Lex(Tok);
3801-
}
3802-
3803-
if (Tok.isNot(tok::r_paren)) {
3804-
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
3805-
<< "intrinsic";
3806-
return;
3807-
}
3808-
PP.Lex(Tok);
3809-
3810-
if (Tok.isNot(tok::eod))
3811-
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
3812-
<< "intrinsic";
3813-
}
3814-
38153760
bool Parser::HandlePragmaMSFunction(StringRef PragmaName,
38163761
SourceLocation PragmaLocation) {
38173762
Token FirstTok = Tok;
@@ -3907,6 +3852,56 @@ bool Parser::HandlePragmaMSOptimize(StringRef PragmaName,
39073852
return true;
39083853
}
39093854

3855+
/// Handle the Microsoft \#pragma intrinsic extension.
3856+
///
3857+
/// The syntax is:
3858+
/// \code
3859+
/// #pragma intrinsic(memset)
3860+
/// #pragma intrinsic(strlen, memcpy)
3861+
/// \endcode
3862+
///
3863+
/// Pragma intrisic tells the compiler to use a builtin version of the
3864+
/// function. Clang does it anyway, so the pragma doesn't really do anything.
3865+
/// Anyway, we emit a warning if the function specified in \#pragma intrinsic
3866+
/// isn't an intrinsic in clang and suggest to include intrin.h, as well as
3867+
/// declare the builtin if it has not been declared.
3868+
bool Parser::HandlePragmaMSIntrinsic(StringRef PragmaName,
3869+
SourceLocation PragmaLocation) {
3870+
if (ExpectAndConsume(tok::l_paren, diag::warn_pragma_expected_lparen,
3871+
PragmaName))
3872+
return false;
3873+
3874+
bool SuggestIntrinH = !PP.isMacroDefined("__INTRIN_H");
3875+
3876+
while (Tok.is(tok::identifier)) {
3877+
IdentifierInfo *II = Tok.getIdentifierInfo();
3878+
if (!II->getBuiltinID())
3879+
PP.Diag(Tok.getLocation(), diag::warn_pragma_intrinsic_builtin)
3880+
<< II << SuggestIntrinH;
3881+
// If the builtin hasn't already been declared, declare it now.
3882+
DeclarationNameInfo NameInfo(II, Tok.getLocation());
3883+
LookupResult Previous(Actions, NameInfo, Sema::LookupOrdinaryName,
3884+
RedeclarationKind::NotForRedeclaration);
3885+
Actions.LookupName(Previous, Actions.getCurScope(),
3886+
/*CreateBuiltins*/ false);
3887+
if (Previous.empty())
3888+
Actions.LazilyCreateBuiltin(II, II->getBuiltinID(), Actions.getCurScope(),
3889+
/*ForRedeclaration*/ true, Tok.getLocation());
3890+
PP.Lex(Tok);
3891+
if (Tok.isNot(tok::comma))
3892+
break;
3893+
PP.Lex(Tok);
3894+
}
3895+
if (ExpectAndConsume(tok::r_paren, diag::warn_pragma_expected_rparen,
3896+
PragmaName))
3897+
return false;
3898+
3899+
if (ExpectAndConsume(tok::eof, diag::warn_pragma_extra_tokens_at_eol,
3900+
PragmaName))
3901+
return false;
3902+
return true;
3903+
}
3904+
39103905
void PragmaForceCUDAHostDeviceHandler::HandlePragma(
39113906
Preprocessor &PP, PragmaIntroducer Introducer, Token &Tok) {
39123907
Token FirstTok = Tok;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifdef USE_PRAGMA_BEFORE
2+
#pragma intrinsic(_InterlockedOr64)
3+
#endif
4+
5+
#define MACRO(x,y) _InterlockedOr64(x,y);
6+
7+
#ifdef USE_PRAGMA_AFTER
8+
#pragma intrinsic(_InterlockedOr64)
9+
#endif
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-compatibility -fsyntax-only -verify -DOUTSIDE %s
2+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-compatibility -fsyntax-only -verify -DINSIDE %s
3+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-compatibility -fsyntax-only -verify -DNESTED %s
4+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-compatibility -fsyntax-only -verify -DOUTSIDE -DEXTERN %s
5+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-compatibility -fsyntax-only -verify -DINSIDE -DEXTERN %s
6+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-compatibility -fsyntax-only -verify -DNESTED -DEXTERN %s
7+
8+
// expected-no-diagnostics
9+
#ifdef EXTERN
10+
extern "C"
11+
#endif
12+
unsigned __int64 _umul128(unsigned __int64, unsigned __int64,
13+
unsigned __int64 *);
14+
namespace {
15+
#ifdef INSIDE
16+
#pragma intrinsic(_umul128)
17+
#endif
18+
#ifdef NESTED
19+
namespace {
20+
#pragma intrinsic(_umul128)
21+
}
22+
#endif
23+
}
24+
25+
#ifdef OUTSIDE
26+
#pragma intrinsic(_umul128)
27+
#endif
28+
29+
void foo() {
30+
unsigned __int64 carry;
31+
unsigned __int64 low = _umul128(0, 0, &carry);
32+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -triple arm64-windows -isystem %S/Inputs %s -DUSE_PRAGMA_BEFORE
2+
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -triple arm64-windows -isystem %S/Inputs %s -DUSE_PRAGMA_AFTER
3+
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -triple arm64-windows -isystem %S/Inputs %s -DUSE_PRAGMA_AFTER_USE
4+
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -triple arm64-windows -isystem %S/Inputs %s -DUSE_PRAGMA_SAME_FILE
5+
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -triple arm64-windows -isystem %S/Inputs %s
6+
7+
#if defined(USE_PRAGMA_BEFORE) || defined(USE_PRAGMA_AFTER) || defined(USE_PRAGMA_SAME_FILE)
8+
// expected-no-diagnostics
9+
#else
10+
// expected-error@+10 {{call to undeclared library function '_InterlockedOr64'}}
11+
// expected-note@+9 {{include the header <intrin.h> or explicitly provide a declaration for '_InterlockedOr64'}}
12+
#endif
13+
#include <builtin-system-header.h>
14+
15+
#ifdef USE_PRAGMA_SAME_FILE
16+
#pragma intrinsic(_InterlockedOr64)
17+
#endif
18+
19+
void foo() {
20+
MACRO(0,0);
21+
}
22+
23+
#ifdef USE_PRAGMA_AFTER_USE
24+
#pragma intrinsic(_InterlockedOr64)
25+
#endif

0 commit comments

Comments
 (0)