2020-06-07 12:24:28 +08:00
|
|
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
# See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
"""
|
|
|
|
Frontend to the compiler, allowing various ways to import code.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import ast
|
|
|
|
import inspect
|
2020-06-30 08:48:17 +08:00
|
|
|
import textwrap
|
2020-06-23 09:15:56 +08:00
|
|
|
from typing import Optional
|
2020-06-07 12:24:28 +08:00
|
|
|
|
2020-12-30 05:22:18 +08:00
|
|
|
from mlir import ir as _ir
|
2020-11-11 10:03:25 +08:00
|
|
|
from ..utils import logging
|
2020-06-10 08:16:36 +08:00
|
|
|
from .importer import *
|
2020-06-29 07:52:25 +08:00
|
|
|
from .interfaces import *
|
|
|
|
from .name_resolver_base import *
|
|
|
|
from .value_coder_base import *
|
2020-06-14 05:43:10 +08:00
|
|
|
from .target import *
|
2020-12-30 05:22:18 +08:00
|
|
|
from ..utils.mlir_utils import *
|
2020-06-07 12:24:28 +08:00
|
|
|
|
|
|
|
__all__ = [
|
|
|
|
"ImportFrontend",
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
class ImportFrontend:
|
|
|
|
"""Frontend for importing various entities into a Module."""
|
2020-06-14 05:43:10 +08:00
|
|
|
__slots__ = [
|
|
|
|
"_ir_module",
|
2020-06-29 07:52:25 +08:00
|
|
|
"_config",
|
2020-12-30 05:22:18 +08:00
|
|
|
"_ic",
|
2020-06-14 05:43:10 +08:00
|
|
|
]
|
|
|
|
|
|
|
|
def __init__(self,
|
2020-06-23 09:15:56 +08:00
|
|
|
*,
|
2020-06-29 07:52:25 +08:00
|
|
|
config: Configuration,
|
2020-12-30 05:22:18 +08:00
|
|
|
ir_context: Optional[_ir.Context] = None):
|
2020-06-29 07:52:25 +08:00
|
|
|
super().__init__()
|
2020-12-30 05:22:18 +08:00
|
|
|
ic = self._ic = ImportContext(ir_context)
|
|
|
|
self._ic.module = _ir.Module.create(loc=ic.loc)
|
2020-06-29 07:52:25 +08:00
|
|
|
self._config = config
|
2020-06-07 12:24:28 +08:00
|
|
|
|
|
|
|
@property
|
2020-12-30 05:22:18 +08:00
|
|
|
def ir_context(self) -> _ir.Context:
|
|
|
|
return self._ic.context
|
2020-06-07 12:24:28 +08:00
|
|
|
|
|
|
|
@property
|
2020-12-30 05:22:18 +08:00
|
|
|
def ir_module(self) -> _ir.Module:
|
|
|
|
return self._ic.module
|
2020-06-26 14:10:58 +08:00
|
|
|
|
2020-06-07 12:24:28 +08:00
|
|
|
def import_global_function(self, f):
|
|
|
|
"""Imports a global function.
|
|
|
|
|
|
|
|
This facility is not general and does not allow customization of the
|
|
|
|
containing environment, method import, etc.
|
|
|
|
|
|
|
|
Most errors are emitted via the MLIR context's diagnostic infrastructure,
|
|
|
|
but errors related to extracting source, etc are raised directly.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
f: The python callable.
|
|
|
|
"""
|
2020-12-30 05:22:18 +08:00
|
|
|
ic = self._ic
|
|
|
|
target = self._config.target_factory(ic)
|
2020-06-07 12:24:28 +08:00
|
|
|
filename = inspect.getsourcefile(f)
|
|
|
|
source_lines, start_lineno = inspect.getsourcelines(f)
|
|
|
|
source = "".join(source_lines)
|
2020-06-30 08:48:17 +08:00
|
|
|
source = textwrap.dedent(source)
|
2020-06-07 12:24:28 +08:00
|
|
|
ast_root = ast.parse(source, filename=filename)
|
|
|
|
ast.increment_lineno(ast_root, start_lineno - 1)
|
|
|
|
ast_fd = ast_root.body[0]
|
|
|
|
|
|
|
|
# Define the function.
|
|
|
|
# TODO: Much more needs to be done here (arg/result mapping, etc)
|
2020-06-08 06:15:19 +08:00
|
|
|
logging.debug(":::::::")
|
|
|
|
logging.debug("::: Importing global function {}:\n{}", ast_fd.name,
|
2020-06-07 12:24:28 +08:00
|
|
|
ast.dump(ast_fd, include_attributes=True))
|
2020-06-11 10:17:29 +08:00
|
|
|
|
|
|
|
# TODO: VERY BAD: Assumes all positional params.
|
2020-06-14 14:45:43 +08:00
|
|
|
f_signature = inspect.signature(f)
|
|
|
|
f_params = f_signature.parameters
|
|
|
|
f_input_types = [
|
|
|
|
self._resolve_signature_annotation(target, p.annotation)
|
|
|
|
for p in f_params.values()
|
|
|
|
]
|
|
|
|
f_return_type = self._resolve_signature_annotation(
|
|
|
|
target, f_signature.return_annotation)
|
2020-12-30 05:22:18 +08:00
|
|
|
ir_f_type = _ir.FunctionType.get(f_input_types, [f_return_type],
|
|
|
|
context=ic.context)
|
|
|
|
|
|
|
|
ic.set_file_line_col(filename, ast_fd.lineno, ast_fd.col_offset)
|
|
|
|
ic.insert_before_terminator(ic.module.body)
|
|
|
|
ir_f, entry_block = ic.FuncOp(ast_fd.name,
|
|
|
|
ir_f_type,
|
|
|
|
create_entry_block=True)
|
|
|
|
ic.insert_end_of_block(entry_block)
|
2020-06-29 07:52:25 +08:00
|
|
|
env = self._create_const_global_env(f,
|
|
|
|
parameter_bindings=zip(
|
|
|
|
f_params.keys(),
|
2020-12-30 05:22:18 +08:00
|
|
|
entry_block.arguments),
|
2020-06-29 07:52:25 +08:00
|
|
|
target=target)
|
2020-12-30 05:22:18 +08:00
|
|
|
fctx = FunctionContext(ic=ic, ir_f=ir_f, filename=filename, environment=env)
|
2020-06-11 10:17:29 +08:00
|
|
|
|
2020-06-07 12:24:28 +08:00
|
|
|
fdimport = FunctionDefImporter(fctx, ast_fd)
|
|
|
|
fdimport.import_body()
|
|
|
|
return ir_f
|
2020-06-14 14:45:43 +08:00
|
|
|
|
2020-06-29 07:52:25 +08:00
|
|
|
def _create_const_global_env(self, f, parameter_bindings, target):
|
|
|
|
"""Helper to generate an environment for a global function.
|
|
|
|
|
|
|
|
This is a helper for the very common case and will be wholly insufficient
|
|
|
|
for advanced cases, including mutable global state, closures, etc.
|
|
|
|
Globals from the module are considered immutable.
|
|
|
|
"""
|
2020-12-30 05:22:18 +08:00
|
|
|
ic = self._ic
|
2020-06-29 07:52:25 +08:00
|
|
|
try:
|
|
|
|
code = f.__code__
|
|
|
|
globals_dict = f.__globals__
|
|
|
|
builtins_module = globals_dict["__builtins__"]
|
|
|
|
except AttributeError:
|
|
|
|
assert False, (
|
|
|
|
"Function {} does not have required user-defined function attributes".
|
|
|
|
format(f))
|
|
|
|
|
|
|
|
# Locals resolver.
|
|
|
|
# Note that co_varnames should include both parameter and local names.
|
|
|
|
locals_resolver = LocalNameResolver(code.co_varnames)
|
|
|
|
resolvers = (
|
|
|
|
locals_resolver,
|
|
|
|
ConstModuleNameResolver(globals_dict, as_dict=True),
|
|
|
|
ConstModuleNameResolver(builtins_module),
|
|
|
|
)
|
2020-12-30 05:22:18 +08:00
|
|
|
env = Environment(config=self._config, ic=ic, name_resolvers=resolvers)
|
2020-06-29 07:52:25 +08:00
|
|
|
|
|
|
|
# Bind parameters.
|
|
|
|
for name, value in parameter_bindings:
|
|
|
|
logging.debug("STORE PARAM: {} <- {}", name, value)
|
|
|
|
locals_resolver.checked_resolve_name(name).store(env, value)
|
|
|
|
return env
|
|
|
|
|
2020-06-14 14:45:43 +08:00
|
|
|
def _resolve_signature_annotation(self, target: Target, annot):
|
2020-12-30 05:22:18 +08:00
|
|
|
ic = self._ic
|
2020-06-14 14:45:43 +08:00
|
|
|
if annot is inspect.Signature.empty:
|
2020-12-30 05:22:18 +08:00
|
|
|
return ic.unknown_type
|
2020-06-14 14:45:43 +08:00
|
|
|
|
|
|
|
# TODO: Do something real here once we need more than the primitive types.
|
|
|
|
if annot is int:
|
|
|
|
return target.impl_int_type
|
|
|
|
elif annot is float:
|
|
|
|
return target.impl_float_type
|
|
|
|
elif annot is bool:
|
2020-12-30 05:22:18 +08:00
|
|
|
return ic.bool_type
|
2020-06-14 14:45:43 +08:00
|
|
|
elif annot is str:
|
2020-12-30 05:22:18 +08:00
|
|
|
return ic.str_type
|
2020-06-14 14:45:43 +08:00
|
|
|
else:
|
2020-12-30 05:22:18 +08:00
|
|
|
return ic.unknown_type
|