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