Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maps, Unrolling, and Dynamic Frame Size #147

Open
wants to merge 63 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
a5fc296
make shell work after translation
timfel Jan 31, 2017
c009380
- load files in the shell, allow -r to pass shell line (so we can lau…
timfel Jan 31, 2017
8d8818c
Merge branch 'master' into tim/maps
timfel Jan 31, 2017
af7962b
Merge branch 'tim/guard_opt' into tim/maps
timfel Feb 1, 2017
69edf19
Revert "Revert "split PointersObjects into Fixed and Non-Fixed, imple…
timfel Feb 1, 2017
05516f3
Merge remote-tracking branch 'origin/master' into tim/maps
timfel Mar 6, 2017
1ad1975
Merge remote-tracking branch 'origin/master' into tim/maps
timfel Mar 6, 2017
588e9e6
fix translation with immutability plugin
timfel Mar 6, 2017
b604699
promote perform selectors
timfel Mar 6, 2017
41bc04c
don't allocate _storage field if map doesn't need it
timfel Mar 6, 2017
165ada5
try to avoid some allocation overhead when switching strategies
timfel Mar 6, 2017
c599ed5
update the singleton nodes for MapStrategy, so that we'll always get …
timfel Mar 6, 2017
d134857
use nan-tagging for nil in float and integer storages
timfel Mar 6, 2017
141029f
Revert "use nan-tagging for nil in float and integer storages"
timfel Mar 6, 2017
8c263aa
Revert "update the singleton nodes for MapStrategy, so that we'll alw…
timfel Mar 6, 2017
96d3f22
if w_self is constant, promote our strategy
timfel Mar 6, 2017
a91b67f
if self is constant, call an elidable strategy accessor
timfel Mar 6, 2017
547c288
compute the actually required frame size
timfel Mar 7, 2017
89687c3
fix tests and frame size calculation, as well as a couple of stack er…
timfel Mar 7, 2017
c8a58d2
skip two bytes for Spur prim bytecode 139 as params
timfel Mar 7, 2017
1f6bf27
fix tests and extend stack size computation
timfel Mar 8, 2017
360aaff
switch to dynamic computation of frame size
timfel Mar 13, 2017
b96acde
punt on untranslated for now
timfel Mar 13, 2017
8a4591c
use the discovered frame size and go through slow path in interpreted…
timfel Mar 13, 2017
b1c7eae
we need to have space at least for args and temps. the size from the …
timfel Mar 13, 2017
d79c899
dynamically overflow into an 'overflow stack' array on the heap. shou…
timfel Mar 13, 2017
5cd8292
don't store the w_self_size, calculate it if required
timfel Mar 13, 2017
795f054
merge blockmethod and extra_data into a single field
timfel Mar 13, 2017
7d822df
let's ignore all stores behind the stack pointer, even those out of b…
timfel Mar 13, 2017
d90f464
implement switches to disable or limit maps
timfel Mar 14, 2017
0161ce3
Merge remote-tracking branch 'origin/master' into tim/dynamic_frame_size
timfel Mar 14, 2017
0061221
if we run out of the end of a compiled method, return the receiver
timfel Mar 14, 2017
5709807
log map transitions, too
timfel Mar 14, 2017
fe7ce83
remove unneeded imports
timfel Mar 14, 2017
d5c4eb7
fix merge error
timfel Mar 14, 2017
02d2696
fix logging of map transitions
timfel Mar 14, 2017
ecfcd89
use logname method for strategy logs
timfel Mar 14, 2017
97f2513
try to be more precise in how map transitions work, transitioning
timfel Mar 14, 2017
177c202
avoid checking for None on the stack
timfel Mar 17, 2017
b6f7277
try unrolling at least a little
timfel Mar 19, 2017
9289d0c
Merge remote-tracking branch 'origin/tim/loop_unrolling' into tim/dyn…
timfel Mar 20, 2017
c582599
try harder to avoid an extra guard in bytecodePrimAt
timfel Mar 20, 2017
9992067
test fix
timfel Mar 20, 2017
f3c3316
only set the framesize if it grows, only let it grow, and make it int…
timfel Mar 20, 2017
5ff5b92
only jit frames that have no overflow stack
timfel Mar 20, 2017
101e312
forgot to init the frame size on fillin
timfel Mar 20, 2017
74bf834
wrong return
timfel Mar 20, 2017
2a35d5f
spy hacks are gone
timfel Mar 20, 2017
4e7fd7b
split safe identification methods on compiled method
timfel Mar 21, 2017
c4c98f7
patch the jitdriver to produce better jitlog and vmprof info
timfel Mar 21, 2017
b412e7f
try harder to find the selector when filling in compiled methods
timfel Mar 21, 2017
59f27c6
fix tests
timfel Mar 21, 2017
e526d1a
add --run-file argument to support the Squeak-style for running code …
timfel Mar 21, 2017
ffe009f
add a -rr cmdline argument as a hack to run a doit twice, so it gets …
timfel Mar 22, 2017
f6d331c
avoid creating an infinite recursion when using -rr
timfel Mar 22, 2017
41cb793
use double-run for jittests as a workaround for our new frame size di…
timfel Mar 22, 2017
93fdd55
update jittests
timfel Mar 22, 2017
7526c30
only interrupt on keyup
timfel Mar 23, 2017
bc0162c
address code review concerns
timfel Mar 23, 2017
a967a31
Merge remote-tracking branch 'origin/tim/dynamic_frame_size' into tim…
timfel Mar 28, 2017
f5f834a
really only unroll once
timfel Mar 28, 2017
c6b982c
make stack_frame no longer take a default argument, to make life easi…
timfel Apr 2, 2017
f3ba427
update jittests
timfel Apr 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ exclude_paths:
- "repository/*"
- "scripts/**/*"
- "scripts/*"

56 changes: 28 additions & 28 deletions rsqueakvm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,42 +127,42 @@
SO_JIT_HOOK_RCVR = 59 # really selectorTrap

constant_objects_in_special_object_table = {
"nil": (SO_NIL, "POINTERS"),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use maps for W_FixedPointersObjects that have inline fields. These objects are prebuilt constants - we need to create them to be the right kind of empty PBCs.

"true": (SO_TRUE, "POINTERS"),
"false": (SO_FALSE, "POINTERS"),
"nil": (SO_NIL, "FIXED"),
"true": (SO_TRUE, "FIXED"),
"false": (SO_FALSE, "FIXED"),
"charactertable": (SO_CHARACTER_TABLE_ARRAY, "POINTERS"),
"schedulerassociationpointer": (SO_SCHEDULERASSOCIATIONPOINTER, "POINTERS"),
"schedulerassociationpointer": (SO_SCHEDULERASSOCIATIONPOINTER, "FIXED"),
"special_selectors": (SO_SPECIAL_SELECTORS_ARRAY, "POINTERS"),
"smalltalkdict": (SO_SMALLTALK, "POINTERS"),
"smalltalkdict": (SO_SMALLTALK, "FIXED"),
"doesNotUnderstand": (SO_DOES_NOT_UNDERSTAND, "BYTES"),
"mustBeBoolean": (SO_MUST_BE_BOOLEAN, "BYTES"),
"runWithIn": (SO_RUN_WITH_IN, "BYTES"),
"cannotReturn": (SO_CANNOT_RETURN, "BYTES"),
# classes
"Bitmap": (SO_BITMAP_CLASS, "POINTERS"),
"SmallInteger": (SO_SMALLINTEGER_CLASS, "POINTERS"),
"String": (SO_STRING_CLASS, "POINTERS"),
"Array": (SO_ARRAY_CLASS, "POINTERS"),
"Float": (SO_FLOAT_CLASS, "POINTERS"),
"MethodContext": (SO_METHODCONTEXT_CLASS, "POINTERS"),
"BlockContext": (SO_BLOCKCONTEXT_CLASS, "POINTERS"),
"BlockClosure": (SO_BLOCKCLOSURE_CLASS, "POINTERS"),
"Point": (SO_POINT_CLASS, "POINTERS"),
"LargePositiveInteger": (SO_LARGEPOSITIVEINTEGER_CLASS, "POINTERS"),
"Message": (SO_MESSAGE_CLASS, "POINTERS"),
"CompiledMethod": (SO_COMPILEDMETHOD_CLASS, "POINTERS"),
"Semaphore": (SO_SEMAPHORE_CLASS, "POINTERS"),
"Character": (SO_CHARACTER_CLASS, "POINTERS"),
"ByteArray": (SO_BYTEARRAY_CLASS, "POINTERS"),
"Process": (SO_PROCESS_CLASS, "POINTERS"),
# "PseudoContext" : (SO_PSEUDOCONTEXT_CLASS, "POINTERS"),
# "TranslatedMethod" : (SO_TRANSLATEDMETHOD_CLASS, "POINTERS"),
"LargeNegativeInteger" : (SO_LARGENEGATIVEINTEGER_CLASS, "POINTERS"),
"Bitmap": (SO_BITMAP_CLASS, "FIXED"),
"SmallInteger": (SO_SMALLINTEGER_CLASS, "FIXED"),
"String": (SO_STRING_CLASS, "FIXED"),
"Array": (SO_ARRAY_CLASS, "FIXED"),
"Float": (SO_FLOAT_CLASS, "FIXED"),
"MethodContext": (SO_METHODCONTEXT_CLASS, "FIXED"),
"BlockContext": (SO_BLOCKCONTEXT_CLASS, "FIXED"),
"BlockClosure": (SO_BLOCKCLOSURE_CLASS, "FIXED"),
"Point": (SO_POINT_CLASS, "FIXED"),
"LargePositiveInteger": (SO_LARGEPOSITIVEINTEGER_CLASS, "FIXED"),
"Message": (SO_MESSAGE_CLASS, "FIXED"),
"CompiledMethod": (SO_COMPILEDMETHOD_CLASS, "FIXED"),
"Semaphore": (SO_SEMAPHORE_CLASS, "FIXED"),
"Character": (SO_CHARACTER_CLASS, "FIXED"),
"ByteArray": (SO_BYTEARRAY_CLASS, "FIXED"),
"Process": (SO_PROCESS_CLASS, "FIXED"),
# "PseudoContext" : (SO_PSEUDOCONTEXT_CLASS, "FIXED"),
# "TranslatedMethod" : (SO_TRANSLATEDMETHOD_CLASS, "FIXED"),
"LargeNegativeInteger" : (SO_LARGENEGATIVEINTEGER_CLASS, "FIXED"),
# ours, not in the table, but we'd like it to
"ClassBinding": (SPECIAL_OBJECTS_SIZE + 30, "POINTERS"),
"Metaclass": (SPECIAL_OBJECTS_SIZE + 31, "POINTERS"),
"Processor": (SPECIAL_OBJECTS_SIZE + 32, "POINTERS"),
"ByteSymbol": (SPECIAL_OBJECTS_SIZE + 33, "POINTERS")
"ClassBinding": (SPECIAL_OBJECTS_SIZE + 30, "FIXED"),
"Metaclass": (SPECIAL_OBJECTS_SIZE + 31, "FIXED"),
"Processor": (SPECIAL_OBJECTS_SIZE + 32, "FIXED"),
"ByteSymbol": (SPECIAL_OBJECTS_SIZE + 33, "FIXED")
}

variables_in_special_object_table = {
Expand Down
81 changes: 64 additions & 17 deletions rsqueakvm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,17 @@ def __init__(self, s_new_context, forced=False):
ContextSwitchException.__init__(self, s_new_context)
self.forced = forced

class Optargs(object):
_attrs_ = ["max_squeak_unroll_count", "squeak_unroll_trace_limit"]
_immutable_fields_ = ["max_squeak_unroll_count", "squeak_unroll_trace_limit"]
def __init__(self):
self.max_squeak_unroll_count = 2
self.squeak_unroll_trace_limit = 32000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment to explain the two values?



UNROLLING_BYTECODE_RANGES = unroll.unrolling_iterable(interpreter_bytecodes.BYTECODE_RANGES)

def get_printable_location(pc, self, method, w_class, blockmethod):
def get_printable_location(pc, jump_back_pc, unrollings, frame_size, self, method, w_class, blockmethod):
bc = ord(method.bytes[pc])
name = method.safe_identifier_string()
classname = "???"
Expand All @@ -143,8 +151,8 @@ def get_printable_location(pc, self, method, w_class, blockmethod):
blockname = blockmethod.safe_identifier_string()
return '%s(%s): (%s) [%d]: <%s>%s' % (classname, name, blockname, pc, hex(bc), interpreter_bytecodes.BYTECODE_NAMES[bc])

def resume_get_printable_location(pc, self, method, w_class):
return "resume: %s" % get_printable_location(pc, self, method, w_class, None)
def resume_get_printable_location(pc, frame_size, self, method, w_class):
return "resume: %s" % get_printable_location(pc, 0, 0, frame_size, self, method, w_class, None)

# def confirm_enter_jit(pc, self, method, w_class, s_context):
# print get_printable_location(pc, self, method, w_class)
Expand All @@ -161,11 +169,13 @@ class Interpreter(object):
"evented",
"interrupts",
"trace_important",
"trace"]
"trace",
"optargs"]

jit_driver = jit.JitDriver(
name=jit_driver_name,
greens=['pc', 'self', 'method', 'w_class', 'blockmethod'],
greens=['pc', 'jump_back_pc', 'unrollings', 'frame_size', 'self',
'method', 'w_class', 'blockmethod'],
reds=['s_context'],
virtualizables=['s_context'],
get_printable_location=get_printable_location,
Expand All @@ -174,15 +184,15 @@ class Interpreter(object):

resume_driver = jit.JitDriver(
name=jit_driver_name + "_resume",
greens=['pc', 'self', 'method', 'w_class'],
greens=['pc', 'frame_size', 'self', 'method', 'w_class'],
reds=['s_context'],
# virtualizables=['s_context'],
get_printable_location=resume_get_printable_location,
is_recursive=True
)

def __init__(self, space, image=None, trace_important=False,
trace=False, evented=True, interrupts=True):
trace=False, evented=True, interrupts=True, optargs=None):
# === Initialize immutable variables
self.space = space
self.image = image
Expand All @@ -199,6 +209,7 @@ def __init__(self, space, image=None, trace_important=False,
self.interrupt_counter_size = constants.INTERRUPT_COUNTER_SIZE
self.last_check = self.time_now()
self.trace = trace
self.optargs = optargs or Optargs()

# === Initialize mutable variables
self.interrupt_check_counter = self.interrupt_counter_size
Expand All @@ -217,9 +228,11 @@ def loop(self, w_active_context):
s_context = w_active_context.as_context_get_shadow(self.space)
while True:
method = s_context.w_method()
frame_size = method.frame_size()
pc = s_context.pc()
self.resume_driver.jit_merge_point(
pc=pc,
frame_size=frame_size,
self=self,
method=method,
w_class=self.getreceiverclass(s_context),
Expand All @@ -239,6 +252,7 @@ def loop(self, w_active_context):
self.resume_driver.can_enter_jit(
pc=pc,
self=self,
frame_size=frame_size,
method=method,
w_class=self.getreceiverclass(s_context),
s_context=s_context)
Expand Down Expand Up @@ -304,27 +318,60 @@ def getreceiverclass(self, s_context):
return s_context.w_receiver().safe_getclass(self.space)

def getblockmethod(self, s_context):
return s_context.blockmethod
return s_context.blockmethod()

def loop_bytecodes(self, s_context, may_context_switch=True):
old_pc = 0
jump_back_pc = 0
unrollings = 0
if not jit.we_are_jitted() and may_context_switch:
self.quick_check_for_interrupt(s_context)
method = s_context.w_method()
frame_size = method.frame_size()
while True:
pc = s_context.pc()
if pc < old_pc:
if jit.we_are_jitted():
# Do the interrupt-check at the end of a loop, don't interrupt loops midway.
self.jitted_check_for_interrupt(s_context)
self.jit_driver.can_enter_jit(
pc=pc, self=self, method=method,
w_class=self.getreceiverclass(s_context),
blockmethod=self.getblockmethod(s_context),
s_context=s_context)
if jump_back_pc == old_pc:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows us to manually unroll a variable amount of times. At least one time is useful in Squeak, because of the way loops are compiled by the Squeak compiler (see also the jittests for unrolling)

if (unrollings < self.optargs.max_squeak_unroll_count and
jit.current_trace_length() < self.optargs.squeak_unroll_trace_limit):
unrollings += 1
else:
if jit.we_are_jitted():
# Do the interrupt-check at the end of a loop, don't
# interrupt loops midway.
self.jitted_check_for_interrupt(s_context)
if not s_context.has_overflow_stack():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we're in a loop where the initially allocated frame size was smaller than what we actually used in the loop, we'll have an overflow stack. The next time we enter this method the larger frame size will be allocated and no overflow stack will be needed, but we don't want to jit a loop that uses the overflow stack only for the one time we have it.

self.jit_driver.can_enter_jit(
pc=pc,
jump_back_pc=jump_back_pc,
unrollings=unrollings,
frame_size=frame_size,
self=self, method=method,
w_class=self.getreceiverclass(s_context),
blockmethod=self.getblockmethod(s_context),
s_context=s_context)
else:
jump_back_pc = old_pc
unrollings = 1
# we jumped back from the end of a loop. Instead of allowing
# to enter the JIT here, we instead wait for the second time
# this loop runs and call can_enter_jit only then
# (effectively unrolling at least two iterations). This is
# because the way the Squeak compiler generates loop
# bytecodes: we get the branch condition in the header and a
# conditional jump forward in case it is false. Then the
# loop body and an unconditional jump back. In the case of 1
# to: 1 do: or other loops that run for exactly one
# iteration, we will still generate a call_assembler in that
# case, which we work around with this. There are indeed a
# few examples of loops that run exactly one iteration
old_pc = pc
self.jit_driver.jit_merge_point(
pc=pc, self=self, method=method,
pc=pc,
jump_back_pc=jump_back_pc,
unrollings=unrollings,
frame_size=frame_size,
self=self, method=method,
w_class=self.getreceiverclass(s_context),
blockmethod=self.getblockmethod(s_context),
s_context=s_context)
Expand Down
2 changes: 1 addition & 1 deletion rsqueakvm/interpreter_bytecodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def _sendSelfSelectorSpecial(self, selector, numargs, interp):

@objectmodel.specialize.arg(3)
def _sendSpecialSelector(self, interp, receiver, special_selector, w_args=[]):
space = jit.promote(self.space)
space = self.space
w_special_selector = getattr(space, "w_" + special_selector)
s_class = receiver.class_shadow(space)

Expand Down
Loading