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 }