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 }