first step of a "if" implementation

This commit is contained in:
Volker Berlin 2018-03-25 12:57:04 +02:00
parent 014d3a1acf
commit d26b9bbb94
8 changed files with 252 additions and 1 deletions

View File

@ -27,6 +27,7 @@ import javax.annotation.Nullable;
import de.inetsoftware.classparser.MethodInfo;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.BlockOperator;
import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.NumericOperator;
import de.inetsoftware.jwebassembly.module.ValueType;
@ -437,9 +438,39 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
break;
}
break;
case eq:
switch( valueType ) {
case i32:
op = I32_EQ;
break;
case i64:
op = I64_EQ;
break;
case f32:
op = F32_EQ;
break;
case f64:
op = F64_EQ;
break;
}
case ne:
switch( valueType ) {
case i32:
op = I32_NE;
break;
case i64:
op = I64_NE;
break;
case f32:
op = F32_NE;
break;
case f64:
op = F64_NE;
break;
}
}
if( op == 0 ) {
throw new Error();
throw new Error( valueType + "." + numOp );
}
codeStream.write( op );
}
@ -483,4 +514,22 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
}
codeStream.writeVaruint32( func.id );
}
/**
* {@inheritDoc}
*/
@Override
protected void writeBlockCode( BlockOperator op ) throws IOException {
switch( op ) {
case IF:
codeStream.write( IF );
codeStream.write( 0x40 ); // void; the return type of the block. currently we does not use it
break;
case END:
codeStream.write( END );
break;
default:
throw new Error( "Unknown block: " + op );
}
}
}

View File

@ -0,0 +1,12 @@
package de.inetsoftware.jwebassembly.module;
/**
* Block operators
*
* @author Volker Berlin
*
*/
public enum BlockOperator {
IF,
END,
}

View File

@ -0,0 +1,69 @@
package de.inetsoftware.jwebassembly.module;
import java.io.IOException;
import java.util.ArrayDeque;
import de.inetsoftware.classparser.CodeInputStream;
/**
* This calculate the goto offsets from Java back to block operations
*
* @author Volker Berlin
*
*/
class BranchManger {
private final ArrayDeque<Block> stack = new ArrayDeque<>();
/**
* Start a new block.
*
* @param op
* the start operation
* @param basePosition
* the byte position of the start position
* @param offset
* the relative jump position
*/
void start( BlockOperator op, int basePosition, int offset ) {
stack.push( new Block( op, basePosition, offset ) );
}
/**
* Check on every instruction position if there any branch is ending
*
* @param byteCode
* the byte code stream
* @param writer
* the current module writer
* @throws IOException
* if any I/O exception occur
*/
void handle( CodeInputStream byteCode, ModuleWriter writer ) throws IOException {
int position = byteCode.getCodePosition();
Block block = stack.peek();
if( block != null ) {
if( block.endPosition == position ) {
writer.writeBlockCode( BlockOperator.END );
stack.pop();
}
}
}
/**
* Description of single block/branch
*/
private static class Block {
private BlockOperator op;
private int basePosition;
private int endPosition;
private Block( BlockOperator op, int basePosition, int offset ) {
this.op = op;
this.basePosition = basePosition;
this.endPosition = basePosition + offset;
}
}
}

View File

@ -52,6 +52,8 @@ public abstract class ModuleWriter implements Closeable {
private String sourceFile;
private BranchManger branchManager = new BranchManger();
/**
* Prepare the content of the class.
*
@ -303,6 +305,7 @@ public abstract class ModuleWriter implements Closeable {
private void writeCodeChunk( CodeInputStream byteCode, int lineNumber, ConstantPool constantPool ) throws WasmException {
try {
while( byteCode.available() > 0 ) {
branchManager.handle( byteCode, this );
int op = byteCode.readUnsignedByte();
switch( op ) {
case 2: // iconst_m1
@ -493,6 +496,14 @@ public abstract class ModuleWriter implements Closeable {
case 136: // l2i
writeCast( ValueTypeConvertion.l2i );
break;
case 153: // ifeq
opIfCondition( NumericOperator.ne, byteCode );
break;
case 167: // goto
int baseCodePosition = byteCode.getCodePosition() - 1;
int offset = byteCode.readUnsignedShort();
if(true)throw new WasmException( "Unimplemented byte code operation: " + op, sourceFile, lineNumber );
break;
case 172: // ireturn
case 173: // lreturn
case 174: // freturn
@ -514,6 +525,27 @@ public abstract class ModuleWriter implements Closeable {
}
}
/**
* 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
* condition for the if block. The caller of the method must already negate this
*
* @param numOp
* The condition for the if block.
* @param byteCode
* current byte code stream to read the taget offset.
* @throws IOException
* if any I/O errors occur.
*/
private void opIfCondition( NumericOperator numOp, CodeInputStream byteCode ) throws IOException {
int baseCodePosition = byteCode.getCodePosition() - 1;
int offset = byteCode.readUnsignedShort();
branchManager.start( BlockOperator.IF, baseCodePosition, offset );
writeConstInt( 0 );
writeNumericOperator( numOp, ValueType.i32 );
writeBlockCode( BlockOperator.IF );
}
/**
* Write a constant value.
*
@ -670,4 +702,14 @@ public abstract class ModuleWriter implements Closeable {
* if any I/O error occur
*/
protected abstract void writeFunctionCall( String name ) throws IOException;
/**
* Write a block/branch code
*
* @param op
* the operation
* @throws IOException
* if any I/O error occur
*/
protected abstract void writeBlockCode( BlockOperator op ) throws IOException;
}

View File

@ -30,4 +30,6 @@ public enum NumericOperator {
shl,
shr_s,
shr_u,
eq,
ne,
}

View File

@ -24,6 +24,7 @@ public enum ValueType {
f32(-3),
f64(-4),
func(-0x20);
// void(-0x40);
private int code;

View File

@ -20,6 +20,7 @@ import java.util.List;
import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.module.BlockOperator;
import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.NumericOperator;
import de.inetsoftware.jwebassembly.module.ValueType;
@ -221,4 +222,25 @@ public class TextModuleWriter extends ModuleWriter {
name = name.substring( 0, name.indexOf( '(' ) );
methodOutput.append( "call $" ).append( name );
}
/**
* {@inheritDoc}
*/
@Override
protected void writeBlockCode( BlockOperator op ) throws IOException {
switch( op ) {
case IF:
newline( methodOutput );
methodOutput.append( "if" );
inset++;
break;
case END:
inset--;
newline( methodOutput );
methodOutput.append( "end" );
break;
default:
throw new Error( "Unknown block: " + op );
}
}
}

View File

@ -0,0 +1,54 @@
package de.inetsoftware.jwebassembly.runtime;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.ClassRule;
import org.junit.runners.Parameterized.Parameters;
import org.webassembly.annotation.Export;
import de.inetsoftware.jwebassembly.ScriptEngine;
import de.inetsoftware.jwebassembly.WasmRule;
public class ControlFlowOperators extends AbstractBaseTest {
@ClassRule
public static WasmRule rule = new WasmRule( TestClass.class );
public ControlFlowOperators( ScriptEngine script, String method, Object[] params ) {
super( rule, script, method, params );
}
@Parameters( name = "{0}-{1}" )
public static Collection<Object[]> data() {
ArrayList<Object[]> list = new ArrayList<>();
for( ScriptEngine[] val : ScriptEngine.testParams() ) {
ScriptEngine script = val[0];
addParam( list, script, "ifThenElse_Int0" );
addParam( list, script, "forLoop" );
}
return list;
}
static class TestClass {
@Export
static int ifThenElse_Int0() {
int condition = 0;
if( condition != 0 ) {
return 13;
} else {
return 76;
}
}
@Export
static int forLoop() {
int a = 0;
// for( int i=0; i<10;i++) {
// a += i;
// }
return a;
}
}
}