mirror of https://github.com/llvm/torch-mlir
Wire up IREE compilation and runtime in a new backend test.
* Adds python bindings for invoking flow, HAL, and VM lowering pipelines. * Adds pythong bindings for translating to VM module flatbuffer. * Adds a new backend_test/iree directory and configure lit to find the IREE python rt bindings. * Open code a simple_invoke.py that exercises the whole pipeline (need real APIs for a lot of this). * Fails when invoking the function because I never implemented argument marshaling for scalars :( * Plenty of stuff to do tomorrow.pull/1/head
parent
373878f31f
commit
529873d13c
|
@ -58,8 +58,9 @@ if(NPCOMP_ENABLE_IREE)
|
|||
string(APPEND NPCOMP_TABLEGEN_ARGS "-DNPCOMP_ENABLE_IREE")
|
||||
if(NPCOMP_IREE_SRCDIR)
|
||||
message(STATUS "Depending on IREE source: ${NPCOMP_IREE_SRCDIR}")
|
||||
set(IREE_BUILD_TESTS OFF CACHE BOOL "Override IREE setting")
|
||||
set(IREE_BUILD_SAMPLES OFF CACHE BOOL "Override IREE setting")
|
||||
set(IREE_BUILD_TESTS OFF CACHE BOOL "Override IREE setting" FORCE)
|
||||
set(IREE_BUILD_SAMPLES OFF CACHE BOOL "Override IREE setting" FORCE)
|
||||
set(IREE_BUILD_PYTHON_BINDINGS ON CACHE BOOL "Override IREE setting" FORCE)
|
||||
set(IREE_MLIR_DEP_MODE "DISABLED" CACHE STRING "Override IREE setting")
|
||||
add_subdirectory("${NPCOMP_IREE_SRCDIR}" "iree" EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
|
@ -99,3 +100,7 @@ add_subdirectory(pytest)
|
|||
add_subdirectory(runtime)
|
||||
add_subdirectory(tools)
|
||||
add_subdirectory(test)
|
||||
|
||||
if(NPCOMP_ENABLE_IREE)
|
||||
add_subdirectory(backend_test/iree)
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py
|
||||
MAIN_CONFIG
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
|
||||
)
|
||||
|
||||
set(NPCOMP_TEST_DEPENDS
|
||||
FileCheck count not
|
||||
npcomp-opt
|
||||
NPCOMPNativePyExt
|
||||
# TODO: Fix this so it has an IREE prefix
|
||||
bindings_python_pyiree_rt_rt
|
||||
# TODO: Why is this separate?
|
||||
bindings_python_pyiree_rt_system_api
|
||||
)
|
||||
|
||||
add_lit_testsuite(check-npcomp-backend-iree-lit "Running npcomp IREE tests"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${NPCOMP_TEST_DEPENDS}
|
||||
)
|
||||
set_target_properties(check-npcomp-backend-iree-lit PROPERTIES FOLDER "Tests")
|
||||
|
||||
add_lit_testsuites(NPCOMP_PYTEST ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${NPCOMP_TEST_DEPENDS})
|
||||
|
||||
add_dependencies(check-npcomp check-npcomp-backend-iree-lit)
|
|
@ -0,0 +1,53 @@
|
|||
# XFAIL: *
|
||||
# RUN: %PYTHON %s
|
||||
|
||||
from npcomp.compiler.frontend import *
|
||||
from npcomp.compiler.target import *
|
||||
|
||||
# TODO: This should all exist in a high level API somewhere.
|
||||
from _npcomp import mlir
|
||||
from _npcomp.backend import iree as ireec
|
||||
|
||||
from pyiree import rt
|
||||
|
||||
|
||||
def compile_function(f):
|
||||
fe = ImportFrontend(target_factory=GenericTarget32)
|
||||
ir_f = fe.import_global_function(f)
|
||||
|
||||
input_m = fe.ir_module
|
||||
# For easier debugging, split into to pass manager invocations.
|
||||
pm = mlir.passes.PassManager(input_m.context)
|
||||
# TOOD: Have an API for this
|
||||
pm.addPassPipelines(
|
||||
"basicpy-type-inference", "convert-basicpy-to-std", "canonicalize")
|
||||
pm.run(input_m)
|
||||
print("INPUT MODULE:")
|
||||
print(input_m.to_asm())
|
||||
|
||||
# Main IREE compiler.
|
||||
pm = mlir.passes.PassManager(input_m.context)
|
||||
ireec.build_flow_transform_pass_pipeline(pm)
|
||||
ireec.build_hal_transform_pass_pipeline(pm)
|
||||
ireec.build_vm_transform_pass_pipeline(pm)
|
||||
pm.run(input_m)
|
||||
print("VM MODULE:")
|
||||
print(input_m.to_asm())
|
||||
|
||||
# Translate to VM bytecode flatbuffer.
|
||||
vm_blob = ireec.translate_to_vm_bytecode(input_m)
|
||||
print("VM BLOB: len =", len(vm_blob))
|
||||
return vm_blob
|
||||
|
||||
|
||||
def int_add(a: int, b: int):
|
||||
return a + b
|
||||
|
||||
vm_blob = compile_function(int_add)
|
||||
m = rt.VmModule.from_flatbuffer(vm_blob)
|
||||
config = rt.Config("vmla")
|
||||
ctx = rt.SystemContext(config=config)
|
||||
ctx.add_module(m)
|
||||
|
||||
f = ctx.modules.module.int_add
|
||||
print(f(5, 6))
|
|
@ -0,0 +1,80 @@
|
|||
# -*- Python -*-
|
||||
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import lit.formats
|
||||
import lit.util
|
||||
|
||||
from lit.llvm import llvm_config
|
||||
from lit.llvm.subst import ToolSubst
|
||||
from lit.llvm.subst import FindTool
|
||||
|
||||
# Configuration file for the 'lit' test runner.
|
||||
|
||||
# name: The name of this test suite.
|
||||
config.name = 'NPCOMP_BACKEND_IREE'
|
||||
|
||||
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 = ['.mlir', '.py']
|
||||
|
||||
# test_source_root: The root path where tests are located.
|
||||
config.test_source_root = os.path.dirname(__file__)
|
||||
|
||||
# test_exec_root: The root path where tests should be run.
|
||||
config.test_exec_root = os.path.join(config.npcomp_obj_root, 'backend_test', 'iree')
|
||||
|
||||
config.substitutions.append(('%PATH%', config.environment['PATH']))
|
||||
config.substitutions.append(('%shlibext', config.llvm_shlib_ext))
|
||||
config.substitutions.append(('%PYTHON', config.python_executable))
|
||||
|
||||
llvm_config.with_system_environment(
|
||||
['HOME', 'INCLUDE', 'LIB', 'NPCOMP_DEBUG', 'TMP', 'TEMP'])
|
||||
|
||||
llvm_config.use_default_substitutions()
|
||||
|
||||
# excludes: A list of files/directories to exclude from the testsuite. The
|
||||
# 'Inputs'subdirectories contain auxiliary inputs for various tests in their
|
||||
# parent directories.
|
||||
config.excludes = [
|
||||
'Inputs', 'Examples', 'lit.cfg.py', 'CMakeLists.txt', 'README.txt',
|
||||
'LICENSE.txt'
|
||||
]
|
||||
|
||||
# test_source_root: The root path where tests are located.
|
||||
config.test_source_root = os.path.dirname(__file__)
|
||||
|
||||
# test_exec_root: The root path where tests should be run.
|
||||
config.test_exec_root = os.path.join(config.npcomp_obj_root, 'backend_test', 'iree')
|
||||
config.npcomp_tools_dir = os.path.join(config.npcomp_obj_root, 'tools')
|
||||
config.npcomp_runtime_shlib = os.path.join(
|
||||
config.npcomp_obj_root, 'runtime',
|
||||
'libNPCOMPRuntime' + config.llvm_shlib_ext)
|
||||
|
||||
# Tweak the PATH and PYTHONPATH to include the tools dir.
|
||||
llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True)
|
||||
llvm_config.with_environment('PYTHONPATH', [
|
||||
os.path.join(config.npcomp_obj_root, "python"),
|
||||
os.path.join(config.npcomp_obj_root, "python_native"),
|
||||
os.path.join(config.npcomp_obj_root, "iree", "bindings", "python"),
|
||||
],
|
||||
append_path=True)
|
||||
|
||||
tool_dirs = [
|
||||
os.path.join(config.npcomp_tools_dir, 'npcomp-opt'),
|
||||
os.path.join(config.npcomp_tools_dir, 'npcomp-run-mlir'),
|
||||
config.llvm_tools_dir,
|
||||
]
|
||||
tools = [
|
||||
'npcomp-opt',
|
||||
'npcomp-run-mlir',
|
||||
ToolSubst('%npcomp_runtime_shlib', config.npcomp_runtime_shlib),
|
||||
]
|
||||
|
||||
llvm_config.add_tool_substitutions(tools, tool_dirs)
|
|
@ -0,0 +1,49 @@
|
|||
@LIT_SITE_CFG_IN_HEADER@
|
||||
|
||||
import sys
|
||||
|
||||
config.host_triple = "@LLVM_HOST_TRIPLE@"
|
||||
config.target_triple = "@TARGET_TRIPLE@"
|
||||
config.llvm_src_root = "@LLVM_SOURCE_DIR@"
|
||||
config.llvm_obj_root = "@LLVM_BINARY_DIR@"
|
||||
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
|
||||
config.llvm_lib_dir = "@LLVM_LIBRARY_DIR@"
|
||||
config.llvm_shlib_dir = "@SHLIBDIR@"
|
||||
config.llvm_shlib_ext = "@SHLIBEXT@"
|
||||
config.llvm_exe_ext = "@EXEEXT@"
|
||||
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
|
||||
config.python_executable = "@PYTHON_EXECUTABLE@"
|
||||
config.gold_executable = "@GOLD_EXECUTABLE@"
|
||||
config.ld64_executable = "@LD64_EXECUTABLE@"
|
||||
config.enable_shared = @ENABLE_SHARED@
|
||||
config.enable_assertions = @ENABLE_ASSERTIONS@
|
||||
config.targets_to_build = "@TARGETS_TO_BUILD@"
|
||||
config.native_target = "@LLVM_NATIVE_ARCH@"
|
||||
config.llvm_bindings = "@LLVM_BINDINGS@".split(' ')
|
||||
config.host_os = "@HOST_OS@"
|
||||
config.host_cc = "@HOST_CC@"
|
||||
config.host_cxx = "@HOST_CXX@"
|
||||
# Note: ldflags can contain double-quoted paths, so must use single quotes here.
|
||||
config.host_ldflags = '@HOST_LDFLAGS@'
|
||||
config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@"
|
||||
config.llvm_host_triple = '@LLVM_HOST_TRIPLE@'
|
||||
config.host_arch = "@HOST_ARCH@"
|
||||
config.npcomp_src_root = "@CMAKE_SOURCE_DIR@"
|
||||
config.npcomp_obj_root = "@CMAKE_BINARY_DIR@"
|
||||
|
||||
# Support substitution of the tools_dir with user parameters. This is
|
||||
# used when we can't determine the tool dir at configuration time.
|
||||
try:
|
||||
config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
|
||||
config.llvm_shlib_dir = config.llvm_shlib_dir % lit_config.params
|
||||
except KeyError:
|
||||
e = sys.exc_info()[1]
|
||||
key, = e.args
|
||||
lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
|
||||
|
||||
|
||||
import lit.llvm
|
||||
lit.llvm.initialize(lit_config, config)
|
||||
|
||||
# Let the main config do the real work.
|
||||
lit_config.load_config(config, "@CMAKE_SOURCE_DIR@/backend_test/iree/lit.cfg.py")
|
|
@ -295,6 +295,8 @@ def Basicpy_UnknownCastOp : Basicpy_Op<"unknown_cast", [NoSideEffect]> {
|
|||
let arguments = (ins AnyType:$operand);
|
||||
let results = (outs AnyType:$result);
|
||||
let assemblyFormat = "operands attr-dict `:` type(operands) `->` type(results)";
|
||||
|
||||
let hasCanonicalizer = 1;
|
||||
}
|
||||
|
||||
#endif // NPCOMP_DIALECT_BASICPY_IR_BASICPY_OPS
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
/// Defines an "iree" module with backend support definitions.
|
||||
void mlir::npcomp::python::defineBackendIREEModule(py::module m) {
|
||||
py::class_<Blob>(m, "Blob", py::buffer_protocol())
|
||||
.def("__len__", [](Blob &self) { return self.contents.size(); })
|
||||
.def_buffer([](Blob &self) -> py::buffer_info {
|
||||
return py::buffer_info(
|
||||
static_cast<void *>(&self.contents.front()), // Pointer to buffer
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/FunctionImplementation.h"
|
||||
#include "mlir/IR/OpImplementation.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
#include "npcomp/Dialect/Basicpy/IR/BasicpyDialect.h"
|
||||
|
||||
#include "npcomp/Dialect/Basicpy/IR/BasicpyOpsEnums.cpp.inc"
|
||||
|
@ -192,6 +193,31 @@ OpFoldResult StrConstantOp::fold(ArrayRef<Attribute> operands) {
|
|||
return valueAttr();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// UnknownCastOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
class ElideIdentityUnknownCast : public OpRewritePattern<UnknownCastOp> {
|
||||
public:
|
||||
using OpRewritePattern::OpRewritePattern;
|
||||
LogicalResult matchAndRewrite(UnknownCastOp op,
|
||||
PatternRewriter &rewriter) const {
|
||||
if (op.operand().getType() != op.result().getType())
|
||||
return failure();
|
||||
rewriter.replaceOp(op, op.operand());
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void UnknownCastOp::getCanonicalizationPatterns(
|
||||
OwningRewritePatternList &patterns, MLIRContext *context) {
|
||||
patterns.insert<ElideIdentityUnknownCast>(context);
|
||||
}
|
||||
|
||||
#define GET_OP_CLASSES
|
||||
#include "npcomp/Dialect/Basicpy/IR/BasicpyOps.cpp.inc"
|
||||
} // namespace Basicpy
|
||||
|
|
|
@ -224,17 +224,26 @@ void PyDialectHelper::bind(py::module m) {
|
|||
py::arg("attrs") = llvm::Optional<PyAttribute>())
|
||||
.def("func_op",
|
||||
[](PyDialectHelper &self, const std::string &name, PyType type,
|
||||
bool createEntryBlock) {
|
||||
bool createEntryBlock, llvm::Optional<PyAttribute> attrs) {
|
||||
auto functionType = type.type.dyn_cast_or_null<FunctionType>();
|
||||
if (!functionType) {
|
||||
throw py::raiseValueError("Illegal function type");
|
||||
}
|
||||
OpBuilder &opBuilder = self.pyOpBuilder.getBuilder(true);
|
||||
Location loc = self.pyOpBuilder.getCurrentLoc();
|
||||
// TODO: Add function and arg/result attributes.
|
||||
// TODO: Dedup attr creation from op().
|
||||
MutableDictionaryAttr attrList;
|
||||
if (attrs) {
|
||||
auto dictAttrs = attrs->attr.dyn_cast<DictionaryAttr>();
|
||||
if (!dictAttrs) {
|
||||
throw py::raiseValueError(
|
||||
"Expected `attrs` to be a DictionaryAttr");
|
||||
}
|
||||
attrList = MutableDictionaryAttr(dictAttrs);
|
||||
}
|
||||
FuncOp op =
|
||||
opBuilder.create<FuncOp>(loc, StringRef(name), functionType,
|
||||
/*attrs=*/ArrayRef<NamedAttribute>());
|
||||
/*attrs=*/attrList.getAttrs());
|
||||
if (createEntryBlock) {
|
||||
Block *entryBlock = new Block();
|
||||
entryBlock->addArguments(functionType.getInputs());
|
||||
|
@ -245,6 +254,7 @@ void PyDialectHelper::bind(py::module m) {
|
|||
},
|
||||
py::arg("name"), py::arg("type"),
|
||||
py::arg("create_entry_block") = false,
|
||||
py::arg("attrs") = llvm::Optional<PyAttribute>(),
|
||||
R"(Creates a new `func` op, optionally creating an entry block.
|
||||
If an entry block is created, the builder will be positioned
|
||||
to its start.)")
|
||||
|
@ -507,7 +517,10 @@ void PyContext::bind(py::module m) {
|
|||
return createDenseElementsAttrFromBuffer(&self.context,
|
||||
array_info);
|
||||
},
|
||||
py::arg("array"));
|
||||
py::arg("array"))
|
||||
.def_property_readonly("unit_attr", [](PyContext &self) -> PyAttribute {
|
||||
return UnitAttr::get(&self.context);
|
||||
});
|
||||
}
|
||||
|
||||
PyModuleOp PyContext::parseAsm(const std::string &asm_text) {
|
||||
|
|
|
@ -105,7 +105,12 @@ class ImportFrontend:
|
|||
h.builder.set_file_line_col(filename_ident, ast_fd.lineno,
|
||||
ast_fd.col_offset)
|
||||
h.builder.insert_before_terminator(ir_m.first_block)
|
||||
ir_f = h.func_op(ast_fd.name, ir_f_type, create_entry_block=True)
|
||||
# TODO: Do not hardcode this IREE attribute.
|
||||
attrs = ir_c.dictionary_attr({"iree.module.export": ir_c.unit_attr})
|
||||
ir_f = h.func_op(ast_fd.name,
|
||||
ir_f_type,
|
||||
create_entry_block=True,
|
||||
attrs=attrs)
|
||||
fctx = FunctionContext(ir_c=ir_c,
|
||||
ir_f=ir_f,
|
||||
ir_h=h,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// RUN: npcomp-opt -split-input-file %s | npcomp-opt -canonicalize | FileCheck --dump-input=fail %s
|
||||
|
||||
// CHECK-LABEL: func @unknown_cast_elide
|
||||
func @unknown_cast_elide(%arg0 : i32) -> i32 {
|
||||
// CHECK-NOT: basicpy.unknown_cast
|
||||
%0 = basicpy.unknown_cast %arg0 : i32 -> i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @unknown_cast_preserve
|
||||
func @unknown_cast_preserve(%arg0 : i32) -> !basicpy.UnknownType {
|
||||
// CHECK: basicpy.unknown_cast
|
||||
%0 = basicpy.unknown_cast %arg0 : i32 -> !basicpy.UnknownType
|
||||
return %0 : !basicpy.UnknownType
|
||||
}
|
Loading…
Reference in New Issue