Skip to content

[clang] Macro for constant rounding mode #92699

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,8 @@ def note_pp_module_begin_here : Note<
"entering module '%0' due to this pragma">;
def err_pp_module_build_missing_end : Error<
"no matching '#pragma clang module endbuild' for this '#pragma clang module build'">;
def err_pragma_round_expected_mode : Error<"expected rounding mode">;
def err_pragma_round_unknown_mode : Error<"invalid or unsupported rounding mode">;

def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
def err_paste_at_start : Error<
Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1269,9 +1269,6 @@ def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
def warn_stdc_fenv_round_not_supported :
Warning<"pragma STDC FENV_ROUND is not supported">,
InGroup<UnknownPragmas>;
def warn_stdc_unknown_rounding_mode : Warning<
"invalid or unsupported rounding mode in '#pragma STDC FENV_ROUND' - ignored">,
InGroup<IgnoredPragmas>;
def warn_pragma_fp_ignored : Warning<
"'#pragma %0' is not supported on this target - ignored">,
InGroup<IgnoredPragmas>;
Expand Down
16 changes: 16 additions & 0 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class Preprocessor {
IdentifierInfo *Ident__is_target_variant_os;
IdentifierInfo *Ident__is_target_variant_environment;
IdentifierInfo *Ident__FLT_EVAL_METHOD__; // __FLT_EVAL_METHOD
IdentifierInfo *Ident__ROUNDING_MODE__; // __ROUNDING_MODE__

// Weak, only valid (and set) while InMacroArgs is true.
Token* ArgMacro;
Expand Down Expand Up @@ -1162,6 +1163,17 @@ class Preprocessor {
/// skipped.
llvm::DenseMap<const char *, unsigned> RecordedSkippedRanges;

/// Nesting level of curly braces.
unsigned CurlyBraceLevel = 0;

/// Information about an instance of pragma FENV_ROUND.
struct RoundingPragmaRecord {
unsigned Level;
LangOptions::RoundingMode RM;
};

SmallVector<RoundingPragmaRecord, 8> RoundingPragmas;

void updateOutOfDateIdentifier(const IdentifierInfo &II) const;

public:
Expand Down Expand Up @@ -2356,6 +2368,10 @@ class Preprocessor {
TUFPEvalMethod = Val;
}

void setRoundingMode(LangOptions::RoundingMode RM);

LangOptions::RoundingMode getCurrentRoundingMode() const;

/// Retrieves the module that we're currently building, if any.
Module *getCurrentModule();

Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> NoUnrollAndJamHintHandler;
std::unique_ptr<PragmaHandler> FPHandler;
std::unique_ptr<PragmaHandler> STDCFenvAccessHandler;
std::unique_ptr<PragmaHandler> STDCFenvRoundHandler;
std::unique_ptr<PragmaHandler> STDCCXLIMITHandler;
std::unique_ptr<PragmaHandler> STDCUnknownHandler;
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Frontend/PrintPreprocessedOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1001,9 +1001,14 @@ void clang::DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream *OS,
"#pragma clang", Callbacks,
/*RequireTokenExpansion=*/PP.getLangOpts().MicrosoftExt));

std::unique_ptr<UnknownPragmaHandler> STDCHandler(new UnknownPragmaHandler(
"#pragma STDC", Callbacks,
/*RequireTokenExpansion=*/PP.getLangOpts().MicrosoftExt));

PP.AddPragmaHandler(MicrosoftExtHandler.get());
PP.AddPragmaHandler("GCC", GCCHandler.get());
PP.AddPragmaHandler("clang", ClangHandler.get());
PP.AddPragmaHandler("STDC", STDCHandler.get());

// The tokens after pragma omp need to be expanded.
//
Expand Down Expand Up @@ -1050,4 +1055,5 @@ void clang::DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream *OS,
PP.RemovePragmaHandler("GCC", GCCHandler.get());
PP.RemovePragmaHandler("clang", ClangHandler.get());
PP.RemovePragmaHandler("omp", OpenMPHandler.get());
PP.RemovePragmaHandler("STDC", STDCHandler.get());
}
25 changes: 25 additions & 0 deletions clang/lib/Lex/PPMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ void Preprocessor::RegisterBuiltinMacros() {
Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
Ident_Pragma = RegisterBuiltinMacro(*this, "_Pragma");
Ident__FLT_EVAL_METHOD__ = RegisterBuiltinMacro(*this, "__FLT_EVAL_METHOD__");
Ident__ROUNDING_MODE__ = RegisterBuiltinMacro(*this, "__ROUNDING_MODE__");

// C++ Standing Document Extensions.
if (getLangOpts().CPlusPlus)
Expand Down Expand Up @@ -1654,6 +1655,30 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
Diag(Tok, diag::err_illegal_use_of_flt_eval_macro);
Diag(getLastFPEvalPragmaLocation(), diag::note_pragma_entered_here);
}
} else if (II == Ident__ROUNDING_MODE__) {
switch (getCurrentRoundingMode()) {
case LangOptions::RoundingMode::TowardZero:
OS << "_rtz";
break;
case LangOptions::RoundingMode::NearestTiesToEven:
OS << "_rte";
break;
case LangOptions::RoundingMode::TowardPositive:
OS << "_rtp";
break;
case LangOptions::RoundingMode::TowardNegative:
OS << "_rtn";
break;
case LangOptions::RoundingMode::NearestTiesToAway:
OS << "_rta";
break;
case LangOptions::RoundingMode::Dynamic:
OS << "";
break;
default:
llvm_unreachable("unknown rounding mode");
}
Tok.setKind(tok::string_literal);
} else if (II == Ident__COUNTER__) {
// __COUNTER__ expands to a simple numeric value.
OS << CounterValue++;
Expand Down
48 changes: 48 additions & 0 deletions clang/lib/Lex/Pragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2125,6 +2125,51 @@ struct PragmaFinalHandler : public PragmaHandler {
}
};

struct FenvRoundHandler : public PragmaHandler {
FenvRoundHandler() : PragmaHandler("FENV_ROUND") {}

void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
Token &Tok) override {
PP.Lex(Tok);
if (Tok.isNot(tok::identifier)) {
PP.Diag(Tok.getLocation(), diag::err_pragma_round_expected_mode);
return;
}
IdentifierInfo *II = Tok.getIdentifierInfo();

auto RM =
llvm::StringSwitch<llvm::RoundingMode>(II->getName())
.Case("FE_TOWARDZERO", llvm::RoundingMode::TowardZero)
.Case("FE_TONEAREST", llvm::RoundingMode::NearestTiesToEven)
.Case("FE_UPWARD", llvm::RoundingMode::TowardPositive)
.Case("FE_DOWNWARD", llvm::RoundingMode::TowardNegative)
.Case("FE_TONEARESTFROMZERO", llvm::RoundingMode::NearestTiesToAway)
.Case("FE_DYNAMIC", llvm::RoundingMode::Dynamic)
.Default(llvm::RoundingMode::Invalid);
if (RM == llvm::RoundingMode::Invalid) {
PP.Diag(Tok.getLocation(), diag::err_pragma_round_unknown_mode);
return;
}
PP.Lex(Tok);

if (Tok.isNot(tok::eod)) {
PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol)
<< "pragma STDC FENV_ROUND";
return;
}

PP.setRoundingMode(RM);

Token PragmaFenvRound;
PragmaFenvRound.startToken();
PragmaFenvRound.setKind(tok::annot_pragma_fenv_round);
PragmaFenvRound.setAnnotationRange(SourceRange(Tok.getLocation()));
PragmaFenvRound.setAnnotationValue(
reinterpret_cast<void *>(static_cast<uintptr_t>(RM)));
PP.EnterToken(PragmaFenvRound, /*IsReinject*/ false);
}
};

} // namespace

/// RegisterBuiltinPragmas - Install the standard preprocessor pragmas:
Expand Down Expand Up @@ -2184,6 +2229,9 @@ void Preprocessor::RegisterBuiltinPragmas() {
AddPragmaHandler(new PragmaManagedHandler("unmanaged"));
}

// #pragma STDC FENV_ROUND
AddPragmaHandler("STDC", new FenvRoundHandler());

// Pragmas added by plugins
for (const PragmaHandlerRegistry::entry &handler :
PragmaHandlerRegistry::entries()) {
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/Lex/Preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,16 @@ void Preprocessor::Lex(Token &Result) {
LastTokenWasAt = Result.is(tok::at);
--LexLevel;

if (Result.is(tok::l_brace)) {
CurlyBraceLevel++;
} else if (Result.is(tok::r_brace)) {
if (!RoundingPragmas.empty() &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can move the !RoundingPragmas.empty() outside the if statement, so we can skip a little more of the code if the user isn't using any pragmas? I mean, it probably doesn't make a big difference, but Lex() is performance-sensitive.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good idea, thank you.

RoundingPragmas.back().Level >= CurlyBraceLevel)
RoundingPragmas.pop_back();
if (CurlyBraceLevel > 0)
CurlyBraceLevel--;
}

if ((LexLevel == 0 || PreprocessToken) &&
!Result.getFlag(Token::IsReinjected)) {
if (LexLevel == 0)
Expand Down Expand Up @@ -1582,3 +1592,20 @@ const char *Preprocessor::getCheckPoint(FileID FID, const char *Start) const {

return nullptr;
}

void Preprocessor::setRoundingMode(LangOptions::RoundingMode RM) {
if (!RoundingPragmas.empty()) {
RoundingPragmaRecord &LastRecord = RoundingPragmas.back();
if (LastRecord.Level == CurlyBraceLevel) {
LastRecord.RM = RM;
return;
}
}
RoundingPragmas.push_back({CurlyBraceLevel, RM});
}

LangOptions::RoundingMode Preprocessor::getCurrentRoundingMode() const {
if (RoundingPragmas.empty())
return LangOptions::RoundingMode::Dynamic;
return RoundingPragmas.back().RM;
}
81 changes: 10 additions & 71 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,6 @@ struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler {
}
};

/// Handler for "\#pragma STDC FENV_ROUND ...".
struct PragmaSTDC_FENV_ROUNDHandler : public PragmaHandler {
PragmaSTDC_FENV_ROUNDHandler() : PragmaHandler("FENV_ROUND") {}

void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
Token &Tok) override;
};

/// PragmaSTDC_UnknownHandler - "\#pragma STDC ...".
struct PragmaSTDC_UnknownHandler : public PragmaHandler {
PragmaSTDC_UnknownHandler() = default;
Expand Down Expand Up @@ -447,9 +439,6 @@ void Parser::initializePragmaHandlers() {
STDCFenvAccessHandler = std::make_unique<PragmaSTDC_FENV_ACCESSHandler>();
PP.AddPragmaHandler("STDC", STDCFenvAccessHandler.get());

STDCFenvRoundHandler = std::make_unique<PragmaSTDC_FENV_ROUNDHandler>();
PP.AddPragmaHandler("STDC", STDCFenvRoundHandler.get());

STDCCXLIMITHandler = std::make_unique<PragmaSTDC_CX_LIMITED_RANGEHandler>();
PP.AddPragmaHandler("STDC", STDCCXLIMITHandler.get());

Expand Down Expand Up @@ -656,9 +645,6 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("STDC", STDCFenvAccessHandler.get());
STDCFenvAccessHandler.reset();

PP.RemovePragmaHandler("STDC", STDCFenvRoundHandler.get());
STDCFenvRoundHandler.reset();

PP.RemovePragmaHandler("STDC", STDCCXLIMITHandler.get());
STDCCXLIMITHandler.reset();

Expand Down Expand Up @@ -903,9 +889,17 @@ void Parser::HandlePragmaFEnvRound() {
assert(Tok.is(tok::annot_pragma_fenv_round));
auto RM = static_cast<llvm::RoundingMode>(
reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
SourceLocation Loc = ConsumeAnnotationToken();

SourceLocation PragmaLoc = ConsumeAnnotationToken();
Actions.ActOnPragmaFEnvRound(PragmaLoc, RM);
if (!getTargetInfo().hasStrictFP() && !getLangOpts().ExpStrictFP) {
PP.Diag(Loc, diag::warn_pragma_fp_ignored) << "FENV_ROUND";
return;
}

// Until the pragma is fully implemented, issue a warning.
PP.Diag(Loc, diag::warn_stdc_fenv_round_not_supported);

Actions.ActOnPragmaFEnvRound(Loc, RM);
}

void Parser::HandlePragmaCXLimitedRange() {
Expand Down Expand Up @@ -3425,61 +3419,6 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP,
/*DisableMacroExpansion=*/false, /*IsReinject=*/false);
}

void PragmaSTDC_FENV_ROUNDHandler::HandlePragma(Preprocessor &PP,
PragmaIntroducer Introducer,
Token &Tok) {
Token PragmaName = Tok;
SmallVector<Token, 1> TokenList;
if (!PP.getTargetInfo().hasStrictFP() && !PP.getLangOpts().ExpStrictFP) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_fp_ignored)
<< PragmaName.getIdentifierInfo()->getName();
return;
}

PP.Lex(Tok);
if (Tok.isNot(tok::identifier)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
<< PragmaName.getIdentifierInfo()->getName();
return;
}
IdentifierInfo *II = Tok.getIdentifierInfo();

auto RM =
llvm::StringSwitch<llvm::RoundingMode>(II->getName())
.Case("FE_TOWARDZERO", llvm::RoundingMode::TowardZero)
.Case("FE_TONEAREST", llvm::RoundingMode::NearestTiesToEven)
.Case("FE_UPWARD", llvm::RoundingMode::TowardPositive)
.Case("FE_DOWNWARD", llvm::RoundingMode::TowardNegative)
.Case("FE_TONEARESTFROMZERO", llvm::RoundingMode::NearestTiesToAway)
.Case("FE_DYNAMIC", llvm::RoundingMode::Dynamic)
.Default(llvm::RoundingMode::Invalid);
if (RM == llvm::RoundingMode::Invalid) {
PP.Diag(Tok.getLocation(), diag::warn_stdc_unknown_rounding_mode);
return;
}
PP.Lex(Tok);

if (Tok.isNot(tok::eod)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
<< "STDC FENV_ROUND";
return;
}

// Until the pragma is fully implemented, issue a warning.
PP.Diag(Tok.getLocation(), diag::warn_stdc_fenv_round_not_supported);

MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
1);
Toks[0].startToken();
Toks[0].setKind(tok::annot_pragma_fenv_round);
Toks[0].setLocation(Tok.getLocation());
Toks[0].setAnnotationEndLoc(Tok.getLocation());
Toks[0].setAnnotationValue(
reinterpret_cast<void *>(static_cast<uintptr_t>(RM)));
PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
/*IsReinject=*/false);
}

void Parser::HandlePragmaFP() {
assert(Tok.is(tok::annot_pragma_fp));
auto *AnnotValue =
Expand Down
5 changes: 3 additions & 2 deletions clang/test/Parser/pragma-fenv_round.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// RUN: %clang_cc1 -fexperimental-strict-floating-point -fsyntax-only -Wignored-pragmas -verify %s

#pragma STDC FENV_ROUND ON // expected-warning {{invalid or unsupported rounding mode}}
#pragma STDC FENV_ROUND // expected-error{{expected rounding mode}}
#pragma STDC FENV_ROUND ON // expected-error{{invalid or unsupported rounding mode}}
#pragma STDC FENV_ROUND FE_DYNAMIC 0 // expected-warning{{extra tokens at end of #pragma STDC FENV_ROUND directive}}

float func_01(int x, float y) {
if (x)
return y + 2;
#pragma STDC FENV_ROUND FE_DOWNWARD // expected-error{{'#pragma STDC FENV_ROUND' can only appear at file scope or at the start of a compound statement}}
// expected-warning@-1{{pragma STDC FENV_ROUND is not supported}}
return x + y;
}
Loading
Loading