forked from M-Labs/artiq
Add complete IR generator.
This commit is contained in:
parent
f28549a11a
commit
e96bc3c36c
@ -3,6 +3,8 @@ The :mod:`ir` module contains the intermediate representation
|
|||||||
of the ARTIQ compiler.
|
of the ARTIQ compiler.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
from pythonparser import ast
|
||||||
from . import types, builtins
|
from . import types, builtins
|
||||||
|
|
||||||
# Generic SSA IR classes
|
# Generic SSA IR classes
|
||||||
@ -13,13 +15,13 @@ def escape_name(name):
|
|||||||
else:
|
else:
|
||||||
return "\"{}\"".format(name.replace("\"", "\\\""))
|
return "\"{}\"".format(name.replace("\"", "\\\""))
|
||||||
|
|
||||||
class TSSABasicBlock(types.TMono):
|
class TBasicBlock(types.TMono):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("ssa.basic_block")
|
super().__init__("label")
|
||||||
|
|
||||||
class TSSAOption(types.TMono):
|
class TOption(types.TMono):
|
||||||
def __init__(self, inner):
|
def __init__(self, inner):
|
||||||
super().__init__("ssa.option", {"inner": inner})
|
super().__init__("option", {"inner": inner})
|
||||||
|
|
||||||
class Value:
|
class Value:
|
||||||
"""
|
"""
|
||||||
@ -30,7 +32,7 @@ class Value:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, typ):
|
def __init__(self, typ):
|
||||||
self.uses, self.type = set(), typ
|
self.uses, self.type = set(), typ.find()
|
||||||
|
|
||||||
def replace_all_uses_with(self, value):
|
def replace_all_uses_with(self, value):
|
||||||
for user in self.uses:
|
for user in self.uses:
|
||||||
@ -40,7 +42,7 @@ class Constant(Value):
|
|||||||
"""
|
"""
|
||||||
A constant value.
|
A constant value.
|
||||||
|
|
||||||
:ivar value: (None, True or False) value
|
:ivar value: (Python object) value
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, value, typ):
|
def __init__(self, value, typ):
|
||||||
@ -127,6 +129,8 @@ class Instruction(User):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, operands, typ, name=""):
|
def __init__(self, operands, typ, name=""):
|
||||||
|
assert isinstance(operands, list)
|
||||||
|
assert isinstance(typ, types.Type)
|
||||||
super().__init__(operands, typ, name)
|
super().__init__(operands, typ, name)
|
||||||
self.basic_block = None
|
self.basic_block = None
|
||||||
|
|
||||||
@ -160,6 +164,9 @@ class Instruction(User):
|
|||||||
else:
|
else:
|
||||||
self.erase()
|
self.erase()
|
||||||
|
|
||||||
|
def _operands_as_string(self):
|
||||||
|
return ", ".join([operand.as_operand() for operand in self.operands])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if builtins.is_none(self.type):
|
if builtins.is_none(self.type):
|
||||||
prefix = ""
|
prefix = ""
|
||||||
@ -168,8 +175,7 @@ class Instruction(User):
|
|||||||
types.TypePrinter().name(self.type))
|
types.TypePrinter().name(self.type))
|
||||||
|
|
||||||
if any(self.operands):
|
if any(self.operands):
|
||||||
return "{}{} {}".format(prefix, self.opcode(),
|
return "{}{} {}".format(prefix, self.opcode(), self._operands_as_string())
|
||||||
", ".join([operand.as_operand() for operand in self.operands]))
|
|
||||||
else:
|
else:
|
||||||
return "{}{}".format(prefix, self.opcode())
|
return "{}{}".format(prefix, self.opcode())
|
||||||
|
|
||||||
@ -182,7 +188,7 @@ class Phi(Instruction):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, typ, name=""):
|
def __init__(self, typ, name=""):
|
||||||
super().__init__(typ, name)
|
super().__init__([], typ, name)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "phi"
|
return "phi"
|
||||||
@ -217,9 +223,11 @@ class Phi(Instruction):
|
|||||||
types.TypePrinter().name(self.type))
|
types.TypePrinter().name(self.type))
|
||||||
|
|
||||||
if any(self.operands):
|
if any(self.operands):
|
||||||
operand_list = ["%{} => %{}".format(escape_name(block.name), escape_name(value.name))
|
operand_list = ["%{} => {}".format(escape_name(block.name), value.as_operand())
|
||||||
for operand in self.operands]
|
for value, block in self.incoming()]
|
||||||
return "{}{} [{}]".format(prefix, self.opcode(), ", ".join(operand_list))
|
return "{}{} [{}]".format(prefix, self.opcode(), ", ".join(operand_list))
|
||||||
|
else:
|
||||||
|
return "{}{} [???]".format(prefix, self.opcode())
|
||||||
|
|
||||||
class Terminator(Instruction):
|
class Terminator(Instruction):
|
||||||
"""
|
"""
|
||||||
@ -237,7 +245,7 @@ class BasicBlock(NamedValue):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, instructions, name=""):
|
def __init__(self, instructions, name=""):
|
||||||
super().__init__(TSSABasicBlock(), name)
|
super().__init__(TBasicBlock(), name)
|
||||||
self.instructions = []
|
self.instructions = []
|
||||||
self.set_instructions(instructions)
|
self.set_instructions(instructions)
|
||||||
|
|
||||||
@ -253,10 +261,13 @@ class BasicBlock(NamedValue):
|
|||||||
self.function.remove(self)
|
self.function.remove(self)
|
||||||
|
|
||||||
def prepend(self, insn):
|
def prepend(self, insn):
|
||||||
|
assert isinstance(insn, Instruction)
|
||||||
insn.set_basic_block(self)
|
insn.set_basic_block(self)
|
||||||
self.instructions.insert(0, insn)
|
self.instructions.insert(0, insn)
|
||||||
|
return insn
|
||||||
|
|
||||||
def append(self, insn):
|
def append(self, insn):
|
||||||
|
assert isinstance(insn, Instruction)
|
||||||
insn.set_basic_block(self)
|
insn.set_basic_block(self)
|
||||||
self.instructions.append(insn)
|
self.instructions.append(insn)
|
||||||
return insn
|
return insn
|
||||||
@ -265,11 +276,13 @@ class BasicBlock(NamedValue):
|
|||||||
return self.instructions.index(insn)
|
return self.instructions.index(insn)
|
||||||
|
|
||||||
def insert(self, before, insn):
|
def insert(self, before, insn):
|
||||||
|
assert isinstance(insn, Instruction)
|
||||||
insn.set_basic_block(self)
|
insn.set_basic_block(self)
|
||||||
self.instructions.insert(self.index(before), insn)
|
self.instructions.insert(self.index(before), insn)
|
||||||
return insn
|
return insn
|
||||||
|
|
||||||
def remove(self, insn):
|
def remove(self, insn):
|
||||||
|
assert insn in self.instructions
|
||||||
insn._detach()
|
insn._detach()
|
||||||
self.instructions.remove(insn)
|
self.instructions.remove(insn)
|
||||||
return insn
|
return insn
|
||||||
@ -290,10 +303,13 @@ class BasicBlock(NamedValue):
|
|||||||
|
|
||||||
def predecessors(self):
|
def predecessors(self):
|
||||||
assert self.function is not None
|
assert self.function is not None
|
||||||
self.function.predecessors_of(self)
|
return self.function.predecessors_of(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
lines = ["{}:".format(escape_name(self.name))]
|
lines = ["{}:".format(escape_name(self.name))]
|
||||||
|
if self.function is not None:
|
||||||
|
lines[0] += " ; predecessors: {}".format(
|
||||||
|
", ".join([escape_name(pred.name) for pred in self.predecessors()]))
|
||||||
for insn in self.instructions:
|
for insn in self.instructions:
|
||||||
lines.append(" " + str(insn))
|
lines.append(" " + str(insn))
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
@ -312,10 +328,9 @@ class Function(Value):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, typ, name, arguments):
|
def __init__(self, typ, name, arguments):
|
||||||
self.type, self.name = typ, name
|
super().__init__(typ)
|
||||||
self.arguments = []
|
self.name = name
|
||||||
self.basic_blocks = []
|
self.names, self.arguments, self.basic_blocks = set(), [], []
|
||||||
self.names = set()
|
|
||||||
self.set_arguments(arguments)
|
self.set_arguments(arguments)
|
||||||
|
|
||||||
def _remove_name(self, name):
|
def _remove_name(self, name):
|
||||||
@ -325,7 +340,7 @@ class Function(Value):
|
|||||||
name, counter = base_name, 1
|
name, counter = base_name, 1
|
||||||
while name in self.names or name == "":
|
while name in self.names or name == "":
|
||||||
if base_name == "":
|
if base_name == "":
|
||||||
name = str(counter)
|
name = "v.{}".format(str(counter))
|
||||||
else:
|
else:
|
||||||
name = "{}.{}".format(name, counter)
|
name = "{}.{}".format(name, counter)
|
||||||
counter += 1
|
counter += 1
|
||||||
@ -346,10 +361,11 @@ class Function(Value):
|
|||||||
|
|
||||||
def remove(self, basic_block):
|
def remove(self, basic_block):
|
||||||
basic_block._detach()
|
basic_block._detach()
|
||||||
self.basic_block.remove(basic_block)
|
self.basic_blocks.remove(basic_block)
|
||||||
|
|
||||||
def predecessors_of(self, successor):
|
def predecessors_of(self, successor):
|
||||||
return [block for block in self.basic_blocks if successor in block.successors()]
|
return [block for block in self.basic_blocks
|
||||||
|
if block.is_terminated() and successor in block.successors()]
|
||||||
|
|
||||||
def as_operand(self):
|
def as_operand(self):
|
||||||
return "{} @{}".format(types.TypePrinter().name(self.type),
|
return "{} @{}".format(types.TypePrinter().name(self.type),
|
||||||
@ -369,6 +385,412 @@ class Function(Value):
|
|||||||
|
|
||||||
# Python-specific SSA IR classes
|
# Python-specific SSA IR classes
|
||||||
|
|
||||||
|
class TEnvironment(types.TMono):
|
||||||
|
def __init__(self, vars, outer=None):
|
||||||
|
if outer is not None:
|
||||||
|
assert isinstance(outer, TEnvironment)
|
||||||
|
env = OrderedDict({".outer": outer})
|
||||||
|
env.update(vars)
|
||||||
|
else:
|
||||||
|
env = OrderedDict(vars)
|
||||||
|
|
||||||
|
super().__init__("environment", env)
|
||||||
|
|
||||||
|
def type_of(self, name):
|
||||||
|
if name in self.params:
|
||||||
|
return self.params[name].find()
|
||||||
|
elif ".outer" in self.params:
|
||||||
|
return self.params[".outer"].type_of(name)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
"""
|
||||||
|
Add a new binding, ensuring hygiene.
|
||||||
|
|
||||||
|
:returns: (string) mangled name
|
||||||
|
"""
|
||||||
|
def add(self, base_name, typ):
|
||||||
|
name, counter = base_name, 1
|
||||||
|
while name in self.params or name == "":
|
||||||
|
if base_name == "":
|
||||||
|
name = str(counter)
|
||||||
|
else:
|
||||||
|
name = "{}.{}".format(name, counter)
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
self.params[name] = typ.find()
|
||||||
|
return name
|
||||||
|
|
||||||
|
def is_environment(typ):
|
||||||
|
return isinstance(typ, TEnvironment)
|
||||||
|
|
||||||
|
class EnvironmentArgument(NamedValue):
|
||||||
|
"""
|
||||||
|
A function argument specifying an outer environment.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def as_operand(self):
|
||||||
|
return "environment(...) %{}".format(escape_name(self.name))
|
||||||
|
|
||||||
|
class Alloc(Instruction):
|
||||||
|
"""
|
||||||
|
An instruction that allocates an object specified by
|
||||||
|
the type of the intsruction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, operands, typ, name=""):
|
||||||
|
for operand in operands: assert isinstance(operand, Value)
|
||||||
|
super().__init__(operands, typ, name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "alloc"
|
||||||
|
|
||||||
|
def as_operand(self):
|
||||||
|
if is_environment(self.type):
|
||||||
|
# Only show full environment in the instruction itself
|
||||||
|
return "%{}".format(escape_name(self.name))
|
||||||
|
else:
|
||||||
|
return super().as_operand()
|
||||||
|
|
||||||
|
class GetLocal(Instruction):
|
||||||
|
"""
|
||||||
|
An intruction that loads a local variable from an environment,
|
||||||
|
possibly going through multiple levels of indirection.
|
||||||
|
|
||||||
|
:ivar var_name: (string) variable name
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param env: (:class:`Value`) local environment
|
||||||
|
:param var_name: (string) local variable name
|
||||||
|
"""
|
||||||
|
def __init__(self, env, var_name, name=""):
|
||||||
|
assert isinstance(env, Alloc)
|
||||||
|
assert isinstance(env.type, TEnvironment)
|
||||||
|
assert isinstance(var_name, str)
|
||||||
|
super().__init__([env], env.type.type_of(var_name), name)
|
||||||
|
self.var_name = var_name
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "getlocal({})".format(self.var_name)
|
||||||
|
|
||||||
|
def get_env(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
class SetLocal(Instruction):
|
||||||
|
"""
|
||||||
|
An intruction that stores a local variable into an environment,
|
||||||
|
possibly going through multiple levels of indirection.
|
||||||
|
|
||||||
|
:ivar var_name: (string) variable name
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param env: (:class:`Value`) local environment
|
||||||
|
:param var_name: (string) local variable name
|
||||||
|
:param value: (:class:`Value`) value to assign
|
||||||
|
"""
|
||||||
|
def __init__(self, env, var_name, value, name=""):
|
||||||
|
assert isinstance(env, Alloc)
|
||||||
|
assert isinstance(env.type, TEnvironment)
|
||||||
|
assert isinstance(var_name, str)
|
||||||
|
assert env.type.type_of(var_name) == value.type
|
||||||
|
assert isinstance(value, Value)
|
||||||
|
super().__init__([env, value], builtins.TNone(), name)
|
||||||
|
self.var_name = var_name
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "setlocal({})".format(self.var_name)
|
||||||
|
|
||||||
|
def get_env(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
class GetAttr(Instruction):
|
||||||
|
"""
|
||||||
|
An intruction that loads an attribute from an object,
|
||||||
|
or extracts a tuple element.
|
||||||
|
|
||||||
|
:ivar attr: (string) variable name
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param obj: (:class:`Value`) object or tuple
|
||||||
|
:param attr: (string or integer) attribute or index
|
||||||
|
"""
|
||||||
|
def __init__(self, obj, attr, name=""):
|
||||||
|
assert isinstance(obj, Value)
|
||||||
|
assert isinstance(attr, (str, int))
|
||||||
|
if isinstance(attr, int):
|
||||||
|
assert isinstance(obj.type, types.TTuple)
|
||||||
|
typ = obj.type.elts[attr]
|
||||||
|
else:
|
||||||
|
typ = obj.type.attributes[attr]
|
||||||
|
super().__init__([obj], typ, name)
|
||||||
|
self.attr = attr
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "getattr({})".format(repr(self.attr))
|
||||||
|
|
||||||
|
def get_env(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
class SetAttr(Instruction):
|
||||||
|
"""
|
||||||
|
An intruction that stores an attribute to an object.
|
||||||
|
|
||||||
|
:ivar attr: (string) variable name
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param obj: (:class:`Value`) object or tuple
|
||||||
|
:param attr: (string or integer) attribute
|
||||||
|
:param value: (:class:`Value`) value to store
|
||||||
|
"""
|
||||||
|
def __init__(self, obj, attr, value, name=""):
|
||||||
|
assert isinstance(obj, Value)
|
||||||
|
assert isinstance(attr, (str, int))
|
||||||
|
assert isinstance(value, Value)
|
||||||
|
if isinstance(attr, int):
|
||||||
|
assert value.type == obj.type.elts[attr]
|
||||||
|
else:
|
||||||
|
assert value.type == obj.type.attributes[attr]
|
||||||
|
super().__init__([obj, value], typ, name)
|
||||||
|
self.attr = attr
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "setattr({})".format(repr(self.attr))
|
||||||
|
|
||||||
|
def get_env(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
class GetElem(Instruction):
|
||||||
|
"""
|
||||||
|
An intruction that loads an element from a list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param lst: (:class:`Value`) list
|
||||||
|
:param index: (:class:`Value`) index
|
||||||
|
"""
|
||||||
|
def __init__(self, lst, index, name=""):
|
||||||
|
assert isinstance(lst, Value)
|
||||||
|
assert isinstance(index, Value)
|
||||||
|
super().__init__([lst, index], builtins.get_iterable_elt(lst.type), name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "getelem"
|
||||||
|
|
||||||
|
def get_list(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_index(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
class SetElem(Instruction):
|
||||||
|
"""
|
||||||
|
An intruction that stores an element into a list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param lst: (:class:`Value`) list
|
||||||
|
:param index: (:class:`Value`) index
|
||||||
|
:param value: (:class:`Value`) value to store
|
||||||
|
"""
|
||||||
|
def __init__(self, lst, index, value, name=""):
|
||||||
|
assert isinstance(lst, Value)
|
||||||
|
assert isinstance(index, Value)
|
||||||
|
assert isinstance(value, Value)
|
||||||
|
assert builtins.get_iterable_elt(lst.type) == value.type.find()
|
||||||
|
super().__init__([lst, index, value], builtins.TNone(), name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "setelem"
|
||||||
|
|
||||||
|
def get_list(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_index(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.operands[2]
|
||||||
|
|
||||||
|
class UnaryOp(Instruction):
|
||||||
|
"""
|
||||||
|
An unary operation on numbers.
|
||||||
|
|
||||||
|
:ivar op: (:class:`pythonparser.ast.unaryop`) operation
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param op: (:class:`pythonparser.ast.unaryop`) operation
|
||||||
|
:param operand: (:class:`Value`) operand
|
||||||
|
"""
|
||||||
|
def __init__(self, op, operand, name=""):
|
||||||
|
assert isinstance(op, ast.unaryop)
|
||||||
|
assert isinstance(operand, Value)
|
||||||
|
super().__init__([operand], operand.type, name)
|
||||||
|
self.op = op
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "unaryop({})".format(type(self.op).__name__)
|
||||||
|
|
||||||
|
def get_operand(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
class Coerce(Instruction):
|
||||||
|
"""
|
||||||
|
A coercion operation for numbers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, value, typ, name=""):
|
||||||
|
assert isinstance(value, Value)
|
||||||
|
assert isinstance(typ, types.Type)
|
||||||
|
super().__init__([value], typ, name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "coerce"
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
class BinaryOp(Instruction):
|
||||||
|
"""
|
||||||
|
A binary operation on numbers.
|
||||||
|
|
||||||
|
:ivar op: (:class:`pythonparser.ast.unaryop`) operation
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param op: (:class:`pythonparser.ast.operator`) operation
|
||||||
|
:param lhs: (:class:`Value`) left-hand operand
|
||||||
|
:param rhs: (:class:`Value`) right-hand operand
|
||||||
|
"""
|
||||||
|
def __init__(self, op, lhs, rhs, name=""):
|
||||||
|
assert isinstance(op, ast.operator)
|
||||||
|
assert isinstance(lhs, Value)
|
||||||
|
assert isinstance(rhs, Value)
|
||||||
|
assert lhs.type == rhs.type
|
||||||
|
super().__init__([lhs, rhs], lhs.type, name)
|
||||||
|
self.op = op
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "binaryop({})".format(type(self.op).__name__)
|
||||||
|
|
||||||
|
def get_lhs(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_rhs(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
class Compare(Instruction):
|
||||||
|
"""
|
||||||
|
A comparison operation on numbers.
|
||||||
|
|
||||||
|
:ivar op: (:class:`pythonparser.ast.cmpop`) operation
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param op: (:class:`pythonparser.ast.cmpop`) operation
|
||||||
|
:param lhs: (:class:`Value`) left-hand operand
|
||||||
|
:param rhs: (:class:`Value`) right-hand operand
|
||||||
|
"""
|
||||||
|
def __init__(self, op, lhs, rhs, name=""):
|
||||||
|
assert isinstance(op, ast.cmpop)
|
||||||
|
assert isinstance(lhs, Value)
|
||||||
|
assert isinstance(rhs, Value)
|
||||||
|
assert lhs.type == rhs.type
|
||||||
|
super().__init__([lhs, rhs], builtins.TBool(), name)
|
||||||
|
self.op = op
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "compare({})".format(type(self.op).__name__)
|
||||||
|
|
||||||
|
def get_lhs(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_rhs(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
class Builtin(Instruction):
|
||||||
|
"""
|
||||||
|
A builtin operation. Similar to a function call that
|
||||||
|
never raises.
|
||||||
|
|
||||||
|
:ivar op: (string) operation name
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param op: (string) operation name
|
||||||
|
"""
|
||||||
|
def __init__(self, op, operands, typ, name=""):
|
||||||
|
assert isinstance(op, str)
|
||||||
|
for operand in operands: assert isinstance(operand, Value)
|
||||||
|
super().__init__(operands, typ, name)
|
||||||
|
self.op = op
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "builtin({})".format(self.op)
|
||||||
|
|
||||||
|
class Call(Instruction):
|
||||||
|
"""
|
||||||
|
A function call operation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param func: (:class:`Value`) function to call
|
||||||
|
:param args: (list of :class:`Value`) function arguments
|
||||||
|
"""
|
||||||
|
def __init__(self, func, args, name=""):
|
||||||
|
print(func)
|
||||||
|
assert isinstance(func, Value)
|
||||||
|
for arg in args: assert isinstance(arg, Value)
|
||||||
|
super().__init__([func] + args, func.type.ret, name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "call"
|
||||||
|
|
||||||
|
def get_function(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_arguments(self):
|
||||||
|
return self.operands[1:]
|
||||||
|
|
||||||
|
class Select(Instruction):
|
||||||
|
"""
|
||||||
|
A conditional select instruction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param cond: (:class:`Value`) select condition
|
||||||
|
:param if_true: (:class:`Value`) value of select if condition is truthful
|
||||||
|
:param if_false: (:class:`Value`) value of select if condition is falseful
|
||||||
|
"""
|
||||||
|
def __init__(self, cond, if_true, if_false, name=""):
|
||||||
|
assert isinstance(cond, Value)
|
||||||
|
assert isinstance(if_true, Value)
|
||||||
|
assert isinstance(if_false, Value)
|
||||||
|
assert if_true.type == if_false.type
|
||||||
|
super().__init__([cond, if_true, if_false], if_true.type, name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "select"
|
||||||
|
|
||||||
|
def get_condition(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_if_true(self):
|
||||||
|
return self.operands[1]
|
||||||
|
|
||||||
|
def get_if_false(self):
|
||||||
|
return self.operands[2]
|
||||||
|
|
||||||
class Branch(Terminator):
|
class Branch(Terminator):
|
||||||
"""
|
"""
|
||||||
An unconditional branch instruction.
|
An unconditional branch instruction.
|
||||||
@ -378,12 +800,13 @@ class Branch(Terminator):
|
|||||||
:param target: (:class:`BasicBlock`) branch target
|
:param target: (:class:`BasicBlock`) branch target
|
||||||
"""
|
"""
|
||||||
def __init__(self, target, name=""):
|
def __init__(self, target, name=""):
|
||||||
|
assert isinstance(target, BasicBlock)
|
||||||
super().__init__([target], builtins.TNone(), name)
|
super().__init__([target], builtins.TNone(), name)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "branch"
|
return "branch"
|
||||||
|
|
||||||
def target(self):
|
def get_target(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
class BranchIf(Terminator):
|
class BranchIf(Terminator):
|
||||||
@ -393,24 +816,57 @@ class BranchIf(Terminator):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
:param cond: (:class:`Value`) branch condition
|
:param cond: (:class:`Value`) branch condition
|
||||||
:param if_true: (:class:`BasicBlock`) branch target if expression is truthful
|
:param if_true: (:class:`BasicBlock`) branch target if condition is truthful
|
||||||
:param if_false: (:class:`BasicBlock`) branch target if expression is falseful
|
:param if_false: (:class:`BasicBlock`) branch target if condition is falseful
|
||||||
"""
|
"""
|
||||||
def __init__(self, cond, if_true, if_false, name=""):
|
def __init__(self, cond, if_true, if_false, name=""):
|
||||||
|
assert isinstance(cond, Value)
|
||||||
|
assert isinstance(if_true, BasicBlock)
|
||||||
|
assert isinstance(if_false, BasicBlock)
|
||||||
super().__init__([cond, if_true, if_false], builtins.TNone(), name)
|
super().__init__([cond, if_true, if_false], builtins.TNone(), name)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "branch_if"
|
return "branchif"
|
||||||
|
|
||||||
def condition(self):
|
def get_condition(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
def if_true(self):
|
def get_if_true(self):
|
||||||
return self.operands[1]
|
return self.operands[1]
|
||||||
|
|
||||||
def if_false(self):
|
def get_if_false(self):
|
||||||
return self.operands[2]
|
return self.operands[2]
|
||||||
|
|
||||||
|
class IndirectBranch(Terminator):
|
||||||
|
"""
|
||||||
|
An indirect branch instruction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param target: (:class:`Value`) branch target
|
||||||
|
:param destinations: (list of :class:`BasicBlock`) all possible values of `target`
|
||||||
|
"""
|
||||||
|
def __init__(self, target, destinations, name=""):
|
||||||
|
assert isinstance(target, Value)
|
||||||
|
assert all([isinstance(dest, BasicBlock) for dest in destinations])
|
||||||
|
super().__init__([target] + destinations, builtins.TNone(), name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "indirectbranch"
|
||||||
|
|
||||||
|
def get_target(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_destinations(self):
|
||||||
|
return self.operands[1:]
|
||||||
|
|
||||||
|
def add_destination(self, destination):
|
||||||
|
self.operands.append(destination)
|
||||||
|
|
||||||
|
def _operands_as_string(self):
|
||||||
|
return "{}, [{}]".format(self.operands[0].as_operand(),
|
||||||
|
", ".join([dest.as_operand() for dest in self.operands[1:]]))
|
||||||
|
|
||||||
class Return(Terminator):
|
class Return(Terminator):
|
||||||
"""
|
"""
|
||||||
A return instruction.
|
A return instruction.
|
||||||
@ -420,28 +876,113 @@ class Return(Terminator):
|
|||||||
:param value: (:class:`Value`) return value
|
:param value: (:class:`Value`) return value
|
||||||
"""
|
"""
|
||||||
def __init__(self, value, name=""):
|
def __init__(self, value, name=""):
|
||||||
|
assert isinstance(value, Value)
|
||||||
super().__init__([value], builtins.TNone(), name)
|
super().__init__([value], builtins.TNone(), name)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "return"
|
return "return"
|
||||||
|
|
||||||
def value(self):
|
def get_value(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
class Eval(Instruction):
|
class Unreachable(Terminator):
|
||||||
"""
|
"""
|
||||||
An instruction that evaluates an AST fragment.
|
An instruction used to mark unreachable branches.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
:param ast: (:class:`.asttyped.AST`) return value
|
:param target: (:class:`BasicBlock`) branch target
|
||||||
"""
|
"""
|
||||||
def __init__(self, ast, name=""):
|
def __init__(self, name=""):
|
||||||
super().__init__([], ast.type, name)
|
super().__init__([], builtins.TNone(), name)
|
||||||
self.ast = ast
|
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "eval"
|
return "unreachable"
|
||||||
|
|
||||||
def __str__(self):
|
class Raise(Terminator):
|
||||||
return super().__str__() + " `{}`".format(self.ast.loc.source())
|
"""
|
||||||
|
A raise instruction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param value: (:class:`Value`) exception value
|
||||||
|
"""
|
||||||
|
def __init__(self, value, name=""):
|
||||||
|
assert isinstance(value, Value)
|
||||||
|
super().__init__([value], builtins.TNone(), name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "raise"
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
class Invoke(Terminator):
|
||||||
|
"""
|
||||||
|
A function call operation that supports exception handling.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param func: (:class:`Value`) function to call
|
||||||
|
:param args: (list of :class:`Value`) function arguments
|
||||||
|
:param normal: (:class:`BasicBlock`) normal target
|
||||||
|
:param exn: (:class:`BasicBlock`) exceptional target
|
||||||
|
"""
|
||||||
|
def __init__(self, func, args, normal, exn, name=""):
|
||||||
|
assert isinstance(func, Value)
|
||||||
|
for arg in args: assert isinstance(arg, Value)
|
||||||
|
assert isinstance(normal, BasicBlock)
|
||||||
|
assert isinstance(exn, BasicBlock)
|
||||||
|
super().__init__([func] + args + [normal, exn], func.type.ret, name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "invoke"
|
||||||
|
|
||||||
|
def get_function(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
|
def get_arguments(self):
|
||||||
|
return self.operands[1:-2]
|
||||||
|
|
||||||
|
def get_normal_target(self):
|
||||||
|
return self.operands[-2]
|
||||||
|
|
||||||
|
def get_exception_target(self):
|
||||||
|
return self.operands[-1]
|
||||||
|
|
||||||
|
def _operands_as_string(self):
|
||||||
|
result = ", ".join([operand.as_operand() for operand in self.operands[:-2]])
|
||||||
|
result += " to {} unwind {}".format(self.operands[-2].as_operand(),
|
||||||
|
self.operands[-1].as_operand())
|
||||||
|
return result
|
||||||
|
|
||||||
|
class LandingPad(Terminator):
|
||||||
|
"""
|
||||||
|
An instruction that gives an incoming exception a name and
|
||||||
|
dispatches it according to its type.
|
||||||
|
|
||||||
|
Once dispatched, the exception should be cast to its proper
|
||||||
|
type by calling the "exncast" builtin on the landing pad value.
|
||||||
|
|
||||||
|
:ivar types: (a list of :class:`builtins.TException`)
|
||||||
|
exception types corresponding to the basic block operands
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name=""):
|
||||||
|
super().__init__([], builtins.TException(), name)
|
||||||
|
self.types = []
|
||||||
|
|
||||||
|
def add_clause(self, target, typ):
|
||||||
|
assert isinstance(target, BasicBlock)
|
||||||
|
assert builtins.is_exception(typ)
|
||||||
|
self.operands.append(target)
|
||||||
|
self.types.append(typ.find())
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "landingpad"
|
||||||
|
|
||||||
|
def _operands_as_string(self):
|
||||||
|
table = []
|
||||||
|
for typ, target in zip(self.types, self.operands):
|
||||||
|
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
||||||
|
return "[{}]".format(", ".join(table))
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user