blob: 1c0c8b5bd67d741ad5d1251f096d67e9ed6018ca [file] [log] [blame] [view]
# Function signatures
A key job of the IREE compiler and runtime is capturing function call semantics
from the originating system and providing mechanisms so that invocations can be
performed in as similar way as possible in various target languages. In general,
this requires additional metadata on top of the raw characteristics of a
function. Where possible, this is done by attaching attributes to a function.
* `abi` : string indiciating the abi/calling convention in use
* `abiv` : numeric version of the `abi`
Each abi can require additional attributes as needed.
## Generic Signature Mangling
Where possible, ABI metadata is encoded into a plain-text signature in a way
that is easily transported across component boundaries and can be efficiently
implemented without additional dependencies (i.e. just string manipulation).
The suggested format is manipulated via the C++ reference implementations
`SignatureBuilder` and `SignatureParser` classes (see
`iree/base/signature_mangle.h`). See documentation and code for those classes
for more details.
## ABIs
### Structured Index Path ABI
* `abi` = `sip`
* Current `abiv` version = 1
This ABI maps a raw, linear sequence of inputs and results onto an input and
result "structure" -- which in this context refers to a nested assembly of
sequences (with integer keys) and dictionaries (with string keys). Such a
facility is useful for encoding input/result mappings in a way that is common in
dynamic languages (such as Python).
In practice, this ABI supports the calling convention for TensorFlow, which
allows functions that accept and produce nestings via the
[`tf.nest`](https://www.tensorflow.org/api_docs/python/tf/nest) facility. In
implementing it, however, care has been taken to allow the calling convention to
generalize to other similar cases.
#### Grammar
The signature is implemented in terms of the SignatureBuilder, using tagged
Integer and Spans.
```text
# Defines the structured value for the inputs ('I') and results ('R')
# of the function.
signature ::= 'I' length-prefixed(structured-value)
'R' length-prefixed(structured-value)
structured-value ::= raw-fn-index | sequence | dict
raw-fn-index ::= '_' integer
sequence ::= 'S' length-prefixed( (integer-key structured-value)* )
integer-key ::= 'k' integer
dict ::= 'D' length-prefixed( (string-key structured-value)* )
string-key ::= 'K' length-prefixed( any-byte-sequence )
# Low-level lexical primitives:
integer ::= -?[0-9]+
length ::= [0-9]+
# The `length` encodes the length in bytes of `production`, plus 1 for the '!'.
length-prefixed(production) ::= length '!' production
any-byte-sequence ::= <any byte sequence>
```
Structured values define a tree of recursive dicts/lists, with `raw-fn-index` at
the leaves. The interpretation is that a raw-fn-index that has been reached by
traversing N expansions of the structured-value production is assigned an "index
path" which is a list of the N keys that were traversed to reach it. For
example, for N=0, the index path is empty. For N=1, and if an integer-key with
numerical value 0 was traversed to reach the raw-fn-index, then the index path
is [0].
.... give a few examples more, writing out various nested dicts/lists in
Python-esque notation to clarify this concept ....
See the `SipSignatureParser::ToStringVisitor` for a canonical example of how to
interpret the signature.
#### Implementations
* C++
* `SipSignatureMangler`: Produces a function signature given individual
input and result assignment of physical indices to nested index paths in
the structure tree.
* `SipSignatureParser`: Parses signatures and dispatches calls to a
visitor.