Yep, EObject has all the arithmetic operators.
EObject has lots of methods, and I have not worried about trying to
reduce them. EObject is an abstract class; and is not intended to be
subclassed outside of the erjang eco-system. I have an EObject
subclass called EPseudoTerm which is intended as an opaque reference
that you can subclass and can be passed around inside the Erjang
system.
dec(), inc() and is_zero() are special cases of arithmetics that are
there simply because they occur very often, especially in loops which
are typically written like thins in erlang programs:
do_something(0, OtherArgs, ...) -> done;
do_something(N, OtherArgs, ...) ->
do .. what ... ever ... with .. otherargs,
do_something(N-1, OtherArgs, ...).
dec(), inc() and is_zero() are implemented generically in EObject:
EObject dec() { return ESmall.MINUS_ONE.plus(this); } // (*note1)
EObject inc() { return ESmall.ONE.plus(this); }
boolean is_zero() { return false; }
and those three are then overridden in ESmall, as ...
EInteger dec() { return this.value == Integer.MIN_VALUE ?
EBig.MIN_INT_MINUS_ONE : new ESmall(this.value-1) ; }
EInteger inc() { return this.value == Integer.MAX_VALUE ?
EBig.MAX_INT_PLUS_ONE : new ESmall(this.value+1) ; }
boolean is_zero() { return this.value == 0; }
(*note1) The compiler will swap order of arguments to arithmetic
operators (if it can be done semantics-preserving) if the second has
static type, and the first has not; since binary operators dispatch
virtually on the first argument. So "X+1" becomes "1+X", "X-3"
becomes "-3 + X", etc.
Arithmetics is double dispatched, like this:
in ESmall:
ENumber minus(EObject other) {
return other.r_minus(this.value);
}
EInteger r_minus(int lhs) {
return EInteger.box((long)lhs - (long)this.value);
}
EDouble r_minus(double lhs) {
return EDouble.box(lhs - this.value);
}
EInteger r_minus(BigInteger lhs) {
return
EInteger.box(lhs.subtract(BigInteger.fromLong(this.value)));
}
... etc... and all those methods are declared in EObject ... most of
which throw "aritherr" in the generic implementation. But that is not
really an issue as far as I can see.
Kresten
On Jun 5, 6:27 pm, Charles Oliver Nutter <[email protected]> wrote:
> Thanks very much Kresten. That satisfies my curiousity, and it makes
> perfect sense that you'd get performance close to the JRuby dynopt
> version with the way this compiles. It makes me wish Ruby were as
> straightforward at times :)
>
> I also see something else I find interesting: EObject apparently
> implements dec() and is_ge()...I'm guessing you have implemented a
> full set of numeric operators on EObject so that when they're
> explicitly provided by a subtype it can be no more than an interface
> or abstract dispatch? We've thought of doing the same for JRuby, but
> were unsure whether it would be too cumbersome. The essential idea
> would be that the default implementations would proceed to doing a
> dynamic dispatch while the overridden versions would provide the
> implementation in-place, perhaps with a guard in case someone
> monkey-patched them. It would reduce most math operators against core
> types to static calls (+ a two-field object comparison for the guard)
> and greatly improve their inlinability.
>
> Is that what you've done and why you've done it for Erjang?
>
> On Sat, Jun 5, 2010 at 11:13 AM, Kresten Krab Thorup <[email protected]> wrote:
>
>
>
> > For reference, here's the bytecode for tak in Erjang [JAD'ed bytecode
> > even further below].
>
> > In the actual generated code, it moves things around quite a bit in
> > local variables, but that is because of the way I translate BEAM to
> > JVM by keeping a "stack" in the local variables. All function calls
> > then do aload_0, aload_1, ... aload_N before a function call with
> > N-1. I'm assuming that the JVM will see through all these register
> > moves and optimize them away.
>
> > tak is very easy to translate for Erjang, because a simple local
> > analysis says that it doesn't suspend. So because of that it
> > generates very straightforward code. This is often the case for
> > "leafs" in the call graph.
>
> > Except for all the extra moving things around in variables, this is
> > what it does. The only "issue" with this is that Erjang cannot
> > suspend the thread while running in this loop (i.e. Kilim is not
> > pushed on this code). The check_exit() at the end of the loop is
> > there to make sure I can kill the process. is_ge() and dec()
> > [decrement] are native method on EObject.
>
> > static EObject tak(EProc proc, EObject X, EObject Y, EObject Z)
> > {
> > do {
> > if (Y.is_ge(X)) { return Z; }
> > EObject X1 = tak(proc, X.dec(), Y, Z);
> > EObject Y1 = tak(proc, Y.dec(), Z, X);
> > EObject Z1 = tak(proc, Z.dec(), X, Y);
> > X = X1; Y = Y1; Z = Z1;
> > proc.check_exit();
> > } while (true);
> > }
>
> > Kresten
>
> > -----[actual generated code]----
> > Method name:"tak__3" public static Signature:
> > (erjang.EProc,erjang.EObject,erjang.EObject,erjang.EObject)erjang.EObject
> > Attribute "Code", length:170, max_stack:4, max_locals:9, code_length:
> > 138
> > 0: goto 3
> > 3: aload_2
> > 4: aload_1
> > 5: invokevirtual <Method erjang.EObject.is_ge
> > (erjang.EObject)boolean>
> > 8: ifeq 15
> > 11: aload_3
> > 12: astore_1
> > 13: aload_1
> > 14: areturn
> > 15: getstatic <Field erjang.ERT.NIL erjang.ENil>
> > 18: dup
> > 19: nop
> > 20: astore 5
> > 22: dup
> > 23: nop
> > 24: astore 6
> > 26: dup
> > 27: nop
> > 28: astore 7
> > 30: nop
> > 31: astore 8
> > 33: aload_1
> > 34: invokevirtual <Method erjang.EObject.dec ()erjang.EObject>
> > 37: astore 4
> > 39: aload_1
> > 40: astore 5
> > 42: aload 4
> > 44: astore_1
> > 45: aload_3
> > 46: astore 7
> > 48: aload_2
> > 49: astore 6
> > 51: aload_0
> > 52: aload_1
> > 53: aload_2
> > 54: aload_3
> > 55: invokestatic <Method erjang.m.bench_tak.bench_tak.tak__3
> > (erjang.EProc,erjang.EObject,erjang.EObject,erjang.EObject)erjang.EObject>
> > 58: astore_1
> > 59: aload 6
> > 61: invokevirtual <Method erjang.EObject.dec ()erjang.EObject>
> > 64: astore_2
> > 65: aload_1
> > 66: astore 8
> > 68: aload 5
> > 70: astore_3
> > 71: aload_2
> > 72: astore_1
> > 73: aload 7
> > 75: astore_2
> > 76: aload_0
> > 77: aload_1
> > 78: aload_2
> > 79: aload_3
> > 80: invokestatic <Method erjang.m.bench_tak.bench_tak.tak__3
> > (erjang.EProc,erjang.EObject,erjang.EObject,erjang.EObject)erjang.EObject>
> > 83: astore_1
> > 84: aload 7
> > 86: invokevirtual <Method erjang.EObject.dec ()erjang.EObject>
> > 89: astore_2
> > 90: aload_1
> > 91: astore 4 93: aload 6
> > 95: astore_3
> > 96: aload_2
> > 97: astore_1
> > 98: aload 5
> > 100: astore_2
> > 101: aload 4
> > 103: astore 5
> > 105: getstatic <Field erjang.ERT.NIL erjang.ENil>
> > 108: astore 7
> > 110: getstatic <Field erjang.ERT.NIL erjang.ENil>
> > 113: astore 6
> > 115: aload_0
> > 116: aload_1
> > 117: aload_2
> > 118: aload_3
> > 119: invokestatic <Method erjang.m.bench_tak.bench_tak.tak__3
> > (erjang.EProc,erjang.EObject,erjang.EObject,erjang.EObject)erjang.EObject>
> > 122: astore_1
> > 123: aload 5
> > 125: astore_2
> > 126: aload_1
> > 127: astore_3
> > 128: aload 8
> > 130: astore_1
> > 131: aload_0
> > 132: invokevirtual <Method erjang.EProc.check_exit ()void>
> > 135: goto 3
>
> > Here's the JAD'ed version of the same code.
>
> > public static EObject tak__3(EProc eproc, EObject eobject, EObject
> > eobject1, EObject eobject2)
> > {
> > do
> > {
> > if(eobject1.is_ge(eobject))
> > {
> > eobject = eobject2;
> > return eobject;
> > }
> > EObject obj;
> > EObject obj1;
> > EObject obj2;
> > EObject obj3 = obj2 = obj1 = obj = ERT.NIL;
> > EObject eobject3 = eobject.dec();
> > obj = eobject;
> > eobject = eobject3;
> > obj2 = eobject2;
> > obj1 = eobject1;
> > eobject = tak__3(eproc, eobject, eobject1, eobject2);
> > eobject1 = obj1.dec();
> > obj3 = eobject;
> > eobject2 = obj;
> > eobject = eobject1;
> > eobject1 = obj2;
> > eobject = tak__3(eproc, eobject, eobject1, eobject2);
> > eobject1 = obj2.dec();
> > eobject3 = eobject;
> > eobject2 = obj1;
> > eobject = eobject1;
> > eobject1 = obj;
> > obj = eobject3;
> > obj2 = ERT.NIL;
> > obj1 = ERT.NIL;
> > eobject = tak__3(eproc, eobject, eobject1, eobject2);
> > eobject1 = obj;
> > eobject2 = eobject;
> > eobject = obj3;
> > eproc.check_exit();
> > } while(true);
> > }
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "JVM Languages" group.
> > To post to this group, send email to [email protected].
> > To unsubscribe from this group, send email to
> > [email protected].
> > For more options, visit this group
> > athttp://groups.google.com/group/jvm-languages?hl=en.
--
You received this message because you are subscribed to the Google Groups "JVM
Languages" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/jvm-languages?hl=en.