1 /* 2 * Copyright (c) 2014, 2014, 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 jdk.vm.ci.code; 24 25 import static jdk.vm.ci.meta.MetaUtil.*; 26 27 import java.nio.*; 28 import java.util.*; 29 import java.util.function.*; 30 31 import jdk.vm.ci.code.CompilationResult.*; 32 import jdk.vm.ci.code.DataSection.*; 33 import jdk.vm.ci.meta.*; 34 35 public final class DataSection implements Iterable<Data> { 36 37 @FunctionalInterface 38 public interface DataBuilder { 39 40 void emit(ByteBuffer buffer, Consumer<DataPatch> patch); 41 42 static DataBuilder raw(byte[] data) { 43 return (buffer, patch) -> buffer.put(data); 44 } 45 46 static DataBuilder serializable(SerializableConstant c) { 47 return (buffer, patch) -> c.serialize(buffer); 48 } 49 50 static DataBuilder zero(int size) { 51 switch (size) { 52 case 1: 53 return (buffer, patch) -> buffer.put((byte) 0); 54 case 2: 55 return (buffer, patch) -> buffer.putShort((short) 0); 56 case 4: 57 return (buffer, patch) -> buffer.putInt(0); 58 case 8: 59 return (buffer, patch) -> buffer.putLong(0L); 60 default: 61 return (buffer, patch) -> { 62 int rest = size; 63 while (rest > 8) { 64 buffer.putLong(0L); 65 rest -= 8; 66 } 67 while (rest > 0) { 68 buffer.put((byte) 0); 69 rest--; 70 } 71 }; 72 } 73 } 74 } 75 76 public static final class Data { 77 78 private int alignment; 79 private final int size; 80 private final DataBuilder builder; 81 82 private DataSectionReference ref; 83 84 public Data(int alignment, int size, DataBuilder builder) { 85 this.alignment = alignment; 86 this.size = size; 87 this.builder = builder; 88 89 // initialized in DataSection.insertData(Data) 90 ref = null; 91 } 92 93 public void updateAlignment(int newAlignment) { 94 if (newAlignment == alignment) { 95 return; 96 } 97 alignment = lcm(alignment, newAlignment); 98 } 99 100 public int getAlignment() { 101 return alignment; 102 } 103 104 public int getSize() { 105 return size; 106 } 107 108 public DataBuilder getBuilder() { 109 return builder; 110 } 111 112 @Override 113 public int hashCode() { 114 // Data instances should not be used as hash map keys 115 throw new UnsupportedOperationException("hashCode"); 116 } 117 118 @Override 119 public String toString() { 120 return identityHashCodeString(this); 121 } 122 123 @Override 124 public boolean equals(Object obj) { 125 assert ref != null; 126 if (obj == this) { 127 return true; 128 } 129 if (obj instanceof Data) { 130 Data that = (Data) obj; 131 if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) { 132 return true; 133 } 134 } 135 return false; 136 } 137 } 138 139 private final ArrayList<Data> dataItems = new ArrayList<>(); 140 141 private boolean finalLayout; 142 private int sectionAlignment; 143 private int sectionSize; 144 145 @Override 146 public int hashCode() { 147 // DataSection instances should not be used as hash map keys 148 throw new UnsupportedOperationException("hashCode"); 149 } 150 151 @Override 152 public String toString() { 153 return identityHashCodeString(this); 154 } 155 156 @Override 157 public boolean equals(Object obj) { 158 if (this == obj) { 159 return true; 160 } 161 if (obj instanceof DataSection) { 162 DataSection that = (DataSection) obj; 163 if (this.finalLayout == that.finalLayout && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 170 /** 171 * Insert a {@link Data} item into the data section. If the item is already in the data section, 172 * the same {@link DataSectionReference} is returned. 173 * 174 * @param data the {@link Data} item to be inserted 175 * @return a unique {@link DataSectionReference} identifying the {@link Data} item 176 */ 177 public DataSectionReference insertData(Data data) { 178 assert !finalLayout; 179 if (data.ref == null) { 180 data.ref = new DataSectionReference(); 181 dataItems.add(data); 182 } 183 return data.ref; 184 } 185 186 /** 187 * Compute the layout of the data section. This can be called only once, and after it has been 188 * called, the data section can no longer be modified. 189 */ 190 public void finalizeLayout() { 191 assert !finalLayout; 192 finalLayout = true; 193 194 // simple heuristic: put items with larger alignment requirement first 195 dataItems.sort((a, b) -> a.alignment - b.alignment); 196 197 int position = 0; 198 for (Data d : dataItems) { 199 sectionAlignment = lcm(sectionAlignment, d.alignment); 200 position = align(position, d.alignment); 201 202 d.ref.setOffset(position); 203 position += d.size; 204 } 205 206 sectionSize = position; 207 } 208 209 public boolean isFinalized() { 210 return finalLayout; 211 } 212 213 /** 214 * Get the size of the data section. Can only be called after {@link #finalizeLayout}. 215 */ 216 public int getSectionSize() { 217 assert finalLayout; 218 return sectionSize; 219 } 220 221 /** 222 * Get the minimum alignment requirement of the data section. Can only be called after 223 * {@link #finalizeLayout}. 224 */ 225 public int getSectionAlignment() { 226 assert finalLayout; 227 return sectionAlignment; 228 } 229 230 /** 231 * Build the data section. Can only be called after {@link #finalizeLayout}. 232 * 233 * @param buffer The {@link ByteBuffer} where the data section should be built. The buffer must 234 * hold at least {@link #getSectionSize()} bytes. 235 * @param patch A {@link Consumer} to receive {@link DataPatch data patches} for relocations in 236 * the data section. 237 */ 238 public void buildDataSection(ByteBuffer buffer, Consumer<DataPatch> patch) { 239 assert finalLayout; 240 for (Data d : dataItems) { 241 buffer.position(d.ref.getOffset()); 242 d.builder.emit(buffer, patch); 243 } 244 } 245 246 public Data findData(DataSectionReference ref) { 247 for (Data d : dataItems) { 248 if (d.ref == ref) { 249 return d; 250 } 251 } 252 return null; 253 } 254 255 public Iterator<Data> iterator() { 256 return dataItems.iterator(); 257 } 258 259 public static int lcm(int x, int y) { 260 if (x == 0) { 261 return y; 262 } else if (y == 0) { 263 return x; 264 } 265 266 int a = Math.max(x, y); 267 int b = Math.min(x, y); 268 while (b > 0) { 269 int tmp = a % b; 270 a = b; 271 b = tmp; 272 } 273 274 int gcd = a; 275 return x * y / gcd; 276 } 277 278 private static int align(int position, int alignment) { 279 return ((position + alignment - 1) / alignment) * alignment; 280 } 281 282 public void clear() { 283 assert !finalLayout; 284 this.dataItems.clear(); 285 this.sectionAlignment = 0; 286 this.sectionSize = 0; 287 } 288 }