1 /*
   2  * Copyright (c) 2014, 2014, 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 jdk.vm.ci.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.Consumer;
  32 
  33 import jdk.vm.ci.code.CompilationResult.DataPatch;
  34 import jdk.vm.ci.code.CompilationResult.DataSectionReference;
  35 import jdk.vm.ci.code.DataSection.Data;
  36 import jdk.vm.ci.meta.SerializableConstant;
  37 
  38 public final class DataSection implements Iterable<Data> {
  39 
  40     @FunctionalInterface
  41     public interface DataBuilder {
  42 
  43         void emit(ByteBuffer buffer, Consumer<DataPatch> patch);
  44 
  45         static DataBuilder raw(byte[] data) {
  46             return (buffer, patch) -> buffer.put(data);
  47         }
  48 
  49         static DataBuilder serializable(SerializableConstant c) {
  50             return (buffer, patch) -> c.serialize(buffer);
  51         }
  52 
  53         static DataBuilder zero(int size) {
  54             switch (size) {
  55                 case 1:
  56                     return (buffer, patch) -> buffer.put((byte) 0);
  57                 case 2:
  58                     return (buffer, patch) -> buffer.putShort((short) 0);
  59                 case 4:
  60                     return (buffer, patch) -> buffer.putInt(0);
  61                 case 8:
  62                     return (buffer, patch) -> buffer.putLong(0L);
  63                 default:
  64                     return (buffer, patch) -> {
  65                         int rest = size;
  66                         while (rest > 8) {
  67                             buffer.putLong(0L);
  68                             rest -= 8;
  69                         }
  70                         while (rest > 0) {
  71                             buffer.put((byte) 0);
  72                             rest--;
  73                         }
  74                     };
  75             }
  76         }
  77     }
  78 
  79     public static final class Data {
  80 
  81         private int alignment;
  82         private final int size;
  83         private final DataBuilder builder;
  84 
  85         private DataSectionReference ref;
  86 
  87         public Data(int alignment, int size, DataBuilder builder) {
  88             this.alignment = alignment;
  89             this.size = size;
  90             this.builder = builder;
  91 
  92             // initialized in DataSection.insertData(Data)
  93             ref = null;
  94         }
  95 
  96         public void updateAlignment(int newAlignment) {
  97             if (newAlignment == alignment) {
  98                 return;
  99             }
 100             alignment = lcm(alignment, newAlignment);
 101         }
 102 
 103         public int getAlignment() {
 104             return alignment;
 105         }
 106 
 107         public int getSize() {
 108             return size;
 109         }
 110 
 111         public DataBuilder getBuilder() {
 112             return builder;
 113         }
 114 
 115         @Override
 116         public int hashCode() {
 117             // Data instances should not be used as hash map keys
 118             throw new UnsupportedOperationException("hashCode");
 119         }
 120 
 121         @Override
 122         public String toString() {
 123             return identityHashCodeString(this);
 124         }
 125 
 126         @Override
 127         public boolean equals(Object obj) {
 128             assert ref != null;
 129             if (obj == this) {
 130                 return true;
 131             }
 132             if (obj instanceof Data) {
 133                 Data that = (Data) obj;
 134                 if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) {
 135                     return true;
 136                 }
 137             }
 138             return false;
 139         }
 140     }
 141 
 142     private final ArrayList<Data> dataItems = new ArrayList<>();
 143 
 144     private boolean closed;
 145     private int sectionAlignment;
 146     private int sectionSize;
 147 
 148     @Override
 149     public int hashCode() {
 150         // DataSection instances should not be used as hash map keys
 151         throw new UnsupportedOperationException("hashCode");
 152     }
 153 
 154     @Override
 155     public String toString() {
 156         return identityHashCodeString(this);
 157     }
 158 
 159     @Override
 160     public boolean equals(Object obj) {
 161         if (this == obj) {
 162             return true;
 163         }
 164         if (obj instanceof DataSection) {
 165             DataSection that = (DataSection) obj;
 166             if (this.closed == that.closed && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) {
 167                 return true;
 168             }
 169         }
 170         return false;
 171     }
 172 
 173     /**
 174      * Inserts a {@link Data} item into the data section. If the item is already in the data
 175      * section, the same {@link DataSectionReference} is returned.
 176      *
 177      * @param data the {@link Data} item to be inserted
 178      * @return a unique {@link DataSectionReference} identifying the {@link Data} item
 179      */
 180     public DataSectionReference insertData(Data data) {
 181         checkOpen();
 182         synchronized (data) {
 183             if (data.ref == null) {
 184                 data.ref = new DataSectionReference();
 185                 dataItems.add(data);
 186             }
 187             return data.ref;
 188         }
 189     }
 190 
 191     /**
 192      * Transfers all {@link Data} from the provided other {@link DataSection} to this
 193      * {@link DataSection}, and empties the other section.
 194      */
 195     public void addAll(DataSection other) {
 196         checkOpen();
 197         other.checkOpen();
 198 
 199         for (Data data : other.dataItems) {
 200             assert data.ref != null;
 201             dataItems.add(data);
 202         }
 203         other.dataItems.clear();
 204     }
 205 
 206     /**
 207      * Determines if this object has been {@link #close() closed}.
 208      */
 209     public boolean closed() {
 210         return closed;
 211     }
 212 
 213     /**
 214      * Computes the layout of the data section and closes this object to further updates.
 215      *
 216      * This must be called exactly once.
 217      */
 218     void close() {
 219         checkOpen();
 220         closed = true;
 221 
 222         // simple heuristic: put items with larger alignment requirement first
 223         dataItems.sort((a, b) -> a.alignment - b.alignment);
 224 
 225         int position = 0;
 226         int alignment = 1;
 227         for (Data d : dataItems) {
 228             alignment = lcm(alignment, d.alignment);
 229             position = align(position, d.alignment);
 230 
 231             d.ref.setOffset(position);
 232             position += d.size;
 233         }
 234 
 235         sectionAlignment = alignment;
 236         sectionSize = position;
 237     }
 238 
 239     /**
 240      * Gets the size of the data section.
 241      *
 242      * This must only be called once this object has been {@linkplain #closed() closed}.
 243      */
 244     public int getSectionSize() {
 245         checkClosed();
 246         return sectionSize;
 247     }
 248 
 249     /**
 250      * Gets the minimum alignment requirement of the data section.
 251      *
 252      * This must only be called once this object has been {@linkplain #closed() closed}.
 253      */
 254     public int getSectionAlignment() {
 255         checkClosed();
 256         return sectionAlignment;
 257     }
 258 
 259     /**
 260      * Builds the data section into a given buffer.
 261      *
 262      * This must only be called once this object has been {@linkplain #closed() closed}.
 263      *
 264      * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must
 265      *            hold at least {@link #getSectionSize()} bytes.
 266      * @param patch a {@link Consumer} to receive {@link DataPatch data patches} for relocations in
 267      *            the data section
 268      */
 269     public void buildDataSection(ByteBuffer buffer, Consumer<DataPatch> patch) {
 270         checkClosed();
 271         for (Data d : dataItems) {
 272             buffer.position(d.ref.getOffset());
 273             d.builder.emit(buffer, patch);
 274         }
 275     }
 276 
 277     public Data findData(DataSectionReference ref) {
 278         for (Data d : dataItems) {
 279             if (d.ref == ref) {
 280                 return d;
 281             }
 282         }
 283         return null;
 284     }
 285 
 286     public Iterator<Data> iterator() {
 287         return dataItems.iterator();
 288     }
 289 
 290     public static int lcm(int x, int y) {
 291         if (x == 0) {
 292             return y;
 293         } else if (y == 0) {
 294             return x;
 295         }
 296 
 297         int a = Math.max(x, y);
 298         int b = Math.min(x, y);
 299         while (b > 0) {
 300             int tmp = a % b;
 301             a = b;
 302             b = tmp;
 303         }
 304 
 305         int gcd = a;
 306         return x * y / gcd;
 307     }
 308 
 309     private static int align(int position, int alignment) {
 310         return ((position + alignment - 1) / alignment) * alignment;
 311     }
 312 
 313     private void checkClosed() {
 314         if (!closed) {
 315             throw new IllegalStateException();
 316         }
 317     }
 318 
 319     private void checkOpen() {
 320         if (closed) {
 321             throw new IllegalStateException();
 322         }
 323     }
 324 
 325     public void clear() {
 326         checkOpen();
 327         this.dataItems.clear();
 328         this.sectionAlignment = 0;
 329         this.sectionSize = 0;
 330     }
 331 }