https://github.com/AlexDenisov created https://github.com/llvm/llvm-project/pull/236997
Adds support for `ldexp` variants. The new op lowers to `llvm.ldexp` or `llvm.experimental.constrained.ldexp` depending on `fenv` attribute. Assisted-by: Claude >From 08399ac4e70c4b94a0d02c2b2aa87888d14078d8 Mon Sep 17 00:00:00 2001 From: Alex Denisov <[email protected]> Date: Thu, 29 Oct 2026 23:30:43 +0100 Subject: [PATCH] [CIR] Add support for ldexp builtins Adds support for `ldexp` variants. The new op lowers to `llvm.ldexp` or `llvm.experimental.constrained.ldexp` depending on `fenv` attribute. Assisted-by: Claude --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 37 ++++++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 + clang/test/CIR/CodeGen/pragma-fenv_access.c | 90 +++++++++++++++++++ .../test/CIR/CodeGenBuiltins/builtin-float.c | 66 ++++++++++++++ clang/test/CIR/Lowering/fenv.cir | 11 +++ 5 files changed, 206 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index bd762aae3e1d7e..04c549d9e3317b 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -8761,6 +8761,43 @@ def CIR_ATan2Op : CIR_BinaryFPToFPBuiltinOp<"atan2", "ATan2Op"> { let constrainedLLVMIntrinsic = "atan2"; } +def CIR_LdexpOp : CIR_Op<"ldexp", + !listconcat([AllTypesMatch<["src", "result"]>], CIR_FenvOpTraits)> { + let summary = "Multiplies a floating-point value by an integral power of two"; + let description = [{ + `cir.ldexp` computes `src * 2^exp`. + + Lowers to `llvm.ldexp`, or to `llvm.experimental.constrained.ldexp` when + the `fenv` attribute is present. + + Example: + %r = cir.ldexp %x, %e : !cir.float, !s32i + }]; + + let arguments = (ins + CIR_AnyFloatType:$src, + CIR_AnyIntType:$exp, + OptionalAttr<CIR_FenvAttr>:$fenv + ); + + let results = (outs CIR_AnyFloatType:$result); + + let assemblyFormat = [{ + $src `,` $exp `:` qualified(type($src)) `,` qualified(type($exp)) + (`fenv` `` $fenv^)? attr-dict + }]; + + let builders = [ + OpBuilder<(ins "mlir::Value":$src, "mlir::Value":$exp), [{ + build($_builder, $_state, src, exp, cir::FenvAttr{}); + }]> + ]; + + let llvmOp = "LoadExpOp"; + let constrainedLLVMIntrinsic = "ldexp"; + let constrainedLLVMIntrinsicHasRoundingMode = 0; // MUTATION +} + def CIR_FrexpOp : CIR_Op<"frexp", [Pure]> { let summary = "Decomposes a floating-point value into significand and exponent"; let description = [{ diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 21a86114420e19..8e3b95fa6746aa 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -1128,6 +1128,8 @@ static RValue tryEmitFPMathIntrinsic(CIRGenFunction &cgf, const CallExpr *e, case Builtin::BI__builtin_ldexpl: case Builtin::BI__builtin_ldexpf16: case Builtin::BI__builtin_ldexpf128: + return RValue::get( + emitBinaryMaybeConstrainedFPBuiltin<cir::LdexpOp>(cgf, *e)); case Builtin::BI__builtin_elementwise_ldexp: return errorBuiltinNYI(cgf, e, builtinID); default: diff --git a/clang/test/CIR/CodeGen/pragma-fenv_access.c b/clang/test/CIR/CodeGen/pragma-fenv_access.c index 2f422a81e70abc..b947d1cb6ec46f 100644 --- a/clang/test/CIR/CodeGen/pragma-fenv_access.c +++ b/clang/test/CIR/CodeGen/pragma-fenv_access.c @@ -176,6 +176,15 @@ void test_cmdline_defaults(float x, float y, float z, int i, double d) { // LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") // LLVM-DEFAULT: call float @llvm.pow.f32 // LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + sf = __builtin_ldexpf(x, i); +// CIR-STRICT: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// CIR-STRICT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> +// CIR-DEFAULT: cir.ldexp {{.*}} : !cir.float, !s32i loc +// CIR-DEFAULT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = masked, strict_except = false> +// LLVM-STRICT: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.ldexp.f32.i32 +// LLVM-DEFAULT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") sf = __builtin_fmaf(x, y, z); // CIR-STRICT: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // CIR-STRICT-RND: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> @@ -251,6 +260,9 @@ void test_global_fenv_access_on(float x, float y, float z, int i, double d) { sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") @@ -311,6 +323,9 @@ void test_except_off_fenv_access_off(float x, float y, float z, int i, double d) sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") @@ -369,6 +384,9 @@ void test_fenv_access_on_after_local_off(float x, float y, float z, int i, doubl sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") @@ -496,6 +514,15 @@ void test_float_control_except_off(float x, float y, float z, int i, double d) { // LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") // LLVM-DEFAULT: call float @llvm.pow.f32 // LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_ldexpf(x, i); +// CIR-STRICT: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// CIR-STRICT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// CIR-DEFAULT: cir.ldexp {{.*}} : !cir.float, !s32i loc +// CIR-DEFAULT-RND: cir.ldexp {{.*}} : !cir.float, !s32i loc +// LLVM-STRICT: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.ldexp.f32.i32 +// LLVM-DEFAULT-RND: call float @llvm.ldexp.f32.i32 sf = __builtin_fmaf(x, y, z); // CIR-STRICT: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // CIR-STRICT-RND: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> @@ -570,6 +597,9 @@ void test_float_control_except_on(float x, float y, float z, int i, double d) { sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") @@ -629,6 +659,9 @@ void test_local_fenv_access_on(float x, float y, float z, int i, double d) { sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") @@ -754,6 +787,15 @@ void test_float_control_except_off_after_on(float x, float y, float z, int i, do // LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") // LLVM-DEFAULT: call float @llvm.pow.f32 // LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_ldexpf(x, i); +// CIR-STRICT: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// CIR-STRICT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// CIR-DEFAULT: cir.ldexp {{.*}} : !cir.float, !s32i loc +// CIR-DEFAULT-RND: cir.ldexp {{.*}} : !cir.float, !s32i loc +// LLVM-STRICT: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.ldexp.f32.i32 +// LLVM-DEFAULT-RND: call float @llvm.ldexp.f32.i32 sf = __builtin_fmaf(x, y, z); // CIR-STRICT: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // CIR-STRICT-RND: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> @@ -841,6 +883,9 @@ void test_fenv_round_upward(float x, float y, float z, int i, double d) { sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") @@ -901,6 +946,9 @@ void test_fenv_round_tonearest(float x, float y, float z, int i, double d) { sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") @@ -962,6 +1010,9 @@ void test_fenv_round_tonearest_except_ignore(float x, float y, float z, int i, d sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") @@ -1089,6 +1140,15 @@ void test_fenv_round_tonearest_except_ignore_fenv_off(float x, float y, float z, // LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") // LLVM-DEFAULT: call float @llvm.pow.f32 // LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_ldexpf(x, i); +// CIR-STRICT: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// CIR-STRICT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> +// CIR-DEFAULT: cir.ldexp {{.*}} : !cir.float, !s32i loc +// CIR-DEFAULT-RND: cir.ldexp {{.*}} : !cir.float, !s32i loc +// LLVM-STRICT: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// LLVM-DEFAULT: call float @llvm.ldexp.f32.i32 +// LLVM-DEFAULT-RND: call float @llvm.ldexp.f32.i32 sf = __builtin_fmaf(x, y, z); // CIR-STRICT: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> // CIR-STRICT-RND: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = masked, strict_except = false> @@ -1164,6 +1224,9 @@ void test_fp_exceptions_maytrap(float x, float y, float z, int i, double d) { sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = false> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = false> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = false> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") @@ -1225,6 +1288,9 @@ void test_fp_exceptions_maytrap_round_upward(float x, float y, float z, int i, d sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = false> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = false> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = false> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") @@ -1358,6 +1424,9 @@ void test_fenv_round_dynamic_with_fenv_access(float x, float y, float z, int i, sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") @@ -1483,6 +1552,15 @@ void test_fenv_round_dynamic_without_fenv_access(float x, float y, float z, int // LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") // LLVM-DEFAULT: call float @llvm.pow.f32 // LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_ldexpf(x, i); +// CIR-STRICT: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// CIR-STRICT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// CIR-DEFAULT: cir.ldexp {{.*}} : !cir.float, !s32i loc +// CIR-DEFAULT-RND: cir.ldexp {{.*}} : !cir.float, !s32i loc +// LLVM-STRICT: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.ldexp.f32.i32 +// LLVM-DEFAULT-RND: call float @llvm.ldexp.f32.i32 sf = __builtin_fmaf(x, y, z); // CIR-STRICT: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // CIR-STRICT-RND: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> @@ -1557,6 +1635,9 @@ void test_file_scope_fenv_access_on(float x, float y, float z, int i, double d) sf = __builtin_powf(x, y); // CIR: cir.pow {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + sf = __builtin_ldexpf(x, i); +// CIR: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> +// LLVM: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") sf = __builtin_fmaf(x, y, z); // CIR: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = unknown, except_mode = unknown, strict_except = true> // LLVM-STRICT: call float @llvm.experimental.constrained.fma.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") @@ -1682,6 +1763,15 @@ void test_file_scope_fenv_access_off(float x, float y, float z, int i, double d) // LLVM-STRICT-RND: call float @llvm.experimental.constrained.pow.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") // LLVM-DEFAULT: call float @llvm.pow.f32 // LLVM-DEFAULT-RND: call float @llvm.pow.f32 + sf = __builtin_ldexpf(x, i); +// CIR-STRICT: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// CIR-STRICT-RND: cir.ldexp {{.*}} : !cir.float, !s32i fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> +// CIR-DEFAULT: cir.ldexp {{.*}} : !cir.float, !s32i loc +// CIR-DEFAULT-RND: cir.ldexp {{.*}} : !cir.float, !s32i loc +// LLVM-STRICT: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-STRICT-RND: call float @llvm.experimental.constrained.ldexp.f32.i32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// LLVM-DEFAULT: call float @llvm.ldexp.f32.i32 +// LLVM-DEFAULT-RND: call float @llvm.ldexp.f32.i32 sf = __builtin_fmaf(x, y, z); // CIR-STRICT: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> // CIR-STRICT-RND: cir.fma {{.*}} : !cir.float fenv<dynamic_rounding_mode = tonearest, except_mode = unknown, strict_except = true> diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-float.c b/clang/test/CIR/CodeGenBuiltins/builtin-float.c index f50bcbba7f8bfe..64d06a9220f9f5 100644 --- a/clang/test/CIR/CodeGenBuiltins/builtin-float.c +++ b/clang/test/CIR/CodeGenBuiltins/builtin-float.c @@ -230,6 +230,72 @@ long double test_powil(long double x, int y) { // OGCG: call x86_fp80 @llvm.powi.f80.i32(x86_fp80 %{{.*}}, i32 %{{.*}}) // OGCG: ret x86_fp80 +// ldexp: float, double, long double, __float128 + +float test_ldexpf(float x, int e) { + return __builtin_ldexpf(x, e); +} + +// CIR-LABEL: cir.func {{.*}} @test_ldexpf +// CIR: %{{.*}} = cir.ldexp %{{.*}}, %{{.*}} : !cir.float, !s32i +// CIR: cir.return + +// LLVM-LABEL: define {{.*}} float @test_ldexpf +// LLVM: call float @llvm.ldexp.f32.i32(float %{{.*}}, i32 %{{.*}}) +// LLVM: ret float + +// OGCG-LABEL: define {{.*}} float @test_ldexpf +// OGCG: call float @llvm.ldexp.f32.i32(float %{{.*}}, i32 %{{.*}}) +// OGCG: ret float + +double test_ldexp(double x, int e) { + return __builtin_ldexp(x, e); +} + +// CIR-LABEL: cir.func {{.*}} @test_ldexp +// CIR: %{{.*}} = cir.ldexp %{{.*}}, %{{.*}} : !cir.double, !s32i +// CIR: cir.return + +// LLVM-LABEL: define {{.*}} double @test_ldexp +// LLVM: call double @llvm.ldexp.f64.i32(double %{{.*}}, i32 %{{.*}}) +// LLVM: ret double + +// OGCG-LABEL: define {{.*}} double @test_ldexp +// OGCG: call double @llvm.ldexp.f64.i32(double %{{.*}}, i32 %{{.*}}) +// OGCG: ret double + +long double test_ldexpl(long double x, int e) { + return __builtin_ldexpl(x, e); +} + +// CIR-LABEL: cir.func {{.*}} @test_ldexpl +// CIR: %{{.*}} = cir.ldexp %{{.*}}, %{{.*}} : !cir.long_double<!cir.f80>, !s32i +// CIR: cir.return + +// LLVM-LABEL: define {{.*}} x86_fp80 @test_ldexpl +// LLVM: call x86_fp80 @llvm.ldexp.f80.i32(x86_fp80 %{{.*}}, i32 %{{.*}}) +// LLVM: ret x86_fp80 + +// OGCG-LABEL: define {{.*}} x86_fp80 @test_ldexpl +// OGCG: call x86_fp80 @llvm.ldexp.f80.i32(x86_fp80 %{{.*}}, i32 %{{.*}}) +// OGCG: ret x86_fp80 + +__float128 test_ldexpf128(__float128 x, int e) { + return __builtin_ldexpf128(x, e); +} + +// CIR-LABEL: cir.func {{.*}} @test_ldexpf128 +// CIR: %{{.*}} = cir.ldexp %{{.*}}, %{{.*}} : !cir.f128, !s32i +// CIR: cir.return + +// LLVM-LABEL: define {{.*}} fp128 @test_ldexpf128 +// LLVM: call fp128 @llvm.ldexp.f128.i32(fp128 %{{.*}}, i32 %{{.*}}) +// LLVM: ret fp128 + +// OGCG-LABEL: define {{.*}} fp128 @test_ldexpf128 +// OGCG: call fp128 @llvm.ldexp.f128.i32(fp128 %{{.*}}, i32 %{{.*}}) +// OGCG: ret fp128 + // FP comparison builtins: float void test_floats(float x, float y, int *out) { diff --git a/clang/test/CIR/Lowering/fenv.cir b/clang/test/CIR/Lowering/fenv.cir index 27f3455c16ea37..19b3a6476232d4 100644 --- a/clang/test/CIR/Lowering/fenv.cir +++ b/clang/test/CIR/Lowering/fenv.cir @@ -117,6 +117,17 @@ module { cir.return } + // CHECK-LABEL: llvm.func @ldexp_variants + cir.func @ldexp_variants(%a: !cir.double, %e: !s32i) { + // CHECK: llvm.intr.ldexp(%{{.*}}, %{{.*}}) : (f64, i32) -> f64 + %0 = cir.ldexp %a, %e : !cir.double, !s32i + // CHECK: %[[RM:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"round.upward"> + // CHECK: %[[EB:.*]] = llvm.mlir.metadata_as_value #llvm.md_string<"fpexcept.strict"> + // CHECK: llvm.call_intrinsic "llvm.experimental.constrained.ldexp"(%{{.*}}, %{{.*}}, %[[RM]], %[[EB]]) : (f64, i32, !llvm.metadata, !llvm.metadata) -> f64 + %1 = cir.ldexp %a, %e : !cir.double, !s32i fenv<dynamic_rounding_mode = upward, except_mode = unknown, strict_except = true> + cir.return + } + // CHECK-LABEL: llvm.func @ternary_fp_builtins cir.func @ternary_fp_builtins(%a: !cir.double, %b: !cir.double, %c: !cir.double) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
