2015-07-14 11:44:16 +08:00
|
|
|
"""
|
2015-07-21 09:54:34 +08:00
|
|
|
:class:`ARTIQIRGenerator` transforms typed AST into ARTIQ intermediate
|
2015-07-18 02:29:06 +08:00
|
|
|
representation. ARTIQ IR is designed to be low-level enough that
|
|
|
|
its operations are elementary--contain no internal branching--
|
|
|
|
but without too much detail, such as exposing the reference/value
|
|
|
|
semantics explicitly.
|
2015-07-14 11:44:16 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
from collections import OrderedDict
|
|
|
|
from pythonparser import algorithm, diagnostic, ast
|
|
|
|
from .. import types, builtins, ir
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def _readable_name(insn):
|
|
|
|
if isinstance(insn, ir.Constant):
|
|
|
|
return str(insn.value)
|
|
|
|
else:
|
|
|
|
return insn.name
|
|
|
|
|
2015-07-18 12:49:42 +08:00
|
|
|
def _extract_loc(node):
|
|
|
|
if "keyword_loc" in node._locs:
|
|
|
|
return node.keyword_loc
|
|
|
|
else:
|
|
|
|
return node.loc
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
# We put some effort in keeping generated IR readable,
|
|
|
|
# i.e. with a more or less linear correspondence to the source.
|
|
|
|
# This is why basic blocks sometimes seem to be produced in an odd order.
|
2015-07-21 09:54:34 +08:00
|
|
|
class ARTIQIRGenerator(algorithm.Visitor):
|
2015-07-18 02:29:06 +08:00
|
|
|
"""
|
2015-07-21 09:54:34 +08:00
|
|
|
:class:`ARTIQIRGenerator` contains a lot of internal state,
|
2015-07-18 02:29:06 +08:00
|
|
|
which is effectively maintained in a stack--with push/pop
|
|
|
|
pairs around any state updates. It is comprised of following:
|
|
|
|
|
2015-07-18 12:49:42 +08:00
|
|
|
:ivar current_loc: (:class:`pythonparser.source.Range`)
|
|
|
|
source range of the node being currently visited
|
2015-07-18 02:29:06 +08:00
|
|
|
:ivar current_function: (:class:`ir.Function` or None)
|
2015-07-18 12:49:42 +08:00
|
|
|
module, def or lambda currently being translated
|
2015-07-18 14:10:41 +08:00
|
|
|
:ivar current_globals: (set of string)
|
|
|
|
set of variables that will be resolved in global scope
|
2015-07-18 02:29:06 +08:00
|
|
|
:ivar current_block: (:class:`ir.BasicBlock`)
|
|
|
|
basic block to which any new instruction will be appended
|
2015-07-22 07:58:59 +08:00
|
|
|
:ivar current_env: (:class:`ir.Alloc` of type :class:`ir.TEnvironment`)
|
2015-07-18 02:29:06 +08:00
|
|
|
the chained function environment, containing variables that
|
|
|
|
can become upvalues
|
2015-07-22 07:58:59 +08:00
|
|
|
:ivar current_private_env: (:class:`ir.Alloc` of type :class:`ir.TEnvironment`)
|
2015-07-18 02:29:06 +08:00
|
|
|
the private function environment, containing internal state
|
|
|
|
:ivar current_assign: (:class:`ir.Value` or None)
|
|
|
|
the right-hand side of current assignment statement, or
|
|
|
|
a component of a composite right-hand side when visiting
|
|
|
|
a composite left-hand side, such as, in ``x, y = z``,
|
|
|
|
the 2nd tuple element when visting ``y``
|
2015-07-22 07:58:59 +08:00
|
|
|
:ivar current_assert_env: (:class:`ir.Alloc` of type :class:`ir.TEnvironment`)
|
|
|
|
the environment where the individual components of current assert
|
|
|
|
statement are stored until display
|
|
|
|
:ivar current_assert_subexprs: (list of (:class:`ast.AST`, string))
|
|
|
|
the mapping from components of current assert statement to the names
|
|
|
|
their values have in :ivar:`current_assert_env`
|
2015-07-18 02:29:06 +08:00
|
|
|
:ivar break_target: (:class:`ir.BasicBlock` or None)
|
|
|
|
the basic block to which ``break`` will transfer control
|
|
|
|
:ivar continue_target: (:class:`ir.BasicBlock` or None)
|
|
|
|
the basic block to which ``continue`` will transfer control
|
|
|
|
:ivar return_target: (:class:`ir.BasicBlock` or None)
|
|
|
|
the basic block to which ``return`` will transfer control
|
|
|
|
:ivar unwind_target: (:class:`ir.BasicBlock` or None)
|
|
|
|
the basic block to which unwinding will transfer control
|
|
|
|
"""
|
|
|
|
|
|
|
|
_size_type = builtins.TInt(types.TValue(32))
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
def __init__(self, module_name, engine):
|
|
|
|
self.engine = engine
|
|
|
|
self.functions = []
|
2015-08-07 18:20:29 +08:00
|
|
|
self.name = [module_name] if module_name != "" else []
|
2015-07-18 12:49:42 +08:00
|
|
|
self.current_loc = None
|
2015-07-14 11:44:16 +08:00
|
|
|
self.current_function = None
|
2015-07-18 14:10:41 +08:00
|
|
|
self.current_globals = set()
|
2015-07-14 11:44:16 +08:00
|
|
|
self.current_block = None
|
2015-07-18 02:29:06 +08:00
|
|
|
self.current_env = None
|
|
|
|
self.current_private_env = None
|
|
|
|
self.current_assign = None
|
2015-07-22 07:58:59 +08:00
|
|
|
self.current_assert_env = None
|
|
|
|
self.current_assert_subexprs = None
|
2015-07-18 02:29:06 +08:00
|
|
|
self.break_target = None
|
|
|
|
self.continue_target = None
|
|
|
|
self.return_target = None
|
|
|
|
self.unwind_target = None
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def add_block(self, name=""):
|
|
|
|
block = ir.BasicBlock([], name)
|
2015-07-14 11:44:16 +08:00
|
|
|
self.current_function.add(block)
|
|
|
|
return block
|
|
|
|
|
2015-07-18 12:49:42 +08:00
|
|
|
def append(self, insn, block=None, loc=None):
|
|
|
|
if loc is None:
|
|
|
|
loc = self.current_loc
|
|
|
|
if block is None:
|
|
|
|
block = self.current_block
|
|
|
|
|
|
|
|
if insn.loc is None:
|
2015-07-19 15:29:33 +08:00
|
|
|
insn.loc = loc
|
2015-07-18 12:49:42 +08:00
|
|
|
return block.append(insn)
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
def terminate(self, insn):
|
|
|
|
if not self.current_block.is_terminated():
|
|
|
|
self.append(insn)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
insn.drop_references()
|
|
|
|
|
|
|
|
# Visitors
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
def visit(self, obj):
|
|
|
|
if isinstance(obj, list):
|
|
|
|
for elt in obj:
|
|
|
|
self.visit(elt)
|
|
|
|
if self.current_block.is_terminated():
|
|
|
|
break
|
|
|
|
elif isinstance(obj, ast.AST):
|
2015-07-18 12:49:42 +08:00
|
|
|
try:
|
|
|
|
old_loc, self.current_loc = self.current_loc, _extract_loc(obj)
|
|
|
|
return self._visit_one(obj)
|
|
|
|
finally:
|
|
|
|
self.current_loc = old_loc
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
# Module visitor
|
|
|
|
|
|
|
|
def visit_ModuleT(self, node):
|
2015-07-18 12:49:42 +08:00
|
|
|
# Treat start of module as synthesized
|
|
|
|
self.current_loc = None
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
try:
|
2015-07-18 02:29:06 +08:00
|
|
|
typ = types.TFunction(OrderedDict(), OrderedDict(), builtins.TNone())
|
|
|
|
func = ir.Function(typ, ".".join(self.name + ['__modinit__']), [])
|
|
|
|
self.functions.append(func)
|
|
|
|
old_func, self.current_function = self.current_function, func
|
|
|
|
|
|
|
|
entry = self.add_block("entry")
|
|
|
|
old_block, self.current_block = self.current_block, entry
|
|
|
|
|
|
|
|
env = self.append(ir.Alloc([], ir.TEnvironment(node.typing_env), name="env"))
|
|
|
|
old_env, self.current_env = self.current_env, env
|
|
|
|
|
2015-07-18 14:27:34 +08:00
|
|
|
priv_env = self.append(ir.Alloc([], ir.TEnvironment({ ".return": typ.ret }),
|
|
|
|
name="privenv"))
|
2015-07-18 02:29:06 +08:00
|
|
|
old_priv_env, self.current_private_env = self.current_private_env, priv_env
|
|
|
|
|
|
|
|
self.generic_visit(node)
|
|
|
|
self.terminate(ir.Return(ir.Constant(None, builtins.TNone())))
|
|
|
|
|
2015-07-19 16:44:51 +08:00
|
|
|
return self.functions
|
2015-07-18 02:29:06 +08:00
|
|
|
finally:
|
|
|
|
self.current_function = old_func
|
|
|
|
self.current_block = old_block
|
|
|
|
self.current_env = old_env
|
|
|
|
self.current_private_env = old_priv_env
|
|
|
|
|
|
|
|
# Statement visitors
|
|
|
|
|
2015-07-30 01:35:16 +08:00
|
|
|
def visit_function(self, node, is_lambda, is_internal):
|
2015-07-18 02:29:06 +08:00
|
|
|
if is_lambda:
|
2015-08-08 21:01:08 +08:00
|
|
|
name = "lambda@{}:{}".format(node.loc.line(), node.loc.column())
|
2015-07-18 02:29:06 +08:00
|
|
|
typ = node.type.find()
|
|
|
|
else:
|
|
|
|
name = node.name
|
|
|
|
typ = node.signature_type.find()
|
|
|
|
|
|
|
|
try:
|
|
|
|
defaults = []
|
|
|
|
for arg_name, default_node in zip(typ.optargs, node.args.defaults):
|
|
|
|
default = self.visit(default_node)
|
|
|
|
env_default_name = \
|
|
|
|
self.current_env.type.add("default." + arg_name, default.type)
|
|
|
|
self.append(ir.SetLocal(self.current_env, env_default_name, default))
|
|
|
|
defaults.append(env_default_name)
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
old_name, self.name = self.name, self.name + [name]
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
env_arg = ir.EnvironmentArgument(self.current_env.type, "outerenv")
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
args = []
|
|
|
|
for arg_name in typ.args:
|
2015-07-18 02:29:06 +08:00
|
|
|
args.append(ir.Argument(typ.args[arg_name], "arg." + arg_name))
|
|
|
|
|
|
|
|
optargs = []
|
2015-07-14 11:44:16 +08:00
|
|
|
for arg_name in typ.optargs:
|
2015-07-21 18:45:27 +08:00
|
|
|
optargs.append(ir.Argument(ir.TOption(typ.optargs[arg_name]), "arg." + arg_name))
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
func = ir.Function(typ, ".".join(self.name), [env_arg] + args + optargs)
|
2015-07-30 01:35:16 +08:00
|
|
|
func.is_internal = is_internal
|
2015-07-14 11:44:16 +08:00
|
|
|
self.functions.append(func)
|
|
|
|
old_func, self.current_function = self.current_function, func
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
entry = self.add_block()
|
|
|
|
old_block, self.current_block = self.current_block, entry
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 14:10:41 +08:00
|
|
|
old_globals, self.current_globals = self.current_globals, node.globals_in_scope
|
|
|
|
|
|
|
|
env_without_globals = \
|
|
|
|
{var: node.typing_env[var]
|
|
|
|
for var in node.typing_env
|
|
|
|
if var not in node.globals_in_scope}
|
|
|
|
env_type = ir.TEnvironment(env_without_globals, self.current_env.type)
|
2015-07-18 02:29:06 +08:00
|
|
|
env = self.append(ir.Alloc([], env_type, name="env"))
|
|
|
|
old_env, self.current_env = self.current_env, env
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
if not is_lambda:
|
2015-07-18 14:27:34 +08:00
|
|
|
priv_env = self.append(ir.Alloc([], ir.TEnvironment({ ".return": typ.ret }),
|
|
|
|
name="privenv"))
|
2015-07-18 02:29:06 +08:00
|
|
|
old_priv_env, self.current_private_env = self.current_private_env, priv_env
|
|
|
|
|
|
|
|
self.append(ir.SetLocal(env, ".outer", env_arg))
|
|
|
|
for index, arg_name in enumerate(typ.args):
|
|
|
|
self.append(ir.SetLocal(env, arg_name, args[index]))
|
|
|
|
for index, (arg_name, env_default_name) in enumerate(zip(typ.optargs, defaults)):
|
|
|
|
default = self.append(ir.GetLocal(self.current_env, env_default_name))
|
2015-07-22 07:58:59 +08:00
|
|
|
value = self.append(ir.Builtin("unwrap_or", [optargs[index], default],
|
2015-07-18 02:29:06 +08:00
|
|
|
typ.optargs[arg_name]))
|
|
|
|
self.append(ir.SetLocal(env, arg_name, value))
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
result = self.visit(node.body)
|
|
|
|
|
|
|
|
if is_lambda:
|
|
|
|
self.terminate(ir.Return(result))
|
|
|
|
elif builtins.is_none(typ.ret):
|
2015-07-18 14:10:41 +08:00
|
|
|
if not self.current_block.is_terminated():
|
|
|
|
self.current_block.append(ir.Return(ir.Constant(None, builtins.TNone())))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
2015-07-18 14:10:41 +08:00
|
|
|
if not self.current_block.is_terminated():
|
|
|
|
self.current_block.append(ir.Unreachable())
|
2015-07-18 02:29:06 +08:00
|
|
|
finally:
|
|
|
|
self.name = old_name
|
|
|
|
self.current_function = old_func
|
|
|
|
self.current_block = old_block
|
2015-07-18 14:10:41 +08:00
|
|
|
self.current_globals = old_globals
|
2015-07-18 02:29:06 +08:00
|
|
|
self.current_env = old_env
|
|
|
|
if not is_lambda:
|
|
|
|
self.current_private_env = old_priv_env
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 14:27:34 +08:00
|
|
|
return self.append(ir.Closure(func, self.current_env))
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
def visit_FunctionDefT(self, node):
|
2015-08-06 13:24:06 +08:00
|
|
|
func = self.visit_function(node, is_lambda=False, is_internal=len(self.name) > 2)
|
2015-07-18 14:10:41 +08:00
|
|
|
self._set_local(node.name, func)
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
def visit_Return(self, node):
|
|
|
|
if node.value is None:
|
|
|
|
return_value = ir.Constant(None, builtins.TNone())
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
return_value = self.visit(node.value)
|
|
|
|
|
|
|
|
if self.return_target is None:
|
2015-07-14 11:44:16 +08:00
|
|
|
self.append(ir.Return(return_value))
|
|
|
|
else:
|
2015-07-18 02:29:06 +08:00
|
|
|
self.append(ir.SetLocal(self.current_private_env, ".return", return_value))
|
|
|
|
self.append(ir.Branch(self.return_target))
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
def visit_Expr(self, node):
|
2015-07-19 16:44:51 +08:00
|
|
|
# Ignore the value, do it for side effects.
|
|
|
|
result = self.visit(node.value)
|
|
|
|
|
|
|
|
# See comment in visit_Pass.
|
|
|
|
if isinstance(result, ir.Constant):
|
|
|
|
self.visit_Pass(node)
|
|
|
|
|
|
|
|
def visit_Pass(self, node):
|
|
|
|
# Insert a dummy instruction so that analyses which extract
|
|
|
|
# locations from CFG have something to use.
|
2015-07-21 09:54:34 +08:00
|
|
|
self.append(ir.Builtin("nop", [], builtins.TNone()))
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def visit_Assign(self, node):
|
|
|
|
try:
|
|
|
|
self.current_assign = self.visit(node.value)
|
|
|
|
assert self.current_assign is not None
|
|
|
|
for target in node.targets:
|
|
|
|
self.visit(target)
|
|
|
|
finally:
|
|
|
|
self.current_assign = None
|
|
|
|
|
|
|
|
def visit_AugAssign(self, node):
|
2015-07-22 23:34:52 +08:00
|
|
|
lhs = self.visit(node.target)
|
2015-07-18 02:29:06 +08:00
|
|
|
rhs = self.visit(node.value)
|
2015-07-19 16:51:53 +08:00
|
|
|
value = self.append(ir.Arith(node.op, lhs, rhs))
|
2015-07-18 02:29:06 +08:00
|
|
|
try:
|
|
|
|
self.current_assign = value
|
|
|
|
self.visit(node.target)
|
|
|
|
finally:
|
|
|
|
self.current_assign = None
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-23 00:09:14 +08:00
|
|
|
def coerce_to_bool(self, insn, block=None):
|
|
|
|
if builtins.is_bool(insn.type):
|
|
|
|
return insn
|
|
|
|
elif builtins.is_int(insn.type):
|
|
|
|
return self.append(ir.Compare(ast.NotEq(loc=None), insn, ir.Constant(0, insn.type)),
|
|
|
|
block=block)
|
|
|
|
elif builtins.is_float(insn.type):
|
|
|
|
return self.append(ir.Compare(ast.NotEq(loc=None), insn, ir.Constant(0, insn.type)),
|
|
|
|
block=block)
|
|
|
|
elif builtins.is_iterable(insn.type):
|
|
|
|
length = self.iterable_len(insn)
|
|
|
|
return self.append(ir.Compare(ast.NotEq(loc=None), length, ir.Constant(0, length.type)),
|
|
|
|
block=block)
|
|
|
|
else:
|
|
|
|
note = diagnostic.Diagnostic("note",
|
|
|
|
"this expression has type {type}",
|
|
|
|
{"type": types.TypePrinter().name(insn.type)},
|
|
|
|
insn.loc)
|
|
|
|
diag = diagnostic.Diagnostic("warning",
|
|
|
|
"this expression, which is always truthful, is coerced to bool", {},
|
|
|
|
insn.loc, notes=[note])
|
|
|
|
self.engine.process(diag)
|
|
|
|
return ir.Constant(True, builtins.TBool())
|
|
|
|
|
2015-07-14 11:44:16 +08:00
|
|
|
def visit_If(self, node):
|
2015-07-18 02:29:06 +08:00
|
|
|
cond = self.visit(node.test)
|
2015-07-23 00:09:14 +08:00
|
|
|
cond = self.coerce_to_bool(cond)
|
2015-07-14 11:44:16 +08:00
|
|
|
head = self.current_block
|
|
|
|
|
|
|
|
if_true = self.add_block()
|
|
|
|
self.current_block = if_true
|
|
|
|
self.visit(node.body)
|
2015-07-22 23:34:52 +08:00
|
|
|
post_if_true = self.current_block
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 12:49:27 +08:00
|
|
|
if any(node.orelse):
|
|
|
|
if_false = self.add_block()
|
|
|
|
self.current_block = if_false
|
|
|
|
self.visit(node.orelse)
|
2015-07-22 23:34:52 +08:00
|
|
|
post_if_false = self.current_block
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
tail = self.add_block()
|
|
|
|
self.current_block = tail
|
2015-07-22 23:34:52 +08:00
|
|
|
if not post_if_true.is_terminated():
|
|
|
|
post_if_true.append(ir.Branch(tail))
|
2015-07-18 12:49:27 +08:00
|
|
|
|
|
|
|
if any(node.orelse):
|
2015-07-22 23:34:52 +08:00
|
|
|
if not post_if_false.is_terminated():
|
|
|
|
post_if_false.append(ir.Branch(tail))
|
2015-07-19 16:44:51 +08:00
|
|
|
self.append(ir.BranchIf(cond, if_true, if_false), block=head)
|
2015-07-18 12:49:27 +08:00
|
|
|
else:
|
2015-07-19 16:44:51 +08:00
|
|
|
self.append(ir.BranchIf(cond, if_true, tail), block=head)
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
def visit_While(self, node):
|
|
|
|
try:
|
2015-07-18 02:29:06 +08:00
|
|
|
head = self.add_block("while.head")
|
2015-07-14 11:44:16 +08:00
|
|
|
self.append(ir.Branch(head))
|
|
|
|
self.current_block = head
|
2015-07-18 02:29:06 +08:00
|
|
|
old_continue, self.continue_target = self.continue_target, head
|
|
|
|
cond = self.visit(node.test)
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
break_block = self.add_block("while.break")
|
|
|
|
old_break, self.break_target = self.break_target, break_block
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
body = self.add_block("while.body")
|
2015-07-14 11:44:16 +08:00
|
|
|
self.current_block = body
|
|
|
|
self.visit(node.body)
|
2015-07-22 23:34:52 +08:00
|
|
|
post_body = self.current_block
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
if any(node.orelse):
|
|
|
|
else_tail = self.add_block("while.else")
|
|
|
|
self.current_block = else_tail
|
|
|
|
self.visit(node.orelse)
|
2015-07-22 23:34:52 +08:00
|
|
|
post_else_tail = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
tail = self.add_block("while.tail")
|
2015-07-14 11:44:16 +08:00
|
|
|
self.current_block = tail
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
if any(node.orelse):
|
2015-07-22 23:34:52 +08:00
|
|
|
if not post_else_tail.is_terminated():
|
|
|
|
post_else_tail.append(ir.Branch(tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
else_tail = tail
|
|
|
|
|
|
|
|
head.append(ir.BranchIf(cond, body, else_tail))
|
2015-07-22 23:34:52 +08:00
|
|
|
if not post_body.is_terminated():
|
|
|
|
post_body.append(ir.Branch(head))
|
2015-07-18 02:29:06 +08:00
|
|
|
break_block.append(ir.Branch(tail))
|
2015-07-14 11:44:16 +08:00
|
|
|
finally:
|
|
|
|
self.break_target = old_break
|
|
|
|
self.continue_target = old_continue
|
|
|
|
|
2015-07-22 23:34:52 +08:00
|
|
|
def iterable_len(self, value, typ=_size_type):
|
2015-07-18 02:29:06 +08:00
|
|
|
if builtins.is_list(value.type):
|
2015-07-22 23:34:52 +08:00
|
|
|
return self.append(ir.Builtin("len", [value], typ,
|
|
|
|
name="{}.len".format(value.name)))
|
2015-07-18 02:29:06 +08:00
|
|
|
elif builtins.is_range(value.type):
|
|
|
|
start = self.append(ir.GetAttr(value, "start"))
|
|
|
|
stop = self.append(ir.GetAttr(value, "stop"))
|
|
|
|
step = self.append(ir.GetAttr(value, "step"))
|
2015-07-19 16:51:53 +08:00
|
|
|
spread = self.append(ir.Arith(ast.Sub(loc=None), stop, start))
|
2015-07-22 23:34:52 +08:00
|
|
|
return self.append(ir.Arith(ast.FloorDiv(loc=None), spread, step,
|
|
|
|
name="{}.len".format(value.name)))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
2015-07-22 03:32:10 +08:00
|
|
|
def iterable_get(self, value, index):
|
2015-07-18 02:29:06 +08:00
|
|
|
# Assuming the value is within bounds.
|
|
|
|
if builtins.is_list(value.type):
|
|
|
|
return self.append(ir.GetElem(value, index))
|
|
|
|
elif builtins.is_range(value.type):
|
|
|
|
start = self.append(ir.GetAttr(value, "start"))
|
|
|
|
step = self.append(ir.GetAttr(value, "step"))
|
2015-07-19 16:51:53 +08:00
|
|
|
offset = self.append(ir.Arith(ast.Mult(loc=None), step, index))
|
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), start, offset))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
|
|
|
def visit_For(self, node):
|
|
|
|
try:
|
|
|
|
iterable = self.visit(node.iter)
|
2015-07-22 03:32:10 +08:00
|
|
|
length = self.iterable_len(iterable)
|
2015-07-21 09:54:34 +08:00
|
|
|
prehead = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
head = self.add_block("for.head")
|
|
|
|
self.append(ir.Branch(head))
|
|
|
|
self.current_block = head
|
|
|
|
phi = self.append(ir.Phi(length.type))
|
2015-07-21 09:54:34 +08:00
|
|
|
phi.add_incoming(ir.Constant(0, phi.type), prehead)
|
2015-07-18 02:29:06 +08:00
|
|
|
cond = self.append(ir.Compare(ast.Lt(loc=None), phi, length))
|
|
|
|
|
|
|
|
break_block = self.add_block("for.break")
|
|
|
|
old_break, self.break_target = self.break_target, break_block
|
|
|
|
|
|
|
|
continue_block = self.add_block("for.continue")
|
|
|
|
old_continue, self.continue_target = self.continue_target, continue_block
|
|
|
|
self.current_block = continue_block
|
|
|
|
|
2015-07-19 16:51:53 +08:00
|
|
|
updated_index = self.append(ir.Arith(ast.Add(loc=None), phi, ir.Constant(1, phi.type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
phi.add_incoming(updated_index, continue_block)
|
|
|
|
self.append(ir.Branch(head))
|
|
|
|
|
|
|
|
body = self.add_block("for.body")
|
|
|
|
self.current_block = body
|
2015-07-22 03:32:10 +08:00
|
|
|
elt = self.iterable_get(iterable, phi)
|
2015-07-18 02:29:06 +08:00
|
|
|
try:
|
|
|
|
self.current_assign = elt
|
2015-07-18 12:58:43 +08:00
|
|
|
self.visit(node.target)
|
2015-07-18 02:29:06 +08:00
|
|
|
finally:
|
|
|
|
self.current_assign = None
|
|
|
|
self.visit(node.body)
|
2015-07-22 23:34:52 +08:00
|
|
|
post_body = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
if any(node.orelse):
|
|
|
|
else_tail = self.add_block("for.else")
|
|
|
|
self.current_block = else_tail
|
|
|
|
self.visit(node.orelse)
|
2015-07-22 23:34:52 +08:00
|
|
|
post_else_tail = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
tail = self.add_block("for.tail")
|
|
|
|
self.current_block = tail
|
|
|
|
|
|
|
|
if any(node.orelse):
|
2015-07-22 23:34:52 +08:00
|
|
|
if not post_else_tail.is_terminated():
|
|
|
|
post_else_tail.append(ir.Branch(tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
else_tail = tail
|
|
|
|
|
|
|
|
head.append(ir.BranchIf(cond, body, else_tail))
|
2015-07-22 23:34:52 +08:00
|
|
|
if not post_body.is_terminated():
|
|
|
|
post_body.append(ir.Branch(continue_block))
|
2015-07-18 02:29:06 +08:00
|
|
|
break_block.append(ir.Branch(tail))
|
|
|
|
finally:
|
|
|
|
self.break_target = old_break
|
|
|
|
self.continue_target = old_continue
|
2015-07-14 11:44:16 +08:00
|
|
|
|
|
|
|
def visit_Break(self, node):
|
|
|
|
self.append(ir.Branch(self.break_target))
|
|
|
|
|
|
|
|
def visit_Continue(self, node):
|
|
|
|
self.append(ir.Branch(self.continue_target))
|
|
|
|
|
2015-08-08 21:18:57 +08:00
|
|
|
def raise_exn(self, exn, loc=None):
|
2015-07-27 17:36:21 +08:00
|
|
|
if exn is not None:
|
2015-08-08 21:18:57 +08:00
|
|
|
if loc is None:
|
|
|
|
loc = self.current_loc
|
|
|
|
|
|
|
|
loc_file = ir.Constant(loc.source_buffer.name, builtins.TStr())
|
|
|
|
loc_line = ir.Constant(loc.line(), builtins.TInt(types.TValue(32)))
|
|
|
|
loc_column = ir.Constant(loc.column(), builtins.TInt(types.TValue(32)))
|
2015-08-08 21:01:08 +08:00
|
|
|
loc_function = ir.Constant(".".join(self.name), builtins.TStr())
|
2015-08-08 21:18:57 +08:00
|
|
|
|
2015-07-27 17:36:21 +08:00
|
|
|
self.append(ir.SetAttr(exn, "__file__", loc_file))
|
|
|
|
self.append(ir.SetAttr(exn, "__line__", loc_line))
|
|
|
|
self.append(ir.SetAttr(exn, "__col__", loc_column))
|
2015-08-08 21:01:08 +08:00
|
|
|
self.append(ir.SetAttr(exn, "__func__", loc_function))
|
2015-07-27 17:36:21 +08:00
|
|
|
|
|
|
|
if self.unwind_target is not None:
|
|
|
|
self.append(ir.Raise(exn, self.unwind_target))
|
|
|
|
else:
|
|
|
|
self.append(ir.Raise(exn))
|
2015-07-27 14:10:20 +08:00
|
|
|
else:
|
2015-07-27 17:36:21 +08:00
|
|
|
if self.unwind_target is not None:
|
|
|
|
self.append(ir.Reraise(self.unwind_target))
|
|
|
|
else:
|
|
|
|
self.append(ir.Reraise())
|
2015-07-25 10:37:37 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def visit_Raise(self, node):
|
2015-07-25 10:37:37 +08:00
|
|
|
self.raise_exn(self.visit(node.exc))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
def visit_Try(self, node):
|
|
|
|
dispatcher = self.add_block("try.dispatch")
|
|
|
|
|
|
|
|
if any(node.finalbody):
|
|
|
|
# k for continuation
|
2015-07-19 15:30:42 +08:00
|
|
|
final_state = self.append(ir.Alloc([], ir.TEnvironment({ ".k": ir.TBasicBlock() })))
|
2015-07-18 02:29:06 +08:00
|
|
|
final_targets = []
|
|
|
|
|
|
|
|
if self.break_target is not None:
|
|
|
|
break_proxy = self.add_block("try.break")
|
|
|
|
old_break, self.break_target = self.break_target, break_proxy
|
2015-07-19 15:30:42 +08:00
|
|
|
break_proxy.append(ir.SetLocal(final_state, ".k", old_break))
|
2015-07-18 02:29:06 +08:00
|
|
|
final_targets.append(old_break)
|
|
|
|
if self.continue_target is not None:
|
|
|
|
continue_proxy = self.add_block("try.continue")
|
|
|
|
old_continue, self.continue_target = self.continue_target, continue_proxy
|
2015-07-19 15:30:42 +08:00
|
|
|
continue_proxy.append(ir.SetLocal(final_state, ".k", old_continue))
|
2015-07-18 02:29:06 +08:00
|
|
|
final_targets.append(old_continue)
|
|
|
|
|
|
|
|
return_proxy = self.add_block("try.return")
|
|
|
|
old_return, self.return_target = self.return_target, return_proxy
|
|
|
|
if old_return is not None:
|
2015-07-19 15:30:42 +08:00
|
|
|
return_proxy.append(ir.SetLocal(final_state, ".k", old_return))
|
2015-07-18 02:29:06 +08:00
|
|
|
final_targets.append(old_return)
|
|
|
|
else:
|
|
|
|
return_action = self.add_block("try.doreturn")
|
|
|
|
value = return_action.append(ir.GetLocal(self.current_private_env, ".return"))
|
|
|
|
return_action.append(ir.Return(value))
|
2015-07-19 15:30:42 +08:00
|
|
|
return_proxy.append(ir.SetLocal(final_state, ".k", return_action))
|
2015-07-18 02:29:06 +08:00
|
|
|
final_targets.append(return_action)
|
|
|
|
|
|
|
|
body = self.add_block("try.body")
|
|
|
|
self.append(ir.Branch(body))
|
|
|
|
self.current_block = body
|
|
|
|
|
|
|
|
try:
|
|
|
|
old_unwind, self.unwind_target = self.unwind_target, dispatcher
|
|
|
|
self.visit(node.body)
|
|
|
|
finally:
|
|
|
|
self.unwind_target = old_unwind
|
|
|
|
|
|
|
|
self.visit(node.orelse)
|
|
|
|
body = self.current_block
|
|
|
|
|
|
|
|
if any(node.finalbody):
|
|
|
|
if self.break_target:
|
|
|
|
self.break_target = old_break
|
|
|
|
if self.continue_target:
|
|
|
|
self.continue_target = old_continue
|
|
|
|
self.return_target = old_return
|
|
|
|
|
2015-07-27 17:36:21 +08:00
|
|
|
cleanup = self.add_block('handler.cleanup')
|
|
|
|
landingpad = dispatcher.append(ir.LandingPad(cleanup))
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
handlers = []
|
|
|
|
for handler_node in node.handlers:
|
2015-07-25 10:37:37 +08:00
|
|
|
exn_type = handler_node.name_type.find()
|
|
|
|
if handler_node.filter is not None and \
|
|
|
|
not builtins.is_exception(exn_type, 'Exception'):
|
|
|
|
handler = self.add_block("handler." + exn_type.name)
|
|
|
|
landingpad.add_clause(handler, exn_type)
|
|
|
|
else:
|
|
|
|
handler = self.add_block("handler.catchall")
|
|
|
|
landingpad.add_clause(handler, None)
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
self.current_block = handler
|
|
|
|
if handler_node.name is not None:
|
|
|
|
exn = self.append(ir.Builtin("exncast", [landingpad], handler_node.name_type))
|
2015-07-18 14:10:41 +08:00
|
|
|
self._set_local(handler_node.name, exn)
|
2015-07-18 02:29:06 +08:00
|
|
|
self.visit(handler_node.body)
|
2015-07-27 15:13:57 +08:00
|
|
|
post_handler = self.current_block
|
|
|
|
|
|
|
|
handlers.append((handler, post_handler))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
if any(node.finalbody):
|
|
|
|
finalizer = self.add_block("finally")
|
|
|
|
self.current_block = finalizer
|
|
|
|
|
|
|
|
self.visit(node.finalbody)
|
2015-07-25 10:37:37 +08:00
|
|
|
post_finalizer = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-27 17:36:21 +08:00
|
|
|
reraise = self.add_block('try.reraise')
|
|
|
|
reraise.append(ir.Reraise(self.unwind_target))
|
|
|
|
|
2015-07-25 10:37:37 +08:00
|
|
|
self.current_block = tail = self.add_block("try.tail")
|
2015-07-18 02:29:06 +08:00
|
|
|
if any(node.finalbody):
|
2015-07-25 10:37:37 +08:00
|
|
|
final_targets.append(tail)
|
2015-07-27 17:36:21 +08:00
|
|
|
final_targets.append(reraise)
|
2015-07-25 10:37:37 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
if self.break_target:
|
|
|
|
break_proxy.append(ir.Branch(finalizer))
|
|
|
|
if self.continue_target:
|
|
|
|
continue_proxy.append(ir.Branch(finalizer))
|
|
|
|
return_proxy.append(ir.Branch(finalizer))
|
2015-07-25 10:37:37 +08:00
|
|
|
|
|
|
|
if not body.is_terminated():
|
2015-07-19 15:30:42 +08:00
|
|
|
body.append(ir.SetLocal(final_state, ".k", tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
body.append(ir.Branch(finalizer))
|
|
|
|
|
2015-07-27 17:36:21 +08:00
|
|
|
cleanup.append(ir.SetLocal(final_state, ".k", reraise))
|
|
|
|
cleanup.append(ir.Branch(finalizer))
|
2015-07-25 10:37:37 +08:00
|
|
|
|
2015-07-27 15:13:57 +08:00
|
|
|
for handler, post_handler in handlers:
|
|
|
|
if not post_handler.is_terminated():
|
|
|
|
post_handler.append(ir.SetLocal(final_state, ".k", tail))
|
2015-07-27 17:36:21 +08:00
|
|
|
post_handler.append(ir.Branch(finalizer))
|
2015-07-25 10:37:37 +08:00
|
|
|
|
|
|
|
if not post_finalizer.is_terminated():
|
|
|
|
dest = post_finalizer.append(ir.GetLocal(final_state, ".k"))
|
|
|
|
post_finalizer.append(ir.IndirectBranch(dest, final_targets))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
2015-07-25 10:37:37 +08:00
|
|
|
if not body.is_terminated():
|
|
|
|
body.append(ir.Branch(tail))
|
|
|
|
|
2015-07-27 17:36:21 +08:00
|
|
|
cleanup.append(ir.Reraise(self.unwind_target))
|
|
|
|
|
2015-07-27 15:13:57 +08:00
|
|
|
for handler, post_handler in handlers:
|
|
|
|
if not post_handler.is_terminated():
|
|
|
|
post_handler.append(ir.Branch(tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
# TODO: With
|
|
|
|
|
|
|
|
# Expression visitors
|
|
|
|
# These visitors return a node in addition to mutating
|
|
|
|
# the IR.
|
|
|
|
|
|
|
|
def visit_LambdaT(self, node):
|
2015-07-30 01:35:16 +08:00
|
|
|
return self.visit_function(node, is_lambda=True, is_internal=True)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
def visit_IfExpT(self, node):
|
|
|
|
cond = self.visit(node.test)
|
|
|
|
head = self.current_block
|
|
|
|
|
|
|
|
if_true = self.add_block()
|
|
|
|
self.current_block = if_true
|
|
|
|
true_result = self.visit(node.body)
|
|
|
|
|
|
|
|
if_false = self.add_block()
|
|
|
|
self.current_block = if_false
|
|
|
|
false_result = self.visit(node.orelse)
|
|
|
|
|
|
|
|
tail = self.add_block()
|
|
|
|
self.current_block = tail
|
|
|
|
|
|
|
|
if not if_true.is_terminated():
|
|
|
|
if_true.append(ir.Branch(tail))
|
|
|
|
if not if_false.is_terminated():
|
|
|
|
if_false.append(ir.Branch(tail))
|
|
|
|
head.append(ir.BranchIf(cond, if_true, if_false))
|
|
|
|
|
|
|
|
phi = self.append(ir.Phi(node.type))
|
|
|
|
phi.add_incoming(true_result, if_true)
|
|
|
|
phi.add_incoming(false_result, if_false)
|
|
|
|
return phi
|
|
|
|
|
|
|
|
def visit_NumT(self, node):
|
|
|
|
return ir.Constant(node.n, node.type)
|
|
|
|
|
2015-07-21 19:27:48 +08:00
|
|
|
def visit_StrT(self, node):
|
|
|
|
return ir.Constant(node.s, node.type)
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def visit_NameConstantT(self, node):
|
|
|
|
return ir.Constant(node.value, node.type)
|
|
|
|
|
2015-07-18 14:10:41 +08:00
|
|
|
def _env_for(self, name):
|
|
|
|
if name in self.current_globals:
|
|
|
|
return self.append(ir.Builtin("globalenv", [self.current_env],
|
|
|
|
self.current_env.type.outermost()))
|
|
|
|
else:
|
|
|
|
return self.current_env
|
|
|
|
|
|
|
|
def _get_local(self, name):
|
|
|
|
return self.append(ir.GetLocal(self._env_for(name), name, name="local." + name))
|
|
|
|
|
|
|
|
def _set_local(self, name, value):
|
|
|
|
self.append(ir.SetLocal(self._env_for(name), name, value))
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def visit_NameT(self, node):
|
|
|
|
if self.current_assign is None:
|
2015-07-18 14:10:41 +08:00
|
|
|
return self._get_local(node.id)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
2015-07-18 14:10:41 +08:00
|
|
|
return self._set_local(node.id, self.current_assign)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
def visit_AttributeT(self, node):
|
|
|
|
try:
|
|
|
|
old_assign, self.current_assign = self.current_assign, None
|
|
|
|
obj = self.visit(node.value)
|
|
|
|
finally:
|
|
|
|
self.current_assign = old_assign
|
|
|
|
|
|
|
|
if self.current_assign is None:
|
|
|
|
return self.append(ir.GetAttr(obj, node.attr,
|
|
|
|
name="{}.{}".format(_readable_name(obj), node.attr)))
|
|
|
|
else:
|
|
|
|
self.append(ir.SetAttr(obj, node.attr, self.current_assign))
|
|
|
|
|
2015-08-08 21:18:57 +08:00
|
|
|
def _map_index(self, length, index, one_past_the_end=False, loc=None):
|
2015-07-18 02:29:06 +08:00
|
|
|
lt_0 = self.append(ir.Compare(ast.Lt(loc=None),
|
|
|
|
index, ir.Constant(0, index.type)))
|
2015-07-19 16:51:53 +08:00
|
|
|
from_end = self.append(ir.Arith(ast.Add(loc=None), length, index))
|
2015-07-18 02:29:06 +08:00
|
|
|
mapped_index = self.append(ir.Select(lt_0, from_end, index))
|
|
|
|
mapped_ge_0 = self.append(ir.Compare(ast.GtE(loc=None),
|
|
|
|
mapped_index, ir.Constant(0, mapped_index.type)))
|
2015-07-22 23:34:52 +08:00
|
|
|
end_cmpop = ast.LtE(loc=None) if one_past_the_end else ast.Lt(loc=None)
|
|
|
|
mapped_lt_len = self.append(ir.Compare(end_cmpop, mapped_index, length))
|
2015-07-18 02:29:06 +08:00
|
|
|
in_bounds = self.append(ir.Select(mapped_ge_0, mapped_lt_len,
|
|
|
|
ir.Constant(False, builtins.TBool())))
|
2015-07-25 10:37:37 +08:00
|
|
|
head = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-25 10:37:37 +08:00
|
|
|
self.current_block = out_of_bounds_block = self.add_block()
|
|
|
|
exn = self.alloc_exn(builtins.TIndexError(),
|
|
|
|
ir.Constant("index {0} out of bounds 0:{1}", builtins.TStr()),
|
|
|
|
index, length)
|
2015-08-08 21:18:57 +08:00
|
|
|
self.raise_exn(exn, loc=loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-25 10:37:37 +08:00
|
|
|
self.current_block = in_bounds_block = self.add_block()
|
|
|
|
head.append(ir.BranchIf(in_bounds, in_bounds_block, out_of_bounds_block))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
return mapped_index
|
|
|
|
|
2015-08-08 21:26:53 +08:00
|
|
|
def _make_check(self, cond, exn_gen, loc=None):
|
2015-07-18 02:29:06 +08:00
|
|
|
# cond: bool Value, condition
|
|
|
|
# exn_gen: lambda()->exn Value, exception if condition not true
|
|
|
|
cond_block = self.current_block
|
|
|
|
|
|
|
|
self.current_block = body_block = self.add_block()
|
2015-08-08 21:26:53 +08:00
|
|
|
self.raise_exn(exn_gen(), loc=loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
self.current_block = tail_block = self.add_block()
|
|
|
|
cond_block.append(ir.BranchIf(cond, tail_block, body_block))
|
|
|
|
|
|
|
|
def _make_loop(self, init, cond_gen, body_gen):
|
|
|
|
# init: 'iter Value, initial loop variable value
|
|
|
|
# cond_gen: lambda('iter Value)->bool Value, loop condition
|
|
|
|
# body_gen: lambda('iter Value)->'iter Value, loop body,
|
|
|
|
# returns next loop variable value
|
|
|
|
init_block = self.current_block
|
|
|
|
|
|
|
|
self.current_block = head_block = self.add_block()
|
|
|
|
init_block.append(ir.Branch(head_block))
|
|
|
|
phi = self.append(ir.Phi(init.type))
|
|
|
|
phi.add_incoming(init, init_block)
|
|
|
|
cond = cond_gen(phi)
|
|
|
|
|
|
|
|
self.current_block = body_block = self.add_block()
|
|
|
|
body = body_gen(phi)
|
|
|
|
self.append(ir.Branch(head_block))
|
|
|
|
phi.add_incoming(body, self.current_block)
|
|
|
|
|
|
|
|
self.current_block = tail_block = self.add_block()
|
|
|
|
head_block.append(ir.BranchIf(cond, body_block, tail_block))
|
|
|
|
|
|
|
|
return head_block, body_block, tail_block
|
|
|
|
|
|
|
|
def visit_SubscriptT(self, node):
|
|
|
|
try:
|
|
|
|
old_assign, self.current_assign = self.current_assign, None
|
|
|
|
value = self.visit(node.value)
|
|
|
|
finally:
|
|
|
|
self.current_assign = old_assign
|
|
|
|
|
|
|
|
if isinstance(node.slice, ast.Index):
|
|
|
|
index = self.visit(node.slice.value)
|
2015-07-22 03:32:10 +08:00
|
|
|
length = self.iterable_len(value, index.type)
|
2015-08-08 21:18:57 +08:00
|
|
|
mapped_index = self._map_index(length, index,
|
|
|
|
loc=node.begin_loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
if self.current_assign is None:
|
2015-07-22 03:32:10 +08:00
|
|
|
result = self.iterable_get(value, mapped_index)
|
2015-07-18 02:29:06 +08:00
|
|
|
result.set_name("{}.at.{}".format(value.name, _readable_name(index)))
|
|
|
|
return result
|
|
|
|
else:
|
|
|
|
self.append(ir.SetElem(value, mapped_index, self.current_assign,
|
|
|
|
name="{}.at.{}".format(value.name, _readable_name(index))))
|
|
|
|
else: # Slice
|
2015-07-22 03:32:10 +08:00
|
|
|
length = self.iterable_len(value, node.slice.type)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
if node.slice.lower is not None:
|
2015-07-23 12:57:49 +08:00
|
|
|
start_index = self.visit(node.slice.lower)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
2015-07-23 12:57:49 +08:00
|
|
|
start_index = ir.Constant(0, node.slice.type)
|
2015-08-08 21:18:57 +08:00
|
|
|
mapped_start_index = self._map_index(length, start_index,
|
|
|
|
loc=node.begin_loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
if node.slice.upper is not None:
|
2015-07-23 12:57:49 +08:00
|
|
|
stop_index = self.visit(node.slice.upper)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
2015-07-23 12:57:49 +08:00
|
|
|
stop_index = length
|
2015-08-08 21:18:57 +08:00
|
|
|
mapped_stop_index = self._map_index(length, stop_index, one_past_the_end=True,
|
|
|
|
loc=node.begin_loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
if node.slice.step is not None:
|
|
|
|
step = self.visit(node.slice.step)
|
2015-07-25 10:37:37 +08:00
|
|
|
self._make_check(
|
|
|
|
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
|
|
|
lambda: self.alloc_exn(builtins.TValueError(),
|
2015-08-08 21:26:53 +08:00
|
|
|
ir.Constant("step cannot be zero", builtins.TStr())),
|
|
|
|
loc=node.slice.step.loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
step = ir.Constant(1, node.slice.type)
|
2015-07-23 12:57:49 +08:00
|
|
|
counting_up = self.append(ir.Compare(ast.Gt(loc=None), step,
|
|
|
|
ir.Constant(0, step.type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-19 16:51:53 +08:00
|
|
|
unstepped_size = self.append(ir.Arith(ast.Sub(loc=None),
|
2015-07-23 12:57:49 +08:00
|
|
|
mapped_stop_index, mapped_start_index))
|
|
|
|
slice_size_a = self.append(ir.Arith(ast.FloorDiv(loc=None), unstepped_size, step))
|
|
|
|
slice_size_b = self.append(ir.Arith(ast.Mod(loc=None), unstepped_size, step))
|
|
|
|
rem_not_empty = self.append(ir.Compare(ast.NotEq(loc=None), slice_size_b,
|
|
|
|
ir.Constant(0, slice_size_b.type)))
|
|
|
|
slice_size_c = self.append(ir.Arith(ast.Add(loc=None), slice_size_a,
|
|
|
|
ir.Constant(1, slice_size_a.type)))
|
|
|
|
slice_size = self.append(ir.Select(rem_not_empty,
|
|
|
|
slice_size_c, slice_size_a,
|
|
|
|
name="slice.size"))
|
2015-07-25 10:37:37 +08:00
|
|
|
self._make_check(
|
|
|
|
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
|
|
|
lambda: self.alloc_exn(builtins.TValueError(),
|
|
|
|
ir.Constant("slice size {0} is larger than iterable length {1}",
|
|
|
|
builtins.TStr()),
|
2015-08-08 21:26:53 +08:00
|
|
|
slice_size, length),
|
|
|
|
loc=node.slice.loc)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
if self.current_assign is None:
|
2015-07-23 12:57:49 +08:00
|
|
|
is_neg_size = self.append(ir.Compare(ast.Lt(loc=None),
|
|
|
|
slice_size, ir.Constant(0, slice_size.type)))
|
|
|
|
abs_slice_size = self.append(ir.Select(is_neg_size,
|
|
|
|
ir.Constant(0, slice_size.type), slice_size))
|
|
|
|
other_value = self.append(ir.Alloc([abs_slice_size], value.type,
|
|
|
|
name="slice.result"))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
other_value = self.current_assign
|
|
|
|
|
2015-07-23 12:57:49 +08:00
|
|
|
prehead = self.current_block
|
|
|
|
|
|
|
|
head = self.current_block = self.add_block()
|
|
|
|
prehead.append(ir.Branch(head))
|
|
|
|
|
|
|
|
index = self.append(ir.Phi(node.slice.type,
|
|
|
|
name="slice.index"))
|
|
|
|
index.add_incoming(mapped_start_index, prehead)
|
|
|
|
other_index = self.append(ir.Phi(node.slice.type,
|
|
|
|
name="slice.resindex"))
|
|
|
|
other_index.add_incoming(ir.Constant(0, node.slice.type), prehead)
|
|
|
|
|
|
|
|
# Still within bounds?
|
|
|
|
bounded_up = self.append(ir.Compare(ast.Lt(loc=None), index, mapped_stop_index))
|
|
|
|
bounded_down = self.append(ir.Compare(ast.Gt(loc=None), index, mapped_stop_index))
|
|
|
|
within_bounds = self.append(ir.Select(counting_up, bounded_up, bounded_down))
|
|
|
|
|
|
|
|
body = self.current_block = self.add_block()
|
|
|
|
|
|
|
|
if self.current_assign is None:
|
|
|
|
elem = self.iterable_get(value, index)
|
|
|
|
self.append(ir.SetElem(other_value, other_index, elem))
|
|
|
|
else:
|
|
|
|
elem = self.append(ir.GetElem(self.current_assign, other_index))
|
|
|
|
self.append(ir.SetElem(value, index, elem))
|
|
|
|
|
|
|
|
next_index = self.append(ir.Arith(ast.Add(loc=None), index, step))
|
|
|
|
index.add_incoming(next_index, body)
|
|
|
|
next_other_index = self.append(ir.Arith(ast.Add(loc=None), other_index,
|
|
|
|
ir.Constant(1, node.slice.type)))
|
|
|
|
other_index.add_incoming(next_other_index, body)
|
|
|
|
self.append(ir.Branch(head))
|
|
|
|
|
|
|
|
tail = self.current_block = self.add_block()
|
|
|
|
head.append(ir.BranchIf(within_bounds, body, tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-22 23:34:52 +08:00
|
|
|
if self.current_assign is None:
|
|
|
|
return other_value
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def visit_TupleT(self, node):
|
|
|
|
if self.current_assign is None:
|
|
|
|
return self.append(ir.Alloc([self.visit(elt) for elt in node.elts], node.type))
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
old_assign = self.current_assign
|
2015-07-19 15:31:11 +08:00
|
|
|
for index, elt_node in enumerate(node.elts):
|
2015-07-18 02:29:06 +08:00
|
|
|
self.current_assign = \
|
|
|
|
self.append(ir.GetAttr(old_assign, index,
|
2015-07-22 07:58:59 +08:00
|
|
|
name="{}.e{}".format(old_assign.name, index)),
|
2015-07-19 15:31:11 +08:00
|
|
|
loc=elt_node.loc)
|
|
|
|
self.visit(elt_node)
|
2015-07-18 02:29:06 +08:00
|
|
|
finally:
|
|
|
|
self.current_assign = old_assign
|
|
|
|
|
|
|
|
def visit_ListT(self, node):
|
|
|
|
if self.current_assign is None:
|
2015-07-19 15:31:11 +08:00
|
|
|
elts = [self.visit(elt_node) for elt_node in node.elts]
|
2015-07-18 02:29:06 +08:00
|
|
|
lst = self.append(ir.Alloc([ir.Constant(len(node.elts), self._size_type)],
|
|
|
|
node.type))
|
2015-07-19 15:31:11 +08:00
|
|
|
for index, elt_node in enumerate(elts):
|
|
|
|
self.append(ir.SetElem(lst, ir.Constant(index, self._size_type), elt_node))
|
2015-07-18 02:29:06 +08:00
|
|
|
return lst
|
|
|
|
else:
|
2015-07-22 23:34:52 +08:00
|
|
|
length = self.iterable_len(self.current_assign)
|
2015-07-25 10:37:37 +08:00
|
|
|
self._make_check(
|
|
|
|
self.append(ir.Compare(ast.Eq(loc=None), length,
|
|
|
|
ir.Constant(len(node.elts), self._size_type))),
|
|
|
|
lambda: self.alloc_exn(builtins.TValueError(),
|
|
|
|
ir.Constant("list must be {0} elements long to decompose", builtins.TStr()),
|
|
|
|
length))
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
for index, elt_node in enumerate(node.elts):
|
|
|
|
elt = self.append(ir.GetElem(self.current_assign,
|
|
|
|
ir.Constant(index, self._size_type)))
|
|
|
|
try:
|
|
|
|
old_assign, self.current_assign = self.current_assign, elt
|
|
|
|
self.visit(elt_node)
|
|
|
|
finally:
|
|
|
|
self.current_assign = old_assign
|
|
|
|
|
|
|
|
def visit_ListCompT(self, node):
|
|
|
|
assert len(node.generators) == 1
|
|
|
|
comprehension = node.generators[0]
|
|
|
|
assert comprehension.ifs == []
|
|
|
|
|
|
|
|
iterable = self.visit(comprehension.iter)
|
2015-07-22 03:32:10 +08:00
|
|
|
length = self.iterable_len(iterable)
|
2015-07-18 02:29:06 +08:00
|
|
|
result = self.append(ir.Alloc([length], node.type))
|
|
|
|
|
|
|
|
try:
|
|
|
|
env_type = ir.TEnvironment(node.typing_env, self.current_env.type)
|
|
|
|
env = self.append(ir.Alloc([], env_type, name="env.gen"))
|
|
|
|
old_env, self.current_env = self.current_env, env
|
|
|
|
|
|
|
|
self.append(ir.SetLocal(env, ".outer", old_env))
|
|
|
|
|
|
|
|
def body_gen(index):
|
2015-07-22 03:32:10 +08:00
|
|
|
elt = self.iterable_get(iterable, index)
|
2015-07-18 02:29:06 +08:00
|
|
|
try:
|
|
|
|
old_assign, self.current_assign = self.current_assign, elt
|
|
|
|
self.visit(comprehension.target)
|
|
|
|
finally:
|
|
|
|
self.current_assign = old_assign
|
|
|
|
|
|
|
|
mapped_elt = self.visit(node.elt)
|
|
|
|
self.append(ir.SetElem(result, index, mapped_elt))
|
2015-07-19 16:51:53 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
|
|
|
ir.Constant(1, length.type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self._make_loop(ir.Constant(0, length.type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, length)),
|
|
|
|
body_gen)
|
|
|
|
|
|
|
|
return result
|
|
|
|
finally:
|
|
|
|
self.current_env = old_env
|
|
|
|
|
|
|
|
def visit_BoolOpT(self, node):
|
|
|
|
blocks = []
|
|
|
|
for value_node in node.values:
|
2015-07-22 07:58:59 +08:00
|
|
|
value_head = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
value = self.visit(value_node)
|
2015-07-22 07:58:59 +08:00
|
|
|
self.instrument_assert(value_node, value)
|
|
|
|
value_tail = self.current_block
|
|
|
|
|
|
|
|
blocks.append((value, value_head, value_tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
self.current_block = self.add_block()
|
|
|
|
|
|
|
|
tail = self.current_block
|
|
|
|
phi = self.append(ir.Phi(node.type))
|
2015-07-22 07:58:59 +08:00
|
|
|
for ((value, value_head, value_tail), (next_value_head, next_value_tail)) in \
|
|
|
|
zip(blocks, [(h,t) for (v,h,t) in blocks[1:]] + [(tail, tail)]):
|
|
|
|
phi.add_incoming(value, value_tail)
|
|
|
|
if next_value_head != tail:
|
2015-07-23 00:09:14 +08:00
|
|
|
cond = self.coerce_to_bool(value, block=value_tail)
|
2015-07-22 07:58:59 +08:00
|
|
|
if isinstance(node.op, ast.And):
|
2015-07-23 00:09:14 +08:00
|
|
|
value_tail.append(ir.BranchIf(cond, next_value_head, tail))
|
2015-07-22 07:58:59 +08:00
|
|
|
else:
|
2015-07-23 00:09:14 +08:00
|
|
|
value_tail.append(ir.BranchIf(cond, tail, next_value_head))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
2015-07-22 07:58:59 +08:00
|
|
|
value_tail.append(ir.Branch(tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
return phi
|
|
|
|
|
|
|
|
def visit_UnaryOpT(self, node):
|
|
|
|
if isinstance(node.op, ast.Not):
|
2015-07-23 00:09:14 +08:00
|
|
|
cond = self.coerce_to_bool(self.visit(node.operand))
|
|
|
|
return self.append(ir.Select(cond,
|
2015-07-18 02:29:06 +08:00
|
|
|
ir.Constant(False, builtins.TBool()),
|
|
|
|
ir.Constant(True, builtins.TBool())))
|
2015-07-19 16:51:53 +08:00
|
|
|
elif isinstance(node.op, ast.USub):
|
|
|
|
operand = self.visit(node.operand)
|
|
|
|
return self.append(ir.Arith(ast.Sub(loc=None),
|
|
|
|
ir.Constant(0, operand.type), operand))
|
|
|
|
elif isinstance(node.op, ast.UAdd):
|
|
|
|
# No-op.
|
|
|
|
return self.visit(node.operand)
|
|
|
|
else:
|
|
|
|
assert False
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
def visit_CoerceT(self, node):
|
|
|
|
value = self.visit(node.value)
|
|
|
|
if node.type.find() == value.type:
|
|
|
|
return value
|
|
|
|
else:
|
|
|
|
return self.append(ir.Coerce(value, node.type,
|
|
|
|
name="{}.{}".format(_readable_name(value),
|
|
|
|
node.type.name)))
|
|
|
|
|
|
|
|
def visit_BinOpT(self, node):
|
|
|
|
if builtins.is_numeric(node.type):
|
2015-08-08 21:26:53 +08:00
|
|
|
lhs = self.visit(node.left)
|
2015-07-23 05:58:41 +08:00
|
|
|
rhs = self.visit(node.right)
|
|
|
|
if isinstance(node.op, (ast.LShift, ast.RShift)):
|
|
|
|
# Check for negative shift amount.
|
2015-07-25 10:37:37 +08:00
|
|
|
self._make_check(
|
|
|
|
self.append(ir.Compare(ast.GtE(loc=None), rhs, ir.Constant(0, rhs.type))),
|
|
|
|
lambda: self.alloc_exn(builtins.TValueError(),
|
2015-08-08 21:26:53 +08:00
|
|
|
ir.Constant("shift amount must be nonnegative", builtins.TStr())),
|
|
|
|
loc=node.right.loc)
|
2015-07-23 06:26:50 +08:00
|
|
|
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
2015-07-25 10:37:37 +08:00
|
|
|
self._make_check(
|
|
|
|
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
|
|
|
lambda: self.alloc_exn(builtins.TZeroDivisionError(),
|
2015-08-08 21:26:53 +08:00
|
|
|
ir.Constant("cannot divide by zero", builtins.TStr())),
|
|
|
|
loc=node.right.loc)
|
2015-07-23 05:58:41 +08:00
|
|
|
|
2015-08-08 21:26:53 +08:00
|
|
|
return self.append(ir.Arith(node.op, lhs, rhs))
|
2015-07-18 02:29:06 +08:00
|
|
|
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
|
|
|
|
lhs, rhs = self.visit(node.left), self.visit(node.right)
|
2015-07-22 23:34:52 +08:00
|
|
|
if types.is_tuple(node.left.type) and types.is_tuple(node.right.type):
|
2015-07-18 02:29:06 +08:00
|
|
|
elts = []
|
|
|
|
for index, elt in enumerate(node.left.type.elts):
|
|
|
|
elts.append(self.append(ir.GetAttr(lhs, index)))
|
|
|
|
for index, elt in enumerate(node.right.type.elts):
|
|
|
|
elts.append(self.append(ir.GetAttr(rhs, index)))
|
|
|
|
return self.append(ir.Alloc(elts, node.type))
|
|
|
|
elif builtins.is_list(node.left.type) and builtins.is_list(node.right.type):
|
2015-07-22 23:34:52 +08:00
|
|
|
lhs_length = self.iterable_len(lhs)
|
|
|
|
rhs_length = self.iterable_len(rhs)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-19 16:51:53 +08:00
|
|
|
result_length = self.append(ir.Arith(ast.Add(loc=None), lhs_length, rhs_length))
|
2015-07-18 02:29:06 +08:00
|
|
|
result = self.append(ir.Alloc([result_length], node.type))
|
|
|
|
|
|
|
|
# Copy lhs
|
|
|
|
def body_gen(index):
|
|
|
|
elt = self.append(ir.GetElem(lhs, index))
|
|
|
|
self.append(ir.SetElem(result, index, elt))
|
2015-07-19 16:51:53 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
|
|
|
ir.Constant(1, self._size_type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self._make_loop(ir.Constant(0, self._size_type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, lhs_length)),
|
|
|
|
body_gen)
|
|
|
|
|
|
|
|
# Copy rhs
|
|
|
|
def body_gen(index):
|
|
|
|
elt = self.append(ir.GetElem(rhs, index))
|
2015-07-19 16:51:53 +08:00
|
|
|
result_index = self.append(ir.Arith(ast.Add(loc=None), index, lhs_length))
|
2015-07-18 02:29:06 +08:00
|
|
|
self.append(ir.SetElem(result, result_index, elt))
|
2015-07-19 16:51:53 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
|
|
|
ir.Constant(1, self._size_type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self._make_loop(ir.Constant(0, self._size_type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, rhs_length)),
|
|
|
|
body_gen)
|
|
|
|
|
|
|
|
return result
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif isinstance(node.op, ast.Mult): # list * int, int * list
|
|
|
|
lhs, rhs = self.visit(node.left), self.visit(node.right)
|
|
|
|
if builtins.is_list(lhs.type) and builtins.is_int(rhs.type):
|
|
|
|
lst, num = lhs, rhs
|
|
|
|
elif builtins.is_int(lhs.type) and builtins.is_list(rhs.type):
|
|
|
|
lst, num = rhs, lhs
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
2015-07-22 23:34:52 +08:00
|
|
|
lst_length = self.iterable_len(lst)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
2015-07-19 16:51:53 +08:00
|
|
|
result_length = self.append(ir.Arith(ast.Mult(loc=None), lst_length, num))
|
2015-07-18 02:29:06 +08:00
|
|
|
result = self.append(ir.Alloc([result_length], node.type))
|
|
|
|
|
|
|
|
# num times...
|
|
|
|
def body_gen(num_index):
|
|
|
|
# ... copy the list
|
|
|
|
def body_gen(lst_index):
|
|
|
|
elt = self.append(ir.GetElem(lst, lst_index))
|
2015-07-19 16:51:53 +08:00
|
|
|
base_index = self.append(ir.Arith(ast.Mult(loc=None),
|
|
|
|
num_index, lst_length))
|
|
|
|
result_index = self.append(ir.Arith(ast.Add(loc=None),
|
|
|
|
base_index, lst_index))
|
2015-07-18 02:29:06 +08:00
|
|
|
self.append(ir.SetElem(result, base_index, elt))
|
2015-07-19 16:51:53 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), lst_index,
|
|
|
|
ir.Constant(1, self._size_type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self._make_loop(ir.Constant(0, self._size_type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, lst_length)),
|
|
|
|
body_gen)
|
|
|
|
|
2015-07-22 23:34:52 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), num_index,
|
2015-07-19 16:51:53 +08:00
|
|
|
ir.Constant(1, self._size_type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self._make_loop(ir.Constant(0, self._size_type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, num)),
|
|
|
|
body_gen)
|
2015-07-22 23:34:52 +08:00
|
|
|
|
|
|
|
return result
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
2015-07-22 03:32:10 +08:00
|
|
|
def polymorphic_compare_pair_order(self, op, lhs, rhs):
|
2015-07-18 02:29:06 +08:00
|
|
|
if builtins.is_numeric(lhs.type) and builtins.is_numeric(rhs.type):
|
|
|
|
return self.append(ir.Compare(op, lhs, rhs))
|
2015-07-22 23:34:52 +08:00
|
|
|
elif builtins.is_bool(lhs.type) and builtins.is_bool(rhs.type):
|
|
|
|
return self.append(ir.Compare(op, lhs, rhs))
|
2015-07-18 02:29:06 +08:00
|
|
|
elif types.is_tuple(lhs.type) and types.is_tuple(rhs.type):
|
|
|
|
result = None
|
|
|
|
for index in range(len(lhs.type.elts)):
|
|
|
|
lhs_elt = self.append(ir.GetAttr(lhs, index))
|
|
|
|
rhs_elt = self.append(ir.GetAttr(rhs, index))
|
|
|
|
elt_result = self.append(ir.Compare(op, lhs_elt, rhs_elt))
|
|
|
|
if result is None:
|
|
|
|
result = elt_result
|
|
|
|
else:
|
|
|
|
result = self.append(ir.Select(result, elt_result,
|
|
|
|
ir.Constant(False, builtins.TBool())))
|
|
|
|
return result
|
|
|
|
elif builtins.is_list(lhs.type) and builtins.is_list(rhs.type):
|
|
|
|
head = self.current_block
|
2015-07-22 23:34:52 +08:00
|
|
|
lhs_length = self.iterable_len(lhs)
|
|
|
|
rhs_length = self.iterable_len(rhs)
|
2015-07-18 02:29:06 +08:00
|
|
|
compare_length = self.append(ir.Compare(op, lhs_length, rhs_length))
|
|
|
|
eq_length = self.append(ir.Compare(ast.Eq(loc=None), lhs_length, rhs_length))
|
|
|
|
|
|
|
|
# If the length is the same, compare element-by-element
|
|
|
|
# and break when the comparison result is false
|
|
|
|
loop_head = self.add_block()
|
|
|
|
self.current_block = loop_head
|
|
|
|
index_phi = self.append(ir.Phi(self._size_type))
|
|
|
|
index_phi.add_incoming(ir.Constant(0, self._size_type), head)
|
|
|
|
loop_cond = self.append(ir.Compare(ast.Lt(loc=None), index_phi, lhs_length))
|
|
|
|
|
|
|
|
loop_body = self.add_block()
|
|
|
|
self.current_block = loop_body
|
|
|
|
lhs_elt = self.append(ir.GetElem(lhs, index_phi))
|
|
|
|
rhs_elt = self.append(ir.GetElem(rhs, index_phi))
|
2015-07-22 03:32:10 +08:00
|
|
|
body_result = self.polymorphic_compare_pair(op, lhs_elt, rhs_elt)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
loop_body2 = self.add_block()
|
|
|
|
self.current_block = loop_body2
|
2015-07-19 16:51:53 +08:00
|
|
|
index_next = self.append(ir.Arith(ast.Add(loc=None), index_phi,
|
|
|
|
ir.Constant(1, self._size_type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self.append(ir.Branch(loop_head))
|
|
|
|
index_phi.add_incoming(index_next, loop_body2)
|
|
|
|
|
|
|
|
tail = self.add_block()
|
|
|
|
self.current_block = tail
|
|
|
|
phi = self.append(ir.Phi(builtins.TBool()))
|
|
|
|
head.append(ir.BranchIf(eq_length, loop_head, tail))
|
|
|
|
phi.add_incoming(compare_length, head)
|
|
|
|
loop_head.append(ir.BranchIf(loop_cond, loop_body, tail))
|
|
|
|
phi.add_incoming(ir.Constant(True, builtins.TBool()), loop_head)
|
|
|
|
loop_body.append(ir.BranchIf(body_result, loop_body2, tail))
|
|
|
|
phi.add_incoming(body_result, loop_body)
|
|
|
|
|
|
|
|
if isinstance(op, ast.NotEq):
|
|
|
|
result = self.append(ir.Select(phi,
|
|
|
|
ir.Constant(False, builtins.TBool()), ir.Constant(True, builtins.TBool())))
|
|
|
|
else:
|
|
|
|
result = phi
|
|
|
|
|
|
|
|
return result
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
2015-07-22 03:32:10 +08:00
|
|
|
def polymorphic_compare_pair_inclusion(self, op, needle, haystack):
|
2015-07-18 02:29:06 +08:00
|
|
|
if builtins.is_range(haystack.type):
|
|
|
|
# Optimized range `in` operator
|
|
|
|
start = self.append(ir.GetAttr(haystack, "start"))
|
|
|
|
stop = self.append(ir.GetAttr(haystack, "stop"))
|
|
|
|
step = self.append(ir.GetAttr(haystack, "step"))
|
|
|
|
after_start = self.append(ir.Compare(ast.GtE(loc=None), needle, start))
|
|
|
|
after_stop = self.append(ir.Compare(ast.Lt(loc=None), needle, stop))
|
2015-07-19 16:51:53 +08:00
|
|
|
from_start = self.append(ir.Arith(ast.Sub(loc=None), needle, start))
|
|
|
|
mod_step = self.append(ir.Arith(ast.Mod(loc=None), from_start, step))
|
2015-07-18 02:29:06 +08:00
|
|
|
on_step = self.append(ir.Compare(ast.Eq(loc=None), mod_step,
|
|
|
|
ir.Constant(0, mod_step.type)))
|
|
|
|
result = self.append(ir.Select(after_start, after_stop,
|
|
|
|
ir.Constant(False, builtins.TBool())))
|
|
|
|
result = self.append(ir.Select(result, on_step,
|
|
|
|
ir.Constant(False, builtins.TBool())))
|
2015-07-22 07:58:59 +08:00
|
|
|
elif builtins.is_iterable(haystack.type):
|
2015-07-22 03:32:10 +08:00
|
|
|
length = self.iterable_len(haystack)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
cmp_result = loop_body2 = None
|
|
|
|
def body_gen(index):
|
|
|
|
nonlocal cmp_result, loop_body2
|
|
|
|
|
2015-07-22 03:32:10 +08:00
|
|
|
elt = self.iterable_get(haystack, index)
|
|
|
|
cmp_result = self.polymorphic_compare_pair(ast.Eq(loc=None), needle, elt)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
loop_body2 = self.add_block()
|
|
|
|
self.current_block = loop_body2
|
2015-07-19 16:51:53 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
|
|
|
ir.Constant(1, length.type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
loop_head, loop_body, loop_tail = \
|
|
|
|
self._make_loop(ir.Constant(0, length.type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, length)),
|
|
|
|
body_gen)
|
|
|
|
|
|
|
|
loop_body.append(ir.BranchIf(cmp_result, loop_tail, loop_body2))
|
|
|
|
phi = loop_tail.prepend(ir.Phi(builtins.TBool()))
|
|
|
|
phi.add_incoming(ir.Constant(False, builtins.TBool()), loop_head)
|
|
|
|
phi.add_incoming(ir.Constant(True, builtins.TBool()), loop_body)
|
|
|
|
|
|
|
|
result = phi
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
|
|
|
if isinstance(op, ast.NotIn):
|
|
|
|
result = self.append(ir.Select(result,
|
|
|
|
ir.Constant(False, builtins.TBool()),
|
|
|
|
ir.Constant(True, builtins.TBool())))
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2015-07-22 03:32:10 +08:00
|
|
|
def polymorphic_compare_pair(self, op, lhs, rhs):
|
2015-07-18 02:29:06 +08:00
|
|
|
if isinstance(op, (ast.Is, ast.IsNot)):
|
2015-07-22 23:34:52 +08:00
|
|
|
# The backend will handle equality of aggregates.
|
|
|
|
return self.append(ir.Compare(op, lhs, rhs))
|
2015-07-18 02:29:06 +08:00
|
|
|
elif isinstance(op, (ast.In, ast.NotIn)):
|
2015-07-22 03:32:10 +08:00
|
|
|
return self.polymorphic_compare_pair_inclusion(op, lhs, rhs)
|
2015-07-18 02:29:06 +08:00
|
|
|
else: # Eq, NotEq, Lt, LtE, Gt, GtE
|
2015-07-22 03:32:10 +08:00
|
|
|
return self.polymorphic_compare_pair_order(op, lhs, rhs)
|
2015-07-18 02:29:06 +08:00
|
|
|
|
|
|
|
def visit_CompareT(self, node):
|
|
|
|
# Essentially a sequence of `and`s performed over results
|
|
|
|
# of comparisons.
|
|
|
|
blocks = []
|
|
|
|
lhs = self.visit(node.left)
|
2015-07-22 07:58:59 +08:00
|
|
|
self.instrument_assert(node.left, lhs)
|
2015-07-18 02:29:06 +08:00
|
|
|
for op, rhs_node in zip(node.ops, node.comparators):
|
2015-07-22 23:34:52 +08:00
|
|
|
result_head = self.current_block
|
2015-07-18 02:29:06 +08:00
|
|
|
rhs = self.visit(rhs_node)
|
2015-07-22 07:58:59 +08:00
|
|
|
self.instrument_assert(rhs_node, rhs)
|
2015-07-22 03:32:10 +08:00
|
|
|
result = self.polymorphic_compare_pair(op, lhs, rhs)
|
2015-07-22 23:34:52 +08:00
|
|
|
result_tail = self.current_block
|
|
|
|
|
|
|
|
blocks.append((result, result_head, result_tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
self.current_block = self.add_block()
|
|
|
|
lhs = rhs
|
|
|
|
|
|
|
|
tail = self.current_block
|
|
|
|
phi = self.append(ir.Phi(node.type))
|
2015-07-22 23:34:52 +08:00
|
|
|
for ((result, result_head, result_tail), (next_result_head, next_result_tail)) in \
|
|
|
|
zip(blocks, [(h,t) for (v,h,t) in blocks[1:]] + [(tail, tail)]):
|
|
|
|
phi.add_incoming(result, result_tail)
|
|
|
|
if next_result_head != tail:
|
|
|
|
result_tail.append(ir.BranchIf(result, next_result_head, tail))
|
2015-07-22 07:58:59 +08:00
|
|
|
else:
|
2015-07-22 23:34:52 +08:00
|
|
|
result_tail.append(ir.Branch(tail))
|
2015-07-18 02:29:06 +08:00
|
|
|
return phi
|
|
|
|
|
2015-07-25 10:37:37 +08:00
|
|
|
# Keep this function with builtins.TException.attributes.
|
|
|
|
def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None):
|
|
|
|
attributes = [
|
|
|
|
ir.Constant(typ.find().name, ir.TExceptionTypeInfo()), # typeinfo
|
|
|
|
ir.Constant("<not thrown>", builtins.TStr()), # file
|
|
|
|
ir.Constant(0, builtins.TInt(types.TValue(32))), # line
|
|
|
|
ir.Constant(0, builtins.TInt(types.TValue(32))), # column
|
2015-08-08 21:01:08 +08:00
|
|
|
ir.Constant("<not thrown>", builtins.TStr()), # function
|
2015-07-25 10:37:37 +08:00
|
|
|
]
|
|
|
|
|
|
|
|
if message is None:
|
|
|
|
attributes.append(ir.Constant(typ.find().name, builtins.TStr()))
|
|
|
|
else:
|
|
|
|
attributes.append(message) # message
|
|
|
|
|
|
|
|
param_type = builtins.TInt(types.TValue(64))
|
|
|
|
for param in [param0, param1, param2]:
|
|
|
|
if param is None:
|
|
|
|
attributes.append(ir.Constant(0, builtins.TInt(types.TValue(64))))
|
|
|
|
else:
|
|
|
|
if param.type != param_type:
|
|
|
|
param = self.append(ir.Coerce(param, param_type))
|
|
|
|
attributes.append(param) # paramN, N=0:2
|
|
|
|
|
|
|
|
return self.append(ir.Alloc(attributes, typ))
|
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
def visit_builtin_call(self, node):
|
|
|
|
# A builtin by any other name... Ignore node.func, just use the type.
|
|
|
|
typ = node.func.type
|
|
|
|
if types.is_builtin(typ, "bool"):
|
|
|
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
|
|
|
return ir.Constant(False, builtins.TBool())
|
|
|
|
elif len(node.args) == 1 and len(node.keywords) == 0:
|
|
|
|
arg = self.visit(node.args[0])
|
2015-07-23 00:09:14 +08:00
|
|
|
return self.coerce_to_bool(arg)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif types.is_builtin(typ, "int"):
|
|
|
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
|
|
|
return ir.Constant(0, node.type)
|
|
|
|
elif len(node.args) == 1 and \
|
|
|
|
(len(node.keywords) == 0 or \
|
|
|
|
len(node.keywords) == 1 and node.keywords[0].arg == 'width'):
|
|
|
|
# The width argument is purely type-level
|
|
|
|
arg = self.visit(node.args[0])
|
|
|
|
return self.append(ir.Coerce(arg, node.type))
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif types.is_builtin(typ, "float"):
|
|
|
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
|
|
|
return ir.Constant(0.0, builtins.TFloat())
|
|
|
|
elif len(node.args) == 1 and len(node.keywords) == 0:
|
|
|
|
arg = self.visit(node.args[0])
|
|
|
|
return self.append(ir.Coerce(arg, node.type))
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif types.is_builtin(typ, "list"):
|
|
|
|
if len(node.args) == 0 and len(node.keywords) == 0:
|
|
|
|
length = ir.Constant(0, builtins.TInt(types.TValue(32)))
|
2015-07-22 23:34:52 +08:00
|
|
|
return self.append(ir.Alloc([length], node.type))
|
2015-07-18 02:29:06 +08:00
|
|
|
elif len(node.args) == 1 and len(node.keywords) == 0:
|
|
|
|
arg = self.visit(node.args[0])
|
2015-07-22 03:32:10 +08:00
|
|
|
length = self.iterable_len(arg)
|
2015-07-18 02:29:06 +08:00
|
|
|
result = self.append(ir.Alloc([length], node.type))
|
|
|
|
|
|
|
|
def body_gen(index):
|
2015-07-22 03:32:10 +08:00
|
|
|
elt = self.iterable_get(arg, index)
|
2015-07-18 02:29:06 +08:00
|
|
|
self.append(ir.SetElem(result, index, elt))
|
2015-07-19 16:51:53 +08:00
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
|
|
|
ir.Constant(1, length.type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
self._make_loop(ir.Constant(0, length.type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, length)),
|
|
|
|
body_gen)
|
|
|
|
|
|
|
|
return result
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif types.is_builtin(typ, "range"):
|
2015-07-22 23:34:52 +08:00
|
|
|
elt_typ = builtins.get_iterable_elt(node.type)
|
2015-07-18 02:29:06 +08:00
|
|
|
if len(node.args) == 1 and len(node.keywords) == 0:
|
|
|
|
max_arg = self.visit(node.args[0])
|
|
|
|
return self.append(ir.Alloc([
|
|
|
|
ir.Constant(elt_typ.zero(), elt_typ),
|
|
|
|
max_arg,
|
|
|
|
ir.Constant(elt_typ.one(), elt_typ),
|
|
|
|
], node.type))
|
|
|
|
elif len(node.args) == 2 and len(node.keywords) == 0:
|
|
|
|
min_arg = self.visit(node.args[0])
|
|
|
|
max_arg = self.visit(node.args[1])
|
|
|
|
return self.append(ir.Alloc([
|
|
|
|
min_arg,
|
|
|
|
max_arg,
|
|
|
|
ir.Constant(elt_typ.one(), elt_typ),
|
|
|
|
], node.type))
|
|
|
|
elif len(node.args) == 3 and len(node.keywords) == 0:
|
|
|
|
min_arg = self.visit(node.args[0])
|
|
|
|
max_arg = self.visit(node.args[1])
|
|
|
|
step_arg = self.visit(node.args[2])
|
|
|
|
return self.append(ir.Alloc([
|
|
|
|
min_arg,
|
|
|
|
max_arg,
|
|
|
|
step_arg,
|
|
|
|
], node.type))
|
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif types.is_builtin(typ, "len"):
|
|
|
|
if len(node.args) == 1 and len(node.keywords) == 0:
|
|
|
|
arg = self.visit(node.args[0])
|
2015-07-22 03:32:10 +08:00
|
|
|
return self.iterable_len(arg)
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
elif types.is_builtin(typ, "round"):
|
|
|
|
if len(node.args) == 1 and len(node.keywords) == 0:
|
|
|
|
arg = self.visit(node.args[0])
|
2015-07-22 23:34:52 +08:00
|
|
|
return self.append(ir.Builtin("round", [arg], node.type))
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
2015-07-22 03:32:10 +08:00
|
|
|
elif types.is_builtin(typ, "print"):
|
|
|
|
self.polymorphic_print([self.visit(arg) for arg in node.args],
|
|
|
|
separator=" ", suffix="\n")
|
|
|
|
return ir.Constant(None, builtins.TNone())
|
2015-07-18 02:29:06 +08:00
|
|
|
elif types.is_exn_constructor(typ):
|
2015-07-25 10:37:37 +08:00
|
|
|
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
|
2015-07-18 02:29:06 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
|
|
|
def visit_CallT(self, node):
|
|
|
|
if types.is_builtin(node.func.type):
|
|
|
|
return self.visit_builtin_call(node)
|
|
|
|
else:
|
|
|
|
typ = node.func.type.find()
|
|
|
|
func = self.visit(node.func)
|
2015-07-23 08:07:30 +08:00
|
|
|
args = [None] * (len(typ.args) + len(typ.optargs))
|
|
|
|
|
|
|
|
for index, arg_node in enumerate(node.args):
|
|
|
|
arg = self.visit(arg_node)
|
|
|
|
if index < len(typ.args):
|
|
|
|
args[index] = arg
|
|
|
|
else:
|
|
|
|
args[index] = self.append(ir.Alloc([arg], ir.TOption(arg.type)))
|
|
|
|
|
|
|
|
for keyword in node.keywords:
|
|
|
|
arg = self.visit(keyword.value)
|
|
|
|
if keyword.arg in typ.args:
|
|
|
|
for index, arg_name in enumerate(typ.args):
|
|
|
|
if keyword.arg == arg_name:
|
|
|
|
assert args[index] is None
|
|
|
|
args[index] = arg
|
|
|
|
break
|
|
|
|
elif keyword.arg in typ.optargs:
|
|
|
|
for index, optarg_name in enumerate(typ.optargs):
|
2015-07-18 02:29:06 +08:00
|
|
|
if keyword.arg == optarg_name:
|
2015-07-23 08:07:30 +08:00
|
|
|
assert args[len(typ.args) + index] is None
|
|
|
|
args[len(typ.args) + index] = \
|
|
|
|
self.append(ir.Alloc([arg], ir.TOption(arg.type)))
|
2015-07-18 02:29:06 +08:00
|
|
|
break
|
2015-07-23 08:07:30 +08:00
|
|
|
|
|
|
|
for index, optarg_name in enumerate(typ.optargs):
|
|
|
|
if args[len(typ.args) + index] is None:
|
|
|
|
args[len(typ.args) + index] = \
|
|
|
|
self.append(ir.Alloc([], ir.TOption(typ.optargs[optarg_name])))
|
|
|
|
|
|
|
|
assert None not in args
|
2015-07-14 11:44:16 +08:00
|
|
|
|
2015-07-18 02:29:06 +08:00
|
|
|
if self.unwind_target is None:
|
|
|
|
return self.append(ir.Call(func, args))
|
|
|
|
else:
|
|
|
|
after_invoke = self.add_block()
|
|
|
|
invoke = self.append(ir.Invoke(func, args, after_invoke, self.unwind_target))
|
|
|
|
self.current_block = after_invoke
|
|
|
|
return invoke
|
2015-07-22 03:32:10 +08:00
|
|
|
|
2015-07-22 07:58:59 +08:00
|
|
|
def instrument_assert(self, node, value):
|
|
|
|
if self.current_assert_env is not None:
|
|
|
|
if isinstance(value, ir.Constant):
|
|
|
|
return # don't display the values of constants
|
|
|
|
|
|
|
|
if any([algorithm.compare(node, subexpr)
|
|
|
|
for (subexpr, name) in self.current_assert_subexprs]):
|
|
|
|
return # don't display the same subexpression twice
|
|
|
|
|
2015-07-22 23:34:52 +08:00
|
|
|
name = self.current_assert_env.type.add(".subexpr", ir.TOption(node.type))
|
2015-07-22 07:58:59 +08:00
|
|
|
value_opt = self.append(ir.Alloc([value], ir.TOption(node.type)),
|
|
|
|
loc=node.loc)
|
|
|
|
self.append(ir.SetLocal(self.current_assert_env, name, value_opt),
|
|
|
|
loc=node.loc)
|
|
|
|
self.current_assert_subexprs.append((node, name))
|
|
|
|
|
|
|
|
def visit_Assert(self, node):
|
|
|
|
try:
|
|
|
|
assert_env = self.current_assert_env = \
|
|
|
|
self.append(ir.Alloc([], ir.TEnvironment({}), name="assertenv"))
|
|
|
|
assert_subexprs = self.current_assert_subexprs = []
|
|
|
|
init = self.current_block
|
|
|
|
|
|
|
|
prehead = self.current_block = self.add_block()
|
|
|
|
cond = self.visit(node.test)
|
|
|
|
head = self.current_block
|
|
|
|
finally:
|
|
|
|
self.current_assert_env = None
|
|
|
|
self.current_assert_subexprs = None
|
|
|
|
|
|
|
|
for subexpr_node, subexpr_name in assert_subexprs:
|
|
|
|
empty = init.append(ir.Alloc([], ir.TOption(subexpr_node.type)))
|
|
|
|
init.append(ir.SetLocal(assert_env, subexpr_name, empty))
|
|
|
|
init.append(ir.Branch(prehead))
|
|
|
|
|
|
|
|
if_failed = self.current_block = self.add_block()
|
|
|
|
|
|
|
|
if node.msg:
|
|
|
|
explanation = node.msg.s
|
|
|
|
else:
|
|
|
|
explanation = node.loc.source()
|
|
|
|
self.append(ir.Builtin("printf", [
|
|
|
|
ir.Constant("assertion failed at %s: %s\n", builtins.TStr()),
|
|
|
|
ir.Constant(str(node.loc.begin()), builtins.TStr()),
|
|
|
|
ir.Constant(str(explanation), builtins.TStr()),
|
|
|
|
], builtins.TNone()))
|
|
|
|
|
|
|
|
for subexpr_node, subexpr_name in assert_subexprs:
|
|
|
|
subexpr_head = self.current_block
|
|
|
|
subexpr_value_opt = self.append(ir.GetLocal(assert_env, subexpr_name))
|
|
|
|
subexpr_cond = self.append(ir.Builtin("is_some", [subexpr_value_opt],
|
|
|
|
builtins.TBool()))
|
|
|
|
|
|
|
|
subexpr_body = self.current_block = self.add_block()
|
|
|
|
self.append(ir.Builtin("printf", [
|
|
|
|
ir.Constant(" (%s) = ", builtins.TStr()),
|
|
|
|
ir.Constant(subexpr_node.loc.source(), builtins.TStr())
|
|
|
|
], builtins.TNone()))
|
|
|
|
subexpr_value = self.append(ir.Builtin("unwrap", [subexpr_value_opt],
|
|
|
|
subexpr_node.type))
|
|
|
|
self.polymorphic_print([subexpr_value], separator="", suffix="\n")
|
|
|
|
subexpr_postbody = self.current_block
|
|
|
|
|
|
|
|
subexpr_tail = self.current_block = self.add_block()
|
|
|
|
self.append(ir.Branch(subexpr_tail), block=subexpr_postbody)
|
|
|
|
self.append(ir.BranchIf(subexpr_cond, subexpr_body, subexpr_tail), block=subexpr_head)
|
|
|
|
|
|
|
|
self.append(ir.Builtin("abort", [], builtins.TNone()))
|
|
|
|
self.append(ir.Unreachable())
|
|
|
|
|
|
|
|
tail = self.current_block = self.add_block()
|
|
|
|
self.append(ir.BranchIf(cond, tail, if_failed), block=head)
|
|
|
|
|
2015-08-10 01:27:04 +08:00
|
|
|
def polymorphic_print(self, values, separator, suffix="", as_repr=False):
|
2015-07-22 03:32:10 +08:00
|
|
|
format_string = ""
|
|
|
|
args = []
|
|
|
|
def flush():
|
|
|
|
nonlocal format_string, args
|
|
|
|
if format_string != "":
|
|
|
|
format_arg = [ir.Constant(format_string, builtins.TStr())]
|
|
|
|
self.append(ir.Builtin("printf", format_arg + args, builtins.TNone()))
|
|
|
|
format_string = ""
|
|
|
|
args = []
|
|
|
|
|
|
|
|
for value in values:
|
|
|
|
if format_string != "":
|
|
|
|
format_string += separator
|
|
|
|
|
|
|
|
if types.is_tuple(value.type):
|
|
|
|
format_string += "("; flush()
|
|
|
|
self.polymorphic_print([self.append(ir.GetAttr(value, index))
|
|
|
|
for index in range(len(value.type.elts))],
|
2015-08-10 01:27:04 +08:00
|
|
|
separator=", ", as_repr=True)
|
2015-07-22 23:34:52 +08:00
|
|
|
if len(value.type.elts) == 1:
|
|
|
|
format_string += ",)"
|
|
|
|
else:
|
|
|
|
format_string += ")"
|
2015-07-22 03:32:10 +08:00
|
|
|
elif types.is_function(value.type):
|
|
|
|
format_string += "<closure %p(%p)>"
|
|
|
|
# We're relying on the internal layout of the closure here.
|
|
|
|
args.append(self.append(ir.GetAttr(value, 0)))
|
|
|
|
args.append(self.append(ir.GetAttr(value, 1)))
|
|
|
|
elif builtins.is_none(value.type):
|
|
|
|
format_string += "None"
|
|
|
|
elif builtins.is_bool(value.type):
|
|
|
|
format_string += "%s"
|
|
|
|
args.append(self.append(ir.Select(value,
|
|
|
|
ir.Constant("True", builtins.TStr()),
|
|
|
|
ir.Constant("False", builtins.TStr()))))
|
|
|
|
elif builtins.is_int(value.type):
|
2015-07-22 08:05:15 +08:00
|
|
|
width = builtins.get_int_width(value.type)
|
|
|
|
if width <= 32:
|
2015-07-22 23:34:52 +08:00
|
|
|
format_string += "%d"
|
2015-07-22 08:05:15 +08:00
|
|
|
elif width <= 64:
|
|
|
|
format_string += "%lld"
|
|
|
|
else:
|
|
|
|
assert False
|
2015-07-22 03:32:10 +08:00
|
|
|
args.append(value)
|
|
|
|
elif builtins.is_float(value.type):
|
|
|
|
format_string += "%g"
|
|
|
|
args.append(value)
|
|
|
|
elif builtins.is_str(value.type):
|
2015-08-10 01:27:04 +08:00
|
|
|
if as_repr:
|
|
|
|
format_string += "\"%s\""
|
|
|
|
else:
|
|
|
|
format_string += "%s"
|
2015-07-22 03:32:10 +08:00
|
|
|
args.append(value)
|
|
|
|
elif builtins.is_list(value.type):
|
|
|
|
format_string += "["; flush()
|
|
|
|
|
|
|
|
length = self.iterable_len(value)
|
|
|
|
last = self.append(ir.Arith(ast.Sub(loc=None), length, ir.Constant(1, length.type)))
|
|
|
|
def body_gen(index):
|
|
|
|
elt = self.iterable_get(value, index)
|
2015-08-10 01:27:04 +08:00
|
|
|
self.polymorphic_print([elt], separator="", as_repr=True)
|
2015-07-22 03:32:10 +08:00
|
|
|
is_last = self.append(ir.Compare(ast.Lt(loc=None), index, last))
|
|
|
|
head = self.current_block
|
|
|
|
|
|
|
|
if_last = self.current_block = self.add_block()
|
|
|
|
self.append(ir.Builtin("printf",
|
|
|
|
[ir.Constant(", ", builtins.TStr())], builtins.TNone()))
|
|
|
|
|
|
|
|
tail = self.current_block = self.add_block()
|
|
|
|
if_last.append(ir.Branch(tail))
|
|
|
|
head.append(ir.BranchIf(is_last, if_last, tail))
|
|
|
|
|
|
|
|
return self.append(ir.Arith(ast.Add(loc=None), index,
|
|
|
|
ir.Constant(1, length.type)))
|
|
|
|
self._make_loop(ir.Constant(0, length.type),
|
|
|
|
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, length)),
|
|
|
|
body_gen)
|
|
|
|
|
|
|
|
format_string += "]"
|
|
|
|
elif builtins.is_range(value.type):
|
|
|
|
format_string += "range("; flush()
|
|
|
|
|
|
|
|
start = self.append(ir.GetAttr(value, "start"))
|
|
|
|
stop = self.append(ir.GetAttr(value, "stop"))
|
|
|
|
step = self.append(ir.GetAttr(value, "step"))
|
|
|
|
self.polymorphic_print([start, stop, step], separator=", ")
|
|
|
|
|
|
|
|
format_string += ")"
|
|
|
|
elif builtins.is_exception(value.type):
|
2015-07-25 10:37:37 +08:00
|
|
|
name = self.append(ir.GetAttr(value, "__name__"))
|
|
|
|
message = self.append(ir.GetAttr(value, "__message__"))
|
|
|
|
param1 = self.append(ir.GetAttr(value, "__param0__"))
|
|
|
|
param2 = self.append(ir.GetAttr(value, "__param1__"))
|
|
|
|
param3 = self.append(ir.GetAttr(value, "__param2__"))
|
|
|
|
|
|
|
|
format_string += "%s(%s, %lld, %lld, %lld)"
|
|
|
|
args += [name, message, param1, param2, param3]
|
2015-07-22 03:32:10 +08:00
|
|
|
else:
|
|
|
|
assert False
|
|
|
|
|
|
|
|
format_string += suffix
|
|
|
|
flush()
|