diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 62ed09f078c18..d7b429c38f102 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -550,9 +550,86 @@ bool ByteCodeExprGen::VisitCharacterLiteral( return this->emitConst(E->getValue(), E); } +template +bool ByteCodeExprGen::VisitFloatCompoundAssignOperator( + const CompoundAssignOperator *E) { + assert(E->getType()->isFloatingType()); + + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + llvm::RoundingMode RM = getRoundingMode(E); + QualType LHSComputationType = E->getComputationLHSType(); + QualType ResultType = E->getComputationResultType(); + std::optional LT = classify(LHSComputationType); + std::optional RT = classify(ResultType); + + if (!LT || !RT) + return false; + + // First, visit LHS. + if (!visit(LHS)) + return false; + + if (!this->emitLoad(*LT, E)) + return false; + + // If necessary, convert LHS to its computation type. + if (LHS->getType() != LHSComputationType) { + const auto *TargetSemantics = + &Ctx.getASTContext().getFloatTypeSemantics(LHSComputationType); + + if (!this->emitCastFP(TargetSemantics, RM, E)) + return false; + } + + // Now load RHS. + if (!visit(RHS)) + return false; + + switch (E->getOpcode()) { + case BO_AddAssign: + if (!this->emitAddf(RM, E)) + return false; + break; + case BO_SubAssign: + if (!this->emitSubf(RM, E)) + return false; + break; + case BO_MulAssign: + if (!this->emitMulf(RM, E)) + return false; + break; + case BO_DivAssign: + if (!this->emitDivf(RM, E)) + return false; + break; + default: + return false; + } + + // If necessary, convert result to LHS's type. + if (LHS->getType() != ResultType) { + const auto *TargetSemantics = + &Ctx.getASTContext().getFloatTypeSemantics(LHS->getType()); + + if (!this->emitCastFP(TargetSemantics, RM, E)) + return false; + } + + if (DiscardResult) + return this->emitStorePop(*LT, E); + return this->emitStore(*LT, E); +} + template bool ByteCodeExprGen::VisitCompoundAssignOperator( const CompoundAssignOperator *E) { + + // Handle floating point operations separately here, since they + // require special care. + if (E->getType()->isFloatingType()) + return VisitFloatCompoundAssignOperator(E); + const Expr *LHS = E->getLHS(); const Expr *RHS = E->getRHS(); std::optional LHSComputationT = @@ -567,8 +644,7 @@ bool ByteCodeExprGen::VisitCompoundAssignOperator( assert(!E->getType()->isPointerType() && "Support pointer arithmethic in compound assignment operators"); - assert(!E->getType()->isFloatingType() && - "Support floating types in compound assignment operators"); + assert(!E->getType()->isFloatingType() && "Handled above"); // Get LHS pointer, load its value and get RHS value. if (!visit(LHS)) diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 6b82818fe09ae..ed33e0285a8f1 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -85,6 +85,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, bool VisitStringLiteral(const StringLiteral *E); bool VisitCharacterLiteral(const CharacterLiteral *E); bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); + bool VisitFloatCompoundAssignOperator(const CompoundAssignOperator *E); protected: bool visitExpr(const Expr *E) override; diff --git a/clang/test/AST/Interp/const-fpfeatures.cpp b/clang/test/AST/Interp/const-fpfeatures.cpp index 8f5d872916674..e242108100258 100644 --- a/clang/test/AST/Interp/const-fpfeatures.cpp +++ b/clang/test/AST/Interp/const-fpfeatures.cpp @@ -50,9 +50,6 @@ float V2 = add_round_up(1.0F, 0x0.000001p0F); // CHECK: @V2 = {{.*}} float 0x3FF0000020000000 -/// FIXME: The following tests need support for compound assign operators -/// with LHS and RHS of different semantics. -#if 0 constexpr float add_cast_round_down(float x, double y) { #pragma STDC FENV_ROUND FE_DOWNWARD float res = x; @@ -70,5 +67,5 @@ constexpr float add_cast_round_up(float x, double y) { float V3 = add_cast_round_down(1.0F, 0x0.000001p0F); float V4 = add_cast_round_up(1.0F, 0x0.000001p0F); - -#endif +// CHECK: @V3 = {{.*}} float 1.000000e+00 +// CHECK: @V4 = {{.*}} float 0x3FF0000020000000 diff --git a/clang/test/AST/Interp/floats.cpp b/clang/test/AST/Interp/floats.cpp index ab5d51ca400de..7b9328c4d1182 100644 --- a/clang/test/AST/Interp/floats.cpp +++ b/clang/test/AST/Interp/floats.cpp @@ -54,3 +54,27 @@ constexpr int someInt = fm; // ref-error {{must be initialized by a constant exp // ref-note {{is outside the range of representable values}} \ // expected-error {{must be initialized by a constant expression}} \ // expected-note {{is outside the range of representable values}} + +namespace compound { + constexpr float f1() { + float f = 0; + f += 3.0; + f -= 3.0f; + + f += 1; + f /= 1; + f /= 1.0; + f *= f; + + f *= 2.0; + return f; + } + static_assert(f1() == 2, ""); + + constexpr float f2() { + float f = __FLT_MAX__; + f += 1.0; + return f; + } + static_assert(f2() == __FLT_MAX__, ""); +}