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");
* 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.
*/
public void writeValueType( AnyType type ) throws IOException {
if( !type.isRefType() && !options.useGC() ) {
switch( (ValueType)type ) {
case eqref:
type = ValueType.externref;
break;
}
if( !options.useGC() && type == ValueType.eqref ) {
type = ValueType.externref;
}
writeVarint( type.getCode() );
}

View File

@ -32,6 +32,7 @@ import de.inetsoftware.classparser.CodeInputStream;
import de.inetsoftware.classparser.ConstantClass;
import de.inetsoftware.classparser.TryCatchFinally;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
import de.inetsoftware.jwebassembly.wasm.AnyType;
@ -64,6 +65,7 @@ class BranchManager {
private final ArrayList<BreakBlock> breakOperations = new ArrayList<>();
private BlockType switchType;
/**
* Create a branch manager.
@ -534,7 +536,7 @@ class BranchManager {
// find the code position where the condition values are push on the stack
List<WasmInstruction> instructions = this.instructions;
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 ) ) {
branch = addMiddleNode( parent, parent.startPos, endPos );
} else {
@ -853,6 +855,10 @@ class BranchManager {
* the not consumed operations in the parent branch
*/
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 posCount = switchBlock.positions.length;
boolean isTable = switchBlock.keys == null;
@ -883,7 +889,7 @@ class BranchManager {
}
lastPosition = currentPosition;
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 ) {
node.add( blockNode );
}
@ -914,7 +920,7 @@ class BranchManager {
while( parentNode != null && end > parentNode.endPos ) {
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 ) {
BranchNode child = parentNode.remove( 0 );
parentNode.add( middleNode );
@ -980,7 +986,7 @@ class BranchManager {
}
// 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 );
parent.add( switchNode );
@ -1467,7 +1473,8 @@ class BranchManager {
* @return the new node
*/
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;
for( Iterator<BranchNode> it = parent.iterator(); it.hasNext(); ) {
BranchNode child = it.next();
@ -1510,7 +1517,6 @@ class BranchManager {
} while( codePosition < nextCodePosition );
}
root.handle( byteCode.getCodePosition(), instructions, instructions.size(), byteCode.getLineNumber() );
root.calculateBlockType( instructions );
}
@ -1725,9 +1731,20 @@ class BranchManager {
@Override
public boolean add( BranchNode node ) {
node.parent = this;
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 );
assert node.startOp == null || (node.startPos >= startPos && node.endPos <= endPos) : "Node outside parent: " + this + " + " + 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
*/
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 {
ArrayDeque<AnyType> stack = new ArrayDeque<>();
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++ ) {
WasmInstruction instr = instructions.get( i );
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 ) {
throw WasmException.create( th, startBlock.getLineNumber() );
}

View File

@ -787,7 +787,6 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
byteCode.skip( 4 - padding );
}
startPosition--;
int switchValuestartPosition = findBlockStartCodePosition( 1 );
int defaultPosition = startPosition + byteCode.readInt();
int[] keys;
@ -846,7 +845,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
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;
import java.io.IOException;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ValueType;
import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
@ -104,7 +106,14 @@ class WasmBlockInstruction extends WasmInstruction {
switch( op ) {
case IF:
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:
return (AnyType)data;
default:
@ -125,6 +134,8 @@ class WasmBlockInstruction extends WasmInstruction {
case THROW:
case RETHROW:
return 1;
case BLOCK:
return data instanceof BlockType ? ((BlockType)data).getParams().size() : 0;
case RETURN:
return data == null ? 0 : 1;
default:
@ -146,6 +157,8 @@ class WasmBlockInstruction extends WasmInstruction {
case THROW:
case RETHROW:
return new AnyType[] { ValueType.exnref };
case BLOCK:
return data instanceof BlockType ? ((BlockType)data).getParams().toArray( new AnyType[0] ) : null;
case RETURN:
return data == null ? null : new AnyType[] { (AnyType)data };
default:

View File

@ -162,41 +162,13 @@ public abstract class WasmCodeBuilder {
*
* @param count
* 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
* the instruction list for searching
* @param idx
* the start index for the search. Between 0 and instructions.size().
* @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;
for( int i = idx - 1; i >= 0; i-- ) {
WasmInstruction instr = instructions.get( i );
@ -204,12 +176,13 @@ public abstract class WasmCodeBuilder {
if( valueType != null ) {
valueCount++;
}
valueCount -= instr.getPopCount();
if( valueCount == count ) {
int popCount = instr.getPopCount();
valueCount -= popCount;
if( valueCount == count && popCount == 0 ) {
int codePos = instr.getCodePosition();
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
return codePosition ? codePos : i;
return codePos;
}
}
}

View File

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