diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -230,6 +230,8 @@ LANGOPT(HIPUseNewLaunchAPI, 1, 0, "Use new kernel launching API for HIP") +LANGOPT(ExperimentalTransformPragma, 1, 0, "Enable #pragma clang transform") + LANGOPT(SizedDeallocation , 1, 0, "sized deallocation") LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -830,6 +830,11 @@ PRAGMA_ANNOTATION(pragma_openmp) PRAGMA_ANNOTATION(pragma_openmp_end) +// Annotations for code transformation pragmas +// #pragma clang transform ... +PRAGMA_ANNOTATION(pragma_transform) +PRAGMA_ANNOTATION(pragma_transform_end) + // Annotations for loop pragma directives #pragma clang loop ... // The lexer produces these so that they only take effect when the parser // handles #pragma loop ... directives. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1661,6 +1661,10 @@ Flags<[NoArgumentUnused, HelpHidden]>; def static_openmp: Flag<["-"], "static-openmp">, HelpText<"Use the static host OpenMP runtime while linking.">; +def fexperimental_transform_pragma : Flag<["-"], "fexperimental-transform-pragma">, Group, + Flags<[CC1Option, HelpHidden]>, HelpText<"Parse experimental #pragma clang transform directives.">; +def fno_experimental_transform_pragma : Flag<["-"], "fno-experimental-transform-pragma">, Group, + Flags<[CC1Option, HelpHidden]>; def fno_optimize_sibling_calls : Flag<["-"], "fno-optimize-sibling-calls">, Group; def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group; def fno_escaping_block_tail_calls : Flag<["-"], "fno-escaping-block-tail-calls">, Group, Flags<[CC1Option]>; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -200,6 +200,7 @@ std::unique_ptr STDCCXLIMITHandler; std::unique_ptr STDCUnknownHandler; std::unique_ptr AttributePragmaHandler; + std::unique_ptr TransformHandler; std::unique_ptr CommentSemaHandler; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3227,6 +3227,11 @@ Opts.CompleteMemberPointers = Args.hasArg(OPT_fcomplete_member_pointers); Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj); + + // Enable or disable support for #pragma clang transform. + Opts.ExperimentalTransformPragma = + Args.hasFlag(options::OPT_fexperimental_transform_pragma, + options::OPT_fno_experimental_transform_pragma, false); } static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -247,6 +247,12 @@ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &FirstToken) override; +struct PragmaTransformHandler : public PragmaHandler { + PragmaTransformHandler() : PragmaHandler("transform") {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; +}; + private: Sema &Actions; }; @@ -382,6 +388,11 @@ AttributePragmaHandler = std::make_unique(AttrFactory); PP.AddPragmaHandler("clang", AttributePragmaHandler.get()); + + if (getLangOpts().ExperimentalTransformPragma) { + TransformHandler = std::make_unique(); + PP.AddPragmaHandler("clang", TransformHandler.get()); + } } void Parser::resetPragmaHandlers() { @@ -487,6 +498,11 @@ PP.RemovePragmaHandler("clang", AttributePragmaHandler.get()); AttributePragmaHandler.reset(); + + if (getLangOpts().ExperimentalTransformPragma) { + PP.RemovePragmaHandler("clang", TransformHandler.get()); + TransformHandler.reset(); + } } /// Handle the annotation token produced for #pragma unused(...) @@ -3008,6 +3024,71 @@ /*DisableMacroExpansion=*/false, /*IsReinject=*/false); } +/// Handle +/// #pragma clang transform ... +void PragmaTransformHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &FirstTok) { + // "clang" token is not passed + // "transform" is FirstTok + // Everything up until tok::eod (or tok::eof) is wrapped between + // tok::annot_pragma_transform and tok::annot_pragma_transform_end, and + // pushed-back into the token stream. The tok::eod/eof is consumed as well: + // + // Token stream before: + // FirstTok:"transform" | [clauses..] eod ... + // + // Token stream after : + // "transform" [clauses..] eod | ... + // After pushing the annotation tokens: + // + // | annot_pragma_transform [clauses..] annot_pragma_transform_end ... + // + // The symbol | is before the next token returned by PP.Lex() + SmallVector PragmaToks; + + Token StartTok; + StartTok.startToken(); + StartTok.setKind(tok::annot_pragma_transform); + StartTok.setLocation(FirstTok.getLocation()); + PragmaToks.push_back(StartTok); + + SourceLocation EodLoc = FirstTok.getLocation(); + while (true) { + Token Tok; + PP.Lex(Tok); + + // TODO: Handle nested pragmas as in r325369. + assert(!Tok.isAnnotation()); + assert(Tok.isNot(tok::annot_pragma_transform)); + assert(Tok.isNot(tok::annot_pragma_transform_end)); + assert(Tok.isNot(tok::annot_pragma_openmp)); + assert(Tok.isNot(tok::annot_pragma_openmp_end)); + assert(Tok.isNot(tok::annot_pragma_loop_hint)); + + if (Tok.is(tok::eod) || Tok.is(tok::eof)) { + EodLoc = Tok.getLocation(); + break; + } + + PragmaToks.push_back(Tok); + } + + Token EndTok; + EndTok.startToken(); + EndTok.setKind(tok::annot_pragma_transform_end); + EndTok.setLocation(EodLoc); + PragmaToks.push_back(EndTok); + + // Copy tokens for the preprocessor to own and free. + auto Toks = std::make_unique(PragmaToks.size()); + std::copy(PragmaToks.begin(), PragmaToks.end(), Toks.get()); + + // Handle in parser + PP.EnterTokenStream(std::move(Toks), PragmaToks.size(), + /*DisableMacroExpansion=*/false, /*IsReinject=*/false); +} + /// Handle the Microsoft \#pragma intrinsic extension. /// /// The syntax is: diff --git a/clang/test/Parser/pragma-no-transform.cpp b/clang/test/Parser/pragma-no-transform.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/pragma-no-transform.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++11 -fno-experimental-transform-pragma -Wall -verify %s + +void pragma_transform(int *List, int Length) { +/* expected-warning@+1 {{unknown pragma ignored}} */ +#pragma clang transform unroll partial(4) + for (int i = 0; i < Length; i+=1) + List[i] = i; +} +