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 finalLayout;
 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.finalLayout == that.finalLayout && 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      * Insert a {@link Data} item into the data section. If the item is already in the data section,
 175      * 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         assert !finalLayout;
 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         assert !finalLayout && !other.finalLayout;
 197 
 198         for (Data data : other.dataItems) {
 199             assert data.ref != null;
 200             dataItems.add(data);
 201         }
 202         other.dataItems.clear();
 203     }
 204 
 205     /**
 206      * Compute the layout of the data section. This can be called only once, and after it has been
 207      * called, the data section can no longer be modified.
 208      */
 209     public void finalizeLayout() {
 210         assert !finalLayout;
 211         finalLayout = true;
 212 
 213         // simple heuristic: put items with larger alignment requirement first
 214         dataItems.sort((a, b) -> a.alignment - b.alignment);
 215 
 216         int position = 0;
 217         int alignment = 1;
 218         for (Data d : dataItems) {
 219             alignment = lcm(alignment, d.alignment);
 220             position = align(position, d.alignment);
 221 
 222             d.ref.setOffset(position);
 223             position += d.size;
 224         }
 225 
 226         sectionAlignment = alignment;
 227         sectionSize = position;
 228     }
 229 
 230     public boolean isFinalized() {
 231         return finalLayout;
 232     }
 233 
 234     /**
 235      * Get the size of the data section. Can only be called after {@link #finalizeLayout}.
 236      */
 237     public int getSectionSize() {
 238         assert finalLayout;
 239         return sectionSize;
 240     }
 241 
 242     /**
 243      * Get the minimum alignment requirement of the data section. Can only be called after
 244      * {@link #finalizeLayout}.
 245      */
 246     public int getSectionAlignment() {
 247         assert finalLayout;
 248         return sectionAlignment;
 249     }
 250 
 251     /**
 252      * Build the data section. Can only be called after {@link #finalizeLayout}.
 253      *
 254      * @param buffer The {@link ByteBuffer} where the data section should be built. The buffer must
 255      *            hold at least {@link #getSectionSize()} bytes.
 256      * @param patch A {@link Consumer} to receive {@link DataPatch data patches} for relocations in
 257      *            the data section.
 258      */
 259     public void buildDataSection(ByteBuffer buffer, Consumer<DataPatch> patch) {
 260         assert finalLayout;
 261         for (Data d : dataItems) {
 262             buffer.position(d.ref.getOffset());
 263             d.builder.emit(buffer, patch);
 264         }
 265     }
 266 
 267     public Data findData(DataSectionReference ref) {
 268         for (Data d : dataItems) {
 269             if (d.ref == ref) {
 270                 return d;
 271             }
 272         }
 273         return null;
 274     }
 275 
 276     public Iterator<Data> iterator() {
 277         return dataItems.iterator();
 278     }
 279 
 280     public static int lcm(int x, int y) {
 281         if (x == 0) {
 282             return y;
 283         } else if (y == 0) {
 284             return x;
 285         }
 286 
 287         int a = Math.max(x, y);
 288         int b = Math.min(x, y);
 289         while (b > 0) {
 290             int tmp = a % b;
 291             a = b;
 292             b = tmp;
 293         }
 294 
 295         int gcd = a;
 296         return x * y / gcd;
 297     }
 298 
 299     private static int align(int position, int alignment) {
 300         return ((position + alignment - 1) / alignment) * alignment;
 301     }
 302 
 303     public void clear() {
 304         assert !finalLayout;
 305         this.dataItems.clear();
 306         this.sectionAlignment = 0;
 307         this.sectionSize = 0;
 308     }
 309 }