diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td @@ -373,12 +373,13 @@ # !if(!gt(numResults, 0), "$res = inst;", ""); string mlirBuilder = [{ + FailureOr> mlirOperands = convertValues(llvmOperands); + if (failed(mlirOperands)) + return failure(); SmallVector resultTypes = }] # !if(!gt(numResults, 0), "{$_resultType};", "{};") # [{ Operation *op = $_builder.create<$_qualCppClassName>( - $_location, - resultTypes, - convertValues(llvmOperands)); + $_location, resultTypes, *mlirOperands); }] # !if(!gt(numResults, 0), "$res = op->getResult(0);", "(void)op;"); } diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -810,7 +810,10 @@ builder.CreateRetVoid(); }]; string mlirBuilder = [{ - $_builder.create($_location, convertValues(llvmOperands)); + FailureOr> mlirOperands = convertValues(llvmOperands); + if (failed(mlirOperands)) + return failure(); + $_builder.create($_location, *mlirOperands); }]; } 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 @@ -34,6 +34,7 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" @@ -363,18 +364,20 @@ return blockMapping.lookup(block); } - /// Converts an LLVM value either to the mapped MLIR value or to a constant - /// using the `convertConstant` method. - Value convertValue(llvm::Value *value); + /// Converts an LLVM value to an MLIR value, or returns failure if the + /// conversion fails. Uses the `convertConstant` method to translate constant + /// LLVM values. + FailureOr convertValue(llvm::Value *value); /// Converts a range of LLVM values to a range of MLIR values using the - /// `convertValue` method. - SmallVector convertValues(ArrayRef values); + /// `convertValue` method, or returns failure if the conversion fails. + FailureOr> convertValues(ArrayRef values); - /// Converts `value` to an integer attribute. Asserts if the conversion fails. + /// Converts `value` to an integer attribute. Asserts if the matching fails. IntegerAttr matchIntegerAttr(llvm::Value *value); - /// Converts `value` to a local variable attribute. + /// Converts `value` to a local variable attribute. Asserts if the matching + /// fails. DILocalVariableAttr matchLocalVariableAttr(llvm::Value *value); /// Translates the debug location. @@ -389,10 +392,11 @@ /// Converts an LLVM intrinsic to an MLIR LLVM dialect operation if an MLIR /// counterpart exists. Otherwise, returns failure. - LogicalResult convertIntrinsic(OpBuilder &odsBuilder, llvm::CallInst *inst); + LogicalResult convertIntrinsic(OpBuilder &odsBuilder, llvm::CallInst *inst, + llvm::Intrinsic::ID intrinsicID); - /// Converts an LLVM instruction to an MLIR LLVM dialect operation if the - /// operation defines an MLIR Builder. Otherwise, returns failure. + /// Converts an LLVM instruction to an MLIR LLVM dialect operation if an MLIR + /// counterpart exists. Otherwise, returns failure. LogicalResult convertOperation(OpBuilder &odsBuilder, llvm::Instruction *inst); @@ -435,9 +439,9 @@ /// Appends the converted result type and operands of `callInst` to the /// `types` and `operands` arrays. For indirect calls, the method additionally /// inserts the called function at the beginning of the `operands` array. - void convertCallTypeAndOperands(llvm::CallBase *callInst, - SmallVectorImpl &types, - SmallVectorImpl &operands); + LogicalResult convertCallTypeAndOperands(llvm::CallBase *callInst, + SmallVectorImpl &types, + SmallVectorImpl &operands); /// Returns the builtin type equivalent to be used in attributes for the given /// LLVM IR dialect type. Type getStdTypeForAttr(Type type); @@ -446,15 +450,17 @@ /// Returns the topologically sorted set of transitive dependencies needed to /// convert the given constant. SetVector getConstantsToConvert(llvm::Constant *constant); - /// Converts an LLVM constant to an MLIR value produced by a ConstantOp, + /// Converts an LLVM constant to an MLIR value, or returns failure if the + /// conversion fails. The MLIR value may be produced by a ConstantOp, /// AddressOfOp, NullOp, or a side-effect free operation (for ConstantExprs or /// ConstantGEPs). - Value convertConstant(llvm::Constant *constant); - /// Converts an LLVM constant and its transitive dependencies to MLIR + FailureOr convertConstant(llvm::Constant *constant); + /// Converts an LLVM constant and its transitive constant dependencies to MLIR /// operations by converting them in topological order using the - /// `convertConstant` method. All operations are inserted at the - /// start of the current function entry block. - Value convertConstantExpr(llvm::Constant *constant); + /// `convertConstant` method, or returns failure if the conversion of any of + /// them fails. All operations are inserted at the start of the current + /// function entry block. + FailureOr convertConstantExpr(llvm::Constant *constant); /// Builder pointing at where the next instruction should be generated. OpBuilder builder; @@ -482,38 +488,6 @@ }; } // namespace -LogicalResult Importer::convertIntrinsic(OpBuilder &odsBuilder, - llvm::CallInst *inst) { - // Check if the callee is an intrinsic. - llvm::Function *callee = inst->getCalledFunction(); - if (!callee || !callee->isIntrinsic()) - return failure(); - - // Check if the intrinsic is convertible to an MLIR dialect counterpart. - llvm::Intrinsic::ID intrinsicID = callee->getIntrinsicID(); - if (!isConvertibleIntrinsic(intrinsicID)) - return failure(); - - // Copy the call arguments to initialize operands array reference used by - // the conversion. - SmallVector args(inst->args()); - ArrayRef llvmOperands(args); -#include "mlir/Dialect/LLVMIR/LLVMIntrinsicFromLLVMIRConversions.inc" - - return failure(); -} - -LogicalResult Importer::convertOperation(OpBuilder &odsBuilder, - llvm::Instruction *inst) { - // Copy the instruction operands to initialize the operands array reference - // used by the conversion. - SmallVector operands(inst->operands()); - ArrayRef llvmOperands(operands); -#include "mlir/Dialect/LLVMIR/LLVMOpFromLLVMIRConversions.inc" - - return failure(); -} - // We only need integers, floats, doubles, and vectors and tensors thereof for // attributes. Scalar and vector types are converted to the standard // equivalents. Array types are converted to ranked tensors; nested array types @@ -690,8 +664,11 @@ clearBlockAndValueMapping(); Block *block = builder.createBlock(&globalOp.getInitializerRegion()); setConstantInsertionPointToStart(block); - Value value = convertConstantExpr(globalVar->getInitializer()); - builder.create(globalOp.getLoc(), value); + FailureOr initializer = + convertConstantExpr(globalVar->getInitializer()); + if (failed(initializer)) + return {}; + builder.create(globalOp.getLoc(), initializer.value()); } if (globalVar->hasAtLeastLocalUnnamedAddr()) { globalOp.setUnnamedAddr( @@ -715,12 +692,12 @@ if (valueMapping.count(constant)) continue; orderedList.push_back(current); - // Add the current constant's dependencies to the work list. - for (llvm::Value *operand : current->operands()) { - assert(isa(operand) && - "expected constants to have constant operands only"); - workList.push_back(cast(operand)); - } + // Add the current constant's dependencies to the work list. Only add + // constant dependencies and skip any other values such as basic block + // addresses. + for (llvm::Value *operand : current->operands()) + if (auto *constDependency = dyn_cast(operand)) + workList.push_back(constDependency); // Use the `getElementValue` method to add the dependencies of zero // initialized aggregate constants since they do not take any operands. if (auto *constAgg = dyn_cast(current)) { @@ -739,33 +716,37 @@ return orderedSet; } -Value Importer::convertConstant(llvm::Constant *constant) { +FailureOr Importer::convertConstant(llvm::Constant *constant) { // Constants have no location attached. Location loc = UnknownLoc::get(context); // Convert constants that can be represented as attributes. if (Attribute attr = getConstantAsAttr(constant)) { Type type = convertType(constant->getType()); - if (auto symbolRef = attr.dyn_cast()) - return builder.create(loc, type, symbolRef.getValue()); - return builder.create(loc, type, attr); + if (auto symbolRef = attr.dyn_cast()) { + return builder.create(loc, type, symbolRef.getValue()) + .getResult(); + } + return builder.create(loc, type, attr).getResult(); } // Convert null pointer constants. if (auto *nullPtr = dyn_cast(constant)) { Type type = convertType(nullPtr->getType()); - return builder.create(loc, type); + return builder.create(loc, type).getResult(); } // Convert undef. if (auto *undefVal = dyn_cast(constant)) { Type type = convertType(undefVal->getType()); - return builder.create(loc, type); + return builder.create(loc, type).getResult(); } // Convert global variable accesses. - if (auto *globalVar = dyn_cast(constant)) - return builder.create(loc, processGlobal(globalVar)); + if (auto *globalVar = dyn_cast(constant)) { + return builder.create(loc, processGlobal(globalVar)) + .getResult(); + } // Convert constant expressions. if (auto *constExpr = dyn_cast(constant)) { @@ -785,7 +766,7 @@ return valueMapping.count(value); })); if (failed(processInstruction(inst))) - return nullptr; + return failure(); return lookupValue(inst); } @@ -828,10 +809,10 @@ return root; } - return nullptr; + return emitError(loc) << "unhandled constant " << diag(*constant); } -Value Importer::convertConstantExpr(llvm::Constant *constant) { +FailureOr Importer::convertConstantExpr(llvm::Constant *constant) { assert(constantInsertionBlock && "expected the constant insertion block to be non-null"); @@ -847,13 +828,10 @@ SetVector constantsToConvert = getConstantsToConvert(constant); for (llvm::Constant *constantToConvert : constantsToConvert) { - if (Value value = convertConstant(constantToConvert)) { - mapValue(constantToConvert, value); - continue; - } - - llvm::errs() << diag(*constantToConvert) << "\n"; - llvm_unreachable("unhandled constant"); + FailureOr converted = convertConstant(constantToConvert); + if (failed(converted)) + return failure(); + mapValue(constantToConvert, converted.value()); } // Update the constant insertion point and return the converted constant. @@ -862,7 +840,7 @@ return result; } -Value Importer::convertValue(llvm::Value *value) { +FailureOr Importer::convertValue(llvm::Value *value) { // A value may be wrapped as metadata, for example, when passed to a debug // intrinsic. Unwrap these values before the conversion. if (auto *nodeAsVal = dyn_cast(value)) @@ -877,21 +855,30 @@ if (auto *constant = dyn_cast(value)) return convertConstantExpr(constant); - llvm::errs() << diag(*value) << "\n"; - llvm_unreachable("unhandled value"); + Location loc = UnknownLoc::get(context); + if (auto *inst = dyn_cast(value)) + loc = translateLoc(inst->getDebugLoc()); + return emitError(loc) << "unhandled value " << diag(*value); } -SmallVector Importer::convertValues(ArrayRef values) { +FailureOr> +Importer::convertValues(ArrayRef values) { SmallVector remapped; remapped.reserve(values.size()); - for (llvm::Value *value : values) - remapped.push_back(convertValue(value)); + for (llvm::Value *value : values) { + FailureOr converted = convertValue(value); + if (failed(converted)) + return failure(); + remapped.push_back(converted.value()); + } return remapped; } IntegerAttr Importer::matchIntegerAttr(llvm::Value *value) { IntegerAttr integerAttr; - bool success = matchPattern(convertValue(value), m_Constant(&integerAttr)); + FailureOr converted = convertValue(value); + bool success = succeeded(converted) && + matchPattern(converted.value(), m_Constant(&integerAttr)); assert(success && "expected a constant value"); (void)success; return integerAttr; @@ -907,42 +894,61 @@ Importer::convertBranchArgs(llvm::Instruction *branch, llvm::BasicBlock *target, SmallVectorImpl &blockArguments) { for (auto inst = target->begin(); isa(inst); ++inst) { - auto *phi = cast(&*inst); - llvm::Value *value = phi->getIncomingValueForBlock(branch->getParent()); - blockArguments.push_back(convertValue(value)); + auto *phiInst = cast(&*inst); + llvm::Value *value = phiInst->getIncomingValueForBlock(branch->getParent()); + FailureOr converted = convertValue(value); + if (failed(converted)) + return failure(); + blockArguments.push_back(converted.value()); } return success(); } -void Importer::convertCallTypeAndOperands(llvm::CallBase *callInst, - SmallVectorImpl &types, - SmallVectorImpl &operands) { +LogicalResult +Importer::convertCallTypeAndOperands(llvm::CallBase *callInst, + SmallVectorImpl &types, + SmallVectorImpl &operands) { if (!callInst->getType()->isVoidTy()) types.push_back(convertType(callInst->getType())); if (!callInst->getCalledFunction()) { - Value called = convertValue(callInst->getCalledOperand()); - operands.push_back(called); + FailureOr called = convertValue(callInst->getCalledOperand()); + if (failed(called)) + return failure(); + operands.push_back(called.value()); } SmallVector args(callInst->args()); - llvm::append_range(operands, convertValues(args)); + FailureOr> arguments = convertValues(args); + if (failed(arguments)) + return failure(); + llvm::append_range(operands, arguments.value()); + return success(); } -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 the indirectbr, cleanupret, catchret, catchswitch, - // callbr, vaarg, landingpad, catchpad, cleanuppad instructions. +LogicalResult Importer::convertIntrinsic(OpBuilder &odsBuilder, + llvm::CallInst *inst, + llvm::Intrinsic::ID intrinsicID) { + Location loc = translateLoc(inst->getDebugLoc()); - // Convert all intrinsics that provide an MLIR builder. - if (auto *callInst = dyn_cast(inst)) - if (succeeded(convertIntrinsic(builder, callInst))) - return success(); + // Check if the intrinsic is convertible to an MLIR dialect counterpart and + // copy the arguments to an an LLVM operands array reference for conversion. + if (isConvertibleIntrinsic(intrinsicID)) { + SmallVector args(inst->args()); + ArrayRef llvmOperands(args); +#include "mlir/Dialect/LLVMIR/LLVMIntrinsicFromLLVMIRConversions.inc" + } - // Convert all operations that provide an MLIR builder. - if (succeeded(convertOperation(builder, inst))) - return success(); + return emitError(loc) << "unhandled intrinsic " << diag(*inst); +} + +LogicalResult Importer::convertOperation(OpBuilder &odsBuilder, + llvm::Instruction *inst) { + // Copy the operands to an LLVM operands array reference for conversion. + SmallVector operands(inst->operands()); + ArrayRef llvmOperands(operands); + + // Convert all instructions that provide an MLIR builder. +#include "mlir/Dialect/LLVMIR/LLVMOpFromLLVMIRConversions.inc" // Convert all remaining instructions that do not provide an MLIR builder. Location loc = translateLoc(inst->getDebugLoc()); @@ -961,8 +967,10 @@ } if (brInst->isConditional()) { - Value condition = convertValue(brInst->getCondition()); - builder.create(loc, condition, succBlocks.front(), + FailureOr condition = convertValue(brInst->getCondition()); + if (failed(condition)) + return failure(); + builder.create(loc, condition.value(), succBlocks.front(), succBlockArgs.front(), succBlocks.back(), succBlockArgs.back()); } else { @@ -974,7 +982,9 @@ if (inst->getOpcode() == llvm::Instruction::Switch) { auto *swInst = cast(inst); // Process the condition value. - Value condition = convertValue(swInst->getCondition()); + FailureOr condition = convertValue(swInst->getCondition()); + if (failed(condition)) + return failure(); SmallVector defaultBlockArgs; // Process the default case. llvm::BasicBlock *defaultBB = swInst->getDefaultDest(); @@ -997,7 +1007,7 @@ caseBlocks[it.index()] = lookupBlock(succBB); } - builder.create(loc, condition, lookupBlock(defaultBB), + builder.create(loc, condition.value(), lookupBlock(defaultBB), defaultBlockArgs, caseValues, caseBlocks, caseOperandRefs); return success(); @@ -1013,7 +1023,8 @@ SmallVector types; SmallVector operands; - convertCallTypeAndOperands(callInst, types, operands); + if (failed(convertCallTypeAndOperands(callInst, types, operands))) + return failure(); CallOp callOp; if (llvm::Function *callee = callInst->getCalledFunction()) { @@ -1032,8 +1043,10 @@ SmallVector operands; operands.reserve(lpInst->getNumClauses()); for (auto i : llvm::seq(0, lpInst->getNumClauses())) { - Value operand = convertConstantExpr(lpInst->getClause(i)); - operands.push_back(operand); + FailureOr operand = convertConstantExpr(lpInst->getClause(i)); + if (failed(operand)) + return failure(); + operands.push_back(operand.value()); } Type type = convertType(lpInst->getType()); @@ -1047,7 +1060,8 @@ SmallVector types; SmallVector operands; - convertCallTypeAndOperands(invokeInst, types, operands); + if (failed(convertCallTypeAndOperands(invokeInst, types, operands))) + return failure(); SmallVector normalArgs, unwindArgs; (void)convertBranchArgs(invokeInst, invokeInst->getNormalDest(), @@ -1075,7 +1089,9 @@ // FIXME: Support inbounds GEPs. auto *gepInst = cast(inst); Type sourceElementType = convertType(gepInst->getSourceElementType()); - Value basePtr = convertValue(gepInst->getOperand(0)); + FailureOr basePtr = convertValue(gepInst->getOperand(0)); + if (failed(basePtr)) + return failure(); // Treat every indices as dynamic since GEPOp::build will refine those // indices into static attributes later. One small downside of this @@ -1083,18 +1099,38 @@ // at first place. SmallVector indices; for (llvm::Value *operand : llvm::drop_begin(gepInst->operand_values())) { - Value index = convertValue(operand); - indices.push_back(index); + FailureOr index = convertValue(operand); + if (failed(index)) + return failure(); + indices.push_back(index.value()); } Type type = convertType(inst->getType()); - Value res = - builder.create(loc, type, sourceElementType, basePtr, indices); + Value res = builder.create(loc, type, sourceElementType, + basePtr.value(), indices); mapValue(inst, res); return success(); } - return emitError(loc) << "unknown instruction: " << diag(*inst); + return emitError(loc) << "unhandled instruction " << diag(*inst); +} + +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 the indirectbr, cleanupret, catchret, catchswitch, + // callbr, vaarg, landingpad, catchpad, cleanuppad instructions. + + // Convert LLVM intrinsics calls to MLIR intrinsics. + if (auto *callInst = dyn_cast(inst)) { + llvm::Function *callee = callInst->getCalledFunction(); + if (callee && callee->isIntrinsic()) + return convertIntrinsic(builder, callInst, callInst->getIntrinsicID()); + } + + // Convert all remaining LLVM instructions to MLIR operations. + return convertOperation(builder, inst); } FlatSymbolRefAttr Importer::getPersonalityAsAttr(llvm::Function *f) { diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll new file mode 100644 --- /dev/null +++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll @@ -0,0 +1,36 @@ +; RUN: not mlir-translate -import-llvm -split-input-file %s 2>&1 | FileCheck %s + +; CHECK: unhandled instruction indirectbr i8* %dst, [label %bb1, label %bb2] +define i32 @unhandled_instruction(i8* %dst) { + indirectbr i8* %dst, [label %bb1, label %bb2] +bb1: + ret i32 0 +bb2: + ret i32 1 +} + +; // ----- + +; CHECK: unhandled value ptr asm "bswap $0", "=r,r" +define i32 @unhandled_value(i32 %arg0) { + %1 = call i32 asm "bswap $0", "=r,r"(i32 %arg0) + ret i32 %1 +} + +; // ----- + +; CHECK: unhandled constant i8* blockaddress(@unhandled_constant, %bb1) +define i8* @unhandled_constant() { +bb1: + ret i8* blockaddress(@unhandled_constant, %bb1) +} + +; // ----- + +declare void @llvm.gcroot(ptr %arg0, ptr %arg1) + +; CHECK: unhandled intrinsic call void @llvm.gcroot(ptr %arg0, ptr %arg1) +define void @unhandled_intrinsic(ptr %arg0, ptr %arg1) { + call void @llvm.gcroot(ptr %arg0, ptr %arg1) + ret void +} diff --git a/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp b/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp --- a/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp +++ b/mlir/tools/mlir-tblgen/LLVMIRConversionGen.cpp @@ -220,9 +220,12 @@ // Progressively create the builder string by replacing $-variables. Keep only // the not-yet-traversed part of the builder pattern to avoid re-traversing - // the string multiple times. - std::string builder; - llvm::raw_string_ostream bs(builder); + // the string multiple times. Additionally, emit an argument string + // immediately before the builder string. This argument string converts all + // operands used by the builder to MLIR values and returns failure if one of + // the conversions fails. + std::string arguments, builder; + llvm::raw_string_ostream as(arguments), bs(builder); while (StringLoc loc = findNextVariable(builderStrRef)) { auto name = loc.in(builderStrRef).drop_front(); // First, insert the non-matched part as is. @@ -240,17 +243,25 @@ if (isAttributeName(op, name)) { bs << formatv("llvmOperands[{0}]", idx); } else { - bool isVariadicOperand = isVariadicOperandName(op, name); - auto result = - isVariadicOperand - ? formatv("convertValues(llvmOperands.drop_front({0}))", idx) - : formatv("convertValue(llvmOperands[{0}])", idx); - bs << result; + if (isVariadicOperandName(op, name)) { + as << formatv( + "FailureOr> _llvmir_gen_operand_{0} = " + "convertValues(llvmOperands.drop_front({1}));\n", + name, idx); + } else { + as << formatv("FailureOr _llvmir_gen_operand_{0} = " + "convertValue(llvmOperands[{1}]);\n", + name, idx); + } + as << formatv("if (failed(_llvmir_gen_operand_{0}))\n" + " return failure();\n", + name); + bs << formatv("_llvmir_gen_operand_{0}.value()", name); } } else if (isResultName(op, name)) { if (op.getNumResults() != 1) return emitError(record, "expected op to have one result"); - bs << formatv("mapValue(inst)"); + bs << "mapValue(inst)"; } else if (name == "_int_attr") { bs << "matchIntegerAttr"; } else if (name == "_var_attr") { @@ -273,8 +284,9 @@ builderStrRef = builderStrRef.substr(loc.pos + loc.length); } - // Output the check and the builder string. + // Output the check, the argument conversion, and the builder string. os << "if (" << conditionFn(record) << ") {\n"; + os << as.str() << "\n"; os << bs.str() << builderStrRef << "\n"; os << " return success();\n"; os << "}\n";