From 4e98efeb5d809955dc3a6d451dbe2db61dcdf342 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 5 May 2019 17:25:43 +0200 Subject: [PATCH] prepare the using of call_indirect --- .../binary/BinaryModuleWriter.java | 68 +++++++++++ .../jwebassembly/module/ModuleWriter.java | 10 ++ .../module/WasmCallIndirectInstruction.java | 115 ++++++++++++++++++ .../jwebassembly/module/WasmInstruction.java | 2 +- .../jwebassembly/text/TextModuleWriter.java | 30 +++++ 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java diff --git a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java index 9fb60b6..0a29c77 100644 --- a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java @@ -87,6 +87,8 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod private String javaSourceFile; + private boolean callIndirect; + /** * Create new instance. * @@ -123,10 +125,12 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod writeSection( SectionType.Type, functionTypes ); writeSection( SectionType.Import, imports.values() ); writeSection( SectionType.Function, functions.values() ); + writeTableSection(); writeMemorySection(); writeSection( SectionType.Global, globals.values() ); writeEventSection(); writeExportSection(); + writeElementSection(); writeCodeSection(); writeDataSection(); writeDebugNames(); @@ -158,6 +162,31 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } } + /** + * Write the table section. It declare the space for the element section. + * + * @throws IOException + * if any I/O error occur + */ + private void writeTableSection() throws IOException { + if( !callIndirect ) { + return; + } + + int elemCount = imports.size() + functions.size(); + WasmOutputStream stream = new WasmOutputStream(); + int count = 1; + stream.writeVaruint32( count ); // count of tables + for( int i = 0; i < count; i++ ) { + stream.writeValueType( ValueType.anyfunc ); // the type of elements + stream.writeVaruint32( 1 ); // flags; 1-maximum is available, 0-no maximum value available + stream.writeVaruint32( elemCount ); // initial length + stream.writeVaruint32( elemCount ); // maximum length + } + + wasm.writeSection( SectionType.Table, stream ); + } + /** * Write the memory section. * @@ -220,6 +249,33 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } } + /** + * Write element section. This section create a matching between direct and indirect function call IDs. + * + * @throws IOException + * if any I/O error occur + */ + private void writeElementSection() throws IOException { + if( !callIndirect ) { + return; + } + + int elemCount = imports.size() + functions.size(); + WasmOutputStream stream = new WasmOutputStream(); + stream.writeVaruint32( 1 ); // count of element segments to follow + + // element_segment + stream.writeVaruint32( 0 ); // the table index (0 in the MVP) + stream.writeConst( 0, ValueType.i32 ); // the offset on which the elements start + stream.writeOpCode( END ); // end of offset instruction + stream.writeVaruint32( elemCount ); + for( int i = 0; i < elemCount; i++ ) { + stream.writeVaruint32( i ); // we use a 1:1 matching between direct function call numbers and indrect function numbers because the most functions will be indirect + } + + wasm.writeSection( SectionType.Element, stream ); + } + /** * Write the code section to the output. This section contains the byte code. * @@ -941,6 +997,18 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod codeStream.writeVaruint32( func.id ); } + /** + * {@inheritDoc} + */ + @Override + protected void writeFunctionCallIndirect( FunctionName name ) throws IOException { + callIndirect = true; + Function func = getFunction( name ); + codeStream.writeOpCode( CALL_INDIRECT ); + codeStream.writeVaruint32( func.typeId ); + codeStream.writeVaruint32( 0 ); // table 0 + } + /** * Get the function object for the name. If not exists then it will be created. * diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java index da2da4e..8a4bcdd 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java @@ -224,6 +224,16 @@ public abstract class ModuleWriter implements Closeable { */ protected abstract void writeFunctionCall( FunctionName name ) throws IOException; + /** + * Write a call indirect to a function. + * + * @param name + * the function name + * @throws IOException + * if any I/O error occur + */ + protected abstract void writeFunctionCallIndirect( FunctionName name ) throws IOException; + /** * Write a block/branch code * diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java b/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java new file mode 100644 index 0000000..b4e7023 --- /dev/null +++ b/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java @@ -0,0 +1,115 @@ +/* + Copyright 2019 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. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +package de.inetsoftware.jwebassembly.module; + +import java.io.IOException; +import java.util.Iterator; + +import javax.annotation.Nonnull; + +import de.inetsoftware.jwebassembly.wasm.AnyType; + +/** + * WasmInstruction for a function call. + * + * @author Volker Berlin + * + */ +class WasmCallIndirectInstruction extends WasmInstruction { + + private AnyType valueType; + + private final FunctionName name; + + private int paramCount = -1; + + /** + * Create an instance of a function call instruction + * + * @param name + * the function name that should be called + * @param javaCodePos + * the code position/offset in the Java method + * @param lineNumber + * the line number in the Java source code + */ + WasmCallIndirectInstruction( FunctionName name, int javaCodePos, int lineNumber ) { + super( javaCodePos, lineNumber ); + this.name = name; + } + + /** + * {@inheritDoc} + */ + @Override + Type getType() { + return Type.CallIndirect; + } + + /** + * Get the function name that should be called + * + * @return the name + */ + @Nonnull + FunctionName getFunctionName() { + return name; + } + + /** + * {@inheritDoc} + */ + public void writeTo( @Nonnull ModuleWriter writer ) throws IOException { + writer.writeFunctionCallIndirect( name ); + } + + /** + * {@inheritDoc} + */ + AnyType getPushValueType() { + countParams(); + return valueType; + } + + /** + * {@inheritDoc} + */ + @Override + int getPopCount() { + countParams(); + return paramCount; + } + + /** + * Count the parameters in the signature + */ + private void countParams() { + if( paramCount >= 0 ) { + return; + } + Iterator parser = name.getSignature(); + paramCount = 1; + while( parser.next() != null ) { + paramCount++; + } + valueType = parser.next(); + while( parser.hasNext() ) { + valueType = parser.next(); + paramCount--; + } + } +} diff --git a/src/de/inetsoftware/jwebassembly/module/WasmInstruction.java b/src/de/inetsoftware/jwebassembly/module/WasmInstruction.java index ef9c471..2c17c0d 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmInstruction.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmInstruction.java @@ -35,7 +35,7 @@ abstract class WasmInstruction { * Type of instruction to faster differ as with instanceof. */ static enum Type { - Const, Convert, Local, Global, Block, Numeric, Nop, Call, Array, Struct; + Const, Convert, Local, Global, Block, Numeric, Nop, Call, CallIndirect, Array, Struct; } private int javaCodePos; diff --git a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java index d802031..63fe22f 100644 --- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java @@ -67,6 +67,12 @@ public class TextModuleWriter extends ModuleWriter { private boolean useExceptions; + private int importCount; + + private int functionCount; + + private boolean callIndirect; + /** * Create a new instance. * @@ -94,6 +100,18 @@ public class TextModuleWriter extends ModuleWriter { public void close() throws IOException { output.append( methodOutput ); + if( callIndirect ) { + int count = importCount + functionCount; + String countStr = Integer.toString( count ); + output.append( "(table " ).append( countStr ).append( ' ' ).append( countStr ).append( " anyfunc)" ); + newline( output ); + output.append( "(elem (i32.const 0) " ); + for( int i = 0; i < count; i++ ) { + output.append( Integer.toString( i ) ).append( ' ' ); + } + output.append( ')' ); + } + int dataSize = dataStream.size(); if( dataSize > 0 ) { int pages = (dataSize + 0xFFFF) / 0x10000; @@ -166,6 +184,7 @@ public class TextModuleWriter extends ModuleWriter { newline( methodOutput ); methodOutput.append( "(import \"" ).append( importModule ).append( "\" \"" ).append( importName ).append( "\" (func $" ).append( normalizeName( name ) ); isImport = true; + importCount++; } } @@ -215,6 +234,7 @@ public class TextModuleWriter extends ModuleWriter { methodOutput.append( normalizeName( name ) ); inset++; methodParamNames.clear(); + functionCount++; } /** @@ -504,6 +524,16 @@ public class TextModuleWriter extends ModuleWriter { methodOutput.append( "call $" ).append( normalizeName( name ) ); } + /** + * {@inheritDoc} + */ + @Override + protected void writeFunctionCallIndirect( FunctionName name ) throws IOException { + callIndirect = true; + newline( methodOutput ); + methodOutput.append( "call_indirect $" ).append( normalizeName( name ) ); + } + /** * {@inheritDoc} */