Skip to content

Commit cf65396

Browse files
committed
Support rounding mode switching for function calls
1 parent cd1a7c6 commit cf65396

File tree

8 files changed

+248
-37
lines changed

8 files changed

+248
-37
lines changed

clang/include/clang/Basic/TargetInfo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,14 @@ class TargetInfo : public TransferrableTargetInfo,
12321232
return true;
12331233
}
12341234

1235+
/// Returns true, if an operations that depends on rounding mode can be
1236+
/// implemented without changing FP environment. In this case the rounding
1237+
/// mode is encoded in the bits of implementing instruction.
1238+
virtual bool hasStaticRounding() const {
1239+
// Most supported targets require setting hardware register to use
1240+
// particular rounding mode.
1241+
return false;
1242+
}
12351243
/// Returns the target triple of the primary target.
12361244
const llvm::Triple &getTriple() const {
12371245
return Triple;

clang/lib/Basic/Targets/RISCV.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ class RISCVTargetInfo : public TargetInfo {
110110

111111
bool hasBFloat16Type() const override { return true; }
112112

113+
bool hasStaticRounding() const override { return true; }
114+
113115
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override;
114116

115117
bool useFP16ConversionIntrinsics() const override {

clang/lib/CodeGen/CGCall.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "clang/CodeGen/CGFunctionInfo.h"
3131
#include "clang/CodeGen/SwiftCallingConv.h"
3232
#include "llvm/ADT/StringExtras.h"
33+
#include "llvm/ADT/StringSet.h"
3334
#include "llvm/Analysis/ValueTracking.h"
3435
#include "llvm/IR/Assumptions.h"
3536
#include "llvm/IR/AttributeMask.h"
@@ -5693,6 +5694,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
56935694
AllocAlignAttrEmitter AllocAlignAttrEmitter(*this, TargetDecl, CallArgs);
56945695
Attrs = AllocAlignAttrEmitter.TryEmitAsCallSiteAttribute(Attrs);
56955696

5697+
// Prepare execution environment.
5698+
setRoundingModeForCall(Callee);
5699+
56965700
// Emit the actual call/invoke instruction.
56975701
llvm::CallBase *CI;
56985702
if (!InvokeDest) {
@@ -5846,6 +5850,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
58465850
// lexical order, so deactivate it and run it manually here.
58475851
CallArgs.freeArgumentMemory(*this);
58485852

5853+
// Restore execution environment.
5854+
restoreRoundingModeAfterCall();
5855+
58495856
// Extract the return value.
58505857
RValue Ret = [&] {
58515858
switch (RetAI.getKind()) {
@@ -5980,6 +5987,64 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
59805987
return Ret;
59815988
}
59825989

5990+
static bool endsWithRoundingModeSuffix(StringRef FuncName) {
5991+
size_t Underscore = FuncName.find_last_of("_");
5992+
if (Underscore == StringRef::npos || Underscore < 2)
5993+
return false;
5994+
StringRef Suffix = FuncName.substr(Underscore + 1);
5995+
static const StringRef RMSuffixes[] = {"rtz", "rte", "rtp", "rtn", "rhaz",
5996+
"rz", "rn", "ru", "rd"};
5997+
for (auto RM : RMSuffixes) {
5998+
if (Suffix == RM)
5999+
return true;
6000+
}
6001+
return false;
6002+
}
6003+
6004+
bool CodeGenFunction::requiresDynamicRounding(const CGCallee &Callee) {
6005+
if (Callee.isOrdinary()) {
6006+
const Decl *CalleeDecl = Callee.getAbstractInfo().getCalleeDecl().getDecl();
6007+
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CalleeDecl)) {
6008+
IdentifierInfo *FuncNameII = FD->getDeclName().getAsIdentifierInfo();
6009+
if (FuncNameII) {
6010+
StringRef FuncName = FuncNameII->getName();
6011+
// If a reserved identifier ends with rounding mode suffix preceded by
6012+
// underscore, this function does not need the previous dynamic rounding
6013+
// mode to be set.
6014+
if (isReservedInAllContexts(
6015+
FuncNameII->isReserved(getContext().getLangOpts()))) {
6016+
if (endsWithRoundingModeSuffix(FuncName))
6017+
return false;
6018+
}
6019+
}
6020+
}
6021+
}
6022+
return true;
6023+
}
6024+
6025+
/// Sets dynamic rounding mode for the function called in the region where
6026+
/// pragma FENV_ROUND is in effect.
6027+
void CodeGenFunction::setRoundingModeForCall(const CGCallee &Callee) {
6028+
if (Target.hasStaticRounding() || Callee.isBuiltin() ||
6029+
!requiresDynamicRounding(Callee))
6030+
return;
6031+
if (!CurrentRoundingIsStatic || !DynamicRoundingMode)
6032+
return;
6033+
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::set_rounding),
6034+
DynamicRoundingMode);
6035+
CurrentRoundingIsStatic = false;
6036+
}
6037+
6038+
void CodeGenFunction::restoreRoundingModeAfterCall() {
6039+
if (Target.hasStaticRounding() || CurFPFeatures.isRoundingModeDynamic())
6040+
return;
6041+
if (CurrentRoundingIsStatic || !StaticRoundingMode)
6042+
return;
6043+
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::set_rounding),
6044+
StaticRoundingMode);
6045+
CurrentRoundingIsStatic = true;
6046+
}
6047+
59836048
CGCallee CGCallee::prepareConcreteCallee(CodeGenFunction &CGF) const {
59846049
if (isVirtual()) {
59856050
const CallExpr *CE = getVirtualCallExpr();

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -499,41 +499,47 @@ struct FPControlModesCleanup final : EHScopeStack::Cleanup {
499499
};
500500
} // namespace
501501

502-
void CodeGenFunction::emitSetFPControlModes(FPOptions NewFP) {
503-
if (NewFP == CurFPFeatures)
502+
void CodeGenFunction::emitSetFPControlModes(FPOptions NewFP, FPOptions OldFP) {
503+
if (NewFP == OldFP)
504504
return;
505505

506506
// For now only rounding mode is handled.
507507

508+
if (Target.hasStaticRounding())
509+
return;
510+
508511
// If the new rounding mode is unknown in compile-time, it means that the
509512
// compound statement contains `#pragma STDC FENV_ACCESS ON`. In this case all
510513
// manipulations on FP environment, including setting and restoring control
511514
// modes are made by the user.
512515
if (NewFP.isRoundingModeDynamic())
513516
return;
514517

515-
llvm::RoundingMode OldConstRM = CurFPFeatures.getConstRoundingMode();
518+
llvm::RoundingMode OldConstRM = OldFP.getConstRoundingMode();
516519
llvm::RoundingMode NewConstRM = NewFP.getConstRoundingMode();
517520
if (OldConstRM == NewConstRM)
518521
return;
519522

520-
llvm::RoundingMode OldRM = CurFPFeatures.getRoundingMode();
523+
llvm::RoundingMode OldRM = OldFP.getRoundingMode();
521524
if (OldRM == NewConstRM)
522525
return;
523526

524-
llvm::Value *PreviousRM = nullptr;
525-
if (CurFPFeatures.isRoundingModeDynamic()) {
527+
if (OldFP.isRoundingModeDynamic()) {
526528
llvm::Function *FGetRound = CGM.getIntrinsic(llvm::Intrinsic::get_rounding);
527-
PreviousRM = Builder.CreateCall(FGetRound);
529+
DynamicRoundingMode = Builder.CreateCall(FGetRound);
528530
} else {
529-
PreviousRM = llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(OldRM));
531+
DynamicRoundingMode =
532+
llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(OldRM));
530533
}
531534

532535
llvm::RoundingMode NewRM = NewFP.getRoundingMode();
533-
Builder.CreateIntrinsic(
534-
llvm::Intrinsic::set_rounding, {},
535-
llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(NewRM)));
536-
EHStack.pushCleanup<FPControlModesCleanup>(NormalAndEHCleanup, PreviousRM);
536+
StaticRoundingMode =
537+
llvm::ConstantInt::get(Int32Ty, static_cast<uint64_t>(NewRM));
538+
Builder.CreateIntrinsic(llvm::Intrinsic::set_rounding, {},
539+
StaticRoundingMode);
540+
EHStack.pushCleanup<FPControlModesCleanup>(NormalAndEHCleanup,
541+
DynamicRoundingMode);
542+
CurrentRoundingIsStatic = true;
537543
}
538544

539545
/// EmitCompoundStmt - Emit a compound statement {..} node. If GetLast is true,
@@ -562,8 +568,8 @@ CodeGenFunction::EmitCompoundStmtWithoutScope(const CompoundStmt &S,
562568
// Optionally set up the new FP environment, if the compound statement
563569
// contains a pragma that modifies it.
564570
FPOptions NewFP = S.getNewFPOptions(CurFPFeatures);
565-
emitSetFPControlModes(NewFP);
566571
CGFPOptionsRAII SavedFPFeatues(*this, NewFP);
572+
emitSetFPControlModes(NewFP, SavedFPFeatues.getOldFPOptions());
567573

568574
Address RetAlloca = Address::invalid();
569575

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ CodeGenFunction::CGFPOptionsRAII::CGFPOptionsRAII(CodeGenFunction &CGF,
147147
void CodeGenFunction::CGFPOptionsRAII::ConstructorHelper(FPOptions FPFeatures) {
148148
OldFPFeatures = CGF.CurFPFeatures;
149149
CGF.CurFPFeatures = FPFeatures;
150+
OldDynamicRM = CGF.DynamicRoundingMode;
151+
OldStaticRM = CGF.StaticRoundingMode;
152+
OldCurrentRoundingIsStatic = CGF.CurrentRoundingIsStatic;
150153

151154
OldExcept = CGF.Builder.getDefaultConstrainedExcept();
152155
OldRounding = CGF.Builder.getDefaultConstrainedRounding();
@@ -191,6 +194,9 @@ void CodeGenFunction::CGFPOptionsRAII::ConstructorHelper(FPOptions FPFeatures) {
191194

192195
CodeGenFunction::CGFPOptionsRAII::~CGFPOptionsRAII() {
193196
CGF.CurFPFeatures = OldFPFeatures;
197+
CGF.DynamicRoundingMode = OldDynamicRM;
198+
CGF.StaticRoundingMode = OldStaticRM;
199+
CGF.CurrentRoundingIsStatic = OldCurrentRoundingIsStatic;
194200
CGF.Builder.setDefaultConstrainedExcept(OldExcept);
195201
CGF.Builder.setDefaultConstrainedRounding(OldRounding);
196202
}

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,15 +817,23 @@ class CodeGenFunction : public CodeGenTypeCache {
817817
CGFPOptionsRAII(CodeGenFunction &CGF, const Expr *E);
818818
~CGFPOptionsRAII();
819819

820+
FPOptions getOldFPOptions() const { return OldFPFeatures; }
821+
820822
private:
821823
void ConstructorHelper(FPOptions FPFeatures);
822824
CodeGenFunction &CGF;
823825
FPOptions OldFPFeatures;
826+
llvm::Value *OldDynamicRM;
827+
llvm::Value *OldStaticRM;
828+
bool OldCurrentRoundingIsStatic;
824829
llvm::fp::ExceptionBehavior OldExcept;
825830
llvm::RoundingMode OldRounding;
826831
std::optional<CGBuilderTy::FastMathFlagGuard> FMFGuard;
827832
};
828833
FPOptions CurFPFeatures;
834+
llvm::Value *DynamicRoundingMode = nullptr;
835+
llvm::Value *StaticRoundingMode = nullptr;
836+
bool CurrentRoundingIsStatic = false;
829837

830838
public:
831839
/// ObjCEHValueStack - Stack of Objective-C exception values, used for
@@ -3326,7 +3334,12 @@ class CodeGenFunction : public CodeGenTypeCache {
33263334

33273335
/// Optionally emit code that sets required floating-point control modes and
33283336
/// creates corresponding cleanup action.
3329-
void emitSetFPControlModes(FPOptions NewFP);
3337+
void emitSetFPControlModes(FPOptions NewFP, FPOptions OldFP);
3338+
3339+
void setRoundingModeForCall(const CGCallee &Callee);
3340+
void restoreRoundingModeAfterCall();
3341+
bool requiresDynamicRounding(const CGCallee &Callee);
3342+
33303343

33313344
//===--------------------------------------------------------------------===//
33323345
// Declaration Emission

clang/test/CodeGen/complex-strictfp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ void test3b(void) {
6161
// CHECK-NEXT: [[CF_IMAG:%.*]] = load float, ptr getelementptr inbounds ({ float, float }, ptr @cf, i32 0, i32 1), align 4
6262
// CHECK-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR3]]
6363
// CHECK-NEXT: [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR3]]
64+
// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR3]]
6465
// CHECK-NEXT: [[CALL:%.*]] = call { double, double } @__divdc3(double noundef [[CONV]], double noundef [[CONV1]], double noundef [[G1_REAL]], double noundef [[G1_IMAG]]) #[[ATTR4:[0-9]+]]
66+
// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR3]]
6567
// CHECK-NEXT: [[TMP0:%.*]] = extractvalue { double, double } [[CALL]], 0
6668
// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[CALL]], 1
6769
// CHECK-NEXT: [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR3]]

0 commit comments

Comments
 (0)