[clang] 2502e3b - IR: Promote "denormal-fp-math" to a first class attribute (#174293)

via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 5 05:31:39 PST 2026


Author: Matt Arsenault
Date: 2026-02-05T13:31:26Z
New Revision: 2502e3b7badcaf2d911410978bf8f8638b363854

URL: https://github.com/llvm/llvm-project/commit/2502e3b7badcaf2d911410978bf8f8638b363854
DIFF: https://github.com/llvm/llvm-project/commit/2502e3b7badcaf2d911410978bf8f8638b363854.diff

LOG: IR: Promote "denormal-fp-math" to a first class attribute (#174293)

Convert "denormal-fp-math" and "denormal-fp-math-f32" into a first
class denormal_fpenv attribute. Previously the query for the effective
denormal mode involved two string attribute queries with parsing. I'm
introducing more uses of this, so it makes sense to convert this
to a more efficient encoding. The old representation was also awkward
since it was split across two separate attributes. The new encoding
just stores the default and float modes as bitfields, largely avoiding
the need to consider if the other mode is set.

The syntax in the common cases looks like this:
  `denormal_fpenv(preservesign,preservesign)`
  `denormal_fpenv(float: preservesign,preservesign)`
  `denormal_fpenv(dynamic,dynamic float: preservesign,preservesign)`

I wasn't sure about reusing the float type name instead of adding a
new keyword. It's parsed as a type but only accepts float. I'm also
debating switching the name to subnormal to match the current
preferred IEEE terminology (also used by nofpclass and other
contexts).

This has a behavior change when using the command flag debug
options to set the denormal mode. The behavior of the flag
ignored functions with an explicit attribute set, per
the default and f32 version. Now that these are one attribute,
the flag logic can't distinguish which of the two components
were explicitly set on the function. Only one test appeared to
rely on this behavior, so I just avoided using the flags in it.

This also does not perform all the code cleanups this enables.
In particular the attributor handling could be cleaned up.

I also guessed at how to support this in MLIR. I followed
MemoryEffects as a reference; it appears bitfields are expanded
into arguments to attributes, so the representation there is
a bit uglier with the 2 2-element fields flattened into 4 arguments.

Added: 
    llvm/test/Assembler/denormal_fpenv.ll
    llvm/test/Assembler/invalid_denormal_fpenv.ll
    llvm/test/Bitcode/auto_upgrade_denormal_fp_math.ll
    llvm/test/Verifier/denormal_fpenv.ll

Modified: 
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CGCall.h
    clang/test/CodeGen/denormalfpmode-f32.c
    clang/test/CodeGen/denormalfpmode.c
    clang/test/CodeGenCUDA/flush-denormals.cu
    clang/test/CodeGenCUDA/link-builtin-bitcode-denormal-fp-mode.cu
    clang/test/CodeGenCUDA/propagate-attributes.cu
    clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl
    clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
    llvm/docs/LangRef.rst
    llvm/docs/ReleaseNotes.md
    llvm/include/llvm-c/Core.h
    llvm/include/llvm/ADT/FloatingPointMode.h
    llvm/include/llvm/Analysis/ConstantFolding.h
    llvm/include/llvm/AsmParser/LLParser.h
    llvm/include/llvm/AsmParser/LLToken.h
    llvm/include/llvm/Bitcode/LLVMBitCodes.h
    llvm/include/llvm/IR/Attributes.h
    llvm/include/llvm/IR/Attributes.td
    llvm/include/llvm/IR/Function.h
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/AsmParser/LLLexer.cpp
    llvm/lib/AsmParser/LLParser.cpp
    llvm/lib/Bitcode/Reader/BitcodeReader.cpp
    llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
    llvm/lib/CodeGen/CommandFlags.cpp
    llvm/lib/IR/Attributes.cpp
    llvm/lib/IR/AutoUpgrade.cpp
    llvm/lib/IR/Core.cpp
    llvm/lib/IR/Function.cpp
    llvm/lib/IR/Verifier.cpp
    llvm/lib/Support/FloatingPointMode.cpp
    llvm/lib/Target/AMDGPU/SIModeRegisterDefaults.cpp
    llvm/lib/Target/ARM/ARMAsmPrinter.cpp
    llvm/lib/Target/ARM/ARMTargetMachine.cpp
    llvm/lib/Transforms/IPO/AttributorAttributes.cpp
    llvm/lib/Transforms/Utils/CodeExtractor.cpp
    llvm/test/Analysis/CostModel/AMDGPU/fdiv.ll
    llvm/test/Bitcode/compatibility.ll
    llvm/test/CodeGen/AArch64/sqrt-fastmath.ll
    llvm/test/CodeGen/AArch64/stack-tagging-ex-1.ll
    llvm/test/CodeGen/AArch64/stack-tagging-untag-placement.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/fdiv.f32.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/fmamix-constant-bus-violation.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/fp-atomics-gfx942.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/fp64-atomics-gfx90a.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/frem.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll
    llvm/test/CodeGen/AMDGPU/GlobalISel/madmix-constant-bus-violation.ll
    llvm/test/CodeGen/AMDGPU/amdgpu-codegenprepare-fdiv.f64.ll
    llvm/test/CodeGen/AMDGPU/amdpal-msgpack-denormal.ll
    llvm/test/CodeGen/AMDGPU/atomics-hw-remarks-gfx90a.ll
    llvm/test/CodeGen/AMDGPU/clamp-modifier.ll
    llvm/test/CodeGen/AMDGPU/clamp.ll
    llvm/test/CodeGen/AMDGPU/dagcombine-fma-fmad.ll
    llvm/test/CodeGen/AMDGPU/default-fp-mode.ll
    llvm/test/CodeGen/AMDGPU/fabs-known-signbit-combine-fast-fdiv-lowering.ll
    llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
    llvm/test/CodeGen/AMDGPU/fcanonicalize.bf16.ll
    llvm/test/CodeGen/AMDGPU/fcanonicalize.f16.ll
    llvm/test/CodeGen/AMDGPU/fcanonicalize.ll
    llvm/test/CodeGen/AMDGPU/fdiv-nofpexcept.ll
    llvm/test/CodeGen/AMDGPU/fdiv.ll
    llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fadd.ll
    llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmax.ll
    llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmin.ll
    llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fsub.ll
    llvm/test/CodeGen/AMDGPU/fmaxnum.ll
    llvm/test/CodeGen/AMDGPU/fminnum.f64.ll
    llvm/test/CodeGen/AMDGPU/fminnum.ll
    llvm/test/CodeGen/AMDGPU/fneg-combines.f16.ll
    llvm/test/CodeGen/AMDGPU/fneg-combines.legal.f16.ll
    llvm/test/CodeGen/AMDGPU/fneg-combines.ll
    llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
    llvm/test/CodeGen/AMDGPU/fp-atomics-gfx942.ll
    llvm/test/CodeGen/AMDGPU/fp64-atomics-gfx90a.ll
    llvm/test/CodeGen/AMDGPU/frem.ll
    llvm/test/CodeGen/AMDGPU/fsub-as-fneg-src-modifier.ll
    llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd-wrong-subtarget.ll
    llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd.ll
    llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmax.ll
    llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmin.ll
    llvm/test/CodeGen/AMDGPU/global-atomicrmw-fsub.ll
    llvm/test/CodeGen/AMDGPU/global-atomics-fp-wrong-subtarget.ll
    llvm/test/CodeGen/AMDGPU/global_atomic_optimizer_fp_rtn.ll
    llvm/test/CodeGen/AMDGPU/global_atomics_optimizer_fp_no_rtn.ll
    llvm/test/CodeGen/AMDGPU/global_atomics_scan_fadd.ll
    llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmax.ll
    llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmin.ll
    llvm/test/CodeGen/AMDGPU/global_atomics_scan_fsub.ll
    llvm/test/CodeGen/AMDGPU/hsa-fp-mode.ll
    llvm/test/CodeGen/AMDGPU/known-never-snan.ll
    llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll
    llvm/test/CodeGen/AMDGPU/llvm.amdgcn.rcp.ll
    llvm/test/CodeGen/AMDGPU/llvm.exp.ll
    llvm/test/CodeGen/AMDGPU/llvm.exp10.ll
    llvm/test/CodeGen/AMDGPU/llvm.exp2.ll
    llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.bf16.ll
    llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.f16.ll
    llvm/test/CodeGen/AMDGPU/llvm.log.ll
    llvm/test/CodeGen/AMDGPU/llvm.log10.ll
    llvm/test/CodeGen/AMDGPU/llvm.log2.ll
    llvm/test/CodeGen/AMDGPU/llvm.maxnum.f16.ll
    llvm/test/CodeGen/AMDGPU/llvm.minnum.f16.ll
    llvm/test/CodeGen/AMDGPU/mad-mix-bf16.ll
    llvm/test/CodeGen/AMDGPU/mad-mix-hi-bf16.ll
    llvm/test/CodeGen/AMDGPU/mad-mix-hi.ll
    llvm/test/CodeGen/AMDGPU/mad-mix-lo-bf16.ll
    llvm/test/CodeGen/AMDGPU/mad-mix-lo.ll
    llvm/test/CodeGen/AMDGPU/mad-mix.ll
    llvm/test/CodeGen/AMDGPU/madak.ll
    llvm/test/CodeGen/AMDGPU/madmk.ll
    llvm/test/CodeGen/AMDGPU/mul24-pass-ordering.ll
    llvm/test/CodeGen/AMDGPU/omod.ll
    llvm/test/CodeGen/AMDGPU/operand-folding.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx1250.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx950.ll
    llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll
    llvm/test/CodeGen/AMDGPU/rcp-pattern.ll
    llvm/test/CodeGen/AMDGPU/rcp_iflag.ll
    llvm/test/CodeGen/AMDGPU/repeated-divisor.ll
    llvm/test/CodeGen/AMDGPU/rsq.f32-safe.ll
    llvm/test/CodeGen/AMDGPU/rsq.f32.ll
    llvm/test/CodeGen/AMDGPU/sdwa-peephole.ll
    llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
    llvm/test/CodeGen/AMDGPU/udivrem24.ll
    llvm/test/CodeGen/AMDGPU/v_mac.ll
    llvm/test/CodeGen/AMDGPU/v_mac_f16.ll
    llvm/test/CodeGen/AMDGPU/v_madak_f16.ll
    llvm/test/CodeGen/ARM/build-attributes-fn-attr3.ll
    llvm/test/CodeGen/ARM/build-attributes-fn-attr4.ll
    llvm/test/CodeGen/ARM/build-attributes-fn-attr5.ll
    llvm/test/CodeGen/ARM/build-attributes-fn-attr6.ll
    llvm/test/CodeGen/ARM/clang-section.ll
    llvm/test/CodeGen/ARM/cmse-clear-float-bigend.mir
    llvm/test/CodeGen/ARM/softfp-constant-comparison.ll
    llvm/test/CodeGen/Generic/denormal-fp-math-cl-opt.ll
    llvm/test/CodeGen/NVPTX/div.ll
    llvm/test/CodeGen/NVPTX/f32x2-instructions.ll
    llvm/test/CodeGen/NVPTX/fast-math.ll
    llvm/test/CodeGen/NVPTX/fexp2.ll
    llvm/test/CodeGen/NVPTX/flog2.ll
    llvm/test/CodeGen/NVPTX/math-intrins-sm80-ptx70-instcombine.ll
    llvm/test/CodeGen/NVPTX/math-intrins.ll
    llvm/test/CodeGen/NVPTX/nvptx-prec-divf32-flag.ll
    llvm/test/CodeGen/NVPTX/rsqrt-opt.ll
    llvm/test/CodeGen/NVPTX/sqrt-approx.ll
    llvm/test/CodeGen/PowerPC/fmf-propagation.ll
    llvm/test/CodeGen/PowerPC/recipest.ll
    llvm/test/CodeGen/Thumb2/LowOverheadLoops/skip-vpt-debug.mir
    llvm/test/CodeGen/Thumb2/mve-vpt-2-blocks-1-pred.mir
    llvm/test/CodeGen/Thumb2/pacbti-m-outliner-4.ll
    llvm/test/CodeGen/X86/clang-section-coff.ll
    llvm/test/CodeGen/X86/is_fpclass.ll
    llvm/test/CodeGen/X86/pow.ll
    llvm/test/CodeGen/X86/sqrt-fastmath-mir.ll
    llvm/test/CodeGen/X86/sqrt-fastmath-tune.ll
    llvm/test/CodeGen/X86/sqrt-fastmath.ll
    llvm/test/DebugInfo/COFF/fortran-contained-proc.ll
    llvm/test/Instrumentation/NumericalStabilitySanitizer/basic.ll
    llvm/test/Instrumentation/NumericalStabilitySanitizer/non_float_store.ll
    llvm/test/Instrumentation/NumericalStabilitySanitizer/scalable_vector.ll
    llvm/test/Other/opt-override-denormal-fp-math-f32.ll
    llvm/test/Other/opt-override-denormal-fp-math-mixed.ll
    llvm/test/Other/opt-override-denormal-fp-math.ll
    llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-rmw-fadd.ll
    llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-simplify-cfg-CAS-block.ll
    llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-log.ll
    llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rcp.ll
    llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
    llvm/test/Transforms/Attributor/denormal-fp-math.ll
    llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
    llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
    llvm/test/Transforms/Attributor/nofpclass-frem.ll
    llvm/test/Transforms/Attributor/nofpclass-frexp.ll
    llvm/test/Transforms/Attributor/nofpclass-ldexp.ll
    llvm/test/Transforms/Attributor/nofpclass-log.ll
    llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
    llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
    llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
    llvm/test/Transforms/Attributor/nofpclass-nan-fmul.ll
    llvm/test/Transforms/Attributor/nofpclass-powi.ll
    llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
    llvm/test/Transforms/Attributor/nofpclass.ll
    llvm/test/Transforms/Attributor/reduced/register_benchmark_test.ll
    llvm/test/Transforms/EarlyCSE/cannot-be-negative-zero-assert.ll
    llvm/test/Transforms/IndVarSimplify/addrec_no_exec_on_every_iteration.ll
    llvm/test/Transforms/InferAddressSpaces/AMDGPU/global-atomicrmw-fadd.ll
    llvm/test/Transforms/Inline/AMDGPU/inline-denormal-fp-math.ll
    llvm/test/Transforms/InstCombine/NVPTX/nvvm-intrins.ll
    llvm/test/Transforms/InstCombine/combine-is.fpclass-and-fcmp.ll
    llvm/test/Transforms/InstCombine/create-class-from-logic-fcmp.ll
    llvm/test/Transforms/InstCombine/fcmp-denormals-are-zero.ll
    llvm/test/Transforms/InstCombine/fcmp.ll
    llvm/test/Transforms/InstCombine/fmod.ll
    llvm/test/Transforms/InstCombine/is_fpclass.ll
    llvm/test/Transforms/InstCombine/log-to-intrinsic.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fadd.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
    llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-sqrt.ll
    llvm/test/Transforms/InstSimplify/canonicalize.ll
    llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
    llvm/test/Transforms/InstSimplify/floating-point-compare.ll
    llvm/test/Transforms/SCCP/float-denormal-simplification.ll
    llvm/test/Transforms/SCCP/no-fold-fcmp-dynamic-denormal-mode-issue114947.ll
    llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll
    llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.expected
    llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.expected
    llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.globals.expected
    llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.noglobals.expected
    llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.transitiveglobals.expected
    llvm/unittests/ADT/FloatingPointMode.cpp
    llvm/unittests/IR/AttributesTest.cpp
    mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
    mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td
    mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
    mlir/lib/Target/LLVMIR/ModuleImport.cpp
    mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
    mlir/test/Target/LLVMIR/Import/function-attributes.ll
    mlir/test/Target/LLVMIR/fp-math-function-attributes.mlir

Removed: 
    llvm/test/Verifier/denormal-fp-math.ll


################################################################################
diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 04f44146e1269..8ab277c3474b3 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -1942,11 +1942,9 @@ static bool HasStrictReturn(const CodeGenModule &Module, QualType RetTy,
 static void addDenormalModeAttrs(llvm::DenormalMode FPDenormalMode,
                                  llvm::DenormalMode FP32DenormalMode,
                                  llvm::AttrBuilder &FuncAttrs) {
-  if (FPDenormalMode != llvm::DenormalMode::getDefault())
-    FuncAttrs.addAttribute("denormal-fp-math", FPDenormalMode.str());
-
-  if (FP32DenormalMode != FPDenormalMode && FP32DenormalMode.isValid())
-    FuncAttrs.addAttribute("denormal-fp-math-f32", FP32DenormalMode.str());
+  llvm::DenormalFPEnv FPEnv(FPDenormalMode, FP32DenormalMode);
+  if (FPEnv != llvm::DenormalFPEnv::getDefault())
+    FuncAttrs.addDenormalFPEnvAttr(FPEnv);
 }
 
 /// Add default attributes to a function, which have merge semantics under
@@ -2168,35 +2166,19 @@ void CodeGen::mergeDefaultFunctionDefinitionAttributes(
 
   llvm::AttributeMask AttrsToRemove;
 
-  llvm::DenormalMode DenormModeToMerge = F.getDenormalModeRaw();
-  llvm::DenormalMode DenormModeToMergeF32 = F.getDenormalModeF32Raw();
-  llvm::DenormalMode Merged =
-      CodeGenOpts.FPDenormalMode.mergeCalleeMode(DenormModeToMerge);
-  llvm::DenormalMode MergedF32 = CodeGenOpts.FP32DenormalMode;
-
-  if (DenormModeToMergeF32.isValid()) {
-    MergedF32 =
-        CodeGenOpts.FP32DenormalMode.mergeCalleeMode(DenormModeToMergeF32);
-  }
+  llvm::DenormalFPEnv OptsFPEnv(CodeGenOpts.FPDenormalMode,
+                                CodeGenOpts.FP32DenormalMode);
+  llvm::DenormalFPEnv MergedFPEnv =
+      OptsFPEnv.mergeCalleeMode(F.getDenormalFPEnv());
 
-  if (Merged == llvm::DenormalMode::getDefault()) {
-    AttrsToRemove.addAttribute("denormal-fp-math");
-  } else if (Merged != DenormModeToMerge) {
-    // Overwrite existing attribute
-    FuncAttrs.addAttribute("denormal-fp-math",
-                           CodeGenOpts.FPDenormalMode.str());
-  }
-
-  if (MergedF32 == llvm::DenormalMode::getDefault()) {
-    AttrsToRemove.addAttribute("denormal-fp-math-f32");
-  } else if (MergedF32 != DenormModeToMergeF32) {
+  if (MergedFPEnv == llvm::DenormalFPEnv::getDefault()) {
+    AttrsToRemove.addAttribute(llvm::Attribute::DenormalFPEnv);
+  } else {
     // Overwrite existing attribute
-    FuncAttrs.addAttribute("denormal-fp-math-f32",
-                           CodeGenOpts.FP32DenormalMode.str());
+    FuncAttrs.addDenormalFPEnvAttr(MergedFPEnv);
   }
 
   F.removeFnAttrs(AttrsToRemove);
-  addDenormalModeAttrs(Merged, MergedF32, FuncAttrs);
 
   overrideFunctionFeaturesWithTargetFeatures(FuncAttrs, F, TargetOpts);
 

diff  --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h
index 4a86d58895dd9..145992652934f 100644
--- a/clang/lib/CodeGen/CGCall.h
+++ b/clang/lib/CodeGen/CGCall.h
@@ -410,7 +410,7 @@ class ReturnValueSlot {
 /// This is useful for adding attrs to bitcode modules that you want to link
 /// with but don't control, such as CUDA's libdevice.  When linking with such
 /// a bitcode library, you might want to set e.g. its functions'
-/// "denormal-fp-math" attribute to match the attr of the functions you're
+/// denormal_fp_math attribute to match the attr of the functions you're
 /// codegen'ing.  Otherwise, LLVM will interpret the bitcode module's lack of
 /// denormal-fp-math attrs as tantamount to denormal-fp-math=ieee, and then LLVM
 /// will propagate denormal-fp-math=ieee up to every transitive caller of a

diff  --git a/clang/test/CodeGen/denormalfpmode-f32.c b/clang/test/CodeGen/denormalfpmode-f32.c
index 312d1c9277722..f80e5539369cd 100644
--- a/clang/test/CodeGen/denormalfpmode-f32.c
+++ b/clang/test/CodeGen/denormalfpmode-f32.c
@@ -1,48 +1,54 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-DYNAMIC,CHECK-F32-NONE
+// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE
+// RUN: %clang_cc1 -fdenormal-fp-math=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE
+// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS
+// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ
+// RUN: %clang_cc1 -fdenormal-fp-math=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-DYNAMIC
 
-// RUN: %clang_cc1 -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=ieee -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS,CHECK-F32-IEEE
-// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ,CHECK-F32-IEEE
-// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ,CHECK-F32-DYNAMIC
+// RUN: %clang_cc1 -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE
+// RUN: %clang_cc1 -fdenormal-fp-math=ieee -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE
+// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS-F32-IEEE
+// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=ieee %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ-F32-IEEE
+// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ-F32-DYNAMIC
 
 
 // RUN: %clang_cc1 -fdenormal-fp-math-f32=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-PS
 // RUN: %clang_cc1 -fdenormal-fp-math=ieee -fdenormal-fp-math-f32=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-PS
-// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign -fdenormal-fp-math-f32=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ,CHECK-F32-PS
+// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign -fdenormal-fp-math-f32=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS
+// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=preserve-sign %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ-F32-PS
 // RUN: %clang_cc1 -fdenormal-fp-math=ieee -fdenormal-fp-math-f32=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-DYNAMIC
 
 
 // RUN: %clang_cc1 -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-PZ
 // RUN: %clang_cc1 -fdenormal-fp-math-f32=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-DYNAMIC
-// RUN: %clang_cc1 -fdenormal-fp-math=ieee -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-NONE,CHECK-F32-PZ
-// RUN: %clang_cc1 -fdenormal-fp-math=dynamic -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-DYNAMIC,CHECK-F32-PZ
-// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS,CHECK-F32-PZ
-// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ,CHECK-F32-NONE
-// RUN: %clang_cc1 -fdenormal-fp-math=dynamic -fdenormal-fp-math-f32=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-DYNAMIC,CHECK-F32-NONE
+// RUN: %clang_cc1 -fdenormal-fp-math=ieee -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-F32-PZ
+// RUN: %clang_cc1 -fdenormal-fp-math=dynamic -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-DYNAMIC-F32-PZ
+// RUN: %clang_cc1 -fdenormal-fp-math=preserve-sign -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PS-F32-PZ
+// RUN: %clang_cc1 -fdenormal-fp-math=positive-zero -fdenormal-fp-math-f32=positive-zero %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-PZ
+// RUN: %clang_cc1 -fdenormal-fp-math=dynamic -fdenormal-fp-math-f32=dynamic %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-ATTR,CHECK-DYNAMIC
 
 
 // CHECK-LABEL: main
 
 // CHECK-ATTR: attributes #0 =
-// CHECK-NONE-NOT:"denormal-fp-math"
-// CHECK-IEEE: "denormal-fp-math"="ieee,ieee"
-// CHECK-PS: "denormal-fp-math"="preserve-sign,preserve-sign"
-// CHECK-PZ: "denormal-fp-math"="positive-zero,positive-zero"
-// CHECK-DYNAMIC: "denormal-fp-math"="dynamic,dynamic"
+// CHECK-NONE-NOT: denormal_fpenv
 
-// CHECK-F32-NONE-NOT:"denormal-fp-math-f32"
-// CHECK-F32-IEEE: "denormal-fp-math-f32"="ieee,ieee"
-// CHECK-F32-PS: "denormal-fp-math-f32"="preserve-sign,preserve-sign"
-// CHECK-F32-PZ: "denormal-fp-math-f32"="positive-zero,positive-zero"
+// CHECK-IEEE: denormal_fpenv(ieee)
+// CHECK-PS: denormal_fpenv(preservesign)
+// CHECK-PZ: denormal_fpenv(positivezero)
+// CHECK-DYNAMIC: denormal_fpenv(dynamic)
 
 
-// CHECK-F32-DYNAMIC: "denormal-fp-math-f32"="dynamic,dynamic"
+// CHECK-PS-F32-IEEE: denormal_fpenv(preservesign, float: ieee)
+// CHECK-PZ-F32-IEEE: denormal_fpenv(positivezero, float: ieee)
+// CHECK-PZ-F32-DYNAMIC: denormal_fpenv(positivezero, float: dynamic)
+// CHECK-PZ-F32-PS: denormal_fpenv(positivezero, float: preservesign)
+// CHECK-DYNAMIC-F32-PZ: denormal_fpenv(dynamic, float: positivezero)
+// CHECK: CHECK-PS-F32-PZ: denormal_fpenv(preservesign, float: positivezero)
+
+// CHECK-F32-IEEE: denormal_fpenv(float: ieee)
+// CHECK-F32-PS: denormal_fpenv(float: preservesign)
+// CHECK-F32-PZ: denormal_fpenv(float: positivezero)
+// CHECK-F32-DYNAMIC: denormal_fpenv(float: dynamic)
 
 int main(void) {
   return 0;

diff  --git a/clang/test/CodeGen/denormalfpmode.c b/clang/test/CodeGen/denormalfpmode.c
index cffff90d6fbe7..f475a1186392b 100644
--- a/clang/test/CodeGen/denormalfpmode.c
+++ b/clang/test/CodeGen/denormalfpmode.c
@@ -6,10 +6,10 @@
 // CHECK-LABEL: main
 
 // The ieee,ieee is the default, so omit the attribute
-// CHECK-IEEE-NOT:"denormal-fp-math"
-// CHECK-PS: attributes #0 = {{.*}}"denormal-fp-math"="preserve-sign,preserve-sign"{{.*}}
-// CHECK-PZ: attributes #0 = {{.*}}"denormal-fp-math"="positive-zero,positive-zero"{{.*}}
-// CHECK-DYNAMIC: attributes #0 = {{.*}}"denormal-fp-math"="dynamic,dynamic"{{.*}}
+// CHECK-IEEE-NOT:denormal_fpenv
+// CHECK-PS: attributes #0 = {{.*}}denormal_fpenv(preservesign){{.*}}
+// CHECK-PZ: attributes #0 = {{.*}}denormal_fpenv(positivezero){{.*}}
+// CHECK-DYNAMIC: attributes #0 = {{.*}}denormal_fpenv(dynamic){{.*}}
 
 int main(void) {
   return 0;

diff  --git a/clang/test/CodeGenCUDA/flush-denormals.cu b/clang/test/CodeGenCUDA/flush-denormals.cu
index b5abc29dea14b..c6a4046fdc965 100644
--- a/clang/test/CodeGenCUDA/flush-denormals.cu
+++ b/clang/test/CodeGenCUDA/flush-denormals.cu
@@ -24,7 +24,7 @@
 
 #include "Inputs/cuda.h"
 
-// Checks that device function calls get emitted with the "denormal-fp-math-f32"
+// Checks that device function calls get emitted with the denormal_fpenv
 // attribute set when we compile CUDA device code with
 // -fdenormal-fp-math-f32. Further, check that we reflect the presence or
 // absence of -fgpu-flush-denormals-to-zero in a module flag.
@@ -41,8 +41,8 @@
 // CHECK-LABEL: define void @foo() #0
 extern "C" __device__ void foo() {}
 
-// FTZ: attributes #0 = {{.*}} "denormal-fp-math-f32"="preserve-sign,preserve-sign"
-// NOFTZ-NOT: "denormal-fp-math-f32"
+// FTZ: attributes #0 = {{.*}} denormal_fpenv(float: preservesign)
+// NOFTZ-NOT: denormal_fpenv
 
 // PTXFTZ:!llvm.module.flags = !{{{.*}}[[MODFLAG:![0-9]+]]}
 // PTXFTZ:[[MODFLAG]] = !{i32 4, !"nvvm-reflect-ftz", i32 1}

diff  --git a/clang/test/CodeGenCUDA/link-builtin-bitcode-denormal-fp-mode.cu b/clang/test/CodeGenCUDA/link-builtin-bitcode-denormal-fp-mode.cu
index ef02668c3697b..df72d3a1a3b9b 100644
--- a/clang/test/CodeGenCUDA/link-builtin-bitcode-denormal-fp-mode.cu
+++ b/clang/test/CodeGenCUDA/link-builtin-bitcode-denormal-fp-mode.cu
@@ -127,39 +127,39 @@ __global__ void kernel_f64(double* out, double* a, double* b, double* c) {
 // We should not be littering call sites with the attribute
 // Everything should use the default ieee with no explicit attribute
 
-// FIXME: Should check-not "denormal-fp-math" within the denormal-fp-math-f32
+// FIXME: Should check-not denormal_fpenv within the denormal-fp-math-f32
 // lines.
 
 // Default mode relies on the implicit check-not for the denormal-fp-math.
 
-// PSZ: #[[$KERNELATTR]] = { {{.*}} "denormal-fp-math"="preserve-sign,preserve-sign"
+// PSZ: #[[$KERNELATTR]] = { {{.*}} denormal_fpenv(preservesign)
 // PSZ-SAME: "target-cpu"="gfx803"
-// PSZ: #[[$FUNCATTR]] = { {{.*}} "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+// PSZ: #[[$FUNCATTR]] = { {{.*}} denormal_fpenv(float: preservesign)
 // PSZ-SAME: "target-cpu"="gfx803"
-// PSZ: #[[$WEAK_FUNCATTR]] = { {{.*}} "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+// PSZ: #[[$WEAK_FUNCATTR]] = { {{.*}} denormal_fpenv(float: preservesign)
 // PSZ-SAME: "target-cpu"="gfx803"
 
-// FIXME: Should check-not "denormal-fp-math" within the line
-// IEEEF64-PSZF32: #[[$KERNELATTR]] = { {{.*}} "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+// FIXME: Should check-not denormal_fpenv within the line
+// IEEEF64-PSZF32: #[[$KERNELATTR]] = { {{.*}} denormal_fpenv(float: preservesign)
 // IEEEF64-PSZF32-SAME: "target-cpu"="gfx803"
-// IEEEF64-PSZF32: #[[$FUNCATTR]] = { {{.*}} "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+// IEEEF64-PSZF32: #[[$FUNCATTR]] = { {{.*}} denormal_fpenv(float: preservesign)
 // IEEEF64-PSZF32-SAME: "target-cpu"="gfx803"
-// IEEEF64-PSZF32: #[[$WEAK_FUNCATTR]] = { {{.*}} "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+// IEEEF64-PSZF32: #[[$WEAK_FUNCATTR]] = { {{.*}} denormal_fpenv(float: preservesign)
 // IEEEF64-PSZF32-SAME: "target-cpu"="gfx803"
 
-// IEEEF32-PSZF64-DYNF32: #[[$KERNELATTR]] = { {{.*}} "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" {{.*}} "target-cpu"="gfx803" {{.*}}  }
-// implicit check-not
+// IEEEF32-PSZF64-DYNF32: #[[$KERNELATTR]] = { {{.*}} denormal_fpenv(preservesign, float: ieee) {{.*}} "target-cpu"="gfx803" {{.*}}  }
+// implicit-not
 // implicit check-not
 
 
-// IEEEF32-PSZF64-DYNFULL: #[[$KERNELATTR]] = { {{.*}} "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee"
+// IEEEF32-PSZF64-DYNFULL: #[[$KERNELATTR]] = { {{.*}} denormal_fpenv(preservesign, float: ieee)
 // IEEEF32-PSZF64-DYNFULL-SAME: "target-cpu"="gfx803"
-// IEEEF32-PSZF64-DYNFULL: #[[$FUNCATTR]] = { {{.*}} "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee"
+// IEEEF32-PSZF64-DYNFULL: #[[$FUNCATTR]] = { {{.*}} denormal_fpenv(preservesign, float: ieee)
 // IEEEF32-PSZF64-DYNFULL-SAME: "target-cpu"="gfx803"
-// IEEEF32-PSZF64-DYNFULL: #[[$WEAK_FUNCATTR]] = { {{.*}} "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee"
+// IEEEF32-PSZF64-DYNFULL: #[[$WEAK_FUNCATTR]] = { {{.*}} denormal_fpenv(preservesign, float: ieee)
 // IEEEF32-PSZF64-DYNFULL-SAME: "target-cpu"="gfx803"
 
 // -mlink-bitcode-file doesn't internalize or propagate attributes.
-// NOINTERNALIZE-IEEEF32-PSZF64-DYNFULL: #[[$KERNELATTR]] = { {{.*}} "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" {{.*}} "target-cpu"="gfx803" {{.*}} }
-// NOINTERNALIZE-IEEEF32-PSZF64-DYNFULL: #[[$FUNCATTR]] = { {{.*}} "denormal-fp-math"="dynamic,dynamic" {{.*}} }
-// NOINTERNALIZE-IEEEF32-PSZF64-DYNFULL: #[[$WEAK_FUNCATTR]] = { {{.*}} "denormal-fp-math"="dynamic,dynamic" {{.*}} }
+// NOINTERNALIZE-IEEEF32-PSZF64-DYNFULL: #[[$KERNELATTR]] = { {{.*}} denormal_fpenv(preservesign, float: ieee) {{.*}} "target-cpu"="gfx803" {{.*}} }
+// NOINTERNALIZE-IEEEF32-PSZF64-DYNFULL: #[[$FUNCATTR]] = { {{.*}} denormal_fpenv(dynamic) {{.*}} }
+// NOINTERNALIZE-IEEEF32-PSZF64-DYNFULL: #[[$WEAK_FUNCATTR]] = { {{.*}} denormal_fpenv(dynamic) {{.*}} }

diff  --git a/clang/test/CodeGenCUDA/propagate-attributes.cu b/clang/test/CodeGenCUDA/propagate-attributes.cu
index a7e6b09ff97db..2d5a250974fb8 100644
--- a/clang/test/CodeGenCUDA/propagate-attributes.cu
+++ b/clang/test/CodeGenCUDA/propagate-attributes.cu
@@ -53,14 +53,14 @@ __global__ void kernel() { lib_fn(); }
 // line.
 
 // Check the attribute list for kernel.
+// NOFTZ-NOT: denormal_fpenv
+
 // CHECK: attributes [[kattr]] = {
 
 // CHECK-SAME: convergent
 // CHECK-SAME: norecurse
 
-// FTZ-NOT: "denormal-fp-math"
-// FTZ-SAME: "denormal-fp-math-f32"="preserve-sign,preserve-sign"
-// NOFTZ-NOT: "denormal-fp-math-f32"
+// FTZ-SAME: denormal_fpenv(float: preservesign)
 
 // CHECK-SAME: "no-trapping-math"="true"
 
@@ -70,10 +70,4 @@ __global__ void kernel() { lib_fn(); }
 // CHECK-SAME: convergent
 // CHECK-NOT: norecurse
 
-// FTZ-NOT: "denormal-fp-math"
-// NOFTZ-NOT: "denormal-fp-math"
-
-// FTZ-SAME: "denormal-fp-math-f32"="preserve-sign,preserve-sign"
-// NOFTZ-NOT: "denormal-fp-math-f32"
-
 // CHECK-SAME: "no-trapping-math"="true"

diff  --git a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl
index c2d7616a33754..002c19ede0e56 100644
--- a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl
+++ b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel.cl
@@ -69,7 +69,7 @@ kernel void test_target_features_kernel(global int *i) {
 // CHECK: @__test_target_features_kernel_block_invoke_kernel.runtime.handle = internal addrspace(1) externally_initialized constant %block.runtime.handle.t.3 zeroinitializer, section ".amdgpu.kernel.runtime.handle"
 // CHECK: @llvm.used = appending addrspace(1) global [10 x ptr] [ptr @__test_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_kernel.runtime.handle to ptr), ptr @__test_block_invoke_2_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_2_kernel.runtime.handle to ptr), ptr @__test_block_invoke_3_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_3_kernel.runtime.handle to ptr), ptr @__test_block_invoke_4_kernel, ptr addrspacecast (ptr addrspace(1) @__test_block_invoke_4_kernel.runtime.handle to ptr), ptr @__test_target_features_kernel_block_invoke_kernel, ptr addrspacecast (ptr addrspace(1) @__test_target_features_kernel_block_invoke_kernel.runtime.handle to ptr)], section "llvm.metadata"
 //.
-// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone
+// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define dso_local void @callee(
 // NOCPU-SAME: i64 noundef [[ID:%.*]], ptr addrspace(1) noundef [[OUT:%.*]]) #[[ATTR1:[0-9]+]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -87,7 +87,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone
+// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define dso_local amdgpu_kernel void @test(
 // NOCPU-SAME: ptr addrspace(1) noundef align 1 [[A:%.*]], i8 noundef [[B:%.*]], ptr addrspace(1) noundef align 8 [[C:%.*]], i64 noundef [[D:%.*]]) #[[ATTR2:[0-9]+]] !kernel_arg_addr_space [[META3:![0-9]+]] !kernel_arg_access_qual [[META4:![0-9]+]] !kernel_arg_type [[META5:![0-9]+]] !kernel_arg_base_type [[META5]] !kernel_arg_type_qual [[META6:![0-9]+]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -111,7 +111,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone
+// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define dso_local void @__clang_ocl_kern_imp_test(
 // NOCPU-SAME: ptr addrspace(1) noundef align 1 [[A:%.*]], i8 noundef signext [[B:%.*]], ptr addrspace(1) noundef align 8 [[C:%.*]], i64 noundef [[D:%.*]]) #[[ATTR3:[0-9]+]] !kernel_arg_addr_space [[META3]] !kernel_arg_access_qual [[META4]] !kernel_arg_type [[META5]] !kernel_arg_base_type [[META5]] !kernel_arg_type_qual [[META6]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -233,7 +233,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone
+// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define dso_local amdgpu_kernel void @test_target_features_kernel(
 // NOCPU-SAME: ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR4:[0-9]+]] !kernel_arg_addr_space [[META7:![0-9]+]] !kernel_arg_access_qual [[META8:![0-9]+]] !kernel_arg_type [[META9:![0-9]+]] !kernel_arg_base_type [[META9]] !kernel_arg_type_qual [[META10:![0-9]+]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -245,7 +245,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone
+// NOCPU: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define dso_local void @__clang_ocl_kern_imp_test_target_features_kernel(
 // NOCPU-SAME: ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR5:[0-9]+]] !kernel_arg_addr_space [[META7]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META9]] !kernel_arg_base_type [[META9]] !kernel_arg_type_qual [[META10]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -266,7 +266,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline nounwind optnone
+// NOCPU: Function Attrs: convergent noinline nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal void @__test_block_invoke(
 // NOCPU-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR7:[0-9]+]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -285,7 +285,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent nounwind
+// NOCPU: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal amdgpu_kernel void @__test_block_invoke_kernel(
 // NOCPU-SAME: <{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0:%.*]]) #[[ATTR8:[0-9]+]] !associated [[META11:![0-9]+]] !kernel_arg_addr_space [[META12:![0-9]+]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META13:![0-9]+]] !kernel_arg_base_type [[META13]] !kernel_arg_type_qual [[META10]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -296,7 +296,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline nounwind optnone
+// NOCPU: Function Attrs: convergent noinline nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal void @__test_block_invoke_2(
 // NOCPU-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR7]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -321,7 +321,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent nounwind
+// NOCPU: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal amdgpu_kernel void @__test_block_invoke_2_kernel(
 // NOCPU-SAME: <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]]) #[[ATTR8]] !associated [[META14:![0-9]+]] !kernel_arg_addr_space [[META12]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META13]] !kernel_arg_base_type [[META13]] !kernel_arg_type_qual [[META10]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -332,7 +332,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline nounwind optnone
+// NOCPU: Function Attrs: convergent noinline nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal void @__test_block_invoke_3(
 // NOCPU-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]], ptr addrspace(3) noundef [[LP:%.*]]) #[[ATTR7]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -363,7 +363,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent nounwind
+// NOCPU: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal amdgpu_kernel void @__test_block_invoke_3_kernel(
 // NOCPU-SAME: <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]], ptr addrspace(3) [[TMP1:%.*]]) #[[ATTR8]] !associated [[META15:![0-9]+]] !kernel_arg_addr_space [[META16:![0-9]+]] !kernel_arg_access_qual [[META17:![0-9]+]] !kernel_arg_type [[META18:![0-9]+]] !kernel_arg_base_type [[META18]] !kernel_arg_type_qual [[META19:![0-9]+]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -374,7 +374,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline nounwind optnone
+// NOCPU: Function Attrs: convergent noinline nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal void @__test_block_invoke_4(
 // NOCPU-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR7]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -392,7 +392,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent nounwind
+// NOCPU: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal amdgpu_kernel void @__test_block_invoke_4_kernel(
 // NOCPU-SAME: <{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0:%.*]]) #[[ATTR8]] !associated [[META20:![0-9]+]] !kernel_arg_addr_space [[META12]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META13]] !kernel_arg_base_type [[META13]] !kernel_arg_type_qual [[META10]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -403,7 +403,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent noinline nounwind optnone
+// NOCPU: Function Attrs: convergent noinline nounwind optnone denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal void @__test_target_features_kernel_block_invoke(
 // NOCPU-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR7]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -417,7 +417,7 @@ kernel void test_target_features_kernel(global int *i) {
 // NOCPU-NEXT:    ret void
 //
 //
-// NOCPU: Function Attrs: convergent nounwind
+// NOCPU: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // NOCPU-LABEL: define internal amdgpu_kernel void @__test_target_features_kernel_block_invoke_kernel(
 // NOCPU-SAME: { i32, i32, ptr } [[TMP0:%.*]]) #[[ATTR8]] !associated [[META21:![0-9]+]] !kernel_arg_addr_space [[META12]] !kernel_arg_access_qual [[META8]] !kernel_arg_type [[META13]] !kernel_arg_base_type [[META13]] !kernel_arg_type_qual [[META10]] {
 // NOCPU-NEXT:  [[ENTRY:.*:]]
@@ -443,7 +443,7 @@ kernel void test_target_features_kernel(global int *i) {
 //
 //
 //
-// GFX900: Function Attrs: convergent norecurse nounwind
+// GFX900: Function Attrs: convergent norecurse nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define dso_local void @callee(
 // GFX900-SAME: i64 noundef [[ID:%.*]], ptr addrspace(1) noundef [[OUT:%.*]]) #[[ATTR1:[0-9]+]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -461,7 +461,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent norecurse nounwind
+// GFX900: Function Attrs: convergent norecurse nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define dso_local amdgpu_kernel void @test(
 // GFX900-SAME: ptr addrspace(1) noundef align 1 [[A:%.*]], i8 noundef [[B:%.*]], ptr addrspace(1) noundef align 8 [[C:%.*]], i64 noundef [[D:%.*]]) #[[ATTR2:[0-9]+]] !kernel_arg_addr_space [[META12:![0-9]+]] !kernel_arg_access_qual [[META13:![0-9]+]] !kernel_arg_type [[META14:![0-9]+]] !kernel_arg_base_type [[META14]] !kernel_arg_type_qual [[META15:![0-9]+]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -485,7 +485,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: alwaysinline convergent norecurse nounwind
+// GFX900: Function Attrs: alwaysinline convergent norecurse nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define dso_local void @__clang_ocl_kern_imp_test(
 // GFX900-SAME: ptr addrspace(1) noundef align 1 [[A:%.*]], i8 noundef signext [[B:%.*]], ptr addrspace(1) noundef align 8 [[C:%.*]], i64 noundef [[D:%.*]]) #[[ATTR3:[0-9]+]] !kernel_arg_addr_space [[META12]] !kernel_arg_access_qual [[META13]] !kernel_arg_type [[META14]] !kernel_arg_base_type [[META14]] !kernel_arg_type_qual [[META15]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -617,7 +617,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent norecurse nounwind
+// GFX900: Function Attrs: convergent norecurse nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define dso_local amdgpu_kernel void @test_target_features_kernel(
 // GFX900-SAME: ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR2]] !kernel_arg_addr_space [[META22:![0-9]+]] !kernel_arg_access_qual [[META23:![0-9]+]] !kernel_arg_type [[META24:![0-9]+]] !kernel_arg_base_type [[META24]] !kernel_arg_type_qual [[META25:![0-9]+]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -629,7 +629,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: alwaysinline convergent norecurse nounwind
+// GFX900: Function Attrs: alwaysinline convergent norecurse nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define dso_local void @__clang_ocl_kern_imp_test_target_features_kernel(
 // GFX900-SAME: ptr addrspace(1) noundef align 4 [[I:%.*]]) #[[ATTR3]] !kernel_arg_addr_space [[META22]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META24]] !kernel_arg_base_type [[META24]] !kernel_arg_type_qual [[META25]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -656,7 +656,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal void @__test_block_invoke(
 // GFX900-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR6:[0-9]+]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -672,7 +672,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal amdgpu_kernel void @__test_block_invoke_kernel(
 // GFX900-SAME: <{ i32, i32, ptr, ptr addrspace(1), i8 }> [[TMP0:%.*]]) #[[ATTR6]] !associated [[META28:![0-9]+]] !kernel_arg_addr_space [[META29:![0-9]+]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META30:![0-9]+]] !kernel_arg_base_type [[META30]] !kernel_arg_type_qual [[META25]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -683,7 +683,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal void @__test_block_invoke_2(
 // GFX900-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR6]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -705,7 +705,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal amdgpu_kernel void @__test_block_invoke_2_kernel(
 // GFX900-SAME: <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]]) #[[ATTR6]] !associated [[META31:![0-9]+]] !kernel_arg_addr_space [[META29]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META30]] !kernel_arg_base_type [[META30]] !kernel_arg_type_qual [[META25]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -716,7 +716,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal void @__test_block_invoke_3(
 // GFX900-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]], ptr addrspace(3) noundef [[LP:%.*]]) #[[ATTR6]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -744,7 +744,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal amdgpu_kernel void @__test_block_invoke_3_kernel(
 // GFX900-SAME: <{ i32, i32, ptr, ptr addrspace(1), ptr addrspace(1), i64, i8 }> [[TMP0:%.*]], ptr addrspace(3) [[TMP1:%.*]]) #[[ATTR6]] !associated [[META33:![0-9]+]] !kernel_arg_addr_space [[META34:![0-9]+]] !kernel_arg_access_qual [[META35:![0-9]+]] !kernel_arg_type [[META36:![0-9]+]] !kernel_arg_base_type [[META36]] !kernel_arg_type_qual [[META37:![0-9]+]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -755,7 +755,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal void @__test_block_invoke_4(
 // GFX900-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR6]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -770,7 +770,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal amdgpu_kernel void @__test_block_invoke_4_kernel(
 // GFX900-SAME: <{ i32, i32, ptr, i64, ptr addrspace(1) }> [[TMP0:%.*]]) #[[ATTR6]] !associated [[META38:![0-9]+]] !kernel_arg_addr_space [[META29]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META30]] !kernel_arg_base_type [[META30]] !kernel_arg_type_qual [[META25]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -781,7 +781,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal void @__test_target_features_kernel_block_invoke(
 // GFX900-SAME: ptr noundef [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR6]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -792,7 +792,7 @@ kernel void test_target_features_kernel(global int *i) {
 // GFX900-NEXT:    ret void
 //
 //
-// GFX900: Function Attrs: convergent nounwind
+// GFX900: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // GFX900-LABEL: define internal amdgpu_kernel void @__test_target_features_kernel_block_invoke_kernel(
 // GFX900-SAME: { i32, i32, ptr } [[TMP0:%.*]]) #[[ATTR6]] !associated [[META39:![0-9]+]] !kernel_arg_addr_space [[META29]] !kernel_arg_access_qual [[META23]] !kernel_arg_type [[META30]] !kernel_arg_base_type [[META30]] !kernel_arg_type_qual [[META25]] {
 // GFX900-NEXT:  [[ENTRY:.*:]]
@@ -804,24 +804,24 @@ kernel void test_target_features_kernel(global int *i) {
 //
 //.
 // NOCPU: attributes #[[ATTR0:[0-9]+]] = { "objc_arc_inert" }
-// NOCPU: attributes #[[ATTR1]] = { convergent noinline norecurse nounwind optnone "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
-// NOCPU: attributes #[[ATTR2]] = { convergent noinline norecurse nounwind optnone "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" }
-// NOCPU: attributes #[[ATTR3]] = { convergent noinline norecurse nounwind optnone "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
-// NOCPU: attributes #[[ATTR4]] = { convergent noinline norecurse nounwind optnone "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+s-memtime-inst" "uniform-work-group-size"="false" }
-// NOCPU: attributes #[[ATTR5]] = { convergent noinline norecurse nounwind optnone "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+s-memtime-inst" }
+// NOCPU: attributes #[[ATTR1]] = { convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// NOCPU: attributes #[[ATTR2]] = { convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign) "amdgpu-flat-work-group-size"="1,256" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" }
+// NOCPU: attributes #[[ATTR3]] = { convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign) "amdgpu-flat-work-group-size"="1,256" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// NOCPU: attributes #[[ATTR4]] = { convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign) "amdgpu-flat-work-group-size"="1,256" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+s-memtime-inst" "uniform-work-group-size"="false" }
+// NOCPU: attributes #[[ATTR5]] = { convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign) "amdgpu-flat-work-group-size"="1,256" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+s-memtime-inst" }
 // NOCPU: attributes #[[ATTR6:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
-// NOCPU: attributes #[[ATTR7]] = { convergent noinline nounwind optnone "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
-// NOCPU: attributes #[[ATTR8]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// NOCPU: attributes #[[ATTR7]] = { convergent noinline nounwind optnone denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// NOCPU: attributes #[[ATTR8]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // NOCPU: attributes #[[ATTR9:[0-9]+]] = { nocallback nofree nosync nounwind willreturn }
 // NOCPU: attributes #[[ATTR10]] = { convergent nounwind }
 //.
 // GFX900: attributes #[[ATTR0:[0-9]+]] = { "objc_arc_inert" }
-// GFX900: attributes #[[ATTR1]] = { convergent norecurse nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" }
-// GFX900: attributes #[[ATTR2]] = { convergent norecurse nounwind "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" "uniform-work-group-size"="false" }
-// GFX900: attributes #[[ATTR3]] = { alwaysinline convergent norecurse nounwind "amdgpu-flat-work-group-size"="1,256" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" }
+// GFX900: attributes #[[ATTR1]] = { convergent norecurse nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" }
+// GFX900: attributes #[[ATTR2]] = { convergent norecurse nounwind denormal_fpenv(float: preservesign) "amdgpu-flat-work-group-size"="1,256" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" "uniform-work-group-size"="false" }
+// GFX900: attributes #[[ATTR3]] = { alwaysinline convergent norecurse nounwind denormal_fpenv(float: preservesign) "amdgpu-flat-work-group-size"="1,256" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" }
 // GFX900: attributes #[[ATTR4:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // GFX900: attributes #[[ATTR5:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
-// GFX900: attributes #[[ATTR6]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" }
+// GFX900: attributes #[[ATTR6]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx900" "target-features"="-sram-ecc" }
 // GFX900: attributes #[[ATTR7:[0-9]+]] = { nocallback nofree nosync nounwind willreturn }
 // GFX900: attributes #[[ATTR8]] = { convergent nounwind }
 // GFX900: attributes #[[ATTR9]] = { nounwind }

diff  --git a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
index 5cbf6452d4c85..0149d1618e2b0 100644
--- a/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
+++ b/clang/test/CodeGenOpenCL/cl20-device-side-enqueue-attributes.cl
@@ -20,7 +20,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
                    a[i] = 4.0f * b[i] + 1.0f;
                  });
 }
-// SPIR32: Function Attrs: convergent noinline norecurse nounwind optnone
+// SPIR32: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // SPIR32-LABEL: define dso_local spir_kernel void @device_side_enqueue(
 // SPIR32-SAME: ptr addrspace(1) align 4 [[A:%.*]], ptr addrspace(1) align 4 [[B:%.*]], i32 [[I:%.*]]) #[[ATTR0:[0-9]+]] !kernel_arg_addr_space [[META2:![0-9]+]] !kernel_arg_access_qual [[META3:![0-9]+]] !kernel_arg_type [[META4:![0-9]+]] !kernel_arg_base_type [[META4]] !kernel_arg_type_qual [[META5:![0-9]+]] {
 // SPIR32-NEXT:  [[ENTRY:.*:]]
@@ -37,7 +37,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32-NEXT:    ret void
 //
 //
-// SPIR32: Function Attrs: convergent noinline norecurse nounwind optnone
+// SPIR32: Function Attrs: convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign)
 // SPIR32-LABEL: define dso_local spir_func void @__clang_ocl_kern_imp_device_side_enqueue(
 // SPIR32-SAME: ptr addrspace(1) align 4 [[A:%.*]], ptr addrspace(1) align 4 [[B:%.*]], i32 [[I:%.*]]) #[[ATTR0]] !kernel_arg_addr_space [[META2]] !kernel_arg_access_qual [[META3]] !kernel_arg_type [[META4]] !kernel_arg_base_type [[META4]] !kernel_arg_type_qual [[META5]] {
 // SPIR32-NEXT:  [[ENTRY:.*:]]
@@ -76,7 +76,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32-NEXT:    ret void
 //
 //
-// SPIR32: Function Attrs: convergent noinline nounwind optnone
+// SPIR32: Function Attrs: convergent noinline nounwind optnone denormal_fpenv(float: preservesign)
 // SPIR32-LABEL: define internal spir_func void @__device_side_enqueue_block_invoke(
 // SPIR32-SAME: ptr addrspace(4) [[DOTBLOCK_DESCRIPTOR:%.*]]) #[[ATTR2:[0-9]+]] {
 // SPIR32-NEXT:  [[ENTRY:.*:]]
@@ -100,7 +100,7 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // SPIR32-NEXT:    ret void
 //
 //
-// SPIR32: Function Attrs: convergent nounwind
+// SPIR32: Function Attrs: convergent nounwind denormal_fpenv(float: preservesign)
 // SPIR32-LABEL: define spir_kernel void @__device_side_enqueue_block_invoke_kernel(
 // SPIR32-SAME: ptr addrspace(4) [[TMP0:%.*]]) #[[ATTR4:[0-9]+]] {
 // SPIR32-NEXT:  [[ENTRY:.*:]]
@@ -196,11 +196,11 @@ kernel void device_side_enqueue(global float *a, global float *b, int i) {
 // STRICTFP-NEXT:    ret void
 //
 //.
-// SPIR32: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" }
+// SPIR32: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" }
 // SPIR32: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
-// SPIR32: attributes #[[ATTR2]] = { convergent noinline nounwind optnone "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// SPIR32: attributes #[[ATTR2]] = { convergent noinline nounwind optnone denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR3:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
-// SPIR32: attributes #[[ATTR4]] = { convergent nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// SPIR32: attributes #[[ATTR4]] = { convergent nounwind denormal_fpenv(float: preservesign) "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
 // SPIR32: attributes #[[ATTR5]] = { convergent nounwind "uniform-work-group-size"="true" }
 //.
 // STRICTFP: attributes #[[ATTR0]] = { convergent noinline norecurse nounwind optnone strictfp "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" }

diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 9c3ffb396649b..ddd5087830acc 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1671,7 +1671,7 @@ Currently, only the following parameter attributes are defined:
    This does not depend on the floating-point environment. For
    example, a function parameter marked ``nofpclass(zero)`` indicates
    no zero inputs. If this is applied to an argument in a function
-   marked with :ref:`\"denormal-fp-math\" <denormal_fp_math>`
+   marked with :ref:`denormal_fpenv <denormal_fpenv>`
    indicating zero treatment of input denormals, it does not imply the
    value cannot be a denormal value which would compare equal to 0.
 
@@ -2655,28 +2655,28 @@ For example:
     might otherwise be set or cleared by calling this function. LLVM will
     not introduce any new floating-point instructions that may trap.
 
-.. _denormal_fp_math:
+.. _denormal_fpenv:
 
-``"denormal-fp-math"``
+``denormal_fpenv``
     This indicates the denormal (subnormal) handling that may be
-    assumed for the default floating-point environment. This is a
-    comma separated pair. The elements may be one of ``"ieee"``,
-    ``"preserve-sign"``, ``"positive-zero"``, or ``"dynamic"``. The
-    first entry indicates the flushing mode for the result of floating
-    point operations. The second indicates the handling of denormal inputs
+    assumed for the default floating-point environment. The base form
+    is a ``|`` separated pair. The elements may be one of ``ieee``,
+    ``preservesign``, ``positivezero``, or ``dynamic``. The first
+    entry indicates the flushing mode for the result of floating point
+    operations. The second indicates the handling of denormal inputs
     to floating point instructions. For compatibility with older
     bitcode, if the second value is omitted, both input and output
     modes will assume the same mode.
 
-    If this is attribute is not specified, the default is ``"ieee,ieee"``.
+    If this is attribute is not specified, the default is ``ieee|ieee``.
 
-    If the output mode is ``"preserve-sign"``, or ``"positive-zero"``,
+    If the output mode is ``preservesign``, or ``positivezero``,
     denormal outputs may be flushed to zero by standard floating-point
     operations. It is not mandated that flushing to zero occurs, but if
     a denormal output is flushed to zero, it must respect the sign
     mode. Not all targets support all modes.
 
-    If the mode is ``"dynamic"``, the behavior is derived from the
+    If the mode is ``dynamic``, the behavior is derived from the
     dynamic state of the floating-point environment. Transformations
     which depend on the behavior of denormal values should not be
     performed.
@@ -2686,20 +2686,31 @@ For example:
     the mode is consistent. User or platform code is expected to set
     the floating point mode appropriately before function entry.
 
-    If the input mode is ``"preserve-sign"``, or ``"positive-zero"``,
+    This may optionally specify a second pair, prefixed with
+    ``float:``. This provides an override for the behavior of 32-bit
+    float type (or vectors of 32-bit floats).
+
+    If the input mode is ``preservesign``, or ``positivezero``,
     a floating-point operation must treat any input denormal value as
     zero. In some situations, if an instruction does not respect this
     mode, the input may need to be converted to 0 as if by
     ``@llvm.canonicalize`` during lowering for correctness.
 
-``"denormal-fp-math-f32"``
-    Same as ``"denormal-fp-math"``, but only controls the behavior of
-    the 32-bit float type (or vectors of 32-bit floats). If both are
-    are present, this overrides ``"denormal-fp-math"``. Not all targets
-    support separately setting the denormal mode per type, and no
-    attempt is made to diagnose unsupported uses. Currently this
+    This may optionally specify a second pair, prefixed with
+    ``float:``. This provides an override for the behavior of 32-bit
+    float type. (or vectors of 32-bit floats). If this is present,
+    this overrides the base handling of the default mode. Not all
+    targets support separately setting the denormal mode per type, and
+    no attempt is made to diagnose unsupported uses. Currently this
     attribute is respected by the AMDGPU and NVPTX backends.
 
+:Examples:
+   ``denormal_fpenv(preservesign)``
+   ``denormal_fpenv(float: preservesign)``
+   ``denormal_fpenv(dynamic, float: preservesign|ieee)``
+   ``denormal_fpenv(ieee|ieee, float: preservesign|preservesign)``
+   ``denormal_fpenv(ieee|dynamic, float: preservesign|ieee)``
+
 ``"thunk"``
     This attribute indicates that the function will delegate to some other
     function with a tail call. The prototype of a thunk should not be used for
@@ -4013,10 +4024,11 @@ not have side effects and may be speculated freely. Results assume the
 round-to-nearest rounding mode, and subnormals are assumed to be preserved.
 
 Running LLVM code in an environment where these assumptions are not met
-typically leads to undefined behavior. The ``strictfp`` and ``denormal-fp-math``
-attributes as well as :ref:`Constrained Floating-Point Intrinsics
-<constrainedfp>` can be used to weaken LLVM's assumptions and ensure defined
-behavior in non-default floating-point environments; see their respective
+typically leads to undefined behavior. The ``strictfp`` and
+:ref:`denormal_fpenv <denormal_fpenv>` attributes as well as
+:ref:`Constrained Floating-Point Intrinsics <constrainedfp>` can be
+used to weaken LLVM's assumptions and ensure defined behavior in
+non-default floating-point environments; see their respective
 documentation for details.
 
 .. _floatnan:
@@ -4119,7 +4131,7 @@ exceptions.)
 Various flags, attributes, and metadata can alter the behavior of these
 operations and thus make them not bit-identical across machines and optimization
 levels any more: most notably, the :ref:`fast-math flags <fastmath>` as well as
-the :ref:`strictfp <strictfp>` and :ref:`denormal-fp-math <denormal_fp_math>`
+the :ref:`strictfp <strictfp>` and :ref:`denormal_fpenv <denormal_fpenv>`
 attributes and :ref:`!fpmath metadata <fpmath-metadata>`. See their
 corresponding documentation for details.
 
@@ -30256,8 +30268,8 @@ normal value. The function never raises floating-point exceptions. The
 function does not canonicalize its input value and does not depend
 on the floating-point environment. If the floating-point environment
 has a zeroing treatment of subnormal input values (such as indicated
-by the ``"denormal-fp-math"`` attribute), a subnormal value will be
-observed (will not be implicitly treated as zero).
+by the :ref:`denormal_fpenv <denormal_fpenv>` attribute), a subnormal
+value will be observed (will not be implicitly treated as zero).
 
 
 General Intrinsics

diff  --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 2a535dc0530a0..6252f39610f69 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -63,6 +63,9 @@ Changes to the LLVM IR
   intrinsics. These are equivalent to `fptrunc` and `fpext` with half
   with a bitcast.
 
+* "denormal-fp-math" and "denormal-fp-math-f32" string attributes were
+  migrated to first-class denormal_fpenv attribute.
+
 Changes to LLVM infrastructure
 ------------------------------
 

diff  --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h
index 2bd971983e287..3fa2801e4b4bd 100644
--- a/llvm/include/llvm-c/Core.h
+++ b/llvm/include/llvm-c/Core.h
@@ -731,6 +731,40 @@ LLVM_C_ABI LLVMAttributeRef LLVMCreateConstantRangeAttribute(
     LLVMContextRef C, unsigned KindID, unsigned NumBits,
     const uint64_t LowerWords[], const uint64_t UpperWords[]);
 
+/**
+ * Represent 
diff erent denormal handling kinds for use with
+ * LLVMCreateDenormalFPEnvAttribute.
+ */
+typedef enum {
+  LLVMDenormalModeKindIEEE = 0,
+  LLVMDenormalModeKindPreserveSign = 1,
+  LLVMDenormalModeKindPositiveZero = 2,
+  LLVMDenormalModeKindDynamic = 3
+} LLVMDenormalModeKind;
+
+/**
+ * Create a DenormalFPEnv attribute.
+ *
+ * \p DefaultModeOutput is the assumed denormal handling for the outputs of most
+ *    floating-point types.
+ *
+ * \p DefaultModeInput is the assumed denormal handling for the inputs of most
+ *    floating-point types.
+ *
+ * \p FloatModeOutput is the assumed denormal handling for the outputs of
+ *    float. This should always be the same as as DefaultModeOutput for most
+ *    targets.
+ *
+ * \p FloatModeInput is the assumed denormal handling for the inputs of
+ *    float. This should always be the same as as DefaultModeInput for most
+ *    targets.
+ *
+ */
+LLVM_C_ABI LLVMAttributeRef LLVMCreateDenormalFPEnvAttribute(
+    LLVMContextRef C, LLVMDenormalModeKind DefaultModeOutput,
+    LLVMDenormalModeKind DefaultModeInput, LLVMDenormalModeKind FloatModeOutput,
+    LLVMDenormalModeKind FloatModeInput);
+
 /**
  * Create a string attribute.
  */

diff  --git a/llvm/include/llvm/ADT/FloatingPointMode.h b/llvm/include/llvm/ADT/FloatingPointMode.h
index 36e20d8f25c98..207fbf96aeb97 100644
--- a/llvm/include/llvm/ADT/FloatingPointMode.h
+++ b/llvm/include/llvm/ADT/FloatingPointMode.h
@@ -75,16 +75,16 @@ struct DenormalMode {
     Invalid = -1,
 
     /// IEEE-754 denormal numbers preserved.
-    IEEE,
+    IEEE = 0,
 
     /// The sign of a flushed-to-zero number is preserved in the sign of 0
-    PreserveSign,
+    PreserveSign = 1,
 
     /// Denormals are flushed to positive zero.
-    PositiveZero,
+    PositiveZero = 2,
 
     /// Denormals have unknown treatment.
-    Dynamic
+    Dynamic = 3
   };
 
   /// Denormal flushing mode for floating point instruction results in the
@@ -130,6 +130,19 @@ struct DenormalMode {
     return DenormalMode(DenormalModeKind::Dynamic, DenormalModeKind::Dynamic);
   }
 
+  constexpr uint32_t toIntValue() const {
+    assert(Input != Invalid && Output != Invalid);
+    return (static_cast<uint32_t>(Input) << 2) | static_cast<uint32_t>(Output);
+  }
+
+  static constexpr DenormalMode createFromIntValue(uint32_t Data) {
+    uint32_t OutputMode = Data & 0x3;
+    uint32_t InputMode = (Data >> 2) & 0x3;
+
+    return {static_cast<DenormalModeKind>(OutputMode),
+            static_cast<DenormalModeKind>(InputMode)};
+  }
+
   bool operator==(DenormalMode Other) const {
     return Output == Other.Output && Input == Other.Input;
   }
@@ -193,7 +206,8 @@ struct DenormalMode {
     return MergedMode;
   }
 
-  inline void print(raw_ostream &OS) const;
+  inline void print(raw_ostream &OS, bool Legacy = true,
+                    bool OmitIfSame = false) const;
 
   inline std::string str() const {
     std::string storage;
@@ -214,22 +228,23 @@ parseDenormalFPAttributeComponent(StringRef Str) {
   // Assume ieee on unspecified attribute.
   return StringSwitch<DenormalMode::DenormalModeKind>(Str)
       .Cases({"", "ieee"}, DenormalMode::IEEE)
-      .Case("preserve-sign", DenormalMode::PreserveSign)
-      .Case("positive-zero", DenormalMode::PositiveZero)
+      .Cases({"preservesign", "preserve-sign"}, DenormalMode::PreserveSign)
+      .Cases({"positivezero", "positive-zero"}, DenormalMode::PositiveZero)
       .Case("dynamic", DenormalMode::Dynamic)
       .Default(DenormalMode::Invalid);
 }
 
 /// Return the name used for the denormal handling mode used by the
 /// expected names from the denormal-fp-math attribute.
-inline StringRef denormalModeKindName(DenormalMode::DenormalModeKind Mode) {
+constexpr StringRef denormalModeKindName(DenormalMode::DenormalModeKind Mode,
+                                         bool LegacyName = true) {
   switch (Mode) {
   case DenormalMode::IEEE:
     return "ieee";
   case DenormalMode::PreserveSign:
-    return "preserve-sign";
+    return LegacyName ? "preserve-sign" : "preservesign";
   case DenormalMode::PositiveZero:
-    return "positive-zero";
+    return LegacyName ? "positive-zero" : "positivezero";
   case DenormalMode::Dynamic:
     return "dynamic";
   default:
@@ -253,8 +268,71 @@ inline DenormalMode parseDenormalFPAttribute(StringRef Str) {
   return Mode;
 }
 
-void DenormalMode::print(raw_ostream &OS) const {
-  OS << denormalModeKindName(Output) << ',' << denormalModeKindName(Input);
+void DenormalMode::print(raw_ostream &OS, bool Legacy, bool OmitIfSame) const {
+  OS << denormalModeKindName(Output, Legacy);
+  if (!OmitIfSame || Input != Output) {
+    OS << (Legacy ? ',' : '|');
+    OS << denormalModeKindName(Input, Legacy);
+  }
+}
+
+/// Represents the full denormal controls for a function, including the default
+/// mode and the f32 specific override.
+struct DenormalFPEnv {
+private:
+  static constexpr unsigned BitsPerEntry = 2;
+  static constexpr unsigned BitsPerMode = 4;
+  static constexpr unsigned ModeMask = (1 << BitsPerMode) - 1;
+
+public:
+  DenormalMode DefaultMode;
+  DenormalMode F32Mode;
+
+  constexpr DenormalFPEnv(DenormalMode BaseMode,
+                          DenormalMode FloatMode = DenormalMode::getInvalid())
+      : DefaultMode(BaseMode),
+        F32Mode(FloatMode.Output == DenormalMode::Invalid ? BaseMode.Output
+                                                          : FloatMode.Output,
+                FloatMode.Input == DenormalMode::Invalid ? BaseMode.Input
+                                                         : FloatMode.Input) {}
+
+  static constexpr DenormalFPEnv getDefault() {
+    return DenormalFPEnv(DenormalMode::getIEEE(), DenormalMode::getIEEE());
+  }
+
+  constexpr uint32_t toIntValue() const {
+    assert(DefaultMode.isValid() && F32Mode.isValid());
+    uint32_t Data =
+        DefaultMode.toIntValue() | (F32Mode.toIntValue() << BitsPerMode);
+
+    assert(isUInt<8>(Data));
+    return Data;
+  }
+
+  static constexpr DenormalFPEnv createFromIntValue(uint32_t Data) {
+    return {DenormalMode::createFromIntValue(Data),
+            DenormalMode::createFromIntValue(Data >> BitsPerMode)};
+  }
+
+  constexpr bool operator==(DenormalFPEnv Other) const {
+    return DefaultMode == Other.DefaultMode && F32Mode == Other.F32Mode;
+  }
+
+  constexpr bool operator!=(DenormalFPEnv Other) const {
+    return !(*this == Other);
+  }
+
+  LLVM_ABI void print(raw_ostream &OS, bool OmitIfSame = true) const;
+
+  DenormalFPEnv mergeCalleeMode(DenormalFPEnv Callee) const {
+    return DenormalFPEnv{DefaultMode.mergeCalleeMode(Callee.DefaultMode),
+                         F32Mode.mergeCalleeMode(Callee.F32Mode)};
+  }
+};
+
+inline raw_ostream &operator<<(raw_ostream &OS, DenormalFPEnv FPEnv) {
+  FPEnv.print(OS);
+  return OS;
 }
 
 /// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual

diff  --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h
index ea22ed48ab763..5e77b2ae6f15b 100644
--- a/llvm/include/llvm/Analysis/ConstantFolding.h
+++ b/llvm/include/llvm/Analysis/ConstantFolding.h
@@ -114,7 +114,7 @@ ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS, Constant *RHS,
 /// floating point instructions can have their mode set separately, so the
 /// direction is also needed.
 ///
-/// If the calling function's "denormal-fp-math" input mode is "dynamic" for the
+/// If the calling function's denormal_fpenv input mode is dynamic for the
 /// floating-point type, returns nullptr for denormal inputs.
 LLVM_ABI Constant *FlushFPConstant(Constant *Operand, const Instruction *I,
                                    bool IsOutput);

diff  --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 040a2153875db..a7d90a8168960 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -329,6 +329,8 @@ namespace llvm {
     bool parseOptionalUWTableKind(UWTableKind &Kind);
     bool parseAllocKind(AllocFnKind &Kind);
     std::optional<MemoryEffects> parseMemoryAttr();
+    std::optional<DenormalMode> parseDenormalFPEnvEntry();
+    std::optional<DenormalFPEnv> parseDenormalFPEnvAttr();
     unsigned parseNoFPClassAttr();
     bool parseScopeAndOrdering(bool IsAtomic, SyncScope::ID &SSID,
                                AtomicOrdering &Ordering);

diff  --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index 24f84cfa09e34..dfe51466c6152 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -222,6 +222,12 @@ enum Kind {
   kw_provenance,
   kw_read_provenance,
 
+  // denormal_fpenv attribute:
+  kw_ieee,
+  kw_preservesign,
+  kw_positivezero,
+  kw_dynamic,
+
   // nofpclass attribute:
   kw_all,
   kw_nan,

diff  --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 20d19f21a64ce..811116a3e1756 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -807,6 +807,7 @@ enum AttributeKindCodes {
   ATTR_KIND_DEAD_ON_RETURN = 103,
   ATTR_KIND_SANITIZE_ALLOC_TOKEN = 104,
   ATTR_KIND_NO_CREATE_UNDEF_OR_POISON = 105,
+  ATTR_KIND_DENORMAL_FPENV = 106,
 };
 
 enum ComdatSelectionKindCodes {

diff  --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 534d96d2c46e5..c88d230bc1321 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -47,6 +47,8 @@ class Instruction;
 class Type;
 class raw_ostream;
 enum FPClassTest : unsigned;
+struct DenormalFPEnv;
+struct DenormalMode;
 
 enum class AllocFnKind : uint64_t {
   Unknown = 0,
@@ -341,6 +343,9 @@ class Attribute {
   /// Returns memory effects.
   LLVM_ABI MemoryEffects getMemoryEffects() const;
 
+  /// Returns denormal_fpenv.
+  LLVM_ABI struct DenormalFPEnv getDenormalFPEnv() const;
+
   /// Returns information from captures attribute.
   LLVM_ABI CaptureInfo getCaptureInfo() const;
 
@@ -1344,6 +1349,9 @@ class AttrBuilder {
   /// Add captures attribute.
   LLVM_ABI AttrBuilder &addCapturesAttr(CaptureInfo CI);
 
+  /// Add denormal_fpenv attribute.
+  LLVM_ABI AttrBuilder &addDenormalFPEnvAttr(DenormalFPEnv Mode);
+
   // Add nofpclass attribute
   LLVM_ABI AttrBuilder &addNoFPClassAttr(FPClassTest NoFPClassMask);
 

diff  --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index e4db0e6984780..5220b4768a39f 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -400,6 +400,10 @@ def CoroDestroyOnlyWhenComplete : EnumAttr<"coro_only_destroy_when_complete", In
 /// pipeline to perform elide on the call or invoke instruction.
 def CoroElideSafe : EnumAttr<"coro_elide_safe", IntersectPreserve, [FnAttr]>;
 
+/// Indicate the denormal handling of the default floating-point
+/// environment.
+def DenormalFPEnv : IntAttr<"denormal_fpenv", IntersectPreserve, [FnAttr]>;
+
 /// Function is marked for Windows Hot Patching
 def MarkedForWindowsSecureHotPatching
     : StrBoolAttr<"marked_for_windows_hot_patching">;
@@ -421,9 +425,6 @@ def ProfileSampleAccurate : StrBoolAttr<"profile-sample-accurate">;
 def UseSampleProfile : StrBoolAttr<"use-sample-profile">;
 def LoaderReplaceable : StrBoolAttr<"loader-replaceable">;
 
-def DenormalFPMath : ComplexStrAttr<"denormal-fp-math", [FnAttr]>;
-def DenormalFPMathF32 : ComplexStrAttr<"denormal-fp-math-f32", [FnAttr]>;
-
 // Attribute compatiblity rules are generated to check the attribute of the
 // caller and callee and decide whether inlining should be allowed. CompatRule
 // and child classes are used for the rule generation. CompatRule takes only a

diff  --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index fd94b4fd98b4e..8760e99d38169 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -49,6 +49,7 @@ class AssemblyAnnotationWriter;
 class Constant;
 class ConstantRange;
 class DataLayout;
+struct DenormalFPEnv;
 struct DenormalMode;
 class DISubprogram;
 enum LibFunc : unsigned;
@@ -717,14 +718,8 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
   /// function.
   DenormalMode getDenormalMode(const fltSemantics &FPType) const;
 
-  /// Return the representational value of "denormal-fp-math". Code interested
-  /// in the semantics of the function should use getDenormalMode instead.
-  DenormalMode getDenormalModeRaw() const;
-
-  /// Return the representational value of "denormal-fp-math-f32". Code
-  /// interested in the semantics of the function should use getDenormalMode
-  /// instead.
-  DenormalMode getDenormalModeF32Raw() const;
+  /// Return the representational value of the denormal_fpenv attribute.
+  DenormalFPEnv getDenormalFPEnv() const;
 
   /// copyAttributesFrom - copy all additional attributes (those not needed to
   /// create a Function) from the Function Src to this one.

diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index f18db527a1f04..f2657a338ba76 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -6593,7 +6593,7 @@ struct AAIndirectCallInfo
 };
 
 /// An abstract Attribute for specializing "dynamic" components of
-/// "denormal-fp-math" and "denormal-fp-math-f32" to a known denormal mode.
+/// denormal_fpenv to a known denormal mode.
 struct AADenormalFPMath
     : public StateWrapper<DenormalFPMathState, AbstractAttribute> {
   using Base = StateWrapper<DenormalFPMathState, AbstractAttribute>;

diff  --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 4e7e31da09a52..f47579ca8fa9b 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -720,6 +720,12 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(provenance);
   KEYWORD(read_provenance);
 
+  // denormal_fpenv attribute
+  KEYWORD(ieee);
+  KEYWORD(preservesign);
+  KEYWORD(positivezero);
+  KEYWORD(dynamic);
+
   // nofpclass attribute
   KEYWORD(all);
   KEYWORD(nan);

diff  --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index a691caa57b1e0..5b916711690ec 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1658,6 +1658,14 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
     B.addMemoryAttr(*ME);
     return false;
   }
+  case Attribute::DenormalFPEnv: {
+    std::optional<DenormalFPEnv> Mode = parseDenormalFPEnvAttr();
+    if (!Mode)
+      return true;
+
+    B.addDenormalFPEnvAttr(*Mode);
+    return false;
+  }
   case Attribute::NoFPClass: {
     if (FPClassTest NoFPClass =
             static_cast<FPClassTest>(parseNoFPClassAttr())) {
@@ -2603,6 +2611,22 @@ static std::optional<ModRefInfo> keywordToModRef(lltok::Kind Tok) {
   }
 }
 
+static std::optional<DenormalMode::DenormalModeKind>
+keywordToDenormalModeKind(lltok::Kind Tok) {
+  switch (Tok) {
+  case lltok::kw_ieee:
+    return DenormalMode::IEEE;
+  case lltok::kw_preservesign:
+    return DenormalMode::PreserveSign;
+  case lltok::kw_positivezero:
+    return DenormalMode::PositiveZero;
+  case lltok::kw_dynamic:
+    return DenormalMode::Dynamic;
+  default:
+    return std::nullopt;
+  }
+}
+
 std::optional<MemoryEffects> LLParser::parseMemoryAttr() {
   MemoryEffects ME = MemoryEffects::none();
 
@@ -2658,6 +2682,87 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() {
   return std::nullopt;
 }
 
+std::optional<DenormalMode> LLParser::parseDenormalFPEnvEntry() {
+  std::optional<DenormalMode::DenormalModeKind> OutputMode =
+      keywordToDenormalModeKind(Lex.getKind());
+  if (!OutputMode) {
+    tokError("expected denormal behavior kind (ieee, preservesign, "
+             "positivezero, dynamic)");
+    return {};
+  }
+
+  Lex.Lex();
+
+  std::optional<DenormalMode::DenormalModeKind> InputMode;
+  if (EatIfPresent(lltok::bar)) {
+    InputMode = keywordToDenormalModeKind(Lex.getKind());
+    if (!InputMode) {
+      tokError("expected denormal behavior kind (ieee, preservesign, "
+               "positivezero, dynamic)");
+      return {};
+    }
+
+    Lex.Lex();
+  } else {
+    // Single item, input == output mode
+    InputMode = OutputMode;
+  }
+
+  return DenormalMode(*OutputMode, *InputMode);
+}
+
+std::optional<DenormalFPEnv> LLParser::parseDenormalFPEnvAttr() {
+  // We use syntax like denormal_fpenv(float: preservesign), so the colon should
+  // not be interpreted as a label terminator.
+  Lex.setIgnoreColonInIdentifiers(true);
+  llvm::scope_exit _([&] { Lex.setIgnoreColonInIdentifiers(false); });
+
+  Lex.Lex();
+
+  if (parseToken(lltok::lparen, "expected '('"))
+    return {};
+
+  DenormalMode DefaultMode = DenormalMode::getIEEE();
+  DenormalMode F32Mode = DenormalMode::getInvalid();
+
+  bool HasDefaultSection = false;
+  if (Lex.getKind() != lltok::Type) {
+    std::optional<DenormalMode> ParsedDefaultMode = parseDenormalFPEnvEntry();
+    if (!ParsedDefaultMode)
+      return {};
+    DefaultMode = *ParsedDefaultMode;
+    HasDefaultSection = true;
+  }
+
+  bool HasComma = EatIfPresent(lltok::comma);
+  if (Lex.getKind() == lltok::Type) {
+    if (HasDefaultSection && !HasComma) {
+      tokError("expected ',' before float:");
+      return {};
+    }
+
+    Type *Ty = nullptr;
+    if (parseType(Ty) || !Ty->isFloatTy()) {
+      tokError("expected float:");
+      return {};
+    }
+
+    if (parseToken(lltok::colon, "expected ':' before float denormal_fpenv"))
+      return {};
+
+    std::optional<DenormalMode> ParsedF32Mode = parseDenormalFPEnvEntry();
+    if (!ParsedF32Mode)
+      return {};
+
+    F32Mode = *ParsedF32Mode;
+  }
+
+  if (parseToken(lltok::rparen, "unterminated denormal_fpenv"))
+    return {};
+
+  return DenormalFPEnv(DefaultMode, F32Mode);
+}
+
 static unsigned keywordToFPClassTest(lltok::Kind Tok) {
   switch (Tok) {
   case lltok::kw_all:

diff  --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 036f3c2964997..749df3f2fc612 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2269,6 +2269,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::DeadOnReturn;
   case bitc::ATTR_KIND_NO_CREATE_UNDEF_OR_POISON:
     return Attribute::NoCreateUndefOrPoison;
+  case bitc::ATTR_KIND_DENORMAL_FPENV:
+    return Attribute::DenormalFPEnv;
   }
 }
 
@@ -2442,6 +2444,10 @@ Error BitcodeReader::parseAttributeGroupBlock() {
           else if (Kind == Attribute::NoFPClass)
             B.addNoFPClassAttr(
                 static_cast<FPClassTest>(Record[++i] & fcAllFlags));
+          else if (Kind == Attribute::DenormalFPEnv) {
+            B.addDenormalFPEnvAttr(
+                DenormalFPEnv::createFromIntValue(Record[++i]));
+          }
         } else if (Record[i] == 3 || Record[i] == 4) { // String attribute
           bool HasValue = (Record[i++] == 4);
           SmallString<64> KindStr;

diff  --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 8bc98c92bc698..2566b2a292300 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -954,6 +954,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_DEAD_ON_RETURN;
   case Attribute::NoCreateUndefOrPoison:
     return bitc::ATTR_KIND_NO_CREATE_UNDEF_OR_POISON;
+  case Attribute::DenormalFPEnv:
+    return bitc::ATTR_KIND_DENORMAL_FPENV;
   case Attribute::EndAttrKinds:
     llvm_unreachable("Can not encode end-attribute kinds marker.");
   case Attribute::None:

diff  --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp
index ef5eabccab480..e2e5e2263062f 100644
--- a/llvm/lib/CodeGen/CommandFlags.cpp
+++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -751,23 +751,16 @@ void codegen::setFunctionAttributes(StringRef CPU, StringRef Features,
   HANDLE_BOOL_ATTR(EnableNoNaNsFPMathView, "no-nans-fp-math");
   HANDLE_BOOL_ATTR(EnableNoSignedZerosFPMathView, "no-signed-zeros-fp-math");
 
-  if (DenormalFPMathView->getNumOccurrences() > 0 &&
-      !F.hasFnAttribute("denormal-fp-math")) {
+  if ((DenormalFPMathView->getNumOccurrences() > 0 ||
+       DenormalFP32MathView->getNumOccurrences() > 0) &&
+      !F.hasFnAttribute(Attribute::DenormalFPEnv)) {
     DenormalMode::DenormalModeKind DenormKind = getDenormalFPMath();
+    DenormalMode::DenormalModeKind DenormKindF32 = getDenormalFP32Math();
 
+    DenormalFPEnv FPEnv(DenormalMode{DenormKind, DenormKind},
+                        DenormalMode{DenormKindF32, DenormKindF32});
     // FIXME: Command line flag should expose separate input/output modes.
-    NewAttrs.addAttribute("denormal-fp-math",
-                          DenormalMode(DenormKind, DenormKind).str());
-  }
-
-  if (DenormalFP32MathView->getNumOccurrences() > 0 &&
-      !F.hasFnAttribute("denormal-fp-math-f32")) {
-    // FIXME: Command line flag should expose separate input/output modes.
-    DenormalMode::DenormalModeKind DenormKind = getDenormalFP32Math();
-
-    NewAttrs.addAttribute(
-      "denormal-fp-math-f32",
-      DenormalMode(DenormKind, DenormKind).str());
+    NewAttrs.addDenormalFPEnvAttr(FPEnv);
   }
 
   if (TrapFuncNameView->getNumOccurrences() > 0)

diff  --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 726a3d8dd906f..696bc6fffc035 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -513,6 +513,10 @@ CaptureInfo Attribute::getCaptureInfo() const {
   return CaptureInfo::createFromIntValue(pImpl->getValueAsInt());
 }
 
+DenormalFPEnv Attribute::getDenormalFPEnv() const {
+  return DenormalFPEnv::createFromIntValue(pImpl->getValueAsInt());
+}
+
 FPClassTest Attribute::getNoFPClass() const {
   assert(hasAttribute(Attribute::NoFPClass) &&
          "Can only call getNoFPClass() on nofpclass attribute");
@@ -693,6 +697,17 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return Result;
   }
 
+  if (hasAttribute(Attribute::DenormalFPEnv)) {
+    std::string Result = "denormal_fpenv(";
+    raw_string_ostream OS(Result);
+
+    struct DenormalFPEnv FPEnv = getDenormalFPEnv();
+    FPEnv.print(OS, /*OmitIfSame=*/true);
+
+    OS << ')';
+    return Result;
+  }
+
   if (hasAttribute(Attribute::NoFPClass)) {
     std::string Result = "nofpclass";
     raw_string_ostream(Result) << getNoFPClass();
@@ -2285,6 +2300,10 @@ AttrBuilder &AttrBuilder::addCapturesAttr(CaptureInfo CI) {
   return addRawIntAttr(Attribute::Captures, CI.toIntValue());
 }
 
+AttrBuilder &AttrBuilder::addDenormalFPEnvAttr(DenormalFPEnv FPEnv) {
+  return addRawIntAttr(Attribute::DenormalFPEnv, FPEnv.toIntValue());
+}
+
 AttrBuilder &AttrBuilder::addNoFPClassAttr(FPClassTest Mask) {
   if (Mask == fcNone)
     return *this;
@@ -2543,16 +2562,16 @@ static bool denormModeCompatible(DenormalMode CallerMode,
 }
 
 static bool checkDenormMode(const Function &Caller, const Function &Callee) {
-  DenormalMode CallerMode = Caller.getDenormalModeRaw();
-  DenormalMode CalleeMode = Callee.getDenormalModeRaw();
+  DenormalFPEnv CallerEnv = Caller.getDenormalFPEnv();
+  DenormalFPEnv CalleeEnv = Callee.getDenormalFPEnv();
 
-  if (denormModeCompatible(CallerMode, CalleeMode)) {
-    DenormalMode CallerModeF32 = Caller.getDenormalModeF32Raw();
-    DenormalMode CalleeModeF32 = Callee.getDenormalModeF32Raw();
+  if (denormModeCompatible(CallerEnv.DefaultMode, CalleeEnv.DefaultMode)) {
+    DenormalMode CallerModeF32 = CallerEnv.F32Mode;
+    DenormalMode CalleeModeF32 = CalleeEnv.F32Mode;
     if (CallerModeF32 == DenormalMode::getInvalid())
-      CallerModeF32 = CallerMode;
+      CallerModeF32 = CallerEnv.DefaultMode;
     if (CalleeModeF32 == DenormalMode::getInvalid())
-      CalleeModeF32 = CalleeMode;
+      CalleeModeF32 = CalleeEnv.DefaultMode;
     return denormModeCompatible(CallerModeF32, CalleeModeF32);
   }
 

diff  --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index 726ebf0697233..0efeeb999d3dd 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -6332,12 +6332,17 @@ void llvm::UpgradeFunctionAttributes(Function &F) {
     Arg.removeAttrs(
         AttributeFuncs::typeIncompatible(Arg.getType(), Arg.getAttributes()));
 
+  bool AddingAttrs = false, RemovingAttrs = false;
+  AttrBuilder AttrsToAdd(F.getContext());
+  AttributeMask AttrsToRemove;
+
   // Older versions of LLVM treated an "implicit-section-name" attribute
   // similarly to directly setting the section on a Function.
   if (Attribute A = F.getFnAttribute("implicit-section-name");
       A.isValid() && A.isStringAttribute()) {
     F.setSection(A.getValueAsString());
-    F.removeFnAttr("implicit-section-name");
+    AttrsToRemove.addAttribute("implicit-section-name");
+    RemovingAttrs = true;
   }
 
   if (!F.empty()) {
@@ -6354,9 +6359,46 @@ void llvm::UpgradeFunctionAttributes(Function &F) {
 
       // We will leave behind dead attribute uses on external declarations, but
       // clang never added these to declarations anyway.
-      F.removeFnAttr("amdgpu-unsafe-fp-atomics");
+      AttrsToRemove.addAttribute("amdgpu-unsafe-fp-atomics");
+      RemovingAttrs = true;
+    }
+  }
+
+  DenormalMode DenormalFPMath = DenormalMode::getIEEE();
+  DenormalMode DenormalFPMathF32 = DenormalMode::getInvalid();
+
+  bool HandleDenormalMode = false;
+
+  if (Attribute Attr = F.getFnAttribute("denormal-fp-math"); Attr.isValid()) {
+    DenormalMode ParsedMode = parseDenormalFPAttribute(Attr.getValueAsString());
+    if (ParsedMode.isValid()) {
+      DenormalFPMath = ParsedMode;
+      AttrsToRemove.addAttribute("denormal-fp-math");
+      AddingAttrs = RemovingAttrs = true;
+      HandleDenormalMode = true;
     }
   }
+
+  if (Attribute Attr = F.getFnAttribute("denormal-fp-math-f32");
+      Attr.isValid()) {
+    DenormalMode ParsedMode = parseDenormalFPAttribute(Attr.getValueAsString());
+    if (ParsedMode.isValid()) {
+      DenormalFPMathF32 = ParsedMode;
+      AttrsToRemove.addAttribute("denormal-fp-math-f32");
+      AddingAttrs = RemovingAttrs = true;
+      HandleDenormalMode = true;
+    }
+  }
+
+  if (HandleDenormalMode)
+    AttrsToAdd.addDenormalFPEnvAttr(
+        DenormalFPEnv(DenormalFPMath, DenormalFPMathF32));
+
+  if (RemovingAttrs)
+    F.removeFnAttrs(AttrsToRemove);
+
+  if (AddingAttrs)
+    F.addFnAttrs(AttrsToAdd);
 }
 
 // Check if the function attribute is not present and set it.

diff  --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp
index d31322a3013b6..448171b2bbbb1 100644
--- a/llvm/lib/IR/Core.cpp
+++ b/llvm/lib/IR/Core.cpp
@@ -209,6 +209,22 @@ LLVMAttributeRef LLVMCreateConstantRangeAttribute(LLVMContextRef C,
                     APInt(NumBits, ArrayRef(UpperWords, NumWords)))));
 }
 
+LLVMAttributeRef LLVMCreateDenormalFPEnvAttribute(
+    LLVMContextRef C, LLVMDenormalModeKind DefaultModeOutput,
+    LLVMDenormalModeKind DefaultModeInput, LLVMDenormalModeKind FloatModeOutput,
+    LLVMDenormalModeKind FloatModeInput) {
+  auto &Ctx = *unwrap(C);
+
+  DenormalFPEnv Env(
+      DenormalMode(
+          static_cast<DenormalMode::DenormalModeKind>(DefaultModeOutput),
+          static_cast<DenormalMode::DenormalModeKind>(DefaultModeInput)),
+      DenormalMode(
+          static_cast<DenormalMode::DenormalModeKind>(FloatModeOutput),
+          static_cast<DenormalMode::DenormalModeKind>(FloatModeInput)));
+  return wrap(Attribute::get(Ctx, Attribute::DenormalFPEnv, Env.toIntValue()));
+}
+
 LLVMAttributeRef LLVMCreateStringAttribute(LLVMContextRef C,
                                            const char *K, unsigned KLength,
                                            const char *V, unsigned VLength) {

diff  --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 467bd80d818fe..ef94c218edb27 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -803,31 +803,17 @@ void Function::addRangeRetAttr(const ConstantRange &CR) {
 }
 
 DenormalMode Function::getDenormalMode(const fltSemantics &FPType) const {
-  if (&FPType == &APFloat::IEEEsingle()) {
-    DenormalMode Mode = getDenormalModeF32Raw();
-    // If the f32 variant of the attribute isn't specified, try to use the
-    // generic one.
-    if (Mode.isValid())
-      return Mode;
-  }
-
-  return getDenormalModeRaw();
-}
+  Attribute Attr = getFnAttribute(Attribute::DenormalFPEnv);
+  if (!Attr.isValid())
+    return DenormalMode::getDefault();
 
-DenormalMode Function::getDenormalModeRaw() const {
-  Attribute Attr = getFnAttribute("denormal-fp-math");
-  StringRef Val = Attr.getValueAsString();
-  return parseDenormalFPAttribute(Val);
+  DenormalFPEnv FPEnv = Attr.getDenormalFPEnv();
+  return &FPType == &APFloat::IEEEsingle() ? FPEnv.F32Mode : FPEnv.DefaultMode;
 }
 
-DenormalMode Function::getDenormalModeF32Raw() const {
-  Attribute Attr = getFnAttribute("denormal-fp-math-f32");
-  if (Attr.isValid()) {
-    StringRef Val = Attr.getValueAsString();
-    return parseDenormalFPAttribute(Val);
-  }
-
-  return DenormalMode::getInvalid();
+DenormalFPEnv Function::getDenormalFPEnv() const {
+  Attribute Attr = getFnAttribute(Attribute::DenormalFPEnv);
+  return Attr.isValid() ? Attr.getDenormalFPEnv() : DenormalFPEnv::getDefault();
 }
 
 const std::string &Function::getGC() const {

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 01d32d4a1825f..120a7daa16f05 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2625,19 +2625,6 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
       CheckFailed("invalid name for a VFABI variant: " + S, V);
   }
 
-  if (auto A = Attrs.getFnAttr("denormal-fp-math"); A.isValid()) {
-    StringRef S = A.getValueAsString();
-    if (!parseDenormalFPAttribute(S).isValid())
-      CheckFailed("invalid value for 'denormal-fp-math' attribute: " + S, V);
-  }
-
-  if (auto A = Attrs.getFnAttr("denormal-fp-math-f32"); A.isValid()) {
-    StringRef S = A.getValueAsString();
-    if (!parseDenormalFPAttribute(S).isValid())
-      CheckFailed("invalid value for 'denormal-fp-math-f32' attribute: " + S,
-                  V);
-  }
-
   if (auto A = Attrs.getFnAttr("modular-format"); A.isValid()) {
     StringRef S = A.getValueAsString();
     SmallVector<StringRef> Args;
@@ -3910,6 +3897,9 @@ void Verifier::visitCallBase(CallBase &Call) {
           "llvm.call.preallocated.arg");
   }
 
+  Check(!Attrs.hasFnAttr(Attribute::DenormalFPEnv),
+        "denormal_fpenv attribute may not apply to call sites", Call);
+
   // Verify call attributes.
   verifyFunctionAttrs(FTy, Attrs, &Call, IsIntrinsic, Call.isInlineAsm());
 

diff  --git a/llvm/lib/Support/FloatingPointMode.cpp b/llvm/lib/Support/FloatingPointMode.cpp
index f2cd008277236..a81a2644a3713 100644
--- a/llvm/lib/Support/FloatingPointMode.cpp
+++ b/llvm/lib/Support/FloatingPointMode.cpp
@@ -107,6 +107,22 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, FPClassTest Mask) {
   return OS;
 }
 
+void DenormalFPEnv::print(raw_ostream &OS, bool OmitIfSame) const {
+  if (F32Mode == DefaultMode) {
+    DefaultMode.print(OS, /*Legacy=*/false, OmitIfSame);
+    return;
+  }
+
+  // Omit printing the base mode if only the f32 mode isn't the default.
+  if (DefaultMode != DenormalMode::getDefault()) {
+    DefaultMode.print(OS, /*Legacy=*/false, OmitIfSame);
+    OS << ", ";
+  }
+
+  OS << "float: ";
+  F32Mode.print(OS, /*Legacy=*/false, OmitIfSame);
+}
+
 static bool cannotOrderStrictlyGreaterImpl(FPClassTest LHS, FPClassTest RHS,
                                            bool OrEqual, bool OrderedZero) {
   LHS &= ~fcNan;

diff  --git a/llvm/lib/Target/AMDGPU/SIModeRegisterDefaults.cpp b/llvm/lib/Target/AMDGPU/SIModeRegisterDefaults.cpp
index f9efee6d39341..9a58382e13c6e 100644
--- a/llvm/lib/Target/AMDGPU/SIModeRegisterDefaults.cpp
+++ b/llvm/lib/Target/AMDGPU/SIModeRegisterDefaults.cpp
@@ -28,19 +28,9 @@ SIModeRegisterDefaults::SIModeRegisterDefaults(const Function &F,
       DX10Clamp = DX10ClampAttr == "true";
   }
 
-  StringRef DenormF32Attr =
-      F.getFnAttribute("denormal-fp-math-f32").getValueAsString();
-  if (!DenormF32Attr.empty())
-    FP32Denormals = parseDenormalFPAttribute(DenormF32Attr);
-
-  StringRef DenormAttr =
-      F.getFnAttribute("denormal-fp-math").getValueAsString();
-  if (!DenormAttr.empty()) {
-    DenormalMode DenormMode = parseDenormalFPAttribute(DenormAttr);
-    if (DenormF32Attr.empty())
-      FP32Denormals = DenormMode;
-    FP64FP16Denormals = DenormMode;
-  }
+  DenormalFPEnv FPEnv = F.getDenormalFPEnv();
+  FP64FP16Denormals = FPEnv.DefaultMode;
+  FP32Denormals = FPEnv.F32Mode;
 }
 
 using namespace AMDGPU;

diff  --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index c77023aed437f..45f647951df33 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -651,13 +651,12 @@ static bool checkFunctionsAttributeConsistency(const Module &M, StringRef Attr,
 }
 // Returns true if all functions definitions have the same denormal mode.
 // It also returns true when the module has no functions.
-static bool checkDenormalAttributeConsistency(const Module &M, StringRef Attr,
-                                              DenormalMode Value) {
+static bool checkDenormalAttributeConsistency(const Module &M,
+                                              DenormalFPEnv Value) {
   return !any_of(M, [&](const Function &F) {
     if (F.isDeclaration())
       return false;
-    StringRef AttrVal = F.getFnAttribute(Attr).getValueAsString();
-    return parseDenormalFPAttribute(AttrVal) != Value;
+    return F.getDenormalFPEnv() != Value;
   });
 }
 
@@ -667,10 +666,10 @@ static bool checkDenormalAttributeInconsistency(const Module &M) {
   auto E = M.functions().end();
   if (F == E)
     return false;
-  DenormalMode Value = F->getDenormalModeRaw();
+  DenormalFPEnv Value = F->getDenormalFPEnv();
   ++F;
   return std::any_of(F, E, [&](const Function &F) {
-    return !F.isDeclaration() && F.getDenormalModeRaw() != Value;
+    return !F.isDeclaration() && F.getDenormalFPEnv() != Value;
   });
 }
 
@@ -736,18 +735,16 @@ void ARMAsmPrinter::emitAttributes() {
     if (unsigned TagVal = DM->getZExtValue())
       ATS.emitAttribute(ARMBuildAttrs::ABI_FP_denormal, TagVal);
   } else if (checkDenormalAttributeConsistency(*MMI->getModule(),
-                                               "denormal-fp-math",
                                                DenormalMode::getPreserveSign()))
     ATS.emitAttribute(ARMBuildAttrs::ABI_FP_denormal,
                       ARMBuildAttrs::PreserveFPSign);
   else if (checkDenormalAttributeConsistency(*MMI->getModule(),
-                                             "denormal-fp-math",
                                              DenormalMode::getPositiveZero()))
     ATS.emitAttribute(ARMBuildAttrs::ABI_FP_denormal,
                       ARMBuildAttrs::PositiveZero);
   else if (checkDenormalAttributeInconsistency(*MMI->getModule()) ||
-           checkDenormalAttributeConsistency(
-               *MMI->getModule(), "denormal-fp-math", DenormalMode::getIEEE()))
+           checkDenormalAttributeConsistency(*MMI->getModule(),
+                                             DenormalMode::getIEEE()))
     ATS.emitAttribute(ARMBuildAttrs::ABI_FP_denormal,
                       ARMBuildAttrs::IEEEDenormals);
   else {

diff  --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index 590d4c70592f8..d74d7737995a1 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -230,7 +230,7 @@ ARMBaseTargetMachine::getSubtargetImpl(const Function &F) const {
   if (F.hasMinSize())
     Key += "+minsize";
 
-  DenormalMode DM = F.getDenormalModeRaw();
+  DenormalMode DM = F.getDenormalFPEnv().DefaultMode;
   if (DM != DenormalMode::getIEEE())
     Key += "denormal-fp-math=" + DM.str();
 

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 86bdb7c49c0e3..37bf2b9c1a966 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -9016,15 +9016,9 @@ struct AADenormalFPMathFunction final : AADenormalFPMathImpl {
 
   void initialize(Attributor &A) override {
     const Function *F = getAnchorScope();
-    DenormalMode Mode = F->getDenormalModeRaw();
-    DenormalMode ModeF32 = F->getDenormalModeF32Raw();
-
-    // TODO: Handling this here prevents handling the case where a callee has a
-    // fixed denormal-fp-math with dynamic denormal-fp-math-f32, but called from
-    // a function with a fully fixed mode.
-    if (ModeF32 == DenormalMode::getInvalid())
-      ModeF32 = Mode;
-    Known = DenormalState{Mode, ModeF32};
+    DenormalFPEnv DenormEnv = F->getDenormalFPEnv();
+
+    Known = DenormalState{DenormEnv.DefaultMode, DenormEnv.F32Mode};
     if (isModeFixed())
       indicateFixpoint();
   }
@@ -9060,19 +9054,17 @@ struct AADenormalFPMathFunction final : AADenormalFPMathImpl {
     LLVMContext &Ctx = getAssociatedFunction()->getContext();
 
     SmallVector<Attribute, 2> AttrToAdd;
-    SmallVector<StringRef, 2> AttrToRemove;
-    if (Known.Mode == DenormalMode::getDefault()) {
-      AttrToRemove.push_back("denormal-fp-math");
-    } else {
-      AttrToAdd.push_back(
-          Attribute::get(Ctx, "denormal-fp-math", Known.Mode.str()));
-    }
+    SmallVector<Attribute::AttrKind, 2> AttrToRemove;
+
+    // TODO: Change to use DenormalFPEnv everywhere.
+    DenormalFPEnv KnownEnv(Known.Mode, Known.ModeF32);
 
-    if (Known.ModeF32 != Known.Mode) {
-      AttrToAdd.push_back(
-          Attribute::get(Ctx, "denormal-fp-math-f32", Known.ModeF32.str()));
+    if (KnownEnv == DenormalFPEnv::getDefault()) {
+      AttrToRemove.push_back(Attribute::DenormalFPEnv);
     } else {
-      AttrToRemove.push_back("denormal-fp-math-f32");
+      AttrToAdd.push_back(Attribute::get(
+          Ctx, Attribute::DenormalFPEnv,
+          DenormalFPEnv(Known.Mode, Known.ModeF32).toIntValue()));
     }
 
     auto &IRP = getIRPosition();
@@ -9083,7 +9075,7 @@ struct AADenormalFPMathFunction final : AADenormalFPMathImpl {
   }
 
   void trackStatistics() const override {
-    STATS_DECLTRACK_FN_ATTR(denormal_fp_math)
+    STATS_DECLTRACK_FN_ATTR(denormal_fpenv)
   }
 };
 } // namespace

diff  --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index a6ba42f5bec2a..fe8758b942938 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -982,6 +982,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
       case Attribute::MustProgress:
       case Attribute::NoProfile:
       case Attribute::SkipProfile:
+      case Attribute::DenormalFPEnv:
         break;
       // These attributes cannot be applied to functions.
       case Attribute::Alignment:

diff  --git a/llvm/test/Analysis/CostModel/AMDGPU/fdiv.ll b/llvm/test/Analysis/CostModel/AMDGPU/fdiv.ll
index ab9d7f9dc859d..99a9bc2ec04f7 100644
--- a/llvm/test/Analysis/CostModel/AMDGPU/fdiv.ll
+++ b/llvm/test/Analysis/CostModel/AMDGPU/fdiv.ll
@@ -739,5 +739,5 @@ define i32 @frem(i32 %arg) {
   ret i32 undef
 }
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: ieee|ieee) }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/Assembler/denormal_fpenv.ll b/llvm/test/Assembler/denormal_fpenv.ll
new file mode 100644
index 0000000000000..95f70bfb44503
--- /dev/null
+++ b/llvm/test/Assembler/denormal_fpenv.ll
@@ -0,0 +1,295 @@
+; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
+
+define void @func_ieee() denormal_fpenv(ieee) {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @func_ieee(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_preservesign() denormal_fpenv(preservesign) {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
+; CHECK-LABEL: define void @func_preservesign(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_positivezero() denormal_fpenv(positivezero) {
+; CHECK: Function Attrs: denormal_fpenv(positivezero)
+; CHECK-LABEL: define void @func_positivezero(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_dynamic() denormal_fpenv(dynamic) {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
+; CHECK-LABEL: define void @func_dynamic(
+; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_ieee_ieee() denormal_fpenv(ieee|ieee) {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @func_ieee_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_preservesign_preservesign() denormal_fpenv(preservesign|preservesign) {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
+; CHECK-LABEL: define void @func_preservesign_preservesign(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_positivezero_positivezero() denormal_fpenv(positivezero|positivezero) {
+; CHECK: Function Attrs: denormal_fpenv(positivezero)
+; CHECK-LABEL: define void @func_positivezero_positivezero(
+; CHECK-SAME: ) #[[ATTR2]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_dynamic_dynamic() denormal_fpenv(dynamic|dynamic) {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
+; CHECK-LABEL: define void @func_dynamic_dynamic(
+; CHECK-SAME: ) #[[ATTR3]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_ieee_preservesign() denormal_fpenv(ieee|preservesign) {
+; CHECK-LABEL: define void @func_ieee_preservesign(
+; CHECK-SAME: ) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_preservesign_ieee() denormal_fpenv(preservesign|ieee) {
+; CHECK-LABEL: define void @func_preservesign_ieee(
+; CHECK-SAME: ) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_ieee_dynamic() denormal_fpenv(ieee|dynamic) {
+; CHECK-LABEL: define void @func_ieee_dynamic(
+; CHECK-SAME: ) #[[ATTR6:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_dynamic_ieee() denormal_fpenv(dynamic|ieee) {
+; CHECK-LABEL: define void @func_dynamic_ieee(
+; CHECK-SAME: ) #[[ATTR7:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_ieee_positivezero() denormal_fpenv(ieee|positivezero) {
+; CHECK-LABEL: define void @func_ieee_positivezero(
+; CHECK-SAME: ) #[[ATTR8:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_positivezero_ieee() denormal_fpenv(positivezero|ieee) {
+; CHECK-LABEL: define void @func_positivezero_ieee(
+; CHECK-SAME: ) #[[ATTR9:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_positivezero_dynamic() denormal_fpenv(positivezero|dynamic) {
+; CHECK-LABEL: define void @func_positivezero_dynamic(
+; CHECK-SAME: ) #[[ATTR10:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_dynamic_positivezero() denormal_fpenv(dynamic|positivezero) {
+; CHECK-LABEL: define void @func_dynamic_positivezero(
+; CHECK-SAME: ) #[[ATTR11:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_attr_group0() #0 {
+; CHECK-LABEL: define void @func_attr_group0(
+; CHECK-SAME: ) #[[ATTR12:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_attr_group1() #1 {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
+; CHECK-LABEL: define void @func_attr_group1(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_ieee() denormal_fpenv(float:ieee) {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @func_f32_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_preservesign() denormal_fpenv(float: preservesign) {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
+; CHECK-LABEL: define void @func_f32_preservesign(
+; CHECK-SAME: ) #[[ATTR13:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_positivezero() denormal_fpenv(float: positivezero) {
+; CHECK: Function Attrs: denormal_fpenv(float: positivezero)
+; CHECK-LABEL: define void @func_f32_positivezero(
+; CHECK-SAME: ) #[[ATTR14:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_dynamic() denormal_fpenv(float: dynamic) {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
+; CHECK-LABEL: define void @func_f32_dynamic(
+; CHECK-SAME: ) #[[ATTR15:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_ieee_ieee() denormal_fpenv(float: ieee|ieee) {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @func_f32_ieee_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_preservesign_preservesign() denormal_fpenv(float: preservesign|preservesign) {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
+; CHECK-LABEL: define void @func_f32_preservesign_preservesign(
+; CHECK-SAME: ) #[[ATTR13]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_positivezero_positivezero() denormal_fpenv(float: positivezero|positivezero) {
+; CHECK: Function Attrs: denormal_fpenv(float: positivezero)
+; CHECK-LABEL: define void @func_f32_positivezero_positivezero(
+; CHECK-SAME: ) #[[ATTR14]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_dynamic_dynamic() denormal_fpenv(float: dynamic|dynamic) {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
+; CHECK-LABEL: define void @func_f32_dynamic_dynamic(
+; CHECK-SAME: ) #[[ATTR15]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_preservesign_ieee() denormal_fpenv(float: preservesign|ieee) {
+; CHECK-LABEL: define void @func_f32_preservesign_ieee(
+; CHECK-SAME: ) #[[ATTR16:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f32_dynamic_positivezero() denormal_fpenv(float:dynamic|positivezero) {
+; CHECK-LABEL: define void @func_f32_dynamic_positivezero(
+; CHECK-SAME: ) #[[ATTR17:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f64_dynamic__f32_ieee() denormal_fpenv(dynamic, float:ieee) {
+; CHECK: Function Attrs: denormal_fpenv(dynamic, float: ieee)
+; CHECK-LABEL: define void @func_f64_dynamic__f32_ieee(
+; CHECK-SAME: ) #[[ATTR18:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f64_dynamic__f32_preservesign() denormal_fpenv(dynamic, float:preservesign) {
+; CHECK: Function Attrs: denormal_fpenv(dynamic, float: preservesign)
+; CHECK-LABEL: define void @func_f64_dynamic__f32_preservesign(
+; CHECK-SAME: ) #[[ATTR19:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @func_f64_dynamic_ieee__f32_preservesign_ieee() denormal_fpenv(dynamic|ieee, float:preservesign|ieee) {
+;
+; CHECK-LABEL: define void @func_f64_dynamic_ieee__f32_preservesign_ieee(
+; CHECK-SAME: ) #[[ATTR20:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+attributes #0 = { denormal_fpenv(preservesign|dynamic) }
+attributes #1 = { denormal_fpenv(preservesign) }
+;.
+; CHECK: attributes #[[ATTR0]] = { denormal_fpenv(ieee) }
+; CHECK: attributes #[[ATTR1]] = { denormal_fpenv(preservesign) }
+; CHECK: attributes #[[ATTR2]] = { denormal_fpenv(positivezero) }
+; CHECK: attributes #[[ATTR3]] = { denormal_fpenv(dynamic) }
+; CHECK: attributes #[[ATTR4]] = { denormal_fpenv(ieee|preservesign) }
+; CHECK: attributes #[[ATTR5]] = { denormal_fpenv(preservesign|ieee) }
+; CHECK: attributes #[[ATTR6]] = { denormal_fpenv(ieee|dynamic) }
+; CHECK: attributes #[[ATTR7]] = { denormal_fpenv(dynamic|ieee) }
+; CHECK: attributes #[[ATTR8]] = { denormal_fpenv(ieee|positivezero) }
+; CHECK: attributes #[[ATTR9]] = { denormal_fpenv(positivezero|ieee) }
+; CHECK: attributes #[[ATTR10]] = { denormal_fpenv(positivezero|dynamic) }
+; CHECK: attributes #[[ATTR11]] = { denormal_fpenv(dynamic|positivezero) }
+; CHECK: attributes #[[ATTR12]] = { denormal_fpenv(preservesign|dynamic) }
+; CHECK: attributes #[[ATTR13]] = { denormal_fpenv(float: preservesign) }
+; CHECK: attributes #[[ATTR14]] = { denormal_fpenv(float: positivezero) }
+; CHECK: attributes #[[ATTR15]] = { denormal_fpenv(float: dynamic) }
+; CHECK: attributes #[[ATTR16]] = { denormal_fpenv(float: preservesign|ieee) }
+; CHECK: attributes #[[ATTR17]] = { denormal_fpenv(float: dynamic|positivezero) }
+; CHECK: attributes #[[ATTR18]] = { denormal_fpenv(dynamic, float: ieee) }
+; CHECK: attributes #[[ATTR19]] = { denormal_fpenv(dynamic, float: preservesign) }
+; CHECK: attributes #[[ATTR20]] = { denormal_fpenv(dynamic|ieee, float: preservesign|ieee) }
+;.

diff  --git a/llvm/test/Assembler/invalid_denormal_fpenv.ll b/llvm/test/Assembler/invalid_denormal_fpenv.ll
new file mode 100644
index 0000000000000..ce8aa65fedea4
--- /dev/null
+++ b/llvm/test/Assembler/invalid_denormal_fpenv.ll
@@ -0,0 +1,201 @@
+; RUN: rm -rf %t && split-file %s %t
+; RUN: not llvm-as %t/denormal_fpenv_no_parens.ll -disable-output 2>&1 | FileCheck -check-prefix=NOPARENS %s
+; RUN: not llvm-as %t/denormal_fpenv_lparen.ll -disable-output 2>&1 | FileCheck -check-prefix=LPAREN %s
+; RUN: not llvm-as %t/denormal_fpenv_rparen.ll -disable-output 2>&1 | FileCheck -check-prefix=RPAREN %s
+; RUN: not llvm-as %t/denormal_fpenv_empty_parens.ll -disable-output 2>&1 | FileCheck -check-prefix=EMPTYPARENS %s
+; RUN: not llvm-as %t/invalid_single_entry.ll -disable-output 2>&1 | FileCheck -check-prefix=INVALID_SINGLE_ENTRY %s
+; RUN: not llvm-as %t/invalid_multi_entry0.ll -disable-output 2>&1 | FileCheck -check-prefix=INVALID_MULTI_ENTRY0 %s
+; RUN: not llvm-as %t/invalid_multi_entry1.ll -disable-output 2>&1 | FileCheck -check-prefix=INVALID_MULTI_ENTRY1 %s
+; RUN: not llvm-as %t/invalid_second_element.ll -disable-output 2>&1 | FileCheck -check-prefix=INVALID_SECOND_ELEMENT %s
+; RUN: not llvm-as %t/missing_bar.ll -disable-output 2>&1 | FileCheck -check-prefix=MISSING_BAR %s
+; RUN: not llvm-as %t/with_comma.ll -disable-output 2>&1 | FileCheck -check-prefix=WITH_COMMA %s
+; RUN: not llvm-as %t/missing_rparen_one_elt.ll -disable-output 2>&1 | FileCheck -check-prefix=MISSING_RPAREN_ONEELT %s
+; RUN: not llvm-as %t/missing_rparen_two_elt.ll -disable-output 2>&1 | FileCheck -check-prefix=MISSING_RPAREN_TWOELT %s
+; RUN: not llvm-as %t/extra_elt.ll -disable-output 2>&1 | FileCheck -check-prefix=EXTRA_ELT %s
+; RUN: not llvm-as %t/arg_attr.ll -disable-output 2>&1 | FileCheck -check-prefix=ARG_ATTR %s
+; RUN: not llvm-as %t/ret_attr.ll -disable-output 2>&1 | FileCheck -check-prefix=RET_ATTR %s
+; RUN: not llvm-as %t/start_not_float_type.ll -disable-output 2>&1 | FileCheck -check-prefix=START_NOT_FLOAT %s
+; RUN: not llvm-as %t/only_float.ll -disable-output 2>&1 | FileCheck -check-prefix=ONLY_FLOAT %s
+; RUN: not llvm-as %t/only_float_colon.ll -disable-output 2>&1 | FileCheck -check-prefix=ONLY_FLOAT_COLON %s
+; RUN: not llvm-as %t/only_float_mode_invalid_single_entry.ll -disable-output 2>&1 | FileCheck -check-prefix=FLOAT_INVALID_SINGLE_ELT %s
+; RUN: not llvm-as %t/only_float_mode_invalid_two_entries.ll -disable-output 2>&1 | FileCheck -check-prefix=FLOAT_INVALID_TWO_ELT %s
+; RUN: not llvm-as %t/only_float_mode_invalid_second_entry.ll -disable-output 2>&1 | FileCheck -check-prefix=FLOAT_INVALID_SECOND_ENTRY %s
+; RUN: not llvm-as %t/both_sections_wrong_type.ll -disable-output 2>&1 | FileCheck -check-prefix=BOTH_SECTIONS_WRONG_TYPE %s
+; RUN: not llvm-as %t/both_sections_invalid_float_entry0.ll -disable-output 2>&1 | FileCheck -check-prefix=BOTH_SECTIONS_INVALID_FLOAT_ENTRY0 %s
+; RUN: not llvm-as %t/both_sections_invalid_float_entry1.ll -disable-output 2>&1 | FileCheck -check-prefix=BOTH_SECTIONS_INVALID_FLOAT_ENTRY1 %s
+; RUN: not llvm-as %t/missing_comma_float.ll -disable-output 2>&1 | FileCheck -check-prefix=MISSING_COMMA_FLOAT %s
+
+
+;--- denormal_fpenv_no_parens.ll
+
+; NOPARENS: :36: error: expected '('
+define void @func() denormal_fpenv {
+  ret void
+}
+
+;--- denormal_fpenv_lparen.ll
+; LPAREN: 37: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv( {
+  ret void
+}
+
+;--- denormal_fpenv_rparen.ll
+
+; RPAREN: :35: error: expected '('
+define void @func() denormal_fpenv) {
+  ret void
+}
+
+;--- denormal_fpenv_empty_parens.ll
+
+; EMPTYPARENS: :36: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv() {
+  ret void
+}
+
+;--- invalid_single_entry.ll
+
+; INVALID_SINGLE_ENTRY: :36: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(invalid) {
+  ret void
+}
+
+;--- invalid_multi_entry0.ll
+
+; INVALID_MULTI_ENTRY0: :36: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(invalid|invalid) {
+  ret void
+}
+
+;--- invalid_multi_entry1.ll
+
+; INVALID_MULTI_ENTRY1: :36: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(invalid0|invalid1) {
+  ret void
+}
+
+;--- invalid_second_element.ll
+
+; INVALID_SECOND_ELEMENT: :44: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(dynamic|invalid1) {
+  ret void
+}
+
+;--- missing_bar.ll
+
+; MISSING_BAR: :49: error: unterminated denormal_fpenv
+define void @func() denormal_fpenv(preservesign preservesign) {
+  ret void
+}
+
+;--- with_comma.ll
+
+; WITH_COMMA: :49: error: unterminated denormal_fpenv
+define void @func() denormal_fpenv(preservesign,preservesign) {
+  ret void
+}
+
+;--- missing_rparen_one_elt.ll
+
+; MISSING_RPAREN_ONEELT: :49: error: unterminated denormal_fpenv
+define void @func() denormal_fpenv(preservesign {
+  ret void
+}
+
+;--- missing_rparen_two_elt.ll
+
+; MISSING_RPAREN_TWOELT: :58: error: unterminated denormal_fpenv
+define void @func() denormal_fpenv(preservesign| dynamic {
+  ret void
+}
+
+;--- extra_elt.ll
+
+; EXTRA_ELT: :53: error: unterminated denormal_fpenv
+define void @func() denormal_fpenv(preservesign|ieee|preservesign) {
+  ret void
+}
+
+;--- arg_attr.ll
+
+; ARG_ATTR: :25: error: this attribute does not apply to parameters
+define void @func(float denormal_fpenv(preservesign) %arg) {
+  ret void
+}
+
+;--- ret_attr.ll
+
+; RET_ATTR: :8: error: this attribute does not apply to return values
+define denormal_fpenv(preservesign) float @func() {
+  ret void
+}
+
+;--- start_not_float_type.ll
+
+; START_NOT_FLOAT: :42: error: expected float:
+define void @func() denormal_fpenv(double) {
+  ret void
+}
+
+;--- only_float.ll
+
+; ONLY_FLOAT: :41: error: expected ':' before float denormal_fpenv
+define void @func() denormal_fpenv(float) {
+  ret void
+}
+
+;--- only_float_colon.ll
+
+; ONLY_FLOAT_COLON: :42: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(float:) {
+  ret void
+}
+
+;--- only_float_mode_invalid_single_entry.ll
+
+; FLOAT_INVALID_SINGLE_ELT: :42: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(float:invalid) {
+  ret void
+}
+
+;--- only_float_mode_invalid_two_entries.ll
+
+; FLOAT_INVALID_TWO_ELT: :42: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(float:invalid|invalid) {
+  ret void
+}
+
+;--- only_float_mode_invalid_second_entry.ll
+
+; FLOAT_INVALID_SECOND_ENTRY: :55: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(float:preservesign|invalid) {
+  ret void
+}
+
+;--- both_sections_wrong_type.ll
+
+; BOTH_SECTIONS_WRONG_TYPE: :61: error: expected float:
+define void @func() denormal_fpenv(preservesign|ieee, double:dynamic|preservesign) {
+  ret void
+}
+
+;--- both_sections_invalid_float_entry0.ll
+
+; BOTH_SECTIONS_INVALID_FLOAT_ENTRY0: :61: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(preservesign|ieee, float:invalid|dynamic) {
+  ret void
+}
+
+;--- both_sections_invalid_float_entry1.ll
+
+; BOTH_SECTIONS_INVALID_FLOAT_ENTRY1: :69: error: expected denormal behavior kind (ieee, preservesign, positivezero, dynamic)
+define void @func() denormal_fpenv(preservesign|ieee, float:dynamic|invalid) {
+  ret void
+}
+
+;--- missing_comma_float.ll
+
+; MISSING_COMMA_FLOAT: :54: error: expected ',' before float:
+define void @func() denormal_fpenv(preservesign|ieee float:dynamic|invalid) {
+  ret void
+}

diff  --git a/llvm/test/Bitcode/auto_upgrade_denormal_fp_math.ll b/llvm/test/Bitcode/auto_upgrade_denormal_fp_math.ll
new file mode 100644
index 0000000000000..ccbd7189910ce
--- /dev/null
+++ b/llvm/test/Bitcode/auto_upgrade_denormal_fp_math.ll
@@ -0,0 +1,378 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+; Invalid but didn't fail the verifier
+define void @str_denormal_fp_math_no_val() "denormal-fp-math" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_no_val(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+; Invalid but didn't fail the verifier
+define void @str_denormal_fp_math_empty_str() "denormal-fp-math"="" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_empty_str(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_ieee() "denormal-fp-math"="ieee" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_ieee_ieee() "denormal-fp-math"="ieee,ieee" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_preserve_sign() "denormal-fp-math"="preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_preserve_sign(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_preserve_sign_preserve_sign() "denormal-fp-math"="preserve-sign,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_preserve_sign_preserve_sign(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_dynamic() "denormal-fp-math"="dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
+; CHECK-LABEL: define void @str_denormal_fp_math_dynamic(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_dynamic_dynamic() "denormal-fp-math"="dynamic,dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
+; CHECK-LABEL: define void @str_denormal_fp_math_dynamic_dynamic(
+; CHECK-SAME: ) #[[ATTR2]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_positivezero() "denormal-fp-math"="positive-zero" {
+; CHECK: Function Attrs: denormal_fpenv(positivezero)
+; CHECK-LABEL: define void @str_denormal_fp_math_positivezero(
+; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_positivezero_positivezero() "denormal-fp-math"="positive-zero,positive-zero" {
+; CHECK: Function Attrs: denormal_fpenv(positivezero)
+; CHECK-LABEL: define void @str_denormal_fp_math_positivezero_positivezero(
+; CHECK-SAME: ) #[[ATTR3]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_ieee_preservesign() "denormal-fp-math"="ieee,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(ieee|preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee_preservesign(
+; CHECK-SAME: ) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_preservesign_ieee() "denormal-fp-math"="preserve-sign,ieee" {
+; CHECK: Function Attrs: denormal_fpenv(preservesign|ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_preservesign_ieee(
+; CHECK-SAME: ) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+
+; Invalid but didn't fail the verifier
+define void @str_denormal_fp_math_f32_no_val() "denormal-fp-math-f32" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_no_val(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+; Invalid but didn't fail the verifier
+define void @str_denormal_fp_math_f32_empty_str() "denormal-fp-math-f32"="" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_empty_str(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_ieee() "denormal-fp-math-f32"="ieee" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_ieee_ieee() "denormal-fp-math-f32"="ieee,ieee" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_ieee_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_preserve_sign() "denormal-fp-math-f32"="preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_preserve_sign(
+; CHECK-SAME: ) #[[ATTR6:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_preserve_sign_preserve_sign() "denormal-fp-math-f32"="preserve-sign,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_preserve_sign_preserve_sign(
+; CHECK-SAME: ) #[[ATTR6]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_dynamic() "denormal-fp-math-f32"="dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_dynamic(
+; CHECK-SAME: ) #[[ATTR7:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_dynamic_dynamic() "denormal-fp-math-f32"="dynamic,dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_dynamic_dynamic(
+; CHECK-SAME: ) #[[ATTR7]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_positivezero() "denormal-fp-math-f32"="positive-zero" {
+; CHECK: Function Attrs: denormal_fpenv(float: positivezero)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_positivezero(
+; CHECK-SAME: ) #[[ATTR8:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_positivezero_positivezero() "denormal-fp-math-f32"="positive-zero,positive-zero" {
+; CHECK: Function Attrs: denormal_fpenv(float: positivezero)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_positivezero_positivezero(
+; CHECK-SAME: ) #[[ATTR8]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_ieee_preservesign() "denormal-fp-math-f32"="ieee,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(float: ieee|preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_ieee_preservesign(
+; CHECK-SAME: ) #[[ATTR9:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_f32_preservesign_ieee() "denormal-fp-math-f32"="preserve-sign,ieee" {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign|ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_f32_preservesign_ieee(
+; CHECK-SAME: ) #[[ATTR10:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_ieee__denormal_fp_math_f32_ieee() "denormal-fp-math"="ieee" "denormal-fp-math-f32"="ieee" {
+; CHECK: Function Attrs: denormal_fpenv(ieee)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee__denormal_fp_math_f32_ieee(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+
+define void @str_denormal_fp_math_ieee__denormal_fp_math_f32_preserve_sign() "denormal-fp-math"="ieee" "denormal-fp-math-f32"="preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee__denormal_fp_math_f32_preserve_sign(
+; CHECK-SAME: ) #[[ATTR6]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_ieee_ieee__denormal_fp_math_f32_preserve_sign_preserve_sign() "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="preserve-sign,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee_ieee__denormal_fp_math_f32_preserve_sign_preserve_sign(
+; CHECK-SAME: ) #[[ATTR6]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_ieee_ieee__denormal_fp_math_f32_preserve_sign_dynamic_dynamic() "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="dynamic,dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
+; CHECK-LABEL: define void @str_denormal_fp_math_ieee_ieee__denormal_fp_math_f32_preserve_sign_dynamic_dynamic(
+; CHECK-SAME: ) #[[ATTR7]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_dynamic_dynamic__denormal_fp_math_f32_preserve_sign_dynamic_dynamic() "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="dynamic,dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
+; CHECK-LABEL: define void @str_denormal_fp_math_dynamic_dynamic__denormal_fp_math_f32_preserve_sign_dynamic_dynamic(
+; CHECK-SAME: ) #[[ATTR2]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_dynamic_dynamic__denormal_fp_math_f32_preserve_sign_preserve_sign() "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(dynamic, float: preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_dynamic_dynamic__denormal_fp_math_f32_preserve_sign_preserve_sign(
+; CHECK-SAME: ) #[[ATTR11:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+define void @str_denormal_fp_math_dynamic_positive_zero__denormal_fp_math_f32_preserve_sign_dynamic_preserve_sign() "denormal-fp-math"="dynamic,positive-zero" "denormal-fp-math-f32"="dynamic,preserve-sign" {
+; CHECK: Function Attrs: denormal_fpenv(dynamic|positivezero, float: dynamic|preservesign)
+; CHECK-LABEL: define void @str_denormal_fp_math_dynamic_positive_zero__denormal_fp_math_f32_preserve_sign_dynamic_preserve_sign(
+; CHECK-SAME: ) #[[ATTR12:[0-9]+]] {
+; CHECK-NEXT:    ret void
+;
+  ret void
+}
+
+declare void @func()
+
+; These never did anything, but the new attribute fails the verifier
+; on call sites.
+define void @ignore_callsite_attrs() {
+; CHECK-LABEL: define void @ignore_callsite_attrs() {
+; CHECK-NEXT:    call void @func() #[[ATTR19:[0-9]+]]
+; CHECK-NEXT:    call void @func() #[[ATTR20:[0-9]+]]
+; CHECK-NEXT:    call void @func() #[[ATTR21:[0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  call void @func() "denormal-fp-math"="preserve-sign,preserve-sign"
+  call void @func() "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+  call void @func() "denormal-fp-math"="dynamic,ieee" "denormal-fp-math-f32"="preserve-sign,preserve-sign"
+  ret void
+}
+
+define float @test_denormal_fp_math_invalid1() "denormal-fp-math"="foo,ieee" {
+; CHECK-LABEL: define float @test_denormal_fp_math_invalid1(
+; CHECK-SAME: ) #[[ATTR13:[0-9]+]] {
+; CHECK-NEXT:    ret float 1.000000e+00
+;
+  ret float 1.0
+}
+
+define float @test_denormal_fp_math_invalid2() "denormal-fp-math"="ieee,ieee,ieee" {
+; CHECK-LABEL: define float @test_denormal_fp_math_invalid2(
+; CHECK-SAME: ) #[[ATTR14:[0-9]+]] {
+; CHECK-NEXT:    ret float 1.000000e+00
+;
+  ret float 1.0
+}
+
+define float @test_denormal_fp_math_f32_invalid() "denormal-fp-math-f32"="foo,ieee" {
+; CHECK-LABEL: define float @test_denormal_fp_math_f32_invalid(
+; CHECK-SAME: ) #[[ATTR15:[0-9]+]] {
+; CHECK-NEXT:    ret float 1.000000e+00
+;
+  ret float 1.0
+}
+
+define float @test_both_denormal_fp_math_invalid() "denormal-fp-math"="bar" "denormal-fp-math-f32"="foo,ieee" {
+; CHECK-LABEL: define float @test_both_denormal_fp_math_invalid(
+; CHECK-SAME: ) #[[ATTR16:[0-9]+]] {
+; CHECK-NEXT:    ret float 1.000000e+00
+;
+  ret float 1.0
+}
+
+define float @test_denormal_fp_math_invalid_with_invalid_f32() "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="foo,ieee" {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
+; CHECK-LABEL: define float @test_denormal_fp_math_invalid_with_invalid_f32(
+; CHECK-SAME: ) #[[ATTR17:[0-9]+]] {
+; CHECK-NEXT:    ret float 1.000000e+00
+;
+  ret float 1.0
+}
+
+define float @test_invalid_denormal_fp_math_with_valid_f32() "denormal-fp-math"="foo,dynamic" "denormal-fp-math-f32"="dynamic,dynamic" {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
+; CHECK-LABEL: define float @test_invalid_denormal_fp_math_with_valid_f32(
+; CHECK-SAME: ) #[[ATTR18:[0-9]+]] {
+; CHECK-NEXT:    ret float 1.000000e+00
+;
+  ret float 1.0
+}
+
+;.
+; CHECK: attributes #[[ATTR0]] = { denormal_fpenv(ieee) }
+; CHECK: attributes #[[ATTR1]] = { denormal_fpenv(preservesign) }
+; CHECK: attributes #[[ATTR2]] = { denormal_fpenv(dynamic) }
+; CHECK: attributes #[[ATTR3]] = { denormal_fpenv(positivezero) }
+; CHECK: attributes #[[ATTR4]] = { denormal_fpenv(ieee|preservesign) }
+; CHECK: attributes #[[ATTR5]] = { denormal_fpenv(preservesign|ieee) }
+; CHECK: attributes #[[ATTR6]] = { denormal_fpenv(float: preservesign) }
+; CHECK: attributes #[[ATTR7]] = { denormal_fpenv(float: dynamic) }
+; CHECK: attributes #[[ATTR8]] = { denormal_fpenv(float: positivezero) }
+; CHECK: attributes #[[ATTR9]] = { denormal_fpenv(float: ieee|preservesign) }
+; CHECK: attributes #[[ATTR10]] = { denormal_fpenv(float: preservesign|ieee) }
+; CHECK: attributes #[[ATTR11]] = { denormal_fpenv(dynamic, float: preservesign) }
+; CHECK: attributes #[[ATTR12]] = { denormal_fpenv(dynamic|positivezero, float: dynamic|preservesign) }
+; CHECK: attributes #[[ATTR13]] = { "denormal-fp-math"="foo,ieee" }
+; CHECK: attributes #[[ATTR14]] = { "denormal-fp-math"="ieee,ieee,ieee" }
+; CHECK: attributes #[[ATTR15]] = { "denormal-fp-math-f32"="foo,ieee" }
+; CHECK: attributes #[[ATTR16]] = { "denormal-fp-math"="bar" "denormal-fp-math-f32"="foo,ieee" }
+; CHECK: attributes #[[ATTR17]] = { denormal_fpenv(dynamic) "denormal-fp-math-f32"="foo,ieee" }
+; CHECK: attributes #[[ATTR18]] = { denormal_fpenv(float: dynamic) "denormal-fp-math"="foo,dynamic" }
+; CHECK: attributes #[[ATTR19]] = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+; CHECK: attributes #[[ATTR20]] = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+; CHECK: attributes #[[ATTR21]] = { "denormal-fp-math"="dynamic,ieee" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+;.

diff  --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll
index 53cbe2d6ffd37..7ecc92ae16e97 100644
--- a/llvm/test/Bitcode/compatibility.ll
+++ b/llvm/test/Bitcode/compatibility.ll
@@ -1722,7 +1722,7 @@ exit:
   ; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2>
 
   call void @f.nobuiltin() builtin
-  ; CHECK: call void @f.nobuiltin() #55
+  ; CHECK: call void @f.nobuiltin() #87
 
   call fastcc noalias ptr @f.noalias() noinline
   ; CHECK: call fastcc noalias ptr @f.noalias() #12
@@ -2237,6 +2237,200 @@ define float @nofpclass_callsites(float %arg, { float } %arg1) {
   ret float %add1
 }
 
+; CHECK: define void @denormal_fpenv__ieee_ieee() #55 {
+define void @denormal_fpenv__ieee_ieee() denormal_fpenv(ieee|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__ieee_preservesign() #56 {
+define void @denormal_fpenv__ieee_preservesign() denormal_fpenv(ieee|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__ieee_positivezero() #57 {
+define void @denormal_fpenv__ieee_positivezero() denormal_fpenv(ieee|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__ieee_dynamic() #58 {
+define void @denormal_fpenv__ieee_dynamic() denormal_fpenv(ieee|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__preservesign_ieee() #59 {
+define void @denormal_fpenv__preservesign_ieee() denormal_fpenv(preservesign|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__preservesign_preservesign() #60 {
+define void @denormal_fpenv__preservesign_preservesign() denormal_fpenv(preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__preservesign_positivezero() #61 {
+define void @denormal_fpenv__preservesign_positivezero() denormal_fpenv(preservesign|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__preservesign_dynamic() #62 {
+define void @denormal_fpenv__preservesign_dynamic() denormal_fpenv(preservesign|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__positivezero_ieee() #63 {
+define void @denormal_fpenv__positivezero_ieee() denormal_fpenv(positivezero|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__positivezero_preservesign() #64 {
+define void @denormal_fpenv__positivezero_preservesign() denormal_fpenv(positivezero|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__positivezero_positivezero() #65 {
+define void @denormal_fpenv__positivezero_positivezero() denormal_fpenv(positivezero|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__positivezero_dynamix() #66 {
+define void @denormal_fpenv__positivezero_dynamix() denormal_fpenv(positivezero|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__dynamic_ieee() #67 {
+define void @denormal_fpenv__dynamic_ieee() denormal_fpenv(dynamic|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__dynamic_preservesign() #68 {
+define void @denormal_fpenv__dynamic_preservesign() denormal_fpenv(dynamic|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__dynamic_positivezero() #69 {
+define void @denormal_fpenv__dynamic_positivezero() denormal_fpenv(dynamic|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__dynamic_dynamic() #70 {
+define void @denormal_fpenv__dynamic_dynamic() denormal_fpenv(dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__ieee_ieee() #55 {
+define void @denormal_fpenv_float__ieee_ieee() denormal_fpenv(float: ieee|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__ieee_preservesign() #71 {
+define void @denormal_fpenv_float__ieee_preservesign() denormal_fpenv(float: ieee|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__ieee_positivezero() #72 {
+define void @denormal_fpenv_float__ieee_positivezero() denormal_fpenv(float: ieee|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__ieee_dynamic() #73 {
+define void @denormal_fpenv_float__ieee_dynamic() denormal_fpenv(float: ieee|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__preservesign_ieee() #74 {
+define void @denormal_fpenv_float__preservesign_ieee() denormal_fpenv(float: preservesign|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__preservesign_preservesign() #75 {
+define void @denormal_fpenv_float__preservesign_preservesign() denormal_fpenv(float: preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__preservesign_positivezero() #76 {
+define void @denormal_fpenv_float__preservesign_positivezero() denormal_fpenv(float: preservesign|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__preservesign_dynamic() #77 {
+define void @denormal_fpenv_float__preservesign_dynamic() denormal_fpenv(float: preservesign|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__positivezero_ieee() #78 {
+define void @denormal_fpenv_float__positivezero_ieee() denormal_fpenv(float: positivezero|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__positivezero_preservesign() #79 {
+define void @denormal_fpenv_float__positivezero_preservesign() denormal_fpenv(float: positivezero|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__positivezero_positivezero() #80 {
+define void @denormal_fpenv_float__positivezero_positivezero() denormal_fpenv(float: positivezero|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__positivezero_dynamix() #81 {
+define void @denormal_fpenv_float__positivezero_dynamix() denormal_fpenv(float: positivezero|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__dynamic_ieee() #82 {
+define void @denormal_fpenv_float__dynamic_ieee() denormal_fpenv(float: dynamic|ieee) {
+  ret void
+}
+
+; Function Attrs: denormal_fpenv(float: dynamic|preservesign)
+define void @denormal_fpenv_float__dynamic_preservesign() denormal_fpenv(float: dynamic|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__dynamic_positivezero() #84 {
+define void @denormal_fpenv_float__dynamic_positivezero() denormal_fpenv(float: dynamic|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv_float__dynamic_dynamic() #85 {
+define void @denormal_fpenv_float__dynamic_dynamic() denormal_fpenv(float: dynamic|dynamic) {
+  ret void
+}
+; CHECK: define void @denormal_fpenv__ieee_ieee_float_ieee_ieee() #55 {
+define void @denormal_fpenv__ieee_ieee_float_ieee_ieee() denormal_fpenv(ieee|ieee, float: ieee|ieee) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__ieee_ieee_float_preservesign_preservesign() #75 {
+define void @denormal_fpenv__ieee_ieee_float_preservesign_preservesign() denormal_fpenv(ieee|ieee, float: preservesign|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__preservesign_preservesign_float_preservesign_preservesign() #60 {
+define void @denormal_fpenv__preservesign_preservesign_float_preservesign_preservesign() denormal_fpenv(preservesign|preservesign, float: preservesign|preservesign) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__positivezero_positivezero_float_positivezero_positivezero() #65 {
+define void @denormal_fpenv__positivezero_positivezero_float_positivezero_positivezero() denormal_fpenv(positivezero|positivezero, float: positivezero|positivezero) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__dynamic_dynamic_float_dynamic_dynamic() #70 {
+define void @denormal_fpenv__dynamic_dynamic_float_dynamic_dynamic() denormal_fpenv(dynamic|dynamic, float: dynamic|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__ieee_ieee_float_dynamic_dynamic() #85 {
+define void @denormal_fpenv__ieee_ieee_float_dynamic_dynamic() denormal_fpenv(ieee|ieee, float: dynamic|dynamic) {
+  ret void
+}
+
+; CHECK: define void @denormal_fpenv__preservesign_preservesign_float_dynamic_dynamic() #86 {
+define void @denormal_fpenv__preservesign_preservesign_float_dynamic_dynamic() denormal_fpenv(preservesign|preservesign, float: dynamic|dynamic) {
+  ret void
+}
+
 ; CHECK: attributes #0 = { alignstack=4 }
 ; CHECK: attributes #1 = { alignstack=8 }
 ; CHECK: attributes #2 = { alwaysinline }
@@ -2292,7 +2486,39 @@ define float @nofpclass_callsites(float %arg, { float } %arg1) {
 ; CHECK: attributes #52 = { sanitize_realtime }
 ; CHECK: attributes #53 = { sanitize_realtime_blocking }
 ; CHECK: attributes #54 = { sanitize_alloc_token }
-; CHECK: attributes #55 = { builtin }
+; CHECK: attributes #55 = { denormal_fpenv(ieee) }
+; CHECK: attributes #56 = { denormal_fpenv(ieee|preservesign) }
+; CHECK: attributes #57 = { denormal_fpenv(ieee|positivezero) }
+; CHECK: attributes #58 = { denormal_fpenv(ieee|dynamic) }
+; CHECK: attributes #59 = { denormal_fpenv(preservesign|ieee) }
+; CHECK: attributes #60 = { denormal_fpenv(preservesign) }
+; CHECK: attributes #61 = { denormal_fpenv(preservesign|positivezero) }
+; CHECK: attributes #62 = { denormal_fpenv(preservesign|dynamic) }
+; CHECK: attributes #63 = { denormal_fpenv(positivezero|ieee) }
+; CHECK: attributes #64 = { denormal_fpenv(positivezero|preservesign) }
+; CHECK: attributes #65 = { denormal_fpenv(positivezero) }
+; CHECK: attributes #66 = { denormal_fpenv(positivezero|dynamic) }
+; CHECK: attributes #67 = { denormal_fpenv(dynamic|ieee) }
+; CHECK: attributes #68 = { denormal_fpenv(dynamic|preservesign) }
+; CHECK: attributes #69 = { denormal_fpenv(dynamic|positivezero) }
+; CHECK: attributes #70 = { denormal_fpenv(dynamic) }
+; CHECK: attributes #71 = { denormal_fpenv(float: ieee|preservesign) }
+; CHECK: attributes #72 = { denormal_fpenv(float: ieee|positivezero) }
+; CHECK: attributes #73 = { denormal_fpenv(float: ieee|dynamic) }
+; CHECK: attributes #74 = { denormal_fpenv(float: preservesign|ieee) }
+; CHECK: attributes #75 = { denormal_fpenv(float: preservesign) }
+; CHECK: attributes #76 = { denormal_fpenv(float: preservesign|positivezero) }
+; CHECK: attributes #77 = { denormal_fpenv(float: preservesign|dynamic) }
+; CHECK: attributes #78 = { denormal_fpenv(float: positivezero|ieee) }
+; CHECK: attributes #79 = { denormal_fpenv(float: positivezero|preservesign) }
+; CHECK: attributes #80 = { denormal_fpenv(float: positivezero) }
+; CHECK: attributes #81 = { denormal_fpenv(float: positivezero|dynamic) }
+; CHECK: attributes #82 = { denormal_fpenv(float: dynamic|ieee) }
+; CHECK: attributes #83 = { denormal_fpenv(float: dynamic|preservesign) }
+; CHECK: attributes #84 = { denormal_fpenv(float: dynamic|positivezero) }
+; CHECK: attributes #85 = { denormal_fpenv(float: dynamic) }
+; CHECK: attributes #86 = { denormal_fpenv(preservesign, float: dynamic) }
+; CHECK: attributes #87 = { builtin }
 
 ;; Metadata
 

diff  --git a/llvm/test/CodeGen/AArch64/sqrt-fastmath.ll b/llvm/test/CodeGen/AArch64/sqrt-fastmath.ll
index e29993d02935a..241621b51eba8 100644
--- a/llvm/test/CodeGen/AArch64/sqrt-fastmath.ll
+++ b/llvm/test/CodeGen/AArch64/sqrt-fastmath.ll
@@ -671,4 +671,4 @@ define double @sqrt_simplify_before_recip_4_uses(double %x, ptr %p1, ptr %p2, pt
   ret double %sqrt_fast
 }
 
-attributes #0 = { "denormal-fp-math"="ieee" }
+attributes #0 = { denormal_fpenv(ieee) }

diff  --git a/llvm/test/CodeGen/AArch64/stack-tagging-ex-1.ll b/llvm/test/CodeGen/AArch64/stack-tagging-ex-1.ll
index 1b846cbb0a222..013040ebe9a7c 100644
--- a/llvm/test/CodeGen/AArch64/stack-tagging-ex-1.ll
+++ b/llvm/test/CodeGen/AArch64/stack-tagging-ex-1.ll
@@ -65,6 +65,6 @@ declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
 ; Function Attrs: argmemonly nounwind willreturn
 declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1
 
-attributes #0 = { sanitize_memtag "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+mte,+neon,+v8.5a" "use-soft-float"="false" }
+attributes #0 = { sanitize_memtag "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+mte,+neon,+v8.5a" "use-soft-float"="false" }
 attributes #1 = { argmemonly nounwind willreturn }
 attributes #2 = { nounwind }

diff  --git a/llvm/test/CodeGen/AArch64/stack-tagging-untag-placement.ll b/llvm/test/CodeGen/AArch64/stack-tagging-untag-placement.ll
index 59b6ec7011f6b..5a05cb99c83b6 100644
--- a/llvm/test/CodeGen/AArch64/stack-tagging-untag-placement.ll
+++ b/llvm/test/CodeGen/AArch64/stack-tagging-untag-placement.ll
@@ -79,6 +79,6 @@ declare void @llvm.lifetime.start.p0(ptr nocapture) #1
 
 declare void @llvm.lifetime.end.p0(ptr nocapture) #1
 
-attributes #0 = { sanitize_memtag "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+mte,+neon,+v8.5a" "use-soft-float"="false" }
+attributes #0 = { sanitize_memtag "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+mte,+neon,+v8.5a" "use-soft-float"="false" }
 attributes #1 = { nounwind }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fdiv.f32.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fdiv.f32.ll
index 5db0f7f601e4f..717f0e0bb5d61 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fdiv.f32.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fdiv.f32.ll
@@ -1,23 +1,26 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=tahiti -denormal-fp-math=ieee < %s | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX6-IEEE,GFX6-IEEE-FASTFMA %s
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=tahiti -denormal-fp-math=preserve-sign < %s | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX6-FLUSH,GFX6-FLUSH-FASTFMA %s
+; RUN: sed 's/DEFAULT_MODE/ieee/g' %s > %t.ieee
+; RUN: sed 's/DEFAULT_MODE/preservesign/g' %s > %t.preservesign
 
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=pitcairn -denormal-fp-math=ieee < %s | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX6-IEEE,GFX6-IEEE-SLOWFMA %s
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=pitcairn -denormal-fp-math=preserve-sign < %s | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX6-FLUSH,GFX6-FLUSH-SLOWFMA %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=tahiti < %t.ieee | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX6-IEEE,GFX6-IEEE-FASTFMA %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=tahiti < %t.preservesign | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX6-FLUSH,GFX6-FLUSH-FASTFMA %s
 
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=fiji -denormal-fp-math=ieee < %s | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX89-IEEE %s
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=fiji -denormal-fp-math=preserve-sign < %s | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX89-FLUSH %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=pitcairn < %t.ieee | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX6-IEEE,GFX6-IEEE-SLOWFMA %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=pitcairn < %t.preservesign | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX6-FLUSH,GFX6-FLUSH-SLOWFMA %s
 
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx900 -denormal-fp-math=ieee < %s | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX89-IEEE %s
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx900 -denormal-fp-math=preserve-sign < %s | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX89-FLUSH %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=fiji < %t.ieee | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX89-IEEE %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=fiji < %t.preservesign | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX89-FLUSH %s
 
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1010 -denormal-fp-math=ieee < %s | FileCheck -check-prefixes=GFX10,GFX10-IEEE %s
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1010 -denormal-fp-math=preserve-sign < %s | FileCheck -check-prefixes=GFX10,GFX10-FLUSH %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx900 < %t.ieee | FileCheck -check-prefixes=GCN,GCN-IEEE,GFX89-IEEE %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx900 < %t.preservesign | FileCheck -check-prefixes=GCN,GCN-FLUSH,GFX89-FLUSH %s
 
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1100 -denormal-fp-math=ieee < %s | FileCheck -check-prefixes=GFX11,GFX11-IEEE %s
-; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1100 -denormal-fp-math=preserve-sign < %s | FileCheck -check-prefixes=GFX11,GFX11-FLUSH %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1010 < %t.ieee | FileCheck -check-prefixes=GFX10,GFX10-IEEE %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1010 < %t.preservesign | FileCheck -check-prefixes=GFX10,GFX10-FLUSH %s
 
-define float @v_fdiv_f32(float %a, float %b) {
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1100 < %t.ieee | FileCheck -check-prefixes=GFX11,GFX11-IEEE %s
+; RUN: llc -global-isel -mtriple=amdgcn -mcpu=gfx1100 < %t.preservesign | FileCheck -check-prefixes=GFX11,GFX11-FLUSH %s
+
+define float @v_fdiv_f32(float %a, float %b) #1 {
 ; GFX6-IEEE-FASTFMA-LABEL: v_fdiv_f32:
 ; GFX6-IEEE-FASTFMA:       ; %bb.0:
 ; GFX6-IEEE-FASTFMA-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -406,7 +409,7 @@ define float @v_fdiv_f32_dynamic_denorm(float %a, float %b) #0 {
   ret float %fdiv
 }
 
-define float @v_fdiv_f32_afn(float %a, float %b) {
+define float @v_fdiv_f32_afn(float %a, float %b) #1 {
 ; GCN-LABEL: v_fdiv_f32_afn:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -432,7 +435,7 @@ define float @v_fdiv_f32_afn(float %a, float %b) {
   ret float %fdiv
 }
 
-define float @v_fdiv_f32_ulp25(float %a, float %b) {
+define float @v_fdiv_f32_ulp25(float %a, float %b) #1 {
 ; GFX6-IEEE-LABEL: v_fdiv_f32_ulp25:
 ; GFX6-IEEE:       ; %bb.0:
 ; GFX6-IEEE-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -634,7 +637,7 @@ define float @v_fdiv_f32_dynamic_25ulp(float %x, float %y) #0 {
   ret float %div
 }
 
-define float @v_rcp_f32(float %x) {
+define float @v_rcp_f32(float %x) #1 {
 ; GFX6-IEEE-FASTFMA-LABEL: v_rcp_f32:
 ; GFX6-IEEE-FASTFMA:       ; %bb.0:
 ; GFX6-IEEE-FASTFMA-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -818,7 +821,7 @@ define float @v_rcp_f32(float %x) {
   ret float %fdiv
 }
 
-define float @v_rcp_f32_arcp(float %x) {
+define float @v_rcp_f32_arcp(float %x) #1 {
 ; GFX6-IEEE-FASTFMA-LABEL: v_rcp_f32_arcp:
 ; GFX6-IEEE-FASTFMA:       ; %bb.0:
 ; GFX6-IEEE-FASTFMA-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1002,7 +1005,7 @@ define float @v_rcp_f32_arcp(float %x) {
   ret float %fdiv
 }
 
-define float @v_rcp_f32_arcp_afn(float %x) {
+define float @v_rcp_f32_arcp_afn(float %x) #1 {
 ; GCN-LABEL: v_rcp_f32_arcp_afn:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1024,7 +1027,7 @@ define float @v_rcp_f32_arcp_afn(float %x) {
   ret float %fdiv
 }
 
-define float @v_rcp_f32_ulp25(float %x) {
+define float @v_rcp_f32_ulp25(float %x) #1 {
 ; GFX6-IEEE-LABEL: v_rcp_f32_ulp25:
 ; GFX6-IEEE:       ; %bb.0:
 ; GFX6-IEEE-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1081,7 +1084,7 @@ define float @v_rcp_f32_ulp25(float %x) {
   ret float %fdiv
 }
 
-define float @v_fdiv_f32_afn_ulp25(float %a, float %b) {
+define float @v_fdiv_f32_afn_ulp25(float %a, float %b) #1 {
 ; GCN-LABEL: v_fdiv_f32_afn_ulp25:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1107,7 +1110,7 @@ define float @v_fdiv_f32_afn_ulp25(float %a, float %b) {
   ret float %fdiv
 }
 
-define float @v_fdiv_f32_arcp_ulp25(float %a, float %b) {
+define float @v_fdiv_f32_arcp_ulp25(float %a, float %b) #1 {
 ; GFX6-IEEE-LABEL: v_fdiv_f32_arcp_ulp25:
 ; GFX6-IEEE:       ; %bb.0:
 ; GFX6-IEEE-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1172,7 +1175,7 @@ define float @v_fdiv_f32_arcp_ulp25(float %a, float %b) {
   ret float %fdiv
 }
 
-define <2 x float> @v_fdiv_v2f32(<2 x float> %a, <2 x float> %b) {
+define <2 x float> @v_fdiv_v2f32(<2 x float> %a, <2 x float> %b) #1 {
 ; GFX6-IEEE-FASTFMA-LABEL: v_fdiv_v2f32:
 ; GFX6-IEEE-FASTFMA:       ; %bb.0:
 ; GFX6-IEEE-FASTFMA-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1488,7 +1491,7 @@ define <2 x float> @v_fdiv_v2f32(<2 x float> %a, <2 x float> %b) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_fdiv_v2f32_afn(<2 x float> %a, <2 x float> %b) {
+define <2 x float> @v_fdiv_v2f32_afn(<2 x float> %a, <2 x float> %b) #1 {
 ; GCN-LABEL: v_fdiv_v2f32_afn:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1519,7 +1522,7 @@ define <2 x float> @v_fdiv_v2f32_afn(<2 x float> %a, <2 x float> %b) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_fdiv_v2f32_ulp25(<2 x float> %a, <2 x float> %b) {
+define <2 x float> @v_fdiv_v2f32_ulp25(<2 x float> %a, <2 x float> %b) #1 {
 ; GFX6-IEEE-LABEL: v_fdiv_v2f32_ulp25:
 ; GFX6-IEEE:       ; %bb.0:
 ; GFX6-IEEE-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1651,7 +1654,7 @@ define <2 x float> @v_fdiv_v2f32_ulp25(<2 x float> %a, <2 x float> %b) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_rcp_v2f32(<2 x float> %x) {
+define <2 x float> @v_rcp_v2f32(<2 x float> %x) #1 {
 ; GFX6-IEEE-FASTFMA-LABEL: v_rcp_v2f32:
 ; GFX6-IEEE-FASTFMA:       ; %bb.0:
 ; GFX6-IEEE-FASTFMA-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -1967,7 +1970,7 @@ define <2 x float> @v_rcp_v2f32(<2 x float> %x) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_rcp_v2f32_arcp(<2 x float> %x) {
+define <2 x float> @v_rcp_v2f32_arcp(<2 x float> %x) #1 {
 ; GFX6-IEEE-FASTFMA-LABEL: v_rcp_v2f32_arcp:
 ; GFX6-IEEE-FASTFMA:       ; %bb.0:
 ; GFX6-IEEE-FASTFMA-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -2283,7 +2286,7 @@ define <2 x float> @v_rcp_v2f32_arcp(<2 x float> %x) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_rcp_v2f32_arcp_afn(<2 x float> %x) {
+define <2 x float> @v_rcp_v2f32_arcp_afn(<2 x float> %x) #1 {
 ; GCN-LABEL: v_rcp_v2f32_arcp_afn:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -2308,7 +2311,7 @@ define <2 x float> @v_rcp_v2f32_arcp_afn(<2 x float> %x) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_rcp_v2f32_ulp25(<2 x float> %x) {
+define <2 x float> @v_rcp_v2f32_ulp25(<2 x float> %x) #1 {
 ; GFX6-IEEE-LABEL: v_rcp_v2f32_ulp25:
 ; GFX6-IEEE:       ; %bb.0:
 ; GFX6-IEEE-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -2386,7 +2389,7 @@ define <2 x float> @v_rcp_v2f32_ulp25(<2 x float> %x) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_fdiv_v2f32_afn_ulp25(<2 x float> %a, <2 x float> %b) {
+define <2 x float> @v_fdiv_v2f32_afn_ulp25(<2 x float> %a, <2 x float> %b) #1 {
 ; GCN-LABEL: v_fdiv_v2f32_afn_ulp25:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -2417,7 +2420,7 @@ define <2 x float> @v_fdiv_v2f32_afn_ulp25(<2 x float> %a, <2 x float> %b) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_fdiv_v2f32_arcp_ulp25(<2 x float> %a, <2 x float> %b) {
+define <2 x float> @v_fdiv_v2f32_arcp_ulp25(<2 x float> %a, <2 x float> %b) #1 {
 ; GFX6-IEEE-LABEL: v_fdiv_v2f32_arcp_ulp25:
 ; GFX6-IEEE:       ; %bb.0:
 ; GFX6-IEEE-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -2507,7 +2510,7 @@ define <2 x float> @v_fdiv_v2f32_arcp_ulp25(<2 x float> %a, <2 x float> %b) {
   ret <2 x float> %fdiv
 }
 
-define <2 x float> @v_fdiv_v2f32_arcp_afn_ulp25(<2 x float> %a, <2 x float> %b) {
+define <2 x float> @v_fdiv_v2f32_arcp_afn_ulp25(<2 x float> %a, <2 x float> %b) #1 {
 ; GCN-LABEL: v_fdiv_v2f32_arcp_afn_ulp25:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -5229,7 +5232,8 @@ define float @v_fdiv_f32_dynamic_25ulp_nodenorm_y(float %x, float nofpclass(sub)
 
 !0 = !{float 2.500000e+00}
 
-attributes #0 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(DEFAULT_MODE, float: dynamic) }
+attributes #1 = { denormal_fpenv(DEFAULT_MODE) }
 
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GCN-IEEE: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fmamix-constant-bus-violation.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fmamix-constant-bus-violation.ll
index cc2a8ee11f180..3c847d79861c7 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fmamix-constant-bus-violation.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fmamix-constant-bus-violation.ll
@@ -103,4 +103,4 @@ define float @test_fmamix_constant_bus_violation_vss(i32 %val.0, i32 inreg %val.
   ret float %fma
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-atomics-gfx942.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-atomics-gfx942.ll
index 7766b3ad45962..624b20afeb370 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-atomics-gfx942.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-atomics-gfx942.ll
@@ -127,6 +127,6 @@ define void @flat_atomic_fadd_noret_v2f16_agent_offset(ptr %ptr, <2 x half> %val
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,ieee" }
+attributes #0 = { denormal_fpenv(float: ieee|ieee) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fp64-atomics-gfx90a.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fp64-atomics-gfx90a.ll
index 62ed4bbfe54e9..983059bf184fb 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fp64-atomics-gfx90a.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fp64-atomics-gfx90a.ll
@@ -2502,9 +2502,9 @@ main_body:
   ret double %ret
 }
 
-attributes #0 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(preservesign) }
 attributes #1 = { nounwind }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #2 = { nounwind denormal_fpenv(preservesign) }
 
 !0 = !{}
 !1 = !{i32 5, i32 6}

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/frem.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/frem.ll
index 62e97279b606a..61b87d19c6b6a 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/frem.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/frem.ll
@@ -3548,5 +3548,5 @@ define amdgpu_kernel void @frem_v2f64(ptr addrspace(1) %out, ptr addrspace(1) %i
    ret void
 }
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll
index 34bf3c21a8c71..e1c2b797390c2 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll
@@ -636,4 +636,4 @@ declare float @llvm.amdgcn.fmul.legacy(float, float) #1
 
 attributes #0 = { nounwind readnone speculatable willreturn }
 attributes #1 = { nounwind readnone speculatable }
-attributes #2 = { "denormal-fp-math-f32"="preserve-sign" }
+attributes #2 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/madmix-constant-bus-violation.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/madmix-constant-bus-violation.ll
index 52425323332dd..af0185915517d 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/madmix-constant-bus-violation.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/madmix-constant-bus-violation.ll
@@ -103,4 +103,4 @@ define float @test_fmamix_constant_bus_violation_vss(i32 %val.0, i32 inreg %val.
   ret float %fma
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/amdgpu-codegenprepare-fdiv.f64.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-codegenprepare-fdiv.f64.ll
index 1fecc2b613c4c..fa1ba6ceff58b 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-codegenprepare-fdiv.f64.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-codegenprepare-fdiv.f64.ll
@@ -767,8 +767,8 @@ define double @rsq_f64_assume_nonzero(double %x) {
   ret double %fdiv
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|dynamic) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
 
 !0 = !{float 2.500000e+00}
 ;.

diff  --git a/llvm/test/CodeGen/AMDGPU/amdpal-msgpack-denormal.ll b/llvm/test/CodeGen/AMDGPU/amdpal-msgpack-denormal.ll
index e16c94cc0c458..b77a3a4c3f504 100644
--- a/llvm/test/CodeGen/AMDGPU/amdpal-msgpack-denormal.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdpal-msgpack-denormal.ll
@@ -66,7 +66,7 @@ define amdgpu_vs half @vs_amdpal(half %arg0) #0 {
   ret half %add
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
 
 ; amdgpu.pal.metadata.msgpack represents this:
 ;

diff  --git a/llvm/test/CodeGen/AMDGPU/atomics-hw-remarks-gfx90a.ll b/llvm/test/CodeGen/AMDGPU/atomics-hw-remarks-gfx90a.ll
index 587157b5bc3af..232103a7035f4 100644
--- a/llvm/test/CodeGen/AMDGPU/atomics-hw-remarks-gfx90a.ll
+++ b/llvm/test/CodeGen/AMDGPU/atomics-hw-remarks-gfx90a.ll
@@ -91,6 +91,6 @@ main_body:
   ret void
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/clamp-modifier.ll b/llvm/test/CodeGen/AMDGPU/clamp-modifier.ll
index 638e4b01488a0..3937b1cb05d3e 100644
--- a/llvm/test/CodeGen/AMDGPU/clamp-modifier.ll
+++ b/llvm/test/CodeGen/AMDGPU/clamp-modifier.ll
@@ -2382,10 +2382,10 @@ declare <2 x half> @llvm.amdgcn.cvt.pkrtz(float, float) #1
 
 declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
-attributes #2 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="ieee,ieee" "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #2 = { nounwind denormal_fpenv(float: ieee|ieee) }
+attributes #3 = { nounwind denormal_fpenv(float: ieee|ieee) denormal_fpenv(preservesign) }
 
 !llvm.dbg.cu = !{!0}
 !llvm.module.flags = !{!2, !3}

diff  --git a/llvm/test/CodeGen/AMDGPU/clamp.ll b/llvm/test/CodeGen/AMDGPU/clamp.ll
index 0d3567faaa10c..ffd52c2704409 100644
--- a/llvm/test/CodeGen/AMDGPU/clamp.ll
+++ b/llvm/test/CodeGen/AMDGPU/clamp.ll
@@ -4831,10 +4831,10 @@ declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #1
 declare <2 x half> @llvm.minnum.v2f16(<2 x half>, <2 x half>) #1
 declare <2 x half> @llvm.maxnum.v2f16(<2 x half>, <2 x half>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
-attributes #2 = { nounwind "amdgpu-dx10-clamp"="false" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-nans-fp-math"="false" }
-attributes #3 = { nounwind "amdgpu-dx10-clamp"="true" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-nans-fp-math"="false" }
-attributes #4 = { nounwind "amdgpu-dx10-clamp"="false" "denormal-fp-math-f32"="preserve-sign,preserve-sign" "no-nans-fp-math"="false" }
+attributes #2 = { nounwind "amdgpu-dx10-clamp"="false" denormal_fpenv(float: preservesign) "no-nans-fp-math"="false" }
+attributes #3 = { nounwind "amdgpu-dx10-clamp"="true" denormal_fpenv(float: preservesign) "no-nans-fp-math"="false" }
+attributes #4 = { nounwind "amdgpu-dx10-clamp"="false" denormal_fpenv(float: preservesign) "no-nans-fp-math"="false" }
 attributes #5 = { nounwind readnone "amdgpu-ieee"="false" }
 attributes #6 = { nounwind "amdgpu-dx10-clamp"="false" }

diff  --git a/llvm/test/CodeGen/AMDGPU/dagcombine-fma-fmad.ll b/llvm/test/CodeGen/AMDGPU/dagcombine-fma-fmad.ll
index a7a9851cc5c26..28a18ec3845e0 100644
--- a/llvm/test/CodeGen/AMDGPU/dagcombine-fma-fmad.ll
+++ b/llvm/test/CodeGen/AMDGPU/dagcombine-fma-fmad.ll
@@ -439,7 +439,7 @@ declare i32 @llvm.amdgcn.s.buffer.load.i32(<4 x i32>, i32, i32 immarg) #3
 ; Function Attrs: nounwind readnone willreturn
 declare <3 x i32> @llvm.amdgcn.s.buffer.load.v3i32(<4 x i32>, i32, i32 immarg) #3
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
 attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
 attributes #2 = { nounwind readnone speculatable willreturn }
 attributes #3 = { nounwind readonly willreturn }

diff  --git a/llvm/test/CodeGen/AMDGPU/default-fp-mode.ll b/llvm/test/CodeGen/AMDGPU/default-fp-mode.ll
index b63fff38f34f6..e9146c0d241ba 100644
--- a/llvm/test/CodeGen/AMDGPU/default-fp-mode.ll
+++ b/llvm/test/CodeGen/AMDGPU/default-fp-mode.ll
@@ -172,17 +172,18 @@ declare void @llvm.amdgcn.kill(i1)
 
 attributes #0 = { nounwind "target-cpu"="tahiti" }
 attributes #1 = { nounwind "target-cpu"="fiji" }
-attributes #2 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-attributes #4 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #5 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #6 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #7 = { nounwind "denormal-fp-math-f32"="ieee,ieee" "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #8 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #9 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #10 = { nounwind "denormal-fp-math"="preserve-sign,ieee" }
-attributes #11 = { nounwind "denormal-fp-math"="ieee,preserve-sign" }
-attributes #12 = { nounwind "denormal-fp-math-f32"="ieee,preserve-sign" "denormal-fp-math"="ieee,ieee" }
-attributes #13 = { nounwind "denormal-fp-math-f32"="preserve-sign,ieee" "denormal-fp-math"="ieee,ieee" }
-attributes #14 = { nounwind "denormal-fp-math"="ieee,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" }
-attributes #15 = { nounwind "denormal-fp-math"="preserve-sign,ieee" "denormal-fp-math-f32"="ieee,ieee" }
+
+attributes #2 = { nounwind denormal_fpenv(ieee) }
+attributes #3 = { nounwind denormal_fpenv(float:ieee|ieee) }
+attributes #4 = { nounwind denormal_fpenv(ieee) }
+attributes #5 = { nounwind denormal_fpenv(preservesign) }
+attributes #6 = { nounwind denormal_fpenv(ieee) }
+attributes #7 = { nounwind denormal_fpenv(preservesign, float:ieee) }
+attributes #8 = { nounwind denormal_fpenv(ieee) }
+attributes #9 = { nounwind denormal_fpenv(float:preservesign|preservesign) }
+attributes #10 = { nounwind denormal_fpenv(preservesign|ieee) }
+attributes #11 = { nounwind denormal_fpenv(ieee|preservesign) }
+attributes #12 = { nounwind denormal_fpenv(ieee, float:ieee|preservesign) }
+attributes #13 = { nounwind denormal_fpenv(ieee, float:preservesign|ieee) }
+attributes #14 = { nounwind denormal_fpenv(ieee|preservesign, float:ieee) }
+attributes #15 = { nounwind denormal_fpenv(preservesign|ieee, float:ieee) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fabs-known-signbit-combine-fast-fdiv-lowering.ll b/llvm/test/CodeGen/AMDGPU/fabs-known-signbit-combine-fast-fdiv-lowering.ll
index 750f390e79110..f6886d7499d73 100644
--- a/llvm/test/CodeGen/AMDGPU/fabs-known-signbit-combine-fast-fdiv-lowering.ll
+++ b/llvm/test/CodeGen/AMDGPU/fabs-known-signbit-combine-fast-fdiv-lowering.ll
@@ -314,6 +314,6 @@ declare float @llvm.maximumnum.f32(float, float) #0
 declare float @llvm.minimumnum.f32(float, float) #0
 
 attributes #0 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
-attributes #1 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { denormal_fpenv(float: preservesign) }
 
 !0 = !{float 2.500000e+00}

diff  --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
index a62673679eb8e..a87570ef5d848 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
@@ -948,4 +948,4 @@ declare float @llvm.amdgcn.exp2.f32(float) #0
 
 attributes #0 = { nounwind readnone }
 attributes #1 = { "no-nans-fp-math"="true" }
-attributes #2 = { "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" }
+attributes #2 = { denormal_fpenv(preservesign, float: ieee) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize.bf16.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize.bf16.ll
index b23645839944e..933d427243d76 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize.bf16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize.bf16.ll
@@ -1389,6 +1389,6 @@ define <64 x bfloat> @v_test_canonicalize_var_v64bf16(<64 x bfloat> %val) #1 {
 }
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #2 = { nounwind denormal_fpenv(preservesign) }
+attributes #3 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize.f16.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize.f16.ll
index 805b1421f94d0..06212cf96cfa6 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize.f16.ll
@@ -4040,6 +4040,6 @@ define <64 x half> @v_test_canonicalize_var_v64f16(<64 x half> %val) #1 {
 }
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #2 = { nounwind denormal_fpenv(preservesign) }
+attributes #3 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize.ll
index 78ea7fde4bf28..53b6ceb45531e 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize.ll
@@ -5519,13 +5519,13 @@ define <4 x double> @v_test_canonicalize_v4f64(<4 x double> %arg) #1 {
 }
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #3 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #4 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #5 = { nounwind "denormal-fp-math-f32"="dynamic,dynamic" }
-attributes #6 = { nounwind "denormal-fp-math-f32"="dynamic,ieee" }
-attributes #7 = { nounwind "denormal-fp-math-f32"="ieee,dynamic" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #2 = { nounwind denormal_fpenv(preservesign) }
+attributes #3 = { nounwind denormal_fpenv(ieee|ieee) }
+attributes #4 = { nounwind denormal_fpenv(preservesign) }
+attributes #5 = { nounwind denormal_fpenv(float: dynamic|dynamic) }
+attributes #6 = { nounwind denormal_fpenv(float: dynamic|ieee) }
+attributes #7 = { nounwind denormal_fpenv(float: ieee|dynamic) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX11-GISEL-FAKE16: {{.*}}
 ; GFX11-GISEL-TRUE16: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/fdiv-nofpexcept.ll b/llvm/test/CodeGen/AMDGPU/fdiv-nofpexcept.ll
index 2d6ae31f8e585..5874c72df41ab 100644
--- a/llvm/test/CodeGen/AMDGPU/fdiv-nofpexcept.ll
+++ b/llvm/test/CodeGen/AMDGPU/fdiv-nofpexcept.ll
@@ -66,4 +66,4 @@ entry:
   ret float %fdiv
 }
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fdiv.ll b/llvm/test/CodeGen/AMDGPU/fdiv.ll
index c510c40c8536c..0f71cc48a57cf 100644
--- a/llvm/test/CodeGen/AMDGPU/fdiv.ll
+++ b/llvm/test/CodeGen/AMDGPU/fdiv.ll
@@ -8044,8 +8044,8 @@ define float @v_fdiv_f32_daz_25ulp_nodenorm_y(float %x, float nofpclass(sub) %y)
   ret float %div
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="ieee,ieee" }
-attributes #2 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { denormal_fpenv(float: ieee|ieee) }
+attributes #2 = { denormal_fpenv(float: dynamic|dynamic) }
 
 !0 = !{float 2.500000e+00}

diff  --git a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fadd.ll b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fadd.ll
index 262b6c53fa2f8..bd272f5d210b3 100644
--- a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fadd.ll
+++ b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fadd.ll
@@ -22387,6 +22387,6 @@ define void @flat_agent_atomic_fadd_noret_v2bf16__amdgpu_no_fine_grained_memory_
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmax.ll b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmax.ll
index 3919ba4e2b1c2..24e98b67cfc76 100644
--- a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmax.ll
+++ b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmax.ll
@@ -19633,6 +19633,6 @@ define void @flat_system_atomic_fmax_noret_v2bf16__offset12b_pos__amdgpu_no_fine
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmin.ll b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmin.ll
index 858ff79ade52f..c3c46ddeadcf7 100644
--- a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmin.ll
+++ b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fmin.ll
@@ -19633,6 +19633,6 @@ define void @flat_system_atomic_fmin_noret_v2bf16__offset12b_pos__amdgpu_no_fine
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fsub.ll b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fsub.ll
index 0fb799ea66461..5b1f19ab89cdd 100644
--- a/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fsub.ll
+++ b/llvm/test/CodeGen/AMDGPU/flat-atomicrmw-fsub.ll
@@ -19056,4 +19056,4 @@ define void @flat_system_atomic_fsub_noret_v2bf16__offset12b_pos(ptr %ptr, <2 x
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fmaxnum.ll b/llvm/test/CodeGen/AMDGPU/fmaxnum.ll
index be9027bdef823..f538cba5ed356 100644
--- a/llvm/test/CodeGen/AMDGPU/fmaxnum.ll
+++ b/llvm/test/CodeGen/AMDGPU/fmaxnum.ll
@@ -218,5 +218,5 @@ declare <8 x float> @llvm.maxnum.v8f32(<8 x float>, <8 x float>) #1
 declare <16 x float> @llvm.maxnum.v16f32(<16 x float>, <16 x float>) #1
 declare double @llvm.maxnum.f64(double, double)
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }

diff  --git a/llvm/test/CodeGen/AMDGPU/fminnum.f64.ll b/llvm/test/CodeGen/AMDGPU/fminnum.f64.ll
index 92597594123ee..d15a144984ddc 100644
--- a/llvm/test/CodeGen/AMDGPU/fminnum.f64.ll
+++ b/llvm/test/CodeGen/AMDGPU/fminnum.f64.ll
@@ -113,5 +113,5 @@ define amdgpu_kernel void @test_fmin_v16f64(ptr addrspace(1) %out, <16 x double>
 }
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(ieee|ieee) }
+attributes #2 = { nounwind denormal_fpenv(preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fminnum.ll b/llvm/test/CodeGen/AMDGPU/fminnum.ll
index 22bd13f2379cf..95b315b8baa59 100644
--- a/llvm/test/CodeGen/AMDGPU/fminnum.ll
+++ b/llvm/test/CodeGen/AMDGPU/fminnum.ll
@@ -225,5 +225,5 @@ declare <4 x float> @llvm.minnum.v4f32(<4 x float>, <4 x float>) #1
 declare <8 x float> @llvm.minnum.v8f32(<8 x float>, <8 x float>) #1
 declare <16 x float> @llvm.minnum.v16f32(<16 x float>, <16 x float>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }

diff  --git a/llvm/test/CodeGen/AMDGPU/fneg-combines.f16.ll b/llvm/test/CodeGen/AMDGPU/fneg-combines.f16.ll
index fb4289144ca1f..13e4206ab7f57 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-combines.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-combines.f16.ll
@@ -6106,7 +6106,7 @@ declare <2 x half> @llvm.fma.v2f16(<2 x half>, <2 x half>, <2 x half>)
 declare half @llvm.fmuladd.f16(half, half, half) #1
 declare <4 x half> @llvm.fmuladd.v4f16(<4 x half>, <4 x half>, <4 x half>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
 attributes #2 = { nounwind }
-attributes #4 = { nounwind "amdgpu-ieee"="false" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #4 = { nounwind "amdgpu-ieee"="false" denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fneg-combines.legal.f16.ll b/llvm/test/CodeGen/AMDGPU/fneg-combines.legal.f16.ll
index 69d1ee3f533a6..e50489cdeba54 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-combines.legal.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-combines.legal.f16.ll
@@ -157,11 +157,11 @@ declare half @llvm.arithmetic.fence.f16(half) #1
 declare float @llvm.amdgcn.interp.p1.f16(float, i32, i32, i1, i32) #0
 declare half @llvm.amdgcn.interp.p2.f16(float, float, i32, i32, i1, i32) #0
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
 attributes #2 = { nounwind }
 attributes #3 = { nounwind "no-signed-zeros-fp-math"="true" }
-attributes #4 = { nounwind "amdgpu-ieee"="false" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #4 = { nounwind "amdgpu-ieee"="false" denormal_fpenv(float: preservesign) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GCN-NSZ: {{.*}}
 ; GCN-SAFE: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/fneg-combines.ll b/llvm/test/CodeGen/AMDGPU/fneg-combines.ll
index 4a5eae770a674..cfa5247267559 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-combines.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-combines.ll
@@ -8323,6 +8323,6 @@ declare float @llvm.amdgcn.fmul.legacy(float, float) #1
 declare float @llvm.amdgcn.interp.p1(float, i32, i32, i32) #0
 declare float @llvm.amdgcn.interp.p2(float, float, i32, i32, i32) #0
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
 attributes #2 = { nounwind }

diff  --git a/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll b/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
index 3e7d67a40b12b..e1fef9083e132 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
@@ -4657,7 +4657,7 @@ declare half @llvm.maxnum.f16(half, half) #1
 declare half @llvm.amdgcn.sin.f16(half) #1
 declare half @llvm.amdgcn.rcp.f16(half) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
 attributes #2 = { nounwind }
-attributes #4 = { nounwind "amdgpu-ieee"="false" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #4 = { nounwind "amdgpu-ieee"="false" denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fp-atomics-gfx942.ll b/llvm/test/CodeGen/AMDGPU/fp-atomics-gfx942.ll
index 24c6303a6306c..72f4660630241 100644
--- a/llvm/test/CodeGen/AMDGPU/fp-atomics-gfx942.ll
+++ b/llvm/test/CodeGen/AMDGPU/fp-atomics-gfx942.ll
@@ -268,6 +268,6 @@ define <2 x i16> @local_atomic_fadd_v2bf16_rtn(ptr addrspace(3) %ptr, <2 x i16>
   ret <2 x i16> %ret
 }
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,ieee" }
+attributes #0 = { denormal_fpenv(float: ieee|ieee) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/fp64-atomics-gfx90a.ll b/llvm/test/CodeGen/AMDGPU/fp64-atomics-gfx90a.ll
index 82af553f60059..4775978f3ff02 100644
--- a/llvm/test/CodeGen/AMDGPU/fp64-atomics-gfx90a.ll
+++ b/llvm/test/CodeGen/AMDGPU/fp64-atomics-gfx90a.ll
@@ -2427,11 +2427,11 @@ main_body:
   ret double %ret
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { nounwind }
-attributes #2 = { "denormal-fp-math"="ieee,ieee" }
-attributes #3 = { "denormal-fp-math"="ieee,ieee" }
-attributes #4 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #2 = { denormal_fpenv(ieee|ieee) }
+attributes #3 = { denormal_fpenv(ieee|ieee) }
+attributes #4 = { denormal_fpenv(preservesign) }
 
 !0 = !{}
 !1 = !{i32 5, i32 6}

diff  --git a/llvm/test/CodeGen/AMDGPU/frem.ll b/llvm/test/CodeGen/AMDGPU/frem.ll
index 48705a4d80d4c..a9beee69d5cb3 100644
--- a/llvm/test/CodeGen/AMDGPU/frem.ll
+++ b/llvm/test/CodeGen/AMDGPU/frem.ll
@@ -18903,4 +18903,4 @@ define amdgpu_kernel void @frem_v2f64_const(ptr addrspace(1) %out) #0 {
 
 
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/fsub-as-fneg-src-modifier.ll b/llvm/test/CodeGen/AMDGPU/fsub-as-fneg-src-modifier.ll
index 2629fb3ff0a73..1004afc9a1ac1 100644
--- a/llvm/test/CodeGen/AMDGPU/fsub-as-fneg-src-modifier.ll
+++ b/llvm/test/CodeGen/AMDGPU/fsub-as-fneg-src-modifier.ll
@@ -1315,9 +1315,9 @@ declare i1 @llvm.amdgcn.class.f16(half, i32)
 declare float @llvm.amdgcn.interp.p1(float, i32, i32, i32)
 declare float @llvm.amdgcn.interp.p1.f16(float, i32, i32, i1, i32)
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #3 = { "denormal-fp-math"="ieee,ieee" strictfp }
-attributes #4 = { "denormal-fp-math"="preserve-sign,preserve-sign" strictfp }
-attributes #5 = { "denormal-fp-math"="dynamic,dynamic" strictfp }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(dynamic) }
+attributes #3 = { denormal_fpenv(ieee|ieee) strictfp }
+attributes #4 = { denormal_fpenv(preservesign) strictfp }
+attributes #5 = { denormal_fpenv(dynamic) strictfp }

diff  --git a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd-wrong-subtarget.ll b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd-wrong-subtarget.ll
index 6b02e6b05f1b7..ab242eba6f19b 100644
--- a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd-wrong-subtarget.ll
+++ b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd-wrong-subtarget.ll
@@ -74,6 +74,6 @@ define amdgpu_kernel void @global_atomic_fadd_noret_f32_wrong_subtarget(ptr addr
   ret void
 }
 
-attributes #1 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" "target-cpu"="gfx803" "target-features"="+atomic-fadd-no-rtn-insts" }
+attributes #1 = { denormal_fpenv(float: preservesign) "target-cpu"="gfx803" "target-features"="+atomic-fadd-no-rtn-insts" }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd.ll b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd.ll
index fcf640f65a95a..15e9eda90d04e 100644
--- a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd.ll
+++ b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fadd.ll
@@ -28004,6 +28004,6 @@ define void @global_agent_atomic_fadd_noret_v2bf16__maybe_remote(ptr addrspace(1
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmax.ll b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmax.ll
index 041a77c960f04..6e1d5293c54eb 100644
--- a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmax.ll
+++ b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmax.ll
@@ -19776,6 +19776,6 @@ define void @global_system_atomic_fmax_noret_v2bf16__offset12b_pos__amdgpu_no_fi
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmin.ll b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmin.ll
index e13a16b762d6d..2de8873d185d8 100644
--- a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmin.ll
+++ b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fmin.ll
@@ -19776,6 +19776,6 @@ define void @global_system_atomic_fmin_noret_v2bf16__offset12b_pos__amdgpu_no_fi
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fsub.ll b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fsub.ll
index 0229a482ca17b..e565d70fe93c7 100644
--- a/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fsub.ll
+++ b/llvm/test/CodeGen/AMDGPU/global-atomicrmw-fsub.ll
@@ -20138,4 +20138,4 @@ define void @global_system_atomic_fsub_noret_v2bf16__offset12b_pos(ptr addrspace
 }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/global-atomics-fp-wrong-subtarget.ll b/llvm/test/CodeGen/AMDGPU/global-atomics-fp-wrong-subtarget.ll
index bd9aa0f5a454a..74f85467f8872 100644
--- a/llvm/test/CodeGen/AMDGPU/global-atomics-fp-wrong-subtarget.ll
+++ b/llvm/test/CodeGen/AMDGPU/global-atomics-fp-wrong-subtarget.ll
@@ -33,6 +33,6 @@ define amdgpu_kernel void @global_atomic_fadd_noret_f32_wrong_subtarget(ptr addr
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" "target-features"="+atomic-fadd-no-rtn-insts" }
+attributes #0 = { denormal_fpenv(float: preservesign) "target-features"="+atomic-fadd-no-rtn-insts" }
 
 !0 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global_atomic_optimizer_fp_rtn.ll b/llvm/test/CodeGen/AMDGPU/global_atomic_optimizer_fp_rtn.ll
index 94d9092cda2b1..8a61b8f5eeda5 100644
--- a/llvm/test/CodeGen/AMDGPU/global_atomic_optimizer_fp_rtn.ll
+++ b/llvm/test/CodeGen/AMDGPU/global_atomic_optimizer_fp_rtn.ll
@@ -1946,6 +1946,6 @@ define amdgpu_ps double @global_atomic_fadd_double_div_address_div_value_system_
   ret double %result
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { strictfp "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { strictfp denormal_fpenv(float: preservesign) }
 attributes #2 = { strictfp }

diff  --git a/llvm/test/CodeGen/AMDGPU/global_atomics_optimizer_fp_no_rtn.ll b/llvm/test/CodeGen/AMDGPU/global_atomics_optimizer_fp_no_rtn.ll
index af38d6e27f6ff..8587ab3fdb3af 100644
--- a/llvm/test/CodeGen/AMDGPU/global_atomics_optimizer_fp_no_rtn.ll
+++ b/llvm/test/CodeGen/AMDGPU/global_atomics_optimizer_fp_no_rtn.ll
@@ -1638,6 +1638,6 @@ define amdgpu_ps void @global_atomic_fadd_double_div_address_div_value_system_sc
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { strictfp "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { strictfp denormal_fpenv(float: preservesign) }
 attributes #2 = { strictfp }

diff  --git a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fadd.ll b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fadd.ll
index a6a886dc321ce..103ca48a7dc5f 100644
--- a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fadd.ll
+++ b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fadd.ll
@@ -12931,8 +12931,8 @@ define amdgpu_kernel void @global_atomic_fadd_uni_address_uni_value_system_scope
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign"  }
-attributes #1 = { strictfp "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign)  }
+attributes #1 = { strictfp denormal_fpenv(float: preservesign) }
 attributes #2 = { strictfp }
 
 !1 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmax.ll b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmax.ll
index e62d6c593215b..2160976599dd7 100644
--- a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmax.ll
+++ b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmax.ll
@@ -7413,7 +7413,7 @@ define amdgpu_kernel void @global_atomic_fmax_uni_address_uni_value_system_scope
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
 
 !1 = !{}
 

diff  --git a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmin.ll b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmin.ll
index 1c14ff65dcbb6..029fb9c118344 100644
--- a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmin.ll
+++ b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fmin.ll
@@ -7413,6 +7413,6 @@ define amdgpu_kernel void @global_atomic_fmin_uni_address_uni_value_system_scope
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
 
 !1 = !{}

diff  --git a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fsub.ll b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fsub.ll
index b97c3cdf32d12..3250d95bb0b7d 100644
--- a/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fsub.ll
+++ b/llvm/test/CodeGen/AMDGPU/global_atomics_scan_fsub.ll
@@ -12565,7 +12565,7 @@ define amdgpu_kernel void @global_atomic_fsub_double_uni_address_div_value_defau
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { strictfp "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { strictfp denormal_fpenv(float: preservesign) }
 attributes #2 = { strictfp }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/hsa-fp-mode.ll b/llvm/test/CodeGen/AMDGPU/hsa-fp-mode.ll
index c24c3f8f6159b..7956670a16530 100644
--- a/llvm/test/CodeGen/AMDGPU/hsa-fp-mode.ll
+++ b/llvm/test/CodeGen/AMDGPU/hsa-fp-mode.ll
@@ -92,10 +92,10 @@ define amdgpu_kernel void @test_no_ieee_mode_no_dx10_clamp_vi(ptr addrspace(1) %
 
 attributes #0 = { nounwind "target-cpu"="kaveri" }
 attributes #1 = { nounwind "target-cpu"="fiji" }
-attributes #2 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="ieee,ieee" "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #4 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #5 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #2 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #3 = { nounwind denormal_fpenv(preservesign, float: ieee) }
+attributes #4 = { nounwind denormal_fpenv(ieee) }
+attributes #5 = { nounwind denormal_fpenv(preservesign) }
 attributes #6 = { nounwind "amdgpu-dx10-clamp"="false" "target-cpu"="fiji" }
 attributes #7 = { nounwind "amdgpu-ieee"="false" "target-cpu"="fiji" }
 attributes #8 = { nounwind "amdgpu-dx10-clamp"="false" "amdgpu-ieee"="false" "target-cpu"="fiji" }

diff  --git a/llvm/test/CodeGen/AMDGPU/known-never-snan.ll b/llvm/test/CodeGen/AMDGPU/known-never-snan.ll
index 5691fc8740a6b..078f3014fdc9b 100644
--- a/llvm/test/CodeGen/AMDGPU/known-never-snan.ll
+++ b/llvm/test/CodeGen/AMDGPU/known-never-snan.ll
@@ -667,7 +667,7 @@ declare float @llvm.amdgcn.rsq.f32(float) #1
 declare float @llvm.amdgcn.fract.f32(float) #1
 declare float @llvm.amdgcn.cubeid(float, float, float) #0
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone speculatable }
 
 !0 = !{float 2.500000e+00}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll
index c5daf21a43dee..d5cff22756b53 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll
@@ -107,4 +107,4 @@ declare float @llvm.amdgcn.fmul.legacy(float, float) #1
 
 attributes #0 = { nounwind }
 attributes #1 = { nounwind readnone }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign" "amdgpu-no-dispatch-id" "amdgpu-no-dispatch-ptr" "amdgpu-no-implicitarg-ptr" "amdgpu-no-lds-kernel-id" "amdgpu-no-queue-ptr" "amdgpu-no-workgroup-id-x" "amdgpu-no-workgroup-id-y" "amdgpu-no-workgroup-id-z" "amdgpu-no-workitem-id-x" "amdgpu-no-workitem-id-y" "amdgpu-no-workitem-id-z" }
+attributes #2 = { nounwind denormal_fpenv(preservesign) "amdgpu-no-dispatch-id" "amdgpu-no-dispatch-ptr" "amdgpu-no-implicitarg-ptr" "amdgpu-no-lds-kernel-id" "amdgpu-no-queue-ptr" "amdgpu-no-workgroup-id-x" "amdgpu-no-workgroup-id-y" "amdgpu-no-workgroup-id-z" "amdgpu-no-workitem-id-x" "amdgpu-no-workitem-id-y" "amdgpu-no-workitem-id-z" }

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.rcp.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.rcp.ll
index 477f0a610feec..130de84a9407d 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.rcp.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.rcp.ll
@@ -214,9 +214,9 @@ define amdgpu_kernel void @unsafe_amdgcn_sqrt_rsq_rcp_pat_f64(ptr addrspace(1) %
 }
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #2 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-attributes #4 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #2 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #3 = { nounwind denormal_fpenv(float: ieee|ieee) }
+attributes #4 = { nounwind denormal_fpenv(float: ieee|ieee) }
 
 !0 = !{float 2.500000e+00}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.exp.ll b/llvm/test/CodeGen/AMDGPU/llvm.exp.ll
index 1485e3f88f942..6e028f080b481 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.exp.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.exp.ll
@@ -7158,6 +7158,6 @@ declare <2 x half> @llvm.exp.v2f16(<2 x half>) #2
 declare <3 x half> @llvm.exp.v3f16(<3 x half>) #2
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #2
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #1 = { denormal_fpenv(float: dynamic|dynamic) }
 attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.exp10.ll b/llvm/test/CodeGen/AMDGPU/llvm.exp10.ll
index c4204417362a4..bf8197a4cd9cb 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.exp10.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.exp10.ll
@@ -7391,6 +7391,6 @@ declare <2 x half> @llvm.exp10.v2f16(<2 x half>) #2
 declare <3 x half> @llvm.exp10.v3f16(<3 x half>) #2
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #2
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #1 = { denormal_fpenv(float: dynamic|dynamic) }
 attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.exp2.ll b/llvm/test/CodeGen/AMDGPU/llvm.exp2.ll
index 21c7f56aa0816..caa771326d847 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.exp2.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.exp2.ll
@@ -3777,8 +3777,8 @@ declare <2 x half> @llvm.exp2.v2f16(<2 x half>) #2
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #2
 declare <3 x half> @llvm.exp2.v3f16(<3 x half>) #2
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #1 = { denormal_fpenv(float: dynamic|dynamic) }
 attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GCN-GISEL: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.bf16.ll b/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.bf16.ll
index 38a0140e36c33..d80fb6d8ea108 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.bf16.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.bf16.ll
@@ -3303,10 +3303,10 @@ declare <3 x i1> @llvm.is.fpclass.v3bf16(<3 x bfloat>, i32)
 declare <4 x i1> @llvm.is.fpclass.v4bf16(<4 x bfloat>, i32)
 
 ; Assume DAZ
-attributes #0 = { "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|preservesign) }
 
 ; Maybe daz
-attributes #1 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #1 = { denormal_fpenv(ieee|dynamic) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX10SELDAG: {{.*}}
 ; GFX11SELDAG: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.f16.ll b/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.f16.ll
index cc5f6f842625c..bc01298205901 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.is.fpclass.f16.ll
@@ -4428,10 +4428,10 @@ declare <3 x i1> @llvm.is.fpclass.v3f16(<3 x half>, i32)
 declare <4 x i1> @llvm.is.fpclass.v4f16(<4 x half>, i32)
 
 ; Assume DAZ
-attributes #0 = { "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|preservesign) }
 
 ; Maybe daz
-attributes #1 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #1 = { denormal_fpenv(ieee|dynamic) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX11GLISEL: {{.*}}
 ; GFX11SELDAG: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.log.ll b/llvm/test/CodeGen/AMDGPU/llvm.log.ll
index 7903ae93d770c..e2fd0a2d2d692 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.log.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.log.ll
@@ -7958,8 +7958,8 @@ declare <3 x half> @llvm.log.v3f16(<3 x half>) #2
 declare <4 x half> @llvm.log.v4f16(<4 x half>) #2
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #2
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #1 = { denormal_fpenv(float: dynamic|dynamic) }
 attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX689-GISEL: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.log10.ll b/llvm/test/CodeGen/AMDGPU/llvm.log10.ll
index 478580ff8ec0a..b36ea6b1a134d 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.log10.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.log10.ll
@@ -7958,8 +7958,8 @@ declare <3 x half> @llvm.log10.v3f16(<3 x half>) #2
 declare <4 x half> @llvm.log10.v4f16(<4 x half>) #2
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #2
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #1 = { denormal_fpenv(float: dynamic|dynamic) }
 attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX689-GISEL: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.log2.ll b/llvm/test/CodeGen/AMDGPU/llvm.log2.ll
index 8401e05b39c19..7035d143ff679 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.log2.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.log2.ll
@@ -5072,8 +5072,8 @@ declare <3 x half> @llvm.log2.v3f16(<3 x half>) #2
 declare <4 x half> @llvm.log2.v4f16(<4 x half>) #2
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #2
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #1 = { denormal_fpenv(float: dynamic|dynamic) }
 attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX689-GISEL: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.maxnum.f16.ll b/llvm/test/CodeGen/AMDGPU/llvm.maxnum.f16.ll
index 83f3a7571f9c0..2da2aa182971c 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.maxnum.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.maxnum.f16.ll
@@ -1115,4 +1115,4 @@ entry:
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/llvm.minnum.f16.ll b/llvm/test/CodeGen/AMDGPU/llvm.minnum.f16.ll
index 56554ed17f177..6b40024d3af01 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.minnum.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.minnum.f16.ll
@@ -1177,4 +1177,4 @@ entry:
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/mad-mix-bf16.ll b/llvm/test/CodeGen/AMDGPU/mad-mix-bf16.ll
index c59c8c452fc6d..02c2336844a00 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix-bf16.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix-bf16.ll
@@ -1393,6 +1393,6 @@ declare float @llvm.maxnum.f32(float, float) #2
 declare float @llvm.fmuladd.f32(float, float, float) #2
 declare <2 x float> @llvm.fmuladd.v2f32(<2 x float>, <2 x float>, <2 x float>) #2
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #1 = { nounwind denormal_fpenv(float: ieee|ieee) }
 attributes #2 = { nounwind readnone speculatable }

diff  --git a/llvm/test/CodeGen/AMDGPU/mad-mix-hi-bf16.ll b/llvm/test/CodeGen/AMDGPU/mad-mix-hi-bf16.ll
index 393581fd148f6..4c3f61522f5ae 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix-hi-bf16.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix-hi-bf16.ll
@@ -163,5 +163,5 @@ declare float @llvm.maxnum.f32(float, float) #1
 declare float @llvm.fmuladd.f32(float, float, float) #1
 declare <2 x float> @llvm.fmuladd.v2f32(<2 x float>, <2 x float>, <2 x float>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone speculatable }

diff  --git a/llvm/test/CodeGen/AMDGPU/mad-mix-hi.ll b/llvm/test/CodeGen/AMDGPU/mad-mix-hi.ll
index 7044afb09e371..051470df48fdc 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix-hi.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix-hi.ll
@@ -651,7 +651,7 @@ declare float @llvm.maxnum.f32(float, float) #1
 declare float @llvm.fmuladd.f32(float, float, float) #1
 declare <2 x float> @llvm.fmuladd.v2f32(<2 x float>, <2 x float>, <2 x float>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone speculatable }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GISEL-GFX11-FAKE16: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/mad-mix-lo-bf16.ll b/llvm/test/CodeGen/AMDGPU/mad-mix-lo-bf16.ll
index 03304ae3946b3..a2f6b5f7cd073 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix-lo-bf16.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix-lo-bf16.ll
@@ -496,5 +496,5 @@ declare <2 x float> @llvm.fmuladd.v2f32(<2 x float>, <2 x float>, <2 x float>) #
 declare <3 x float> @llvm.fmuladd.v3f32(<3 x float>, <3 x float>, <3 x float>) #1
 declare <4 x float> @llvm.fmuladd.v4f32(<4 x float>, <4 x float>, <4 x float>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone speculatable }

diff  --git a/llvm/test/CodeGen/AMDGPU/mad-mix-lo.ll b/llvm/test/CodeGen/AMDGPU/mad-mix-lo.ll
index 154d6c7079672..689263d21f2cc 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix-lo.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix-lo.ll
@@ -2758,7 +2758,7 @@ declare <2 x float> @llvm.fmuladd.v2f32(<2 x float>, <2 x float>, <2 x float>) #
 declare <3 x float> @llvm.fmuladd.v3f32(<3 x float>, <3 x float>, <3 x float>) #1
 declare <4 x float> @llvm.fmuladd.v4f32(<4 x float>, <4 x float>, <4 x float>) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone speculatable }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GISEL-GFX1100-FAKE16: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/mad-mix.ll b/llvm/test/CodeGen/AMDGPU/mad-mix.ll
index fad7d38758b06..8ae3780796563 100644
--- a/llvm/test/CodeGen/AMDGPU/mad-mix.ll
+++ b/llvm/test/CodeGen/AMDGPU/mad-mix.ll
@@ -5898,8 +5898,8 @@ declare float @llvm.maxnum.f32(float, float) #2
 declare float @llvm.fmuladd.f32(float, float, float) #2
 declare <2 x float> @llvm.fmuladd.v2f32(<2 x float>, <2 x float>, <2 x float>) #2
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #1 = { nounwind denormal_fpenv(float: ieee|ieee) }
 attributes #2 = { nounwind readnone speculatable }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GISEL-GFX1100-FAKE16: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/madak.ll b/llvm/test/CodeGen/AMDGPU/madak.ll
index b2813b392d253..b16dad4316db4 100644
--- a/llvm/test/CodeGen/AMDGPU/madak.ll
+++ b/llvm/test/CodeGen/AMDGPU/madak.ll
@@ -1510,4 +1510,4 @@ bb4:
   ret void
 }
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/madmk.ll b/llvm/test/CodeGen/AMDGPU/madmk.ll
index 4ef752b45e143..7b96296172035 100644
--- a/llvm/test/CodeGen/AMDGPU/madmk.ll
+++ b/llvm/test/CodeGen/AMDGPU/madmk.ll
@@ -214,5 +214,5 @@ bb6:                                              ; preds = %bb2
 
 declare i32 @llvm.amdgcn.mbcnt.lo(i32, i32) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }

diff  --git a/llvm/test/CodeGen/AMDGPU/mul24-pass-ordering.ll b/llvm/test/CodeGen/AMDGPU/mul24-pass-ordering.ll
index b13ad2d25b31d..0c0919de4aedc 100644
--- a/llvm/test/CodeGen/AMDGPU/mul24-pass-ordering.ll
+++ b/llvm/test/CodeGen/AMDGPU/mul24-pass-ordering.ll
@@ -263,8 +263,8 @@ define void @slsr1_1(i32 %b.arg, i32 %s.arg) #0 {
 declare void @foo(i32) #2
 declare float @llvm.fmuladd.f32(float, float, float) #1
 
-attributes #0 = { nounwind willreturn "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind willreturn denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone speculatable }
-attributes #2 = { nounwind "amdgpu-no-dispatch-id" "amdgpu-no-dispatch-ptr" "amdgpu-no-implicitarg-ptr" "amdgpu-no-queue-ptr" "amdgpu-no-workgroup-id-x" "amdgpu-no-cluster-id-x" "amdgpu-no-workgroup-id-y" "amdgpu-no-cluster-id-y" "amdgpu-no-workgroup-id-z" "amdgpu-no-cluster-id-z" "amdgpu-no-workitem-id-x" "amdgpu-no-workitem-id-y" "amdgpu-no-workitem-id-z" "uniform-work-group-size"="false" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #2 = { nounwind "amdgpu-no-dispatch-id" "amdgpu-no-dispatch-ptr" "amdgpu-no-implicitarg-ptr" "amdgpu-no-queue-ptr" "amdgpu-no-workgroup-id-x" "amdgpu-no-cluster-id-x" "amdgpu-no-workgroup-id-y" "amdgpu-no-cluster-id-y" "amdgpu-no-workgroup-id-z" "amdgpu-no-cluster-id-z" "amdgpu-no-workitem-id-x" "amdgpu-no-workitem-id-y" "amdgpu-no-workitem-id-z" "uniform-work-group-size"="false" denormal_fpenv(float: preservesign) }
 
 !0 = !{float 2.500000e+00}

diff  --git a/llvm/test/CodeGen/AMDGPU/omod.ll b/llvm/test/CodeGen/AMDGPU/omod.ll
index c248de294eb81..d0d4e27a5a7f5 100644
--- a/llvm/test/CodeGen/AMDGPU/omod.ll
+++ b/llvm/test/CodeGen/AMDGPU/omod.ll
@@ -1319,13 +1319,13 @@ declare half @llvm.minnum.f16(half, half) #1
 declare half @llvm.maxnum.f16(half, half) #1
 declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
-attributes #2 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-attributes #3 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #2 = { nounwind denormal_fpenv(float: ieee) }
+attributes #3 = { nounwind denormal_fpenv(preservesign) }
 attributes #4 = { nounwind "no-signed-zeros-fp-math"="false" }
-attributes #5 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #6 = { nounwind "denormal-fp-math"="ieee,ieee" }
+attributes #5 = { nounwind denormal_fpenv(preservesign) }
+attributes #6 = { nounwind denormal_fpenv(ieee) }
 
 !llvm.dbg.cu = !{!0}
 !llvm.module.flags = !{!2, !3}

diff  --git a/llvm/test/CodeGen/AMDGPU/operand-folding.ll b/llvm/test/CodeGen/AMDGPU/operand-folding.ll
index 1427225d25ee6..3177eef80436f 100644
--- a/llvm/test/CodeGen/AMDGPU/operand-folding.ll
+++ b/llvm/test/CodeGen/AMDGPU/operand-folding.ll
@@ -168,4 +168,4 @@ define i32 @issue139908(i64 %in) {
 declare i32 @llvm.amdgcn.workitem.id.x() #0
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
index cb2c8ddbaa6f2..555c49f186399 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
@@ -201,7 +201,7 @@ declare i64 @llvm.amdgcn.s.getpc() #2
 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(write)
 declare void @llvm.amdgcn.raw.buffer.store.i32(i32, <4 x i32>, i32, i32, i32 immarg) #3
 
-attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" "denormal-fp-math-f32"="preserve-sign" "target-features"="+wavefrontsize64,+cumode" "amdgpu-dynamic-vgpr-block-size"="16" }
+attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" denormal_fpenv(float: preservesign) "target-features"="+wavefrontsize64,+cumode" "amdgpu-dynamic-vgpr-block-size"="16" }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx1250.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx1250.ll
index 68694faf833e9..6ee48a9775629 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx1250.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx1250.ll
@@ -200,7 +200,7 @@ declare i64 @llvm.amdgcn.s.getpc() #2
 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(write)
 declare void @llvm.amdgcn.raw.buffer.store.i32(i32, <4 x i32>, i32, i32, i32 immarg) #3
 
-attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" "denormal-fp-math-f32"="preserve-sign" }
+attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" denormal_fpenv(float: preservesign) }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx950.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx950.ll
index b3575c68b892f..858a6a06eb029 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx950.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.gfx950.ll
@@ -207,7 +207,7 @@ declare i64 @llvm.amdgcn.s.getpc() #2
 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(write)
 declare void @llvm.amdgcn.raw.buffer.store.i32(i32, <4 x i32>, i32, i32, i32 immarg) #3
 
-attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" "denormal-fp-math-f32"="preserve-sign" }
+attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" denormal_fpenv(float: preservesign) }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll b/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll
index 6ce614bd92480..7a627733e12c2 100644
--- a/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll
+++ b/llvm/test/CodeGen/AMDGPU/prevent-fmul-hoist-ir.ll
@@ -398,8 +398,8 @@ if.else:                                          ; preds = %entry
   ret double %sub
 }
 
-attributes #0 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { nounwind "denormal-fp-math"="ieee,ieee" }
+attributes #0 = { nounwind denormal_fpenv(preservesign) }
+attributes #1 = { nounwind denormal_fpenv(ieee|ieee) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; FP-CONTRACT-FAST: {{.*}}
 ; NO-UNSAFE-FP-MATH: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/rcp-pattern.ll b/llvm/test/CodeGen/AMDGPU/rcp-pattern.ll
index 9f0ffbcf6eff9..26f2b3d2af60d 100644
--- a/llvm/test/CodeGen/AMDGPU/rcp-pattern.ll
+++ b/llvm/test/CodeGen/AMDGPU/rcp-pattern.ll
@@ -1411,10 +1411,10 @@ define amdgpu_kernel void @s_div_arcp_neg_k_x_pat_f32_daz(ptr addrspace(1) %out)
 declare float @llvm.fabs.f32(float) #1
 declare float @llvm.sqrt.f32(float) #1
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 attributes #1 = { nounwind readnone }
-attributes #2 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #3 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-attributes #4 = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
+attributes #2 = { nounwind denormal_fpenv(float: preservesign) }
+attributes #3 = { nounwind denormal_fpenv(float: ieee|ieee) }
+attributes #4 = { nounwind denormal_fpenv(float: ieee|ieee) }
 
 !0 = !{float 2.500000e+00}

diff  --git a/llvm/test/CodeGen/AMDGPU/rcp_iflag.ll b/llvm/test/CodeGen/AMDGPU/rcp_iflag.ll
index 54c3b46589a51..db3c902ec2416 100644
--- a/llvm/test/CodeGen/AMDGPU/rcp_iflag.ll
+++ b/llvm/test/CodeGen/AMDGPU/rcp_iflag.ll
@@ -42,5 +42,5 @@ define amdgpu_kernel void @rcp_sint_denorm(ptr addrspace(1) %in, ptr addrspace(1
 
 !0 = !{float 2.500000e+00}
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="ieee,ieee" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { denormal_fpenv(float: ieee|ieee) }

diff  --git a/llvm/test/CodeGen/AMDGPU/repeated-divisor.ll b/llvm/test/CodeGen/AMDGPU/repeated-divisor.ll
index 2d3524d711788..1196d51b89557 100644
--- a/llvm/test/CodeGen/AMDGPU/repeated-divisor.ll
+++ b/llvm/test/CodeGen/AMDGPU/repeated-divisor.ll
@@ -1011,8 +1011,8 @@ define <6 x half> @v_repeat_divisor_v3f16_x2(<3 x half> %x, <3 x half> %y, <3 x
   ret <6 x half> %shuffle
 }
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: ieee|ieee) }
+attributes #1 = { denormal_fpenv(float: preservesign) }
 
 !0 = !{float 2.5}
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:

diff  --git a/llvm/test/CodeGen/AMDGPU/rsq.f32-safe.ll b/llvm/test/CodeGen/AMDGPU/rsq.f32-safe.ll
index d9fdfb38ef344..dbeeb9085f59a 100644
--- a/llvm/test/CodeGen/AMDGPU/rsq.f32-safe.ll
+++ b/llvm/test/CodeGen/AMDGPU/rsq.f32-safe.ll
@@ -923,7 +923,7 @@ define float @v_rsq_f32_known_never_posdenormal(float nofpclass(psub) %val) {
 !0 = !{float 2.500000e+00}
 !1 = !{float 1.000000e+00}
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; CI-DAZ-SAFE: {{.*}}
 ; GCN-DAZ-SAFE: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/rsq.f32.ll b/llvm/test/CodeGen/AMDGPU/rsq.f32.ll
index f967e951b27a4..e841cb1518532 100644
--- a/llvm/test/CodeGen/AMDGPU/rsq.f32.ll
+++ b/llvm/test/CodeGen/AMDGPU/rsq.f32.ll
@@ -961,7 +961,7 @@ define float @v_rsq_f32_known_never_posdenormal(float nofpclass(psub) %val) {
 !0 = !{float 2.500000e+00}
 !1 = !{float 1.000000e+00}
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; CI-DAZ-UNSAFE: {{.*}}
 ; CI-IEEE-UNSAFE: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/sdwa-peephole.ll b/llvm/test/CodeGen/AMDGPU/sdwa-peephole.ll
index 6de85255e9c17..fa705fb455ba8 100644
--- a/llvm/test/CodeGen/AMDGPU/sdwa-peephole.ll
+++ b/llvm/test/CodeGen/AMDGPU/sdwa-peephole.ll
@@ -2293,7 +2293,7 @@ entry:
 
 declare i32 @llvm.amdgcn.workitem.id.x()
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(preservesign) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GCN: {{.*}}
 ; GFX9_10: {{.*}}

diff  --git a/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir b/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
index b4f23ec00b8e2..a48a3b63c5016 100644
--- a/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
+++ b/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
@@ -6,7 +6,7 @@
     ret [13 x i32] poison
   }
 
-  attributes #0 = { alwaysinline nounwind memory(readwrite) "amdgpu-flat-work-group-size"="32,32" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "denormal-fp-math-f32"="preserve-sign" "target-cpu"="gfx1030" "target-features"="+wavefrontsize32,+cumode,+enable-flat-scratch" "uniform-work-group-size"="false" }
+  attributes #0 = { alwaysinline nounwind memory(readwrite) "amdgpu-flat-work-group-size"="32,32" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" denormal_fpenv(float: preservesign) "target-cpu"="gfx1030" "target-features"="+wavefrontsize32,+cumode,+enable-flat-scratch" "uniform-work-group-size"="false" }
 ...
 ---
 

diff  --git a/llvm/test/CodeGen/AMDGPU/udivrem24.ll b/llvm/test/CodeGen/AMDGPU/udivrem24.ll
index 1e5ec59456a26..935a9bf23c9cb 100644
--- a/llvm/test/CodeGen/AMDGPU/udivrem24.ll
+++ b/llvm/test/CodeGen/AMDGPU/udivrem24.ll
@@ -2077,6 +2077,6 @@ define amdgpu_kernel void @test_udiv24_u23_u16_i32(ptr addrspace(1) %out, ptr ad
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math-f32"="preserve-sign,ieee" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { denormal_fpenv(float: ieee|preservesign) }
+attributes #2 = { denormal_fpenv(float: preservesign|ieee) }

diff  --git a/llvm/test/CodeGen/AMDGPU/v_mac.ll b/llvm/test/CodeGen/AMDGPU/v_mac.ll
index d987e7c65e692..4658332ac395d 100644
--- a/llvm/test/CodeGen/AMDGPU/v_mac.ll
+++ b/llvm/test/CodeGen/AMDGPU/v_mac.ll
@@ -291,7 +291,7 @@ bb:
 ; GCN-LABEL: {{^}}v_mac_f32_dynamic:
 ; GCN: v_mul_f32
 ; GCN: v_add_f32
-define float @v_mac_f32_dynamic(float %a, float %b, float %c) "denormal-fp-math-f32"="dynamic,dynamic" {
+define float @v_mac_f32_dynamic(float %a, float %b, float %c) denormal_fpenv(float: dynamic|dynamic) {
   %mul = fmul float %a, %b
   %mad = fadd float %mul, %c
   ret float %mad
@@ -300,7 +300,7 @@ define float @v_mac_f32_dynamic(float %a, float %b, float %c) "denormal-fp-math-
 ; GCN-LABEL: {{^}}v_mac_f32_dynamic_daz:
 ; GCN: v_mul_f32
 ; GCN: v_add_f32
-define float @v_mac_f32_dynamic_daz(float %a, float %b, float %c) "denormal-fp-math-f32"="preserve-sign,dynamic" {
+define float @v_mac_f32_dynamic_daz(float %a, float %b, float %c) denormal_fpenv(float: preservesign|dynamic) {
   %mul = fmul float %a, %b
   %mad = fadd float %mul, %c
   ret float %mad
@@ -309,7 +309,7 @@ define float @v_mac_f32_dynamic_daz(float %a, float %b, float %c) "denormal-fp-m
 ; GCN-LABEL: {{^}}v_mac_f32_dynamic_ftz:
 ; GCN: v_mul_f32
 ; GCN: v_add_f32
-define float @v_mac_f32_dynamic_ftz(float %a, float %b, float %c) "denormal-fp-math-f32"="dynamic,preserve-sign" {
+define float @v_mac_f32_dynamic_ftz(float %a, float %b, float %c) denormal_fpenv(float: dynamic|preservesign) {
   %mul = fmul float %a, %b
   %mad = fadd float %mul, %c
   ret float %mad

diff  --git a/llvm/test/CodeGen/AMDGPU/v_mac_f16.ll b/llvm/test/CodeGen/AMDGPU/v_mac_f16.ll
index 34cf771fae45e..8e1a4bf2a2d9f 100644
--- a/llvm/test/CodeGen/AMDGPU/v_mac_f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/v_mac_f16.ll
@@ -738,6 +738,6 @@ entry:
 
 declare void @llvm.amdgcn.s.barrier() #2
 
-attributes #0 = { nounwind "no-signed-zeros-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind "no-signed-zeros-fp-math"="false" denormal_fpenv(preservesign) }
+attributes #1 = { nounwind denormal_fpenv(preservesign) }
 attributes #2 = { nounwind convergent }

diff  --git a/llvm/test/CodeGen/AMDGPU/v_madak_f16.ll b/llvm/test/CodeGen/AMDGPU/v_madak_f16.ll
index de44175f7c5bd..2e85301edd3c1 100644
--- a/llvm/test/CodeGen/AMDGPU/v_madak_f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/v_madak_f16.ll
@@ -291,6 +291,6 @@ entry:
   ret void
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(preservesign) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GFX11: {{.*}}

diff  --git a/llvm/test/CodeGen/ARM/build-attributes-fn-attr3.ll b/llvm/test/CodeGen/ARM/build-attributes-fn-attr3.ll
index 27d1dc20bd815..e082e34ca1253 100644
--- a/llvm/test/CodeGen/ARM/build-attributes-fn-attr3.ll
+++ b/llvm/test/CodeGen/ARM/build-attributes-fn-attr3.ll
@@ -17,4 +17,4 @@ entry:
 
 declare float @llvm.fma.f32(float, float, float)
 
-attributes #0 = { minsize norecurse nounwind optsize readnone "no-trapping-math"="true" "denormal-fp-math"="ieee"}
+attributes #0 = { minsize norecurse nounwind optsize readnone "no-trapping-math"="true" denormal_fpenv(ieee)}

diff  --git a/llvm/test/CodeGen/ARM/build-attributes-fn-attr4.ll b/llvm/test/CodeGen/ARM/build-attributes-fn-attr4.ll
index 9c8dd8d95c61c..10cca563adc4a 100644
--- a/llvm/test/CodeGen/ARM/build-attributes-fn-attr4.ll
+++ b/llvm/test/CodeGen/ARM/build-attributes-fn-attr4.ll
@@ -16,4 +16,4 @@ entry:
 
 declare float @llvm.fma.f32(float, float, float)
 
-attributes #0 = { minsize norecurse nounwind optsize readnone "denormal-fp-math"="positive-zero,positive-zero" }
+attributes #0 = { minsize norecurse nounwind optsize readnone denormal_fpenv(positivezero|positivezero) }

diff  --git a/llvm/test/CodeGen/ARM/build-attributes-fn-attr5.ll b/llvm/test/CodeGen/ARM/build-attributes-fn-attr5.ll
index cda3ea0fc6d18..c294582f4c462 100644
--- a/llvm/test/CodeGen/ARM/build-attributes-fn-attr5.ll
+++ b/llvm/test/CodeGen/ARM/build-attributes-fn-attr5.ll
@@ -16,4 +16,4 @@ entry:
 
 declare float @llvm.fma.f32(float, float, float)
 
-attributes #0 = { minsize norecurse nounwind optsize readnone "denormal-fp-math"="preserve-sign,preserve-sign"}
+attributes #0 = { minsize norecurse nounwind optsize readnone denormal_fpenv(preservesign)}

diff  --git a/llvm/test/CodeGen/ARM/build-attributes-fn-attr6.ll b/llvm/test/CodeGen/ARM/build-attributes-fn-attr6.ll
index 59d0a40198392..85e3dbf5d5bd3 100644
--- a/llvm/test/CodeGen/ARM/build-attributes-fn-attr6.ll
+++ b/llvm/test/CodeGen/ARM/build-attributes-fn-attr6.ll
@@ -22,5 +22,5 @@ entry:
 
 declare float @llvm.fma.f32(float, float, float)
 
-attributes #0 = { minsize norecurse nounwind optsize readnone "denormal-fp-math"="preserve-sign,preserve-sign"}
-attributes #1 = { minsize norecurse nounwind optsize readnone "denormal-fp-math"="positive-zero,positive-zero"}
+attributes #0 = { minsize norecurse nounwind optsize readnone denormal_fpenv(preservesign)}
+attributes #1 = { minsize norecurse nounwind optsize readnone denormal_fpenv(positivezero|positivezero)}

diff  --git a/llvm/test/CodeGen/ARM/clang-section.ll b/llvm/test/CodeGen/ARM/clang-section.ll
index 9c32ab27cd9f2..c77d9d1610c79 100644
--- a/llvm/test/CodeGen/ARM/clang-section.ll
+++ b/llvm/test/CodeGen/ARM/clang-section.ll
@@ -35,8 +35,8 @@ attributes #0 = { "bss-section"="my_bss.1" "data-section"="my_data.1" "rodata-se
 attributes #1 = { "data-section"="my_data.1" "rodata-section"="my_rodata.1" }
 attributes #2 = { "bss-section"="my_bss.2" "rodata-section"="my_rodata.1" }
 attributes #3 = { "bss-section"="my_bss.2" "data-section"="my_data.2" "rodata-section"="my_rodata.2" }
-attributes #6 = { "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+dsp,+fp16,+neon,+vfp3" "use-soft-float"="false" }
-attributes #7 = { noinline nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+dsp,+fp16,+neon,+vfp3" "use-soft-float"="false" }
+attributes #6 = { "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+dsp,+fp16,+neon,+vfp3" "use-soft-float"="false" }
+attributes #7 = { noinline nounwind "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+dsp,+fp16,+neon,+vfp3" "use-soft-float"="false" }
 
 !llvm.module.flags = !{!0, !1, !2, !3}
 

diff  --git a/llvm/test/CodeGen/ARM/cmse-clear-float-bigend.mir b/llvm/test/CodeGen/ARM/cmse-clear-float-bigend.mir
index ae36da480537f..1be25ae3d3dc1 100644
--- a/llvm/test/CodeGen/ARM/cmse-clear-float-bigend.mir
+++ b/llvm/test/CodeGen/ARM/cmse-clear-float-bigend.mir
@@ -16,7 +16,7 @@
   ; Function Attrs: nounwind
   declare void @llvm.stackprotector(ptr, ptr) #1
 
-  attributes #0 = { "cmse_nonsecure_entry" nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+8msecext,+armv8-m.main,-d32,-fp64,+fp-armv8,+hwdiv,+thumb-mode,-crypto,-fullfp16,-neon" "use-soft-float"="false" }
+  attributes #0 = { "cmse_nonsecure_entry" nounwind "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+8msecext,+armv8-m.main,-d32,-fp64,+fp-armv8,+hwdiv,+thumb-mode,-crypto,-fullfp16,-neon" "use-soft-float"="false" }
   attributes #1 = { nounwind }
   attributes #2 = { "cmse_nonsecure_call" nounwind }
 

diff  --git a/llvm/test/CodeGen/ARM/softfp-constant-comparison.ll b/llvm/test/CodeGen/ARM/softfp-constant-comparison.ll
index 2aa7611347a07..24e0a7e60e5fd 100644
--- a/llvm/test/CodeGen/ARM/softfp-constant-comparison.ll
+++ b/llvm/test/CodeGen/ARM/softfp-constant-comparison.ll
@@ -32,4 +32,4 @@ land.end:                                         ; preds = %land.rhs, %entry
   ret void
 }
 
-attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m4" "target-features"="+armv7e-m,+dsp,+fp16,+hwdiv,+thumb-mode,+vfp2sp,+vfp3d16sp,+vfp4d16sp,-aes,-crc,-crypto,-dotprod,-fp16fml,-fullfp16,-hwdiv-arm,-lob,-mve,-mve.fp,-ras,-sb,-sha2" "use-soft-float"="false" }
+attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m4" "target-features"="+armv7e-m,+dsp,+fp16,+hwdiv,+thumb-mode,+vfp2sp,+vfp3d16sp,+vfp4d16sp,-aes,-crc,-crypto,-dotprod,-fp16fml,-fullfp16,-hwdiv-arm,-lob,-mve,-mve.fp,-ras,-sb,-sha2" "use-soft-float"="false" }

diff  --git a/llvm/test/CodeGen/Generic/denormal-fp-math-cl-opt.ll b/llvm/test/CodeGen/Generic/denormal-fp-math-cl-opt.ll
index 7e0922c7d0810..026ed5ed53f0e 100644
--- a/llvm/test/CodeGen/Generic/denormal-fp-math-cl-opt.ll
+++ b/llvm/test/CodeGen/Generic/denormal-fp-math-cl-opt.ll
@@ -3,7 +3,7 @@
 ; Check that the command line flag annotates the IR with the
 ; appropriate attributes.
 
-; CHECK: attributes #0 = { "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+; CHECK: attributes #0 = { denormal_fpenv(dynamic, float: preservesign) }
 define float @foo(float %var) {
   ret float %var
 }

diff  --git a/llvm/test/CodeGen/NVPTX/div.ll b/llvm/test/CodeGen/NVPTX/div.ll
index 00fc3d2341a5e..9f5f081731331 100644
--- a/llvm/test/CodeGen/NVPTX/div.ll
+++ b/llvm/test/CodeGen/NVPTX/div.ll
@@ -163,4 +163,4 @@ define float @div_fast_vec_ftz(float %a, float %b, float %c, float %d) #0 {
   ret float %fadd
 }
 
-attributes #0 = { "denormal-fp-math-f32" = "preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/NVPTX/f32x2-instructions.ll b/llvm/test/CodeGen/NVPTX/f32x2-instructions.ll
index a90cfff51e2c6..987fdf8bc6b69 100644
--- a/llvm/test/CodeGen/NVPTX/f32x2-instructions.ll
+++ b/llvm/test/CodeGen/NVPTX/f32x2-instructions.ll
@@ -3004,4 +3004,4 @@ define void @test_trunc_to_v2f16(<2 x float> %a, ptr %p) {
 
 
 attributes #0 = { nounwind }
-attributes #2 = { "denormal-fp-math"="preserve-sign" }
+attributes #2 = { denormal_fpenv(preservesign) }

diff  --git a/llvm/test/CodeGen/NVPTX/fast-math.ll b/llvm/test/CodeGen/NVPTX/fast-math.ll
index 7e778c40b8302..cc6301bccbdf8 100644
--- a/llvm/test/CodeGen/NVPTX/fast-math.ll
+++ b/llvm/test/CodeGen/NVPTX/fast-math.ll
@@ -549,4 +549,4 @@ define double @frem_f64(double %a, double %b) {
   ret double %rem
 }
 
-attributes #1 = { "denormal-fp-math-f32" = "preserve-sign" }
+attributes #1 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/NVPTX/fexp2.ll b/llvm/test/CodeGen/NVPTX/fexp2.ll
index d9e82cc372e24..fe205aca7a278 100644
--- a/llvm/test/CodeGen/NVPTX/fexp2.ll
+++ b/llvm/test/CodeGen/NVPTX/fexp2.ll
@@ -389,4 +389,4 @@ declare bfloat @llvm.exp2.bf16(bfloat %val)
 
 declare <2 x bfloat> @llvm.exp2.v2bf16(<2 x bfloat> %val)
 
-attributes #0 = {"denormal-fp-math"="preserve-sign"}
+attributes #0 = {denormal_fpenv(preservesign)}

diff  --git a/llvm/test/CodeGen/NVPTX/flog2.ll b/llvm/test/CodeGen/NVPTX/flog2.ll
index 4aafc986db1d9..f5ae1b1f4bd5d 100644
--- a/llvm/test/CodeGen/NVPTX/flog2.ll
+++ b/llvm/test/CodeGen/NVPTX/flog2.ll
@@ -215,4 +215,4 @@ declare bfloat @llvm.log2.bf16(bfloat %val)
 
 declare <2 x bfloat> @llvm.log2.v2bf16(<2 x bfloat> %val)
 
-attributes #0 = {"denormal-fp-math"="preserve-sign"}
+attributes #0 = {denormal_fpenv(preservesign)}

diff  --git a/llvm/test/CodeGen/NVPTX/math-intrins-sm80-ptx70-instcombine.ll b/llvm/test/CodeGen/NVPTX/math-intrins-sm80-ptx70-instcombine.ll
index 714cac19b782c..94f1d3fff2040 100644
--- a/llvm/test/CodeGen/NVPTX/math-intrins-sm80-ptx70-instcombine.ll
+++ b/llvm/test/CodeGen/NVPTX/math-intrins-sm80-ptx70-instcombine.ll
@@ -318,5 +318,5 @@ define <2 x half> @fma_rn_ftz_f16x2_no_attr(<2 x half> %0, <2 x half> %1, <2 x h
   ret <2 x half> %res
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign" }
-attributes #1 = { "denormal-fp-math-f32"="preserve-sign" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/NVPTX/math-intrins.ll b/llvm/test/CodeGen/NVPTX/math-intrins.ll
index a35d6ec8f17f6..1ed296269c521 100644
--- a/llvm/test/CodeGen/NVPTX/math-intrins.ll
+++ b/llvm/test/CodeGen/NVPTX/math-intrins.ll
@@ -1839,4 +1839,4 @@ define double @fma_double(double %a, double %b, double %c) {
 }
 
 attributes #0 = { nounwind readnone }
-attributes #1 = { "denormal-fp-math-f32" = "preserve-sign" }
+attributes #1 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/NVPTX/nvptx-prec-divf32-flag.ll b/llvm/test/CodeGen/NVPTX/nvptx-prec-divf32-flag.ll
index aaa3dfa86b1d1..9c9a3dff482e3 100644
--- a/llvm/test/CodeGen/NVPTX/nvptx-prec-divf32-flag.ll
+++ b/llvm/test/CodeGen/NVPTX/nvptx-prec-divf32-flag.ll
@@ -6,7 +6,7 @@
 
 target triple = "nvptx64-nvidia-cuda"
 
-define float @div_ftz(float %a, float %b) "denormal-fp-math-f32" = "preserve-sign" {
+define float @div_ftz(float %a, float %b) denormal_fpenv(float: preservesign) {
 ; APPROX-LABEL: div_ftz(
 ; APPROX:       {
 ; APPROX-NEXT:    .reg .b32 %r<4>;

diff  --git a/llvm/test/CodeGen/NVPTX/rsqrt-opt.ll b/llvm/test/CodeGen/NVPTX/rsqrt-opt.ll
index 7a235b17f7d5a..7c06d5426583c 100644
--- a/llvm/test/CodeGen/NVPTX/rsqrt-opt.ll
+++ b/llvm/test/CodeGen/NVPTX/rsqrt-opt.ll
@@ -72,4 +72,4 @@ declare float @llvm.nvvm.sqrt.approx.f(float)
 declare float @llvm.nvvm.sqrt.approx.ftz.f(float)
 declare float @llvm.sqrt.f32(float)
 
-attributes #0 = { "denormal-fp-math-f32" = "preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/CodeGen/NVPTX/sqrt-approx.ll b/llvm/test/CodeGen/NVPTX/sqrt-approx.ll
index 7e4e701af4cd1..a9e35b85e1b5b 100644
--- a/llvm/test/CodeGen/NVPTX/sqrt-approx.ll
+++ b/llvm/test/CodeGen/NVPTX/sqrt-approx.ll
@@ -436,5 +436,5 @@ define double @test_sqrt64_refined_ftz_ninf(double %a) #1 #2 {
   ret double %ret
 }
 
-attributes #1 = { "denormal-fp-math-f32" = "preserve-sign,preserve-sign" }
+attributes #1 = { denormal_fpenv(float: preservesign) }
 attributes #2 = { "reciprocal-estimates" = "rsqrtf:1,rsqrtd:1,sqrtf:1,sqrtd:1" }

diff  --git a/llvm/test/CodeGen/PowerPC/fmf-propagation.ll b/llvm/test/CodeGen/PowerPC/fmf-propagation.ll
index baa127e451701..5ba9b1e28a45e 100644
--- a/llvm/test/CodeGen/PowerPC/fmf-propagation.ll
+++ b/llvm/test/CodeGen/PowerPC/fmf-propagation.ll
@@ -632,8 +632,8 @@ define float @fneg_fsub_nozeros_1(float %x, float %y, float %z) {
   ret float %add
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; FMFDEBUG: {{.*}}
 ; GLOBALDEBUG: {{.*}}

diff  --git a/llvm/test/CodeGen/PowerPC/recipest.ll b/llvm/test/CodeGen/PowerPC/recipest.ll
index 504216921110b..b8629f7f442b7 100644
--- a/llvm/test/CodeGen/PowerPC/recipest.ll
+++ b/llvm/test/CodeGen/PowerPC/recipest.ll
@@ -1300,5 +1300,5 @@ define fp128 @hoo5_safe(fp128 %a) #1 {
 }
 
 attributes #0 = { nounwind "reciprocal-estimates"="sqrtf:0,sqrtd:0" }
-attributes #1 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #1 = { nounwind denormal_fpenv(preservesign) }
 attributes #2 = { nounwind readnone "target-features"="-crbits" }

diff  --git a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/skip-vpt-debug.mir b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/skip-vpt-debug.mir
index 17ef52a002163..66d01a3773035 100644
--- a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/skip-vpt-debug.mir
+++ b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/skip-vpt-debug.mir
@@ -64,7 +64,7 @@
 
   declare <4 x i1> @llvm.arm.mve.vctp32(i32) #5
 
-  attributes #0 = { nofree norecurse nounwind optsize "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m55" "target-features"="+armv8.1-m.main,+dsp,+fp-armv8d16,+fp-armv8d16sp,+fp16,+fp64,+fullfp16,+hwdiv,+lob,+mve,+mve.fp,+ras,+thumb-mode,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,+vfp4d16,+vfp4d16sp,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-dotprod,-fp16fml,-hwdiv-arm,-i8mm,-sb,-sha2" }
+  attributes #0 = { nofree norecurse nounwind optsize denormal_fpenv(preservesign, float: ieee) "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m55" "target-features"="+armv8.1-m.main,+dsp,+fp-armv8d16,+fp-armv8d16sp,+fp16,+fp64,+fullfp16,+hwdiv,+lob,+mve,+mve.fp,+ras,+thumb-mode,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,+vfp4d16,+vfp4d16sp,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-dotprod,-fp16fml,-hwdiv-arm,-i8mm,-sb,-sha2" }
   attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
   attributes #2 = { nofree nosync nounwind readnone willreturn }
   attributes #3 = { argmemonly nofree nosync nounwind readonly willreturn }

diff  --git a/llvm/test/CodeGen/Thumb2/mve-vpt-2-blocks-1-pred.mir b/llvm/test/CodeGen/Thumb2/mve-vpt-2-blocks-1-pred.mir
index e48a038037a55..c9a9239b52f51 100644
--- a/llvm/test/CodeGen/Thumb2/mve-vpt-2-blocks-1-pred.mir
+++ b/llvm/test/CodeGen/Thumb2/mve-vpt-2-blocks-1-pred.mir
@@ -13,7 +13,7 @@
     ret <4 x float> %inactive1
   }
 
-  attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="128" "frame-pointer"="none" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+armv8.1-m.main,+hwdiv,+mve.fp,+ras,+thumb-mode" "use-soft-float"="false" }
+  attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="128" "frame-pointer"="none" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+armv8.1-m.main,+hwdiv,+mve.fp,+ras,+thumb-mode" "use-soft-float"="false" }
   attributes #1 = { nounwind readnone }
   attributes #2 = { nounwind }
 

diff  --git a/llvm/test/CodeGen/Thumb2/pacbti-m-outliner-4.ll b/llvm/test/CodeGen/Thumb2/pacbti-m-outliner-4.ll
index db779dea717da..5a154f4f4adbe 100644
--- a/llvm/test/CodeGen/Thumb2/pacbti-m-outliner-4.ll
+++ b/llvm/test/CodeGen/Thumb2/pacbti-m-outliner-4.ll
@@ -179,7 +179,7 @@ return:                                           ; preds = %entry, %if.end
 ; CHECK-NOT: aut
 ; CHECK:        b    _Z1hii
 
-attributes #0 = { minsize noinline optsize "sign-return-address"="non-leaf" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m3" "target-features"="+armv7-m,+hwdiv,+thumb-mode" "use-soft-float"="false" }
+attributes #0 = { minsize noinline optsize "sign-return-address"="non-leaf" denormal_fpenv(preservesign, float: ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m3" "target-features"="+armv7-m,+hwdiv,+thumb-mode" "use-soft-float"="false" }
 attributes #1 = { nounwind "sign-return-address"="non-leaf" }
 attributes #2 = { noreturn "sign-return-address"="non-leaf" }
 

diff  --git a/llvm/test/CodeGen/X86/clang-section-coff.ll b/llvm/test/CodeGen/X86/clang-section-coff.ll
index 6b76bb6d222e3..1630f2d006b59 100644
--- a/llvm/test/CodeGen/X86/clang-section-coff.ll
+++ b/llvm/test/CodeGen/X86/clang-section-coff.ll
@@ -37,8 +37,8 @@ attributes #0 = { "bss-section"="my_bss.1" "data-section"="my_data.1" "rodata-se
 attributes #1 = { "data-section"="my_data.1" "rodata-section"="my_rodata.1" }
 attributes #2 = { "bss-section"="my_bss.2" "rodata-section"="my_rodata.1" }
 attributes #3 = { "bss-section"="my_bss.2" "data-section"="my_data.2" "rodata-section"="my_rodata.2" }
-attributes #6 = { "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "use-soft-float"="false" }
-attributes #7 = { noinline nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "use-soft-float"="false" }
+attributes #6 = { "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "use-soft-float"="false" }
+attributes #7 = { noinline nounwind "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign) "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "use-soft-float"="false" }
 
 !llvm.module.flags = !{!0, !1, !2, !3}
 

diff  --git a/llvm/test/CodeGen/X86/is_fpclass.ll b/llvm/test/CodeGen/X86/is_fpclass.ll
index 97136dafa6c2c..70062b245f36d 100644
--- a/llvm/test/CodeGen/X86/is_fpclass.ll
+++ b/llvm/test/CodeGen/X86/is_fpclass.ll
@@ -2908,7 +2908,7 @@ declare <2 x i1> @llvm.is.fpclass.v2f32(<2 x float>, i32)
 declare <4 x i1> @llvm.is.fpclass.v4f32(<4 x float>, i32)
 
 ; Assume DAZ
-attributes #0 = { "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|preservesign) }
 
 ; Maybe daz
-attributes #1 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #1 = { denormal_fpenv(ieee|dynamic) }

diff  --git a/llvm/test/CodeGen/X86/pow.ll b/llvm/test/CodeGen/X86/pow.ll
index b3a5268460aa3..c2f7eb66ab01c 100644
--- a/llvm/test/CodeGen/X86/pow.ll
+++ b/llvm/test/CodeGen/X86/pow.ll
@@ -222,4 +222,4 @@ define double @pow_f64_not_enough_fmf(double %x) nounwind {
   ret double %r
 }
 
-attributes #0 = { nounwind "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(ieee|preservesign) }

diff  --git a/llvm/test/CodeGen/X86/sqrt-fastmath-mir.ll b/llvm/test/CodeGen/X86/sqrt-fastmath-mir.ll
index fade0f7d1d130..5ac82c715dd04 100644
--- a/llvm/test/CodeGen/X86/sqrt-fastmath-mir.ll
+++ b/llvm/test/CodeGen/X86/sqrt-fastmath-mir.ll
@@ -144,6 +144,6 @@ define float @rsqrt_daz(float %f) #1 {
   ret float %div
 }
 
-attributes #0 = { "reciprocal-estimates"="sqrt:2" "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "reciprocal-estimates"="sqrt:2" "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { "reciprocal-estimates"="sqrt:2" denormal_fpenv(ieee|ieee) }
+attributes #1 = { "reciprocal-estimates"="sqrt:2" denormal_fpenv(ieee|preservesign) }
 attributes #2 = { nounwind readnone }

diff  --git a/llvm/test/CodeGen/X86/sqrt-fastmath-tune.ll b/llvm/test/CodeGen/X86/sqrt-fastmath-tune.ll
index 74b51ac21dc1f..9750618818eec 100644
--- a/llvm/test/CodeGen/X86/sqrt-fastmath-tune.ll
+++ b/llvm/test/CodeGen/X86/sqrt-fastmath-tune.ll
@@ -403,6 +403,6 @@ declare float @llvm.sqrt.f32(float) #2
 declare <4 x float> @llvm.sqrt.v4f32(<4 x float>) #2
 declare <8 x float> @llvm.sqrt.v8f32(<8 x float>) #2
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
 attributes #2 = { nounwind readnone }

diff  --git a/llvm/test/CodeGen/X86/sqrt-fastmath.ll b/llvm/test/CodeGen/X86/sqrt-fastmath.ll
index 83bfcd7f04d9c..143edacb5390d 100644
--- a/llvm/test/CodeGen/X86/sqrt-fastmath.ll
+++ b/llvm/test/CodeGen/X86/sqrt-fastmath.ll
@@ -1015,12 +1015,12 @@ define double @sqrt_simplify_before_recip_order(double %x, ptr %p) nounwind {
 attributes #0 = { "reciprocal-estimates"="!sqrtf,!vec-sqrtf,!divf,!vec-divf" }
 attributes #1 = { "reciprocal-estimates"="sqrt,vec-sqrt" }
 attributes #2 = { nounwind readnone }
-attributes #3 = { "reciprocal-estimates"="sqrt,vec-sqrt" "denormal-fp-math"="preserve-sign,ieee" }
-attributes #4 = { "reciprocal-estimates"="sqrt,vec-sqrt" "denormal-fp-math"="ieee,preserve-sign" }
+attributes #3 = { "reciprocal-estimates"="sqrt,vec-sqrt" denormal_fpenv(preservesign|ieee) }
+attributes #4 = { "reciprocal-estimates"="sqrt,vec-sqrt" denormal_fpenv(ieee|preservesign) }
 attributes #5 = { "reciprocal-estimates"="all:0" }
-attributes #6 = { "reciprocal-estimates"="sqrt,vec-sqrt" "denormal-fp-math"="preserve-sign,dynamic" }
+attributes #6 = { "reciprocal-estimates"="sqrt,vec-sqrt" denormal_fpenv(preservesign|dynamic) }
 
 ; Attributes without
 ; TODO: Merge with previous attributes when this attribute can be deleted.
-attributes #7 = { "reciprocal-estimates"="sqrt,vec-sqrt" "denormal-fp-math"="preserve-sign,ieee" } ; #3
-attributes #8 = { "reciprocal-estimates"="sqrt,vec-sqrt" "denormal-fp-math"="preserve-sign,dynamic" } ; #6
+attributes #7 = { "reciprocal-estimates"="sqrt,vec-sqrt" denormal_fpenv(preservesign|ieee) } ; #3
+attributes #8 = { "reciprocal-estimates"="sqrt,vec-sqrt" denormal_fpenv(preservesign|dynamic) } ; #6

diff  --git a/llvm/test/DebugInfo/COFF/fortran-contained-proc.ll b/llvm/test/DebugInfo/COFF/fortran-contained-proc.ll
index 82b39d4fa34cb..9d79d913ba57b 100644
--- a/llvm/test/DebugInfo/COFF/fortran-contained-proc.ll
+++ b/llvm/test/DebugInfo/COFF/fortran-contained-proc.ll
@@ -74,8 +74,8 @@ declare void @llvm.dbg.declare(metadata, metadata, metadata) #3
 ; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn
 declare void @llvm.dbg.value(metadata, metadata, metadata) #3
 
-attributes #0 = { nounwind uwtable "denormal-fp-math"="preserve-sign,preserve-sign" "frame-pointer"="none" "intel-lang"="fortran" "min-legal-vector-width"="0" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
-attributes #1 = { mustprogress nofree norecurse nosync nounwind uwtable willreturn writeonly "denormal-fp-math"="preserve-sign,preserve-sign" "frame-pointer"="none" "intel-lang"="fortran" "min-legal-vector-width"="0" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
+attributes #0 = { nounwind uwtable denormal_fpenv(preservesign) "frame-pointer"="none" "intel-lang"="fortran" "min-legal-vector-width"="0" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { mustprogress nofree norecurse nosync nounwind uwtable willreturn writeonly denormal_fpenv(preservesign) "frame-pointer"="none" "intel-lang"="fortran" "min-legal-vector-width"="0" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
 attributes #2 = { nofree "intel-lang"="fortran" }
 attributes #3 = { mustprogress nofree nosync nounwind readnone speculatable willreturn }
 attributes #4 = { nounwind }

diff  --git a/llvm/test/Instrumentation/NumericalStabilitySanitizer/basic.ll b/llvm/test/Instrumentation/NumericalStabilitySanitizer/basic.ll
index 0f3287b07697b..304508d9f86c4 100644
--- a/llvm/test/Instrumentation/NumericalStabilitySanitizer/basic.ll
+++ b/llvm/test/Instrumentation/NumericalStabilitySanitizer/basic.ll
@@ -914,4 +914,4 @@ entry:
 }
 
 
-attributes #0 = { nounwind readonly uwtable sanitize_numerical_stability "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #0 = { nounwind readonly uwtable sanitize_numerical_stability "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign, float: ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }

diff  --git a/llvm/test/Instrumentation/NumericalStabilitySanitizer/non_float_store.ll b/llvm/test/Instrumentation/NumericalStabilitySanitizer/non_float_store.ll
index ce2daedf94b0b..aa1df9566d4f4 100644
--- a/llvm/test/Instrumentation/NumericalStabilitySanitizer/non_float_store.ll
+++ b/llvm/test/Instrumentation/NumericalStabilitySanitizer/non_float_store.ll
@@ -16,4 +16,4 @@ entry:
   ret void
 }
 
-attributes #0 = { nounwind readonly uwtable sanitize_numerical_stability "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="true" "use-soft-float"="false" }
+attributes #0 = { nounwind readonly uwtable sanitize_numerical_stability "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign, float: ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="true" "use-soft-float"="false" }

diff  --git a/llvm/test/Instrumentation/NumericalStabilitySanitizer/scalable_vector.ll b/llvm/test/Instrumentation/NumericalStabilitySanitizer/scalable_vector.ll
index 3263b8b5fe749..c071ce70d25c3 100644
--- a/llvm/test/Instrumentation/NumericalStabilitySanitizer/scalable_vector.ll
+++ b/llvm/test/Instrumentation/NumericalStabilitySanitizer/scalable_vector.ll
@@ -12,4 +12,4 @@ define void @add_scalable_vector(<vscale x 2 x double> %a) sanitize_numerical_st
   ret void
 }
 
-attributes #0 = { nounwind readonly uwtable sanitize_numerical_stability "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="true" "use-soft-float"="false" }
+attributes #0 = { nounwind readonly uwtable sanitize_numerical_stability "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(preservesign, float: ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-jump-tables"="false" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="true" "use-soft-float"="false" }

diff  --git a/llvm/test/Other/opt-override-denormal-fp-math-f32.ll b/llvm/test/Other/opt-override-denormal-fp-math-f32.ll
index d7ee5b8cd1d2b..845fca9e1b568 100644
--- a/llvm/test/Other/opt-override-denormal-fp-math-f32.ll
+++ b/llvm/test/Other/opt-override-denormal-fp-math-f32.ll
@@ -14,10 +14,10 @@ entry:
   ret i32 0
 }
 
-; ALL-DAG: attributes [[ATTR]] = { nounwind "denormal-fp-math-f32"="preserve-sign,ieee" }
-; IEEE-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-; PRESERVESIGN-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-; POSITIVEZERO-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math-f32"="positive-zero,positive-zero" }
+; ALL-DAG: attributes [[ATTR]] = { nounwind denormal_fpenv(float: preservesign|ieee) }
+; IEEE-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(ieee) }
+; PRESERVESIGN-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(float: preservesign) }
+; POSITIVEZERO-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(float: positivezero) }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math-f32"="preserve-sign,ieee" }
+attributes #1 = { nounwind denormal_fpenv(float: preservesign|ieee) }

diff  --git a/llvm/test/Other/opt-override-denormal-fp-math-mixed.ll b/llvm/test/Other/opt-override-denormal-fp-math-mixed.ll
index 306fc78a2183a..59e0b32035b0a 100644
--- a/llvm/test/Other/opt-override-denormal-fp-math-mixed.ll
+++ b/llvm/test/Other/opt-override-denormal-fp-math-mixed.ll
@@ -24,19 +24,19 @@ entry:
   ret i32 0
 }
 
-; ALL-DAG: attributes [[ATTR]] = { nounwind "denormal-fp-math"="preserve-sign,ieee" "denormal-fp-math-f32"="preserve-sign,ieee" }
+; ALL-DAG: attributes [[ATTR]] = { nounwind denormal_fpenv(preservesign|ieee) }
 
-; IEEE-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="ieee,ieee" }
-; PRESERVESIGN-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-; POSITIVEZERO-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="positive-zero,positive-zero" }
+; IEEE-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(ieee) }
+; PRESERVESIGN-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(preservesign) }
+; POSITIVEZERO-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(positivezero) }
 
-; IEEEF32-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math-f32"="ieee,ieee" }
-; PRESERVESIGNF32-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-; POSITIVEZEROF32-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math-f32"="positive-zero,positive-zero" }
+; IEEEF32-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(ieee) }
+; PRESERVESIGNF32-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(float: preservesign) }
+; POSITIVEZEROF32-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(float: positivezero) }
 
-; IEEE-BOTH-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" }
-; PRESERVESIGN-BOTH-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-; POSITIVEZERO-BOTH-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="positive-zero,positive-zero" "denormal-fp-math-f32"="positive-zero,positive-zero" }
+; IEEE-BOTH-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(ieee) }
+; PRESERVESIGN-BOTH-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(preservesign) }
+; POSITIVEZERO-BOTH-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(positivezero) }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math"="preserve-sign,ieee" "denormal-fp-math-f32"="preserve-sign,ieee" }
+attributes #1 = { nounwind denormal_fpenv(preservesign|ieee, float: preservesign|ieee) }

diff  --git a/llvm/test/Other/opt-override-denormal-fp-math.ll b/llvm/test/Other/opt-override-denormal-fp-math.ll
index f108ac64045e2..6618abd86aeb6 100644
--- a/llvm/test/Other/opt-override-denormal-fp-math.ll
+++ b/llvm/test/Other/opt-override-denormal-fp-math.ll
@@ -14,10 +14,10 @@ entry:
   ret i32 0
 }
 
-; ALL-DAG: attributes [[ATTR]] = { nounwind "denormal-fp-math"="preserve-sign,ieee" }
-; IEEE-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="ieee,ieee" }
-; PRESERVESIGN-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-; POSITIVEZERO-DAG: attributes [[NOATTR]] = { nounwind "denormal-fp-math"="positive-zero,positive-zero" }
+; ALL-DAG: attributes [[ATTR]] = { nounwind denormal_fpenv(preservesign|ieee) }
+; IEEE-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(ieee) }
+; PRESERVESIGN-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(preservesign) }
+; POSITIVEZERO-DAG: attributes [[NOATTR]] = { nounwind denormal_fpenv(positivezero) }
 
 attributes #0 = { nounwind }
-attributes #1 = { nounwind "denormal-fp-math"="preserve-sign,ieee" }
+attributes #1 = { nounwind denormal_fpenv(preservesign|ieee) }

diff  --git a/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-rmw-fadd.ll b/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-rmw-fadd.ll
index 8a02562bea642..223ba631b354d 100644
--- a/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-rmw-fadd.ll
+++ b/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-rmw-fadd.ll
@@ -5219,12 +5219,12 @@ define void @test_atomicrmw_fadd_v2bf16_global_agent_noret__unsafe(ptr addrspace
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #1 = { strictfp "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }
+attributes #1 = { strictfp denormal_fpenv(float: preservesign) }
 attributes #2 = { strictfp }
-attributes #3 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #4 = { "denormal-fp-math-f32"="dynamic,dynamic" }
-attributes #5 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #3 = { denormal_fpenv(float: preservesign) }
+attributes #4 = { denormal_fpenv(float: dynamic) }
+attributes #5 = { denormal_fpenv(dynamic) }
 
 !0 = !{}
 !1 = !{i32 5, i32 6}

diff  --git a/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-simplify-cfg-CAS-block.ll b/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-simplify-cfg-CAS-block.ll
index 13c65ec5abb8c..ae414ae83b9a4 100644
--- a/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-simplify-cfg-CAS-block.ll
+++ b/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-simplify-cfg-CAS-block.ll
@@ -63,4 +63,4 @@ endif:
   ret void
 }
 
-attributes #0 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(float: preservesign) }

diff  --git a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-log.ll b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-log.ll
index 53bd642c55b97..c7186d1cb12af 100644
--- a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-log.ll
+++ b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-log.ll
@@ -269,8 +269,8 @@ declare half @llvm.amdgcn.log.f16(half) #0
 declare float @llvm.amdgcn.log.f32(float) #0
 
 attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
-attributes #1 = { "denormal-fp-math"="ieee,ieee" }
-attributes #2 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #4 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #5 = { "denormal-fp-math"="dynamic,ieee" }
+attributes #1 = { denormal_fpenv(ieee) }
+attributes #2 = { denormal_fpenv(ieee|preservesign) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
+attributes #4 = { denormal_fpenv(preservesign|ieee) }
+attributes #5 = { denormal_fpenv(dynamic|ieee) }

diff  --git a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rcp.ll b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rcp.ll
index 0dc179b1a9d6a..1c1d014d54906 100644
--- a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rcp.ll
+++ b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rcp.ll
@@ -426,5 +426,5 @@ define float @ret_rcp_f32_known_negative_not_nan(float nofpclass(nan pinf pzero
 }
 
 attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
-attributes #1 = { "denormal-fp-math-f32"="ieee,dynamic" }
-attributes #2 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #1 = { denormal_fpenv(float: ieee|dynamic) }
+attributes #2 = { denormal_fpenv(ieee|dynamic) }

diff  --git a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
index 2e5bcf2bfff2e..9f1e7fb49c73d 100644
--- a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
+++ b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
@@ -286,5 +286,5 @@ define double @ret_rsq_f64_known_not_inf(double nofpclass(inf) %arg) {
   ret double %call
 }
 
-attributes #0 = { "denormal-fp-math-f32"="ieee,dynamic" }
-attributes #1 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(float: ieee|dynamic) }
+attributes #1 = { denormal_fpenv(ieee|dynamic) }

diff  --git a/llvm/test/Transforms/Attributor/denormal-fp-math.ll b/llvm/test/Transforms/Attributor/denormal-fp-math.ll
index 573a05041bff1..824f05cd973e3 100644
--- a/llvm/test/Transforms/Attributor/denormal-fp-math.ll
+++ b/llvm/test/Transforms/Attributor/denormal-fp-math.ll
@@ -39,6 +39,7 @@ define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_d
 
 ; Should infer to preserve-sign,preserve-sign
 define internal void @leaf_dynamic_dynamic_from_daz_daz() #0 {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
 ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_daz_daz
 ; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -51,6 +52,7 @@ define internal void @leaf_dynamic_dynamic_from_daz_daz() #0 {
 ; This goes to invalid because of the mismatch in the main mode. We
 ; could theoretically refine to denormal-fp-math-f32=preserve-sign,preserve-sign
 define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz() #1 {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
 ; CHECK-LABEL: define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz
 ; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -62,6 +64,7 @@ define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_d
 
 ; Leave this alone, must stay dynamic,dynamic
 define internal void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee() #0 {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
 ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee
 ; CHECK-SAME: () #[[ATTR2:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -73,6 +76,7 @@ define internal void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee() #0 {
 
 ; Leave this alone, should only have denormal-fp-math-f32=ieee,ieee
 define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz_and_ieee_ieee() #1 {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
 ; CHECK-LABEL: define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz_and_ieee_ieee
 ; CHECK-SAME: () #[[ATTR1]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -84,6 +88,7 @@ define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_d
 
 ; Leave as dynamic,dynamic
 define void @externally_visible_dynamic_dynamic_from_ieee_ieee() #0 {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
 ; CHECK-LABEL: define void @externally_visible_dynamic_dynamic_from_ieee_ieee
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -95,6 +100,7 @@ define void @externally_visible_dynamic_dynamic_from_ieee_ieee() #0 {
 
 ; Should infer to positive-zero,positive-zero
 define internal void @leaf_dynamic_dynamic_from_dapz_dapz() #0 {
+; CHECK: Function Attrs: denormal_fpenv(positivezero)
 ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_dapz_dapz
 ; CHECK-SAME: () #[[ATTR3:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -154,6 +160,7 @@ define void @func_default_is_ieee_ieee() {
 
 ; preserve-sign,preserve-sign entry point
 define void @func_daz_daz() #3 {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
 ; CHECK-LABEL: define void @func_daz_daz
 ; CHECK-SAME: () #[[ATTR0]] {
 ; CHECK-NEXT:    call void @leaf_dynamic_dynamic_from_daz_daz()
@@ -173,6 +180,7 @@ define void @func_daz_daz() #3 {
 
 ; positive-zero,positive-zero entry point
 define void @func_dapz_dapz() #4 {
+; CHECK: Function Attrs: denormal_fpenv(positivezero)
 ; CHECK-LABEL: define void @func_dapz_dapz
 ; CHECK-SAME: () #[[ATTR3]] {
 ; CHECK-NEXT:    call void @leaf_dynamic_dynamic_from_dapz_dapz()
@@ -185,6 +193,7 @@ define void @func_dapz_dapz() #4 {
 ; Could be fully preserve-sign,preserve-sign, but this isn't a
 ; realistic case and we don't bother trying to handle it.
 define internal void @leaf_f64_dynamic_f64_dynamic__f32_daz_f32_daz_from__daz_daz() #5 {
+; CHECK: Function Attrs: denormal_fpenv(dynamic, float: preservesign)
 ; CHECK-LABEL: define internal void @leaf_f64_dynamic_f64_dynamic__f32_daz_f32_daz_from__daz_daz
 ; CHECK-SAME: () #[[ATTR4:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -216,6 +225,7 @@ define internal void @leaf_ieee_dynamic_from_ieee_ieee() #7 {
 
 ; Specialize the f64 mode to ieee,ieee but leave f32 as dynamic,dynamic
 define internal void @leaf_dynamic_dynamic_from_f64_ieee_f32_dynamic() #0 {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
 ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_f64_ieee_f32_dynamic
 ; CHECK-SAME: () #[[ATTR1]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -226,6 +236,7 @@ define internal void @leaf_dynamic_dynamic_from_f64_ieee_f32_dynamic() #0 {
 }
 
 define void @func_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic() #1 {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
 ; CHECK-LABEL: define void @func_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic
 ; CHECK-SAME: () #[[ATTR1]] {
 ; CHECK-NEXT:    call void @leaf_dynamic_dynamic_from_f64_ieee_f32_dynamic()
@@ -278,6 +289,7 @@ define internal void @leaf_dynamic_from_dynamic() {
 
 ; Leave unchanged as preserve-sign,preserve-sign
 define internal void @leaf_daz_daz_from_dynamic() #3 {
+; CHECK: Function Attrs: denormal_fpenv(preservesign)
 ; CHECK-LABEL: define internal void @leaf_daz_daz_from_dynamic
 ; CHECK-SAME: () #[[ATTR0]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -288,6 +300,7 @@ define internal void @leaf_daz_daz_from_dynamic() #3 {
 }
 
 define void @dynamic_dynamic() #0 {
+; CHECK: Function Attrs: denormal_fpenv(dynamic)
 ; CHECK-LABEL: define void @dynamic_dynamic
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    call void @leaf_ieee_ieee_from_dynamic()
@@ -302,6 +315,7 @@ define void @dynamic_dynamic() #0 {
 }
 
 define internal void @leaf_ieee_f64_daz_f32() #9 {
+; CHECK: Function Attrs: denormal_fpenv(float: preservesign)
 ; CHECK-LABEL: define internal void @leaf_ieee_f64_daz_f32
 ; CHECK-SAME: () #[[ATTR7:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -312,6 +326,7 @@ define internal void @leaf_ieee_f64_daz_f32() #9 {
 }
 
 define internal void @leaf_ieee_f64_daz_f32_from_ieee_f64_dynamic_f32() #10 {
+; CHECK: Function Attrs: denormal_fpenv(preservesign, float: ieee)
 ; CHECK-LABEL: define internal void @leaf_ieee_f64_daz_f32_from_ieee_f64_dynamic_f32
 ; CHECK-SAME: () #[[ATTR8:[0-9]+]] {
 ; CHECK-NEXT:    call void @call_of_mystery()
@@ -322,6 +337,7 @@ define internal void @leaf_ieee_f64_daz_f32_from_ieee_f64_dynamic_f32() #10 {
 }
 
 define void @ieee_f64_dynamic_f32() #1 {
+; CHECK: Function Attrs: denormal_fpenv(float: dynamic)
 ; CHECK-LABEL: define void @ieee_f64_dynamic_f32
 ; CHECK-SAME: () #[[ATTR1]] {
 ; CHECK-NEXT:    call void @leaf_ieee_f64_daz_f32()
@@ -333,7 +349,7 @@ define void @ieee_f64_dynamic_f32() #1 {
   ret void
 }
 
-; => "preserve-sign,positive-zero" + "denormal-fp-math-f32"="ieee,positive-zero"
+; => "preserve-sign,positive-zero" + denormal_fpenv(float: ieee|positivezero)
 define internal void @leaf_daz_dynamic_dynamic_dapz_from_daz_dapz_ieee_dapz() #11 {
 ; CHECK-LABEL: define internal void @leaf_daz_dynamic_dynamic_dapz_from_daz_dapz_ieee_dapz
 ; CHECK-SAME: () #[[ATTR9:[0-9]+]] {
@@ -354,28 +370,28 @@ define void @daz_dapz_ieee_dapz() #12 {
   ret void
 }
 
-attributes #0 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #1 = { "denormal-fp-math-f32"="dynamic,dynamic" }
-attributes #2 = { "denormal-fp-math"="ieee,ieee" }
-attributes #3 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #4 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #5 = { "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #6 = { "denormal-fp-math"="dynamic,ieee" }
-attributes #7 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #8 = { "denormal-fp-math"="preserve-sign,dynamic" }
-attributes #9 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #10 = { "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" }
-attributes #11 = { "denormal-fp-math"="preserve-sign,dynamic" "denormal-fp-math-f32"="dynamic,positive-zero" }
-attributes #12 = { "denormal-fp-math"="preserve-sign,positive-zero" "denormal-fp-math-f32"="ieee,positive-zero" }
+attributes #0 = { denormal_fpenv(dynamic) }
+attributes #1 = { denormal_fpenv(float: dynamic) }
+attributes #2 = { denormal_fpenv(ieee|ieee) }
+attributes #3 = { denormal_fpenv(preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|positivezero) }
+attributes #5 = { denormal_fpenv(dynamic, float: preservesign) }
+attributes #6 = { denormal_fpenv(dynamic|ieee) }
+attributes #7 = { denormal_fpenv(ieee|dynamic) }
+attributes #8 = { denormal_fpenv(preservesign|dynamic) }
+attributes #9 = { denormal_fpenv(float: preservesign) }
+attributes #10 = { denormal_fpenv(preservesign|preservesign, float: ieee) }
+attributes #11 = { denormal_fpenv(preservesign|dynamic, float: dynamic|positivezero) }
+attributes #12 = { denormal_fpenv(preservesign|positivezero, float: ieee|positivezero) }
 ;.
-; CHECK: attributes #[[ATTR0]] = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-; CHECK: attributes #[[ATTR1]] = { "denormal-fp-math-f32"="dynamic,dynamic" }
-; CHECK: attributes #[[ATTR2]] = { "denormal-fp-math"="dynamic,dynamic" }
-; CHECK: attributes #[[ATTR3]] = { "denormal-fp-math"="positive-zero,positive-zero" }
-; CHECK: attributes #[[ATTR4]] = { "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-; CHECK: attributes #[[ATTR5]] = { "denormal-fp-math"="preserve-sign,ieee" }
-; CHECK: attributes #[[ATTR6]] = { "denormal-fp-math"="dynamic,ieee" }
-; CHECK: attributes #[[ATTR7]] = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-; CHECK: attributes #[[ATTR8]] = { "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" }
-; CHECK: attributes #[[ATTR9]] = { "denormal-fp-math"="preserve-sign,positive-zero" "denormal-fp-math-f32"="ieee,positive-zero" }
+; CHECK: attributes #[[ATTR0]] = { denormal_fpenv(preservesign) }
+; CHECK: attributes #[[ATTR1]] = { denormal_fpenv(float: dynamic) }
+; CHECK: attributes #[[ATTR2]] = { denormal_fpenv(dynamic) }
+; CHECK: attributes #[[ATTR3]] = { denormal_fpenv(positivezero) }
+; CHECK: attributes #[[ATTR4]] = { denormal_fpenv(dynamic, float: preservesign) }
+; CHECK: attributes #[[ATTR5]] = { denormal_fpenv(preservesign|ieee) }
+; CHECK: attributes #[[ATTR6]] = { denormal_fpenv(dynamic|ieee) }
+; CHECK: attributes #[[ATTR7]] = { denormal_fpenv(float: preservesign) }
+; CHECK: attributes #[[ATTR8]] = { denormal_fpenv(preservesign, float: ieee) }
+; CHECK: attributes #[[ATTR9]] = { denormal_fpenv(preservesign|positivezero, float: ieee|positivezero) }
 ;.

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll b/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
index d1879cbb9add9..443771da59ff9 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
@@ -23,7 +23,7 @@ define float @ret_canonicalize_noinf(float nofpclass(inf) %arg0) {
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_denormal(float nofpclass(inf) %arg0) denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define nofpclass(snan inf) float @ret_canonicalize_dynamic_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR2:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -33,7 +33,7 @@ define float @ret_canonicalize_dynamic_denormal(float nofpclass(inf) %arg0) "den
   ret float %call
 }
 
-define float @ret_canonicalize_daz_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="dynamic,preserve-sign" {
+define float @ret_canonicalize_daz_denormal(float nofpclass(inf) %arg0) denormal_fpenv(dynamic|preservesign) {
 ; CHECK-LABEL: define nofpclass(snan inf sub) float @ret_canonicalize_daz_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR3:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -43,7 +43,7 @@ define float @ret_canonicalize_daz_denormal(float nofpclass(inf) %arg0) "denorma
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="dynamic,positive-zero" {
+define float @ret_canonicalize_dapz_denormal(float nofpclass(inf) %arg0) denormal_fpenv(dynamic|positivezero) {
 ; CHECK-LABEL: define nofpclass(snan inf nzero sub) float @ret_canonicalize_dapz_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR4:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf nzero sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -53,7 +53,7 @@ define float @ret_canonicalize_dapz_denormal(float nofpclass(inf) %arg0) "denorm
   ret float %call
 }
 
-define float @ret_canonicalize_ftpz_dapz_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="positive-zero,preserve-sign" {
+define float @ret_canonicalize_ftpz_dapz_denormal(float nofpclass(inf) %arg0) denormal_fpenv(positivezero|preservesign) {
 ; CHECK-LABEL: define nofpclass(snan inf sub) float @ret_canonicalize_ftpz_dapz_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR5:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -63,7 +63,7 @@ define float @ret_canonicalize_ftpz_dapz_denormal(float nofpclass(inf) %arg0) "d
   ret float %call
 }
 
-define float @ret_canonicalize_ftpz_ieee_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="positive-zero,ieee" {
+define float @ret_canonicalize_ftpz_ieee_denormal(float nofpclass(inf) %arg0) denormal_fpenv(positivezero|ieee) {
 ; CHECK-LABEL: define nofpclass(snan inf nzero sub) float @ret_canonicalize_ftpz_ieee_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR6:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf nzero sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -73,7 +73,7 @@ define float @ret_canonicalize_ftpz_ieee_denormal(float nofpclass(inf) %arg0) "d
   ret float %call
 }
 
-define float @ret_canonicalize_ftpz_dynamic_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="positive-zero,dynamic" {
+define float @ret_canonicalize_ftpz_dynamic_denormal(float nofpclass(inf) %arg0) denormal_fpenv(positivezero|dynamic) {
 ; CHECK-LABEL: define nofpclass(snan inf sub) float @ret_canonicalize_ftpz_dynamic_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR7:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -83,7 +83,7 @@ define float @ret_canonicalize_ftpz_dynamic_denormal(float nofpclass(inf) %arg0)
   ret float %call
 }
 
-define float @ret_canonicalize_ftz_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="preserve-sign,dynamic" {
+define float @ret_canonicalize_ftz_denormal(float nofpclass(inf) %arg0) denormal_fpenv(preservesign|dynamic) {
 ; CHECK-LABEL: define nofpclass(snan inf sub) float @ret_canonicalize_ftz_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR8:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -93,7 +93,7 @@ define float @ret_canonicalize_ftz_denormal(float nofpclass(inf) %arg0) "denorma
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_denormal(float nofpclass(inf) %arg0) "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_denormal(float nofpclass(inf) %arg0) denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define nofpclass(snan inf) float @ret_canonicalize_ieee_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR9:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
@@ -103,7 +103,7 @@ define float @ret_canonicalize_ieee_denormal(float nofpclass(inf) %arg0) "denorm
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_pos_denormal() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_pos_denormal() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf zero nsub norm) float @ret_canonicalize_ieee_constant_pos_denormal
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero nsub norm) float @llvm.canonicalize.f32(float noundef 0x36A0000000000000) #[[ATTR12]]
@@ -113,7 +113,7 @@ define float @ret_canonicalize_ieee_constant_pos_denormal() "denormal-fp-math"="
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_neg_denormal() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_neg_denormal() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf zero psub norm) float @ret_canonicalize_ieee_constant_neg_denormal
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero psub norm) float @llvm.canonicalize.f32(float noundef 0xB6A0000000000000) #[[ATTR12]]
@@ -123,7 +123,7 @@ define float @ret_canonicalize_ieee_constant_neg_denormal() "denormal-fp-math"="
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_pos_zero() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_pos_zero() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_ieee_constant_pos_zero
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0.000000e+00) #[[ATTR12]]
@@ -133,7 +133,7 @@ define float @ret_canonicalize_ieee_constant_pos_zero() "denormal-fp-math"="ieee
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_neg_zero() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_neg_zero() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @ret_canonicalize_ieee_constant_neg_zero
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf pzero sub norm) float @llvm.canonicalize.f32(float noundef -0.000000e+00) #[[ATTR12]]
@@ -143,7 +143,7 @@ define float @ret_canonicalize_ieee_constant_neg_zero() "denormal-fp-math"="ieee
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_pos_normal() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_pos_normal() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf zero sub nnorm) float @ret_canonicalize_ieee_constant_pos_normal
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero sub nnorm) float @llvm.canonicalize.f32(float noundef 8.000000e+00) #[[ATTR12]]
@@ -153,7 +153,7 @@ define float @ret_canonicalize_ieee_constant_pos_normal() "denormal-fp-math"="ie
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_neg_normal() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_neg_normal() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf zero sub pnorm) float @ret_canonicalize_ieee_constant_neg_normal
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero sub pnorm) float @llvm.canonicalize.f32(float noundef -8.000000e+00) #[[ATTR12]]
@@ -163,7 +163,7 @@ define float @ret_canonicalize_ieee_constant_neg_normal() "denormal-fp-math"="ie
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_pos_inf() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_pos_inf() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan ninf zero sub norm) float @ret_canonicalize_ieee_constant_pos_inf
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF0000000000000) #[[ATTR12]]
@@ -173,7 +173,7 @@ define float @ret_canonicalize_ieee_constant_pos_inf() "denormal-fp-math"="ieee,
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_neg_inf() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_neg_inf() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(nan pinf zero sub norm) float @ret_canonicalize_ieee_constant_neg_inf
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf zero sub norm) float @llvm.canonicalize.f32(float noundef 0xFFF0000000000000) #[[ATTR12]]
@@ -183,7 +183,7 @@ define float @ret_canonicalize_ieee_constant_neg_inf() "denormal-fp-math"="ieee,
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_qnan() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_qnan() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_ieee_constant_qnan
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF8000000000000) #[[ATTR12]]
@@ -193,7 +193,7 @@ define float @ret_canonicalize_ieee_constant_qnan() "denormal-fp-math"="ieee,iee
   ret float %call
 }
 
-define float @ret_canonicalize_ieee_constant_snan() "denormal-fp-math"="ieee,ieee" {
+define float @ret_canonicalize_ieee_constant_snan() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_ieee_constant_snan
 ; CHECK-SAME: () #[[ATTR9]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF1000000000000) #[[ATTR12]]
@@ -203,7 +203,7 @@ define float @ret_canonicalize_ieee_constant_snan() "denormal-fp-math"="ieee,iee
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_pos_denormal() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_pos_denormal() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_daz_constant_pos_denormal
 ; CHECK-SAME: () #[[ATTR10:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0x36A0000000000000) #[[ATTR12]]
@@ -213,7 +213,7 @@ define float @ret_canonicalize_daz_constant_pos_denormal() "denormal-fp-math"="p
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_neg_denormal() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_neg_denormal() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @ret_canonicalize_daz_constant_neg_denormal
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf pzero sub norm) float @llvm.canonicalize.f32(float noundef 0xB6A0000000000000) #[[ATTR12]]
@@ -223,7 +223,7 @@ define float @ret_canonicalize_daz_constant_neg_denormal() "denormal-fp-math"="p
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_pos_zero() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_pos_zero() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_daz_constant_pos_zero
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0.000000e+00) #[[ATTR12]]
@@ -233,7 +233,7 @@ define float @ret_canonicalize_daz_constant_pos_zero() "denormal-fp-math"="prese
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_neg_zero() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_neg_zero() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @ret_canonicalize_daz_constant_neg_zero
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf pzero sub norm) float @llvm.canonicalize.f32(float noundef -0.000000e+00) #[[ATTR12]]
@@ -243,7 +243,7 @@ define float @ret_canonicalize_daz_constant_neg_zero() "denormal-fp-math"="prese
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_pos_normal() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_pos_normal() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf zero sub nnorm) float @ret_canonicalize_daz_constant_pos_normal
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero sub nnorm) float @llvm.canonicalize.f32(float noundef 8.000000e+00) #[[ATTR12]]
@@ -253,7 +253,7 @@ define float @ret_canonicalize_daz_constant_pos_normal() "denormal-fp-math"="pre
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_neg_normal() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_neg_normal() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf zero sub pnorm) float @ret_canonicalize_daz_constant_neg_normal
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero sub pnorm) float @llvm.canonicalize.f32(float noundef -8.000000e+00) #[[ATTR12]]
@@ -263,7 +263,7 @@ define float @ret_canonicalize_daz_constant_neg_normal() "denormal-fp-math"="pre
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_pos_inf() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_pos_inf() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan ninf zero sub norm) float @ret_canonicalize_daz_constant_pos_inf
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF0000000000000) #[[ATTR12]]
@@ -273,7 +273,7 @@ define float @ret_canonicalize_daz_constant_pos_inf() "denormal-fp-math"="preser
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_neg_inf() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_neg_inf() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(nan pinf zero sub norm) float @ret_canonicalize_daz_constant_neg_inf
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf zero sub norm) float @llvm.canonicalize.f32(float noundef 0xFFF0000000000000) #[[ATTR12]]
@@ -283,7 +283,7 @@ define float @ret_canonicalize_daz_constant_neg_inf() "denormal-fp-math"="preser
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_qnan() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_qnan() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_daz_constant_qnan
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF8000000000000) #[[ATTR12]]
@@ -293,7 +293,7 @@ define float @ret_canonicalize_daz_constant_qnan() "denormal-fp-math"="preserve-
   ret float %call
 }
 
-define float @ret_canonicalize_daz_constant_snan() "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_constant_snan() denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_daz_constant_snan
 ; CHECK-SAME: () #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF1000000000000) #[[ATTR12]]
@@ -303,7 +303,7 @@ define float @ret_canonicalize_daz_constant_snan() "denormal-fp-math"="preserve-
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_pos_denormal() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_pos_denormal() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_dapz_constant_pos_denormal
 ; CHECK-SAME: () #[[ATTR11:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0x36A0000000000000) #[[ATTR12]]
@@ -313,7 +313,7 @@ define float @ret_canonicalize_dapz_constant_pos_denormal() "denormal-fp-math"="
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_neg_denormal() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_neg_denormal() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_dapz_constant_neg_denormal
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0xB6A0000000000000) #[[ATTR12]]
@@ -323,7 +323,7 @@ define float @ret_canonicalize_dapz_constant_neg_denormal() "denormal-fp-math"="
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_pos_zero() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_pos_zero() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_dapz_constant_pos_zero
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0.000000e+00) #[[ATTR12]]
@@ -333,7 +333,7 @@ define float @ret_canonicalize_dapz_constant_pos_zero() "denormal-fp-math"="posi
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_neg_zero() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_neg_zero() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_dapz_constant_neg_zero
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef -0.000000e+00) #[[ATTR12]]
@@ -343,7 +343,7 @@ define float @ret_canonicalize_dapz_constant_neg_zero() "denormal-fp-math"="posi
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_pos_normal() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_pos_normal() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub nnorm) float @ret_canonicalize_dapz_constant_pos_normal
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub nnorm) float @llvm.canonicalize.f32(float noundef 8.000000e+00) #[[ATTR12]]
@@ -353,7 +353,7 @@ define float @ret_canonicalize_dapz_constant_pos_normal() "denormal-fp-math"="po
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_neg_normal() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_neg_normal() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub pnorm) float @ret_canonicalize_dapz_constant_neg_normal
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub pnorm) float @llvm.canonicalize.f32(float noundef -8.000000e+00) #[[ATTR12]]
@@ -363,7 +363,7 @@ define float @ret_canonicalize_dapz_constant_neg_normal() "denormal-fp-math"="po
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_pos_inf() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_pos_inf() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan ninf nzero sub norm) float @ret_canonicalize_dapz_constant_pos_inf
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF0000000000000) #[[ATTR12]]
@@ -373,7 +373,7 @@ define float @ret_canonicalize_dapz_constant_pos_inf() "denormal-fp-math"="posit
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_neg_inf() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_neg_inf() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(nan pinf nzero sub norm) float @ret_canonicalize_dapz_constant_neg_inf
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0xFFF0000000000000) #[[ATTR12]]
@@ -383,7 +383,7 @@ define float @ret_canonicalize_dapz_constant_neg_inf() "denormal-fp-math"="posit
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_qnan() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_qnan() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf nzero sub norm) float @ret_canonicalize_dapz_constant_qnan
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF8000000000000) #[[ATTR12]]
@@ -393,7 +393,7 @@ define float @ret_canonicalize_dapz_constant_qnan() "denormal-fp-math"="positive
   ret float %call
 }
 
-define float @ret_canonicalize_dapz_constant_snan() "denormal-fp-math"="positive-zero,positive-zero" {
+define float @ret_canonicalize_dapz_constant_snan() denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf nzero sub norm) float @ret_canonicalize_dapz_constant_snan
 ; CHECK-SAME: () #[[ATTR11]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF1000000000000) #[[ATTR12]]
@@ -403,7 +403,7 @@ define float @ret_canonicalize_dapz_constant_snan() "denormal-fp-math"="positive
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_pos_denormal() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_pos_denormal() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf nsub norm) float @ret_canonicalize_dynamic_constant_pos_denormal
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nsub norm) float @llvm.canonicalize.f32(float noundef 0x36A0000000000000) #[[ATTR12]]
@@ -413,7 +413,7 @@ define float @ret_canonicalize_dynamic_constant_pos_denormal() "denormal-fp-math
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_neg_denormal() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_neg_denormal() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf psub norm) float @ret_canonicalize_dynamic_constant_neg_denormal
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf psub norm) float @llvm.canonicalize.f32(float noundef 0xB6A0000000000000) #[[ATTR12]]
@@ -423,7 +423,7 @@ define float @ret_canonicalize_dynamic_constant_neg_denormal() "denormal-fp-math
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_pos_zero() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_pos_zero() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf sub norm) float @ret_canonicalize_dynamic_constant_pos_zero
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub norm) float @llvm.canonicalize.f32(float noundef 0.000000e+00) #[[ATTR12]]
@@ -433,7 +433,7 @@ define float @ret_canonicalize_dynamic_constant_pos_zero() "denormal-fp-math"="d
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_neg_zero() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_neg_zero() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf sub norm) float @ret_canonicalize_dynamic_constant_neg_zero
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub norm) float @llvm.canonicalize.f32(float noundef -0.000000e+00) #[[ATTR12]]
@@ -443,7 +443,7 @@ define float @ret_canonicalize_dynamic_constant_neg_zero() "denormal-fp-math"="d
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_pos_normal() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_pos_normal() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf sub nnorm) float @ret_canonicalize_dynamic_constant_pos_normal
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub nnorm) float @llvm.canonicalize.f32(float noundef 8.000000e+00) #[[ATTR12]]
@@ -453,7 +453,7 @@ define float @ret_canonicalize_dynamic_constant_pos_normal() "denormal-fp-math"=
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_neg_normal() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_neg_normal() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan inf sub pnorm) float @ret_canonicalize_dynamic_constant_neg_normal
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub pnorm) float @llvm.canonicalize.f32(float noundef -8.000000e+00) #[[ATTR12]]
@@ -463,7 +463,7 @@ define float @ret_canonicalize_dynamic_constant_neg_normal() "denormal-fp-math"=
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_pos_inf() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_pos_inf() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan ninf sub norm) float @ret_canonicalize_dynamic_constant_pos_inf
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF0000000000000) #[[ATTR12]]
@@ -473,7 +473,7 @@ define float @ret_canonicalize_dynamic_constant_pos_inf() "denormal-fp-math"="dy
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_neg_inf() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_neg_inf() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(nan pinf sub norm) float @ret_canonicalize_dynamic_constant_neg_inf
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf sub norm) float @llvm.canonicalize.f32(float noundef 0xFFF0000000000000) #[[ATTR12]]
@@ -483,7 +483,7 @@ define float @ret_canonicalize_dynamic_constant_neg_inf() "denormal-fp-math"="dy
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_qnan() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_qnan() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf sub norm) float @ret_canonicalize_dynamic_constant_qnan
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF8000000000000) #[[ATTR12]]
@@ -493,7 +493,7 @@ define float @ret_canonicalize_dynamic_constant_qnan() "denormal-fp-math"="dynam
   ret float %call
 }
 
-define float @ret_canonicalize_dynamic_constant_snan() "denormal-fp-math"="dynamic,dynamic" {
+define float @ret_canonicalize_dynamic_constant_snan() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define noundef nofpclass(snan inf sub norm) float @ret_canonicalize_dynamic_constant_snan
 ; CHECK-SAME: () #[[ATTR2]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF1000000000000) #[[ATTR12]]
@@ -503,7 +503,7 @@ define float @ret_canonicalize_dynamic_constant_snan() "denormal-fp-math"="dynam
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_nzero_not_nsub(float nofpclass(nzero nsub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_nzero_not_nsub(float nofpclass(nzero nsub) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan nzero sub) float @ret_canonicalize_daz_not_nzero_not_nsub
 ; CHECK-SAME: (float nofpclass(nzero nsub) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan nzero sub) float @llvm.canonicalize.f32(float nofpclass(nzero nsub) [[X]]) #[[ATTR12]]
@@ -513,7 +513,7 @@ define float @ret_canonicalize_daz_not_nzero_not_nsub(float nofpclass(nzero nsub
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_nsub(float nofpclass(nsub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_nsub(float nofpclass(nsub) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_nsub
 ; CHECK-SAME: (float nofpclass(nsub) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(nsub) [[X]]) #[[ATTR12]]
@@ -523,7 +523,7 @@ define float @ret_canonicalize_daz_not_nsub(float nofpclass(nsub) %x) "denormal-
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_nzero(float nofpclass(nzero) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_nzero(float nofpclass(nzero) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_nzero
 ; CHECK-SAME: (float nofpclass(nzero) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(nzero) [[X]]) #[[ATTR12]]
@@ -533,7 +533,7 @@ define float @ret_canonicalize_daz_not_nzero(float nofpclass(nzero) %x) "denorma
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_pzero_not_psub(float nofpclass(pzero psub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_pzero_not_psub(float nofpclass(pzero psub) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan pzero sub) float @ret_canonicalize_daz_not_pzero_not_psub
 ; CHECK-SAME: (float nofpclass(pzero psub) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan pzero sub) float @llvm.canonicalize.f32(float nofpclass(pzero psub) [[X]]) #[[ATTR12]]
@@ -543,7 +543,7 @@ define float @ret_canonicalize_daz_not_pzero_not_psub(float nofpclass(pzero psub
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_psub(float nofpclass(psub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_psub(float nofpclass(psub) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_psub
 ; CHECK-SAME: (float nofpclass(psub) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(psub) [[X]]) #[[ATTR12]]
@@ -553,7 +553,7 @@ define float @ret_canonicalize_daz_not_psub(float nofpclass(psub) %x) "denormal-
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_pzero(float nofpclass(pzero) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_pzero(float nofpclass(pzero) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_pzero
 ; CHECK-SAME: (float nofpclass(pzero) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(pzero) [[X]]) #[[ATTR12]]
@@ -563,7 +563,7 @@ define float @ret_canonicalize_daz_not_pzero(float nofpclass(pzero) %x) "denorma
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_zero(float nofpclass(zero) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_zero(float nofpclass(zero) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_zero
 ; CHECK-SAME: (float nofpclass(zero) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(zero) [[X]]) #[[ATTR12]]
@@ -573,7 +573,7 @@ define float @ret_canonicalize_daz_not_zero(float nofpclass(zero) %x) "denormal-
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_sub(float nofpclass(sub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_sub(float nofpclass(sub) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_sub
 ; CHECK-SAME: (float nofpclass(sub) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(sub) [[X]]) #[[ATTR12]]
@@ -583,7 +583,7 @@ define float @ret_canonicalize_daz_not_sub(float nofpclass(sub) %x) "denormal-fp
   ret float %call
 }
 
-define float @ret_canonicalize_daz_not_sub_not_nzero(float nofpclass(sub nzero) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @ret_canonicalize_daz_not_sub_not_nzero(float nofpclass(sub nzero) %x) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define nofpclass(snan nzero sub) float @ret_canonicalize_daz_not_sub_not_nzero
 ; CHECK-SAME: (float nofpclass(nzero sub) [[X:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan nzero sub) float @llvm.canonicalize.f32(float nofpclass(nzero sub) [[X]]) #[[ATTR12]]

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index 7ae52a4d02ad9..fef871a1e85f7 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
@@ -1056,9 +1056,9 @@ define float @ret_known_inf_or_nan_fdiv_known_inf_or_nan(float nofpclass(norm su
   ret float %fdiv
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-frem.ll b/llvm/test/Transforms/Attributor/nofpclass-frem.ll
index 2c442c10289f5..42d9ac7da1cc9 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-frem.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-frem.ll
@@ -1064,9 +1064,9 @@ define float @ret_known_inf_or_nan_frem_known_inf_or_nan(float nofpclass(norm su
   ret float %frem
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-frexp.ll b/llvm/test/Transforms/Attributor/nofpclass-frexp.ll
index 21fe6ce5676ce..085647bb2b6f0 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-frexp.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-frexp.ll
@@ -460,12 +460,12 @@ define float @ret_frexp_f32_0_nopzero_daz_ieee(float nofpclass(pzero) %arg0) #5
 }
 
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #3 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #4 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #5 = { "denormal-fp-math"="preserve-sign,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }
+attributes #3 = { denormal_fpenv(dynamic) }
+attributes #4 = { denormal_fpenv(ieee|preservesign) }
+attributes #5 = { denormal_fpenv(preservesign|ieee) }
 
 
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-ldexp.ll b/llvm/test/Transforms/Attributor/nofpclass-ldexp.ll
index 5743aa71b3912..1d8c7af63c4fe 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-ldexp.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-ldexp.ll
@@ -1035,15 +1035,15 @@ define float @ret_ldexp_f32_neg127(float %arg0) #0 {
   ret float %call
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #3 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #4 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #5 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #6 = { "denormal-fp-math"="dynamic,preserve-sign" }
-attributes #7 = { "denormal-fp-math"="dynamic,positive-zero" }
-attributes #8 = { "denormal-fp-math"="positive-zero,dynamic" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }
+attributes #3 = { denormal_fpenv(preservesign|ieee) }
+attributes #4 = { denormal_fpenv(ieee|dynamic) }
+attributes #5 = { denormal_fpenv(ieee|preservesign) }
+attributes #6 = { denormal_fpenv(dynamic|preservesign) }
+attributes #7 = { denormal_fpenv(dynamic|positivezero) }
+attributes #8 = { denormal_fpenv(positivezero|dynamic) }
 
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-log.ll b/llvm/test/Transforms/Attributor/nofpclass-log.ll
index cb9ebec4a6c58..4bd62d8e903ba 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-log.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-log.ll
@@ -385,13 +385,13 @@ define float @ret_constrained_log10_noinf_noneg(float nofpclass(inf nsub nnorm)
   ret float %call
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #4 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #5 = { "denormal-fp-math"="positive-zero,ieee" }
-attributes #6 = { "denormal-fp-math"="dynamic,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
+attributes #4 = { denormal_fpenv(preservesign|ieee) }
+attributes #5 = { denormal_fpenv(positivezero|ieee) }
+attributes #6 = { denormal_fpenv(dynamic|ieee) }
 
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll b/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
index d9b1375d3c633..7059620febe74 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
@@ -504,13 +504,13 @@ define float @ret_maximum_any__nopos(float %arg0, float nofpclass(pinf psub pnor
   ret float %call
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #3 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #4 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #5 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #6 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #7 = { "denormal-fp-math"="positive-zero,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }
+attributes #3 = { denormal_fpenv(dynamic) }
+attributes #4 = { denormal_fpenv(ieee|preservesign) }
+attributes #5 = { denormal_fpenv(preservesign|ieee) }
+attributes #6 = { denormal_fpenv(ieee|positivezero) }
+attributes #7 = { denormal_fpenv(positivezero|ieee) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll b/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
index f21e9afe9e3ba..d5a289b51c107 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
@@ -784,13 +784,13 @@ define float @ret_maximumnum_noqnan__nosnan(float nofpclass(qnan) %arg0, float n
   ret float %call
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #3 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #4 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #5 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #6 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #7 = { "denormal-fp-math"="positive-zero,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }
+attributes #3 = { denormal_fpenv(dynamic) }
+attributes #4 = { denormal_fpenv(ieee|preservesign) }
+attributes #5 = { denormal_fpenv(preservesign|ieee) }
+attributes #6 = { denormal_fpenv(ieee|positivezero) }
+attributes #7 = { denormal_fpenv(positivezero|ieee) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll b/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
index ddfddbc22a517..308f58b7dd2fc 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
@@ -504,13 +504,13 @@ define float @ret_maxnum_any__nopos(float %arg0, float nofpclass(pinf psub pnorm
   ret float %call
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #3 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #4 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #5 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #6 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #7 = { "denormal-fp-math"="positive-zero,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }
+attributes #3 = { denormal_fpenv(dynamic) }
+attributes #4 = { denormal_fpenv(ieee|preservesign) }
+attributes #5 = { denormal_fpenv(preservesign|ieee) }
+attributes #6 = { denormal_fpenv(ieee|positivezero) }
+attributes #7 = { denormal_fpenv(positivezero|ieee) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-nan-fmul.ll b/llvm/test/Transforms/Attributor/nofpclass-nan-fmul.ll
index 1312f06cde0a4..6b09ba7223cae 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-nan-fmul.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-nan-fmul.ll
@@ -364,9 +364,9 @@ define float @ret_fmul_ieee_nonan__nozero_nonan_noinf(float nofpclass(nan) %arg0
   ret float %fmul
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-powi.ll b/llvm/test/Transforms/Attributor/nofpclass-powi.ll
index 1d59907a34f19..a3f5c5d3e08dc 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-powi.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-powi.ll
@@ -285,10 +285,10 @@ define <4 x float> @powi_v4f32_i32_regression(<4 x float> %arg) {
   ret <4 x float> %user
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #3 = { "denormal-fp-math"="preserve-sign,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }
+attributes #3 = { denormal_fpenv(preservesign|ieee) }
 
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll b/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
index 91831cd4621c9..aa62e807cddb9 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
@@ -270,13 +270,13 @@ define float @constrained_sqrt_nozero(float nofpclass(zero) %arg) strictfp {
   ret float %val
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #4 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #5 = { "denormal-fp-math"="positive-zero,ieee" }
-attributes #6 = { "denormal-fp-math"="dynamic,ieee" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
+attributes #4 = { denormal_fpenv(preservesign|ieee) }
+attributes #5 = { denormal_fpenv(positivezero|ieee) }
+attributes #6 = { denormal_fpenv(dynamic|ieee) }
 
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index c3ee163d19dc8..18adaf437f938 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -2025,7 +2025,7 @@ define float @fsub_n0_commute(float %arg0) {
 }
 
 define float @fadd_p0_ftz_daz(float %arg0) #3 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(positivezero) memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0_ftz_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR9:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00
@@ -2036,7 +2036,7 @@ define float @fadd_p0_ftz_daz(float %arg0) #3 {
 }
 
 define float @fadd_n0_ftz_daz(float %arg0) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fadd_n0_ftz_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR10:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], -0.000000e+00
@@ -2047,7 +2047,7 @@ define float @fadd_n0_ftz_daz(float %arg0) #0 {
 }
 
 define float @fsub_p0_ftz_daz(float %arg0) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fsub_p0_ftz_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float [[ARG0]], 0.000000e+00
@@ -2058,7 +2058,7 @@ define float @fsub_p0_ftz_daz(float %arg0) #0 {
 }
 
 define float @fsub_n0_ftz_daz(float %arg0) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fsub_n0_ftz_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float [[ARG0]], -0.000000e+00
@@ -2069,7 +2069,7 @@ define float @fsub_n0_ftz_daz(float %arg0) #0 {
 }
 
 define float @fsub_p0_commute_ftz_daz(float %arg0) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fsub_p0_commute_ftz_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float 0.000000e+00, [[ARG0]]
@@ -2080,7 +2080,7 @@ define float @fsub_p0_commute_ftz_daz(float %arg0) #0 {
 }
 
 define float @fsub_n0_commute_ftz_daz(float %arg0) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fsub_n0_commute_ftz_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float -0.000000e+00, [[ARG0]]
@@ -2091,7 +2091,6 @@ define float @fsub_n0_commute_ftz_daz(float %arg0) #0 {
 }
 
 define float @fadd_p0_ieee_daz(float %arg0) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0_ieee_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR11:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00
@@ -2102,7 +2101,6 @@ define float @fadd_p0_ieee_daz(float %arg0) #2 {
 }
 
 define float @fadd_p0_dapz_ieee(float %arg0) #4 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_p0_dapz_ieee
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR12:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], 0.000000e+00
@@ -2113,7 +2111,6 @@ define float @fadd_p0_dapz_ieee(float %arg0) #4 {
 }
 
 define float @fadd_n0_ieee_daz(float %arg0) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define float @fadd_n0_ieee_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], -0.000000e+00
@@ -2124,7 +2121,6 @@ define float @fadd_n0_ieee_daz(float %arg0) #2 {
 }
 
 define float @fsub_p0_ieee_daz(float %arg0) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define float @fsub_p0_ieee_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float [[ARG0]], 0.000000e+00
@@ -2135,7 +2131,6 @@ define float @fsub_p0_ieee_daz(float %arg0) #2 {
 }
 
 define float @fsub_n0_ieee_daz(float %arg0) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fsub_n0_ieee_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float [[ARG0]], -0.000000e+00
@@ -2146,7 +2141,6 @@ define float @fsub_n0_ieee_daz(float %arg0) #2 {
 }
 
 define float @fsub_p0_commute_ieee_daz(float %arg0) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fsub_p0_commute_ieee_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float 0.000000e+00, [[ARG0]]
@@ -2157,7 +2151,6 @@ define float @fsub_p0_commute_ieee_daz(float %arg0) #2 {
 }
 
 define float @fsub_n0_commute_ieee_daz(float %arg0) #1 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define float @fsub_n0_commute_ieee_daz
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR13:[0-9]+]] {
 ; CHECK-NEXT:    [[SUB:%.*]] = fsub float -0.000000e+00, [[ARG0]]
@@ -2179,7 +2172,7 @@ define float @fadd_never_negzero_or_negsub(float nofpclass(nzero nsub) %a, float
 }
 
 define float @fadd_never_negzero_or_ftz_daz(float nofpclass(nzero nsub) %a, float nofpclass(nzero nsub) %b) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fadd_never_negzero_or_ftz_daz
 ; CHECK-SAME: (float nofpclass(nzero nsub) [[A:%.*]], float nofpclass(nzero nsub) [[B:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2190,7 +2183,6 @@ define float @fadd_never_negzero_or_ftz_daz(float nofpclass(nzero nsub) %a, floa
 }
 
 define float @fadd_never_negzero_or_negsub_daz(float nofpclass(nzero nsub) %a, float nofpclass(nzero nsub) %b) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_negsub_daz
 ; CHECK-SAME: (float nofpclass(nzero nsub) [[A:%.*]], float nofpclass(nzero nsub) [[B:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2201,7 +2193,6 @@ define float @fadd_never_negzero_or_negsub_daz(float nofpclass(nzero nsub) %a, f
 }
 
 define float @fadd_never_negzero_or_negsub_dapz(float nofpclass(nzero nsub) %a, float nofpclass(nzero nsub) %b) #5 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_negsub_dapz
 ; CHECK-SAME: (float nofpclass(nzero nsub) [[A:%.*]], float nofpclass(nzero nsub) [[B:%.*]]) #[[ATTR14:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2223,7 +2214,6 @@ define float @fadd_never_negzero_or_possub(float nofpclass(nzero psub) %a, float
 }
 
 define float @fadd_never_negzero_or_possub_daz(float nofpclass(nzero psub) %a, float nofpclass(nzero psub) %b) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define float @fadd_never_negzero_or_possub_daz
 ; CHECK-SAME: (float nofpclass(nzero psub) [[A:%.*]], float nofpclass(nzero psub) [[B:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2234,7 +2224,6 @@ define float @fadd_never_negzero_or_possub_daz(float nofpclass(nzero psub) %a, f
 }
 
 define float @fadd_never_negzero_or_possub_dapz(float nofpclass(nzero psub) %a, float nofpclass(nzero psub) %b) #5 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_possub_dapz
 ; CHECK-SAME: (float nofpclass(nzero psub) [[A:%.*]], float nofpclass(nzero psub) [[B:%.*]]) #[[ATTR14]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2245,7 +2234,6 @@ define float @fadd_never_negzero_or_possub_dapz(float nofpclass(nzero psub) %a,
 }
 
 define float @fadd_never_negzero_or_sub_daz(float nofpclass(nzero sub) %a, float nofpclass(nzero sub) %b) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_sub_daz
 ; CHECK-SAME: (float nofpclass(nzero sub) [[A:%.*]], float nofpclass(nzero sub) [[B:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2256,7 +2244,6 @@ define float @fadd_never_negzero_or_sub_daz(float nofpclass(nzero sub) %a, float
 }
 
 define float @fadd_never_negzero_or_sub_dapz(float nofpclass(nzero sub) %a, float nofpclass(nzero sub) %b) #5 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_never_negzero_or_sub_dapz
 ; CHECK-SAME: (float nofpclass(nzero sub) [[A:%.*]], float nofpclass(nzero sub) [[B:%.*]]) #[[ATTR14]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[A]], [[B]]
@@ -2300,7 +2287,7 @@ define float @fadd_known_positive(float nofpclass(ninf nsub nnorm) %arg0, float
 }
 
 define float @fadd_known_positive_daz(float nofpclass(ninf nsub nnorm) %arg0, float nofpclass(ninf nsub nnorm) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @fadd_known_positive_daz
 ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nsub nnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -2344,7 +2331,7 @@ define float @fadd_known_positive_nzero(float nofpclass(ninf nsub nnorm nzero) %
 }
 
 define float @fadd_known_positive_nzero_ftz_daz(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @fadd_known_positive_nzero_ftz_daz
 ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -2355,7 +2342,6 @@ define float @fadd_known_positive_nzero_ftz_daz(float nofpclass(ninf nsub nnorm
 }
 
 define float @fadd_known_positive_nzero_ftz(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) #1 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @fadd_known_positive_nzero_ftz
 ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR13]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -2366,7 +2352,6 @@ define float @fadd_known_positive_nzero_ftz(float nofpclass(ninf nsub nnorm nzer
 }
 
 define float @fadd_known_positive_nzero_daz(float nofpclass(ninf nsub nnorm nzero) %arg0, float nofpclass(ninf nsub nnorm nzero) %arg1) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @fadd_known_positive_nzero_daz
 ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -2388,7 +2373,7 @@ define float @fadd_known_positive_normal(float nofpclass(ninf nnorm nzero) %arg0
 }
 
 define float @fadd_known_positive_normal_daz(float nofpclass(ninf nnorm nzero) %arg0, float nofpclass(ninf nnorm nzero) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fadd_known_positive_normal_daz
 ; CHECK-SAME: (float nofpclass(ninf nzero nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -2399,7 +2384,7 @@ define float @fadd_known_positive_normal_daz(float nofpclass(ninf nnorm nzero) %
 }
 
 define float @fadd_known_positive_normal_except0_daz(float nofpclass(ninf nnorm) %arg0, float nofpclass(ninf nnorm) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fadd_known_positive_normal_except0_daz
 ; CHECK-SAME: (float nofpclass(ninf nnorm) [[ARG0:%.*]], float nofpclass(ninf nnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -2410,7 +2395,7 @@ define float @fadd_known_positive_normal_except0_daz(float nofpclass(ninf nnorm)
 }
 
 define float @fadd_known_positive_normal_dapz(float nofpclass(ninf nnorm nzero) %arg0, float nofpclass(ninf nnorm nzero) %arg1) #3 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(positivezero) memory(none)
 ; CHECK-LABEL: define nofpclass(nzero) float @fadd_known_positive_normal_dapz
 ; CHECK-SAME: (float nofpclass(ninf nzero nnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nnorm) [[ARG1:%.*]]) #[[ATTR9]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3196,7 +3181,6 @@ define float @fadd_double_no_nonsub_nzero(float noundef nofpclass(nsub nzero) %a
 }
 
 define float @fadd_double_no_nopsub_pzero__ieee_daz(float noundef nofpclass(psub pzero) %arg) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(pzero) float @fadd_double_no_nopsub_pzero__ieee_daz
 ; CHECK-SAME: (float noundef nofpclass(pzero psub) [[ARG:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3207,7 +3191,7 @@ define float @fadd_double_no_nopsub_pzero__ieee_daz(float noundef nofpclass(psub
 }
 
 define float @fadd_double_no_nopsub_pzero__ftz_daz(float noundef nofpclass(psub pzero) %arg) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define noundef nofpclass(pzero) float @fadd_double_no_nopsub_pzero__ftz_daz
 ; CHECK-SAME: (float noundef nofpclass(pzero psub) [[ARG:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3218,7 +3202,6 @@ define float @fadd_double_no_nopsub_pzero__ftz_daz(float noundef nofpclass(psub
 }
 
 define float @fadd_double_no_nonsub_nzero__ieee_daz(float noundef nofpclass(nsub nzero) %arg) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(nzero) float @fadd_double_no_nonsub_nzero__ieee_daz
 ; CHECK-SAME: (float noundef nofpclass(nzero nsub) [[ARG:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3229,7 +3212,7 @@ define float @fadd_double_no_nonsub_nzero__ieee_daz(float noundef nofpclass(nsub
 }
 
 define float @fadd_double_no_nonsub_nzero__ftz_daz(float noundef nofpclass(nsub nzero) %arg) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define noundef float @fadd_double_no_nonsub_nzero__ftz_daz
 ; CHECK-SAME: (float noundef nofpclass(nzero nsub) [[ARG:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3240,7 +3223,6 @@ define float @fadd_double_no_nonsub_nzero__ftz_daz(float noundef nofpclass(nsub
 }
 
 define float @fadd_double_no_nopsub_pzero__ieee_dynamic(float noundef nofpclass(psub pzero) %arg) #9 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef float @fadd_double_no_nopsub_pzero__ieee_dynamic
 ; CHECK-SAME: (float noundef nofpclass(pzero psub) [[ARG:%.*]]) #[[ATTR17:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3251,7 +3233,6 @@ define float @fadd_double_no_nopsub_pzero__ieee_dynamic(float noundef nofpclass(
 }
 
 define float @fadd_double_no_nonsub_nzero__ieee_dynamic(float noundef nofpclass(nsub nzero) %arg) #9 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(nzero) float @fadd_double_no_nonsub_nzero__ieee_dynamic
 ; CHECK-SAME: (float noundef nofpclass(nzero nsub) [[ARG:%.*]]) #[[ATTR17]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3273,7 +3254,6 @@ define float @fadd_double_known_positive_nonsub_ieee(float noundef nofpclass(nin
 }
 
 define float @fadd_double_known_positive_nonsub__ieee_daz(float noundef nofpclass(ninf nnorm sub zero) %arg) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(ninf zero nsub nnorm) float @fadd_double_known_positive_nonsub__ieee_daz
 ; CHECK-SAME: (float noundef nofpclass(ninf zero sub nnorm) [[ARG:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3284,7 +3264,7 @@ define float @fadd_double_known_positive_nonsub__ieee_daz(float noundef nofpclas
 }
 
 define float @fadd_double_known_positive_nonsub__ftz_daz(float noundef nofpclass(ninf nnorm sub zero) %arg) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define noundef nofpclass(ninf zero nsub nnorm) float @fadd_double_known_positive_nonsub__ftz_daz
 ; CHECK-SAME: (float noundef nofpclass(ninf zero sub nnorm) [[ARG:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3295,7 +3275,6 @@ define float @fadd_double_known_positive_nonsub__ftz_daz(float noundef nofpclass
 }
 
 define float @fadd_double_known_positive_nonsub__ieee_dynamic(float noundef nofpclass(ninf nnorm sub zero) %arg) #9 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(ninf zero nsub nnorm) float @fadd_double_known_positive_nonsub__ieee_dynamic
 ; CHECK-SAME: (float noundef nofpclass(ninf zero sub nnorm) [[ARG:%.*]]) #[[ATTR17]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3317,7 +3296,6 @@ define float @fadd_double_known_negative_nonsub_ieee(float noundef nofpclass(pin
 }
 
 define float @fadd_double_known_negative_nonsub__ieee_daz(float noundef nofpclass(pinf pnorm sub zero) %arg) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(pinf zero psub pnorm) float @fadd_double_known_negative_nonsub__ieee_daz
 ; CHECK-SAME: (float noundef nofpclass(pinf zero sub pnorm) [[ARG:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3328,7 +3306,7 @@ define float @fadd_double_known_negative_nonsub__ieee_daz(float noundef nofpclas
 }
 
 define float @fadd_double_known_negative_nonsub__ftz_daz(float noundef nofpclass(pinf pnorm sub zero) %arg) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define noundef nofpclass(pinf zero psub pnorm) float @fadd_double_known_negative_nonsub__ftz_daz
 ; CHECK-SAME: (float noundef nofpclass(pinf zero sub pnorm) [[ARG:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3339,7 +3317,6 @@ define float @fadd_double_known_negative_nonsub__ftz_daz(float noundef nofpclass
 }
 
 define float @fadd_double_known_negative_nonsub_dynamic(float noundef nofpclass(pinf pnorm sub zero) %arg) #9 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(pinf zero psub pnorm) float @fadd_double_known_negative_nonsub_dynamic
 ; CHECK-SAME: (float noundef nofpclass(pinf zero sub pnorm) [[ARG:%.*]]) #[[ATTR17]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3416,7 +3393,6 @@ define float @fadd_known_negative(float nofpclass(pinf psub pnorm) %arg0, float
 }
 
 define float @fadd_known_negative_daz(float nofpclass(pinf psub pnorm) %arg0, float nofpclass(pinf psub pnorm) %arg1) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @fadd_known_negative_daz
 ; CHECK-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]], float nofpclass(pinf psub pnorm) [[ARG1:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3460,7 +3436,7 @@ define float @fadd_known_negative_pzero(float nofpclass(pinf psub pnorm pzero) %
 }
 
 define float @fadd_known_negative_pzero_ftz_daz(float nofpclass(pinf psub pnorm pzero) %arg0, float nofpclass(pinf psub pnorm pzero) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @fadd_known_negative_pzero_ftz_daz
 ; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(pinf pzero psub pnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3471,7 +3447,6 @@ define float @fadd_known_negative_pzero_ftz_daz(float nofpclass(pinf psub pnorm
 }
 
 define float @fadd_known_negative_pzero_ftz(float nofpclass(pinf psub pnorm pzero) %arg0, float nofpclass(pinf psub pnorm pzero) %arg1) #1 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @fadd_known_negative_pzero_ftz
 ; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(pinf pzero psub pnorm) [[ARG1:%.*]]) #[[ATTR13]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3482,7 +3457,6 @@ define float @fadd_known_negative_pzero_ftz(float nofpclass(pinf psub pnorm pzer
 }
 
 define float @fadd_known_negative_pzero_daz(float nofpclass(pinf psub pnorm pzero) %arg0, float nofpclass(pinf psub pnorm pzero) %arg1) #2 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @fadd_known_negative_pzero_daz
 ; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(pinf pzero psub pnorm) [[ARG1:%.*]]) #[[ATTR11]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3504,7 +3478,7 @@ define float @fadd_known_negative_normal(float nofpclass(pinf pnorm pzero) %arg0
 }
 
 define float @fadd_known_negative_normal_daz(float nofpclass(pinf pnorm pzero) %arg0, float nofpclass(pinf pnorm pzero) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fadd_known_negative_normal_daz
 ; CHECK-SAME: (float nofpclass(pinf pzero pnorm) [[ARG0:%.*]], float nofpclass(pinf pzero pnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3515,7 +3489,7 @@ define float @fadd_known_negative_normal_daz(float nofpclass(pinf pnorm pzero) %
 }
 
 define float @fadd_known_negative_normal_except0_daz(float nofpclass(pinf pnorm) %arg0, float nofpclass(pinf pnorm) %arg1) #0 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
 ; CHECK-LABEL: define float @fadd_known_negative_normal_except0_daz
 ; CHECK-SAME: (float nofpclass(pinf pnorm) [[ARG0:%.*]], float nofpclass(pinf pnorm) [[ARG1:%.*]]) #[[ATTR10]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3526,7 +3500,7 @@ define float @fadd_known_negative_normal_except0_daz(float nofpclass(pinf pnorm)
 }
 
 define float @fadd_known_negative_normal_dapz(float nofpclass(pinf pnorm pzero) %arg0, float nofpclass(pinf pnorm pzero) %arg1) #3 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(positivezero) memory(none)
 ; CHECK-LABEL: define float @fadd_known_negative_normal_dapz
 ; CHECK-SAME: (float nofpclass(pinf pzero pnorm) [[ARG0:%.*]], float nofpclass(pinf pzero pnorm) [[ARG1:%.*]]) #[[ATTR9]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG0]], [[ARG1]]
@@ -3571,7 +3545,6 @@ define float @fadd_double_no_nzero_dapz_dapz(float noundef nofpclass(nzero) %arg
 }
 
 define float @fadd_double_no_nzero_dapz_ieee(float noundef nofpclass(nzero) %arg) #4 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(nzero) float @fadd_double_no_nzero_dapz_ieee
 ; CHECK-SAME: (float noundef nofpclass(nzero) [[ARG:%.*]]) #[[ATTR12]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3606,7 +3579,6 @@ define float @fadd_double_no_pzero_maybe_undef(float nofpclass(pzero) %arg) {
 ; The input is not logical 0 since it's IEEE, but the output could
 ; still be flushed.
 define float @fadd_double_no_zero__output_only_is_ftz(float noundef nofpclass(zero) %arg) #7 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef float @fadd_double_no_zero__output_only_is_ftz
 ; CHECK-SAME: (float noundef nofpclass(zero) [[ARG:%.*]]) #[[ATTR13]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3619,7 +3591,6 @@ define float @fadd_double_no_zero__output_only_is_ftz(float noundef nofpclass(ze
 ; The input is not logical 0 since it's IEEE, but the output could
 ; still be flushed.
 define float @fadd_double_no_zero__output_only_is_dynamic(float noundef nofpclass(zero) %arg) #8 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef float @fadd_double_no_zero__output_only_is_dynamic
 ; CHECK-SAME: (float noundef nofpclass(zero) [[ARG:%.*]]) #[[ATTR18:[0-9]+]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3733,7 +3704,6 @@ entry:
 }
 
 define float @fadd_double_no_zero__output_only_is_ftpz(float noundef nofpclass(zero) %arg) #4 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(nzero) float @fadd_double_no_zero__output_only_is_ftpz
 ; CHECK-SAME: (float noundef nofpclass(zero) [[ARG:%.*]]) #[[ATTR12]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3744,7 +3714,6 @@ define float @fadd_double_no_zero__output_only_is_ftpz(float noundef nofpclass(z
 }
 
 define float @fadd_double_no_zero_or_nsub__output_only_is_ftpz(float noundef nofpclass(zero nsub) %arg) #4 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(nzero) float @fadd_double_no_zero_or_nsub__output_only_is_ftpz
 ; CHECK-SAME: (float noundef nofpclass(zero nsub) [[ARG:%.*]]) #[[ATTR12]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3755,7 +3724,6 @@ define float @fadd_double_no_zero_or_nsub__output_only_is_ftpz(float noundef nof
 }
 
 define float @fadd_double_no_zero_or_psub__output_only_is_ftpz(float noundef nofpclass(zero psub) %arg) #4 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(nzero) float @fadd_double_no_zero_or_psub__output_only_is_ftpz
 ; CHECK-SAME: (float noundef nofpclass(zero psub) [[ARG:%.*]]) #[[ATTR12]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3766,7 +3734,6 @@ define float @fadd_double_no_zero_or_psub__output_only_is_ftpz(float noundef nof
 }
 
 define float @fadd_double_no_zero_or_sub__output_only_is_ftpz(float noundef nofpclass(zero sub) %arg) #4 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef nofpclass(zero) float @fadd_double_no_zero_or_sub__output_only_is_ftpz
 ; CHECK-SAME: (float noundef nofpclass(zero sub) [[ARG:%.*]]) #[[ATTR12]] {
 ; CHECK-NEXT:    [[ADD:%.*]] = fadd float [[ARG]], [[ARG]]
@@ -3882,16 +3849,16 @@ define [4 x float] @infer_return_from_load_nofpclass_md_array(ptr %ptr) {
 
 declare i64 @_Z13get_global_idj(i32 noundef)
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #2 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #3 = { "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #4 = { "denormal-fp-math"="positive-zero,ieee" }
-attributes #5 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #6 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #7 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #8 = { "denormal-fp-math"="dynamic,ieee" }
-attributes #9 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(preservesign|ieee) }
+attributes #2 = { denormal_fpenv(ieee|preservesign) }
+attributes #3 = { denormal_fpenv(positivezero|positivezero) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }
+attributes #5 = { denormal_fpenv(ieee|positivezero) }
+attributes #6 = { denormal_fpenv(dynamic) }
+attributes #7 = { denormal_fpenv(preservesign|ieee) }
+attributes #8 = { denormal_fpenv(dynamic|ieee) }
+attributes #9 = { denormal_fpenv(ieee|dynamic) }
 
 !0 = !{}
 !1 = !{i32 3}

diff  --git a/llvm/test/Transforms/Attributor/reduced/register_benchmark_test.ll b/llvm/test/Transforms/Attributor/reduced/register_benchmark_test.ll
index 470423874441b..7462fee057500 100644
--- a/llvm/test/Transforms/Attributor/reduced/register_benchmark_test.ll
+++ b/llvm/test/Transforms/Attributor/reduced/register_benchmark_test.ll
@@ -1557,24 +1557,24 @@ declare dso_local void @_GLOBAL__sub_I_register_benchmark_test.cc() #0 section "
 ; Function Attrs: cold noreturn nounwind
 declare void @llvm.trap() #20
 
-attributes #0 = { uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #0 = { uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
 attributes #3 = { nounwind }
-attributes #4 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #4 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
 attributes #5 = { argmemonly nounwind willreturn }
-attributes #6 = { alwaysinline uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #7 = { alwaysinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #8 = { nobuiltin "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #9 = { nobuiltin nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #10 = { inlinehint uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #11 = { inlinehint nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #12 = { noreturn nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #13 = { norecurse uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #6 = { alwaysinline uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #7 = { alwaysinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #8 = { nobuiltin "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #9 = { nobuiltin nounwind "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee, float: ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #10 = { inlinehint uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #11 = { inlinehint nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #12 = { noreturn nounwind "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #13 = { norecurse uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
 attributes #14 = { nounwind readnone willreturn }
-attributes #15 = { noreturn "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #15 = { noreturn "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
 attributes #16 = { noinline noreturn nounwind }
 attributes #17 = { argmemonly nounwind willreturn writeonly }
-attributes #18 = { noreturn uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
-attributes #19 = { inlinehint noreturn uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #18 = { noreturn uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
+attributes #19 = { inlinehint noreturn uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-jump-tables"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "use-soft-float"="false" }
 attributes #20 = { cold noreturn nounwind }

diff  --git a/llvm/test/Transforms/EarlyCSE/cannot-be-negative-zero-assert.ll b/llvm/test/Transforms/EarlyCSE/cannot-be-negative-zero-assert.ll
index 1a24c47337757..d86b09afa50aa 100644
--- a/llvm/test/Transforms/EarlyCSE/cannot-be-negative-zero-assert.ll
+++ b/llvm/test/Transforms/EarlyCSE/cannot-be-negative-zero-assert.ll
@@ -38,4 +38,4 @@ entry:
   ret double %add.i
 }
 
-attributes #0 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/IndVarSimplify/addrec_no_exec_on_every_iteration.ll b/llvm/test/Transforms/IndVarSimplify/addrec_no_exec_on_every_iteration.ll
index 9524fcd998aff..d8bf1567d00ed 100644
--- a/llvm/test/Transforms/IndVarSimplify/addrec_no_exec_on_every_iteration.ll
+++ b/llvm/test/Transforms/IndVarSimplify/addrec_no_exec_on_every_iteration.ll
@@ -334,7 +334,7 @@ bb201:                                            ; preds = %bb194
   ret void
 }
 
-attributes #0 = { nofree norecurse nounwind uwtable "denormal-fp-math"="preserve-sign" "no-frame-pointer-elim"="false" }
+attributes #0 = { nofree norecurse nounwind uwtable denormal_fpenv(preservesign) "no-frame-pointer-elim"="false" }
 
 !0 = !{}
 !1 = !{i64 800}

diff  --git a/llvm/test/Transforms/InferAddressSpaces/AMDGPU/global-atomicrmw-fadd.ll b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/global-atomicrmw-fadd.ll
index 1bb4f0ede1f09..fd698196ffb77 100644
--- a/llvm/test/Transforms/InferAddressSpaces/AMDGPU/global-atomicrmw-fadd.ll
+++ b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/global-atomicrmw-fadd.ll
@@ -14,7 +14,7 @@ define amdgpu_kernel void @infer_as_before_atomic(ptr addrspace(4) %arg) #0 {
   ret void
 }
 
-attributes #0 = { nounwind "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(float: preservesign) }
 
 !0 = !{}
 

diff  --git a/llvm/test/Transforms/Inline/AMDGPU/inline-denormal-fp-math.ll b/llvm/test/Transforms/Inline/AMDGPU/inline-denormal-fp-math.ll
index e58f09e5edc34..0706e9d972394 100644
--- a/llvm/test/Transforms/Inline/AMDGPU/inline-denormal-fp-math.ll
+++ b/llvm/test/Transforms/Inline/AMDGPU/inline-denormal-fp-math.ll
@@ -861,16 +861,16 @@ define i32 @call_dynamic_dynamic_psz_psz_f32_from_psz_psz() #1 {
   ret i32 %result
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #3 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #4 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #5 = { "denormal-fp-math"="dynamic,ieee" }
-attributes #6 = { "denormal-fp-math"="ieee,dynamic" }
-attributes #7 = { "denormal-fp-math"="preserve-sign,dynamic" }
-attributes #8 = { "denormal-fp-math"="dynamic,preserve-sign" }
-attributes #9 = { "denormal-fp-math-f32"="dynamic,dynamic" }
-attributes #10 = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
-attributes #11 = { "denormal-fp-math-f32"="ieee,ieee" "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #12 = { "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(preservesign) }
+attributes #2 = { denormal_fpenv(preservesign|ieee) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(dynamic) }
+attributes #5 = { denormal_fpenv(dynamic|ieee) }
+attributes #6 = { denormal_fpenv(ieee|dynamic) }
+attributes #7 = { denormal_fpenv(preservesign|dynamic) }
+attributes #8 = { denormal_fpenv(dynamic|preservesign) }
+attributes #9 = { denormal_fpenv(float: dynamic) }
+attributes #10 = { denormal_fpenv(float: preservesign) }
+attributes #11 = { denormal_fpenv(float: ieee|ieee) denormal_fpenv(preservesign) }
+attributes #12 = { denormal_fpenv(dynamic, float: preservesign) }

diff  --git a/llvm/test/Transforms/InstCombine/NVPTX/nvvm-intrins.ll b/llvm/test/Transforms/InstCombine/NVPTX/nvvm-intrins.ll
index 4d856699b2d24..856c103cd1f58 100644
--- a/llvm/test/Transforms/InstCombine/NVPTX/nvvm-intrins.ll
+++ b/llvm/test/Transforms/InstCombine/NVPTX/nvvm-intrins.ll
@@ -5,11 +5,11 @@
 ; hackery:
 
 ; RUN: cat %s > %t.ftz
-; RUN: echo 'attributes #0 = { "denormal-fp-math-f32" = "preserve-sign" }' >> %t.ftz
+; RUN: echo 'attributes #0 = { denormal_fpenv(float: preservesign) }' >> %t.ftz
 ; RUN: opt < %t.ftz -passes=instcombine -mtriple=nvptx64-nvidia-cuda -S | FileCheck %s --check-prefix=CHECK --check-prefix=FTZ
 
 ; RUN: cat %s > %t.noftz
-; RUN: echo 'attributes #0 = { "denormal-fp-math-f32" = "ieee" }' >> %t.noftz
+; RUN: echo 'attributes #0 = { denormal_fpenv(float: ieee) }' >> %t.noftz
 ; RUN: opt < %t.noftz -passes=instcombine -mtriple=nvptx64-nvidia-cuda -S | FileCheck %s --check-prefix=CHECK --check-prefix=NOFTZ
 
 ; We handle nvvm intrinsics with ftz variants as follows:

diff  --git a/llvm/test/Transforms/InstCombine/combine-is.fpclass-and-fcmp.ll b/llvm/test/Transforms/InstCombine/combine-is.fpclass-and-fcmp.ll
index dcd79f5839002..9fb3f685a0b9c 100644
--- a/llvm/test/Transforms/InstCombine/combine-is.fpclass-and-fcmp.ll
+++ b/llvm/test/Transforms/InstCombine/combine-is.fpclass-and-fcmp.ll
@@ -435,5 +435,5 @@ declare i1 @llvm.is.fpclass.f16(half, i32 immarg) #0
 declare <2 x i1> @llvm.is.fpclass.v2f16(<2 x half>, i32 immarg) #0
 
 attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/create-class-from-logic-fcmp.ll b/llvm/test/Transforms/InstCombine/create-class-from-logic-fcmp.ll
index 9a723e8bc89ff..3ac245cb0bec6 100644
--- a/llvm/test/Transforms/InstCombine/create-class-from-logic-fcmp.ll
+++ b/llvm/test/Transforms/InstCombine/create-class-from-logic-fcmp.ll
@@ -2221,5 +2221,5 @@ declare half @llvm.canonicalize.f16(half) #0
 declare <2 x half> @llvm.fabs.v2f16(<2 x half>) #0
 
 attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
-attributes #1 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/fcmp-denormals-are-zero.ll b/llvm/test/Transforms/InstCombine/fcmp-denormals-are-zero.ll
index eea1dda6230a9..7affd9215d0b7 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-denormals-are-zero.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-denormals-are-zero.ll
@@ -348,7 +348,7 @@ declare <2 x half> @llvm.fabs.v2f16(<2 x half>)
 declare double @llvm.fabs.f64(double)
 declare <2 x double> @llvm.fabs.v2f64(<2 x double>)
 
-attributes #0 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #2 = { "denormal-fp-math"="ieee,ieee" }
-attributes #3 = { "denormal-fp-math-f32"="ieee,preserve-sign" }
+attributes #0 = { denormal_fpenv(ieee|preservesign) }
+attributes #1 = { denormal_fpenv(ieee|positivezero) }
+attributes #2 = { denormal_fpenv(ieee|ieee) }
+attributes #3 = { denormal_fpenv(float: ieee|preservesign) }

diff  --git a/llvm/test/Transforms/InstCombine/fcmp.ll b/llvm/test/Transforms/InstCombine/fcmp.ll
index d94e78c55a375..61490d120c81c 100644
--- a/llvm/test/Transforms/InstCombine/fcmp.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp.ll
@@ -2125,7 +2125,7 @@ define <8 x i1> @fcmp_une_fsub_const_nnan_vec(<8 x float> %x, <8 x float> %y) {
   ret <8 x i1> %cmp
 }
 
-define <8 x i1> @fcmp_ugt_fsub_const_vec_denormal_positive-zero(<8 x float> %x, <8 x float> %y) "denormal-fp-math"="positive-zero,positive-zero" {
+define <8 x i1> @fcmp_ugt_fsub_const_vec_denormal_positive-zero(<8 x float> %x, <8 x float> %y) denormal_fpenv(positivezero|positivezero) {
 ; CHECK-LABEL: @fcmp_ugt_fsub_const_vec_denormal_positive-zero(
 ; CHECK-NEXT:    [[FS:%.*]] = fsub <8 x float> [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt <8 x float> [[FS]], zeroinitializer
@@ -2136,7 +2136,7 @@ define <8 x i1> @fcmp_ugt_fsub_const_vec_denormal_positive-zero(<8 x float> %x,
   ret <8 x i1> %cmp
 }
 
-define <8 x i1> @fcmp_ogt_fsub_const_vec_denormal_dynamic(<8 x float> %x, <8 x float> %y) "denormal-fp-math"="dynamic,dynamic" {
+define <8 x i1> @fcmp_ogt_fsub_const_vec_denormal_dynamic(<8 x float> %x, <8 x float> %y) denormal_fpenv(dynamic) {
 ; CHECK-LABEL: @fcmp_ogt_fsub_const_vec_denormal_dynamic(
 ; CHECK-NEXT:    [[FS:%.*]] = fsub <8 x float> [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt <8 x float> [[FS]], zeroinitializer
@@ -2147,7 +2147,7 @@ define <8 x i1> @fcmp_ogt_fsub_const_vec_denormal_dynamic(<8 x float> %x, <8 x f
   ret <8 x i1> %cmp
 }
 
-define <8 x i1> @fcmp_ogt_fsub_const_vec_denormal_preserve-sign(<8 x float> %x, <8 x float> %y) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define <8 x i1> @fcmp_ogt_fsub_const_vec_denormal_preserve-sign(<8 x float> %x, <8 x float> %y) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: @fcmp_ogt_fsub_const_vec_denormal_preserve-sign(
 ; CHECK-NEXT:    [[FS:%.*]] = fsub <8 x float> [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt <8 x float> [[FS]], zeroinitializer

diff  --git a/llvm/test/Transforms/InstCombine/fmod.ll b/llvm/test/Transforms/InstCombine/fmod.ll
index 10cff189b8dfc..8e970c1b49532 100644
--- a/llvm/test/Transforms/InstCombine/fmod.ll
+++ b/llvm/test/Transforms/InstCombine/fmod.ll
@@ -99,7 +99,7 @@ entry:
   ret fp128 %call
 }
 
-define float @test_noinf_nozero_dazpreservesign(float nofpclass(inf) %f, float nofpclass(zero) %g) "denormal-fp-math"="preserve-sign,preserve-sign" {
+define float @test_noinf_nozero_dazpreservesign(float nofpclass(inf) %f, float nofpclass(zero) %g) denormal_fpenv(preservesign) {
 ; CHECK-LABEL: define float @test_noinf_nozero_dazpreservesign(
 ; CHECK-SAME: float nofpclass(inf) [[F:%.*]], float nofpclass(zero) [[G:%.*]]) #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  entry:
@@ -111,7 +111,7 @@ entry:
   ret float %call
 }
 
-define float @test_noinf_nozero_dazdynamic(float nofpclass(inf) %f, float nofpclass(zero) %g) "denormal-fp-math"="dynamic,dynamic" {
+define float @test_noinf_nozero_dazdynamic(float nofpclass(inf) %f, float nofpclass(zero) %g) denormal_fpenv(dynamic) {
 ; CHECK-LABEL: define float @test_noinf_nozero_dazdynamic(
 ; CHECK-SAME: float nofpclass(inf) [[F:%.*]], float nofpclass(zero) [[G:%.*]]) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:  entry:

diff  --git a/llvm/test/Transforms/InstCombine/is_fpclass.ll b/llvm/test/Transforms/InstCombine/is_fpclass.ll
index b86b307e4c7fd..70a7663e5768a 100644
--- a/llvm/test/Transforms/InstCombine/is_fpclass.ll
+++ b/llvm/test/Transforms/InstCombine/is_fpclass.ll
@@ -132,7 +132,7 @@ define <2 x i1> @test_class_is_p0_n0_v2f32(<2 x float> %x) {
   ret <2 x i1> %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_v2f32_daz(<2 x float> %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define <2 x i1> @test_class_is_p0_n0_v2f32_daz(<2 x float> %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_v2f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 96)
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -141,7 +141,7 @@ define <2 x i1> @test_class_is_p0_n0_v2f32_daz(<2 x float> %x) "denormal-fp-math
   ret <2 x i1> %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_v2f32_dynamic(<2 x float> %x) "denormal-fp-math-f32"="ieee,dynamic" {
+define <2 x i1> @test_class_is_p0_n0_v2f32_dynamic(<2 x float> %x) denormal_fpenv(float: ieee|dynamic) {
 ; CHECK-LABEL: @test_class_is_p0_n0_v2f32_dynamic(
 ; CHECK-NEXT:    [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 96)
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -168,7 +168,7 @@ define <2 x i1> @test_class_is_p0_n0_or_nan_v2f32(<2 x float> %x) {
   ret <2 x i1> %val
 }
 
-define i1 @test_class_is_p0_n0_or_nan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_p0_n0_or_nan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_or_nan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 99)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -177,7 +177,7 @@ define i1 @test_class_is_p0_n0_or_nan_f32_daz(float %x) "denormal-fp-math-f32"="
   ret i1 %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_or_nan_v2f32_daz(<2 x float> %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define <2 x i1> @test_class_is_p0_n0_or_nan_v2f32_daz(<2 x float> %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_or_nan_v2f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 99)
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -205,7 +205,7 @@ define <2 x i1> @test_class_is_p0_n0_or_sub_or_nan_v2f32(<2 x float> %x) {
   ret <2 x i1> %val
 }
 
-define i1 @test_class_is_p0_n0_or_sub_or_nan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_p0_n0_or_sub_or_nan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_or_sub_or_nan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp ueq float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -214,7 +214,7 @@ define i1 @test_class_is_p0_n0_or_sub_or_nan_f32_daz(float %x) "denormal-fp-math
   ret i1 %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_or_sub_or_nan_v2f32_daz(<2 x float> %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define <2 x i1> @test_class_is_p0_n0_or_sub_or_nan_v2f32_daz(<2 x float> %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_or_sub_or_nan_v2f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp ueq <2 x float> [[X:%.*]], zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -223,7 +223,7 @@ define <2 x i1> @test_class_is_p0_n0_or_sub_or_nan_v2f32_daz(<2 x float> %x) "de
   ret <2 x i1> %val
 }
 
-define i1 @test_class_is_p0_n0_or_sub_or_snan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_p0_n0_or_sub_or_snan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_or_sub_or_snan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 241)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -232,7 +232,7 @@ define i1 @test_class_is_p0_n0_or_sub_or_snan_f32_daz(float %x) "denormal-fp-mat
   ret i1 %val
 }
 
-define i1 @test_class_is_p0_n0_or_sub_or_qnan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_p0_n0_or_sub_or_qnan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_or_sub_or_qnan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 242)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -268,7 +268,7 @@ define i1 @test_class_is_not_p0_n0_or_snan_f32(float %x) {
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_or_nan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_not_p0_n0_or_nan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_or_nan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 924)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -286,7 +286,7 @@ define i1 @test_class_is_not_p0_n0_or_sub_or_nan_f32(float %x) {
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_or_sub_or_nan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_not_p0_n0_or_sub_or_nan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_or_sub_or_nan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp une float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -304,7 +304,7 @@ define i1 @test_class_is_not_p0_n0_or_sub_and_not_nan_f32(float %x) {
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_or_sub_and_not_nan_f32_daz(float %x) "denormal-fp-math-f32"="ieee,preserve-sign" {
+define i1 @test_class_is_not_p0_n0_or_sub_and_not_nan_f32_daz(float %x) denormal_fpenv(float: ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_or_sub_and_not_nan_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp one float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -340,7 +340,7 @@ define i1 @test_class_is_not_p0_n0_f32_strict(float %x) strictfp {
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_f32_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @test_class_is_not_p0_n0_f32_daz(float %x) denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 927)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -349,7 +349,7 @@ define i1 @test_class_is_not_p0_n0_f32_daz(float %x) "denormal-fp-math"="ieee,pr
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_f32_dynamic(float %x) "denormal-fp-math"="ieee,dynamic" {
+define i1 @test_class_is_not_p0_n0_f32_dynamic(float %x) denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_f32_dynamic(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 927)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -358,7 +358,7 @@ define i1 @test_class_is_not_p0_n0_f32_dynamic(float %x) "denormal-fp-math"="iee
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_psub_nsub_f32_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @test_class_is_not_p0_n0_psub_nsub_f32_daz(float %x) denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_psub_nsub_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp une float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -367,7 +367,7 @@ define i1 @test_class_is_not_p0_n0_psub_nsub_f32_daz(float %x) "denormal-fp-math
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_psub_nsub_f32_dapz(float %x) "denormal-fp-math"="ieee,positive-zero" {
+define i1 @test_class_is_not_p0_n0_psub_nsub_f32_dapz(float %x) denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_psub_nsub_f32_dapz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp une float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -376,7 +376,7 @@ define i1 @test_class_is_not_p0_n0_psub_nsub_f32_dapz(float %x) "denormal-fp-mat
   ret i1 %val
 }
 
-define i1 @test_class_is_not_p0_n0_psub_nsub_f32_dynamic(float %x) "denormal-fp-math"="ieee,dynamic" {
+define i1 @test_class_is_not_p0_n0_psub_nsub_f32_dynamic(float %x) denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @test_class_is_not_p0_n0_psub_nsub_f32_dynamic(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 783)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -394,7 +394,7 @@ define i1 @test_class_is_p0_n0_f32_strict(float %x) strictfp {
   ret i1 %val
 }
 
-define i1 @test_class_is_p0_n0_f32_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @test_class_is_p0_n0_f32_daz(float %x) denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 96)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -403,7 +403,7 @@ define i1 @test_class_is_p0_n0_f32_daz(float %x) "denormal-fp-math"="ieee,preser
   ret i1 %val
 }
 
-define i1 @test_class_is_p0_n0_f32_dapz(float %x) "denormal-fp-math"="ieee,positive-zero" {
+define i1 @test_class_is_p0_n0_f32_dapz(float %x) denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @test_class_is_p0_n0_f32_dapz(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 96)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -421,7 +421,7 @@ define i1 @test_class_is_p0_n0_psub_nsub_f32(float %x) {
   ret i1 %val
 }
 
-define i1 @test_class_is_p0_n0_psub_nsub_f32_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @test_class_is_p0_n0_psub_nsub_f32_daz(float %x) denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp oeq float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -430,7 +430,7 @@ define i1 @test_class_is_p0_n0_psub_nsub_f32_daz(float %x) "denormal-fp-math"="i
   ret i1 %val
 }
 
-define i1 @test_class_is_p0_n0_psub_nsub_f32_dapz(float %x) "denormal-fp-math"="ieee,positive-zero" {
+define i1 @test_class_is_p0_n0_psub_nsub_f32_dapz(float %x) denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_f32_dapz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp oeq float [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -439,7 +439,7 @@ define i1 @test_class_is_p0_n0_psub_nsub_f32_dapz(float %x) "denormal-fp-math"="
   ret i1 %val
 }
 
-define i1 @test_class_is_p0_n0_psub_nsub_f32_dynamic(float %x) "denormal-fp-math"="ieee,dynamic" {
+define i1 @test_class_is_p0_n0_psub_nsub_f32_dynamic(float %x) denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_f32_dynamic(
 ; CHECK-NEXT:    [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 240)
 ; CHECK-NEXT:    ret i1 [[VAL]]
@@ -457,7 +457,7 @@ define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32(<2 x float> %x) {
   ret <2 x i1> %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_daz(<2 x float> %x) "denormal-fp-math"="ieee,preserve-sign" {
+define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_daz(<2 x float> %x) denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_v2f32_daz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp oeq <2 x float> [[X:%.*]], zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -466,7 +466,7 @@ define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_daz(<2 x float> %x) "denorm
   ret <2 x i1> %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_dapz(<2 x float> %x) "denormal-fp-math"="ieee,positive-zero" {
+define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_dapz(<2 x float> %x) denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_v2f32_dapz(
 ; CHECK-NEXT:    [[VAL:%.*]] = fcmp oeq <2 x float> [[X:%.*]], zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -475,7 +475,7 @@ define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_dapz(<2 x float> %x) "denor
   ret <2 x i1> %val
 }
 
-define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_dynamic(<2 x float> %x) "denormal-fp-math"="ieee,dynamic" {
+define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_dynamic(<2 x float> %x) denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_v2f32_dynamic(
 ; CHECK-NEXT:    [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 240)
 ; CHECK-NEXT:    ret <2 x i1> [[VAL]]
@@ -3961,7 +3961,7 @@ declare float @llvm.fabs.f32(float)
 declare <2 x float> @llvm.fabs.v2f32(<2 x float>)
 declare void @llvm.assume(i1 noundef)
 
-attributes #0 = { "denormal-fp-math"="dynamic,ieee" }
-attributes #1 = { "denormal-fp-math"="dynamic,preserve-sign" }
-attributes #2 = { "denormal-fp-math"="dynamic,positive-zero" }
-attributes #3 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(dynamic|ieee) }
+attributes #1 = { denormal_fpenv(dynamic|preservesign) }
+attributes #2 = { denormal_fpenv(dynamic|positivezero) }
+attributes #3 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/log-to-intrinsic.ll b/llvm/test/Transforms/InstCombine/log-to-intrinsic.ll
index 273d44c091919..527dc6e138440 100644
--- a/llvm/test/Transforms/InstCombine/log-to-intrinsic.ll
+++ b/llvm/test/Transforms/InstCombine/log-to-intrinsic.ll
@@ -291,7 +291,7 @@ return:
 }
 !0 = !{ float 2.5 }
 
-define float @test_logf_pos_denormalpreserve(float %f) "denormal-fp-math"="preserve-sign,preserve-sign"  {
+define float @test_logf_pos_denormalpreserve(float %f) denormal_fpenv(preservesign)  {
 ; CHECK-LABEL: define float @test_logf_pos_denormalpreserve(
 ; CHECK-SAME: float [[F:%.*]]) #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
@@ -315,7 +315,7 @@ return:
   ret float 0.0
 }
 
-define float @test_logf_pos_denormaldynamic(float %f) "denormal-fp-math"="dynamic,dynamic"  {
+define float @test_logf_pos_denormaldynamic(float %f) denormal_fpenv(dynamic)  {
 ; CHECK-LABEL: define float @test_logf_pos_denormaldynamic(
 ; CHECK-SAME: float [[F:%.*]]) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
index ffc8a4438cddd..ce5bdfb2a4973 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
@@ -600,6 +600,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_src(i1 %cond, float %unkn
   ret float %result
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #2 = { "denormal-fp-math"="positive-zero,positive-zero" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(positivezero|positivezero) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fadd.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fadd.ll
index 522fbb07d2ffa..eee3405e9b8cf 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fadd.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fadd.ll
@@ -2086,9 +2086,9 @@ define nofpclass(snan) half @qnan_result_demands_snan_rhs(i1 %cond, half %unknow
   ret half %result
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #2 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #3 = { "denormal-fp-math"="preserve-sign,ieee" }
-attributes #4 = { "denormal-fp-math"="ieee,positive-zero" }
-attributes #5 = { "denormal-fp-math"="positive-zero,ieee" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|preservesign) }
+attributes #3 = { denormal_fpenv(preservesign|ieee) }
+attributes #4 = { denormal_fpenv(ieee|positivezero) }
+attributes #5 = { denormal_fpenv(positivezero|ieee) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
index b5886619c8e20..dc31cd9a37922 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
@@ -2324,17 +2324,17 @@ define nofpclass(snan) half @qnan_result_demands_snan_src_rhs(i1 %cond, half %un
   ret half %div
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
-attributes #2 = { "denormal-fp-math"="ieee,preserve-sign" }
-attributes #3 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|preservesign) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
 
 !0 = !{!"function_entry_count", i64 1000}
 ;.
-; CHECK: attributes #[[ATTR0]] = { "denormal-fp-math"="ieee,preserve-sign" }
-; CHECK: attributes #[[ATTR1]] = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-; CHECK: attributes #[[ATTR2]] = { "denormal-fp-math"="dynamic,dynamic" }
-; CHECK: attributes #[[ATTR3]] = { "denormal-fp-math"="ieee,dynamic" }
+; CHECK: attributes #[[ATTR0]] = { denormal_fpenv(ieee|preservesign) }
+; CHECK: attributes #[[ATTR1]] = { denormal_fpenv(preservesign) }
+; CHECK: attributes #[[ATTR2]] = { denormal_fpenv(dynamic) }
+; CHECK: attributes #[[ATTR3]] = { denormal_fpenv(ieee|dynamic) }
 ; CHECK: attributes #[[ATTR4:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
 ;.
 ; CHECK: [[PROF0]] = !{!"function_entry_count", i64 1000}

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
index 08fe9b5bb3e18..80c0d20a4ca95 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
@@ -1445,5 +1445,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
   ret float %mul
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
index 68978c6e45821..88cbb7d86c1e7 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
@@ -1233,5 +1233,5 @@ define nofpclass(snan) half @qnan_result_demands_snan_rhs(i1 %cond, half %unknow
   ret half %result
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
index ff1e63c13cad2..6f9f20f12a007 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
@@ -367,7 +367,9 @@ define nofpclass(snan) float @qnan_result_demands_snan_src(i1 %cond, float %unkn
   ret float %result
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }
 
 !0 = !{}
+
+

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
index 4bc116c3e30e8..420588bce238a 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
@@ -2190,5 +2190,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 !0 = !{}
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
index 4dd580bd471e7..7c36c3eb871a6 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
@@ -2195,5 +2195,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 !0 = !{}
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
index 4e9dcc5634ddb..ddd316baa1fd5 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
@@ -2197,5 +2197,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 !0 = !{}
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
index 67f29fbd68ae9..ab1f64209c8d1 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
@@ -2179,5 +2179,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 !0 = !{}
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
index eaf5b71771dbf..4eb1286685b7a 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
@@ -2186,5 +2186,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 !0 = !{}
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
index 10c251db8e432..adcb9f067a998 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
@@ -2187,5 +2187,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 !0 = !{}
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-sqrt.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-sqrt.ll
index b1af8d5ad6334..dc24630c43231 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-sqrt.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-sqrt.ll
@@ -359,5 +359,5 @@ define nofpclass(snan) float @qnan_result_demands_snan_src(i1 %cond, float %unkn
   ret float %result
 }
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #1 = { "denormal-fp-math"="dynamic,dynamic" }
+attributes #0 = { denormal_fpenv(preservesign) }
+attributes #1 = { denormal_fpenv(dynamic) }

diff  --git a/llvm/test/Transforms/InstSimplify/canonicalize.ll b/llvm/test/Transforms/InstSimplify/canonicalize.ll
index 9d2bdd1b853e6..15c45b34826ca 100644
--- a/llvm/test/Transforms/InstSimplify/canonicalize.ll
+++ b/llvm/test/Transforms/InstSimplify/canonicalize.ll
@@ -81,7 +81,7 @@ define float @canonicalize_denorm() {
   ret float %ret
 }
 
-define float @canonicalize_pos_denorm_preserve_sign_output() "denormal-fp-math"="preserve-sign,ieee" {
+define float @canonicalize_pos_denorm_preserve_sign_output() denormal_fpenv(preservesign|ieee) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_preserve_sign_output(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -89,7 +89,7 @@ define float @canonicalize_pos_denorm_preserve_sign_output() "denormal-fp-math"=
   ret float %ret
 }
 
-define float @canonicalize_pos_denorm_preserve_sign_input() "denormal-fp-math"="ieee,preserve-sign" {
+define float @canonicalize_pos_denorm_preserve_sign_input() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_preserve_sign_input(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -97,7 +97,7 @@ define float @canonicalize_pos_denorm_preserve_sign_input() "denormal-fp-math"="
   ret float %ret
 }
 
-define float @canonicalize_neg_denorm_preserve_sign_output() "denormal-fp-math"="preserve-sign,ieee" {
+define float @canonicalize_neg_denorm_preserve_sign_output() denormal_fpenv(preservesign|ieee) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_preserve_sign_output(
 ; CHECK-NEXT:    ret float -0.000000e+00
 ;
@@ -105,7 +105,7 @@ define float @canonicalize_neg_denorm_preserve_sign_output() "denormal-fp-math"=
   ret float %ret
 }
 
-define float @canonicalize_neg_denorm_preserve_sign_input() "denormal-fp-math"="ieee,preserve-sign" {
+define float @canonicalize_neg_denorm_preserve_sign_input() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_preserve_sign_input(
 ; CHECK-NEXT:    ret float -0.000000e+00
 ;
@@ -113,7 +113,7 @@ define float @canonicalize_neg_denorm_preserve_sign_input() "denormal-fp-math"="
   ret float %ret
 }
 
-define float @canonicalize_pos_denorm_positive_zero_output() "denormal-fp-math"="positive-zero,ieee" {
+define float @canonicalize_pos_denorm_positive_zero_output() denormal_fpenv(positivezero|ieee) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_positive_zero_output(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -121,7 +121,7 @@ define float @canonicalize_pos_denorm_positive_zero_output() "denormal-fp-math"=
   ret float %ret
 }
 
-define float @canonicalize_pos_denorm_positive_zero_input() "denormal-fp-math"="ieee,positive-zero" {
+define float @canonicalize_pos_denorm_positive_zero_input() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_positive_zero_input(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -129,7 +129,7 @@ define float @canonicalize_pos_denorm_positive_zero_input() "denormal-fp-math"="
   ret float %ret
 }
 
-define float @canonicalize_neg_denorm_positive_zero_output() "denormal-fp-math"="positive-zero,ieee" {
+define float @canonicalize_neg_denorm_positive_zero_output() denormal_fpenv(positivezero|ieee) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_positive_zero_output(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -137,7 +137,7 @@ define float @canonicalize_neg_denorm_positive_zero_output() "denormal-fp-math"=
   ret float %ret
 }
 
-define float @canonicalize_neg_denorm_positive_zero_input() "denormal-fp-math"="ieee,positive-zero" {
+define float @canonicalize_neg_denorm_positive_zero_input() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_positive_zero_input(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -145,7 +145,7 @@ define float @canonicalize_neg_denorm_positive_zero_input() "denormal-fp-math"="
   ret float %ret
 }
 
-define float @canonicalize_pos_denorm_dynamic_dynamic() "denormal-fp-math"="dynamic,dynamic" {
+define float @canonicalize_pos_denorm_dynamic_dynamic() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_dynamic_dynamic(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0x380FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -154,7 +154,7 @@ define float @canonicalize_pos_denorm_dynamic_dynamic() "denormal-fp-math"="dyna
   ret float %ret
 }
 
-define float @canonicalize_neg_denorm_dynamic_dynamic() "denormal-fp-math"="dynamic,dynamic" {
+define float @canonicalize_neg_denorm_dynamic_dynamic() denormal_fpenv(dynamic) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_dynamic_dynamic(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0xB80FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -164,7 +164,7 @@ define float @canonicalize_neg_denorm_dynamic_dynamic() "denormal-fp-math"="dyna
 }
 
 ; Dynamic output - cannot flush
-define float @canonicalize_pos_denorm_dynamic_output() "denormal-fp-math"="dynamic,ieee" {
+define float @canonicalize_pos_denorm_dynamic_output() denormal_fpenv(dynamic|ieee) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_dynamic_output(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0x380FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -174,7 +174,7 @@ define float @canonicalize_pos_denorm_dynamic_output() "denormal-fp-math"="dynam
 }
 
 ; Dynamic output - cannot flush
-define float @canonicalize_neg_denorm_dynamic_output() "denormal-fp-math"="dynamic,ieee" {
+define float @canonicalize_neg_denorm_dynamic_output() denormal_fpenv(dynamic|ieee) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_dynamic_output(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0xB80FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -184,7 +184,7 @@ define float @canonicalize_neg_denorm_dynamic_output() "denormal-fp-math"="dynam
 }
 
 ; Dynamic input - cannot flush
-define float @canonicalize_pos_denorm_dynamic_input() "denormal-fp-math"="ieee,dynamic" {
+define float @canonicalize_pos_denorm_dynamic_input() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_dynamic_input(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0x380FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -194,7 +194,7 @@ define float @canonicalize_pos_denorm_dynamic_input() "denormal-fp-math"="ieee,d
 }
 
 ; Dynamic input - cannot flush
-define float @canonicalize_neg_denorm_dynamic_input() "denormal-fp-math"="ieee,dynamic" {
+define float @canonicalize_neg_denorm_dynamic_input() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_dynamic_input(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0xB80FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -204,7 +204,7 @@ define float @canonicalize_neg_denorm_dynamic_input() "denormal-fp-math"="ieee,d
 }
 
 ; Input is flushed, can fold
-define float @canonicalize_pos_denorm_dynamic_output_preserve_sign_input() "denormal-fp-math"="dynamic,preserve-sign" {
+define float @canonicalize_pos_denorm_dynamic_output_preserve_sign_input() denormal_fpenv(dynamic|preservesign) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_dynamic_output_preserve_sign_input(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -213,7 +213,7 @@ define float @canonicalize_pos_denorm_dynamic_output_preserve_sign_input() "deno
 }
 
 ; Input is flushed, can fold
-define float @canonicalize_neg_denorm_dynamic_output_preserve_sign_input() "denormal-fp-math"="dynamic,preserve-sign" {
+define float @canonicalize_neg_denorm_dynamic_output_preserve_sign_input() denormal_fpenv(dynamic|preservesign) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_dynamic_output_preserve_sign_input(
 ; CHECK-NEXT:    ret float -0.000000e+00
 ;
@@ -222,7 +222,7 @@ define float @canonicalize_neg_denorm_dynamic_output_preserve_sign_input() "deno
 }
 
 ; Output is known flushed, can fold
-define float @canonicalize_pos_preserve_sign_output_denorm_dynamic_input() "denormal-fp-math"="preserve-sign,dynamic" {
+define float @canonicalize_pos_preserve_sign_output_denorm_dynamic_input() denormal_fpenv(preservesign|dynamic) {
 ; CHECK-LABEL: @canonicalize_pos_preserve_sign_output_denorm_dynamic_input(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0x380FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -232,7 +232,7 @@ define float @canonicalize_pos_preserve_sign_output_denorm_dynamic_input() "deno
 }
 
 ; Output is known flushed, can fold
-define float @canonicalize_neg_denorm_preserve_sign_output_dynamic_input() "denormal-fp-math"="preserve-sign,dynamic" {
+define float @canonicalize_neg_denorm_preserve_sign_output_dynamic_input() denormal_fpenv(preservesign|dynamic) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_preserve_sign_output_dynamic_input(
 ; CHECK-NEXT:    [[RET:%.*]] = call float @llvm.canonicalize.f32(float 0xB80FFFFFC0000000)
 ; CHECK-NEXT:    ret float [[RET]]
@@ -291,7 +291,7 @@ define float @canonicalize_neg_normal() {
   ret float %ret
 }
 
-define <2 x float> @canonicalize_pos_denorm_preserve_sign_output_mixed_vector() "denormal-fp-math"="preserve-sign,ieee" {
+define <2 x float> @canonicalize_pos_denorm_preserve_sign_output_mixed_vector() denormal_fpenv(preservesign|ieee) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_preserve_sign_output_mixed_vector(
 ; CHECK-NEXT:    ret <2 x float> <float 0.000000e+00, float -0.000000e+00>
 ;
@@ -299,7 +299,7 @@ define <2 x float> @canonicalize_pos_denorm_preserve_sign_output_mixed_vector()
   ret <2 x float> %ret
 }
 
-define <2 x float> @canonicalize_pos_denorm_preserve_sign_input_mixed_vector() "denormal-fp-math"="ieee,preserve-sign" {
+define <2 x float> @canonicalize_pos_denorm_preserve_sign_input_mixed_vector() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_preserve_sign_input_mixed_vector(
 ; CHECK-NEXT:    ret <2 x float> <float -0.000000e+00, float 0.000000e+00>
 ;
@@ -307,7 +307,7 @@ define <2 x float> @canonicalize_pos_denorm_preserve_sign_input_mixed_vector() "
   ret <2 x float> %ret
 }
 
-define <2 x float> @canonicalize_pos_denorm_positive_zero_output_mixed_vector() "denormal-fp-math"="positive-zero,ieee" {
+define <2 x float> @canonicalize_pos_denorm_positive_zero_output_mixed_vector() denormal_fpenv(positivezero|ieee) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_positive_zero_output_mixed_vector(
 ; CHECK-NEXT:    ret <2 x float> zeroinitializer
 ;
@@ -315,7 +315,7 @@ define <2 x float> @canonicalize_pos_denorm_positive_zero_output_mixed_vector()
   ret <2 x float> %ret
 }
 
-define <2 x float> @canonicalize_pos_denorm_positive_zero_input_mixed_vector() "denormal-fp-math"="ieee,positive-zero" {
+define <2 x float> @canonicalize_pos_denorm_positive_zero_input_mixed_vector() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @canonicalize_pos_denorm_positive_zero_input_mixed_vector(
 ; CHECK-NEXT:    ret <2 x float> zeroinitializer
 ;
@@ -323,7 +323,7 @@ define <2 x float> @canonicalize_pos_denorm_positive_zero_input_mixed_vector() "
   ret <2 x float> %ret
 }
 
-define float @canonicalize_neg_denorm_preserve_sign_output_positive_zero_input() "denormal-fp-math"="preserve-sign,positive-zero" {
+define float @canonicalize_neg_denorm_preserve_sign_output_positive_zero_input() denormal_fpenv(preservesign|positivezero) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_preserve_sign_output_positive_zero_input(
 ; CHECK-NEXT:    ret float 0.000000e+00
 ;
@@ -331,7 +331,7 @@ define float @canonicalize_neg_denorm_preserve_sign_output_positive_zero_input()
   ret float %ret
 }
 
-define float @canonicalize_neg_denorm_positive_zero_output_preserve_sign_input() "denormal-fp-math"="positive-zero,preserve-sign" {
+define float @canonicalize_neg_denorm_positive_zero_output_preserve_sign_input() denormal_fpenv(positivezero|preservesign) {
 ; CHECK-LABEL: @canonicalize_neg_denorm_positive_zero_output_preserve_sign_input(
 ; CHECK-NEXT:    ret float -0.000000e+00
 ;
@@ -766,7 +766,7 @@ define ppc_fp128 @canonicalize_neg1.0_ppcf128() {
 ; Test folds of using canonicalize + is.fpclass to inspect the denormal mode.
 ; --------------------------------------------------------------------
 
-define i1 @is_poszero_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic" {
+define i1 @is_poszero_daz_enabled_check_dynamic() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_dynamic(
 ; CHECK-NEXT:    [[CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float 0x36A0000000000000)
 ; CHECK-NEXT:    [[IS_POS_ZERO:%.*]] = call i1 @llvm.is.fpclass.f32(float [[CANONICAL]], i32 64)
@@ -777,7 +777,7 @@ define i1 @is_poszero_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynam
   ret i1 %is.pos.zero
 }
 
-define i1 @is_preserve_sign_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic" {
+define i1 @is_preserve_sign_daz_enabled_check_dynamic() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @is_preserve_sign_daz_enabled_check_dynamic(
 ; CHECK-NEXT:    [[CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float 0xB6A0000000000000)
 ; CHECK-NEXT:    [[IS_NEG_ZERO:%.*]] = call i1 @llvm.is.fpclass.f32(float [[CANONICAL]], i32 32)
@@ -788,7 +788,7 @@ define i1 @is_preserve_sign_daz_enabled_check_dynamic() "denormal-fp-math"="ieee
   ret i1 %is.neg.zero
 }
 
-define i1 @is_positive_zero_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic" {
+define i1 @is_positive_zero_daz_enabled_check_dynamic() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @is_positive_zero_daz_enabled_check_dynamic(
 ; CHECK-NEXT:    [[CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float 0xB6A0000000000000)
 ; CHECK-NEXT:    [[IS_POS_ZERO:%.*]] = call i1 @llvm.is.fpclass.f32(float [[CANONICAL]], i32 64)
@@ -799,7 +799,7 @@ define i1 @is_positive_zero_daz_enabled_check_dynamic() "denormal-fp-math"="ieee
   ret i1 %is.pos.zero
 }
 
-define i1 @is_any_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic" {
+define i1 @is_any_daz_enabled_check_dynamic() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @is_any_daz_enabled_check_dynamic(
 ; CHECK-NEXT:    [[CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float 0xB6A0000000000000)
 ; CHECK-NEXT:    [[IS_ANY_ZERO:%.*]] = call i1 @llvm.is.fpclass.f32(float [[CANONICAL]], i32 96)
@@ -810,7 +810,7 @@ define i1 @is_any_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic"
   ret i1 %is.any.zero
 }
 
-define i1 @is_not_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic" {
+define i1 @is_not_daz_enabled_check_dynamic() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @is_not_daz_enabled_check_dynamic(
 ; CHECK-NEXT:    [[CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float 0x36A0000000000000)
 ; CHECK-NEXT:    [[IS_NOT_POS_ZERO:%.*]] = call i1 @llvm.is.fpclass.f32(float [[CANONICAL]], i32 959)
@@ -821,7 +821,7 @@ define i1 @is_not_daz_enabled_check_dynamic() "denormal-fp-math"="ieee,dynamic"
   ret i1 %is.not.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
+define i1 @is_poszero_daz_enabled_check_ieee() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_ieee(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -830,7 +830,7 @@ define i1 @is_poszero_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
   ret i1 %is.pos.zero
 }
 
-define i1 @is_preserve_sign_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
+define i1 @is_preserve_sign_daz_enabled_check_ieee() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: @is_preserve_sign_daz_enabled_check_ieee(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -839,7 +839,7 @@ define i1 @is_preserve_sign_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ie
   ret i1 %is.neg.zero
 }
 
-define i1 @is_positive_zero_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
+define i1 @is_positive_zero_daz_enabled_check_ieee() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: @is_positive_zero_daz_enabled_check_ieee(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -848,7 +848,7 @@ define i1 @is_positive_zero_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ie
   ret i1 %is.pos.zero
 }
 
-define i1 @is_any_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
+define i1 @is_any_daz_enabled_check_ieee() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: @is_any_daz_enabled_check_ieee(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -857,7 +857,7 @@ define i1 @is_any_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
   ret i1 %is.any.zero
 }
 
-define i1 @is_not_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
+define i1 @is_not_daz_enabled_check_ieee() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: @is_not_daz_enabled_check_ieee(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -866,7 +866,7 @@ define i1 @is_not_daz_enabled_check_ieee() "denormal-fp-math"="ieee,ieee" {
   ret i1 %is.not.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @is_poszero_daz_enabled_check_preserve_sign() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_preserve_sign(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -875,7 +875,7 @@ define i1 @is_poszero_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee
   ret i1 %is.pos.zero
 }
 
-define i1 @is_preserve_sign_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @is_preserve_sign_daz_enabled_check_preserve_sign() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @is_preserve_sign_daz_enabled_check_preserve_sign(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -884,7 +884,7 @@ define i1 @is_preserve_sign_daz_enabled_check_preserve_sign() "denormal-fp-math"
   ret i1 %is.neg.zero
 }
 
-define i1 @is_positive_zero_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @is_positive_zero_daz_enabled_check_preserve_sign() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @is_positive_zero_daz_enabled_check_preserve_sign(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -893,7 +893,7 @@ define i1 @is_positive_zero_daz_enabled_check_preserve_sign() "denormal-fp-math"
   ret i1 %is.pos.zero
 }
 
-define i1 @is_any_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @is_any_daz_enabled_check_preserve_sign() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @is_any_daz_enabled_check_preserve_sign(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -902,7 +902,7 @@ define i1 @is_any_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,pre
   ret i1 %is.any.zero
 }
 
-define i1 @is_not_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @is_not_daz_enabled_check_preserve_sign() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @is_not_daz_enabled_check_preserve_sign(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -911,7 +911,7 @@ define i1 @is_not_daz_enabled_check_preserve_sign() "denormal-fp-math"="ieee,pre
   ret i1 %is.not.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,positive-zero" {
+define i1 @is_poszero_daz_enabled_check_positive_zero() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_positive_zero(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -920,7 +920,7 @@ define i1 @is_poszero_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee
   ret i1 %is.pos.zero
 }
 
-define i1 @is_preserve_sign_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,positive-zero" {
+define i1 @is_preserve_sign_daz_enabled_check_positive_zero() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @is_preserve_sign_daz_enabled_check_positive_zero(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -929,7 +929,7 @@ define i1 @is_preserve_sign_daz_enabled_check_positive_zero() "denormal-fp-math"
   ret i1 %is.neg.zero
 }
 
-define i1 @is_positive_zero_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,positive-zero" {
+define i1 @is_positive_zero_daz_enabled_check_positive_zero() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @is_positive_zero_daz_enabled_check_positive_zero(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -938,7 +938,7 @@ define i1 @is_positive_zero_daz_enabled_check_positive_zero() "denormal-fp-math"
   ret i1 %is.pos.zero
 }
 
-define i1 @is_any_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,positive-zero" {
+define i1 @is_any_daz_enabled_check_positive_zero() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @is_any_daz_enabled_check_positive_zero(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -947,7 +947,7 @@ define i1 @is_any_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,pos
   ret i1 %is.any.zero
 }
 
-define i1 @is_not_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,positive-zero" {
+define i1 @is_not_daz_enabled_check_positive_zero() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @is_not_daz_enabled_check_positive_zero(
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -956,7 +956,7 @@ define i1 @is_not_daz_enabled_check_positive_zero() "denormal-fp-math"="ieee,pos
   ret i1 %is.not.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_dynamic_bitcast() "denormal-fp-math"="ieee,dynamic" {
+define i1 @is_poszero_daz_enabled_check_dynamic_bitcast() denormal_fpenv(ieee|dynamic) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_dynamic_bitcast(
 ; CHECK-NEXT:    [[CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float 0x36A0000000000000)
 ; CHECK-NEXT:    [[BITCAST:%.*]] = bitcast float [[CANONICAL]] to i32
@@ -969,7 +969,7 @@ define i1 @is_poszero_daz_enabled_check_dynamic_bitcast() "denormal-fp-math"="ie
   ret i1 %is.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_preserve_sign_bitcast() "denormal-fp-math"="ieee,preserve-sign" {
+define i1 @is_poszero_daz_enabled_check_preserve_sign_bitcast() denormal_fpenv(ieee|preservesign) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_preserve_sign_bitcast(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -979,7 +979,7 @@ define i1 @is_poszero_daz_enabled_check_preserve_sign_bitcast() "denormal-fp-mat
   ret i1 %is.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_positive_zero_bitcast() "denormal-fp-math"="ieee,positive-zero" {
+define i1 @is_poszero_daz_enabled_check_positive_zero_bitcast() denormal_fpenv(ieee|positivezero) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_positive_zero_bitcast(
 ; CHECK-NEXT:    ret i1 true
 ;
@@ -989,7 +989,7 @@ define i1 @is_poszero_daz_enabled_check_positive_zero_bitcast() "denormal-fp-mat
   ret i1 %is.pos.zero
 }
 
-define i1 @is_poszero_daz_enabled_check_ieee_bitcast() "denormal-fp-math"="ieee,ieee" {
+define i1 @is_poszero_daz_enabled_check_ieee_bitcast() denormal_fpenv(ieee|ieee) {
 ; CHECK-LABEL: @is_poszero_daz_enabled_check_ieee_bitcast(
 ; CHECK-NEXT:    ret i1 false
 ;

diff  --git a/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll b/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
index bcd75898a4ffd..16d5f0d2871b1 100644
--- a/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
+++ b/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll
@@ -1203,16 +1203,16 @@ define i1 @fcmp_double_dynamic_dynamic_normals() #11 {
   ret i1 %cmp
 }
 
-attributes #0 = { nounwind "denormal-fp-math"="ieee,ieee" }
-attributes #1 = { nounwind "denormal-fp-math"="positive-zero,ieee" }
-attributes #2 = { nounwind "denormal-fp-math"="preserve-sign,ieee" }
-attributes #3 = { nounwind "denormal-fp-math"="ieee,positive-zero" }
-attributes #4 = { nounwind "denormal-fp-math"="ieee,preserve-sign" }
-attributes #5 = { nounwind "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="positive-zero,ieee" }
-attributes #6 = { nounwind "denormal-fp-math"="positive-zero,positive-zero" }
-attributes #7 = { nounwind "denormal-fp-math"="preserve-sign,preserve-sign" }
-attributes #8 = { nounwind "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="positive-zero,positive-zero" }
-attributes #9 = { nounwind "denormal-fp-math"="dynamic,ieee" }
-attributes #10 = { nounwind "denormal-fp-math"="ieee,dynamic" }
-attributes #11 = { nounwind "denormal-fp-math"="dynamic,dynamic" }
-attributes #12 = { nounwind "denormal-fp-math"="dynamic,preserve-sign" }
+attributes #0 = { nounwind denormal_fpenv(ieee) }
+attributes #1 = { nounwind denormal_fpenv(positivezero|ieee) }
+attributes #2 = { nounwind denormal_fpenv(preservesign|ieee) }
+attributes #3 = { nounwind denormal_fpenv(ieee|positivezero) }
+attributes #4 = { nounwind denormal_fpenv(ieee|preservesign) }
+attributes #5 = { nounwind denormal_fpenv(ieee, float: positivezero|ieee) }
+attributes #6 = { nounwind denormal_fpenv(positivezero|positivezero) }
+attributes #7 = { nounwind denormal_fpenv(preservesign) }
+attributes #8 = { nounwind denormal_fpenv(ieee, float: positivezero) }
+attributes #9 = { nounwind denormal_fpenv(dynamic|ieee) }
+attributes #10 = { nounwind denormal_fpenv(ieee|dynamic) }
+attributes #11 = { nounwind denormal_fpenv(dynamic) }
+attributes #12 = { nounwind denormal_fpenv(dynamic|preservesign) }

diff  --git a/llvm/test/Transforms/InstSimplify/floating-point-compare.ll b/llvm/test/Transforms/InstSimplify/floating-point-compare.ll
index 4286d978555e1..259d98acb1a2d 100644
--- a/llvm/test/Transforms/InstSimplify/floating-point-compare.ll
+++ b/llvm/test/Transforms/InstSimplify/floating-point-compare.ll
@@ -1720,7 +1720,7 @@ bb:
   ret float %i5
 }
 
-define i1 @is_olt_smallest_normal_dynamic(float %x) "denormal-fp-math"="dynamic,dynamic" {
+define i1 @is_olt_smallest_normal_dynamic(float %x) denormal_fpenv(dynamic) {
 ; CHECK-LABEL: @is_olt_smallest_normal_dynamic(
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[X:%.*]], 0x3810000000000000
 ; CHECK-NEXT:    ret i1 [[IS_DENORM_OR_ZERO]]
@@ -1729,7 +1729,7 @@ define i1 @is_olt_smallest_normal_dynamic(float %x) "denormal-fp-math"="dynamic,
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_olt_smallest_normal_ieee(float %x) "denormal-fp-math"="dynamic,ieee" {
+define i1 @is_olt_smallest_normal_ieee(float %x) denormal_fpenv(dynamic|ieee) {
 ; CHECK-LABEL: @is_olt_smallest_normal_ieee(
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[X:%.*]], 0x3810000000000000
 ; CHECK-NEXT:    ret i1 [[IS_DENORM_OR_ZERO]]
@@ -1738,7 +1738,7 @@ define i1 @is_olt_smallest_normal_ieee(float %x) "denormal-fp-math"="dynamic,iee
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_olt_smallest_normal_preserve_sign(float %x) "denormal-fp-math"="dynamic,preserve-sign" {
+define i1 @is_olt_smallest_normal_preserve_sign(float %x) denormal_fpenv(dynamic|preservesign) {
 ; CHECK-LABEL: @is_olt_smallest_normal_preserve_sign(
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[X:%.*]], 0x3810000000000000
 ; CHECK-NEXT:    ret i1 [[IS_DENORM_OR_ZERO]]
@@ -1747,7 +1747,7 @@ define i1 @is_olt_smallest_normal_preserve_sign(float %x) "denormal-fp-math"="dy
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_olt_smallest_normal_positive_zero(float %x) "denormal-fp-math"="dynamic,positive-zero" {
+define i1 @is_olt_smallest_normal_positive_zero(float %x) denormal_fpenv(dynamic|positivezero) {
 ; CHECK-LABEL: @is_olt_smallest_normal_positive_zero(
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[X:%.*]], 0x3810000000000000
 ; CHECK-NEXT:    ret i1 [[IS_DENORM_OR_ZERO]]
@@ -1756,7 +1756,7 @@ define i1 @is_olt_smallest_normal_positive_zero(float %x) "denormal-fp-math"="dy
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_fabs_olt_smallest_normal_dynamic(float %x) "denormal-fp-math"="dynamic,dynamic" {
+define i1 @is_fabs_olt_smallest_normal_dynamic(float %x) denormal_fpenv(dynamic) {
 ; CHECK-LABEL: @is_fabs_olt_smallest_normal_dynamic(
 ; CHECK-NEXT:    [[FABS_X:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[FABS_X]], 0x3810000000000000
@@ -1767,7 +1767,7 @@ define i1 @is_fabs_olt_smallest_normal_dynamic(float %x) "denormal-fp-math"="dyn
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_fabs_olt_smallest_normal_ieee(float %x) "denormal-fp-math"="dynamic,ieee" {
+define i1 @is_fabs_olt_smallest_normal_ieee(float %x) denormal_fpenv(dynamic|ieee) {
 ; CHECK-LABEL: @is_fabs_olt_smallest_normal_ieee(
 ; CHECK-NEXT:    [[FABS_X:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[FABS_X]], 0x3810000000000000
@@ -1778,7 +1778,7 @@ define i1 @is_fabs_olt_smallest_normal_ieee(float %x) "denormal-fp-math"="dynami
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_fabs_olt_smallest_normal_preserve_sign(float %x) "denormal-fp-math"="dynamic,preserve-sign" {
+define i1 @is_fabs_olt_smallest_normal_preserve_sign(float %x) denormal_fpenv(dynamic|preservesign) {
 ; CHECK-LABEL: @is_fabs_olt_smallest_normal_preserve_sign(
 ; CHECK-NEXT:    [[FABS_X:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[FABS_X]], 0x3810000000000000
@@ -1789,7 +1789,7 @@ define i1 @is_fabs_olt_smallest_normal_preserve_sign(float %x) "denormal-fp-math
   ret i1 %is.denorm.or.zero
 }
 
-define i1 @is_fabs_olt_smallest_normal_positive_zero(float %x) "denormal-fp-math"="dynamic,positive-zero" {
+define i1 @is_fabs_olt_smallest_normal_positive_zero(float %x) denormal_fpenv(dynamic|positivezero) {
 ; CHECK-LABEL: @is_fabs_olt_smallest_normal_positive_zero(
 ; CHECK-NEXT:    [[FABS_X:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
 ; CHECK-NEXT:    [[IS_DENORM_OR_ZERO:%.*]] = fcmp olt float [[FABS_X]], 0x3810000000000000
@@ -1821,4 +1821,4 @@ declare double @llvm.copysign.f64(double, double)
 declare half @llvm.fabs.f16(half)
 declare void @llvm.assume(i1 noundef)
 
-attributes #0 = { "denormal-fp-math"="preserve-sign,preserve-sign" }
+attributes #0 = { denormal_fpenv(preservesign) }

diff  --git a/llvm/test/Transforms/SCCP/float-denormal-simplification.ll b/llvm/test/Transforms/SCCP/float-denormal-simplification.ll
index fec9883aabddd..b99c4025490ac 100644
--- a/llvm/test/Transforms/SCCP/float-denormal-simplification.ll
+++ b/llvm/test/Transforms/SCCP/float-denormal-simplification.ll
@@ -17,5 +17,5 @@ define float @test_preserve_sign() #1 {
   ret float %1
 }
 
-attributes #0 = {"denormal-fp-math"="ieee,ieee"}
-attributes #1 = {"denormal-fp-math"="preserve-sign,preserve-sign"}
+attributes #0 = {denormal_fpenv(ieee|ieee)}
+attributes #1 = {denormal_fpenv(preservesign)}

diff  --git a/llvm/test/Transforms/SCCP/no-fold-fcmp-dynamic-denormal-mode-issue114947.ll b/llvm/test/Transforms/SCCP/no-fold-fcmp-dynamic-denormal-mode-issue114947.ll
index 285122d104c7e..78609ec8b0e51 100644
--- a/llvm/test/Transforms/SCCP/no-fold-fcmp-dynamic-denormal-mode-issue114947.ll
+++ b/llvm/test/Transforms/SCCP/no-fold-fcmp-dynamic-denormal-mode-issue114947.ll
@@ -116,4 +116,4 @@ define <vscale x 2 x i1> @no_fold_fcmp_denormal_double_ieee_dynamic_scalable_vec
   ret <vscale x 2 x i1> %cmp
 }
 
-attributes #0 = { "denormal-fp-math"="ieee,dynamic" }
+attributes #0 = { denormal_fpenv(ieee|dynamic) }

diff  --git a/llvm/test/Verifier/denormal-fp-math.ll b/llvm/test/Verifier/denormal-fp-math.ll
deleted file mode 100644
index f79f28a366944..0000000000000
--- a/llvm/test/Verifier/denormal-fp-math.ll
+++ /dev/null
@@ -1,20 +0,0 @@
-; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck --implicit-check-not="invalid value" %s
-
-define float @test_denormal_fp_math_valid() "denormal-fp-math"="ieee,ieee" {
-  ret float 1.0
-}
-
-; CHECK: invalid value for 'denormal-fp-math' attribute: foo,ieee
-define float @test_denormal_fp_math_invalid1() "denormal-fp-math"="foo,ieee" {
-  ret float 1.0
-}
-
-; CHECK: invalid value for 'denormal-fp-math' attribute: ieee,ieee,ieee
-define float @test_denormal_fp_math_invalid2() "denormal-fp-math"="ieee,ieee,ieee" {
-  ret float 1.0
-}
-
-; CHECK: invalid value for 'denormal-fp-math-f32' attribute: foo,ieee
-define float @test_denormal_fp_math_f32_invalid() "denormal-fp-math-f32"="foo,ieee" {
-  ret float 1.0
-}

diff  --git a/llvm/test/Verifier/denormal_fpenv.ll b/llvm/test/Verifier/denormal_fpenv.ll
new file mode 100644
index 0000000000000..0e4ad7c083c11
--- /dev/null
+++ b/llvm/test/Verifier/denormal_fpenv.ll
@@ -0,0 +1,10 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+
+declare void @func()
+
+; CHECK: denormal_fpenv attribute may not apply to call sites
+; CHECK-NEXT: call void @func() #0
+define void @no_callsites() {
+  call void @func() denormal_fpenv(preservesign)
+  ret void
+}

diff  --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll
index ef601189855b4..aac338e825840 100644
--- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll
@@ -95,7 +95,7 @@ for.end:                                          ; preds = %for.cond.cleanup
   ret void, !dbg !62
 }
 
-attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 attributes #2 = { nounwind }
 

diff  --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.expected
index 4bae52e7b3815..f7ad99b55e072 100644
--- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.expected
@@ -165,7 +165,7 @@ for.end:                                          ; preds = %for.cond.cleanup
   ret void, !dbg !62
 }
 
-attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 attributes #2 = { nounwind }
 

diff  --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.expected
index 12c6e4eee0147..097fb90dc3f9c 100644
--- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.expected
@@ -167,7 +167,7 @@ for.end:                                          ; preds = %for.cond.cleanup
   ret void, !dbg !62
 }
 
-attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 attributes #2 = { nounwind }
 

diff  --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.globals.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.globals.expected
index d67a303236369..0e23016cd2e60 100644
--- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.globals.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.globals.expected
@@ -167,7 +167,7 @@ for.end:                                          ; preds = %for.cond.cleanup
   ret void, !dbg !62
 }
 
-attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 attributes #2 = { nounwind }
 
@@ -239,7 +239,7 @@ attributes #2 = { nounwind }
 !61 = !DILocation(line: 10, column: 12, scope: !45)
 !62 = !DILocation(line: 11, column: 1, scope: !41)
 ;.
-; CHECK: attributes #[[ATTR0]] = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+; CHECK: attributes #[[ATTR0]] = { nounwind denormal_fpenv(ieee) uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 ; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 ; CHECK: attributes #[[ATTR2]] = { nounwind }
 ;.

diff  --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.noglobals.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.noglobals.expected
index 4bae52e7b3815..f7ad99b55e072 100644
--- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.noglobals.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.noglobals.expected
@@ -165,7 +165,7 @@ for.end:                                          ; preds = %for.cond.cleanup
   ret void, !dbg !62
 }
 
-attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 attributes #2 = { nounwind }
 

diff  --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.transitiveglobals.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.transitiveglobals.expected
index fb3a76f305e6a..762af848e14ba 100644
--- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.transitiveglobals.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values_dbgrecords.ll.funcsig.transitiveglobals.expected
@@ -165,7 +165,7 @@ for.end:                                          ; preds = %for.cond.cleanup
   ret void, !dbg !62
 }
 
-attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" denormal_fpenv(ieee) "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 attributes #2 = { nounwind }
 

diff  --git a/llvm/unittests/ADT/FloatingPointMode.cpp b/llvm/unittests/ADT/FloatingPointMode.cpp
index cbdd64aeb00cd..3f9e71a16f523 100644
--- a/llvm/unittests/ADT/FloatingPointMode.cpp
+++ b/llvm/unittests/ADT/FloatingPointMode.cpp
@@ -25,6 +25,10 @@ TEST(FloatingPointModeTest, ParseDenormalFPAttributeComponent) {
             parseDenormalFPAttributeComponent("preserve-sign"));
   EXPECT_EQ(DenormalMode::PositiveZero,
             parseDenormalFPAttributeComponent("positive-zero"));
+  EXPECT_EQ(DenormalMode::PreserveSign,
+            parseDenormalFPAttributeComponent("preservesign"));
+  EXPECT_EQ(DenormalMode::PositiveZero,
+            parseDenormalFPAttributeComponent("positivezero"));
   EXPECT_EQ(DenormalMode::Dynamic,
             parseDenormalFPAttributeComponent("dynamic"));
   EXPECT_EQ(DenormalMode::Invalid, parseDenormalFPAttributeComponent("foo"));
@@ -102,10 +106,16 @@ TEST(FloatingPointModeTest, RenderDenormalFPAttribute) {
   EXPECT_EQ(
     "preserve-sign,preserve-sign",
     DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign).str());
+  EXPECT_EQ("preserve-sign,preserve-sign",
+            DenormalMode(DenormalMode::PreserveSign, DenormalMode::PreserveSign)
+                .str());
 
   EXPECT_EQ(
     "positive-zero,positive-zero",
     DenormalMode(DenormalMode::PositiveZero, DenormalMode::PositiveZero).str());
+  EXPECT_EQ("positive-zero,positive-zero",
+            DenormalMode(DenormalMode::PositiveZero, DenormalMode::PositiveZero)
+                .str());
 
   EXPECT_EQ(
     "ieee,preserve-sign",

diff  --git a/llvm/unittests/IR/AttributesTest.cpp b/llvm/unittests/IR/AttributesTest.cpp
index 50270024f20f0..5e795ef771713 100644
--- a/llvm/unittests/IR/AttributesTest.cpp
+++ b/llvm/unittests/IR/AttributesTest.cpp
@@ -344,6 +344,36 @@ TEST(Attributes, ConstantRangeAttributeCAPI) {
   }
 }
 
+TEST(Attributes, DenormalFPEnvAttributeCAPI) {
+  LLVMContext C;
+  LLVMContextRef CtxC = wrap(&C);
+  {
+    LLVMAttributeRef CAttr = LLVMCreateDenormalFPEnvAttribute(
+        CtxC, LLVMDenormalModeKindIEEE, LLVMDenormalModeKindIEEE,
+        LLVMDenormalModeKindPreserveSign, LLVMDenormalModeKindPreserveSign);
+
+    Attribute OutAttr = unwrap(CAttr);
+
+    EXPECT_EQ(OutAttr.getDenormalFPEnv(),
+              DenormalFPEnv(DenormalMode::getIEEE(),
+                            DenormalMode::getPreserveSign()));
+  }
+
+  {
+    LLVMAttributeRef CAttr = LLVMCreateDenormalFPEnvAttribute(
+        CtxC, LLVMDenormalModeKindDynamic, LLVMDenormalModeKindPositiveZero,
+        LLVMDenormalModeKindPreserveSign, LLVMDenormalModeKindIEEE);
+
+    Attribute OutAttr = unwrap(CAttr);
+
+    EXPECT_EQ(
+        OutAttr.getDenormalFPEnv(),
+        DenormalFPEnv(
+            DenormalMode(DenormalMode::Dynamic, DenormalMode::PositiveZero),
+            DenormalMode(DenormalMode::PreserveSign, DenormalMode::IEEE)));
+  }
+}
+
 TEST(Attributes, CalleeAttributes) {
   const char *IRString = R"IR(
     declare void @f1(i32 %i)

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index f21a03976dd86..0bcf8a4d6bb34 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -944,6 +944,18 @@ def LLVM_MemoryEffectsAttr : LLVM_Attr<"MemoryEffects", "memory_effects"> {
   let assemblyFormat = "`<` struct(params) `>`";
 }
 
+//===----------------------------------------------------------------------===//
+// DenormalFPEnvAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_DenormalFPEnvAttr : LLVM_Attr<"DenormalFPEnv", "denormal_fpenv"> {
+  let parameters = (ins "DenormalModeKind":$default_output_mode,
+                        "DenormalModeKind":$default_input_mode,
+                        "DenormalModeKind":$float_output_mode,
+                        "DenormalModeKind":$float_input_mode);
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
 //===----------------------------------------------------------------------===//
 // AliasScopeDomainAttr
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td
index e2edab44153ca..6125ac0e328dd 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td
@@ -806,6 +806,32 @@ def RoundingModeAttr : LLVM_EnumAttr<
 
 def ValidRoundingModeAttr : ConfinedAttr<RoundingModeAttr, [IntMinValue<0>]>;
 
+//===----------------------------------------------------------------------===//
+// DenormalModeKind
+//===----------------------------------------------------------------------===//
+
+def DenormalModeIEEE : LLVM_EnumAttrCase<"IEEE", "ieee", "IEEE", 0>;
+def DenormalModePreserveSign
+    : LLVM_EnumAttrCase<"PreserveSign", "preservesign", "PreserveSign", 1>;
+def DenormalModePositiveZero
+    : LLVM_EnumAttrCase<"PositiveZero", "positivezero", "PositiveZero", 2>;
+def DenormalModeDynamic : LLVM_EnumAttrCase<"Dynamic", "dynamic", "Dynamic", 3>;
+// Needed as llvm::DenormalModeKind defines this.
+def DenormalModeInvalid
+    : LLVM_EnumAttrCase<"Invalid", "invalid", "Invalid", -1>;
+
+def DenormalModeKindAttr : LLVM_EnumAttr<
+    "DenormalModeKind",
+    "::llvm::DenormalMode::DenormalModeKind",
+    "LLVM Denormal Mode Kinds",
+    [DenormalModeIEEE,
+     DenormalModePreserveSign,
+     DenormalModePositiveZero,
+     DenormalModeDynamic,
+     DenormalModeInvalid]> {
+  let cppNamespace = "::mlir::LLVM";
+}
+
 //===----------------------------------------------------------------------===//
 // FPExceptionBehavior
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index a03a933eed370..c57d0a15c404a 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1988,8 +1988,7 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
     OptionalAttr<BoolAttr>:$no_infs_fp_math,
     OptionalAttr<BoolAttr>:$no_nans_fp_math,
     OptionalAttr<BoolAttr>:$no_signed_zeros_fp_math,
-    OptionalAttr<StrAttr>:$denormal_fp_math,
-    OptionalAttr<StrAttr>:$denormal_fp_math_f32,
+    OptionalAttr<LLVM_DenormalFPEnvAttr>:$denormal_fpenv,
     OptionalAttr<StrAttr>:$fp_contract,
     OptionalAttr<StrAttr>:$instrument_function_entry,
     OptionalAttr<StrAttr>:$instrument_function_exit,

diff  --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 2c1613c890923..1653c2e358d6a 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -2648,6 +2648,23 @@ static void processMemoryEffects(llvm::Function *func, LLVMFuncOp funcOp) {
   funcOp.setMemoryEffectsAttr(memAttr);
 }
 
+static void processDenormalFPEnv(llvm::Function *func, LLVMFuncOp funcOp) {
+  llvm::DenormalFPEnv denormalFpEnv = func->getDenormalFPEnv();
+  // Only set the attr when it does not match the default value.
+  if (denormalFpEnv == llvm::DenormalFPEnv::getDefault())
+    return;
+
+  llvm::DenormalMode defaultMode = denormalFpEnv.DefaultMode;
+  llvm::DenormalMode floatMode = denormalFpEnv.F32Mode;
+
+  auto denormalFpEnvAttr = DenormalFPEnvAttr::get(
+      funcOp.getContext(), convertDenormalModeKindFromLLVM(defaultMode.Output),
+      convertDenormalModeKindFromLLVM(defaultMode.Input),
+      convertDenormalModeKindFromLLVM(floatMode.Output),
+      convertDenormalModeKindFromLLVM(floatMode.Input));
+  funcOp.setDenormalFpenvAttr(denormalFpEnvAttr);
+}
+
 // List of LLVM IR attributes that map to an explicit attribute on the MLIR
 // LLVMFuncOp.
 static constexpr std::array kExplicitLLVMFuncOpAttributes{
@@ -2663,8 +2680,6 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{
     StringLiteral("alwaysinline"),
     StringLiteral("cold"),
     StringLiteral("convergent"),
-    StringLiteral("denormal-fp-math"),
-    StringLiteral("denormal-fp-math-f32"),
     StringLiteral("fp-contract"),
     StringLiteral("frame-pointer"),
     StringLiteral("hot"),
@@ -2690,6 +2705,7 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{
     StringLiteral("uwtable"),
     StringLiteral("vscale_range"),
     StringLiteral("willreturn"),
+    StringLiteral("denormal_fpenv"),
 };
 
 // List of LLVM IR attributes that are handled by prefix to map onto an MLIR
@@ -2760,6 +2776,7 @@ static void processPassthroughAttrs(llvm::Function *func, LLVMFuncOp funcOp) {
 void ModuleImport::processFunctionAttributes(llvm::Function *func,
                                              LLVMFuncOp funcOp) {
   processMemoryEffects(func, funcOp);
+  processDenormalFPEnv(func, funcOp);
   processPassthroughAttrs(func, funcOp);
 
   if (func->hasFnAttribute(llvm::Attribute::NoInline))
@@ -2878,16 +2895,6 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func,
       attr.isStringAttribute())
     funcOp.setNoSignedZerosFpMath(attr.getValueAsBool());
 
-  if (llvm::Attribute attr = func->getFnAttribute("denormal-fp-math");
-      attr.isStringAttribute())
-    funcOp.setDenormalFpMathAttr(
-        StringAttr::get(context, attr.getValueAsString()));
-
-  if (llvm::Attribute attr = func->getFnAttribute("denormal-fp-math-f32");
-      attr.isStringAttribute())
-    funcOp.setDenormalFpMathF32Attr(
-        StringAttr::get(context, attr.getValueAsString()));
-
   if (llvm::Attribute attr = func->getFnAttribute("fp-contract");
       attr.isStringAttribute())
     funcOp.setFpContractAttr(StringAttr::get(context, attr.getValueAsString()));

diff  --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 6ee9b0cc1cee7..7115386c18c13 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1516,6 +1516,7 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
   valueMapping.clear();
   branchMapping.clear();
   llvm::Function *llvmFunc = lookupFunction(func.getName());
+  llvm::LLVMContext &llvmContext = llvmFunc->getContext();
 
   // Add function arguments to the value remapping table.
   for (auto [mlirArg, llvmArg] :
@@ -1578,12 +1579,6 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
     llvmFunc->addFnAttr("no-signed-zeros-fp-math",
                         llvm::toStringRef(*noSignedZerosFpMath));
 
-  if (auto denormalFpMath = func.getDenormalFpMath())
-    llvmFunc->addFnAttr("denormal-fp-math", *denormalFpMath);
-
-  if (auto denormalFpMathF32 = func.getDenormalFpMathF32())
-    llvmFunc->addFnAttr("denormal-fp-math-f32", *denormalFpMathF32);
-
   if (auto fpContract = func.getFpContract())
     llvmFunc->addFnAttr("fp-contract", *fpContract);
 
@@ -1594,7 +1589,6 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
     llvmFunc->addFnAttr("instrument-function-exit", *instrumentFunctionExit);
 
   // First, create all blocks so we can jump to them.
-  llvm::LLVMContext &llvmContext = llvmFunc->getContext();
   for (auto &bb : func) {
     auto *llvmBB = llvm::BasicBlock::Create(llvmContext);
     llvmBB->insertInto(llvmFunc);
@@ -1672,10 +1666,29 @@ ModuleTranslation::convertAllocsizeAttr(DenseI32ArrayAttr allocSizeAttr) {
   return llvm::Attribute::getWithAllocSizeArgs(getLLVMContext(), elemSize,
                                                numElems);
 }
+static void convertDenormalFPEnvAttribute(LLVMFuncOp func,
+                                          llvm::AttrBuilder &Attrs) {
+  std::optional<DenormalFPEnvAttr> denormalFpEnv = func.getDenormalFpenv();
+  if (!denormalFpEnv)
+    return;
+
+  llvm::DenormalMode DefaultMode(
+      convertDenormalModeKindToLLVM(denormalFpEnv->getDefaultOutputMode()),
+      convertDenormalModeKindToLLVM(denormalFpEnv->getDefaultInputMode()));
+  llvm::DenormalMode FloatMode(
+      convertDenormalModeKindToLLVM(denormalFpEnv->getFloatOutputMode()),
+      convertDenormalModeKindToLLVM(denormalFpEnv->getFloatInputMode()));
+
+  llvm::DenormalFPEnv FPEnv(DefaultMode, FloatMode);
+  Attrs.addDenormalFPEnvAttr(FPEnv);
+}
 
 /// Converts function attributes from `func` and attaches them to `llvmFunc`.
 static void convertFunctionAttributes(ModuleTranslation &mod, LLVMFuncOp func,
                                       llvm::Function *llvmFunc) {
+  // FIXME: Use AttrBuilder far all cases
+  llvm::AttrBuilder AttrBuilder(llvmFunc->getContext());
+
   if (func.getNoInlineAttr())
     llvmFunc->addFnAttr(llvm::Attribute::NoInline);
   if (func.getAlwaysInlineAttr())
@@ -1728,6 +1741,9 @@ static void convertFunctionAttributes(ModuleTranslation &mod, LLVMFuncOp func,
     llvmFunc->addFnAttr(attr);
 
   convertFunctionMemoryAttributes(func, llvmFunc);
+
+  convertDenormalFPEnvAttribute(func, AttrBuilder);
+  llvmFunc->addFnAttrs(AttrBuilder);
 }
 
 /// Converts function attributes from `func` and attaches them to `llvmFunc`.

diff  --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 6348511935e0c..6b04f6e5c5622 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -339,15 +339,20 @@ declare void @func_attr_no_signed_zeros_fp_math_false() "no-signed-zeros-fp-math
 
 ; // -----
 
-; CHECK-LABEL: @func_attr_denormal_fp_math_ieee
-; CHECK-SAME: attributes {denormal_fp_math = "ieee"}
-declare void @func_attr_denormal_fp_math_ieee() "denormal-fp-math"="ieee"
+; CHECK-LABEL: @func_attr_denormal_fp_math_ieee(){{$}}
+declare void @func_attr_denormal_fp_math_ieee() denormal_fpenv(ieee)
 
 ; // -----
 
 ; CHECK-LABEL: @func_attr_denormal_fp_math_f32_preserve_sign
-; CHECK-SAME: attributes {denormal_fp_math_f32 = "preserve-sign"}
-declare void @func_attr_denormal_fp_math_f32_preserve_sign() "denormal-fp-math-f32"="preserve-sign"
+; CHECK-SAME: attributes {denormal_fpenv = #llvm.denormal_fpenv<default_output_mode = ieee, default_input_mode = ieee, float_output_mode = preservesign, float_input_mode = preservesign>}
+declare void @func_attr_denormal_fp_math_f32_preserve_sign() denormal_fpenv(float: preservesign)
+
+; // -----
+
+; CHECK-LABEL: @func_attr_mixed_denormal_modes
+; CHECK: attributes {denormal_fpenv = #llvm.denormal_fpenv<default_output_mode = dynamic, default_input_mode = preservesign, float_output_mode = preservesign, float_input_mode = dynamic>}
+declare void @func_attr_mixed_denormal_modes() denormal_fpenv(dynamic|preservesign, float: preservesign|dynamic)
 
 ; // -----
 

diff  --git a/mlir/test/Target/LLVMIR/fp-math-function-attributes.mlir b/mlir/test/Target/LLVMIR/fp-math-function-attributes.mlir
index f76a6cf9d6fa1..6d563e46c62b3 100644
--- a/mlir/test/Target/LLVMIR/fp-math-function-attributes.mlir
+++ b/mlir/test/Target/LLVMIR/fp-math-function-attributes.mlir
@@ -56,19 +56,37 @@ llvm.func @no_signed_zeros_fp_math_func_false() attributes {no_signed_zeros_fp_m
 
 // CHECK-LABEL: define void @denormal_fp_math_func_ieee()
 // CHECK-SAME: #[[ATTRS:[0-9]+]]
-llvm.func @denormal_fp_math_func_ieee() attributes {denormal_fp_math = "ieee"}  {
+llvm.func @denormal_fp_math_func_ieee() attributes { denormal_fpenv = #llvm.denormal_fpenv<default_output_mode = ieee, default_input_mode = ieee, float_output_mode = ieee, float_input_mode = ieee>}  {
   llvm.return
 }
-// CHECK: attributes #[[ATTRS]] = { "denormal-fp-math"="ieee" }
+// CHECK: attributes #[[ATTRS]] = { denormal_fpenv(ieee) }
 
 // -----
 
 // CHECK-LABEL: define void @denormal_fp_math_f32_func_preserve_sign()
 // CHECK-SAME: #[[ATTRS:[0-9]+]]
-llvm.func @denormal_fp_math_f32_func_preserve_sign() attributes {denormal_fp_math_f32 = "preserve-sign"}  {
+llvm.func @denormal_fp_math_f32_func_preserve_sign() attributes {denormal_fpenv = #llvm.denormal_fpenv<default_output_mode = ieee, default_input_mode = ieee, float_output_mode = preservesign, float_input_mode = preservesign>}  {
   llvm.return
 }
-// CHECK: attributes #[[ATTRS]] = { "denormal-fp-math-f32"="preserve-sign" }
+// CHECK: attributes #[[ATTRS]] = { denormal_fpenv(float: preservesign) }
+
+// -----
+
+// CHECK-LABEL: define void @denormal_fp_math_dynamic_f32_func_preserve_sign()
+// CHECK-SAME: #[[ATTRS:[0-9]+]]
+llvm.func @denormal_fp_math_dynamic_f32_func_preserve_sign() attributes {denormal_fpenv = #llvm.denormal_fpenv<default_output_mode = dynamic, default_input_mode = dynamic, float_output_mode = preservesign, float_input_mode = preservesign>}  {
+  llvm.return
+}
+// CHECK: attributes #[[ATTRS]] = { denormal_fpenv(dynamic, float: preservesign) }
+
+// -----
+
+// CHECK-LABEL: define void @denormal_fp_math_mixed_modes()
+// CHECK-SAME: #[[ATTRS:[0-9]+]]
+llvm.func @denormal_fp_math_mixed_modes() attributes {denormal_fpenv = #llvm.denormal_fpenv<default_output_mode = dynamic, default_input_mode = positivezero, float_output_mode = dynamic, float_input_mode = ieee>}  {
+  llvm.return
+}
+// CHECK: attributes #[[ATTRS]] = { denormal_fpenv(dynamic|positivezero, float: dynamic|ieee) }
 
 // -----
 


        


More information about the cfe-commits mailing list