1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package compiler.compilercontrol.share.scenario; 25 26 import compiler.compilercontrol.share.JSONFile; 27 import compiler.compilercontrol.share.method.MethodDescriptor; 28 import compiler.compilercontrol.share.method.MethodGenerator; 29 import jdk.test.lib.Pair; 30 import pool.PoolHelper; 31 32 import java.lang.reflect.Executable; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.ArrayList; 36 import java.util.HashMap; 37 import java.util.LinkedHashMap; 38 import java.util.concurrent.Callable; 39 40 /** 41 * Directive file and state builder class 42 */ 43 public class DirectiveBuilder implements StateBuilder<CompileCommand> { 44 private static final List<Pair<Executable, Callable<?>>> METHODS 45 = new PoolHelper().getAllMethods(); 46 private final Map<Executable, State> stateMap = new HashMap<>(); 47 private final String fileName; 48 private Map<MethodDescriptor, List<CompileCommand>> matchBlocks 49 = new LinkedHashMap<>(); 50 private List<String> inlineMatch = new ArrayList<>(); 51 private boolean isFileValid = true; 52 53 public DirectiveBuilder(String fileName) { 54 this.fileName = fileName; 55 } 56 57 @Override 58 public List<String> getOptions() { 59 List<String> options = new ArrayList<>(); 60 if (!matchBlocks.isEmpty()) { 61 // add option only if there are any directive available 62 options.add("-XX:CompilerDirectivesFile=" + fileName); 63 } 64 return options; 65 } 66 67 @Override 68 public List<CompileCommand> getCompileCommands() { 69 throw new Error("TESTBUG: isn't applicable for directives"); 70 } 71 72 @Override 73 public boolean isValid() { 74 // Invalid directives file makes VM exit with error code 75 return isFileValid; 76 } 77 78 @Override 79 public Map<Executable, State> getStates() { 80 try (DirectiveWriter dirFile = new DirectiveWriter(fileName)) { 81 for (MethodDescriptor matchDescriptor : matchBlocks.keySet()) { 82 // Write match block with all options converted from commands 83 dirFile.match(matchDescriptor); 84 for (CompileCommand compileCommand : 85 matchBlocks.get(matchDescriptor)) { 86 isFileValid &= compileCommand.isValid(); 87 handleCommand(dirFile, compileCommand); 88 } 89 if ("Inlinee.caller".matches((matchDescriptor.getRegexp()))) { 90 // Got a *.* match block, where inline would be written 91 dirFile.inline(inlineMatch.toArray( 92 new String[inlineMatch.size()])); 93 inlineMatch.clear(); 94 } 95 dirFile.end(); // ends match block 96 } 97 98 /* 99 * Write inline directive in the end to the latest match block 100 * if we didn't do this before 101 * Inlinee caller methods should match this block only 102 */ 103 if (!inlineMatch.isEmpty()) { 104 Pair<Executable, Callable<?>> pair = METHODS.get(0); 105 MethodDescriptor md = MethodGenerator.anyMatchDescriptor( 106 pair.first); 107 CompileCommand cc = new CompileCommand(Command.QUIET, md, null, 108 Scenario.Type.DIRECTIVE); 109 List<CompileCommand> commands = new ArrayList<>(); 110 111 // Add appropriate "*.*" match block 112 commands.add(cc); 113 matchBlocks.put(md, commands); 114 // Add match block for this descriptor with inlines 115 dirFile.match(md); 116 dirFile.inline(inlineMatch.toArray( 117 new String[inlineMatch.size()])); 118 dirFile.end(); 119 } 120 if (!matchBlocks.isEmpty()) { 121 // terminates file 122 dirFile.end(); 123 } 124 125 // Build states for each method according to match blocks 126 for (Pair<Executable, Callable<?>> pair : METHODS) { 127 State state = getState(pair); 128 if (state != null) { 129 stateMap.put(pair.first, state); 130 } 131 } 132 } 133 if (isFileValid) { 134 return stateMap; 135 } else { 136 // return empty map because invalid file doesn't change states 137 return new HashMap<>(); 138 } 139 } 140 141 private State getState(Pair<Executable, Callable<?>> pair) { 142 State state = null; 143 MethodDescriptor execDesc = MethodGenerator.commandDescriptor( 144 pair.first); 145 boolean isMatchFound = false; 146 147 if (stateMap.containsKey(pair.first)) { 148 state = stateMap.get(pair.first); 149 } 150 for (MethodDescriptor matchDesc : matchBlocks.keySet()) { 151 if (execDesc.getCanonicalString().matches(matchDesc.getRegexp())) { 152 /* 153 * if executable matches regex 154 * then apply commands from this match to the state 155 */ 156 for (CompileCommand cc : matchBlocks.get(matchDesc)) { 157 state = new State(); 158 if (!isMatchFound) { 159 // this is a first found match, apply all commands 160 state.apply(cc); 161 } else { 162 // apply only inline directives 163 switch (cc.command) { 164 case INLINE: 165 case DONTINLINE: 166 state.apply(cc); 167 break; 168 } 169 } 170 } 171 isMatchFound = true; 172 } 173 } 174 return state; 175 } 176 177 private void handleCommand(DirectiveWriter dirFile, CompileCommand cmd) { 178 Command command = cmd.command; 179 180 switch (command) { 181 case COMPILEONLY: 182 dirFile.excludeCompile(cmd.compiler, false); 183 break; 184 case EXCLUDE: 185 dirFile.excludeCompile(cmd.compiler, true); 186 break; 187 case INLINE: 188 case DONTINLINE: 189 // Inline commands will be written later 190 break; 191 case LOG: 192 dirFile.option(DirectiveWriter.Option.LOG, true); 193 break; 194 case QUIET: 195 // there are no appropriate directive for this 196 break; 197 case PRINT: 198 dirFile.option(DirectiveWriter.Option.PRINT_ASSEMBLY, true); 199 break; 200 case NONEXISTENT: 201 dirFile.write(JSONFile.Element.PAIR, command.name); 202 dirFile.write(JSONFile.Element.OBJECT); 203 dirFile.write(JSONFile.Element.PAIR, command.name); 204 dirFile.write(JSONFile.Element.VALUE, 205 cmd.methodDescriptor.getString()); 206 dirFile.end(); // ends object 207 break; 208 default: 209 throw new Error("TESTBUG: wrong command: " + command); 210 } 211 } 212 213 @Override 214 public void add(CompileCommand compileCommand) { 215 MethodDescriptor methodDescriptor = compileCommand.methodDescriptor; 216 217 switch (compileCommand.command) { 218 case INLINE: 219 inlineMatch.add("+" + methodDescriptor.getString()); 220 break; 221 case DONTINLINE: 222 inlineMatch.add("-" + methodDescriptor.getString()); 223 break; 224 } 225 for (MethodDescriptor md: matchBlocks.keySet()) { 226 if (methodDescriptor.getCanonicalString().matches(md.getRegexp())) { 227 matchBlocks.get(md).add(compileCommand); 228 } 229 } 230 if (!matchBlocks.containsKey(compileCommand.methodDescriptor)) { 231 List<CompileCommand> commands = new ArrayList<>(); 232 commands.add(compileCommand); 233 matchBlocks.put(compileCommand.methodDescriptor, commands); 234 } 235 } 236 }