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 }