TAIL_MARKER==null does not work with guard functions #31

eriksoe opened this Issue Oct 19, 2010 · 7 comments


None yet
2 participants

eriksoe commented Oct 19, 2010

This is primarily a problem in connection with the interpreter; the compiler calls the guard functions directly without wrapping them in EFun's.

Option 1: Introduce opcodes for all guard functions (and probably other builtins as well).
Option 2: Make a special, non-tail-optimizing version of generated EFun's.
Temporary workaround: Have TAIL_MARKER = new ETailMarker().


krestenkrab commented Oct 28, 2010

I don't understand why this does not work. Perhaps you can explain more?


eriksoe commented Jan 17, 2014

So anyway...

  • at present, booting with the interpreter ("+i") doesn't work.

The symptoms are warnings like
WARNING: DB| Error when interpreting prim_inet@8182; opcode=K; prefetched1=set; prefetched2=null : java.lang.NullPointerException
and, more to the point, processes crashing like this:

SEVERE: [java] exiting <0.0.48> with: 
        at erjang.m.erlang.ErlBif.tuple_size_guard(ErlBif.java)
        at erjang.m.erlang.ErlBif$FN_tuple_size^A__1.go(Unknown Source)
        at erjang.EFun1.invoke(Unknown Source)
        at erjang.EFun1.invoke(Unknown Source)
        at erjang.beam.interpreter.Interpreter$Module$Function.interpret(Interpreter.java:3747)
  • This problem entirely goes away with the simple change "TAIL_MARKER = new ETailMarker()" instead of null.
    That is: With this simple change, "erj +i" boots without any errors!
  • Analysis of the cause follows, as soon as I've reconstructed it... :-)

krestenkrab commented Jan 17, 2014

OK, I also don't think there was any noticeable performance difference between the two. But it still annoys me if we don't understand why one fails and the other one doesn't. Somewhere the null must be leaking out of a tail call invocation.


eriksoe commented Jan 17, 2014

It has something to do with null being used for meaning both "guard failed" and "tail call", of course.
The interpreter treats regular BIF and guard-BIF calls the same except for this:
if (IS_GUARD(bif) && tmp==null) GOTO(onFail)

Hang on... IS_GUARD(bif) is expanded to "true" in both the guard and the non-guard case; that may be a clue...


eriksoe commented Jan 17, 2014

Fixed IS_GUARD expansion error. That wasn't it though.


eriksoe commented Jan 17, 2014

It turns out to be quite simple...:

  • A guard function returns null to signal guard rejection.
  • Its wrapper interprets this as a tail-call signal
  • The process continues at whatever is registered in the tail call section of its data structure
  • Confusion ensues.

The solution is to disable the tail-call loop (presently inherited from EFun.invoke) for guard functions.
I'll see if I can do this.


eriksoe commented Jan 20, 2014

As a side effect, we now have doc/RuntimeClassHierarchy.md which describes most of the EFun class hierarchy and methods.

@eriksoe eriksoe closed this Jan 20, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment