Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 05bdd5c4a90f
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2e33084a5f38
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Jan 1, 2016

  1. transforms.artiq_ir_generator: handle raise in except: with `fina…

    …lly:`.
    whitequark committed Jan 1, 2016
    Copy the full SHA
    5f68cc6 View commit details
  2. transforms.llvm_ir_generator: implement instrumentation for attribute…

    … writeback.
    whitequark committed Jan 1, 2016
    Copy the full SHA
    2e33084 View commit details
Showing with 158 additions and 25 deletions.
  1. +3 −0 artiq/compiler/embedding.py
  2. +6 −4 artiq/compiler/module.py
  3. +27 −13 artiq/compiler/transforms/artiq_ir_generator.py
  4. +117 −6 artiq/compiler/transforms/llvm_ir_generator.py
  5. +5 −2 artiq/compiler/types.py
3 changes: 3 additions & 0 deletions artiq/compiler/embedding.py
Original file line number Diff line number Diff line change
@@ -41,6 +41,9 @@ def has_rpc(self):
return any(filter(lambda x: inspect.isfunction(x) or inspect.ismethod(x),
self.forward_map.values()))

def __iter__(self):
return iter(self.forward_map.keys())

class ASTSynthesizer:
def __init__(self, object_map, type_map, value_map, quote_function=None, expanded_from=None):
self.source = ""
10 changes: 6 additions & 4 deletions artiq/compiler/module.py
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ def __init__(self, source_buffer, engine=None):
self.engine = engine

self.object_map = None
self.type_map = {}

self.name, _ = os.path.splitext(os.path.basename(source_buffer.name))

@@ -45,6 +46,7 @@ class Module:
def __init__(self, src, ref_period=1e-6):
self.engine = src.engine
self.object_map = src.object_map
self.type_map = src.type_map

int_monomorphizer = transforms.IntMonomorphizer(engine=self.engine)
inferencer = transforms.Inferencer(engine=self.engine)
@@ -76,10 +78,10 @@ def __init__(self, src, ref_period=1e-6):

def build_llvm_ir(self, target):
"""Compile the module to LLVM IR for the specified target."""
llvm_ir_generator = transforms.LLVMIRGenerator(engine=self.engine,
module_name=self.name, target=target,
object_map=self.object_map)
return llvm_ir_generator.process(self.artiq_ir)
llvm_ir_generator = transforms.LLVMIRGenerator(
engine=self.engine, module_name=self.name, target=target,
object_map=self.object_map, type_map=self.type_map)
return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True)

def entry_point(self):
"""Return the name of the function that is the entry point of this module."""
40 changes: 27 additions & 13 deletions artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -66,6 +66,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
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
:ivar final_branch: (function (target: :class:`ir.BasicBlock`, block: :class:`ir.BasicBlock)
or None)
the function that appends to ``block`` a jump through the ``finally`` statement
to ``target``
There is, additionally, some global state that is used to translate
the results of analyses on AST level to IR level:
@@ -102,6 +106,7 @@ def __init__(self, module_name, engine, ref_period):
self.continue_target = None
self.return_target = None
self.unwind_target = None
self.final_branch = None
self.function_map = dict()
self.variable_map = dict()
self.method_map = defaultdict(lambda: [])
@@ -551,6 +556,11 @@ def visit_Continue(self, node):
self.append(ir.Branch(self.continue_target))

def raise_exn(self, exn, loc=None):
if self.final_branch is not None:
raise_proxy = self.add_block("try.raise")
self.final_branch(raise_proxy, self.current_block)
self.current_block = raise_proxy

if exn is not None:
if loc is None:
loc = self.current_loc
@@ -588,29 +598,32 @@ def visit_Try(self, node):
vars={ "$k": ir.TBasicBlock() })
final_state = self.append(ir.Alloc([], final_env_type))
final_targets = []
final_paths = []

def final_branch(target, block):
block.append(ir.SetLocal(final_state, "$k", target))
final_targets.append(target)
final_paths.append(block)

if self.break_target is not None:
break_proxy = self.add_block("try.break")
old_break, self.break_target = self.break_target, break_proxy
break_proxy.append(ir.SetLocal(final_state, "$k", old_break))
final_targets.append(old_break)
final_branch(old_break, break_proxy)

if self.continue_target is not None:
continue_proxy = self.add_block("try.continue")
old_continue, self.continue_target = self.continue_target, continue_proxy
continue_proxy.append(ir.SetLocal(final_state, "$k", old_continue))
final_targets.append(old_continue)
final_branch(old_continue, continue_proxy)

return_proxy = self.add_block("try.return")
old_return, self.return_target = self.return_target, return_proxy
if old_return is not None:
return_proxy.append(ir.SetLocal(final_state, "$k", old_return))
final_targets.append(old_return)
final_branch(old_return, return_proxy)
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))
return_proxy.append(ir.SetLocal(final_state, "$k", return_action))
final_targets.append(return_action)
final_branch(return_action, return_proxy)

body = self.add_block("try.body")
self.append(ir.Branch(body))
@@ -635,6 +648,8 @@ def visit_Try(self, node):
self.continue_target = old_continue
self.return_target = old_return

old_final_branch, self.final_branch = self.final_branch, final_branch

cleanup = self.add_block('handler.cleanup')
landingpad = dispatcher.append(ir.LandingPad(cleanup))

@@ -659,6 +674,8 @@ def visit_Try(self, node):
handlers.append((handler, post_handler))

if any(node.finalbody):
self.final_branch = old_final_branch

finalizer = self.add_block("finally")
self.current_block = finalizer

@@ -673,11 +690,8 @@ def visit_Try(self, node):
final_targets.append(tail)
final_targets.append(reraise)

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))
for block in final_paths:
block.append(ir.Branch(finalizer))

if not body.is_terminated():
body.append(ir.SetLocal(final_state, "$k", tail))
123 changes: 117 additions & 6 deletions artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
into LLVM intermediate representation.
"""

import os
import os, re
from collections import defaultdict
from pythonparser import ast, diagnostic
from llvmlite_artiq import ir as ll
from ...language import core as language_core
@@ -161,10 +162,11 @@ def finalize(self, source_buffer):


class LLVMIRGenerator:
def __init__(self, engine, module_name, target, object_map):
def __init__(self, engine, module_name, target, object_map, type_map):
self.engine = engine
self.target = target
self.object_map = object_map
self.type_map = type_map
self.llcontext = target.llcontext
self.llmodule = ll.Module(context=self.llcontext, name=module_name)
self.llmodule.triple = target.triple
@@ -285,16 +287,16 @@ def llty_of_type(self, typ, bare=False, for_return=False):
else:
return llty.as_pointer()

def llstr_of_str(self, value, name=None,
linkage="private", unnamed_addr=True):
def llstr_of_str(self, value, name=None, linkage="private", unnamed_addr=True):
if isinstance(value, str):
assert "\0" not in value
as_bytes = (value + "\0").encode("utf-8")
else:
as_bytes = value

if name is None:
name = self.llmodule.get_unique_name("str")
sanitized_str = re.sub(rb"[^a-zA-Z0-9_.]", b"", as_bytes[:20]).decode('ascii')
name = self.llmodule.get_unique_name("str.{}".format(sanitized_str))

llstr = self.llmodule.get_global(name)
if llstr is None:
@@ -403,15 +405,95 @@ def map(self, value):
else:
assert False

def process(self, functions):
def process(self, functions, attribute_writeback):
for func in functions:
self.process_function(func)

if any(functions):
self.debug_info_emitter.finalize(functions[0].loc.source_buffer)

if attribute_writeback:
self.emit_attribute_writeback()

return self.llmodule

def emit_attribute_writeback(self):
shadow_memory_dim = defaultdict(lambda: 0)

for obj_id in self.object_map:
obj_ref = self.object_map.retrieve(obj_id)
if isinstance(obj_ref, type):
_, typ = self.type_map[obj_ref]
else:
typ, _ = self.type_map[type(obj_ref)]

if shadow_memory_dim[typ] <= obj_id:
shadow_memory_dim[typ] = obj_id + 1

lldescty = self.llcontext.get_identified_type("shadow.desc")
lldescty.elements = [llptr.as_pointer(), llptr, lli32, llptr]

lldescs = []
for typ in shadow_memory_dim:
if "__objectid__" not in typ.attributes:
continue
assert list(typ.attributes.keys())[0] == "__objectid__"

if types.is_constructor(typ):
type_name = "class.{}".format(typ.name)
else:
type_name = "instance.{}".format(typ.name)

shadowname = "shadow.{}".format(type_name)
llshadow = self.llmodule.get_global(shadowname)
if llshadow is None:
continue

llshadowlen = shadow_memory_dim[typ] * len(typ.attributes)
llshadowty = ll.ArrayType(lli8, llshadowlen)
llshadow.gtype = llshadowty
llshadow.type = llshadowty.as_pointer()
llshadow.initializer = ll.Constant(llshadowty, None)

def rpc_tag_error(typ):
print(typ)
assert False

rpcattrary = list(typ.attributes.keys())[1:]
llrpcattraryty = ll.ArrayType(llptr, len(rpcattrary) + 1)
llrpcattrary = list(map(lambda attr: self.llstr_of_str(attr), rpcattrary))

llrpcattrs = ll.GlobalVariable(self.llmodule, llrpcattraryty,
name="shadow.attrs.{}".format(type_name))
llrpcattrs.initializer = ll.Constant(llrpcattraryty,
llrpcattrary + [ll.Constant(llptr, None)])
llrpcattrs.linkage = 'internal'

rpctag = b""
for attr_type in list(typ.attributes.values())[1:]:
if types.is_function(attr_type) or types.is_method(attr_type):
continue
rpctag += self._rpc_tag(attr_type, error_handler=rpc_tag_error)
rpctag += b"\x00"
llrpctag = self.llstr_of_str(rpctag)

lldesc = ll.GlobalVariable(self.llmodule, lldescty,
name="shadow.desc.{}".format(type_name))
lldesc.initializer = ll.Constant(lldescty, [
llrpcattrs.bitcast(llptr.as_pointer()),
llrpctag,
ll.Constant(lli32, shadow_memory_dim[typ]),
llshadow.bitcast(llptr)])
lldesc.linkage = 'internal'
lldescs.append(lldesc)

llglobaldescty = ll.ArrayType(lldescty.as_pointer(), len(lldescs) + 1)
llglobaldesc = ll.GlobalVariable(self.llmodule, llglobaldescty,
name="shadow.descs")
llglobaldesc.initializer = ll.Constant(llglobaldescty,
lldescs + [ll.Constant(lldescty.as_pointer(), None)])
# llglobaldesc.linkage = 'internal'

def process_function(self, func):
try:
self.llfunction = self.llmodule.get_global(func.name)
@@ -571,6 +653,34 @@ def process_GetAttr(self, insn):

def process_SetAttr(self, insn):
assert builtins.is_allocated(insn.object().type)

object_type = insn.object().type.find()
value_type = insn.value().type.find()
if "__objectid__" in object_type.attributes and \
not (types.is_function(value_type) or types.is_method(value_type)):
llidptr = self.llbuilder.gep(self.map(insn.object()),
[self.llindex(0), self.llindex(0)])
llid = self.llbuilder.load(llidptr, name="shadow.id")
llattrcount = ll.Constant(lli32, len(object_type.attributes) - 1)
llshadowpos = self.llbuilder.add(
self.llbuilder.mul(llid, llattrcount),
ll.Constant(lli32, self.attr_index(insn) - 1))

if types.is_constructor(object_type):
shadowname = "shadow.class.{}".format(object_type.name)
else:
shadowname = "shadow.instance.{}".format(object_type.name)

llshadow = self.llmodule.get_global(shadowname)
if llshadow is None:
llshadowty = ll.ArrayType(lli8, 0)
llshadow = ll.GlobalVariable(self.llmodule, llshadowty, shadowname)
llshadow.linkage = 'internal'

llshadowptr = self.llbuilder.gep(llshadow, [self.llindex(0), llshadowpos],
name="shadow.ptr")
self.llbuilder.store(ll.Constant(lli8, 1), llshadowptr)

llptr = self.llbuilder.gep(self.map(insn.object()),
[self.llindex(0), self.llindex(self.attr_index(insn))],
name=insn.name)
@@ -838,6 +948,7 @@ def _prepare_ffi_call(self, insn):

# See session.c:{send,receive}_rpc_value and comm_generic.py:_{send,receive}_rpc_value.
def _rpc_tag(self, typ, error_handler):
typ = typ.find()
if types.is_tuple(typ):
assert len(typ.elts) < 256
return b"t" + bytes([len(typ.elts)]) + \
7 changes: 5 additions & 2 deletions artiq/compiler/types.py
Original file line number Diff line number Diff line change
@@ -578,14 +578,17 @@ def is_var(typ):

def is_mono(typ, name=None, **params):
typ = typ.find()

if not isinstance(typ, TMono):
return False

params_match = True
for param in params:
if param not in typ.params:
return False
params_match = params_match and \
typ.params[param].find() == params[param].find()
return isinstance(typ, TMono) and \
(name is None or (typ.name == name and params_match))
return name is None or (typ.name == name and params_match)

def is_polymorphic(typ):
return typ.fold(False, lambda accum, typ: accum or is_var(typ))