[clang] Enable support for #pragma STDC FENV_ACCESS

Reviewers: rjmccall, rsmith, sepavloff

Differential Revision: https://reviews.llvm.org/D87528
This commit is contained in:
Melanie Blower 2020-09-29 10:44:36 -07:00
parent 3052e474ec
commit 2e204e2391
28 changed files with 340 additions and 75 deletions

View File

@ -1386,7 +1386,7 @@ Note that floating-point operations performed as part of constant initialization
Details:
* ``precise`` Disables optimizations that are not value-safe on floating-point data, although FP contraction (FMA) is enabled (``-ffp-contract=fast``). This is the default behavior.
* ``strict`` Enables ``-frounding-math`` and ``-ffp-exception-behavior=strict``, and disables contractions (FMA). All of the ``-ffast-math`` enablements are disabled.
* ``strict`` Enables ``-frounding-math`` and ``-ffp-exception-behavior=strict``, and disables contractions (FMA). All of the ``-ffast-math`` enablements are disabled. Enables ``STDC FENV_ACCESS``: by default ``FENV_ACCESS`` is disabled. This option setting behaves as though ``#pragma STDC FENV_ACESS ON`` appeared at the top of the source file.
* ``fast`` Behaves identically to specifying both ``-ffast-math`` and ``ffp-contract=fast``
Note: If your command line specifies multiple instances
@ -1408,6 +1408,44 @@ Note that floating-point operations performed as part of constant initialization
* ``strict`` The compiler ensures that all transformations strictly preserve the floating point exception semantics of the original code.
.. _fp-constant-eval:
A note about Floating Point Constant Evaluation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In C, the only place floating point operations are guaranteed to be evaluated
during translation is in the initializers of variables of static storage
duration, which are all notionally initialized before the program begins
executing (and thus before a non-default floating point environment can be
entered). But C++ has many more contexts where floating point constant
evaluation occurs. Specifically: for static/thread-local variables,
first try evaluating the initializer in a constant context, including in the
constant floating point environment (just like in C), and then, if that fails,
fall back to emitting runtime code to perform the initialization (which might
in general be in a different floating point environment).
Consider this example when compiled with ``-frounding-math``
.. code-block:: console
constexpr float func_01(float x, float y) {
return x + y;
}
float V1 = func_01(1.0F, 0x0.000001p0F);
The C++ rule is that initializers for static storage duration variables are
first evaluated during translation (therefore, in the default rounding mode),
and only evaluated at runtime (and therefore in the runtime rounding mode) if
the compile-time evaluation fails. This is in line with the C rules;
C11 F.8.5 says: *All computation for automatic initialization is done (as if)
at execution time; thus, it is affected by any operative modes and raises
floating-point exceptions as required by IEC 60559 (provided the state for the
FENV_ACCESS pragma is on). All computation for initialization of objects
that have static or thread storage duration is done (as if) at translation
time.* C++ generalizes this by adding another phase of initialization
(at runtime) if the translation-time initialization fails, but the
translation-time evaluation of the initializer of succeeds, it will be
treated as a constant initializer.
.. _controlling-code-generation:

View File

@ -2253,10 +2253,6 @@ public:
bool usesSEHTry() const { return FunctionDeclBits.UsesSEHTry; }
void setUsesSEHTry(bool UST) { FunctionDeclBits.UsesSEHTry = UST; }
/// Indicates the function uses Floating Point constrained intrinsics
bool usesFPIntrin() const { return FunctionDeclBits.UsesFPIntrin; }
void setUsesFPIntrin(bool Val) { FunctionDeclBits.UsesFPIntrin = Val; }
/// Whether this function has been deleted.
///
/// A function that is "deleted" (via the C++0x "= delete" syntax)

View File

@ -2267,6 +2267,14 @@ def PragmaClangRelroSection : InheritableAttr {
let Documentation = [Undocumented];
}
def StrictFP : InheritableAttr {
// This attribute has no spellings as it is only ever created implicitly.
// Function uses strict floating point operations.
let Spellings = [];
let Subjects = SubjectList<[Function]>;
let Documentation = [Undocumented];
}
def PragmaClangTextSection : InheritableAttr {
// This attribute has no spellings as it is only ever created implicitly.
let Spellings = [];

View File

@ -74,6 +74,8 @@ def note_constexpr_float_arithmetic : Note<
"floating point arithmetic produces %select{an infinity|a NaN}0">;
def note_constexpr_dynamic_rounding : Note<
"cannot evaluate this expression if rounding mode is dynamic">;
def note_constexpr_float_arithmetic_strict : Note<
"compile time floating point arithmetic suppressed in strict evaluation modes">;
def note_constexpr_pointer_subtraction_not_same_array : Note<
"subtracted pointers are not elements of the same array">;
def note_constexpr_pointer_subtraction_zero_size : Note<

View File

@ -1135,9 +1135,12 @@ def err_pragma_file_or_compound_scope : Error<
// - #pragma stdc unknown
def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
InGroup<UnknownPragmas>;
def warn_stdc_fenv_access_not_supported :
Warning<"pragma STDC FENV_ACCESS ON is not supported, ignoring pragma">,
InGroup<UnknownPragmas>;
// The C standard 7.6.1p2 says "The [FENV_ACCESS] pragma shall occur either
// outside external declarations or preceding all explicit declarations and
// statements inside a compound statement.
def err_pragma_stdc_fenv_access_scope : Error<
"'#pragma STDC FENV_ACCESS' can only appear at file scope or at the start of"
" a compound statement">;
def warn_stdc_fenv_round_not_supported :
Warning<"pragma STDC FENV_ROUND is not supported">,
InGroup<UnknownPragmas>;

View File

@ -413,13 +413,20 @@ public:
setFPContractMode(LO.getDefaultFPContractMode());
setRoundingMode(LO.getFPRoundingMode());
setFPExceptionMode(LO.getFPExceptionMode());
setAllowFEnvAccess(LangOptions::FPM_Off);
setAllowFPReassociate(LO.AllowFPReassoc);
setNoHonorNaNs(LO.NoHonorNaNs);
setNoHonorInfs(LO.NoHonorInfs);
setNoSignedZero(LO.NoSignedZero);
setAllowReciprocal(LO.AllowRecip);
setAllowApproxFunc(LO.ApproxFunc);
if (getFPContractMode() == LangOptions::FPM_On &&
getRoundingMode() == llvm::RoundingMode::Dynamic &&
getFPExceptionMode() == LangOptions::FPE_Strict)
// If the FP settings are set to the "strict" model, then
// FENV access is set to true. (ffp-model=strict)
setAllowFEnvAccess(true);
else
setAllowFEnvAccess(LangOptions::FPM_Off);
}
bool allowFPContractWithinStatement() const {

View File

@ -127,6 +127,9 @@ public:
/// Whether there is a fallthrough statement in this function.
bool HasFallthroughStmt : 1;
/// Whether this function uses constrained floating point intrinsics
bool UsesFPIntrin : 1;
/// Whether we make reference to a declaration that could be
/// unavailable.
bool HasPotentialAvailabilityViolations : 1;
@ -369,7 +372,8 @@ public:
: Kind(SK_Function), HasBranchProtectedScope(false),
HasBranchIntoScope(false), HasIndirectGoto(false),
HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false),
HasFallthroughStmt(false), HasPotentialAvailabilityViolations(false),
HasFallthroughStmt(false), UsesFPIntrin(false),
HasPotentialAvailabilityViolations(false),
ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false),
ObjCWarnForNoDesignatedInitChain(false), ObjCIsSecondaryInit(false),
ObjCWarnForNoInitDelegation(false), NeedsCoroutineSuspends(true),
@ -431,6 +435,10 @@ public:
HasFallthroughStmt = true;
}
void setUsesFPIntrin() {
UsesFPIntrin = true;
}
void setHasCXXTry(SourceLocation TryLoc) {
setHasBranchProtectedScope();
FirstCXXTryLoc = TryLoc;

View File

@ -4478,6 +4478,7 @@ public:
bool HasLeadingEmptyMacro = false);
void ActOnStartOfCompoundStmt(bool IsStmtExpr);
void ActOnAfterCompoundStatementLeadingPragmas();
void ActOnFinishOfCompoundStmt();
StmtResult ActOnCompoundStmt(SourceLocation L, SourceLocation R,
ArrayRef<Stmt *> Elts, bool isStmtExpr);

View File

@ -2601,6 +2601,14 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E,
return false;
}
if ((St != APFloat::opOK) &&
(FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
FPO.getFPExceptionMode() != LangOptions::FPE_Ignore ||
FPO.getAllowFEnvAccess())) {
Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
return false;
}
if ((St & APFloat::opStatus::opInvalidOp) &&
FPO.getFPExceptionMode() != LangOptions::FPE_Ignore) {
// There is no usefully definable result.
@ -2644,11 +2652,17 @@ static APSInt HandleIntToIntCast(EvalInfo &Info, const Expr *E,
}
static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E,
const FPOptions FPO,
QualType SrcType, const APSInt &Value,
QualType DestType, APFloat &Result) {
Result = APFloat(Info.Ctx.getFloatTypeSemantics(DestType), 1);
Result.convertFromAPInt(Value, Value.isSigned(),
APFloat::rmNearestTiesToEven);
APFloat::opStatus St = Result.convertFromAPInt(Value, Value.isSigned(),
APFloat::rmNearestTiesToEven);
if (!Info.InConstantContext && St != llvm::APFloatBase::opOK &&
FPO.isFPConstrained()) {
Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
return false;
}
return true;
}
@ -4370,9 +4384,11 @@ struct CompoundAssignSubobjectHandler {
Value = HandleIntToIntCast(Info, E, SubobjType, PromotedLHSType, LHS);
return true;
} else if (RHS.isFloat()) {
const FPOptions FPO = E->getFPFeaturesInEffect(
Info.Ctx.getLangOpts());
APFloat FValue(0.0);
return HandleIntToFloatCast(Info, E, SubobjType, Value, PromotedLHSType,
FValue) &&
return HandleIntToFloatCast(Info, E, FPO, SubobjType, Value,
PromotedLHSType, FValue) &&
handleFloatFloatBinOp(Info, E, FValue, Opcode, RHS.getFloat()) &&
HandleFloatToIntCast(Info, E, PromotedLHSType, FValue, SubobjType,
Value);
@ -12545,8 +12561,16 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
return false;
assert(E->isComparisonOp() && "Invalid binary operator!");
llvm::APFloatBase::cmpResult CmpResult = LHS.compare(RHS);
if (!Info.InConstantContext &&
CmpResult == APFloat::cmpUnordered &&
E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()).isFPConstrained()) {
// Note: Compares may raise invalid in some cases involving NaN or sNaN.
Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
return false;
}
auto GetCmpRes = [&]() {
switch (LHS.compare(RHS)) {
switch (CmpResult) {
case APFloat::cmpEqual:
return CmpResult::Equal;
case APFloat::cmpLessThan:
@ -13604,6 +13628,11 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
case Builtin::BI__builtin_fabsf:
case Builtin::BI__builtin_fabsl:
case Builtin::BI__builtin_fabsf128:
// The C standard says "fabs raises no floating-point exceptions,
// even if x is a signaling NaN. The returned value is independent of
// the current rounding direction mode." Therefore constant folding can
// proceed without regard to the floating point settings.
// Reference, WG14 N2478 F.10.4.3
if (!EvaluateFloat(E->getArg(0), Result, Info))
return false;
@ -13662,6 +13691,9 @@ bool FloatExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
case UO_Plus:
return EvaluateFloat(E->getSubExpr(), Result, Info);
case UO_Minus:
// In C standard, WG14 N2478 F.3 p4
// "the unary - raises no floating point exceptions,
// even if the operand is signalling."
if (!EvaluateFloat(E->getSubExpr(), Result, Info))
return false;
Result.changeSign();
@ -13695,9 +13727,11 @@ bool FloatExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_IntegralToFloating: {
APSInt IntResult;
const FPOptions FPO = E->getFPFeaturesInEffect(
Info.Ctx.getLangOpts());
return EvaluateInteger(SubExpr, IntResult, Info) &&
HandleIntToFloatCast(Info, E, SubExpr->getType(), IntResult,
E->getType(), Result);
HandleIntToFloatCast(Info, E, FPO, SubExpr->getType(),
IntResult, E->getType(), Result);
}
case CK_FixedPointToFloating: {
@ -13936,13 +13970,15 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
if (!Visit(E->getSubExpr()))
return false;
const FPOptions FPO = E->getFPFeaturesInEffect(
Info.Ctx.getLangOpts());
QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
= E->getSubExpr()->getType()->castAs<ComplexType>()->getElementType();
Result.makeComplexFloat();
return HandleIntToFloatCast(Info, E, From, Result.IntReal,
return HandleIntToFloatCast(Info, E, FPO, From, Result.IntReal,
To, Result.FloatReal) &&
HandleIntToFloatCast(Info, E, From, Result.IntImag,
HandleIntToFloatCast(Info, E, FPO, From, Result.IntImag,
To, Result.FloatImag);
}
}

View File

@ -4837,7 +4837,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
/*AttrOnCallSite=*/true);
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl))
if (FD->usesFPIntrin())
if (FD->hasAttr<StrictFPAttr>())
// All calls within a strictfp function are marked strictfp
Attrs =
Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex,
@ -4902,7 +4902,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
getBundlesForFunclet(CalleePtr);
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl))
if (FD->usesFPIntrin())
if (FD->hasAttr<StrictFPAttr>())
// All calls within a strictfp function are marked strictfp
Attrs =
Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex,

View File

@ -915,8 +915,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
}
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
Builder.setIsFPConstrained(FD->usesFPIntrin());
if (FD->usesFPIntrin())
Builder.setIsFPConstrained(FD->hasAttr<StrictFPAttr>());
if (FD->hasAttr<StrictFPAttr>())
Fn->addFnAttr(llvm::Attribute::StrictFP);
}

View File

@ -1742,6 +1742,15 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
}
}
void CodeGenModule::setLLVMFunctionFEnvAttributes(const FunctionDecl *D,
llvm::Function *F) {
if (D->hasAttr<StrictFPAttr>()) {
llvm::AttrBuilder FuncAttrs;
FuncAttrs.addAttribute("strictfp");
F->addAttributes(llvm::AttributeList::FunctionIndex, FuncAttrs);
}
}
void CodeGenModule::SetCommonAttributes(GlobalDecl GD, llvm::GlobalValue *GV) {
const Decl *D = GD.getDecl();
if (dyn_cast_or_null<NamedDecl>(D))
@ -4587,9 +4596,11 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
MaybeHandleStaticInExternC(D, Fn);
maybeSetTrivialComdat(*D, *Fn);
// Set CodeGen attributes that represent floating point environment.
setLLVMFunctionFEnvAttributes(D, Fn);
CodeGenFunction(*this).GenerateCode(GD, Fn, FI);
setNonAliasAttributes(GD, Fn);

View File

@ -1131,6 +1131,10 @@ public:
/// definition.
void SetLLVMFunctionAttributesForDefinition(const Decl *D, llvm::Function *F);
/// Set the LLVM function attributes that represent floating point
/// environment.
void setLLVMFunctionFEnvAttributes(const FunctionDecl *D, llvm::Function *F);
/// Return true iff the given type uses 'sret' when used as a return type.
bool ReturnTypeUsesSRet(const CGFunctionInfo &FI);

View File

@ -106,10 +106,6 @@ struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler {
tok::OnOffSwitch OOS;
if (PP.LexOnOffSwitch(OOS))
return;
if (OOS == tok::OOS_ON) {
PP.Diag(Tok, diag::warn_stdc_fenv_access_not_supported);
return;
}
MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1),
1);

View File

@ -366,7 +366,8 @@ Retry:
case tok::annot_pragma_fenv_access:
ProhibitAttributes(Attrs);
HandlePragmaFEnvAccess();
Diag(Tok, diag::err_pragma_stdc_fenv_access_scope);
ConsumeAnnotationToken();
return StmtEmpty();
case tok::annot_pragma_fenv_round:
@ -1033,9 +1034,9 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
Tok.getLocation(),
"in compound statement ('{}')");
// Record the state of the FPFeatures, restore on leaving the
// Record the current FPFeatures, restore on leaving the
// compound statement.
Sema::FPFeaturesStateRAII SaveFPContractState(Actions);
Sema::FPFeaturesStateRAII SaveFPFeatures(Actions);
InMessageExpressionRAIIObject InMessage(*this, false);
BalancedDelimiterTracker T(*this, tok::l_brace);
@ -1046,6 +1047,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
// Parse any pragmas at the beginning of the compound statement.
ParseCompoundStatementLeadingPragmas();
Actions.ActOnAfterCompoundStatementLeadingPragmas();
StmtVector Stmts;

View File

@ -29,6 +29,7 @@ void FunctionScopeInfo::Clear() {
HasDroppedStmt = false;
HasOMPDeclareReductionCombiner = false;
HasFallthroughStmt = false;
UsesFPIntrin = false;
HasPotentialAvailabilityViolations = false;
ObjCShouldCallSuper = false;
ObjCIsDesignatedInit = false;

View File

@ -1002,6 +1002,7 @@ void Sema::setExceptionMode(SourceLocation Loc,
void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) {
FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides();
auto LO = getLangOpts();
if (IsEnabled) {
// Verify Microsoft restriction:
// You can't enable fenv_access unless precise semantics are enabled.
@ -1010,10 +1011,15 @@ void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) {
if (!isPreciseFPEnabled())
Diag(Loc, diag::err_pragma_fenv_requires_precise);
NewFPFeatures.setAllowFEnvAccessOverride(true);
} else
// Enabling FENV access sets the RoundingMode to Dynamic.
// and ExceptionBehavior to Strict
NewFPFeatures.setRoundingModeOverride(llvm::RoundingMode::Dynamic);
NewFPFeatures.setFPExceptionModeOverride(LangOptions::FPE_Strict);
} else {
NewFPFeatures.setAllowFEnvAccessOverride(false);
}
FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures);
CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts());
CurFPFeatures = NewFPFeatures.applyOverrides(LO);
}
void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr,

View File

@ -14285,12 +14285,16 @@ static void diagnoseImplicitlyRetainedSelf(Sema &S) {
Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
bool IsInstantiation) {
FunctionScopeInfo *FSI = getCurFunction();
FunctionDecl *FD = dcl ? dcl->getAsFunction() : nullptr;
if (FSI->UsesFPIntrin && !FD->hasAttr<StrictFPAttr>())
FD->addAttr(StrictFPAttr::CreateImplicit(Context));
sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr;
if (getLangOpts().Coroutines && getCurFunction()->isCoroutine())
if (getLangOpts().Coroutines && FSI->isCoroutine())
CheckCompletedCoroutineBody(FD, Body);
// Do not call PopExpressionEvaluationContext() if it is a lambda because one
@ -14367,7 +14371,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
// to deduce an implicit return type.
if (FD->getReturnType()->isRecordType() &&
(!getLangOpts().CPlusPlus || !FD->isDependentContext()))
computeNRVO(Body, getCurFunction());
computeNRVO(Body, FSI);
}
// GNU warning -Wmissing-prototypes:
@ -14491,14 +14495,14 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
MD->getReturnType(), MD);
if (Body)
computeNRVO(Body, getCurFunction());
computeNRVO(Body, FSI);
}
if (getCurFunction()->ObjCShouldCallSuper) {
if (FSI->ObjCShouldCallSuper) {
Diag(MD->getEndLoc(), diag::warn_objc_missing_super_call)
<< MD->getSelector().getAsString();
getCurFunction()->ObjCShouldCallSuper = false;
FSI->ObjCShouldCallSuper = false;
}
if (getCurFunction()->ObjCWarnForNoDesignatedInitChain) {
if (FSI->ObjCWarnForNoDesignatedInitChain) {
const ObjCMethodDecl *InitMethod = nullptr;
bool isDesignated =
MD->isDesignatedInitializerForTheInterface(&InitMethod);
@ -14523,14 +14527,14 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
Diag(InitMethod->getLocation(),
diag::note_objc_designated_init_marked_here);
}
getCurFunction()->ObjCWarnForNoDesignatedInitChain = false;
FSI->ObjCWarnForNoDesignatedInitChain = false;
}
if (getCurFunction()->ObjCWarnForNoInitDelegation) {
if (FSI->ObjCWarnForNoInitDelegation) {
// Don't issue this warning for unavaialable inits.
if (!MD->isUnavailable())
Diag(MD->getLocation(),
diag::warn_objc_secondary_init_missing_init_call);
getCurFunction()->ObjCWarnForNoInitDelegation = false;
FSI->ObjCWarnForNoInitDelegation = false;
}
diagnoseImplicitlyRetainedSelf(*this);
@ -14541,10 +14545,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
return nullptr;
}
if (Body && getCurFunction()->HasPotentialAvailabilityViolations)
if (Body && FSI->HasPotentialAvailabilityViolations)
DiagnoseUnguardedAvailabilityViolations(dcl);
assert(!getCurFunction()->ObjCShouldCallSuper &&
assert(!FSI->ObjCShouldCallSuper &&
"This should only be set for ObjC methods, which should have been "
"handled in the block above.");
@ -14557,7 +14561,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
DiagnoseReturnInConstructorExceptionHandler(cast<CXXTryStmt>(Body));
// Verify that gotos and switch cases don't jump into scopes illegally.
if (getCurFunction()->NeedsScopeChecking() &&
if (FSI->NeedsScopeChecking() &&
!PP.isCodeCompletionEnabled())
DiagnoseInvalidJumps(Body);

View File

@ -385,6 +385,14 @@ void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) {
PushCompoundScope(IsStmtExpr);
}
void Sema::ActOnAfterCompoundStatementLeadingPragmas() {
if (getCurFPFeatures().isFPConstrained()) {
FunctionScopeInfo *FSI = getCurFunction();
assert(FSI);
FSI->setUsesFPIntrin();
}
}
void Sema::ActOnFinishOfCompoundStmt() {
PopCompoundScope();
}
@ -397,11 +405,6 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
ArrayRef<Stmt *> Elts, bool isStmtExpr) {
const unsigned NumElts = Elts.size();
// Mark the current function as usng floating point constrained intrinsics
if (getCurFPFeatures().isFPConstrained())
if (FunctionDecl *F = dyn_cast<FunctionDecl>(CurContext))
F->setUsesFPIntrin(true);
// If we're in C89 mode, check that we don't have any decls after stmts. If
// so, emit an extension diagnostic.
if (!getLangOpts().C99 && !getLangOpts().CPlusPlus) {

View File

@ -1951,7 +1951,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
D->hasWrittenPrototype(), D->getConstexprKind(),
TrailingRequiresClause);
Function->setRangeEnd(D->getSourceRange().getEnd());
Function->setUsesFPIntrin(D->usesFPIntrin());
}
if (D->isInlined())

View File

@ -888,7 +888,6 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
FD->ODRHash = Record.readInt();
FD->setHasODRHash(true);
FD->setUsesFPIntrin(Record.readInt());
if (FD->isDefaulted()) {
if (unsigned NumLookups = Record.readInt()) {

View File

@ -566,7 +566,6 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
Record.AddSourceLocation(D->getEndLoc());
Record.push_back(D->getODRHash());
Record.push_back(D->usesFPIntrin());
if (D->isDefaulted()) {
if (auto *FDI = D->getDefaultedFunctionInfo()) {

View File

@ -280,6 +280,16 @@ namespace UndefinedBehavior {
constexpr float f10 = f2 - f2; // expected-error {{constant expression}} expected-note {{produces a NaN}}
constexpr float f11 = f2 + f4; // expected-error {{constant expression}} expected-note {{produces a NaN}}
constexpr float f12 = f2 / f2; // expected-error {{constant expression}} expected-note {{produces a NaN}}
#pragma float_control(push)
#pragma float_control(except, on)
constexpr float pi = 3.14f;
constexpr unsigned ubig = 0xFFFFFFFF;
constexpr float ce = 1.0 / 3.0; // not-expected-error {{constant expression}} not-expected-note {{floating point arithmetic suppressed in strict evaluation modes}}
constexpr int ci = (int) pi;
constexpr float fbig = (float) ubig; // not-expected-error {{constant expression}} not-expected-note {{floating point arithmetic suppressed in strict evaluation modes}}
constexpr float fabspi = __builtin_fabs(pi); // no error expected
constexpr float negpi = -pi; // expect no error on unary operator
#pragma float_control(pop)
static_assert(!isinf(f1), "");
static_assert(isinf(f2), "");
static_assert(!isinf(f3), "");

View File

@ -1,6 +1,6 @@
// RUN: %clang_cc1 -DEXCEPT=1 -fcxx-exceptions -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-NS %s
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -verify -DFENV_ON=1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -DFENV_ON=1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-FENV %s
// RUN: %clang_cc1 -triple %itanium_abi_triple -O3 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-O3 %s
// Verify float_control(precise, off) enables fast math flags on fp operations.
@ -138,7 +138,6 @@ float test_OperatorCall() {
// CHECK-LABEL define float {{.*}}test_OperatorCall{{.*}}
#if FENV_ON
// expected-warning@+1{{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}}
#pragma STDC FENV_ACCESS ON
#endif
// CHECK-LABEL: define {{.*}}callt{{.*}}
@ -146,7 +145,21 @@ float test_OperatorCall() {
void callt() {
volatile float z;
z = z * z;
//CHECK: = fmul float
//CHECK-FENV: llvm.experimental.constrained.fmul{{.*}}
}
// CHECK-LABEL: define {{.*}}myAdd{{.*}}
float myAdd(int i, float f) {
if (i<0)
return 1.0 + 2.0;
// Check that floating point constant folding doesn't occur if
// #pragma STC FENV_ACCESS is enabled.
//CHECK-FENV: llvm.experimental.constrained.fadd{{.*}}double 1.0{{.*}}double 2.0{{.*}}
//CHECK: store float 3.0{{.*}}retval{{.*}}
static double v = 1.0 / 3.0;
//CHECK-FENV: llvm.experimental.constrained.fptrunc.f32.f64{{.*}}
//CHECK-NOT: fdiv
return v;
}
#if EXCEPT
@ -201,3 +214,18 @@ float xx(double x, float z) {
return fc_template_namespace::exc_on<float>(x, z);
}
#endif // EXCEPT
float try_lam(float x, unsigned n) {
// CHECK: define {{.*}}try_lam{{.*}}class.anon{{.*}}
float result;
auto t =
// Lambda expression begins
[](float a, float b) {
#pragma float_control( except, on)
return a * b;
//CHECK: llvm.experimental.constrained.fmul{{.*}}fpexcept.strict
} // end of lambda expression
(1.0f,2.0f);
result = x + t;
return result;
}

View File

@ -0,0 +1,65 @@
// RUN: %clang_cc1 -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck %s
#pragma STDC FENV_ACCESS ON
float func_01(float x, float y) {
return x + y;
}
// CHECK-LABEL: @func_01
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
float func_02(float x, float y) {
#pragma float_control(except, off)
#pragma STDC FENV_ACCESS OFF
return x + y;
}
// CHECK-LABEL: @func_02
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
float func_03(float x, float y) {
return x + y;
}
// CHECK-LABEL: @func_03
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
#pragma STDC FENV_ACCESS OFF
float func_04(float x, float y) {
#pragma float_control(except, off)
return x + y;
}
// CHECK-LABEL: @func_04
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
float func_05(float x, float y) {
#pragma STDC FENV_ACCESS ON
return x + y;
}
// CHECK-LABEL: @func_05
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
float func_06(float x, float y) {
#pragma float_control(except, off)
return x + y;
}
// CHECK-LABEL: @func_06
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore")
float func_07(float x, float y) {
x -= y;
if (x) {
#pragma STDC FENV_ACCESS ON
y *= 2;
}
return y + 4;
}
// CHECK-LABEL: @func_07
// CHECK: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
// CHECK: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")
// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict")

View File

@ -26,19 +26,13 @@ void check_stack() {
double a = 0.0;
double b = 1.0;
//FIXME At some point this warning will be removed, until then
// document the warning
#ifdef FAST
// expected-warning@+1{{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}}
#pragma STDC FENV_ACCESS ON
#else
#pragma STDC FENV_ACCESS ON // expected-warning{{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}}
#endif
#ifdef STRICT
#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}}
#else
// Currently FENV_ACCESS cannot be enabled by pragma, skip error check
#pragma float_control(precise, off) // not-expected-error {{'#pragma float_control(precise, off)' is illegal when fenv_access is enabled}}
#ifndef FAST
#pragma STDC FENV_ACCESS ON
#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}}
#endif
#endif
#pragma float_control(precise, on)

View File

@ -0,0 +1,54 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -ffp-exception-behavior=strict -DSTRICT -fsyntax-only -verify %s
// RUN: %clang_cc1 -x c++ -DCPP -DSTRICT -ffp-exception-behavior=strict -fsyntax-only -verify %s
#ifdef CPP
#define CONST constexpr
#else
#define CONST const
#endif
#pragma STDC FENV_ACCESS IN_BETWEEN // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}}
#pragma STDC FENV_ACCESS OFF
float func_04(int x, float y) {
if (x)
return y + 2;
#pragma STDC FENV_ACCESS ON // expected-error{{'#pragma STDC FENV_ACCESS' can only appear at file scope or at the start of a compound statement}}
return x + y;
}
#pragma STDC FENV_ACCESS ON
int main() {
CONST float one = 1.0F ;
CONST float three = 3.0F ;
CONST float four = 4.0F ;
CONST float frac_ok = one/four;
#if !defined(CPP)
//expected-note@+2 {{declared here}}
#endif
CONST float frac = one/three;
CONST double d = one;
CONST int not_too_big = 255;
CONST float fnot_too_big = not_too_big;
CONST int too_big = 0x7ffffff0;
#if defined(CPP)
//expected-warning@+2{{implicit conversion}}
#endif
CONST float fbig = too_big; // inexact
#if !defined(CPP)
#define static_assert _Static_assert
#endif
enum {
e1 = (int)one, e3 = (int)three, e4 = (int)four, e_four_quarters = (int)(frac_ok * 4)
};
static_assert(e1 == 1 && e3 == 3 && e4 == 4 && e_four_quarters == 1, "");
enum {
#if !defined(CPP)
// expected-error@+2 {{not an integer constant expression}} expected-note@+2 {{is not a constant expression}}
#endif
e_three_thirds = (int)(frac * 3)
};
if (one <= four) return 0;
return -1;
}

View File

@ -16,15 +16,6 @@
// CHECK: {{^}}#pragma STDC FP_CONTRACT DEFAULT{{$}}
// CHECK: {{^}}#pragma STDC FP_CONTRACT IN_BETWEEN{{$}}
#pragma STDC FENV_ACCESS ON // expected-warning {{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}}
#pragma STDC FENV_ACCESS OFF
#pragma STDC FENV_ACCESS DEFAULT
#pragma STDC FENV_ACCESS IN_BETWEEN // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}}
// CHECK: {{^}}#pragma STDC FENV_ACCESS ON{{$}}
// CHECK: {{^}}#pragma STDC FENV_ACCESS OFF{{$}}
// CHECK: {{^}}#pragma STDC FENV_ACCESS DEFAULT{{$}}
// CHECK: {{^}}#pragma STDC FENV_ACCESS IN_BETWEEN{{$}}
#pragma STDC CX_LIMITED_RANGE ON
#pragma STDC CX_LIMITED_RANGE OFF
#pragma STDC CX_LIMITED_RANGE DEFAULT