-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
Add labels and gotos. (Fixes #101) #5699
Conversation
Impressive, @dcjones! |
This does pretty much work; no real I suspect the macroexpander will have to handle renaming labels to make them locally unique. The good news is the new types of AST nodes are not really necessary for this approach. Macros only need to emit |
Ok, so the AST nodes are only actually needed if I were to introduce new syntax. If no one feels strongly about that, I'll strip those out and update this PR. |
No, new AST nodes are only needed in the internal lowered form, if you need to convey something fundamentally different to the code generator. New surface syntax can be added just in the parser, without touching any other layers. |
I've finished my parser generator backend using this PR. The results are both encouraging and disappointing. Running the generated parser function on my benchmark the first time take ~30 seconds, and the second time ~0.5 seconds. The 0.5 number is a terrific start; it's only about 4-5x slower than the fastest C parser, but the function takes a seemingly excessive time to compile. Granted, the resulting code is super gnarly, with ~1700 labels and ~3000 gotos. Maybe there's no easy remedy, but is there at least a good way to go about tracking down the bottleneck? The profiler doesn't tell me much here. Is valgrind the next step? |
Probably certain LLVM optimization passes are very complex in the number of basic blocks. You can try commenting out |
That drops it by a few seconds, but it's still ~25 seconds on the first run. |
Hmm. Seems like a good use case for pre-compilation. |
Aha, I think there are some cases (at least in inference.jl) where we do linear lookup per-label. That will add an n^2 term. We should use a hash table for long functions. |
Updated with goto without the unnecessary AST nodes, plus some simple optimizations. My test case takes 10 seconds now instead of 25. As far as I can tell, most of the time is still spent bouncing around in |
Can we have gotos in 0.3, pretty please? There are now proper error messages when the same label is defined twice. And labels that are both defined and referenced in the same macro are renamed by macroexpander, which seemed to me the right way to deal with that. |
Looking pretty solid; great job with this. I'll read it in detail in a bit but this seems possible to merge for 0.3. |
Tagging as 0.3 just to pester you. 😉 It's ok if this doesn't make 0.3, but please consider before tagging. |
@@ -480,7 +500,13 @@ static jl_value_t *eval_body(jl_array_t *stmts, jl_value_t **locals, size_t nl, | |||
} | |||
if (jl_is_expr(stmt)) { | |||
jl_sym_t *head = ((jl_expr_t*)stmt)->head; | |||
if (head == goto_ifnot_sym) { | |||
if (head == symbolicgoto_sym) { |
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.
Is this necessary? It looks like the front end converts all symbolicgotos to plain gotos.
The interpret.c changes were unnecessary, just leftovers from figuring the right way to do this. I removed them and rebased. |
Ok, I think my only remaining concern here is exception handlers. Looking at the case for The really hard problem is |
this appears to be missing the code for inlining these correctly |
I don't think it matters, since these are lowered to normal gotos.
|
How's that? It will throw an error if jumping into a try/catch block, and generate the correct leave expression when jumping out now. |
What about finally blocks? I'm in favor of throwing an error for now when jumping out of finally blocks. |
Oh, right. Goto out of a finally throws an error now. |
Not exactly what I meant:
|
Ah, that makes more sense. |
Does this work with nested try/catch/finally blocks? |
You mean detecting gotos that skip finally blocks? It should. All these examples correctly throw errors. try
try
@goto a
catch
finally
end
@label a
catch
finally
end
try
try
@goto a
catch
finally
end
catch
finally
end
@label a
try
@goto a
try
catch
finally
end
catch
finally
end
@label a |
Bump. One of the remaining v0.3 issues. |
I'm still not convinced that this feature needs to be in 0.3 |
I'm not either, but it seems so very close. The only issue is jumping out of a try/finally, which I don't think we need to support immediately. |
Obviously goto statements aren't the mission-critical feature that everyone's been pining for. But I think this PR is pretty solid now, it should not effect existing behavior, and the addition of goto statements will enable some pretty nice things: notably, non-standard control-flow constructs implemented as macros (e.g. switch statements, multilevel breaks), and very fast pure julia parsers (as mentioned, I've already written a julia ragel backend but need this PR to actually use it). |
@@ -77,6 +77,7 @@ jl_sym_t *line_sym; jl_sym_t *jl_incomplete_sym; | |||
// head symbols for each expression type | |||
jl_sym_t *goto_sym; jl_sym_t *goto_ifnot_sym; | |||
jl_sym_t *label_sym; jl_sym_t *return_sym; | |||
jl_sym_t *symboliclabel_sym; jl_sym_t *symbolicgoto_sym; |
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.
Looks like these are now unused?
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.
Right. I've removed them now.
While it does seem strange to add a new control flow construct at this point, the implementation is now pretty much impeccable. After all of @dcjones ' work on this, I'm inclined to merge it. |
+1 |
Yes, I think we can merge this as well. |
Add labels and gotos. (Fixes #101)
Nice! Looking forward to trying it. |
Hey me too! Time to convert all my for and while loops to |
@quinnj Don't forget the else blocks. |
Am I doing something really obvious wrong? julia> function foo()
@label top
x = 0
println(x)
x += 1
if x < 10
@goto top
end
end
foo (generic function with 1 method)
julia> foo()
ERROR: error compiling foo: unsupported or misplaced expression symbolicgoto in function foo |
It looks like there is an issue with function foo()
@label top
x = 0
println(x)
x += 1
if x < 10
@goto top
end
return
end I'll see if I can figure out the correct behavior. I guess a special case is needed for dealing with implicit returns. |
Funny how different people hit entirely different use cases naturally. The first several things I tried all resulted in this problem – clearly that didn't ever happen while you were testing. |
I've taken a crack at implementing gotos.
What I've done is add new ast nodes
SymbolicLabelNode
andSymbolicGotoNode
mirroringLabelNode
andGotoNode
but with symbol labels instead of integers. Ingoto-form
, integer labels are assigned along with everything else, so this plays nicely with other control flow.Rather than introducing new syntax or keywords I just made macros
@label
and@goto
. I figured goto is (should be) used rarely enough that new syntax might not be worth it.Now we can make local jumps to our hearts' content:
This is my first foray into the src directory, so there could be something I've totally overlooked.