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: 435559fe501a
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: 67967b9cba19
Choose a head ref
  • 2 commits
  • 8 files changed
  • 1 contributor

Commits on Aug 10, 2015

  1. Implement syscalls for the new compiler.

    whitequark committed Aug 10, 2015
    Copy the full SHA
    b6bc868 View commit details
  2. ARTIQException: tell linecache where to look for runtime sources.

    Runtime sources can appear in the backtrace when
    artiq_raise_from_c is used.
    whitequark committed Aug 10, 2015
    Copy the full SHA
    67967b9 View commit details
104 changes: 75 additions & 29 deletions artiq/compiler/embedding.py
Original file line number Diff line number Diff line change
@@ -238,6 +238,10 @@ def _function_loc(self, function):
name = function.__code__.co_name

source_line = linecache.getline(filename, line)
while source_line.lstrip().startswith("@"):
line += 1
source_line = linecache.getline(filename, line)

column = re.search("def", source_line).start(0)
source_buffer = source.Buffer(source_line, filename, line)
return source.Range(source_buffer, column, column)
@@ -248,11 +252,16 @@ def _function_def_note(self, function):
{"function": function.__name__},
self._function_loc(function))

def _extract_annot(self, function, annot, kind, call_loc):
def _extract_annot(self, function, annot, kind, call_loc, is_syscall):
if not isinstance(annot, types.Type):
note = diagnostic.Diagnostic("note",
"in function called remotely here", {},
call_loc)
if is_syscall:
note = diagnostic.Diagnostic("note",
"in system call here", {},
call_loc)
else:
note = diagnostic.Diagnostic("note",
"in function called remotely here", {},
call_loc)
diag = diagnostic.Diagnostic("error",
"type annotation for {kind}, '{annot}', is not an ARTIQ type",
{"kind": kind, "annot": repr(annot)},
@@ -264,11 +273,19 @@ def _extract_annot(self, function, annot, kind, call_loc):
else:
return annot

def _type_of_param(self, function, loc, param):
def _type_of_param(self, function, loc, param, is_syscall):
if param.annotation is not inspect.Parameter.empty:
# Type specified explicitly.
return self._extract_annot(function, param.annotation,
"argument {}".format(param.name), loc)
"argument '{}'".format(param.name), loc,
is_syscall)
elif is_syscall:
# Syscalls must be entirely annotated.
diag = diagnostic.Diagnostic("error",
"system call argument '{argument}' must have a type annotation",
{"argument": param.name},
self._function_loc(function))
self.engine.process(diag)
elif param.default is not inspect.Parameter.empty:
# Try and infer the type from the default value.
# This is tricky, because the default value might not have
@@ -281,8 +298,8 @@ def _type_of_param(self, function, loc, param):
def proxy_diagnostic(diag):
note = diagnostic.Diagnostic("note",
"expanded from here while trying to infer a type for an"
" unannotated optional argument '{param_name}' from its default value",
{"param_name": param.name},
" unannotated optional argument '{argument}' from its default value",
{"argument": param.name},
loc)
diag.notes.append(note)

@@ -300,7 +317,7 @@ def proxy_diagnostic(diag):
# Let the rest of the program decide.
return types.TVar()

def _quote_rpc_function(self, function, loc):
def _quote_foreign_function(self, function, loc, syscall):
signature = inspect.signature(function)

arg_types = OrderedDict()
@@ -318,44 +335,73 @@ def _quote_rpc_function(self, function, loc):
continue

if param.default is inspect.Parameter.empty:
arg_types[param.name] = self._type_of_param(function, loc, param)
arg_types[param.name] = self._type_of_param(function, loc, param,
is_syscall=syscall is not None)
elif syscall is None:
optarg_types[param.name] = self._type_of_param(function, loc, param,
is_syscall=syscall is not None)
else:
optarg_types[param.name] = self._type_of_param(function, loc, param)
diag = diagnostic.Diagnostic("error",
"system call argument '{argument}' must not have a default value",
{"argument": param.name},
self._function_loc(function))
self.engine.process(diag)

if signature.return_annotation is not inspect.Signature.empty:
ret_type = self._extract_annot(function, signature.return_annotation,
"return type", loc)
else:
diag = diagnostic.Diagnostic("fatal",
"function must have a return type specified to be called remotely", {},
"return type", loc, is_syscall=syscall is not None)
elif syscall is None:
diag = diagnostic.Diagnostic("error",
"function must have a return type annotation to be called remotely", {},
self._function_loc(function))
self.engine.process(diag)
ret_type = types.TVar()
else: # syscall is not None
diag = diagnostic.Diagnostic("error",
"system call must have a return type annotation", {},
self._function_loc(function))
self.engine.process(diag)
ret_type = types.TVar()

rpc_type = types.TRPCFunction(arg_types, optarg_types, ret_type,
service=self._map(function))
if syscall is None:
function_type = types.TRPCFunction(arg_types, optarg_types, ret_type,
service=self._map(function))
function_name = "__rpc_{}__".format(function_type.service)
else:
function_type = types.TCFunction(arg_types, ret_type,
name=syscall)
function_name = "__ffi_{}__".format(function_type.name)

rpc_name = "__rpc_{}__".format(rpc_type.service)
self.globals[rpc_name] = rpc_type
self.functions[function] = rpc_name
self.globals[function_name] = function_type
self.functions[function] = function_name

return rpc_name
return function_name

def _quote_function(self, function, loc):
if function in self.functions:
return self.functions[function]

if hasattr(function, "artiq_embedded"):
# Insert the typed AST for the new function and restart inference.
# It doesn't really matter where we insert as long as it is before
# the final call.
function_node = self._quote_embedded_function(function)
self.typedtree.insert(0, function_node)
self.inference_finished = False
return function_node.name
if function.artiq_embedded.function is not None:
# Insert the typed AST for the new function and restart inference.
# It doesn't really matter where we insert as long as it is before
# the final call.
function_node = self._quote_embedded_function(function)
self.typedtree.insert(0, function_node)
self.inference_finished = False
return function_node.name
elif function.artiq_embedded.syscall is not None:
# Insert a storage-less global whose type instructs the compiler
# to perform a system call instead of a regular call.
return self._quote_foreign_function(function, loc,
syscall=function.artiq_embedded.syscall)
else:
assert False
else:
# Insert a storage-less global whose type instructs the compiler
# to perform an RPC instead of a regular call.
return self._quote_rpc_function(function, loc)
return self._quote_foreign_function(function, loc,
syscall=None)

def stitch_call(self, function, args, kwargs):
function_node = self._quote_embedded_function(function)
25 changes: 21 additions & 4 deletions artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -175,7 +175,7 @@ def llty_of_type(self, typ, bare=False, for_return=False):
typ = typ.find()
if types.is_tuple(typ):
return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.elts])
elif types.is_rpc_function(typ):
elif types.is_rpc_function(typ) or types.is_c_function(typ):
if for_return:
return llvoid
else:
@@ -731,11 +731,20 @@ def process_Closure(self, insn):
return llvalue

def _prepare_closure_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)
llclosure = self.map(insn.target_function())
llargs = [self.map(arg) for arg in insn.arguments()]
llenv = self.llbuilder.extract_value(llclosure, 0)
llfun = self.llbuilder.extract_value(llclosure, 1)
return llfun, [llenv] + list(llargs)

def _prepare_ffi_call(self, insn):
llargs = [self.map(arg) for arg in insn.arguments()]
llfunty = ll.FunctionType(self.llty_of_type(insn.type, for_return=True),
[llarg.type for llarg in llargs])
llfun = ll.Function(self.llmodule, llfunty,
insn.target_function().type.name)
return llfun, list(llargs)

# See session.c:{send,receive}_rpc_value and comm_generic.py:_{send,receive}_rpc_value.
def _rpc_tag(self, typ, error_handler):
if types.is_tuple(typ):
@@ -869,6 +878,10 @@ def process_Call(self, insn):
insn.target_function().type,
insn.arguments(),
llnormalblock=None, llunwindblock=None)
elif types.is_c_function(insn.target_function().type):
llfun, llargs = self._prepare_ffi_call(insn)
return self.llbuilder.call(llfun, llargs,
name=insn.name)
else:
llfun, llargs = self._prepare_closure_call(insn)
return self.llbuilder.call(llfun, llargs,
@@ -882,6 +895,10 @@ def process_Invoke(self, insn):
insn.target_function().type,
insn.arguments(),
llnormalblock, llunwindblock)
elif types.is_c_function(insn.target_function().type):
llfun, llargs = self._prepare_ffi_call(insn)
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
name=insn.name)
else:
llfun, llargs = self._prepare_closure_call(insn)
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
27 changes: 26 additions & 1 deletion artiq/compiler/types.py
Original file line number Diff line number Diff line change
@@ -258,6 +258,26 @@ def unify(self, other):
else:
raise UnificationError(self, other)

class TCFunction(TFunction):
"""
A function type of a runtime-provided C function.
:ivar name: (str) C function name
"""

def __init__(self, args, ret, name):
super().__init__(args, OrderedDict(), ret)
self.name = name

def unify(self, other):
if isinstance(other, TCFunction) and \
self.name == other.name:
super().unify(other)
elif isinstance(other, TVar):
other.unify(self)
else:
raise UnificationError(self, other)

class TBuiltin(Type):
"""
An instance of builtin type. Every instance of a builtin
@@ -371,6 +391,9 @@ def is_function(typ):
def is_rpc_function(typ):
return isinstance(typ.find(), TRPCFunction)

def is_c_function(typ):
return isinstance(typ.find(), TCFunction)

def is_builtin(typ, name=None):
typ = typ.find()
if name is None:
@@ -423,14 +446,16 @@ def name(self, typ):
return "(%s,)" % self.name(typ.elts[0])
else:
return "(%s)" % ", ".join(list(map(self.name, typ.elts)))
elif isinstance(typ, (TFunction, TRPCFunction)):
elif isinstance(typ, (TFunction, TRPCFunction, TCFunction)):
args = []
args += [ "%s:%s" % (arg, self.name(typ.args[arg])) for arg in typ.args]
args += ["?%s:%s" % (arg, self.name(typ.optargs[arg])) for arg in typ.optargs]
signature = "(%s)->%s" % (", ".join(args), self.name(typ.ret))

if isinstance(typ, TRPCFunction):
return "rpc({}) {}".format(typ.service, signature)
if isinstance(typ, TCFunction):
return "ffi({}) {}".format(repr(typ.name), signature)
elif isinstance(typ, TFunction):
return signature
elif isinstance(typ, TBuiltinFunction):
26 changes: 22 additions & 4 deletions artiq/coredevice/dds.py
Original file line number Diff line number Diff line change
@@ -9,6 +9,24 @@
PHASE_MODE_TRACKING = 2


@syscall
def dds_init(time_mu: TInt64, channel: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")

@syscall
def dds_batch_enter(time_mu: TInt64) -> TNone:
raise NotImplementedError("syscall not simulated")

@syscall
def dds_batch_exit() -> TNone:
raise NotImplementedError("syscall not simulated")

@syscall
def dds_set(time_mu: TInt64, channel: TInt32, ftw: TInt32,
pow: TInt32, phase_mode: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")


class _BatchContextManager:
def __init__(self, dds_bus):
self.dds_bus = dds_bus
@@ -34,13 +52,13 @@ def __init__(self, dmgr):
def batch_enter(self):
"""Starts a DDS command batch. All DDS commands are buffered
after this call, until ``batch_exit`` is called."""
syscall("dds_batch_enter", now_mu())
dds_batch_enter(now_mu())

@kernel
def batch_exit(self):
"""Ends a DDS command batch. All buffered DDS commands are issued
on the bus, and FUD is pulsed at the time the batch started."""
syscall("dds_batch_exit")
dds_batch_exit()


class _DDSGeneric:
@@ -91,7 +109,7 @@ def init(self):
"""Resets and initializes the DDS channel.
The runtime does this for all channels upon core device startup."""
syscall("dds_init", now_mu(), self.channel)
dds_init(now_mu(), self.channel)

@kernel
def set_phase_mode(self, phase_mode):
@@ -128,7 +146,7 @@ def set_mu(self, frequency, phase=0, phase_mode=_PHASE_MODE_DEFAULT):
"""
if phase_mode == _PHASE_MODE_DEFAULT:
phase_mode = self.phase_mode
syscall("dds_set", now_mu(), self.channel,
dds_set(now_mu(), self.channel,
frequency, round(phase*2**self.pow_width), phase_mode)

@kernel
Loading