From ca60af3b89428af0dfc5b3a019a6083dedd53e9e Mon Sep 17 00:00:00 2001 From: Florian Huemer Date: Tue, 12 Apr 2022 16:28:49 +0200 Subject: [PATCH 1/4] Introduced compact and extended extra data array format. --- .../org/graalvm/wasm/test/WasmTestSuite.java | 2 + .../wasm/test/suites/ExtraDataSuite.java | 408 ++++++++++++++++++ .../src/org/graalvm/wasm/BinaryParser.java | 12 +- .../src/org/graalvm/wasm/WasmCodeEntry.java | 93 ---- .../wasm/constants/ExtraDataOffsets.java | 216 ---------- .../org/graalvm/wasm/exception/Failure.java | 4 +- .../graalvm/wasm/nodes/WasmFunctionNode.java | 228 +++++++--- .../wasm/parser/validation/BlockFrame.java | 14 +- .../wasm/parser/validation/ControlFrame.java | 60 +-- .../wasm/parser/validation/ExtraDataList.java | 265 ------------ .../wasm/parser/validation/IfFrame.java | 37 +- .../wasm/parser/validation/LoopFrame.java | 27 +- .../wasm/parser/validation/ParserState.java | 47 +- .../{ => collections}/ControlStack.java | 22 +- .../collections/ExtraDataFormatHelper.java | 52 +++ .../validation/collections/ExtraDataList.java | 200 +++++++++ .../collections/entries/BranchTableEntry.java | 128 ++++++ .../collections/entries/BranchTarget.java | 123 ++++++ .../entries/BranchTargetWithStackChange.java | 82 ++++ .../collections/entries/CallEntry.java | 90 ++++ .../collections/entries/CallTarget.java | 65 +++ .../entries/ConditionalBranchEntry.java | 93 ++++ .../collections/entries/ElseEntry.java | 90 ++++ .../collections/entries/ExtraDataEntry.java | 118 +++++ .../collections/entries/IfEntry.java | 93 ++++ .../entries/IndirectCallEntry.java | 79 ++++ .../entries/UnconditionalBranchEntry.java | 92 ++++ .../graalvm/wasm/util/ExtraDataAccessor.java | 118 +++++ .../org/graalvm/wasm/util/ExtraDataUtil.java | 359 +++++++++++++++ 29 files changed, 2452 insertions(+), 765 deletions(-) create mode 100644 wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java delete mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/ExtraDataOffsets.java delete mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ExtraDataList.java rename wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/{ => collections}/ControlStack.java (89%) create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataFormatHelper.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ExtraDataEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IndirectCallEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java create mode 100644 wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmTestSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmTestSuite.java index 702c9ba5a9b6..2711aaa704c0 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmTestSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmTestSuite.java @@ -40,6 +40,7 @@ */ package org.graalvm.wasm.test; +import org.graalvm.wasm.test.suites.ExtraDataSuite; import org.graalvm.wasm.test.suites.WasmImplementationLimitationsSuite; import org.graalvm.wasm.test.suites.arithmetic.Float32Suite; import org.graalvm.wasm.test.suites.arithmetic.Float64Suite; @@ -83,6 +84,7 @@ ValidationSuite.class, WasmLateLinkingSuite.class, WasmImplementationLimitationsSuite.class, + ExtraDataSuite.class }) public class WasmTestSuite { @Test diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java new file mode 100644 index 000000000000..c698b1273c22 --- /dev/null +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.test.suites; + +import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.parser.validation.collections.ExtraDataList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTableEntry; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTarget; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; +import org.junit.Assert; +import org.junit.Test; + +public class ExtraDataSuite { + @Test + public void testCompactByteCodeDisplacementSimpleForwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + t.setTargetInfo(1, 0, 0); + final int[] expected = {1, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactByteCodeDisplacementSimpleBackwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(1); + t.setTargetInfo(0, 0, 0); + final int[] expected = {65535, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactByteCodeDisplacementMaxForwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + t.setTargetInfo(32767, 0, 0); + final int[] expected = {32767, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactBytecodeDisplacementMaxBackwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(32768); + t.setTargetInfo(0, 0, 0); + final int[] expected = {32768, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedByteCodeDisplacementMinForwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + t.setTargetInfo(32768, 0, 0); + final int[] expected = {-2147483648, 32768, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedByteCodeDisplacementMinBackwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(32769); + t.setTargetInfo(0, 0, 0); + final int[] expected = {-2147483648, -32769, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedByteCodeDisplacementMaxForwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + t.setTargetInfo(2147483647, 0, 0); + final int[] expected = {-2147483648, 2147483647, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedByteCodeDisplacementMaxBackwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(2147483647); + t.setTargetInfo(0, 0, 0); + final int[] expected = {-2147483648, -2147483647, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactExtraDataDisplacementSimpleForwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + l.addCall(0); + t.setTargetInfo(0, 2, 1); + final int[] expected = {131072, 0, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactExtraDataDisplacementSimpleBackwardJump() { + ExtraDataList l = new ExtraDataList(); + l.addCall(0); + BranchTarget t = l.addIf(0); + t.setTargetInfo(0, 0, 0); + final int[] expected = {0, 2147418112, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactExtraDataDisplacementMaxForwardJump() { + ExtraDataList l = new ExtraDataList(); + int displacement = 16383; + BranchTarget t = l.addIf(0); + for (int i = 0; i < displacement - 1; i++) { + l.addCall(0); + } + t.setTargetInfo(0, displacement, displacement - 2); + final int[] expected = new int[displacement + 1]; + expected[0] = 1073676288; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactExtraDataDisplacementMaxBackwardJump() { + ExtraDataList l = new ExtraDataList(); + int displacement = 16384; + for (int i = 0; i < displacement; i++) { + l.addCall(0); + } + BranchTarget t = l.addIf(displacement); + t.setTargetInfo(displacement, 0, 0); + final int[] expected = new int[displacement + 2]; + expected[displacement] = 1073741824; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedExtraDataDisplacementMinForwardJump() { + ExtraDataList l = new ExtraDataList(); + int displacement = 16384; + BranchTarget t = l.addIf(0); + for (int i = 0; i < displacement - 1; i++) { + l.addCall(0); + } + t.setTargetInfo(0, displacement, displacement - 2); + final int[] expected = new int[displacement + 2]; + expected[0] = -2147467264; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedExtraDataDisplacementMinBackwardJump() { + ExtraDataList l = new ExtraDataList(); + int displacement = 16385; + for (int i = 0; i < displacement; i++) { + l.addCall(0); + } + BranchTarget t = l.addIf(displacement); + t.setTargetInfo(displacement, 0, 0); + final int[] expected = new int[displacement + 3]; + expected[displacement] = -2147467263; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedExtraDataDisplacementOutOfBoundsForwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + try { + t.setTargetInfo(0, 1073741824, 0); + Assert.fail("Should have thrown exception"); + } catch (WasmException e) { + Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + } + } + + @Test + public void testExtendedExtraDataDisplacementOutOfBoundsBackwardJump() { + ExtraDataList l = new ExtraDataList(); + BranchTarget t = l.addIf(0); + try { + t.setTargetInfo(0, -1073741825, 0); + Assert.fail("Should have thrown exception"); + } catch (WasmException e) { + Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + } + } + + @Test + public void testCompactReturnLength() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(1, 0); + final int[] expected = {0, 16777216}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactReturnLengthMaxValue() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(255, 0); + final int[] expected = {0, -16777216}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedReturnLengthMinValue() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(256, 0); + final int[] expected = {-2147483648, 0, 256, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedReturnLengthMaxValue() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(2147483647, 0); + final int[] expected = {-2147483648, 0, 2147483647, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedReturnLengthOutOfBounds() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + try { + t.setStackInfo(-1, 0); + Assert.fail("Should have thrown exception"); + } catch (WasmException e) { + Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + } + } + + @Test + public void testCompactStackSize() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(0, 1); + final int[] expected = {0, 65536}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactStackSizeMaxValue() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(0, 255); + final int[] expected = {0, 16711680}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedStackSizeMinValue() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(0, 256); + final int[] expected = {-2147483648, 0, 0, 256}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedStackSizeMaxValue() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + t.setStackInfo(0, 2147483647); + final int[] expected = {-2147483648, 0, 0, 2147483647}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedStackSizeOutOfBounds() { + ExtraDataList l = new ExtraDataList(); + BranchTargetWithStackChange t = l.addUnconditionalBranch(0); + try { + t.setStackInfo(0, -1); + Assert.fail("Should have thrown exception"); + } catch (WasmException e) { + Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + } + } + + @Test + public void testCompactTableHeader() { + ExtraDataList l = new ExtraDataList(); + l.addBranchTable(0, 1); + final int[] expected = {65536, 0, 0}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactTableHeaderMaxValue() { + ExtraDataList l = new ExtraDataList(); + l.addBranchTable(0, 32767); + final int[] expected = new int[1 + 2 * 32767]; + expected[0] = 2147418112; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedTableHeaderMinValue() { + ExtraDataList l = new ExtraDataList(); + l.addBranchTable(0, 32768); + final int[] expected = new int[2 + 5 * 32768]; + expected[0] = -2147450880; + for (int i = 2; i < expected.length; i += 5) { + expected[i] = -2147483648; + } + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedTableHeaderOutOfBounds() { + ExtraDataList l = new ExtraDataList(); + try { + l.addBranchTable(0, -1); + Assert.fail("Should have thrown exception"); + } catch (WasmException e) { + Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + } + } + + @Test + public void testTableHeaderWithExtendedItem() { + ExtraDataList l = new ExtraDataList(); + BranchTableEntry b = l.addBranchTable(0, 2); + b.item(0).setTargetInfo(32768, 0, 0); + for (int i = 0; i < 32764; i++) { + l.addCall(0); + } + final int[] expected = new int[12 + 32764]; + expected[0] = -2147483646; + expected[2] = -2147483648; + expected[3] = 32768; + expected[7] = -2147483648; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactCallIndex() { + ExtraDataList l = new ExtraDataList(); + l.addCall(1); + final int[] expected = {65536}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testCompactCallIndexMaxValue() { + ExtraDataList l = new ExtraDataList(); + l.addCall(32767); + final int[] expected = {2147418112}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedCallIndexMinValue() { + ExtraDataList l = new ExtraDataList(); + l.addCall(32768); + final int[] expected = {-2147450880}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } + + @Test + public void testExtendedCallIndexMaxValue() { + ExtraDataList l = new ExtraDataList(); + l.addCall(2147483647); + final int[] expected = {-1}; + Assert.assertArrayEquals(expected, l.extraDataArray()); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java index ca1ff792c2fb..ca6ad89b30b7 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java @@ -435,10 +435,11 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte returnType break; } case Instructions.IF: { + final int ifOffset = offset; state.popChecked(I32_TYPE); // condition final byte ifReturnType = readBlockType(); - state.enterIf(ifReturnType); + state.enterIf(ifReturnType, ifOffset); break; } case Instructions.END: { @@ -450,21 +451,24 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte returnType break; } case Instructions.BR: { + final int brOffset = offset; final int branchLabel = readTargetOffset(); - state.addUnconditionalBranch(branchLabel); + state.addUnconditionalBranch(branchLabel, brOffset); // This instruction is stack-polymorphic state.setUnreachable(); break; } case Instructions.BR_IF: { + final int brIfOffset = offset; final int branchLabel = readTargetOffset(); state.popChecked(I32_TYPE); // condition - state.addConditionalBranch(branchLabel); + state.addConditionalBranch(branchLabel, brIfOffset); break; } case Instructions.BR_TABLE: { + final int brTableOffset = offset; state.popChecked(I32_TYPE); // index final int length = readLength(); @@ -473,7 +477,7 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte returnType final int branchLabel = readTargetOffset(); branchTable[i] = branchLabel; } - state.addBranchTable(branchTable); + state.addBranchTable(branchTable, brTableOffset); // This instruction is stack-polymorphic state.setUnreachable(); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmCodeEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmCodeEntry.java index 46c6505be631..af8aef4a4c59 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmCodeEntry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmCodeEntry.java @@ -40,7 +40,6 @@ */ package org.graalvm.wasm; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.profiles.BranchProfile; @@ -91,98 +90,6 @@ public int[] extraData() { return extraData; } - /** - * A constant holding the maximum value an {@code int} can have, 215-1. The sum of - * the true and false count must not overflow. This constant is used to check whether one of the - * counts does not exceed the required maximum value. - */ - public static final int CONDITION_COUNT_MAX_VALUE = 0x3fff; - - /** - * Same logic as in {@link com.oracle.truffle.api.profiles.ConditionProfile#profile}. - * - * @param index Condition index - * @param condition Condition value - * @return {@code condition} - */ - public static boolean profileCondition(int[] counters, int index, boolean condition) { - // locals required to guarantee no overflow in multi-threaded environments - int tf = counters[index]; - int t = tf >>> 16; - int f = tf & 0xffff; - boolean val = condition; - if (val) { - if (!CompilerDirectives.inInterpreter()) { - if (t == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - } - if (f == 0) { - // Make this branch fold during PE - val = true; - } - } else { - if (t < CONDITION_COUNT_MAX_VALUE) { - counters[index] = ((t + 1) << 16) | f; - } - } - } else { - if (!CompilerDirectives.inInterpreter()) { - if (f == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - } - if (t == 0) { - // Make this branch fold during PE - val = false; - } - } else { - if (f < CONDITION_COUNT_MAX_VALUE) { - counters[index] = (t << 16) | (f + 1); - } - } - } - - if (CompilerDirectives.inInterpreter()) { - // no branch probability calculation in the interpreter - return val; - } else { - int sum = t + f; - return CompilerDirectives.injectBranchProbability((double) t / (double) sum, val); - } - } - - public static void updateTableConditionProfile(int[] profileArray, int counterIndex, int profileIndex) { - if (profileArray[counterIndex] < Integer.MAX_VALUE) { - profileArray[counterIndex]++; - profileArray[profileIndex]++; - } - } - - public static boolean injectTableConditionProfile(int[] profileArray, int counterIndex, int profileIndex, boolean condition) { - int sum = profileArray[counterIndex]; - int t = profileArray[profileIndex]; - // Clamp probability to 1.0 - if (t > sum) { - t = sum; - } - boolean val = condition; - if (val) { - if (t == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - } - if (t == sum) { - val = true; - } - } else { - if (t == sum) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - } - if (t == 0) { - val = false; - } - } - return CompilerDirectives.injectBranchProbability((double) t / (double) sum, val); - } - public void errorBranch() { errorBranch.enter(); } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/ExtraDataOffsets.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/ExtraDataOffsets.java deleted file mode 100644 index f88f3c970b34..000000000000 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/ExtraDataOffsets.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.graalvm.wasm.constants; - -/** - * Represents the offsets of entries in the extra data array. - */ -public final class ExtraDataOffsets { - /** - * If: - * - * - * | bytecodeIndex (4 byte) | extraIndex (4 byte) | conditionProfile (4 byte) | - * - * - * bytecodeIndex (data array): The index of the first instruction in the else branch. - * - * extraIndex (extra data array): The index of the first instruction, which needs extra data, in - * the else branch. - * - * conditionProfile: The condition profile probability for the false jump. - */ - public static final int IF_BYTECODE_INDEX = 0; - public static final int IF_EXTRA_INDEX = 1; - public static final int IF_PROFILE = 2; - public static final int IF_LENGTH = 3; - - /** - * Else: - * - * - * | bytecodeIndex (4 byte) | extraIndex (4 byte) | - * - * - * bytecodeIndex (data array): The index of the first instruction after the else branch. - * - * extraIndex (extra data array): The index of the first instruction, which needs extra data, - * after the else branch. - */ - - public static final int ELSE_BYTECODE_INDEX = 0; - public static final int ELSE_EXTRA_INDEX = 1; - public static final int ELSE_LENGTH = 2; - - /** - * Constants to extract the returnLength and stackSize from a stackInfo entry. - */ - public static final int STACK_INFO_RETURN_LENGTH_SHIFT = 24; - public static final int STACK_INFO_STACK_SIZE_MASK = 0x00FF_FFFF; - - /** - * Br_if: - * - * - * | bytecodeIndex (4 byte) | extraIndex (4 byte) | returnLength (1 byte) | stackSize (3 byte) | conditionProfile (4 byte) | - * - * - * bytecodeIndex (data array): The index of the first instruction after the jump. - * - * extraIndex (extra data array): The index of the first instruction, which needs extra data, - * after the jump. - * - * stackInfo: Combination of returnLength and stackSize. - * - * returnLength: The number of return values of the jump target block. - * - * stackSize: The stack pointer of the jump target block (number of stack values after the - * jump). - * - * conditionProfile: The condition profile probability for the jump. - */ - public static final int BR_IF_BYTECODE_INDEX = 0; - public static final int BR_IF_EXTRA_INDEX = 1; - public static final int BR_IF_STACK_INFO = 2; - public static final int BR_IF_PROFILE = 3; - public static final int BR_IF_LENGTH = 4; - - /** - * Br: - * - * - * | bytecodeIndex (4 byte) | extraIndex (4 byte) | returnLength (1 byte) | stackSize (3 byte) | - * - * - * bytecodeIndex (data array): The index of the first instruction after the jump. - * - * extraIndex (extra data array): The index of the first instruction, which needs extra data, - * after the jump. - * - * stackInfo: Combination of returnLength and stackSize. - * - * returnLength: The number of return values of the jump target block. - * - * stackSize: The stack pointer of the jump target block (number of stack values after the - * jump). - */ - public static final int BR_BYTECODE_INDEX = 0; - public static final int BR_EXTRA_INDEX = 1; - public static final int BR_STACK_INFO = 2; - public static final int BR_LENGTH = 3; - - /** - * Br_table: - * - * - * | size | count | entry | ... | entry | default entry | - * - * - * size: The number of entries in the branch table. - * - * count: The number of times the branch table instruction has been executed. Used for - * calculating the branch probabilities. - * - * entry: A branch table entry. (see below) - */ - public static final int BR_TABLE_SIZE = 0; - public static final int BR_TABLE_COUNT = 1; - public static final int BR_TABLE_ENTRY_OFFSET = 2; - - /** - * Br_table_entry: - * - * - * | bytecodeIndex (4 byte) | extraIndex (4 byte) | returnLength (1 byte) | stackSize (3 byte) | profileCount (4 byte) | - * - * - * bytecodeIndex (data array): The index of the first instruction after the jump. - * - * extraIndex (extra data array): The index of the first instruction, which needs extra data, - * after the jump. - * - * stackInfo: Combination of returnLength and stackSize. - * - * returnLength: The number of values of the jump target block. - * - * stackSize: The stack pointer of the jump target block (number of stack values after the - * jump). - * - * profileCount: The number of times this branch table entry has been chosen. Used for - * calculating the branch probability. - */ - public static final int BR_TABLE_ENTRY_BYTECODE_INDEX = 0; - public static final int BR_TABLE_ENTRY_EXTRA_INDEX = 1; - public static final int BR_TABLE_ENTRY_STACK_INFO = 2; - public static final int BR_TABLE_ENTRY_PROFILE = 3; - public static final int BR_TABLE_ENTRY_LENGTH = 4; - - /** - * Call_indirect: - * - * - * | nodeIndex (4 byte) | conditionProfile (4 byte) | - * - * - * nodeIndex: The index in the call node array. - * - * conditionProfile: The condition profile probability for calling an external module. - */ - public static final int CALL_INDIRECT_NODE_INDEX = 0; - public static final int CALL_INDIRECT_PROFILE = 1; - public static final int CALL_INDIRECT_LENGTH = 2; - - /** - * Call: - * - * - * | nodeIndex (4 byte) | - * - * - * nodeIndex: The index in the call node array. - */ - public static final int CALL_NODE_INDEX = 0; - public static final int CALL_LENGTH = 1; - - private ExtraDataOffsets() { - } -} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java index cc119a3e1927..8363004a5335 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java @@ -127,7 +127,9 @@ public enum Failure { MEMORY_ALLOCATION_FAILED(Type.EXHAUSTION, "could not allocate memory"), // TODO(mbovel): replace UNSPECIFIED_INTERNAL usages with assertInternal/shouldNotReachHere. - UNSPECIFIED_INTERNAL(Type.INTERNAL, "unspecified"); + UNSPECIFIED_INTERNAL(Type.INTERNAL, "unspecified"), + + NON_REPRESENTABLE_EXTRA_DATA_VALUE(Type.MALFORMED, "value cannot be represented in extra data"); public enum Type { TRAP("trap"), diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java index af757081cf61..4db5975adeea 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java @@ -42,35 +42,6 @@ import static org.graalvm.wasm.BinaryStreamParser.length; import static org.graalvm.wasm.BinaryStreamParser.value; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_PROFILE; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_STACK_INFO; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_STACK_INFO; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_COUNT; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_OFFSET; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_PROFILE; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_STACK_INFO; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_SIZE; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_INDIRECT_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_INDIRECT_NODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_INDIRECT_PROFILE; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_NODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.ELSE_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.ELSE_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_PROFILE; -import static org.graalvm.wasm.constants.ExtraDataOffsets.STACK_INFO_RETURN_LENGTH_SHIFT; -import static org.graalvm.wasm.constants.ExtraDataOffsets.STACK_INFO_STACK_SIZE_MASK; import static org.graalvm.wasm.constants.Instructions.BLOCK; import static org.graalvm.wasm.constants.Instructions.BR; import static org.graalvm.wasm.constants.Instructions.BR_IF; @@ -267,6 +238,29 @@ import static org.graalvm.wasm.nodes.WasmFrame.pushFloat; import static org.graalvm.wasm.nodes.WasmFrame.pushInt; import static org.graalvm.wasm.nodes.WasmFrame.pushLong; +import static org.graalvm.wasm.util.ExtraDataAccessor.CALL_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_BR_IF_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_BR_IF_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_BR_TABLE_HEADER_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_BR_TABLE_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_CALL_INDIRECT_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_CALL_INDIRECT_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_IF_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.COMPACT_IF_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_BR_IF_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_BR_IF_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_BR_TABLE_HEADER_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_BR_TABLE_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_CALL_INDIRECT_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_CALL_INDIRECT_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_IF_LENGTH; +import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_IF_PROFILE_OFFSET; +import static org.graalvm.wasm.util.ExtraDataAccessor.firstValueSigned; +import static org.graalvm.wasm.util.ExtraDataAccessor.firstValueUnsigned; +import static org.graalvm.wasm.util.ExtraDataAccessor.forthValueUnsigned; +import static org.graalvm.wasm.util.ExtraDataAccessor.isCompactFormat; +import static org.graalvm.wasm.util.ExtraDataAccessor.secondValueSigned; +import static org.graalvm.wasm.util.ExtraDataAccessor.thirdValueUnsigned; import org.graalvm.wasm.BinaryStreamParser; import org.graalvm.wasm.SymbolTable; @@ -486,84 +480,96 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int break; case IF: { stackPointer--; - if (WasmCodeEntry.profileCondition(extraData, extraOffset + IF_PROFILE, popBoolean(frame, stackPointer))) { + final boolean compact = isCompactFormat(extraData, extraOffset); + CompilerAsserts.partialEvaluationConstant(compact); + final int profileOffset = extraOffset + (compact ? COMPACT_IF_PROFILE_OFFSET : EXTENDED_IF_PROFILE_OFFSET); + if (profileCondition(extraData, profileOffset, popBoolean(frame, stackPointer))) { // Skip return type. offset++; // Jump to first extra data entry in the then branch. - extraOffset += IF_LENGTH; + extraOffset += compact ? COMPACT_IF_LENGTH : EXTENDED_IF_LENGTH; } else { // Jump to the else branch. - offset = extraData[extraOffset + IF_BYTECODE_INDEX]; - extraOffset = extraData[extraOffset + IF_EXTRA_INDEX]; + offset += secondValueSigned(extraData, extraOffset, compact); + extraOffset += firstValueSigned(extraData, extraOffset, compact); } break; } - case ELSE: + case ELSE: { // The then branch was executed at this point. Jump to end of the if statement. - offset = extraData[extraOffset + ELSE_BYTECODE_INDEX]; - extraOffset = extraData[extraOffset + ELSE_EXTRA_INDEX]; + final boolean compact = isCompactFormat(extraData, extraOffset); + CompilerAsserts.partialEvaluationConstant(compact); + offset += secondValueSigned(extraData, extraOffset, compact); + extraOffset += firstValueSigned(extraData, extraOffset, compact); break; + } case END: break; case BR: { - final int targetStackInfo = extraData[extraOffset + BR_STACK_INFO]; - final int targetStackPointer = numLocals + (targetStackInfo & STACK_INFO_STACK_SIZE_MASK); - final int targetReturnLength = (targetStackInfo >> STACK_INFO_RETURN_LENGTH_SHIFT); + final boolean compact = isCompactFormat(extraData, extraOffset); + CompilerAsserts.partialEvaluationConstant(compact); + final int targetStackPointer = numLocals + forthValueUnsigned(extraData, extraOffset, compact); + final int targetReturnLength = thirdValueUnsigned(extraData, extraOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); // Jump to the target block. - offset = extraData[extraOffset + BR_BYTECODE_INDEX]; - extraOffset = extraData[extraOffset + BR_EXTRA_INDEX]; + offset += secondValueSigned(extraData, extraOffset, compact); + extraOffset += firstValueSigned(extraData, extraOffset, compact); stackPointer = targetStackPointer + targetReturnLength; break; } case BR_IF: { stackPointer--; - if (WasmCodeEntry.profileCondition(extraData, extraOffset + BR_IF_PROFILE, popBoolean(frame, stackPointer))) { - final int stackInfo = extraData[extraOffset + BR_IF_STACK_INFO]; - final int targetStackPointer = numLocals + (stackInfo & STACK_INFO_STACK_SIZE_MASK); - final int targetReturnValueCount = (stackInfo >> STACK_INFO_RETURN_LENGTH_SHIFT); + final boolean compact = isCompactFormat(extraData, extraOffset); + CompilerAsserts.partialEvaluationConstant(compact); + final int profileOffset = extraOffset + (compact ? COMPACT_BR_IF_PROFILE_OFFSET : EXTENDED_BR_IF_PROFILE_OFFSET); + if (profileCondition(extraData, profileOffset, popBoolean(frame, stackPointer))) { + final int targetStackPointer = numLocals + forthValueUnsigned(extraData, extraOffset, compact); + final int targetReturnLength = thirdValueUnsigned(extraData, extraOffset, compact); - unwindStack(frame, stackPointer, targetStackPointer, targetReturnValueCount); + unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); // Jump to the target block. - offset = extraData[extraOffset + BR_IF_BYTECODE_INDEX]; - extraOffset = extraData[extraOffset + BR_IF_EXTRA_INDEX]; - stackPointer = targetStackPointer + targetReturnValueCount; + offset += secondValueSigned(extraData, extraOffset, compact); + extraOffset += firstValueSigned(extraData, extraOffset, compact); + stackPointer = targetStackPointer + targetReturnLength; } else { // Skip condition. offset++; // Jump to next extra data entry after the branch. - extraOffset += BR_IF_LENGTH; + extraOffset += compact ? COMPACT_BR_IF_LENGTH : EXTENDED_BR_IF_LENGTH; } break; } case BR_TABLE: { stackPointer--; + final boolean compact = isCompactFormat(extraData, extraOffset); + CompilerAsserts.partialEvaluationConstant(compact); int index = popInt(frame, stackPointer); - final int size = extraData[extraOffset + BR_TABLE_SIZE]; + final int size = firstValueUnsigned(extraData, extraOffset, compact); if (index < 0 || index >= size) { // If unsigned index is larger or equal to the table size use the // default (last) index. index = size - 1; } - // TODO: Find benchmark that heavily uses branch tables. + final int profileOffset = extraOffset + (compact ? COMPACT_BR_TABLE_PROFILE_OFFSET : EXTENDED_BR_TABLE_PROFILE_OFFSET); if (CompilerDirectives.inInterpreter()) { - final int indexLocation = extraOffset + BR_TABLE_ENTRY_OFFSET + (index * BR_TABLE_ENTRY_LENGTH); + final int indexOffset = extraOffset + + (compact ? COMPACT_BR_TABLE_HEADER_LENGTH + index * COMPACT_BR_IF_LENGTH : EXTENDED_BR_TABLE_HEADER_LENGTH + index * EXTENDED_BR_IF_LENGTH); + final int indexProfileOffset = indexOffset + (compact ? COMPACT_BR_IF_PROFILE_OFFSET : EXTENDED_BR_IF_PROFILE_OFFSET); - WasmCodeEntry.updateTableConditionProfile(extraData, extraOffset + BR_TABLE_COUNT, indexLocation + BR_TABLE_ENTRY_PROFILE); + updateBranchTableProfile(extraData, profileOffset, indexProfileOffset); - final int stackInfo = extraData[indexLocation + BR_TABLE_ENTRY_STACK_INFO]; - final int targetStackPointer = numLocals + (stackInfo & STACK_INFO_STACK_SIZE_MASK); - final int targetReturnLength = (stackInfo >> STACK_INFO_RETURN_LENGTH_SHIFT); + final int targetStackPointer = numLocals + forthValueUnsigned(extraData, indexOffset, compact); + final int targetReturnLength = thirdValueUnsigned(extraData, indexOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); // Jump to the branch target. - offset = extraData[indexLocation + BR_TABLE_ENTRY_BYTECODE_INDEX]; - extraOffset = extraData[indexLocation + BR_TABLE_ENTRY_EXTRA_INDEX]; + offset += secondValueSigned(extraData, indexOffset, compact); + extraOffset += firstValueSigned(extraData, indexOffset, compact); stackPointer = targetStackPointer + targetReturnLength; break; } else { @@ -571,17 +577,18 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int // guarantees that all values inside the if statement are treated as compile // time constants, since the loop is unrolled. for (int i = 0; i < size; i++) { - final int indexLocation = extraOffset + BR_TABLE_ENTRY_OFFSET + (i * BR_TABLE_ENTRY_LENGTH); - if (WasmCodeEntry.injectTableConditionProfile(extraData, extraOffset + BR_TABLE_COUNT, indexLocation + BR_TABLE_ENTRY_PROFILE, i == index)) { - final int stackInfo = extraData[indexLocation + BR_TABLE_ENTRY_STACK_INFO]; - final int targetStackPointer = numLocals + (stackInfo & STACK_INFO_STACK_SIZE_MASK); - final int targetReturnLength = (stackInfo >> STACK_INFO_RETURN_LENGTH_SHIFT); + final int indexOffset = extraOffset + + (compact ? COMPACT_BR_TABLE_HEADER_LENGTH + i * COMPACT_BR_IF_LENGTH : EXTENDED_BR_TABLE_HEADER_LENGTH + i * EXTENDED_BR_IF_LENGTH); + final int indexProfileOffset = indexOffset + (compact ? COMPACT_BR_IF_PROFILE_OFFSET : EXTENDED_BR_IF_PROFILE_OFFSET); + if (profileBranchTable(extraData, profileOffset, indexProfileOffset, i == index)) { + final int targetStackPointer = numLocals + forthValueUnsigned(extraData, indexOffset, compact); + final int targetReturnLength = thirdValueUnsigned(extraData, indexOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); // Jump to the branch target. - offset = extraData[indexLocation + BR_TABLE_ENTRY_BYTECODE_INDEX]; - extraOffset = extraData[indexLocation + BR_TABLE_ENTRY_EXTRA_INDEX]; + offset += secondValueSigned(extraData, indexOffset, compact); + extraOffset += firstValueSigned(extraData, indexOffset, compact); stackPointer = targetStackPointer + targetReturnLength; continue loop; } @@ -616,7 +623,8 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int Object[] args = createArgumentsForCall(frame, function.typeIndex(), numArgs, stackPointer); stackPointer -= args.length; - int callNodeIndex = extraData[extraOffset + CALL_NODE_INDEX]; + final boolean compact = isCompactFormat(extraData, extraOffset); + final int callNodeIndex = firstValueUnsigned(extraData, extraOffset, compact); extraOffset += CALL_LENGTH; Object result = executeDirectCall(callNodeIndex, function, args); @@ -703,8 +711,11 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int offset += 1; // Validate that the function type matches the expected type. - boolean functionFromCurrentContext = WasmCodeEntry.profileCondition(extraData, extraOffset + CALL_INDIRECT_PROFILE, functionInstanceContext == context); - if (functionFromCurrentContext) { + final boolean functionFromCurrentContext = functionInstanceContext == context; + + final boolean compact = isCompactFormat(extraData, extraOffset); + final int profileOffset = extraOffset + (compact ? COMPACT_CALL_INDIRECT_PROFILE_OFFSET : EXTENDED_CALL_INDIRECT_PROFILE_OFFSET); + if (profileCondition(extraData, profileOffset, functionFromCurrentContext)) { // We can do a quick equivalence-class check. if (expectedTypeEquivalenceClass != function.typeEquivalenceClass()) { enterErrorBranch(); @@ -738,8 +749,8 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int prev = null; } - int callNodeIndex = extraData[extraOffset + CALL_INDIRECT_NODE_INDEX]; - extraOffset += CALL_INDIRECT_LENGTH; + final int callNodeIndex = firstValueUnsigned(extraData, extraOffset, compact); + extraOffset += compact ? COMPACT_CALL_INDIRECT_LENGTH : EXTENDED_CALL_INDIRECT_LENGTH; final Object result; try { @@ -2807,4 +2818,79 @@ private static long signedLongConstant(byte[] data, int offset) { private static int offsetDelta(byte[] data, int offset) { return BinaryStreamParser.peekLeb128Length(data, offset); } + + private static final int MAX_PROFILE_VALUE = 0x0000_00ff; + private static final int MAX_TABLE_PROFILE_VALUE = 0x0000_ffff; + + private static boolean profileCondition(int[] extraData, int profileOffset, boolean condition) { + int t = (extraData[profileOffset] & 0x0000_ff00) >> 8; + int f = extraData[profileOffset] & 0x0000_00ff; + boolean val = condition; + if (val) { + if (t == 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + if (f == 0) { + // Make this branch fold during PE + val = true; + } + if (CompilerDirectives.inInterpreter()) { + if (t < MAX_PROFILE_VALUE) { + extraData[profileOffset] += 0x0100; + } + } + } else { + if (f == 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + if (t == 0) { + // Make this branch fold during PE + val = false; + } + if (CompilerDirectives.inInterpreter()) { + if (f < MAX_PROFILE_VALUE) { + extraData[profileOffset] += 0x0001; + } + } + } + if (CompilerDirectives.inInterpreter()) { + return val; + } else { + int sum = t + f; + return CompilerDirectives.injectBranchProbability((double) t / (double) sum, val); + } + } + + private static void updateBranchTableProfile(int[] extraData, int counterOffset, int profileOffset) { + assert CompilerDirectives.inInterpreter(); + if ((extraData[counterOffset] & 0x0000_ffff) < MAX_TABLE_PROFILE_VALUE) { + extraData[counterOffset]++; + extraData[profileOffset]++; + } + } + + private static boolean profileBranchTable(int[] extraData, int counterOffset, int profileOffset, boolean condition) { + assert !CompilerDirectives.inInterpreter(); + int t = extraData[profileOffset] & 0x0000_ffff; + int sum = extraData[counterOffset] & 0x0000_ffff; + boolean val = condition; + if (val) { + if (t == 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + if (t == sum) { + // Make this branch fold during PE + val = true; + } + } else { + if (t == sum) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + if (t == 0) { + // Make this branch fold during PE + val = false; + } + } + return CompilerDirectives.injectBranchProbability((double) t / (double) sum, val); + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/BlockFrame.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/BlockFrame.java index 927e68407a19..da0bfc4b1351 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/BlockFrame.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/BlockFrame.java @@ -43,6 +43,8 @@ import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.parser.validation.collections.ExtraDataList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; /** * Representation of a wasm block during module validation. @@ -53,8 +55,8 @@ class BlockFrame extends ControlFrame { } @Override - byte[] getLabelTypes() { - return getResultTypes(); + byte[] labelTypes() { + return resultTypes(); } @Override @@ -64,11 +66,9 @@ void enterElse(ParserState state, ExtraDataList extraData, int offset) { @Override void exit(ExtraDataList extraData, int offset) { - for (int location : conditionalBranches()) { - extraData.setConditionalBranchTarget(location, offset, extraData.getLocation(), getInitialStackSize(), getLabelTypeLength()); - } - for (int location : unconditionalBranches()) { - extraData.setUnconditionalBranchTarget(location, offset, extraData.getLocation(), getInitialStackSize(), getLabelTypeLength()); + for (BranchTargetWithStackChange jumpTarget : branchTargets()) { + jumpTarget.setTargetInfo(offset, extraData.nextEntryLocation(), extraData.nextEntryIndex()); + jumpTarget.setStackInfo(labelTypeLength(), initialStackSize()); } } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlFrame.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlFrame.java index ef0771ca81e4..eb49b9d96893 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlFrame.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlFrame.java @@ -41,7 +41,10 @@ package org.graalvm.wasm.parser.validation; -import org.graalvm.wasm.collection.IntArrayList; +import org.graalvm.wasm.parser.validation.collections.ExtraDataList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; + +import java.util.ArrayList; /** * Represents the scope of a block structure during module validation. @@ -51,8 +54,7 @@ public abstract class ControlFrame { private final byte[] resultTypes; private final int initialStackSize; private boolean unreachable; - private final IntArrayList conditionalBranches; - private final IntArrayList unconditionalBranches; + private final ArrayList branchTargets; /** * @param paramTypes The parameter value types of the block structure. @@ -66,14 +68,13 @@ public abstract class ControlFrame { this.initialStackSize = initialStackSize; this.unreachable = unreachable; - this.conditionalBranches = new IntArrayList(); - this.unconditionalBranches = new IntArrayList(); + this.branchTargets = new ArrayList<>(0); } /** * @return The types that must be on the value stack when branching to this frame. */ - abstract byte[] getLabelTypes(); + abstract byte[] labelTypes(); /** * Performs checks and actions when entering an else branch. @@ -92,56 +93,27 @@ public abstract class ControlFrame { */ abstract void exit(ExtraDataList extraData, int offset); - /** - * Adds a conditional branch to this frame. - * - * @param extraData The current extra data array. - */ - void addConditionalBranch(ExtraDataList extraData) { - conditionalBranches.add(extraData.addConditionalBranchLocation()); - } - - /** - * Adds an unconditional branch to this frame. - * - * @param extraData The current extra data array. - */ - void addUnconditionalBranch(ExtraDataList extraData) { - unconditionalBranches.add(extraData.addUnconditionalBranchLocation()); - } - - /** - * Adds a conditional branch from a branch table entry to this fraem. - * - * @param extraData The current extra data array. - * @param location The location of the branch table in the extra data array. - * @param index The index of the entry in the branch table. - */ - void addBranchTableEntry(ExtraDataList extraData, int location, int index) { - conditionalBranches.add(extraData.getBranchTableEntryLocation(location, index)); - } - - protected int[] conditionalBranches() { - return conditionalBranches.toArray(); + void addBranchTarget(BranchTargetWithStackChange jumpTarget) { + branchTargets.add(jumpTarget); } - protected int[] unconditionalBranches() { - return unconditionalBranches.toArray(); + protected ArrayList branchTargets() { + return branchTargets; } - protected byte[] getParamTypes() { + protected byte[] paramTypes() { return paramTypes; } - public byte[] getResultTypes() { + public byte[] resultTypes() { return resultTypes; } - protected int getLabelTypeLength() { - return getLabelTypes().length; + protected int labelTypeLength() { + return labelTypes().length; } - int getInitialStackSize() { + int initialStackSize() { return initialStackSize; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ExtraDataList.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ExtraDataList.java deleted file mode 100644 index 9b47ee820f0e..000000000000 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ExtraDataList.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.graalvm.wasm.parser.validation; - -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_IF_STACK_INFO; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_STACK_INFO; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_ENTRY_OFFSET; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_TABLE_SIZE; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_NODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_INDIRECT_NODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_INDIRECT_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.CALL_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.ELSE_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.ELSE_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.ELSE_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_BYTECODE_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_EXTRA_INDEX; -import static org.graalvm.wasm.constants.ExtraDataOffsets.IF_LENGTH; -import static org.graalvm.wasm.constants.ExtraDataOffsets.STACK_INFO_RETURN_LENGTH_SHIFT; -import static org.graalvm.wasm.constants.ExtraDataOffsets.STACK_INFO_STACK_SIZE_MASK; -import static org.graalvm.wasm.constants.ExtraDataOffsets.BR_LENGTH; - -/** - * Representation of extra data during module validation. Used to build up the extra data array. - */ -class ExtraDataList { - private static final int INITIAL_EXTRA_DATA_SIZE = 8; - private static final int[] EMPTY_INT_ARRAY = new int[0]; - - private int[] extraData; - private int extraDataCount; - - ExtraDataList() { - this.extraData = new int[INITIAL_EXTRA_DATA_SIZE]; - this.extraDataCount = 0; - } - - /** - * Allocates space for if information. - * - * @return The location of the if information in the extra data array. - */ - int addIfLocation() { - ensureExtraDataSize(IF_LENGTH); - // initializes condition profile to 0. - int location = extraDataCount; - extraDataCount += IF_LENGTH; - return location; - } - - /** - * Sets the if information for the given location. - * - * @param location The location of the if information. - * @param target The branch target in the wasm binary. - * @param extraTarget The branch target in the extra data array. - */ - void setIfTarget(int location, int target, int extraTarget) { - extraData[location + IF_BYTECODE_INDEX] = target; - extraData[location + IF_EXTRA_INDEX] = extraTarget; - } - - /** - * Allocates space for else information. - * - * @return The location of the else information in the extra data array. - */ - int addElseLocation() { - ensureExtraDataSize(ELSE_LENGTH); - int location = extraDataCount; - extraDataCount += ELSE_LENGTH; - return location; - } - - /** - * Sets the else information for the given location. - * - * @param location The location of the else information. - * @param target The branch target in the wasm binary. - * @param extraTarget The branch target in the extra data array. - */ - void setElseTarget(int location, int target, int extraTarget) { - extraData[location + ELSE_BYTECODE_INDEX] = target; - extraData[location + ELSE_EXTRA_INDEX] = extraTarget; - } - - /** - * Allocates space for conditional branch information. - * - * @return The location of the conditional branch information in the extra data array. - */ - int addConditionalBranchLocation() { - ensureExtraDataSize(BR_IF_LENGTH); - // initializes condition profile with 0. - int location = extraDataCount; - extraDataCount += BR_IF_LENGTH; - return location; - } - - /** - * Sets the conditional branch information for the given location. - * - * @param location The location of the conditional branch information. - * @param target The branch target in the wasm binary. - * @param extraTarget The branch target in the extra data array. - * @param stackSize The stack size after the branch. - * @param returnLength The number of return values returned by the current block. - */ - void setConditionalBranchTarget(int location, int target, int extraTarget, int stackSize, int returnLength) { - extraData[location + BR_IF_BYTECODE_INDEX] = target; - extraData[location + BR_IF_EXTRA_INDEX] = extraTarget; - extraData[location + BR_IF_STACK_INFO] = (stackSize & STACK_INFO_STACK_SIZE_MASK) + (returnLength << STACK_INFO_RETURN_LENGTH_SHIFT); - } - - /** - * Allocates space for unconditional branch information. - * - * @return The location of the unconditional branch information in the extra data array. - */ - int addUnconditionalBranchLocation() { - ensureExtraDataSize(BR_LENGTH); - int location = extraDataCount; - extraDataCount += BR_LENGTH; - return location; - } - - /** - * Sets the unconditional branch information for the given location. - * - * @param location The location of the unconditional branch information. - * @param target The branch target in the wasm binary. - * @param extraTarget The branch target in the extra data array. - * @param stackSize The stack size after the branch. - * @param returnLength The number of return values returned by the current block. - */ - void setUnconditionalBranchTarget(int location, int target, int extraTarget, int stackSize, int returnLength) { - extraData[location + BR_BYTECODE_INDEX] = target; - extraData[location + BR_EXTRA_INDEX] = extraTarget; - extraData[location + BR_STACK_INFO] = (stackSize & STACK_INFO_STACK_SIZE_MASK) + (returnLength << STACK_INFO_RETURN_LENGTH_SHIFT); - } - - /** - * Allocates space for branch table information. - * - * @param size The number of entries in the branch table. - * @return The location of the branch table information in the extra data array. - */ - int addBranchTableLocation(int size) { - ensureExtraDataSize(BR_TABLE_ENTRY_OFFSET + BR_TABLE_ENTRY_LENGTH * size); - extraData[extraDataCount + BR_TABLE_SIZE] = size; - // initializes branch table count with 0. - int location = extraDataCount; - extraDataCount += BR_TABLE_ENTRY_OFFSET + BR_TABLE_ENTRY_LENGTH * size; - return location; - } - - /** - * @param location The branch table location. - * @param index The entry index. - * @return The location of the entry in the extra data array. - */ - int getBranchTableEntryLocation(int location, int index) { - return location + BR_TABLE_ENTRY_OFFSET + index * BR_TABLE_ENTRY_LENGTH; - } - - /** - * Adds the indirect call information to the extra data array. - * - * @param nodeIndex The index of the call node. - */ - void addIndirectCall(int nodeIndex) { - ensureExtraDataSize(CALL_INDIRECT_LENGTH); - extraData[extraDataCount + CALL_INDIRECT_NODE_INDEX] = nodeIndex; - // initializes condition profile with 0. - extraDataCount += CALL_INDIRECT_LENGTH; - } - - /** - * Adds the direct call information to the extra data array. - * - * @param nodeIndex The index of the call node. - */ - void addCall(int nodeIndex) { - ensureExtraDataSize(CALL_LENGTH); - extraData[extraDataCount + CALL_NODE_INDEX] = nodeIndex; - extraDataCount += CALL_LENGTH; - } - - /** - * @return The current location in the extra data array. - */ - int getLocation() { - return extraDataCount; - } - - /** - * @return A copy of the underlying extra data array. - */ - int[] getExtraDataArray() { - if (extraDataCount == 0) { - return EMPTY_INT_ARRAY; - } else { - int[] result = new int[extraDataCount]; - System.arraycopy(extraData, 0, result, 0, extraDataCount); - return result; - } - } - - private void ensureExtraDataSize(int requiredSize) { - int nextSizeFactor = 0; - while (extraDataCount + requiredSize >= extraData.length << nextSizeFactor) { - nextSizeFactor++; - } - if (nextSizeFactor != 0) { - int[] updatedExtraData = new int[extraData.length << nextSizeFactor]; - System.arraycopy(extraData, 0, updatedExtraData, 0, extraDataCount); - extraData = updatedExtraData; - } - } -} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/IfFrame.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/IfFrame.java index fb6ccd596b2e..8537b3289958 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/IfFrame.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/IfFrame.java @@ -43,51 +43,48 @@ import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.parser.validation.collections.ExtraDataList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTarget; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; /** * Representation of a wasm if and else block during module validation. */ class IfFrame extends ControlFrame { - private int falseJumpLocation; + private BranchTarget falseJump; private boolean elseBranch; - IfFrame(byte[] paramTypes, byte[] resultTypes, int initialStackSize, boolean unreachable, int falseJumpLocation) { + IfFrame(byte[] paramTypes, byte[] resultTypes, int initialStackSize, boolean unreachable, BranchTarget falseJump) { super(paramTypes, resultTypes, initialStackSize, unreachable); - this.falseJumpLocation = falseJumpLocation; + this.falseJump = falseJump; this.elseBranch = false; } @Override - byte[] getLabelTypes() { - return getResultTypes(); + byte[] labelTypes() { + return resultTypes(); } @Override void enterElse(ParserState state, ExtraDataList extraData, int offset) { - int endJumpLocation = extraData.addElseLocation(); - extraData.setIfTarget(falseJumpLocation, offset, extraData.getLocation()); - falseJumpLocation = endJumpLocation; + BranchTarget endJump = extraData.addElse(offset); + falseJump.setTargetInfo(offset, extraData.nextEntryLocation(), extraData.nextEntryIndex()); + falseJump = endJump; elseBranch = true; - state.checkStackAfterFrameExit(this, getResultTypes()); + state.checkStackAfterFrameExit(this, resultTypes()); // Since else is a separate block the unreachable state has to be reset. resetUnreachable(); } @Override void exit(ExtraDataList extraData, int offset) { - if (!elseBranch && getLabelTypeLength() > 0) { + if (!elseBranch && labelTypeLength() > 0) { throw WasmException.create(Failure.TYPE_MISMATCH, "Expected else branch. If with result value requires then and else branch."); } - if (elseBranch) { - extraData.setElseTarget(falseJumpLocation, offset, extraData.getLocation()); - } else { - extraData.setIfTarget(falseJumpLocation, offset, extraData.getLocation()); - } - for (int location : conditionalBranches()) { - extraData.setConditionalBranchTarget(location, offset, extraData.getLocation(), getInitialStackSize(), getLabelTypeLength()); - } - for (int location : unconditionalBranches()) { - extraData.setUnconditionalBranchTarget(location, offset, extraData.getLocation(), getInitialStackSize(), getLabelTypeLength()); + falseJump.setTargetInfo(offset, extraData.nextEntryLocation(), extraData.nextEntryIndex()); + for (BranchTargetWithStackChange branchTarget : branchTargets()) { + branchTarget.setTargetInfo(offset, extraData.nextEntryLocation(), extraData.nextEntryIndex()); + branchTarget.setStackInfo(labelTypeLength(), initialStackSize()); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/LoopFrame.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/LoopFrame.java index 1f697ec2b141..8ac94b130ecb 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/LoopFrame.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/LoopFrame.java @@ -43,23 +43,28 @@ import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.parser.validation.collections.ExtraDataList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; /** * Representation of a wasm loop during module validation. */ class LoopFrame extends ControlFrame { - private final int target; - private final int extraTarget; + private final int byteCodeTarget; + private final int extraDataTarget; - LoopFrame(byte[] paramTypes, byte[] resultTypes, int initialStackSize, boolean unreachable, int target, int extraTarget) { + private final int extraDataTargetIndex; + + LoopFrame(byte[] paramTypes, byte[] resultTypes, int initialStackSize, boolean unreachable, int byteCodeTarget, int extraDataTarget, int extraDataTargetIndex) { super(paramTypes, resultTypes, initialStackSize, unreachable); - this.target = target; - this.extraTarget = extraTarget; + this.byteCodeTarget = byteCodeTarget; + this.extraDataTarget = extraDataTarget; + this.extraDataTargetIndex = extraDataTargetIndex; } @Override - byte[] getLabelTypes() { - return getParamTypes(); + byte[] labelTypes() { + return paramTypes(); } @Override @@ -69,11 +74,9 @@ void enterElse(ParserState state, ExtraDataList extraData, int offset) { @Override void exit(ExtraDataList extraData, int offset) { - for (int location : conditionalBranches()) { - extraData.setConditionalBranchTarget(location, target, extraTarget, getInitialStackSize(), getLabelTypeLength()); - } - for (int location : unconditionalBranches()) { - extraData.setUnconditionalBranchTarget(location, target, extraTarget, getInitialStackSize(), getLabelTypeLength()); + for (BranchTargetWithStackChange branchTarget : branchTargets()) { + branchTarget.setTargetInfo(byteCodeTarget, extraDataTarget, extraDataTargetIndex); + branchTarget.setStackInfo(labelTypeLength(), initialStackSize()); } } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java index 99f1ed27b814..a978a3d89948 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java @@ -41,13 +41,16 @@ package org.graalvm.wasm.parser.validation; +import static java.lang.Integer.compareUnsigned; + import org.graalvm.wasm.Assert; import org.graalvm.wasm.WasmType; import org.graalvm.wasm.collection.ByteArrayList; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; - -import static java.lang.Integer.compareUnsigned; +import org.graalvm.wasm.parser.validation.collections.ControlStack; +import org.graalvm.wasm.parser.validation.collections.ExtraDataList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTableEntry; /** * Represents the values and stack frames of a Wasm code section during validation. Stores @@ -222,7 +225,7 @@ public byte[] popAll(byte[] expectedValueTypes) { * @return The number of remaining values of the current control stack. */ private int availableStackSize() { - return valueStack.size() - controlStack.peek().getInitialStackSize(); + return valueStack.size() - controlStack.peek().initialStackSize(); } /** @@ -279,7 +282,7 @@ public void enterBlock(byte returnType) { * @param offset The offset of the loop that was entered in the wasm binary. */ public void enterLoop(byte returnType, int offset) { - ControlFrame frame = new LoopFrame(EMPTY_ARRAY, getReturnTypeArray(returnType), valueStack.size(), false, offset, extraData.getLocation()); + ControlFrame frame = new LoopFrame(EMPTY_ARRAY, getReturnTypeArray(returnType), valueStack.size(), false, offset, extraData.nextEntryLocation(), extraData.nextEntryIndex()); controlStack.push(frame); } @@ -289,8 +292,8 @@ public void enterLoop(byte returnType, int offset) { * * @param returnType The return type of the if and else branch that was entered. */ - public void enterIf(byte returnType) { - ControlFrame frame = new IfFrame(EMPTY_ARRAY, getReturnTypeArray(returnType), valueStack.size(), false, extraData.addIfLocation()); + public void enterIf(byte returnType, int offset) { + ControlFrame frame = new IfFrame(EMPTY_ARRAY, getReturnTypeArray(returnType), valueStack.size(), false, extraData.addIf(offset)); controlStack.push(frame); } @@ -310,13 +313,13 @@ public void enterElse(int offset) { * * @param branchLabel The target label. */ - public void addConditionalBranch(int branchLabel) { + public void addConditionalBranch(int branchLabel, int offset) { checkLabelExists(branchLabel); ControlFrame frame = getFrame(branchLabel); - final byte[] labelTypes = frame.getLabelTypes(); + final byte[] labelTypes = frame.labelTypes(); popAll(labelTypes); pushAll(labelTypes); - frame.addConditionalBranch(extraData); + frame.addBranchTarget(extraData.addConditionalBranch(offset)); } /** @@ -325,12 +328,12 @@ public void addConditionalBranch(int branchLabel) { * * @param branchLabel The target label. */ - public void addUnconditionalBranch(int branchLabel) { + public void addUnconditionalBranch(int branchLabel, int offset) { checkLabelExists(branchLabel); ControlFrame frame = getFrame(branchLabel); - final byte[] labelTypes = frame.getLabelTypes(); + final byte[] labelTypes = frame.labelTypes(); popAll(labelTypes); - frame.addUnconditionalBranch(extraData); + frame.addBranchTarget(extraData.addUnconditionalBranch(offset)); } /** @@ -339,20 +342,20 @@ public void addUnconditionalBranch(int branchLabel) { * * @param branchLabels The target labels. */ - public void addBranchTable(int[] branchLabels) { + public void addBranchTable(int[] branchLabels, int offset) { int branchLabel = branchLabels[branchLabels.length - 1]; checkLabelExists(branchLabel); ControlFrame frame = getFrame(branchLabel); - byte[] branchLabelReturnTypes = frame.getLabelTypes(); - int location = extraData.addBranchTableLocation(branchLabels.length); + byte[] branchLabelReturnTypes = frame.labelTypes(); + BranchTableEntry branchTable = extraData.addBranchTable(offset, branchLabels.length); for (int i = 0; i < branchLabels.length; i++) { int otherBranchLabel = branchLabels[i]; checkLabelExists(otherBranchLabel); frame = getFrame(otherBranchLabel); - byte[] otherBranchLabelReturnTypes = frame.getLabelTypes(); + byte[] otherBranchLabelReturnTypes = frame.labelTypes(); checkLabelTypes(branchLabelReturnTypes, otherBranchLabelReturnTypes); pushAll(popAll(otherBranchLabelReturnTypes)); - frame.addBranchTableEntry(extraData, location, i); + frame.addBranchTarget(branchTable.item(i)); } popAll(branchLabelReturnTypes); } @@ -362,7 +365,7 @@ public void addBranchTable(int[] branchLabels) { */ public void addReturn() { ControlFrame frame = getRootBlock(); - Assert.assertIntLessOrEqual(frame.getLabelTypeLength(), 1, Failure.INVALID_RESULT_ARITY); + Assert.assertIntLessOrEqual(frame.labelTypeLength(), 1, Failure.INVALID_RESULT_ARITY); checkReturnTypes(frame); } @@ -393,7 +396,7 @@ public void addCall(int nodeIndex) { public void exit(int offset) { Assert.assertTrue(!controlStack.isEmpty(), Failure.UNEXPECTED_END_OF_BLOCK); ControlFrame frame = controlStack.peek(); - byte[] resultTypes = frame.getResultTypes(); + byte[] resultTypes = frame.resultTypes(); frame.exit(extraData, offset); @@ -444,7 +447,7 @@ public ControlFrame getRootBlock() { * stack. */ public void checkReturnTypes(ControlFrame frame) { - byte[] resultTypes = frame.getResultTypes(); + byte[] resultTypes = frame.resultTypes(); if (isCurrentStackUnreachable()) { popAll(resultTypes); } else { @@ -516,7 +519,7 @@ public void checkFunctionTypeExists(int typeIndex, int max) { */ public void setUnreachable() { ControlFrame frame = controlStack.peek(); - unwindStack(frame.getInitialStackSize()); + unwindStack(frame.initialStackSize()); frame.setUnreachable(); } @@ -533,6 +536,6 @@ public int maxStackSize() { } public int[] extraData() { - return extraData.getExtraDataArray(); + return extraData.extraDataArray(); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlStack.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ControlStack.java similarity index 89% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlStack.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ControlStack.java index 2bbfbb97775f..7c1a246b42e7 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ControlStack.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ControlStack.java @@ -39,17 +39,19 @@ * SOFTWARE. */ -package org.graalvm.wasm.parser.validation; +package org.graalvm.wasm.parser.validation.collections; + +import org.graalvm.wasm.parser.validation.ControlFrame; /** * Represents a stack of control frames that are used for validation of modules. */ -class ControlStack { +public class ControlStack { private ControlFrame[] stack; private int size; - ControlStack() { + public ControlStack() { stack = new ControlFrame[4]; size = 0; } @@ -67,7 +69,7 @@ private void ensureSize() { * * @param frame A control frame. */ - void push(ControlFrame frame) { + public void push(ControlFrame frame) { ensureSize(); stack[size] = frame; size++; @@ -76,7 +78,7 @@ void push(ControlFrame frame) { /** * Pops the topmost control frame from the stack. */ - void pop() { + public void pop() { assert size > 0 : "cannot pop from empty stack"; size--; } @@ -86,7 +88,7 @@ void pop() { * * @return The topmost control frame. */ - ControlFrame peek() { + public ControlFrame peek() { assert size > 0 : "cannot peek empty stack"; return stack[size - 1]; } @@ -95,7 +97,7 @@ ControlFrame peek() { * @param index Index from top of the stack * @return The value at (size - index - 1) */ - ControlFrame get(int index) { + public ControlFrame get(int index) { assert (size - index - 1) >= 0 && (size - index - 1) < size : "invalid element index"; return stack[size - index - 1]; } @@ -103,15 +105,15 @@ ControlFrame get(int index) { /** * @return The control frame on the bottom of the stack. */ - ControlFrame getFirst() { + public ControlFrame getFirst() { return stack[0]; } - boolean isEmpty() { + public boolean isEmpty() { return size == 0; } - int size() { + public int size() { return size; } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataFormatHelper.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataFormatHelper.java new file mode 100644 index 000000000000..b47d0dfc60f8 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataFormatHelper.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections; + +/** + * Helper class to indicate if a format extension is necessary. + */ +public interface ExtraDataFormatHelper { + /** + * Updates the current state of the extra data format. + */ + void extendExtraDataFormat(); +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java new file mode 100644 index 000000000000..5ca336779c7b --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections; + +import java.util.ArrayList; + +import org.graalvm.wasm.collection.IntArrayList; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTableEntry; +import org.graalvm.wasm.parser.validation.collections.entries.CallEntry; +import org.graalvm.wasm.parser.validation.collections.entries.ConditionalBranchEntry; +import org.graalvm.wasm.parser.validation.collections.entries.ElseEntry; +import org.graalvm.wasm.parser.validation.collections.entries.ExtraDataEntry; +import org.graalvm.wasm.parser.validation.collections.entries.IfEntry; +import org.graalvm.wasm.parser.validation.collections.entries.IndirectCallEntry; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTarget; +import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; +import org.graalvm.wasm.parser.validation.collections.entries.UnconditionalBranchEntry; + +/** + * Representation of extra data during module validation. Used to build up the extra data array. + */ +public class ExtraDataList implements ExtraDataFormatHelper { + private static final int INITIAL_EXTRA_DATA_SIZE = 8; + private static final int[] EMPTY_INT_ARRAY = new int[0]; + + private final ArrayList entries; + private final IntArrayList entrySizes; + + private int size; + private boolean extend; + + public ExtraDataList() { + this.entries = new ArrayList<>(INITIAL_EXTRA_DATA_SIZE); + this.entrySizes = new IntArrayList(); + this.size = 0; + this.extend = false; + } + + private void addEntry(ExtraDataEntry entry) { + entries.add(entry); + entrySizes.add(entry.length()); + size += entry.length(); + } + + @Override + public void extendExtraDataFormat() { + this.extend = true; + } + + public BranchTarget addIf(int offset) { + IfEntry entry = new IfEntry(offset, size, this); + addEntry(entry); + return entry; + } + + public BranchTarget addElse(int offset) { + ElseEntry entry = new ElseEntry(offset, size, this); + addEntry(entry); + return entry; + } + + public BranchTargetWithStackChange addConditionalBranch(int offset) { + ConditionalBranchEntry entry = new ConditionalBranchEntry(offset, size, this); + addEntry(entry); + return entry; + } + + public BranchTargetWithStackChange addUnconditionalBranch(int offset) { + UnconditionalBranchEntry entry = new UnconditionalBranchEntry(offset, size, this); + addEntry(entry); + return entry; + } + + public BranchTableEntry addBranchTable(int offset, int size) { + BranchTableEntry entry = new BranchTableEntry(offset, this.size, size, this); + addEntry(entry); + return entry; + } + + public void addIndirectCall(int nodeIndex) { + addEntry(new IndirectCallEntry(nodeIndex, this)); + } + + public void addCall(int nodeIndex) { + addEntry(new CallEntry(nodeIndex, this)); + } + + /** + * @return The next location in the extra data array + */ + public int nextEntryLocation() { + return size; + } + + /** + * @return The next index in the extra data list + */ + public int nextEntryIndex() { + return entries.size(); + } + + public int[] extraDataArray() { + if (size == 0) { + return EMPTY_INT_ARRAY; + } else { + int dataSize = size; + if (extend) { + int[] sizes = entrySizes.toArray(); + // Every size extensions can cause other size extensions (extra data displacement + // changes). Therefore, we can only stop if no more extensions happened. + while (extend) { + extend = false; + // Sync the sizes array with the new sizes of the entries for displacement + // calculations. + dataSize = 0; + for (int i = 0; i < entries.size(); i++) { + ExtraDataEntry entry = entries.get(i); + final int s = entry.length(); + dataSize += s; + sizes[i] = s; + } + // Update extra data displacements caused by entry size changes. + for (int i = 0; i < entries.size(); i++) { + ExtraDataEntry entry = entries.get(i); + if (entry instanceof BranchTarget) { + BranchTarget branchTargetEntry = (BranchTarget) entry; + branchTargetEntry.updateExtraDataDisplacement(distanceBetween(sizes, i, branchTargetEntry.extraDataTargetIndex())); + } + if (entry instanceof BranchTableEntry) { + BranchTableEntry branchTable = (BranchTableEntry) entry; + for (int j = 0; j < branchTable.size(); j++) { + BranchTargetWithStackChange branchTableEntry = branchTable.item(j); + branchTableEntry.updateExtraDataDisplacement(distanceBetween(sizes, i, branchTableEntry.extraDataTargetIndex())); + } + } + } + } + } + int[] data = new int[dataSize]; + int entryOffset = 0; + for (ExtraDataEntry entry : entries) { + entryOffset = entry.generateData(data, entryOffset); + } + return data; + } + } + + private static int distanceBetween(int[] sizes, int startIndex, int endIndex) { + int distance = 0; + if (endIndex > startIndex) { + for (int i = startIndex; i < endIndex; i++) { + distance += sizes[i]; + } + } else { + for (int i = endIndex; i < startIndex; i++) { + distance += sizes[i]; + } + } + return distance; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java new file mode 100644 index 000000000000..32bd75e4c44d --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents a br_table entry in the extra data list. + * + * Compact format: + * + * + * | compactFormatIndicator (1-bit) | size (unsigned 15-bit) | profileCounter (unsigned 16-bit) | compact brIfEntry | ... | compact brIfEntry | + * + * + * Extended format: + * + * + * | extendedFormatIndicator (1-bit) | size (unsigned 31-bit) | unused (16-bit) | profileCounter (unsigned 16-bit) | extended brIfEntry | ... | extended brIfEntry | + * + */ +public class BranchTableEntry extends ExtraDataEntry implements ExtraDataFormatHelper { + private final ConditionalBranchEntry[] items; + + public BranchTableEntry(int byteCodeOffset, int extraDataOffset, int size, ExtraDataFormatHelper formatHelper) { + super(formatHelper); + if (!ExtraDataUtil.isCompactUnsignedShortValueWithIndicator(size)) { + extendFormat(); + ExtraDataUtil.checkRepresentableUnsignedValueWithIndicator(size); + } + this.items = new ConditionalBranchEntry[size]; + for (int i = 0; i < size; i++) { + this.items[i] = new ConditionalBranchEntry(byteCodeOffset, extraDataOffset, this); + } + } + + @Override + public void extendExtraDataFormat() { + extendFormat(); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactTableHeader(extraData, offset, items.length); + for (ConditionalBranchEntry item : items) { + offset = item.generateCompactData(extraData, offset); + } + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedTableHeader(extraData, offset, items.length); + offset += ExtraDataUtil.addProfileCounter(extraData, offset); + for (ConditionalBranchEntry item : items) { + offset = item.generateExtendedData(extraData, offset); + } + return offset; + } + + @Override + public int compactLength() { + int size = ExtraDataUtil.COMPACT_TABLE_HEADER_SIZE; + for (ConditionalBranchEntry item : items) { + size += item.compactLength(); + } + return size; + } + + @Override + public int extendedLength() { + int size = ExtraDataUtil.EXTENDED_TABLE_HEADER_SIZE + ExtraDataUtil.PROFILE_SIZE; + for (ConditionalBranchEntry item : items) { + size += item.extendedLength(); + } + return size; + } + + public BranchTargetWithStackChange item(int index) { + return items[index]; + } + + public int size() { + return items.length; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java new file mode 100644 index 000000000000..3c889afc6286 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.exception.Failure; +import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents an entry that refers to a different location in the byte code and extra data. + */ +public abstract class BranchTarget extends ExtraDataEntry { + private final int byteCodeOffset; + private final int extraDataOffset; + private int byteCodeDisplacement; + private int extraDataDisplacement; + private int extraDataTargetIndex; + + private boolean compactExtraDataDisplacement; + + protected BranchTarget(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + super(formatHelper); + this.byteCodeOffset = byteCodeOffset; + this.extraDataOffset = extraDataOffset; + this.compactExtraDataDisplacement = true; + } + + /** + * Sets the information about the branch target. + * + * @param byteCodeTarget The target in the byte code + * @param extraDataTarget The target location in the extra data array + * @param extraDataTargetIndex The target index in the extra data list + */ + public void setTargetInfo(int byteCodeTarget, int extraDataTarget, int extraDataTargetIndex) { + try { + this.byteCodeDisplacement = Math.subtractExact(byteCodeTarget, byteCodeOffset); + this.extraDataDisplacement = Math.subtractExact(extraDataTarget, extraDataOffset); + } catch (ArithmeticException e) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } + this.extraDataTargetIndex = extraDataTargetIndex; + final boolean compact = ExtraDataUtil.areCompactSignedShortValuesWithIndicator(extraDataDisplacement, byteCodeDisplacement); + if (!compact) { + extendFormat(); + ExtraDataUtil.checkRepresentableSignedValueWithIndicator(extraDataDisplacement); + } + this.compactExtraDataDisplacement = compact; + } + + protected int compactByteCodeDisplacement() { + return Short.toUnsignedInt((short) byteCodeDisplacement); + } + + protected int extendedByteCodeDisplacement() { + return byteCodeDisplacement; + } + + protected int compactExtraDataDisplacement() { + return Short.toUnsignedInt((short) extraDataDisplacement); + } + + protected int extendedExtraDataDisplacement() { + return extraDataDisplacement; + } + + /** + * @return The index of the branch target in the extra data list + */ + public int extraDataTargetIndex() { + return extraDataTargetIndex; + } + + public void updateExtraDataDisplacement(int extraDataDisplacement) { + this.extraDataDisplacement = extraDataDisplacement; + final boolean compact = ExtraDataUtil.isCompactSignedShortValueWithIndicator(extraDataDisplacement); + if (compactExtraDataDisplacement != compact) { + compactExtraDataDisplacement = compact; + extendFormat(); + } + ExtraDataUtil.checkRepresentableSignedValueWithIndicator(extraDataDisplacement); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java new file mode 100644 index 000000000000..288ee6d48ac4 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents and entry that changes the number of values on the values stack and potentially + * returns a value. + */ +public abstract class BranchTargetWithStackChange extends BranchTarget { + private int returnLength; + private int stackSize; + + protected BranchTargetWithStackChange(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + super(byteCodeOffset, extraDataOffset, formatHelper); + } + + /** + * Sets the information about the stack change. + * + * @param returnLength The number of return values + * @param stackSize The stack size after the jump + */ + public void setStackInfo(int returnLength, int stackSize) { + this.returnLength = returnLength; + this.stackSize = stackSize; + if (!ExtraDataUtil.areCompactUnsignedBytes(returnLength, stackSize)) { + extendFormat(); + ExtraDataUtil.checkRepresentableValue(returnLength); + ExtraDataUtil.checkRepresentableValue(stackSize); + } + } + + protected int returnLength() { + return returnLength; + } + + protected int stackSize() { + return stackSize; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallEntry.java new file mode 100644 index 000000000000..fd7222ee173b --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallEntry.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents a call entry in the extra data list. + * + * Compact format: + * + * + * | compactFormatIndicator (1-bit) | nodeIndex (unsigned 15-bit) | unused (16-bit) | + * + * + * Extended format: + * + * + * | extendedFormatIndicator (1-bit) | nodeIndex (unsigned 31-bit) | + * + */ +public class CallEntry extends CallTarget { + public CallEntry(int nodeIndex, ExtraDataFormatHelper formatHelper) { + super(nodeIndex, formatHelper); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactCallTarget(extraData, offset, getNodeIndex()); + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedCallTarget(extraData, offset, getNodeIndex()); + return offset; + } + + @Override + public int compactLength() { + return ExtraDataUtil.COMPACT_CALL_TARGET_SIZE; + } + + @Override + public int extendedLength() { + return ExtraDataUtil.EXTENDED_CALL_TARGET_SIZE; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java new file mode 100644 index 000000000000..82c29ef0a6d0 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents an entry that is a function call. + */ +public abstract class CallTarget extends ExtraDataEntry { + private final int nodeIndex; + + protected CallTarget(int nodeIndex, ExtraDataFormatHelper formatHelper) { + super(formatHelper); + this.nodeIndex = nodeIndex; + if (!ExtraDataUtil.isCompactUnsignedShortValueWithIndicator(nodeIndex)) { + extendFormat(); + ExtraDataUtil.checkRepresentableUnsignedValueWithIndicator(nodeIndex); + } + } + + public int getNodeIndex() { + return nodeIndex; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java new file mode 100644 index 000000000000..cb9b2ffc1821 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents a br_if entry in the extra data list. + * + * Compact format: + * + * + * | compactFormatIndicator (1-bit) | extraDataDisplacement (signed 15-bit) | byteCodeDisplacement (signed 16-bit) | returnLength (unsigned 8-bit) | stackSize (unsigned 8-bit) | profileCounter (unsigned 16-bit) | + * + * + * Extended format: + * + * + * | extendedFormatIndicator (1-bit) | extraDataDisplacement (signed 31-bit) | byteCodeDisplacement (signed 32-bit) | returnLength (signed 32-bit) | stackSize (signed 32-bit) | unused (16-bit) | profileCounter (unsigned 16-bit) | + * + */ +public class ConditionalBranchEntry extends BranchTargetWithStackChange { + public ConditionalBranchEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + super(byteCodeOffset, extraDataOffset, formatHelper); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactBranchTarget(extraData, offset, compactByteCodeDisplacement(), compactExtraDataDisplacement()); + offset += ExtraDataUtil.addCompactStackChange(extraData, offset, returnLength(), stackSize()); + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedBranchTarget(extraData, offset, extendedByteCodeDisplacement(), extendedExtraDataDisplacement()); + offset += ExtraDataUtil.addExtendedStackChange(extraData, offset, returnLength(), stackSize()); + offset += ExtraDataUtil.addProfileCounter(extraData, offset); + return offset; + } + + @Override + public int compactLength() { + return ExtraDataUtil.COMPACT_JUMP_TARGET_SIZE + ExtraDataUtil.COMPACT_STACK_CHANGE_SIZE; + } + + @Override + public int extendedLength() { + return ExtraDataUtil.EXTENDED_JUMP_TARGET_SIZE + ExtraDataUtil.EXTENDED_STACK_CHANGE_SIZE + ExtraDataUtil.PROFILE_SIZE; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java new file mode 100644 index 000000000000..e5cb2478e590 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents an else entry in the extra data list. + * + * Compact format: + * + * + * | compactFormatIndicator (1-bit) | extraDataDisplacement (signed 15-bit) | byteCodeDisplacement (signed 16-bit) | + * + * + * Extended format: + * + * + * | extendedFormatIndicator (1-bit) | extraDataDisplacement (signed 31-bit) | byteCodeDisplacement (signed 32-bit) | + * + */ +public class ElseEntry extends BranchTarget { + public ElseEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + super(byteCodeOffset, extraDataOffset, formatHelper); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactBranchTarget(extraData, offset, compactByteCodeDisplacement(), compactExtraDataDisplacement()); + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedBranchTarget(extraData, offset, extendedByteCodeDisplacement(), extendedExtraDataDisplacement()); + return offset; + } + + @Override + public int compactLength() { + return ExtraDataUtil.COMPACT_JUMP_TARGET_SIZE; + } + + @Override + public int extendedLength() { + return ExtraDataUtil.EXTENDED_JUMP_TARGET_SIZE; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ExtraDataEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ExtraDataEntry.java new file mode 100644 index 000000000000..3f051b9fdf1a --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ExtraDataEntry.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; + +/** + * Represents an entry in the extra data list. + */ +public abstract class ExtraDataEntry { + private final ExtraDataFormatHelper formatHelper; + private boolean compact; + + protected ExtraDataEntry(ExtraDataFormatHelper formatHelper) { + this.compact = true; + this.formatHelper = formatHelper; + } + + /** + * Extend the internal format of the entry. + */ + protected void extendFormat() { + this.compact = false; + formatHelper.extendExtraDataFormat(); + } + + /** + * Generates compact data for this entry in the extra data array. + * + * @param extraData The extra data array + * @param entryOffset The offset in the extra data + * @return The offset after the generated entry + */ + protected abstract int generateCompactData(int[] extraData, int entryOffset); + + /** + * Generates extended data for this entry in the extra data array. + * + * @param extraData The extra data array + * @param entryOffset The offset in the extra data + * @return The offset after the generated entry + */ + protected abstract int generateExtendedData(int[] extraData, int entryOffset); + + /** + * Generates data for this entry in the extra data array. + * + * @param extraData The extra data array. + * @param entryOffset The offset in the extra data + * @return The offset after the generated entry in the extra data array + */ + public int generateData(int[] extraData, int entryOffset) { + if (this.compact) { + return generateCompactData(extraData, entryOffset); + } + return generateExtendedData(extraData, entryOffset); + } + + /** + * @return The compact size of this entry in the extra data array + */ + protected abstract int compactLength(); + + /** + * @return The extended size of this entry in the extra data array + */ + protected abstract int extendedLength(); + + /** + * @return The size of this entry in the extra data array + */ + public int length() { + if (this.compact) { + return compactLength(); + } else { + return extendedLength(); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java new file mode 100644 index 000000000000..606731af8477 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents an if entry in the extra data list. + * + * Compact format: + * + * + * | compactFormatIndicator (1-bit) | extraDataDisplacement (signed 15-bit) | byteCodeDisplacement (signed 16-bit) | unused (16-bit) | profileCounter (unsigned 16-bit) | + * + * + * Extended format: + * + * + * | extendedFormatIndicator (1-bit) | extraDataDisplacement (signed 32-bit) | byteCodeDisplacement (signed 32-bit) | unused (16-bit) | profileCounter (unsigned 16-bit) | + * + */ +public class IfEntry extends BranchTarget { + + public IfEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + super(byteCodeOffset, extraDataOffset, formatHelper); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactBranchTarget(extraData, offset, compactByteCodeDisplacement(), compactExtraDataDisplacement()); + offset += ExtraDataUtil.addProfileCounter(extraData, offset); + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedBranchTarget(extraData, offset, extendedByteCodeDisplacement(), extendedExtraDataDisplacement()); + offset += ExtraDataUtil.addProfileCounter(extraData, offset); + return offset; + } + + @Override + public int compactLength() { + return ExtraDataUtil.COMPACT_JUMP_TARGET_SIZE + ExtraDataUtil.PROFILE_SIZE; + } + + @Override + public int extendedLength() { + return ExtraDataUtil.EXTENDED_JUMP_TARGET_SIZE + ExtraDataUtil.PROFILE_SIZE; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IndirectCallEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IndirectCallEntry.java new file mode 100644 index 000000000000..4cb137637780 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IndirectCallEntry.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents a call_indirect entry in the extra data list. + */ +public class IndirectCallEntry extends CallTarget { + public IndirectCallEntry(int nodeIndex, ExtraDataFormatHelper formatHelper) { + super(nodeIndex, formatHelper); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactCallTarget(extraData, offset, getNodeIndex()); + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedCallTarget(extraData, offset, getNodeIndex()); + offset += ExtraDataUtil.addProfileCounter(extraData, offset); + return offset; + } + + @Override + public int compactLength() { + return ExtraDataUtil.COMPACT_CALL_TARGET_SIZE; + } + + @Override + public int extendedLength() { + return ExtraDataUtil.EXTENDED_CALL_TARGET_SIZE + ExtraDataUtil.PROFILE_SIZE; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java new file mode 100644 index 000000000000..4fe9d6ae82b3 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.parser.validation.collections.entries; + +import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; +import org.graalvm.wasm.util.ExtraDataUtil; + +/** + * Represents a br entry in the extra data list. + * + * Compact format: + * + * + * | compactFormatIndicator (1-bit) | extraDataDisplacement (signed 15-bit) | byteCodeDisplacement (signed 16-bit) | returnLength (unsigned 8-bit) | stackSize (unsigned 8-bit) | unused (16-bit) | + * + * + * Extended format: + * + * + * | extendedFormatIndicator (1-bit) | extraDataDisplacement (signed 31-bit) | byteCodeDisplacement (signed 32-bit) | returnLength (signed 32-bit) | stackSize (signed 32-bit) | + * + */ +public class UnconditionalBranchEntry extends BranchTargetWithStackChange { + public UnconditionalBranchEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + super(byteCodeOffset, extraDataOffset, formatHelper); + } + + @Override + protected int generateCompactData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addCompactBranchTarget(extraData, offset, compactByteCodeDisplacement(), compactExtraDataDisplacement()); + offset += ExtraDataUtil.addCompactStackChange(extraData, offset, returnLength(), stackSize()); + return offset; + } + + @Override + protected int generateExtendedData(int[] extraData, int entryOffset) { + int offset = entryOffset; + offset += ExtraDataUtil.addExtendedBranchTarget(extraData, offset, extendedByteCodeDisplacement(), extendedExtraDataDisplacement()); + offset += ExtraDataUtil.addExtendedStackChange(extraData, offset, returnLength(), stackSize()); + return offset; + } + + @Override + public int compactLength() { + return ExtraDataUtil.COMPACT_JUMP_TARGET_SIZE + ExtraDataUtil.COMPACT_STACK_CHANGE_SIZE; + } + + @Override + public int extendedLength() { + return ExtraDataUtil.EXTENDED_JUMP_TARGET_SIZE + ExtraDataUtil.EXTENDED_STACK_CHANGE_SIZE; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java new file mode 100644 index 000000000000..c0f70bedd60f --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.util; + +/** + * Helper class for accessing extra data entries. + */ +public class ExtraDataAccessor { + private static final int EXTENDED_FORMAT_INDICATOR = 0x8000_0000; + private static final int INDICATOR_REMOVAL_MASK = 0x7fff_ffff; + + public static final int COMPACT_IF_LENGTH = 2; + public static final int EXTENDED_IF_LENGTH = 3; + + public static final int COMPACT_IF_PROFILE_OFFSET = 1; + public static final int EXTENDED_IF_PROFILE_OFFSET = 2; + + public static final int COMPACT_BR_IF_LENGTH = 2; + public static final int EXTENDED_BR_IF_LENGTH = 5; + + public static final int COMPACT_BR_IF_PROFILE_OFFSET = 1; + public static final int EXTENDED_BR_IF_PROFILE_OFFSET = 4; + + public static final int COMPACT_BR_TABLE_PROFILE_OFFSET = 0; + public static final int EXTENDED_BR_TABLE_PROFILE_OFFSET = 1; + + public static final int COMPACT_BR_TABLE_HEADER_LENGTH = 1; + public static final int EXTENDED_BR_TABLE_HEADER_LENGTH = 2; + + public static final int CALL_LENGTH = 1; + + public static final int COMPACT_CALL_INDIRECT_LENGTH = 1; + public static final int EXTENDED_CALL_INDIRECT_LENGTH = 2; + + public static final int COMPACT_CALL_INDIRECT_PROFILE_OFFSET = 0; + public static final int EXTENDED_CALL_INDIRECT_PROFILE_OFFSET = 1; + + /** + * Checks if the given entry is in compact format. + */ + public static boolean isCompactFormat(int[] extraData, int offset) { + return (extraData[offset] & EXTENDED_FORMAT_INDICATOR) == 0; + } + + public static int firstValueSigned(int[] extraData, int offset, boolean compact) { + if (compact) { + return (extraData[offset] << 1) >> 17; + } + return extraData[offset] & INDICATOR_REMOVAL_MASK; + } + + public static int firstValueUnsigned(int[] extraData, int offset, boolean compact) { + if (compact) { + return (extraData[offset] & INDICATOR_REMOVAL_MASK) >>> 16; + } + return extraData[offset] & INDICATOR_REMOVAL_MASK; + } + + public static int secondValueSigned(int[] extraData, int offset, boolean compact) { + if (compact) { + return (extraData[offset] << 16) >> 16; + } + return extraData[offset + 1]; + } + + public static int thirdValueUnsigned(int[] extraData, int offset, boolean compact) { + if (compact) { + return (extraData[offset + 1] & 0xff00_0000) >>> 24; + } + return extraData[offset + 2]; + } + + public static int forthValueUnsigned(int[] extraData, int offset, boolean compact) { + if (compact) { + return (extraData[offset + 1] & 0x00ff_0000) >>> 16; + } + return extraData[offset + 3]; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java new file mode 100644 index 000000000000..b5990a1b2984 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.wasm.util; + +import org.graalvm.wasm.exception.Failure; +import org.graalvm.wasm.exception.WasmException; + +/** + * Helper class for generating extra data entries. + */ +public class ExtraDataUtil { + private static final int EXTENDED_FORMAT_INDICATOR = 0x8000_0000; + + private static final int MIN_SIGNED_31BIT_VALUE = 0xc000_0000; + private static final int MAX_SIGNED_31BIT_VALUE = 0x3fff_ffff; + + private static final int MAX_UNSIGNED_15BIT_VALUE = 0x0000_7fff; + private static final int MIN_SIGNED_15BIT_VALUE = 0xffff_c000; + private static final int MAX_SIGNED_15BIT_VALUE = 0x0000_3fff; + + private static final int MAX_UNSIGNED_8BIT_VALUE = 0x0000_00ff; + + /** + * Compact sizes. + */ + public static final int COMPACT_JUMP_TARGET_SIZE = 1; + public static final int COMPACT_STACK_CHANGE_SIZE = 1; + public static final int COMPACT_CALL_TARGET_SIZE = 1; + public static final int COMPACT_TABLE_HEADER_SIZE = 1; + + /** + * Extended sizes. + */ + public static final int EXTENDED_JUMP_TARGET_SIZE = 2; + public static final int EXTENDED_STACK_CHANGE_SIZE = 2; + public static final int EXTENDED_CALL_TARGET_SIZE = 1; + public static final int EXTENDED_TABLE_HEADER_SIZE = 1; + + public static final int PROFILE_SIZE = 1; + + /** + * Combines the given value into a single integer in the following format: + * + * + * | 0 (1-bit) | upperValue (15-bit) | lowerValue (16-bit) | + * + * + * The {@link ExtraDataUtil#areCompactSignedShortValuesWithIndicator(int, int)} method can be + * used to check if the given values can be represented by the resulting integer. + * + * @param upperValue The upper value of the resulting integer + * @param lowerValue The lower value of the resulting integer + * @return The combined integer value + */ + private static int createCompactShortValuesWithIndicator(int upperValue, int lowerValue) { + return ((upperValue << 16) | lowerValue) & 0x7fff_ffff; + } + + /** + * Combines the given values into the upper 16-bit of a single integer in the following format: + * + * + * | upperValue (8-bit) | lowerValue (8-bit) | 0 (16-bit) | + * + * + * The {@link ExtraDataUtil#areCompactUnsignedBytes(int, int)} method can be used to check if + * the given values can be represented by the resulting integer. + * + * @param upperValue The return length + * @param lowerValue The stack size + * @return The stack change entry + */ + private static int createCompactUpperBytes(int upperValue, int lowerValue) { + return (upperValue << 24) | (lowerValue << 16); + } + + /** + * Checks if the given values can be represented in a compactShortValuesWithIndicator format. + * + * @param upperValue The upper value of the resulting integer + * @param lowerValue The lower value of the resulting integer + * @return True if the values fit into the compactShortValuesWithIndicator format, false + * otherwise + */ + public static boolean areCompactSignedShortValuesWithIndicator(int upperValue, int lowerValue) { + return isCompactSignedShortValueWithIndicator(upperValue) && Short.MIN_VALUE <= lowerValue && lowerValue <= Short.MAX_VALUE; + } + + /** + * Checks if the given value can be represented as the upper value of a + * compactShortValuesWithIndicator format. + * + * @param upperValue The upper value of the resulting integer + * @return True if the value fits the upper part of the compactShortValuesWithIndicator format, + * false otherwise + */ + public static boolean isCompactUnsignedShortValueWithIndicator(int upperValue) { + return Integer.compareUnsigned(upperValue, MAX_UNSIGNED_15BIT_VALUE) <= 0; + } + + public static boolean isCompactSignedShortValueWithIndicator(int upperValue) { + return MIN_SIGNED_15BIT_VALUE <= upperValue && upperValue <= MAX_SIGNED_15BIT_VALUE; + } + + public static void checkRepresentableUnsignedValueWithIndicator(int value) { + if (value < 0) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } + } + + public static void checkRepresentableSignedValueWithIndicator(int value) { + if (value < MIN_SIGNED_31BIT_VALUE || value >= MAX_SIGNED_31BIT_VALUE) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } + } + + /** + * Checks if the given values can be represented as the compactUpperBytes format. + * + * @param upperValue The return length + * @param lowerValue The stack size + * @return True, if a compact stack change entry could be constructed + */ + public static boolean areCompactUnsignedBytes(int upperValue, int lowerValue) { + return Integer.compareUnsigned(upperValue, MAX_UNSIGNED_8BIT_VALUE) <= 0 && Integer.compareUnsigned(lowerValue, MAX_UNSIGNED_8BIT_VALUE) <= 0; + } + + public static void checkRepresentableValue(int value) { + if (value < 0) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } + } + + /** + * Adds a compact jump target entry, consisting of byte code displacement and extra data + * displacement, at the given offset. The jump target entry has to be at the start of an extra + * data entry. + * + * The resulting entry looks as follows: + * + * + * | 0 (1-bit) | extraDataDisplacement (15-bits) | byteCodeDisplacement (16-bits) | + * + * + * @param extraData The extra data array + * @param branchTargetOffset The offset in the array + * @param byteCodeDisplacement The relative byte code offset + * @param extraDataDisplacement The relative extra offset + * @return The number of added array entries + */ + public static int addCompactBranchTarget(int[] extraData, int branchTargetOffset, int byteCodeDisplacement, int extraDataDisplacement) { + extraData[branchTargetOffset] = createCompactShortValuesWithIndicator(extraDataDisplacement, byteCodeDisplacement); + return COMPACT_JUMP_TARGET_SIZE; + } + + /** + * Adds an extended jump target entry, consisting of byte code displacement and extra data + * displacement, at the given offset. The jump target entry has to be at the start of an extra + * data entry. + * + * The resulting entry looks as follows: + * + * + * | 1 (1-bit) | extraDataDisplacement (31-bits) | byteCodeDisplacement (32-bit) | + * + * + * @param extraData The extra data array + * @param branchTargetOffset The offset in the array + * @param byteCodeDisplacement The relative byte code offset + * @param extraDataDisplacement The relative extra offset + * @return The number of added array entries + */ + public static int addExtendedBranchTarget(int[] extraData, int branchTargetOffset, int byteCodeDisplacement, int extraDataDisplacement) { + extraData[branchTargetOffset] = extraDataDisplacement | EXTENDED_FORMAT_INDICATOR; + extraData[branchTargetOffset + 1] = byteCodeDisplacement; + return EXTENDED_JUMP_TARGET_SIZE; + } + + /** + * Adds a compact stack change entry, consisting of return length and stack size, at the given + * offset. The stack change entry cannot be at the start of an extra data entry. + * + * The resulting entry looks as follows: + * + * + * | returnLength (8-bit) | stackSize (8-bit) | 0 (16-bit) | + * + * + * @param extraData The extra data array + * @param stackChangeOffset The offset in the array + * @param returnLength The return length + * @param stackSize The stack size + * @return The number of added array entries + */ + public static int addCompactStackChange(int[] extraData, int stackChangeOffset, int returnLength, int stackSize) { + extraData[stackChangeOffset] = createCompactUpperBytes(returnLength, stackSize); + return COMPACT_STACK_CHANGE_SIZE; + } + + /** + * Adds an extended stack change entry, consisting of return length and stack size, at the given + * offset. The stack change entry cannot be at the start of an extra data entry. + * + * The resulting entry looks as follows: + * + * + * | returnLength (32-bit) | stackSize (32-bit) | + * + * + * @param extraData The extra data array + * @param stackChangeOffset The offset in the array + * @param returnLength The return length + * @param stackSize The stack size + * @return The number of added array entries + */ + public static int addExtendedStackChange(int[] extraData, int stackChangeOffset, int returnLength, int stackSize) { + extraData[stackChangeOffset] = returnLength; + extraData[stackChangeOffset + 1] = stackSize; + return EXTENDED_STACK_CHANGE_SIZE; + } + + /** + * Adds a compact call target entry, consisting of a node index, at the given offset. The call + * target entry has to be at the start of an extra data entry. + * + * The resulting entry looks as follows: + * + * + * | 0 (1-bit) | nodeIndex (15-bit) | 0 (16-bit) | + * + * + * @param extraData The extra data array + * @param callTargetOffset The offset in the array + * @param nodeIndex The node index + * @return The number of added array entries + */ + public static int addCompactCallTarget(int[] extraData, int callTargetOffset, int nodeIndex) { + extraData[callTargetOffset] = createCompactShortValuesWithIndicator(nodeIndex, 0); + return COMPACT_CALL_TARGET_SIZE; + } + + /** + * Adds an extended call target entry, consisting of a node index, at the given offset. The call + * target entry has to be at the start of an extra data entry. + * + * The resulting entry looks as follows: + * + * + * | 1 (1-bit) | nodeIndex (31-bit) | + * + * + * @param extraData The extra data array + * @param callTargetOffset The offset in the array + * @param nodeIndex The node index + * @return The number of added array entries + */ + public static int addExtendedCallTarget(int[] extraData, int callTargetOffset, int nodeIndex) { + extraData[callTargetOffset] = (nodeIndex | EXTENDED_FORMAT_INDICATOR); + return EXTENDED_CALL_TARGET_SIZE; + } + + /** + * Adds a compact table header entry, consisting of the table size, at the given offset. The + * table header entry has to be at the start of an extra data entry. + * + * The resulting entry looks a follows: + * + * + * | 0 (1-bit) | size (15-bit) | 0 (16-bit) | + * + * + * @param extraData The extra data array + * @param tableOffset The offset in the array + * @param size The size of the table + * @return The number of added array entries + */ + public static int addCompactTableHeader(int[] extraData, int tableOffset, int size) { + extraData[tableOffset] = createCompactShortValuesWithIndicator(size, 0); + return COMPACT_TABLE_HEADER_SIZE; + } + + /** + * Adds an extended table header entry, consisting of the table size, at the given offset. The + * table header entry has to be at the start of an extra data entry. + * + * The resulting entry looks as follows: + * + * + * | 1 (1-bit) | size (31-bit) | + * + * + * @param extraData The extra data array + * @param tableOffset The offset in the array + * @param size The size of the table + * @return The number of added array entries + */ + public static int addExtendedTableHeader(int[] extraData, int tableOffset, int size) { + extraData[tableOffset] = (size | EXTENDED_FORMAT_INDICATOR); + return EXTENDED_TABLE_HEADER_SIZE; + } + + /** + * Adds a profile counter at the given offset. The profile counter cannot be at the start of an + * extra data entry. + * + * The resulting entry looks as follows: + * + * + * | profileCounter (32-bit) | + * + * + * @param extraData The extra data array + * @param profileOffset The offset in the array + * @return The number of added array entries + */ + @SuppressWarnings("unused") + public static int addProfileCounter(int[] extraData, int profileOffset) { + return PROFILE_SIZE; + } +} From 99b6fb1c033e6e32337af350c755cccdc82a20e5 Mon Sep 17 00:00:00 2001 From: Florian Huemer Date: Mon, 23 May 2022 14:19:47 +0200 Subject: [PATCH 2/4] Removed unnecessary operations. --- .../wasm/test/suites/ExtraDataSuite.java | 468 ++++++------------ .../src/org/graalvm/wasm/BinaryParser.java | 16 +- .../graalvm/wasm/nodes/WasmFunctionNode.java | 57 +-- .../wasm/parser/validation/ParserState.java | 2 +- .../validation/collections/ExtraDataList.java | 65 +-- .../collections/entries/BranchTableEntry.java | 16 +- .../collections/entries/BranchTarget.java | 46 +- .../entries/BranchTargetWithStackChange.java | 13 +- .../collections/entries/CallTarget.java | 8 +- .../entries/ConditionalBranchEntry.java | 4 +- .../collections/entries/ElseEntry.java | 4 +- .../collections/entries/IfEntry.java | 4 +- .../entries/UnconditionalBranchEntry.java | 4 +- .../graalvm/wasm/util/ExtraDataAccessor.java | 35 +- .../org/graalvm/wasm/util/ExtraDataUtil.java | 92 +--- 15 files changed, 304 insertions(+), 530 deletions(-) diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java index c698b1273c22..35cc6b281ee4 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/ExtraDataSuite.java @@ -41,368 +41,198 @@ package org.graalvm.wasm.test.suites; -import org.graalvm.wasm.exception.WasmException; -import org.graalvm.wasm.parser.validation.collections.ExtraDataList; -import org.graalvm.wasm.parser.validation.collections.entries.BranchTableEntry; -import org.graalvm.wasm.parser.validation.collections.entries.BranchTarget; -import org.graalvm.wasm.parser.validation.collections.entries.BranchTargetWithStackChange; +import org.graalvm.wasm.WasmType; +import org.graalvm.wasm.parser.validation.ParserState; import org.junit.Assert; import org.junit.Test; public class ExtraDataSuite { - @Test - public void testCompactByteCodeDisplacementSimpleForwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - t.setTargetInfo(1, 0, 0); - final int[] expected = {1, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactByteCodeDisplacementSimpleBackwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(1); - t.setTargetInfo(0, 0, 0); - final int[] expected = {65535, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactByteCodeDisplacementMaxForwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - t.setTargetInfo(32767, 0, 0); - final int[] expected = {32767, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); + private static int compactTargetInfo(int byteCodeTarget, int extraDataTarget) { + int e = Short.toUnsignedInt((short) extraDataTarget); + int b = Short.toUnsignedInt((short) byteCodeTarget); + return ((e << 16) + b) & 0x7fff_ffff; } - @Test - public void testCompactBytecodeDisplacementMaxBackwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(32768); - t.setTargetInfo(0, 0, 0); - final int[] expected = {32768, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); + private static int extendedTargetInfo(int extraDataTarget) { + return 0x8000_0000 | extraDataTarget; } @Test - public void testExtendedByteCodeDisplacementMinForwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - t.setTargetInfo(32768, 0, 0); - final int[] expected = {-2147483648, 32768, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } + public void testCompactByteCodeTargetMaxBackJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterLoop(WasmType.VOID_TYPE, 10); + state.addConditionalBranch(0, 32778); + state.exit(32779); + state.exit(32780); - @Test - public void testExtendedByteCodeDisplacementMinBackwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(32769); - t.setTargetInfo(0, 0, 0); - final int[] expected = {-2147483648, -32769, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedByteCodeDisplacementMaxForwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - t.setTargetInfo(2147483647, 0, 0); - final int[] expected = {-2147483648, 2147483647, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedByteCodeDisplacementMaxBackwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(2147483647); - t.setTargetInfo(0, 0, 0); - final int[] expected = {-2147483648, -2147483647, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactExtraDataDisplacementSimpleForwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - l.addCall(0); - t.setTargetInfo(0, 2, 1); - final int[] expected = {131072, 0, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); + final int[] expected = {compactTargetInfo(-32768, 0), 0}; + Assert.assertArrayEquals(expected, state.extraData()); } @Test - public void testCompactExtraDataDisplacementSimpleBackwardJump() { - ExtraDataList l = new ExtraDataList(); - l.addCall(0); - BranchTarget t = l.addIf(0); - t.setTargetInfo(0, 0, 0); - final int[] expected = {0, 2147418112, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); + public void testCompactByteCodeTargetMaxForwardJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 10); + state.exit(32777); + state.exit(32778); + final int[] expected = {compactTargetInfo(32767, 2), 0}; + Assert.assertArrayEquals(expected, state.extraData()); } @Test - public void testCompactExtraDataDisplacementMaxForwardJump() { - ExtraDataList l = new ExtraDataList(); - int displacement = 16383; - BranchTarget t = l.addIf(0); - for (int i = 0; i < displacement - 1; i++) { - l.addCall(0); - } - t.setTargetInfo(0, displacement, displacement - 2); - final int[] expected = new int[displacement + 1]; - expected[0] = 1073676288; - Assert.assertArrayEquals(expected, l.extraDataArray()); + public void testExtendedByteCodeTargetMinBackJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterLoop(WasmType.VOID_TYPE, 10); + state.addConditionalBranch(0, 32779); + state.exit(32780); + state.exit(32781); + final int[] expected = {extendedTargetInfo(0), -32769, 0, 0, 0}; + Assert.assertArrayEquals(expected, state.extraData()); } @Test - public void testCompactExtraDataDisplacementMaxBackwardJump() { - ExtraDataList l = new ExtraDataList(); - int displacement = 16384; - for (int i = 0; i < displacement; i++) { - l.addCall(0); - } - BranchTarget t = l.addIf(displacement); - t.setTargetInfo(displacement, 0, 0); - final int[] expected = new int[displacement + 2]; - expected[displacement] = 1073741824; - Assert.assertArrayEquals(expected, l.extraDataArray()); + public void testExtendedByteCodeTargetMinForwardJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 10); + state.exit(32778); + state.exit(32779); + final int[] expected = {extendedTargetInfo(5), 32768, 0, 0, 0}; + Assert.assertArrayEquals(expected, state.extraData()); } @Test - public void testExtendedExtraDataDisplacementMinForwardJump() { - ExtraDataList l = new ExtraDataList(); - int displacement = 16384; - BranchTarget t = l.addIf(0); - for (int i = 0; i < displacement - 1; i++) { - l.addCall(0); + public void testCompactExtraDataTargetMaxBackJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterLoop(WasmType.VOID_TYPE, 0); + for (int i = 0; i < 16384; i++) { + state.addCall(0); } - t.setTargetInfo(0, displacement, displacement - 2); - final int[] expected = new int[displacement + 2]; - expected[0] = -2147467264; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedExtraDataDisplacementMinBackwardJump() { - ExtraDataList l = new ExtraDataList(); - int displacement = 16385; - for (int i = 0; i < displacement; i++) { - l.addCall(0); + state.addConditionalBranch(0, 16385); + state.exit(16386); + state.exit(16387); + final int[] expected = new int[16386]; + expected[16384] = compactTargetInfo(-16385, -16384); + Assert.assertArrayEquals(expected, state.extraData()); + } + + @Test + public void testCompactExtraDataTargetMaxForwardJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 0); + for (int i = 0; i < 16381; i++) { + state.addCall(0); } - BranchTarget t = l.addIf(displacement); - t.setTargetInfo(displacement, 0, 0); - final int[] expected = new int[displacement + 3]; - expected[displacement] = -2147467263; - Assert.assertArrayEquals(expected, l.extraDataArray()); + state.exit(16382); + state.exit(16383); + final int[] expected = new int[16383]; + expected[0] = compactTargetInfo(16382, 16383); + Assert.assertArrayEquals(expected, state.extraData()); } @Test - public void testExtendedExtraDataDisplacementOutOfBoundsForwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - try { - t.setTargetInfo(0, 1073741824, 0); - Assert.fail("Should have thrown exception"); - } catch (WasmException e) { - Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + public void testExtendedExtraDataTargetMinBackwardJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterLoop(WasmType.VOID_TYPE, 0); + for (int i = 0; i < 16385; i++) { + state.addCall(0); } - } - - @Test - public void testExtendedExtraDataDisplacementOutOfBoundsBackwardJump() { - ExtraDataList l = new ExtraDataList(); - BranchTarget t = l.addIf(0); - try { - t.setTargetInfo(0, -1073741825, 0); - Assert.fail("Should have thrown exception"); - } catch (WasmException e) { - Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + state.addConditionalBranch(0, 16386); + state.exit(16387); + state.exit(16388); + final int[] expected = new int[16390]; + expected[16385] = extendedTargetInfo(-16385); + expected[16386] = -16386; + Assert.assertArrayEquals(expected, state.extraData()); + } + + @Test + public void testExtendedExtraDataTargetMinForwardJump() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 0); + for (int i = 0; i < 16382; i++) { + state.addCall(0); } + state.exit(16383); + state.exit(16384); + final int[] expected = new int[16387]; + expected[0] = extendedTargetInfo(16387); + expected[1] = 16383; + Assert.assertArrayEquals(expected, state.extraData()); } @Test public void testCompactReturnLength() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(1, 0); - final int[] expected = {0, 16777216}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactReturnLengthMaxValue() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(255, 0); - final int[] expected = {0, -16777216}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedReturnLengthMinValue() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(256, 0); - final int[] expected = {-2147483648, 0, 256, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedReturnLengthMaxValue() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(2147483647, 0); - final int[] expected = {-2147483648, 0, 2147483647, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedReturnLengthOutOfBounds() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - try { - t.setStackInfo(-1, 0); - Assert.fail("Should have thrown exception"); - } catch (WasmException e) { - Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); - } + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.enterBlock(WasmType.I32_TYPE); + state.push(WasmType.I32_TYPE); + state.addConditionalBranch(0, 10); + state.exit(20); + state.pop(); + state.exit(21); + final int[] expected = {compactTargetInfo(10, 2), 1 << 24}; + Assert.assertArrayEquals(expected, state.extraData()); } @Test public void testCompactStackSize() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(0, 1); - final int[] expected = {0, 65536}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactStackSizeMaxValue() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(0, 255); - final int[] expected = {0, 16711680}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedStackSizeMinValue() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(0, 256); - final int[] expected = {-2147483648, 0, 0, 256}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedStackSizeMaxValue() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - t.setStackInfo(0, 2147483647); - final int[] expected = {-2147483648, 0, 0, 2147483647}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedStackSizeOutOfBounds() { - ExtraDataList l = new ExtraDataList(); - BranchTargetWithStackChange t = l.addUnconditionalBranch(0); - try { - t.setStackInfo(0, -1); - Assert.fail("Should have thrown exception"); - } catch (WasmException e) { - Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + state.push(WasmType.I32_TYPE); + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 10); + state.exit(20); + state.pop(); + state.exit(21); + final int[] expected = {compactTargetInfo(10, 2), 1 << 16}; + Assert.assertArrayEquals(expected, state.extraData()); + } + + @Test + public void testCompactStackSizeMax() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + for (int i = 0; i < 255; i++) { + state.push(WasmType.I32_TYPE); } - } - - @Test - public void testCompactTableHeader() { - ExtraDataList l = new ExtraDataList(); - l.addBranchTable(0, 1); - final int[] expected = {65536, 0, 0}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactTableHeaderMaxValue() { - ExtraDataList l = new ExtraDataList(); - l.addBranchTable(0, 32767); - final int[] expected = new int[1 + 2 * 32767]; - expected[0] = 2147418112; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedTableHeaderMinValue() { - ExtraDataList l = new ExtraDataList(); - l.addBranchTable(0, 32768); - final int[] expected = new int[2 + 5 * 32768]; - expected[0] = -2147450880; - for (int i = 2; i < expected.length; i += 5) { - expected[i] = -2147483648; + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 10); + state.exit(20); + for (int i = 0; i < 255; i++) { + state.pop(); } - Assert.assertArrayEquals(expected, l.extraDataArray()); + state.exit(21); + final int[] expected = {compactTargetInfo(10, 2), 255 << 16}; + Assert.assertArrayEquals(expected, state.extraData()); } @Test - public void testExtendedTableHeaderOutOfBounds() { - ExtraDataList l = new ExtraDataList(); - try { - l.addBranchTable(0, -1); - Assert.fail("Should have thrown exception"); - } catch (WasmException e) { - Assert.assertTrue(e.getMessage().contains("value cannot be represented in extra data")); + public void testExtendedStackSizeMin() { + ParserState state = new ParserState(); + state.enterBlock(WasmType.VOID_TYPE); + for (int i = 0; i < 256; i++) { + state.push(WasmType.I32_TYPE); } - } - - @Test - public void testTableHeaderWithExtendedItem() { - ExtraDataList l = new ExtraDataList(); - BranchTableEntry b = l.addBranchTable(0, 2); - b.item(0).setTargetInfo(32768, 0, 0); - for (int i = 0; i < 32764; i++) { - l.addCall(0); + state.enterBlock(WasmType.VOID_TYPE); + state.addConditionalBranch(0, 10); + state.exit(20); + for (int i = 0; i < 256; i++) { + state.pop(); } - final int[] expected = new int[12 + 32764]; - expected[0] = -2147483646; - expected[2] = -2147483648; - expected[3] = 32768; - expected[7] = -2147483648; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactCallIndex() { - ExtraDataList l = new ExtraDataList(); - l.addCall(1); - final int[] expected = {65536}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testCompactCallIndexMaxValue() { - ExtraDataList l = new ExtraDataList(); - l.addCall(32767); - final int[] expected = {2147418112}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedCallIndexMinValue() { - ExtraDataList l = new ExtraDataList(); - l.addCall(32768); - final int[] expected = {-2147450880}; - Assert.assertArrayEquals(expected, l.extraDataArray()); - } - - @Test - public void testExtendedCallIndexMaxValue() { - ExtraDataList l = new ExtraDataList(); - l.addCall(2147483647); - final int[] expected = {-1}; - Assert.assertArrayEquals(expected, l.extraDataArray()); + state.exit(21); + final int[] expected = {extendedTargetInfo(5), 10, 0, 256, 0}; + Assert.assertArrayEquals(expected, state.extraData()); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java index ca6ad89b30b7..48a113ae7bad 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java @@ -435,11 +435,11 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte returnType break; } case Instructions.IF: { - final int ifOffset = offset; + final int branchOffset = offset; state.popChecked(I32_TYPE); // condition final byte ifReturnType = readBlockType(); - state.enterIf(ifReturnType, ifOffset); + state.enterIf(ifReturnType, branchOffset); break; } case Instructions.END: { @@ -451,24 +451,24 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte returnType break; } case Instructions.BR: { - final int brOffset = offset; + final int branchOffset = offset; final int branchLabel = readTargetOffset(); - state.addUnconditionalBranch(branchLabel, brOffset); + state.addUnconditionalBranch(branchLabel, branchOffset); // This instruction is stack-polymorphic state.setUnreachable(); break; } case Instructions.BR_IF: { - final int brIfOffset = offset; + final int branchOffset = offset; final int branchLabel = readTargetOffset(); state.popChecked(I32_TYPE); // condition - state.addConditionalBranch(branchLabel, brIfOffset); + state.addConditionalBranch(branchLabel, branchOffset); break; } case Instructions.BR_TABLE: { - final int brTableOffset = offset; + final int branchOffset = offset; state.popChecked(I32_TYPE); // index final int length = readLength(); @@ -477,7 +477,7 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte returnType final int branchLabel = readTargetOffset(); branchTable[i] = branchLabel; } - state.addBranchTable(branchTable, brTableOffset); + state.addBranchTable(branchTable, branchOffset); // This instruction is stack-polymorphic state.setUnreachable(); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java index 4db5975adeea..3cea04092d20 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java @@ -258,7 +258,6 @@ import static org.graalvm.wasm.util.ExtraDataAccessor.firstValueSigned; import static org.graalvm.wasm.util.ExtraDataAccessor.firstValueUnsigned; import static org.graalvm.wasm.util.ExtraDataAccessor.forthValueUnsigned; -import static org.graalvm.wasm.util.ExtraDataAccessor.isCompactFormat; import static org.graalvm.wasm.util.ExtraDataAccessor.secondValueSigned; import static org.graalvm.wasm.util.ExtraDataAccessor.thirdValueUnsigned; @@ -480,7 +479,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int break; case IF: { stackPointer--; - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; CompilerAsserts.partialEvaluationConstant(compact); final int profileOffset = extraOffset + (compact ? COMPACT_IF_PROFILE_OFFSET : EXTENDED_IF_PROFILE_OFFSET); if (profileCondition(extraData, profileOffset, popBoolean(frame, stackPointer))) { @@ -497,16 +496,16 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int } case ELSE: { // The then branch was executed at this point. Jump to end of the if statement. - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; CompilerAsserts.partialEvaluationConstant(compact); offset += secondValueSigned(extraData, extraOffset, compact); - extraOffset += firstValueSigned(extraData, extraOffset, compact); + extraOffset += firstValueUnsigned(extraData, extraOffset, compact); break; } case END: break; case BR: { - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; CompilerAsserts.partialEvaluationConstant(compact); final int targetStackPointer = numLocals + forthValueUnsigned(extraData, extraOffset, compact); final int targetReturnLength = thirdValueUnsigned(extraData, extraOffset, compact); @@ -521,7 +520,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int } case BR_IF: { stackPointer--; - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; CompilerAsserts.partialEvaluationConstant(compact); final int profileOffset = extraOffset + (compact ? COMPACT_BR_IF_PROFILE_OFFSET : EXTENDED_BR_IF_PROFILE_OFFSET); if (profileCondition(extraData, profileOffset, popBoolean(frame, stackPointer))) { @@ -544,7 +543,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int } case BR_TABLE: { stackPointer--; - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; CompilerAsserts.partialEvaluationConstant(compact); int index = popInt(frame, stackPointer); final int size = firstValueUnsigned(extraData, extraOffset, compact); @@ -623,7 +622,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int Object[] args = createArgumentsForCall(frame, function.typeIndex(), numArgs, stackPointer); stackPointer -= args.length; - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; final int callNodeIndex = firstValueUnsigned(extraData, extraOffset, compact); extraOffset += CALL_LENGTH; @@ -713,7 +712,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int // Validate that the function type matches the expected type. final boolean functionFromCurrentContext = functionInstanceContext == context; - final boolean compact = isCompactFormat(extraData, extraOffset); + final boolean compact = extraData[extraOffset] >= 0; final int profileOffset = extraOffset + (compact ? COMPACT_CALL_INDIRECT_PROFILE_OFFSET : EXTENDED_CALL_INDIRECT_PROFILE_OFFSET); if (profileCondition(extraData, profileOffset, functionFromCurrentContext)) { // We can do a quick equivalence-class check. @@ -2822,32 +2821,34 @@ private static int offsetDelta(byte[] data, int offset) { private static final int MAX_PROFILE_VALUE = 0x0000_00ff; private static final int MAX_TABLE_PROFILE_VALUE = 0x0000_ffff; - private static boolean profileCondition(int[] extraData, int profileOffset, boolean condition) { + private static boolean profileCondition(int[] extraData, final int profileOffset, boolean condition) { int t = (extraData[profileOffset] & 0x0000_ff00) >> 8; int f = extraData[profileOffset] & 0x0000_00ff; boolean val = condition; if (val) { - if (t == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - } - if (f == 0) { - // Make this branch fold during PE - val = true; - } - if (CompilerDirectives.inInterpreter()) { + if (!CompilerDirectives.inInterpreter()) { + if (t == 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + if (f == 0) { + // Make this branch fold during PE + val = true; + } + } else { if (t < MAX_PROFILE_VALUE) { extraData[profileOffset] += 0x0100; } } } else { - if (f == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - } - if (t == 0) { - // Make this branch fold during PE - val = false; - } - if (CompilerDirectives.inInterpreter()) { + if (!CompilerDirectives.inInterpreter()) { + if (f == 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + if (t == 0) { + // Make this branch fold during PE + val = false; + } + } else { if (f < MAX_PROFILE_VALUE) { extraData[profileOffset] += 0x0001; } @@ -2861,7 +2862,7 @@ private static boolean profileCondition(int[] extraData, int profileOffset, bool } } - private static void updateBranchTableProfile(int[] extraData, int counterOffset, int profileOffset) { + private static void updateBranchTableProfile(int[] extraData, final int counterOffset, final int profileOffset) { assert CompilerDirectives.inInterpreter(); if ((extraData[counterOffset] & 0x0000_ffff) < MAX_TABLE_PROFILE_VALUE) { extraData[counterOffset]++; @@ -2869,7 +2870,7 @@ private static void updateBranchTableProfile(int[] extraData, int counterOffset, } } - private static boolean profileBranchTable(int[] extraData, int counterOffset, int profileOffset, boolean condition) { + private static boolean profileBranchTable(int[] extraData, final int counterOffset, final int profileOffset, boolean condition) { assert !CompilerDirectives.inInterpreter(); int t = extraData[profileOffset] & 0x0000_ffff; int sum = extraData[counterOffset] & 0x0000_ffff; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java index a978a3d89948..e864a9467985 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java @@ -347,7 +347,7 @@ public void addBranchTable(int[] branchLabels, int offset) { checkLabelExists(branchLabel); ControlFrame frame = getFrame(branchLabel); byte[] branchLabelReturnTypes = frame.labelTypes(); - BranchTableEntry branchTable = extraData.addBranchTable(offset, branchLabels.length); + BranchTableEntry branchTable = extraData.addBranchTable(branchLabels.length, offset); for (int i = 0; i < branchLabels.length; i++) { int otherBranchLabel = branchLabels[i]; checkLabelExists(otherBranchLabel); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java index 5ca336779c7b..6b185ba447dd 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java @@ -63,21 +63,23 @@ public class ExtraDataList implements ExtraDataFormatHelper { private static final int[] EMPTY_INT_ARRAY = new int[0]; private final ArrayList entries; - private final IntArrayList entrySizes; + private final ArrayList branchEntries; + private final IntArrayList entryExtraDataOffsets; private int size; private boolean extend; public ExtraDataList() { this.entries = new ArrayList<>(INITIAL_EXTRA_DATA_SIZE); - this.entrySizes = new IntArrayList(); + this.branchEntries = new ArrayList<>(0); + this.entryExtraDataOffsets = new IntArrayList(); this.size = 0; this.extend = false; } private void addEntry(ExtraDataEntry entry) { entries.add(entry); - entrySizes.add(entry.length()); + entryExtraDataOffsets.add(size); size += entry.length(); } @@ -87,32 +89,39 @@ public void extendExtraDataFormat() { } public BranchTarget addIf(int offset) { - IfEntry entry = new IfEntry(offset, size, this); + IfEntry entry = new IfEntry(this, offset, size, entries.size()); addEntry(entry); + branchEntries.add(entry); return entry; } public BranchTarget addElse(int offset) { - ElseEntry entry = new ElseEntry(offset, size, this); + ElseEntry entry = new ElseEntry(this, offset, size, entries.size()); addEntry(entry); + branchEntries.add(entry); return entry; } public BranchTargetWithStackChange addConditionalBranch(int offset) { - ConditionalBranchEntry entry = new ConditionalBranchEntry(offset, size, this); + ConditionalBranchEntry entry = new ConditionalBranchEntry(this, offset, size, entries.size()); addEntry(entry); + branchEntries.add(entry); return entry; } public BranchTargetWithStackChange addUnconditionalBranch(int offset) { - UnconditionalBranchEntry entry = new UnconditionalBranchEntry(offset, size, this); + UnconditionalBranchEntry entry = new UnconditionalBranchEntry(this, offset, size, entries.size()); addEntry(entry); + branchEntries.add(entry); return entry; } - public BranchTableEntry addBranchTable(int offset, int size) { - BranchTableEntry entry = new BranchTableEntry(offset, this.size, size, this); + public BranchTableEntry addBranchTable(int elementCount, int offset) { + BranchTableEntry entry = new BranchTableEntry(elementCount, this, offset, size, entries.size()); addEntry(entry); + for (int i = 0; i < entry.size(); i++) { + branchEntries.add(entry.item(i)); + } return entry; } @@ -144,34 +153,28 @@ public int[] extraDataArray() { } else { int dataSize = size; if (extend) { - int[] sizes = entrySizes.toArray(); + int[] offsets = entryExtraDataOffsets.toArray(); // Every size extensions can cause other size extensions (extra data displacement // changes). Therefore, we can only stop if no more extensions happened. while (extend) { extend = false; - // Sync the sizes array with the new sizes of the entries for displacement + // Sync the offsets array with the new sizes of the entries for displacement // calculations. dataSize = 0; for (int i = 0; i < entries.size(); i++) { ExtraDataEntry entry = entries.get(i); final int s = entry.length(); + offsets[i] = dataSize; dataSize += s; - sizes[i] = s; } // Update extra data displacements caused by entry size changes. - for (int i = 0; i < entries.size(); i++) { - ExtraDataEntry entry = entries.get(i); - if (entry instanceof BranchTarget) { - BranchTarget branchTargetEntry = (BranchTarget) entry; - branchTargetEntry.updateExtraDataDisplacement(distanceBetween(sizes, i, branchTargetEntry.extraDataTargetIndex())); - } - if (entry instanceof BranchTableEntry) { - BranchTableEntry branchTable = (BranchTableEntry) entry; - for (int j = 0; j < branchTable.size(); j++) { - BranchTargetWithStackChange branchTableEntry = branchTable.item(j); - branchTableEntry.updateExtraDataDisplacement(distanceBetween(sizes, i, branchTableEntry.extraDataTargetIndex())); - } + for (BranchTarget b : branchEntries) { + int targetIndex = b.extraDataTargetIndex(); + int targetOffset = dataSize; + if (targetIndex < offsets.length) { + targetOffset = offsets[targetIndex]; } + b.updateExtraDataDisplacement(offsets[b.extraDataIndex()], targetOffset); } } } @@ -183,18 +186,4 @@ public int[] extraDataArray() { return data; } } - - private static int distanceBetween(int[] sizes, int startIndex, int endIndex) { - int distance = 0; - if (endIndex > startIndex) { - for (int i = startIndex; i < endIndex; i++) { - distance += sizes[i]; - } - } else { - for (int i = endIndex; i < startIndex; i++) { - distance += sizes[i]; - } - } - return distance; - } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java index 32bd75e4c44d..32290e6b39e0 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTableEntry.java @@ -41,6 +41,8 @@ package org.graalvm.wasm.parser.validation.collections.entries; +import org.graalvm.wasm.exception.Failure; +import org.graalvm.wasm.exception.WasmException; import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; import org.graalvm.wasm.util.ExtraDataUtil; @@ -62,15 +64,17 @@ public class BranchTableEntry extends ExtraDataEntry implements ExtraDataFormatHelper { private final ConditionalBranchEntry[] items; - public BranchTableEntry(int byteCodeOffset, int extraDataOffset, int size, ExtraDataFormatHelper formatHelper) { + public BranchTableEntry(int elementCount, ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { super(formatHelper); - if (!ExtraDataUtil.isCompactUnsignedShortValueWithIndicator(size)) { + if (ExtraDataUtil.exceedsUnsignedShortValueWithIndicator(elementCount)) { + if (ExtraDataUtil.exceedsUnsignedIntValueWithIndicator(elementCount)) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } extendFormat(); - ExtraDataUtil.checkRepresentableUnsignedValueWithIndicator(size); } - this.items = new ConditionalBranchEntry[size]; - for (int i = 0; i < size; i++) { - this.items[i] = new ConditionalBranchEntry(byteCodeOffset, extraDataOffset, this); + this.items = new ConditionalBranchEntry[elementCount]; + for (int i = 0; i < elementCount; i++) { + this.items[i] = new ConditionalBranchEntry(this, byteCodeOffset, extraDataOffset, extraDataIndex); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java index 3c889afc6286..035134560d74 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTarget.java @@ -52,17 +52,21 @@ public abstract class BranchTarget extends ExtraDataEntry { private final int byteCodeOffset; private final int extraDataOffset; + + private final int extraDataIndex; + private int byteCodeDisplacement; private int extraDataDisplacement; private int extraDataTargetIndex; - private boolean compactExtraDataDisplacement; + private boolean compactExtraDataTarget; - protected BranchTarget(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { + protected BranchTarget(ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { super(formatHelper); + this.compactExtraDataTarget = true; this.byteCodeOffset = byteCodeOffset; this.extraDataOffset = extraDataOffset; - this.compactExtraDataDisplacement = true; + this.extraDataIndex = extraDataIndex; } /** @@ -73,22 +77,20 @@ protected BranchTarget(int byteCodeOffset, int extraDataOffset, ExtraDataFormatH * @param extraDataTargetIndex The target index in the extra data list */ public void setTargetInfo(int byteCodeTarget, int extraDataTarget, int extraDataTargetIndex) { - try { - this.byteCodeDisplacement = Math.subtractExact(byteCodeTarget, byteCodeOffset); - this.extraDataDisplacement = Math.subtractExact(extraDataTarget, extraDataOffset); - } catch (ArithmeticException e) { - throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); - } + this.byteCodeDisplacement = byteCodeTarget - byteCodeOffset; + this.extraDataDisplacement = extraDataTarget - extraDataOffset; this.extraDataTargetIndex = extraDataTargetIndex; - final boolean compact = ExtraDataUtil.areCompactSignedShortValuesWithIndicator(extraDataDisplacement, byteCodeDisplacement); - if (!compact) { + if (ExtraDataUtil.exceedsSignedShortValueWithIndicator(this.extraDataDisplacement) || ExtraDataUtil.exceedsSignedShortValue(this.byteCodeDisplacement)) { + if (ExtraDataUtil.exceedsSignedIntValueWithIndicator(this.extraDataDisplacement)) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } extendFormat(); - ExtraDataUtil.checkRepresentableSignedValueWithIndicator(extraDataDisplacement); + this.compactExtraDataTarget = false; } - this.compactExtraDataDisplacement = compact; } protected int compactByteCodeDisplacement() { + // Needed to correctly convert negative values return Short.toUnsignedInt((short) byteCodeDisplacement); } @@ -97,6 +99,7 @@ protected int extendedByteCodeDisplacement() { } protected int compactExtraDataDisplacement() { + // Needed to correctly convert negative values return Short.toUnsignedInt((short) extraDataDisplacement); } @@ -111,13 +114,18 @@ public int extraDataTargetIndex() { return extraDataTargetIndex; } - public void updateExtraDataDisplacement(int extraDataDisplacement) { - this.extraDataDisplacement = extraDataDisplacement; - final boolean compact = ExtraDataUtil.isCompactSignedShortValueWithIndicator(extraDataDisplacement); - if (compactExtraDataDisplacement != compact) { - compactExtraDataDisplacement = compact; + public int extraDataIndex() { + return extraDataIndex; + } + + public void updateExtraDataDisplacement(int offset, int targetOffset) { + extraDataDisplacement = targetOffset - offset; + if (compactExtraDataTarget && ExtraDataUtil.exceedsSignedShortValueWithIndicator(extraDataDisplacement)) { + if (ExtraDataUtil.exceedsSignedIntValueWithIndicator(extraDataDisplacement)) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } extendFormat(); + compactExtraDataTarget = false; } - ExtraDataUtil.checkRepresentableSignedValueWithIndicator(extraDataDisplacement); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java index 288ee6d48ac4..9e25e9608dfb 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/BranchTargetWithStackChange.java @@ -41,6 +41,8 @@ package org.graalvm.wasm.parser.validation.collections.entries; +import org.graalvm.wasm.exception.Failure; +import org.graalvm.wasm.exception.WasmException; import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; import org.graalvm.wasm.util.ExtraDataUtil; @@ -52,8 +54,8 @@ public abstract class BranchTargetWithStackChange extends BranchTarget { private int returnLength; private int stackSize; - protected BranchTargetWithStackChange(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { - super(byteCodeOffset, extraDataOffset, formatHelper); + protected BranchTargetWithStackChange(ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { + super(formatHelper, byteCodeOffset, extraDataOffset, extraDataIndex); } /** @@ -65,10 +67,11 @@ protected BranchTargetWithStackChange(int byteCodeOffset, int extraDataOffset, E public void setStackInfo(int returnLength, int stackSize) { this.returnLength = returnLength; this.stackSize = stackSize; - if (!ExtraDataUtil.areCompactUnsignedBytes(returnLength, stackSize)) { + if (ExtraDataUtil.exceedsUnsignedByteValue(returnLength) || ExtraDataUtil.exceedsUnsignedByteValue(stackSize)) { + if (ExtraDataUtil.exceedsPositiveIntValue(returnLength) || ExtraDataUtil.exceedsPositiveIntValue(stackSize)) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } extendFormat(); - ExtraDataUtil.checkRepresentableValue(returnLength); - ExtraDataUtil.checkRepresentableValue(stackSize); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java index 82c29ef0a6d0..ea4aecde70a4 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/CallTarget.java @@ -41,6 +41,8 @@ package org.graalvm.wasm.parser.validation.collections.entries; +import org.graalvm.wasm.exception.Failure; +import org.graalvm.wasm.exception.WasmException; import org.graalvm.wasm.parser.validation.collections.ExtraDataFormatHelper; import org.graalvm.wasm.util.ExtraDataUtil; @@ -53,9 +55,11 @@ public abstract class CallTarget extends ExtraDataEntry { protected CallTarget(int nodeIndex, ExtraDataFormatHelper formatHelper) { super(formatHelper); this.nodeIndex = nodeIndex; - if (!ExtraDataUtil.isCompactUnsignedShortValueWithIndicator(nodeIndex)) { + if (ExtraDataUtil.exceedsUnsignedShortValueWithIndicator(nodeIndex)) { + if (ExtraDataUtil.exceedsUnsignedIntValueWithIndicator(nodeIndex)) { + throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); + } extendFormat(); - ExtraDataUtil.checkRepresentableUnsignedValueWithIndicator(nodeIndex); } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java index cb9b2ffc1821..f62e16be156b 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ConditionalBranchEntry.java @@ -60,8 +60,8 @@ * */ public class ConditionalBranchEntry extends BranchTargetWithStackChange { - public ConditionalBranchEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { - super(byteCodeOffset, extraDataOffset, formatHelper); + public ConditionalBranchEntry(ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { + super(formatHelper, byteCodeOffset, extraDataOffset, extraDataIndex); } @Override diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java index e5cb2478e590..205e935afe44 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/ElseEntry.java @@ -60,8 +60,8 @@ * */ public class ElseEntry extends BranchTarget { - public ElseEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { - super(byteCodeOffset, extraDataOffset, formatHelper); + public ElseEntry(ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { + super(formatHelper, byteCodeOffset, extraDataOffset, extraDataIndex); } @Override diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java index 606731af8477..7a251f5ffdf0 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/IfEntry.java @@ -61,8 +61,8 @@ */ public class IfEntry extends BranchTarget { - public IfEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { - super(byteCodeOffset, extraDataOffset, formatHelper); + public IfEntry(ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { + super(formatHelper, byteCodeOffset, extraDataOffset, extraDataIndex); } @Override diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java index 4fe9d6ae82b3..984612b25b68 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/entries/UnconditionalBranchEntry.java @@ -60,8 +60,8 @@ * */ public class UnconditionalBranchEntry extends BranchTargetWithStackChange { - public UnconditionalBranchEntry(int byteCodeOffset, int extraDataOffset, ExtraDataFormatHelper formatHelper) { - super(byteCodeOffset, extraDataOffset, formatHelper); + public UnconditionalBranchEntry(ExtraDataFormatHelper formatHelper, int byteCodeOffset, int extraDataOffset, int extraDataIndex) { + super(formatHelper, byteCodeOffset, extraDataOffset, extraDataIndex); } @Override diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java index c0f70bedd60f..18a65d906ac6 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java @@ -45,7 +45,6 @@ * Helper class for accessing extra data entries. */ public class ExtraDataAccessor { - private static final int EXTENDED_FORMAT_INDICATOR = 0x8000_0000; private static final int INDICATOR_REMOVAL_MASK = 0x7fff_ffff; public static final int COMPACT_IF_LENGTH = 2; @@ -74,45 +73,43 @@ public class ExtraDataAccessor { public static final int COMPACT_CALL_INDIRECT_PROFILE_OFFSET = 0; public static final int EXTENDED_CALL_INDIRECT_PROFILE_OFFSET = 1; - /** - * Checks if the given entry is in compact format. - */ - public static boolean isCompactFormat(int[] extraData, int offset) { - return (extraData[offset] & EXTENDED_FORMAT_INDICATOR) == 0; - } - - public static int firstValueSigned(int[] extraData, int offset, boolean compact) { + public static int firstValueUnsigned(int[] extraData, int offset, boolean compact) { if (compact) { - return (extraData[offset] << 1) >> 17; + return extraData[offset] >>> 16; + } else { + return extraData[offset] & INDICATOR_REMOVAL_MASK; } - return extraData[offset] & INDICATOR_REMOVAL_MASK; } - public static int firstValueUnsigned(int[] extraData, int offset, boolean compact) { + public static int firstValueSigned(int[] extraData, int offset, boolean compact) { if (compact) { - return (extraData[offset] & INDICATOR_REMOVAL_MASK) >>> 16; + return extraData[offset] << 1 >> 17; + } else { + return extraData[offset] << 1 >> 1; } - return extraData[offset] & INDICATOR_REMOVAL_MASK; } public static int secondValueSigned(int[] extraData, int offset, boolean compact) { if (compact) { - return (extraData[offset] << 16) >> 16; + return extraData[offset] << 16 >> 16; + } else { + return extraData[offset + 1]; } - return extraData[offset + 1]; } public static int thirdValueUnsigned(int[] extraData, int offset, boolean compact) { if (compact) { - return (extraData[offset + 1] & 0xff00_0000) >>> 24; + return extraData[offset + 1] >>> 24; + } else { + return extraData[offset + 2]; } - return extraData[offset + 2]; } public static int forthValueUnsigned(int[] extraData, int offset, boolean compact) { if (compact) { return (extraData[offset + 1] & 0x00ff_0000) >>> 16; + } else { + return extraData[offset + 3]; } - return extraData[offset + 3]; } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java index b5990a1b2984..843586caa695 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java @@ -41,22 +41,17 @@ package org.graalvm.wasm.util; -import org.graalvm.wasm.exception.Failure; -import org.graalvm.wasm.exception.WasmException; - /** * Helper class for generating extra data entries. */ public class ExtraDataUtil { private static final int EXTENDED_FORMAT_INDICATOR = 0x8000_0000; - private static final int MIN_SIGNED_31BIT_VALUE = 0xc000_0000; private static final int MAX_SIGNED_31BIT_VALUE = 0x3fff_ffff; - + private static final int MAX_UNSIGNED_16BIT_VALUE = 0x0000_ffff; private static final int MAX_UNSIGNED_15BIT_VALUE = 0x0000_7fff; private static final int MIN_SIGNED_15BIT_VALUE = 0xffff_c000; private static final int MAX_SIGNED_15BIT_VALUE = 0x0000_3fff; - private static final int MAX_UNSIGNED_8BIT_VALUE = 0x0000_00ff; /** @@ -77,97 +72,40 @@ public class ExtraDataUtil { public static final int PROFILE_SIZE = 1; - /** - * Combines the given value into a single integer in the following format: - * - * - * | 0 (1-bit) | upperValue (15-bit) | lowerValue (16-bit) | - * - * - * The {@link ExtraDataUtil#areCompactSignedShortValuesWithIndicator(int, int)} method can be - * used to check if the given values can be represented by the resulting integer. - * - * @param upperValue The upper value of the resulting integer - * @param lowerValue The lower value of the resulting integer - * @return The combined integer value - */ private static int createCompactShortValuesWithIndicator(int upperValue, int lowerValue) { return ((upperValue << 16) | lowerValue) & 0x7fff_ffff; } - /** - * Combines the given values into the upper 16-bit of a single integer in the following format: - * - * - * | upperValue (8-bit) | lowerValue (8-bit) | 0 (16-bit) | - * - * - * The {@link ExtraDataUtil#areCompactUnsignedBytes(int, int)} method can be used to check if - * the given values can be represented by the resulting integer. - * - * @param upperValue The return length - * @param lowerValue The stack size - * @return The stack change entry - */ private static int createCompactUpperBytes(int upperValue, int lowerValue) { return (upperValue << 24) | (lowerValue << 16); } - /** - * Checks if the given values can be represented in a compactShortValuesWithIndicator format. - * - * @param upperValue The upper value of the resulting integer - * @param lowerValue The lower value of the resulting integer - * @return True if the values fit into the compactShortValuesWithIndicator format, false - * otherwise - */ - public static boolean areCompactSignedShortValuesWithIndicator(int upperValue, int lowerValue) { - return isCompactSignedShortValueWithIndicator(upperValue) && Short.MIN_VALUE <= lowerValue && lowerValue <= Short.MAX_VALUE; + public static boolean exceedsUnsignedByteValue(int value) { + return Integer.compareUnsigned(value, MAX_UNSIGNED_8BIT_VALUE) > 0; } - /** - * Checks if the given value can be represented as the upper value of a - * compactShortValuesWithIndicator format. - * - * @param upperValue The upper value of the resulting integer - * @return True if the value fits the upper part of the compactShortValuesWithIndicator format, - * false otherwise - */ - public static boolean isCompactUnsignedShortValueWithIndicator(int upperValue) { - return Integer.compareUnsigned(upperValue, MAX_UNSIGNED_15BIT_VALUE) <= 0; + public static boolean exceedsUnsignedShortValueWithIndicator(int value) { + return Integer.compareUnsigned(value, MAX_UNSIGNED_15BIT_VALUE) > 0; } - public static boolean isCompactSignedShortValueWithIndicator(int upperValue) { - return MIN_SIGNED_15BIT_VALUE <= upperValue && upperValue <= MAX_SIGNED_15BIT_VALUE; + public static boolean exceedsSignedShortValueWithIndicator(int value) { + return value < MIN_SIGNED_15BIT_VALUE || MAX_SIGNED_15BIT_VALUE < value; } - public static void checkRepresentableUnsignedValueWithIndicator(int value) { - if (value < 0) { - throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); - } + public static boolean exceedsSignedShortValue(int value) { + return value < Short.MIN_VALUE || Short.MAX_VALUE < value; } - public static void checkRepresentableSignedValueWithIndicator(int value) { - if (value < MIN_SIGNED_31BIT_VALUE || value >= MAX_SIGNED_31BIT_VALUE) { - throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); - } + public static boolean exceedsUnsignedIntValueWithIndicator(int value) { + return value < 0; } - /** - * Checks if the given values can be represented as the compactUpperBytes format. - * - * @param upperValue The return length - * @param lowerValue The stack size - * @return True, if a compact stack change entry could be constructed - */ - public static boolean areCompactUnsignedBytes(int upperValue, int lowerValue) { - return Integer.compareUnsigned(upperValue, MAX_UNSIGNED_8BIT_VALUE) <= 0 && Integer.compareUnsigned(lowerValue, MAX_UNSIGNED_8BIT_VALUE) <= 0; + public static boolean exceedsSignedIntValueWithIndicator(int value) { + return value < MIN_SIGNED_31BIT_VALUE || MAX_SIGNED_31BIT_VALUE < value; } - public static void checkRepresentableValue(int value) { - if (value < 0) { - throw WasmException.create(Failure.NON_REPRESENTABLE_EXTRA_DATA_VALUE); - } + public static boolean exceedsPositiveIntValue(int value) { + return value < 0; } /** From 89eba4060d4e27f7d21c0858a3944151fc8ce7be Mon Sep 17 00:00:00 2001 From: Florian Huemer Date: Tue, 14 Jun 2022 10:48:48 +0200 Subject: [PATCH 3/4] Code cleanup. --- .../src/org/graalvm/wasm/util/ExtraDataUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java index 843586caa695..31185743e4bc 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataUtil.java @@ -48,7 +48,6 @@ public class ExtraDataUtil { private static final int EXTENDED_FORMAT_INDICATOR = 0x8000_0000; private static final int MIN_SIGNED_31BIT_VALUE = 0xc000_0000; private static final int MAX_SIGNED_31BIT_VALUE = 0x3fff_ffff; - private static final int MAX_UNSIGNED_16BIT_VALUE = 0x0000_ffff; private static final int MAX_UNSIGNED_15BIT_VALUE = 0x0000_7fff; private static final int MIN_SIGNED_15BIT_VALUE = 0xffff_c000; private static final int MAX_SIGNED_15BIT_VALUE = 0x0000_3fff; From b7a0a29873e7f8a42a682b96bb75d96c2555b708 Mon Sep 17 00:00:00 2001 From: Florian Huemer Date: Fri, 17 Jun 2022 10:17:37 +0200 Subject: [PATCH 4/4] Fixed typo. Added documentation. --- .../src/org/graalvm/wasm/nodes/WasmFunctionNode.java | 10 +++++----- .../parser/validation/collections/ExtraDataList.java | 2 ++ .../src/org/graalvm/wasm/util/ExtraDataAccessor.java | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java index 3cea04092d20..0676133cd7c8 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java @@ -257,7 +257,7 @@ import static org.graalvm.wasm.util.ExtraDataAccessor.EXTENDED_IF_PROFILE_OFFSET; import static org.graalvm.wasm.util.ExtraDataAccessor.firstValueSigned; import static org.graalvm.wasm.util.ExtraDataAccessor.firstValueUnsigned; -import static org.graalvm.wasm.util.ExtraDataAccessor.forthValueUnsigned; +import static org.graalvm.wasm.util.ExtraDataAccessor.fourthValueUnsigned; import static org.graalvm.wasm.util.ExtraDataAccessor.secondValueSigned; import static org.graalvm.wasm.util.ExtraDataAccessor.thirdValueUnsigned; @@ -507,7 +507,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int case BR: { final boolean compact = extraData[extraOffset] >= 0; CompilerAsserts.partialEvaluationConstant(compact); - final int targetStackPointer = numLocals + forthValueUnsigned(extraData, extraOffset, compact); + final int targetStackPointer = numLocals + fourthValueUnsigned(extraData, extraOffset, compact); final int targetReturnLength = thirdValueUnsigned(extraData, extraOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); @@ -524,7 +524,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int CompilerAsserts.partialEvaluationConstant(compact); final int profileOffset = extraOffset + (compact ? COMPACT_BR_IF_PROFILE_OFFSET : EXTENDED_BR_IF_PROFILE_OFFSET); if (profileCondition(extraData, profileOffset, popBoolean(frame, stackPointer))) { - final int targetStackPointer = numLocals + forthValueUnsigned(extraData, extraOffset, compact); + final int targetStackPointer = numLocals + fourthValueUnsigned(extraData, extraOffset, compact); final int targetReturnLength = thirdValueUnsigned(extraData, extraOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); @@ -561,7 +561,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int updateBranchTableProfile(extraData, profileOffset, indexProfileOffset); - final int targetStackPointer = numLocals + forthValueUnsigned(extraData, indexOffset, compact); + final int targetStackPointer = numLocals + fourthValueUnsigned(extraData, indexOffset, compact); final int targetReturnLength = thirdValueUnsigned(extraData, indexOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); @@ -580,7 +580,7 @@ public Object executeBodyFromOffset(WasmContext context, VirtualFrame frame, int (compact ? COMPACT_BR_TABLE_HEADER_LENGTH + i * COMPACT_BR_IF_LENGTH : EXTENDED_BR_TABLE_HEADER_LENGTH + i * EXTENDED_BR_IF_LENGTH); final int indexProfileOffset = indexOffset + (compact ? COMPACT_BR_IF_PROFILE_OFFSET : EXTENDED_BR_IF_PROFILE_OFFSET); if (profileBranchTable(extraData, profileOffset, indexProfileOffset, i == index)) { - final int targetStackPointer = numLocals + forthValueUnsigned(extraData, indexOffset, compact); + final int targetStackPointer = numLocals + fourthValueUnsigned(extraData, indexOffset, compact); final int targetReturnLength = thirdValueUnsigned(extraData, indexOffset, compact); unwindStack(frame, stackPointer, targetStackPointer, targetReturnLength); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java index 6b185ba447dd..b69ef2c61986 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/collections/ExtraDataList.java @@ -156,6 +156,8 @@ public int[] extraDataArray() { int[] offsets = entryExtraDataOffsets.toArray(); // Every size extensions can cause other size extensions (extra data displacement // changes). Therefore, we can only stop if no more extensions happened. + // This algorithm is quadratic in the worst case, but should need fewer iterations + // on average. while (extend) { extend = false; // Sync the offsets array with the new sizes of the entries for displacement diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java index 18a65d906ac6..64bf061327c4 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/util/ExtraDataAccessor.java @@ -105,7 +105,7 @@ public static int thirdValueUnsigned(int[] extraData, int offset, boolean compac } } - public static int forthValueUnsigned(int[] extraData, int offset, boolean compact) { + public static int fourthValueUnsigned(int[] extraData, int offset, boolean compact) { if (compact) { return (extraData[offset + 1] & 0x00ff_0000) >>> 16; } else {