summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mlir/include/mlir/Target/LLVMIR.h10
-rw-r--r--mlir/lib/Target/CMakeLists.txt5
-rw-r--r--mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp593
-rw-r--r--mlir/test/Target/import.ll146
-rw-r--r--mlir/test/lit.cfg.py2
5 files changed, 754 insertions, 2 deletions
diff --git a/mlir/include/mlir/Target/LLVMIR.h b/mlir/include/mlir/Target/LLVMIR.h
index a79aaee12d9..7ed7b39c4db 100644
--- a/mlir/include/mlir/Target/LLVMIR.h
+++ b/mlir/include/mlir/Target/LLVMIR.h
@@ -32,6 +32,8 @@ class Module;
namespace mlir {
+class OwningModuleRef;
+class MLIRContext;
class ModuleOp;
/// Convert the given MLIR module into LLVM IR. The LLVM context is extracted
@@ -40,6 +42,14 @@ class ModuleOp;
/// the MLIR module), and return `nullptr`.
std::unique_ptr<llvm::Module> translateModuleToLLVMIR(ModuleOp m);
+/// Convert the given LLVM module into MLIR's LLVM dialect. The LLVM context is
+/// extracted from the registered LLVM IR dialect. In case of error, report it
+/// to the error handler registered with the MLIR context, if any (obtained from
+/// the MLIR module), and return `{}`.
+OwningModuleRef
+translateLLVMIRToModule(std::unique_ptr<llvm::Module> llvmModule,
+ MLIRContext *context);
+
} // namespace mlir
#endif // MLIR_TARGET_LLVMIR_H
diff --git a/mlir/lib/Target/CMakeLists.txt b/mlir/lib/Target/CMakeLists.txt
index efeae86db60..25000d97f5f 100644
--- a/mlir/lib/Target/CMakeLists.txt
+++ b/mlir/lib/Target/CMakeLists.txt
@@ -6,8 +6,11 @@ add_llvm_library(MLIRTargetLLVMIRModuleTranslation
DEPENDS
intrinsics_gen
)
-target_link_libraries(MLIRTargetLLVMIRModuleTranslation MLIRLLVMIR LLVMCore LLVMSupport LLVMTransformUtils MLIRTranslation)
+target_link_libraries(MLIRTargetLLVMIRModuleTranslation
+ MLIRLLVMIR LLVMCore LLVMIRReader LLVMSupport LLVMTransformUtils
+ MLIRTranslation)
add_llvm_library(MLIRTargetLLVMIR
+ LLVMIR/ConvertFromLLVMIR.cpp
LLVMIR/ConvertToLLVMIR.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp
new file mode 100644
index 00000000000..ebcb285e156
--- /dev/null
+++ b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp
@@ -0,0 +1,593 @@
+//===- ConvertFromLLVMIR.cpp - MLIR to LLVM IR conversion -----------------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This file implements a translation between LLVM IR and the MLIR LLVM dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/MLIRContext.h"
+#include "mlir/IR/Module.h"
+#include "mlir/IR/StandardTypes.h"
+#include "mlir/Target/LLVMIR.h"
+#include "mlir/Translation.h"
+
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/SourceMgr.h"
+
+using namespace mlir;
+using namespace mlir::LLVM;
+
+// Utility to print an LLVM value as a string for passing to emitError().
+// FIXME: Diagnostic should be able to natively handle types that have
+// operator << (raw_ostream&) defined.
+static std::string diag(llvm::Value &v) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << v;
+ return os.str();
+}
+
+// Handles importing globals and functions from an LLVM module.
+namespace {
+class Importer {
+public:
+ Importer(MLIRContext *context, ModuleOp module)
+ : b(context), context(context), module(module),
+ unknownLoc(FileLineColLoc::get("imported-bitcode", 0, 0, context)) {
+ b.setInsertionPointToStart(module.getBody());
+ dialect = context->getRegisteredDialect<LLVMDialect>();
+ }
+
+ /// Imports `f` into the current module.
+ LogicalResult processFunction(llvm::Function *f);
+
+ /// Imports GV as a GlobalOp, creating it if it doesn't exist.
+ GlobalOp processGlobal(llvm::GlobalVariable *GV);
+
+private:
+ /// Imports `bb` into `block`, which must be initially empty.
+ LogicalResult processBasicBlock(llvm::BasicBlock *bb, Block *block);
+ /// Imports `inst` and populates instMap[inst] with the imported Value.
+ LogicalResult processInstruction(llvm::Instruction *inst);
+ /// Creates an LLVMType for `type`.
+ LLVMType processType(llvm::Type *type);
+ /// `value` is an SSA-use. Return the remapped version of `value` or a
+ /// placeholder that will be remapped later if this is an instruction that
+ /// has not yet been visited.
+ Value *processValue(llvm::Value *value);
+ /// Create the most accurate Location possible using a llvm::DebugLoc and
+ /// possibly an llvm::Instruction to narrow the Location if debug information
+ /// is unavailable.
+ Location processDebugLoc(const llvm::DebugLoc &loc,
+ llvm::Instruction *inst = nullptr);
+ /// `br` branches to `target`. Return the block arguments to attach to the
+ /// generated branch op. These should be in the same order as the PHIs in
+ /// `target`.
+ SmallVector<Value *, 4> processBranchArgs(llvm::BranchInst *br,
+ llvm::BasicBlock *target);
+ /// Return `value` as an attribute to attach to a GlobalOp.
+ Attribute getConstantAsAttr(llvm::Constant *value);
+ /// Return `c` as an MLIR Value. This could either be a ConstantOp, or
+ /// an expanded sequence of ops in the current function's entry block (for
+ /// ConstantExprs or ConstantGEPs).
+ Value *processConstant(llvm::Constant *c);
+
+ /// The current builder, pointing at where the next Instruction should be
+ /// generated.
+ OpBuilder b;
+ /// The current context.
+ MLIRContext *context;
+ /// The current module being created.
+ ModuleOp module;
+ /// The entry block of the current function being processed.
+ Block *currentEntryBlock;
+
+ /// Globals are inserted before the first function, if any.
+ Block::iterator getGlobalInsertPt() {
+ auto i = module.getBody()->begin();
+ while (!isa<LLVMFuncOp>(i) && !isa<ModuleTerminatorOp>(i))
+ ++i;
+ return i;
+ }
+
+ /// Functions are always inserted before the module terminator.
+ Block::iterator getFuncInsertPt() {
+ return std::prev(module.getBody()->end());
+ }
+
+ /// Remapped blocks, for the current function.
+ DenseMap<llvm::BasicBlock *, Block *> blocks;
+ /// Remapped values. These are function-local.
+ DenseMap<llvm::Value *, Value *> instMap;
+ /// Instructions that had not been defined when first encountered as a use.
+ /// Maps to the dummy Operation that was created in processValue().
+ DenseMap<llvm::Value *, Operation *> unknownInstMap;
+ /// Uniquing map of GlobalVariables.
+ DenseMap<llvm::GlobalVariable *, GlobalOp> globals;
+ /// Cached FileLineColLoc::get("imported-bitcode", 0, 0).
+ Location unknownLoc;
+ /// Cached dialect.
+ LLVMDialect *dialect;
+};
+} // namespace
+
+Location Importer::processDebugLoc(const llvm::DebugLoc &loc,
+ llvm::Instruction *inst) {
+ if (!loc && inst) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << "llvm-imported-inst-%";
+ inst->printAsOperand(os, /*PrintType=*/false);
+ return FileLineColLoc::get(os.str(), 0, 0, context);
+ } else if (!loc) {
+ return unknownLoc;
+ }
+ // FIXME: Obtain the filename from DILocationInfo.
+ return FileLineColLoc::get("imported-bitcode", loc.getLine(), loc.getCol(),
+ context);
+}
+
+LLVMType Importer::processType(llvm::Type *type) {
+ switch (type->getTypeID()) {
+ case llvm::Type::FloatTyID:
+ return LLVMType::getFloatTy(dialect);
+ case llvm::Type::DoubleTyID:
+ return LLVMType::getDoubleTy(dialect);
+ case llvm::Type::IntegerTyID:
+ return LLVMType::getIntNTy(dialect, type->getIntegerBitWidth());
+ case llvm::Type::PointerTyID:
+ return processType(type->getPointerElementType())
+ .getPointerTo(type->getPointerAddressSpace());
+ case llvm::Type::ArrayTyID:
+ return LLVMType::getArrayTy(processType(type->getArrayElementType()),
+ type->getArrayNumElements());
+ case llvm::Type::VectorTyID: {
+ if (type->getVectorIsScalable())
+ emitError(unknownLoc) << "scalable vector types not supported";
+ return LLVMType::getVectorTy(processType(type->getVectorElementType()),
+ type->getVectorNumElements());
+ }
+ case llvm::Type::VoidTyID:
+ return LLVMType::getVoidTy(dialect);
+ case llvm::Type::FP128TyID:
+ return LLVMType::getFP128Ty(dialect);
+ case llvm::Type::X86_FP80TyID:
+ return LLVMType::getX86_FP80Ty(dialect);
+ case llvm::Type::StructTyID: {
+ SmallVector<LLVMType, 4> elementTypes;
+ for (unsigned i = 0, e = type->getStructNumElements(); i != e; ++i)
+ elementTypes.push_back(processType(type->getStructElementType(i)));
+ return LLVMType::getStructTy(dialect, elementTypes,
+ cast<llvm::StructType>(type)->isPacked());
+ }
+ case llvm::Type::FunctionTyID: {
+ llvm::FunctionType *fty = cast<llvm::FunctionType>(type);
+ SmallVector<LLVMType, 4> paramTypes;
+ for (unsigned i = 0, e = fty->getNumParams(); i != e; ++i)
+ paramTypes.push_back(processType(fty->getParamType(i)));
+ return LLVMType::getFunctionTy(processType(fty->getReturnType()),
+ paramTypes, fty->isVarArg());
+ }
+ default: {
+ // FIXME: Diagnostic should be able to natively handle types that have
+ // operator<<(raw_ostream&) defined.
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << *type;
+ emitError(unknownLoc) << "unhandled type: " << os.str();
+ return {};
+ }
+ }
+}
+
+// Get the given constant as an attribute. Not all constants can be represented
+// as attributes.
+Attribute Importer::getConstantAsAttr(llvm::Constant *value) {
+ if (auto *ci = dyn_cast<llvm::ConstantInt>(value))
+ return b.getIntegerAttr(
+ IntegerType::get(ci->getType()->getBitWidth(), context),
+ ci->getValue());
+ if (auto *c = dyn_cast<llvm::ConstantDataArray>(value))
+ if (c->isString())
+ return b.getStringAttr(c->getAsString());
+ emitError(unknownLoc) << "unhandled constant type for attribute: "
+ << diag(*value);
+ return Attribute();
+}
+
+GlobalOp Importer::processGlobal(llvm::GlobalVariable *GV) {
+ auto it = globals.find(GV);
+ if (it != globals.end())
+ return it->second;
+
+ OpBuilder b(module.getBody(), getGlobalInsertPt());
+ Attribute valueAttr;
+ if (GV->hasInitializer())
+ valueAttr = getConstantAsAttr(GV->getInitializer());
+ return globals[GV] = b.create<GlobalOp>(
+ UnknownLoc::get(context), processType(GV->getValueType()),
+ GV->isConstant(), GV->getName(), valueAttr);
+}
+
+Value *Importer::processConstant(llvm::Constant *c) {
+ if (isa<llvm::ConstantInt>(c) || isa<llvm::ConstantDataArray>(c)) {
+ // These constants can be represented as attributes.
+ OpBuilder b(currentEntryBlock, currentEntryBlock->begin());
+ return instMap[c] = b.create<ConstantOp>(
+ unknownLoc, processType(c->getType()), getConstantAsAttr(c));
+ }
+ if (auto *cn = dyn_cast<llvm::ConstantPointerNull>(c)) {
+ OpBuilder b(currentEntryBlock, currentEntryBlock->begin());
+ return instMap[c] =
+ b.create<NullOp>(unknownLoc, processType(cn->getType()));
+ }
+ if (auto *ce = dyn_cast<llvm::ConstantExpr>(c)) {
+ llvm::Instruction *i = ce->getAsInstruction();
+ OpBuilder::InsertionGuard guard(b);
+ b.setInsertionPoint(currentEntryBlock, currentEntryBlock->begin());
+ if (failed(processInstruction(i)))
+ return nullptr;
+ assert(instMap.count(i));
+
+ // Remove this zombie LLVM instruction now, leaving us only with the MLIR
+ // op.
+ i->deleteValue();
+ return instMap[c] = instMap[i];
+ }
+ emitError(unknownLoc) << "unhandled constant: " << diag(*c);
+ return nullptr;
+}
+
+Value *Importer::processValue(llvm::Value *value) {
+ auto it = instMap.find(value);
+ if (it != instMap.end())
+ return it->second;
+
+ // We don't expect to see instructions in dominator order. If we haven't seen
+ // this instruction yet, create an unknown op and remap it later.
+ if (isa<llvm::Instruction>(value)) {
+ OperationState state(UnknownLoc::get(context), "unknown");
+ state.addTypes({processType(value->getType())});
+ unknownInstMap[value] = b.createOperation(state);
+ return unknownInstMap[value]->getResult(0);
+ }
+
+ if (auto *GV = dyn_cast<llvm::GlobalVariable>(value)) {
+ return b.create<AddressOfOp>(UnknownLoc::get(context), processGlobal(GV),
+ ArrayRef<NamedAttribute>());
+ }
+
+ // Note, constant global variables are both GlobalVariables and Constants,
+ // so we handle GlobalVariables first above.
+ if (auto *c = dyn_cast<llvm::Constant>(value))
+ return processConstant(c);
+
+ emitError(unknownLoc) << "unhandled value: " << diag(*value);
+ return nullptr;
+}
+
+// Maps from LLVM opcode to MLIR OperationName. This is deliberately ordered
+// as in llvm/IR/Instructions.def to aid comprehension and spot missing
+// instructions.
+#define INST(llvm_n, mlir_n) \
+ { llvm::Instruction::llvm_n, LLVM::mlir_n##Op::getOperationName() }
+static const DenseMap<unsigned, StringRef> opcMap = {
+ // Ret is handled specially.
+ // Br is handled specially.
+ // FIXME: switch
+ // FIXME: indirectbr
+ // FIXME: invoke
+ // FIXME: resume
+ // FIXME: unreachable
+ // FIXME: cleanupret
+ // FIXME: catchret
+ // FIXME: catchswitch
+ // FIXME: callbr
+ // FIXME: fneg
+ INST(Add, Add), INST(FAdd, FAdd), INST(Sub, Sub), INST(FSub, FSub),
+ INST(Mul, Mul), INST(FMul, FMul), INST(UDiv, UDiv), INST(SDiv, SDiv),
+ INST(FDiv, FDiv), INST(URem, URem), INST(SRem, SRem), INST(FRem, FRem),
+ INST(Shl, Shl), INST(LShr, LShr), INST(AShr, AShr), INST(And, And),
+ INST(Or, Or), INST(Xor, XOr), INST(Alloca, Alloca), INST(Load, Load),
+ INST(Store, Store),
+ // Getelementptr is handled specially.
+ INST(Ret, Return),
+ // FIXME: fence
+ // FIXME: atomiccmpxchg
+ // FIXME: atomicrmw
+ INST(Trunc, Trunc), INST(ZExt, ZExt), INST(SExt, SExt),
+ INST(FPToUI, FPToUI), INST(FPToSI, FPToSI), INST(UIToFP, UIToFP),
+ INST(SIToFP, SIToFP), INST(FPTrunc, FPTrunc), INST(FPExt, FPExt),
+ INST(PtrToInt, PtrToInt), INST(IntToPtr, IntToPtr), INST(BitCast, Bitcast),
+ INST(AddrSpaceCast, AddrSpaceCast),
+ // FIXME: cleanuppad
+ // FIXME: catchpad
+ // ICmp is handled specially.
+ // FIXME: fcmp
+ // PHI is handled specially.
+ INST(Call, Call),
+ // FIXME: select
+ // FIXME: vaarg
+ // FIXME: extractelement
+ // FIXME: insertelement
+ // FIXME: shufflevector
+ // FIXME: extractvalue
+ // FIXME: insertvalue
+ // FIXME: landingpad
+};
+#undef INST
+
+static ICmpPredicate getICmpPredicate(llvm::CmpInst::Predicate p) {
+ switch (p) {
+ default:
+ llvm_unreachable("incorrect comparison predicate");
+ case llvm::CmpInst::Predicate::ICMP_EQ:
+ return LLVM::ICmpPredicate::eq;
+ case llvm::CmpInst::Predicate::ICMP_NE:
+ return LLVM::ICmpPredicate::ne;
+ case llvm::CmpInst::Predicate::ICMP_SLT:
+ return LLVM::ICmpPredicate::slt;
+ case llvm::CmpInst::Predicate::ICMP_SLE:
+ return LLVM::ICmpPredicate::sle;
+ case llvm::CmpInst::Predicate::ICMP_SGT:
+ return LLVM::ICmpPredicate::sgt;
+ case llvm::CmpInst::Predicate::ICMP_SGE:
+ return LLVM::ICmpPredicate::sge;
+ case llvm::CmpInst::Predicate::ICMP_ULT:
+ return LLVM::ICmpPredicate::ult;
+ case llvm::CmpInst::Predicate::ICMP_ULE:
+ return LLVM::ICmpPredicate::ule;
+ case llvm::CmpInst::Predicate::ICMP_UGT:
+ return LLVM::ICmpPredicate::ugt;
+ case llvm::CmpInst::Predicate::ICMP_UGE:
+ return LLVM::ICmpPredicate::uge;
+ }
+ llvm_unreachable("incorrect comparison predicate");
+}
+
+// `br` branches to `target`. Return the branch arguments to `br`, in the
+// same order of the PHIs in `target`.
+SmallVector<Value *, 4> Importer::processBranchArgs(llvm::BranchInst *br,
+ llvm::BasicBlock *target) {
+ SmallVector<Value *, 4> v;
+ for (auto inst = target->begin(); isa<llvm::PHINode>(inst); ++inst) {
+ auto *PN = cast<llvm::PHINode>(&*inst);
+ v.push_back(processValue(PN->getIncomingValueForBlock(br->getParent())));
+ }
+ return v;
+}
+
+LogicalResult Importer::processInstruction(llvm::Instruction *inst) {
+ // FIXME: Support uses of SubtargetData. Currently inbounds GEPs, fast-math
+ // flags and call / operand attributes are not supported.
+ Location loc = processDebugLoc(inst->getDebugLoc(), inst);
+ Value *&v = instMap[inst];
+ assert(!v && "processInstruction must be called only once per instruction!");
+ switch (inst->getOpcode()) {
+ default:
+ return emitError(loc) << "unknown instruction: " << diag(*inst);
+ case llvm::Instruction::Add:
+ case llvm::Instruction::FAdd:
+ case llvm::Instruction::Sub:
+ case llvm::Instruction::FSub:
+ case llvm::Instruction::Mul:
+ case llvm::Instruction::FMul:
+ case llvm::Instruction::UDiv:
+ case llvm::Instruction::SDiv:
+ case llvm::Instruction::FDiv:
+ case llvm::Instruction::URem:
+ case llvm::Instruction::SRem:
+ case llvm::Instruction::FRem:
+ case llvm::Instruction::Shl:
+ case llvm::Instruction::LShr:
+ case llvm::Instruction::AShr:
+ case llvm::Instruction::And:
+ case llvm::Instruction::Or:
+ case llvm::Instruction::Xor:
+ case llvm::Instruction::Alloca:
+ case llvm::Instruction::Load:
+ case llvm::Instruction::Store:
+ case llvm::Instruction::Ret:
+ case llvm::Instruction::Trunc:
+ case llvm::Instruction::ZExt:
+ case llvm::Instruction::SExt:
+ case llvm::Instruction::FPToUI:
+ case llvm::Instruction::FPToSI:
+ case llvm::Instruction::UIToFP:
+ case llvm::Instruction::SIToFP:
+ case llvm::Instruction::FPTrunc:
+ case llvm::Instruction::FPExt:
+ case llvm::Instruction::PtrToInt:
+ case llvm::Instruction::IntToPtr:
+ case llvm::Instruction::AddrSpaceCast:
+ case llvm::Instruction::BitCast: {
+ OperationState state(loc, opcMap.lookup(inst->getOpcode()));
+ SmallVector<Value *, 4> ops;
+ ops.reserve(inst->getNumOperands());
+ for (auto *op : inst->operand_values())
+ ops.push_back(processValue(op));
+ state.addOperands(ops);
+ if (!inst->getType()->isVoidTy())
+ state.addTypes(ArrayRef<Type>({processType(inst->getType())}));
+ Operation *op = b.createOperation(state);
+ if (!inst->getType()->isVoidTy())
+ v = op->getResult(0);
+ return success();
+ }
+ case llvm::Instruction::ICmp: {
+ v = b.create<ICmpOp>(
+ loc, getICmpPredicate(cast<llvm::ICmpInst>(inst)->getPredicate()),
+ processValue(inst->getOperand(0)), processValue(inst->getOperand(1)));
+ return success();
+ }
+ case llvm::Instruction::Br: {
+ auto *brInst = cast<llvm::BranchInst>(inst);
+ OperationState state(loc,
+ brInst->isConditional() ? "llvm.cond_br" : "llvm.br");
+ SmallVector<Value *, 4> ops;
+ if (brInst->isConditional())
+ ops.push_back(processValue(brInst->getCondition()));
+ state.addOperands(ops);
+ SmallVector<Block *, 4> succs;
+ for (auto *succ : llvm::reverse(brInst->successors()))
+ state.addSuccessor(blocks[succ], processBranchArgs(brInst, succ));
+ b.createOperation(state);
+ return success();
+ }
+ case llvm::Instruction::PHI: {
+ v = b.getInsertionBlock()->addArgument(processType(inst->getType()));
+ return success();
+ }
+ case llvm::Instruction::Call: {
+ llvm::CallInst *ci = cast<llvm::CallInst>(inst);
+ SmallVector<Value *, 4> ops;
+ ops.reserve(inst->getNumOperands());
+ for (auto &op : ci->arg_operands())
+ ops.push_back(processValue(op.get()));
+
+ SmallVector<Type, 2> tys;
+ if (!ci->getType()->isVoidTy())
+ tys.push_back(processType(inst->getType()));
+ Operation *op;
+ if (llvm::Function *callee = ci->getCalledFunction()) {
+ op = b.create<CallOp>(loc, tys, b.getSymbolRefAttr(callee->getName()),
+ ops);
+ } else {
+ ops.insert(ops.begin(), processValue(ci->getCalledValue()));
+ op = b.create<CallOp>(loc, tys, ops, ArrayRef<NamedAttribute>());
+ }
+ if (!ci->getType()->isVoidTy())
+ v = op->getResult(0);
+ return success();
+ }
+ case llvm::Instruction::GetElementPtr: {
+ // FIXME: Support inbounds GEPs.
+ llvm::GetElementPtrInst *gep = cast<llvm::GetElementPtrInst>(inst);
+ SmallVector<Value *, 4> ops;
+ for (auto *op : gep->operand_values())
+ ops.push_back(processValue(op));
+ v = b.create<GEPOp>(loc, processType(inst->getType()), ops,
+ ArrayRef<NamedAttribute>());
+ return success();
+ }
+ }
+}
+
+LogicalResult Importer::processFunction(llvm::Function *f) {
+ blocks.clear();
+ instMap.clear();
+ unknownInstMap.clear();
+
+ b.setInsertionPoint(module.getBody(), getFuncInsertPt());
+ LLVMFuncOp fop = b.create<LLVMFuncOp>(UnknownLoc::get(context), f->getName(),
+ processType(f->getFunctionType()));
+ if (f->isDeclaration())
+ return success();
+
+ // Eagerly create all blocks.
+ SmallVector<Block *, 4> blockList;
+ for (llvm::BasicBlock &bb : *f) {
+ blockList.push_back(b.createBlock(&fop.body(), fop.body().end()));
+ blocks[&bb] = blockList.back();
+ }
+ currentEntryBlock = blockList[0];
+
+ // Add function arguments to the entry block.
+ for (auto &arg : f->args())
+ instMap[&arg] = blockList[0]->addArgument(processType(arg.getType()));
+
+ for (auto bbs : llvm::zip(*f, blockList)) {
+ if (failed(processBasicBlock(&std::get<0>(bbs), std::get<1>(bbs))))
+ return failure();
+ }
+
+ // Now that all instructions are guaranteed to have been visited, ensure
+ // any unknown uses we encountered are remapped.
+ for (auto &llvmAndUnknown : unknownInstMap) {
+ assert(instMap.count(llvmAndUnknown.first));
+ Value *newValue = instMap[llvmAndUnknown.first];
+ Value *oldValue = llvmAndUnknown.second->getResult(0);
+ oldValue->replaceAllUsesWith(newValue);
+ llvmAndUnknown.second->erase();
+ }
+ return success();
+}
+
+LogicalResult Importer::processBasicBlock(llvm::BasicBlock *bb, Block *block) {
+ b.setInsertionPointToStart(block);
+ for (llvm::Instruction &inst : *bb) {
+ if (failed(processInstruction(&inst)))
+ return failure();
+ }
+ return success();
+}
+
+OwningModuleRef
+mlir::translateLLVMIRToModule(std::unique_ptr<llvm::Module> llvmModule,
+ MLIRContext *context) {
+ OwningModuleRef module(ModuleOp::create(
+ FileLineColLoc::get("", /*line=*/0, /*column=*/0, context)));
+
+ Importer deserializer(context, module.get());
+ for (llvm::GlobalVariable &gv : llvmModule->globals()) {
+ if (!deserializer.processGlobal(&gv))
+ return {};
+ }
+ for (llvm::Function &f : llvmModule->functions()) {
+ if (failed(deserializer.processFunction(&f)))
+ return {};
+ }
+
+ return module;
+}
+
+// Deserializes the LLVM bitcode stored in `input` into an MLIR module in the
+// LLVM dialect.
+OwningModuleRef
+translateLLVMIRToModule(std::unique_ptr<llvm::MemoryBuffer> input,
+ MLIRContext *context) {
+ LLVMDialect *dialect = context->getRegisteredDialect<LLVMDialect>();
+ assert(dialect && "Could not find LLVMDialect?");
+
+ llvm::SMDiagnostic err;
+ std::unique_ptr<llvm::Module> llvmModule =
+ llvm::parseIR(*input, err, dialect->getLLVMContext(),
+ /*UpgradeDebugInfo=*/true,
+ /*DataLayoutString=*/"");
+ if (!llvmModule) {
+ std::string errStr;
+ llvm::raw_string_ostream errStream(errStr);
+ err.print(/*ProgName=*/"", errStream);
+ emitError(UnknownLoc::get(context)) << errStream.str();
+ return {};
+ }
+ return translateLLVMIRToModule(std::move(llvmModule), context);
+}
+
+static TranslateToMLIRRegistration
+ fromLLVM("import-llvm", [](std::unique_ptr<llvm::MemoryBuffer> input,
+ MLIRContext *context) {
+ return translateLLVMIRToModule(std::move(input), context);
+ });
diff --git a/mlir/test/Target/import.ll b/mlir/test/Target/import.ll
new file mode 100644
index 00000000000..6a0c88bb77f
--- /dev/null
+++ b/mlir/test/Target/import.ll
@@ -0,0 +1,146 @@
+; RUN: mlir-translate -import-llvm %s | FileCheck %s
+
+%struct.t = type {}
+%struct.s = type { %struct.t, i64 }
+
+; CHECK: llvm.mlir.global @g1() : !llvm<"{ {}, i64 }">
+@g1 = external global %struct.s, align 8
+; CHECK: llvm.mlir.global @g2() : !llvm.double
+@g2 = external global double, align 8
+; CHECK: llvm.mlir.global @g3("string")
+@g3 = internal global [6 x i8] c"string"
+
+; CHECK: llvm.mlir.global @g5() : !llvm<"<8 x i32>">
+@g5 = external global <8 x i32>
+
+; CHECK: llvm.func @fe(!llvm.i32) -> !llvm.float
+declare float @fe(i32)
+
+; FIXME: function attributes.
+; CHECK-LABEL: llvm.func @f1(%arg0: !llvm.i64) -> !llvm.i32 {
+; CHECK-DAG: %[[c2:[0-9]+]] = llvm.mlir.constant(2 : i32) : !llvm.i32
+; CHECK-DAG: %[[c42:[0-9]+]] = llvm.mlir.constant(42 : i32) : !llvm.i32
+; CHECK-DAG: %[[c1:[0-9]+]] = llvm.mlir.constant(1 : i1) : !llvm.i1
+; CHECK-DAG: %[[c43:[0-9]+]] = llvm.mlir.constant(43 : i32) : !llvm.i32
+define internal dso_local i32 @f1(i64 %a) norecurse {
+entry:
+; CHECK: %{{[0-9]+}} = llvm.inttoptr %arg0 : !llvm.i64 to !llvm<"i64*">
+ %aa = inttoptr i64 %a to i64*
+; CHECK: %[[addrof:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
+; CHECK: %{{[0-9]+}} = llvm.ptrtoint %[[addrof]] : !llvm<"double*"> to !llvm.i64
+ %bb = ptrtoint double* @g2 to i64
+; CHECK-DAG: %[[addrof2:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
+; CHECK: %{{[0-9]+}} = llvm.getelementptr %[[addrof2]][%[[c2]]] : (!llvm<"double*">, !llvm.i32) -> !llvm<"double*">
+ %cc = getelementptr double, double* @g2, i32 2
+; CHECK: %[[b:[0-9]+]] = llvm.trunc %arg0 : !llvm.i64 to !llvm.i32
+ %b = trunc i64 %a to i32
+; CHECK: %[[c:[0-9]+]] = llvm.call @fe(%[[b]]) : (!llvm.i32) -> !llvm.float
+ %c = call float @fe(i32 %b)
+; CHECK: %[[d:[0-9]+]] = llvm.fptosi %[[c]] : !llvm.float to !llvm.i32
+ %d = fptosi float %c to i32
+; FIXME: icmp should return i1.
+; CHECK: %[[e:[0-9]+]] = llvm.icmp "ne" %[[d]], %[[c2]] : !llvm.i32
+ %e = icmp ne i32 %d, 2
+; CHECK: llvm.cond_br %[[e]], ^bb1, ^bb2
+ br i1 %e, label %if.then, label %if.end
+
+; CHECK: ^bb1:
+if.then:
+; CHECK: llvm.return %[[c42]] : !llvm.i32
+ ret i32 42
+
+; CHECK: ^bb2:
+if.end:
+; CHECK: %[[orcond:[0-9]+]] = llvm.or %[[e]], %[[c1]] : !llvm.i1
+ %or.cond = or i1 %e, 1
+; CHECK: llvm.return %[[c43]]
+ ret i32 43
+}
+
+; Test that instructions that dominate can be out of sequential order.
+; CHECK-LABEL: llvm.func @f2(%arg0: !llvm.i64) -> !llvm.i64 {
+; CHECK-DAG: %[[c3:[0-9]+]] = llvm.mlir.constant(3 : i64) : !llvm.i64
+define i64 @f2(i64 %a) noduplicate {
+entry:
+; CHECK: llvm.br ^bb2
+ br label %next
+
+; CHECK: ^bb1:
+end:
+; CHECK: llvm.return %1
+ ret i64 %b
+
+; CHECK: ^bb2:
+next:
+; CHECK: %1 = llvm.add %arg0, %[[c3]] : !llvm.i64
+ %b = add i64 %a, 3
+; CHECK: llvm.br ^bb1
+ br label %end
+}
+
+; Test arguments/phis.
+; CHECK-LABEL: llvm.func @f2_phis(%arg0: !llvm.i64) -> !llvm.i64 {
+; CHECK-DAG: %[[c3:[0-9]+]] = llvm.mlir.constant(3 : i64) : !llvm.i64
+define i64 @f2_phis(i64 %a) noduplicate {
+entry:
+; CHECK: llvm.br ^bb2
+ br label %next
+
+; CHECK: ^bb1(%1: !llvm.i64):
+end:
+ %c = phi i64 [ %b, %next ]
+; CHECK: llvm.return %1
+ ret i64 %c
+
+; CHECK: ^bb2:
+next:
+; CHECK: %2 = llvm.add %arg0, %[[c3]] : !llvm.i64
+ %b = add i64 %a, 3
+; CHECK: llvm.br ^bb1
+ br label %end
+}
+
+; CHECK-LABEL: llvm.func @f3() -> !llvm<"i32*">
+define i32* @f3() {
+; CHECK: %[[c:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
+; CHECK: %[[b:[0-9]+]] = llvm.bitcast %[[c]] : !llvm<"double*"> to !llvm<"i32*">
+; CHECK: llvm.return %[[b]] : !llvm<"i32*">
+ ret i32* bitcast (double* @g2 to i32*)
+}
+
+; CHECK-LABEL: llvm.func @f4() -> !llvm<"i32*">
+define i32* @f4() {
+; CHECK: %[[b:[0-9]+]] = llvm.mlir.null : !llvm<"i32*">
+; CHECK: llvm.return %[[b]] : !llvm<"i32*">
+ ret i32* bitcast (double* null to i32*)
+}
+
+; CHECK-LABEL: llvm.func @f5
+define void @f5(i32 %d) {
+; FIXME: icmp should return i1.
+; CHECK: = llvm.icmp "eq"
+ %1 = icmp eq i32 %d, 2
+; CHECK: = llvm.icmp "slt"
+ %2 = icmp slt i32 %d, 2
+; CHECK: = llvm.icmp "sle"
+ %3 = icmp sle i32 %d, 2
+; CHECK: = llvm.icmp "sgt"
+ %4 = icmp sgt i32 %d, 2
+; CHECK: = llvm.icmp "sge"
+ %5 = icmp sge i32 %d, 2
+; CHECK: = llvm.icmp "ult"
+ %6 = icmp ult i32 %d, 2
+; CHECK: = llvm.icmp "ule"
+ %7 = icmp ule i32 %d, 2
+; CHECK: = llvm.icmp "ugt"
+ %8 = icmp ugt i32 %d, 2
+ ret void
+}
+
+; CHECK-LABEL: llvm.func @f6(%arg0: !llvm<"void (i16)*">)
+define void @f6(void (i16) *%fn) {
+; CHECK: %[[c:[0-9]+]] = llvm.mlir.constant(0 : i16) : !llvm.i16
+; CHECK: llvm.call %arg0(%[[c]])
+ call void %fn(i16 0)
+ ret void
+} \ No newline at end of file
diff --git a/mlir/test/lit.cfg.py b/mlir/test/lit.cfg.py
index cf938946289..6333eaab89a 100644
--- a/mlir/test/lit.cfg.py
+++ b/mlir/test/lit.cfg.py
@@ -21,7 +21,7 @@ config.name = 'MLIR'
config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
# suffixes: A list of file extensions to treat as test files.
-config.suffixes = ['.td', '.mlir', '.toy']
+config.suffixes = ['.td', '.mlir', '.toy', '.ll']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
OpenPOWER on IntegriCloud