mirror of https://github.com/llvm/torch-mlir
Rename 'macro' nomenclature to 'partial eval'.
parent
dd6a4e638b
commit
e45287d83e
|
@ -17,13 +17,13 @@ from .target import *
|
|||
__all__ = [
|
||||
"BuiltinsValueCoder",
|
||||
"Environment",
|
||||
"MacroEvalResult",
|
||||
"MacroEvalType",
|
||||
"MacroResolver",
|
||||
"MacroValueRef",
|
||||
"LiveValueRef",
|
||||
"NameReference",
|
||||
"NameResolver",
|
||||
"ResolveAttrMacroValueRef",
|
||||
"PartialEvalResult",
|
||||
"PartialEvalType",
|
||||
"PartialEvalHook",
|
||||
"ResolveAttrLiveValueRef",
|
||||
"ValueCoder",
|
||||
"ValueCoderChain",
|
||||
]
|
||||
|
@ -59,7 +59,7 @@ class NameReference:
|
|||
self.name = name
|
||||
|
||||
def load(self, env: "Environment",
|
||||
ir_h: ir.DialectHelper) -> "MacroEvalResult":
|
||||
ir_h: ir.DialectHelper) -> "PartialEvalResult":
|
||||
"""Loads the IR Value associated with the name.
|
||||
|
||||
The load may either be direct, returning an existing value or
|
||||
|
@ -68,9 +68,9 @@ class NameReference:
|
|||
Args:
|
||||
ir_h: The dialect helper used to emit code.
|
||||
Returns:
|
||||
A macro evaluation result.
|
||||
A partial evaluation result.
|
||||
"""
|
||||
return MacroEvalResult.not_evaluated()
|
||||
return PartialEvalResult.not_evaluated()
|
||||
|
||||
def store(self, env: "Environment", value: ir.Value, ir_h: ir.DialectHelper):
|
||||
"""Stores a new value into the name.
|
||||
|
@ -103,57 +103,64 @@ class NameResolver:
|
|||
|
||||
|
||||
################################################################################
|
||||
# Macro evaluation
|
||||
# Partial evaluation
|
||||
# When the compiler is extracting from a running program, it is likely that
|
||||
# evaluations produce live values which can be further partially evaluated
|
||||
# at import time, in the context of the running instance (versus emitting
|
||||
# program IR to do so). This facility is called macro evaluation and is
|
||||
# a pluggable component on the environment.
|
||||
# program IR to do so). This behavior is controlled through a PartialEvalHook
|
||||
# on the environment.
|
||||
################################################################################
|
||||
|
||||
|
||||
class MacroEvalType(Enum):
|
||||
# The macro could not be evaluated immediately and the operation should
|
||||
# be code-generated. yields NotImplemented.
|
||||
class PartialEvalType(Enum):
|
||||
# Could not be evaluated immediately and the operation should be
|
||||
# code-generated. yields NotImplemented.
|
||||
NOT_EVALUATED = 0
|
||||
|
||||
# The macro yields a LiveValueRef
|
||||
# Yields a LiveValueRef
|
||||
YIELDS_LIVE_VALUE = 1
|
||||
|
||||
# The macro yields an IR value
|
||||
# Yields an IR value
|
||||
YIELDS_IR_VALUE = 2
|
||||
|
||||
# Evaluation yielded an error (yields contains exc_info from sys.exc_info()).
|
||||
ERROR = 3
|
||||
|
||||
|
||||
class MacroEvalResult(namedtuple("MacroEvalResult", "type,yields")):
|
||||
"""Encapsulates the result of a macro evaluation."""
|
||||
class PartialEvalResult(namedtuple("PartialEvalResult", "type,yields")):
|
||||
"""Encapsulates the result of a partial evaluation."""
|
||||
|
||||
@classmethod
|
||||
def not_evaluated(cls):
|
||||
return cls(MacroEvalType.NOT_EVALUATED, NotImplemented)
|
||||
return cls(PartialEvalType.NOT_EVALUATED, NotImplemented)
|
||||
|
||||
@classmethod
|
||||
def yields_live_value(cls, live_value):
|
||||
assert isinstance(live_value, MacroValueRef)
|
||||
return cls(MacroEvalType.YIELDS_LIVE_VALUE, live_value)
|
||||
assert isinstance(live_value, LiveValueRef)
|
||||
return cls(PartialEvalType.YIELDS_LIVE_VALUE, live_value)
|
||||
|
||||
@classmethod
|
||||
def yields_ir_value(cls, ir_value):
|
||||
assert isinstance(ir_value, ir.Value)
|
||||
return cls(MacroEvalType.YIELDS_IR_VALUE, ir_value)
|
||||
return cls(PartialEvalType.YIELDS_IR_VALUE, ir_value)
|
||||
|
||||
@classmethod
|
||||
def error(cls):
|
||||
return cls(MacroEvalType.ERROR, sys.exc_info())
|
||||
return cls(PartialEvalType.ERROR, sys.exc_info())
|
||||
|
||||
@classmethod
|
||||
def error_message(cls, message):
|
||||
try:
|
||||
raise RuntimeError(message)
|
||||
except RuntimeError:
|
||||
return cls.error()
|
||||
|
||||
|
||||
class MacroValueRef:
|
||||
class LiveValueRef:
|
||||
"""Wraps a live value from the containing environment.
|
||||
|
||||
Typically, when expressions encounter a live value, a limited number of
|
||||
"macro" expansions can be done against it in place (versus emitting the code
|
||||
partial evaluations can be done against it in place (versus emitting the code
|
||||
to import it and perform the operation). This default base class will not
|
||||
perform any static evaluations.
|
||||
"""
|
||||
|
@ -165,31 +172,31 @@ class MacroValueRef:
|
|||
super().__init__()
|
||||
self.live_value = live_value
|
||||
|
||||
def resolve_getattr(self, env: "Environment", attr_name) -> MacroEvalResult:
|
||||
def resolve_getattr(self, env: "Environment", attr_name) -> PartialEvalResult:
|
||||
"""Gets a named attribute from the live value."""
|
||||
return MacroEvalResult.not_evaluated()
|
||||
return PartialEvalResult.not_evaluated()
|
||||
|
||||
def __repr__(self):
|
||||
return "MacroValueRef({}, {})".format(self.__class__.__name__,
|
||||
self.live_value)
|
||||
|
||||
|
||||
class ResolveAttrMacroValueRef(MacroValueRef):
|
||||
class ResolveAttrLiveValueRef(LiveValueRef):
|
||||
"""Custom MacroValueRef that will resolve attributes via getattr."""
|
||||
__slots__ = []
|
||||
|
||||
def resolve_getattr(self, env: "Environment", attr_name) -> MacroEvalResult:
|
||||
def resolve_getattr(self, env: "Environment", attr_name) -> PartialEvalResult:
|
||||
logging.debug("RESOLVE_GETATTR '{}' on {}".format(attr_name,
|
||||
self.live_value))
|
||||
try:
|
||||
attr_py_value = getattr(self.live_value, attr_name)
|
||||
except:
|
||||
return MacroEvalResult.error()
|
||||
return env.macro_resolver.resolve(attr_py_value)
|
||||
return PartialEvalResult.error()
|
||||
return env.partial_eval_hook.resolve(attr_py_value)
|
||||
|
||||
|
||||
class MacroResolver:
|
||||
"""Owned by an environment and performs system-wide macro resolution."""
|
||||
class PartialEvalHook:
|
||||
"""Owned by an environment to customize partial evaluation."""
|
||||
__slots__ = [
|
||||
"_value_map",
|
||||
]
|
||||
|
@ -198,26 +205,26 @@ class MacroResolver:
|
|||
super().__init__()
|
||||
self._value_map = PyValueMap()
|
||||
|
||||
def resolve(self, py_value) -> MacroEvalResult:
|
||||
"""Performs macro resolution on a python value."""
|
||||
def resolve(self, py_value) -> PartialEvalResult:
|
||||
"""Performs partial evaluation on a python value."""
|
||||
binding = self._value_map.lookup(py_value)
|
||||
if binding is None:
|
||||
logging.debug("MACRO RESOLVE {}: Passthrough", py_value)
|
||||
return MacroEvalResult.yields_live_value(MacroValueRef(py_value))
|
||||
if isinstance(binding, MacroValueRef):
|
||||
logging.debug("MACRO RESOLVE {}: {}", py_value, binding)
|
||||
return MacroEvalResult.yields_live_value(binding)
|
||||
if isinstance(binding, MacroEvalResult):
|
||||
logging.debug("PARTIAL EVAL RESOLVE {}: Passthrough", py_value)
|
||||
return PartialEvalResult.yields_live_value(LiveValueRef(py_value))
|
||||
if isinstance(binding, LiveValueRef):
|
||||
logging.debug("PARTIAL EVAL RESOLVE {}: {}", py_value, binding)
|
||||
return PartialEvalResult.yields_live_value(binding)
|
||||
if isinstance(binding, PartialEvalResult):
|
||||
return binding
|
||||
# Attempt to call.
|
||||
try:
|
||||
binding = binding(py_value)
|
||||
assert isinstance(binding, MacroEvalResult), (
|
||||
"Expected MacroEvalResult but got {}".format(binding))
|
||||
logging.debug("MACRO RESOLVE {}: {}", py_value, binding)
|
||||
assert isinstance(binding, PartialEvalResult), (
|
||||
"Expected PartialEvalResult but got {}".format(binding))
|
||||
logging.debug("PARTIAL EVAL RESOLVE {}: {}", py_value, binding)
|
||||
return binding
|
||||
except:
|
||||
return MacroEvalResult.error()
|
||||
return PartialEvalResult.error()
|
||||
|
||||
def _bind(self,
|
||||
binding,
|
||||
|
@ -236,10 +243,10 @@ class MacroResolver:
|
|||
"Must specify one of 'for_ref', 'for_type' or 'for_predicate")
|
||||
|
||||
def enable_getattr(self, **kwargs):
|
||||
"""Enables macro attribute resolution."""
|
||||
"""Enables partial evaluation of getattr."""
|
||||
self._bind(
|
||||
lambda pv: MacroEvalResult.yields_live_value(
|
||||
ResolveAttrMacroValueRef(pv)), **kwargs)
|
||||
lambda pv: PartialEvalResult.yields_live_value(
|
||||
ResolveAttrLiveValueRef(pv)), **kwargs)
|
||||
|
||||
|
||||
################################################################################
|
||||
|
@ -258,7 +265,7 @@ class Environment(NameResolver):
|
|||
"_name_resolvers",
|
||||
"target",
|
||||
"value_coder",
|
||||
"macro_resolver",
|
||||
"partial_eval_hook",
|
||||
]
|
||||
|
||||
def __init__(self,
|
||||
|
@ -267,13 +274,14 @@ class Environment(NameResolver):
|
|||
target: Target,
|
||||
name_resolvers=(),
|
||||
value_coder,
|
||||
macro_resolver=None):
|
||||
partial_eval_hook=None):
|
||||
super().__init__()
|
||||
self.ir_h = ir_h
|
||||
self.target = target
|
||||
self._name_resolvers = name_resolvers
|
||||
self.value_coder = value_coder
|
||||
self.macro_resolver = macro_resolver if macro_resolver else MacroResolver()
|
||||
self.partial_eval_hook = partial_eval_hook if partial_eval_hook else PartialEvalHook(
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def for_const_global_function(cls, ir_h: ir.DialectHelper, f, *,
|
||||
|
@ -332,12 +340,11 @@ class LocalNameReference(NameReference):
|
|||
super().__init__(name)
|
||||
self._current_value = initial_value
|
||||
|
||||
def load(self, env: "Environment") -> MacroEvalResult:
|
||||
def load(self, env: "Environment") -> PartialEvalResult:
|
||||
if self._current_value is None:
|
||||
return MacroEvalResult.error(
|
||||
RuntimeError("Attempt to access local '{}' before assignment".format(
|
||||
self.name)))
|
||||
return MacroEvalResult.yields_ir_value(self._current_value)
|
||||
return PartialEvalResult.error_message(
|
||||
"Attempt to access local '{}' before assignment".format(self.name))
|
||||
return PartialEvalResult.yields_ir_value(self._current_value)
|
||||
|
||||
def store(self, env: "Environment", value: ir.Value):
|
||||
self._current_value = value
|
||||
|
@ -374,8 +381,8 @@ class ConstNameReference(NameReference):
|
|||
super().__init__(name)
|
||||
self._py_value = py_value
|
||||
|
||||
def load(self, env: "Environment") -> MacroEvalResult:
|
||||
return env.macro_resolver.resolve(self._py_value)
|
||||
def load(self, env: "Environment") -> PartialEvalResult:
|
||||
return env.partial_eval_hook.resolve(self._py_value)
|
||||
|
||||
def __repr__(self):
|
||||
return "<ConstNameReference({}={})>".format(self.name, self._py_value)
|
||||
|
|
|
@ -31,7 +31,7 @@ class ImportFrontend:
|
|||
"_helper",
|
||||
"_target_factory",
|
||||
"_value_coder",
|
||||
"_macro_resolver",
|
||||
"_partial_eval_hook",
|
||||
]
|
||||
|
||||
def __init__(self,
|
||||
|
@ -39,15 +39,15 @@ class ImportFrontend:
|
|||
*,
|
||||
target_factory: TargetFactory = GenericTarget64,
|
||||
value_coder: Optional[ValueCoder] = None,
|
||||
macro_resolver: Optional[MacroResolver] = None):
|
||||
partial_eval_hook: Optional[PartialEvalHook] = None):
|
||||
self._ir_context = ir.MLIRContext() if not ir_context else ir_context
|
||||
self._ir_module = self._ir_context.new_module()
|
||||
self._helper = AllDialectHelper(self._ir_context,
|
||||
ir.OpBuilder(self._ir_context))
|
||||
self._target_factory = target_factory
|
||||
self._value_coder = value_coder if value_coder else BuiltinsValueCoder()
|
||||
self._macro_resolver = (macro_resolver if macro_resolver else
|
||||
build_default_macro_resolver())
|
||||
self._partial_eval_hook = (partial_eval_hook if partial_eval_hook else
|
||||
build_default_partial_eval_hook())
|
||||
|
||||
@property
|
||||
def ir_context(self):
|
||||
|
@ -62,8 +62,8 @@ class ImportFrontend:
|
|||
return self._helper
|
||||
|
||||
@property
|
||||
def macro_resolver(self):
|
||||
return self._macro_resolver
|
||||
def partial_eval_hook(self):
|
||||
return self._partial_eval_hook
|
||||
|
||||
def import_global_function(self, f):
|
||||
"""Imports a global function.
|
||||
|
@ -121,7 +121,7 @@ class ImportFrontend:
|
|||
parameter_bindings=zip(f_params.keys(), ir_f.first_block.args),
|
||||
value_coder=self._value_coder,
|
||||
target=target,
|
||||
macro_resolver=self._macro_resolver)
|
||||
partial_eval_hook=self._partial_eval_hook)
|
||||
fctx = FunctionContext(ir_c=ir_c,
|
||||
ir_f=ir_f,
|
||||
ir_h=h,
|
||||
|
@ -166,8 +166,8 @@ class AllDialectHelper(Numpy.DialectHelper, ScfDialectHelper):
|
|||
ScfDialectHelper.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
def build_default_macro_resolver() -> MacroResolver:
|
||||
mr = MacroResolver()
|
||||
def build_default_partial_eval_hook() -> PartialEvalHook:
|
||||
mr = PartialEvalHook()
|
||||
### Modules
|
||||
mr.enable_getattr(for_type=ast.__class__) # The module we use is arbitrary.
|
||||
|
||||
|
|
|
@ -46,16 +46,16 @@ class FunctionContext:
|
|||
ir.emit_error(loc, message)
|
||||
raise EmittedError(loc, message)
|
||||
|
||||
def check_macro_evaluated(self, result: MacroEvalResult):
|
||||
"""Checks that a macro has evaluated without error."""
|
||||
if result.type == MacroEvalType.ERROR:
|
||||
def check_partial_evaluated(self, result: PartialEvalResult):
|
||||
"""Checks that a PartialEvalResult has evaluated without error."""
|
||||
if result.type == PartialEvalType.ERROR:
|
||||
exc_info = result.yields
|
||||
loc = self.current_loc
|
||||
message = ("Error while evaluating value from environment:\n" +
|
||||
"".join(traceback.format_exception(*exc_info)))
|
||||
ir.emit_error(loc, message)
|
||||
raise EmittedError(loc, message)
|
||||
if result.type == MacroEvalType.NOT_EVALUATED:
|
||||
if result.type == PartialEvalType.NOT_EVALUATED:
|
||||
self.abort("Unable to evaluate expression")
|
||||
|
||||
@property
|
||||
|
@ -82,22 +82,26 @@ class FunctionContext:
|
|||
self.abort("Cannot code python value as constant: {}".format(py_value))
|
||||
return result
|
||||
|
||||
def emit_macro_result(self, macro_result: MacroEvalResult) -> ir.Value:
|
||||
"""Emits a macro result either as a direct IR value or a constant."""
|
||||
self.check_macro_evaluated(macro_result)
|
||||
if macro_result.type == MacroEvalType.YIELDS_IR_VALUE:
|
||||
def emit_partial_eval_result(self,
|
||||
partial_result: PartialEvalResult) -> ir.Value:
|
||||
"""Emits a partial eval result either as a direct IR value or a constant."""
|
||||
self.check_partial_evaluated(partial_result)
|
||||
if partial_result.type == PartialEvalType.YIELDS_IR_VALUE:
|
||||
# Return directly.
|
||||
return macro_result.yields
|
||||
elif macro_result.type == MacroEvalType.YIELDS_LIVE_VALUE:
|
||||
return partial_result.yields
|
||||
elif partial_result.type == PartialEvalType.YIELDS_LIVE_VALUE:
|
||||
# Import constant.
|
||||
return self.emit_const_value(macro_result.yields.live_value)
|
||||
return self.emit_const_value(partial_result.yields.live_value)
|
||||
else:
|
||||
self.abort("Unhandled macro result type {}".format(macro_result))
|
||||
self.abort("Unhandled partial eval result type {}".format(partial_result))
|
||||
|
||||
|
||||
class BaseNodeVisitor(ast.NodeVisitor):
|
||||
"""Base class of a node visitor that aborts on unhandled nodes."""
|
||||
IMPORTER_TYPE = "<unknown>"
|
||||
__slots__ = [
|
||||
"fctx",
|
||||
]
|
||||
|
||||
def __init__(self, fctx):
|
||||
super().__init__()
|
||||
|
@ -119,6 +123,10 @@ class FunctionDefImporter(BaseNodeVisitor):
|
|||
Handles nodes that are direct children of a FunctionDef.
|
||||
"""
|
||||
IMPORTER_TYPE = "statement"
|
||||
__slots__ = [
|
||||
"ast_fd",
|
||||
"_last_was_return",
|
||||
]
|
||||
|
||||
def __init__(self, fctx, ast_fd):
|
||||
super().__init__(fctx)
|
||||
|
@ -186,6 +194,9 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
IR value that the expression lowers to.
|
||||
"""
|
||||
IMPORTER_TYPE = "expression"
|
||||
__slots__ = [
|
||||
"value",
|
||||
]
|
||||
|
||||
def __init__(self, fctx):
|
||||
super().__init__(fctx)
|
||||
|
@ -209,12 +220,13 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
self.value = ir_const_value
|
||||
|
||||
def visit_Attribute(self, ast_node):
|
||||
# Import the attribute's value recursively as a macro if possible.
|
||||
macro_importer = MacroImporter(self.fctx)
|
||||
macro_importer.visit(ast_node)
|
||||
if macro_importer.macro_result:
|
||||
self.fctx.check_macro_evaluated(macro_importer.macro_result)
|
||||
self.value = self.fctx.emit_macro_result(macro_importer.macro_result)
|
||||
# Import the attribute's value recursively as a partial eval if possible.
|
||||
pe_importer = PartialEvalImporter(self.fctx)
|
||||
pe_importer.visit(ast_node)
|
||||
if pe_importer.partial_eval_result:
|
||||
self.fctx.check_partial_evaluated(pe_importer.partial_eval_result)
|
||||
self.value = self.fctx.emit_partial_eval_result(
|
||||
pe_importer.partial_eval_result)
|
||||
return
|
||||
|
||||
self.fctx.abort("unhandled attribute access mode: {}".format(
|
||||
|
@ -331,9 +343,9 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
self.fctx.abort("Unsupported expression name context type %s" %
|
||||
ast_node.ctx.__class__.__name__)
|
||||
name_ref = self.fctx.lookup_name(ast_node.id)
|
||||
macro_result = name_ref.load(self.fctx.environment)
|
||||
logging.debug("LOAD {} -> {}", name_ref, macro_result)
|
||||
self.value = self.fctx.emit_macro_result(macro_result)
|
||||
pe_result = name_ref.load(self.fctx.environment)
|
||||
logging.debug("LOAD {} -> {}", name_ref, pe_result)
|
||||
self.value = self.fctx.emit_partial_eval_result(pe_result)
|
||||
|
||||
def visit_UnaryOp(self, ast_node):
|
||||
ir_h = self.fctx.ir_h
|
||||
|
@ -371,52 +383,55 @@ class ExpressionImporter(BaseNodeVisitor):
|
|||
self.emit_constant(ast_node.value)
|
||||
|
||||
|
||||
class MacroImporter(BaseNodeVisitor):
|
||||
"""Importer for expressions that can resolve through the environment's macro
|
||||
system.
|
||||
class PartialEvalImporter(BaseNodeVisitor):
|
||||
"""Importer for performing greedy partial evaluation.
|
||||
|
||||
Concretely this is used for Attribute.value and Call resolution.
|
||||
|
||||
Attribute resolution is not just treated as a normal expression because it
|
||||
is first subject to "macro expansion", allowing the environment's macro
|
||||
resolution facility to operate on live python values from the containing
|
||||
environment versus naively emitting code for attribute resolution from
|
||||
is first subject to "partial evaluation", allowing the environment's partial
|
||||
eval hook to operate on live python values from the containing
|
||||
environment versus naively emitting code for attribute resolution for
|
||||
entities that can/should be considered constants from the hosting context.
|
||||
This is used, for example, to resolve attributes from modules without
|
||||
by immediately dereferencing/transforming the intervening chain of attributes.
|
||||
immediately dereferencing/transforming the intervening chain of attributes.
|
||||
"""
|
||||
IMPORTER_TYPE = "macro"
|
||||
IMPORTER_TYPE = "partial_eval"
|
||||
__slots__ = [
|
||||
"partial_eval_result",
|
||||
]
|
||||
|
||||
def __init__(self, fctx):
|
||||
super().__init__(fctx)
|
||||
self.macro_result = None
|
||||
self.partial_eval_result = None
|
||||
|
||||
def visit_Attribute(self, ast_node):
|
||||
# Sub-evaluate the 'value'.
|
||||
sub_macro = MacroImporter(self.fctx)
|
||||
sub_macro.visit(ast_node.value)
|
||||
sub_eval = PartialEvalImporter(self.fctx)
|
||||
sub_eval.visit(ast_node.value)
|
||||
|
||||
if sub_macro.macro_result:
|
||||
# Macro sub-evaluation successful.
|
||||
sub_result = sub_macro.macro_result
|
||||
if sub_eval.partial_eval_result:
|
||||
# Partial sub-evaluation successful.
|
||||
sub_result = sub_eval.partial_eval_result
|
||||
else:
|
||||
# Need to evaluate it as an expression.
|
||||
sub_expr = ExpressionImporter(self.fctx)
|
||||
sub_expr.visit(ast_node.value)
|
||||
assert sub_expr.value, (
|
||||
"Macro sub expression did not return a value: %r" % (ast_node.value))
|
||||
sub_result = MacroEvalResult.yields_ir_value(sub_expr.value)
|
||||
"Evaluated sub expression did not return a value: %r" %
|
||||
(ast_node.value))
|
||||
sub_result = PartialEvalResult.yields_ir_value(sub_expr.value)
|
||||
|
||||
# Attempt to perform a static getattr as a macro if still operating on a
|
||||
# live value.
|
||||
self.fctx.check_macro_evaluated(sub_result)
|
||||
if sub_result.type == MacroEvalType.YIELDS_LIVE_VALUE:
|
||||
# Attempt to perform a static getattr as a partial eval if still operating
|
||||
# on a live value.
|
||||
self.fctx.check_partial_evaluated(sub_result)
|
||||
if sub_result.type == PartialEvalType.YIELDS_LIVE_VALUE:
|
||||
logging.debug("STATIC getattr '{}' on {}", ast_node.attr, sub_result)
|
||||
getattr_result = sub_result.yields.resolve_getattr(
|
||||
self.fctx.environment, ast_node.attr)
|
||||
if getattr_result.type != MacroEvalType.NOT_EVALUATED:
|
||||
self.fctx.check_macro_evaluated(getattr_result)
|
||||
self.macro_result = getattr_result
|
||||
if getattr_result.type != PartialEvalType.NOT_EVALUATED:
|
||||
self.fctx.check_partial_evaluated(getattr_result)
|
||||
self.partial_eval_result = getattr_result
|
||||
return
|
||||
# If a non-statically evaluable live value, then convert to a constant
|
||||
# and dynamic dispatch.
|
||||
|
@ -424,7 +439,7 @@ class MacroImporter(BaseNodeVisitor):
|
|||
else:
|
||||
ir_value = sub_result.yields
|
||||
|
||||
# Yielding an IR value from a recursive macro evaluation means that the
|
||||
# Yielding an IR value from a recursive partial evaluation means that the
|
||||
# entire chain needs to be hoisted to IR.
|
||||
# TODO: Implement.
|
||||
self.fctx.abort("dynamic-emitted getattr not yet supported: %r" %
|
||||
|
@ -432,9 +447,9 @@ class MacroImporter(BaseNodeVisitor):
|
|||
|
||||
def visit_Name(self, ast_node):
|
||||
name_ref = self.fctx.lookup_name(ast_node.id)
|
||||
macro_result = name_ref.load(self.fctx.environment)
|
||||
logging.debug("LOAD MACRO {} -> {}", name_ref, macro_result)
|
||||
self.macro_result = macro_result
|
||||
partial_eval_result = name_ref.load(self.fctx.environment)
|
||||
logging.debug("PARTIAL EVAL {} -> {}", name_ref, partial_eval_result)
|
||||
self.partial_eval_result = partial_eval_result
|
||||
|
||||
|
||||
class EmittedError(Exception):
|
||||
|
|
Loading…
Reference in New Issue