Browse Source

Move ValueType into a new cdsl top-level module.

We want to separate the Python classes that make up the DSL used to
define the Cretonne language from the concrete definitions.

- cdsl.types defines the ValueType class hierarchy.
- base.types defines the concrete types.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
f8545574b5
  1. 6
      docs/cton_domain.py
  2. 25
      docs/metaref.rst
  3. 1
      lib/cretonne/meta/base/__init__.py
  4. 4
      lib/cretonne/meta/base/types.py
  5. 6
      lib/cretonne/meta/cdsl/__init__.py
  6. 160
      lib/cretonne/meta/cdsl/types.py
  7. 2
      lib/cretonne/meta/check.sh
  8. 198
      lib/cretonne/meta/cretonne/__init__.py
  9. 2
      lib/cretonne/meta/cretonne/base.py
  10. 5
      lib/cretonne/meta/gen_instr.py
  11. 3
      lib/cretonne/meta/gen_types.py

6
docs/cton_domain.py

@ -245,7 +245,7 @@ class TypeDocumenter(sphinx.ext.autodoc.Documenter):
return False return False
def resolve_name(self, modname, parents, path, base): def resolve_name(self, modname, parents, path, base):
return 'cretonne.types', [base] return 'base.types', [base]
def add_content(self, more_content, no_docstring=False): def add_content(self, more_content, no_docstring=False):
super(TypeDocumenter, self).add_content(more_content, no_docstring) super(TypeDocumenter, self).add_content(more_content, no_docstring)
@ -280,11 +280,11 @@ class InstDocumenter(sphinx.ext.autodoc.Documenter):
op = inst.ins[0] op = inst.ins[0]
sig += ' ' + op.name sig += ' ' + op.name
# If the first input is variable-args, this is 'return'. No parens. # If the first input is variable-args, this is 'return'. No parens.
if op.typ.operand_kind().name == 'variable_args': if op.kind.name == 'variable_args':
sig += '...'.format(op.name) sig += '...'.format(op.name)
for op in inst.ins[1:]: for op in inst.ins[1:]:
# This is a call or branch with args in (...). # This is a call or branch with args in (...).
if op.typ.operand_kind().name == 'variable_args': if op.kind.name == 'variable_args':
sig += '({}...)'.format(op.name) sig += '({}...)'.format(op.name)
else: else:
sig += ', ' + op.name sig += ', ' + op.name

25
docs/metaref.rst

@ -4,7 +4,7 @@ Cretonne Meta Language Reference
.. default-domain:: py .. default-domain:: py
.. highlight:: python .. highlight:: python
.. module:: cretonne .. module:: cdsl
The Cretonne meta language is used to define instructions for Cretonne. It is a The Cretonne meta language is used to define instructions for Cretonne. It is a
domain specific language embedded in Python. This document describes the Python domain specific language embedded in Python. This document describes the Python
@ -16,8 +16,8 @@ steps:
1. The Python modules are imported. This has the effect of building static data 1. The Python modules are imported. This has the effect of building static data
structures in global variables in the modules. These static data structures structures in global variables in the modules. These static data structures
use the classes in the :mod:`cretonne` module to describe instruction sets in the :mod:`base` and :mod:`isa` packages use the classes in the
and other properties. :mod:`cdsl` module to describe instruction sets and other properties.
2. The static data structures are processed to produce Rust source code and 2. The static data structures are processed to produce Rust source code and
constant tables. constant tables.
@ -41,6 +41,7 @@ ISA, and defined in a `settings` module under the appropriate
Settings can take boolean on/off values, small numbers, or explicitly enumerated Settings can take boolean on/off values, small numbers, or explicitly enumerated
symbolic values. Each type is represented by a sub-class of :class:`Setting`: symbolic values. Each type is represented by a sub-class of :class:`Setting`:
.. currentmodule:: cretonne
.. inheritance-diagram:: Setting BoolSetting NumSetting EnumSetting .. inheritance-diagram:: Setting BoolSetting NumSetting EnumSetting
:parts: 1 :parts: 1
@ -125,36 +126,36 @@ Immediate operands
Immediate instruction operands don't correspond to SSA values, but have values Immediate instruction operands don't correspond to SSA values, but have values
that are encoded directly in the instruction. Immediate operands don't that are encoded directly in the instruction. Immediate operands don't
have types from the :class:`cretonne.ValueType` type system; they often have have types from the :class:`cdsl.types.ValueType` type system; they often have
enumerated values of a specific type. The type of an immediate operand is enumerated values of a specific type. The type of an immediate operand is
indicated with an instance of :class:`ImmediateKind`. indicated with an instance of :class:`ImmediateKind`.
.. currentmodule:: cretonne
.. autoclass:: ImmediateKind .. autoclass:: ImmediateKind
.. automodule:: cretonne.immediates .. automodule:: cretonne.immediates
:members: :members:
.. currentmodule:: cretonne
Entity references Entity references
----------------- -----------------
Instruction operands can also refer to other entities in the same function. This Instruction operands can also refer to other entities in the same function. This
can be extended basic blocks, or entities declared in the function preamble. can be extended basic blocks, or entities declared in the function preamble.
.. currentmodule:: cretonne
.. autoclass:: EntityRefKind .. autoclass:: EntityRefKind
.. automodule:: cretonne.entities .. automodule:: cretonne.entities
:members: :members:
.. currentmodule:: cretonne
Value types Value types
----------- -----------
Concrete value types are represented as instances of :class:`cretonne.ValueType`. There are Concrete value types are represented as instances of :class:`cdsl.types.ValueType`. There are
subclasses to represent scalar and vector types. subclasses to represent scalar and vector types.
.. currentmodule:: cdsl.types
.. autoclass:: ValueType .. autoclass:: ValueType
.. inheritance-diagram:: ValueType ScalarType VectorType IntType FloatType BoolType .. inheritance-diagram:: ValueType ScalarType VectorType IntType FloatType BoolType
:parts: 1 :parts: 1
@ -169,11 +170,9 @@ subclasses to represent scalar and vector types.
.. autoclass:: BoolType .. autoclass:: BoolType
:members: :members:
.. automodule:: cretonne.types .. automodule:: base.types
:members: :members:
.. currentmodule:: cretonne
There are no predefined vector types, but they can be created as needed with There are no predefined vector types, but they can be created as needed with
the :func:`ScalarType.by` function. the :func:`ScalarType.by` function.
@ -181,6 +180,8 @@ the :func:`ScalarType.by` function.
Instruction representation Instruction representation
========================== ==========================
.. currentmodule:: cretonne
The Rust in-memory representation of instructions is derived from the The Rust in-memory representation of instructions is derived from the
instruction descriptions. Part of the representation is generated, and part is instruction descriptions. Part of the representation is generated, and part is
written as Rust code in the `cretonne.instructions` module. The instruction written as Rust code in the `cretonne.instructions` module. The instruction

1
lib/cretonne/meta/base/__init__.py

@ -0,0 +1 @@
"""Definitions for the base Cretonne language."""

4
lib/cretonne/meta/cretonne/types.py → lib/cretonne/meta/base/types.py

@ -1,8 +1,8 @@
""" """
The cretonne.types module predefines all the Cretonne scalar types. The base.types module predefines all the Cretonne scalar types.
""" """
from __future__ import absolute_import from __future__ import absolute_import
from . import ScalarType, IntType, FloatType, BoolType from cdsl.types import ScalarType, IntType, FloatType, BoolType
#: Boolean. #: Boolean.
b1 = ScalarType( b1 = ScalarType(

6
lib/cretonne/meta/cdsl/__init__.py

@ -0,0 +1,6 @@
"""
Cretonne DSL classes.
This module defines the classes that are used to define Cretonne instructions
and other entitties.
"""

160
lib/cretonne/meta/cdsl/types.py

@ -0,0 +1,160 @@
"""Cretonne ValueType hierarchy"""
from __future__ import absolute_import
import math
# ValueType instances (i8, i32, ...) are provided in the cretonne.types module.
class ValueType(object):
"""
A concrete SSA value type.
All SSA values have a type that is described by an instance of `ValueType`
or one of its subclasses.
"""
# Map name -> ValueType.
_registry = dict() # type: Dict[str, ValueType]
# List of all the scalar types.
all_scalars = list() # type: List[ValueType]
def __init__(self, name, membytes, doc):
# type: (str, int, str) -> None
self.name = name
self.membytes = membytes
self.__doc__ = doc
assert name not in ValueType._registry
ValueType._registry[name] = self
def __str__(self):
# type: () -> str
return self.name
def free_typevar(self):
return None
@staticmethod
def by_name(name):
# type: (str) -> ValueType
if name in ValueType._registry:
return ValueType._registry[name]
else:
raise AttributeError("No type named '{}'".format(name))
class ScalarType(ValueType):
"""
A concrete scalar (not vector) type.
Also tracks a unique set of :py:class:`VectorType` instances with this type
as the lane type.
"""
def __init__(self, name, membytes, doc):
# type: (str, int, str) -> None
super(ScalarType, self).__init__(name, membytes, doc)
self._vectors = dict() # type: Dict[int, VectorType]
# Assign numbers starting from 1. (0 is VOID).
ValueType.all_scalars.append(self)
self.number = len(ValueType.all_scalars)
assert self.number < 16, 'Too many scalar types'
def __repr__(self):
# type: () -> str
return 'ScalarType({})'.format(self.name)
def rust_name(self):
# type: () -> str
return 'types::' + self.name.upper()
def by(self, lanes):
# type: (int) -> VectorType
"""
Get a vector type with this type as the lane type.
For example, ``i32.by(4)`` returns the :obj:`i32x4` type.
"""
if lanes in self._vectors:
return self._vectors[lanes]
else:
v = VectorType(self, lanes)
self._vectors[lanes] = v
return v
class VectorType(ValueType):
"""
A concrete SIMD vector type.
A vector type has a lane type which is an instance of :class:`ScalarType`,
and a positive number of lanes.
"""
def __init__(self, base, lanes):
# type: (ScalarType, int) -> None
assert isinstance(base, ScalarType), 'SIMD lanes must be scalar types'
super(VectorType, self).__init__(
name='{}x{}'.format(base.name, lanes),
membytes=lanes*base.membytes,
doc="""
A SIMD vector with {} lanes containing a `{}` each.
""".format(lanes, base.name))
self.base = base
self.lanes = lanes
self.number = 16*int(math.log(lanes, 2)) + base.number
def __repr__(self):
# type: () -> str
return ('VectorType(base={}, lanes={})'
.format(self.base.name, self.lanes))
class IntType(ScalarType):
"""A concrete scalar integer type."""
def __init__(self, bits):
# type: (int) -> None
assert bits > 0, 'IntType must have positive number of bits'
super(IntType, self).__init__(
name='i{:d}'.format(bits),
membytes=bits // 8,
doc="An integer type with {} bits.".format(bits))
self.bits = bits
def __repr__(self):
# type: () -> str
return 'IntType(bits={})'.format(self.bits)
class FloatType(ScalarType):
"""A concrete scalar floating point type."""
def __init__(self, bits, doc):
# type: (int, str) -> None
assert bits > 0, 'FloatType must have positive number of bits'
super(FloatType, self).__init__(
name='f{:d}'.format(bits),
membytes=bits // 8,
doc=doc)
self.bits = bits
def __repr__(self):
# type: () -> str
return 'FloatType(bits={})'.format(self.bits)
class BoolType(ScalarType):
"""A concrete scalar boolean type."""
def __init__(self, bits):
# type: (int) -> None
assert bits > 0, 'BoolType must have positive number of bits'
super(BoolType, self).__init__(
name='b{:d}'.format(bits),
membytes=bits // 8,
doc="A boolean type with {} bits.".format(bits))
self.bits = bits
def __repr__(self):
# type: () -> str
return 'BoolType(bits={})'.format(self.bits)

2
lib/cretonne/meta/check.sh

@ -14,7 +14,7 @@ runif() {
# Check Python sources for Python 3 compatibility using pylint. # Check Python sources for Python 3 compatibility using pylint.
# #
# Install pylint with 'pip install pylint'. # Install pylint with 'pip install pylint'.
runif pylint --py3k --reports=no -- *.py cretonne isa runif pylint --py3k --reports=no -- *.py cdsl base cretonne isa
# Style linting. # Style linting.
runif flake8 . runif flake8 .

198
lib/cretonne/meta/cretonne/__init__.py

@ -6,10 +6,10 @@ instructions.
""" """
from __future__ import absolute_import from __future__ import absolute_import
import re import re
import math
import importlib import importlib
from collections import OrderedDict from collections import OrderedDict
from .predicates import And, Predicate, FieldPredicate # noqa from .predicates import And, Predicate, FieldPredicate # noqa
import cdsl.types
# The typing module is only required by mypy, and we don't use these imports # The typing module is only required by mypy, and we don't use these imports
# outside type comments. # outside type comments.
@ -17,7 +17,7 @@ try:
from typing import Tuple, Union, Any, Iterable, Sequence, TYPE_CHECKING # noqa from typing import Tuple, Union, Any, Iterable, Sequence, TYPE_CHECKING # noqa
MaybeBoundInst = Union['Instruction', 'BoundInstruction'] MaybeBoundInst = Union['Instruction', 'BoundInstruction']
AnyPredicate = Union['Predicate', 'FieldPredicate'] AnyPredicate = Union['Predicate', 'FieldPredicate']
OperandSpec = Union['OperandKind', 'ValueType', 'TypeVar'] OperandSpec = Union['OperandKind', 'cdsl.types.ValueType', 'TypeVar']
except ImportError: except ImportError:
TYPE_CHECKING = False TYPE_CHECKING = False
@ -391,171 +391,6 @@ class EntityRefKind(OperandKind):
return 'EntityRefKind({})'.format(self.name) return 'EntityRefKind({})'.format(self.name)
# ValueType instances (i8, i32, ...) are provided in the cretonne.types module.
class ValueType(object):
"""
A concrete SSA value type.
All SSA values have a type that is described by an instance of `ValueType`
or one of its subclasses.
"""
# Map name -> ValueType.
_registry = dict() # type: Dict[str, ValueType]
# List of all the scalar types.
all_scalars = list() # type: List[ValueType]
def __init__(self, name, membytes, doc):
# type: (str, int, str) -> None
self.name = name
self.membytes = membytes
self.__doc__ = doc
assert name not in ValueType._registry
ValueType._registry[name] = self
def __str__(self):
# type: () -> str
return self.name
def operand_kind(self):
# type: () -> OperandKind
"""
When a `ValueType` object is used to describe the type of an `Operand`
in an instruction definition, the kind of that operand is an SSA value.
"""
return value
def free_typevar(self):
return None
@staticmethod
def by_name(name):
# type: (str) -> ValueType
if name in ValueType._registry:
return ValueType._registry[name]
else:
raise AttributeError("No type named '{}'".format(name))
class ScalarType(ValueType):
"""
A concrete scalar (not vector) type.
Also tracks a unique set of :py:class:`VectorType` instances with this type
as the lane type.
"""
def __init__(self, name, membytes, doc):
# type: (str, int, str) -> None
super(ScalarType, self).__init__(name, membytes, doc)
self._vectors = dict() # type: Dict[int, VectorType]
# Assign numbers starting from 1. (0 is VOID).
ValueType.all_scalars.append(self)
self.number = len(ValueType.all_scalars)
assert self.number < 16, 'Too many scalar types'
def __repr__(self):
# type: () -> str
return 'ScalarType({})'.format(self.name)
def rust_name(self):
# type: () -> str
return 'types::' + self.name.upper()
def by(self, lanes):
# type: (int) -> VectorType
"""
Get a vector type with this type as the lane type.
For example, ``i32.by(4)`` returns the :obj:`i32x4` type.
"""
if lanes in self._vectors:
return self._vectors[lanes]
else:
v = VectorType(self, lanes)
self._vectors[lanes] = v
return v
class VectorType(ValueType):
"""
A concrete SIMD vector type.
A vector type has a lane type which is an instance of :class:`ScalarType`,
and a positive number of lanes.
"""
def __init__(self, base, lanes):
# type: (ScalarType, int) -> None
assert isinstance(base, ScalarType), 'SIMD lanes must be scalar types'
super(VectorType, self).__init__(
name='{}x{}'.format(base.name, lanes),
membytes=lanes*base.membytes,
doc="""
A SIMD vector with {} lanes containing a `{}` each.
""".format(lanes, base.name))
self.base = base
self.lanes = lanes
self.number = 16*int(math.log(lanes, 2)) + base.number
def __repr__(self):
# type: () -> str
return ('VectorType(base={}, lanes={})'
.format(self.base.name, self.lanes))
class IntType(ScalarType):
"""A concrete scalar integer type."""
def __init__(self, bits):
# type: (int) -> None
assert bits > 0, 'IntType must have positive number of bits'
super(IntType, self).__init__(
name='i{:d}'.format(bits),
membytes=bits // 8,
doc="An integer type with {} bits.".format(bits))
self.bits = bits
def __repr__(self):
# type: () -> str
return 'IntType(bits={})'.format(self.bits)
class FloatType(ScalarType):
"""A concrete scalar floating point type."""
def __init__(self, bits, doc):
# type: (int, str) -> None
assert bits > 0, 'FloatType must have positive number of bits'
super(FloatType, self).__init__(
name='f{:d}'.format(bits),
membytes=bits // 8,
doc=doc)
self.bits = bits
def __repr__(self):
# type: () -> str
return 'FloatType(bits={})'.format(self.bits)
class BoolType(ScalarType):
"""A concrete scalar boolean type."""
def __init__(self, bits):
# type: (int) -> None
assert bits > 0, 'BoolType must have positive number of bits'
super(BoolType, self).__init__(
name='b{:d}'.format(bits),
membytes=bits // 8,
doc="A boolean type with {} bits.".format(bits))
self.bits = bits
def __repr__(self):
# type: () -> str
return 'BoolType(bits={})'.format(self.bits)
# Defining instructions. # Defining instructions.
@ -633,9 +468,12 @@ class Operand(object):
def __init__(self, name, typ, doc=''): def __init__(self, name, typ, doc=''):
# type: (str, OperandSpec, str) -> None # type: (str, OperandSpec, str) -> None
self.name = name self.name = name
self.typ = typ
self.__doc__ = doc self.__doc__ = doc
self.kind = typ.operand_kind() self.typ = typ
if isinstance(typ, cdsl.types.ValueType):
self.kind = value
else:
self.kind = typ.operand_kind()
def get_doc(self): def get_doc(self):
# type: () -> str # type: () -> str
@ -688,7 +526,7 @@ class InstructionFormat(object):
""" """
# Map (multiple_results, kind, kind, ...) -> InstructionFormat # Map (multiple_results, kind, kind, ...) -> InstructionFormat
_registry = dict() # type: Dict[Tuple, InstructionFormat] _registry = dict() # type: Dict[Tuple[bool, Tuple[OperandKind, ...]], InstructionFormat] # noqa
# All existing formats. # All existing formats.
all_formats = list() # type: List[InstructionFormat] all_formats = list() # type: List[InstructionFormat]
@ -715,7 +553,7 @@ class InstructionFormat(object):
self.typevar_operand = self.value_operands[0] self.typevar_operand = self.value_operands[0]
# Compute a signature for the global registry. # Compute a signature for the global registry.
sig = (self.multiple_results,) + self.kinds sig = (self.multiple_results, self.kinds)
if sig in InstructionFormat._registry: if sig in InstructionFormat._registry:
raise RuntimeError( raise RuntimeError(
"Format '{}' has the same signature as existing format '{}'" "Format '{}' has the same signature as existing format '{}'"
@ -782,11 +620,11 @@ class InstructionFormat(object):
multiple_results = outs[0].kind == variable_args multiple_results = outs[0].kind == variable_args
else: else:
multiple_results = len(outs) > 1 multiple_results = len(outs) > 1
sig = (multiple_results,) + tuple(op.kind for op in ins) sig = (multiple_results, tuple(op.kind for op in ins))
if sig not in InstructionFormat._registry: if sig not in InstructionFormat._registry:
raise RuntimeError( raise RuntimeError(
"No instruction format matches ins = ({}){}".format( "No instruction format matches ins = ({}){}".format(
", ".join(map(str, sig[1:])), ", ".join(map(str, sig[1])),
"[multiple results]" if multiple_results else "")) "[multiple results]" if multiple_results else ""))
return InstructionFormat._registry[sig] return InstructionFormat._registry[sig]
@ -991,7 +829,7 @@ class Instruction(object):
return x return x
def bind(self, *args): def bind(self, *args):
# type: (*ValueType) -> BoundInstruction # type: (*cdsl.types.ValueType) -> BoundInstruction
""" """
Bind a polymorphic instruction to a concrete list of type variable Bind a polymorphic instruction to a concrete list of type variable
values. values.
@ -1007,10 +845,10 @@ class Instruction(object):
>>> iadd.i32 >>> iadd.i32
""" """
return self.bind(ValueType.by_name(name)) return self.bind(cdsl.types.ValueType.by_name(name))
def fully_bound(self): def fully_bound(self):
# type: () -> Tuple[Instruction, Tuple[ValueType, ...]] # type: () -> Tuple[Instruction, Tuple[cdsl.types.ValueType, ...]]
""" """
Verify that all typevars have been bound, and return a Verify that all typevars have been bound, and return a
`(inst, typevars)` pair. `(inst, typevars)` pair.
@ -1036,7 +874,7 @@ class BoundInstruction(object):
""" """
def __init__(self, inst, typevars): def __init__(self, inst, typevars):
# type: (Instruction, Tuple[ValueType, ...]) -> None # type: (Instruction, Tuple[cdsl.types.ValueType, ...]) -> None
self.inst = inst self.inst = inst
self.typevars = typevars self.typevars = typevars
assert len(typevars) <= 1 + len(inst.other_typevars) assert len(typevars) <= 1 + len(inst.other_typevars)
@ -1045,7 +883,7 @@ class BoundInstruction(object):
return '.'.join([self.inst.name, ] + list(map(str, self.typevars))) return '.'.join([self.inst.name, ] + list(map(str, self.typevars)))
def bind(self, *args): def bind(self, *args):
# type: (*ValueType) -> BoundInstruction # type: (*cdsl.types.ValueType) -> BoundInstruction
""" """
Bind additional typevars. Bind additional typevars.
""" """
@ -1058,10 +896,10 @@ class BoundInstruction(object):
>>> uext.i32.i8 >>> uext.i32.i8
""" """
return self.bind(ValueType.by_name(name)) return self.bind(cdsl.types.ValueType.by_name(name))
def fully_bound(self): def fully_bound(self):
# type: () -> Tuple[Instruction, Tuple[ValueType, ...]] # type: () -> Tuple[Instruction, Tuple[cdsl.types.ValueType, ...]]
""" """
Verify that all typevars have been bound, and return a Verify that all typevars have been bound, and return a
`(inst, typevars)` pair. `(inst, typevars)` pair.

2
lib/cretonne/meta/cretonne/base.py

@ -7,7 +7,7 @@ support.
from __future__ import absolute_import from __future__ import absolute_import
from . import Operand, Instruction, InstructionGroup, variable_args from . import Operand, Instruction, InstructionGroup, variable_args
from .typevar import TypeVar from .typevar import TypeVar
from .types import i8, f32, f64, b1 from base.types import i8, f32, f64, b1
from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc
from . import entities from . import entities

5
lib/cretonne/meta/gen_instr.py

@ -5,6 +5,7 @@ from __future__ import absolute_import
import srcgen import srcgen
import constant_hash import constant_hash
from unique_table import UniqueTable, UniqueSeqTable from unique_table import UniqueTable, UniqueSeqTable
import cdsl.types
import cretonne import cretonne
@ -310,11 +311,11 @@ def get_constraint(op, ctrl_typevar, type_sets):
- `Free(idx)` where `idx` is an index into `type_sets`. - `Free(idx)` where `idx` is an index into `type_sets`.
- `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints. - `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints.
""" """
assert op.kind is cretonne.value
t = op.typ t = op.typ
assert t.operand_kind() is cretonne.value
# A concrete value type. # A concrete value type.
if isinstance(t, cretonne.ValueType): if isinstance(t, cdsl.types.ValueType):
return 'Concrete({})'.format(t.rust_name()) return 'Concrete({})'.format(t.rust_name())
if t.free_typevar() is not ctrl_typevar: if t.free_typevar() is not ctrl_typevar:

3
lib/cretonne/meta/gen_types.py

@ -9,7 +9,8 @@ This ensures that Python and Rust use the same type numbering.
""" """
from __future__ import absolute_import from __future__ import absolute_import
import srcgen import srcgen
from cretonne import ValueType from cdsl.types import ValueType
import base.types # noqa
def emit_type(ty, fmt): def emit_type(ty, fmt):

Loading…
Cancel
Save