Skip to content

Commit d132bfa

Browse files
committed
Fix stuff
1 parent 59b5e90 commit d132bfa

File tree

6 files changed

+211
-9
lines changed

6 files changed

+211
-9
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,10 @@ if(EXECUTORCH_BUILD_PTHREADPOOL AND EXECUTORCH_BUILD_CPUINFO)
554554
endif()
555555

556556
if(EXECUTORCH_BUILD_PYBIND)
557+
558+
# Add codegen tools subdirectory for selective_build pybind module
559+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/codegen/tools)
560+
557561
if(NOT EXECUTORCH_BUILD_EXTENSION_DATA_LOADER)
558562
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extension/data_loader)
559563
endif()

codegen/tools/CMakeLists.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
# Check if pybind11 is available
8+
9+
# Create the selective_build pybind11 module
10+
pybind11_add_module(selective_build SHARED selective_build.cpp)
11+
12+
# Set the output name to match the module name
13+
set_target_properties(selective_build PROPERTIES OUTPUT_NAME "selective_build")
14+
15+
# Set the module name for the pybind11 module
16+
target_compile_definitions(
17+
selective_build PUBLIC EXECUTORCH_PYTHON_MODULE_NAME=selective_build
18+
)
19+
20+
# Include directories
21+
target_include_directories(
22+
selective_build PRIVATE
23+
${CMAKE_CURRENT_SOURCE_DIR}/../../..
24+
)
25+
26+
# Compile options
27+
target_compile_options(
28+
selective_build PUBLIC
29+
-Wno-deprecated-declarations
30+
-fPIC
31+
-frtti
32+
-fexceptions
33+
)
34+
35+
# Link against required libraries
36+
target_link_libraries(
37+
selective_build PRIVATE
38+
executorch_core
39+
program_schema
40+
)
41+
42+
# Install the module
43+
install(TARGETS selective_build
44+
LIBRARY DESTINATION executorch/codegen/tools
45+
)

codegen/tools/gen_oplist.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@
2020
# We can use relative import instead.
2121
from ..parse import strip_et_fields
2222

23-
24-
from executorch.codegen.tools.selective_build import ( # type: ignore[import-not-found]
25-
_get_io_metadata_for_program_operators,
26-
_get_program_from_buffer,
27-
_get_program_operators,
28-
_IOMetaData,
29-
)
3023
from torchgen.gen import LineLoader, parse_native_yaml_struct
3124
from torchgen.selective_build.operator import SelectiveBuildOperator
3225
from torchgen.selective_build.selector import merge_et_kernel_metadata
@@ -92,6 +85,10 @@ class KernelType(IntEnum):
9285

9386

9487
def _get_operators(model_file: str) -> List[str]:
88+
from executorch.codegen.tools.selective_build import ( # type: ignore[import-not-found]
89+
_get_program_from_buffer,
90+
_get_program_operators,
91+
)
9592

9693
print("Processing model file: ", model_file)
9794
with open(model_file, "rb") as f:
@@ -104,6 +101,11 @@ def _get_operators(model_file: str) -> List[str]:
104101

105102

106103
def _get_kernel_metadata_for_model(model_file: str) -> Dict[str, List[str]]:
104+
from executorch.codegen.tools.selective_build import ( # type: ignore[import-not-found]
105+
_get_io_metadata_for_program_operators,
106+
_get_program_from_buffer,
107+
_IOMetaData,
108+
)
107109

108110
with open(model_file, "rb") as f:
109111
buf = f.read()

codegen/tools/targets.bzl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def define_common_targets(is_fbcode = False):
1919
"//executorch/codegen:gen_lib",
2020
] + select({
2121
"DEFAULT": [],
22-
"ovr_config//os:linux": [] if runtime.is_oss else ["//executorch/codegen/tools/fb:selective_build"], # TODO(larryliu0820) :selective_build doesn't build in OSS yet
22+
"ovr_config//os:linux": [] if runtime.is_oss else ["//executorch/codegen/tools:selective_build"], # TODO(larryliu0820) :selective_build doesn't build in OSS yet
2323
}),
2424
)
2525

@@ -29,7 +29,7 @@ def define_common_targets(is_fbcode = False):
2929
deps = [
3030
":gen_oplist_lib",
3131
],
32-
preload_deps = [] if runtime.is_oss else ["//executorch/codegen/tools/fb:selective_build"], # TODO(larryliu0820) :selective_build doesn't build in OSS yet
32+
preload_deps = [] if runtime.is_oss else ["//executorch/codegen/tools:selective_build"], # TODO(larryliu0820) :selective_build doesn't build in OSS yet
3333
package_style = "inplace",
3434
visibility = [
3535
"//executorch/...",
@@ -177,6 +177,22 @@ def define_common_targets(is_fbcode = False):
177177
visibility = ["//executorch/codegen/..."],
178178
)
179179

180+
runtime.python_test(
181+
name = "test_selective_build",
182+
srcs = [
183+
"test/test_selective_build.py",
184+
],
185+
package_style = "inplace",
186+
visibility = [
187+
"PUBLIC",
188+
],
189+
deps = [
190+
":selective_build",
191+
"fbsource//third-party/pypi/expecttest:expecttest",
192+
],
193+
_is_external_target = True,
194+
)
195+
180196
# TODO(larryliu0820): This is a hack to only run these two on fbcode. These targets depends on exir which is only available in fbcode.
181197
if not runtime.is_oss and is_fbcode:
182198
runtime.python_binary(
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import unittest
8+
from typing import Any, Optional, Tuple, Union
9+
10+
import torch
11+
12+
from executorch.codegen.tools.selective_build import ( # type: ignore[import-not-found]
13+
_get_io_metadata_for_program_operators,
14+
_get_program_from_buffer,
15+
_get_program_operators,
16+
_IOMetaData,
17+
)
18+
from executorch.exir import ExecutorchProgramManager, to_edge
19+
from executorch.exir.scalar_type import ScalarType
20+
from torch.export import export
21+
22+
23+
class ModuleAdd(torch.nn.Module):
24+
"""The module to serialize and execute."""
25+
26+
def __init__(self):
27+
super(ModuleAdd, self).__init__()
28+
29+
def forward(self, x, y):
30+
return x + y
31+
32+
def get_methods_to_export(self):
33+
return ("forward",)
34+
35+
36+
class ModuleMulti(torch.nn.Module):
37+
"""The module to serialize and execute."""
38+
39+
def __init__(self):
40+
super(ModuleMulti, self).__init__()
41+
42+
def forward(self, x, y):
43+
return x + y
44+
45+
def forward2(self, x, y):
46+
return x + y + 1
47+
48+
def get_methods_to_export(self):
49+
return ("forward", "forward2")
50+
51+
52+
def create_program(
53+
eager_module: Optional[Union[ModuleAdd, ModuleMulti]] = None,
54+
) -> Tuple[ExecutorchProgramManager, Tuple[Any, ...]]:
55+
"""Returns an executorch program based on ModuleAdd, along with inputs."""
56+
57+
if eager_module is None:
58+
eager_module = ModuleAdd()
59+
60+
class WrapperModule(torch.nn.Module):
61+
def __init__(self, fn):
62+
super().__init__()
63+
self.fn = fn
64+
65+
def forward(self, *args, **kwargs):
66+
return self.fn(*args, **kwargs)
67+
68+
# Trace the test module and create a serialized ExecuTorch program.
69+
inputs = (torch.ones(2, 2), torch.ones(2, 2))
70+
input_map = {}
71+
# pyre-fixme[29]: `Union[torch._tensor.Tensor, torch.nn.modules.module.Module]`
72+
# is not a function.
73+
for method in eager_module.get_methods_to_export():
74+
input_map[method] = inputs
75+
76+
exported_methods = {}
77+
# These cleanup passes are required to convert the `add` op to its out
78+
# variant, along with some other transformations.
79+
for method_name, method_input in input_map.items():
80+
module = WrapperModule(getattr(eager_module, method_name))
81+
exported_methods[method_name] = export(module, method_input, strict=True)
82+
83+
exec_prog = to_edge(exported_methods).to_executorch()
84+
85+
# Create the ExecuTorch program from the graph.
86+
exec_prog.dump_executorch_program(verbose=True)
87+
return (exec_prog, inputs)
88+
89+
90+
class PybindingsTest(unittest.TestCase):
91+
def test_dump_operators(self):
92+
# Create and serialize a program.
93+
orig_program, _ = create_program()
94+
95+
# Deserialize the program and demonstrate that we could get its operator
96+
# list.
97+
program = _get_program_from_buffer(orig_program.buffer)
98+
operators = _get_program_operators(program)
99+
self.assertEqual(operators, ["aten::add.out"])
100+
101+
def test_get_op_io_meta(self):
102+
# Checking whether get_op_io_meta returns the correct metadata for all its ios.
103+
orig_program, inputs = create_program()
104+
105+
# Deserialize the program and demonstrate that we could get its operator
106+
# list.
107+
program = _get_program_from_buffer(orig_program.buffer)
108+
program_op_io_metadata = _get_io_metadata_for_program_operators(program)
109+
110+
self.assertTrue(len(program_op_io_metadata) == 1)
111+
self.assertTrue(isinstance(program_op_io_metadata, dict))
112+
113+
self.assertTrue("aten::add.out" in program_op_io_metadata)
114+
self.assertTrue(isinstance(program_op_io_metadata["aten::add.out"], set))
115+
self.assertTrue(len(program_op_io_metadata["aten::add.out"]) == 1)
116+
117+
for op_io_metadata in program_op_io_metadata["aten::add.out"]:
118+
self.assertTrue(len(op_io_metadata) == 5)
119+
self.assertTrue(isinstance(op_io_metadata, tuple))
120+
121+
for io_idx, io_metadata in enumerate(op_io_metadata):
122+
self.assertTrue(isinstance(io_metadata, _IOMetaData))
123+
if io_idx == 2:
124+
# TODO(gasoonjia): Create a enum class to map KernelTypes to int, remove the hardcoded 2 and 5 below.
125+
self.assertEqual(io_metadata.kernel_type, 2)
126+
else:
127+
self.assertEqual(io_metadata.kernel_type, 5)
128+
self.assertEqual(io_metadata.dtype, ScalarType.FLOAT)
129+
self.assertEqual(io_metadata.dim_order, [0, 1])

setup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ def run(self): # noqa C901
729729

730730
if cmake_cache.is_enabled("EXECUTORCH_BUILD_PYBIND"):
731731
cmake_build_args += ["--target", "portable_lib"]
732+
cmake_build_args += ["--target", "selective_build"]
732733

733734
if cmake_cache.is_enabled("EXECUTORCH_BUILD_EXTENSION_TRAINING"):
734735
cmake_build_args += ["--target", "_training_lib"]
@@ -790,6 +791,11 @@ def run(self): # noqa C901
790791
modpath="executorch.extension.training.pybindings._training_lib",
791792
dependent_cmake_flags=["EXECUTORCH_BUILD_EXTENSION_TRAINING"],
792793
),
794+
BuiltExtension(
795+
src="codegen/tools/selective_build.*",
796+
modpath="executorch.codegen.tools.selective_build",
797+
dependent_cmake_flags=["EXECUTORCH_BUILD_PYBIND"],
798+
),
793799
BuiltExtension(
794800
src="executorchcoreml.*",
795801
src_dir="backends/apple/coreml",

0 commit comments

Comments
 (0)