Use a BLOCK with input parameters for SWITCHES so that the start position of the SWITCH value does not have to be determined. #43

This commit is contained in:
Volker Berlin 2022-06-05 23:04:12 +02:00
parent 3d5543fa38
commit c392afc76f
No known key found for this signature in database
GPG Key ID: 988423EF815BE4CB
6 changed files with 75 additions and 54 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 - 2021 Volker Berlin (i-net software) * Copyright 2017 - 2022 Volker Berlin (i-net software)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -84,12 +84,8 @@ class WasmOutputStream extends LittleEndianOutputStream {
* if an I/O error occurs. * if an I/O error occurs.
*/ */
public void writeValueType( AnyType type ) throws IOException { public void writeValueType( AnyType type ) throws IOException {
if( !type.isRefType() && !options.useGC() ) { if( !options.useGC() && type == ValueType.eqref ) {
switch( (ValueType)type ) { type = ValueType.externref;
case eqref:
type = ValueType.externref;
break;
}
} }
writeVarint( type.getCode() ); writeVarint( type.getCode() );
} }

View File

@ -32,6 +32,7 @@ import de.inetsoftware.classparser.CodeInputStream;
import de.inetsoftware.classparser.ConstantClass; import de.inetsoftware.classparser.ConstantClass;
import de.inetsoftware.classparser.TryCatchFinally; import de.inetsoftware.classparser.TryCatchFinally;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type; import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.AnyType;
@ -64,6 +65,7 @@ class BranchManager {
private final ArrayList<BreakBlock> breakOperations = new ArrayList<>(); private final ArrayList<BreakBlock> breakOperations = new ArrayList<>();
private BlockType switchType;
/** /**
* Create a branch manager. * Create a branch manager.
@ -534,7 +536,7 @@ class BranchManager {
// find the code position where the condition values are push on the stack // find the code position where the condition values are push on the stack
List<WasmInstruction> instructions = this.instructions; List<WasmInstruction> instructions = this.instructions;
int idx = instructions.indexOf( startBlock.instr ); int idx = instructions.indexOf( startBlock.instr );
startPos = WasmCodeBuilder.findBlockStart( 1, true, instructions, idx + 1 ); startPos = WasmCodeBuilder.findBlockStart( 1, instructions, idx + 1 );
if( parent.overlapped( startPos ) ) { if( parent.overlapped( startPos ) ) {
branch = addMiddleNode( parent, parent.startPos, endPos ); branch = addMiddleNode( parent, parent.startPos, endPos );
} else { } else {
@ -853,6 +855,10 @@ class BranchManager {
* the not consumed operations in the parent branch * the not consumed operations in the parent branch
*/ */
private void calculateSwitch( BranchNode parent, SwitchParsedBlock switchBlock, List<ParsedBlock> parsedOperations ) { private void calculateSwitch( BranchNode parent, SwitchParsedBlock switchBlock, List<ParsedBlock> parsedOperations ) {
BlockType switchType = this.switchType;
if( switchType == null ) {
this.switchType = switchType = options.types.blockType( Arrays.asList( ValueType.i32 ), Collections.emptyList() );
}
int startPosition = ((ParsedBlock)switchBlock).startPosition; int startPosition = ((ParsedBlock)switchBlock).startPosition;
int posCount = switchBlock.positions.length; int posCount = switchBlock.positions.length;
boolean isTable = switchBlock.keys == null; boolean isTable = switchBlock.keys == null;
@ -883,7 +889,7 @@ class BranchManager {
} }
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, switchType );
if( blockNode != null ) { if( blockNode != null ) {
node.add( blockNode ); node.add( blockNode );
} }
@ -914,7 +920,7 @@ class BranchManager {
while( parentNode != null && end > parentNode.endPos ) { while( parentNode != null && end > parentNode.endPos ) {
parentNode = parentNode.parent; parentNode = parentNode.parent;
} }
BranchNode middleNode = new BranchNode( startPosition, end, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); BranchNode middleNode = new BranchNode( startPosition, end, WasmBlockOperator.BLOCK, WasmBlockOperator.END, switchType );
if( parentNode != null ) { if( parentNode != null ) {
BranchNode child = parentNode.remove( 0 ); BranchNode child = parentNode.remove( 0 );
parentNode.add( middleNode ); parentNode.add( middleNode );
@ -980,7 +986,7 @@ class BranchManager {
} }
// Create the main block around the switch // Create the main block around the switch
BranchNode switchNode = new BranchNode( startPosition, lastPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); BranchNode switchNode = new BranchNode( startPosition, lastPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END, switchType );
switchNode.add( blockNode ); switchNode.add( blockNode );
parent.add( switchNode ); parent.add( switchNode );
@ -1467,7 +1473,8 @@ class BranchManager {
* @return the new node * @return the new node
*/ */
private BranchNode addMiddleNode( BranchNode parent, int startPos, int endPos ) { private BranchNode addMiddleNode( BranchNode parent, int startPos, int endPos ) {
BranchNode middleNode = new BranchNode( startPos, endPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); Object data = parent.data == switchType && parent.startPos == startPos ? switchType : null;
BranchNode middleNode = new BranchNode( startPos, endPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, data );
int idx = 0; int idx = 0;
for( Iterator<BranchNode> it = parent.iterator(); it.hasNext(); ) { for( Iterator<BranchNode> it = parent.iterator(); it.hasNext(); ) {
BranchNode child = it.next(); BranchNode child = it.next();
@ -1510,7 +1517,6 @@ class BranchManager {
} while( codePosition < nextCodePosition ); } while( codePosition < nextCodePosition );
} }
root.handle( byteCode.getCodePosition(), instructions, instructions.size(), byteCode.getLineNumber() ); root.handle( byteCode.getCodePosition(), instructions, instructions.size(), byteCode.getLineNumber() );
root.calculateBlockType( instructions ); root.calculateBlockType( instructions );
} }
@ -1725,9 +1731,20 @@ class BranchManager {
@Override @Override
public boolean add( BranchNode node ) { public boolean add( BranchNode node ) {
node.parent = this; node.parent = this;
assert node.startOp == null || (node.startPos >= startPos && node.endPos <= endPos): "Node outside parent: " + this + " + " + node; assert node.startOp == null || (node.startPos >= startPos && node.endPos <= endPos) : "Node outside parent: " + this + " + " + node;
assert node.startOp == null || !overlapped( node.startPos ) : "Node on wrong level: " + node + "; parent: " + this + "; last: " + get( size() - 1 );
return super.add( node ); int size = size();
if( size > 0 && node.startOp != null ) {
int nodeStartPos = node.startPos;
for( int i = size - 1; i >= 0; i-- ) {
if( get( i ).endPos <= nodeStartPos ) {
super.add( i + 1, node );
return true;
}
}
}
super.add( 0, node );
return true;
} }
/** /**
@ -1746,7 +1763,16 @@ class BranchManager {
* @return true, if the position is already consumed from a child * @return true, if the position is already consumed from a child
*/ */
boolean overlapped( int startPos ) { boolean overlapped( int startPos ) {
return size() > 0 && get( size() - 1 ).endPos > startPos; for( int i = size() - 1; i >= 0; i-- ) {
BranchNode node = get( i );
if( node.endPos <= startPos ) {
return false;
}
if( node.startPos < startPos ) {
return true;
}
}
return false;
} }
/** /**
@ -1801,6 +1827,14 @@ class BranchManager {
try { try {
ArrayDeque<AnyType> stack = new ArrayDeque<>(); ArrayDeque<AnyType> stack = new ArrayDeque<>();
stack.push( ValueType.empty ); stack.push( ValueType.empty );
BlockType blockType = startBlock.getData() instanceof BlockType ? (BlockType)startBlock.getData() : null;
if( blockType != null ) {
for( AnyType param : blockType.getParams() ) {
stack.push( param );
}
}
INSTRUCTIONS: for( int i = startIdx; i < instructions.size(); i++ ) { INSTRUCTIONS: for( int i = startIdx; i < instructions.size(); i++ ) {
WasmInstruction instr = instructions.get( i ); WasmInstruction instr = instructions.get( i );
if( instr.getType() == Type.Jump ) { if( instr.getType() == Type.Jump ) {
@ -1852,7 +1886,13 @@ class BranchManager {
} }
} }
} }
startBlock.setData( stack.pop() );
AnyType result = stack.pop();
if( blockType == null ) {
startBlock.setData( result );
} else if( result != ValueType.empty) {
throw new WasmException( "block with parameter has return parameter", startBlock.getLineNumber() );
}
} catch( Throwable th ) { } catch( Throwable th ) {
throw WasmException.create( th, startBlock.getLineNumber() ); throw WasmException.create( th, startBlock.getLineNumber() );
} }

View File

@ -787,7 +787,6 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
byteCode.skip( 4 - padding ); byteCode.skip( 4 - padding );
} }
startPosition--; startPosition--;
int switchValuestartPosition = findBlockStartCodePosition( 1 );
int defaultPosition = startPosition + byteCode.readInt(); int defaultPosition = startPosition + byteCode.readInt();
int[] keys; int[] keys;
@ -846,7 +845,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos, lineNumber ); addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos, lineNumber );
} }
} }
branchManager.addSwitchOperator( switchValuestartPosition, 0, lineNumber, keys, positions, defaultPosition ); branchManager.addSwitchOperator( startPosition, 0, lineNumber, keys, positions, defaultPosition );
} }
/** /**

View File

@ -17,10 +17,12 @@
package de.inetsoftware.jwebassembly.module; package de.inetsoftware.jwebassembly.module;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ValueType; import de.inetsoftware.jwebassembly.wasm.ValueType;
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator; import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
@ -104,7 +106,14 @@ class WasmBlockInstruction extends WasmInstruction {
switch( op ) { switch( op ) {
case IF: case IF:
case BLOCK: case BLOCK:
return data != ValueType.empty ? (AnyType)data : null; if( data == ValueType.empty || data == null ) {
return null;
}
if( data instanceof BlockType ) {
List<AnyType> results = ((BlockType)data).getResults();
return results.isEmpty() ? null : results.get( 0 );
}
return (AnyType)data;
case RETURN: case RETURN:
return (AnyType)data; return (AnyType)data;
default: default:
@ -125,6 +134,8 @@ class WasmBlockInstruction extends WasmInstruction {
case THROW: case THROW:
case RETHROW: case RETHROW:
return 1; return 1;
case BLOCK:
return data instanceof BlockType ? ((BlockType)data).getParams().size() : 0;
case RETURN: case RETURN:
return data == null ? 0 : 1; return data == null ? 0 : 1;
default: default:
@ -146,6 +157,8 @@ class WasmBlockInstruction extends WasmInstruction {
case THROW: case THROW:
case RETHROW: case RETHROW:
return new AnyType[] { ValueType.exnref }; return new AnyType[] { ValueType.exnref };
case BLOCK:
return data instanceof BlockType ? ((BlockType)data).getParams().toArray( new AnyType[0] ) : null;
case RETURN: case RETURN:
return data == null ? null : new AnyType[] { (AnyType)data }; return data == null ? null : new AnyType[] { (AnyType)data };
default: default:

View File

@ -162,41 +162,13 @@ public abstract class WasmCodeBuilder {
* *
* @param count * @param count
* the count of values on the stack back. 1 means the last value. 2 means the penultimate value. * the count of values on the stack back. 1 means the last value. 2 means the penultimate value.
* @return the code position that push the last instruction
*/
int findBlockStartCodePosition( int count ) {
return findBlockStart( count, true );
}
/**
* We need a value (or several) from the stack inside a block. We need to find the WasmInstruction where the block
* can can begin. If it is a function call or a numeric expression, it can be complicated to find the right point.
*
* @param count
* the count of values on the stack back. 1 means the last value. 2 means the penultimate value.
* @param codePosition
* true, get the code position; false, get the index in the instructions
* @return the code position that push the last instruction
*/
private int findBlockStart( int count, boolean codePosition ) {
return findBlockStart( count, codePosition, instructions, instructions.size() );
}
/**
* We need a value (or several) from the stack inside a block. We need to find the WasmInstruction where the block
* can can begin. If it is a function call or a numeric expression, it can be complicated to find the right point.
*
* @param count
* the count of values on the stack back. 1 means the last value. 2 means the penultimate value.
* @param codePosition
* true, get the code position; false, get the index in the instructions
* @param instructions * @param instructions
* the instruction list for searching * the instruction list for searching
* @param idx * @param idx
* the start index for the search. Between 0 and instructions.size(). * the start index for the search. Between 0 and instructions.size().
* @return the code position that push the last instruction * @return the code position that push the last instruction
*/ */
static int findBlockStart( int count, boolean codePosition, List<WasmInstruction> instructions, int idx ) { static int findBlockStart( int count, List<WasmInstruction> instructions, int idx ) {
int valueCount = 0; int valueCount = 0;
for( int i = idx - 1; i >= 0; i-- ) { for( int i = idx - 1; i >= 0; i-- ) {
WasmInstruction instr = instructions.get( i ); WasmInstruction instr = instructions.get( i );
@ -204,12 +176,13 @@ public abstract class WasmCodeBuilder {
if( valueType != null ) { if( valueType != null ) {
valueCount++; valueCount++;
} }
valueCount -= instr.getPopCount(); int popCount = instr.getPopCount();
if( valueCount == count ) { valueCount -= popCount;
if( valueCount == count && popCount == 0 ) {
int codePos = instr.getCodePosition(); int codePos = instr.getCodePosition();
if( i == 0 || instructions.get( i - 1 ).getCodePosition() < codePos ) { if( i == 0 || instructions.get( i - 1 ).getCodePosition() < codePos ) {
// if the same codePos is used from multiple instructions then it is not an atomic operation in Java // if the same codePos is used from multiple instructions then it is not an atomic operation in Java
return codePosition ? codePos : i; return codePos;
} }
} }
} }

View File

@ -361,7 +361,7 @@ public class TextModuleWriter extends ModuleWriter {
* if any I/O error occur * if any I/O error occur
*/ */
private void writeTypeName( Appendable output, AnyType type ) throws IOException { private void writeTypeName( Appendable output, AnyType type ) throws IOException {
if( !type.isRefType() ) { if( type instanceof ValueType ) {
String name; String name;
switch( (ValueType)type ) { switch( (ValueType)type ) {
case u16: case u16: