1 /*
   2  * Copyright (c) 2016, 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 package org.graalvm.compiler.lir.ssi;
  24 
  25 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
  26 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
  27 
  28 import java.util.BitSet;
  29 import java.util.EnumSet;
  30 import java.util.List;
  31 import java.util.ListIterator;
  32 
  33 import org.graalvm.compiler.common.PermanentBailoutException;
  34 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  35 import org.graalvm.compiler.core.common.cfg.BlockMap;
  36 import org.graalvm.compiler.debug.Debug;
  37 import org.graalvm.compiler.debug.GraalError;
  38 import org.graalvm.compiler.debug.Indent;
  39 import org.graalvm.compiler.lir.InstructionValueConsumer;
  40 import org.graalvm.compiler.lir.LIR;
  41 import org.graalvm.compiler.lir.LIRInstruction;
  42 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
  43 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
  44 
  45 import jdk.vm.ci.meta.Value;
  46 
  47 public final class SSIBuilder extends SSIBuilderBase {
  48 
  49     private static class BlockData {
  50 
  51         /**
  52          * Bit map specifying which operands are live upon entry to this block. These are values
  53          * used in this block or any of its successors where such value are not defined in this
  54          * block. The bit index of an operand is its {@linkplain #operandNumber operand number}.
  55          */
  56         public BitSet liveIn;
  57 
  58         /**
  59          * Bit map specifying which operands are live upon exit from this block. These are values
  60          * used in a successor block that are either defined in this block or were live upon entry
  61          * to this block. The bit index of an operand is its {@linkplain #operandNumber operand
  62          * number}.
  63          */
  64         public BitSet liveOut;
  65 
  66         /**
  67          * Bit map specifying which operands are used (before being defined) in this block. That is,
  68          * these are the values that are live upon entry to the block. The bit index of an operand
  69          * is its {@linkplain #operandNumber operand number}.
  70          */
  71         public BitSet liveGen;
  72 
  73         /**
  74          * Bit map specifying which operands are defined/overwritten in this block. The bit index of
  75          * an operand is its {@linkplain #operandNumber operand number}.
  76          */
  77         public BitSet liveKill;
  78     }
  79 
  80     private final BlockMap<SSIBuilder.BlockData> blockData;
  81 
  82     protected SSIBuilder(LIR lir) {
  83         super(lir);
  84         this.blockData = new BlockMap<>(lir.getControlFlowGraph());
  85     }
  86 
  87     @Override
  88     protected void buildIntern() {
  89         init();
  90         computeLocalLiveSets();
  91         computeGlobalLiveSets();
  92     }
  93 
  94     /**
  95      * Gets the size of the {@link BlockData#liveIn} and {@link BlockData#liveOut} sets for a basic
  96      * block.
  97      */
  98     private int liveSetSize() {
  99         return getLIR().numVariables();
 100     }
 101 
 102     AbstractBlockBase<?>[] getBlocks() {
 103         return getLIR().getControlFlowGraph().getBlocks();
 104     }
 105 
 106     static int operandNumber(Value operand) {
 107         if (isVariable(operand)) {
 108             return asVariable(operand).index;
 109         }
 110         throw GraalError.shouldNotReachHere("Can only handle Variables: " + operand);
 111     }
 112 
 113     private SSIBuilder.BlockData getBlockData(AbstractBlockBase<?> block) {
 114         return blockData.get(block);
 115     }
 116 
 117     private void initBlockData(AbstractBlockBase<?> block) {
 118         blockData.put(block, new SSIBuilder.BlockData());
 119     }
 120 
 121     private void init() {
 122         for (AbstractBlockBase<?> block : getBlocks()) {
 123             initBlockData(block);
 124         }
 125     }
 126 
 127     /**
 128      * Computes local live sets (i.e. {@link BlockData#liveGen} and {@link BlockData#liveKill})
 129      * separately for each block.
 130      */
 131     @SuppressWarnings("try")
 132     private void computeLocalLiveSets() {
 133         int liveSize = liveSetSize();
 134 
 135         // iterate all blocks
 136         AbstractBlockBase<?>[] blocks = getBlocks();
 137         for (int i = blocks.length - 1; i >= 0; i--) {
 138             final AbstractBlockBase<?> block = blocks[i];
 139             try (Indent indent = Debug.logAndIndent(LOG_LEVEL, "compute local live sets for block %s", block)) {
 140 
 141                 final BitSet liveGen = new BitSet(liveSize);
 142                 final BitSet liveKill = new BitSet(liveSize);
 143 
 144                 InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
 145                     @Override
 146                     public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 147                         processLocalUse(liveGen, operand);
 148                     }
 149                 };
 150                 InstructionValueConsumer aliveConsumer = new InstructionValueConsumer() {
 151                     @Override
 152                     public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 153                         processLocalUse(liveGen, operand);
 154                     }
 155                 };
 156                 InstructionValueConsumer stateConsumer = new InstructionValueConsumer() {
 157                     @Override
 158                     public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 159                         if (isVariable(operand)) {
 160                             int operandNum = operandNumber(operand);
 161                             if (!liveKill.get(operandNum)) {
 162                                 liveGen.set(operandNum);
 163                                 if (Debug.isLogEnabled()) {
 164                                     Debug.log(LOG_LEVEL, "liveGen in state for operand %d(%s)", operandNum, operand);
 165                                 }
 166                             }
 167                         }
 168                     }
 169                 };
 170                 InstructionValueConsumer defConsumer = new InstructionValueConsumer() {
 171                     @Override
 172                     public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 173                         processLocalDef(liveGen, liveKill, operand, operands);
 174                     }
 175                 };
 176                 InstructionValueConsumer tempConsumer = new InstructionValueConsumer() {
 177                     @Override
 178                     public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
 179                         processLocalDef(liveGen, liveKill, operand, operands);
 180                     }
 181                 };
 182 
 183                 // iterate all instructions of the block
 184                 List<LIRInstruction> instructions = getLIR().getLIRforBlock(block);
 185                 ListIterator<LIRInstruction> instIt = instructions.listIterator(instructions.size());
 186                 while (instIt.hasPrevious()) {
 187                     final LIRInstruction op = instIt.previous();
 188 
 189                     try (Indent indent2 = Debug.logAndIndent(LOG_LEVEL, "handle op %d: %s", op.id(), op)) {
 190                         op.visitEachOutput(defConsumer);
 191                         op.visitEachTemp(tempConsumer);
 192                         op.visitEachState(stateConsumer);
 193                         op.visitEachAlive(aliveConsumer);
 194                         op.visitEachInput(useConsumer);
 195                     }
 196                 } // end of instruction iteration
 197 
 198                 SSIBuilder.BlockData blockSets = getBlockData(block);
 199                 blockSets.liveGen = liveGen;
 200                 blockSets.liveKill = liveKill;
 201                 blockSets.liveIn = new BitSet(liveSize);
 202                 blockSets.liveOut = new BitSet(liveSize);
 203 
 204                 if (Debug.isLogEnabled()) {
 205                     Debug.log(LOG_LEVEL, "liveGen  B%d %s", block.getId(), blockSets.liveGen);
 206                     Debug.log(LOG_LEVEL, "liveKill B%d %s", block.getId(), blockSets.liveKill);
 207                 }
 208 
 209             }
 210         } // end of block iteration
 211     }
 212 
 213     private static void processLocalUse(final BitSet liveGen, Value operand) {
 214         if (isVariable(operand)) {
 215             int operandNum = operandNumber(operand);
 216             liveGen.set(operandNum);
 217             if (Debug.isLogEnabled()) {
 218                 Debug.log(LOG_LEVEL, "liveGen for operand %d(%s)", operandNum, operand);
 219             }
 220         }
 221     }
 222 
 223     private static void processLocalDef(final BitSet liveGen, final BitSet liveKill, Value operand, Value[] operands) {
 224         if (isVariable(operand)) {
 225             int operandNum = operandNumber(operand);
 226             if (operands[operandNum] == null) {
 227                 operands[operandNum] = operand;
 228             }
 229             liveKill.set(operandNum);
 230             liveGen.clear(operandNum);
 231             if (Debug.isLogEnabled()) {
 232                 Debug.log(LOG_LEVEL, "liveKill for operand %d(%s)", operandNum, operand);
 233             }
 234         }
 235     }
 236 
 237     /**
 238      * Performs a backward dataflow analysis to compute global live sets (i.e.
 239      * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block.
 240      */
 241     @SuppressWarnings("try")
 242     private void computeGlobalLiveSets() {
 243         try (Indent indent = Debug.logAndIndent(LOG_LEVEL, "compute global live sets")) {
 244             boolean changeOccurred;
 245             boolean changeOccurredInBlock;
 246             int iterationCount = 0;
 247             BitSet liveOut = new BitSet(liveSetSize()); // scratch set for
 248                                                         // calculations
 249 
 250             /*
 251              * Perform a backward dataflow analysis to compute liveOut and liveIn for each block.
 252              * The loop is executed until a fixpoint is reached (no changes in an iteration).
 253              */
 254             do {
 255                 changeOccurred = false;
 256 
 257                 try (Indent indent2 = Debug.logAndIndent(LOG_LEVEL, "new iteration %d", iterationCount)) {
 258 
 259                     // iterate all blocks in reverse order
 260                     AbstractBlockBase<?>[] blocks = getBlocks();
 261                     for (int i = blocks.length - 1; i >= 0; i--) {
 262                         final AbstractBlockBase<?> block = blocks[i];
 263                         SSIBuilder.BlockData blockSets = getBlockData(block);
 264 
 265                         changeOccurredInBlock = false;
 266 
 267                         /*
 268                          * liveOut(block) is the union of liveIn(sux), for successors sux of block.
 269                          */
 270                         int n = block.getSuccessorCount();
 271                         if (n > 0) {
 272                             // block has successors
 273                             liveOut.clear();
 274                             for (AbstractBlockBase<?> successor : block.getSuccessors()) {
 275                                 liveOut.or(getBlockData(successor).liveIn);
 276                             }
 277 
 278                             if (!blockSets.liveOut.equals(liveOut)) {
 279                                 /*
 280                                  * A change occurred. Swap the old and new live out sets to avoid
 281                                  * copying.
 282                                  */
 283                                 BitSet temp = blockSets.liveOut;
 284                                 blockSets.liveOut = liveOut;
 285                                 liveOut = temp;
 286 
 287                                 changeOccurred = true;
 288                                 changeOccurredInBlock = true;
 289                             }
 290                         }
 291 
 292                         if (iterationCount == 0 || changeOccurredInBlock) {
 293                             /*
 294                              * liveIn(block) is the union of liveGen(block) with (liveOut(block) &
 295                              * !liveKill(block)).
 296                              *
 297                              * Note: liveIn has to be computed only in first iteration or if liveOut
 298                              * has changed!
 299                              */
 300                             BitSet liveIn = blockSets.liveIn;
 301                             liveIn.clear();
 302                             liveIn.or(blockSets.liveOut);
 303                             liveIn.andNot(blockSets.liveKill);
 304                             liveIn.or(blockSets.liveGen);
 305 
 306                             if (Debug.isLogEnabled()) {
 307                                 Debug.log(LOG_LEVEL, "block %d: livein = %s,  liveout = %s", block.getId(), liveIn, blockSets.liveOut);
 308                             }
 309                         }
 310                     }
 311                     iterationCount++;
 312 
 313                     if (changeOccurred && iterationCount > 50) {
 314                         /*
 315                          * Very unlikely should never happen: If it happens we cannot guarantee it
 316                          * won't happen again.
 317                          */
 318                         throw new PermanentBailoutException("too many iterations in computeGlobalLiveSets");
 319                     }
 320                 }
 321             } while (changeOccurred);
 322         }
 323     }
 324 
 325     @Override
 326     BitSet getLiveIn(final AbstractBlockBase<?> block) {
 327         return getBlockData(block).liveIn;
 328     }
 329 
 330     @Override
 331     BitSet getLiveOut(final AbstractBlockBase<?> block) {
 332         return getBlockData(block).liveOut;
 333     }
 334 }