diff --git a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp --- a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -37,6 +37,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Support/Error.h" @@ -326,8 +327,13 @@ return blocks; } -// Handles importing globals and functions from an LLVM module. namespace { +/// Module import implementation class that provides methods to import globals +/// and functions from an LLVM module into an MLIR module. It holds mappings +/// between the original and translated globals, basic blocks, and values used +/// during the translation. Additionally, it keeps track of the current constant +/// insertion point since LLVM immediate values translate to MLIR operations +/// that are introduced at the beginning of the region. class Importer { public: Importer(MLIRContext *context, ModuleOp module) @@ -485,6 +491,53 @@ /// Stateful debug information importer. DebugImporter debugImporter; }; + +/// Listener that on operation insertion imports the fastmath flags from the +/// provided instruction and attaches them to the operation. +class FastmathListener : OpBuilder::Listener { +public: + FastmathListener(OpBuilder &builder, llvm::Instruction *inst) + : builder(builder), inst(inst) { + listener = builder.getListener(); + builder.setListener(this); + } + ~FastmathListener() final { builder.setListener(listener); } + + // Disallow copying and moving this class. + FastmathListener(const FastmathListener &) = delete; + FastmathListener &operator=(const FastmathListener &) = delete; + FastmathListener(FastmathListener &&other) = delete; + FastmathListener &operator=(FastmathListener &&other) = delete; + + /// Sets the fastmath attribute of the created operation if it implements the + /// fastmath interface and if the instruction it has been converted from has + /// fastmath information attached. + void notifyOperationInserted(Operation *op) final { + if (!isa(inst)) + return; + + if (auto iface = dyn_cast(op)) { + llvm::FastMathFlags flags = inst->getFastMathFlags(); + // Set the fastmath bits flag-by-flag. + FastmathFlags value = {}; + value = bitEnumSet(value, FastmathFlags::nnan, flags.noNaNs()); + value = bitEnumSet(value, FastmathFlags::ninf, flags.noInfs()); + value = bitEnumSet(value, FastmathFlags::nsz, flags.noSignedZeros()); + value = bitEnumSet(value, FastmathFlags::arcp, flags.allowReciprocal()); + value = bitEnumSet(value, FastmathFlags::contract, flags.allowContract()); + value = bitEnumSet(value, FastmathFlags::afn, flags.approxFunc()); + value = bitEnumSet(value, FastmathFlags::reassoc, flags.allowReassoc()); + FastmathFlagsAttr attr = + FastmathFlagsAttr::get(builder.getContext(), value); + iface->setAttr(iface.getFastmathAttrName(), attr); + } + } + +private: + OpBuilder &builder; + llvm::Instruction *inst; + OpBuilder::Listener *listener; +}; } // namespace // We only need integers, floats, doubles, and vectors and tensors thereof for @@ -1112,10 +1165,16 @@ LogicalResult Importer::processInstruction(llvm::Instruction *inst) { // FIXME: Support uses of SubtargetData. // FIXME: Add support for inbounds GEPs. - // FIXME: Add support for fast-math flags and call / operand attributes. + // FIXME: Add support for call / operand attributes. // FIXME: Add support for the indirectbr, cleanupret, catchret, catchswitch, // callbr, vaarg, landingpad, catchpad, cleanuppad instructions. + // Attach a listener to import the fastmath flags of the currently processed + // instruction. Recursively imported constants attach their own listener, + // which ensures the listener only gets notified for operations that map to + // the currently processed instruction. + FastmathListener listener(builder, inst); + // Convert LLVM intrinsics calls to MLIR intrinsics. if (auto *callInst = dyn_cast(inst)) { llvm::Function *callee = callInst->getCalledFunction(); diff --git a/mlir/test/Target/LLVMIR/Import/fastmath.ll b/mlir/test/Target/LLVMIR/Import/fastmath.ll new file mode 100644 --- /dev/null +++ b/mlir/test/Target/LLVMIR/Import/fastmath.ll @@ -0,0 +1,30 @@ +; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s + +; CHECK-LABEL: @fastmath_inst +define void @fastmath_inst(float %arg1, float %arg2) { + ; CHECK: llvm.fadd %{{.*}}, %{{.*}} {fastmathFlags = #llvm.fastmath} : f32 + %1 = fadd nnan ninf float %arg1, %arg2 + ; CHECK: llvm.fsub %{{.*}}, %{{.*}} {fastmathFlags = #llvm.fastmath} : f32 + %2 = fsub nsz float %arg1, %arg2 + ; CHECK: llvm.fmul %{{.*}}, %{{.*}} {fastmathFlags = #llvm.fastmath} : f32 + %3 = fmul arcp contract float %arg1, %arg2 + ; CHECK: llvm.fdiv %{{.*}}, %{{.*}} {fastmathFlags = #llvm.fastmath} : f32 + %4 = fdiv afn reassoc float %arg1, %arg2 + ; CHECK: llvm.frem %{{.*}}, %{{.*}} {fastmathFlags = #llvm.fastmath} : f32 + %5 = frem fast float %arg1, %arg2 + ret void +} + +; // ----- + +declare float @llvm.exp.f32(float) +declare float @llvm.powi.f32.i32(float, i32) + +; CHECK-LABEL: llvm.func @fastmath_intr +define void @fastmath_intr(float %arg1, i32 %arg2) { + ; CHECK: llvm.intr.exp(%{{.*}}) {fastmathFlags = #llvm.fastmath} : (f32) -> f32 + %1 = call nnan ninf float @llvm.exp.f32(float %arg1) + ; CHECK: llvm.intr.powi(%{{.*}}, %{{.*}}) {fastmathFlags = #llvm.fastmath} : (f32, i32) -> f32 + %2 = call fast float @llvm.powi.f32.i32(float %arg1, i32 %arg2) + ret void +}