This document describes the primitive operators (primops) that the
compiler exposes. These are used to define the InlineT
structure,
which, in turn is used in the implementation of the Basis Library.
With the addition of 64-bit targets, the mapping from primop to
internal representation becomes target-specific in many cases.
-
compiler/ElabData/prim/primop.sml
this file defines thePrimop
structure, which includes the various datatypes used to represent primitive operations internally in the front-end of the compiler. The main type isPrimop.primop
. -
compiler/ElabData/prim/primop.sml
this file defines thePRIMOP
signature use for thePrimop
structure. -
compiler/Semant/prim/primop-bindings.sml
this file defines the bindings between the SML variables exposed by the compiler and the internalPrimop.primop
type. -
system/smlnj/init/built-in32.sml
this file defines theInlineT
structure for 32-bit targets
Operations that "belong" to a specific type (e.g., addition) have an initial prefix that specifies the type as follows:
- "
int
" -- default tagged integer type (i.e., eitherInt31.int
orInt63.int
) - "
word
" -- default tagged word type (i.e., eitherWord31.word
orWord63.word
) - "
int32
" -- 32-bit integers - "
word32
" -- 32-bit words - "
int64
" -- 64-bit integers - "
word64
" -- 64-bit words - "
intinf
" -- arbitrary precision integers - "
real32
" -- 32-bit real numbers (not yet supported) - "
real64
" -- 64-bit real numbers - "
ptr
" -- machine address - "
barr
" -- bytearray (used for arrays ofWord8.word
andchar
) - "
bvec
" -- bytevector (used for strings and vectors ofWord8.word
) - "
arr
" -- polymorphic arrays - "
vec
" -- polymorphic vectors - "
seq
" -- sequence types (arrays and vectors)
We use the attribute "raw
" to denote direct machine operations that are not
directly accesible in the Basis Library (e.g., shift operations, where the basis
versions clamp the shift amount to the word size, but the raw versions do not).
We use the attribute "unsafe
" for operations that could potentially result in
a crash (e.g., array subscript operations that do not check the index against
the array bounds).
-
callcc : ('a cont -> 'a) -> 'a
P.CALLCC
-
throw : 'a cont -> 'a -> 'b
P.THROW
-
capture : ('a control_cont -> 'a) -> 'a
P.CAPTURE
-
isolate : ('a -> unit) -> 'a cont
P.ISOLATE
-
cthrow : 'a control_cont -> 'a -> 'b
P.THROW
-
! : 'a ref -> 'a
P.DEREF
-
:= : 'a ref * 'a -> unit
P.ASSIGN
-
makeref : 'a ref * 'a -> unit
P.MAKEREF
-
boxed : 'a -> bool
P.BOXED
-
unboxed : 'a -> bool
P.UNBOXED
cast : 'a -> 'b
P.CAST
-
= : ''a * ''a -> bool
P.POLYEQL
-
<> : ''a * ''a -> bool
P.POLYNEQ
-
ptr_eql : 'a * 'a -> bool
P.PTREQL
-
ptr_neq : 'a * 'a -> bool
P.PTRNEQ
-
getvar : unit -> 'a
P.GETVAR
-
setvar : 'a -> unit
P.SETVAR
-
mkspecial : int * 'a -> 'b
P.MKSPECIAL
-
getspecial : 'a -> int
P.GETSPECIAL
-
setspecial : 'a * int -> unit
P.SETSPECIAL
-
gethdlr : unit -> 'a cont
P.GETHDLR
-
sethdlr : 'a cont -> unit
P.SETHDLR
-
gettag : 'a -> int
P.GETTAG
-
objlength : 'a -> int
extracts the length field from an object's header word.P.OBJLENGTH
These primops are Basis Library functions that should be inlined for efficiency.
-
compose : ('b -> 'c) * ('a -> 'b) -> 'a -> 'c
P.INLCOMPOSE
-
before : 'a * 'b -> 'a
P.INLBEFORE
-
ignore : 'a -> unit
P.INLIGNORE
-
identity : 'a -> 'a
P.INLIDENTITY
-
bool_not : bool -> bool
P.INLNOT
Some additional candidates for inlined operations include hd
, tl
, null
, chr
, and ord
.
If the compiler had the option
and order
datatypes builtin (like bool
and list
),
then valOf
, isSome
, isNone
and some of the compare
functions could be inlined.
Operations on byte/char array/vectors. We renamed these to make it clear which operations do bounds checking and which do not.
-
bvec_unsafe_sub : 'a * int -> 'b
subscript from byte vector without bounds checking (P.NUMSUBSCRIPT{kind=P.INT 8, checked=false, immutable=true}
) -
barr_unsafe_sub : 'a * int -> 'b
subscript from byte array without bounds checking (P.NUMSUBSCRIPT{kind=P.INT 8, checked=false, immutable=false}
) -
barr_unsafe_update : 'a * int * 'b -> unit
update byte array without bounds checking (P.NUMUPDATE{kind=P.INT 8, checked=false}
) -
bvec_sub : 'a * int -> 'b
subscript from byte vector (P.NUMSUBSCRIPT{kind=P.INT 8, checked=true, immutable=true}
) -
barr_sub : 'a * int -> 'b
subscript from byte array (P.NUMSUBSCRIPT{kind=P.INT 8, checked=true, immutable=false}
) -
barr_update : 'a * int * 'b -> unit
update byte array (P.NUMUPDATE{kind=P.INT 8, checked=true}
)
-
mkarray : int * 'a -> 'a array
create a polymorphic array (P.INLMKARRAY
) -
arr_unsafe_sub : 'a array * int -> 'a
subscript from polymorphic array without bounds checking (P.SUBSCRIPT
) -
arr_sub : 'a array * int -> 'a
subscript from polymorphic array (P.INLSUBSCRIPT
) -
vec_unsafe_sub : 'a vector * int -> 'a
subscript from polymorphic vector without bounds checking (P.SUBSCRIPTV
) -
vec_sub : 'a vector * int -> 'a
subscript from polymorphic vector (P.INLSUBSCRIPTV
) -
arr_unsafe_update : 'a array * int * 'a -> unit
update a polymorphic array without bounds checking (P.UPDATE
) -
arr_update : 'a array * int * 'a -> unit
update a polymorphic array (P.INLUPDATE
) -
arr_unboxed_update : 'a array * int * 'a -> unit
update a polymorphic array with an unboxed value, which means that there is no store-list entry created for the update.P.UNBOXEDUPDATE
Sequence values (e.g., string
, 'a array
, RealVector.vector
, etc.)
are represented by a header consisting of a length (in elements) and a data
pointer to the raw sequence data.
-
newArray0 : unit -> 'a
P.NEW_ARRAY0
-
seq_length : 'a -> int
get the length field from a sequence headerP.LENGTH
-
seq_data : 'a -> 'b
get the length field from a sequence headerP.GET_SEQ_DATA
-
unsafe_record_sub : 'a * int -> 'b
P.SUBSCRIPT_REC
-
raw64Sub : 'a * int -> real64
Unclear what purpose this primop servesP.SUBSCRIPT_RAW64
These are the primitive operations on the default tagged integer
type (Int.int
).
-
int_add : int * int -> int
Signed integer addition with overflow checking.P.ARITH{oper=P.ADD, overflow=true, kind=P.INT <int-size>}
-
int_unsafe_add : int * int -> int
Signed integer addition without overflow checking.P.ARITH{oper=P.ADD, overflow=false, kind=P.INT <int-size>}
-
int_sub : int * int -> int
Signed integer subtraction with overflow checking.P.ARITH{oper=P.SUB, overflow=true, kind=P.INT <int-size>}
-
int_unsafe_sub : int * int -> int
Signed integer subtraction without overflow checking.P.ARITH{oper=P.SUB, overflow=false, kind=P.INT <int-size>}
-
int_mul : int * int -> int
P.ARITH{oper=P.MUL, overflow=true, kind=P.INT <int-size>}
-
int_div : int * int -> int
P.ARITH{oper=P.QUOT, overflow=true, kind=P.INT <int-size>}
-
int_mod : int * int -> int
P.ARITH{oper=P.REM, overflow=true, kind=P.INT <int-size>}
-
int_quot : int * int -> int
P.ARITH{oper=P.QUOT, overflow=true, kind=P.INT <int-size>}
-
int_rem : int * int -> int
P.ARITH{oper=P.REM, overflow=true, kind=P.INT <int-size>}
-
int_orb : int * int -> int
P.ARITH{oper=P.ORB, overflow=false, kind=P.INT <int-size>}
-
int_xorb : int * int -> int
P.ARITH{oper=P.XORB, overflow=false, kind=P.INT <int-size>}
-
int_andb : int * int -> int
P.ARITH{oper=P.ANDB, overflow=false, kind=P.INT <int-size>}
-
int_neg : word32 -> word32
P.ARITH{oper=P.NEG, overflow=true, kind=P.INT <int-size>}
-
int_raw_rshift : int * word -> int
P.ARITH{oper=P.RSHIFT, overflow=false, kind=P.INT <int-size>}
-
int_raw_lshift : int * word -> int
P.ARITH{oper=P.LSHIFT, overflow=false, kind=P.INT <int-size>}
-
int_gt : int * int -> bool
P.CMP{oper=P.GT, kind=P.INT <int-size>}
-
int_ge : int * int -> bool
P.CMP{oper=P.GTE, kind=P.INT <int-size>}
-
int_lt : int * int -> bool
P.CMP{oper=P.LT, kind=P.INT <int-size>}
-
int_le : int * int -> bool
P.CMP{oper=P.LTE, kind=P.INT <int-size>}
-
int_eql : int * int -> bool
P.CMP{oper=P.EQL, kind=P.INT <int-size>}
-
int_neq : int * int -> bool
P.CMP{oper=P.NEQ, kind=P.INT <int-size>}
-
int_min : int * int -> int
P.INLMIN (P.INT <int-size>)
-
int_max : int * int -> int
P.INLMAX (P.INT <int-size>)
-
int_abs : word32 -> word32
P.INLABS (P.INT <int-size>)
These are the primitive operations on the default tagged word
type (Word.word
).
-
word_mul : word * word -> word
P.ARITH{oper=P.MUL, overflow=false, kind=P.INT <int-size>}
-
word_div : word * word -> word
P.ARITH{oper=P.QUOT, overflow=false, kind=P.INT <int-size>}
-
word_mod : word * word -> word
P.ARITH{oper=P.REM, overflow=false, kind=P.INT <int-size>}
-
word_add : word * word -> word
P.ARITH{oper=P.ADD, overflow=false, kind=P.INT <int-size>}
-
word_sub : word * word -> word
P.ARITH{oper=P.SUB, overflow=false, kind=P.INT <int-size>}
-
word_orb : word * word -> word
P.ARITH{oper=P.ORB, overflow=false, kind=P.INT <int-size>}
-
word_xorb : word * word -> word
P.ARITH{oper=P.XORB, overflow=false, kind=P.INT <int-size>}
-
word_andb : word * word -> word
P.ARITH{oper=P.ANDB, overflow=false, kind=P.INT <int-size>}
-
word_notb : word -> word
P.ARITH{oper=P.NOTB, overflow=false, kind=P.INT <int-size>}
-
word_neg : word -> word
P.ARITH{oper=P.NEG, overflow=false, kind=P.INT <int-size>}
-
word_rshift : word * word -> word
P.ARITH{oper=P.RSHIFT, overflow=false, kind=P.INT <int-size>}
-
word_rshiftl : word * word -> word
P.ARITH{oper=P.RSHIFTL, overflow=false, kind=P.INT <int-size>}
-
word_lshift : word * word -> word
P.ARITH{oper=P.LSHIFT, overflow=false, kind=P.INT <int-size>}
-
word_gt : word * word -> bool
P.CMP{oper=P.GT, kind=P.UINT <int-size>}
-
word_ge : word * word -> bool
P.CMP{oper=P.GTE, kind=P.UINT <int-size>}
-
word_lt : word * word -> bool
P.CMP{oper=P.LT, kind=P.UINT <int-size>}
-
word_le : word * word -> bool
P.CMP{oper=P.LTE, kind=P.UINT <int-size>}
-
word_eql : word * word -> bool
P.CMP{oper=P.EQL, kind=P.UINT <int-size>}
-
word_neq : word * word -> bool
P.CMP{oper=P.NEQ, kind=P.UINT <int-size>}
-
word_raw_rshift : word * word -> word
P.INLRSHIFT(P.UINT <int-size>)
-
word_raw_rshiftl : word * word -> word
P.INLRSHIFTL(P.UINT <int-size>)
-
word_raw_lshift : word * word -> word
P.INLLSHIFT(P.UINT <int-size>)
-
word_min : word * word -> word
P.INLMIN (P.UINT <int-size>)
-
word_max : word * word -> word
P.INLMAX (P.UINT <int-size>)
-
int32_add : int32 * int32 -> int32
P.ARITH{oper=P.ADD, overflow=true, kind=P.INT 32}
-
int32_sub : int32 * int32 -> int32
P.ARITH{oper=P.SUB, overflow=true, kind=P.INT 32}
-
int32_mul : int32 * int32 -> int32
P.ARITH{oper=P.MUL, overflow=true, kind=P.INT 32}
-
int32_div : int32 * int32 -> int32
P.ARITH{oper=P.QUOT, overflow=true, kind=P.INT 32}
-
int32_mod : int32 * int32 -> int32
P.ARITH{oper=P.REM, overflow=true, kind=P.INT 32}
-
int32_quot : int32 * int32 -> int32
P.ARITH{oper=P.QUOT, overflow=true, kind=P.INT 32}
-
int32_rem : int32 * int32 -> int32
P.ARITH{oper=P.REM, overflow=true, kind=P.INT 32}
-
int32_orb : int32 * int32 -> int32
P.ARITH{oper=P.ORB, overflow=false, kind=P.INT 32}
-
int32_xorb : int32 * int32 -> int32
P.ARITH{oper=P.XORB, overflow=false, kind=P.INT 32}
-
int32_andb : int32 * int32 -> int32
P.ARITH{oper=P.ANDB, overflow=false, kind=P.INT 32}
-
int32_neg : word32 -> word32
P.ARITH{oper=P.NEG, overflow=true, kind=P.INT 32}
-
int32_raw_rshift : int32 * word -> int32
P.ARITH{oper=P.RSHIFT, overflow=false, kind=P.INT 32}
-
int32_raw_lshift : int32 * word -> int32
P.ARITH{oper=P.LSHIFT, overflow=false, kind=P.INT 32}
-
int32_gt : int32 * int32 -> bool
P.CMP{oper=P.GT, kind=P.INT 32}
-
int32_ge : int32 * int32 -> bool
P.CMP{oper=P.GTE, kind=P.INT 32}
-
int32_lt : int32 * int32 -> bool
P.CMP{oper=P.LT, kind=P.INT 32}
-
int32_le : int32 * int32 -> bool
P.CMP{oper=P.LTE, kind=P.INT 32}
-
int32_eql : int32 * int32 -> bool
P.CMP{oper=P.EQL, kind=P.INT 32}
-
int32_neq : int32 * int32 -> bool
P.CMP{oper=P.NEQ, kind=P.INT 32}
-
int32_min : int32 * int32 -> int32
P.INLMIN (P.INT 32)
-
int32_max : int32 * int32 -> int32
P.INLMAX (P.INT 32)
-
int32_abs : word32 -> word32
P.INLABS (P.INT 32)
-
int64_add : int64 * int64 -> int64
P.ARITH{oper=P.ADD, overflow=true, kind=P.INT 64}
-
int64_sub : int64 * int64 -> int64
P.ARITH{oper=P.SUB, overflow=true, kind=P.INT 64}
-
int64_mul : int64 * int64 -> int64
P.ARITH{oper=P.MUL, overflow=true, kind=P.INT 64}
-
int64_div : int64 * int64 -> int64
P.ARITH{oper=P.QUOT, overflow=true, kind=P.INT 64}
-
int64_mod : int64 * int64 -> int64
P.ARITH{oper=P.REM, overflow=true, kind=P.INT 64}
-
int64_quot : int64 * int64 -> int64
P.ARITH{oper=P.QUOT, overflow=true, kind=P.INT 64}
-
int64_rem : int64 * int64 -> int64
P.ARITH{oper=P.REM, overflow=true, kind=P.INT 64}
-
int64_orb : int64 * int64 -> int64
P.ARITH{oper=P.ORB, overflow=false, kind=P.INT 64}
-
int64_xorb : int64 * int64 -> int64
P.ARITH{oper=P.XORB, overflow=false, kind=P.INT 64}
-
int64_andb : int64 * int64 -> int64
P.ARITH{oper=P.ANDB, overflow=false, kind=P.INT 64}
-
int64_neg : word32 -> word32
P.ARITH{oper=P.NEG, overflow=true, kind=P.INT 64}
-
int64_raw_rshift : int64 * word -> int64
P.ARITH{oper=P.RSHIFT, overflow=false, kind=P.INT 64}
-
int64_raw_lshift : int64 * word -> int64
P.ARITH{oper=P.LSHIFT, overflow=false, kind=P.INT 64}
-
int64_gt : int64 * int64 -> bool
P.CMP{oper=P.GT, kind=P.INT 64}
-
int64_ge : int64 * int64 -> bool
P.CMP{oper=P.GTE, kind=P.INT 64}
-
int64_lt : int64 * int64 -> bool
P.CMP{oper=P.LT, kind=P.INT 64}
-
int64_le : int64 * int64 -> bool
P.CMP{oper=P.LTE, kind=P.INT 64}
-
int64_eql : int64 * int64 -> bool
P.CMP{oper=P.EQL, kind=P.INT 64}
-
int64_neq : int64 * int64 -> bool
P.CMP{oper=P.NEQ, kind=P.INT 64}
-
int64_min : int64 * int64 -> int64
P.INLMIN (P.INT 64)
-
int64_max : int64 * int64 -> int64
P.INLMAX (P.INT 64)
-
int64_abs : word32 -> word32
P.INLABS (P.INT 64)