forked from M-Labs/artiq
Add conversion to LLVM IR (except handling of exception handling).
This commit is contained in:
parent
c6cd318f19
commit
6f11fa6bb1
|
@ -19,10 +19,16 @@ class TBasicBlock(types.TMono):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("label")
|
super().__init__("label")
|
||||||
|
|
||||||
|
def is_basic_block(typ):
|
||||||
|
return isinstance(typ, TBasicBlock)
|
||||||
|
|
||||||
class TOption(types.TMono):
|
class TOption(types.TMono):
|
||||||
def __init__(self, inner):
|
def __init__(self, inner):
|
||||||
super().__init__("option", {"inner": inner})
|
super().__init__("option", {"inner": inner})
|
||||||
|
|
||||||
|
def is_option(typ):
|
||||||
|
return isinstance(typ, TOption)
|
||||||
|
|
||||||
class Value:
|
class Value:
|
||||||
"""
|
"""
|
||||||
An SSA value that keeps track of its uses.
|
An SSA value that keeps track of its uses.
|
||||||
|
@ -571,7 +577,7 @@ class GetAttr(Instruction):
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "getattr({})".format(repr(self.attr))
|
return "getattr({})".format(repr(self.attr))
|
||||||
|
|
||||||
def env(self):
|
def object(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
class SetAttr(Instruction):
|
class SetAttr(Instruction):
|
||||||
|
@ -600,7 +606,7 @@ class SetAttr(Instruction):
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "setattr({})".format(repr(self.attr))
|
return "setattr({})".format(repr(self.attr))
|
||||||
|
|
||||||
def env(self):
|
def object(self):
|
||||||
return self.operands[0]
|
return self.operands[0]
|
||||||
|
|
||||||
def value(self):
|
def value(self):
|
||||||
|
|
|
@ -18,9 +18,10 @@ class Module:
|
||||||
int_monomorphizer = transforms.IntMonomorphizer(engine=engine)
|
int_monomorphizer = transforms.IntMonomorphizer(engine=engine)
|
||||||
monomorphism_validator = validators.MonomorphismValidator(engine=engine)
|
monomorphism_validator = validators.MonomorphismValidator(engine=engine)
|
||||||
escape_validator = validators.EscapeValidator(engine=engine)
|
escape_validator = validators.EscapeValidator(engine=engine)
|
||||||
ir_generator = transforms.IRGenerator(engine=engine, module_name=self.name)
|
artiq_ir_generator = transforms.ARTIQIRGenerator(engine=engine, module_name=self.name)
|
||||||
dead_code_eliminator = transforms.DeadCodeEliminator(engine=engine)
|
dead_code_eliminator = transforms.DeadCodeEliminator(engine=engine)
|
||||||
local_access_validator = validators.LocalAccessValidator(engine=engine)
|
local_access_validator = validators.LocalAccessValidator(engine=engine)
|
||||||
|
llvm_ir_generator = transforms.LLVMIRGenerator(engine=engine, module_name=self.name)
|
||||||
|
|
||||||
self.parsetree, self.comments = parse_buffer(source_buffer, engine=engine)
|
self.parsetree, self.comments = parse_buffer(source_buffer, engine=engine)
|
||||||
self.typedtree = asttyped_rewriter.visit(self.parsetree)
|
self.typedtree = asttyped_rewriter.visit(self.parsetree)
|
||||||
|
@ -30,9 +31,11 @@ class Module:
|
||||||
inferencer.visit(self.typedtree)
|
inferencer.visit(self.typedtree)
|
||||||
monomorphism_validator.visit(self.typedtree)
|
monomorphism_validator.visit(self.typedtree)
|
||||||
escape_validator.visit(self.typedtree)
|
escape_validator.visit(self.typedtree)
|
||||||
self.ir = ir_generator.visit(self.typedtree)
|
self.artiq_ir = artiq_ir_generator.visit(self.typedtree)
|
||||||
dead_code_eliminator.process(self.ir)
|
dead_code_eliminator.process(self.artiq_ir)
|
||||||
local_access_validator.process(self.ir)
|
local_access_validator.process(self.artiq_ir)
|
||||||
|
llvm_ir_generator.process(self.artiq_ir)
|
||||||
|
self.llvm_ir = llvm_ir_generator.llmodule
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_string(cls, source_string, name="input.py", first_line=1, engine=None):
|
def from_string(cls, source_string, name="input.py", first_line=1, engine=None):
|
||||||
|
|
|
@ -12,7 +12,7 @@ def main():
|
||||||
engine.process = process_diagnostic
|
engine.process = process_diagnostic
|
||||||
|
|
||||||
mod = Module.from_string("".join(fileinput.input()).expandtabs(), engine=engine)
|
mod = Module.from_string("".join(fileinput.input()).expandtabs(), engine=engine)
|
||||||
for fn in mod.ir:
|
for fn in mod.artiq_ir:
|
||||||
print(fn)
|
print(fn)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import sys, fileinput
|
||||||
|
from pythonparser import diagnostic
|
||||||
|
from .. import Module
|
||||||
|
|
||||||
|
def main():
|
||||||
|
def process_diagnostic(diag):
|
||||||
|
print("\n".join(diag.render()))
|
||||||
|
if diag.level in ("fatal", "error"):
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
engine = diagnostic.Engine()
|
||||||
|
engine.process = process_diagnostic
|
||||||
|
|
||||||
|
mod = Module.from_string("".join(fileinput.input()).expandtabs(), engine=engine)
|
||||||
|
print(mod.llvm_ir)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -1,5 +1,6 @@
|
||||||
from .asttyped_rewriter import ASTTypedRewriter
|
from .asttyped_rewriter import ASTTypedRewriter
|
||||||
from .inferencer import Inferencer
|
from .inferencer import Inferencer
|
||||||
from .int_monomorphizer import IntMonomorphizer
|
from .int_monomorphizer import IntMonomorphizer
|
||||||
from .ir_generator import IRGenerator
|
from .artiq_ir_generator import ARTIQIRGenerator
|
||||||
from .dead_code_eliminator import DeadCodeEliminator
|
from .dead_code_eliminator import DeadCodeEliminator
|
||||||
|
from .llvm_ir_generator import LLVMIRGenerator
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
:class:`IRGenerator` transforms typed AST into ARTIQ intermediate
|
:class:`ARTIQIRGenerator` transforms typed AST into ARTIQ intermediate
|
||||||
representation. ARTIQ IR is designed to be low-level enough that
|
representation. ARTIQ IR is designed to be low-level enough that
|
||||||
its operations are elementary--contain no internal branching--
|
its operations are elementary--contain no internal branching--
|
||||||
but without too much detail, such as exposing the reference/value
|
but without too much detail, such as exposing the reference/value
|
||||||
|
@ -25,9 +25,9 @@ def _extract_loc(node):
|
||||||
# We put some effort in keeping generated IR readable,
|
# We put some effort in keeping generated IR readable,
|
||||||
# i.e. with a more or less linear correspondence to the source.
|
# 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.
|
# This is why basic blocks sometimes seem to be produced in an odd order.
|
||||||
class IRGenerator(algorithm.Visitor):
|
class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
"""
|
"""
|
||||||
:class:`IRGenerator` contains a lot of internal state,
|
:class:`ARTIQIRGenerator` contains a lot of internal state,
|
||||||
which is effectively maintained in a stack--with push/pop
|
which is effectively maintained in a stack--with push/pop
|
||||||
pairs around any state updates. It is comprised of following:
|
pairs around any state updates. It is comprised of following:
|
||||||
|
|
||||||
|
@ -255,8 +255,7 @@ class IRGenerator(algorithm.Visitor):
|
||||||
def visit_Pass(self, node):
|
def visit_Pass(self, node):
|
||||||
# Insert a dummy instruction so that analyses which extract
|
# Insert a dummy instruction so that analyses which extract
|
||||||
# locations from CFG have something to use.
|
# locations from CFG have something to use.
|
||||||
self.append(ir.Arith(ast.Add(loc=None),
|
self.append(ir.Builtin("nop", [], builtins.TNone()))
|
||||||
ir.Constant(0, self._size_type), ir.Constant(0, self._size_type)))
|
|
||||||
|
|
||||||
def visit_Assign(self, node):
|
def visit_Assign(self, node):
|
||||||
try:
|
try:
|
||||||
|
@ -367,12 +366,13 @@ class IRGenerator(algorithm.Visitor):
|
||||||
try:
|
try:
|
||||||
iterable = self.visit(node.iter)
|
iterable = self.visit(node.iter)
|
||||||
length = self._iterable_len(iterable)
|
length = self._iterable_len(iterable)
|
||||||
|
prehead = self.current_block
|
||||||
|
|
||||||
head = self.add_block("for.head")
|
head = self.add_block("for.head")
|
||||||
self.append(ir.Branch(head))
|
self.append(ir.Branch(head))
|
||||||
self.current_block = head
|
self.current_block = head
|
||||||
phi = self.append(ir.Phi(length.type))
|
phi = self.append(ir.Phi(length.type))
|
||||||
phi.add_incoming(ir.Constant(0, phi.type), head)
|
phi.add_incoming(ir.Constant(0, phi.type), prehead)
|
||||||
cond = self.append(ir.Compare(ast.Lt(loc=None), phi, length))
|
cond = self.append(ir.Compare(ast.Lt(loc=None), phi, length))
|
||||||
|
|
||||||
break_block = self.add_block("for.break")
|
break_block = self.add_block("for.break")
|
||||||
|
@ -842,6 +842,8 @@ class IRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
def visit_BinOpT(self, node):
|
def visit_BinOpT(self, node):
|
||||||
if builtins.is_numeric(node.type):
|
if builtins.is_numeric(node.type):
|
||||||
|
# TODO: check for division by zero
|
||||||
|
# TODO: check for shift by too many bits
|
||||||
return self.append(ir.Arith(node.op, self.visit(node.left), self.visit(node.right)))
|
return self.append(ir.Arith(node.op, self.visit(node.left), self.visit(node.right)))
|
||||||
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
|
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
|
||||||
lhs, rhs = self.visit(node.left), self.visit(node.right)
|
lhs, rhs = self.visit(node.left), self.visit(node.right)
|
|
@ -335,9 +335,12 @@ class Inferencer(algorithm.Visitor):
|
||||||
return list_.type, left.type, right.type
|
return list_.type, left.type, right.type
|
||||||
else:
|
else:
|
||||||
return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ))
|
return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ))
|
||||||
elif isinstance(op, (ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, ast.Sub)):
|
elif isinstance(op, (ast.FloorDiv, ast.Mod, ast.Pow, ast.Sub)):
|
||||||
# numeric operators work on any kind of number
|
# numeric operators work on any kind of number
|
||||||
return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ))
|
return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ))
|
||||||
|
elif isinstance(op, ast.Div):
|
||||||
|
# division always returns a float
|
||||||
|
return self._coerce_numeric((left, right), lambda typ: (builtins.TFloat(), typ, typ))
|
||||||
else: # MatMult
|
else: # MatMult
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"operator '{op}' is not supported", {"op": op.loc.source()},
|
"operator '{op}' is not supported", {"op": op.loc.source()},
|
||||||
|
|
|
@ -0,0 +1,408 @@
|
||||||
|
"""
|
||||||
|
:class:`LLVMIRGenerator` transforms ARTIQ intermediate representation
|
||||||
|
into LLVM intermediate representation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import llvmlite.ir as ll
|
||||||
|
from pythonparser import ast
|
||||||
|
from .. import types, builtins, ir
|
||||||
|
|
||||||
|
class LLVMIRGenerator:
|
||||||
|
def __init__(self, engine, module_name, context=ll.Context()):
|
||||||
|
self.engine = engine
|
||||||
|
self.llcontext = context
|
||||||
|
self.llmodule = ll.Module(context=self.llcontext, name=module_name)
|
||||||
|
self.llfunction = None
|
||||||
|
self.llmap = {}
|
||||||
|
self.fixups = []
|
||||||
|
|
||||||
|
def llty_of_type(self, typ, for_alloc=False, for_return=False):
|
||||||
|
if types.is_tuple(typ):
|
||||||
|
return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.elts])
|
||||||
|
elif types.is_function(typ):
|
||||||
|
envarg = ll.IntType(8).as_pointer
|
||||||
|
llty = ll.FunctionType(args=[envarg] +
|
||||||
|
[self.llty_of_type(typ.args[arg])
|
||||||
|
for arg in typ.args] +
|
||||||
|
[self.llty_of_type(ir.TOption(typ.optargs[arg]))
|
||||||
|
for arg in typ.optargs],
|
||||||
|
return_type=self.llty_of_type(typ.ret, for_return=True))
|
||||||
|
return llty.as_pointer()
|
||||||
|
elif builtins.is_none(typ):
|
||||||
|
if for_return:
|
||||||
|
return ll.VoidType()
|
||||||
|
else:
|
||||||
|
return ll.LiteralStructType([])
|
||||||
|
elif builtins.is_bool(typ):
|
||||||
|
return ll.IntType(1)
|
||||||
|
elif builtins.is_int(typ):
|
||||||
|
return ll.IntType(builtins.get_int_width(typ))
|
||||||
|
elif builtins.is_float(typ):
|
||||||
|
return ll.DoubleType()
|
||||||
|
elif builtins.is_list(typ):
|
||||||
|
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||||
|
return ll.LiteralStructType([ll.IntType(32), lleltty.as_pointer()])
|
||||||
|
elif builtins.is_range(typ):
|
||||||
|
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||||
|
return ll.LiteralStructType([lleltty, lleltty, lleltty])
|
||||||
|
elif builtins.is_exception(typ):
|
||||||
|
# TODO: hack before EH is working
|
||||||
|
return ll.LiteralStructType([])
|
||||||
|
elif ir.is_basic_block(typ):
|
||||||
|
return ll.LabelType()
|
||||||
|
elif ir.is_option(typ):
|
||||||
|
return ll.LiteralStructType([ll.IntType(1), self.llty_of_type(typ.params["inner"])])
|
||||||
|
elif ir.is_environment(typ):
|
||||||
|
llty = ll.LiteralStructType([self.llty_of_type(typ.params[name])
|
||||||
|
for name in typ.params])
|
||||||
|
if for_alloc:
|
||||||
|
return llty
|
||||||
|
else:
|
||||||
|
return llty.as_pointer()
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def llconst_of_const(self, const):
|
||||||
|
llty = self.llty_of_type(const.type)
|
||||||
|
if const.value is None:
|
||||||
|
return ll.Constant(llty, [])
|
||||||
|
elif const.value is True:
|
||||||
|
return ll.Constant(llty, True)
|
||||||
|
elif const.value is False:
|
||||||
|
return ll.Constant(llty, False)
|
||||||
|
elif isinstance(const.value, (int, float)):
|
||||||
|
return ll.Constant(llty, const.value)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def map(self, value):
|
||||||
|
if isinstance(value, (ir.Instruction, ir.BasicBlock)):
|
||||||
|
return self.llmap[value]
|
||||||
|
elif isinstance(value, ir.Constant):
|
||||||
|
return self.llconst_of_const(value)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def process(self, functions):
|
||||||
|
for func in functions:
|
||||||
|
self.process_function(func)
|
||||||
|
|
||||||
|
def process_function(self, func):
|
||||||
|
llargtys = []
|
||||||
|
for arg in func.arguments:
|
||||||
|
llargtys.append(self.llty_of_type(arg.type))
|
||||||
|
llfunty = ll.FunctionType(args=llargtys,
|
||||||
|
return_type=self.llty_of_type(func.type.ret, for_return=True))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.llfunction = ll.Function(self.llmodule, llfunty, func.name)
|
||||||
|
self.llmap = {}
|
||||||
|
self.llbuilder = ll.IRBuilder()
|
||||||
|
self.fixups = []
|
||||||
|
|
||||||
|
# First, create all basic blocks.
|
||||||
|
for block in func.basic_blocks:
|
||||||
|
llblock = self.llfunction.append_basic_block(block.name)
|
||||||
|
self.llmap[block] = llblock
|
||||||
|
|
||||||
|
# Second, translate all instructions.
|
||||||
|
for block in func.basic_blocks:
|
||||||
|
self.llbuilder.position_at_end(self.llmap[block])
|
||||||
|
for insn in block.instructions:
|
||||||
|
llinsn = getattr(self, "process_" + type(insn).__name__)(insn)
|
||||||
|
assert llinsn is not None
|
||||||
|
self.llmap[insn] = llinsn
|
||||||
|
|
||||||
|
# Third, fixup phis.
|
||||||
|
for fixup in self.fixups:
|
||||||
|
fixup()
|
||||||
|
finally:
|
||||||
|
self.llfunction = None
|
||||||
|
self.llmap = None
|
||||||
|
self.fixups = []
|
||||||
|
|
||||||
|
def process_Phi(self, insn):
|
||||||
|
llinsn = self.llbuilder.phi(self.llty_of_type(insn.type), name=insn.name)
|
||||||
|
def fixup():
|
||||||
|
for value, block in insn.incoming():
|
||||||
|
llinsn.add_incoming(self.map(value), self.map(block))
|
||||||
|
self.fixups.append(fixup)
|
||||||
|
return llinsn
|
||||||
|
|
||||||
|
def process_Alloc(self, insn):
|
||||||
|
if ir.is_environment(insn.type):
|
||||||
|
return self.llbuilder.alloca(self.llty_of_type(insn.type, for_alloc=True),
|
||||||
|
name=insn.name)
|
||||||
|
elif builtins.is_list(insn.type):
|
||||||
|
llsize = self.map(insn.operands[0])
|
||||||
|
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
|
||||||
|
llvalue = self.llbuilder.insert_value(llvalue, llsize, 0)
|
||||||
|
llalloc = self.llbuilder.alloca(self.llty_of_type(builtins.get_iterable_elt(insn.type)),
|
||||||
|
size=llsize)
|
||||||
|
llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name)
|
||||||
|
return llvalue
|
||||||
|
elif builtins.is_mutable(insn.type):
|
||||||
|
assert False
|
||||||
|
else: # immutable
|
||||||
|
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
|
||||||
|
for index, elt in enumerate(insn.operands):
|
||||||
|
llvalue = self.llbuilder.insert_value(llvalue, self.map(elt), index)
|
||||||
|
llvalue.name = insn.name
|
||||||
|
return llvalue
|
||||||
|
|
||||||
|
def llindex(self, index):
|
||||||
|
return ll.Constant(ll.IntType(32), index)
|
||||||
|
|
||||||
|
def llptr_to_var(self, llenv, env_ty, var_name):
|
||||||
|
if var_name in env_ty.params:
|
||||||
|
var_index = list(env_ty.params.keys()).index(var_name)
|
||||||
|
return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)])
|
||||||
|
else:
|
||||||
|
outer_index = list(env_ty.params.keys()).index(".outer")
|
||||||
|
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)])
|
||||||
|
llouterenv = self.llbuilder.load(llptr)
|
||||||
|
return self.llptr_to_var(llouterenv, env_ty.params[".outer"], var_name)
|
||||||
|
|
||||||
|
def process_GetLocal(self, insn):
|
||||||
|
env = insn.environment()
|
||||||
|
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||||
|
return self.llbuilder.load(llptr)
|
||||||
|
|
||||||
|
def process_SetLocal(self, insn):
|
||||||
|
env = insn.environment()
|
||||||
|
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||||
|
return self.llbuilder.store(self.map(insn.value()), llptr)
|
||||||
|
|
||||||
|
def attr_index(self, insn):
|
||||||
|
return list(insn.object().type.attributes.keys()).index(insn.attr)
|
||||||
|
|
||||||
|
def process_GetAttr(self, insn):
|
||||||
|
if types.is_tuple(insn.object().type):
|
||||||
|
return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn),
|
||||||
|
name=insn.name)
|
||||||
|
elif not builtins.is_mutable(insn.object().type):
|
||||||
|
return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
llptr = self.llbuilder.gep(self.map(insn.object()),
|
||||||
|
[self.llindex(0), self.llindex(self.attr_index(insn))],
|
||||||
|
name=insn.name)
|
||||||
|
return self.llbuilder.load(llptr)
|
||||||
|
|
||||||
|
def process_SetAttr(self, insn):
|
||||||
|
assert builtins.is_mutable(insns.object().type)
|
||||||
|
llptr = self.llbuilder.gep(self.map(insn.object()),
|
||||||
|
[self.llindex(0), self.llindex(self.attr_index(insn))],
|
||||||
|
name=insn.name)
|
||||||
|
return self.llbuilder.store(llptr, self.map(insn.value()))
|
||||||
|
|
||||||
|
def process_GetElem(self, insn):
|
||||||
|
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
|
||||||
|
llelt = self.llbuilder.gep(llelts, [self.map(insn.index())],
|
||||||
|
inbounds=True)
|
||||||
|
return self.llbuilder.load(llelt)
|
||||||
|
|
||||||
|
def process_SetElem(self, insn):
|
||||||
|
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
|
||||||
|
llelt = self.llbuilder.gep(llelts, [self.map(insn.index())],
|
||||||
|
inbounds=True)
|
||||||
|
return self.llbuilder.store(self.map(insn.value()), llelt)
|
||||||
|
|
||||||
|
def process_Coerce(self, insn):
|
||||||
|
typ, value_typ = insn.type, insn.value().type
|
||||||
|
if builtins.is_int(typ) and builtins.is_float(value_typ):
|
||||||
|
return self.llbuilder.fptosi(self.map(insn.value()), self.llty_of_type(typ),
|
||||||
|
name=insn.name)
|
||||||
|
elif builtins.is_float(typ) and builtins.is_int(value_typ):
|
||||||
|
return self.llbuilder.sitofp(self.map(insn.value()), self.llty_of_type(typ),
|
||||||
|
name=insn.name)
|
||||||
|
elif builtins.is_int(typ) and builtins.is_int(value_typ):
|
||||||
|
if builtins.get_int_width(typ) > builtins.get_int_width(value_typ):
|
||||||
|
return self.llbuilder.sext(self.map(insn.value()), self.llty_of_type(typ),
|
||||||
|
name=insn.name)
|
||||||
|
else: # builtins.get_int_width(typ) < builtins.get_int_width(value_typ):
|
||||||
|
return self.llbuilder.trunc(self.map(insn.value()), self.llty_of_type(typ),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def process_Arith(self, insn):
|
||||||
|
if isinstance(insn.op, ast.Add):
|
||||||
|
if builtins.is_float(insn.type):
|
||||||
|
return self.llbuilder.fadd(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
return self.llbuilder.add(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.Sub):
|
||||||
|
if builtins.is_float(insn.type):
|
||||||
|
return self.llbuilder.fsub(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
return self.llbuilder.sub(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.Mult):
|
||||||
|
if builtins.is_float(insn.type):
|
||||||
|
return self.llbuilder.fmul(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
return self.llbuilder.mul(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.Div):
|
||||||
|
if builtins.is_float(insn.lhs().type):
|
||||||
|
return self.llbuilder.fdiv(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
lllhs = self.llbuilder.sitofp(self.map(insn.lhs()), self.llty_of_type(insn.type))
|
||||||
|
llrhs = self.llbuilder.sitofp(self.map(insn.rhs()), self.llty_of_type(insn.type))
|
||||||
|
return self.llbuilder.fdiv(lllhs, llrhs,
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.FloorDiv):
|
||||||
|
if builtins.is_float(insn.type):
|
||||||
|
llvalue = self.llbuilder.fdiv(self.map(insn.lhs()), self.map(insn.rhs()))
|
||||||
|
llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType()])
|
||||||
|
llfn = ll.Function(self.llmodule, llfnty, "llvm.round.f64")
|
||||||
|
return self.llbuilder.call(llfn, [llvalue],
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
return self.llbuilder.sdiv(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.Mod):
|
||||||
|
if builtins.is_float(insn.type):
|
||||||
|
return self.llbuilder.frem(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
return self.llbuilder.srem(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.Pow):
|
||||||
|
if builtins.is_float(insn.type):
|
||||||
|
llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
|
||||||
|
llfn = ll.Function(self.llmodule, llfnty, "llvm.pow.f64")
|
||||||
|
return self.llbuilder.call(llfn, [self.map(insn.lhs()), self.map(insn.rhs())],
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
llrhs = self.llbuilder.trunc(self.map(insn.rhs()), ll.IntType(32))
|
||||||
|
llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.IntType(32)])
|
||||||
|
llfn = ll.Function(self.llmodule, llfnty, "llvm.powi.f64")
|
||||||
|
llvalue = self.llbuilder.call(llfn, [self.map(insn.lhs()), llrhs])
|
||||||
|
return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.LShift):
|
||||||
|
return self.llbuilder.shl(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.RShift):
|
||||||
|
return self.llbuilder.ashr(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.BitAnd):
|
||||||
|
return self.llbuilder.and_(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.BitOr):
|
||||||
|
return self.llbuilder.or_(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
elif isinstance(insn.op, ast.BitXor):
|
||||||
|
return self.llbuilder.xor(self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def process_Compare(self, insn):
|
||||||
|
if isinstance(insn.op, ast.Eq):
|
||||||
|
op = '=='
|
||||||
|
elif isinstance(insn.op, ast.NotEq):
|
||||||
|
op = '!='
|
||||||
|
elif isinstance(insn.op, ast.Gt):
|
||||||
|
op = '>'
|
||||||
|
elif isinstance(insn.op, ast.GtE):
|
||||||
|
op = '>='
|
||||||
|
elif isinstance(insn.op, ast.Lt):
|
||||||
|
op = '<'
|
||||||
|
elif isinstance(insn.op, ast.LtE):
|
||||||
|
op = '<='
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
if builtins.is_float(insn.lhs().type):
|
||||||
|
return self.llbuilder.fcmp_ordered(op, self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
else:
|
||||||
|
return self.llbuilder.icmp_signed(op, self.map(insn.lhs()), self.map(insn.rhs()),
|
||||||
|
name=insn.name)
|
||||||
|
|
||||||
|
def process_Builtin(self, insn):
|
||||||
|
if insn.op == "nop":
|
||||||
|
fn = ll.Function(self.llmodule, ll.FunctionType(ll.VoidType(), []), "llvm.donothing")
|
||||||
|
return self.llbuilder.call(fn, [])
|
||||||
|
elif insn.op == "unwrap":
|
||||||
|
optarg, default = map(self.map, insn.operands)
|
||||||
|
has_arg = self.llbuilder.extract_value(optarg, 0)
|
||||||
|
arg = self.llbuilder.extract_value(optarg, 1)
|
||||||
|
return self.llbuilder.select(has_arg, arg, default,
|
||||||
|
name=insn.name)
|
||||||
|
elif insn.op == "round":
|
||||||
|
llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType()])
|
||||||
|
llfn = ll.Function(self.llmodule, llfnty, "llvm.round.f64")
|
||||||
|
return self.llbuilder.call(llfn, [llvalue],
|
||||||
|
name=insn.name)
|
||||||
|
elif insn.op == "globalenv":
|
||||||
|
def get_outer(llenv, env_ty):
|
||||||
|
if ".outer" in env_ty.params:
|
||||||
|
outer_index = list(env_ty.params.keys()).index(".outer")
|
||||||
|
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)])
|
||||||
|
llouterenv = self.llbuilder.load(llptr)
|
||||||
|
return self.llptr_to_var(llouterenv, env_ty.params[".outer"], var_name)
|
||||||
|
else:
|
||||||
|
return llenv
|
||||||
|
|
||||||
|
env, = insn.operands
|
||||||
|
return get_outer(self.map(env), env.type)
|
||||||
|
elif insn.op == "len":
|
||||||
|
lst, = insn.operands
|
||||||
|
return self.llbuilder.extract_value(self.map(lst), 0)
|
||||||
|
# elif insn.op == "exncast":
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# def process_Closure(self, insn):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# def process_Call(self, insn):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
def process_Select(self, insn):
|
||||||
|
return self.llbuilder.select(self.map(insn.cond()),
|
||||||
|
self.map(insn.lhs()), self.map(insn.rhs()))
|
||||||
|
|
||||||
|
def process_Branch(self, insn):
|
||||||
|
return self.llbuilder.branch(self.map(insn.target()))
|
||||||
|
|
||||||
|
def process_BranchIf(self, insn):
|
||||||
|
return self.llbuilder.cbranch(self.map(insn.condition()),
|
||||||
|
self.map(insn.if_true()), self.map(insn.if_false()))
|
||||||
|
|
||||||
|
# def process_IndirectBranch(self, insn):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
def process_Return(self, insn):
|
||||||
|
if builtins.is_none(insn.type):
|
||||||
|
return self.llbuilder.ret_void()
|
||||||
|
else:
|
||||||
|
return self.llbuilder.ret(self.llmap[insn.value()])
|
||||||
|
|
||||||
|
def process_Unreachable(self, insn):
|
||||||
|
return self.llbuilder.unreachable()
|
||||||
|
|
||||||
|
def process_Raise(self, insn):
|
||||||
|
# TODO: hack before EH is working
|
||||||
|
llfnty = ll.FunctionType(ll.VoidType(), [])
|
||||||
|
llfn = ll.Function(self.llmodule, llfnty, "llvm.abort")
|
||||||
|
llinsn = self.llbuilder.call(llfn, [],
|
||||||
|
name=insn.name)
|
||||||
|
self.llbuilder.unreachable()
|
||||||
|
return llinsn
|
||||||
|
|
||||||
|
# def process_Invoke(self, insn):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# def process_LandingPad(self, insn):
|
||||||
|
# pass
|
||||||
|
|
Loading…
Reference in New Issue