I've completed most of the refactoring now and actually adding support for more complex cases is easier now. As well as adding support for new types such as Vector3 and Matrix33. I'm not sure if the current version will be the final design (I don't really know much about compilers actually and maybe I will learn something better

), but for now I'm happy with it.

I'm pretty sure it can now correctly optimize some complex expressions, and where it suspects the expression may contain mutable things, it will not reuse the cache variables.

This is a pretty contrived example, just to show off some things (length of mutable

v is re-computed as required, v1-(2.0,2.0) is cached, for v1.unit, only the length of v1 is cached)

Code: Select all

`--- ORIGINAL SOURCE ---`

def selectNormalize(v1: org.villane.vecmath.Vector2): org.villane.vecmath.Vector2 = {

var v: org.villane.vecmath.Vector2 = v1;

val b: org.villane.vecmath.Vector2 = v.unit;

val c: org.villane.vecmath.Vector2 = v.unit;

v = v1.-(new org.villane.vecmath.Vector2(2.0, 2.0)).unit;

v = v1.unit;

val d: org.villane.vecmath.Vector2 = v.unit;

d

}

--- OPTIMIZED SOURCE ---

def selectNormalize(v1: org.villane.vecmath.Vector2): org.villane.vecmath.Vector2 = {

var v: org.villane.vecmath.Vector2 = v1;

<synthetic> var v$length: Float = Preamble.sqrt(v.x.*(v.x).+(v.y.*(v.y)));

final <synthetic> val b$x: Float = v.x./(v$length);

final <synthetic> val b$y: Float = v.y./(v$length);

final <synthetic> val c$x: Float = v.x./(v$length);

final <synthetic> val c$y: Float = v.y./(v$length);

final <synthetic> val $anon$v0$x: Float = v1.x.-(2.0);

final <synthetic> val $anon$v0$y: Float = v1.y.-(2.0);

final <synthetic> val $anon$v0$length: Float = Preamble.sqrt($anon$v0$x.*($anon$v0$x).+($anon$v0$y.*($anon$v0$y)));

v = new org.villane.vecmath.Vector2($anon$v0$x./($anon$v0$length), $anon$v0$y./($anon$v0$length));

val $anon$length1: Float = Preamble.sqrt(v1.x.*(v1.x).+(v1.y.*(v1.y)));

v = new org.villane.vecmath.Vector2(v1.x./($anon$length1), v1.y./($anon$length1));

v$length = Preamble.sqrt(v.x.*(v.x).+(v.y.*(v.y)));

final <synthetic> val d$x: Float = v.x./(v$length);

final <synthetic> val d$y: Float = v.y./(v$length);

new org.villane.vecmath.Vector2(d$x, d$y)

}

And here it correctly does not reuse a cached expression:

Code: Select all

`def mutableExprLength(v1: org.villane.vecmath.Vector2, h: test.Test2.MutHolder): Float = {`

val n: Float = v1.-(h.v).length;

h.v_=(new org.villane.vecmath.Vector2(7.0, 7.0));

val m: Float = v1.-(h.v).length;

n.-(m)

}

--- OPTIMIZED SOURCE ---

def mutableExprLength(v1: org.villane.vecmath.Vector2, h: test.Test2.MutHolder): Float = {

final <synthetic> val $anon$v0$x: Float = v1.x.-(h.v.x);

final <synthetic> val $anon$v0$y: Float = v1.y.-(h.v.y);

final <synthetic> val $anon$v0$length: Float = Preamble.sqrt($anon$v0$x.*($anon$v0$x).+($anon$v0$y.*($anon$v0$y)));

val n: Float = $anon$v0$length;

h.v_=(new org.villane.vecmath.Vector2(7.0, 7.0));

final <synthetic> val $anon$v1$x: Float = v1.x.-(h.v.x);

final <synthetic> val $anon$v1$y: Float = v1.y.-(h.v.y);

final <synthetic> val $anon$v1$length: Float = Preamble.sqrt($anon$v1$x.*($anon$v1$x).+($anon$v1$y.*($anon$v1$y)));

val m: Float = $anon$v1$length;

n.-(m)

}

And later I might add some immutability analysis (although probably not unless Scala itself will support it) so if h.v would be immutable, the temporary variables can be reused.

Now I will finish the refactoring ( a few small things still to do ) and then try to apply this to a larger part of Box2D