paul@153 | 1 | Instruction Evaluation Model
|
paul@153 | 2 | ============================
|
paul@153 | 3 |
|
paul@153 | 4 | Programs use a value stack containing local and temporary storage. A value
|
paul@153 | 5 | stack pointer indicates the top of this stack. In addition, a separate stack
|
paul@153 | 6 | is used to record the invocation frames. All stack pointers refer to the next
|
paul@153 | 7 | address to be used by the stack, not the address of the uppermost element.
|
paul@153 | 8 |
|
paul@153 | 9 | Frame Stack Value Stack
|
paul@153 | 10 | ----------- ----------- Address of Callable
|
paul@153 | 11 | -------------------
|
paul@153 | 12 | previous ...
|
paul@153 | 13 | current ------> callable -----> identifier
|
paul@153 | 14 | arg1 reference to code
|
paul@153 | 15 | arg2
|
paul@153 | 16 | arg3
|
paul@153 | 17 | local4
|
paul@153 | 18 | local5
|
paul@153 | 19 | ...
|
paul@153 | 20 |
|
paul@153 | 21 | Unlike the CPython virtual machine, programs do not use a value stack
|
paul@153 | 22 | containing the results of expression evaluations. Instead, temporary storage
|
paul@153 | 23 | is statically allocated and employed by instructions.
|
paul@153 | 24 |
|
paul@153 | 25 | Loading local names and temporary values is a matter of performing
|
paul@153 | 26 | frame-relative accesses to the value stack.
|
paul@153 | 27 |
|
paul@153 | 28 | Invocations and Argument Evaluation
|
paul@153 | 29 | -----------------------------------
|
paul@153 | 30 |
|
paul@153 | 31 | When preparing for an invocation, the caller first sets the invocation frame
|
paul@153 | 32 | pointer. A number of locations for the arguments required by the invocation
|
paul@153 | 33 | are then reserved. With a frame to prepare, positional arguments are added to
|
paul@153 | 34 | the frame in order such that the first argument positions are filled. The
|
paul@153 | 35 | names of keyword arguments are used (in the form of table index values) to
|
paul@153 | 36 | consult the parameter table and to find the frame location in which such
|
paul@153 | 37 | arguments are to be stored.
|
paul@153 | 38 |
|
paul@153 | 39 | fn(a, b, d=1, e=2, c=3) -> fn(a, b, c, d, e)
|
paul@153 | 40 |
|
paul@153 | 41 | Frame
|
paul@153 | 42 | -----
|
paul@153 | 43 |
|
paul@153 | 44 | a a a a
|
paul@153 | 45 | b b b b
|
paul@153 | 46 | ___ ___ ___ --> 3
|
paul@153 | 47 | ___ --> 1 1 | 1
|
paul@153 | 48 | ___ | ___ --> 2 | 2
|
paul@201 | 49 | | | |
|
paul@153 | 50 | 1 ----------- 2 ----------- 3 -----------
|
paul@153 | 51 |
|
paul@153 | 52 | Conceptually, the frame can be considered as a collection of attributes, as
|
paul@153 | 53 | seen in other kinds of structures:
|
paul@153 | 54 |
|
paul@153 | 55 | Frame for invocation of fn:
|
paul@153 | 56 |
|
paul@153 | 57 | 0 1 2 3 4 5
|
paul@153 | 58 | code a b c d e
|
paul@153 | 59 | reference
|
paul@153 | 60 |
|
paul@153 | 61 | However, where arguments are specified positionally, such "attributes" are not
|
paul@153 | 62 | set using a comparable approach to that employed with other structures.
|
paul@153 | 63 | Keyword arguments are set using an attribute-like mechanism, though, where the
|
paul@153 | 64 | position of each argument discovered using the parameter table.
|
paul@153 | 65 |
|
paul@153 | 66 | Method Invocations
|
paul@153 | 67 | ------------------
|
paul@153 | 68 |
|
paul@153 | 69 | Method invocations incorporate an implicit first argument which is obtained
|
paul@153 | 70 | from the context of the method:
|
paul@153 | 71 |
|
paul@153 | 72 | method(a, b, d=1, e=2, c=3) -> method(self, a, b, c, d, e)
|
paul@153 | 73 |
|
paul@153 | 74 | Frame
|
paul@153 | 75 | -----
|
paul@153 | 76 |
|
paul@153 | 77 | context of method
|
paul@153 | 78 | a
|
paul@153 | 79 | b
|
paul@153 | 80 | 3
|
paul@153 | 81 | 1
|
paul@153 | 82 | 2
|
paul@153 | 83 |
|
paul@153 | 84 | Although it could be possible to permit any object to be provided as the first
|
paul@153 | 85 | argument, in order to optimise instance attribute access in methods, we should
|
paul@153 | 86 | seek to restrict the object type.
|
paul@153 | 87 |
|
paul@153 | 88 | Verifying Supplied Arguments
|
paul@153 | 89 | ----------------------------
|
paul@153 | 90 |
|
paul@153 | 91 | In order to ensure a correct invocation, it is also necessary to check the
|
paul@153 | 92 | number of supplied arguments. If the target of the invocation is known at
|
paul@153 | 93 | compile-time, no additional instructions need to be emitted; otherwise, the
|
paul@153 | 94 | generated code must test for the following situations:
|
paul@153 | 95 |
|
paul@153 | 96 | 1. That the number of supplied arguments is equal to the number of expected
|
paul@153 | 97 | parameters.
|
paul@153 | 98 |
|
paul@153 | 99 | 2. That no keyword argument overwrites an existing positional parameter.
|
paul@153 | 100 |
|
paul@153 | 101 | Default Arguments
|
paul@153 | 102 | -----------------
|
paul@153 | 103 |
|
paul@153 | 104 | Some arguments may have default values which are used if no value is provided
|
paul@153 | 105 | in an invocation. Such defaults are initialised when the function itself is
|
paul@153 | 106 | initialised, and are used to fill in any invocation frames that are known at
|
paul@153 | 107 | compile-time.
|
paul@153 | 108 |
|
paul@153 | 109 | Tuples, Frames and Allocation
|
paul@153 | 110 | -----------------------------
|
paul@153 | 111 |
|
paul@153 | 112 | Using the approach where arguments are treated like attributes in some kind of
|
paul@153 | 113 | structure, we could choose to allocate frames in places other than a stack.
|
paul@153 | 114 | This would produce something somewhat similar to a plain tuple object.
|