1 /*
   2  * Copyright (c) 2009, 2011, 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;
  24 
  25 import java.util.ArrayList;
  26 import java.util.Arrays;
  27 import java.util.List;
  28 
  29 /**
  30  * A buffer to enqueue updates to a list. This avoids frequent re-sizing of the list and copying of
  31  * list elements when insertions are done at multiple positions of the list. Additionally, it
  32  * ensures that the list is not modified while it is, e.g., iterated, and instead only modified once
  33  * after the iteration is done.
  34  * <p>
  35  * The buffer uses internal data structures to store the enqueued updates. To avoid allocations, a
  36  * buffer can be re-used. Call the methods in the following order: {@link #init}, {@link #append},
  37  * {@link #append}, ..., {@link #finish()}, {@link #init}, ...
  38  * <p>
  39  * Note: This class does not depend on LIRInstruction, so we could make it a generic utility class.
  40  */
  41 public final class LIRInsertionBuffer {
  42 
  43     /**
  44      * The lir list where ops of this buffer should be inserted later (null when uninitialized).
  45      */
  46     private List<LIRInstruction> lir;
  47 
  48     /**
  49      * List of insertion points. index and count are stored alternately: indexAndCount[i * 2]: the
  50      * index into lir list where "count" ops should be inserted indexAndCount[i * 2 + 1]: the number
  51      * of ops to be inserted at index
  52      */
  53     private int[] indexAndCount;
  54     private int indexAndCountSize;
  55 
  56     /**
  57      * The LIROps to be inserted.
  58      */
  59     private final List<LIRInstruction> ops;
  60 
  61     public LIRInsertionBuffer() {
  62         indexAndCount = new int[8];
  63         ops = new ArrayList<>(4);
  64     }
  65 
  66     /**
  67      * Initialize this buffer. This method must be called before using {@link #append}.
  68      */
  69     public void init(List<LIRInstruction> newLir) {
  70         assert !initialized() : "already initialized";
  71         assert indexAndCountSize == 0 && ops.size() == 0;
  72         this.lir = newLir;
  73     }
  74 
  75     public boolean initialized() {
  76         return lir != null;
  77     }
  78 
  79     public List<LIRInstruction> lirList() {
  80         return lir;
  81     }
  82 
  83     /**
  84      * Enqueue a new instruction that will be appended to the instruction list when
  85      * {@link #finish()} is called. The new instruction is added <b>before</b> the existing
  86      * instruction with the given index. This method can only be called with increasing values of
  87      * index, e.g., once an instruction was appended with index 4, subsequent instructions can only
  88      * be appended with index 4 or higher.
  89      */
  90     public void append(int index, LIRInstruction op) {
  91         int i = numberOfInsertionPoints() - 1;
  92         if (i < 0 || indexAt(i) < index) {
  93             appendNew(index, 1);
  94         } else {
  95             assert indexAt(i) == index : "can append LIROps in ascending order only";
  96             assert countAt(i) > 0 : "check";
  97             setCountAt(i, countAt(i) + 1);
  98         }
  99         ops.add(op);
 100 
 101         assert verify();
 102     }
 103 
 104     /**
 105      * Append all enqueued instructions to the instruction list. After that, {@link #init(List)} can
 106      * be called again to re-use this buffer.
 107      */
 108     public void finish() {
 109         if (ops.size() > 0) {
 110             int n = lir.size();
 111             // increase size of instructions list
 112             for (int i = 0; i < ops.size(); i++) {
 113                 lir.add(null);
 114             }
 115             // insert ops from buffer into instructions list
 116             int opIndex = ops.size() - 1;
 117             int ipIndex = numberOfInsertionPoints() - 1;
 118             int fromIndex = n - 1;
 119             int toIndex = lir.size() - 1;
 120             while (ipIndex >= 0) {
 121                 int index = indexAt(ipIndex);
 122                 // make room after insertion point
 123                 while (fromIndex >= index) {
 124                     lir.set(toIndex--, lir.get(fromIndex--));
 125                 }
 126                 // insert ops from buffer
 127                 for (int i = countAt(ipIndex); i > 0; i--) {
 128                     lir.set(toIndex--, ops.get(opIndex--));
 129                 }
 130                 ipIndex--;
 131             }
 132             indexAndCountSize = 0;
 133             ops.clear();
 134         }
 135         lir = null;
 136     }
 137 
 138     private void appendNew(int index, int count) {
 139         int oldSize = indexAndCountSize;
 140         int newSize = oldSize + 2;
 141         if (newSize > this.indexAndCount.length) {
 142             indexAndCount = Arrays.copyOf(indexAndCount, newSize * 2);
 143         }
 144         indexAndCount[oldSize] = index;
 145         indexAndCount[oldSize + 1] = count;
 146         this.indexAndCountSize = newSize;
 147     }
 148 
 149     private void setCountAt(int i, int value) {
 150         indexAndCount[(i << 1) + 1] = value;
 151     }
 152 
 153     private int numberOfInsertionPoints() {
 154         assert indexAndCount.length % 2 == 0 : "must have a count for each index";
 155         return indexAndCountSize >> 1;
 156     }
 157 
 158     private int indexAt(int i) {
 159         return indexAndCount[(i << 1)];
 160     }
 161 
 162     private int countAt(int i) {
 163         return indexAndCount[(i << 1) + 1];
 164     }
 165 
 166     private boolean verify() {
 167         int sum = 0;
 168         int prevIdx = -1;
 169 
 170         for (int i = 0; i < numberOfInsertionPoints(); i++) {
 171             assert prevIdx < indexAt(i) : "index must be ordered ascending";
 172             sum += countAt(i);
 173         }
 174         assert sum == ops.size() : "wrong total sum";
 175         return true;
 176     }
 177 }