implements lookupswitch

This commit is contained in:
Volker Berlin 2018-05-11 22:07:22 +02:00
parent 61c7f8edf4
commit 6c971c6525
3 changed files with 204 additions and 125 deletions

View File

@ -179,26 +179,50 @@ class BranchManger {
* return b; * return b;
* </pre> * </pre>
* *
* Should be converted to the follow Wasm code: * Should be converted to the follow Wasm code for tableswitch:
* *
* <pre> * <pre>
block * block
block * block
block * block
get_local 0 * get_local 0
i32.const 8 * i32.const 8
i32.sub * i32.sub
br_table 0 1 * br_table 0 1
end * end
i32.const 1 * i32.const 1
set_local 1 * set_local 1
br 1 * br 1
end * end
i32.const 9 * i32.const 9
set_local 1 * set_local 1
end * end
get_local 1 * get_local 1
return * return
* </pre>
*
* and for lookupswitch
*
* <pre>
* block
* block
* block
* get_local 0
* tee_local $switch
* i32.const 8
* i32.eq
* br_if 0
* br 1
* end
* i32.const 1
* set_local 1
* br 1
* end
* i32.const 9
* set_local 1
* end
* get_local 1
* return
* </pre> * </pre>
* *
* @param parent * @param parent
@ -212,15 +236,15 @@ class BranchManger {
int startPosition = ((ParsedBlock)switchBlock).startPosition; int startPosition = ((ParsedBlock)switchBlock).startPosition;
int posCount = switchBlock.positions.length; int posCount = switchBlock.positions.length;
boolean isTable = switchBlock.keys.length == 1; boolean isTable = switchBlock.keys.length == 1;
if( isTable ) {
SwitchCase[] cases = new SwitchCase[posCount+1];
// create a helper structure
SwitchCase[] cases = new SwitchCase[posCount + 1];
SwitchCase switchCase = cases[posCount] = new SwitchCase(); SwitchCase switchCase = cases[posCount] = new SwitchCase();
switchCase.key = Integer.MAX_VALUE; switchCase.key = Long.MAX_VALUE;
switchCase.position = switchBlock.defaultPosition; switchCase.position = switchBlock.defaultPosition;
for( int i = 0; i < switchBlock.positions.length; i++ ) { for( int i = 0; i < switchBlock.positions.length; i++ ) {
switchCase = cases[i] = new SwitchCase(); switchCase = cases[i] = new SwitchCase();
switchCase.key = i; switchCase.key = isTable ? i : switchBlock.keys[i];
switchCase.position = switchBlock.positions[i]; switchCase.position = switchBlock.positions[i];
} }
@ -235,13 +259,15 @@ class BranchManger {
switchCase.block = blockCount; switchCase.block = blockCount;
int currentPosition = switchCase.position; int currentPosition = switchCase.position;
if( lastPosition != currentPosition ) { if( lastPosition != currentPosition ) {
if( blockNode == null ) { if( isTable && blockNode == null ) {
blockNode = brTableNode = new BranchNode( currentPosition, currentPosition, WasmBlockOperator.BR_TABLE, null ); blockNode = brTableNode = new BranchNode( currentPosition, currentPosition, WasmBlockOperator.BR_TABLE, null );
} }
lastPosition = currentPosition; lastPosition = currentPosition;
blockCount++; blockCount++;
BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
if( blockNode != null ) {
node.add( blockNode ); node.add( blockNode );
}
blockNode = node; blockNode = node;
} }
} }
@ -278,14 +304,13 @@ class BranchManger {
parent.add( switchNode ); parent.add( switchNode );
// sort back in the natural order and create a br_table // sort back in the natural order and create a br_table
Arrays.sort( cases, (a, b) -> Integer.compare( a.key, b.key ) ); Arrays.sort( cases, ( a, b ) -> Long.compare( a.key, b.key ) );
int[] data = new int[cases.length]; int[] data = new int[cases.length];
for( int i = 0; i < data.length; i++ ) { for( int i = 0; i < data.length; i++ ) {
data[i] = cases[i].block; data[i] = cases[i].block;
} }
if( brTableNode != null ) {
brTableNode.data = data; brTableNode.data = data;
} else {
throw new WasmException( "Unsupported switch with lookup", null, ((ParsedBlock)switchBlock).lineNumber );
} }
} }
@ -293,7 +318,7 @@ class BranchManger {
* Helper structure for caculateSwitch * Helper structure for caculateSwitch
*/ */
private static class SwitchCase { private static class SwitchCase {
int key; long key;
int position; int position;
int block; int block;
} }

View File

@ -17,7 +17,6 @@ package de.inetsoftware.jwebassembly.module;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -32,7 +31,6 @@ import de.inetsoftware.classparser.Code;
import de.inetsoftware.classparser.CodeInputStream; import de.inetsoftware.classparser.CodeInputStream;
import de.inetsoftware.classparser.ConstantPool; import de.inetsoftware.classparser.ConstantPool;
import de.inetsoftware.classparser.ConstantRef; import de.inetsoftware.classparser.ConstantRef;
import de.inetsoftware.classparser.LocalVariableTable;
import de.inetsoftware.classparser.MethodInfo; import de.inetsoftware.classparser.MethodInfo;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.WasmException;
@ -470,6 +468,7 @@ public abstract class ModuleWriter implements Closeable {
int[] keys; int[] keys;
int[] positions; int[] positions;
if( op == 171 ) { // lookupswitch if( op == 171 ) { // lookupswitch
localVariables.useTempI32();
int nPairs = byteCode.readInt(); int nPairs = byteCode.readInt();
keys = new int[nPairs]; keys = new int[nPairs];
positions = new int[nPairs]; positions = new int[nPairs];
@ -553,78 +552,78 @@ public abstract class ModuleWriter implements Closeable {
writeConst( constantPool.get( byteCode.readUnsignedShort() ) ); writeConst( constantPool.get( byteCode.readUnsignedShort() ) );
break; break;
case 21: // iload case 21: // iload
writeLoadStore( true, ValueType.i32, byteCode.readUnsignedByte() ); writeLoadStore( true, byteCode.readUnsignedByte() );
break; break;
case 22: // lload case 22: // lload
writeLoadStore( true, ValueType.i64, byteCode.readUnsignedByte() ); writeLoadStore( true, byteCode.readUnsignedByte() );
break; break;
case 23: // fload case 23: // fload
writeLoadStore( true, ValueType.f32, byteCode.readUnsignedByte() ); writeLoadStore( true, byteCode.readUnsignedByte() );
break; break;
case 24: // dload case 24: // dload
writeLoadStore( true, ValueType.f64, byteCode.readUnsignedByte() ); writeLoadStore( true, byteCode.readUnsignedByte() );
break; break;
//TODO case 25: // aload //TODO case 25: // aload
case 26: // iload_0 case 26: // iload_0
case 27: // iload_1 case 27: // iload_1
case 28: // iload_2 case 28: // iload_2
case 29: // iload_3 case 29: // iload_3
writeLoadStore( true, ValueType.i32, op - 26 ); writeLoadStore( true, op - 26 );
break; break;
case 30: // lload_0 case 30: // lload_0
case 31: // lload_1 case 31: // lload_1
case 32: // lload_2 case 32: // lload_2
case 33: // lload_3 case 33: // lload_3
writeLoadStore( true, ValueType.i64, op - 30 ); writeLoadStore( true, op - 30 );
break; break;
case 34: // fload_0 case 34: // fload_0
case 35: // fload_1 case 35: // fload_1
case 36: // fload_2 case 36: // fload_2
case 37: // fload_3 case 37: // fload_3
writeLoadStore( true, ValueType.f32, op - 34 ); writeLoadStore( true, op - 34 );
break; break;
case 38: // dload_0 case 38: // dload_0
case 39: // dload_1 case 39: // dload_1
case 40: // dload_2 case 40: // dload_2
case 41: // dload_3 case 41: // dload_3
writeLoadStore( true, ValueType.f64, op - 38 ); writeLoadStore( true, op - 38 );
break; break;
case 54: // istore case 54: // istore
writeLoadStore( false, ValueType.i32, byteCode.readUnsignedByte() ); writeLoadStore( false, byteCode.readUnsignedByte() );
break; break;
case 55: // lstore case 55: // lstore
writeLoadStore( false, ValueType.i64, byteCode.readUnsignedByte() ); writeLoadStore( false, byteCode.readUnsignedByte() );
break; break;
case 56: // fstore case 56: // fstore
writeLoadStore( false, ValueType.f32, byteCode.readUnsignedByte() ); writeLoadStore( false, byteCode.readUnsignedByte() );
break; break;
case 57: // dstore case 57: // dstore
writeLoadStore( false, ValueType.f64, byteCode.readUnsignedByte() ); writeLoadStore( false, byteCode.readUnsignedByte() );
break; break;
//TODO case 58: // astore //TODO case 58: // astore
case 59: // istore_0 case 59: // istore_0
case 60: // istore_1 case 60: // istore_1
case 61: // istore_2 case 61: // istore_2
case 62: // istore_3 case 62: // istore_3
writeLoadStore( false, ValueType.i32, op - 59 ); writeLoadStore( false, op - 59 );
break; break;
case 63: // lstore_0 case 63: // lstore_0
case 64: // lstore_1 case 64: // lstore_1
case 65: // lstore_2 case 65: // lstore_2
case 66: // lstore_3 case 66: // lstore_3
writeLoadStore( false, ValueType.i64, op - 63 ); writeLoadStore( false, op - 63 );
break; break;
case 67: // fstore_0 case 67: // fstore_0
case 68: // fstore_1 case 68: // fstore_1
case 69: // fstore_2 case 69: // fstore_2
case 70: // fstore_3 case 70: // fstore_3
writeLoadStore( false, ValueType.f32, op - 67 ); writeLoadStore( false, op - 67 );
break; break;
case 71: // dstore_0 case 71: // dstore_0
case 72: // dstore_1 case 72: // dstore_1
case 73: // dstore_2 case 73: // dstore_2
case 74: // dstore_3 case 74: // dstore_3
writeLoadStore( false, ValueType.f64, op - 71 ); writeLoadStore( false, op - 71 );
break; break;
case 87: // pop case 87: // pop
case 88: // pop2 case 88: // pop2
@ -752,10 +751,10 @@ public abstract class ModuleWriter implements Closeable {
break; break;
case 132: // iinc case 132: // iinc
int idx = byteCode.readUnsignedByte(); int idx = byteCode.readUnsignedByte();
writeLoadStore( true, ValueType.i32, idx ); writeLoadStore( true, idx );
writeConstInt( byteCode.readUnsignedByte() ); writeConstInt( byteCode.readUnsignedByte() );
writeNumericOperator( NumericOperator.add, ValueType.i32); writeNumericOperator( NumericOperator.add, ValueType.i32);
writeLoadStore( false, ValueType.i32, idx ); writeLoadStore( false, idx );
break; break;
case 133: // i2l case 133: // i2l
writeCast( ValueTypeConvertion.i2l ); writeCast( ValueTypeConvertion.i2l );
@ -839,31 +838,59 @@ public abstract class ModuleWriter implements Closeable {
} }
startPosition--; startPosition--;
byteCode.readInt(); int defaultPosition = byteCode.readInt();
boolean writeFirstKey;
int low = 0;
if( op == 171 ) { // lookupswitch if( op == 171 ) { // lookupswitch
int count = byteCode.readInt(); int count = byteCode.readInt();
writeFirstKey = count == 1; int[] keys = new int[count];
int[] positions = new int[count];
for( int i = 0; i < count; i++ ) { for( int i = 0; i < count; i++ ) {
int key = byteCode.readInt(); keys[i] = byteCode.readInt();
if( i== 0 ) { positions[i] = byteCode.readInt();
low = key;
} }
byteCode.readInt(); int tempI32 = localVariables.getTempI32();
int block = 0;
int defaultBlock = -1;
int currentPos = -1;
writeLoadStore( false, tempI32 );
do {
int nextPos = findNext( currentPos, positions );
if( nextPos == currentPos ) {
break;
} }
currentPos = nextPos;
if( defaultBlock < 0 ) {
if( defaultPosition <= currentPos ) {
defaultBlock = block;
if( defaultPosition < currentPos ) {
block++;
}
}
}
for( int i = 0; i < positions.length; i++ ) {
if( positions[i] == currentPos ) {
writeLoadStore( true, tempI32 );
writeConstInt( keys[i] );
writeNumericOperator( NumericOperator.eq, ValueType.i32 );
writeBlockCode( WasmBlockOperator.BR_IF, block );
}
}
block++;
} while( true );
if( defaultBlock < 0) {
defaultBlock = block;
}
writeBlockCode( WasmBlockOperator.BR, defaultBlock );
} else { } else {
low = byteCode.readInt(); int low = byteCode.readInt();
writeFirstKey = true;
int count = byteCode.readInt() - low + 1; int count = byteCode.readInt() - low + 1;
for( int i = 0; i < count; i++ ) { for( int i = 0; i < count; i++ ) {
byteCode.readInt(); byteCode.readInt();
} }
} if( low != 0 ) { // the br_table starts ever with the value 0. That we need to subtract the start value if it different
if( writeFirstKey && low != 0 ) { // the br_table starts ever with the value 0. That we need to subtract the start value if it different
writeConstInt( low ); writeConstInt( low );
writeNumericOperator( NumericOperator.sub, ValueType.i32 ); writeNumericOperator( NumericOperator.sub, ValueType.i32 );
} }
}
break; break;
case 172: // ireturn case 172: // ireturn
case 173: // lreturn case 173: // lreturn
@ -888,6 +915,18 @@ public abstract class ModuleWriter implements Closeable {
} }
} }
private static int findNext( int current, int[] values ) {
boolean find = false;
int next = Integer.MAX_VALUE;
for( int val : values ) {
if( val > current && val <= next ) {
next = val;
find = true;
}
}
return find ? next : current;
}
/** /**
* Handle the if<condition> of the Java byte code. This Java instruction compare the first stack value with value 0. * Handle the if<condition> of the Java byte code. This Java instruction compare the first stack value with value 0.
* Important: In Java the condition for the jump to the else block is saved. In WebAssembler we need to use * Important: In Java the condition for the jump to the else block is saved. In WebAssembler we need to use
@ -904,7 +943,6 @@ public abstract class ModuleWriter implements Closeable {
byteCode.skip(2); byteCode.skip(2);
writeConstInt( 0 ); writeConstInt( 0 );
writeNumericOperator( numOp, ValueType.i32 ); writeNumericOperator( numOp, ValueType.i32 );
//writeBlockCode( BlockOperator.IF );
} }
/** /**
@ -977,8 +1015,6 @@ public abstract class ModuleWriter implements Closeable {
* *
* @param load * @param load
* true: if load * true: if load
* @param valueType
* the type of the variable
* @param idx * @param idx
* the memory/slot idx of the variable * the memory/slot idx of the variable
* @throws WasmException * @throws WasmException
@ -986,7 +1022,7 @@ public abstract class ModuleWriter implements Closeable {
* @throws IOException * @throws IOException
* if any I/O error occur * if any I/O error occur
*/ */
private void writeLoadStore( boolean load, @Nonnull ValueType valueType, @Nonnegative int idx ) throws WasmException, IOException { private void writeLoadStore( boolean load, @Nonnegative int idx ) throws WasmException, IOException {
idx = localVariables.get( idx ); // translate slot index to position index idx = localVariables.get( idx ); // translate slot index to position index
if( load ) { if( load ) {
writeLoad( idx ); writeLoad( idx );

View File

@ -101,10 +101,10 @@ public class ControlFlowOperators extends AbstractBaseTest {
@Export @Export
static int switchDirect() { static int switchDirect() {
return (switchDirect(10) * 10) + switchDirect( 9 ); return tableSwitch(10) + (tableSwitch( 9 ) * 10) + (lookupSwitch(Integer.MAX_VALUE) * 100) + (lookupSwitch(0) * 1000);
} }
private static int switchDirect( int a ) { private static int tableSwitch( int a ) {
int b; int b;
switch(a){ switch(a){
case 10: case 10:
@ -116,6 +116,24 @@ public class ControlFlowOperators extends AbstractBaseTest {
return b; return b;
} }
private static int lookupSwitch( int a ) {
int b;
switch(a){
case 1:
b = 1;
break;
case 1000:
b = 2;
break;
case Integer.MAX_VALUE:
b = 3;
break;
default:
b = 9;
}
return b;
}
@Export @Export
static int forLoop() { static int forLoop() {
int a = 0; int a = 0;