Skip to content

Commit 8faecca

Browse files
committed
Minimal support of floating-point operand bundles
This is a lite version of llvm#109798, where code changes are minimized to facilitate discussion about the implementation. The motivations and ideas behind the new floating-point operation support are described in that PR and in the discussion https://discourse.llvm.org/t/rfc-change-of-strict-fp-operation-representation-in-ir/85021. There are concerns that the proposed changes are too invasive and a new approach is required to make the transition smoother. This implementation is essentially a subset of PR109798, where everything beyond the minimum is removed. It tries to build eventually the same implementation as that PR but in different steps. The patch does not attempt to modify the existing implementation based on the constrained intrinsics. Instead it introduces a new one using operand bundles. This new implementation initially has very limited functionality, which latter will be extended and finally can replace the existing one. This PR introduces the notion of floating-point operation, this is an intrinsic, that is listed in the file "FloatingPointOps.def". These have two additional properties: 1. In the strict environment (a function with strictfp attribute) calls to these operations acquire side effect, now it is read/write access to inaccessible memory, just as constrained intrinsics do. 2. Calls to these operations may have floating-point operand bundles. There are two kinds of such bundles, tagged with "fp.control" and "fp.except", which are used to carry additional information about control modes and exception handling. Initially the set of control modes consists of rounding mode only. The set of operations enlisted in "FloatingPointOps.def" and in "ConstrainedOps.def" are completely independent, an intrinsic may be in one list or in both. The set of floating-point operations is expected to grow and finally all FP intrinsics will be available in the new implementation. In this patch set of intrinsics in "FloatingPointOps.def" is minimum necessary for tests.
1 parent 60c9066 commit 8faecca

File tree

18 files changed

+717
-23
lines changed

18 files changed

+717
-23
lines changed

llvm/docs/LangRef.rst

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,6 +3199,51 @@ A "convergencectrl" operand bundle is only valid on a ``convergent`` operation.
31993199
When present, the operand bundle must contain exactly one value of token type.
32003200
See the :doc:`ConvergentOperations` document for details.
32013201

3202+
.. _ob_fp:
3203+
3204+
Floating-point Operand Bundles
3205+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3206+
3207+
These operand bundles are used for calls that involve floating-point
3208+
operations and interact with :ref:`floating-point environment <floatenv>` or
3209+
depend on floating-point options, such as rounding mode, denormal modes, etc.
3210+
There are two kinds of such operand bundles, which represent the value of
3211+
floating-point control modes and the treatment of status bits respectively.
3212+
3213+
An operand bundle tagged with "fp.control" contains information about the
3214+
control modes used for the operation execution. Operands specified in this
3215+
bundle represent particular options. Currently, only rounding mode is supported.
3216+
It is represented by a metadata string value, which specifies the rounding mode
3217+
to be used for the operation evaluation. Possible values are:
3218+
3219+
::
3220+
3221+
"rtz" - toward zero
3222+
"rte" - to nearest, ties to even
3223+
"rtp" - toward positive infinity
3224+
"rtn" - toward negative infinity
3225+
"rmm" - to nearest, ties away from zero
3226+
"dyn" - rounding mode is taken from control register
3227+
3228+
If "fp.control" is absent, the default rounding rounding mode is taken from the
3229+
control register (dynamic rounding). In the particular case of
3230+
:ref:`default floating-point environment <floatenv>`, it must be rounding to
3231+
nearest, ties to even.
3232+
3233+
An operand bundle tagged with "fp.except" may be associated with operations
3234+
that can read or write floating-point exception flags. It contains a single
3235+
metadata string value, which can have one of the following values:
3236+
3237+
::
3238+
3239+
"ignore"
3240+
"strict"
3241+
"maytrap"
3242+
3243+
It has the same meaning as the corresponding argument in
3244+
:ref:`constrained intrinsics <constrainedfp>`.
3245+
3246+
32023247
.. _moduleasm:
32033248

32043249
Module-Level Inline Assembly
@@ -3949,9 +3994,9 @@ round-to-nearest rounding mode, and subnormals are assumed to be preserved.
39493994
Running LLVM code in an environment where these assumptions are not met
39503995
typically leads to undefined behavior. The ``strictfp`` and ``denormal-fp-math``
39513996
attributes as well as :ref:`Constrained Floating-Point Intrinsics
3952-
<constrainedfp>` can be used to weaken LLVM's assumptions and ensure defined
3953-
behavior in non-default floating-point environments; see their respective
3954-
documentation for details.
3997+
<constrainedfp>` or :ref:`floating-point operand bundles<ob_fp>` can be used to
3998+
weaken LLVM's assumptions and ensure defined behavior in non-default
3999+
floating-point environments; see their respective documentation for details.
39554000

39564001
.. _floatnan:
39574002

llvm/docs/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Changes to the LLVM IR
6767
Instead, the `align` attribute should be placed on the pointer (or vector of
6868
pointers) argument.
6969
* A `load atomic` may now be used with vector types on x86.
70+
* Floating-point operand bundles have been added.
7071

7172
Changes to LLVM infrastructure
7273
------------------------------

llvm/include/llvm/IR/FPEnv.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,38 @@ enum ExceptionBehavior : uint8_t {
4949
/// metadata.
5050
LLVM_ABI std::optional<RoundingMode> convertStrToRoundingMode(StringRef);
5151

52+
/// Returns a valid RoundingMode enumerator given a string that is used as
53+
/// rounding mode specifier in operand bundles.
54+
std::optional<RoundingMode> convertBundleToRoundingMode(StringRef);
55+
5256
/// For any RoundingMode enumerator, returns a string valid as input in
5357
/// constrained intrinsic rounding mode metadata.
5458
LLVM_ABI std::optional<StringRef> convertRoundingModeToStr(RoundingMode);
5559

60+
/// For any RoundingMode enumerator, returns a string to be used in operand
61+
/// bundles.
62+
std::optional<StringRef> convertRoundingModeToBundle(RoundingMode);
63+
5664
/// Returns a valid ExceptionBehavior enumerator when given a string
5765
/// valid as input in constrained intrinsic exception behavior metadata.
5866
LLVM_ABI std::optional<fp::ExceptionBehavior>
5967
convertStrToExceptionBehavior(StringRef);
6068

69+
/// Returns a valid ExceptionBehavior enumerator given a string from the operand
70+
/// bundle argument.
71+
std::optional<fp::ExceptionBehavior>
72+
convertBundleToExceptionBehavior(StringRef);
73+
6174
/// For any ExceptionBehavior enumerator, returns a string valid as
6275
/// input in constrained intrinsic exception behavior metadata.
6376
LLVM_ABI std::optional<StringRef>
6477
convertExceptionBehaviorToStr(fp::ExceptionBehavior);
6578

79+
/// Return string representing the given exception behavior for use in operand
80+
/// bundles
81+
std::optional<StringRef>
82+
convertExceptionBehaviorToBundle(fp::ExceptionBehavior);
83+
6684
/// Returns true if the exception handling behavior and rounding mode
6785
/// match what is used in the default floating point environment.
6886
inline bool isDefaultFPEnvironment(fp::ExceptionBehavior EB, RoundingMode RM) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===- llvm/IR/FloatingPointOps.def - FP intrinsics -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Defines set of intrinsics, which are classified as floating-point operations.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef FUNCTION
14+
#define FUNCTION(N,D)
15+
#endif
16+
17+
// Arguments of the entries are:
18+
// - intrinsic function name,
19+
// - DAG node corresponding to the intrinsic.
20+
21+
FUNCTION(nearbyint, FNEARBYINT)
22+
FUNCTION(trunc, FTRUNC)
23+
24+
#undef FUNCTION

llvm/include/llvm/IR/IRBuilder.h

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,16 @@ class IRBuilderBase {
10001000
FMFSource FMFSource = {},
10011001
const Twine &Name = "");
10021002

1003+
/// Create a call to intrinsic \p ID with \p Args, mangled using \p Types and
1004+
/// with operand bundles.
1005+
/// If \p FMFSource is provided, copy fast-math-flags from that instruction to
1006+
/// the intrinsic.
1007+
CallInst *CreateIntrinsic(Intrinsic::ID ID, ArrayRef<Type *> Types,
1008+
ArrayRef<Value *> Args,
1009+
ArrayRef<OperandBundleDef> OpBundles,
1010+
Instruction *FMFSource = nullptr,
1011+
const Twine &Name = "");
1012+
10031013
/// Create a call to non-overloaded intrinsic \p ID with \p Args. If
10041014
/// \p FMFSource is provided, copy fast-math-flags from that instruction to
10051015
/// the intrinsic.
@@ -2511,24 +2521,13 @@ class IRBuilderBase {
25112521
CallInst *CreateCall(FunctionType *FTy, Value *Callee,
25122522
ArrayRef<Value *> Args = {}, const Twine &Name = "",
25132523
MDNode *FPMathTag = nullptr) {
2514-
CallInst *CI = CallInst::Create(FTy, Callee, Args, DefaultOperandBundles);
2515-
if (IsFPConstrained)
2516-
setConstrainedFPCallAttr(CI);
2517-
if (isa<FPMathOperator>(CI))
2518-
setFPAttrs(CI, FPMathTag, FMF);
2519-
return Insert(CI, Name);
2524+
return CreateCall(FTy, Callee, Args, DefaultOperandBundles, Name,
2525+
FPMathTag);
25202526
}
25212527

25222528
CallInst *CreateCall(FunctionType *FTy, Value *Callee, ArrayRef<Value *> Args,
25232529
ArrayRef<OperandBundleDef> OpBundles,
2524-
const Twine &Name = "", MDNode *FPMathTag = nullptr) {
2525-
CallInst *CI = CallInst::Create(FTy, Callee, Args, OpBundles);
2526-
if (IsFPConstrained)
2527-
setConstrainedFPCallAttr(CI);
2528-
if (isa<FPMathOperator>(CI))
2529-
setFPAttrs(CI, FPMathTag, FMF);
2530-
return Insert(CI, Name);
2531-
}
2530+
const Twine &Name = "", MDNode *FPMathTag = nullptr);
25322531

25332532
CallInst *CreateCall(FunctionCallee Callee, ArrayRef<Value *> Args = {},
25342533
const Twine &Name = "", MDNode *FPMathTag = nullptr) {
@@ -2767,6 +2766,24 @@ class IRBuilderBase {
27672766
/// assumption on the provided pointer.
27682767
LLVM_ABI CallInst *CreateDereferenceableAssumption(Value *PtrValue,
27692768
Value *SizeValue);
2769+
2770+
/// Create an operand bundle in the provided bundle set to represent given FP
2771+
/// rounding mode.
2772+
///
2773+
/// If the rounding mode is not defined, adds the default rounding mode,
2774+
/// stored in this builder object.
2775+
void
2776+
createFPRoundingBundle(SmallVectorImpl<OperandBundleDef> &Bundles,
2777+
std::optional<RoundingMode> Rounding = std::nullopt);
2778+
2779+
/// Create an operand bundle in the provided bundle set to represent FP
2780+
/// exception behavior.
2781+
///
2782+
/// If the exception behavior is not defined, adds the default behavior,
2783+
/// stored in this builder object.
2784+
void createFPExceptionBundle(
2785+
SmallVectorImpl<OperandBundleDef> &Bundles,
2786+
std::optional<fp::ExceptionBehavior> Except = std::nullopt);
27702787
};
27712788

27722789
/// This provides a uniform API for creating instructions and inserting

llvm/include/llvm/IR/InstrTypes.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/IR/CallingConv.h"
2626
#include "llvm/IR/DerivedTypes.h"
2727
#include "llvm/IR/FMF.h"
28+
#include "llvm/IR/FPEnv.h"
2829
#include "llvm/IR/Function.h"
2930
#include "llvm/IR/Instruction.h"
3031
#include "llvm/IR/LLVMContext.h"
@@ -1093,6 +1094,13 @@ template <typename InputTy> class OperandBundleDefT {
10931094
using OperandBundleDef = OperandBundleDefT<Value *>;
10941095
using ConstOperandBundleDef = OperandBundleDefT<const Value *>;
10951096

1097+
void addFPRoundingBundle(LLVMContext &Ctx,
1098+
SmallVectorImpl<OperandBundleDef> &Bundles,
1099+
RoundingMode Rounding);
1100+
void addFPExceptionBundle(LLVMContext &Ctx,
1101+
SmallVectorImpl<OperandBundleDef> &Bundles,
1102+
fp::ExceptionBehavior Except);
1103+
10961104
//===----------------------------------------------------------------------===//
10971105
// CallBase Class
10981106
//===----------------------------------------------------------------------===//
@@ -1152,6 +1160,8 @@ class CallBase : public Instruction {
11521160
/// number of extra operands.
11531161
LLVM_ABI unsigned getNumSubclassExtraOperandsDynamic() const;
11541162

1163+
MemoryEffects getFloatingPointMemoryEffects() const;
1164+
11551165
public:
11561166
using Instruction::getContext;
11571167

@@ -2162,6 +2172,12 @@ class CallBase : public Instruction {
21622172
return false;
21632173
}
21642174

2175+
/// Return rounding mode specified by operand bundles.
2176+
RoundingMode getRoundingMode() const;
2177+
2178+
/// Return exception behavior specified by operand bundles.
2179+
std::optional<fp::ExceptionBehavior> getExceptionBehavior() const;
2180+
21652181
/// Used to keep track of an operand bundle. See the main comment on
21662182
/// OperandBundleUser above.
21672183
struct BundleOpInfo {

llvm/include/llvm/IR/IntrinsicInst.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ class IntrinsicInst : public CallInst {
129129
/// course of IR transformations
130130
LLVM_ABI static bool mayLowerToFunctionCall(Intrinsic::ID IID);
131131

132+
/// Check if \p ID represents a function that may access FP environment and
133+
/// may have FP operand bundles.
134+
///
135+
/// Access to FP environment means that in the strict FP environment the
136+
/// function has read/write memory effect, which is used to maintain proper
137+
/// instructions ordering.
138+
static bool isFloatingPointOperation(Intrinsic::ID IID);
139+
132140
/// Methods for support type inquiry through isa, cast, and dyn_cast:
133141
static bool classof(const CallInst *I) {
134142
auto *F = dyn_cast_or_null<Function>(I->getCalledOperand());

llvm/include/llvm/IR/LLVMContext.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,9 @@ class LLVMContext {
9898
OB_kcfi = 8, // "kcfi"
9999
OB_convergencectrl = 9, // "convergencectrl"
100100
OB_align = 10, // "align"
101-
OB_LastBundleID = OB_align // Marker for last bundle ID
102-
};
101+
OB_fp_control = 11, // "fp.control"
102+
OB_fp_except = 12, // "fp.except"
103+
OB_LastBundleID = OB_fp_except // Marker for last bundle ID };
103104

104105
/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
105106
/// This ID is uniqued across modules in the current LLVMContext.

llvm/include/llvm/Support/ModRef.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ template <typename LocationEnum> class MemoryEffectsBase {
235235
return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory();
236236
}
237237

238+
/// Whether this function accesses inaccessible memory.
239+
bool doesAccessInaccessibleMem() const {
240+
return isModOrRefSet(getModRef(Location::InaccessibleMem));
241+
}
242+
238243
/// Whether this function only (at most) accesses errno memory.
239244
bool onlyAccessesErrnoMem() const {
240245
return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory();

llvm/lib/IR/FPEnv.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ std::optional<RoundingMode> convertStrToRoundingMode(StringRef RoundingArg) {
3434
.Default(std::nullopt);
3535
}
3636

37+
std::optional<RoundingMode> convertBundleToRoundingMode(StringRef RoundingArg) {
38+
return StringSwitch<std::optional<RoundingMode>>(RoundingArg)
39+
.Case("dyn", RoundingMode::Dynamic)
40+
.Case("rte", RoundingMode::NearestTiesToEven)
41+
.Case("rmm", RoundingMode::NearestTiesToAway)
42+
.Case("rtn", RoundingMode::TowardNegative)
43+
.Case("rtp", RoundingMode::TowardPositive)
44+
.Case("rtz", RoundingMode::TowardZero)
45+
.Default(std::nullopt);
46+
}
47+
3748
std::optional<StringRef> convertRoundingModeToStr(RoundingMode UseRounding) {
3849
std::optional<StringRef> RoundingStr;
3950
switch (UseRounding) {
@@ -61,6 +72,33 @@ std::optional<StringRef> convertRoundingModeToStr(RoundingMode UseRounding) {
6172
return RoundingStr;
6273
}
6374

75+
std::optional<StringRef> convertRoundingModeToBundle(RoundingMode UseRounding) {
76+
std::optional<StringRef> RoundingStr;
77+
switch (UseRounding) {
78+
case RoundingMode::Dynamic:
79+
RoundingStr = "dyn";
80+
break;
81+
case RoundingMode::NearestTiesToEven:
82+
RoundingStr = "rte";
83+
break;
84+
case RoundingMode::NearestTiesToAway:
85+
RoundingStr = "rmm";
86+
break;
87+
case RoundingMode::TowardNegative:
88+
RoundingStr = "rtn";
89+
break;
90+
case RoundingMode::TowardPositive:
91+
RoundingStr = "rtp";
92+
break;
93+
case RoundingMode::TowardZero:
94+
RoundingStr = "rtz";
95+
break;
96+
default:
97+
break;
98+
}
99+
return RoundingStr;
100+
}
101+
64102
std::optional<fp::ExceptionBehavior>
65103
convertStrToExceptionBehavior(StringRef ExceptionArg) {
66104
return StringSwitch<std::optional<fp::ExceptionBehavior>>(ExceptionArg)
@@ -70,6 +108,15 @@ convertStrToExceptionBehavior(StringRef ExceptionArg) {
70108
.Default(std::nullopt);
71109
}
72110

111+
std::optional<fp::ExceptionBehavior>
112+
convertBundleToExceptionBehavior(StringRef ExceptionArg) {
113+
return StringSwitch<std::optional<fp::ExceptionBehavior>>(ExceptionArg)
114+
.Case("ignore", fp::ebIgnore)
115+
.Case("maytrap", fp::ebMayTrap)
116+
.Case("strict", fp::ebStrict)
117+
.Default(std::nullopt);
118+
}
119+
73120
std::optional<StringRef>
74121
convertExceptionBehaviorToStr(fp::ExceptionBehavior UseExcept) {
75122
std::optional<StringRef> ExceptStr;
@@ -87,6 +134,23 @@ convertExceptionBehaviorToStr(fp::ExceptionBehavior UseExcept) {
87134
return ExceptStr;
88135
}
89136

137+
std::optional<StringRef>
138+
convertExceptionBehaviorToBundle(fp::ExceptionBehavior UseExcept) {
139+
std::optional<StringRef> ExceptStr;
140+
switch (UseExcept) {
141+
case fp::ebStrict:
142+
ExceptStr = "strict";
143+
break;
144+
case fp::ebIgnore:
145+
ExceptStr = "ignore";
146+
break;
147+
case fp::ebMayTrap:
148+
ExceptStr = "maytrap";
149+
break;
150+
}
151+
return ExceptStr;
152+
}
153+
90154
Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr) {
91155
Intrinsic::ID IID = Intrinsic::not_intrinsic;
92156
switch (Instr.getOpcode()) {

0 commit comments

Comments
 (0)