mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-15 02:44:47 +01:00
implements lookupswitch
This commit is contained in:
parent
61c7f8edf4
commit
6c971c6525
@ -179,26 +179,50 @@ class BranchManger {
|
||||
* return b;
|
||||
* </pre>
|
||||
*
|
||||
* Should be converted to the follow Wasm code:
|
||||
* Should be converted to the follow Wasm code for tableswitch:
|
||||
*
|
||||
* <pre>
|
||||
block
|
||||
block
|
||||
block
|
||||
get_local 0
|
||||
i32.const 8
|
||||
i32.sub
|
||||
br_table 0 1
|
||||
end
|
||||
i32.const 1
|
||||
set_local 1
|
||||
br 1
|
||||
end
|
||||
i32.const 9
|
||||
set_local 1
|
||||
end
|
||||
get_local 1
|
||||
return
|
||||
* block
|
||||
* block
|
||||
* block
|
||||
* get_local 0
|
||||
* i32.const 8
|
||||
* i32.sub
|
||||
* br_table 0 1
|
||||
* end
|
||||
* i32.const 1
|
||||
* set_local 1
|
||||
* br 1
|
||||
* end
|
||||
* i32.const 9
|
||||
* set_local 1
|
||||
* end
|
||||
* get_local 1
|
||||
* 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>
|
||||
*
|
||||
* @param parent
|
||||
@ -212,80 +236,81 @@ class BranchManger {
|
||||
int startPosition = ((ParsedBlock)switchBlock).startPosition;
|
||||
int posCount = switchBlock.positions.length;
|
||||
boolean isTable = switchBlock.keys.length == 1;
|
||||
if( isTable ) {
|
||||
SwitchCase[] cases = new SwitchCase[posCount+1];
|
||||
|
||||
SwitchCase switchCase = cases[posCount] = new SwitchCase();
|
||||
switchCase.key = Integer.MAX_VALUE;
|
||||
switchCase.position = switchBlock.defaultPosition;
|
||||
for( int i = 0; i < switchBlock.positions.length; i++ ) {
|
||||
switchCase = cases[i] = new SwitchCase();
|
||||
switchCase.key = i;
|
||||
switchCase.position = switchBlock.positions[i];
|
||||
}
|
||||
|
||||
// calculate the block number for ever switch case depending its position order
|
||||
Arrays.sort( cases, (a, b) -> Integer.compare( a.position, b.position ) );
|
||||
int blockCount = 0;
|
||||
int lastPosition = -1;
|
||||
BranchNode brTableNode = null;
|
||||
BranchNode blockNode = null;
|
||||
for( int i = 0; i < cases.length; i++ ) {
|
||||
switchCase = cases[i];
|
||||
switchCase.block = blockCount;
|
||||
int currentPosition = switchCase.position;
|
||||
if( lastPosition != currentPosition ) {
|
||||
if( blockNode == null ) {
|
||||
blockNode = brTableNode = new BranchNode( currentPosition, currentPosition, WasmBlockOperator.BR_TABLE, null );
|
||||
}
|
||||
lastPosition = currentPosition;
|
||||
blockCount++;
|
||||
BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
||||
node.add( blockNode );
|
||||
blockNode = node;
|
||||
// create a helper structure
|
||||
SwitchCase[] cases = new SwitchCase[posCount + 1];
|
||||
SwitchCase switchCase = cases[posCount] = new SwitchCase();
|
||||
switchCase.key = Long.MAX_VALUE;
|
||||
switchCase.position = switchBlock.defaultPosition;
|
||||
for( int i = 0; i < switchBlock.positions.length; i++ ) {
|
||||
switchCase = cases[i] = new SwitchCase();
|
||||
switchCase.key = isTable ? i : switchBlock.keys[i];
|
||||
switchCase.position = switchBlock.positions[i];
|
||||
}
|
||||
|
||||
// calculate the block number for ever switch case depending its position order
|
||||
Arrays.sort( cases, ( a, b ) -> Integer.compare( a.position, b.position ) );
|
||||
int blockCount = 0;
|
||||
int lastPosition = -1;
|
||||
BranchNode brTableNode = null;
|
||||
BranchNode blockNode = null;
|
||||
for( int i = 0; i < cases.length; i++ ) {
|
||||
switchCase = cases[i];
|
||||
switchCase.block = blockCount;
|
||||
int currentPosition = switchCase.position;
|
||||
if( lastPosition != currentPosition ) {
|
||||
if( isTable && blockNode == null ) {
|
||||
blockNode = brTableNode = new BranchNode( currentPosition, currentPosition, WasmBlockOperator.BR_TABLE, null );
|
||||
}
|
||||
}
|
||||
|
||||
// handle the GOTO (breaks) at the end of the CASE blocks.
|
||||
blockCount = 0;
|
||||
BranchNode branch = blockNode;
|
||||
while( branch.size() > 0 ) {
|
||||
BranchNode node = branch.get( 0 );
|
||||
lastPosition = currentPosition;
|
||||
blockCount++;
|
||||
|
||||
for( int p = 0; p < parsedOperations.size(); p++ ) {
|
||||
ParsedBlock parsedBlock = parsedOperations.get( p );
|
||||
if( parsedBlock.startPosition < node.endPos ) {
|
||||
continue;
|
||||
}
|
||||
if( parsedBlock.startPosition < lastPosition ) {
|
||||
if( parsedBlock.endPosition >= lastPosition && parsedBlock.op == JavaBlockOperator.GOTO ) {
|
||||
parsedOperations.remove( p );
|
||||
lastPosition = parsedBlock.endPosition;
|
||||
branch.add( new BranchNode( parsedBlock.startPosition, parsedBlock.startPosition, WasmBlockOperator.BR, null, blockCount ) );
|
||||
p--;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
||||
if( blockNode != null ) {
|
||||
node.add( blockNode );
|
||||
}
|
||||
branch = node;
|
||||
blockNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the main block around the switch
|
||||
BranchNode switchNode = new BranchNode( startPosition, lastPosition , WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
||||
switchNode.add( blockNode );
|
||||
parent.add( switchNode );
|
||||
// handle the GOTO (breaks) at the end of the CASE blocks.
|
||||
blockCount = 0;
|
||||
BranchNode branch = blockNode;
|
||||
while( branch.size() > 0 ) {
|
||||
BranchNode node = branch.get( 0 );
|
||||
blockCount++;
|
||||
|
||||
// sort back in the natural order and create a br_table
|
||||
Arrays.sort( cases, (a, b) -> Integer.compare( a.key, b.key ) );
|
||||
int[] data = new int[cases.length];
|
||||
for( int i = 0; i < data.length; i++ ) {
|
||||
data[i] = cases[i].block;
|
||||
for( int p = 0; p < parsedOperations.size(); p++ ) {
|
||||
ParsedBlock parsedBlock = parsedOperations.get( p );
|
||||
if( parsedBlock.startPosition < node.endPos ) {
|
||||
continue;
|
||||
}
|
||||
if( parsedBlock.startPosition < lastPosition ) {
|
||||
if( parsedBlock.endPosition >= lastPosition && parsedBlock.op == JavaBlockOperator.GOTO ) {
|
||||
parsedOperations.remove( p );
|
||||
lastPosition = parsedBlock.endPosition;
|
||||
branch.add( new BranchNode( parsedBlock.startPosition, parsedBlock.startPosition, WasmBlockOperator.BR, null, blockCount ) );
|
||||
p--;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
branch = node;
|
||||
}
|
||||
|
||||
// Create the main block around the switch
|
||||
BranchNode switchNode = new BranchNode( startPosition, lastPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
|
||||
switchNode.add( blockNode );
|
||||
parent.add( switchNode );
|
||||
|
||||
// sort back in the natural order and create a br_table
|
||||
Arrays.sort( cases, ( a, b ) -> Long.compare( a.key, b.key ) );
|
||||
int[] data = new int[cases.length];
|
||||
for( int i = 0; i < data.length; i++ ) {
|
||||
data[i] = cases[i].block;
|
||||
}
|
||||
if( brTableNode != null ) {
|
||||
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
|
||||
*/
|
||||
private static class SwitchCase {
|
||||
int key;
|
||||
long key;
|
||||
int position;
|
||||
int block;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ package de.inetsoftware.jwebassembly.module;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
@ -32,7 +31,6 @@ import de.inetsoftware.classparser.Code;
|
||||
import de.inetsoftware.classparser.CodeInputStream;
|
||||
import de.inetsoftware.classparser.ConstantPool;
|
||||
import de.inetsoftware.classparser.ConstantRef;
|
||||
import de.inetsoftware.classparser.LocalVariableTable;
|
||||
import de.inetsoftware.classparser.MethodInfo;
|
||||
import de.inetsoftware.jwebassembly.WasmException;
|
||||
|
||||
@ -470,6 +468,7 @@ public abstract class ModuleWriter implements Closeable {
|
||||
int[] keys;
|
||||
int[] positions;
|
||||
if( op == 171 ) { // lookupswitch
|
||||
localVariables.useTempI32();
|
||||
int nPairs = byteCode.readInt();
|
||||
keys = new int[nPairs];
|
||||
positions = new int[nPairs];
|
||||
@ -553,78 +552,78 @@ public abstract class ModuleWriter implements Closeable {
|
||||
writeConst( constantPool.get( byteCode.readUnsignedShort() ) );
|
||||
break;
|
||||
case 21: // iload
|
||||
writeLoadStore( true, ValueType.i32, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( true, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
case 22: // lload
|
||||
writeLoadStore( true, ValueType.i64, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( true, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
case 23: // fload
|
||||
writeLoadStore( true, ValueType.f32, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( true, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
case 24: // dload
|
||||
writeLoadStore( true, ValueType.f64, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( true, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
//TODO case 25: // aload
|
||||
case 26: // iload_0
|
||||
case 27: // iload_1
|
||||
case 28: // iload_2
|
||||
case 29: // iload_3
|
||||
writeLoadStore( true, ValueType.i32, op - 26 );
|
||||
writeLoadStore( true, op - 26 );
|
||||
break;
|
||||
case 30: // lload_0
|
||||
case 31: // lload_1
|
||||
case 32: // lload_2
|
||||
case 33: // lload_3
|
||||
writeLoadStore( true, ValueType.i64, op - 30 );
|
||||
writeLoadStore( true, op - 30 );
|
||||
break;
|
||||
case 34: // fload_0
|
||||
case 35: // fload_1
|
||||
case 36: // fload_2
|
||||
case 37: // fload_3
|
||||
writeLoadStore( true, ValueType.f32, op - 34 );
|
||||
writeLoadStore( true, op - 34 );
|
||||
break;
|
||||
case 38: // dload_0
|
||||
case 39: // dload_1
|
||||
case 40: // dload_2
|
||||
case 41: // dload_3
|
||||
writeLoadStore( true, ValueType.f64, op - 38 );
|
||||
writeLoadStore( true, op - 38 );
|
||||
break;
|
||||
case 54: // istore
|
||||
writeLoadStore( false, ValueType.i32, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( false, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
case 55: // lstore
|
||||
writeLoadStore( false, ValueType.i64, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( false, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
case 56: // fstore
|
||||
writeLoadStore( false, ValueType.f32, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( false, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
case 57: // dstore
|
||||
writeLoadStore( false, ValueType.f64, byteCode.readUnsignedByte() );
|
||||
writeLoadStore( false, byteCode.readUnsignedByte() );
|
||||
break;
|
||||
//TODO case 58: // astore
|
||||
case 59: // istore_0
|
||||
case 60: // istore_1
|
||||
case 61: // istore_2
|
||||
case 62: // istore_3
|
||||
writeLoadStore( false, ValueType.i32, op - 59 );
|
||||
writeLoadStore( false, op - 59 );
|
||||
break;
|
||||
case 63: // lstore_0
|
||||
case 64: // lstore_1
|
||||
case 65: // lstore_2
|
||||
case 66: // lstore_3
|
||||
writeLoadStore( false, ValueType.i64, op - 63 );
|
||||
writeLoadStore( false, op - 63 );
|
||||
break;
|
||||
case 67: // fstore_0
|
||||
case 68: // fstore_1
|
||||
case 69: // fstore_2
|
||||
case 70: // fstore_3
|
||||
writeLoadStore( false, ValueType.f32, op - 67 );
|
||||
writeLoadStore( false, op - 67 );
|
||||
break;
|
||||
case 71: // dstore_0
|
||||
case 72: // dstore_1
|
||||
case 73: // dstore_2
|
||||
case 74: // dstore_3
|
||||
writeLoadStore( false, ValueType.f64, op - 71 );
|
||||
writeLoadStore( false, op - 71 );
|
||||
break;
|
||||
case 87: // pop
|
||||
case 88: // pop2
|
||||
@ -752,10 +751,10 @@ public abstract class ModuleWriter implements Closeable {
|
||||
break;
|
||||
case 132: // iinc
|
||||
int idx = byteCode.readUnsignedByte();
|
||||
writeLoadStore( true, ValueType.i32, idx );
|
||||
writeLoadStore( true, idx );
|
||||
writeConstInt( byteCode.readUnsignedByte() );
|
||||
writeNumericOperator( NumericOperator.add, ValueType.i32);
|
||||
writeLoadStore( false, ValueType.i32, idx );
|
||||
writeLoadStore( false, idx );
|
||||
break;
|
||||
case 133: // i2l
|
||||
writeCast( ValueTypeConvertion.i2l );
|
||||
@ -839,30 +838,58 @@ public abstract class ModuleWriter implements Closeable {
|
||||
}
|
||||
startPosition--;
|
||||
|
||||
byteCode.readInt();
|
||||
boolean writeFirstKey;
|
||||
int low = 0;
|
||||
int defaultPosition = byteCode.readInt();
|
||||
if( op == 171 ) { // lookupswitch
|
||||
int count = byteCode.readInt();
|
||||
writeFirstKey = count == 1;
|
||||
int[] keys = new int[count];
|
||||
int[] positions = new int[count];
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
int key = byteCode.readInt();
|
||||
if( i== 0 ) {
|
||||
low = key;
|
||||
}
|
||||
byteCode.readInt();
|
||||
keys[i] = byteCode.readInt();
|
||||
positions[i] = 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 {
|
||||
low = byteCode.readInt();
|
||||
writeFirstKey = true;
|
||||
int low = byteCode.readInt();
|
||||
int count = byteCode.readInt() - low + 1;
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
byteCode.readInt();
|
||||
}
|
||||
}
|
||||
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 );
|
||||
writeNumericOperator( NumericOperator.sub, ValueType.i32 );
|
||||
if( low != 0 ) { // the br_table starts ever with the value 0. That we need to subtract the start value if it different
|
||||
writeConstInt( low );
|
||||
writeNumericOperator( NumericOperator.sub, ValueType.i32 );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 172: // ireturn
|
||||
@ -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.
|
||||
* 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);
|
||||
writeConstInt( 0 );
|
||||
writeNumericOperator( numOp, ValueType.i32 );
|
||||
//writeBlockCode( BlockOperator.IF );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -977,8 +1015,6 @@ public abstract class ModuleWriter implements Closeable {
|
||||
*
|
||||
* @param load
|
||||
* true: if load
|
||||
* @param valueType
|
||||
* the type of the variable
|
||||
* @param idx
|
||||
* the memory/slot idx of the variable
|
||||
* @throws WasmException
|
||||
@ -986,7 +1022,7 @@ public abstract class ModuleWriter implements Closeable {
|
||||
* @throws IOException
|
||||
* 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
|
||||
if( load ) {
|
||||
writeLoad( idx );
|
||||
|
@ -101,10 +101,10 @@ public class ControlFlowOperators extends AbstractBaseTest {
|
||||
|
||||
@Export
|
||||
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;
|
||||
switch(a){
|
||||
case 10:
|
||||
@ -116,6 +116,24 @@ public class ControlFlowOperators extends AbstractBaseTest {
|
||||
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
|
||||
static int forLoop() {
|
||||
int a = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user