diff --git a/llvm/docs/AddingConstrainedIntrinsics.rst b/llvm/docs/AddingConstrainedIntrinsics.rst --- a/llvm/docs/AddingConstrainedIntrinsics.rst +++ b/llvm/docs/AddingConstrainedIntrinsics.rst @@ -13,52 +13,52 @@ Multiple files need to be updated when adding a new constrained intrinsic. -Add the new intrinsic to the table of intrinsics.:: +Add the new intrinsic to the table of intrinsics:: include/llvm/IR/Intrinsics.td -Update class ConstrainedFPIntrinsic to know about the intrinsics.:: +Add SelectionDAG node types +=========================== - include/llvm/IR/IntrinsicInst.h +Add the new STRICT version of the node type to the ISD::NodeType enum:: -Functions like ConstrainedFPIntrinsic::isUnaryOp() or -ConstrainedFPIntrinsic::isTernaryOp() may need to know about the new -intrinsic.:: + include/llvm/CodeGen/ISDOpcodes.h - lib/IR/IntrinsicInst.cpp +Strict version name must be a concatenation of prefix "STRICT_" and the name +of corresponding non-strict node name. For instance, strict version of the +node FADD must be STRICT_FADD. -Update the IR verifier:: +Update mappings +=============== - lib/IR/Verifier.cpp +Add new record to the mapping of instructions to constrained intrinsic and +DAG nodes:: -Add SelectionDAG node types -=========================== - -Add the new STRICT version of the node type to the ISD::NodeType enum.:: + include/llvm/IR/ConstrainedOps.def - include/llvm/CodeGen/ISDOpcodes.h +Follow instructions provided in this file. -In class SDNode update isStrictFPOpcode():: +Update IR components +==================== - include/llvm/CodeGen/SelectionDAGNodes.h +Update the IR verifier:: -A mapping from the STRICT SDnode type to the non-STRICT is done in -TargetLoweringBase::getStrictFPOperationAction(). This allows STRICT -nodes to be legalized similarly to the non-STRICT node type.:: + lib/IR/Verifier.cpp - include/llvm/CodeGen/TargetLowering.h +Update Selector components +========================== Building the SelectionDAG ------------------------- -The switch statement in SelectionDAGBuilder::visitIntrinsicCall() needs -to be updated to call SelectionDAGBuilder::visitConstrainedFPIntrinsic(). -That function, in turn, needs to be updated to know how to create the -SDNode for the intrinsic. The new STRICT node will eventually be converted +The function SelectionDAGBuilder::visitConstrainedFPIntrinsic builds DAG nodes +using mappings specified in ConstrainedOps.def. If however this default build is +not sufficient, the build can be modified, see how it is implemented for +STRICT_FP_ROUND. The new STRICT node will eventually be converted to the matching non-STRICT node. For this reason it should have the same operands and values as the non-STRICT version but should also use the chain. This makes subsequent sharing of code for STRICT and non-STRICT code paths -easier.:: +easier:: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -74,18 +74,17 @@ often don't. The code to do the conversion or mutation of the STRICT node to a non-STRICT -version of the node happens in SelectionDAG::mutateStrictFPToFP(). Be +version of the node happens in SelectionDAG::mutateStrictFPToFP(). In most cases +the function can do the conversion using information from ConstrainedOps.def. Be careful updating this function since some nodes have the same return type as their input operand, but some are different. Both of these cases must -be properly handled.:: +be properly handled:: lib/CodeGen/SelectionDAG/SelectionDAG.cpp -However, the mutation may not happen if the new node has not been registered -in TargetLoweringBase::initActions(). If the corresponding non-STRICT node -is Legal but a target does not know about STRICT nodes then the STRICT -node will default to Legal and mutation will be bypassed with a "Cannot -select" error. Register the new STRICT node as Expand to avoid this bug.:: +Whether the mutation may happens or not, depends on how the new node has been +registered in TargetLoweringBase::initActions(). By default all strict nodes are +registered with Expand action:: lib/CodeGen/TargetLoweringBase.cpp diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h --- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h @@ -685,38 +685,10 @@ switch (NodeType) { default: return false; - case ISD::STRICT_FADD: - case ISD::STRICT_FSUB: - case ISD::STRICT_FMUL: - case ISD::STRICT_FDIV: - case ISD::STRICT_FREM: - case ISD::STRICT_FMA: - case ISD::STRICT_FSQRT: - case ISD::STRICT_FPOW: - case ISD::STRICT_FPOWI: - case ISD::STRICT_FSIN: - case ISD::STRICT_FCOS: - case ISD::STRICT_FEXP: - case ISD::STRICT_FEXP2: - case ISD::STRICT_FLOG: - case ISD::STRICT_FLOG10: - case ISD::STRICT_FLOG2: - case ISD::STRICT_LRINT: - case ISD::STRICT_LLRINT: - case ISD::STRICT_FRINT: - case ISD::STRICT_FNEARBYINT: - case ISD::STRICT_FMAXNUM: - case ISD::STRICT_FMINNUM: - case ISD::STRICT_FCEIL: - case ISD::STRICT_FFLOOR: - case ISD::STRICT_LROUND: - case ISD::STRICT_LLROUND: - case ISD::STRICT_FROUND: - case ISD::STRICT_FTRUNC: - case ISD::STRICT_FP_TO_SINT: - case ISD::STRICT_FP_TO_UINT: - case ISD::STRICT_FP_ROUND: - case ISD::STRICT_FP_EXTEND: +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" return true; } } diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -940,38 +940,10 @@ unsigned EqOpc; switch (Op) { default: llvm_unreachable("Unexpected FP pseudo-opcode"); - case ISD::STRICT_FADD: EqOpc = ISD::FADD; break; - case ISD::STRICT_FSUB: EqOpc = ISD::FSUB; break; - case ISD::STRICT_FMUL: EqOpc = ISD::FMUL; break; - case ISD::STRICT_FDIV: EqOpc = ISD::FDIV; break; - case ISD::STRICT_FREM: EqOpc = ISD::FREM; break; - case ISD::STRICT_FSQRT: EqOpc = ISD::FSQRT; break; - case ISD::STRICT_FPOW: EqOpc = ISD::FPOW; break; - case ISD::STRICT_FPOWI: EqOpc = ISD::FPOWI; break; - case ISD::STRICT_FMA: EqOpc = ISD::FMA; break; - case ISD::STRICT_FSIN: EqOpc = ISD::FSIN; break; - case ISD::STRICT_FCOS: EqOpc = ISD::FCOS; break; - case ISD::STRICT_FEXP: EqOpc = ISD::FEXP; break; - case ISD::STRICT_FEXP2: EqOpc = ISD::FEXP2; break; - case ISD::STRICT_FLOG: EqOpc = ISD::FLOG; break; - case ISD::STRICT_FLOG10: EqOpc = ISD::FLOG10; break; - case ISD::STRICT_FLOG2: EqOpc = ISD::FLOG2; break; - case ISD::STRICT_LRINT: EqOpc = ISD::LRINT; break; - case ISD::STRICT_LLRINT: EqOpc = ISD::LLRINT; break; - case ISD::STRICT_FRINT: EqOpc = ISD::FRINT; break; - case ISD::STRICT_FNEARBYINT: EqOpc = ISD::FNEARBYINT; break; - case ISD::STRICT_FMAXNUM: EqOpc = ISD::FMAXNUM; break; - case ISD::STRICT_FMINNUM: EqOpc = ISD::FMINNUM; break; - case ISD::STRICT_FCEIL: EqOpc = ISD::FCEIL; break; - case ISD::STRICT_FFLOOR: EqOpc = ISD::FFLOOR; break; - case ISD::STRICT_LROUND: EqOpc = ISD::LROUND; break; - case ISD::STRICT_LLROUND: EqOpc = ISD::LLROUND; break; - case ISD::STRICT_FROUND: EqOpc = ISD::FROUND; break; - case ISD::STRICT_FTRUNC: EqOpc = ISD::FTRUNC; break; - case ISD::STRICT_FP_TO_SINT: EqOpc = ISD::FP_TO_SINT; break; - case ISD::STRICT_FP_TO_UINT: EqOpc = ISD::FP_TO_UINT; break; - case ISD::STRICT_FP_ROUND: EqOpc = ISD::FP_ROUND; break; - case ISD::STRICT_FP_EXTEND: EqOpc = ISD::FP_EXTEND; break; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: EqOpc = ISD::DAGN; break; +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" } return getOperationAction(EqOpc, VT); diff --git a/llvm/include/llvm/IR/ConstrainedOps.def b/llvm/include/llvm/IR/ConstrainedOps.def new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/ConstrainedOps.def @@ -0,0 +1,70 @@ +//===- llvm/IR/ConstrainedOps.def - Constrained intrinsics ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines properties of constrained intrinsics, in particular corresponding +// floating point operations and DAG nodes. +// +//===----------------------------------------------------------------------===// + +#ifndef INSTRUCTION +#define INSTRUCTION(N,A,R,I,D) +#endif + +#ifndef FUNCTION +#define FUNCTION(F,A,R,I,D) +#endif + +// Arguments of the entries are: +// - instruction or intrinsic function name. +// - Number of original instruction/intrinsic arguments. +// - 1 if the corresponding constrained intrinsic has rounding mode argument. +// - name of the constrained intrinsic to represent this instruction/function. +// - DAG node corresponding to the constrained intrinsic without prefix STRICT_. + +// These are definitions for instructions, that are converted into constrained +// intrinsics. +// +INSTRUCTION(FAdd, 2, 1, experimental_constrained_fadd, FADD) +INSTRUCTION(FSub, 2, 1, experimental_constrained_fsub, FSUB) +INSTRUCTION(FMul, 2, 1, experimental_constrained_fmul, FMUL) +INSTRUCTION(FDiv, 2, 1, experimental_constrained_fdiv, FDIV) +INSTRUCTION(FRem, 2, 1, experimental_constrained_frem, FREM) +INSTRUCTION(FPExt, 1, 0, experimental_constrained_fpext, FP_EXTEND) +INSTRUCTION(FPToSI, 1, 0, experimental_constrained_fptosi, FP_TO_SINT) +INSTRUCTION(FPToUI, 1, 0, experimental_constrained_fptoui, FP_TO_UINT) +INSTRUCTION(FPTrunc, 1, 1, experimental_constrained_fptrunc, FP_ROUND) + +// Theses are definitions for intrinsic functions, that are converted into +// constrained intrinsics. +// +FUNCTION(ceil, 1, 1, experimental_constrained_ceil, FCEIL) +FUNCTION(cos, 1, 1, experimental_constrained_cos, FCOS) +FUNCTION(exp, 1, 1, experimental_constrained_exp, FEXP) +FUNCTION(exp2, 1, 1, experimental_constrained_exp2, FEXP2) +FUNCTION(floor, 1, 1, experimental_constrained_floor, FFLOOR) +FUNCTION(fma, 3, 1, experimental_constrained_fma, FMA) +FUNCTION(log, 1, 1, experimental_constrained_log, FLOG) +FUNCTION(log10, 1, 1, experimental_constrained_log10, FLOG10) +FUNCTION(log2, 1, 1, experimental_constrained_log2, FLOG2) +FUNCTION(lrint, 1, 1, experimental_constrained_lrint, LRINT) +FUNCTION(llrint, 1, 1, experimental_constrained_llrint, LLRINT) +FUNCTION(lround, 1, 0, experimental_constrained_lround, LROUND) +FUNCTION(llround, 1, 0, experimental_constrained_llround, LLROUND) +FUNCTION(maxnum, 2, 1, experimental_constrained_maxnum, FMAXNUM) +FUNCTION(minnum, 2, 1, experimental_constrained_minnum, FMINNUM) +FUNCTION(nearbyint, 1, 1, experimental_constrained_nearbyint, FNEARBYINT) +FUNCTION(pow, 2, 1, experimental_constrained_pow, FPOW) +FUNCTION(powi, 2, 1, experimental_constrained_powi, FPOWI) +FUNCTION(rint, 1, 1, experimental_constrained_rint, FRINT) +FUNCTION(round, 1, 1, experimental_constrained_round, FROUND) +FUNCTION(sin, 1, 1, experimental_constrained_sin, FSIN) +FUNCTION(sqrt, 1, 1, experimental_constrained_sqrt, FSQRT) +FUNCTION(trunc, 1, 1, experimental_constrained_trunc, FTRUNC) + +#undef INSTRUCTION +#undef FUNCTION diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -251,44 +251,7 @@ static Optional ExceptionBehaviorToStr(ExceptionBehavior); // Methods for support type inquiry through isa, cast, and dyn_cast: - static bool classof(const IntrinsicInst *I) { - switch (I->getIntrinsicID()) { - case Intrinsic::experimental_constrained_fadd: - case Intrinsic::experimental_constrained_fsub: - case Intrinsic::experimental_constrained_fmul: - case Intrinsic::experimental_constrained_fdiv: - case Intrinsic::experimental_constrained_frem: - case Intrinsic::experimental_constrained_fma: - case Intrinsic::experimental_constrained_fptosi: - case Intrinsic::experimental_constrained_fptoui: - case Intrinsic::experimental_constrained_fptrunc: - case Intrinsic::experimental_constrained_fpext: - case Intrinsic::experimental_constrained_sqrt: - case Intrinsic::experimental_constrained_pow: - case Intrinsic::experimental_constrained_powi: - case Intrinsic::experimental_constrained_sin: - case Intrinsic::experimental_constrained_cos: - case Intrinsic::experimental_constrained_exp: - case Intrinsic::experimental_constrained_exp2: - case Intrinsic::experimental_constrained_log: - case Intrinsic::experimental_constrained_log10: - case Intrinsic::experimental_constrained_log2: - case Intrinsic::experimental_constrained_lrint: - case Intrinsic::experimental_constrained_llrint: - case Intrinsic::experimental_constrained_rint: - case Intrinsic::experimental_constrained_nearbyint: - case Intrinsic::experimental_constrained_maxnum: - case Intrinsic::experimental_constrained_minnum: - case Intrinsic::experimental_constrained_ceil: - case Intrinsic::experimental_constrained_floor: - case Intrinsic::experimental_constrained_lround: - case Intrinsic::experimental_constrained_llround: - case Intrinsic::experimental_constrained_round: - case Intrinsic::experimental_constrained_trunc: - return true; - default: return false; - } - } + static bool classof(const IntrinsicInst *I); static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp @@ -310,34 +310,10 @@ switch (Op.getOpcode()) { default: return TranslateLegalizeResults(Op, Result); - case ISD::STRICT_FADD: - case ISD::STRICT_FSUB: - case ISD::STRICT_FMUL: - case ISD::STRICT_FDIV: - case ISD::STRICT_FREM: - case ISD::STRICT_FSQRT: - case ISD::STRICT_FMA: - case ISD::STRICT_FPOW: - case ISD::STRICT_FPOWI: - case ISD::STRICT_FSIN: - case ISD::STRICT_FCOS: - case ISD::STRICT_FEXP: - case ISD::STRICT_FEXP2: - case ISD::STRICT_FLOG: - case ISD::STRICT_FLOG10: - case ISD::STRICT_FLOG2: - case ISD::STRICT_FRINT: - case ISD::STRICT_FNEARBYINT: - case ISD::STRICT_FMAXNUM: - case ISD::STRICT_FMINNUM: - case ISD::STRICT_FCEIL: - case ISD::STRICT_FFLOOR: - case ISD::STRICT_FROUND: - case ISD::STRICT_FTRUNC: - case ISD::STRICT_FP_TO_SINT: - case ISD::STRICT_FP_TO_UINT: - case ISD::STRICT_FP_ROUND: - case ISD::STRICT_FP_EXTEND: +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0)); // If we're asked to expand a strict vector floating-point operation, // by default we're going to simply unroll it. That is usually the @@ -838,32 +814,10 @@ // targets? This should probably be investigated. And if we still prefer to // unroll an explanation could be helpful. return DAG.UnrollVectorOp(Op.getNode()); - case ISD::STRICT_FADD: - case ISD::STRICT_FSUB: - case ISD::STRICT_FMUL: - case ISD::STRICT_FDIV: - case ISD::STRICT_FREM: - case ISD::STRICT_FSQRT: - case ISD::STRICT_FMA: - case ISD::STRICT_FPOW: - case ISD::STRICT_FPOWI: - case ISD::STRICT_FSIN: - case ISD::STRICT_FCOS: - case ISD::STRICT_FEXP: - case ISD::STRICT_FEXP2: - case ISD::STRICT_FLOG: - case ISD::STRICT_FLOG10: - case ISD::STRICT_FLOG2: - case ISD::STRICT_FRINT: - case ISD::STRICT_FNEARBYINT: - case ISD::STRICT_FMAXNUM: - case ISD::STRICT_FMINNUM: - case ISD::STRICT_FCEIL: - case ISD::STRICT_FFLOOR: - case ISD::STRICT_FROUND: - case ISD::STRICT_FTRUNC: - case ISD::STRICT_FP_TO_SINT: - case ISD::STRICT_FP_TO_UINT: +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" return ExpandStrictFPOp(Op); case ISD::VECREDUCE_ADD: case ISD::VECREDUCE_MUL: diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp @@ -38,6 +38,21 @@ switch (N->getOpcode()) { default: + { + // Process strictfp nodes that were not processes in this switch. + bool IsStrictFPNode = false; + switch (N->getOpcode()) { +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" + R = ScalarizeVecRes_StrictFPOp(N); + IsStrictFPNode = true; + break; + } + if (IsStrictFPNode) + break; + } #ifndef NDEBUG dbgs() << "ScalarizeVectorResult #" << ResNo << ": "; N->dump(&DAG); @@ -146,35 +161,6 @@ case ISD::FMA: R = ScalarizeVecRes_TernaryOp(N); break; - case ISD::STRICT_FADD: - case ISD::STRICT_FSUB: - case ISD::STRICT_FMUL: - case ISD::STRICT_FDIV: - case ISD::STRICT_FREM: - case ISD::STRICT_FSQRT: - case ISD::STRICT_FMA: - case ISD::STRICT_FPOW: - case ISD::STRICT_FPOWI: - case ISD::STRICT_FSIN: - case ISD::STRICT_FCOS: - case ISD::STRICT_FEXP: - case ISD::STRICT_FEXP2: - case ISD::STRICT_FLOG: - case ISD::STRICT_FLOG10: - case ISD::STRICT_FLOG2: - case ISD::STRICT_FRINT: - case ISD::STRICT_FNEARBYINT: - case ISD::STRICT_FMAXNUM: - case ISD::STRICT_FMINNUM: - case ISD::STRICT_FCEIL: - case ISD::STRICT_FFLOOR: - case ISD::STRICT_FROUND: - case ISD::STRICT_FTRUNC: - case ISD::STRICT_FP_TO_SINT: - case ISD::STRICT_FP_TO_UINT: - case ISD::STRICT_FP_EXTEND: - R = ScalarizeVecRes_StrictFPOp(N); - break; case ISD::UADDO: case ISD::SADDO: case ISD::USUBO: @@ -833,6 +819,21 @@ switch (N->getOpcode()) { default: + { + // Process strictfp nodes that were not processes in this switch. + bool IsStrictFPNode = false; + switch (N->getOpcode()) { +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" + SplitVecRes_StrictFPOp(N, Lo, Hi); + IsStrictFPNode = true; + break; + } + if (IsStrictFPNode) + break; + } #ifndef NDEBUG dbgs() << "SplitVectorResult #" << ResNo << ": "; N->dump(&DAG); @@ -964,32 +965,6 @@ case ISD::FMA: SplitVecRes_TernaryOp(N, Lo, Hi); break; - case ISD::STRICT_FADD: - case ISD::STRICT_FSUB: - case ISD::STRICT_FMUL: - case ISD::STRICT_FDIV: - case ISD::STRICT_FREM: - case ISD::STRICT_FSQRT: - case ISD::STRICT_FMA: - case ISD::STRICT_FPOW: - case ISD::STRICT_FPOWI: - case ISD::STRICT_FSIN: - case ISD::STRICT_FCOS: - case ISD::STRICT_FEXP: - case ISD::STRICT_FEXP2: - case ISD::STRICT_FLOG: - case ISD::STRICT_FLOG10: - case ISD::STRICT_FLOG2: - case ISD::STRICT_FRINT: - case ISD::STRICT_FNEARBYINT: - case ISD::STRICT_FMAXNUM: - case ISD::STRICT_FMINNUM: - case ISD::STRICT_FCEIL: - case ISD::STRICT_FFLOOR: - case ISD::STRICT_FROUND: - case ISD::STRICT_FTRUNC: - SplitVecRes_StrictFPOp(N, Lo, Hi); - break; case ISD::UADDO: case ISD::SADDO: case ISD::USUBO: @@ -2698,6 +2673,21 @@ SDValue Res = SDValue(); switch (N->getOpcode()) { default: + { + // Process strictfp nodes that were not processes in this switch. + bool IsStrictFPNode = false; + switch (N->getOpcode()) { +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" + Res = WidenVecRes_StrictFP(N); + IsStrictFPNode = true; + break; + } + if (IsStrictFPNode) + break; + } #ifndef NDEBUG dbgs() << "WidenVectorResult #" << ResNo << ": "; N->dump(&DAG); @@ -2774,33 +2764,6 @@ Res = WidenVecRes_BinaryWithExtraScalarOp(N); break; - case ISD::STRICT_FADD: - case ISD::STRICT_FSUB: - case ISD::STRICT_FMUL: - case ISD::STRICT_FDIV: - case ISD::STRICT_FREM: - case ISD::STRICT_FSQRT: - case ISD::STRICT_FMA: - case ISD::STRICT_FPOW: - case ISD::STRICT_FPOWI: - case ISD::STRICT_FSIN: - case ISD::STRICT_FCOS: - case ISD::STRICT_FEXP: - case ISD::STRICT_FEXP2: - case ISD::STRICT_FLOG: - case ISD::STRICT_FLOG10: - case ISD::STRICT_FLOG2: - case ISD::STRICT_FRINT: - case ISD::STRICT_FNEARBYINT: - case ISD::STRICT_FMAXNUM: - case ISD::STRICT_FMINNUM: - case ISD::STRICT_FCEIL: - case ISD::STRICT_FFLOOR: - case ISD::STRICT_FROUND: - case ISD::STRICT_FTRUNC: - Res = WidenVecRes_StrictFP(N); - break; - case ISD::UADDO: case ISD::SADDO: case ISD::USUBO: diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -7751,38 +7751,10 @@ switch (OrigOpc) { default: llvm_unreachable("mutateStrictFPToFP called with unexpected opcode!"); - case ISD::STRICT_FADD: NewOpc = ISD::FADD; break; - case ISD::STRICT_FSUB: NewOpc = ISD::FSUB; break; - case ISD::STRICT_FMUL: NewOpc = ISD::FMUL; break; - case ISD::STRICT_FDIV: NewOpc = ISD::FDIV; break; - case ISD::STRICT_FREM: NewOpc = ISD::FREM; break; - case ISD::STRICT_FMA: NewOpc = ISD::FMA; break; - case ISD::STRICT_FSQRT: NewOpc = ISD::FSQRT; break; - case ISD::STRICT_FPOW: NewOpc = ISD::FPOW; break; - case ISD::STRICT_FPOWI: NewOpc = ISD::FPOWI; break; - case ISD::STRICT_FSIN: NewOpc = ISD::FSIN; break; - case ISD::STRICT_FCOS: NewOpc = ISD::FCOS; break; - case ISD::STRICT_FEXP: NewOpc = ISD::FEXP; break; - case ISD::STRICT_FEXP2: NewOpc = ISD::FEXP2; break; - case ISD::STRICT_FLOG: NewOpc = ISD::FLOG; break; - case ISD::STRICT_FLOG10: NewOpc = ISD::FLOG10; break; - case ISD::STRICT_FLOG2: NewOpc = ISD::FLOG2; break; - case ISD::STRICT_LRINT: NewOpc = ISD::LRINT; break; - case ISD::STRICT_LLRINT: NewOpc = ISD::LLRINT; break; - case ISD::STRICT_FRINT: NewOpc = ISD::FRINT; break; - case ISD::STRICT_FNEARBYINT: NewOpc = ISD::FNEARBYINT; break; - case ISD::STRICT_FMAXNUM: NewOpc = ISD::FMAXNUM; break; - case ISD::STRICT_FMINNUM: NewOpc = ISD::FMINNUM; break; - case ISD::STRICT_FCEIL: NewOpc = ISD::FCEIL; break; - case ISD::STRICT_FFLOOR: NewOpc = ISD::FFLOOR; break; - case ISD::STRICT_LROUND: NewOpc = ISD::LROUND; break; - case ISD::STRICT_LLROUND: NewOpc = ISD::LLROUND; break; - case ISD::STRICT_FROUND: NewOpc = ISD::FROUND; break; - case ISD::STRICT_FTRUNC: NewOpc = ISD::FTRUNC; break; - case ISD::STRICT_FP_ROUND: NewOpc = ISD::FP_ROUND; break; - case ISD::STRICT_FP_EXTEND: NewOpc = ISD::FP_EXTEND; break; - case ISD::STRICT_FP_TO_SINT: NewOpc = ISD::FP_TO_SINT; break; - case ISD::STRICT_FP_TO_UINT: NewOpc = ISD::FP_TO_UINT; break; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case ISD::STRICT_##DAGN: NewOpc = ISD::DAGN; break; +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" } assert(Node->getNumValues() == 2 && "Unexpected number of results!"); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6120,38 +6120,10 @@ getValue(I.getArgOperand(1)), getValue(I.getArgOperand(2)))); return; - case Intrinsic::experimental_constrained_fadd: - case Intrinsic::experimental_constrained_fsub: - case Intrinsic::experimental_constrained_fmul: - case Intrinsic::experimental_constrained_fdiv: - case Intrinsic::experimental_constrained_frem: - case Intrinsic::experimental_constrained_fma: - case Intrinsic::experimental_constrained_fptosi: - case Intrinsic::experimental_constrained_fptoui: - case Intrinsic::experimental_constrained_fptrunc: - case Intrinsic::experimental_constrained_fpext: - case Intrinsic::experimental_constrained_sqrt: - case Intrinsic::experimental_constrained_pow: - case Intrinsic::experimental_constrained_powi: - case Intrinsic::experimental_constrained_sin: - case Intrinsic::experimental_constrained_cos: - case Intrinsic::experimental_constrained_exp: - case Intrinsic::experimental_constrained_exp2: - case Intrinsic::experimental_constrained_log: - case Intrinsic::experimental_constrained_log10: - case Intrinsic::experimental_constrained_log2: - case Intrinsic::experimental_constrained_lrint: - case Intrinsic::experimental_constrained_llrint: - case Intrinsic::experimental_constrained_rint: - case Intrinsic::experimental_constrained_nearbyint: - case Intrinsic::experimental_constrained_maxnum: - case Intrinsic::experimental_constrained_minnum: - case Intrinsic::experimental_constrained_ceil: - case Intrinsic::experimental_constrained_floor: - case Intrinsic::experimental_constrained_lround: - case Intrinsic::experimental_constrained_llround: - case Intrinsic::experimental_constrained_round: - case Intrinsic::experimental_constrained_trunc: +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" visitConstrainedFPIntrinsic(cast(I)); return; case Intrinsic::fmuladd: { @@ -6917,106 +6889,18 @@ unsigned Opcode; switch (FPI.getIntrinsicID()) { default: llvm_unreachable("Impossible intrinsic"); // Can't reach here. - case Intrinsic::experimental_constrained_fadd: - Opcode = ISD::STRICT_FADD; - break; - case Intrinsic::experimental_constrained_fsub: - Opcode = ISD::STRICT_FSUB; - break; - case Intrinsic::experimental_constrained_fmul: - Opcode = ISD::STRICT_FMUL; - break; - case Intrinsic::experimental_constrained_fdiv: - Opcode = ISD::STRICT_FDIV; - break; - case Intrinsic::experimental_constrained_frem: - Opcode = ISD::STRICT_FREM; - break; - case Intrinsic::experimental_constrained_fma: - Opcode = ISD::STRICT_FMA; - break; - case Intrinsic::experimental_constrained_fptosi: - Opcode = ISD::STRICT_FP_TO_SINT; - break; - case Intrinsic::experimental_constrained_fptoui: - Opcode = ISD::STRICT_FP_TO_UINT; - break; - case Intrinsic::experimental_constrained_fptrunc: - Opcode = ISD::STRICT_FP_ROUND; - Opers.push_back(DAG.getTargetConstant(0, sdl, - TLI.getPointerTy(DAG.getDataLayout()))); - break; - case Intrinsic::experimental_constrained_fpext: - Opcode = ISD::STRICT_FP_EXTEND; - break; - case Intrinsic::experimental_constrained_sqrt: - Opcode = ISD::STRICT_FSQRT; - break; - case Intrinsic::experimental_constrained_pow: - Opcode = ISD::STRICT_FPOW; - break; - case Intrinsic::experimental_constrained_powi: - Opcode = ISD::STRICT_FPOWI; - break; - case Intrinsic::experimental_constrained_sin: - Opcode = ISD::STRICT_FSIN; - break; - case Intrinsic::experimental_constrained_cos: - Opcode = ISD::STRICT_FCOS; - break; - case Intrinsic::experimental_constrained_exp: - Opcode = ISD::STRICT_FEXP; - break; - case Intrinsic::experimental_constrained_exp2: - Opcode = ISD::STRICT_FEXP2; - break; - case Intrinsic::experimental_constrained_log: - Opcode = ISD::STRICT_FLOG; - break; - case Intrinsic::experimental_constrained_log10: - Opcode = ISD::STRICT_FLOG10; - break; - case Intrinsic::experimental_constrained_log2: - Opcode = ISD::STRICT_FLOG2; - break; - case Intrinsic::experimental_constrained_lrint: - Opcode = ISD::STRICT_LRINT; - break; - case Intrinsic::experimental_constrained_llrint: - Opcode = ISD::STRICT_LLRINT; - break; - case Intrinsic::experimental_constrained_rint: - Opcode = ISD::STRICT_FRINT; - break; - case Intrinsic::experimental_constrained_nearbyint: - Opcode = ISD::STRICT_FNEARBYINT; - break; - case Intrinsic::experimental_constrained_maxnum: - Opcode = ISD::STRICT_FMAXNUM; - break; - case Intrinsic::experimental_constrained_minnum: - Opcode = ISD::STRICT_FMINNUM; - break; - case Intrinsic::experimental_constrained_ceil: - Opcode = ISD::STRICT_FCEIL; - break; - case Intrinsic::experimental_constrained_floor: - Opcode = ISD::STRICT_FFLOOR; - break; - case Intrinsic::experimental_constrained_lround: - Opcode = ISD::STRICT_LROUND; - break; - case Intrinsic::experimental_constrained_llround: - Opcode = ISD::STRICT_LLROUND; - break; - case Intrinsic::experimental_constrained_round: - Opcode = ISD::STRICT_FROUND; - break; - case Intrinsic::experimental_constrained_trunc: - Opcode = ISD::STRICT_FTRUNC; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: \ + Opcode = ISD::STRICT_##DAGN; \ break; +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" } + if (Opcode == ISD::STRICT_FP_ROUND) + Opers.push_back( + DAG.getTargetConstant(0, sdl, TLI.getPointerTy(DAG.getDataLayout()))); + SDVTList VTs = DAG.getVTList(ValueVTs); SDValue Result = DAG.getNode(Opcode, sdl, VTs, Opers); diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -694,38 +694,10 @@ } // Constrained floating-point operations default to expand. - setOperationAction(ISD::STRICT_FADD, VT, Expand); - setOperationAction(ISD::STRICT_FSUB, VT, Expand); - setOperationAction(ISD::STRICT_FMUL, VT, Expand); - setOperationAction(ISD::STRICT_FDIV, VT, Expand); - setOperationAction(ISD::STRICT_FREM, VT, Expand); - setOperationAction(ISD::STRICT_FMA, VT, Expand); - setOperationAction(ISD::STRICT_FSQRT, VT, Expand); - setOperationAction(ISD::STRICT_FPOW, VT, Expand); - setOperationAction(ISD::STRICT_FPOWI, VT, Expand); - setOperationAction(ISD::STRICT_FSIN, VT, Expand); - setOperationAction(ISD::STRICT_FCOS, VT, Expand); - setOperationAction(ISD::STRICT_FEXP, VT, Expand); - setOperationAction(ISD::STRICT_FEXP2, VT, Expand); - setOperationAction(ISD::STRICT_FLOG, VT, Expand); - setOperationAction(ISD::STRICT_FLOG10, VT, Expand); - setOperationAction(ISD::STRICT_FLOG2, VT, Expand); - setOperationAction(ISD::STRICT_LRINT, VT, Expand); - setOperationAction(ISD::STRICT_LLRINT, VT, Expand); - setOperationAction(ISD::STRICT_FRINT, VT, Expand); - setOperationAction(ISD::STRICT_FNEARBYINT, VT, Expand); - setOperationAction(ISD::STRICT_FCEIL, VT, Expand); - setOperationAction(ISD::STRICT_FFLOOR, VT, Expand); - setOperationAction(ISD::STRICT_LROUND, VT, Expand); - setOperationAction(ISD::STRICT_LLROUND, VT, Expand); - setOperationAction(ISD::STRICT_FROUND, VT, Expand); - setOperationAction(ISD::STRICT_FTRUNC, VT, Expand); - setOperationAction(ISD::STRICT_FMAXNUM, VT, Expand); - setOperationAction(ISD::STRICT_FMINNUM, VT, Expand); - setOperationAction(ISD::STRICT_FP_ROUND, VT, Expand); - setOperationAction(ISD::STRICT_FP_EXTEND, VT, Expand); - setOperationAction(ISD::STRICT_FP_TO_SINT, VT, Expand); - setOperationAction(ISD::STRICT_FP_TO_UINT, VT, Expand); +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + setOperationAction(ISD::STRICT_##DAGN, VT, Expand); +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" // For most targets @llvm.get.dynamic.area.offset just returns 0. setOperationAction(ISD::GET_DYNAMIC_AREA_OFFSET, VT, Expand); diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp --- a/llvm/lib/IR/IntrinsicInst.cpp +++ b/llvm/lib/IR/IntrinsicInst.cpp @@ -188,29 +188,11 @@ switch (getIntrinsicID()) { default: return false; - case Intrinsic::experimental_constrained_fptosi: - case Intrinsic::experimental_constrained_fptoui: - case Intrinsic::experimental_constrained_fptrunc: - case Intrinsic::experimental_constrained_fpext: - case Intrinsic::experimental_constrained_sqrt: - case Intrinsic::experimental_constrained_sin: - case Intrinsic::experimental_constrained_cos: - case Intrinsic::experimental_constrained_exp: - case Intrinsic::experimental_constrained_exp2: - case Intrinsic::experimental_constrained_log: - case Intrinsic::experimental_constrained_log10: - case Intrinsic::experimental_constrained_log2: - case Intrinsic::experimental_constrained_lrint: - case Intrinsic::experimental_constrained_llrint: - case Intrinsic::experimental_constrained_rint: - case Intrinsic::experimental_constrained_nearbyint: - case Intrinsic::experimental_constrained_ceil: - case Intrinsic::experimental_constrained_floor: - case Intrinsic::experimental_constrained_lround: - case Intrinsic::experimental_constrained_llround: - case Intrinsic::experimental_constrained_round: - case Intrinsic::experimental_constrained_trunc: - return true; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: \ + return NARG == 1; +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" } } @@ -218,8 +200,23 @@ switch (getIntrinsicID()) { default: return false; - case Intrinsic::experimental_constrained_fma: - return true; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: \ + return NARG == 3; +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" + } +} + +bool ConstrainedFPIntrinsic::classof(const IntrinsicInst *I) { + switch (I->getIntrinsicID()) { +#define INSTRUCTION(NAME, NARGS, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" + return true; + default: + return false; } } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -4300,38 +4300,10 @@ "an array"); break; } - case Intrinsic::experimental_constrained_fadd: - case Intrinsic::experimental_constrained_fsub: - case Intrinsic::experimental_constrained_fmul: - case Intrinsic::experimental_constrained_fdiv: - case Intrinsic::experimental_constrained_frem: - case Intrinsic::experimental_constrained_fma: - case Intrinsic::experimental_constrained_fptosi: - case Intrinsic::experimental_constrained_fptoui: - case Intrinsic::experimental_constrained_fptrunc: - case Intrinsic::experimental_constrained_fpext: - case Intrinsic::experimental_constrained_sqrt: - case Intrinsic::experimental_constrained_pow: - case Intrinsic::experimental_constrained_powi: - case Intrinsic::experimental_constrained_sin: - case Intrinsic::experimental_constrained_cos: - case Intrinsic::experimental_constrained_exp: - case Intrinsic::experimental_constrained_exp2: - case Intrinsic::experimental_constrained_log: - case Intrinsic::experimental_constrained_log10: - case Intrinsic::experimental_constrained_log2: - case Intrinsic::experimental_constrained_lrint: - case Intrinsic::experimental_constrained_llrint: - case Intrinsic::experimental_constrained_rint: - case Intrinsic::experimental_constrained_nearbyint: - case Intrinsic::experimental_constrained_maxnum: - case Intrinsic::experimental_constrained_minnum: - case Intrinsic::experimental_constrained_ceil: - case Intrinsic::experimental_constrained_floor: - case Intrinsic::experimental_constrained_lround: - case Intrinsic::experimental_constrained_llround: - case Intrinsic::experimental_constrained_round: - case Intrinsic::experimental_constrained_trunc: +#define INSTRUCTION(NAME, NARGS, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" visitConstrainedFPIntrinsic(cast(Call)); break; case Intrinsic::dbg_declare: // llvm.dbg.declare @@ -4758,83 +4730,44 @@ } void Verifier::visitConstrainedFPIntrinsic(ConstrainedFPIntrinsic &FPI) { - unsigned NumOperands = FPI.getNumArgOperands(); - bool HasExceptionMD = false; - bool HasRoundingMD = false; + unsigned NumOperands; + bool HasRoundingMD; switch (FPI.getIntrinsicID()) { - case Intrinsic::experimental_constrained_sqrt: - case Intrinsic::experimental_constrained_sin: - case Intrinsic::experimental_constrained_cos: - case Intrinsic::experimental_constrained_exp: - case Intrinsic::experimental_constrained_exp2: - case Intrinsic::experimental_constrained_log: - case Intrinsic::experimental_constrained_log10: - case Intrinsic::experimental_constrained_log2: - case Intrinsic::experimental_constrained_rint: - case Intrinsic::experimental_constrained_nearbyint: - case Intrinsic::experimental_constrained_ceil: - case Intrinsic::experimental_constrained_floor: - case Intrinsic::experimental_constrained_round: - case Intrinsic::experimental_constrained_trunc: - Assert((NumOperands == 3), "invalid arguments for constrained FP intrinsic", - &FPI); - HasExceptionMD = true; - HasRoundingMD = true; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \ + case Intrinsic::INTRINSIC: \ + NumOperands = NARG; \ + HasRoundingMD = ROUND_MODE; \ break; +#define FUNCTION INSTRUCTION +#include "llvm/IR/ConstrainedOps.def" + default: + llvm_unreachable("Invalid constrained FP intrinsic!"); + } + NumOperands += (1 + HasRoundingMD); + Assert((FPI.getNumArgOperands() == NumOperands), + "invalid arguments for constrained FP intrinsic", &FPI); + switch (FPI.getIntrinsicID()) { case Intrinsic::experimental_constrained_lrint: case Intrinsic::experimental_constrained_llrint: { - Assert((NumOperands == 3), "invalid arguments for constrained FP intrinsic", - &FPI); Type *ValTy = FPI.getArgOperand(0)->getType(); Type *ResultTy = FPI.getType(); Assert(!ValTy->isVectorTy() && !ResultTy->isVectorTy(), "Intrinsic does not support vectors", &FPI); - HasExceptionMD = true; - HasRoundingMD = true; } break; case Intrinsic::experimental_constrained_lround: case Intrinsic::experimental_constrained_llround: { - Assert((NumOperands == 2), "invalid arguments for constrained FP intrinsic", - &FPI); Type *ValTy = FPI.getArgOperand(0)->getType(); Type *ResultTy = FPI.getType(); Assert(!ValTy->isVectorTy() && !ResultTy->isVectorTy(), "Intrinsic does not support vectors", &FPI); - HasExceptionMD = true; break; } - case Intrinsic::experimental_constrained_fma: - Assert((NumOperands == 5), "invalid arguments for constrained FP intrinsic", - &FPI); - HasExceptionMD = true; - HasRoundingMD = true; - break; - - case Intrinsic::experimental_constrained_fadd: - case Intrinsic::experimental_constrained_fsub: - case Intrinsic::experimental_constrained_fmul: - case Intrinsic::experimental_constrained_fdiv: - case Intrinsic::experimental_constrained_frem: - case Intrinsic::experimental_constrained_pow: - case Intrinsic::experimental_constrained_powi: - case Intrinsic::experimental_constrained_maxnum: - case Intrinsic::experimental_constrained_minnum: - Assert((NumOperands == 4), "invalid arguments for constrained FP intrinsic", - &FPI); - HasExceptionMD = true; - HasRoundingMD = true; - break; - case Intrinsic::experimental_constrained_fptosi: case Intrinsic::experimental_constrained_fptoui: { - Assert((NumOperands == 2), - "invalid arguments for constrained FP intrinsic", &FPI); - HasExceptionMD = true; - Value *Operand = FPI.getArgOperand(0); uint64_t NumSrcElem = 0; Assert(Operand->getType()->isFPOrFPVectorTy(), @@ -4858,16 +4791,6 @@ case Intrinsic::experimental_constrained_fptrunc: case Intrinsic::experimental_constrained_fpext: { - if (FPI.getIntrinsicID() == Intrinsic::experimental_constrained_fptrunc) { - Assert((NumOperands == 3), - "invalid arguments for constrained FP intrinsic", &FPI); - HasRoundingMD = true; - } else { - Assert((NumOperands == 2), - "invalid arguments for constrained FP intrinsic", &FPI); - } - HasExceptionMD = true; - Value *Operand = FPI.getArgOperand(0); Type *OperandTy = Operand->getType(); Value *Result = &FPI; @@ -4898,7 +4821,7 @@ break; default: - llvm_unreachable("Invalid constrained FP intrinsic!"); + break; } // If a non-metadata argument is passed in a metadata slot then the @@ -4906,10 +4829,8 @@ // match the specification in the intrinsic call table. Thus, no // argument type check is needed here. - if (HasExceptionMD) { - Assert(FPI.getExceptionBehavior().hasValue(), - "invalid exception behavior argument", &FPI); - } + Assert(FPI.getExceptionBehavior().hasValue(), + "invalid exception behavior argument", &FPI); if (HasRoundingMD) { Assert(FPI.getRoundingMode().hasValue(), "invalid rounding mode argument", &FPI);