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

multi sub infix:<==> (Int $a, Str $b) { $a == $b } hangs in COMPILE time #5101

Open
p6rt opened this issue Jan 29, 2016 · 3 comments
Open

multi sub infix:<==> (Int $a, Str $b) { $a == $b } hangs in COMPILE time #5101

p6rt opened this issue Jan 29, 2016 · 3 comments

Comments

@p6rt
Copy link

p6rt commented Jan 29, 2016

Migrated from rt.perl.org#127421 (status was 'open')

Searchable as RT127421$

@p6rt
Copy link
Author

p6rt commented Jan 29, 2016

From @bbkr

multi sub infix​:<==> (Int $a, Str $b) { $a == $b }

Will never finish compilation on Rakudo 6.c and causes severe memory leak. I found that​:

1. Types must be different in signature
(Int $a, Str $b) # hangs
(Str $a, Int $b) # hangs
(Int $a, Int $b) # compiles

2. Operator must be the same in sub and block
multi sub infix​:<eq> (Int $a, Str $b) { $a eq $b } # hangs
multi sub infix​:<eq> (Int $a, Str $b) { $a == $b } # compiles

@p6rt
Copy link
Author

p6rt commented Sep 18, 2016

From @zoffixznet

On Fri Jan 29 06​:50​:08 2016, pawel.pabian@​getresponse.com wrote​:

multi sub infix​:<==> (Int $a, Str $b) { $a == $b }

Will never finish compilation on Rakudo 6.c and causes severe memory
leak. I found that​:

1. Types must be different in signature
(Int $a, Str $b) # hangs
(Str $a, Int $b) # hangs
(Int $a, Int $b) # compiles

2. Operator must be the same in sub and block
multi sub infix​:<eq> (Int $a, Str $b) { $a eq $b } # hangs
multi sub infix​:<eq> (Int $a, Str $b) { $a == $b } # compiles

Thanks for the report. I'm unsure whether this would qualify as a bug, but jnthn++ wanted to know more about this
behaviour, in case it suggests buggy behaviour elsewhere, so what follows are my findings.

The body of the multi you're creating calls itself, which causes infinite recursion, hence the hang and memory growth.

The reason you see this happen during compile time is due to routine inlining optimizations. You can see that
by turning off the optimizer​:

  perl6 --optimize=0 -e 'multi sub infix​:<==> (Int $a, Str $b) { $a == $b }' # doesn't hang

The reason (Int $a, Int $b) compiles is because it's ambiguous with the Int​:D, Int​:D candidate defined in core,
so the optimization infini-loop for it does not occur.

Using a different operator, as you described in point (2), simply avoids using the same operator in the body, and
so the multi never ends up calling itself.


Digging into the optimizer, it inlines the hanging multi, then runs the optimizer over it again, inlines it again, and
just keeps doing that, forever inlining and digging into it.

Here's the call graph for the infini-looping portion​:

visit_op
����$optype eq 'chain' -> 'call'
����self.visit_op_children($op)
� ����visit_children($op)
� ����self.visit_var($visit)
â�� â��  â��â��â��@​!block_var_stack[$top].add_usage($var);
� ����self.visit_var($visit)
â�� â��â��â��@​!block_var_stack[$top].add_usage($var);
�
����self.optimize_call($op)
  â��â��â��â��$!symbols.find_lexical($op.name)
  â�� â��â��â��â��self.force_value(%sym, $name, 1)
  â��
  â�� # inside if $dispatcher && $obj.onlystar {...}
  â��â��â��â��my @​ct_arg_info := self.analyze_args_for_ct_call($op)
  â��â��â��â��self.inline_call($op, $chosen)
  â��â��â��â��$!symbols.faking_top_routine
  â��â��â��â��self.visit_children($inlined) <-- optimizing newly inlined code
  â��â��â��â��self.visit_op($visit)
  â��â��â��â��self.visit_op_children($op) <-- we're back where we started

I also added a print statement to start of every method and sub in Optimizer.nqp and here's the repeating portion of the calls​:

CALLING -> in visit_op
CALLING -> in optimize
CALLING -> in is_outer_foldable
CALLING -> in visit_op_children
CALLING -> in visit_children
CALLING -> in visit_var
CALLING -> in add_usage
CALLING -> in visit_var
CALLING -> in add_usage
CALLING -> in optimize_call
CALLING -> in find_lexical
CALLING -> in force_value
CALLING -> in analyze_args_for_ct_call
CALLING -> in inline_call
CALLING -> in faking_top_routine
CALLING -> in visit_children
CALLING -> in visit_op
CALLING -> in optimize
CALLING -> in is_outer_foldable
CALLING -> in visit_op_children
CALLING -> in visit_children
CALLING -> in visit_op
CALLING -> in optimize
CALLING -> in is_outer_foldable
CALLING -> in visit_op_children
CALLING -> in visit_children
CALLING -> in visit_children
CALLING -> in visit_children

Cheers,
ZZ

@p6rt
Copy link
Author

p6rt commented Sep 18, 2016

The RT System itself - Status changed from 'new' to 'open'

@p6rt p6rt added the optimizer label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant