1 /* 2 * Copyright (c) 2014, 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.code; 24 25 import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString; 26 27 import java.nio.ByteBuffer; 28 import java.util.ArrayList; 29 import java.util.Iterator; 30 import java.util.Objects; 31 import java.util.function.BiConsumer; 32 33 import org.graalvm.compiler.code.DataSection.Data; 34 35 import jdk.vm.ci.code.site.DataSectionReference; 36 import jdk.vm.ci.meta.SerializableConstant; 37 import jdk.vm.ci.meta.VMConstant; 38 39 public final class DataSection implements Iterable<Data> { 40 41 public interface Patches { 42 43 void registerPatch(VMConstant c); 44 } 45 46 public abstract static class Data { 47 48 private int alignment; 49 private final int size; 50 51 private DataSectionReference ref; 52 53 protected Data(int alignment, int size) { 54 this.alignment = alignment; 55 this.size = size; 56 57 // initialized in DataSection.insertData(Data) 58 ref = null; 59 } 60 61 protected abstract void emit(ByteBuffer buffer, Patches patches); 62 63 public void updateAlignment(int newAlignment) { 64 if (newAlignment == alignment) { 65 return; 66 } 67 alignment = lcm(alignment, newAlignment); 68 } 69 70 public int getAlignment() { 71 return alignment; 72 } 73 74 public int getSize() { 75 return size; 76 } 77 78 @Override 79 public int hashCode() { 80 // Data instances should not be used as hash map keys 81 throw new UnsupportedOperationException("hashCode"); 82 } 83 84 @Override 85 public String toString() { 86 return identityHashCodeString(this); 87 } 88 89 @Override 90 public boolean equals(Object obj) { 91 assert ref != null; 92 if (obj == this) { 93 return true; 94 } 95 if (obj instanceof Data) { 96 Data that = (Data) obj; 97 if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) { 98 return true; 99 } 100 } 101 return false; 102 } 103 } 104 105 public static final class RawData extends Data { 106 107 private final byte[] data; 108 109 public RawData(byte[] data, int alignment) { 110 super(alignment, data.length); 111 this.data = data; 112 } 113 114 @Override 115 protected void emit(ByteBuffer buffer, Patches patches) { 116 buffer.put(data); 117 } 118 } 119 120 public static final class SerializableData extends Data { 121 122 private final SerializableConstant constant; 123 124 public SerializableData(SerializableConstant constant) { 125 this(constant, 1); 126 } 127 128 public SerializableData(SerializableConstant constant, int alignment) { 129 super(alignment, constant.getSerializedSize()); 130 this.constant = constant; 131 } 132 133 @Override 134 protected void emit(ByteBuffer buffer, Patches patches) { 135 int position = buffer.position(); 136 constant.serialize(buffer); 137 assert buffer.position() - position == constant.getSerializedSize() : "wrong number of bytes written"; 138 } 139 } 140 141 public static class ZeroData extends Data { 142 143 protected ZeroData(int alignment, int size) { 144 super(alignment, size); 145 } 146 147 public static ZeroData create(int alignment, int size) { 148 switch (size) { 149 case 1: 150 return new ZeroData(alignment, size) { 151 @Override 152 protected void emit(ByteBuffer buffer, Patches patches) { 153 buffer.put((byte) 0); 154 } 155 }; 156 157 case 2: 158 return new ZeroData(alignment, size) { 159 @Override 160 protected void emit(ByteBuffer buffer, Patches patches) { 161 buffer.putShort((short) 0); 162 } 163 }; 164 165 case 4: 166 return new ZeroData(alignment, size) { 167 @Override 168 protected void emit(ByteBuffer buffer, Patches patches) { 169 buffer.putInt(0); 170 } 171 }; 172 173 case 8: 174 return new ZeroData(alignment, size) { 175 @Override 176 protected void emit(ByteBuffer buffer, Patches patches) { 177 buffer.putLong(0); 178 } 179 }; 180 181 default: 182 return new ZeroData(alignment, size); 183 } 184 } 185 186 @Override 187 protected void emit(ByteBuffer buffer, Patches patches) { 188 int rest = getSize(); 189 while (rest > 8) { 190 buffer.putLong(0L); 191 rest -= 8; 192 } 193 while (rest > 0) { 194 buffer.put((byte) 0); 195 rest--; 196 } 197 } 198 } 199 200 public static final class PackedData extends Data { 201 202 private final Data[] nested; 203 204 private PackedData(int alignment, int size, Data[] nested) { 205 super(alignment, size); 206 this.nested = nested; 207 } 208 209 public static PackedData create(Data[] nested) { 210 int size = 0; 211 int alignment = 1; 212 for (int i = 0; i < nested.length; i++) { 213 assert size % nested[i].getAlignment() == 0 : "invalid alignment in packed constants"; 214 alignment = DataSection.lcm(alignment, nested[i].getAlignment()); 215 size += nested[i].getSize(); 216 } 217 return new PackedData(alignment, size, nested); 218 } 219 220 @Override 221 protected void emit(ByteBuffer buffer, Patches patches) { 222 for (Data data : nested) { 223 data.emit(buffer, patches); 224 } 225 } 226 } 227 228 private final ArrayList<Data> dataItems = new ArrayList<>(); 229 230 private boolean closed; 231 private int sectionAlignment; 232 private int sectionSize; 233 234 @Override 235 public int hashCode() { 236 // DataSection instances should not be used as hash map keys 237 throw new UnsupportedOperationException("hashCode"); 238 } 239 240 @Override 241 public String toString() { 242 return identityHashCodeString(this); 243 } 244 245 @Override 246 public boolean equals(Object obj) { 247 if (this == obj) { 248 return true; 249 } 250 if (obj instanceof DataSection) { 251 DataSection that = (DataSection) obj; 252 if (this.closed == that.closed && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) { 253 return true; 254 } 255 } 256 return false; 257 } 258 259 /** 260 * Inserts a {@link Data} item into the data section. If the item is already in the data 261 * section, the same {@link DataSectionReference} is returned. 262 * 263 * @param data the {@link Data} item to be inserted 264 * @return a unique {@link DataSectionReference} identifying the {@link Data} item 265 */ 266 public DataSectionReference insertData(Data data) { 267 checkOpen(); 268 synchronized (data) { 269 if (data.ref == null) { 270 data.ref = new DataSectionReference(); 271 dataItems.add(data); 272 } 273 return data.ref; 274 } 275 } 276 277 /** 278 * Transfers all {@link Data} from the provided other {@link DataSection} to this 279 * {@link DataSection}, and empties the other section. 280 */ 281 public void addAll(DataSection other) { 282 checkOpen(); 283 other.checkOpen(); 284 285 for (Data data : other.dataItems) { 286 assert data.ref != null; 287 dataItems.add(data); 288 } 289 other.dataItems.clear(); 290 } 291 292 /** 293 * Determines if this object has been {@link #close() closed}. 294 */ 295 public boolean closed() { 296 return closed; 297 } 298 299 /** 300 * Computes the layout of the data section and closes this object to further updates. 301 * 302 * This must be called exactly once. 303 */ 304 public void close() { 305 checkOpen(); 306 closed = true; 307 308 // simple heuristic: put items with larger alignment requirement first 309 dataItems.sort((a, b) -> a.alignment - b.alignment); 310 311 int position = 0; 312 int alignment = 1; 313 for (Data d : dataItems) { 314 alignment = lcm(alignment, d.alignment); 315 position = align(position, d.alignment); 316 317 d.ref.setOffset(position); 318 position += d.size; 319 } 320 321 sectionAlignment = alignment; 322 sectionSize = position; 323 } 324 325 /** 326 * Gets the size of the data section. 327 * 328 * This must only be called once this object has been {@linkplain #closed() closed}. 329 */ 330 public int getSectionSize() { 331 checkClosed(); 332 return sectionSize; 333 } 334 335 /** 336 * Gets the minimum alignment requirement of the data section. 337 * 338 * This must only be called once this object has been {@linkplain #closed() closed}. 339 */ 340 public int getSectionAlignment() { 341 checkClosed(); 342 return sectionAlignment; 343 } 344 345 /** 346 * Builds the data section into a given buffer. 347 * 348 * This must only be called once this object has been {@linkplain #closed() closed}. 349 * 350 * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must 351 * hold at least {@link #getSectionSize()} bytes. 352 * @param patch a {@link Patches} instance to receive {@link VMConstant constants} for 353 * relocations in the data section 354 */ 355 public void buildDataSection(ByteBuffer buffer, Patches patch) { 356 buildDataSection(buffer, patch, (r, s) -> { 357 }); 358 } 359 360 /** 361 * Builds the data section into a given buffer. 362 * 363 * This must only be called once this object has been {@linkplain #closed() closed}. When this 364 * method returns, the buffers' position is just after the last data item. 365 * 366 * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must 367 * hold at least {@link #getSectionSize()} bytes. 368 * @param patch a {@link Patches} instance to receive {@link VMConstant constants} for 369 * @param onEmit a function that is called before emitting each data item with the 370 * {@link DataSectionReference} and the size of the data. 371 */ 372 public void buildDataSection(ByteBuffer buffer, Patches patch, BiConsumer<DataSectionReference, Integer> onEmit) { 373 checkClosed(); 374 assert buffer.remaining() >= sectionSize; 375 int start = buffer.position(); 376 for (Data d : dataItems) { 377 buffer.position(start + d.ref.getOffset()); 378 onEmit.accept(d.ref, d.getSize()); 379 d.emit(buffer, patch); 380 } 381 buffer.position(start + sectionSize); 382 } 383 384 public Data findData(DataSectionReference ref) { 385 for (Data d : dataItems) { 386 if (d.ref == ref) { 387 return d; 388 } 389 } 390 return null; 391 } 392 393 public static void emit(ByteBuffer buffer, Data data, Patches patch) { 394 data.emit(buffer, patch); 395 } 396 397 @Override 398 public Iterator<Data> iterator() { 399 return dataItems.iterator(); 400 } 401 402 private static int lcm(int x, int y) { 403 if (x == 0) { 404 return y; 405 } else if (y == 0) { 406 return x; 407 } 408 409 int a = Math.max(x, y); 410 int b = Math.min(x, y); 411 while (b > 0) { 412 int tmp = a % b; 413 a = b; 414 b = tmp; 415 } 416 417 int gcd = a; 418 return x * y / gcd; 419 } 420 421 private static int align(int position, int alignment) { 422 return ((position + alignment - 1) / alignment) * alignment; 423 } 424 425 private void checkClosed() { 426 if (!closed) { 427 throw new IllegalStateException(); 428 } 429 } 430 431 private void checkOpen() { 432 if (closed) { 433 throw new IllegalStateException(); 434 } 435 } 436 437 public void clear() { 438 checkOpen(); 439 this.dataItems.clear(); 440 this.sectionAlignment = 0; 441 this.sectionSize = 0; 442 } 443 }