LLVM dialect: introduce fmuladd intrinsic as operation

This operation is important to achieve decent performance in computational
kernels.  In LLVM, it is implemented as an intrinsic (through function
declaration and function call).  Thanks to MLIR's extendable set of operations,
it does not have to differentiate between built-ins and intrinsics, so fmuladd
is introduced as a general type-polymorphic operation.  Custom printing and
parsing will be added later.

PiperOrigin-RevId: 263106305
diff --git a/include/mlir/LLVMIR/LLVMOps.td b/include/mlir/LLVMIR/LLVMOps.td
index 80e6284..3783ad9 100644
--- a/include/mlir/LLVMIR/LLVMOps.td
+++ b/include/mlir/LLVMIR/LLVMOps.td
@@ -498,4 +498,21 @@
   let printer = [{ printConstantOp(p, *this); }];
 }
 
+// Operations that correspond to LLVM intrinsics. With MLIR operation set being
+// extendable, there is no reason to introduce a hard boundary between "core"
+// operations and intrinsics.
+
+def LLVM_fmuladd : LLVM_Op<"fmuladd", [NoSideEffect]>,
+                   Arguments<(ins LLVM_Type:$a, LLVM_Type:$b, LLVM_Type:$c)>,
+                   Results<(outs LLVM_Type:$res)> {
+  let llvmBuilder = [{
+    llvm::Module *module = builder.GetInsertBlock()->getModule();
+    llvm::Function *fn = llvm::Intrinsic::getDeclaration(
+        module, llvm::Intrinsic::fmuladd,
+        {$a->getType(), $b->getType(), $c->getType()});
+    $res = builder.CreateCall(fn, {$a, $b, $c});
+  }];
+}
+
+
 #endif // LLVMIR_OPS
diff --git a/test/Target/llvmir-intrinsics.mlir b/test/Target/llvmir-intrinsics.mlir
new file mode 100644
index 0000000..6f1baed
--- /dev/null
+++ b/test/Target/llvmir-intrinsics.mlir
@@ -0,0 +1,15 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+// CHECK-LABEL: @intrinsics
+func @intrinsics(%arg0: !llvm.float, %arg1: !llvm.float, %arg2: !llvm<"<8 x float>">) {
+  // CHECK: call float @llvm.fmuladd.f32.f32.f32
+  "llvm.fmuladd"(%arg0, %arg1, %arg0) : (!llvm.float, !llvm.float, !llvm.float) -> !llvm.float
+  // CHECK: call <8 x float> @llvm.fmuladd.v8f32.v8f32.v8f32
+  "llvm.fmuladd"(%arg2, %arg2, %arg2) : (!llvm<"<8 x float>">, !llvm<"<8 x float>">, !llvm<"<8 x float>">) -> !llvm<"<8 x float>">
+  llvm.return
+}
+
+// Check that intrinsics are declared with appropriate types.
+// CHECK: declare float @llvm.fmuladd.f32.f32.f32(float, float, float)
+// CHECK: declare <8 x float> @llvm.fmuladd.v8f32.v8f32.v8f32(<8 x float>, <8 x float>, <8 x float>) #0
+