[Mlir-commits] [mlir] 0efff7c - [mlir] Add call_intrinsic op to LLVMIIR

Jeff Niu llvmlistbot at llvm.org
Wed Nov 2 15:56:53 PDT 2022


Author: electriclilies
Date: 2022-11-02T15:56:46-07:00
New Revision: 0efff7cdcb26917b9acd1a280911317ea12dc937

URL: https://github.com/llvm/llvm-project/commit/0efff7cdcb26917b9acd1a280911317ea12dc937
DIFF: https://github.com/llvm/llvm-project/commit/0efff7cdcb26917b9acd1a280911317ea12dc937.diff

LOG: [mlir] Add call_intrinsic op to LLVMIIR

The call_intrinsic op allows us to call LLVM intrinsics from the LLVMDialect without implementing a new op every time.

Reviewed By: lattner, rriddle

Differential Revision: https://reviews.llvm.org/D137187

Added: 
    mlir/test/Dialect/LLVMIR/call-intrin.mlir

Modified: 
    mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
    mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index b2257a163932c..bb2668790dbfb 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -707,6 +707,25 @@ def LLVM_vector_extract
   }];
 }
 
+//===--------------------------------------------------------------------===//
+// CallIntrinsicOp
+//===--------------------------------------------------------------------===//
+def LLVM_CallIntrinsicOp : LLVM_Op<"call_intrinsic", [Pure]> {
+  let summary = "Call to an LLVM intrinsic function.";
+  let description = [{
+    Call the specified llvm intrinsic. If the intrinsic is overloaded, use
+    the MLIR function type of this op to determine which intrinsic to call.
+    }];
+  let arguments = (ins StrAttr:$intrin, Variadic<LLVM_Type>:$args);
+  let results = (outs Variadic<LLVM_Type>:$results);
+  let llvmBuilder = [{
+    return convertCallLLVMIntrinsicOp(op, builder, moduleTranslation);
+  }];
+  let assemblyFormat = [{
+    $intrin `(` $args `)` `:` functional-type($args, $results) attr-dict
+  }];
+}
+
 //
 // LLVM Vector Predication operations.
 //

diff  --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 1f89a55ee363e..abc2fadbbc9ac 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -258,6 +258,70 @@ static SmallVector<unsigned> extractPosition(ArrayRef<int64_t> indices) {
   return position;
 }
 
+/// Get the declaration of an overloaded llvm intrinsic. First we get the
+/// overloaded argument types and/or result type from the CallIntrinsicOp, and
+/// then use those to get the correct declaration of the overloaded intrinsic.
+static FailureOr<llvm::Function *>
+getOverloadedDeclaration(CallIntrinsicOp &op, llvm::Intrinsic::ID id,
+                         llvm::Module *module,
+                         LLVM::ModuleTranslation &moduleTranslation) {
+  SmallVector<llvm::Type *, 8> allArgTys;
+  for (Type type : op->getOperandTypes())
+    allArgTys.push_back(moduleTranslation.convertType(type));
+
+  llvm::Type *resTy;
+  if (op.getNumResults() == 0)
+    resTy = llvm::Type::getVoidTy(module->getContext());
+  else
+    resTy = moduleTranslation.convertType(op.getResult(0).getType());
+
+  // ATM we do not support variadic intrinsics.
+  llvm::FunctionType *ft = llvm::FunctionType::get(resTy, allArgTys, false);
+
+  SmallVector<llvm::Intrinsic::IITDescriptor, 8> table;
+  getIntrinsicInfoTableEntries(id, table);
+  ArrayRef<llvm::Intrinsic::IITDescriptor> tableRef = table;
+
+  SmallVector<llvm::Type *, 8> overloadedArgTys;
+  if (llvm::Intrinsic::matchIntrinsicSignature(ft, tableRef,
+                                               overloadedArgTys) !=
+      llvm::Intrinsic::MatchIntrinsicTypesResult::MatchIntrinsicTypes_Match) {
+    return op.emitOpError("intrinsic type is not a match");
+  }
+
+  ArrayRef<llvm::Type *> overloadedArgTysRef = overloadedArgTys;
+  return llvm::Intrinsic::getDeclaration(module, id, overloadedArgTysRef);
+}
+
+/// Builder for LLVM_CallIntrinsicOp
+static LogicalResult
+convertCallLLVMIntrinsicOp(CallIntrinsicOp &op, llvm::IRBuilderBase &builder,
+                           LLVM::ModuleTranslation &moduleTranslation) {
+  llvm::Module *module = builder.GetInsertBlock()->getModule();
+  llvm::Intrinsic::ID id =
+      llvm::Function::lookupIntrinsicID(op.getIntrinAttr());
+  if (!id)
+    return op.emitOpError()
+           << "couldn't find intrinsic: " << op.getIntrinAttr();
+
+  llvm::Function *fn = nullptr;
+  if (llvm::Intrinsic::isOverloaded(id)) {
+    auto fnOrFailure =
+        getOverloadedDeclaration(op, id, module, moduleTranslation);
+    if (failed(fnOrFailure))
+      return failure();
+    fn = fnOrFailure.value();
+  } else {
+    fn = llvm::Intrinsic::getDeclaration(module, id, {});
+  }
+
+  auto *inst =
+      builder.CreateCall(fn, moduleTranslation.lookupValues(op.getOperands()));
+  if (op.getNumResults() == 1)
+    moduleTranslation.mapValue(op->getResults().front()) = inst;
+  return success();
+}
+
 static LogicalResult
 convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
                      LLVM::ModuleTranslation &moduleTranslation) {
@@ -272,8 +336,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
   // Emit function calls.  If the "callee" attribute is present, this is a
   // direct function call and we also need to look up the remapped function
   // itself.  Otherwise, this is an indirect call and the callee is the first
-  // operand, look it up as a normal value.  Return the llvm::Value representing
-  // the function result, which may be of llvm::VoidTy type.
+  // operand, look it up as a normal value.  Return the llvm::Value
+  // representing the function result, which may be of llvm::VoidTy type.
   auto convertCall = [&](Operation &op) -> llvm::Value * {
     auto operands = moduleTranslation.lookupValues(op.getOperands());
     ArrayRef<llvm::Value *> operandsRef(operands);
@@ -404,8 +468,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     return success();
   }
 
-  // Emit branches.  We need to look up the remapped blocks and ignore the block
-  // arguments that were transformed into PHI nodes.
+  // Emit branches.  We need to look up the remapped blocks and ignore the
+  // block arguments that were transformed into PHI nodes.
   if (auto brOp = dyn_cast<LLVM::BrOp>(opInst)) {
     llvm::BranchInst *branch =
         builder.CreateBr(moduleTranslation.lookupBlock(brOp.getSuccessor()));

diff  --git a/mlir/test/Dialect/LLVMIR/call-intrin.mlir b/mlir/test/Dialect/LLVMIR/call-intrin.mlir
new file mode 100644
index 0000000000000..30f5c9fb82572
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/call-intrin.mlir
@@ -0,0 +1,82 @@
+// RUN: mlir-translate -mlir-to-llvmir -split-input-file -verify-diagnostics %s | FileCheck %s
+
+// CHECK: ; ModuleID = 'LLVMDialectModule'
+// CHECK: source_filename = "LLVMDialectModule"
+// CHECK: declare ptr @malloc(i64)
+// CHECK: declare void @free(ptr)
+// CHECK: define <4 x float> @round_sse41() {
+// CHECK:  %1 = call <4 x float> @llvm.x86.sse41.round.ss(<4 x float> <float 0x3FC99999A0000000, float 0x3FC99999A0000000, float 0x3FC99999A0000000, float 0x3FC99999A0000000>, <4 x float> <float 0x3FC99999A0000000, float 0x3FC99999A0000000, float 0x3FC99999A0000000, float 0x3FC99999A0000000>, i32 1)
+// CHECK:  ret <4 x float> %1
+// CHECK: }
+llvm.func @round_sse41() -> vector<4xf32> {
+    %0 = llvm.mlir.constant(1 : i32) : i32
+    %1 = llvm.mlir.constant(dense<0.2> : vector<4xf32>) : vector<4xf32>
+    %res = llvm.call_intrinsic "llvm.x86.sse41.round.ss"(%1, %1, %0) : (vector<4xf32>, vector<4xf32>, i32) -> vector<4xf32> {}
+    llvm.return %res: vector<4xf32>
+}
+
+// -----
+
+// CHECK: ; ModuleID = 'LLVMDialectModule'
+// CHECK: source_filename = "LLVMDialectModule"
+
+// CHECK: declare ptr @malloc(i64)
+
+// CHECK: declare void @free(ptr)
+
+// CHECK: define float @round_overloaded() {
+// CHECK:   %1 = call float @llvm.round.f32(float 1.000000e+00)
+// CHECK:   ret float %1
+// CHECK: }
+llvm.func @round_overloaded() -> f32 {
+    %0 = llvm.mlir.constant(1.0 : f32) : f32
+    %res = llvm.call_intrinsic "llvm.round"(%0) : (f32) -> f32 {}
+    llvm.return %res: f32
+}
+
+// -----
+
+// CHECK: ; ModuleID = 'LLVMDialectModule'
+// CHECK: source_filename = "LLVMDialectModule"
+// CHECK: declare ptr @malloc(i64)
+// CHECK: declare void @free(ptr)
+// CHECK: define void @lifetime_start() {
+// CHECK:   %1 = alloca float, i8 1, align 4
+// CHECK:   call void @llvm.lifetime.start.p0(i64 4, ptr %1)
+// CHECK:   ret void
+// CHECK: }
+llvm.func @lifetime_start() {
+    %0 = llvm.mlir.constant(4 : i64) : i64
+    %1 = llvm.mlir.constant(1 : i8) : i8
+    %2 = llvm.alloca %1 x f32 : (i8) -> !llvm.ptr
+    llvm.call_intrinsic "llvm.lifetime.start"(%0, %2) : (i64, !llvm.ptr) -> () {}
+    llvm.return
+}
+
+// -----
+
+llvm.func @variadic() {
+    %0 = llvm.mlir.constant(1 : i8) : i8
+    %1 = llvm.alloca %0 x f32 : (i8) -> !llvm.ptr
+    llvm.call_intrinsic "llvm.localescape"(%1, %1) : (!llvm.ptr, !llvm.ptr) -> ()
+    llvm.return
+}
+
+// -----
+
+llvm.func @no_intrinsic() {
+    // expected-error at below {{'llvm.call_intrinsic' op couldn't find intrinsic: "llvm.does_not_exist"}}
+    // expected-error at below {{LLVM Translation failed for operation: llvm.call_intrinsic}}
+    llvm.call_intrinsic "llvm.does_not_exist"() : () -> ()
+    llvm.return
+}
+
+// -----
+
+llvm.func @bad_types() {
+    %0 = llvm.mlir.constant(1 : i8) : i8
+    // expected-error at below {{'llvm.call_intrinsic' op intrinsic type is not a match}}
+    // expected-error at below {{LLVM Translation failed for operation: llvm.call_intrinsic}}
+    llvm.call_intrinsic "llvm.round"(%0) : (i8) -> i8 {}
+    llvm.return
+}


        


More information about the Mlir-commits mailing list