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