compiler: allow flagging syscalls, providing information to optimizer.

This also fixes a crash in test_cache introduced in 1d8b0d46.
This commit is contained in:
whitequark 2016-03-28 19:56:53 +00:00
parent 049bd11bd6
commit 6f5332f892
9 changed files with 47 additions and 33 deletions

View File

@ -693,7 +693,7 @@ class Stitcher:
# Let the rest of the program decide. # Let the rest of the program decide.
return types.TVar() return types.TVar()
def _quote_foreign_function(self, function, loc, syscall): def _quote_foreign_function(self, function, loc, syscall, flags):
signature = inspect.signature(function) signature = inspect.signature(function)
arg_types = OrderedDict() arg_types = OrderedDict()
@ -742,7 +742,7 @@ class Stitcher:
service=self.object_map.store(function)) service=self.object_map.store(function))
else: else:
function_type = types.TCFunction(arg_types, ret_type, function_type = types.TCFunction(arg_types, ret_type,
name=syscall) name=syscall, flags=flags)
self.functions[function] = function_type self.functions[function] = function_type
@ -779,7 +779,8 @@ class Stitcher:
# Insert a storage-less global whose type instructs the compiler # Insert a storage-less global whose type instructs the compiler
# to perform a system call instead of a regular call. # to perform a system call instead of a regular call.
self._quote_foreign_function(function, loc, 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: elif function.artiq_embedded.forbidden is not None:
diag = diagnostic.Diagnostic("fatal", diag = diagnostic.Diagnostic("fatal",
"this function cannot be called as an RPC", {}, "this function cannot be called as an RPC", {},
@ -791,7 +792,7 @@ class Stitcher:
else: else:
# Insert a storage-less global whose type instructs the compiler # Insert a storage-less global whose type instructs the compiler
# to perform an RPC instead of a regular call. # 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] function_type = self.functions[function]
if types.is_rpc_function(function_type): if types.is_rpc_function(function_type):

View File

@ -184,8 +184,8 @@ class LLVMIRGenerator:
self.tbaa_tree = self.llmodule.add_metadata([ self.tbaa_tree = self.llmodule.add_metadata([
ll.MetaDataString(self.llmodule, "ARTIQ TBAA") ll.MetaDataString(self.llmodule, "ARTIQ TBAA")
]) ])
self.tbaa_noalias_call = self.llmodule.add_metadata([ self.tbaa_nowrite_call = self.llmodule.add_metadata([
ll.MetaDataString(self.llmodule, "non-aliasing function call"), ll.MetaDataString(self.llmodule, "ref-only function call"),
self.tbaa_tree, self.tbaa_tree,
ll.Constant(lli64, 1) ll.Constant(lli64, 1)
]) ])
@ -401,6 +401,9 @@ class LLVMIRGenerator:
llglobal = ll.Function(self.llmodule, llty, name) llglobal = ll.Function(self.llmodule, llty, name)
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
llglobal.attributes.add("noreturn") llglobal.attributes.add("noreturn")
if name in ("rtio_log", "send_rpc", "watchdog_set", "watchdog_clear",
self.target.print_function):
llglobal.attributes.add("nounwind")
else: else:
llglobal = ll.GlobalVariable(self.llmodule, llty, name) llglobal = ll.GlobalVariable(self.llmodule, llty, name)
@ -1117,6 +1120,8 @@ class LLVMIRGenerator:
byvals = [i + 1 for i in byvals] byvals = [i + 1 for i in byvals]
for i in byvals: for i in byvals:
llfun.args[i].add_attribute('byval') llfun.args[i].add_attribute('byval')
if 'nounwind' in insn.target_function().type.flags:
llfun.attributes.add('nounwind')
return llfun, list(llargs) return llfun, list(llargs)
@ -1282,14 +1287,14 @@ class LLVMIRGenerator:
else: else:
llcall = llresult = self.llbuilder.call(llfun, llargs, name=insn.name) 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: if insn.is_cold:
llcall.cconv = 'coldcc' 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 return llresult
def process_Invoke(self, insn): def process_Invoke(self, insn):

View File

@ -342,14 +342,21 @@ class TCFunction(TFunction):
A function type of a runtime-provided C function. A function type of a runtime-provided C function.
:ivar name: (str) C function name :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() 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) super().__init__(args, OrderedDict(), ret)
self.name = name self.name = name
self.delay = TFixedDelay(iodelay.Const(0)) self.delay = TFixedDelay(iodelay.Const(0))
self.flags = flags
def unify(self, other): def unify(self, other):
if isinstance(other, TCFunction) and \ if isinstance(other, TCFunction) and \

View File

@ -2,11 +2,11 @@ from artiq.language.core import *
from artiq.language.types import * from artiq.language.types import *
@syscall @syscall("cache_get", flags={"nounwind", "nowrite"})
def cache_get(key: TStr) -> TList(TInt32): def cache_get(key: TStr) -> TList(TInt32):
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("cache_put", flags={"nowrite"})
def cache_put(key: TStr, value: TList(TInt32)) -> TNone: def cache_put(key: TStr, value: TList(TInt32)) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")

View File

@ -37,7 +37,7 @@ class CompileError(Exception):
return "\n" + _render_diagnostic(self.diagnostic, colored=colors_supported) return "\n" + _render_diagnostic(self.diagnostic, colored=colors_supported)
@syscall @syscall("rtio_get_counter", flags={"nounwind", "nowrite"})
def rtio_get_counter() -> TInt64: def rtio_get_counter() -> TInt64:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")

View File

@ -10,20 +10,20 @@ PHASE_MODE_ABSOLUTE = 1
PHASE_MODE_TRACKING = 2 PHASE_MODE_TRACKING = 2
@syscall @syscall("dds_init", flags={"nowrite"})
def dds_init(time_mu: TInt64, bus_channel: TInt32, channel: TInt32) -> TNone: def dds_init(time_mu: TInt64, bus_channel: TInt32, channel: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("dds_set", flags={"nowrite"})
def dds_set(time_mu: TInt64, bus_channel: TInt32, channel: TInt32, ftw: TInt32, def dds_set(time_mu: TInt64, bus_channel: TInt32, channel: TInt32, ftw: TInt32,
pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone: pow: TInt32, phase_mode: TInt32, amplitude: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("dds_batch_enter", flags={"nowrite"})
def dds_batch_enter(time_mu: TInt64) -> TNone: def dds_batch_enter(time_mu: TInt64) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("dds_batch_exit", flags={"nowrite"})
def dds_batch_exit() -> TNone: def dds_batch_exit() -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")

View File

@ -3,27 +3,27 @@ from artiq.language.types import TBool, TInt32, TNone
from artiq.coredevice.exceptions import I2CError from artiq.coredevice.exceptions import I2CError
@syscall @syscall("i2c_init", flags={"nowrite"})
def i2c_init(busno: TInt32) -> TNone: def i2c_init(busno: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("i2c_start", flags={"nounwind", "nowrite"})
def i2c_start(busno: TInt32) -> TNone: def i2c_start(busno: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("i2c_stop", flags={"nounwind", "nowrite"})
def i2c_stop(busno: TInt32) -> TNone: def i2c_stop(busno: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("i2c_write", flags={"nounwind", "nowrite"})
def i2c_write(busno: TInt32, b: TInt32) -> TBool: def i2c_write(busno: TInt32, b: TInt32) -> TBool:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("i2c_read", flags={"nounwind", "nowrite"})
def i2c_read(busno: TInt32, ack: TBool) -> TInt32: def i2c_read(busno: TInt32, ack: TBool) -> TInt32:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")

View File

@ -2,17 +2,17 @@ from artiq.language.core import syscall
from artiq.language.types import TInt64, TInt32, TNone 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 def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32
) -> TNone: ) -> TNone:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("rtio_input_timestamp", flags={"nowrite"})
def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64: def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")
@syscall @syscall("rtio_input_data", flags={"nowrite"})
def rtio_input_data(channel: TInt32) -> TInt32: def rtio_input_data(channel: TInt32) -> TInt32:
raise NotImplementedError("syscall not simulated") raise NotImplementedError("syscall not simulated")

View File

@ -163,7 +163,7 @@ def round(value, width=32):
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo", _ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo",
"core_name function syscall forbidden") "core_name function syscall forbidden flags")
def kernel(arg): def kernel(arg):
""" """
@ -192,7 +192,7 @@ def kernel(arg):
return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs) return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs)
run_on_core.artiq_embedded = _ARTIQEmbeddedInfo( run_on_core.artiq_embedded = _ARTIQEmbeddedInfo(
core_name=arg, function=function, syscall=None, core_name=arg, function=function, syscall=None,
forbidden=False) forbidden=False, flags={})
return run_on_core return run_on_core
return inner_decorator return inner_decorator
else: else:
@ -210,10 +210,10 @@ def portable(function):
""" """
function.artiq_embedded = \ function.artiq_embedded = \
_ARTIQEmbeddedInfo(core_name=None, function=function, syscall=None, _ARTIQEmbeddedInfo(core_name=None, function=function, syscall=None,
forbidden=False) forbidden=False, flags={})
return function return function
def syscall(arg): def syscall(arg, flags={}):
""" """
This decorator marks a function as a system call. When executed on a core 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 device, a C function with the provided name (or the same name as
@ -228,7 +228,8 @@ def syscall(arg):
def inner_decorator(function): def inner_decorator(function):
function.artiq_embedded = \ function.artiq_embedded = \
_ARTIQEmbeddedInfo(core_name=None, function=None, _ARTIQEmbeddedInfo(core_name=None, function=None,
syscall=function.__name__, forbidden=False) syscall=function.__name__, forbidden=False,
flags=flags)
return function return function
return inner_decorator return inner_decorator
else: else:
@ -241,7 +242,7 @@ def host_only(function):
""" """
function.artiq_embedded = \ function.artiq_embedded = \
_ARTIQEmbeddedInfo(core_name=None, function=None, syscall=None, _ARTIQEmbeddedInfo(core_name=None, function=None, syscall=None,
forbidden=True) forbidden=True, flags={})
return function return function