diff --git a/docs/cton_domain.py b/docs/cton_domain.py index 9f153f3a50..e1dc08a67b 100644 --- a/docs/cton_domain.py +++ b/docs/cton_domain.py @@ -19,7 +19,7 @@ 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, TypedField +from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.nodes import make_refnode class CtonObject(ObjectDescription): @@ -120,6 +120,7 @@ class CtonInst(CtonObject): TypedField('result', label=l_('Results'), names=('out', 'result'), typerolename='type', typenames=('type',)), + GroupedField('flag', names=('flag',), label=l_('Flags')), Field('resulttype', label=l_('Result type'), has_arg=False, names=('rtype',)), ] diff --git a/docs/cton_lexer.py b/docs/cton_lexer.py index 4c309d779b..33f769dfc6 100644 --- a/docs/cton_lexer.py +++ b/docs/cton_lexer.py @@ -28,7 +28,7 @@ class CretonneLexer(RegexLexer): # Reserved words. (keywords('function', 'entry'), Keyword), # Known attributes. - (keywords('align', 'uext', 'sext', 'inreg'), Name.Attribute), + (keywords('align', 'aligntrap', 'uext', 'sext', 'inreg'), Name.Attribute), # Well known value types. (r'\b(bool|i\d+|f32|f64)(x\d+)?\b', Keyword.Type), # v = value @@ -38,7 +38,7 @@ class CretonneLexer(RegexLexer): (r'(ebb)\d+', Name.Label), # Match instruction names in context. (r'(=)( *)([a-z]\w*)', bygroups(Operator, Whitespace, Name.Function)), - (r'^( +)([a-z]\w*\b)(?! *[,=])', bygroups(Whitespace, Name.Function)), + (r'^( *)([a-z]\w*\b)(?! *[,=])', bygroups(Whitespace, Name.Function)), # Other names: results and arguments (r'[a-z]\w*', Name), (r'->|=|:', Operator), diff --git a/docs/langref.rst b/docs/langref.rst index b3f8b14ea1..6d64b42f03 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -390,6 +390,130 @@ Indirect function calls use a signature declared in the preample. :inst:`br_table`. +Memory +====== + +Cretonne provides fully general :inst:`load` and :inst:`store` instructions for +accessing memory. However, it can be very complicated to verify the safety of +general loads and stores when compiling code for a sandboxed environment, so +Cretonne also provides more restricted memory operations that are always safe. + +.. inst:: a = load p, Offset, Flags... + + Load from memory at ``p + Offset``. + + This is a polymorphic instruction that can load any value type which has a + memory representation (i.e., everything except :type:`bool` and boolean + vectors). + + :arg iPtr p: Base address. + :arg Offset: Immediate signed offset. + :flag align(N): Expected alignment of ``p + Offset``. Power of two. + :flag aligntrap: Always trap if the memory access is misaligned. + :result T a: Loaded value. + +.. inst:: store x, p, Offset, Flags... + + Store ``x`` to memory at ``p + Offset``. + + This is a polymorphic instruction that can store any value type with a + memory representation. + + :arg T x: Value to store. + :arg iPtr p: Base address. + :arg Offset: Immediate signed offset. + :flag align(N): Expected alignment of ``p + Offset``. Power of two. + :flag aligntrap: Always trap if the memory access is misaligned. + +Loads and stores are *misaligned* if the resultant address is not a multiple of +the expected alignment. Depending on the target architecture, misaligned memory +accesses may trap, or they may work. Sometimes, operating systems catch +alignment traps and emulate the misaligned memory access. + +On target architectures like x86 that don't check alignment, Cretonne expands +the aligntrap flag into a conditional trap instruction:: + + v5 = load.i32 v1, 4, align(4), aligntrap + ; Becomes: + v10 = and_imm v1, 3 + trapnz v10 + v5 = load.i32 v1, 4 + + +Local variables +--------------- + +One set of restricted memory operations access the current function's stack +frame. The stack frame is divided into fixed-size stack slots that are +allocated in the :term:`function preample`. Stack slots are not typed, they +simply represent a contiguous sequence of bytes in the stack frame. + +.. inst:: SS = stack_slot Bytes, Flags... + + Allocate a stack slot in the preample. + + If no alignment is specified, Cretonne will pick an appropriate alignment + for the stack slot based on its size and access patterns. + + :arg Bytes: Stack slot size on bytes. + :flag align(N): Request at least N bytes alignment. + :result SS: Stack slot index. + +.. inst:: a = stack_load SS, Offset + + Load a value from a stack slot at the constant offset. + + This is a polymorphic instruction that can load any value type which has a + memory representation. + + The offset is an immediate constant, not an SSA value. The memory access + cannot go out of bounds, i.e. ``sizeof(a) + Offset <= sizeof(SS)``. + + :arg SS: Stack slot declared with :inst:`stack_slot`. + :arg Offset: Immediate non-negative offset. + :result T a: Value loaded. + +.. inst:: stack_store x, SS, Offset + + Store a value to a stack slot at a constant offset. + + This is a polymorphic instruction that can store any value type with a + memory representation. + + The offset is an immediate constant, not an SSA value. The memory access + cannot go out of bounds, i.e. ``sizeof(a) + Offset <= sizeof(SS)``. + + :arg T x: Value to be stored. + :arg SS: Stack slot declared with :inst:`stack_slot`. + :arg Offset: Immediate non-negative offset. + +The dedicated stack access instructions are easy ofr 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 +and stack slot alignments. + +It can be necessary to escape from the safety of the restricted instructions by +taking the address of a stack slot. + +.. inst:: a = stack_addr SS, Offset + + Get the address of a stack slot. + + Compute the absolute address of a byte in a stack slot. The offset must + refer to a byte inside the stack slot: ``0 <= Offset < sizeof(SS)``. + + :arg SS: Stack slot declared with :inst:`stack_slot`. + :arg Offset: Immediate non-negative offset. + :result iPtr a: Address. + +The :inst:`stack_addr` instruction can be used to macro-expand the stack access +instructions before instruction selection:: + + v1 = stack_load.f64 ss3, 16 + ; Expands to: + v9 = stack_addr ss3, 16 + v1 = load.f64 v9 + Operations ==========