| /* |
| * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package compiler.compilercontrol.share.scenario; |
| |
| import jdk.test.lib.Asserts; |
| |
| import java.util.Arrays; |
| import java.util.Optional; |
| |
| /** |
| * Represents method compilation state |
| */ |
| public class State { |
| // Each of the two-elements array contains a state for each compiler |
| private Optional<Boolean>[] compile = |
| (Optional<Boolean>[]) new Optional[Scenario.Compiler.values().length]; |
| private Optional<Boolean>[] forceInline = |
| (Optional<Boolean>[]) new Optional[Scenario.Compiler.values().length]; |
| private Optional<Boolean>[] dontInline = |
| (Optional<Boolean>[]) new Optional[Scenario.Compiler.values().length]; |
| private Optional<Boolean> printAssembly = Optional.empty(); |
| private Optional<Boolean> printInline = Optional.empty(); |
| private Optional<Boolean> log = Optional.empty(); |
| |
| public State() { |
| Arrays.fill(compile, Optional.empty()); |
| Arrays.fill(forceInline, Optional.empty()); |
| Arrays.fill(dontInline, Optional.empty()); |
| } |
| |
| /** |
| * Creates state from the string |
| * |
| * @param strings array of strings that represent the state |
| * @return State instance |
| * @see #toString() |
| */ |
| public static State fromString(String[] strings) { |
| Asserts.assertNotNull(strings, "Non null array is required"); |
| Asserts.assertNE(strings.length, 0, "Non empty array is required"); |
| State st = new State(); |
| for (String string : strings) { |
| int i = string.indexOf(' '); |
| String command = string.substring(0, i); |
| String values = string.substring(i + 1); // skip space symbol |
| switch (command) { |
| case "compile" : |
| parseArray(st.compile, values); |
| break; |
| case "force_inline" : |
| parseArray(st.forceInline, values); |
| break; |
| case "dont_inline" : |
| parseArray(st.dontInline, values); |
| break; |
| case "log" : |
| st.log = parseElement(values); |
| break; |
| case "print_assembly" : |
| st.printAssembly = parseElement(values); |
| break; |
| case "print_inline" : |
| st.printInline = parseElement(values); |
| break; |
| default: |
| throw new Error("TESTBUG: "); |
| } |
| } |
| return st; |
| } |
| |
| private static void parseArray(Optional<Boolean>[] array, String str) { |
| Asserts.assertNotNull(str); |
| int beginBrace = 0; |
| int endBrace = str.length() - 1; |
| if (str.charAt(beginBrace) != '[' || str.charAt(endBrace) != ']') { |
| throw new Error("TESTBUG: not an array type: " + str); |
| } |
| // Get all elements divided with comma as an array |
| String[] strValues = str.substring(beginBrace + 1, endBrace) |
| .split(", "); |
| Asserts.assertEQ(strValues.length, array.length, "Different amount of " |
| + "elements in the string"); |
| for (int i = 0; i < strValues.length; i++) { |
| array[i] = parseElement(strValues[i]); |
| } |
| } |
| |
| private static Optional<Boolean> parseElement(String str) { |
| Asserts.assertNotNull(str); |
| Asserts.assertTrue(str.startsWith(Optional.class.getSimpleName()), |
| "String is not of type Optional: " + str); |
| if ("Optional.empty".equals(str)) { |
| return Optional.empty(); |
| } |
| int begin = str.indexOf('['); |
| Asserts.assertNE(begin, -1, "TEST BUG: Wrong Optional string"); |
| int end = str.indexOf(']'); |
| Asserts.assertEQ(end, str.length() - 1); |
| boolean b = Boolean.parseBoolean(str.substring(begin + 1, end)); |
| return Optional.of(b); |
| } |
| |
| /** |
| * Gets string representation of this state |
| */ |
| @Override |
| public String toString() { |
| return "compile " + Arrays.toString(compile) |
| + "\nforce_inline " + Arrays.toString(forceInline) |
| + "\ndont_inline " + Arrays.toString(dontInline) |
| + "\nlog " + log |
| + "\nprint_assembly " + printAssembly |
| + "\nprint_inline " + printInline; |
| } |
| |
| public boolean isC1Compilable() { |
| return compile[Scenario.Compiler.C1.ordinal()].orElse(true); |
| } |
| |
| public boolean isC2Compilable() { |
| return compile[Scenario.Compiler.C2.ordinal()].orElse(true); |
| } |
| |
| public boolean isCompilable() { |
| return isC1Compilable() && isC2Compilable(); |
| } |
| |
| public void setC1Compilable(boolean value) { |
| setCompilable(Scenario.Compiler.C1.ordinal(), value); |
| } |
| |
| public void setC2Compilable(boolean value) { |
| setCompilable(Scenario.Compiler.C2.ordinal(), value); |
| } |
| |
| public void setCompilable(Scenario.Compiler compiler, boolean value) { |
| if (compiler == null) { |
| setC1Compilable(value); |
| setC2Compilable(value); |
| return; |
| } |
| switch (compiler) { |
| case C1: |
| setC1Compilable(value); |
| break; |
| case C2: |
| setC2Compilable(value); |
| break; |
| default: |
| throw new Error("Unknown compiler"); |
| } |
| } |
| |
| private void setCompilable(int level, boolean value) { |
| check(level); |
| compile[level] = Optional.of(value); |
| if (!value) { |
| setDontInline(level); |
| } |
| } |
| |
| public boolean isC1Inlinable() { |
| return ! dontInline[Scenario.Compiler.C1.ordinal()].orElse(false); |
| } |
| |
| public boolean isC2Inlinable() { |
| return ! dontInline[Scenario.Compiler.C2.ordinal()].orElse(false); |
| } |
| |
| public boolean isInlinable() { |
| return isC1Inlinable() && isC2Inlinable(); |
| } |
| |
| private void setDontInline(int level) { |
| check(level); |
| dontInline[level] = Optional.of(true); |
| forceInline[level] = Optional.of(false); |
| } |
| |
| private void setForceInline(int level) { |
| check(level); |
| dontInline[level] = Optional.of(false); |
| forceInline[level] = Optional.of(true); |
| } |
| |
| public boolean isC1ForceInline() { |
| return forceInline[Scenario.Compiler.C1.ordinal()].orElse(false); |
| } |
| |
| public boolean isC2ForceInline() { |
| return forceInline[Scenario.Compiler.C2.ordinal()].orElse(false); |
| } |
| |
| public boolean isForceInline() { |
| return isC1ForceInline() && isC2ForceInline(); |
| } |
| |
| public void setC1Inline(boolean value) { |
| if (value && isC1Compilable()) { |
| setForceInline(Scenario.Compiler.C1.ordinal()); |
| } else { |
| setDontInline(Scenario.Compiler.C1.ordinal()); |
| } |
| } |
| |
| public void setC2Inline(boolean value) { |
| if (value && isC2Compilable()) { |
| setForceInline(Scenario.Compiler.C2.ordinal()); |
| } else { |
| setDontInline(Scenario.Compiler.C1.ordinal()); |
| } |
| } |
| |
| public void setInline(Scenario.Compiler compiler, boolean value) { |
| if (compiler == null) { |
| setC1Inline(value); |
| setC2Inline(value); |
| return; |
| } |
| switch (compiler) { |
| case C1: |
| setC1Inline(value); |
| break; |
| case C2: |
| setC2Inline(value); |
| break; |
| default: |
| throw new Error("Unknown compiler"); |
| } |
| } |
| |
| public boolean isPrintAssembly() { |
| return printAssembly.orElse(false); |
| } |
| |
| public void setPrintAssembly(boolean value) { |
| printAssembly = Optional.of(value); |
| } |
| |
| public boolean isPrintInline() { |
| return printInline.orElse(false); |
| } |
| |
| public void setPrintInline(boolean value) { |
| printInline = Optional.of(value); |
| } |
| |
| public boolean isLog() { |
| return log.orElse(false); |
| } |
| |
| public void setLog(boolean log) { |
| this.log = Optional.of(log); |
| } |
| |
| private void check(int level) { |
| if (level < 0 || level > compile.length) { |
| throw new IllegalArgumentException("TESTBUG: Wrong level " + level); |
| } |
| } |
| |
| /** |
| * Applies given command to the state. |
| * |
| * @param compileCommand command to be applied |
| */ |
| public void apply(CompileCommand compileCommand) { |
| switch (compileCommand.command) { |
| case COMPILEONLY: |
| setCompilable(compileCommand.compiler, true); |
| break; |
| case EXCLUDE: |
| setCompilable(compileCommand.compiler, false); |
| break; |
| case INLINE: |
| setInline(compileCommand.compiler, true); |
| break; |
| case DONTINLINE: |
| setInline(compileCommand.compiler, false); |
| break; |
| case LOG: |
| setLog(true); |
| break; |
| case PRINT: |
| setPrintAssembly(true); |
| break; |
| case QUIET: |
| case NONEXISTENT: |
| // doesn't apply the state |
| break; |
| default: |
| throw new Error("Wrong command: " + compileCommand.command); |
| } |
| } |
| |
| /** |
| * Merges two given states with different priority |
| * |
| * @param low state with lower merge priority |
| * @param high state with higher merge priority |
| */ |
| public static State merge(State low, State high) { |
| if (high == null) { |
| if (low == null) { |
| return new State(); |
| } |
| return low; |
| } |
| if (low == null) { |
| return high; |
| } |
| State result = new State(); |
| // Compilable |
| result.compile[Scenario.Compiler.C1.ordinal()] = mergeOptional( |
| high.compile[Scenario.Compiler.C1.ordinal()], |
| low.compile[Scenario.Compiler.C1.ordinal()]); |
| result.compile[Scenario.Compiler.C2.ordinal()] = mergeOptional( |
| high.compile[Scenario.Compiler.C2.ordinal()], |
| low.compile[Scenario.Compiler.C2.ordinal()]); |
| // Force inline |
| result.forceInline[Scenario.Compiler.C1.ordinal()] = mergeOptional( |
| high.forceInline[Scenario.Compiler.C1.ordinal()], |
| low.forceInline[Scenario.Compiler.C1.ordinal()]); |
| result.forceInline[Scenario.Compiler.C2.ordinal()] = mergeOptional( |
| high.forceInline[Scenario.Compiler.C2.ordinal()], |
| low.forceInline[Scenario.Compiler.C2.ordinal()]); |
| // Don't inline |
| result.dontInline[Scenario.Compiler.C1.ordinal()] = mergeOptional( |
| high.dontInline[Scenario.Compiler.C1.ordinal()], |
| low.dontInline[Scenario.Compiler.C1.ordinal()]); |
| result.dontInline[Scenario.Compiler.C2.ordinal()] = mergeOptional( |
| high.dontInline[Scenario.Compiler.C2.ordinal()], |
| low.dontInline[Scenario.Compiler.C2.ordinal()]); |
| // set PrintAssembly |
| result.printAssembly = mergeOptional(high.printAssembly, |
| low.printAssembly); |
| // set PrintInline |
| result.printInline = mergeOptional(high.printInline, low.printInline); |
| // set LogCompilation |
| result.log = mergeOptional(high.log, low.log); |
| return result; |
| } |
| |
| private static <T> Optional<T> mergeOptional(Optional<T> high, |
| Optional<T> low) { |
| T val = high.orElse(low.orElse(null)); |
| return Optional.ofNullable(val); |
| } |
| } |