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 compiler.compilercontrol.share.pool.PoolHelper; 30 import jdk.test.lib.util.Pair; 31 32 import java.lang.reflect.Executable; 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.LinkedHashMap; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.concurrent.Callable; 39 import java.util.stream.Collectors; 40 41 /** 42 * Directive file and state builder class 43 */ 44 public class DirectiveBuilder implements StateBuilder<CompileCommand> { 45 private static final List<Pair<Executable, Callable<?>>> METHODS 46 = new PoolHelper().getAllMethods(); 47 private final Map<Executable, State> stateMap = new HashMap<>(); 48 private final String fileName; 49 private final Map<MethodDescriptor, List<CompileCommand>> matchBlocks 50 = new LinkedHashMap<>(); 51 private final List<CompileCommand> inlines = new ArrayList<>(); 52 private boolean isFileValid = true; 53 54 public DirectiveBuilder(String fileName) { 55 this.fileName = fileName; 56 } 57 58 @Override 59 public List<String> getOptions() { 60 List<String> options = new ArrayList<>(); 61 if (!matchBlocks.isEmpty()) { 62 // add option only if there are any directive available 63 options.add("-XX:CompilerDirectivesFile=" + fileName); 64 } 65 return options; 66 } 67 68 @Override 69 public List<CompileCommand> getCompileCommands() { 70 return matchBlocks.keySet().stream() 71 // only method descriptor is required to check print_directives 72 .map(md -> new CompileCommand(null, md, null, null)) 73 .collect(Collectors.toList()); 74 } 75 76 @Override 77 public boolean isValid() { 78 // Invalid directives file makes VM exit with error code 79 return isFileValid; 80 } 81 82 @Override 83 public Map<Executable, State> getStates() { 84 writeDirectiveFile(); 85 if (isFileValid) { 86 // Build states for each method according to match blocks 87 for (Pair<Executable, Callable<?>> pair : METHODS) { 88 State state = getState(pair); 89 if (state != null) { 90 stateMap.put(pair.first, state); 91 } 92 } 93 return stateMap; 94 } else { 95 // return empty map because invalid file doesn't change states 96 return new HashMap<>(); 97 } 98 } 99 100 private void writeDirectiveFile() { 101 try (DirectiveWriter dirFile = new DirectiveWriter(fileName)) { 102 for (MethodDescriptor matchDescriptor : matchBlocks.keySet()) { 103 // Write match block with all options converted from commands 104 dirFile.match(matchDescriptor); 105 for (CompileCommand compileCommand : 106 matchBlocks.get(matchDescriptor)) { 107 handleCommand(dirFile, compileCommand); 108 } 109 if ("Inlinee.caller()".matches(matchDescriptor.getRegexp()) 110 && !inlines.isEmpty()) { 111 // Got a *.* match block, where inline would be written 112 writeInlines(dirFile); 113 inlines.clear(); 114 } 115 dirFile.end(); // ends match block 116 } 117 118 /* 119 * Write inline directive in the end to the latest match block 120 * if we didn't do this before 121 * Inlinee caller methods should match this block only 122 */ 123 if (!inlines.isEmpty()) { 124 Pair<Executable, Callable<?>> pair = METHODS.get(0); 125 MethodDescriptor md = MethodGenerator.anyMatchDescriptor( 126 pair.first); 127 CompileCommand cc = new CompileCommand(Command.QUIET, md, 128 null, Scenario.Type.DIRECTIVE); 129 List<CompileCommand> commands = new ArrayList<>(); 130 131 // Add appropriate "*.*" match block 132 commands.add(cc); 133 matchBlocks.put(md, commands); 134 // Add match block for this descriptor with inlines 135 dirFile.match(md); 136 writeInlines(dirFile); 137 dirFile.end(); 138 } 139 if (!matchBlocks.isEmpty()) { 140 // terminates file 141 dirFile.end(); 142 } 143 144 } 145 } 146 147 private State getState(Pair<Executable, Callable<?>> pair) { 148 State state = null; 149 MethodDescriptor execDesc = MethodGenerator.commandDescriptor( 150 pair.first); 151 boolean isMatchFound = false; 152 153 if (stateMap.containsKey(pair.first)) { 154 state = stateMap.get(pair.first); 155 } 156 for (MethodDescriptor matchDesc : matchBlocks.keySet()) { 157 if (execDesc.getCanonicalString().matches(matchDesc.getRegexp())) { 158 /* 159 * if executable matches regex 160 * then apply commands from this match to the state 161 */ 162 for (CompileCommand cc : matchBlocks.get(matchDesc)) { 163 if (state == null) { 164 state = new State(); 165 } 166 if (!isMatchFound) { 167 // this is a first found match, apply all commands 168 state.apply(cc); 169 } else { 170 // apply only inline directives 171 switch (cc.command) { 172 case INLINE: 173 case DONTINLINE: 174 state.apply(cc); 175 break; 176 } 177 } 178 } 179 isMatchFound = true; 180 } 181 } 182 return state; 183 } 184 185 private void handleCommand(DirectiveWriter dirFile, CompileCommand cmd) { 186 Command command = cmd.command; 187 188 switch (command) { 189 case COMPILEONLY: 190 dirFile.excludeCompile(cmd.compiler, false); 191 break; 192 case EXCLUDE: 193 dirFile.excludeCompile(cmd.compiler, true); 194 break; 195 case QUIET: 196 /* there are no appropriate directive for this, just make 197 match be enabled */ 198 case INLINE: 199 case DONTINLINE: 200 /* Inline commands will be written later. 201 Just make this match be enabled */ 202 dirFile.emitCompiler(Scenario.Compiler.C1); 203 dirFile.option(DirectiveWriter.Option.ENABLE, true); 204 dirFile.end(); 205 dirFile.emitCompiler(Scenario.Compiler.C2); 206 dirFile.option(DirectiveWriter.Option.ENABLE, true); 207 dirFile.end(); 208 break; 209 case LOG: 210 dirFile.option(DirectiveWriter.Option.LOG, true); 211 break; 212 case PRINT: 213 dirFile.option(DirectiveWriter.Option.PRINT_ASSEMBLY, true); 214 break; 215 case NONEXISTENT: 216 dirFile.write(JSONFile.Element.PAIR, command.name); 217 dirFile.write(JSONFile.Element.OBJECT); 218 dirFile.write(JSONFile.Element.PAIR, command.name); 219 dirFile.write(JSONFile.Element.VALUE, 220 cmd.methodDescriptor.getString()); 221 dirFile.end(); // ends object 222 break; 223 default: 224 throw new Error("TESTBUG: wrong command: " + command); 225 } 226 } 227 228 private void writeInlines(DirectiveWriter dirFile) { 229 List<String> c1Block = new ArrayList<>(); 230 List<String> c2Block = new ArrayList<>(); 231 List<String> allBlock = new ArrayList<>(); 232 for (CompileCommand cc : inlines) { 233 String inlineMethodPattern; 234 switch (cc.command) { 235 case INLINE: 236 inlineMethodPattern = "+" + cc.methodDescriptor.getString(); 237 break; 238 case DONTINLINE: 239 inlineMethodPattern = "-" + cc.methodDescriptor.getString(); 240 break; 241 default: 242 throw new Error("TESTBUG: incorrect command got in " 243 + "the list: " + cc.command); 244 } 245 if (cc.compiler == Scenario.Compiler.C1) { 246 c1Block.add(inlineMethodPattern); 247 } else if (cc.compiler == Scenario.Compiler.C2) { 248 c2Block.add(inlineMethodPattern); 249 } else { 250 allBlock.add(inlineMethodPattern); 251 } 252 } 253 dirFile.emitCompiler(Scenario.Compiler.C1); 254 if (!c1Block.isEmpty()) { 255 dirFile.inline(c1Block); 256 } else { 257 dirFile.option(DirectiveWriter.Option.ENABLE, true); 258 } 259 dirFile.end(); 260 dirFile.emitCompiler(Scenario.Compiler.C2); 261 if (!c2Block.isEmpty()) { 262 dirFile.inline(c2Block); 263 } else { 264 dirFile.option(DirectiveWriter.Option.ENABLE, true); 265 } 266 dirFile.end(); 267 if (!allBlock.isEmpty()) { 268 dirFile.inline(allBlock); 269 } 270 } 271 272 @Override 273 public void add(CompileCommand compileCommand) { 274 isFileValid &= compileCommand.isValid(); 275 MethodDescriptor methodDescriptor = compileCommand.methodDescriptor; 276 277 switch (compileCommand.command) { 278 case INLINE: 279 case DONTINLINE: 280 inlines.add(compileCommand); 281 break; 282 } 283 for (MethodDescriptor md: matchBlocks.keySet()) { 284 if (methodDescriptor.getCanonicalString().matches(md.getRegexp())) { 285 matchBlocks.get(md).add(compileCommand); 286 } 287 } 288 if (!matchBlocks.containsKey(compileCommand.methodDescriptor)) { 289 List<CompileCommand> commands = new ArrayList<>(); 290 commands.add(compileCommand); 291 matchBlocks.put(compileCommand.methodDescriptor, commands); 292 } 293 } 294 }