micropython

Annotated README.txt

139:29103989ca36
2008-09-05 Paul Boddie Added the final Return instruction to generated images. Fixed the For node processing to store assigned values before visiting assignment nodes. Changed source processing to not use temporary storage optimisations, since the conditions for such optimisations are not met in general when processing assignments. Adopted a list to hold sources (assignment expression values), since list and tuple assignment act on a hierarchy of such values. Added elementary support and tests for list assignment.
paul@15 1
Namespace Definition
paul@15 2
====================
paul@15 3
paul@15 4
Module attributes are defined either at the module level or by global
paul@15 5
statements.
paul@15 6
paul@15 7
Class attributes are defined only within class statements.
paul@15 8
paul@15 9
Instance attributes are defined only by assignments to attributes of self
paul@15 10
within __init__ methods.
paul@15 11
paul@123 12
(These restrictions apply because such attributes are thus explicitly
paul@123 13
declared. Module and class attributes can also be finalised in this way in
paul@123 14
order to permit certain optimisations.)
paul@123 15
paul@42 16
Potential Restrictions
paul@42 17
----------------------
paul@42 18
paul@42 19
Names of classes and functions could be restricted to only refer to those
paul@42 20
objects within the same namespace. If redefinition were to occur, or if
paul@42 21
multiple possibilities were present, these restrictions could be moderated as
paul@42 22
follows:
paul@42 23
paul@42 24
  * Classes assigned to the same name could provide the union of their
paul@42 25
    attributes. This would, however, cause a potential collision of attribute
paul@42 26
    definitions such as methods.
paul@42 27
paul@42 28
  * Functions, if they share compatible signatures, could share parameter list
paul@42 29
    definitions.
paul@42 30
paul@21 31
Instruction Evaluation Model
paul@21 32
============================
paul@21 33
paul@21 34
Programs use a value stack where evaluated instructions may save their
paul@21 35
results. A value stack pointer indicates the top of this stack. In addition, a
paul@21 36
separate stack is used to record the invocation frames. All stack pointers
paul@21 37
refer to the next address to be used by the stack, not the address of the
paul@21 38
uppermost element.
paul@21 39
paul@21 40
    Frame Stack     Value Stack
paul@21 41
    -----------     -----------     Address of Callable
paul@21 42
                                    -------------------
paul@21 43
    previous        ...
paul@21 44
    current ------> callable -----> identifier
paul@21 45
                    arg1            reference to code
paul@21 46
                    arg2
paul@21 47
                    arg3
paul@21 48
                    local4
paul@21 49
                    local5
paul@21 50
                    ...
paul@21 51
paul@21 52
Loading local names is a matter of performing frame-relative accesses to the
paul@21 53
value stack.
paul@21 54
paul@21 55
Invocations and Argument Evaluation
paul@21 56
-----------------------------------
paul@21 57
paul@21 58
When preparing for an invocation, the caller first sets the invocation frame
paul@21 59
pointer. Then, positional arguments are added to the stack such that the first
paul@21 60
argument positions are filled. A number of stack locations for the remaining
paul@21 61
arguments specified in the program are then reserved. The names of keyword
paul@21 62
arguments are used (in the form of table index values) to consult the
paul@21 63
parameter table and to find the location in which such arguments are to be
paul@21 64
stored.
paul@21 65
paul@21 66
    fn(a, b, d=1, e=2, c=3) -> fn(a, b, c, d, e)
paul@21 67
paul@21 68
    Value Stack
paul@21 69
    -----------
paul@21 70
paul@21 71
    ...             ...             ...             ...
paul@21 72
    fn              fn              fn              fn
paul@21 73
    a               a               a               a
paul@21 74
    b               b               b               b
paul@21 75
    ___             ___             ___         --> 3
paul@21 76
    ___         --> 1               1           |   1
paul@21 77
    ___         |   ___         --> 2           |   2
paul@21 78
    1 -----------   2 -----------   3 -----------
paul@21 79
paul@21 80
Conceptually, the frame can be considered as a collection of attributes, as
paul@21 81
seen in other kinds of structures:
paul@21 82
paul@21 83
Frame for invocation of fn:
paul@21 84
paul@21 85
    0           1           2           3           4           5
paul@21 86
    code        a           b           c           d           e
paul@21 87
    reference
paul@21 88
paul@21 89
However, where arguments are specified positionally, such "attributes" are not
paul@21 90
set using a comparable approach to that employed with other structures.
paul@21 91
Keyword arguments are set using an attribute-like mechanism, though, where the
paul@21 92
position of each argument discovered using the parameter table.
paul@21 93
paul@67 94
Where the target of the invocation is known, the above procedure can be
paul@67 95
optimised slightly by attempting to add keyword argument values directly to
paul@67 96
the stack:
paul@67 97
paul@67 98
    Value Stack
paul@67 99
    -----------
paul@67 100
paul@67 101
    ...             ...             ...             ...             ...
paul@67 102
    fn              fn              fn              fn              fn
paul@67 103
    a               a               a               a               a
paul@67 104
    b               b               b               b               b
paul@67 105
                    ___             ___             ___         --> 3
paul@67 106
                    1               1               1           |   1
paul@67 107
                                    2               1           |   2
paul@67 108
                                                    3 -----------
paul@67 109
paul@67 110
    (reserve for    (add in-place)  (add in-place)  (evaluate)      (store by
paul@67 111
    parameter c)                                                    index)
paul@67 112
paul@67 113
Method Invocations
paul@67 114
------------------
paul@67 115
paul@45 116
Method invocations incorporate an implicit first argument which is obtained
paul@45 117
from the context of the method:
paul@45 118
paul@45 119
    method(a, b, d=1, e=2, c=3) -> method(self, a, b, c, d, e)
paul@45 120
paul@45 121
    Value Stack
paul@45 122
    -----------
paul@45 123
paul@45 124
    ...
paul@45 125
    method
paul@45 126
    context of method
paul@45 127
    a
paul@45 128
    b
paul@45 129
    3
paul@45 130
    1
paul@45 131
    2
paul@45 132
paul@45 133
Although it could be possible to permit any object to be provided as the first
paul@45 134
argument, in order to optimise instance attribute access in methods, we should
paul@45 135
seek to restrict the object type.
paul@45 136
paul@58 137
Verifying Supplied Arguments
paul@58 138
----------------------------
paul@58 139
paul@58 140
In order to ensure a correct invocation, it is also necessary to check the
paul@58 141
number of supplied arguments. If the target of the invocation is known at
paul@58 142
compile-time, no additional instructions need to be emitted; otherwise, the
paul@58 143
generated code must test for the following situations:
paul@58 144
paul@58 145
 1. That the number of supplied arguments is equal to the number of expected
paul@58 146
    parameters.
paul@58 147
paul@58 148
 2. That no keyword argument overwrites an existing positional parameter.
paul@58 149
paul@66 150
Default Arguments
paul@66 151
-----------------
paul@66 152
paul@66 153
Some arguments may have default values which are used if no value is provided
paul@66 154
in an invocation. Such defaults are initialised when the function itself is
paul@66 155
initialised, and are used to fill in any invocation frames that are known at
paul@66 156
compile-time.
paul@66 157
paul@21 158
Tuples, Frames and Allocation
paul@21 159
-----------------------------
paul@21 160
paul@21 161
Using the approach where arguments are treated like attributes in some kind of
paul@21 162
structure, we could choose to allocate frames in places other than a stack.
paul@21 163
This would produce something somewhat similar to a plain tuple object.
paul@23 164
paul@23 165
Optimisations
paul@23 166
=============
paul@23 167
paul@29 168
Some optimisations around constant objects might be possible; these depend on
paul@29 169
the following:
paul@29 170
paul@29 171
  * Reliable tracking of assignments: where assignment operations occur, the
paul@29 172
    target of the assignment should be determined if any hope of optimisation
paul@29 173
    is to be maintained. Where no guarantees can be made about the target of
paul@29 174
    an assignment, no assignment-related information should be written to
paul@29 175
    potential targets.
paul@29 176
paul@29 177
  * Objects acting as "containers" of attributes must be regarded as "safe":
paul@29 178
    where assignments are recorded as occurring on an attribute, it must be
paul@29 179
    guaranteed that no other unforeseen ways exist to assign to such
paul@29 180
    attributes.
paul@29 181
paul@29 182
The discussion below presents certain rules which must be imposed to uphold
paul@29 183
the above requirements.
paul@29 184
paul@30 185
Safe Containers
paul@30 186
---------------
paul@28 187
paul@23 188
Where attributes of modules, classes and instances are only set once and are
paul@23 189
effectively constant, it should be possible to circumvent the attribute lookup
paul@28 190
mechanism and to directly reference the attribute value. This technique may
paul@30 191
only be considered applicable for the following "container" objects, subject
paul@30 192
to the noted constraints:
paul@28 193
paul@30 194
 1. For modules, "safety" is enforced by ensuring that assignments to module
paul@30 195
    attributes are only permitted within the module itself either at the
paul@30 196
    top-level or via names declared as globals. Thus, the following would not
paul@30 197
    be permitted:
paul@28 198
paul@28 199
    another_module.this_module.attr = value
paul@28 200
paul@29 201
    In the above, this_module is a reference to the current module.
paul@28 202
paul@30 203
 2. For classes, "safety" is enforced by ensuring that assignments to class
paul@30 204
    attributes are only permitted within the class definition, outside
paul@30 205
    methods. This would mean that classes would be "sealed" at definition time
paul@30 206
    (like functions).
paul@28 207
paul@28 208
Unlike the property of function locals that they may only sensibly be accessed
paul@28 209
within the function in which they reside, these cases demand additional
paul@28 210
controls or assumptions on or about access to the stored data. Meanwhile, it
paul@28 211
would be difficult to detect eligible attributes on arbitrary instances due to
paul@28 212
the need for some kind of type inference or abstract execution.
paul@28 213
paul@30 214
Constant Attributes
paul@30 215
-------------------
paul@30 216
paul@30 217
When accessed via "safe containers", as described above, any attribute with
paul@30 218
only one recorded assignment on it can be considered a constant attribute and
paul@30 219
this eligible for optimisation, the consequence of which would be the
paul@30 220
replacement of a LoadAttrIndex instruction (which needs to look up an
paul@30 221
attribute using the run-time details of the "container" and the compile-time
paul@30 222
details of the attribute) with a LoadAttr instruction.
paul@30 223
paul@30 224
However, some restrictions exist on assignment operations which may be
paul@30 225
regarded to cause only one assignment in the lifetime of a program:
paul@30 226
paul@30 227
 1. For module attributes, only assignments at the top-level outside loop
paul@30 228
    statements can be reliably assumed to cause only a single assignment.
paul@30 229
paul@30 230
 2. For class attributes, only assignments at the top-level within class
paul@30 231
    definitions and outside loop statements can be reliably assumed to cause
paul@30 232
    only a single assignment.
paul@30 233
paul@30 234
All assignments satisfying the "safe container" requirements, but not the
paul@30 235
requirements immediately above, should each be recorded as causing at least
paul@30 236
one assignment.
paul@28 237
paul@29 238
Additional Controls
paul@29 239
-------------------
paul@29 240
paul@29 241
For the above cases for "container" objects, the following controls would need
paul@29 242
to apply:
paul@29 243
paul@29 244
 1. Modules would need to be immutable after initialisation. However, during
paul@29 245
    initialisation, there remains a possibility of another module attempting
paul@29 246
    to access the original module. For example, if ppp/__init__.py contained
paul@29 247
    the following...
paul@29 248
paul@29 249
    x = 1
paul@29 250
    import ppp.qqq
paul@29 251
    print x
paul@29 252
paul@29 253
    ...and if ppp/qqq.py contained the following...
paul@29 254
paul@29 255
    import ppp
paul@29 256
    ppp.x = 2
paul@29 257
paul@29 258
    ...then the value 2 would be printed. Since modules are objects which are
paul@29 259
    registered globally in a program, it would be possible to set attributes
paul@29 260
    in the above way.
paul@29 261
paul@29 262
 2. Classes would need to be immutable after initialisation. However, since
paul@29 263
    classes are objects, any reference to a class after initialisation could
paul@29 264
    be used to set attributes on the class.
paul@29 265
paul@29 266
Solutions:
paul@29 267
paul@29 268
 1. Insist on global scope for module attribute assignments.
paul@29 269
paul@29 270
 2. Insist on local scope within classes.
paul@29 271
paul@29 272
Both of the above measures need to be enforced at run-time, since an arbitrary
paul@29 273
attribute assignment could be attempted on any kind of object, yet to uphold
paul@29 274
the properties of "safe containers", attempts to change attributes of such
paul@29 275
objects should be denied. Since foreseen attribute assignment operations have
paul@29 276
certain properties detectable at compile-time, it could be appropriate to
paul@29 277
generate special instructions (or modified instructions) during the
paul@29 278
initialisation of modules and classes for such foreseen assignments, whilst
paul@29 279
employing normal attribute assignment operations in all other cases. Indeed,
paul@29 280
the StoreAttr instruction, which is used to set attributes in "safe
paul@29 281
containers" would be used exclusively for this purpose; the StoreAttrIndex
paul@29 282
instruction would be used exclusively for all other attribute assignments.
paul@29 283
paul@43 284
To ensure the "sealing" of modules and classes, entries in the attribute
paul@43 285
lookup table would encode whether a class or module is being accessed, so
paul@43 286
that the StoreAttrIndex instruction could reject such accesses.
paul@43 287
paul@28 288
Constant Attribute Values
paul@28 289
-------------------------
paul@28 290
paul@29 291
Where an attribute value is itself regarded as constant, is a "safe container"
paul@29 292
and is used in an operation accessing its own attributes, the value can be
paul@29 293
directly inspected for optimisations or employed in the generated code. For
paul@29 294
the attribute values themselves, only objects of a constant nature may be
paul@28 295
considered suitable for this particular optimisation:
paul@28 296
paul@28 297
  * Classes
paul@28 298
  * Modules
paul@28 299
  * Instances defined as constant literals
paul@28 300
paul@28 301
This is because arbitrary objects (such as most instances) have no
paul@28 302
well-defined form before run-time and cannot be investigated further at
paul@28 303
compile-time or have a representation inserted into the generated code.
paul@29 304
paul@29 305
Class Attributes and Access via Instances
paul@29 306
-----------------------------------------
paul@29 307
paul@29 308
Unlike module attributes, class attributes can be accessed in a number of
paul@29 309
different ways:
paul@29 310
paul@29 311
  * Using the class itself:
paul@29 312
paul@29 313
    C.x = 123
paul@29 314
    cls = C; cls.x = 234
paul@29 315
paul@29 316
  * Using a subclass of the class (for reading attributes):
paul@29 317
paul@29 318
    class D(C):
paul@29 319
        pass
paul@29 320
    D.x # setting D.x would populate D, not C
paul@29 321
paul@29 322
  * Using instances of the class or a subclass of the class (for reading
paul@29 323
    attributes):
paul@29 324
paul@29 325
    c = C()
paul@29 326
    c.x # setting c.x would populate c, not C
paul@29 327
paul@29 328
Since assignments are only achieved using direct references to the class, and
paul@29 329
since class attributes should be defined only within the class initialisation
paul@29 330
process, the properties of class attributes should be consistent with those
paul@29 331
desired.
paul@29 332
paul@29 333
Method Access via Instances
paul@29 334
---------------------------
paul@29 335
paul@29 336
It is desirable to optimise method access, even though most method calls are
paul@29 337
likely to occur via instances. It is possible, given the properties of methods
paul@29 338
as class attributes to investigate the kind of instance that the self
paul@29 339
parameter/local refers to within each method: it should be an instance either
paul@29 340
of the class in which the method is defined or a compatible class, although
paul@29 341
situations exist where this might not be the case:
paul@29 342
paul@29 343
  * Explicit invocation of a method:
paul@29 344
paul@29 345
    d = D() # D is not related to C
paul@29 346
    C.f(d) # calling f(self) in C
paul@29 347
paul@30 348
If blatant usage of incompatible instances were somehow disallowed, it would
paul@30 349
still be necessary to investigate the properties of an instance's class and
paul@30 350
its relationship with other classes. Consider the following example:
paul@30 351
paul@30 352
    class A:
paul@30 353
        def f(self): ...
paul@30 354
paul@30 355
    class B:
paul@30 356
        def f(self): ...
paul@30 357
        def g(self):
paul@30 358
            self.f()
paul@30 359
paul@30 360
    class C(A, B):
paul@30 361
        pass
paul@30 362
paul@30 363
Here, instances of B passed into the method B.g could be assumed to provide
paul@30 364
access to B.f when self.f is resolved at compile-time. However, instances of C
paul@30 365
passed into B.g would instead provide access to A.f when self.f is resolved at
paul@30 366
compile-time (since the method resolution order is C, A, B instead of just B).
paul@30 367
paul@30 368
One solution might be to specialise methods for each instance type, but this
paul@30 369
could be costly. Another less ambitious solution might only involve the
paul@30 370
optimisation of such internal method calls if an unambiguous target can be
paul@30 371
resolved.
paul@30 372
paul@29 373
Optimising Function Invocations
paul@29 374
-------------------------------
paul@29 375
paul@29 376
Where an attribute value is itself regarded as constant and is a function,
paul@29 377
knowledge about the parameters of the function can be employed to optimise the
paul@29 378
preparation of the invocation frame.