This feature was originally added to implement the state monad. It is not needed anymore since we have a better encoding that doesn't require this feature and avoids memory allocations.
274 lines
12 KiB
Text
274 lines
12 KiB
Text
/-
|
|
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura
|
|
-/
|
|
prelude
|
|
import init.data.rbmap init.data.int init.control.state init.control.except init.control.combinators
|
|
import init.lean.name
|
|
|
|
/-
|
|
Missing
|
|
- float/double literals are strings since we did not define them in Lean
|
|
- borrowed annotations
|
|
-/
|
|
|
|
namespace lean
|
|
namespace ir
|
|
/-
|
|
The type `object` is a pointer to constructor/composite objects, arrays of objects, scalar arrays (`string` is a scalar array),
|
|
closures, big numbers (implemented using GMP), external objects or a tagged pointer.
|
|
Tagged pointers are used to represent small scalar values in polymorphic code.
|
|
|
|
The other types `bool/byte/uint16/uint32/uint64/usize/int16/int32/int64/float/double` are the usual unboxed scalar data.
|
|
We box values of type `bool/byte/uint16/uint32/int16/int32/float` are boxed (in 64-bit machines) as tagged pointers.
|
|
`usize/uint64/int64/double` are boxed by creating a composite object with a single field. -/
|
|
inductive type | bool | byte | uint16 | uint32 |
|
|
uint64 | usize | int16 | int32 | int64 | float | double | object
|
|
|
|
/-- Operators for instructions of the form `x : t := op y`
|
|
|
|
- `x : t := not y`, if `t = bool`, then it is the logical negation.
|
|
Otherwise, it is bitwise negation if `t` is `uint32/uint64/usize`.
|
|
|
|
- `x : t := neg y`, arithmetical `-`. `t` is `int16/int32/int64/float/double`.
|
|
|
|
- `x : object := ineg y`, integer `-`.
|
|
|
|
- `x : object := nat2int y`, convert a natural (big) number into an integer (big) number.
|
|
|
|
- `x : bool := is_scalar y`, set `x` to `tt` iff `y : object` is a tagged
|
|
pointer.
|
|
|
|
- `x : bool := is_shared y`, set `x` to `tt` iff `y : object` `RC(y)` is greater than 1.
|
|
The behavior is unspecified if `y` is a tagged pointer.
|
|
|
|
- `x : bool := is_null y`, set `x` to `tt` iff `y : object` is null.
|
|
|
|
- `x : t := cast y` is a scalar type casting operation. `t` and `typeof(y)` must not be
|
|
`object`.
|
|
|
|
- `x : object := box y` convert `y : t` where `t` is `int32/uint32` into a tagged
|
|
pointer. `y` must fit in 31bits (in 32-bit machines).
|
|
|
|
- `x : t := unbox y` convert `y : object` back into a scalar value. `t` is `int32/uint32`.
|
|
The behavior is unspecified if `y` is not a tagged pointer.
|
|
|
|
- `x : object := array_copy y` creates a copy of the array `y : object`.
|
|
The behavior is unspecified if `y` is not an array of objects.
|
|
|
|
- `x : object := sarray_copy y` creates a copy of the scalar array `y : object`.
|
|
The behavior is unspecified if `y` is not an array of scalar values.
|
|
Remark: `sarray_copy` can be used to copy strings.
|
|
|
|
- `x : usize := array_size y` stores the size of the array `y : object` into `x`.
|
|
The behavior is unspecified if `y` is not an array of objects.
|
|
|
|
- `x : usize := sarray_size y` stores the size of the scalar array `y : object` into `x`.
|
|
The behavior is unspecified if `y` is not an array of scalar values.
|
|
|
|
- `x : usize := string_len y` stores the length of the string `y : object` into `x`.
|
|
The length is the number of unicode scalar values.
|
|
The behavior is unspecified if `y` is not a string.
|
|
|
|
- `x : object := succ y` natural number successor.
|
|
|
|
- `x : uint32 := tag_ref y` return the tag of the (constructor) object `y`. `y` must not be
|
|
a tagged pointer.
|
|
|
|
- `x : uint32 := tag y` return the tag of the (constructor) object `y` OR tagged pointer.
|
|
-/
|
|
inductive assign_unop
|
|
| not | neg | ineg | nat2int | is_scalar | is_shared | is_null | cast | box | unbox
|
|
| array_copy | sarray_copy | array_size | sarray_size | string_len
|
|
| succ | tag | tag_ref
|
|
|
|
/-- Operators for instructions of the form `x : t := op y z`
|
|
|
|
- `x : t := add y z`: addition. Remark: `t ≠ bool`.
|
|
When `t = object`, i.e., `t` is big number or tagged pointer, a new big number may be allocated
|
|
if the result does not fit in a tagged pointer.
|
|
|
|
- `x : t := sub y z`: subtraction. Remark: `t ≠ bool`. See `add` for big number case.
|
|
|
|
- `x : t := mul y z`: multiplication. Remark: `t ≠ bool`. See `add` for big number case.
|
|
|
|
- `x : t := div y z`: division. Remark: `t ≠ bool`. See `add` for big number case.
|
|
|
|
- `x : t := mod y z`: modulo. Remark: `t ≠ bool`, `t ≠ float` and `t ≠ double`. See `add` for big number case.
|
|
|
|
- `x : object := iadd y z`: (big) integer addition.
|
|
|
|
- `x : object := isub y z`: (big) integer subtraction.
|
|
|
|
- `x : object := imul y z`: (big) integer multiplication.
|
|
|
|
- `x : object := idiv y z`: (big) integer division.
|
|
|
|
- `x : object := imod y z`: (big) integer modulo.
|
|
|
|
- `x : t := shl y z`: bit shift left. Remark: `t ≠ bool`, `t ≠ float`, `t ≠ double` and `t ≠ object`.
|
|
|
|
- `x : t := shr y z`: bit shift right. Remark: `t ≠ bool`, `t ≠ float`, `t ≠ double` and `t ≠ object`.
|
|
If `t` is `int16`, `int32` or `int64`, it is an arithmetical bit shift.
|
|
|
|
- `x : t := and y z`: if `t = bool`, then it is the logical and. Otherwise, it is the bitwise and.
|
|
Remark: `t ≠ bool`, `t ≠ float`, `t ≠ double` and `t ≠ object`.
|
|
|
|
- `x : t := or y z`: if `t = bool`, then it is the logical or. Otherwise, it is the bitwise or.
|
|
Remark: `t ≠ bool`, `t ≠ float`, `t ≠ double` and `t ≠ object`.
|
|
|
|
- `x : t := xor y z`: if `t = bool`, then it is the logical xor. Otherwise, it is the bitwise xor.
|
|
Remark: `t ≠ bool`, `t ≠ float`, `t ≠ double` and `t ≠ object`.
|
|
|
|
- `x : bool := le y z`: less than or equal to. Remark: `t ≠ bool`.
|
|
If `y` and `z` are `object`, then they must be big numbers.
|
|
|
|
- `x : bool := lt y z`: less than. Remark: `t ≠ bool`.
|
|
If `y` and `z` are `object`, then they must be big numbers.
|
|
|
|
- `x : bool := eq y z`: equality test. If `y` and `z` are `object`, then they must be big numbers.
|
|
|
|
- `x : bool := ne y z`: disequality test. If `y` and `z` are `object`, then they must be big numbers.
|
|
|
|
- `x : bool := ile y z`: (big) integer less than or equal to. `y` and `z` have type `object`.
|
|
|
|
- `x : bool := ilt y z`: (big) integer less than. `y` and `z` have type `object`.
|
|
|
|
- `x : bool := ieq y z`: (big) integer equality. `y` and `z` have type `object`.
|
|
|
|
- `x : bool := ine y z`: (big) integer disequality. `y` and `z` have type `object`.
|
|
|
|
- `x : t := array_read a i`: Read position `i` of the array `a`. `a` must be an (array) `object`.
|
|
If `a` is a scalar array, then `t ≠ object`. If `a` is an (non-scalar) array, then `t = object`.
|
|
|
|
- `r : object := array_push a x`: push element `x` in the end of array `a`. `x` must have type `object`, `RC(x) = 1`, and it must
|
|
be an array. If `x` has a scalar type, then `a` is an array of scalar. Otherwise, it is an array of objects.
|
|
Remark: if `a` has space for the new element, then `r` is set to `a`. Otherwise, a new array object is allocated, set to `r`,
|
|
and `a` is deleted.
|
|
|
|
- `r : object := string_push s c`: push character `c` in the end of string `s`. `s` must have type `object`, and `RC(s) = 1`.
|
|
be a string.
|
|
Remark: if `s` has space for the new element, then `r` is set to `s`. Otherwise, a new string is allocated, set to `r`,
|
|
and `s` is deleted.
|
|
|
|
- `r : object : string_append s₁ s₂`: append string `s₂` in the end of string `s₁`. `s₁` must have type `object`, and `RC(s₁) = 1`.
|
|
Remark: if `s₁` has space for all `s₂` characters, then `r` is set to `s₁`. Otherwise, a new string is allocated, set to `r`,
|
|
and `s₁` is deleted.
|
|
|
|
Remark: in the future we may add instructions for performing updates destructively on big numbers. Example:
|
|
`add_acc x y` would be `x += y`, and require `RC(x) = 1`. -/
|
|
inductive assign_binop
|
|
| add | sub | mul | div | mod
|
|
| iadd | isub | imul | idiv | imod
|
|
| shl | shr | and | or | xor
|
|
| le | lt | eq | ne
|
|
| ile | ilt | ieq | ine
|
|
| array_read | array_push | string_push | string_append
|
|
|
|
/-- Operators for instructions of the form `op x`
|
|
|
|
- `inc_ref x`: increment `RC(x)`, `x` must have type `object`, and it must not be a tagged pointer.
|
|
|
|
- `dec_ref x`: decrement `RC(x)`, `x` must have type `object`, and it must not be a tagged pointer.
|
|
If `RC(x)` becomes zero, then `x` is deleted.
|
|
|
|
- `dec_sref x`: decrement `RC(x)`, `x` must have type `object`, `RC(x) > 1`, and it must not be a tagged pointer.
|
|
That is, `x` must be a shared object.
|
|
|
|
- `inc x`: increment `RC(x)` IF `x` is not a tagged pointer. `x` must have type `object`.
|
|
|
|
- `dec x`: decrement `RC(x)` IF `x` is not a tagged pointer. `x` must have type `object`.
|
|
|
|
- `free x`: free `x`'s memory. `x` must have type `object`, it must not be a tagged pointer,
|
|
and it must not be an external or big number.
|
|
|
|
- `dealloc x`: finalize `x` and free `x`'s memory. `x` must have type `object`, and it must not be a tagged pointer.
|
|
|
|
- `array_pop x`: remove the last element of array `x`. `x` must have type `object`, `RC(x) = 1`, and it must
|
|
be an array of objects.
|
|
|
|
- `sarray_pop x`: remove the last element of array `x`. `x` must have type `object`, `RC(x) = 1`, and it must
|
|
be an array of scalar values. -/
|
|
inductive unop
|
|
| inc_ref | dec_ref | dec_sref | inc | dec
|
|
| free | dealloc
|
|
| array_pop | sarray_pop
|
|
|
|
inductive literal
|
|
| bool : bool → literal
|
|
| str : string → literal
|
|
| num : int → literal -- for uint32/uint64/int32/int64/byte/object literals
|
|
| float : string → literal -- for float/double literals
|
|
|
|
def tag := uint16
|
|
def var := name
|
|
def fnid := name
|
|
def blockid := name
|
|
|
|
/- IR Instructions -/
|
|
inductive instr
|
|
| assign (x : var) (ty : type) (y : var) -- x : ty := y
|
|
| assign_lit (x : var) (ty : type) (lit : literal) -- x : ty := lit
|
|
| assign_unop (x : var) (ty : type) (op : assign_unop) (y : var) -- x : ty := op y
|
|
| assign_binop (x : var) (ty : type) (op : assign_binop) (y z : var) -- x : ty := op y z
|
|
| unop (op : unop) (x : var) -- op x
|
|
| call (x : var) (f : fnid) (ys : list var) -- Function call: x := f ys
|
|
/- Constructor objects -/
|
|
| cnstr (o : var) (tag : tag) (nobjs : uint16) (ssz : usize) -- Create constructor object
|
|
| set (o : var) (i : uint16) (x : var) -- Set object field: set o i x
|
|
| get (x : var) (o : var) (i : uint16) -- Get object field: x := get o i
|
|
| sset (o : var) (d : usize) (v : var) -- Set scalar field: sset o d v
|
|
| sget (x : var) (ty : type) (o : var) (d : usize) -- Get scalar field: x : ty := sget o d
|
|
/- Closures -/
|
|
| closure (x : var) (f : fnid) (ys : list var) -- Create closure: x := closure f ys
|
|
| apply (x : var) (ys : list var) -- Apply closure: x := apply ys
|
|
/- Arrays -/
|
|
| array (a sz c : var) -- Create array of objects with size `sz` and capacity `c`
|
|
| sarray (a : var) (ty : type) (sz c : var) -- Create scalar array
|
|
| array_write (a i v : var) -- (scalar) Array write write a i v
|
|
|
|
structure phi :=
|
|
(x : var) (ty : type) (ys : list var)
|
|
|
|
inductive terminator
|
|
| ret (y : var)
|
|
| case (x : var) (bs : list blockid)
|
|
| jmp (b : blockid)
|
|
|
|
structure block :=
|
|
(id : blockid) (phis : list phi) (instrs : list instr) (term : terminator)
|
|
|
|
structure arg :=
|
|
(n : var) (ty : type)
|
|
|
|
structure result :=
|
|
(ty : type)
|
|
|
|
/--
|
|
Header of function declarations.
|
|
If `is_const` is `tt` than it is a constant declaration.
|
|
The result of this kind of function (when `args = []`) is precomputed
|
|
during compilation unit initialization. -/
|
|
structure header :=
|
|
(name : fnid) (args : list arg) (return : result) (is_const : bool)
|
|
|
|
inductive decl
|
|
| external (h : header)
|
|
| defn (h : header) (bs : list block)
|
|
|
|
def decl.is_definition : decl → bool
|
|
| (decl.defn _ _) := tt
|
|
| _ := ff
|
|
|
|
def decl.header : decl → header
|
|
| (decl.external h) := h
|
|
| (decl.defn h _) := h
|
|
|
|
def decl.name (d : decl) : name :=
|
|
d.header.name
|
|
|
|
def environment := fnid → option decl
|
|
|
|
end ir
|
|
end lean
|