Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -88,6 +88,7 @@ bool VisitStringLiteral(const StringLiteral *E); bool VisitCharacterLiteral(const CharacterLiteral *E); bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); + bool VisitFloatCompoundAssignOperator(const CompoundAssignOperator *E); bool VisitExprWithCleanups(const ExprWithCleanups *E); bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -593,9 +593,86 @@ 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 LT = classify(E->getComputationLHSType()); @@ -607,8 +684,7 @@ 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)) Index: clang/test/AST/Interp/const-fpfeatures.cpp =================================================================== --- clang/test/AST/Interp/const-fpfeatures.cpp +++ clang/test/AST/Interp/const-fpfeatures.cpp @@ -50,9 +50,6 @@ // 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 @@ 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 Index: clang/test/AST/Interp/floats.cpp =================================================================== --- clang/test/AST/Interp/floats.cpp +++ clang/test/AST/Interp/floats.cpp @@ -54,3 +54,27 @@ // 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__); +}