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 }