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 }