I have been working on and off on this project and I think I am getting to the point where I might have a prototype that actually does a passable job of running lpc. I am doing this for the dgd driver as a module. My question however is this and has to do with certain peculiarities of the lpc language. These might be true for other languages as well. In (dgd) lpc both the new function(which instantiates) and the call semantics can be replaced. The question for me is this. if you are doing replacement like this to what extent can you deduce (assuming that the overridden new calls the basic new) the return value of the overridden new function? Similarly for call can one statically determine to some extent what is being called when you see a.b() (or in lpc a->b() ).
If I could resolve this particular problem I suspect that I could do interprocedural analysis on lpc and get quite substantial speedup. If anyone has any thoughts I would appreciate it.
The question for me is this. if you are doing replacement like this to what extent can you deduce (assuming that the overridden new calls the basic new) the return value of the overridden new function?
Well, without knowing the defined semantics of LPC, it would seem to me that if something is overridden its return value can be many things.
Similarly for call can one statically determine to some extent what is being called when you see a.b() (or in lpc a->b() ).
Isn't the whole point of run-time dynamic typing that you can't determine statically what exactly is being called?
One of the oddities (IMHO) of at least the FluffOS version of LPC is that types tend to stick to the data itself, rather than the variables. Where this stands out as particularly weird is type casting.
In C, one expects a type cast to convert values. So, int a = (int) 3.4; works, and puts a 3 in a. However, in LPC, the cast really tells the compiler to "trust me". Thus, int a = (int) 3.4; in LPC puts the value 3.4 into a. The variable a effectively becomes a float, because the value was a float and the "cast" told the compiler to ignore the mismatch.
Because of this kind of behavior, at compile time you often can't even trust static typed variables, because the value attached to them may not match the type. Effectively, you MUST examine the data to be 100% sure what type it really is.
My particular thoughts on this is that new has a certain expectation in terms of semantics as does call. If you override it you will most likely replace it with something similar with added features- (such as redirection in case an object is shadowed- which is sort of a decorator without writing forward functions) or with new perhaps doing some additional work before return the value of the actual new call.
My thoughts on this are is it possible to ascertain information flow like this in some limited manner. Right now for the JIT I am just doing something very rudimentary but I am (fingers crossed) hoping it can be extended in this way so I can maximize the optimization I get out of it. Obviously finishing it first (going to do more work this weekend) is perhaps of higher priority).