-
Notifications
You must be signed in to change notification settings - Fork 14
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
base: main
Are you sure you want to change the base?
Changes from 56 commits
a5fc296
c009380
8d8818c
af7962b
69edf19
05516f3
1ad1975
588e9e6
b604699
41bc04c
165ada5
c599ed5
d134857
141029f
8c263aa
96d3f22
a91b67f
547c288
89687c3
c8a58d2
1f6bf27
360aaff
b96acde
8a4591c
b1c7eae
d79c899
5cd8292
795f054
7d822df
d90f464
0161ce3
0061221
5709807
fe7ce83
d5c4eb7
02d2696
ecfcd89
97f2513
177c202
b6f7277
9289d0c
c582599
9992067
f3c3316
5ff5b92
101e312
74bf834
2a35d5f
4e7fd7b
c4c98f7
b412e7f
59f27c6
e526d1a
ffe009f
f6d331c
41cb793
93fdd55
7526c30
bc0162c
a967a31
f5f834a
c6b982c
f3ba427
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,4 @@ exclude_paths: | |
- "repository/*" | ||
- "scripts/**/*" | ||
- "scripts/*" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 = "???" | ||
|
@@ -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) | ||
|
@@ -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, | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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), | ||
|
@@ -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) | ||
|
@@ -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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
There was a problem hiding this comment.
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.