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"); * 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.
@ -1363,7 +1363,9 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
int opCode; int opCode;
switch(op) { switch(op) {
case NEW: case NEW:
opCode = ARRAY_NEW; codeStream.writeOpCode( RTT_CANON );
codeStream.writeValueType( type.getNativeArrayType() );
opCode = ARRAY_NEW_DEFAULT;
break; break;
case GET: case GET:
opCode = ARRAY_GET; opCode = ARRAY_GET;
@ -1374,11 +1376,14 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
case LEN: case LEN:
opCode = ARRAY_LEN; opCode = ARRAY_LEN;
break; break;
case NEW_ARRAY_WITH_RTT:
opCode = ARRAY_NEW_DEFAULT;
break;
default: default:
throw new Error( "Unknown operator: " + op ); throw new Error( "Unknown operator: " + op );
} }
codeStream.writeOpCode( opCode ); codeStream.writeOpCode( opCode );
codeStream.writeValueType( type ); codeStream.writeValueType( type.getNativeArrayType() );
} }
/** /**
@ -1404,6 +1409,12 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
opCode = REF_NULL; opCode = REF_NULL;
type = options.useGC() ? type : ValueType.externref; type = options.useGC() ? type : ValueType.externref;
break; break;
case RTT_CANON:
opCode = RTT_CANON;
break;
case NEW_WITH_RTT:
opCode = STRUCT_NEW;
break;
default: default:
throw new Error( "Unknown operator: " + op ); throw new Error( "Unknown operator: " + op );
} }
@ -1411,7 +1422,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
if( type != null ) { if( type != null ) {
codeStream.writeValueType( type ); codeStream.writeValueType( type );
} }
if( fieldName != null ) { if( idx >= 0 ) {
codeStream.writeVaruint32( idx ); 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"); * 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.
@ -498,6 +498,8 @@ interface InstructionOpcodes {
static final int ARRAY_NEW = 0xFB11; static final int ARRAY_NEW = 0xFB11;
static final int ARRAY_NEW_DEFAULT = 0xFB12;
static final int ARRAY_GET = 0xFB13; static final int ARRAY_GET = 0xFB13;
static final int ARRAY_SET = 0xFB16; 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"); 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.
@ -19,7 +19,6 @@ package de.inetsoftware.jwebassembly.module;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName; 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.AnyType;
import de.inetsoftware.jwebassembly.wasm.ArrayOperator; import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
import de.inetsoftware.jwebassembly.wasm.ArrayType; import de.inetsoftware.jwebassembly.wasm.ArrayType;
import de.inetsoftware.jwebassembly.wasm.StructOperator;
import de.inetsoftware.jwebassembly.wasm.ValueType; import de.inetsoftware.jwebassembly.wasm.ValueType;
/** /**
@ -37,6 +37,7 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
*/ */
class WasmArrayInstruction extends WasmInstruction { class WasmArrayInstruction extends WasmInstruction {
@Nonnull
private final ArrayOperator op; private final ArrayOperator op;
private final AnyType type; private final AnyType type;
@ -61,7 +62,7 @@ class WasmArrayInstruction extends WasmInstruction {
* @param lineNumber * @param lineNumber
* the line number in the Java source code * 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 ); super( javaCodePos, lineNumber );
this.op = op; this.op = op;
this.type = type; 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 * @return the function or null if not needed
*/ */
SyntheticFunctionName createNonGcFunction() { SyntheticFunctionName createNonGcFunction( boolean useGC ) {
// i8 and i16 are not valid in function signatures if( useGC ) {
AnyType functionType = type == ValueType.i8 || type == ValueType.i16 ? ValueType.i32 : type; switch( op ) {
switch( op ) { case NEW:
case NEW: functionName = new WatCodeSyntheticFunctionName( "array_new_" + validJsName( type ), "", ValueType.i32, null, arrayType ) {
String cmd; @Override
if( type.isRefType() ) { protected String getCode() {
cmd = "Object.seal(new Array(l).fill(null))"; String nativeArrayTypeName = ((ArrayType)arrayType.getNativeArrayType()).getName();
} else { return "i32.const " + arrayType.getVTable() + " i32.const 0" // hashcode
switch( (ValueType)type ) { + " local.get 0" // array size
case i8: + " rtt.canon " + nativeArrayTypeName //
cmd = "new Uint8Array(l)"; + " array.new_default_with_rtt " + nativeArrayTypeName //
break; + " rtt.canon " + arrayType.getName() //
case i16: + " struct.new_with_rtt " + arrayType.getName() //
cmd = "new Int16Array(l)"; + " return";
break; }
case i32: };
cmd = "new Int32Array(l)"; }
break; } else {
case i64: // i8 and i16 are not valid in function signatures
cmd = "new BigInt64Array(l)"; AnyType functionType = type == ValueType.i8 || type == ValueType.i16 ? ValueType.i32 : type;
break; switch( op ) {
case f32: case NEW:
cmd = "new Float32Array(l)"; String cmd;
break; if( type.isRefType() ) {
case f64: cmd = "Object.seal(new Array(l).fill(null))";
cmd = "new Float64Array(l)"; } else {
break; switch( (ValueType)type ) {
default: case i8:
cmd = "Object.seal(new Array(l).fill(null))"; 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 ), () -> {
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_new_" + validJsName( type ), () -> { // create the default values of a new type
// create the default values of a new type return new StringBuilder( "(l)=>Object.seal({0:" ) // fix count of elements
return new StringBuilder( "(l)=>Object.seal({0:" ) // fix count of elements .append( arrayType.getVTable() ) // .vtable
.append( arrayType.getVTable() ) // .vtable .append( ",1:0,2:" ) // .hashCode
.append( ",1:0,2:" ) // .hashCode .append( cmd ) // the array data
.append( cmd ) // the array data .append( "})" ) //
.append( "})" ) // .toString();
.toString(); }, ValueType.i32, null, ValueType.externref );
}, ValueType.i32, null, ValueType.externref ); break;
break; case GET:
case GET: functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_get_" + validJsName( functionType ), () -> "(a,i)=>a[2][i]", ValueType.externref, ValueType.i32, null, functionType );
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_get_" + validJsName( functionType ), () -> "(a,i)=>a[2][i]", ValueType.externref, ValueType.i32, null, functionType ); break;
break; case SET:
case SET: functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_set_" + validJsName( functionType ), () -> "(a,i,v)=>a[2][i]=v", ValueType.externref, ValueType.i32, functionType, null, null );
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_set_" + validJsName( functionType ), () -> "(a,i,v)=>a[2][i]=v", ValueType.externref, ValueType.i32, functionType, null, null ); break;
break; case LEN:
case LEN: functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_len", () -> "(a)=>a[2].length", ValueType.externref, null, ValueType.i32 );
functionName = new JavaScriptSyntheticFunctionName( "NonGC", "array_len", () -> "(a)=>a[2].length", ValueType.externref, null, ValueType.i32 ); break;
break; }
} }
return functionName; return functionName;
} }
@ -154,10 +175,19 @@ class WasmArrayInstruction extends WasmInstruction {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException { public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
if( functionName != null ) { // nonGC if( functionName != null ) { // nonGC
writer.writeFunctionCall( functionName, null ); writer.writeFunctionCall( functionName, null );
} else { } 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 ); writer.writeArrayOperator( op, arrayType );
} }
} }
@ -165,9 +195,11 @@ class WasmArrayInstruction extends WasmInstruction {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
AnyType getPushValueType() { AnyType getPushValueType() {
switch( op ) { switch( op ) {
case NEW: case NEW:
case NEW_ARRAY_WITH_RTT:
return arrayType; return arrayType;
case GET: case GET:
return type instanceof ValueType ? (ValueType)type : ValueType.externref; return type instanceof ValueType ? (ValueType)type : ValueType.externref;
@ -187,6 +219,7 @@ class WasmArrayInstruction extends WasmInstruction {
int getPopCount() { int getPopCount() {
switch( op ) { switch( op ) {
case GET: case GET:
case NEW_ARRAY_WITH_RTT:
return 2; return 2;
case NEW: case NEW:
case LEN: 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"); * 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.
@ -737,12 +737,10 @@ public abstract class WasmCodeBuilder {
protected void addArrayInstruction( ArrayOperator op, AnyType type, int javaCodePos, int lineNumber ) { protected void addArrayInstruction( ArrayOperator op, AnyType type, int javaCodePos, int lineNumber ) {
WasmArrayInstruction arrayInst = new WasmArrayInstruction( op, type, types, javaCodePos, lineNumber ); WasmArrayInstruction arrayInst = new WasmArrayInstruction( op, type, types, javaCodePos, lineNumber );
instructions.add( arrayInst ); instructions.add( arrayInst );
if( !options.useGC() ) { SyntheticFunctionName name = arrayInst.createNonGcFunction( options.useGC() );
SyntheticFunctionName name = arrayInst.createNonGcFunction(); if( name != null ) {
if( name != null ) { functions.markAsNeeded( name );
functions.markAsNeeded( name ); functions.markAsImport( name, name.getAnnotation() );
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"); 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.
@ -38,6 +38,7 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
*/ */
class WasmStructInstruction extends WasmInstruction { class WasmStructInstruction extends WasmInstruction {
@Nonnull
private final StructOperator op; private final StructOperator op;
private final StructType type; private final StructType type;
@ -64,7 +65,7 @@ class WasmStructInstruction extends WasmInstruction {
* @param types * @param types
* the type manager * 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 ); super( javaCodePos, lineNumber );
this.op = op; this.op = op;
this.type = types.valueOf( typeName ); this.type = types.valueOf( typeName );
@ -166,6 +167,7 @@ class WasmStructInstruction extends WasmInstruction {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException { public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
int idx = -1; int idx = -1;
switch( op ) { switch( op ) {
@ -199,6 +201,7 @@ class WasmStructInstruction extends WasmInstruction {
case CAST: case CAST:
idx = type.getClassIndex(); idx = type.getClassIndex();
break; break;
default:
} }
if( functionName != null ) { // nonGC if( functionName != null ) { // nonGC
if( idx >= 0 ) { if( idx >= 0 ) {
@ -213,6 +216,7 @@ class WasmStructInstruction extends WasmInstruction {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
AnyType getPushValueType() { AnyType getPushValueType() {
switch( op ) { switch( op ) {
case NULL: case NULL:
@ -220,6 +224,7 @@ class WasmStructInstruction extends WasmInstruction {
case NEW: case NEW:
case NEW_DEFAULT: case NEW_DEFAULT:
case CAST: case CAST:
case NEW_WITH_RTT:
return type; return type;
case GET: case GET:
return fieldName.getType(); return fieldName.getType();
@ -227,6 +232,8 @@ class WasmStructInstruction extends WasmInstruction {
return null; return null;
case INSTANCEOF: case INSTANCEOF:
return ValueType.i32; // a boolean value return ValueType.i32; // a boolean value
case RTT_CANON:
return ValueType.i32; // rtt type
default: default:
throw new WasmException( "Unknown array operation: " + op, -1 ); throw new WasmException( "Unknown array operation: " + op, -1 );
} }
@ -241,12 +248,14 @@ class WasmStructInstruction extends WasmInstruction {
case GET: case GET:
case INSTANCEOF: case INSTANCEOF:
case CAST: case CAST:
case NEW_WITH_RTT:
return 1; return 1;
case SET: case SET:
return 2; return 2;
case NEW: case NEW:
case NEW_DEFAULT: case NEW_DEFAULT:
case NULL: case NULL:
case RTT_CANON:
return 0; return 0;
default: default:
throw new WasmException( "Unknown array operation: " + op, -1 ); 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"); * 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.
@ -831,7 +831,9 @@ public class TextModuleWriter extends ModuleWriter {
String operation; String operation;
switch( op ) { switch( op ) {
case NEW: case NEW:
operation = "new"; newline( methodOutput );
methodOutput.append( "rtt.canon" ).append( ' ' ).append( normalizeName( type.getNativeArrayType().toString() ) );
operation = "new_default_with_rtt";
break; break;
case GET: case GET:
operation = "get"; operation = "get";
@ -842,11 +844,14 @@ public class TextModuleWriter extends ModuleWriter {
case LEN: case LEN:
operation = "len"; operation = "len";
break; break;
case NEW_ARRAY_WITH_RTT:
operation = "new_default_with_rtt";
break;
default: default:
throw new Error( "Unknown operator: " + op ); throw new Error( "Unknown operator: " + op );
} }
newline( methodOutput ); 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: case NEW_DEFAULT:
newline( methodOutput ); newline( methodOutput );
methodOutput.append( "rtt.canon" ).append( ' ' ).append( normalizeName( type.toString() ) ); methodOutput.append( "rtt.canon" ).append( ' ' ).append( normalizeName( type.toString() ) );
operation = "struct.new"; operation = "struct.new_default_with_rtt";
break; break;
case GET: case GET:
operation = "struct.get"; operation = "struct.get";
@ -876,6 +881,12 @@ public class TextModuleWriter extends ModuleWriter {
type = null; type = null;
} }
break; break;
case RTT_CANON:
operation = "rtt.canon";
break;
case NEW_WITH_RTT:
operation = "struct.new_with_rtt";
break;
default: default:
throw new Error( "Unknown operator: " + op ); throw new Error( "Unknown operator: " + op );
} }
@ -884,8 +895,11 @@ public class TextModuleWriter extends ModuleWriter {
if( type != null ) { if( type != null ) {
methodOutput.append( ' ' ).append( normalizeName( type.toString() ) ); methodOutput.append( ' ' ).append( normalizeName( type.toString() ) );
} }
if( fieldName != null ) { if( idx >= 0 ) {
methodOutput.append( ' ' ).append( idx ).append( " ;; $" ).append( normalizeName( fieldName.getName() ) ); 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"); 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.
@ -26,4 +26,5 @@ public enum ArrayOperator {
GET, GET,
SET, SET,
LEN, 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"); 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.
@ -29,4 +29,6 @@ public enum StructOperator {
NULL, NULL,
CAST, CAST,
INSTANCEOF, 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"); 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.
@ -307,6 +307,19 @@ public class WatParser extends WasmCodeBuilder {
AnyType type = ((ArrayType)getTypeManager().valueOf( typeName )).getArrayType(); AnyType type = ((ArrayType)getTypeManager().valueOf( typeName )).getArrayType();
addArrayInstruction( ArrayOperator.LEN, type, javaCodePos, lineNumber ); addArrayInstruction( ArrayOperator.LEN, type, javaCodePos, lineNumber );
break; 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: default:
throw new WasmException( "Unknown WASM token: " + tok, lineNumber ); throw new WasmException( "Unknown WASM token: " + tok, lineNumber );
} }