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