improve array handling for GC mode

This commit is contained in:
Volker Berlin 2021-01-02 16:44:38 +01:00
parent c96b27a12e
commit f9de14d88e
9 changed files with 160 additions and 77 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 - 2020 Volker Berlin (i-net software)
* Copyright 2017 - 2021 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.
@ -1363,7 +1363,9 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
int opCode;
switch(op) {
case NEW:
opCode = ARRAY_NEW;
codeStream.writeOpCode( RTT_CANON );
codeStream.writeValueType( type.getNativeArrayType() );
opCode = ARRAY_NEW_DEFAULT;
break;
case GET:
opCode = ARRAY_GET;
@ -1374,11 +1376,14 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
case LEN:
opCode = ARRAY_LEN;
break;
case NEW_ARRAY_WITH_RTT:
opCode = ARRAY_NEW_DEFAULT;
break;
default:
throw new Error( "Unknown operator: " + op );
}
codeStream.writeOpCode( opCode );
codeStream.writeValueType( type );
codeStream.writeValueType( type.getNativeArrayType() );
}
/**
@ -1404,6 +1409,12 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
opCode = REF_NULL;
type = options.useGC() ? type : ValueType.externref;
break;
case RTT_CANON:
opCode = RTT_CANON;
break;
case NEW_WITH_RTT:
opCode = STRUCT_NEW;
break;
default:
throw new Error( "Unknown operator: " + op );
}
@ -1411,7 +1422,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
if( type != null ) {
codeStream.writeValueType( type );
}
if( fieldName != null ) {
if( idx >= 0 ) {
codeStream.writeVaruint32( idx );
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 - 2020 Volker Berlin (i-net software)
* Copyright 2017 - 2021 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.
@ -498,6 +498,8 @@ interface InstructionOpcodes {
static final int ARRAY_NEW = 0xFB11;
static final int ARRAY_NEW_DEFAULT = 0xFB12;
static final int ARRAY_GET = 0xFB13;
static final int ARRAY_SET = 0xFB16;

View File

@ -1,5 +1,5 @@
/*
Copyright 2018 - 2020 Volker Berlin (i-net software)
Copyright 2018 - 2021 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.
@ -19,7 +19,6 @@ package de.inetsoftware.jwebassembly.module;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName;
@ -27,6 +26,7 @@ import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
import de.inetsoftware.jwebassembly.wasm.ArrayType;
import de.inetsoftware.jwebassembly.wasm.StructOperator;
import de.inetsoftware.jwebassembly.wasm.ValueType;
/**
@ -37,6 +37,7 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
*/
class WasmArrayInstruction extends WasmInstruction {
@Nonnull
private final ArrayOperator op;
private final AnyType type;
@ -61,7 +62,7 @@ class WasmArrayInstruction extends WasmInstruction {
* @param lineNumber
* the line number in the Java source code
*/
WasmArrayInstruction( @Nullable ArrayOperator op, @Nullable AnyType type, TypeManager types, int javaCodePos, int lineNumber ) {
WasmArrayInstruction( @Nonnull ArrayOperator op, @Nonnull AnyType type, TypeManager types, int javaCodePos, int lineNumber ) {
super( javaCodePos, lineNumber );
this.op = op;
this.type = type;
@ -70,61 +71,81 @@ class WasmArrayInstruction extends WasmInstruction {
}
/**
* Create the synthetic polyfill function of this instruction for nonGC mode.
* Create the synthetic function of this instruction if required for the operation.
*
* @param useGC true, with GC code
* @return the function or null if not needed
*/
SyntheticFunctionName createNonGcFunction() {
// i8 and i16 are not valid in function signatures
AnyType functionType = type == ValueType.i8 || type == ValueType.i16 ? ValueType.i32 : type;
switch( op ) {
case NEW:
String cmd;
if( type.isRefType() ) {
cmd = "Object.seal(new Array(l).fill(null))";
} else {
switch( (ValueType)type ) {
case i8:
cmd = "new Uint8Array(l)";
break;
case i16:
cmd = "new Int16Array(l)";
break;
case i32:
cmd = "new Int32Array(l)";
break;
case i64:
cmd = "new BigInt64Array(l)";
break;
case f32:
cmd = "new Float32Array(l)";
break;
case f64:
cmd = "new Float64Array(l)";
break;
default:
cmd = "Object.seal(new Array(l).fill(null))";
SyntheticFunctionName createNonGcFunction( boolean useGC ) {
if( useGC ) {
switch( op ) {
case NEW:
functionName = new WatCodeSyntheticFunctionName( "array_new_" + validJsName( type ), "", ValueType.i32, null, arrayType ) {
@Override
protected String getCode() {
String nativeArrayTypeName = ((ArrayType)arrayType.getNativeArrayType()).getName();
return "i32.const " + arrayType.getVTable() + " i32.const 0" // hashcode
+ " local.get 0" // array size
+ " rtt.canon " + nativeArrayTypeName //
+ " array.new_default_with_rtt " + nativeArrayTypeName //
+ " rtt.canon " + arrayType.getName() //
+ " struct.new_with_rtt " + arrayType.getName() //
+ " return";
}
};
}
} else {
// i8 and i16 are not valid in function signatures
AnyType functionType = type == ValueType.i8 || type == ValueType.i16 ? ValueType.i32 : type;
switch( op ) {
case NEW:
String cmd;
if( type.isRefType() ) {
cmd = "Object.seal(new Array(l).fill(null))";
} else {
switch( (ValueType)type ) {
case i8:
cmd = "new Uint8Array(l)";
break;
case i16:
cmd = "new Int16Array(l)";
break;
case i32:
cmd = "new Int32Array(l)";
break;
case i64:
cmd = "new BigInt64Array(l)";
break;
case f32:
cmd = "new Float32Array(l)";
break;
case f64:
cmd = "new Float64Array(l)";
break;
default:
cmd = "Object.seal(new Array(l).fill(null))";
}
}
}
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_new_" + validJsName( type ), () -> {
// create the default values of a new type
return new StringBuilder( "(l)=>Object.seal({0:" ) // fix count of elements
.append( arrayType.getVTable() ) // .vtable
.append( ",1:0,2:" ) // .hashCode
.append( cmd ) // the array data
.append( "})" ) //
.toString();
}, ValueType.i32, null, ValueType.externref );
break;
case GET:
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_get_" + validJsName( functionType ), () -> "(a,i)=>a[2][i]", ValueType.externref, ValueType.i32, null, functionType );
break;
case SET:
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_set_" + validJsName( functionType ), () -> "(a,i,v)=>a[2][i]=v", ValueType.externref, ValueType.i32, functionType, null, null );
break;
case LEN:
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_len", () -> "(a)=>a[2].length", ValueType.externref, null, ValueType.i32 );
break;
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_new_" + validJsName( type ), () -> {
// create the default values of a new type
return new StringBuilder( "(l)=>Object.seal({0:" ) // fix count of elements
.append( arrayType.getVTable() ) // .vtable
.append( ",1:0,2:" ) // .hashCode
.append( cmd ) // the array data
.append( "})" ) //
.toString();
}, ValueType.i32, null, ValueType.externref );
break;
case GET:
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_get_" + validJsName( functionType ), () -> "(a,i)=>a[2][i]", ValueType.externref, ValueType.i32, null, functionType );
break;
case SET:
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_set_" + validJsName( functionType ), () -> "(a,i,v)=>a[2][i]=v", ValueType.externref, ValueType.i32, functionType, null, null );
break;
case LEN:
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_len", () -> "(a)=>a[2].length", ValueType.externref, null, ValueType.i32 );
break;
}
}
return functionName;
}
@ -154,10 +175,19 @@ class WasmArrayInstruction extends WasmInstruction {
/**
* {@inheritDoc}
*/
@Override
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
if( functionName != null ) { // nonGC
writer.writeFunctionCall( functionName, null );
} else {
switch( op ) {
case GET:
case SET:
case LEN:
writer.writeStructOperator( StructOperator.GET, arrayType, null, 2 ); // the native array is on position 2 (vtable, hashcode are before)
break;
default:
}
writer.writeArrayOperator( op, arrayType );
}
}
@ -165,9 +195,11 @@ class WasmArrayInstruction extends WasmInstruction {
/**
* {@inheritDoc}
*/
@Override
AnyType getPushValueType() {
switch( op ) {
case NEW:
case NEW_ARRAY_WITH_RTT:
return arrayType;
case GET:
return type instanceof ValueType ? (ValueType)type : ValueType.externref;
@ -187,6 +219,7 @@ class WasmArrayInstruction extends WasmInstruction {
int getPopCount() {
switch( op ) {
case GET:
case NEW_ARRAY_WITH_RTT:
return 2;
case NEW:
case LEN:

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 - 2020 Volker Berlin (i-net software)
* Copyright 2018 - 2021 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.
@ -737,12 +737,10 @@ public abstract class WasmCodeBuilder {
protected void addArrayInstruction( ArrayOperator op, AnyType type, int javaCodePos, int lineNumber ) {
WasmArrayInstruction arrayInst = new WasmArrayInstruction( op, type, types, javaCodePos, lineNumber );
instructions.add( arrayInst );
if( !options.useGC() ) {
SyntheticFunctionName name = arrayInst.createNonGcFunction();
if( name != null ) {
functions.markAsNeeded( name );
functions.markAsImport( name, name.getAnnotation() );
}
SyntheticFunctionName name = arrayInst.createNonGcFunction( options.useGC() );
if( name != null ) {
functions.markAsNeeded( name );
functions.markAsImport( name, name.getAnnotation() );
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2018 - 2020 Volker Berlin (i-net software)
Copyright 2018 - 2021 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.
@ -38,6 +38,7 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
*/
class WasmStructInstruction extends WasmInstruction {
@Nonnull
private final StructOperator op;
private final StructType type;
@ -64,7 +65,7 @@ class WasmStructInstruction extends WasmInstruction {
* @param types
* the type manager
*/
WasmStructInstruction( @Nullable StructOperator op, @Nonnull String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber, TypeManager types ) {
WasmStructInstruction( @Nonnull StructOperator op, @Nonnull String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber, TypeManager types ) {
super( javaCodePos, lineNumber );
this.op = op;
this.type = types.valueOf( typeName );
@ -166,6 +167,7 @@ class WasmStructInstruction extends WasmInstruction {
/**
* {@inheritDoc}
*/
@Override
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
int idx = -1;
switch( op ) {
@ -199,6 +201,7 @@ class WasmStructInstruction extends WasmInstruction {
case CAST:
idx = type.getClassIndex();
break;
default:
}
if( functionName != null ) { // nonGC
if( idx >= 0 ) {
@ -213,6 +216,7 @@ class WasmStructInstruction extends WasmInstruction {
/**
* {@inheritDoc}
*/
@Override
AnyType getPushValueType() {
switch( op ) {
case NULL:
@ -220,6 +224,7 @@ class WasmStructInstruction extends WasmInstruction {
case NEW:
case NEW_DEFAULT:
case CAST:
case NEW_WITH_RTT:
return type;
case GET:
return fieldName.getType();
@ -227,6 +232,8 @@ class WasmStructInstruction extends WasmInstruction {
return null;
case INSTANCEOF:
return ValueType.i32; // a boolean value
case RTT_CANON:
return ValueType.i32; // rtt type
default:
throw new WasmException( "Unknown array operation: " + op, -1 );
}
@ -241,12 +248,14 @@ class WasmStructInstruction extends WasmInstruction {
case GET:
case INSTANCEOF:
case CAST:
case NEW_WITH_RTT:
return 1;
case SET:
return 2;
case NEW:
case NEW_DEFAULT:
case NULL:
case RTT_CANON:
return 0;
default:
throw new WasmException( "Unknown array operation: " + op, -1 );

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 - 2020 Volker Berlin (i-net software)
* Copyright 2017 - 2021 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.
@ -831,7 +831,9 @@ public class TextModuleWriter extends ModuleWriter {
String operation;
switch( op ) {
case NEW:
operation = "new";
newline( methodOutput );
methodOutput.append( "rtt.canon" ).append( ' ' ).append( normalizeName( type.getNativeArrayType().toString() ) );
operation = "new_default_with_rtt";
break;
case GET:
operation = "get";
@ -842,11 +844,14 @@ public class TextModuleWriter extends ModuleWriter {
case LEN:
operation = "len";
break;
case NEW_ARRAY_WITH_RTT:
operation = "new_default_with_rtt";
break;
default:
throw new Error( "Unknown operator: " + op );
}
newline( methodOutput );
methodOutput.append( "array." ).append( operation ).append( ' ' ).append( normalizeName( type.toString() ) );
methodOutput.append( "array." ).append( operation ).append( ' ' ).append( normalizeName( type.getNativeArrayType().toString() ) );
}
/**
@ -860,7 +865,7 @@ public class TextModuleWriter extends ModuleWriter {
case NEW_DEFAULT:
newline( methodOutput );
methodOutput.append( "rtt.canon" ).append( ' ' ).append( normalizeName( type.toString() ) );
operation = "struct.new";
operation = "struct.new_default_with_rtt";
break;
case GET:
operation = "struct.get";
@ -876,6 +881,12 @@ public class TextModuleWriter extends ModuleWriter {
type = null;
}
break;
case RTT_CANON:
operation = "rtt.canon";
break;
case NEW_WITH_RTT:
operation = "struct.new_with_rtt";
break;
default:
throw new Error( "Unknown operator: " + op );
}
@ -884,8 +895,11 @@ public class TextModuleWriter extends ModuleWriter {
if( type != null ) {
methodOutput.append( ' ' ).append( normalizeName( type.toString() ) );
}
if( fieldName != null ) {
methodOutput.append( ' ' ).append( idx ).append( " ;; $" ).append( normalizeName( fieldName.getName() ) );
if( idx >= 0 ) {
methodOutput.append( ' ' ).append( idx );
if( fieldName != null ) {
methodOutput.append( " ;; $" ).append( normalizeName( fieldName.getName() ) );
}
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2018 - 2019 Volker Berlin (i-net software)
Copyright 2018 - 2021 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.
@ -26,4 +26,5 @@ public enum ArrayOperator {
GET,
SET,
LEN,
NEW_ARRAY_WITH_RTT,
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2018 - 2020 Volker Berlin (i-net software)
Copyright 2018 - 2021 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.
@ -29,4 +29,6 @@ public enum StructOperator {
NULL,
CAST,
INSTANCEOF,
RTT_CANON,
NEW_WITH_RTT,
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2018 - 2020 Volker Berlin (i-net software)
Copyright 2018 - 2021 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.
@ -307,6 +307,19 @@ public class WatParser extends WasmCodeBuilder {
AnyType type = ((ArrayType)getTypeManager().valueOf( typeName )).getArrayType();
addArrayInstruction( ArrayOperator.LEN, type, javaCodePos, lineNumber );
break;
case "array.new_default_with_rtt":
typeName = get( tokens, ++i );
type = ((ArrayType)getTypeManager().valueOf( typeName )).getArrayType();
addArrayInstruction( ArrayOperator.NEW_ARRAY_WITH_RTT, type, javaCodePos, lineNumber );
break;
case "rtt.canon":
typeName = get( tokens, ++i );
addStructInstruction( StructOperator.RTT_CANON, typeName, null, javaCodePos, lineNumber );
break;
case "struct.new_with_rtt":
typeName = get( tokens, ++i );
addStructInstruction( StructOperator.NEW_WITH_RTT, typeName, null, javaCodePos, lineNumber );
break;
default:
throw new WasmException( "Unknown WASM token: " + tok, lineNumber );
}