forked from M-Labs/artiq
compiler: allow flagging syscalls, providing information to optimizer.
This also fixes a crash in test_cache introduced in 1d8b0d46
.
This commit is contained in:
parent
049bd11bd6
commit
6f5332f892
@ -693,7 +693,7 @@ class Stitcher:
|
||||
# Let the rest of the program decide.
|
||||
return types.TVar()
|
||||
|
||||
def _quote_foreign_function(self, function, loc, syscall):
|
||||
def _quote_foreign_function(self, function, loc, syscall, flags):
|
||||
signature = inspect.signature(function)
|
||||
|
||||
arg_types = OrderedDict()
|
||||
@ -742,7 +742,7 @@ class Stitcher:
|
||||
service=self.object_map.store(function))
|
||||
else:
|
||||
function_type = types.TCFunction(arg_types, ret_type,
|
||||
name=syscall)
|
||||
name=syscall, flags=flags)
|
||||
|
||||
self.functions[function] = function_type
|
||||
|
||||
@ -779,7 +779,8 @@ class Stitcher:
|
||||
# Insert a storage-less global whose type instructs the compiler
|
||||
# to perform a system call instead of a regular call.
|
||||
self._quote_foreign_function(function, loc,
|
||||
syscall=function.artiq_embedded.syscall)
|
||||
syscall=function.artiq_embedded.syscall,
|
||||
flags=function.artiq_embedded.flags)
|
||||
elif function.artiq_embedded.forbidden is not None:
|
||||
diag = diagnostic.Diagnostic("fatal",
|
||||
"this function cannot be called as an RPC", {},
|
||||
@ -791,7 +792,7 @@ class Stitcher:
|
||||
else:
|
||||
# Insert a storage-less global whose type instructs the compiler
|
||||
# to perform an RPC instead of a regular call.
|
||||
self._quote_foreign_function(function, loc, syscall=None)
|
||||
self._quote_foreign_function(function, loc, syscall=None, flags=None)
|
||||
|
||||
function_type = self.functions[function]
|
||||
if types.is_rpc_function(function_type):
|
||||
|
@ -184,8 +184,8 @@ class LLVMIRGenerator:
|
||||
self.tbaa_tree = self.llmodule.add_metadata([
|
||||
ll.MetaDataString(self.llmodule, "ARTIQ TBAA")
|
||||
])
|
||||
self.tbaa_noalias_call = self.llmodule.add_metadata([
|
||||
ll.MetaDataString(self.llmodule, "non-aliasing function call"),
|
||||
self.tbaa_nowrite_call = self.llmodule.add_metadata([
|
||||
ll.MetaDataString(self.llmodule, "ref-only function call"),
|
||||
self.tbaa_tree,
|
||||
ll.Constant(lli64, 1)
|
||||
])
|
||||
@ -401,6 +401,9 @@ class LLVMIRGenerator:
|
||||
llglobal = ll.Function(self.llmodule, llty, name)
|
||||
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
|
||||
llglobal.attributes.add("noreturn")
|
||||
if name in ("rtio_log", "send_rpc", "watchdog_set", "watchdog_clear",
|
||||
self.target.print_function):
|
||||
llglobal.attributes.add("nounwind")
|
||||
else:
|
||||
llglobal = ll.GlobalVariable(self.llmodule, llty, name)
|
||||
|
||||
@ -1117,6 +1120,8 @@ class LLVMIRGenerator:
|
||||
byvals = [i + 1 for i in byvals]
|
||||
for i in byvals:
|
||||
llfun.args[i].add_attribute('byval')
|
||||
if 'nounwind' in insn.target_function().type.flags:
|
||||
llfun.attributes.add('nounwind')
|
||||
|
||||
return llfun, list(llargs)
|
||||
|
||||
@ -1282,14 +1287,14 @@ class LLVMIRGenerator:
|
||||
else:
|
||||
llcall = llresult = self.llbuilder.call(llfun, llargs, name=insn.name)
|
||||
|
||||
# Never add TBAA nowrite metadata to a functon with sret!
|
||||
# This leads to miscompilations.
|
||||
if types.is_c_function(functiontyp) and 'nowrite' in functiontyp.flags:
|
||||
llcall.metadata['tbaa'] = self.tbaa_nowrite_call
|
||||
|
||||
if insn.is_cold:
|
||||
llcall.cconv = 'coldcc'
|
||||
|
||||
if types.is_c_function(functiontyp):
|
||||
# All our global state is confined to our compilation unit,
|
||||
# so by definition no FFI call can mutate it.
|
||||
llcall.metadata['tbaa'] = self.tbaa_noalias_call
|
||||
|
||||
return llresult
|
||||
|
||||
def process_Invoke(self, insn):
|
||||
|
@ -342,14 +342,21 @@ class TCFunction(TFunction):
|
||||
A function type of a runtime-provided C function.
|
||||
|
||||
:ivar name: (str) C function name
|
||||
:ivar flags: (set of str) C function flags.
|
||||
Flag ``nounwind`` means the function never raises an exception.
|
||||
Flag ``nowrite`` means the function never writes any memory
|
||||
that the ARTIQ Python code can observe.
|
||||
"""
|
||||
|
||||
attributes = OrderedDict()
|
||||
|
||||
def __init__(self, args, ret, name):
|
||||
def __init__(self, args, ret, name, flags={}):
|
||||
for flag in flags:
|
||||
assert flag in {'nounwind', 'nowrite'}
|
||||
super().__init__(args, OrderedDict(), ret)
|
||||
self.name = name
|
||||
self.delay = TFixedDelay(iodelay.Const(0))
|
||||
self.flags = flags
|
||||
|
||||
def unify(self, other):
|
||||
if isinstance(other, TCFunction) and \
|
||||
|
@ -2,11 +2,11 @@ from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("cache_get", flags={"nounwind", "nowrite"})
|
||||
def cache_get(key: TStr) -> TList(TInt32):
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
@syscall("cache_put", flags={"nowrite"})
|
||||
def cache_put(key: TStr, value: TList(TInt32)) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
@ -37,7 +37,7 @@ class CompileError(Exception):
|
||||
return "\n" + _render_diagnostic(self.diagnostic, colored=colors_supported)
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("rtio_get_counter", flags={"nounwind", "nowrite"})
|
||||
def rtio_get_counter() -> TInt64:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
@ -10,20 +10,20 @@ PHASE_MODE_ABSOLUTE = 1
|
||||
PHASE_MODE_TRACKING = 2
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("dds_init", flags={"nowrite"})
|
||||
def dds_init(time_mu: TInt64, bus_channel: TInt32, channel: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
@syscall("dds_set", flags={"nowrite"})
|
||||
def dds_set(time_mu: TInt64, bus_channel: TInt32, channel: TInt32, ftw: TInt32,
|
||||
pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
@syscall("dds_batch_enter", flags={"nowrite"})
|
||||
def dds_batch_enter(time_mu: TInt64) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
@syscall("dds_batch_exit", flags={"nowrite"})
|
||||
def dds_batch_exit() -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
@ -3,27 +3,27 @@ from artiq.language.types import TBool, TInt32, TNone
|
||||
from artiq.coredevice.exceptions import I2CError
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("i2c_init", flags={"nowrite"})
|
||||
def i2c_init(busno: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("i2c_start", flags={"nounwind", "nowrite"})
|
||||
def i2c_start(busno: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("i2c_stop", flags={"nounwind", "nowrite"})
|
||||
def i2c_stop(busno: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("i2c_write", flags={"nounwind", "nowrite"})
|
||||
def i2c_write(busno: TInt32, b: TInt32) -> TBool:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("i2c_read", flags={"nounwind", "nowrite"})
|
||||
def i2c_read(busno: TInt32, ack: TBool) -> TInt32:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
@ -2,17 +2,17 @@ from artiq.language.core import syscall
|
||||
from artiq.language.types import TInt64, TInt32, TNone
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("rtio_output", flags={"nowrite"})
|
||||
def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32
|
||||
) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("rtio_input_timestamp", flags={"nowrite"})
|
||||
def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
@syscall
|
||||
@syscall("rtio_input_data", flags={"nowrite"})
|
||||
def rtio_input_data(channel: TInt32) -> TInt32:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
@ -163,7 +163,7 @@ def round(value, width=32):
|
||||
|
||||
|
||||
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo",
|
||||
"core_name function syscall forbidden")
|
||||
"core_name function syscall forbidden flags")
|
||||
|
||||
def kernel(arg):
|
||||
"""
|
||||
@ -192,7 +192,7 @@ def kernel(arg):
|
||||
return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs)
|
||||
run_on_core.artiq_embedded = _ARTIQEmbeddedInfo(
|
||||
core_name=arg, function=function, syscall=None,
|
||||
forbidden=False)
|
||||
forbidden=False, flags={})
|
||||
return run_on_core
|
||||
return inner_decorator
|
||||
else:
|
||||
@ -210,10 +210,10 @@ def portable(function):
|
||||
"""
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=function, syscall=None,
|
||||
forbidden=False)
|
||||
forbidden=False, flags={})
|
||||
return function
|
||||
|
||||
def syscall(arg):
|
||||
def syscall(arg, flags={}):
|
||||
"""
|
||||
This decorator marks a function as a system call. When executed on a core
|
||||
device, a C function with the provided name (or the same name as
|
||||
@ -228,7 +228,8 @@ def syscall(arg):
|
||||
def inner_decorator(function):
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=None,
|
||||
syscall=function.__name__, forbidden=False)
|
||||
syscall=function.__name__, forbidden=False,
|
||||
flags=flags)
|
||||
return function
|
||||
return inner_decorator
|
||||
else:
|
||||
@ -241,7 +242,7 @@ def host_only(function):
|
||||
"""
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=None, syscall=None,
|
||||
forbidden=True)
|
||||
forbidden=True, flags={})
|
||||
return function
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user