Previously, defined? could result in many more method calls than the code it was checking. defined? a.b.c.d.e.f generated 15 calls, with a called 5 times, b called 4 times, etc.. This was due to the fact that defined works in a recursive manner, but it previously did not cache results. So for defined? a.b.c.d.e.f, the logic was similar to
In addition to eliminating redundant method calls for defined statements, this greatly simplifies the instruction sequences by eliminating duplication. Previously:
This fixes issues where for pathological small examples, Ruby would generate huge instruction sequences.
Unfortunately, implementing this support is kind of a hack. This adds another parameter to compile_call for whether we should assume the receiver is already present on the stack, and has defined? set that parameter for the specific case where it is compiling a method call where the receiver is also a method call.
defined_expr0 also takes an additional parameter for whether it should leave the results of the method call on the stack. If that argument is true, in the case where the method isn't defined, we jump to the pop before the leave, so the extra result is not left on the stack. This requires space for an additional label, so lfinish now needs to be able to hold 3 labels.
Make defined? cache the results of method calls
Previously, defined? could result in many more method calls than
the code it was checking.
defined? a.b.c.d.e.fgenerated 15 calls,with
acalled 5 times,bcalled 4 times, etc.. This was due tothe fact that defined works in a recursive manner, but it previously
did not cache results. So for
defined? a.b.c.d.e.f, the logic wassimilar to
With this change, the logic is similar to the following, without
the creation of a local variable:
In addition to eliminating redundant method calls for defined
statements, this greatly simplifies the instruction sequences by
eliminating duplication. Previously:
After change:
This fixes issues where for pathological small examples, Ruby would generate
huge instruction sequences.
Unfortunately, implementing this support is kind of a hack. This adds another
parameter to compile_call for whether we should assume the receiver is already
present on the stack, and has defined? set that parameter for the specific
case where it is compiling a method call where the receiver is also a method
call.
defined_expr0 also takes an additional parameter for whether it should leave
the results of the method call on the stack. If that argument is true, in
the case where the method isn't defined, we jump to the pop before the leave,
so the extra result is not left on the stack. This requires space for an
additional label, so lfinish now needs to be able to hold 3 labels.
Fixes [Bug #17649]
Fixes [Bug #13708]