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