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: 6f11fa6bb1a9
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: ec9d40b04f53
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Jul 21, 2015

  1. LocalAccessValidator: fix validation of closures with no outer variab…

    …les.
    whitequark committed Jul 21, 2015
    Copy the full SHA
    e299801 View commit details
  2. Add LLVM IR generation for function calls.

    whitequark committed Jul 21, 2015
    Copy the full SHA
    ec9d40b View commit details
Showing with 67 additions and 33 deletions.
  1. +2 −2 artiq/compiler/ir.py
  2. +3 −3 artiq/compiler/transforms/artiq_ir_generator.py
  3. +54 −22 artiq/compiler/transforms/llvm_ir_generator.py
  4. +8 −6 artiq/compiler/validators/local_access.py
4 changes: 2 additions & 2 deletions artiq/compiler/ir.py
Original file line number Diff line number Diff line change
@@ -467,7 +467,7 @@ def add(self, base_name, typ):
def is_environment(typ):
return isinstance(typ, TEnvironment)

class EnvironmentArgument(NamedValue):
class EnvironmentArgument(Argument):
"""
A function argument specifying an outer environment.
"""
@@ -799,7 +799,7 @@ def __init__(self, func, args, name=""):
def opcode(self):
return "call"

def function(self):
def target_function(self):
return self.operands[0]

def arguments(self):
6 changes: 3 additions & 3 deletions artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ def visit_function(self, node, is_lambda):

optargs = []
for arg_name in typ.optargs:
optargs.append(ir.Argument(ir.TSSAOption(typ.optargs[arg_name]), "arg." + arg_name))
optargs.append(ir.Argument(ir.TOption(typ.optargs[arg_name]), "arg." + arg_name))

func = ir.Function(typ, ".".join(self.name), [env_arg] + args + optargs)
self.functions.append(func)
@@ -1189,11 +1189,11 @@ def visit_CallT(self, node):
optarg_typ = ir.TOption(typ.optargs[optarg_name])
for keyword in node.keywords:
if keyword.arg == optarg_name:
value = self.append(ir.Alloc(optarg_typ, [self.visit(keyword.value)]))
value = self.append(ir.Alloc([self.visit(keyword.value)], optarg_typ))
args.append(value)
break
else:
value = self.append(ir.Alloc(optarg_typ, []))
value = self.append(ir.Alloc([], optarg_typ))
args.append(value)

if self.unwind_target is None:
76 changes: 54 additions & 22 deletions artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -16,18 +16,21 @@ def __init__(self, engine, module_name, context=ll.Context()):
self.llmap = {}
self.fixups = []

def llty_of_type(self, typ, for_alloc=False, for_return=False):
def llty_of_type(self, typ, bare=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
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()
if bare:
return llty
else:
return ll.LiteralStructType([envarg, llty.as_pointer()])
elif builtins.is_none(typ):
if for_return:
return ll.VoidType()
@@ -55,7 +58,7 @@ def llty_of_type(self, typ, for_alloc=False, for_return=False):
elif ir.is_environment(typ):
llty = ll.LiteralStructType([self.llty_of_type(typ.params[name])
for name in typ.params])
if for_alloc:
if bare:
return llty
else:
return llty.as_pointer()
@@ -76,10 +79,17 @@ def llconst_of_const(self, const):
assert False

def map(self, value):
if isinstance(value, (ir.Instruction, ir.BasicBlock)):
if isinstance(value, (ir.Argument, ir.Instruction, ir.BasicBlock)):
return self.llmap[value]
elif isinstance(value, ir.Constant):
return self.llconst_of_const(value)
elif isinstance(value, ir.Function):
llfun = self.llmodule.get_global(value.name)
if llfun is None:
return ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True),
value.name)
else:
return llfun
else:
assert False

@@ -88,32 +98,38 @@ def process(self, 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.llfunction = self.llmodule.get_global(func.name)
if self.llfunction is None:
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))
self.llfunction = ll.Function(self.llmodule, llfunty, func.name)

self.llmap = {}
self.llbuilder = ll.IRBuilder()
self.fixups = []

# First, create all basic blocks.
# First, map arguments.
for arg, llarg in zip(func.arguments, self.llfunction.args):
self.llmap[arg] = llarg

# Second, 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.
# Third, 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.
# Fourth, fixup phis.
for fixup in self.fixups:
fixup()
finally:
@@ -131,7 +147,7 @@ def fixup():

def process_Alloc(self, insn):
if ir.is_environment(insn.type):
return self.llbuilder.alloca(self.llty_of_type(insn.type, for_alloc=True),
return self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True),
name=insn.name)
elif builtins.is_list(insn.type):
llsize = self.map(insn.operands[0])
@@ -171,7 +187,14 @@ def process_GetLocal(self, insn):
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)
llvalue = self.map(insn.value())
if llptr.type.pointee != llvalue.type:
# The environment argument is an i8*, so that all closures can
# unify with each other regardless of environment type or size.
# We fixup the type on assignment into the ".outer" slot.
assert isinstance(insn.value(), ir.EnvironmentArgument)
llvalue = self.llbuilder.bitcast(llvalue, llptr.type.pointee)
return self.llbuilder.store(llvalue, llptr)

def attr_index(self, insn):
return list(insn.object().type.attributes.keys()).index(insn.attr)
@@ -362,11 +385,20 @@ def get_outer(llenv, env_ty):
else:
assert False

# def process_Closure(self, insn):
# pass
def process_Closure(self, insn):
llvalue = ll.Constant(self.llty_of_type(insn.target_function.type), ll.Undefined)
llenv = self.llbuilder.bitcast(self.map(insn.environment()), ll.IntType(8).as_pointer())
llvalue = self.llbuilder.insert_value(llvalue, llenv, 0)
llvalue = self.llbuilder.insert_value(llvalue, self.map(insn.target_function), 1,
name=insn.name)
return llvalue

# def process_Call(self, insn):
# pass
def process_Call(self, insn):
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
llenv = self.llbuilder.extract_value(llclosure, 0)
llfun = self.llbuilder.extract_value(llclosure, 1)
return self.llbuilder.call(llfun, [llenv] + list(llargs),
name=insn.name)

def process_Select(self, insn):
return self.llbuilder.select(self.map(insn.cond()),
@@ -383,7 +415,7 @@ def process_BranchIf(self, insn):
# pass

def process_Return(self, insn):
if builtins.is_none(insn.type):
if builtins.is_none(insn.value().type):
return self.llbuilder.ret_void()
else:
return self.llbuilder.ret(self.llmap[insn.value()])
14 changes: 8 additions & 6 deletions artiq/compiler/validators/local_access.py
Original file line number Diff line number Diff line change
@@ -99,12 +99,14 @@ def pred_at_fault(env, var_name):

if isinstance(insn, ir.Closure):
env = insn.environment()
for var_name in block_state[env]:
if not block_state[env][var_name]:
# A closure would capture this variable while it is not always
# initialized. Note that this check is transitive.
self._uninitialized_access(insn, var_name,
pred_at_fault(env, var_name))
# Make sure this environment has any interesting variables.
if env in block_state:
for var_name in block_state[env]:
if not block_state[env][var_name]:
# A closure would capture this variable while it is not always
# initialized. Note that this check is transitive.
self._uninitialized_access(insn, var_name,
pred_at_fault(env, var_name))

# Save the state.
state[block] = block_state