diff --git a/core/src/main/java/org/teavm/ast/ArrayFromDataExpr.java b/core/src/main/java/org/teavm/ast/ArrayFromDataExpr.java new file mode 100644 index 000000000..e47e5314a --- /dev/null +++ b/core/src/main/java/org/teavm/ast/ArrayFromDataExpr.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * 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 org.teavm.ast; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.teavm.model.ValueType; + +public class ArrayFromDataExpr extends Expr { + private ValueType type; + private final List data = new ArrayList<>(); + + public ValueType getType() { + return type; + } + + public void setType(ValueType type) { + this.type = type; + } + + public List getData() { + return data; + } + + @Override + public void acceptVisitor(ExprVisitor visitor) { + visitor.visit(this); + } + + @Override + protected Expr clone(Map cache) { + Expr known = cache.get(this); + if (known != null) { + return known; + } + ArrayFromDataExpr copy = new ArrayFromDataExpr(); + cache.put(this, copy); + copy.setType(type); + for (Expr elem : data) { + copy.data.add(elem.clone(cache)); + } + return copy; + } +} diff --git a/core/src/main/java/org/teavm/ast/ExprVisitor.java b/core/src/main/java/org/teavm/ast/ExprVisitor.java index 1dc82f080..21ca4616d 100644 --- a/core/src/main/java/org/teavm/ast/ExprVisitor.java +++ b/core/src/main/java/org/teavm/ast/ExprVisitor.java @@ -40,6 +40,8 @@ public interface ExprVisitor { void visit(NewMultiArrayExpr expr); + void visit(ArrayFromDataExpr expr); + void visit(InstanceOfExpr expr); void visit(CastExpr expr); diff --git a/core/src/main/java/org/teavm/ast/NewArrayExpr.java b/core/src/main/java/org/teavm/ast/NewArrayExpr.java index 3aee7fbe3..7e3b4c91f 100644 --- a/core/src/main/java/org/teavm/ast/NewArrayExpr.java +++ b/core/src/main/java/org/teavm/ast/NewArrayExpr.java @@ -52,7 +52,7 @@ public class NewArrayExpr extends Expr { NewArrayExpr copy = new NewArrayExpr(); cache.put(this, copy); copy.setType(type); - copy.setLength(length != null ? length.clone() : null); + copy.setLength(length != null ? length.clone(cache) : null); return copy; } } diff --git a/core/src/main/java/org/teavm/ast/RecursiveVisitor.java b/core/src/main/java/org/teavm/ast/RecursiveVisitor.java index 8c3b42873..f1779cab8 100644 --- a/core/src/main/java/org/teavm/ast/RecursiveVisitor.java +++ b/core/src/main/java/org/teavm/ast/RecursiveVisitor.java @@ -171,6 +171,15 @@ public class RecursiveVisitor implements ExprVisitor, StatementVisitor { afterVisit(expr); } + @Override + public void visit(ArrayFromDataExpr expr) { + beforeVisit(expr); + for (Expr element : expr.getData()) { + element.acceptVisitor(this); + } + afterVisit(expr); + } + @Override public void visit(ReturnStatement statement) { if (statement.getResult() != null) { diff --git a/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java b/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java index f1f421c59..ba2abc7ce 100644 --- a/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java +++ b/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java @@ -23,6 +23,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import org.teavm.ast.ArrayFromDataExpr; +import org.teavm.ast.ArrayType; import org.teavm.ast.AssignmentStatement; import org.teavm.ast.BinaryExpr; import org.teavm.ast.BinaryOperation; @@ -63,6 +65,7 @@ import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.VariableExpr; import org.teavm.ast.WhileStatement; import org.teavm.model.TextLocation; +import org.teavm.model.ValueType; class OptimizingVisitor implements StatementVisitor, ExprVisitor { private static final int MAX_DEPTH = 20; @@ -78,6 +81,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { private TextLocation currentLocation; private Deque locationStack = new LinkedList<>(); private Deque notNullLocationStack = new ArrayDeque<>(); + private List pendingArrayOptimizations; OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies, Object[] constants, boolean friendlyToDebugger) { @@ -505,6 +509,21 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { } } + @Override + public void visit(ArrayFromDataExpr expr) { + pushLocation(expr.getLocation()); + try { + for (int i = 0; i < expr.getData().size(); ++i) { + Expr element = expr.getData().get(i); + element.acceptVisitor(this); + expr.getData().set(i, resultExpr); + } + resultExpr = expr; + } finally { + popLocation(); + } + } + @Override public void visit(InstanceOfExpr expr) { pushLocation(expr.getLocation()); @@ -580,10 +599,13 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { private List processSequence(List statements) { List backup = resultSequence; resultSequence = new ArrayList<>(); + List pendingArrayOptimizationsBackup = pendingArrayOptimizations; + pendingArrayOptimizations = new ArrayList<>(); processSequenceImpl(statements); wieldTryCatch(resultSequence); List result = resultSequence.stream().filter(part -> part != null).collect(Collectors.toList()); resultSequence = backup; + pendingArrayOptimizations = pendingArrayOptimizationsBackup; return result; } @@ -604,6 +626,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { continue; } resultSequence.add(part); + tryArrayOptimization(); if (part instanceof BreakStatement) { return false; } @@ -611,6 +634,153 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { return true; } + private void tryArrayOptimization() { + Statement statement; + while (!pendingArrayOptimizations.isEmpty()) { + statement = resultSequence.get(resultSequence.size() - 1); + int i = pendingArrayOptimizations.size() - 1; + if (!tryArrayUnwrap(pendingArrayOptimizations.get(i), statement) + || tryArraySet(pendingArrayOptimizations.get(i), statement)) { + pendingArrayOptimizations.remove(i); + } else { + break; + } + } + statement = resultSequence.get(resultSequence.size() - 1); + tryArrayConstruction(statement); + } + + private void tryArrayConstruction(Statement statement) { + if (!(statement instanceof AssignmentStatement)) { + return; + } + AssignmentStatement assign = (AssignmentStatement) statement; + + if (!(assign.getLeftValue() instanceof VariableExpr)) { + return; + } + int constructedArrayVariable = ((VariableExpr) assign.getLeftValue()).getIndex(); + + if (!(assign.getRightValue() instanceof NewArrayExpr)) { + return; + } + NewArrayExpr constructedArray = (NewArrayExpr) assign.getRightValue(); + if (!(constructedArray.getLength() instanceof ConstantExpr)) { + return; + } + + Object sizeConst = ((ConstantExpr) constructedArray.getLength()).getValue(); + if (!(sizeConst instanceof Integer)) { + return; + } + + int constructedArraySize = (int) sizeConst; + ArrayOptimization optimization = new ArrayOptimization(); + optimization.index = resultSequence.size() - 1; + optimization.arrayVariable = constructedArrayVariable; + optimization.arraySize = constructedArraySize; + optimization.array = constructedArray; + pendingArrayOptimizations.add(optimization); + } + + private boolean tryArrayUnwrap(ArrayOptimization optimization, Statement statement) { + if (optimization.unwrappedArray != null) { + return true; + } + + if (!(statement instanceof AssignmentStatement)) { + return false; + } + AssignmentStatement assign = (AssignmentStatement) statement; + + if (!(assign.getLeftValue() instanceof VariableExpr)) { + return false; + } + optimization.unwrappedArrayVariable = ((VariableExpr) assign.getLeftValue()).getIndex(); + if (writeFrequencies[optimization.unwrappedArrayVariable] != 1) { + return false; + } + + if (!(assign.getRightValue() instanceof UnwrapArrayExpr)) { + return false; + } + optimization.unwrappedArray = (UnwrapArrayExpr) assign.getRightValue(); + + if (!(optimization.unwrappedArray.getArray() instanceof VariableExpr)) { + return false; + } + + VariableExpr arrayVar = (VariableExpr) optimization.unwrappedArray.getArray(); + if (arrayVar.getIndex() != optimization.arrayVariable) { + return false; + } + + if (!matchArrayType(optimization.array.getType(), optimization.unwrappedArray.getElementType())) { + return false; + } + + if (optimization.arraySize != readFrequencies[optimization.unwrappedArrayVariable]) { + return false; + } + + optimization.arrayElementIndex = 0; + return true; + } + + private boolean tryArraySet(ArrayOptimization optimization, Statement statement) { + int expectedIndex = optimization.index + 2 + optimization.arrayElementIndex; + if (resultSequence.size() - 1 != expectedIndex) { + return false; + } + + if (!(statement instanceof AssignmentStatement)) { + return false; + } + AssignmentStatement assign = (AssignmentStatement) statement; + + if (!(assign.getLeftValue() instanceof SubscriptExpr)) { + return false; + } + SubscriptExpr subscript = (SubscriptExpr) assign.getLeftValue(); + + if (subscript.getType() != optimization.unwrappedArray.getElementType()) { + return false; + } + + if (!(subscript.getArray() instanceof VariableExpr)) { + return false; + } + if (((VariableExpr) subscript.getArray()).getIndex() != optimization.unwrappedArrayVariable) { + return false; + } + + if (!(subscript.getIndex() instanceof ConstantExpr)) { + return false; + } + Object constantValue = ((ConstantExpr) subscript.getIndex()).getValue(); + if (!Integer.valueOf(optimization.arrayElementIndex).equals(constantValue)) { + return false; + } + + optimization.elements.add(assign.getRightValue()); + if (++optimization.arrayElementIndex == optimization.arraySize) { + applyArrayOptimization(optimization); + return true; + } + return false; + } + + private void applyArrayOptimization(ArrayOptimization optimization) { + AssignmentStatement assign = (AssignmentStatement) resultSequence.get(optimization.index); + ArrayFromDataExpr arrayFromData = new ArrayFromDataExpr(); + arrayFromData.setLocation(optimization.array.getLocation()); + arrayFromData.setType(optimization.array.getType()); + arrayFromData.getData().addAll(optimization.elements); + assign.setRightValue(arrayFromData); + readFrequencies[optimization.arrayVariable]--; + resultSequence.subList(optimization.index + 1, resultSequence.size()).clear(); + } + private void wieldTryCatch(List statements) { for (int i = 0; i < statements.size() - 1; ++i) { if (statements.get(i) instanceof TryCatchStatement && statements.get(i + 1) instanceof TryCatchStatement) { @@ -645,6 +815,29 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { return false; } + private static boolean matchArrayType(ValueType type, ArrayType arrayType) { + switch (arrayType) { + case BYTE: + return type == ValueType.BYTE || type == ValueType.BOOLEAN; + case SHORT: + return type == ValueType.SHORT; + case CHAR: + return type == ValueType.CHARACTER; + case INT: + return type == ValueType.INTEGER; + case LONG: + return type == ValueType.LONG; + case FLOAT: + return type == ValueType.FLOAT; + case DOUBLE: + return type == ValueType.DOUBLE; + case OBJECT: + return type instanceof ValueType.Object || type instanceof ValueType.Array; + default: + return false; + } + } + private void eliminateRedundantBreaks(List statements, IdentifiedStatement exit) { if (statements.isEmpty()) { return; @@ -1122,4 +1315,15 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { } return expr; } + + static class ArrayOptimization { + int index; + NewArrayExpr array; + int arrayVariable; + UnwrapArrayExpr unwrappedArray; + int unwrappedArrayVariable; + int arrayElementIndex; + int arraySize; + List elements = new ArrayList<>(); + } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index 2abbefb24..7f45a9b7f 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.teavm.ast.ArrayFromDataExpr; import org.teavm.ast.ArrayType; import org.teavm.ast.AssignmentStatement; import org.teavm.ast.BinaryExpr; @@ -947,6 +948,69 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { popLocation(expr.getLocation()); } + @Override + public void visit(ArrayFromDataExpr expr) { + pushLocation(expr.getLocation()); + + boolean needParenthesis = false; + if (needsCallSiteId()) { + needParenthesis = true; + withCallSite(); + } + + if (expr.getType() instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) expr.getType()).getKind()) { + case BOOLEAN: + writer.print("teavm_fillBooleanArray"); + break; + case BYTE: + writer.print("teavm_fillByteArray"); + break; + case SHORT: + writer.print("teavm_fillShortArray"); + break; + case CHARACTER: + writer.print("teavm_fillCharArray"); + break; + case INTEGER: + writer.print("teavm_fillIntArray"); + break; + case LONG: + writer.print("teavm_fillLongArray"); + break; + case FLOAT: + writer.print("teavm_fillFloatArray"); + break; + case DOUBLE: + writer.print("teavm_fillDoubleArray"); + break; + } + } else { + writer.print("teavm_fillArray"); + } + writer.print("("); + + ValueType type = ValueType.arrayOf(expr.getType()); + writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&") + .print(names.forClassInstance(type)).print(", "); + classContext.importMethod(ALLOC_ARRAY_METHOD, true); + includes.includeType(type); + writer.print(expr.getData().size() + ")"); + + for (Expr element : expr.getData()) { + writer.print(", "); + element.acceptVisitor(this); + } + + writer.print(")"); + + if (needParenthesis) { + writer.print(")"); + } + + popLocation(expr.getLocation()); + } + @Override public void visit(NewMultiArrayExpr expr) { pushLocation(expr.getLocation()); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java index c9182a4f9..4f2e29f1c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java @@ -16,6 +16,7 @@ package org.teavm.backend.javascript.rendering; import java.util.Set; +import org.teavm.ast.ArrayFromDataExpr; import org.teavm.ast.AssignmentStatement; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; @@ -295,6 +296,42 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit } } + @Override + public void visit(ArrayFromDataExpr expr) { + super.visit(expr); + visitType(expr.getType()); + if (expr.getType() instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) expr.getType()).getKind()) { + case BOOLEAN: + consumer.consumeFunction("$rt_createBooleanArrayFromData"); + break; + case BYTE: + consumer.consumeFunction("$rt_createByteArrayFromData"); + break; + case SHORT: + consumer.consumeFunction("$rt_createShortArrayFromData"); + break; + case CHARACTER: + consumer.consumeFunction("$rt_createCharArrayFromData"); + break; + case INTEGER: + consumer.consumeFunction("$rt_createIntArrayFromData"); + break; + case LONG: + consumer.consumeFunction("$rt_createLongArrayFromData"); + break; + case FLOAT: + consumer.consumeFunction("$rt_createFloatArrayFromData"); + break; + case DOUBLE: + consumer.consumeFunction("$rt_createDoubleArrayFromData"); + break; + } + } else { + consumer.consumeFunction("$rt_createArrayFromData"); + } + } + @Override public void visit(NewMultiArrayExpr expr) { super.visit(expr); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index fed49b6af..e24ad3299 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -255,7 +255,10 @@ public class Renderer implements RenderingManager { "$rt_s", "$rt_eraseClinit", "$rt_imul", "$rt_wrapException", "$rt_checkBounds", "$rt_checkUpperBound", "$rt_checkLowerBound", "$rt_wrapFunction0", "$rt_wrapFunction1", "$rt_wrapFunction2", "$rt_wrapFunction3", "$rt_wrapFunction4", - "$rt_classWithoutFields" }; + "$rt_classWithoutFields", "$rt_createArrayFromData", "$rt_createCharArrayFromData", + "$rt_createByteArrayFromData", "$rt_createShortArrayFromData", "$rt_createIntArrayFromData", + "$rt_createBooleanArrayFromData", "$rt_createFloatArrayFromData", "$rt_createDoubleArrayFromData", + "$rt_createLongArrayFromData" }; boolean first = true; for (String name : names) { if (!first) { diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 977b210b3..87612076b 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import org.teavm.ast.ArrayFromDataExpr; import org.teavm.ast.AssignmentStatement; import org.teavm.ast.BinaryExpr; import org.teavm.ast.BinaryOperation; @@ -1326,6 +1327,71 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { } } + @Override + public void visit(ArrayFromDataExpr expr) { + try { + if (expr.getLocation() != null) { + pushLocation(expr.getLocation()); + } + ValueType type = expr.getType(); + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + writer.appendFunction("$rt_createBooleanArrayFromData"); + break; + case BYTE: + writer.appendFunction("$rt_createByteArrayFromData"); + break; + case SHORT: + writer.appendFunction("$rt_createShortArrayFromData"); + break; + case INTEGER: + writer.appendFunction("$rt_createIntArrayFromData"); + break; + case LONG: + writer.appendFunction("$rt_createLongArrayFromData"); + break; + case FLOAT: + writer.appendFunction("$rt_createFloatArrayFromData"); + break; + case DOUBLE: + writer.appendFunction("$rt_createDoubleArrayFromData"); + break; + case CHARACTER: + writer.appendFunction("$rt_createCharArrayFromData"); + break; + } + writer.append("("); + } else { + writer.appendFunction("$rt_createArrayFromData").append("("); + context.typeToClsString(writer, expr.getType()); + writer.append(",").ws(); + } + + writer.append("["); + writeCommaSeparated(expr.getData()); + writer.append("])"); + + if (expr.getLocation() != null) { + popLocation(); + } + } catch (IOException e) { + throw new RenderingException("IO error occurred", e); + } + } + + private void writeCommaSeparated(List expressions) throws IOException { + boolean first = true; + for (Expr element : expressions) { + if (!first) { + writer.append(",").ws(); + } + first = false; + precedence = Precedence.min(); + element.acceptVisitor(this); + } + } + @Override public void visit(NewMultiArrayExpr expr) { try { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 952c392af..b0658276d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -25,6 +25,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.teavm.ast.ArrayFromDataExpr; +import org.teavm.ast.ArrayType; import org.teavm.ast.AssignmentStatement; import org.teavm.ast.BinaryExpr; import org.teavm.ast.BlockStatement; @@ -524,32 +526,34 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } private void storeArrayItem(SubscriptExpr leftValue, Expr rightValue) { - WasmExpression ptr = getArrayElementPointer(leftValue); - accept(rightValue); + leftValue.getArray().acceptVisitor(this); + WasmExpression array = result; + leftValue.getIndex().acceptVisitor(this); + WasmExpression index = result; + rightValue.acceptVisitor(this); + WasmExpression value = result; + result = storeArrayItem(getArrayElementPointer(array, index, leftValue.getType()), value, leftValue.getType()); + } - switch (leftValue.getType()) { + private static WasmExpression storeArrayItem(WasmExpression array, WasmExpression value, ArrayType type) { + switch (type) { case BYTE: - result = new WasmStoreInt32(1, ptr, result, WasmInt32Subtype.INT8); - break; + return new WasmStoreInt32(1, array, value, WasmInt32Subtype.INT8); case SHORT: - result = new WasmStoreInt32(2, ptr, result, WasmInt32Subtype.INT16); - break; + return new WasmStoreInt32(2, array, value, WasmInt32Subtype.INT16); case CHAR: - result = new WasmStoreInt32(2, ptr, result, WasmInt32Subtype.UINT16); - break; + return new WasmStoreInt32(2, array, value, WasmInt32Subtype.UINT16); case INT: case OBJECT: - result = new WasmStoreInt32(4, ptr, result, WasmInt32Subtype.INT32); - break; + return new WasmStoreInt32(4, array, value, WasmInt32Subtype.INT32); case LONG: - result = new WasmStoreInt64(8, ptr, result, WasmInt64Subtype.INT64); - break; + return new WasmStoreInt64(8, array, value, WasmInt64Subtype.INT64); case FLOAT: - result = new WasmStoreFloat32(4, ptr, result); - break; + return new WasmStoreFloat32(4, array, value); case DOUBLE: - result = new WasmStoreFloat64(8, ptr, result); - break; + return new WasmStoreFloat64(8, array, value); + default: + throw new IllegalArgumentException(); } } @@ -666,14 +670,16 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } private WasmExpression getArrayElementPointer(SubscriptExpr expr) { - accept(expr.getArray()); + expr.getArray().acceptVisitor(this); WasmExpression array = result; - - accept(expr.getIndex()); + expr.getIndex().acceptVisitor(this); WasmExpression index = result; + return getArrayElementPointer(array, index, expr.getType()); + } + private WasmExpression getArrayElementPointer(WasmExpression array, WasmExpression index, ArrayType type) { int size = -1; - switch (expr.getType()) { + switch (type) { case BYTE: size = 0; break; @@ -1236,6 +1242,62 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { result = call; } + @Override + public void visit(ArrayFromDataExpr expr) { + ValueType type = expr.getType(); + + ArrayType arrayType = ArrayType.OBJECT; + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + case BYTE: + arrayType = ArrayType.BYTE; + break; + case SHORT: + arrayType = ArrayType.SHORT; + break; + case CHARACTER: + arrayType = ArrayType.CHAR; + break; + case INTEGER: + arrayType = ArrayType.INT; + break; + case LONG: + arrayType = ArrayType.LONG; + break; + case FLOAT: + arrayType = ArrayType.FLOAT; + break; + case DOUBLE: + arrayType = ArrayType.DOUBLE; + break; + } + } + + WasmBlock block = new WasmBlock(false); + + WasmLocal array = getTemporary(WasmType.INT32); + int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); + String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateArray", + RuntimeClass.class, int.class, Address.class)); + WasmCall call = new WasmCall(allocName); + call.getArguments().add(new WasmInt32Constant(classPointer)); + call.getArguments().add(new WasmInt32Constant(expr.getData().size())); + call.setLocation(expr.getLocation()); + block.getBody().add(new WasmSetLocal(array, call)); + + for (int i = 0; i < expr.getData().size(); ++i) { + WasmExpression ptr = getArrayElementPointer(new WasmGetLocal(array), new WasmInt32Constant(i), arrayType); + expr.getData().get(i).acceptVisitor(this); + block.getBody().add(storeArrayItem(ptr, result, arrayType)); + } + + block.getBody().add(new WasmGetLocal(array)); + block.setLocation(expr.getLocation()); + + result = block; + } + @Override public void visit(NewMultiArrayExpr expr) { ValueType type = expr.getType(); diff --git a/core/src/main/java/org/teavm/cache/AstIO.java b/core/src/main/java/org/teavm/cache/AstIO.java index 43bd55bd8..8139a047e 100644 --- a/core/src/main/java/org/teavm/cache/AstIO.java +++ b/core/src/main/java/org/teavm/cache/AstIO.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import org.teavm.ast.ArrayFromDataExpr; import org.teavm.ast.ArrayType; import org.teavm.ast.AssignmentStatement; import org.teavm.ast.AsyncMethodNode; @@ -682,6 +683,20 @@ public class AstIO { } } + @Override + public void visit(ArrayFromDataExpr expr) { + try { + output.writeUnsigned(28); + output.writeUnsigned(symbolTable.lookup(expr.getType().toString())); + output.writeUnsigned(expr.getData().size()); + for (Expr element : expr.getData()) { + writeExpr(element); + } + } catch (IOException e) { + throw new IOExceptionWrapper(e); + } + } + @Override public void visit(InstanceOfExpr expr) { try { @@ -1117,6 +1132,15 @@ public class AstIO { expr.setLower(true); return expr; } + case 28: { + ArrayFromDataExpr expr = new ArrayFromDataExpr(); + expr.setType(ValueType.parse(symbolTable.at(input.readUnsigned()))); + int count = input.readUnsigned(); + for (int i = 0; i < count; ++i) { + expr.getData().add(readExpr(input)); + } + return expr; + } default: throw new RuntimeException("Unknown expression type: " + type); } diff --git a/core/src/main/resources/org/teavm/backend/c/core.c b/core/src/main/resources/org/teavm/backend/c/core.c index bf943341b..8526ea05a 100644 --- a/core/src/main/resources/org/teavm/backend/c/core.c +++ b/core/src/main/resources/org/teavm/backend/c/core.c @@ -8,6 +8,7 @@ #include #include #include +#include #if TEAVM_INCREMENTAL #include "virtcall.h" @@ -70,4 +71,26 @@ void teavm_initClasses() { for (int i = 0; i < teavm_classReferencesCount; ++i) { teavm_classReferences[i]->parent.header = classHeader; } -} \ No newline at end of file +} + +#define TEAVM_FILL_ARRAY_F(name, type, arrayType) \ + void* name(void* array, ...) { \ + type* data = TEAVM_ARRAY_DATA(array, type); \ + int32_t size = TEAVM_ARRAY_LENGTH(array); \ + va_list args; \ + va_start(args, array); \ + for (int32_t i = 0; i < size; ++i) { \ + *data++ = (type) va_arg(args, arrayType); \ + } \ + va_end(args); \ + } + +TEAVM_FILL_ARRAY_F(teavm_fillArray, void*, void*) +TEAVM_FILL_ARRAY_F(teavm_fillBooleanArray, int8_t, int) +TEAVM_FILL_ARRAY_F(teavm_fillByteArray, int8_t, int) +TEAVM_FILL_ARRAY_F(teavm_fillShortArray, int16_t, int) +TEAVM_FILL_ARRAY_F(teavm_fillCharArray, char16_t, int) +TEAVM_FILL_ARRAY_F(teavm_fillIntArray, int32_t, int) +TEAVM_FILL_ARRAY_F(teavm_fillLongArray, int64_t, int64_t) +TEAVM_FILL_ARRAY_F(teavm_fillFloatArray, float, double) +TEAVM_FILL_ARRAY_F(teavm_fillDoubleArray, double, double) diff --git a/core/src/main/resources/org/teavm/backend/c/core.h b/core/src/main/resources/org/teavm/backend/c/core.h index 935012eb2..940d3d0d7 100644 --- a/core/src/main/resources/org/teavm/backend/c/core.h +++ b/core/src/main/resources/org/teavm/backend/c/core.h @@ -185,4 +185,14 @@ extern void teavm_initClasses(); inline static void teavm_gc_writeBarrier(void* object) { intptr_t offset = (intptr_t) ((char*) object - (char*) teavm_gc_heapAddress) / teavm_gc_regionSize; ((char*) teavm_gc_cardTable)[offset] = 0; -} \ No newline at end of file +} + +extern void* teavm_fillArray(void* array, ...); +extern void* teavm_fillBooleanArray(void* array, ...); +extern void* teavm_fillByteArray(void* array, ...); +extern void* teavm_fillShortArray(void* array, ...); +extern void* teavm_fillCharArray(void* array, ...); +extern void* teavm_fillIntArray(void* array, ...); +extern void* teavm_fillLongArray(void* array, ...); +extern void* teavm_fillFloatArray(void* array, ...); +extern void* teavm_fillDoubleArray(void* array, ...); diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index 90c4e2d24..3ce9495cb 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -62,9 +62,11 @@ Array.prototype.fill = Array.prototype.fill || function(value,start,end) { }; function $rt_createArray(cls, sz) { var data = new Array(sz); - var arr = new $rt_array(cls, data); data.fill(null); - return arr; + return new $rt_array(cls, data); +} +function $rt_createArrayFromData(cls, init) { + return $rt_wrapArray(cls, init); } function $rt_wrapArray(cls, data) { return new $rt_array(cls, data); @@ -78,30 +80,68 @@ function $rt_createLongArray(sz) { data.fill(Long_ZERO); return arr; } +function $rt_createLongArrayFromData(init) { + return new $rt_array($rt_longcls(), init); +} function $rt_createNumericArray(cls, nativeArray) { return new $rt_array(cls, nativeArray); } function $rt_createCharArray(sz) { return $rt_createNumericArray($rt_charcls(), new Uint16Array(sz)); } +function $rt_createCharArrayFromData(data) { + var buffer = new Uint16Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_charcls(), buffer); +} function $rt_createByteArray(sz) { return $rt_createNumericArray($rt_bytecls(), new Int8Array(sz)); } +function $rt_createByteArrayFromData(data) { + var buffer = new Int8Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_bytecls(), buffer); +} function $rt_createShortArray(sz) { return $rt_createNumericArray($rt_shortcls(), new Int16Array(sz)); } +function $rt_createShortArrayFromData(data) { + var buffer = new Int16Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_shortcls(), buffer); +} function $rt_createIntArray(sz) { return $rt_createNumericArray($rt_intcls(), new Int32Array(sz)); } +function $rt_createIntArrayFromData(data) { + var buffer = new Int32Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_intcls(), buffer); +} function $rt_createBooleanArray(sz) { return $rt_createNumericArray($rt_booleancls(), new Int8Array(sz)); } +function $rt_createBooleanArrayFromData(data) { + var buffer = new Int8Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_booleancls(), buffer); +} function $rt_createFloatArray(sz) { return $rt_createNumericArray($rt_floatcls(), new Float32Array(sz)); } +function $rt_createFloatArrayFromData(data) { + var buffer = new Float32Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_floatcls(), buffer); +} function $rt_createDoubleArray(sz) { return $rt_createNumericArray($rt_doublecls(), new Float64Array(sz)); } +function $rt_createDoubleArrayFromData(data) { + var buffer = new Float64Array(data.length); + buffer.set(data); + return $rt_createNumericArray($rt_doublecls(), buffer); +} function $rt_arraycls(cls) { var result = cls.$array;