language: replace coredevice int with numpy.{int32,int64}.

Fixes #453.
This commit is contained in:
whitequark 2016-07-06 04:44:21 +00:00
parent fa71b40c80
commit 906db876a6
10 changed files with 43 additions and 223 deletions

View File

@ -5,7 +5,7 @@ the references to the host objects and translates the functions
annotated as ``@kernel`` when they are referenced. annotated as ``@kernel`` when they are referenced.
""" """
import sys, os, re, linecache, inspect, textwrap, types as pytypes import sys, os, re, linecache, inspect, textwrap, types as pytypes, numpy
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
from pythonparser import ast, algorithm, source, diagnostic, parse_buffer from pythonparser import ast, algorithm, source, diagnostic, parse_buffer
@ -165,8 +165,12 @@ class ASTSynthesizer:
typ = builtins.TFloat() typ = builtins.TFloat()
return asttyped.NumT(n=value, ctx=None, type=typ, return asttyped.NumT(n=value, ctx=None, type=typ,
loc=self._add(repr(value))) loc=self._add(repr(value)))
elif isinstance(value, language_core.int): elif isinstance(value, numpy.int32):
typ = builtins.TInt(width=types.TValue(value.width)) typ = builtins.TInt32()
return asttyped.NumT(n=int(value), ctx=None, type=typ,
loc=self._add(repr(value)))
elif isinstance(value, numpy.int64):
typ = builtins.TInt64()
return asttyped.NumT(n=int(value), ctx=None, type=typ, return asttyped.NumT(n=int(value), ctx=None, type=typ,
loc=self._add(repr(value))) loc=self._add(repr(value)))
elif isinstance(value, str): elif isinstance(value, str):

View File

@ -620,9 +620,9 @@ class Inferencer(algorithm.Visitor):
node.loc, None) node.loc, None)
elif types.is_builtin(typ, "int"): elif types.is_builtin(typ, "int"):
valid_forms = lambda: [ valid_forms = lambda: [
valid_form("int() -> int(width='a)"), valid_form("int() -> numpy.int?"),
valid_form("int(x:'a) -> int(width='b) where 'a is numeric"), valid_form("int(x:'a) -> numpy.int?"),
valid_form("int(x:'a, width='b:<int literal>) -> int(width='b) where 'a is numeric") valid_form("int(x:'a, width=?) -> numpy.int?")
] ]
self._unify(node.type, builtins.TInt(), self._unify(node.type, builtins.TInt(),
@ -715,11 +715,11 @@ class Inferencer(algorithm.Visitor):
diagnose(valid_forms()) diagnose(valid_forms())
elif types.is_builtin(typ, "range"): elif types.is_builtin(typ, "range"):
valid_forms = lambda: [ valid_forms = lambda: [
valid_form("range(max:int(width='a)) -> range(elt=int(width='a))"), valid_form("range(max:numpy.int?) -> range(elt=numpy.int?)"),
valid_form("range(min:int(width='a), max:int(width='a)) " valid_form("range(min:numpy.int?, max:numpy.int?) "
"-> range(elt=int(width='a))"), "-> range(elt=numpy.int?)"),
valid_form("range(min:int(width='a), max:int(width='a), " valid_form("range(min:numpy.int?, max:numpy.int?, "
"step:int(width='a)) -> range(elt=int(width='a))"), "step:numpy.int?) -> range(elt=numpy.int?)"),
] ]
range_elt = builtins.TInt(types.TVar()) range_elt = builtins.TInt(types.TVar())
@ -734,7 +734,7 @@ class Inferencer(algorithm.Visitor):
diagnose(valid_forms()) diagnose(valid_forms())
elif types.is_builtin(typ, "len"): elif types.is_builtin(typ, "len"):
valid_forms = lambda: [ valid_forms = lambda: [
valid_form("len(x:'a) -> int(width='b) where 'a is iterable"), valid_form("len(x:'a) -> numpy.int?"),
] ]
if len(node.args) == 1 and len(node.keywords) == 0: if len(node.args) == 1 and len(node.keywords) == 0:
@ -762,8 +762,8 @@ class Inferencer(algorithm.Visitor):
diagnose(valid_forms()) diagnose(valid_forms())
elif types.is_builtin(typ, "round"): elif types.is_builtin(typ, "round"):
valid_forms = lambda: [ valid_forms = lambda: [
valid_form("round(x:float) -> int(width='a)"), valid_form("round(x:float) -> numpy.int?"),
valid_form("round(x:float, width='b:<int literal>) -> int(width='b)") valid_form("round(x:float, width=?) -> numpy.int?")
] ]
self._unify(node.type, builtins.TInt(), self._unify(node.type, builtins.TInt(),
@ -793,7 +793,7 @@ class Inferencer(algorithm.Visitor):
fn = typ.name fn = typ.name
valid_forms = lambda: [ valid_forms = lambda: [
valid_form("{}(x:int(width='a), y:int(width='a)) -> int(width='a)".format(fn)), valid_form("{}(x:numpy.int?, y:numpy.int?) -> numpy.int?".format(fn)),
valid_form("{}(x:float, y:float) -> float".format(fn)) valid_form("{}(x:float, y:float) -> float".format(fn))
] ]

View File

@ -3,7 +3,7 @@
into LLVM intermediate representation. into LLVM intermediate representation.
""" """
import os, re, types as pytypes import os, re, types as pytypes, numpy
from collections import defaultdict from collections import defaultdict
from pythonparser import ast, diagnostic from pythonparser import ast, diagnostic
from llvmlite_artiq import ir as ll, binding as llvm from llvmlite_artiq import ir as ll, binding as llvm
@ -1397,7 +1397,7 @@ class LLVMIRGenerator:
assert value in (True, False) assert value in (True, False)
return ll.Constant(llty, value) return ll.Constant(llty, value)
elif builtins.is_int(typ): elif builtins.is_int(typ):
assert isinstance(value, (int, language_core.int)) assert isinstance(value, (int, numpy.int32, numpy.int64))
return ll.Constant(llty, int(value)) return ll.Constant(llty, int(value))
elif builtins.is_float(typ): elif builtins.is_float(typ):
assert isinstance(value, float) assert isinstance(value, float)

View File

@ -1,12 +1,12 @@
import struct import struct
import logging import logging
import traceback import traceback
import numpy
from enum import Enum from enum import Enum
from fractions import Fraction from fractions import Fraction
from collections import namedtuple from collections import namedtuple
from artiq.coredevice import exceptions from artiq.coredevice import exceptions
from artiq.language.core import int as wrapping_int
from artiq import __version__ as software_version from artiq import __version__ as software_version
@ -317,9 +317,9 @@ class CommGeneric:
elif tag == "b": elif tag == "b":
return bool(self._read_int8()) return bool(self._read_int8())
elif tag == "i": elif tag == "i":
return wrapping_int(self._read_int32(), 32) return numpy.int32(self._read_int32())
elif tag == "I": elif tag == "I":
return wrapping_int(self._read_int64(), 64) return numpy.int64(self._read_int64())
elif tag == "f": elif tag == "f":
return self._read_float64() return self._read_float64()
elif tag == "F": elif tag == "F":

View File

@ -1,5 +1,7 @@
import numpy
from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu,
delay_mu, int, mu_to_seconds) delay_mu, mu_to_seconds)
from artiq.language.units import MHz from artiq.language.units import MHz
from artiq.coredevice.rtio import rtio_output, rtio_input_data from artiq.coredevice.rtio import rtio_output, rtio_input_data
@ -60,9 +62,9 @@ class SPIMaster:
self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period, self.ref_period_mu = seconds_to_mu(self.core.coarse_ref_period,
self.core) self.core)
self.channel = channel self.channel = channel
self.write_period_mu = int(0, 64) self.write_period_mu = numpy.int64(0)
self.read_period_mu = int(0, 64) self.read_period_mu = numpy.int64(0)
self.xfer_period_mu = int(0, 64) self.xfer_period_mu = numpy.int64(0)
@portable @portable
def frequency_to_div(self, f): def frequency_to_div(self, f):

View File

@ -1,3 +1,5 @@
import numpy
from artiq.language.core import * from artiq.language.core import *
from artiq.language.types import * from artiq.language.types import *
from artiq.coredevice.rtio import rtio_output, rtio_input_timestamp from artiq.coredevice.rtio import rtio_output, rtio_input_timestamp
@ -17,7 +19,7 @@ class TTLOut:
self.channel = channel self.channel = channel
# in RTIO cycles # in RTIO cycles
self.o_previous_timestamp = int(0, width=64) self.o_previous_timestamp = numpy.int64(0)
@kernel @kernel
def output(self): def output(self):
@ -101,8 +103,8 @@ class TTLInOut:
self.channel = channel self.channel = channel
# in RTIO cycles # in RTIO cycles
self.o_previous_timestamp = int(0, width=64) self.o_previous_timestamp = numpy.int64(0)
self.i_previous_timestamp = int(0, width=64) self.i_previous_timestamp = numpy.int64(0)
@kernel @kernel
def set_oe(self, oe): def set_oe(self, oe):
@ -282,8 +284,8 @@ class TTLClockGen:
self.channel = channel self.channel = channel
# in RTIO cycles # in RTIO cycles
self.previous_timestamp = int(0, width=64) self.previous_timestamp = numpy.int64(0)
self.acc_width = int(24, width=64) self.acc_width = numpy.int64(24)
@portable @portable
def frequency_to_ftw(self, frequency): def frequency_to_ftw(self, frequency):

View File

@ -4,10 +4,10 @@ Core ARTIQ extensions to the Python language.
from collections import namedtuple from collections import namedtuple
from functools import wraps from functools import wraps
import numpy
__all__ = ["host_int", "int", "host_round", "round", __all__ = ["kernel", "portable", "syscall", "host_only",
"kernel", "portable", "syscall", "host_only",
"set_time_manager", "set_watchdog_factory", "set_time_manager", "set_watchdog_factory",
"TerminationRequested"] "TerminationRequested"]
@ -20,147 +20,6 @@ kernel_globals = (
) )
__all__.extend(kernel_globals) __all__.extend(kernel_globals)
host_int = int
class int:
"""
Arbitrary-precision integers for static compilation.
The static compiler does not use unlimited-precision integers,
like Python normally does, because of their unbounded memory requirements.
Instead, it allows to choose a bit width (usually 32 or 64) at compile-time,
and all computations follow wrap-around semantics on overflow.
This class implements the same semantics on the host.
For example:
>>> a = int(1, width=64)
>>> b = int(3, width=64) + 2
>>> isinstance(a, int)
True
>>> isinstance(b, int)
True
>>> a + b
int(6, width=64)
>>> int(10, width=32) + 0x7fffffff
int(9, width=32)
>>> int(0x80000000)
int(-2147483648, width=32)
"""
__slots__ = ['_value', '_width']
def __new__(cls, value, width=32):
if isinstance(value, int):
return value
else:
sign_bit = 2 ** (width - 1)
value = host_int(value)
if value & sign_bit:
value = -1 & ~sign_bit + (value & (sign_bit - 1)) + 1
else:
value &= sign_bit - 1
self = super().__new__(cls)
self._value = value
self._width = width
return self
@property
def width(self):
return self._width
def __int__(self):
return self._value
def __float__(self):
return float(self._value)
def __str__(self):
return str(self._value)
# range() etc call __index__, not __int__
def __index__(self):
return self._value
def __repr__(self):
return "int({}, width={})".format(self._value, self._width)
def _unaryop(lower_fn):
def operator(self):
return int(lower_fn(self._value), self._width)
return operator
__neg__ = _unaryop(host_int.__neg__)
__pos__ = _unaryop(host_int.__pos__)
__abs__ = _unaryop(host_int.__abs__)
__invert__ = _unaryop(host_int.__invert__)
__round__ = _unaryop(host_int.__round__)
def _binaryop(lower_fn, rlower_fn=None):
def operator(self, other):
if isinstance(other, host_int):
return int(lower_fn(self._value, other), self._width)
elif isinstance(other, int):
width = self._width if self._width > other._width else other._width
return int(lower_fn(self._value, other._value), width)
elif rlower_fn:
return getattr(other, rlower_fn)(self._value)
else:
return NotImplemented
return operator
__add__ = __iadd__ = _binaryop(host_int.__add__, "__radd__")
__sub__ = __isub__ = _binaryop(host_int.__sub__, "__rsub__")
__mul__ = __imul__ = _binaryop(host_int.__mul__, "__rmul__")
__truediv__ = __itruediv__ = _binaryop(host_int.__truediv__, "__rtruediv__")
__floordiv__ = __ifloordiv__ = _binaryop(host_int.__floordiv__, "__rfloordiv__")
__mod__ = __imod__ = _binaryop(host_int.__mod__, "__rmod__")
__pow__ = __ipow__ = _binaryop(host_int.__pow__, "__rpow__")
__radd__ = _binaryop(host_int.__radd__, "__add__")
__rsub__ = _binaryop(host_int.__rsub__, "__sub__")
__rmul__ = _binaryop(host_int.__rmul__, "__mul__")
__rfloordiv__ = _binaryop(host_int.__rfloordiv__, "__floordiv__")
__rtruediv__ = _binaryop(host_int.__rtruediv__, "__truediv__")
__rmod__ = _binaryop(host_int.__rmod__, "__mod__")
__rpow__ = _binaryop(host_int.__rpow__, "__pow__")
__lshift__ = __ilshift__ = _binaryop(host_int.__lshift__)
__rshift__ = __irshift__ = _binaryop(host_int.__rshift__)
__and__ = __iand__ = _binaryop(host_int.__and__)
__or__ = __ior__ = _binaryop(host_int.__or__)
__xor__ = __ixor__ = _binaryop(host_int.__xor__)
__rlshift__ = _binaryop(host_int.__rlshift__)
__rrshift__ = _binaryop(host_int.__rrshift__)
__rand__ = _binaryop(host_int.__rand__)
__ror__ = _binaryop(host_int.__ror__)
__rxor__ = _binaryop(host_int.__rxor__)
def _compareop(lower_fn, rlower_fn):
def operator(self, other):
if isinstance(other, host_int):
return lower_fn(self._value, other)
elif isinstance(other, int):
return lower_fn(self._value, other._value)
else:
return getattr(other, rlower_fn)(self._value)
return operator
__eq__ = _compareop(host_int.__eq__, "__ne__")
__ne__ = _compareop(host_int.__ne__, "__eq__")
__gt__ = _compareop(host_int.__gt__, "__le__")
__ge__ = _compareop(host_int.__ge__, "__lt__")
__lt__ = _compareop(host_int.__lt__, "__ge__")
__le__ = _compareop(host_int.__le__, "__gt__")
host_round = round
def round(value, width=32):
return int(host_round(value), width)
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo", _ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo",
"core_name function syscall forbidden flags") "core_name function syscall forbidden flags")
@ -342,7 +201,7 @@ def seconds_to_mu(seconds, core=None):
""" """
if core is None: if core is None:
raise ValueError("Core device must be specified for time conversion") raise ValueError("Core device must be specified for time conversion")
return round(seconds//core.ref_period, width=64) return numpy.int64(seconds//core.ref_period)
def mu_to_seconds(mu, core=None): def mu_to_seconds(mu, core=None):

View File

@ -25,8 +25,6 @@ import tempfile
import numpy import numpy
from ..language.core import int as wrapping_int
_encode_map = { _encode_map = {
type(None): "none", type(None): "none",
@ -41,7 +39,6 @@ _encode_map = {
set: "set", set: "set",
dict: "dict", dict: "dict",
slice: "slice", slice: "slice",
wrapping_int: "number",
Fraction: "fraction", Fraction: "fraction",
OrderedDict: "ordereddict", OrderedDict: "ordereddict",
numpy.ndarray: "nparray" numpy.ndarray: "nparray"
@ -192,7 +189,6 @@ _eval_dict = {
"true": True, "true": True,
"slice": slice, "slice": slice,
"int": wrapping_int,
"Fraction": Fraction, "Fraction": Fraction,
"OrderedDict": OrderedDict, "OrderedDict": OrderedDict,
"nparray": _nparray, "nparray": _nparray,

View File

@ -1,3 +1,4 @@
import numpy
from time import sleep from time import sleep
from artiq.experiment import * from artiq.experiment import *
@ -28,8 +29,8 @@ class RoundtripTest(ExperimentCase):
self.assertRoundtrip(False) self.assertRoundtrip(False)
def test_int(self): def test_int(self):
self.assertRoundtrip(42) self.assertRoundtrip(numpy.int32(42))
self.assertRoundtrip(int(42, width=64)) self.assertRoundtrip(numpy.int64(42))
def test_float(self): def test_float(self):
self.assertRoundtrip(42.0) self.assertRoundtrip(42.0)

View File

@ -1,44 +0,0 @@
import unittest
from artiq.language.core import *
class LanguageCoreTest(unittest.TestCase):
def test_unary(self):
self.assertEqual(int(10), +int(10))
self.assertEqual(int(-10), -int(10))
self.assertEqual(int(~10), ~int(10))
self.assertEqual(int(10), round(int(10)))
def test_arith(self):
self.assertEqual(int(9), int(4) + int(5))
self.assertEqual(int(9), int(4) + 5)
self.assertEqual(int(9), 5 + int(4))
self.assertEqual(9.0, int(4) + 5.0)
self.assertEqual(9.0, 5.0 + int(4))
a = int(5)
a += int(2)
a += 2
self.assertEqual(int(9), a)
def test_compare(self):
self.assertTrue(int(9) > int(8))
self.assertTrue(int(9) > 8)
self.assertTrue(int(9) > 8.0)
self.assertTrue(9 > int(8))
self.assertTrue(9.0 > int(8))
def test_bitwise(self):
self.assertEqual(int(0x100), int(0x10) << int(4))
self.assertEqual(int(0x100), int(0x10) << 4)
self.assertEqual(int(0x100), 0x10 << int(4))
def test_wraparound(self):
self.assertEqual(int(0xffffffff), int(-1))
self.assertTrue(int(0x7fffffff) > int(1))
self.assertTrue(int(0x80000000) < int(-1))
self.assertEqual(int(9), int(10) + int(0xffffffff))
self.assertEqual(-1.0, float(int(0xfffffffe) + int(1)))