Skip to content

Commit

Permalink
Implement {delay,now,at}{,_mu} and {mu,seconds}_to_{seconds,mu}.
Browse files Browse the repository at this point in the history
whitequark committed Aug 31, 2015
1 parent 5151adb commit 501ba91
Showing 22 changed files with 177 additions and 155 deletions.
24 changes: 24 additions & 0 deletions artiq/compiler/builtins.py
Original file line number Diff line number Diff line change
@@ -138,6 +138,30 @@ def fn_print():
def fn_kernel():
return types.TBuiltinFunction("kernel")

def fn_now():
return types.TBuiltinFunction("now")

def fn_delay():
return types.TBuiltinFunction("delay")

def fn_at():
return types.TBuiltinFunction("at")

def fn_now_mu():
return types.TBuiltinFunction("now_mu")

def fn_delay_mu():
return types.TBuiltinFunction("delay_mu")

def fn_at_mu():
return types.TBuiltinFunction("at_mu")

def fn_mu_to_seconds():
return types.TBuiltinFunction("mu_to_seconds")

def fn_seconds_to_mu():
return types.TBuiltinFunction("seconds_to_mu")

# Accessors

def is_none(typ):
5 changes: 3 additions & 2 deletions artiq/compiler/module.py
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ def from_filename(cls, filename, engine=None):
return cls(source.Buffer(f.read(), filename, 1), engine=engine)

class Module:
def __init__(self, src):
def __init__(self, src, ref_period=1e-6):
self.engine = src.engine
self.object_map = src.object_map

@@ -51,7 +51,8 @@ def __init__(self, src):
monomorphism_validator = validators.MonomorphismValidator(engine=self.engine)
escape_validator = validators.EscapeValidator(engine=self.engine)
artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine,
module_name=src.name)
module_name=src.name,
ref_period=ref_period)
dead_code_eliminator = transforms.DeadCodeEliminator(engine=self.engine)
local_access_validator = validators.LocalAccessValidator(engine=self.engine)

17 changes: 17 additions & 0 deletions artiq/compiler/prelude.py
Original file line number Diff line number Diff line change
@@ -7,17 +7,34 @@

def globals():
return {
# Value constructors
"bool": builtins.fn_bool(),
"int": builtins.fn_int(),
"float": builtins.fn_float(),
"list": builtins.fn_list(),
"range": builtins.fn_range(),

# Exception constructors
"Exception": builtins.fn_Exception(),
"IndexError": builtins.fn_IndexError(),
"ValueError": builtins.fn_ValueError(),
"ZeroDivisionError": builtins.fn_ZeroDivisionError(),

# Built-in Python functions
"len": builtins.fn_len(),
"round": builtins.fn_round(),
"print": builtins.fn_print(),

# ARTIQ decorators
"kernel": builtins.fn_kernel(),

# ARTIQ time management functions
"now": builtins.fn_now(),
"delay": builtins.fn_delay(),
"at": builtins.fn_at(),
"now_mu": builtins.fn_now_mu(),
"delay_mu": builtins.fn_delay_mu(),
"at_mu": builtins.fn_at_mu(),
"mu_to_seconds": builtins.fn_mu_to_seconds(),
"seconds_to_mu": builtins.fn_seconds_to_mu(),
}
6 changes: 3 additions & 3 deletions artiq/compiler/testbench/jit.py
Original file line number Diff line number Diff line change
@@ -5,9 +5,9 @@
from ..targets import NativeTarget

def main():
libartiq_personality = os.getenv('LIBARTIQ_PERSONALITY')
if libartiq_personality is not None:
llvm.load_library_permanently(libartiq_personality)
libartiq_support = os.getenv('LIBARTIQ_SUPPORT')
if libartiq_support is not None:
llvm.load_library_permanently(libartiq_support)

def process_diagnostic(diag):
print("\n".join(diag.render()))
36 changes: 35 additions & 1 deletion artiq/compiler/transforms/artiq_ir_generator.py
Original file line number Diff line number Diff line change
@@ -67,10 +67,11 @@ class ARTIQIRGenerator(algorithm.Visitor):

_size_type = builtins.TInt(types.TValue(32))

def __init__(self, module_name, engine):
def __init__(self, module_name, engine, ref_period):
self.engine = engine
self.functions = []
self.name = [module_name] if module_name != "" else []
self.ref_period = ir.Constant(ref_period, builtins.TFloat())
self.current_loc = None
self.current_function = None
self.current_class = None
@@ -1409,6 +1410,39 @@ def body_gen(index):
self.polymorphic_print([self.visit(arg) for arg in node.args],
separator=" ", suffix="\n")
return ir.Constant(None, builtins.TNone())
elif types.is_builtin(typ, "now"):
if len(node.args) == 0 and len(node.keywords) == 0:
now_mu = self.append(ir.Builtin("now_mu", [], builtins.TInt(types.TValue(64))))
now_mu_float = self.append(ir.Coerce(now_mu, builtins.TFloat()))
return self.append(ir.Arith(ast.Mult(loc=None), now_mu_float, self.ref_period))
else:
assert False
elif types.is_builtin(typ, "delay") or types.is_builtin(typ, "at"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_mu_float = self.append(ir.Arith(ast.Div(loc=None), arg, self.ref_period))
arg_mu = self.append(ir.Coerce(arg_mu_float, builtins.TInt(types.TValue(64))))
self.append(ir.Builtin(typ.name + "_mu", [arg_mu], builtins.TNone()))
else:
assert False
elif types.is_builtin(typ, "now_mu") or types.is_builtin(typ, "delay_mu") \
or types.is_builtin(typ, "at_mu"):
return self.append(ir.Builtin(typ.name,
[self.visit(arg) for arg in node.args], node.type))
elif types.is_builtin(typ, "mu_to_seconds"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_float = self.append(ir.Coerce(arg, builtins.TFloat()))
return self.append(ir.Arith(ast.Mult(loc=None), arg_float, self.ref_period))
else:
assert False
elif types.is_builtin(typ, "seconds_to_mu"):
if len(node.args) == 1 and len(node.keywords) == 0:
arg = self.visit(node.args[0])
arg_mu = self.append(ir.Arith(ast.Div(loc=None), arg, self.ref_period))
return self.append(ir.Coerce(arg_mu, builtins.TInt(types.TValue(64))))
else:
assert False
elif types.is_exn_constructor(typ):
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
elif types.is_constructor(typ):
35 changes: 35 additions & 0 deletions artiq/compiler/transforms/inferencer.py
Original file line number Diff line number Diff line change
@@ -505,6 +505,17 @@ def diagnose(valid_forms):
node.func.loc, notes=valid_forms)
self.engine.process(diag)

def simple_form(info, arg_types=[], return_type=builtins.TNone()):
self._unify(node.type, return_type,
node.loc, None)

if len(node.args) == len(arg_types) and len(node.keywords) == 0:
for index, arg_type in enumerate(arg_types):
self._unify(node.args[index].type, arg_type,
node.args[index].loc, None)
else:
diagnose([ valid_form(info) ])

if types.is_exn_constructor(typ):
valid_forms = lambda: [
valid_form("{exn}() -> {exn}".format(exn=typ.name)),
@@ -730,6 +741,30 @@ def makenotes(printer, typea, typeb, loca, locb):
pass
else:
diagnose(valid_forms())
elif types.is_builtin(typ, "now"):
simple_form("now() -> float",
[], builtins.TFloat())
elif types.is_builtin(typ, "delay"):
simple_form("delay(time:float) -> None",
[builtins.TFloat()])
elif types.is_builtin(typ, "at"):
simple_form("at(time:float) -> None",
[builtins.TFloat()])
elif types.is_builtin(typ, "now_mu"):
simple_form("now_mu() -> int(width=64)",
[], builtins.TInt(types.TValue(64)))
elif types.is_builtin(typ, "delay_mu"):
simple_form("delay_mu(time_mu:int(width=64)) -> None",
[builtins.TInt(types.TValue(64))])
elif types.is_builtin(typ, "at_mu"):
simple_form("at_mu(time_mu:int(width=64)) -> None",
[builtins.TInt(types.TValue(64))])
elif types.is_builtin(typ, "mu_to_seconds"):
simple_form("mu_to_seconds(time_mu:int(width=64)) -> float",
[builtins.TInt(types.TValue(64))], builtins.TFloat())
elif types.is_builtin(typ, "seconds_to_mu"):
simple_form("seconds_to_mu(time:float) -> int(width=64)",
[builtins.TFloat()], builtins.TInt(types.TValue(64)))
elif types.is_constructor(typ):
# An user-defined class.
self._unify(node.type, typ.find().instance,
32 changes: 25 additions & 7 deletions artiq/compiler/transforms/llvm_ir_generator.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
lli1 = ll.IntType(1)
lli8 = ll.IntType(8)
lli32 = ll.IntType(32)
lli64 = ll.IntType(64)
lldouble = ll.DoubleType()
llptr = ll.IntType(8).as_pointer()
llmetadata = ll.MetaData()
@@ -331,9 +332,9 @@ def llconst_of_const(self, const):
assert False

def llbuiltin(self, name):
llfun = self.llmodule.get_global(name)
if llfun is not None:
return llfun
llglobal = self.llmodule.get_global(name)
if llglobal is not None:
return llglobal

if name in "llvm.donothing":
llty = ll.FunctionType(llvoid, [])
@@ -366,13 +367,19 @@ def llbuiltin(self, name):
var_arg=True)
elif name == "recv_rpc":
llty = ll.FunctionType(lli32, [llptr])
elif name == "now":
llty = lli64
else:
assert False

llfun = ll.Function(self.llmodule, llty, name)
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
llfun.attributes.add("noreturn")
return llfun
if isinstance(llty, ll.FunctionType):
llglobal = ll.Function(self.llmodule, llty, name)
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
llglobal.attributes.add("noreturn")
else:
llglobal = ll.GlobalVariable(self.llmodule, llty, name)

return llglobal

def map(self, value):
if isinstance(value, (ir.Argument, ir.Instruction, ir.BasicBlock)):
@@ -774,6 +781,17 @@ def get_outer(llenv, env_ty):
elif insn.op == "exncast":
# This is an identity cast at LLVM IR level.
return self.map(insn.operands[0])
elif insn.op == "now_mu":
return self.llbuilder.load(self.llbuiltin("now"), name=insn.name)
elif insn.op == "delay_mu":
interval, = insn.operands
llnowptr = self.llbuiltin("now")
llnow = self.llbuilder.load(llnowptr)
lladjusted = self.llbuilder.add(llnow, self.map(interval))
return self.llbuilder.store(lladjusted, llnowptr)
elif insn.op == "at_mu":
time, = insn.operands
return self.llbuilder.store(self.map(time), self.llbuiltin("now"))
else:
assert False

2 changes: 1 addition & 1 deletion artiq/coredevice/core.py
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ def compile(self, function, args, kwargs, with_attr_writeback=True):
stitcher.stitch_call(function, args, kwargs)
stitcher.finalize()

module = Module(stitcher)
module = Module(stitcher, ref_period=self.ref_period)
target = OR1KTarget()

library = target.compile_and_link([module])
64 changes: 0 additions & 64 deletions artiq/py2llvm_old/transforms/lower_time.py

This file was deleted.

70 changes: 0 additions & 70 deletions artiq/py2llvm_old/transforms/quantize_time.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,3 @@
"""
This transform turns calls to delay() that use non-integer time
expressed in seconds into calls to delay_mu() that use int64 time
expressed in multiples of ref_period.
It does so by inserting multiplication/division/rounding operations around
those calls.
The seconds_to_mu and mu_to_seconds core language functions are also
implemented here, as well as watchdog to syscall conversion.
"""

import ast

from artiq.transforms.tools import value_to_ast


def _seconds_to_mu(ref_period, node):
divided = ast.copy_location(
ast.BinOp(left=node,
op=ast.Div(),
right=value_to_ast(ref_period)),
node)
return ast.copy_location(
ast.Call(func=ast.Name("round64", ast.Load()),
args=[divided],
keywords=[], starargs=[], kwargs=[]),
divided)


def _mu_to_seconds(ref_period, node):
return ast.copy_location(
ast.BinOp(left=node,
op=ast.Mult(),
right=value_to_ast(ref_period)),
node)


class _TimeQuantizer(ast.NodeTransformer):
def __init__(self, ref_period):
self.ref_period = ref_period
self.watchdog_id_counter = 0

def visit_Call(self, node):
funcname = node.func.id
if funcname == "delay":
node.func.id = "delay_mu"
if (isinstance(node.args[0], ast.Call)
and node.args[0].func.id == "mu_to_seconds"):
# optimize:
# delay(mu_to_seconds(x)) -> delay_mu(x)
node.args[0] = self.visit(node.args[0].args[0])
else:
node.args[0] = _seconds_to_mu(self.ref_period,
self.visit(node.args[0]))
return node
elif funcname == "seconds_to_mu":
return _seconds_to_mu(self.ref_period,
self.visit(node.args[0]))
elif funcname == "mu_to_seconds":
return _mu_to_seconds(self.ref_period,
self.visit(node.args[0]))
else:
self.generic_visit(node)
return node

def visit_With(self, node):
self.generic_visit(node)
if (isinstance(node.items[0].context_expr, ast.Call)
@@ -107,7 +41,3 @@ def visit_With(self, node):
finalbody=[stmt_clear])
]
return node


def quantize_time(func_def, ref_period):
_TimeQuantizer(ref_period).visit(func_def)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CC ?= clang

libartiq_personality.so: ../../soc/runtime/artiq_personality.c artiq_terminate.c
libartiq_support.so: ../../soc/runtime/artiq_personality.c artiq_terminate.c artiq_time.c
$(CC) -std=c99 -Wall -Werror -I. -I../../soc/runtime -g -fPIC -shared -o $@ $^
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions lit-test/libartiq_support/artiq_time.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include <stdint.h>

int64_t now = 0;
Binary file added lit-test/libartiq_support/libartiq_personality.so
Binary file not shown.
Binary file added lit-test/libartiq_support/libartiq_support.so
Binary file not shown.
File renamed without changes.
8 changes: 4 additions & 4 deletions lit-test/test/lit.cfg
Original file line number Diff line number Diff line change
@@ -17,11 +17,11 @@ not_ = '{} {}'.format(python_executable, os.path.join(root, 'not.py'))
config.substitutions.append( ('%not', not_) )

if os.name == 'posix':
personality_build = os.path.join(root, 'libartiq_personality')
if subprocess.call(['make', '-sC', personality_build]) != 0:
support_build = os.path.join(root, 'libartiq_support')
if subprocess.call(['make', '-sC', support_build]) != 0:
lit_config.fatal("Unable to build JIT support library")

personality_lib = os.path.join(personality_build, 'libartiq_personality.so')
config.environment['LIBARTIQ_PERSONALITY'] = personality_lib
support_lib = os.path.join(support_build, 'libartiq_support.so')
config.environment['LIBARTIQ_SUPPORT'] = support_lib

config.available_features.add('exceptions')
9 changes: 9 additions & 0 deletions lit-test/test/time/advance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# RUN: %python -m artiq.compiler.testbench.jit %s

assert now() == 0.0
delay(100.0)
assert now() == 100.0
at(12345.0)
assert now() == 12345.0

assert now_mu() == 12345000000
7 changes: 7 additions & 0 deletions lit-test/test/time/advance_mu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# RUN: %python -m artiq.compiler.testbench.jit %s

assert now_mu() == 0
delay_mu(100)
assert now_mu() == 100
at_mu(12345)
assert now_mu() == 12345
4 changes: 4 additions & 0 deletions lit-test/test/time/conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# RUN: %python -m artiq.compiler.testbench.jit %s

assert seconds_to_mu(2.0) == 2000000
assert mu_to_seconds(1500000) == 1.5
8 changes: 6 additions & 2 deletions soc/runtime/ksupport.c
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@

void ksupport_abort(void);

int64_t now;

/* compiler-rt symbols */
extern void __divsi3, __modsi3, __ledf2, __gedf2, __unorddf2, __eqdf2, __ltdf2,
__nedf2, __gtdf2, __negsf2, __negdf2, __addsf3, __subsf3, __mulsf3,
@@ -87,8 +89,7 @@ static const struct symbol runtime_exports[] = {
{"abort", &ksupport_abort},

/* proxified syscalls */
{"now_init", &now_init},
{"now_save", &now_save},
{"now", &now},

{"watchdog_set", &watchdog_set},
{"watchdog_clear", &watchdog_clear},
@@ -212,7 +213,10 @@ int main(void)
void (*kernel_init)() = request->library_info->init;

mailbox_send_and_wait(&load_reply);

now = now_init();
kernel_init();
now_save(now);

struct msg_base finished_reply;
finished_reply.type = MESSAGE_TYPE_FINISHED;

0 comments on commit 501ba91

Please sign in to comment.