About using byte literals instead of int literals

I’m toying with using jasmin this year, writing my games in java assembler.
The common(?) advice to push two small values and multiply them instead of pushing a larger value seems to be false for most reasonable values (ie less than 2^15).

Say I need to push 640 and 480 for the size of my screen… Using just bytes, this doesn’t get much smaller than:

	bipush 10
	dup
	bipush 64
	imul                   
	bipush 48
	imul    

That’s 9 bytes in total.

However, pushing shorts makes it take up just 6 bytes:

	sipush 640
	sipush 480

And the java code for that is just

a = 640;
b = 480;

It works for all values between -32768 and 32767.

That entry in the wiki was created by me. (I assume you refer to that article)

I only used it when I had quite a few values, that could all roughly be divided by N, and then later be multiplied by N.

I recall that with 4 or 5 values, it could save 1 or 2 bytes in the compressed archive.

The effects of GZ are really unpredictable (doh), so even increasing your *.class a little, can gain you a few bytes in the *.jar

That’s bordering on “try some shit, it might get smaller”, and doesn’t really belong on the wiki. sipush doesn’t use the constant pool.

Wouldn’t that leave 10 & 30720 on the operand stack, rather than 640 & 480 ?


bipush 10
// stack-> 10
dup 
// stack-> 10, 10
bipush 64
// stack-> 10, 10, 64
imul
// stack-> 10, 640
bipush 48
// stack-> 10, 640, 48
imul
// stack-> 10, 30720

You’re still right though, regarding sipush being much more efficient in that case.

However, it does get a little more complex - as it depends what you are doing with the constants,
as to what datatype & associated push instruction javac will use in the generated bytecode.

Take this common call for example:


enableEvents(640);

javac will store 640 as a 8 byte value (because the parameter type is long) in the constants pool, and use ldc or ldc_w to retrieve it.
jasmin can help significiantly in this case:


sipush 640
i2l

Personally I don’t think the semantics of java bytecode realy allow for much optimisation beyond what is expressable in the java syntax.
Therefore I don’t think jasmin will be able to help very much - except for allowing you to hand-craft peep-hole optimisations.

But then, if you are only going to use it for making peep-hole optimisations, I think it’d be much more productive to automate them as part of the proguard optimisation/obfuscation step.
That way we can all benefit from them too ;D

:edit:

Thinking back, there was one other instance being able to mangle the bytecode helped me…
During the initialisation stage, I recall I was accessing the same object several times in succession - calling different methods on it. (Frame & GraphicsDevice instances I believe)
Anyhow, as the methods being invoked were void, I determined that it was safe to pull the Frame/GraphicsDevice reference onto the stack just once, and dup it for each future invocation that was going to be made.
This replaced a load of 2 byte ‘aload x’ instructions with 1 byte dup’s.

However, this again is a peep-hole optimisation that I believe could be added to proguard without too much trouble.

Oops, you’re right about that error, heh. Ok, 10 bytes then. :wink:

About size-optimisations, I’ve got a runnable applet with start() and stop() support, event handling and pixel rendering down to under 1kb using jasmin.

For example, the keyDown and keyUp events become

keyDown:
   iconst_1
   goto setKey

keyUp:
   iconst_0

setKey:
; Set keys[((KeyEvent)e).getKeyCode()] to the value on the stack

in java, you’d have to do something like:


boolean down = false;
[...]
    case KeyEvent.KEY_PRESSED:
        down = true;
    case KeyEvent.KEY_RELEASED:
        keys[((KeyEvent)e).getKeyCode()] = down;
        break;

… and that uses a lot more bytes as it stores down in a local variable.

Most of the fancy things I can do in jasmin instead of doing in java is stupid jsr/ret tricks, sinister gotos, and making assumptions about the stack that the compiler doesn’t seem to want to do.
For instance, if you’ve got a lot of this.foo(), this.that(), this.setWidth()-calls, you can load this once, then dup, then dup2 several times until you’ve got the right amount on the stack.

Unfortunately, the fact that the stack has to be identical on an op code regardless of how you get there means that I can’t use loops to process all data on the stack. That’s a shame. :wink:

Perhaps you could keep the game coded in both jasmin & java, so by the end we can see how many bytes you’ve gained!

Either way, Good luck ;D

p.s.

There have been a few times i’ve wondered how easy it would be to make a byte code transformation that changed non-recursive method invocations into jsr/ret pairs.

[quote]Unfortunately, the fact that the stack has to be identical on an op code regardless of how you get there means that I can’t use loops to process all data on the stack. That’s a shame.
[/quote]
Have you tried turning off the VMs bytecode verification (java -noverify )? ;D

The way I’m doing is actually to write the stuff I need first in java, disassembling it to jasmin code, then knitting that into my assembler.
Doing method calls manually from scratch in java bytecodes is just too time consuming.

-noverify, while interesting, might not be the best idea. :wink:
I’ve got a feeling the stack size restriction is there is so the jit can replace stack manipulation with hard coded memory offsets.