src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File hotspot Sdiff src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt

src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java

Print this page




  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 package org.graalvm.compiler.lir.constopt;
  24 
  25 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
  26 import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
  27 
  28 import java.util.ArrayDeque;
  29 import java.util.ArrayList;
  30 import java.util.BitSet;
  31 import java.util.Collections;
  32 import java.util.Deque;
  33 import java.util.EnumSet;
  34 import java.util.List;
  35 
  36 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  37 import org.graalvm.compiler.core.common.cfg.BlockMap;
  38 import org.graalvm.compiler.debug.Debug;
  39 import org.graalvm.compiler.debug.Debug.Scope;
  40 import org.graalvm.compiler.debug.DebugCounter;
  41 import org.graalvm.compiler.debug.Indent;
  42 import org.graalvm.compiler.lir.InstructionValueConsumer;
  43 import org.graalvm.compiler.lir.LIR;
  44 import org.graalvm.compiler.lir.LIRInsertionBuffer;
  45 import org.graalvm.compiler.lir.LIRInstruction;
  46 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
  47 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
  48 import org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
  49 import org.graalvm.compiler.lir.ValueConsumer;
  50 import org.graalvm.compiler.lir.Variable;
  51 import org.graalvm.compiler.lir.constopt.ConstantTree.Flags;
  52 import org.graalvm.compiler.lir.constopt.ConstantTree.NodeCost;
  53 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
  54 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  55 import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase;
  56 import org.graalvm.compiler.options.NestedBooleanOptionKey;
  57 import org.graalvm.compiler.options.Option;
  58 import org.graalvm.compiler.options.OptionType;
  59 
  60 import jdk.vm.ci.code.TargetDescription;


  65 /**
  66  * This optimization tries to improve the handling of constants by replacing a single definition of
  67  * a constant, which is potentially scheduled into a block with high probability, with one or more
  68  * definitions in blocks with a lower probability.
  69  */
  70 public final class ConstantLoadOptimization extends PreAllocationOptimizationPhase {
  71 
  72     public static class Options {
  73         // @formatter:off
  74         @Option(help = "Enable constant load optimization.", type = OptionType.Debug)
  75         public static final NestedBooleanOptionKey LIROptConstantLoadOptimization = new NestedBooleanOptionKey(LIROptimization, true);
  76         // @formatter:on
  77     }
  78 
  79     @Override
  80     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PreAllocationOptimizationContext context) {
  81         LIRGeneratorTool lirGen = context.lirGen;
  82         new Optimization(lirGenRes.getLIR(), lirGen).apply();
  83     }
  84 
  85     private static final DebugCounter constantsTotal = Debug.counter("ConstantLoadOptimization[total]");
  86     private static final DebugCounter phiConstantsSkipped = Debug.counter("ConstantLoadOptimization[PhisSkipped]");
  87     private static final DebugCounter singleUsageConstantsSkipped = Debug.counter("ConstantLoadOptimization[SingleUsageSkipped]");
  88     private static final DebugCounter usageAtDefinitionSkipped = Debug.counter("ConstantLoadOptimization[UsageAtDefinitionSkipped]");
  89     private static final DebugCounter materializeAtDefinitionSkipped = Debug.counter("ConstantLoadOptimization[MaterializeAtDefinitionSkipped]");
  90     private static final DebugCounter constantsOptimized = Debug.counter("ConstantLoadOptimization[optimized]");
  91 
  92     private static final class Optimization {
  93         private final LIR lir;
  94         private final LIRGeneratorTool lirGen;
  95         private final VariableMap<DefUseTree> map;
  96         private final BitSet phiConstants;
  97         private final BitSet defined;
  98         private final BlockMap<List<UseEntry>> blockMap;
  99         private final BlockMap<LIRInsertionBuffer> insertionBuffers;

 100 
 101         private Optimization(LIR lir, LIRGeneratorTool lirGen) {
 102             this.lir = lir;

 103             this.lirGen = lirGen;
 104             this.map = new VariableMap<>();
 105             this.phiConstants = new BitSet();
 106             this.defined = new BitSet();
 107             this.insertionBuffers = new BlockMap<>(lir.getControlFlowGraph());
 108             this.blockMap = new BlockMap<>(lir.getControlFlowGraph());
 109         }
 110 
 111         @SuppressWarnings("try")
 112         private void apply() {
 113             try (Indent indent = Debug.logAndIndent("ConstantLoadOptimization")) {
 114                 try (Scope s = Debug.scope("BuildDefUseTree")) {
 115                     // build DefUseTree
 116                     for (AbstractBlockBase<?> b : lir.getControlFlowGraph().getBlocks()) {
 117                         this.analyzeBlock(b);
 118                     }
 119                     // remove all with only one use
 120                     map.filter(t -> {
 121                         if (t.usageCount() > 1) {
 122                             return true;
 123                         } else {
 124                             singleUsageConstantsSkipped.increment();
 125                             return false;
 126                         }
 127                     });
 128                     // collect block map
 129                     map.forEach(tree -> tree.forEach(this::addUsageToBlockMap));
 130                 } catch (Throwable e) {
 131                     throw Debug.handle(e);
 132                 }
 133 
 134                 try (Scope s = Debug.scope("BuildConstantTree")) {
 135                     // create ConstantTree
 136                     map.forEach(this::createConstantTree);
 137 
 138                     // insert moves, delete null instructions and reset instruction ids
 139                     for (AbstractBlockBase<?> b : lir.getControlFlowGraph().getBlocks()) {
 140                         this.rewriteBlock(b);
 141                     }
 142 
 143                     assert verifyStates();
 144                 } catch (Throwable e) {
 145                     throw Debug.handle(e);
 146                 }
 147             }
 148         }
 149 
 150         private boolean verifyStates() {
 151             map.forEach(this::verifyStateUsage);
 152             return true;
 153         }
 154 
 155         private void verifyStateUsage(DefUseTree tree) {
 156             Variable var = tree.getVariable();
 157             ValueConsumer stateConsumer = new ValueConsumer() {
 158 
 159                 @Override
 160                 public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 161                     assert !operand.equals(var) : "constant usage through variable in frame state " + var;
 162                 }
 163             };
 164             for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
 165                 for (LIRInstruction inst : lir.getLIRforBlock(block)) {


 174                 return false;
 175             }
 176             return isVariable(LoadConstantOp.asLoadConstantOp(inst).getResult());
 177         }
 178 
 179         private void addUsageToBlockMap(UseEntry entry) {
 180             AbstractBlockBase<?> block = entry.getBlock();
 181             List<UseEntry> list = blockMap.get(block);
 182             if (list == null) {
 183                 list = new ArrayList<>();
 184                 blockMap.put(block, list);
 185             }
 186             list.add(entry);
 187         }
 188 
 189         /**
 190          * Collects def-use information for a {@code block}.
 191          */
 192         @SuppressWarnings("try")
 193         private void analyzeBlock(AbstractBlockBase<?> block) {
 194             try (Indent indent = Debug.logAndIndent("Block: %s", block)) {
 195 
 196                 InstructionValueConsumer loadConsumer = (instruction, value, mode, flags) -> {
 197                     if (isVariable(value)) {
 198                         Variable var = (Variable) value;
 199 
 200                         if (!phiConstants.get(var.index)) {
 201                             if (!defined.get(var.index)) {
 202                                 defined.set(var.index);
 203                                 if (isConstantLoad(instruction)) {
 204                                     Debug.log("constant load: %s", instruction);
 205                                     map.put(var, new DefUseTree(instruction, block));
 206                                     constantsTotal.increment();
 207                                 }
 208                             } else {
 209                                 // Variable is redefined, this only happens for constant loads
 210                                 // introduced by phi resolution -> ignore.
 211                                 DefUseTree removed = map.remove(var);
 212                                 if (removed != null) {
 213                                     phiConstantsSkipped.increment();
 214                                 }
 215                                 phiConstants.set(var.index);
 216                                 Debug.log(Debug.VERBOSE_LEVEL, "Removing phi variable: %s", var);
 217                             }
 218                         } else {
 219                             assert defined.get(var.index) : "phi but not defined? " + var;
 220                         }
 221                     }
 222                 };
 223 
 224                 InstructionValueConsumer useConsumer = (instruction, value, mode, flags) -> {
 225                     if (isVariable(value)) {
 226                         Variable var = (Variable) value;
 227                         if (!phiConstants.get(var.index)) {
 228                             DefUseTree tree = map.get(var);
 229                             if (tree != null) {
 230                                 tree.addUsage(block, instruction, value);
 231                                 Debug.log("usage of %s : %s", var, instruction);
 232                             }
 233                         }
 234                     }
 235                 };
 236 
 237                 int opId = 0;
 238                 for (LIRInstruction inst : lir.getLIRforBlock(block)) {
 239                     // set instruction id to the index in the lir instruction list
 240                     inst.setId(opId++);
 241                     inst.visitEachOutput(loadConsumer);
 242                     inst.visitEachInput(useConsumer);
 243                     inst.visitEachAlive(useConsumer);
 244 
 245                 }
 246             }
 247         }
 248 
 249         /**
 250          * Creates the dominator tree and searches for an solution.
 251          */
 252         @SuppressWarnings("try")
 253         private void createConstantTree(DefUseTree tree) {
 254             ConstantTree constTree = new ConstantTree(lir.getControlFlowGraph(), tree);
 255             constTree.set(Flags.SUBTREE, tree.getBlock());
 256             tree.forEach(u -> constTree.set(Flags.USAGE, u.getBlock()));
 257 
 258             if (constTree.get(Flags.USAGE, tree.getBlock())) {
 259                 // usage in the definition block -> no optimization
 260                 usageAtDefinitionSkipped.increment();
 261                 return;
 262             }
 263 
 264             constTree.markBlocks();
 265 
 266             NodeCost cost = ConstantTreeAnalyzer.analyze(constTree, tree.getBlock());
 267             int usageCount = cost.getUsages().size();
 268             assert usageCount == tree.usageCount() : "Usage count differs: " + usageCount + " vs. " + tree.usageCount();
 269 
 270             if (Debug.isLogEnabled()) {
 271                 try (Indent i = Debug.logAndIndent("Variable: %s, Block: %s, prob.: %f", tree.getVariable(), tree.getBlock(), tree.getBlock().probability())) {
 272                     Debug.log("Usages result: %s", cost);
 273                 }
 274 
 275             }
 276 
 277             if (cost.getNumMaterializations() > 1 || cost.getBestCost() < tree.getBlock().probability()) {
 278                 try (Scope s = Debug.scope("CLOmodify", constTree); Indent i = Debug.logAndIndent("Replacing %s = %s", tree.getVariable(), tree.getConstant().toValueString())) {
 279                     // mark original load for removal
 280                     deleteInstruction(tree);
 281                     constantsOptimized.increment();
 282 
 283                     // collect result
 284                     createLoads(tree, constTree, tree.getBlock());
 285 
 286                 } catch (Throwable e) {
 287                     throw Debug.handle(e);
 288                 }
 289             } else {
 290                 // no better solution found
 291                 materializeAtDefinitionSkipped.increment();
 292             }
 293             Debug.dump(Debug.DETAILED_LEVEL, constTree, "ConstantTree for %s", tree.getVariable());
 294         }
 295 
 296         private void createLoads(DefUseTree tree, ConstantTree constTree, AbstractBlockBase<?> startBlock) {
 297             Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>();
 298             worklist.add(startBlock);
 299             while (!worklist.isEmpty()) {
 300                 AbstractBlockBase<?> block = worklist.pollLast();
 301                 if (constTree.get(Flags.CANDIDATE, block)) {
 302                     constTree.set(Flags.MATERIALIZE, block);
 303                     // create and insert load
 304                     insertLoad(tree.getConstant(), tree.getVariable().getValueKind(), block, constTree.getCost(block).getUsages());
 305                 } else {
 306                     AbstractBlockBase<?> dominated = block.getFirstDominated();
 307                     while (dominated != null) {
 308                         if (constTree.isMarked(dominated)) {
 309                             worklist.addLast(dominated);
 310                         }
 311                         dominated = dominated.getDominatedSibling();
 312                     }
 313                 }
 314             }
 315         }
 316 
 317         private void insertLoad(Constant constant, ValueKind<?> kind, AbstractBlockBase<?> block, List<UseEntry> usages) {
 318             assert usages != null && usages.size() > 0 : String.format("No usages %s %s %s", constant, block, usages);
 319             // create variable
 320             Variable variable = lirGen.newVariable(kind);
 321             // create move
 322             LIRInstruction move = lirGen.getSpillMoveFactory().createLoad(variable, constant);
 323             // insert instruction
 324             getInsertionBuffer(block).append(1, move);
 325             Debug.log("new move (%s) and inserted in block %s", move, block);
 326             // update usages
 327             for (UseEntry u : usages) {
 328                 u.setValue(variable);
 329                 Debug.log("patched instruction %s", u.getInstruction());
 330             }
 331         }
 332 
 333         /**
 334          * Inserts the constant loads created in {@link #createConstantTree} and deletes the
 335          * original definition.
 336          */
 337         private void rewriteBlock(AbstractBlockBase<?> block) {
 338             // insert moves
 339             LIRInsertionBuffer buffer = insertionBuffers.get(block);
 340             if (buffer != null) {
 341                 assert buffer.initialized() : "not initialized?";
 342                 buffer.finish();
 343             }
 344 
 345             // delete instructions
 346             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
 347             boolean hasDead = false;
 348             for (LIRInstruction inst : instructions) {
 349                 if (inst == null) {
 350                     hasDead = true;
 351                 } else {
 352                     inst.setId(-1);
 353                 }
 354             }
 355             if (hasDead) {
 356                 // Remove null values from the list.
 357                 instructions.removeAll(Collections.singleton(null));
 358             }
 359         }
 360 
 361         private void deleteInstruction(DefUseTree tree) {
 362             AbstractBlockBase<?> block = tree.getBlock();
 363             LIRInstruction instruction = tree.getInstruction();
 364             Debug.log("deleting instruction %s from block %s", instruction, block);
 365             lir.getLIRforBlock(block).set(instruction.id(), null);
 366         }
 367 
 368         private LIRInsertionBuffer getInsertionBuffer(AbstractBlockBase<?> block) {
 369             LIRInsertionBuffer insertionBuffer = insertionBuffers.get(block);
 370             if (insertionBuffer == null) {
 371                 insertionBuffer = new LIRInsertionBuffer();
 372                 insertionBuffers.put(block, insertionBuffer);
 373                 assert !insertionBuffer.initialized() : "already initialized?";
 374                 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
 375                 insertionBuffer.init(instructions);
 376             }
 377             return insertionBuffer;
 378         }
 379     }
 380 }


  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 package org.graalvm.compiler.lir.constopt;
  24 
  25 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
  26 import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
  27 
  28 import java.util.ArrayDeque;
  29 import java.util.ArrayList;
  30 import java.util.BitSet;
  31 import java.util.Collections;
  32 import java.util.Deque;
  33 import java.util.EnumSet;
  34 import java.util.List;
  35 
  36 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  37 import org.graalvm.compiler.core.common.cfg.BlockMap;
  38 import org.graalvm.compiler.debug.CounterKey;
  39 import org.graalvm.compiler.debug.DebugContext;

  40 import org.graalvm.compiler.debug.Indent;
  41 import org.graalvm.compiler.lir.InstructionValueConsumer;
  42 import org.graalvm.compiler.lir.LIR;
  43 import org.graalvm.compiler.lir.LIRInsertionBuffer;
  44 import org.graalvm.compiler.lir.LIRInstruction;
  45 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
  46 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
  47 import org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
  48 import org.graalvm.compiler.lir.ValueConsumer;
  49 import org.graalvm.compiler.lir.Variable;
  50 import org.graalvm.compiler.lir.constopt.ConstantTree.Flags;
  51 import org.graalvm.compiler.lir.constopt.ConstantTree.NodeCost;
  52 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
  53 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  54 import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase;
  55 import org.graalvm.compiler.options.NestedBooleanOptionKey;
  56 import org.graalvm.compiler.options.Option;
  57 import org.graalvm.compiler.options.OptionType;
  58 
  59 import jdk.vm.ci.code.TargetDescription;


  64 /**
  65  * This optimization tries to improve the handling of constants by replacing a single definition of
  66  * a constant, which is potentially scheduled into a block with high probability, with one or more
  67  * definitions in blocks with a lower probability.
  68  */
  69 public final class ConstantLoadOptimization extends PreAllocationOptimizationPhase {
  70 
  71     public static class Options {
  72         // @formatter:off
  73         @Option(help = "Enable constant load optimization.", type = OptionType.Debug)
  74         public static final NestedBooleanOptionKey LIROptConstantLoadOptimization = new NestedBooleanOptionKey(LIROptimization, true);
  75         // @formatter:on
  76     }
  77 
  78     @Override
  79     protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PreAllocationOptimizationContext context) {
  80         LIRGeneratorTool lirGen = context.lirGen;
  81         new Optimization(lirGenRes.getLIR(), lirGen).apply();
  82     }
  83 
  84     private static final CounterKey constantsTotal = DebugContext.counter("ConstantLoadOptimization[total]");
  85     private static final CounterKey phiConstantsSkipped = DebugContext.counter("ConstantLoadOptimization[PhisSkipped]");
  86     private static final CounterKey singleUsageConstantsSkipped = DebugContext.counter("ConstantLoadOptimization[SingleUsageSkipped]");
  87     private static final CounterKey usageAtDefinitionSkipped = DebugContext.counter("ConstantLoadOptimization[UsageAtDefinitionSkipped]");
  88     private static final CounterKey materializeAtDefinitionSkipped = DebugContext.counter("ConstantLoadOptimization[MaterializeAtDefinitionSkipped]");
  89     private static final CounterKey constantsOptimized = DebugContext.counter("ConstantLoadOptimization[optimized]");
  90 
  91     private static final class Optimization {
  92         private final LIR lir;
  93         private final LIRGeneratorTool lirGen;
  94         private final VariableMap<DefUseTree> map;
  95         private final BitSet phiConstants;
  96         private final BitSet defined;
  97         private final BlockMap<List<UseEntry>> blockMap;
  98         private final BlockMap<LIRInsertionBuffer> insertionBuffers;
  99         private final DebugContext debug;
 100 
 101         private Optimization(LIR lir, LIRGeneratorTool lirGen) {
 102             this.lir = lir;
 103             this.debug = lir.getDebug();
 104             this.lirGen = lirGen;
 105             this.map = new VariableMap<>();
 106             this.phiConstants = new BitSet();
 107             this.defined = new BitSet();
 108             this.insertionBuffers = new BlockMap<>(lir.getControlFlowGraph());
 109             this.blockMap = new BlockMap<>(lir.getControlFlowGraph());
 110         }
 111 
 112         @SuppressWarnings("try")
 113         private void apply() {
 114             try (Indent indent = debug.logAndIndent("ConstantLoadOptimization")) {
 115                 try (DebugContext.Scope s = debug.scope("BuildDefUseTree")) {
 116                     // build DefUseTree
 117                     for (AbstractBlockBase<?> b : lir.getControlFlowGraph().getBlocks()) {
 118                         this.analyzeBlock(b);
 119                     }
 120                     // remove all with only one use
 121                     map.filter(t -> {
 122                         if (t.usageCount() > 1) {
 123                             return true;
 124                         } else {
 125                             singleUsageConstantsSkipped.increment(debug);
 126                             return false;
 127                         }
 128                     });
 129                     // collect block map
 130                     map.forEach(tree -> tree.forEach(this::addUsageToBlockMap));
 131                 } catch (Throwable e) {
 132                     throw debug.handle(e);
 133                 }
 134 
 135                 try (DebugContext.Scope s = debug.scope("BuildConstantTree")) {
 136                     // create ConstantTree
 137                     map.forEach(this::createConstantTree);
 138 
 139                     // insert moves, delete null instructions and reset instruction ids
 140                     for (AbstractBlockBase<?> b : lir.getControlFlowGraph().getBlocks()) {
 141                         this.rewriteBlock(b);
 142                     }
 143 
 144                     assert verifyStates();
 145                 } catch (Throwable e) {
 146                     throw debug.handle(e);
 147                 }
 148             }
 149         }
 150 
 151         private boolean verifyStates() {
 152             map.forEach(this::verifyStateUsage);
 153             return true;
 154         }
 155 
 156         private void verifyStateUsage(DefUseTree tree) {
 157             Variable var = tree.getVariable();
 158             ValueConsumer stateConsumer = new ValueConsumer() {
 159 
 160                 @Override
 161                 public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 162                     assert !operand.equals(var) : "constant usage through variable in frame state " + var;
 163                 }
 164             };
 165             for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
 166                 for (LIRInstruction inst : lir.getLIRforBlock(block)) {


 175                 return false;
 176             }
 177             return isVariable(LoadConstantOp.asLoadConstantOp(inst).getResult());
 178         }
 179 
 180         private void addUsageToBlockMap(UseEntry entry) {
 181             AbstractBlockBase<?> block = entry.getBlock();
 182             List<UseEntry> list = blockMap.get(block);
 183             if (list == null) {
 184                 list = new ArrayList<>();
 185                 blockMap.put(block, list);
 186             }
 187             list.add(entry);
 188         }
 189 
 190         /**
 191          * Collects def-use information for a {@code block}.
 192          */
 193         @SuppressWarnings("try")
 194         private void analyzeBlock(AbstractBlockBase<?> block) {
 195             try (Indent indent = debug.logAndIndent("Block: %s", block)) {
 196 
 197                 InstructionValueConsumer loadConsumer = (instruction, value, mode, flags) -> {
 198                     if (isVariable(value)) {
 199                         Variable var = (Variable) value;
 200 
 201                         if (!phiConstants.get(var.index)) {
 202                             if (!defined.get(var.index)) {
 203                                 defined.set(var.index);
 204                                 if (isConstantLoad(instruction)) {
 205                                     debug.log("constant load: %s", instruction);
 206                                     map.put(var, new DefUseTree(instruction, block));
 207                                     constantsTotal.increment(debug);
 208                                 }
 209                             } else {
 210                                 // Variable is redefined, this only happens for constant loads
 211                                 // introduced by phi resolution -> ignore.
 212                                 DefUseTree removed = map.remove(var);
 213                                 if (removed != null) {
 214                                     phiConstantsSkipped.increment(debug);
 215                                 }
 216                                 phiConstants.set(var.index);
 217                                 debug.log(DebugContext.VERBOSE_LEVEL, "Removing phi variable: %s", var);
 218                             }
 219                         } else {
 220                             assert defined.get(var.index) : "phi but not defined? " + var;
 221                         }
 222                     }
 223                 };
 224 
 225                 InstructionValueConsumer useConsumer = (instruction, value, mode, flags) -> {
 226                     if (isVariable(value)) {
 227                         Variable var = (Variable) value;
 228                         if (!phiConstants.get(var.index)) {
 229                             DefUseTree tree = map.get(var);
 230                             if (tree != null) {
 231                                 tree.addUsage(block, instruction, value);
 232                                 debug.log("usage of %s : %s", var, instruction);
 233                             }
 234                         }
 235                     }
 236                 };
 237 
 238                 int opId = 0;
 239                 for (LIRInstruction inst : lir.getLIRforBlock(block)) {
 240                     // set instruction id to the index in the lir instruction list
 241                     inst.setId(opId++);
 242                     inst.visitEachOutput(loadConsumer);
 243                     inst.visitEachInput(useConsumer);
 244                     inst.visitEachAlive(useConsumer);
 245 
 246                 }
 247             }
 248         }
 249 
 250         /**
 251          * Creates the dominator tree and searches for an solution.
 252          */
 253         @SuppressWarnings("try")
 254         private void createConstantTree(DefUseTree tree) {
 255             ConstantTree constTree = new ConstantTree(lir.getControlFlowGraph(), tree);
 256             constTree.set(Flags.SUBTREE, tree.getBlock());
 257             tree.forEach(u -> constTree.set(Flags.USAGE, u.getBlock()));
 258 
 259             if (constTree.get(Flags.USAGE, tree.getBlock())) {
 260                 // usage in the definition block -> no optimization
 261                 usageAtDefinitionSkipped.increment(debug);
 262                 return;
 263             }
 264 
 265             constTree.markBlocks();
 266 
 267             NodeCost cost = ConstantTreeAnalyzer.analyze(debug, constTree, tree.getBlock());
 268             int usageCount = cost.getUsages().size();
 269             assert usageCount == tree.usageCount() : "Usage count differs: " + usageCount + " vs. " + tree.usageCount();
 270 
 271             if (debug.isLogEnabled()) {
 272                 try (Indent i = debug.logAndIndent("Variable: %s, Block: %s, prob.: %f", tree.getVariable(), tree.getBlock(), tree.getBlock().probability())) {
 273                     debug.log("Usages result: %s", cost);
 274                 }
 275 
 276             }
 277 
 278             if (cost.getNumMaterializations() > 1 || cost.getBestCost() < tree.getBlock().probability()) {
 279                 try (DebugContext.Scope s = debug.scope("CLOmodify", constTree); Indent i = debug.logAndIndent("Replacing %s = %s", tree.getVariable(), tree.getConstant().toValueString())) {
 280                     // mark original load for removal
 281                     deleteInstruction(tree);
 282                     constantsOptimized.increment(debug);
 283 
 284                     // collect result
 285                     createLoads(tree, constTree, tree.getBlock());
 286 
 287                 } catch (Throwable e) {
 288                     throw debug.handle(e);
 289                 }
 290             } else {
 291                 // no better solution found
 292                 materializeAtDefinitionSkipped.increment(debug);
 293             }
 294             debug.dump(DebugContext.DETAILED_LEVEL, constTree, "ConstantTree for %s", tree.getVariable());
 295         }
 296 
 297         private void createLoads(DefUseTree tree, ConstantTree constTree, AbstractBlockBase<?> startBlock) {
 298             Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>();
 299             worklist.add(startBlock);
 300             while (!worklist.isEmpty()) {
 301                 AbstractBlockBase<?> block = worklist.pollLast();
 302                 if (constTree.get(Flags.CANDIDATE, block)) {
 303                     constTree.set(Flags.MATERIALIZE, block);
 304                     // create and insert load
 305                     insertLoad(tree.getConstant(), tree.getVariable().getValueKind(), block, constTree.getCost(block).getUsages());
 306                 } else {
 307                     AbstractBlockBase<?> dominated = block.getFirstDominated();
 308                     while (dominated != null) {
 309                         if (constTree.isMarked(dominated)) {
 310                             worklist.addLast(dominated);
 311                         }
 312                         dominated = dominated.getDominatedSibling();
 313                     }
 314                 }
 315             }
 316         }
 317 
 318         private void insertLoad(Constant constant, ValueKind<?> kind, AbstractBlockBase<?> block, List<UseEntry> usages) {
 319             assert usages != null && usages.size() > 0 : String.format("No usages %s %s %s", constant, block, usages);
 320             // create variable
 321             Variable variable = lirGen.newVariable(kind);
 322             // create move
 323             LIRInstruction move = lirGen.getSpillMoveFactory().createLoad(variable, constant);
 324             // insert instruction
 325             getInsertionBuffer(block).append(1, move);
 326             debug.log("new move (%s) and inserted in block %s", move, block);
 327             // update usages
 328             for (UseEntry u : usages) {
 329                 u.setValue(variable);
 330                 debug.log("patched instruction %s", u.getInstruction());
 331             }
 332         }
 333 
 334         /**
 335          * Inserts the constant loads created in {@link #createConstantTree} and deletes the
 336          * original definition.
 337          */
 338         private void rewriteBlock(AbstractBlockBase<?> block) {
 339             // insert moves
 340             LIRInsertionBuffer buffer = insertionBuffers.get(block);
 341             if (buffer != null) {
 342                 assert buffer.initialized() : "not initialized?";
 343                 buffer.finish();
 344             }
 345 
 346             // delete instructions
 347             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
 348             boolean hasDead = false;
 349             for (LIRInstruction inst : instructions) {
 350                 if (inst == null) {
 351                     hasDead = true;
 352                 } else {
 353                     inst.setId(-1);
 354                 }
 355             }
 356             if (hasDead) {
 357                 // Remove null values from the list.
 358                 instructions.removeAll(Collections.singleton(null));
 359             }
 360         }
 361 
 362         private void deleteInstruction(DefUseTree tree) {
 363             AbstractBlockBase<?> block = tree.getBlock();
 364             LIRInstruction instruction = tree.getInstruction();
 365             debug.log("deleting instruction %s from block %s", instruction, block);
 366             lir.getLIRforBlock(block).set(instruction.id(), null);
 367         }
 368 
 369         private LIRInsertionBuffer getInsertionBuffer(AbstractBlockBase<?> block) {
 370             LIRInsertionBuffer insertionBuffer = insertionBuffers.get(block);
 371             if (insertionBuffer == null) {
 372                 insertionBuffer = new LIRInsertionBuffer();
 373                 insertionBuffers.put(block, insertionBuffer);
 374                 assert !insertionBuffer.initialized() : "already initialized?";
 375                 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
 376                 insertionBuffer.init(instructions);
 377             }
 378             return insertionBuffer;
 379         }
 380     }
 381 }
src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantLoadOptimization.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File