diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td @@ -680,6 +680,70 @@ }]; } +// +// Call an LLVM intrinsic +// +def LLVM_call_intrinsic : LLVM_Op<"call_intrinsic", [Pure]> { + let arguments = (ins StrAttr:$intrin, Variadic:$args); + let results = (outs Variadic:$results); + let llvmBuilder = [{ + llvm::Module *module = builder.GetInsertBlock()->getModule(); + llvm::Intrinsic::ID id = op.getIntrinsicEnum(); + if (!id) { + assert(false && "Couldn't find the intrinsic!\n"); + } + llvm::Function *fn; + if (!llvm::Intrinsic::isOverloaded(id)) { + fn = llvm::Intrinsic::getDeclaration(module, id, {}); + } else { + SmallVector Table; + getIntrinsicInfoTableEntries(id, Table); + ArrayRef TableRef = Table; + + SmallVector opTys; + for (size_t i = 0; i < opInst.getNumOperands(); i++) { + opTys.push_back(moduleTranslation.convertType( + opInst.getOperand(i).getType())); + } + + llvm::Type* resTy; + if (opInst.getNumResults() == 0) { + resTy = llvm::Type::getVoidTy(module->getContext()); + } else { + resTy = moduleTranslation.convertType(opInst.getResult(0).getType()); + } + llvm::FunctionType* FT = llvm::FunctionType::get(resTy, opTys, false); + + SmallVector argTys; + if (llvm::Intrinsic::matchIntrinsicSignature(FT, TableRef, argTys) != + llvm::Intrinsic::MatchIntrinsicTypesResult::MatchIntrinsicTypes_Match) { + assert (false && "Intrinsic type is not a match!\n"); + } + if (llvm::Intrinsic::matchIntrinsicVarArg(FT->isVarArg(), TableRef)) { + assert (false && "Intrinsic variadic-ness is not a match!\n"); + } + + ArrayRef ArgTys = argTys; + fn = llvm::Intrinsic::getDeclaration(module, id, ArgTys); + } + + auto *inst = builder.CreateCall(fn, + moduleTranslation.lookupValues(opInst.getOperands())); + if (opInst.getNumResults() == 1) { + moduleTranslation.mapValue(op->getResults().front()) = inst; + } + }]; + let assemblyFormat = [{ + $intrin `(` $args `)` `:` functional-type($args, $results) attr-dict + }]; + + let extraClassDeclaration = [{ + llvm::Intrinsic::ID getIntrinsicEnum() { + return llvm::Function::lookupIntrinsicID(getIntrinAttr()); + } + }]; +} + // // LLVM Vector Predication operations. // diff --git a/mlir/test/Dialect/LLVMIR/call-intrin.mlir b/mlir/test/Dialect/LLVMIR/call-intrin.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/call-intrin.mlir @@ -0,0 +1,54 @@ +// RUN: mlir-translate -mlir-to-llvmir -split-input-file %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> , <4 x float> , 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 +}