1 /*
   2  * Copyright (c) 2012, 2016, 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 org.graalvm.compiler.hotspot;
  24 
  25 import java.nio.ByteBuffer;
  26 import java.nio.ByteOrder;
  27 import java.util.ArrayList;
  28 import java.util.Collections;
  29 import java.util.Comparator;
  30 import java.util.EnumMap;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.stream.Stream;
  34 import java.util.stream.Stream.Builder;
  35 
  36 import org.graalvm.compiler.code.CompilationResult;
  37 import org.graalvm.compiler.code.CompilationResult.CodeAnnotation;
  38 import org.graalvm.compiler.code.CompilationResult.CodeComment;
  39 import org.graalvm.compiler.code.CompilationResult.JumpTable;
  40 import org.graalvm.compiler.debug.GraalError;
  41 import org.graalvm.compiler.code.DataSection;
  42 import org.graalvm.compiler.code.SourceMapping;
  43 import org.graalvm.compiler.graph.NodeSourcePosition;
  44 
  45 import jdk.vm.ci.code.DebugInfo;
  46 import jdk.vm.ci.code.StackSlot;
  47 import jdk.vm.ci.code.site.ConstantReference;
  48 import jdk.vm.ci.code.site.DataPatch;
  49 import jdk.vm.ci.code.site.Infopoint;
  50 import jdk.vm.ci.code.site.InfopointReason;
  51 import jdk.vm.ci.code.site.Mark;
  52 import jdk.vm.ci.code.site.Site;
  53 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
  54 import jdk.vm.ci.hotspot.HotSpotCompiledCode;
  55 import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment;
  56 import jdk.vm.ci.hotspot.HotSpotCompiledNmethod;
  57 import jdk.vm.ci.hotspot.HotSpotObjectConstant;
  58 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
  59 import jdk.vm.ci.meta.Assumptions.Assumption;
  60 import jdk.vm.ci.meta.ResolvedJavaMethod;
  61 
  62 public class HotSpotCompiledCodeBuilder {
  63 
  64     public static HotSpotCompiledCode createCompiledCode(ResolvedJavaMethod method, HotSpotCompilationRequest compRequest, CompilationResult compResult) {
  65         String name = compResult.getName();
  66 
  67         byte[] targetCode = compResult.getTargetCode();
  68         int targetCodeSize = compResult.getTargetCodeSize();
  69 
  70         Site[] sites = getSortedSites(compResult);
  71 
  72         Assumption[] assumptions = compResult.getAssumptions();
  73 
  74         ResolvedJavaMethod[] methods = compResult.getMethods();
  75 
  76         List<CodeAnnotation> annotations = compResult.getAnnotations();
  77         Comment[] comments = new Comment[annotations.size()];
  78         if (!annotations.isEmpty()) {
  79             for (int i = 0; i < comments.length; i++) {
  80                 CodeAnnotation annotation = annotations.get(i);
  81                 String text;
  82                 if (annotation instanceof CodeComment) {
  83                     CodeComment codeComment = (CodeComment) annotation;
  84                     text = codeComment.value;
  85                 } else if (annotation instanceof JumpTable) {
  86                     JumpTable jumpTable = (JumpTable) annotation;
  87                     text = "JumpTable [" + jumpTable.low + " .. " + jumpTable.high + "]";
  88                 } else {
  89                     text = annotation.toString();
  90                 }
  91                 comments[i] = new Comment(annotation.position, text);
  92             }
  93         }
  94 
  95         DataSection data = compResult.getDataSection();
  96         byte[] dataSection = new byte[data.getSectionSize()];
  97 
  98         ByteBuffer buffer = ByteBuffer.wrap(dataSection).order(ByteOrder.nativeOrder());
  99         Builder<DataPatch> patchBuilder = Stream.builder();
 100         data.buildDataSection(buffer, vmConstant -> {
 101             patchBuilder.accept(new DataPatch(buffer.position(), new ConstantReference(vmConstant)));
 102         });
 103 
 104         int dataSectionAlignment = data.getSectionAlignment();
 105         DataPatch[] dataSectionPatches = patchBuilder.build().toArray(len -> new DataPatch[len]);
 106 
 107         int totalFrameSize = compResult.getTotalFrameSize();
 108         StackSlot customStackArea = compResult.getCustomStackArea();
 109         boolean isImmutablePIC = compResult.isImmutablePIC();
 110 
 111         if (method instanceof HotSpotResolvedJavaMethod) {
 112             HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method;
 113             int entryBCI = compResult.getEntryBCI();
 114             boolean hasUnsafeAccess = compResult.hasUnsafeAccess();
 115 
 116             int id;
 117             long jvmciEnv;
 118             if (compRequest != null) {
 119                 id = compRequest.getId();
 120                 jvmciEnv = compRequest.getJvmciEnv();
 121             } else {
 122                 id = hsMethod.allocateCompileId(entryBCI);
 123                 jvmciEnv = 0L;
 124             }
 125             return new HotSpotCompiledNmethod(name, targetCode, targetCodeSize, sites, assumptions, methods, comments, dataSection, dataSectionAlignment, dataSectionPatches, isImmutablePIC,
 126                             totalFrameSize, customStackArea, hsMethod, entryBCI, id, jvmciEnv, hasUnsafeAccess);
 127         } else {
 128             return new HotSpotCompiledCode(name, targetCode, targetCodeSize, sites, assumptions, methods, comments, dataSection, dataSectionAlignment, dataSectionPatches, isImmutablePIC,
 129                             totalFrameSize, customStackArea);
 130         }
 131     }
 132 
 133     static class SiteComparator implements Comparator<Site> {
 134 
 135         /**
 136          * Defines an order for sorting {@link Infopoint}s based on their
 137          * {@linkplain Infopoint#reason reasons}. This is used to choose which infopoint to preserve
 138          * when multiple infopoints collide on the same PC offset. A negative order value implies a
 139          * non-optional infopoint (i.e., must be preserved).
 140          */
 141         static final Map<InfopointReason, Integer> HOTSPOT_INFOPOINT_SORT_ORDER = new EnumMap<>(InfopointReason.class);
 142 
 143         static {
 144             HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.SAFEPOINT, -4);
 145             HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.CALL, -3);
 146             HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.IMPLICIT_EXCEPTION, -2);
 147             HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.METHOD_START, 2);
 148             HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.METHOD_END, 3);
 149             HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.BYTECODE_POSITION, 4);
 150         }
 151 
 152         static int ord(Infopoint info) {
 153             return HOTSPOT_INFOPOINT_SORT_ORDER.get(info.reason);
 154         }
 155 
 156         static int checkCollision(Infopoint i1, Infopoint i2) {
 157             int o1 = ord(i1);
 158             int o2 = ord(i2);
 159             if (o1 < 0 && o2 < 0) {
 160                 throw new GraalError("Non optional infopoints cannot collide: %s and %s", i1, i2);
 161             }
 162             return o1 - o2;
 163         }
 164 
 165         /**
 166          * Records whether any two {@link Infopoint}s had the same {@link Infopoint#pcOffset}.
 167          */
 168         boolean sawCollidingInfopoints;
 169 
 170         @Override
 171         public int compare(Site s1, Site s2) {
 172             if (s1.pcOffset == s2.pcOffset) {
 173                 // Marks must come first since patching a call site
 174                 // may need to know the mark denoting the call type
 175                 // (see uses of CodeInstaller::_next_call_type).
 176                 boolean s1IsMark = s1 instanceof Mark;
 177                 boolean s2IsMark = s2 instanceof Mark;
 178                 if (s1IsMark != s2IsMark) {
 179                     return s1IsMark ? -1 : 1;
 180                 }
 181 
 182                 // Infopoints must group together so put them after
 183                 // other Site types.
 184                 boolean s1IsInfopoint = s1 instanceof Infopoint;
 185                 boolean s2IsInfopoint = s2 instanceof Infopoint;
 186                 if (s1IsInfopoint != s2IsInfopoint) {
 187                     return s1IsInfopoint ? 1 : -1;
 188                 }
 189 
 190                 if (s1IsInfopoint) {
 191                     sawCollidingInfopoints = true;
 192                     return checkCollision((Infopoint) s1, (Infopoint) s2);
 193                 }
 194             }
 195             return s1.pcOffset - s2.pcOffset;
 196         }
 197     }
 198 
 199     /**
 200      * HotSpot expects sites to be presented in ascending order of PC (see
 201      * {@code DebugInformationRecorder::add_new_pc_offset}). In addition, it expects
 202      * {@link Infopoint} PCs to be unique.
 203      */
 204     private static Site[] getSortedSites(CompilationResult target) {
 205         List<Site> sites = new ArrayList<>(
 206                         target.getExceptionHandlers().size() + target.getInfopoints().size() + target.getDataPatches().size() + target.getMarks().size() + target.getSourceMappings().size());
 207         sites.addAll(target.getExceptionHandlers());
 208         sites.addAll(target.getInfopoints());
 209         sites.addAll(target.getDataPatches());
 210         sites.addAll(target.getMarks());
 211 
 212         /*
 213          * Translate the source mapping into appropriate info points. In HotSpot only one position
 214          * can really be represented and recording the end PC seems to give the best results and
 215          * corresponds with what C1 and C2 do.
 216          */
 217         for (SourceMapping source : target.getSourceMappings()) {
 218             sites.add(new Infopoint(source.getEndOffset(), new DebugInfo(source.getSourcePosition()), InfopointReason.BYTECODE_POSITION));
 219             assert verifySourcePositionReceivers(source.getSourcePosition());
 220         }
 221 
 222         SiteComparator c = new SiteComparator();
 223         Collections.sort(sites, c);
 224         if (c.sawCollidingInfopoints) {
 225             Infopoint lastInfopoint = null;
 226             List<Site> copy = new ArrayList<>(sites.size());
 227             for (Site site : sites) {
 228                 if (site instanceof Infopoint) {
 229                     Infopoint info = (Infopoint) site;
 230                     if (lastInfopoint == null || lastInfopoint.pcOffset != info.pcOffset) {
 231                         lastInfopoint = info;
 232                         copy.add(info);
 233                     } else {
 234                         // Omit this colliding infopoint
 235                         assert lastInfopoint.reason.compareTo(info.reason) <= 0;
 236                     }
 237                 } else {
 238                     copy.add(site);
 239                 }
 240             }
 241             sites = copy;
 242         }
 243         return sites.toArray(new Site[sites.size()]);
 244     }
 245 
 246     /**
 247      * Verifies that the captured receiver type agrees with the declared type of the method.
 248      */
 249     private static boolean verifySourcePositionReceivers(NodeSourcePosition start) {
 250         NodeSourcePosition pos = start;
 251         while (pos != null) {
 252             if (pos.getReceiver() != null) {
 253                 assert ((HotSpotObjectConstant) pos.getReceiver()).asObject(pos.getMethod().getDeclaringClass()) != null;
 254             }
 255             pos = pos.getCaller();
 256         }
 257         return true;
 258     }
 259 }