diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td index 688bc19cbf18a..0785ff6ebfd49 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td @@ -596,6 +596,57 @@ def LLVM_ConstrainedFPExtIntr }]; } +// Generic constrained floating-point intrinsic call. + +def LLVM_CallConstrainedFPIntrinsicOp + : LLVM_Op<"intr.experimental.constrained_fp_call", + [Pure, + DeclareOpInterfaceMethods, + DeclareOpInterfaceMethods]> { + let summary = "Generic call to an LLVM constrained floating-point intrinsic."; + let description = [{ + Calls an LLVM constrained floating-point intrinsic by name. The intrinsic + name is given by the `intrin` attribute (for example + `"llvm.experimental.constrained.cos.f32"`). Overloaded intrinsics are + resolved from the MLIR operand and result types of this op. + + The rounding mode operand is required for intrinsics for which + `llvm::Intrinsic::hasConstrainedFPRoundingModeOperand` returns true and is + forbidden otherwise. The exception behavior attribute is always required. + + This op handles every constrained FP intrinsic that follows the standard + operand layout `(args..., [rounding,] exception)`. The compare variants + `llvm.experimental.constrained.fcmp` and + `llvm.experimental.constrained.fcmps` carry an additional predicate + metadata operand and are not supported. + + Example: + + ```mlir + %res = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.cos.f32"(%arg) + towardzero ignore : (f32) -> f32 + ``` + }]; + + let arguments = (ins StrAttr:$intrin, + Variadic:$args, + OptionalAttr:$roundingmode, + FPExceptionBehaviorAttr:$fpExceptionBehavior); + let results = (outs LLVM_Type:$res); + + let llvmBuilder = [{ + return convertCallConstrainedFPIntrinsicOp(op, builder, moduleTranslation); + }]; + + let assemblyFormat = [{ + $intrin `(` $args `)` ($roundingmode^)? $fpExceptionBehavior + attr-dict `:` functional-type($args, $res) + }]; + + let hasVerifier = 1; +} + // Intrinsics with multiple returns. class LLVM_ArithWithOverflowOp diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 63bd9f8a3d625..5ee8f5004cf48 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/TypeSwitch.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/Support/Error.h" #include "LLVMDialectBytecode.h" @@ -4012,6 +4013,34 @@ LogicalResult CallIntrinsicOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// CallConstrainedFPIntrinsicOp +//===----------------------------------------------------------------------===// + +LogicalResult CallConstrainedFPIntrinsicOp::verify() { + StringRef name = getIntrin(); + llvm::Intrinsic::ID id = llvm::Intrinsic::lookupIntrinsicID(name); + if (!id) + return emitOpError() << "could not find LLVM intrinsic: " << name; + if (!llvm::Intrinsic::isConstrainedFPIntrinsic(id)) + return emitOpError() << "intrinsic " << name + << " is not a constrained FP intrinsic"; + if (id == llvm::Intrinsic::experimental_constrained_fcmp || + id == llvm::Intrinsic::experimental_constrained_fcmps) + return emitOpError() << "intrinsic " << name + << " is a constrained FP compare and is not " + "supported by this op"; + bool requiresRounding = + llvm::Intrinsic::hasConstrainedFPRoundingModeOperand(id); + if (requiresRounding && !getRoundingmodeAttr()) + return emitOpError() << "intrinsic " << name + << " requires a rounding mode attribute"; + if (!requiresRounding && getRoundingmodeAttr()) + return emitOpError() << "intrinsic " << name + << " does not take a rounding mode attribute"; + return success(); +} + void CallIntrinsicOp::build(OpBuilder &builder, OperationState &state, mlir::StringAttr intrin, mlir::ValueRange args) { build(builder, state, /*resultTypes=*/TypeRange{}, intrin, args, diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp index e9cd335835263..89105242e7180 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp @@ -22,6 +22,7 @@ #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/MemoryModelRelaxationAnnotations.h" using namespace mlir; @@ -36,24 +37,113 @@ static constexpr StringLiteral reqdWorkGroupSizeMDName = "reqd_work_group_size"; static constexpr StringLiteral intelReqdSubGroupSizeMDName = "intel_reqd_sub_group_size"; +/// Returns true if `id` is a constrained FP intrinsic that the generic +/// LLVM_CallConstrainedFPIntrinsicOp can model (i.e. it has the standard +/// trailing metadata layout: rounding mode and/or exception behavior, with no +/// additional predicate metadata). +static bool isGenericConstrainedFPIntrinsic(llvm::Intrinsic::ID id) { + if (!llvm::Intrinsic::isConstrainedFPIntrinsic(id)) + return false; + // fcmp / fcmps carry an extra predicate metadata operand and are not + // representable by the generic op. + return id != llvm::Intrinsic::experimental_constrained_fcmp && + id != llvm::Intrinsic::experimental_constrained_fcmps; +} + +/// Returns true if `id` is a constrained FP compare intrinsic. These have a +/// predicate metadata operand in addition to the exception behavior operand +/// and are not currently importable, but should fail with a clean diagnostic +/// instead of falling through to the generic intrinsic path and tripping the +/// metadata assertion in `convertValue`. +static bool isConstrainedFPCmpIntrinsic(llvm::Intrinsic::ID id) { + return id == llvm::Intrinsic::experimental_constrained_fcmp || + id == llvm::Intrinsic::experimental_constrained_fcmps; +} + /// Returns true if the LLVM IR intrinsic is convertible to an MLIR LLVM dialect -/// intrinsic. Returns false otherwise. +/// intrinsic. Returns false otherwise. Constrained FP compare intrinsics are +/// claimed here so that the import emits a targeted error rather than crashing +/// in the unregistered-intrinsic fallback. static bool isConvertibleIntrinsic(llvm::Intrinsic::ID id) { static const DenseSet convertibleIntrinsics = { #include "mlir/Dialect/LLVMIR/LLVMConvertibleLLVMIRIntrinsics.inc" }; - return convertibleIntrinsics.contains(id); + if (convertibleIntrinsics.contains(id)) + return true; + return isGenericConstrainedFPIntrinsic(id) || isConstrainedFPCmpIntrinsic(id); } /// Returns the list of LLVM IR intrinsic identifiers that are convertible to /// MLIR LLVM dialect intrinsics. static ArrayRef getSupportedIntrinsicsImpl() { - static const SmallVector convertibleIntrinsics = { + static const SmallVector convertibleIntrinsics = [] { + SmallVector ids = { #include "mlir/Dialect/LLVMIR/LLVMConvertibleLLVMIRIntrinsics.inc" - }; + }; + // Also register the constrained FP intrinsics that fall back to the + // generic LLVM_CallConstrainedFPIntrinsicOp. Compare variants are + // registered too so the importer can emit a clean error for them instead + // of letting them fall through to the unregistered-intrinsic path, which + // would trip the metadata assertion in `convertValue`. + DenseSet seen(ids.begin(), ids.end()); + for (unsigned id = 1; id < llvm::Intrinsic::num_intrinsics; ++id) { + auto intrinId = static_cast(id); + if (seen.contains(id)) + continue; + if (isGenericConstrainedFPIntrinsic(intrinId) || + isConstrainedFPCmpIntrinsic(intrinId)) + ids.push_back(id); + } + return ids; + }(); return convertibleIntrinsics; } +/// Imports a constrained FP intrinsic call as a generic +/// LLVM_CallConstrainedFPIntrinsicOp. Splits the call's operands into value +/// arguments and the trailing rounding-mode/exception-behavior metadata +/// operands. +static LogicalResult +convertConstrainedFPIntrinsicCallOp(OpBuilder &builder, llvm::CallInst *inst, + LLVM::ModuleImport &moduleImport) { + llvm::Intrinsic::ID id = inst->getIntrinsicID(); + llvm::Function *callee = inst->getCalledFunction(); + if (!callee) + return failure(); + StringRef intrinName = callee->getName(); + bool hasRounding = llvm::Intrinsic::hasConstrainedFPRoundingModeOperand(id); + + unsigned numArgs = inst->arg_size(); + unsigned numMetadata = hasRounding ? 2 : 1; + if (numArgs < numMetadata) + return failure(); + unsigned numValueArgs = numArgs - numMetadata; + + SmallVector args; + args.reserve(numValueArgs); + for (unsigned i = 0; i < numValueArgs; ++i) { + FailureOr v = moduleImport.convertValue(inst->getArgOperand(i)); + if (failed(v)) + return failure(); + args.push_back(*v); + } + + RoundingModeAttr roundingMode; + if (hasRounding) + roundingMode = + moduleImport.matchRoundingModeAttr(inst->getArgOperand(numValueArgs)); + FPExceptionBehaviorAttr exceptionBehavior = + moduleImport.matchFPExceptionBehaviorAttr( + inst->getArgOperand(numArgs - 1)); + + Type resultType = moduleImport.convertType(inst->getType()); + auto op = CallConstrainedFPIntrinsicOp::create( + builder, moduleImport.translateLoc(inst->getDebugLoc()), resultType, + builder.getStringAttr(intrinName), args, roundingMode, exceptionBehavior); + moduleImport.mapValue(inst) = op.getRes(); + return success(); +} + /// Converts the LLVM intrinsic to an MLIR LLVM dialect operation if a /// conversion exits. Returns failure otherwise. static LogicalResult convertIntrinsicImpl(OpBuilder &odsBuilder, @@ -73,6 +163,24 @@ static LogicalResult convertIntrinsicImpl(OpBuilder &odsBuilder, llvmOpBundles.push_back(inst->getOperandBundleAt(i)); #include "mlir/Dialect/LLVMIR/LLVMIntrinsicFromLLVMIRConversions.inc" + + // Fallback for constrained FP intrinsics without a dedicated MLIR op. + if (isGenericConstrainedFPIntrinsic(intrinsicID)) + return convertConstrainedFPIntrinsicCallOp(odsBuilder, inst, + moduleImport); + + // Constrained FP compare intrinsics are claimed here so that we can emit + // a targeted error instead of falling through to convertUnregistered- + // Intrinsic (which would crash on the predicate metadata operand). + if (isConstrainedFPCmpIntrinsic(intrinsicID)) { + Location loc = moduleImport.translateLoc(inst->getDebugLoc()); + StringRef intrinName = inst->getCalledFunction() + ? inst->getCalledFunction()->getName() + : StringRef(""); + return emitError(loc) + << "constrained FP compare intrinsic '" << intrinName + << "' is not supported by the LLVM dialect importer"; + } } return failure(); diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp index 5474689c9b0b5..65cccbd055286 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp @@ -135,6 +135,67 @@ convertOperandBundles(OperandRangeRange bundleOperands, return convertOperandBundles(bundleOperands, *bundleTags, moduleTranslation); } +/// Builder for LLVM_CallConstrainedFPIntrinsicOp. Resolves the intrinsic +/// identifier from the `intrin` attribute, infers any overloaded types from the +/// MLIR operand and result types, and emits an LLVM IR constrained FP call. +static LogicalResult convertCallConstrainedFPIntrinsicOp( + CallConstrainedFPIntrinsicOp op, llvm::IRBuilderBase &builder, + LLVM::ModuleTranslation &moduleTranslation) { + llvm::Module *module = builder.GetInsertBlock()->getModule(); + llvm::Intrinsic::ID id = llvm::Intrinsic::lookupIntrinsicID(op.getIntrin()); + if (!id) + return mlir::emitError(op.getLoc(), "could not find LLVM intrinsic: ") + << op.getIntrin(); + if (!llvm::Intrinsic::isConstrainedFPIntrinsic(id)) + return mlir::emitError(op.getLoc(), "not a constrained FP intrinsic: ") + << op.getIntrin(); + if (id == llvm::Intrinsic::experimental_constrained_fcmp || + id == llvm::Intrinsic::experimental_constrained_fcmps) + return mlir::emitError(op.getLoc()) + << op.getIntrin() + << " is a constrained FP compare and is not supported by this op"; + + // Build a signature matching what the intrinsic declaration looks like in + // LLVM IR, including the trailing metadata operands. This lets + // Intrinsic::isSignatureValid resolve all overloaded types. + SmallVector argTys; + argTys.reserve(op.getArgs().size() + 2); + for (Type type : op.getArgs().getTypes()) + argTys.push_back(moduleTranslation.convertType(type)); + llvm::Type *metadataTy = llvm::Type::getMetadataTy(module->getContext()); + if (llvm::Intrinsic::hasConstrainedFPRoundingModeOperand(id)) + argTys.push_back(metadataTy); + argTys.push_back(metadataTy); + + llvm::Type *resultTy = moduleTranslation.convertType(op.getRes().getType()); + llvm::FunctionType *ft = + llvm::FunctionType::get(resultTy, argTys, /*isVarArg=*/false); + + std::string errorMsg; + llvm::raw_string_ostream errorOS(errorMsg); + SmallVector overloadedTys; + if (!llvm::Intrinsic::isSignatureValid(id, ft, overloadedTys, errorOS)) { + return mlir::emitError(op.getLoc(), "call intrinsic signature ") + << diagStr(ft) << " to constrained FP intrinsic " << op.getIntrin() + << " does not match any overload: " << errorMsg; + } + + llvm::Function *fn = + llvm::Intrinsic::getOrInsertDeclaration(module, id, overloadedTys); + + std::optional rounding; + if (auto roundingAttr = op.getRoundingmodeAttr()) + rounding = moduleTranslation.translateRoundingMode(roundingAttr.getValue()); + llvm::fp::ExceptionBehavior except = + moduleTranslation.translateFPExceptionBehavior( + op.getFpExceptionBehavior()); + + llvm::Value *result = builder.CreateConstrainedFPCall( + fn, moduleTranslation.lookupValues(op.getArgs()), "", rounding, except); + moduleTranslation.mapValue(op.getRes()) = result; + return success(); +} + /// Builder for LLVM_CallIntrinsicOp static LogicalResult convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder, diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index e80094df1eed2..78f3736d13674 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -1783,6 +1783,46 @@ llvm.func @wrong_number_of_bundle_types_intrin(%arg0: i32) -> i32 { // ----- +llvm.func @constrained_fp_call_unknown_intrinsic(%arg0: f32) -> f32 { + // expected-error@+1 {{could not find LLVM intrinsic: llvm.experimental.constrained.bogus.f32}} + %0 = llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.bogus.f32"(%arg0) towardzero ignore : (f32) -> f32 + llvm.return %0 : f32 +} + +// ----- + +llvm.func @constrained_fp_call_not_constrained(%arg0: f32) -> f32 { + // expected-error@+1 {{intrinsic llvm.cos.f32 is not a constrained FP intrinsic}} + %0 = llvm.intr.experimental.constrained_fp_call "llvm.cos.f32"(%arg0) towardzero ignore : (f32) -> f32 + llvm.return %0 : f32 +} + +// ----- + +llvm.func @constrained_fp_call_fcmp_rejected(%arg0: f32) -> i1 { + // expected-error@+1 {{intrinsic llvm.experimental.constrained.fcmp.f32 is a constrained FP compare and is not supported by this op}} + %0 = llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.fcmp.f32"(%arg0, %arg0) ignore : (f32, f32) -> i1 + llvm.return %0 : i1 +} + +// ----- + +llvm.func @constrained_fp_call_missing_rounding(%arg0: f32) -> f32 { + // expected-error@+1 {{intrinsic llvm.experimental.constrained.cos.f32 requires a rounding mode attribute}} + %0 = llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.cos.f32"(%arg0) ignore : (f32) -> f32 + llvm.return %0 : f32 +} + +// ----- + +llvm.func @constrained_fp_call_unexpected_rounding(%arg0: f64) -> f64 { + // expected-error@+1 {{intrinsic llvm.experimental.constrained.maximum.f64 does not take a rounding mode attribute}} + %0 = llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.maximum.f64"(%arg0, %arg0) towardzero ignore : (f64, f64) -> f64 + llvm.return %0 : f64 +} + +// ----- + llvm.func @foo() llvm.func @wrong_number_of_bundle_tags() { %0 = llvm.mlir.constant(0 : i32) : i32 diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index 83d1f1b8e2884..a9861ca756499 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -902,6 +902,23 @@ llvm.func @experimental_constrained_fptrunc(%in: f64) { llvm.return } +// CHECK-LABEL: @experimental_constrained_fp_call +llvm.func @experimental_constrained_fp_call(%s: f32, %d: f64, %p: i32) { + // CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.cos.f32"(%{{.*}}) towardzero ignore : (f32) -> f32 + %0 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.cos.f32"(%s) towardzero ignore + : (f32) -> f32 + // CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.maximum.f64"(%{{.*}}, %{{.*}}) strict : (f64, f64) -> f64 + %1 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.maximum.f64"(%d, %d) strict + : (f64, f64) -> f64 + // CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.powi.f32"(%{{.*}}, %{{.*}}) tonearest ignore : (f32, i32) -> f32 + %2 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.powi.f32"(%s, %p) tonearest ignore + : (f32, i32) -> f32 + llvm.return +} + // CHECK: llvm.func @tail_call_target() -> i32 llvm.func @tail_call_target() -> i32 diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll index b468a3e95c907..86760e26c88a5 100644 --- a/mlir/test/Target/LLVMIR/Import/import-failure.ll +++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll @@ -456,3 +456,23 @@ bb1: !91885 = !{!91886, !91887} !91886 = !{i32 10000, i64 86427, i32 1} !91887 = !{i32 100000, i64 86427, i32 1} + +; // ----- + +; CHECK: error: constrained FP compare intrinsic 'llvm.experimental.constrained.fcmp.f32' is not supported by the LLVM dialect importer +define i1 @constrained_fcmp(float %s) { + %r = call i1 @llvm.experimental.constrained.fcmp.f32(float %s, float %s, metadata !"oeq", metadata !"fpexcept.ignore") + ret i1 %r +} + +declare i1 @llvm.experimental.constrained.fcmp.f32(float, float, metadata, metadata) + +; // ----- + +; CHECK: error: constrained FP compare intrinsic 'llvm.experimental.constrained.fcmps.f32' is not supported by the LLVM dialect importer +define i1 @constrained_fcmps(float %s) { + %r = call i1 @llvm.experimental.constrained.fcmps.f32(float %s, float %s, metadata !"oeq", metadata !"fpexcept.ignore") + ret i1 %r +} + +declare i1 @llvm.experimental.constrained.fcmps.f32(float, float, metadata, metadata) diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic.ll b/mlir/test/Target/LLVMIR/Import/intrinsic.ll index f79d09aa3d633..0a13f31089d0c 100644 --- a/mlir/test/Target/LLVMIR/Import/intrinsic.ll +++ b/mlir/test/Target/LLVMIR/Import/intrinsic.ll @@ -1205,6 +1205,26 @@ define void @experimental_constrained_fpext(float %s, <4 x float> %v) { ret void } +; CHECK-LABEL: experimental_constrained_fp_call +define void @experimental_constrained_fp_call(float %s, <4 x float> %v, double %d, i32 %p) { + ; Unary constrained intrinsic with rounding mode + exception behavior. + ; CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.cos.f32"(%{{.*}}) towardzero ignore : (f32) -> f32 + %1 = call float @llvm.experimental.constrained.cos.f32(float %s, metadata !"round.towardzero", metadata !"fpexcept.ignore") + ; Vector variant. + ; CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.sin.v4f32"(%{{.*}}) dynamic maytrap : (vector<4xf32>) -> vector<4xf32> + %2 = call <4 x float> @llvm.experimental.constrained.sin.v4f32(<4 x float> %v, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + ; Constrained intrinsic with no rounding mode operand. + ; CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.maximum.f64"(%{{.*}}, %{{.*}}) strict : (f64, f64) -> f64 + %3 = call double @llvm.experimental.constrained.maximum.f64(double %d, double %d, metadata !"fpexcept.strict") + ; Result type different from operand type and no rounding mode. + ; CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.fptosi.i32.f32"(%{{.*}}) ignore : (f32) -> i32 + %4 = call i32 @llvm.experimental.constrained.fptosi.i32.f32(float %s, metadata !"fpexcept.ignore") + ; Mixed floating-point and integer operands. + ; CHECK: llvm.intr.experimental.constrained_fp_call "llvm.experimental.constrained.powi.f32"(%{{.*}}, %{{.*}}) tonearest ignore : (f32, i32) -> f32 + %5 = call float @llvm.experimental.constrained.powi.f32(float %s, i32 %p, metadata !"round.tonearest", metadata !"fpexcept.ignore") + ret void +} + ; CHECK-LABEL: llvm.func @ucmp define i2 @ucmp(i32 %a, i32 %b) { ; CHECK: %{{.*}} = llvm.intr.ucmp(%{{.*}}, %{{.*}}) : (i32, i32) -> i2 @@ -1501,6 +1521,11 @@ declare <4 x half> @llvm.experimental.constrained.fptrunc.v4f16.v4f64(<4 x doubl declare float @llvm.experimental.constrained.fptrunc.f32.f64(double, metadata, metadata) declare <4 x double> @llvm.experimental.constrained.fpext.v4f64.v4f32(<4 x float>, metadata) declare double @llvm.experimental.constrained.fpext.f64.f32(float, metadata) +declare float @llvm.experimental.constrained.cos.f32(float, metadata, metadata) +declare <4 x float> @llvm.experimental.constrained.sin.v4f32(<4 x float>, metadata, metadata) +declare double @llvm.experimental.constrained.maximum.f64(double, double, metadata) +declare i32 @llvm.experimental.constrained.fptosi.i32.f32(float, metadata) +declare float @llvm.experimental.constrained.powi.f32(float, i32, metadata, metadata) declare i2 @llvm.ucmp.i2.i32(i32, i32) declare <4 x i32> @llvm.ucmp.v4i32.v4i32(<4 x i32>, <4 x i32>) declare i2 @llvm.scmp.i2.i32(i32, i32) diff --git a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir index 11882a0a1d4c6..69ef56271212e 100644 --- a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir +++ b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir @@ -1384,6 +1384,36 @@ llvm.func @experimental_constrained_fpext(%s: f32, %v: vector<4xf32>) { llvm.return } +// CHECK-LABEL: @experimental_constrained_fp_call +llvm.func @experimental_constrained_fp_call(%s: f32, %v: vector<4xf32>, %d: f64, %p: i32) { + // CHECK: call float @llvm.experimental.constrained.cos.f32( + // CHECK-SAME: metadata !"round.towardzero", metadata !"fpexcept.ignore" + %0 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.cos"(%s) towardzero ignore + : (f32) -> f32 + // CHECK: call <4 x float> @llvm.experimental.constrained.sin.v4f32( + // CHECK-SAME: metadata !"round.dynamic", metadata !"fpexcept.maytrap" + %1 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.sin"(%v) dynamic maytrap + : (vector<4xf32>) -> vector<4xf32> + // CHECK: call double @llvm.experimental.constrained.maximum.f64( + // CHECK-SAME: metadata !"fpexcept.strict" + %2 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.maximum"(%d, %d) strict + : (f64, f64) -> f64 + // CHECK: call i32 @llvm.experimental.constrained.fptosi.i32.f32( + // CHECK-SAME: metadata !"fpexcept.ignore" + %3 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.fptosi"(%s) ignore + : (f32) -> i32 + // CHECK: call float @llvm.experimental.constrained.powi.f32( + // CHECK-SAME: metadata !"round.tonearest", metadata !"fpexcept.ignore" + %4 = llvm.intr.experimental.constrained_fp_call + "llvm.experimental.constrained.powi"(%s, %p) tonearest ignore + : (f32, i32) -> f32 + llvm.return +} + // CHECK-LABEL: @ucmp llvm.func @ucmp(%a: i32, %b: i32) -> i2 { // CHECK: call i2 @llvm.ucmp.i2.i32 @@ -1614,6 +1644,11 @@ llvm.func @vector_scmp(%a: vector<4 x i32>, %b: vector<4 x i32>) -> vector<4 x i // CHECK-DAG: declare <4 x half> @llvm.experimental.constrained.fptrunc.v4f16.v4f32(<4 x float>, metadata, metadata) // CHECK-DAG: declare double @llvm.experimental.constrained.fpext.f64.f32(float, metadata) // CHECK-DAG: declare <4 x double> @llvm.experimental.constrained.fpext.v4f64.v4f32(<4 x float>, metadata) +// CHECK-DAG: declare float @llvm.experimental.constrained.cos.f32(float, metadata, metadata) +// CHECK-DAG: declare <4 x float> @llvm.experimental.constrained.sin.v4f32(<4 x float>, metadata, metadata) +// CHECK-DAG: declare double @llvm.experimental.constrained.maximum.f64(double, double, metadata) +// CHECK-DAG: declare i32 @llvm.experimental.constrained.fptosi.i32.f32(float, metadata) +// CHECK-DAG: declare float @llvm.experimental.constrained.powi.f32(float, i32, metadata, metadata) // CHECK-DAG: declare range(i2 -1, -2) i2 @llvm.ucmp.i2.i32(i32, i32) // CHECK-DAG: declare range(i32 -1, 2) <4 x i32> @llvm.ucmp.v4i32.v4i32(<4 x i32>, <4 x i32>) // CHECK-DAG: declare range(i2 -1, -2) i2 @llvm.scmp.i2.i32(i32, i32)