If you remember back when I did mixins, I made a hack at the compiler level to insert implicit casts to sys::Obj when I knew the actual type was a mixin, and the expected type was Obj. This was necessary because from the Java VM perspective during verification a mixin is an interface type which is not an Obj type. However we want all types to have the functionality of Obj - for example the ability to call type() or toStr() on a mixin.
Well I ran into some trouble with this approach as I starting doing more with mixins. I decided that having the compiler generate casts as a hack wasn't the best strategy because it pushed too much target knowledge into the fcode - the fcode wasn't as pure as it should be. So instead I looked at solutions to fix the problem within the Java runtime so that the compiler wouldn't have to insert casts. The solution I settled on was that the Java runtime will use java.lang.Object in all type signatures where Fan uses sys::Obj. This means mixin interface types pass thru without casts. If you look at my Java sys code you will see that my parameters are Object, and I manually cast to Obj if I need to call an Obj method. But I also got rid of a ton of casts in places where I was mapping to Java such as reflection, lists, and maps. So I think the performance hit is probably a wash.
But just like I had to manually cast Objects to Obj if I wanted to call a method like Obj.toStr, we have to solve that problem in emitted code too. Except it isn't so easy because by the time I see I'm invoking a virtual Obj method the stack has already been written. So what I do is reroute to a static method on Obj with an implicit this parameter typed as Object - then I can cast to Obj and call the virtual method. That works because the stack for the static is setup just like a virtual method on Obj would be. It's a performance hit, but I think any solution has a performance hit. One nice thing is that you often don't call a virtual method directly such as Obj.toStr, but rather you call it on a specific type such as Int.toStr - in which case we don't take this hit.
Another side effect of this change is that I couldn't implement equals() this way because now my parameter is Object, but my return type is Bool. So sys::Obj.equals is now sys::Obj.equal (no s).
Just for the record the other solution I debated was making Obj an interface, with the concrete class ObjFan just being used for the extends clause. The problem with this approach is that virtual Obj methods now become interface dispatch methods. Interface dispatch seems significantly slower in Java than a cast and virtual dispatch. Not to mention that I still end up with lots of casts inside the Java sys code.
brianSun 18 Jun 2006
Actually after sleeping on it I decided to pursue the alternate approach. I completely implemented it by making Obj an interface and having a FanObj that everything extends. Virtual calls to sys::Obj are caught and emitted as interface invokes. Static calls to sys::Obj are caught and routed to FanObj.
I ran some performance tests against the code base we have (the test suite and the tokenizer). The difference in performance was nebulous between Obj as an interface using interface dispatch versus making Obj signatures use java.lang.Object and using static/cast invokes.
Given that the full implementation doesn't show the same performance issues that my small tests show, I decided to go with the Obj as an interface design. The code changes are much smaller, and from a developers point of view keeps the code cleaner (especially when we get into Java native code).
This change means I also reverted Obj.equal back to Obj.equals.
brian Sat 17 Jun 2006
If you remember back when I did mixins, I made a hack at the compiler level to insert implicit casts to sys::Obj when I knew the actual type was a mixin, and the expected type was Obj. This was necessary because from the Java VM perspective during verification a mixin is an interface type which is not an Obj type. However we want all types to have the functionality of Obj - for example the ability to call type() or toStr() on a mixin.
Well I ran into some trouble with this approach as I starting doing more with mixins. I decided that having the compiler generate casts as a hack wasn't the best strategy because it pushed too much target knowledge into the fcode - the fcode wasn't as pure as it should be. So instead I looked at solutions to fix the problem within the Java runtime so that the compiler wouldn't have to insert casts. The solution I settled on was that the Java runtime will use java.lang.Object in all type signatures where Fan uses sys::Obj. This means mixin interface types pass thru without casts. If you look at my Java sys code you will see that my parameters are Object, and I manually cast to Obj if I need to call an Obj method. But I also got rid of a ton of casts in places where I was mapping to Java such as reflection, lists, and maps. So I think the performance hit is probably a wash.
But just like I had to manually cast Objects to Obj if I wanted to call a method like Obj.toStr, we have to solve that problem in emitted code too. Except it isn't so easy because by the time I see I'm invoking a virtual Obj method the stack has already been written. So what I do is reroute to a static method on Obj with an implicit this parameter typed as Object - then I can cast to Obj and call the virtual method. That works because the stack for the static is setup just like a virtual method on Obj would be. It's a performance hit, but I think any solution has a performance hit. One nice thing is that you often don't call a virtual method directly such as Obj.toStr, but rather you call it on a specific type such as Int.toStr - in which case we don't take this hit.
Another side effect of this change is that I couldn't implement equals() this way because now my parameter is Object, but my return type is Bool. So sys::Obj.equals is now sys::Obj.equal (no
s
).Just for the record the other solution I debated was making Obj an interface, with the concrete class ObjFan just being used for the extends clause. The problem with this approach is that virtual Obj methods now become interface dispatch methods. Interface dispatch seems significantly slower in Java than a cast and virtual dispatch. Not to mention that I still end up with lots of casts inside the Java sys code.
brian Sun 18 Jun 2006
Actually after sleeping on it I decided to pursue the alternate approach. I completely implemented it by making Obj an interface and having a FanObj that everything extends. Virtual calls to sys::Obj are caught and emitted as interface invokes. Static calls to sys::Obj are caught and routed to FanObj.
I ran some performance tests against the code base we have (the test suite and the tokenizer). The difference in performance was nebulous between Obj as an interface using interface dispatch versus making Obj signatures use java.lang.Object and using static/cast invokes.
Given that the full implementation doesn't show the same performance issues that my small tests show, I decided to go with the Obj as an interface design. The code changes are much smaller, and from a developers point of view keeps the code cleaner (especially when we get into Java native code).
This change means I also reverted Obj.equal back to Obj.equals.