[docs] Remove special handling of CDSL python modules;
This commit is contained in:
@@ -1,385 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Sphinx domain for documenting compiler intermediate representations.
|
||||
#
|
||||
# This defines a 'clif' Sphinx domain with the following directives and roles:
|
||||
#
|
||||
# .. clif::type:: type
|
||||
# Document an IR type.
|
||||
# .. clif:inst:: v0, v1 = inst op0, op1
|
||||
# Document an IR instruction.
|
||||
#
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.domains import Domain, ObjType
|
||||
from sphinx.locale import l_
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||
from sphinx.util.nodes import make_refnode
|
||||
|
||||
import sphinx.ext.autodoc
|
||||
|
||||
|
||||
class ClifObject(ObjectDescription):
|
||||
"""
|
||||
Any kind of Cranelift IR object.
|
||||
|
||||
This is a shared base class for the different kinds of indexable objects
|
||||
in the Cranelift IR reference.
|
||||
"""
|
||||
option_spec = {
|
||||
'noindex': directives.flag,
|
||||
'module': directives.unchanged,
|
||||
'annotation': directives.unchanged,
|
||||
}
|
||||
|
||||
def add_target_and_index(self, name, sig, signode):
|
||||
"""
|
||||
Add ``name`` to the index.
|
||||
|
||||
:param name: The object name returned by :func:`handle_signature`.
|
||||
:param sig: The signature text.
|
||||
:param signode: The output node.
|
||||
"""
|
||||
targetname = self.objtype + '-' + name
|
||||
if targetname not in self.state.document.ids:
|
||||
signode['names'].append(targetname)
|
||||
signode['ids'].append(targetname)
|
||||
signode['first'] = (not self.names)
|
||||
self.state.document.note_explicit_target(signode)
|
||||
inv = self.env.domaindata['clif']['objects']
|
||||
if name in inv:
|
||||
self.state_machine.reporter.warning(
|
||||
'duplicate Cranelift object description of %s, ' % name +
|
||||
'other instance in ' + self.env.doc2path(inv[name][0]),
|
||||
line=self.lineno)
|
||||
inv[name] = (self.env.docname, self.objtype)
|
||||
|
||||
indextext = self.get_index_text(name)
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext,
|
||||
targetname, '', None))
|
||||
|
||||
|
||||
# Type variables are indicated as %T.
|
||||
typevar = re.compile('(\%[A-Z])')
|
||||
|
||||
|
||||
def parse_type(name, signode):
|
||||
"""
|
||||
Parse a type with embedded type vars and append to signode.
|
||||
|
||||
Return a string that can be compiled into a regular expression matching
|
||||
the type.
|
||||
"""
|
||||
|
||||
re_str = ''
|
||||
|
||||
for part in typevar.split(name):
|
||||
if part == '':
|
||||
continue
|
||||
if len(part) == 2 and part[0] == '%':
|
||||
# This is a type parameter. Don't display the %, use emphasis
|
||||
# instead.
|
||||
part = part[1]
|
||||
signode += nodes.emphasis(part, part)
|
||||
re_str += r'\w+'
|
||||
else:
|
||||
signode += addnodes.desc_name(part, part)
|
||||
re_str += re.escape(part)
|
||||
return re_str
|
||||
|
||||
|
||||
class ClifType(ClifObject):
|
||||
"""A Cranelift IR type description."""
|
||||
|
||||
def handle_signature(self, sig, signode):
|
||||
"""
|
||||
Parse type signature in ``sig`` and append description to signode.
|
||||
|
||||
Return a global object name for ``add_target_and_index``.
|
||||
"""
|
||||
|
||||
name = sig.strip()
|
||||
parse_type(name, signode)
|
||||
return name
|
||||
|
||||
def get_index_text(self, name):
|
||||
return name + ' (IR type)'
|
||||
|
||||
|
||||
sep_equal = re.compile('\s*=\s*')
|
||||
sep_comma = re.compile('\s*,\s*')
|
||||
|
||||
|
||||
def parse_params(s, signode):
|
||||
for i, p in enumerate(sep_comma.split(s)):
|
||||
if i != 0:
|
||||
signode += nodes.Text(', ')
|
||||
signode += nodes.emphasis(p, p)
|
||||
|
||||
|
||||
class ClifInst(ClifObject):
|
||||
"""A Cranelift IR instruction."""
|
||||
|
||||
doc_field_types = [
|
||||
TypedField('argument', label=l_('Arguments'),
|
||||
names=('in', 'arg'),
|
||||
typerolename='type', typenames=('type',)),
|
||||
TypedField('result', label=l_('Results'),
|
||||
names=('out', 'result'),
|
||||
typerolename='type', typenames=('type',)),
|
||||
GroupedField(
|
||||
'typevar', names=('typevar',), label=l_('Type Variables')),
|
||||
GroupedField('flag', names=('flag',), label=l_('Flags')),
|
||||
Field('resulttype', label=l_('Result type'), has_arg=False,
|
||||
names=('rtype',)),
|
||||
]
|
||||
|
||||
def handle_signature(self, sig, signode):
|
||||
# Look for signatures like
|
||||
#
|
||||
# v0, v1 = foo op0, op1
|
||||
# v0 = foo
|
||||
# foo op0
|
||||
|
||||
parts = re.split(sep_equal, sig, 1)
|
||||
if len(parts) == 2:
|
||||
# Outgoing parameters.
|
||||
parse_params(parts[0], signode)
|
||||
signode += nodes.Text(' = ')
|
||||
name = parts[1]
|
||||
else:
|
||||
name = parts[0]
|
||||
|
||||
# Parse 'name arg, arg'
|
||||
parts = name.split(None, 1)
|
||||
name = parts[0]
|
||||
signode += addnodes.desc_name(name, name)
|
||||
|
||||
if len(parts) == 2:
|
||||
# Incoming parameters.
|
||||
signode += nodes.Text(' ')
|
||||
parse_params(parts[1], signode)
|
||||
|
||||
return name
|
||||
|
||||
def get_index_text(self, name):
|
||||
return name
|
||||
|
||||
|
||||
class ClifInstGroup(ClifObject):
|
||||
"""A Cranelift IR instruction group."""
|
||||
|
||||
|
||||
class CraneliftDomain(Domain):
|
||||
"""Cranelift domain for IR objects."""
|
||||
name = 'clif'
|
||||
label = 'Cranelift'
|
||||
|
||||
object_types = {
|
||||
'type': ObjType(l_('type'), 'type'),
|
||||
'inst': ObjType(l_('instruction'), 'inst')
|
||||
}
|
||||
|
||||
directives = {
|
||||
'type': ClifType,
|
||||
'inst': ClifInst,
|
||||
'instgroup': ClifInstGroup,
|
||||
}
|
||||
|
||||
roles = {
|
||||
'type': XRefRole(),
|
||||
'inst': XRefRole(),
|
||||
'instgroup': XRefRole(),
|
||||
}
|
||||
|
||||
initial_data = {
|
||||
'objects': {}, # fullname -> docname, objtype
|
||||
}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
for fullname, (fn, _l) in list(self.data['objects'].items()):
|
||||
if fn == docname:
|
||||
del self.data['objects'][fullname]
|
||||
|
||||
def merge_domaindata(self, docnames, otherdata):
|
||||
for fullname, (fn, objtype) in otherdata['objects'].items():
|
||||
if fn in docnames:
|
||||
self.data['objects'][fullname] = (fn, objtype)
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder, typ, target, node,
|
||||
contnode):
|
||||
objects = self.data['objects']
|
||||
if target not in objects:
|
||||
return None
|
||||
obj = objects[target]
|
||||
return make_refnode(builder, fromdocname, obj[0],
|
||||
obj[1] + '-' + target, contnode, target)
|
||||
|
||||
def resolve_any_xref(self, env, fromdocname, builder, target,
|
||||
node, contnode):
|
||||
objects = self.data['objects']
|
||||
if target not in objects:
|
||||
return []
|
||||
obj = objects[target]
|
||||
return [('clif:' + self.role_for_objtype(obj[1]),
|
||||
make_refnode(builder, fromdocname, obj[0],
|
||||
obj[1] + '-' + target, contnode, target))]
|
||||
|
||||
|
||||
class TypeDocumenter(sphinx.ext.autodoc.Documenter):
|
||||
# Invoke with .. autocliftype::
|
||||
objtype = 'cliftype'
|
||||
# Convert into clif:type directives
|
||||
domain = 'clif'
|
||||
directivetype = 'type'
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
return False
|
||||
|
||||
def resolve_name(self, modname, parents, path, base):
|
||||
return 'base.types', [base]
|
||||
|
||||
def add_content(self, more_content, no_docstring=False):
|
||||
super(TypeDocumenter, self).add_content(more_content, no_docstring)
|
||||
sourcename = self.get_sourcename()
|
||||
membytes = self.object.membytes
|
||||
if membytes:
|
||||
self.add_line(u':bytes: {}'.format(membytes), sourcename)
|
||||
else:
|
||||
self.add_line(u':bytes: Can\'t be stored in memory', sourcename)
|
||||
|
||||
|
||||
class InstDocumenter(sphinx.ext.autodoc.Documenter):
|
||||
# Invoke with .. autoinst::
|
||||
objtype = 'inst'
|
||||
# Convert into clif:inst directives
|
||||
domain = 'clif'
|
||||
directivetype = 'inst'
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
return False
|
||||
|
||||
def resolve_name(self, modname, parents, path, base):
|
||||
if path:
|
||||
return path.rstrip('.'), [base]
|
||||
else:
|
||||
return 'base.instructions', [base]
|
||||
|
||||
def format_signature(self):
|
||||
inst = self.object
|
||||
sig = inst.name
|
||||
if len(inst.outs) > 0:
|
||||
sig = ', '.join([op.name for op in inst.outs]) + ' = ' + sig
|
||||
if len(inst.ins) > 0:
|
||||
op = inst.ins[0]
|
||||
sig += ' ' + op.name
|
||||
# If the first input is variable-args, this is 'return'. No parens.
|
||||
if op.kind.name == 'variable_args':
|
||||
sig += '...'.format(op.name)
|
||||
for op in inst.ins[1:]:
|
||||
# This is a call or branch with args in (...).
|
||||
if op.kind.name == 'variable_args':
|
||||
sig += '({}...)'.format(op.name)
|
||||
else:
|
||||
sig += ', ' + op.name
|
||||
return sig
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
"""Add the directive header and options to the generated content."""
|
||||
domain = getattr(self, 'domain', 'clif')
|
||||
directive = getattr(self, 'directivetype', self.objtype)
|
||||
sourcename = self.get_sourcename()
|
||||
self.add_line(u'.. %s:%s:: %s' % (domain, directive, sig), sourcename)
|
||||
if self.options.noindex:
|
||||
self.add_line(u' :noindex:', sourcename)
|
||||
|
||||
def add_content(self, more_content, no_docstring=False):
|
||||
super(InstDocumenter, self).add_content(more_content, no_docstring)
|
||||
sourcename = self.get_sourcename()
|
||||
inst = self.object
|
||||
|
||||
# Add inputs and outputs.
|
||||
for op in inst.ins:
|
||||
if op.is_value():
|
||||
typ = op.typevar
|
||||
else:
|
||||
typ = op.kind
|
||||
self.add_line(u':in {} {}: {}'.format(
|
||||
typ, op.name, op.get_doc()), sourcename)
|
||||
for op in inst.outs:
|
||||
if op.is_value():
|
||||
typ = op.typevar
|
||||
else:
|
||||
typ = op.kind
|
||||
self.add_line(u':out {} {}: {}'.format(
|
||||
typ, op.name, op.get_doc()), sourcename)
|
||||
|
||||
# Document type inference for polymorphic instructions.
|
||||
if inst.is_polymorphic:
|
||||
if inst.ctrl_typevar is not None:
|
||||
if inst.use_typevar_operand:
|
||||
tvopnum = inst.value_opnums[inst.format.typevar_operand]
|
||||
self.add_line(
|
||||
u':typevar {}: inferred from {}'
|
||||
.format(
|
||||
inst.ctrl_typevar.name,
|
||||
inst.ins[tvopnum]),
|
||||
sourcename)
|
||||
else:
|
||||
self.add_line(
|
||||
u':typevar {}: explicitly provided'
|
||||
.format(inst.ctrl_typevar.name),
|
||||
sourcename)
|
||||
for tv in inst.other_typevars:
|
||||
self.add_line(
|
||||
u':typevar {}: from input operand'.format(tv.name),
|
||||
sourcename)
|
||||
|
||||
|
||||
class InstGroupDocumenter(sphinx.ext.autodoc.ModuleLevelDocumenter):
|
||||
# Invoke with .. autoinstgroup::
|
||||
objtype = 'instgroup'
|
||||
# Convert into clif:instgroup directives
|
||||
domain = 'clif'
|
||||
directivetype = 'instgroup'
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
return False
|
||||
|
||||
def format_name(self):
|
||||
return "{}.{}".format(self.modname, ".".join(self.objpath))
|
||||
|
||||
def add_content(self, more_content, no_docstring=False):
|
||||
super(InstGroupDocumenter, self).add_content(
|
||||
more_content, no_docstring)
|
||||
sourcename = self.get_sourcename()
|
||||
indexed = self.env.domaindata['clif']['objects']
|
||||
|
||||
names = [inst.name for inst in self.object.instructions]
|
||||
names.sort()
|
||||
for name in names:
|
||||
if name in indexed:
|
||||
self.add_line(u':clif:inst:`{}`'.format(name), sourcename)
|
||||
else:
|
||||
self.add_line(u'``{}``'.format(name), sourcename)
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_domain(CraneliftDomain)
|
||||
app.add_autodocumenter(TypeDocumenter)
|
||||
app.add_autodocumenter(InstDocumenter)
|
||||
app.add_autodocumenter(InstGroupDocumenter)
|
||||
|
||||
return {'version': '0.1'}
|
||||
@@ -21,10 +21,6 @@ import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# Also add the meta-python directory to sys.path so autodoc can find the Cranelift meta
|
||||
# language definitions.
|
||||
sys.path.insert(0, os.path.abspath('../cranelift-codegen/meta-python'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# We don't support Sphinx versions before 1.4 since the format of index
|
||||
@@ -41,7 +37,6 @@ extensions = [
|
||||
'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.graphviz',
|
||||
'sphinx.ext.inheritance_diagram',
|
||||
'clif_domain',
|
||||
'clif_lexer',
|
||||
]
|
||||
|
||||
|
||||
@@ -86,10 +86,9 @@ containing multiple assignments to the same variables into SSA form for
|
||||
Cranelift :term:`IR`.
|
||||
|
||||
Such variables can also be presented to Cranelift as :term:`stack slot`\s.
|
||||
Stack slots are accessed with the :inst:`stack_store` and :inst:`stack_load`
|
||||
instructions, and can have their address taken with :inst:`stack_addr`, which
|
||||
supports C-like programming languages where local variables can have their
|
||||
address taken.
|
||||
Stack slots are accessed with the `stack_store` and `stack_load` instructions,
|
||||
and can have their address taken with `stack_addr`, which supports C-like
|
||||
programming languages where local variables can have their address taken.
|
||||
|
||||
.. _value-types:
|
||||
|
||||
@@ -105,20 +104,20 @@ Boolean types
|
||||
|
||||
Boolean values are either true or false.
|
||||
|
||||
The :type:`b1` type represents an abstract boolean value. It can only exist as
|
||||
The `b1` type represents an abstract boolean value. It can only exist as
|
||||
an SSA value, and can't be directly stored in memory. It can, however, be
|
||||
converted into an integer with value 0 or 1 by the :inst:`bint` instruction (and
|
||||
converted back with :inst:`icmp_imm` with 0).
|
||||
converted into an integer with value 0 or 1 by the `bint` instruction (and
|
||||
converted back with `icmp_imm` with 0).
|
||||
|
||||
Several larger boolean types are also defined, primarily to be used as SIMD
|
||||
element types. They can be stored in memory, and are represented as either all
|
||||
zero bits or all one bits.
|
||||
|
||||
.. autocliftype:: b1
|
||||
.. autocliftype:: b8
|
||||
.. autocliftype:: b16
|
||||
.. autocliftype:: b32
|
||||
.. autocliftype:: b64
|
||||
- b1
|
||||
- b8
|
||||
- b16
|
||||
- b32
|
||||
- b64
|
||||
|
||||
Integer types
|
||||
-------------
|
||||
@@ -129,10 +128,10 @@ number, others don't care.
|
||||
|
||||
The support for i8 and i16 arithmetic is incomplete and use could lead to bugs.
|
||||
|
||||
.. autocliftype:: i8
|
||||
.. autocliftype:: i16
|
||||
.. autocliftype:: i32
|
||||
.. autocliftype:: i64
|
||||
- i8
|
||||
- i16
|
||||
- i32
|
||||
- i64
|
||||
|
||||
Floating point types
|
||||
--------------------
|
||||
@@ -160,8 +159,8 @@ instructions are encoded as follows:
|
||||
and all bits of the trailing significand other than the MSB set to
|
||||
nondeterministic values.
|
||||
|
||||
.. autocliftype:: f32
|
||||
.. autocliftype:: f64
|
||||
- f32
|
||||
- f64
|
||||
|
||||
CPU flags types
|
||||
---------------
|
||||
@@ -172,15 +171,15 @@ compared.
|
||||
|
||||
Since some ISAs don't have CPU flags, these value types should not be used
|
||||
until the legalization phase of compilation where the code is adapted to fit
|
||||
the target ISA. Use instructions like :inst:`icmp` instead.
|
||||
the target ISA. Use instructions like `icmp` instead.
|
||||
|
||||
The CPU flags types are also restricted such that two flags values can not be
|
||||
live at the same time. After legalization, some instruction encodings will
|
||||
clobber the flags, and flags values are not allowed to be live across such
|
||||
instructions either. The verifier enforces these rules.
|
||||
|
||||
.. autocliftype:: iflags
|
||||
.. autocliftype:: fflags
|
||||
- iflags
|
||||
- fflags
|
||||
|
||||
SIMD vector types
|
||||
-----------------
|
||||
@@ -189,42 +188,38 @@ A SIMD vector type represents a vector of values from one of the scalar types
|
||||
(boolean, integer, and floating point). Each scalar value in a SIMD type is
|
||||
called a *lane*. The number of lanes must be a power of two in the range 2-256.
|
||||
|
||||
.. type:: i%Bx%N
|
||||
i%Bx%N
|
||||
A SIMD vector of integers. The lane type `iB` is one of the integer
|
||||
types `i8` ... `i64`.
|
||||
|
||||
A SIMD vector of integers. The lane type :type:`iB` is one of the integer
|
||||
types :type:`i8` ... :type:`i64`.
|
||||
|
||||
Some concrete integer vector types are :type:`i32x4`, :type:`i64x8`, and
|
||||
:type:`i16x4`.
|
||||
Some concrete integer vector types are `i32x4`, `i64x8`, and
|
||||
`i16x4`.
|
||||
|
||||
The size of a SIMD integer vector in memory is :math:`N B\over 8` bytes.
|
||||
|
||||
.. type:: f32x%N
|
||||
|
||||
f32x%N
|
||||
A SIMD vector of single precision floating point numbers.
|
||||
|
||||
Some concrete :type:`f32` vector types are: :type:`f32x2`, :type:`f32x4`,
|
||||
and :type:`f32x8`.
|
||||
Some concrete `f32` vector types are: `f32x2`, `f32x4`,
|
||||
and `f32x8`.
|
||||
|
||||
The size of a :type:`f32` vector in memory is :math:`4N` bytes.
|
||||
|
||||
.. type:: f64x%N
|
||||
The size of a `f32` vector in memory is :math:`4N` bytes.
|
||||
|
||||
f64x%N
|
||||
A SIMD vector of double precision floating point numbers.
|
||||
|
||||
Some concrete :type:`f64` vector types are: :type:`f64x2`, :type:`f64x4`,
|
||||
and :type:`f64x8`.
|
||||
Some concrete `f64` vector types are: `f64x2`, `f64x4`,
|
||||
and `f64x8`.
|
||||
|
||||
The size of a :type:`f64` vector in memory is :math:`8N` bytes.
|
||||
|
||||
.. type:: b1x%N
|
||||
The size of a `f64` vector in memory is :math:`8N` bytes.
|
||||
|
||||
b1x%N
|
||||
A boolean SIMD vector.
|
||||
|
||||
Boolean vectors are used when comparing SIMD vectors. For example,
|
||||
comparing two :type:`i32x4` values would produce a :type:`b1x4` result.
|
||||
comparing two `i32x4` values would produce a `b1x4` result.
|
||||
|
||||
Like the :type:`b1` type, a boolean vector cannot be stored in memory.
|
||||
Like the `b1` type, a boolean vector cannot be stored in memory.
|
||||
|
||||
Pseudo-types and type classes
|
||||
-----------------------------
|
||||
@@ -232,40 +227,32 @@ Pseudo-types and type classes
|
||||
These are not concrete types, but convenient names used to refer to real types
|
||||
in this reference.
|
||||
|
||||
.. type:: iAddr
|
||||
|
||||
iAddr
|
||||
A Pointer-sized integer representing an address.
|
||||
|
||||
This is either :type:`i32`, or :type:`i64`, depending on whether the target
|
||||
This is either `i32`, or `i64`, depending on whether the target
|
||||
platform has 32-bit or 64-bit pointers.
|
||||
|
||||
.. type:: iB
|
||||
iB
|
||||
Any of the scalar integer types `i8` -- `i64`.
|
||||
|
||||
Any of the scalar integer types :type:`i8` -- :type:`i64`.
|
||||
Int
|
||||
Any scalar *or vector* integer type: `iB` or `iBxN`.
|
||||
|
||||
.. type:: Int
|
||||
fB
|
||||
Either of the floating point scalar types: `f32` or `f64`.
|
||||
|
||||
Any scalar *or vector* integer type: :type:`iB` or :type:`iBxN`.
|
||||
|
||||
.. type:: fB
|
||||
|
||||
Either of the floating point scalar types: :type:`f32` or :type:`f64`.
|
||||
|
||||
.. type:: Float
|
||||
|
||||
Any scalar *or vector* floating point type: :type:`fB` or :type:`fBxN`.
|
||||
|
||||
.. type:: %Tx%N
|
||||
Float
|
||||
Any scalar *or vector* floating point type: `fB` or `fBxN`.
|
||||
|
||||
%Tx%N
|
||||
Any SIMD vector type.
|
||||
|
||||
.. type:: Mem
|
||||
Mem
|
||||
Any type that can be stored in memory: `Int` or `Float`.
|
||||
|
||||
Any type that can be stored in memory: :type:`Int` or :type:`Float`.
|
||||
|
||||
.. type:: Testable
|
||||
|
||||
Either :type:`b1` or :type:`iN`.
|
||||
Testable
|
||||
Either `b1` or `iN`.
|
||||
|
||||
Immediate operand types
|
||||
-----------------------
|
||||
@@ -273,48 +260,41 @@ Immediate operand types
|
||||
These types are not part of the normal SSA type system. They are used to
|
||||
indicate the different kinds of immediate operands on an instruction.
|
||||
|
||||
.. type:: imm64
|
||||
|
||||
imm64
|
||||
A 64-bit immediate integer. The value of this operand is interpreted as a
|
||||
signed two's complement integer. Instruction encodings may limit the valid
|
||||
range.
|
||||
|
||||
In the textual format, :type:`imm64` immediates appear as decimal or
|
||||
In the textual format, `imm64` immediates appear as decimal or
|
||||
hexadecimal literals using the same syntax as C.
|
||||
|
||||
.. type:: offset32
|
||||
|
||||
offset32
|
||||
A signed 32-bit immediate address offset.
|
||||
|
||||
In the textual format, :type:`offset32` immediates always have an explicit
|
||||
In the textual format, `offset32` immediates always have an explicit
|
||||
sign, and a 0 offset may be omitted.
|
||||
|
||||
.. type:: ieee32
|
||||
|
||||
ieee32
|
||||
A 32-bit immediate floating point number in the IEEE 754-2008 binary32
|
||||
interchange format. All bit patterns are allowed.
|
||||
|
||||
.. type:: ieee64
|
||||
|
||||
ieee64
|
||||
A 64-bit immediate floating point number in the IEEE 754-2008 binary64
|
||||
interchange format. All bit patterns are allowed.
|
||||
|
||||
.. type:: bool
|
||||
|
||||
bool
|
||||
A boolean immediate value, either false or true.
|
||||
|
||||
In the textual format, :type:`bool` immediates appear as 'false'
|
||||
In the textual format, `bool` immediates appear as 'false'
|
||||
and 'true'.
|
||||
|
||||
.. type:: intcc
|
||||
intcc
|
||||
An integer condition code. See the `icmp` instruction for details.
|
||||
|
||||
An integer condition code. See the :inst:`icmp` instruction for details.
|
||||
floatcc
|
||||
A floating point condition code. See the `fcmp` instruction for details.
|
||||
|
||||
.. type:: floatcc
|
||||
|
||||
A floating point condition code. See the :inst:`fcmp` instruction for details.
|
||||
|
||||
The two IEEE floating point immediate types :type:`ieee32` and :type:`ieee64`
|
||||
The two IEEE floating point immediate types `ieee32` and `ieee64`
|
||||
are displayed as hexadecimal floating point literals in the textual :term:`IR`
|
||||
format. Decimal floating point literals are not allowed because some computer
|
||||
systems can round differently when converting to binary. The hexadecimal
|
||||
@@ -324,9 +304,9 @@ to represent all NaN bit patterns:
|
||||
Normal numbers
|
||||
Compatible with C99: ``-0x1.Tpe`` where ``T`` are the trailing
|
||||
significand bits encoded as hexadecimal, and ``e`` is the unbiased exponent
|
||||
as a decimal number. :type:`ieee32` has 23 trailing significand bits. They
|
||||
as a decimal number. `ieee32` has 23 trailing significand bits. They
|
||||
are padded with an extra LSB to produce 6 hexadecimal digits. This is not
|
||||
necessary for :type:`ieee64` which has 52 trailing significand bits
|
||||
necessary for `ieee64` which has 52 trailing significand bits
|
||||
forming 13 hexadecimal digits with no padding.
|
||||
|
||||
Zeros
|
||||
@@ -358,17 +338,10 @@ arguments, if it has any. Conditional branches only take the branch if their
|
||||
condition is satisfied, otherwise execution continues at the following
|
||||
instruction in the EBB.
|
||||
|
||||
.. autoinst:: jump
|
||||
.. autoinst:: brz
|
||||
.. autoinst:: brnz
|
||||
.. autoinst:: br_icmp
|
||||
.. autoinst:: br_table
|
||||
|
||||
.. inst:: JT = jump_table [EBB0, EBB1, ..., EBBn]
|
||||
|
||||
JT = jump_table [EBB0, EBB1, ..., EBBn]
|
||||
Declare a jump table in the :term:`function preamble`.
|
||||
|
||||
This declares a jump table for use by the :inst:`br_table` indirect branch
|
||||
This declares a jump table for use by the `br_table` indirect branch
|
||||
instruction. Entries in the table are EBB names.
|
||||
|
||||
The EBBs listed must belong to the current function, and they can't have
|
||||
@@ -382,13 +355,9 @@ instruction in the EBB.
|
||||
Traps stop the program because something went wrong. The exact behavior depends
|
||||
on the target instruction set architecture and operating system. There are
|
||||
explicit trap instructions defined below, but some instructions may also cause
|
||||
traps for certain input value. For example, :inst:`udiv` traps when the divisor
|
||||
traps for certain input value. For example, `udiv` traps when the divisor
|
||||
is zero.
|
||||
|
||||
.. autoinst:: trap
|
||||
.. autoinst:: trapz
|
||||
.. autoinst:: trapnz
|
||||
|
||||
|
||||
Function calls
|
||||
==============
|
||||
@@ -448,8 +417,7 @@ compilers.
|
||||
Functions that are called directly must be declared in the :term:`function
|
||||
preamble`:
|
||||
|
||||
.. inst:: FN = [colocated] NAME signature
|
||||
|
||||
FN = [colocated] NAME signature
|
||||
Declare a function so it can be called directly.
|
||||
|
||||
If the colocated keyword is present, the symbol's definition will be
|
||||
@@ -458,11 +426,7 @@ preamble`:
|
||||
|
||||
:arg NAME: Name of the function, passed to the linker for resolution.
|
||||
:arg signature: Function signature. See below.
|
||||
:result FN: A function identifier that can be used with :inst:`call`.
|
||||
|
||||
.. autoinst:: call
|
||||
.. autoinst:: x_return
|
||||
.. autoinst:: fallthrough_return
|
||||
:result FN: A function identifier that can be used with `call`.
|
||||
|
||||
This simple example illustrates direct function calls and signatures:
|
||||
|
||||
@@ -472,46 +436,39 @@ This simple example illustrates direct function calls and signatures:
|
||||
|
||||
Indirect function calls use a signature declared in the preamble.
|
||||
|
||||
.. autoinst:: call_indirect
|
||||
.. autoinst:: func_addr
|
||||
|
||||
.. _memory:
|
||||
|
||||
Memory
|
||||
======
|
||||
|
||||
Cranelift provides fully general :inst:`load` and :inst:`store` instructions for
|
||||
accessing memory, as well as :ref:`extending loads and truncating stores
|
||||
Cranelift provides fully general `load` and `store` instructions for accessing
|
||||
memory, as well as :ref:`extending loads and truncating stores
|
||||
<extload-truncstore>`.
|
||||
|
||||
If the memory at the given address is not :term:`addressable`, the behavior of
|
||||
these instructions is undefined. If it is addressable but not
|
||||
:term:`accessible`, they :term:`trap`.
|
||||
|
||||
.. autoinst:: load
|
||||
.. autoinst:: store
|
||||
|
||||
There are also more restricted operations for accessing specific types of memory
|
||||
objects.
|
||||
|
||||
Additionally, instructions are provided for handling multi-register addressing.
|
||||
|
||||
.. autoinst:: load_complex
|
||||
.. autoinst:: store_complex
|
||||
|
||||
Memory operation flags
|
||||
----------------------
|
||||
|
||||
Loads and stores can have flags that loosen their semantics in order to enable
|
||||
optimizations.
|
||||
|
||||
======= ===========================================
|
||||
======== ===========================================
|
||||
Flag Description
|
||||
======= ===========================================
|
||||
======== ===========================================
|
||||
notrap Memory is assumed to be :term:`accessible`.
|
||||
aligned Trapping allowed for misaligned accesses.
|
||||
readonly The data at the specified address will not modified between when this function is called and exited.
|
||||
======= ===========================================
|
||||
readonly The data at the specified address will not
|
||||
modified between when this function is
|
||||
called and exited.
|
||||
======== ===========================================
|
||||
|
||||
When the ``accessible`` flag is set, the behavior is undefined if the memory
|
||||
is not :term:`accessible`.
|
||||
@@ -530,8 +487,7 @@ allocated in the :term:`function preamble`. Stack slots are not typed, they
|
||||
simply represent a contiguous sequence of :term:`accessible` bytes in the stack
|
||||
frame.
|
||||
|
||||
.. inst:: SS = explicit_slot Bytes, Flags...
|
||||
|
||||
SS = explicit_slot Bytes, Flags...
|
||||
Allocate a stack slot in the preamble.
|
||||
|
||||
If no alignment is specified, Cranelift will pick an appropriate alignment
|
||||
@@ -541,9 +497,6 @@ frame.
|
||||
:flag align(N): Request at least N bytes alignment.
|
||||
:result SS: Stack slot index.
|
||||
|
||||
.. autoinst:: stack_load
|
||||
.. autoinst:: stack_store
|
||||
|
||||
The dedicated stack access instructions are easy for the compiler to reason
|
||||
about because stack slots and offsets are fixed at compile time. For example,
|
||||
the alignment of these stack memory accesses can be inferred from the offsets
|
||||
@@ -552,9 +505,7 @@ and stack slot alignments.
|
||||
It's also possible to obtain the address of a stack slot, which can be used
|
||||
in :ref:`unrestricted loads and stores <memory>`.
|
||||
|
||||
.. autoinst:: stack_addr
|
||||
|
||||
The :inst:`stack_addr` instruction can be used to macro-expand the stack access
|
||||
The `stack_addr` instruction can be used to macro-expand the stack access
|
||||
instructions before instruction selection::
|
||||
|
||||
v0 = stack_load.f64 ss3, 16
|
||||
@@ -569,7 +520,7 @@ Global values
|
||||
-------------
|
||||
|
||||
A *global value* is an object whose value is not known at compile time. The
|
||||
value is computed at runtime by :inst:`global_value`, possibly using
|
||||
value is computed at runtime by `global_value`, possibly using
|
||||
information provided by the linker via relocations. There are multiple
|
||||
kinds of global values using different methods for determining their value.
|
||||
Cranelift does not track the type of a global value, for they are just
|
||||
@@ -584,8 +535,7 @@ Cranelift functions.
|
||||
Chains of global value expressions are possible, but cycles are not allowed.
|
||||
They will be caught by the IR verifier.
|
||||
|
||||
.. inst:: GV = vmctx
|
||||
|
||||
GV = vmctx
|
||||
Declare a global value of the address of the VM context struct.
|
||||
|
||||
This declares a global value which is the VM context pointer which may
|
||||
@@ -599,8 +549,7 @@ A global value can also be derived by treating another global variable as a
|
||||
struct pointer and loading from one of its fields. This makes it possible to
|
||||
chase pointers into VM runtime data structures.
|
||||
|
||||
.. inst:: GV = load.Type BaseGV [Offset]
|
||||
|
||||
GV = load.Type BaseGV [Offset]
|
||||
Declare a global value pointed to by BaseGV plus Offset, with type Type.
|
||||
|
||||
It is assumed the BaseGV plus Offset resides in accessible memory with the
|
||||
@@ -610,15 +559,13 @@ chase pointers into VM runtime data structures.
|
||||
:arg Offset: Offset added to the base before loading.
|
||||
:result GV: Global value.
|
||||
|
||||
.. inst:: GV = iadd_imm BaseGV, Offset
|
||||
|
||||
GV = iadd_imm BaseGV, Offset
|
||||
Declare a global value which has the value of BaseGV offset by Offset.
|
||||
|
||||
:arg BaseGV: Global value providing the base value.
|
||||
:arg Offset: Offset added to the base value.
|
||||
|
||||
.. inst:: GV = [colocated] symbol Name
|
||||
|
||||
GV = [colocated] symbol Name
|
||||
Declare a symbolic address global value.
|
||||
|
||||
The value of GV is symbolic and will be assigned a relocation, so that
|
||||
@@ -631,10 +578,6 @@ chase pointers into VM runtime data structures.
|
||||
:arg Name: External name.
|
||||
:result GV: Global value.
|
||||
|
||||
.. autoinst:: global_value
|
||||
.. autoinst:: symbol_value
|
||||
|
||||
|
||||
Heaps
|
||||
-----
|
||||
|
||||
@@ -644,9 +587,9 @@ in, and all accesses are bounds checked. Cranelift models this through the
|
||||
concept of *heaps*.
|
||||
|
||||
A heap is declared in the function preamble and can be accessed with the
|
||||
:inst:`heap_addr` instruction that :term:`traps` on out-of-bounds accesses or
|
||||
`heap_addr` instruction that :term:`traps` on out-of-bounds accesses or
|
||||
returns a pointer that is guaranteed to trap. Heap addresses can be smaller than
|
||||
the native pointer size, for example unsigned :type:`i32` offsets on a 64-bit
|
||||
the native pointer size, for example unsigned `i32` offsets on a 64-bit
|
||||
architecture.
|
||||
|
||||
.. digraph:: static
|
||||
@@ -674,12 +617,10 @@ A heap appears as three consecutive ranges of address space:
|
||||
not :term:`accessible`.
|
||||
|
||||
The *heap bound* is the total size of the mapped and unmapped pages. This is
|
||||
the bound that :inst:`heap_addr` checks against. Memory accesses inside the
|
||||
the bound that `heap_addr` checks against. Memory accesses inside the
|
||||
heap bounds can trap if they hit an unmapped page (which is not
|
||||
:term:`accessible`).
|
||||
|
||||
.. autoinst:: heap_addr
|
||||
|
||||
Two styles of heaps are supported, *static* and *dynamic*. They behave
|
||||
differently when resized.
|
||||
|
||||
@@ -693,8 +634,7 @@ unmapped pages where the heap can grow up to its maximum size. After the
|
||||
unmapped pages follow the offset-guard pages which are also guaranteed to
|
||||
generate a trap when accessed.
|
||||
|
||||
.. inst:: H = static Base, min MinBytes, bound BoundBytes, offset_guard OffsetGuardBytes
|
||||
|
||||
H = static Base, min MinBytes, bound BoundBytes, offset_guard OffsetGuardBytes
|
||||
Declare a static heap in the preamble.
|
||||
|
||||
:arg Base: Global value holding the heap's base address.
|
||||
@@ -712,8 +652,7 @@ A *dynamic heap* can be relocated to a different base address when it is
|
||||
resized, and its bound can move dynamically. The offset-guard pages move when
|
||||
the heap is resized. The bound of a dynamic heap is stored in a global value.
|
||||
|
||||
.. inst:: H = dynamic Base, min MinBytes, bound BoundGV, offset_guard OffsetGuardBytes
|
||||
|
||||
H = dynamic Base, min MinBytes, bound BoundGV, offset_guard OffsetGuardBytes
|
||||
Declare a dynamic heap in the preamble.
|
||||
|
||||
:arg Base: Global value holding the heap's base address.
|
||||
@@ -762,25 +701,22 @@ linear memory. WebAssembly uses *tables* to allow programs to refer to opaque
|
||||
values through integer indices.
|
||||
|
||||
A table is declared in the function preamble and can be accessed with the
|
||||
:inst:`table_addr` instruction that :term:`traps` on out-of-bounds accesses.
|
||||
`table_addr` instruction that :term:`traps` on out-of-bounds accesses.
|
||||
Table addresses can be smaller than the native pointer size, for example
|
||||
unsigned :type:`i32` offsets on a 64-bit architecture.
|
||||
unsigned `i32` offsets on a 64-bit architecture.
|
||||
|
||||
A table appears as a consecutive range of address space, conceptually
|
||||
divided into elements of fixed sizes, which are identified by their index.
|
||||
The memory is :term:`accessible`.
|
||||
|
||||
The *table bound* is the number of elements currently in the table. This is
|
||||
the bound that :inst:`table_addr` checks against.
|
||||
|
||||
.. autoinst:: table_addr
|
||||
the bound that `table_addr` checks against.
|
||||
|
||||
A table can be relocated to a different base address when it is resized, and
|
||||
its bound can move dynamically. The bound of a table is stored in a global
|
||||
value.
|
||||
|
||||
.. inst:: T = dynamic Base, min MinElements, bound BoundGV, element_size ElementSize
|
||||
|
||||
T = dynamic Base, min MinElements, bound BoundGV, element_size ElementSize
|
||||
Declare a table in the preamble.
|
||||
|
||||
:arg Base: Global value holding the table's base address.
|
||||
@@ -788,85 +724,12 @@ value.
|
||||
:arg BoundGV: Global value containing the current heap bound in elements.
|
||||
:arg ElementSize: Size of each element.
|
||||
|
||||
Operations
|
||||
==========
|
||||
|
||||
.. autoinst:: select
|
||||
.. autoinst:: selectif
|
||||
|
||||
Constant materialization
|
||||
------------------------
|
||||
|
||||
A few instructions have variants that take immediate operands (e.g.,
|
||||
:inst:`band` / :inst:`band_imm`), but in general an instruction is required to
|
||||
load a constant into an SSA value.
|
||||
|
||||
.. autoinst:: iconst
|
||||
.. autoinst:: f32const
|
||||
.. autoinst:: f64const
|
||||
.. autoinst:: bconst
|
||||
|
||||
Vector operations
|
||||
-----------------
|
||||
|
||||
.. autoinst:: vsplit
|
||||
.. autoinst:: vconcat
|
||||
.. autoinst:: vselect
|
||||
.. autoinst:: splat
|
||||
.. autoinst:: insertlane
|
||||
.. autoinst:: extractlane
|
||||
|
||||
Integer operations
|
||||
------------------
|
||||
|
||||
.. autoinst:: icmp
|
||||
.. autoinst:: icmp_imm
|
||||
.. autoinst:: iadd
|
||||
.. autoinst:: iadd_imm
|
||||
.. autoinst:: iadd_cin
|
||||
.. autoinst:: iadd_cout
|
||||
.. autoinst:: iadd_carry
|
||||
.. autoinst:: isub
|
||||
.. autoinst:: irsub_imm
|
||||
.. autoinst:: isub_bin
|
||||
.. autoinst:: isub_bout
|
||||
.. autoinst:: isub_borrow
|
||||
|
||||
.. todo:: Add and subtract with signed overflow.
|
||||
|
||||
For example, see
|
||||
`llvm.sadd.with.overflow.*` and `llvm.ssub.with.overflow.*` in
|
||||
`LLVM <https://llvm.org/docs/LangRef.html#arithmetic-with-overflow-intrinsics>`_.
|
||||
|
||||
.. autoinst:: imul
|
||||
.. autoinst:: imul_imm
|
||||
|
||||
.. todo:: Larger multiplication results.
|
||||
|
||||
For example, ``smulx`` which multiplies :type:`i32` operands to produce a
|
||||
:type:`i64` result. Alternatively, ``smulhi`` and ``smullo`` pairs.
|
||||
|
||||
.. autoinst:: udiv
|
||||
.. autoinst:: udiv_imm
|
||||
.. autoinst:: sdiv
|
||||
.. autoinst:: sdiv_imm
|
||||
.. autoinst:: urem
|
||||
.. autoinst:: urem_imm
|
||||
.. autoinst:: srem
|
||||
.. autoinst:: srem_imm
|
||||
|
||||
.. todo:: Integer minimum / maximum.
|
||||
|
||||
NEON has ``smin``, ``smax``, ``umin``, and ``umax`` instructions. We should
|
||||
replicate those for both scalar and vector integer types. Even if the
|
||||
target ISA doesn't have scalar operations, these are good pattern matching
|
||||
targets.
|
||||
|
||||
.. todo:: Saturating arithmetic.
|
||||
|
||||
Mostly for SIMD use, but again these are good patterns for contraction.
|
||||
Something like ``usatadd``, ``usatsub``, ``ssatadd``, and ``ssatsub`` is a
|
||||
good start.
|
||||
A few instructions have variants that take immediate operands, but in general
|
||||
an instruction is required to load a constant into an SSA value: `iconst`,
|
||||
`f32const`, `f64const` and `bconst` serve this purpose.
|
||||
|
||||
Bitwise operations
|
||||
------------------
|
||||
@@ -876,17 +739,6 @@ numbers, and booleans. When operating on integer or floating point types, the
|
||||
bitwise operations are working on the binary representation of the values. When
|
||||
operating on boolean values, the bitwise operations work as logical operators.
|
||||
|
||||
.. autoinst:: band
|
||||
.. autoinst:: band_imm
|
||||
.. autoinst:: bor
|
||||
.. autoinst:: bor_imm
|
||||
.. autoinst:: bxor
|
||||
.. autoinst:: bxor_imm
|
||||
.. autoinst:: bnot
|
||||
.. autoinst:: band_not
|
||||
.. autoinst:: bor_not
|
||||
.. autoinst:: bxor_not
|
||||
|
||||
The shift and rotate operations only work on integer types (scalar and vector).
|
||||
The shift amount does not have to be the same type as the value being shifted.
|
||||
Only the low `B` bits of the shift amount is significant.
|
||||
@@ -895,37 +747,13 @@ When operating on an integer vector type, the shift amount is still a scalar
|
||||
type, and all the lanes are shifted the same amount. The shift amount is masked
|
||||
to the number of bits in a *lane*, not the full size of the vector type.
|
||||
|
||||
.. autoinst:: rotl
|
||||
.. autoinst:: rotl_imm
|
||||
.. autoinst:: rotr
|
||||
.. autoinst:: rotr_imm
|
||||
.. autoinst:: ishl
|
||||
.. autoinst:: ishl_imm
|
||||
.. autoinst:: ushr
|
||||
.. autoinst:: ushr_imm
|
||||
.. autoinst:: sshr
|
||||
.. autoinst:: sshr_imm
|
||||
|
||||
The bit-counting instructions below are scalar only.
|
||||
|
||||
.. autoinst:: clz
|
||||
.. autoinst:: cls
|
||||
.. autoinst:: ctz
|
||||
.. autoinst:: popcnt
|
||||
The bit-counting instructions are scalar only.
|
||||
|
||||
Floating point operations
|
||||
-------------------------
|
||||
|
||||
These operations generally follow IEEE 754-2008 semantics.
|
||||
|
||||
.. autoinst:: fcmp
|
||||
.. autoinst:: fadd
|
||||
.. autoinst:: fsub
|
||||
.. autoinst:: fmul
|
||||
.. autoinst:: fdiv
|
||||
.. autoinst:: sqrt
|
||||
.. autoinst:: fma
|
||||
|
||||
Sign bit manipulations
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -933,10 +761,6 @@ The sign manipulating instructions work as bitwise operations, so they don't
|
||||
have special behavior for signaling NaN operands. The exponent and trailing
|
||||
significand bits are always preserved.
|
||||
|
||||
.. autoinst:: fneg
|
||||
.. autoinst:: fabs
|
||||
.. autoinst:: fcopysign
|
||||
|
||||
Minimum and maximum
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -946,40 +770,15 @@ return NaN when either input is NaN.
|
||||
|
||||
When comparing zeroes, these instructions behave as if :math:`-0.0 < 0.0`.
|
||||
|
||||
.. autoinst:: fmin
|
||||
.. autoinst:: fmax
|
||||
|
||||
Rounding
|
||||
~~~~~~~~
|
||||
|
||||
These instructions round their argument to a nearby integral value, still
|
||||
represented as a floating point number.
|
||||
|
||||
.. autoinst:: ceil
|
||||
.. autoinst:: floor
|
||||
.. autoinst:: trunc
|
||||
.. autoinst:: nearest
|
||||
|
||||
Conversion operations
|
||||
---------------------
|
||||
|
||||
.. autoinst:: bitcast
|
||||
.. autoinst:: breduce
|
||||
.. autoinst:: bextend
|
||||
.. autoinst:: bint
|
||||
.. autoinst:: bmask
|
||||
.. autoinst:: ireduce
|
||||
.. autoinst:: uextend
|
||||
.. autoinst:: sextend
|
||||
.. autoinst:: fpromote
|
||||
.. autoinst:: fdemote
|
||||
.. autoinst:: fcvt_to_uint
|
||||
.. autoinst:: fcvt_to_sint
|
||||
.. autoinst:: fcvt_to_uint_sat
|
||||
.. autoinst:: fcvt_to_sint_sat
|
||||
.. autoinst:: fcvt_from_uint
|
||||
.. autoinst:: fcvt_from_sint
|
||||
|
||||
.. _extload-truncstore:
|
||||
|
||||
Extending loads and truncating stores
|
||||
@@ -989,23 +788,13 @@ Most ISAs provide instructions that load an integer value smaller than a registe
|
||||
and extends it to the width of the register. Similarly, store instructions that
|
||||
only write the low bits of an integer register are common.
|
||||
|
||||
In addition to the normal :inst:`load` and :inst:`store` instructions, Cranelift
|
||||
In addition to the normal `load` and `store` instructions, Cranelift
|
||||
provides extending loads and truncation stores for 8, 16, and 32-bit memory
|
||||
accesses.
|
||||
|
||||
These instructions succeed, trap, or have undefined behavior, under the same
|
||||
conditions as :ref:`normal loads and stores <memory>`.
|
||||
|
||||
.. autoinst:: uload8
|
||||
.. autoinst:: sload8
|
||||
.. autoinst:: istore8
|
||||
.. autoinst:: uload16
|
||||
.. autoinst:: sload16
|
||||
.. autoinst:: istore16
|
||||
.. autoinst:: uload32
|
||||
.. autoinst:: sload32
|
||||
.. autoinst:: istore32
|
||||
|
||||
ISA-specific instructions
|
||||
=========================
|
||||
|
||||
@@ -1017,16 +806,6 @@ x86
|
||||
|
||||
Instructions that can only be used by the x86 target ISA.
|
||||
|
||||
.. autoinst:: isa.x86.instructions.sdivmodx
|
||||
.. autoinst:: isa.x86.instructions.udivmodx
|
||||
.. autoinst:: isa.x86.instructions.cvtt2si
|
||||
.. autoinst:: isa.x86.instructions.fmin
|
||||
.. autoinst:: isa.x86.instructions.fmax
|
||||
.. autoinst:: isa.x86.instructions.bsf
|
||||
.. autoinst:: isa.x86.instructions.bsr
|
||||
.. autoinst:: isa.x86.instructions.push
|
||||
.. autoinst:: isa.x86.instructions.pop
|
||||
|
||||
Codegen implementation instructions
|
||||
===================================
|
||||
|
||||
@@ -1039,42 +818,18 @@ Legalization operations
|
||||
These instructions are used as helpers when legalizing types and operations for
|
||||
the target ISA.
|
||||
|
||||
.. autoinst:: isplit
|
||||
.. autoinst:: iconcat
|
||||
|
||||
Special register operations
|
||||
---------------------------
|
||||
|
||||
The prologue and epilogue of a function needs to manipulate special registers like the stack
|
||||
pointer and the frame pointer. These instructions should not be used in regular code.
|
||||
|
||||
.. autoinst:: adjust_sp_down
|
||||
.. autoinst:: adjust_sp_up_imm
|
||||
.. autoinst:: adjust_sp_down_imm
|
||||
.. autoinst:: ifcmp_sp
|
||||
.. autoinst:: copy_special
|
||||
|
||||
Low-level control flow operations
|
||||
---------------------------------
|
||||
|
||||
.. autoinst:: fallthrough
|
||||
|
||||
CPU flag operations
|
||||
-------------------
|
||||
|
||||
These operations are for working with the "flags" registers of some CPU
|
||||
architectures.
|
||||
|
||||
.. autoinst:: ifcmp
|
||||
.. autoinst:: ifcmp_imm
|
||||
.. autoinst:: ffcmp
|
||||
.. autoinst:: trueif
|
||||
.. autoinst:: trueff
|
||||
.. autoinst:: trapif
|
||||
.. autoinst:: trapff
|
||||
.. autoinst:: brif
|
||||
.. autoinst:: brff
|
||||
|
||||
Live range splitting
|
||||
--------------------
|
||||
|
||||
@@ -1084,37 +839,24 @@ value can be quite large, it is sometimes beneficial to split the live range
|
||||
into smaller parts.
|
||||
|
||||
A live range is split by creating new SSA values that are copies or the
|
||||
original value or each other. The copies are created by inserting :inst:`copy`,
|
||||
:inst:`spill`, or :inst:`fill` instructions, depending on whether the values
|
||||
original value or each other. The copies are created by inserting `copy`,
|
||||
`spill`, or `fill` instructions, depending on whether the values
|
||||
are assigned to registers or stack slots.
|
||||
|
||||
This approach permits SSA form to be preserved throughout the register
|
||||
allocation pass and beyond.
|
||||
|
||||
.. autoinst:: copy
|
||||
.. autoinst:: spill
|
||||
.. autoinst:: fill
|
||||
|
||||
Register values can be temporarily diverted to other registers by the
|
||||
:inst:`regmove` instruction, and to and from stack slots by :inst:`regspill`
|
||||
and :inst:`regfill`.
|
||||
|
||||
.. autoinst:: regmove
|
||||
.. autoinst:: regspill
|
||||
.. autoinst:: regfill
|
||||
|
||||
`regmove` instruction, and to and from stack slots by `regspill`
|
||||
and `regfill`.
|
||||
|
||||
Instruction groups
|
||||
==================
|
||||
|
||||
All of the shared instructions are part of the :instgroup:`base` instruction
|
||||
All of the shared instructions are part of the `base` instruction
|
||||
group.
|
||||
|
||||
.. autoinstgroup:: base.instructions.GROUP
|
||||
|
||||
Target ISAs may define further instructions in their own instruction groups:
|
||||
|
||||
.. autoinstgroup:: isa.x86.instructions.GROUP
|
||||
Target ISAs may define further instructions in their own instruction groups.
|
||||
|
||||
Implementation limits
|
||||
=====================
|
||||
@@ -1272,8 +1014,8 @@ Glossary
|
||||
execution somewhere else. Execution never continues at the instruction
|
||||
following a terminator instruction.
|
||||
|
||||
The basic terminator instructions are :inst:`br`, :inst:`return`, and
|
||||
:inst:`trap`. Conditional branches and instructions that trap
|
||||
The basic terminator instructions are `br`, `return`, and
|
||||
`trap`. Conditional branches and instructions that trap
|
||||
conditionally are not terminator instructions.
|
||||
|
||||
trap
|
||||
|
||||
@@ -4,7 +4,6 @@ Cranelift Meta Language Reference
|
||||
|
||||
.. default-domain:: py
|
||||
.. highlight:: python
|
||||
.. module:: cdsl
|
||||
|
||||
The Cranelift meta language is used to define instructions for Cranelift. It is a
|
||||
domain specific language embedded in Rust.
|
||||
@@ -18,50 +17,36 @@ domain specific language embedded in Rust.
|
||||
This document describes the Python modules that form the embedded DSL.
|
||||
|
||||
The meta language descriptions are Python modules under the
|
||||
:file:`cranelift-codegen/meta-python` directory. The descriptions are processed in two
|
||||
`cranelift-codegen/meta-python` directory. The descriptions are processed in two
|
||||
steps:
|
||||
|
||||
1. The Python modules are imported. This has the effect of building static data
|
||||
structures in global values in the modules. These static data structures
|
||||
in the :mod:`base` and :mod:`isa` packages use the classes in the
|
||||
:mod:`cdsl` package to describe instruction sets and other properties.
|
||||
in the `base` and `isa` packages use the classes in the
|
||||
`cdsl` package to describe instruction sets and other properties.
|
||||
|
||||
2. The static data structures are processed to produce Rust source code and
|
||||
constant tables.
|
||||
|
||||
The main driver for this source code generation process is the
|
||||
:file:`cranelift-codegen/meta-python/build.py` script which is invoked as part of the build
|
||||
process if anything in the :file:`cranelift-codegen/meta-python` directory has changed
|
||||
`cranelift-codegen/meta-python/build.py` script which is invoked as part of the build
|
||||
process if anything in the `cranelift-codegen/meta-python` directory has changed
|
||||
since the last build.
|
||||
|
||||
|
||||
.. module:: cdsl.settings
|
||||
|
||||
Settings
|
||||
========
|
||||
|
||||
Settings are used by the environment embedding Cranelift to control the details
|
||||
of code generation. Each setting is defined in the meta language so a compact
|
||||
and consistent Rust representation can be generated. Shared settings are defined
|
||||
in the :mod:`base.settings` module. Some settings are specific to a target ISA,
|
||||
and defined in a :file:`settings.py` module under the appropriate
|
||||
:file:`cranelift-codegen/meta-python/isa/*` directory.
|
||||
in the `base.settings` module. Some settings are specific to a target ISA,
|
||||
and defined in a `settings.py` module under the appropriate
|
||||
`cranelift-codegen/meta-python/isa/*` directory.
|
||||
|
||||
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.
|
||||
|
||||
.. inheritance-diagram:: Setting BoolSetting NumSetting EnumSetting
|
||||
:parts: 1
|
||||
|
||||
.. autoclass:: Setting
|
||||
.. autoclass:: BoolSetting
|
||||
.. autoclass:: NumSetting
|
||||
.. autoclass:: EnumSetting
|
||||
|
||||
All settings must belong to a *group*, represented by a :class:`SettingGroup`
|
||||
object.
|
||||
|
||||
.. autoclass:: SettingGroup
|
||||
All settings must belong to a *group*, represented by a :class:`SettingGroup` object.
|
||||
|
||||
Normally, a setting group corresponds to all settings defined in a module. Such
|
||||
a module looks like this::
|
||||
@@ -74,9 +59,6 @@ a module looks like this::
|
||||
|
||||
group.close(globals())
|
||||
|
||||
|
||||
.. module:: cdsl.instructions
|
||||
|
||||
Instruction descriptions
|
||||
========================
|
||||
|
||||
@@ -84,27 +66,16 @@ New instructions are defined as instances of the :class:`Instruction`
|
||||
class. As instruction instances are created, they are added to the currently
|
||||
open :class:`InstructionGroup`.
|
||||
|
||||
.. autoclass:: InstructionGroup
|
||||
:members:
|
||||
|
||||
The basic Cranelift instruction set described in :doc:`ir` is defined by the
|
||||
Python module :mod:`base.instructions`. This module has a global value
|
||||
:data:`base.instructions.GROUP` which is an :class:`InstructionGroup` instance
|
||||
Python module `base.instructions`. This module has a global value
|
||||
`base.instructions.GROUP` which is an :class:`InstructionGroup` instance
|
||||
containing all the base instructions.
|
||||
|
||||
.. autoclass:: Instruction
|
||||
|
||||
.. currentmodule:: cdsl.operands
|
||||
|
||||
An instruction is defined with a set of distinct input and output operands which
|
||||
must be instances of the :class:`Operand` class.
|
||||
|
||||
.. autoclass:: Operand
|
||||
|
||||
Cranelift uses two separate type systems for operand kinds and SSA values.
|
||||
|
||||
.. module:: cdsl.typevar
|
||||
|
||||
Type variables
|
||||
--------------
|
||||
|
||||
@@ -113,9 +84,6 @@ Instruction descriptions can be made polymorphic by using
|
||||
instead of a concrete value type. Polymorphism only works for SSA value
|
||||
operands. Other operands have a fixed operand kind.
|
||||
|
||||
.. autoclass:: TypeVar
|
||||
:members:
|
||||
|
||||
If multiple operands refer to the same type variable they will be required to
|
||||
have the same concrete type. For example, this defines an integer addition
|
||||
instruction::
|
||||
@@ -138,61 +106,27 @@ There are some practical restrictions on the use of type variables, see
|
||||
Immediate operands
|
||||
------------------
|
||||
|
||||
.. currentmodule:: cdsl.operands
|
||||
|
||||
Immediate instruction operands don't correspond to SSA values, but have values
|
||||
that are encoded directly in the instruction. Immediate operands don't
|
||||
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
|
||||
indicated with an instance of :class:`ImmediateKind`.
|
||||
|
||||
.. autoclass:: ImmediateKind
|
||||
|
||||
.. automodule:: base.immediates
|
||||
:members:
|
||||
|
||||
Entity references
|
||||
-----------------
|
||||
|
||||
.. currentmodule:: cdsl.operands
|
||||
|
||||
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.
|
||||
|
||||
.. autoclass:: EntityRefKind
|
||||
|
||||
.. automodule:: base.entities
|
||||
:members:
|
||||
|
||||
Value types
|
||||
-----------
|
||||
|
||||
.. currentmodule:: cdsl.types
|
||||
|
||||
Concrete value types are represented as instances of :class:`ValueType`. There
|
||||
are subclasses to represent scalar and vector types.
|
||||
|
||||
.. autoclass:: ValueType
|
||||
.. inheritance-diagram:: ValueType LaneType VectorType IntType FloatType BoolType SpecialType FlagsType
|
||||
:parts: 1
|
||||
.. autoclass:: LaneType
|
||||
:members:
|
||||
.. autoclass:: VectorType
|
||||
.. autoclass:: SpecialType
|
||||
.. autoclass:: IntType
|
||||
.. autoclass:: FloatType
|
||||
.. autoclass:: BoolType
|
||||
.. autoclass:: FlagsType
|
||||
|
||||
.. automodule:: base.types
|
||||
:members:
|
||||
|
||||
There are no predefined vector types, but they can be created as needed with
|
||||
the :func:`LaneType.by` function.
|
||||
|
||||
|
||||
.. module:: cdsl.operands
|
||||
|
||||
Instruction representation
|
||||
==========================
|
||||
|
||||
@@ -202,24 +136,12 @@ written as Rust code in the ``cranelift.instructions`` module. The instruction
|
||||
representation depends on the input operand kinds and whether the instruction
|
||||
can produce multiple results.
|
||||
|
||||
.. autoclass:: OperandKind
|
||||
.. inheritance-diagram:: OperandKind ImmediateKind EntityRefKind
|
||||
|
||||
Since all SSA value operands are represented as a `Value` in Rust code, value
|
||||
types don't affect the representation. Two special operand kinds are used to
|
||||
represent SSA values:
|
||||
|
||||
.. autodata:: VALUE
|
||||
.. autodata:: VARIABLE_ARGS
|
||||
|
||||
.. module:: cdsl.formats
|
||||
types don't affect the representation.
|
||||
|
||||
When an instruction description is created, it is automatically assigned a
|
||||
predefined instruction format which is an instance of
|
||||
:class:`InstructionFormat`:
|
||||
|
||||
.. autoclass:: InstructionFormat
|
||||
|
||||
:class:`InstructionFormat`.
|
||||
|
||||
.. _restricted-polymorphism:
|
||||
|
||||
@@ -264,8 +186,6 @@ controlling type variable, or it can vary independently of the other operands.
|
||||
Encodings
|
||||
=========
|
||||
|
||||
.. currentmodule:: cdsl.isa
|
||||
|
||||
Encodings describe how Cranelift instructions are mapped to binary machine code
|
||||
for the target architecture. After the legalization pass, all remaining
|
||||
instructions are expected to map 1-1 to native instruction encodings. Cranelift
|
||||
@@ -277,7 +197,7 @@ incompatible encodings. For example, a modern ARMv8 CPU might support three
|
||||
different CPU modes: *A64* where instructions are encoded in 32 bits, *A32*
|
||||
where all instructions are 32 bits, and *T32* which has a mix of 16-bit and
|
||||
32-bit instruction encodings. These are incompatible encoding spaces, and while
|
||||
an :clif:inst:`iadd` instruction can be encoded in 32 bits in each of them, it's
|
||||
an `iadd` instruction can be encoded in 32 bits in each of them, it's
|
||||
not the same 32 bits. It's a judgement call if CPU modes should be modelled as
|
||||
separate targets, or as sub-modes of the same target. In the ARMv8 case, the
|
||||
different register banks means that it makes sense to model A64 as a separate
|
||||
@@ -288,8 +208,6 @@ instruction. Both RISC-V and ARMv8's T32 mode have 32-bit encodings of all
|
||||
instructions with 16-bit encodings available for some opcodes if certain
|
||||
constraints are satisfied.
|
||||
|
||||
.. autoclass:: CPUMode
|
||||
|
||||
Encodings are guarded by :term:`sub-target predicate`\s. For example, the RISC-V
|
||||
"C" extension which specifies the compressed encodings may not be supported, and
|
||||
a predicate would be used to disable all of the 16-bit encodings in that case.
|
||||
@@ -327,7 +245,7 @@ An :py:class:`Encoding` instance specifies the encoding of a concrete
|
||||
instruction. The following properties are used to select instructions to be
|
||||
encoded:
|
||||
|
||||
- An opcode, i.e. :clif:inst:`iadd_imm`, that must match the instruction's
|
||||
- An opcode, i.e. `iadd_imm`, that must match the instruction's
|
||||
opcode.
|
||||
- Values for any type variables if the opcode represents a polymorphic
|
||||
instruction.
|
||||
@@ -350,8 +268,6 @@ The additional predicates in the :py:class:`EncRecipe` are merged with the
|
||||
per-encoding predicates when generating the encoding matcher code. Often
|
||||
encodings only need the recipe predicates.
|
||||
|
||||
.. autoclass:: EncRecipe
|
||||
|
||||
Register constraints
|
||||
====================
|
||||
|
||||
@@ -371,9 +287,6 @@ Each encoding recipe specifies separate constraints for its value operands and
|
||||
result. These constraints are separate from the instruction predicate which can
|
||||
only evaluate the instruction's immediate operands.
|
||||
|
||||
.. module:: cdsl.registers
|
||||
.. autoclass:: RegBank
|
||||
|
||||
Register class constraints
|
||||
--------------------------
|
||||
|
||||
@@ -388,8 +301,6 @@ register class::
|
||||
This defines an encoding recipe for the ``Binary`` instruction format where
|
||||
both input operands must be allocated from the ``GPR`` register class.
|
||||
|
||||
.. autoclass:: RegClass
|
||||
|
||||
Tied register operands
|
||||
----------------------
|
||||
|
||||
@@ -420,7 +331,7 @@ Stack operands
|
||||
--------------
|
||||
|
||||
Cranelift's register allocator can assign an SSA value to a stack slot if there
|
||||
isn't enough registers. It will insert :clif:inst:`spill` and :clif:inst:`fill`
|
||||
isn't enough registers. It will insert `spill` and `fill`
|
||||
instructions as needed to satisfy instruction operand constraints, but it is
|
||||
also possible to have instructions that can access stack slots directly::
|
||||
|
||||
@@ -429,27 +340,14 @@ also possible to have instructions that can access stack slots directly::
|
||||
An output stack value implies a store to the stack, an input value implies a
|
||||
load.
|
||||
|
||||
.. module:: cdsl.isa
|
||||
|
||||
Targets
|
||||
=======
|
||||
|
||||
Cranelift can be compiled with support for multiple target instruction set
|
||||
architectures. Each ISA is represented by a :py:class:`cdsl.isa.TargetISA` instance.
|
||||
|
||||
.. autoclass:: TargetISA
|
||||
|
||||
The definitions for each supported target live in a package under
|
||||
:file:`cranelift-codegen/meta-python/isa`.
|
||||
|
||||
.. automodule:: isa
|
||||
:members:
|
||||
|
||||
.. automodule:: isa.riscv
|
||||
.. automodule:: isa.x86
|
||||
.. automodule:: isa.arm32
|
||||
.. automodule:: isa.arm64
|
||||
|
||||
`cranelift-codegen/meta-python/isa`.
|
||||
|
||||
Glossary
|
||||
========
|
||||
|
||||
Reference in New Issue
Block a user