1.1 --- a/docs/invocation.txt Sat Jun 28 20:46:45 2008 +0200
1.2 +++ b/docs/invocation.txt Mon Jun 30 00:32:54 2008 +0200
1.3 @@ -53,7 +53,7 @@
1.4 # f(obj, 1, 2)
1.5 # f known as C.m at compile-time:
1.6 # m(obj "assert isinstance(obj, C)", 1, 2)
1.7 - # f not known at compiler-time:
1.8 + # f not known at compile-time:
1.9 # f(<context>, obj, 1, 2) for instance-accessed methods
1.10 # f(obj, 1, 2) for class-accessed methods
1.11 # f(obj, 1, 2) for functions
1.12 @@ -61,6 +61,51 @@
1.13 (Could either have universal context usage even for functions, which would
1.14 ignore them, or attempt to remove contexts when functions are called.)
1.15
1.16 +Argument lists for functions:
1.17 +
1.18 + f(obj, 1, 2) # f known as function at compile-time
1.19 +
1.20 + f -> don't get any context information
1.21 + obj -> argument #1
1.22 + 1 -> argument #2
1.23 + 2 -> argument #3
1.24 +
1.25 +Argument lists for methods:
1.26 +
1.27 + f(obj, 1, 2) # f known as C.m at compile-time (context is C)
1.28 +
1.29 + f -> C.m - don't get any context information
1.30 + obj -> argument #1
1.31 + 1 -> argument #2
1.32 + 2 -> argument #3
1.33 +
1.34 +Argument lists for methods:
1.35 +
1.36 + f(obj, 1, 2) # f known as C.m at compile-time (context is an instance)
1.37 +
1.38 + f -> C.m
1.39 + -> context is argument #1
1.40 + obj -> argument #2
1.41 + 1 -> argument #3
1.42 + 2 -> argument #4
1.43 +
1.44 +Argument lists for unknown callables:
1.45 +
1.46 + f(obj, 1, 2) # f not known at compile-time
1.47 +
1.48 + f -> f
1.49 + -> load context for argument #1
1.50 + obj -> argument #2
1.51 + 1 -> argument #3
1.52 + 2 -> argument #4
1.53 +
1.54 + Then, check the context and shift the frame if necessary:
1.55 +
1.56 + <context> is module or class:
1.57 + (<context>, obj, 1, 2) -> (obj, 1, 2)
1.58 +
1.59 + <context> is instance: no change
1.60 +
1.61 Functions as methods:
1.62
1.63 def f(x, y, z): ...
2.1 --- a/micropython/ast.py Sat Jun 28 20:46:45 2008 +0200
2.2 +++ b/micropython/ast.py Mon Jun 30 00:32:54 2008 +0200
2.3 @@ -33,6 +33,9 @@
2.4
2.5 supported_optimisations = ["constant_storage", "known_target", "self_access", "temp_storage", "constant_test", "stack_access"]
2.6
2.7 + attribute_load_instructions = (LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex)
2.8 + attribute_store_instructions = (StoreAddress, StoreAddressContext, StoreAttr, StoreAttrIndex)
2.9 +
2.10 def __init__(self, module, importer, optimisations=None):
2.11
2.12 """
2.13 @@ -72,6 +75,7 @@
2.14 # and the presumed state of the value stack.
2.15
2.16 self.code = None
2.17 + self.last_invocation = None
2.18 self.temp_position = 0
2.19 self.stack = []
2.20 self.suspended_frame = []
2.21 @@ -86,6 +90,7 @@
2.22
2.23 self.unit = self.module
2.24 self.code = []
2.25 + self.last_invocation = None
2.26 self.temp_position = self.unit.stack_local_usage
2.27 self.stack = []
2.28
2.29 @@ -100,6 +105,7 @@
2.30
2.31 self.unit = unit
2.32 self.code = []
2.33 + self.last_invocation = None
2.34 self.temp_position = self.unit.stack_local_usage
2.35 self.stack = []
2.36
2.37 @@ -226,10 +232,18 @@
2.38
2.39 "Add 'op' to the generated code."
2.40
2.41 - position = len(self.code)
2.42 + # Optimise stack access by incorporating the source data directly into
2.43 + # instructions.
2.44 +
2.45 self._optimise_stack_access(op)
2.46 +
2.47 + # Optimise away constant storage if appropriate.
2.48 + # The target and value loading operations are also removed.
2.49 +
2.50 + if self._optimise_constant_storage(op):
2.51 + return
2.52 +
2.53 self.adjust_stack(op)
2.54 -
2.55 self.code.append(op)
2.56
2.57 def new_ops(self, ops):
2.58 @@ -288,6 +302,12 @@
2.59 except IndexError:
2.60 return None
2.61
2.62 + def set_last_invocation(self):
2.63 +
2.64 + "Set this location as the point after the last invocation."
2.65 +
2.66 + self.last_invocation = len(self.code)
2.67 +
2.68 # Internal helper methods.
2.69
2.70 def _visitAttr(self, node, classes):
2.71 @@ -306,7 +326,7 @@
2.72 Generate code for the access to 'attrname' using the given 'classes'.
2.73 """
2.74
2.75 - AddressInstruction, AttrInstruction, AttrIndexInstruction = classes
2.76 + AddressInstruction, AddressContextInstruction, AttrInstruction, AttrIndexInstruction = classes
2.77
2.78 last = self.last_op()
2.79
2.80 @@ -315,12 +335,6 @@
2.81
2.82 if self._have_constant_input(0):
2.83
2.84 - # Optimise away the constant storage if appropriate.
2.85 - # The target and value loading operations are removed.
2.86 -
2.87 - if self._optimise_constant_storage(AddressInstruction, 1):
2.88 - return
2.89 -
2.90 # Get the details of the access.
2.91
2.92 if isinstance(last.attr, Const):
2.93 @@ -362,7 +376,7 @@
2.94 # see if the attribute is acceptably positioned and produce a direct
2.95 # access to the attribute.
2.96
2.97 - elif self._optimise_self_access(attrname, (AddressInstruction, AttrInstruction)):
2.98 + elif self._optimise_self_access(attrname, (AddressInstruction, AddressContextInstruction, AttrInstruction)):
2.99 return
2.100
2.101 # Otherwise, perform a normal operation.
2.102 @@ -419,21 +433,13 @@
2.103
2.104 temp = self._optimise_temp_storage()
2.105
2.106 - # Where a target or context are not known, load the target and context.
2.107 + # Where a target or context are not known or where an instance is known
2.108 + # to be the context, load the context.
2.109
2.110 - if context is None:
2.111 + if context is None or isinstance(context, Instance):
2.112 self.new_ops(temp)
2.113 self.new_op(LoadContext())
2.114 -
2.115 - # Check to see if the context is needed for the target.
2.116 -
2.117 - self.new_op(CheckContext())
2.118 -
2.119 - # Where an instance is known to be the context, load the context.
2.120 -
2.121 - elif isinstance(context, Instance):
2.122 - self.new_ops(temp)
2.123 - self.new_op(LoadContext())
2.124 + self.new_op(StoreFrame(0))
2.125
2.126 # Otherwise omit the context.
2.127
2.128 @@ -455,12 +461,17 @@
2.129 employed_positions = set()
2.130 extra_keywords = []
2.131
2.132 - # NOTE: Fix context for self-accessed methods.
2.133 + # Note the presence of the context in the frame where appropriate.
2.134
2.135 - if context is not None and isinstance(context, Instance):
2.136 + if context is None or isinstance(context, Instance):
2.137 ncontext = 1
2.138 + expect_context = 0
2.139 + elif isinstance(context, Class):
2.140 + ncontext = 0
2.141 + expect_context = 1
2.142 else:
2.143 ncontext = 0
2.144 + expect_context = 0
2.145
2.146 first = 1
2.147 frame_pos = ncontext
2.148 @@ -503,22 +514,11 @@
2.149
2.150 employed_positions.add(pos)
2.151
2.152 - # Add space for arguments appearing before this one.
2.153 -
2.154 - if frame_pos < pos:
2.155 - self.new_op(ReserveFrame(pos - frame_pos))
2.156 - frame_pos = pos
2.157 -
2.158 # Generate code for the keyword and the positioning
2.159 # operation.
2.160
2.161 self.dispatch(arg.expr)
2.162 -
2.163 - # If the position corresponds to the current frame element,
2.164 - # skip generating the store instruction.
2.165 -
2.166 - if frame_pos > pos:
2.167 - self.new_op(StoreFrame(pos))
2.168 + self.new_op(StoreFrame(pos))
2.169
2.170 # Otherwise, generate the code needed to obtain the details of
2.171 # the parameter location.
2.172 @@ -550,12 +550,13 @@
2.173
2.174 else:
2.175 self.dispatch(arg)
2.176 - employed_positions.add(frame_pos + ncontext)
2.177 + self.new_op(StoreFrame(frame_pos))
2.178 + employed_positions.add(frame_pos)
2.179
2.180 # Check to see if the first argument is appropriate (compatible with
2.181 - # the # target where methods are being invoked via classes).
2.182 + # the target where methods are being invoked via classes).
2.183
2.184 - if first and context is None:
2.185 + if first and expect_context:
2.186 continue_label = self.new_label()
2.187 self.new_op(CheckSelf())
2.188 self.new_op(JumpIfTrue(continue_label))
2.189 @@ -612,21 +613,15 @@
2.190
2.191 for pos in range(nargs_min, nargs_max):
2.192 if pos not in employed_positions:
2.193 - #self.new_op(LoadConst(target))
2.194 - #self.new_op(LoadAttr(target.default_attrs[pos - nargs_min]))
2.195 self.new_op(LoadAddress(target.default_attrs[pos - nargs_min]))
2.196 -
2.197 - # If the position corresponds to the current frame element,
2.198 - # skip generating the instruction.
2.199 -
2.200 - if frame_pos != pos:
2.201 - self.new_op(StoreFrame(pos))
2.202 + self.new_op(StoreFrame(pos))
2.203
2.204 frame_pos += 1
2.205
2.206 # Or generate instructions to do this at run-time.
2.207 # NOTE: CheckFrame has to check the number of arguments and to fill in
2.208 - # NOTE: defaults.
2.209 + # NOTE: defaults; it also has to shift the invocation frame according to
2.210 + # NOTE: the context in use.
2.211
2.212 else:
2.213 self.new_op(CheckFrame())
2.214 @@ -644,6 +639,10 @@
2.215
2.216 # NOTE: Exception handling required.
2.217
2.218 + # Note this as the most recent invocation.
2.219 +
2.220 + self.set_last_invocation()
2.221 +
2.222 self.new_op(DropFrame())
2.223 if keep_frame:
2.224 self.suspend_frame()
2.225 @@ -679,12 +678,6 @@
2.226
2.227 NameInstruction, AddressInstruction = classes
2.228
2.229 - # Optimise away the constant storage if appropriate.
2.230 - # The target and value loading operations are removed.
2.231 -
2.232 - if self._optimise_constant_storage(NameInstruction, 0):
2.233 - return
2.234 -
2.235 if scope == "local":
2.236 unit = self.unit
2.237 if isinstance(unit, Function):
2.238 @@ -735,34 +728,92 @@
2.239 def _should_optimise_stack_access(self):
2.240 return "stack_access" in self.optimisations
2.241
2.242 + # Simple tests.
2.243 +
2.244 + def _is_constant_input(self, instruction):
2.245 +
2.246 + "Return whether 'instruction' provides a constant input."
2.247 +
2.248 + return isinstance(instruction, LoadAddress) and instruction.attr.assignments == 1 or \
2.249 + isinstance(instruction, LoadConst)
2.250 +
2.251 + def _is_local_input(self, instruction):
2.252 +
2.253 + "Return whether 'instruction' provides a local input."
2.254 +
2.255 + return isinstance(instruction, (LoadName, LoadTemp))
2.256 +
2.257 + def _is_unaffected(self, instruction, position):
2.258 +
2.259 + """
2.260 + Return whether 'instruction' is unaffected by side-effects since it
2.261 + occurs at a 'position' later than the last invocation.
2.262 + """
2.263 +
2.264 + return isinstance(instruction, (LoadResult, LoadAddress)) and (
2.265 + self.last_invocation is None or position >= self.last_invocation)
2.266 +
2.267 + # Convenience tests.
2.268 +
2.269 def _have_constant_input(self, n):
2.270 +
2.271 + "Return whether the last 'n' instructions provide constant inputs."
2.272 +
2.273 last = self.last_ops(n+1)
2.274 - return len(last) > n and (isinstance(last[n], LoadAddress) and last[n].attr.assignments == 1 or
2.275 - isinstance(last[n], LoadConst))
2.276 + return len(last) > n and self._is_constant_input(last[n])
2.277
2.278 def _have_known_target(self):
2.279 +
2.280 + "Return whether the last instruction is a known target."
2.281 +
2.282 return self._have_constant_input(0)
2.283
2.284 def _have_self_input(self):
2.285 +
2.286 + "Return whether the last instruction is a reference to self."
2.287 +
2.288 last = self.last_op()
2.289 return isinstance(self.unit, Function) and \
2.290 self.unit.is_method() and isinstance(last, LoadName) and \
2.291 last.attr.name == "self"
2.292
2.293 def _have_temp_compatible_access(self):
2.294 +
2.295 + """
2.296 + Indicate whether the last instruction can be used in place of access to
2.297 + a temporary variable retaining the result of the last instruction.
2.298 + """
2.299 +
2.300 last = self.last_op()
2.301 # NOTE: Should expand to cover LoadAttr and LoadAttrIndex, but this
2.302 # NOTE: would require inspection of the stack operations.
2.303 return isinstance(last, (LoadName, LoadTemp, LoadAddress, LoadConst))
2.304
2.305 - def _have_fixed_sources(self, instruction):
2.306 + def _get_access_instructions(self, instruction):
2.307 +
2.308 + """
2.309 + Get the stack access instructions employed by the given 'instruction'.
2.310 + It is assumed that the source of the data communicated through the stack
2.311 + is not modified since the storage of the data. However, since the stack
2.312 + should only be employed within statements, there should be no risk of
2.313 + side-effects for local storage.
2.314 + """
2.315 +
2.316 access_ops = []
2.317 for stack_op in instruction.accesses:
2.318 - if isinstance(stack_op, StackLoad):
2.319 + if isinstance(stack_op, (StackLoad, StackPull)):
2.320 position, op = self.stack[stack_op.n]
2.321 - if not isinstance(op, (LoadName, LoadTemp, LoadAddress, LoadConst)):
2.322 +
2.323 + # Insist on constants, locals or other things which should not
2.324 + # have been changed.
2.325 +
2.326 + if not self._is_constant_input(op) and not self._is_local_input(op) and \
2.327 + not self._is_unaffected(op, position):
2.328 +
2.329 return None
2.330 +
2.331 access_ops.append((op, stack_op))
2.332 +
2.333 return access_ops
2.334
2.335 # Optimisation methods. See the supported_optimisations class attribute.
2.336 @@ -779,6 +830,10 @@
2.337 and the appropriate LoadTemp instruction is returned.
2.338
2.339 All returned instructions are provided in a list.
2.340 +
2.341 + Restriction: for use only in situations where the source of the
2.342 + temporary data will not be disturbed between its first access and its
2.343 + subsequent use.
2.344 """
2.345
2.346 if self._should_optimise_temp_storage() and \
2.347 @@ -792,7 +847,7 @@
2.348 self.new_op(StoreTemp(temp_position))
2.349 return [LoadTemp(temp_position)]
2.350
2.351 - def _optimise_constant_storage(self, instruction, n):
2.352 + def _optimise_constant_storage(self, instruction):
2.353
2.354 """
2.355 Where this operation should store a constant into a target which is
2.356 @@ -800,11 +855,9 @@
2.357 """
2.358
2.359 if self._should_optimise_constant_storage() and \
2.360 - instruction in (StoreAddress, StoreName) and \
2.361 - self._have_constant_input(n) and \
2.362 - (n == 0 or self._have_constant_input(n-1)):
2.363 + isinstance(instruction, (StoreName, StoreAddress)) and \
2.364 + instruction.attr.assignments == 1:
2.365
2.366 - self.remove_ops(n+1)
2.367 return 1
2.368 else:
2.369 return 0
2.370 @@ -862,7 +915,7 @@
2.371 directly.
2.372 """
2.373
2.374 - AddressInstruction, AttrInstruction = classes
2.375 + AddressInstruction, AddressContextInstruction, AttrInstruction = classes
2.376
2.377 if self._should_optimise_self_access() and self._have_self_input() and \
2.378 not self.unit.is_relocated(attrname):
2.379 @@ -878,7 +931,7 @@
2.380 except KeyError:
2.381 attr = self.unit.parent.all_attributes()[attrname]
2.382 new_attr = attr.via_instance()
2.383 - self.replace_op(AddressInstruction(new_attr))
2.384 + self.new_op(AddressContextInstruction(new_attr))
2.385
2.386 return 1
2.387 else:
2.388 @@ -892,9 +945,8 @@
2.389 """
2.390
2.391 if self._should_optimise_stack_access():
2.392 - ops = self._have_fixed_sources(instruction)
2.393 + ops = self._get_access_instructions(instruction)
2.394 if ops is not None:
2.395 - #print "Optimised", instruction.accesses, "->", ops
2.396 instruction.accesses = []
2.397 for op, stack_op in ops:
2.398 if self.remove_op_using_stack(op):
2.399 @@ -939,7 +991,7 @@
2.400
2.401 # Get the method on temp.
2.402
2.403 - self._generateAttr(node, method, (LoadAddress, LoadAttr, LoadAttrIndex))
2.404 + self._generateAttr(node, method, self.attr_load_instructions)
2.405
2.406 # Add exception handling to the method acquisition instructions where
2.407 # the attribute access cannot be resolved at compile-time.
2.408 @@ -1016,7 +1068,7 @@
2.409
2.410 # Get left method on temp1.
2.411
2.412 - self._generateAttr(node, left_method, (LoadAddress, LoadAttr, LoadAttrIndex))
2.413 + self._generateAttr(node, left_method, self.attr_load_instructions)
2.414
2.415 # Add exception handling to the method acquisition instructions where
2.416 # the attribute access cannot be resolved at compile-time.
2.417 @@ -1058,7 +1110,7 @@
2.418
2.419 # Get right method on temp2.
2.420
2.421 - self._generateAttr(node, right_method, (LoadAddress, LoadAttr, LoadAttrIndex))
2.422 + self._generateAttr(node, right_method, self.attr_load_instructions)
2.423
2.424 # Add exception handling to the method acquisition instructions where
2.425 # the attribute access cannot be resolved at compile-time.
2.426 @@ -1118,7 +1170,7 @@
2.427 self.dispatch(n)
2.428
2.429 def visitAssAttr(self, node):
2.430 - self._visitAttr(node, (StoreAddress, StoreAttr, StoreAttrIndex))
2.431 + self._visitAttr(node, self.attribute_store_instructions)
2.432
2.433 def visitAssList(self, node): pass
2.434
2.435 @@ -1223,13 +1275,18 @@
2.436
2.437 self._startCallFunc()
2.438 self.dispatch(node.list)
2.439 - self._generateAttr(node, "__iter__", (LoadAddress, LoadAttr, LoadAttrIndex))
2.440 + self._generateAttr(node, "__iter__", self.attr_load_instructions)
2.441 temp = self._generateCallFunc([], node)
2.442 self._doCallFunc(temp)
2.443 self._endCallFunc(temp)
2.444
2.445 # Iterator on stack.
2.446
2.447 + temp_iterator_position = self.reserve_temp(1)
2.448 + temp_iterator = [LoadTemp(temp_iterator_position)]
2.449 +
2.450 + self.new_op(StoreTemp(temp_iterator_position))
2.451 +
2.452 # In the loop...
2.453
2.454 self.set_label(next_label)
2.455 @@ -1237,8 +1294,8 @@
2.456 # Use the iterator to get the next value.
2.457
2.458 self._startCallFunc()
2.459 - self.new_op(Duplicate())
2.460 - self._generateAttr(node, "next", (LoadAddress, LoadAttr, LoadAttrIndex))
2.461 + self.new_ops(temp_iterator)
2.462 + self._generateAttr(node, "next", self.attr_load_instructions)
2.463 temp = self._generateCallFunc([], node)
2.464 self._doCallFunc(temp)
2.465 self._endCallFunc(temp)
2.466 @@ -1272,10 +1329,14 @@
2.467 self.set_label(exit_label)
2.468 self.dispatch(node.else_)
2.469
2.470 - # Pop the iterator.
2.471 + # After the loop...
2.472
2.473 self.set_label(exit_label)
2.474
2.475 + # Compilation duties...
2.476 +
2.477 + self.discard_temp(temp_iterator)
2.478 +
2.479 def visitFrom(self, node): pass
2.480
2.481 def visitFunction(self, node):
2.482 @@ -1309,7 +1370,7 @@
2.483 def visitGenExprInner(self, node): pass
2.484
2.485 def visitGetattr(self, node):
2.486 - self._visitAttr(node, (LoadAddress, LoadAttr, LoadAttrIndex))
2.487 + self._visitAttr(node, self.attribute_load_instructions)
2.488
2.489 def visitGlobal(self, node): pass
2.490
3.1 --- a/micropython/data.py Sat Jun 28 20:46:45 2008 +0200
3.2 +++ b/micropython/data.py Mon Jun 30 00:32:54 2008 +0200
3.3 @@ -700,12 +700,12 @@
3.4
3.5 def __repr__(self):
3.6 if self.location is not None:
3.7 - return "Function(%r, %s, %r, %r, %r, %r, location=%r)" % (
3.8 - self.name, shortrepr(self.parent), self.argnames, self.defaults, self.has_star, self.has_dstar, self.location
3.9 + return "Function(%r, %s, %r, location=%r, code_location=%r)" % (
3.10 + self.name, shortrepr(self.parent), self.argnames, self.location, self.code_location
3.11 )
3.12 else:
3.13 - return "Function(%r, %s, %r, %r, %r, %r)" % (
3.14 - self.name, shortrepr(self.parent), self.argnames, self.defaults, self.has_star, self.has_dstar
3.15 + return "Function(%r, %s, %r)" % (
3.16 + self.name, shortrepr(self.parent), self.argnames
3.17 )
3.18
3.19 def __shortrepr__(self):
4.1 --- a/micropython/rsvp.py Sat Jun 28 20:46:45 2008 +0200
4.2 +++ b/micropython/rsvp.py Mon Jun 30 00:32:54 2008 +0200
4.3 @@ -41,13 +41,13 @@
4.4
4.5 "A generic instruction."
4.6
4.7 - stack_storage = 0
4.8 - stack_access = 0
4.9 + stack_storage = []
4.10 + stack_access = []
4.11
4.12 def __init__(self, attr=None):
4.13 self.attr = attr
4.14 - self.accesses = [StackLoad(-1 - n) for n in range(0, self.stack_access)]
4.15 - self.results = [StackSave(n) for n in range(0, self.stack_storage)]
4.16 + self.accesses = [op(-1 - n) for n, op in enumerate(self.stack_access)]
4.17 + self.results = [op(n) for n, op in enumerate(self.stack_storage)]
4.18
4.19 def copy(self):
4.20 return self.__class__(self.attr)
4.21 @@ -172,6 +172,43 @@
4.22
4.23 Immediate = ImmediateInstruction
4.24
4.25 +# Internal stack and frame operations for instructions.
4.26 +
4.27 +class StackOp:
4.28 +
4.29 + "A generic stack operation."
4.30 +
4.31 + def __init__(self, n):
4.32 + self.n = n
4.33 + self.level = None
4.34 +
4.35 + def fix_stack(self, level):
4.36 + self.level = self.n + level
4.37 +
4.38 + def __repr__(self):
4.39 + return "%s(%s)" % (self.__class__.__name__, self.level == 0 and "0" or self.level or self.n)
4.40 +
4.41 +class StackPull(StackOp):
4.42 +
4.43 + "Load a value from the stack."
4.44 +
4.45 + def get_effect(self):
4.46 + return -1
4.47 +
4.48 +class StackPush(StackOp):
4.49 +
4.50 + "Save a value onto the stack."
4.51 +
4.52 + def get_effect(self):
4.53 + return 1
4.54 +
4.55 +class StackLoad(StackOp):
4.56 +
4.57 + "Load a value from the stack."
4.58 +
4.59 + def get_effect(self):
4.60 + return 0
4.61 +
4.62 # Mix-in classes for stack effects.
4.63
4.64 class StackAdd:
4.65 @@ -181,35 +218,34 @@
4.66 instruction.
4.67 """
4.68
4.69 - stack_storage = 1
4.70 + stack_storage = [StackPush]
4.71
4.72 class StackRemove:
4.73
4.74 "Indicate that the stack must shrink as an effect of this instruction."
4.75
4.76 - stack_storage = -1
4.77 - stack_access = 1
4.78 + stack_access = [StackPull]
4.79
4.80 class StackRemove2:
4.81
4.82 "Indicate that the stack must shrink as an effect of this instruction."
4.83
4.84 - stack_storage = -2
4.85 - stack_access = 2
4.86 + stack_access = [StackPull, StackPull]
4.87
4.88 -class StackReplace:
4.89 +class StackReplace(StackAdd, StackRemove):
4.90
4.91 """
4.92 Indicate that the stack remains at the same level due to the replacement of
4.93 the topmost element.
4.94 """
4.95
4.96 - stack_storage = 1
4.97 - stack_access = 1
4.98 + pass
4.99 +
4.100 +class StackInspect:
4.101
4.102 -# Instructions operating on the value stack.
4.103 + "Indicate that the stack is inspected but unchanged by this instruction."
4.104
4.105 -class Duplicate(StackAdd, Instruction): "Duplicate the top of the stack."
4.106 + stack_access = [StackLoad]
4.107
4.108 # Access to stored constant data.
4.109
4.110 @@ -222,41 +258,43 @@
4.111 class LoadTemp(StackAdd, Immediate): "Load the object from the given temporary location."
4.112 class StoreTemp(StackRemove, Immediate): "Store the object in the given temporary location."
4.113
4.114 +# Access to static data.
4.115 +
4.116 +class LoadAddress(StackAdd, Address): "Load the object from the given fixed attribute address."
4.117 +class StoreAddress(StackRemove, Address): "Store an object in the given fixed attribute address."
4.118 +class LoadAddressContext(StackReplace, Address):"Load the object from the given fixed attribute address, changing the context."
4.119 +class StoreAddressContext(StackRemove2, Address):"Store an object in the given fixed attribute address, changing the context."
4.120 +class MakeObject(StackAdd, Instruction): "Make a new object. There isn't a complementary DropObject."
4.121 +
4.122 # Access to address-relative data.
4.123
4.124 -class MakeObject(StackAdd, Instruction): "Make a new object. There isn't a complementary DropObject."
4.125 class LoadAttr(StackReplace, AR): "Load the object from the given attribute."
4.126 class StoreAttr(StackRemove2, AR): "Store an object in the given attribute."
4.127 class LoadAttrIndex(StackReplace, Immediate): "Load the object for the attribute with the given index."
4.128 class StoreAttrIndex(StackRemove2, Immediate): "Store an object in the attribute with the given index."
4.129 -class LoadAddress(StackAdd, Address): "Load the object from the given fixed attribute address."
4.130 -class StoreAddress(StackRemove, Address): "Store an object in the given fixed attribute address."
4.131
4.132 # Access to invocation frames in preparation.
4.133
4.134 class MakeFrame(Instruction): "Make a new invocation frame."
4.135 -class ReserveFrame(Immediate): "Reserve the given number of entries for the invocation frame."
4.136 -class StoreFrame(StackRemove, Immediate): "Store an argument at the given frame location."
4.137 +class DropFrame(Instruction): "Drop an invocation frame."
4.138 +class StoreFrame(StackRemove, Immediate): "Store an argument for the parameter with the given position."
4.139 class StoreFrameIndex(StackRemove, Immediate): "Store an argument for the parameter with the given index."
4.140 -class LoadCallable(Instruction): "Load the target of an invocation."
4.141 +class LoadCallable(StackInspect, Instruction): "Load the target of an invocation."
4.142 class LoadContext(StackReplace, Instruction): "Load the context of an invocation."
4.143 -class CheckFrame(Instruction): "Check the invocation frame for the target."
4.144 -class CheckSelf(Instruction): "Check the first argument of an invocation against the target."
4.145 -class CheckContext(Instruction): """Check the context of an invocation against the target,
4.146 - potentially discarding the context."""
4.147 +class CheckFrame(Instruction): "Check the invocation frame and context for the target."
4.148 +class CheckSelf(StackAdd, Instruction): "Check the first argument of an invocation against the target."
4.149
4.150 # Invocation-related instructions, using a special result "register".
4.151
4.152 -class JumpWithFrame(Instruction): "Jump, adopting the invocation frame, to the callable found on the stack."
4.153 -class DropFrame(Instruction): "Drop an invocation frame."
4.154 +class JumpWithFrame(StackRemove, Instruction): "Jump, adopting the invocation frame, to the callable found on the stack."
4.155 class Return(StackRemove, Instruction): "Return a value from a subprogram."
4.156 class LoadResult(StackAdd, Instruction): "Load a returned value."
4.157
4.158 # Branch-related instructions.
4.159
4.160 class Jump(Address): "Jump unconditionally."
4.161 -class JumpIfFalse(Address): "Jump if the last evaluation gave a false result."
4.162 -class JumpIfTrue(Address): "Jump if the last evaluation gave a true result."
4.163 +class JumpIfFalse(StackRemove, Address): "Jump if the last evaluation gave a false result."
4.164 +class JumpIfTrue(StackRemove, Address): "Jump if the last evaluation gave a true result."
4.165
4.166 # Exception-related instructions, using a special exception "register".
4.167
4.168 @@ -269,37 +307,4 @@
4.169 class TestIdentity(Instruction): "Test whether the two topmost stack values are identical."
4.170 class TestIdentityAddress(Address): "Test whether the topmost stack value is identical to the given address."
4.171
4.172 -# Internal stack operations for instructions.
4.173 -
4.174 -class StackOp:
4.175 -
4.176 - "A generic stack operation."
4.177 -
4.178 - def __init__(self, n):
4.179 - self.n = n
4.180 - self.level = None
4.181 -
4.182 - def __repr__(self):
4.183 - return "%s(%s)" % (self.__class__.__name__, self.level == 0 and "0" or self.level or self.n)
4.184 -
4.185 -class StackLoad(StackOp):
4.186 -
4.187 - "Load a value from the stack."
4.188 -
4.189 - def fix_stack(self, level):
4.190 - self.level = self.n + level
4.191 -
4.192 - def get_effect(self):
4.193 - return -1
4.194 -
4.195 -class StackSave(StackOp):
4.196 -
4.197 - "Save a value onto the stack."
4.198 -
4.199 - def fix_stack(self, level):
4.200 - self.level = self.n + level
4.201 -
4.202 - def get_effect(self):
4.203 - return 1
4.204 -
4.205 # vim: tabstop=4 expandtab shiftwidth=4