1 /* 2 * Copyright (c) 2009, 2018, 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.core; 26 27 import java.util.Collection; 28 import java.util.List; 29 30 import jdk.internal.vm.compiler.collections.EconomicSet; 31 import org.graalvm.compiler.code.CompilationResult; 32 import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext; 33 import org.graalvm.compiler.core.common.GraalOptions; 34 import org.graalvm.compiler.core.common.PermanentBailoutException; 35 import org.graalvm.compiler.core.common.RetryableBailoutException; 36 import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder; 37 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; 38 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 39 import org.graalvm.compiler.core.common.util.CompilationAlarm; 40 import org.graalvm.compiler.core.target.Backend; 41 import org.graalvm.compiler.debug.CounterKey; 42 import org.graalvm.compiler.debug.DebugCloseable; 43 import org.graalvm.compiler.debug.DebugContext; 44 import org.graalvm.compiler.debug.GraalError; 45 import org.graalvm.compiler.debug.MethodFilter; 46 import org.graalvm.compiler.debug.TimerKey; 47 import org.graalvm.compiler.lir.LIR; 48 import org.graalvm.compiler.lir.alloc.OutOfRegistersException; 49 import org.graalvm.compiler.lir.asm.CompilationResultBuilder; 50 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; 51 import org.graalvm.compiler.lir.framemap.FrameMap; 52 import org.graalvm.compiler.lir.framemap.FrameMapBuilder; 53 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 54 import org.graalvm.compiler.lir.gen.LIRGeneratorTool; 55 import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext; 56 import org.graalvm.compiler.lir.phases.LIRSuites; 57 import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext; 58 import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext; 59 import org.graalvm.compiler.nodes.StructuredGraph; 60 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 61 import org.graalvm.compiler.nodes.cfg.Block; 62 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 63 import org.graalvm.compiler.phases.OptimisticOptimizations; 64 import org.graalvm.compiler.phases.PhaseSuite; 65 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; 66 import org.graalvm.compiler.phases.tiers.HighTierContext; 67 import org.graalvm.compiler.phases.tiers.LowTierContext; 68 import org.graalvm.compiler.phases.tiers.MidTierContext; 69 import org.graalvm.compiler.phases.tiers.Suites; 70 import org.graalvm.compiler.phases.tiers.TargetProvider; 71 import org.graalvm.compiler.phases.util.Providers; 72 73 import jdk.vm.ci.code.RegisterConfig; 74 import jdk.vm.ci.code.TargetDescription; 75 import jdk.vm.ci.code.site.ConstantReference; 76 import jdk.vm.ci.code.site.DataPatch; 77 import jdk.vm.ci.meta.Assumptions; 78 import jdk.vm.ci.meta.JavaConstant; 79 import jdk.vm.ci.meta.JavaKind; 80 import jdk.vm.ci.meta.ProfilingInfo; 81 import jdk.vm.ci.meta.ResolvedJavaField; 82 import jdk.vm.ci.meta.ResolvedJavaMethod; 83 import jdk.vm.ci.meta.VMConstant; 84 85 /** 86 * Static methods for orchestrating the compilation of a {@linkplain StructuredGraph graph}. 87 */ 88 public class GraalCompiler { 89 90 private static final TimerKey CompilerTimer = DebugContext.timer("GraalCompiler").doc("Time spent in compilation (excludes code installation)."); 91 private static final TimerKey FrontEnd = DebugContext.timer("FrontEnd").doc("Time spent processing HIR."); 92 private static final TimerKey EmitLIR = DebugContext.timer("EmitLIR").doc("Time spent generating LIR from HIR."); 93 private static final TimerKey EmitCode = DebugContext.timer("EmitCode").doc("Time spent generating machine code from LIR."); 94 private static final TimerKey BackEnd = DebugContext.timer("BackEnd").doc("Time spent in EmitLIR and EmitCode."); 95 96 /** 97 * Encapsulates all the inputs to a {@linkplain GraalCompiler#compile(Request) compilation}. 98 */ 99 public static class Request<T extends CompilationResult> { 100 public final StructuredGraph graph; 101 public final ResolvedJavaMethod installedCodeOwner; 102 public final Providers providers; 103 public final Backend backend; 104 public final PhaseSuite<HighTierContext> graphBuilderSuite; 105 public final OptimisticOptimizations optimisticOpts; 106 public final ProfilingInfo profilingInfo; 107 public final Suites suites; 108 public final LIRSuites lirSuites; 109 public final T compilationResult; 110 public final CompilationResultBuilderFactory factory; 111 public final boolean verifySourcePositions; 112 113 /** 114 * @param graph the graph to be compiled 115 * @param installedCodeOwner the method the compiled code will be associated with once 116 * installed. This argument can be null. 117 * @param providers 118 * @param backend 119 * @param graphBuilderSuite 120 * @param optimisticOpts 121 * @param profilingInfo 122 * @param suites 123 * @param lirSuites 124 * @param compilationResult 125 * @param factory 126 */ 127 public Request(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend, PhaseSuite<HighTierContext> graphBuilderSuite, 128 OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult, CompilationResultBuilderFactory factory, 129 boolean verifySourcePositions) { 130 this.graph = graph; 131 this.installedCodeOwner = installedCodeOwner; 132 this.providers = providers; 133 this.backend = backend; 134 this.graphBuilderSuite = graphBuilderSuite; 135 this.optimisticOpts = optimisticOpts; 136 this.profilingInfo = profilingInfo; 137 this.suites = suites; 138 this.lirSuites = lirSuites; 139 this.compilationResult = compilationResult; 140 this.factory = factory; 141 this.verifySourcePositions = verifySourcePositions; 142 } 143 144 /** 145 * Executes this compilation request. 146 * 147 * @return the result of the compilation 148 */ 149 public T execute() { 150 return GraalCompiler.compile(this); 151 } 152 } 153 154 /** 155 * Requests compilation of a given graph. 156 * 157 * @param graph the graph to be compiled 158 * @param installedCodeOwner the method the compiled code will be associated with once 159 * installed. This argument can be null. 160 * @return the result of the compilation 161 */ 162 public static <T extends CompilationResult> T compileGraph(StructuredGraph graph, ResolvedJavaMethod installedCodeOwner, Providers providers, Backend backend, 163 PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, Suites suites, LIRSuites lirSuites, T compilationResult, 164 CompilationResultBuilderFactory factory, boolean verifySourcePositions) { 165 return compile(new Request<>(graph, installedCodeOwner, providers, backend, graphBuilderSuite, optimisticOpts, profilingInfo, suites, lirSuites, compilationResult, factory, 166 verifySourcePositions)); 167 } 168 169 /** 170 * Services a given compilation request. 171 * 172 * @return the result of the compilation 173 */ 174 @SuppressWarnings("try") 175 public static <T extends CompilationResult> T compile(Request<T> r) { 176 DebugContext debug = r.graph.getDebug(); 177 try (CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod(r.graph.getOptions())) { 178 assert !r.graph.isFrozen(); 179 try (DebugContext.Scope s0 = debug.scope("GraalCompiler", r.graph, r.providers.getCodeCache()); DebugCloseable a = CompilerTimer.start(debug)) { 180 emitFrontEnd(r.providers, r.backend, r.graph, r.graphBuilderSuite, r.optimisticOpts, r.profilingInfo, r.suites); 181 emitBackEnd(r.graph, null, r.installedCodeOwner, r.backend, r.compilationResult, r.factory, null, r.lirSuites); 182 if (r.verifySourcePositions) { 183 assert r.graph.verifySourcePositions(true); 184 } 185 } catch (Throwable e) { 186 throw debug.handle(e); 187 } 188 checkForRequestedCrash(r.graph); 189 return r.compilationResult; 190 } 191 } 192 193 /** 194 * Checks whether the {@link GraalCompilerOptions#CrashAt} option indicates that the compilation 195 * of {@code graph} should result in an exception. 196 * 197 * @param graph a graph currently being compiled 198 * @throws RuntimeException if the value of {@link GraalCompilerOptions#CrashAt} matches 199 * {@code graph.method()} or {@code graph.name} 200 */ 201 private static void checkForRequestedCrash(StructuredGraph graph) { 202 String value = GraalCompilerOptions.CrashAt.getValue(graph.getOptions()); 203 if (value != null) { 204 boolean bailout = false; 205 boolean permanentBailout = false; 206 String methodPattern = value; 207 if (value.endsWith(":Bailout")) { 208 methodPattern = value.substring(0, value.length() - ":Bailout".length()); 209 bailout = true; 210 } else if (value.endsWith(":PermanentBailout")) { 211 methodPattern = value.substring(0, value.length() - ":PermanentBailout".length()); 212 permanentBailout = true; 213 } 214 String crashLabel = null; 215 if (graph.name != null && graph.name.contains(methodPattern)) { 216 crashLabel = graph.name; 217 } 218 if (crashLabel == null) { 219 ResolvedJavaMethod method = graph.method(); 220 MethodFilter[] filters = MethodFilter.parse(methodPattern); 221 for (MethodFilter filter : filters) { 222 if (filter.matches(method)) { 223 crashLabel = method.format("%H.%n(%p)"); 224 } 225 } 226 } 227 if (crashLabel != null) { 228 if (permanentBailout) { 229 throw new PermanentBailoutException("Forced crash after compiling " + crashLabel); 230 } 231 if (bailout) { 232 throw new RetryableBailoutException("Forced crash after compiling " + crashLabel); 233 } 234 throw new RuntimeException("Forced crash after compiling " + crashLabel); 235 } 236 } 237 } 238 239 /** 240 * Builds the graph, optimizes it. 241 */ 242 @SuppressWarnings("try") 243 public static void emitFrontEnd(Providers providers, TargetProvider target, StructuredGraph graph, PhaseSuite<HighTierContext> graphBuilderSuite, OptimisticOptimizations optimisticOpts, 244 ProfilingInfo profilingInfo, Suites suites) { 245 DebugContext debug = graph.getDebug(); 246 try (DebugContext.Scope s = debug.scope("FrontEnd"); DebugCloseable a = FrontEnd.start(debug)) { 247 HighTierContext highTierContext = new HighTierContext(providers, graphBuilderSuite, optimisticOpts); 248 if (graph.start().next() == null) { 249 graphBuilderSuite.apply(graph, highTierContext); 250 new DeadCodeEliminationPhase(DeadCodeEliminationPhase.Optionality.Optional).apply(graph); 251 debug.dump(DebugContext.BASIC_LEVEL, graph, "After parsing"); 252 } else { 253 debug.dump(DebugContext.INFO_LEVEL, graph, "initial state"); 254 } 255 256 suites.getHighTier().apply(graph, highTierContext); 257 graph.maybeCompress(); 258 debug.dump(DebugContext.BASIC_LEVEL, graph, "After high tier"); 259 260 MidTierContext midTierContext = new MidTierContext(providers, target, optimisticOpts, profilingInfo); 261 suites.getMidTier().apply(graph, midTierContext); 262 graph.maybeCompress(); 263 debug.dump(DebugContext.BASIC_LEVEL, graph, "After mid tier"); 264 265 LowTierContext lowTierContext = new LowTierContext(providers, target); 266 suites.getLowTier().apply(graph, lowTierContext); 267 debug.dump(DebugContext.BASIC_LEVEL, graph, "After low tier"); 268 269 debug.dump(DebugContext.BASIC_LEVEL, graph.getLastSchedule(), "Final HIR schedule"); 270 graph.logInliningTree(); 271 } catch (Throwable e) { 272 throw debug.handle(e); 273 } finally { 274 graph.checkCancellation(); 275 } 276 } 277 278 @SuppressWarnings("try") 279 public static <T extends CompilationResult> void emitBackEnd(StructuredGraph graph, Object stub, ResolvedJavaMethod installedCodeOwner, Backend backend, T compilationResult, 280 CompilationResultBuilderFactory factory, RegisterConfig registerConfig, LIRSuites lirSuites) { 281 DebugContext debug = graph.getDebug(); 282 try (DebugContext.Scope s = debug.scope("BackEnd", graph.getLastSchedule()); DebugCloseable a = BackEnd.start(debug)) { 283 LIRGenerationResult lirGen = null; 284 lirGen = emitLIR(backend, graph, stub, registerConfig, lirSuites); 285 try (DebugContext.Scope s2 = debug.scope("CodeGen", lirGen, lirGen.getLIR())) { 286 int bytecodeSize = graph.method() == null ? 0 : graph.getBytecodeSize(); 287 compilationResult.setHasUnsafeAccess(graph.hasUnsafeAccess()); 288 emitCode(backend, graph.getAssumptions(), graph.method(), graph.getMethods(), graph.getFields(), bytecodeSize, lirGen, compilationResult, installedCodeOwner, factory); 289 } catch (Throwable e) { 290 throw debug.handle(e); 291 } 292 } catch (Throwable e) { 293 throw debug.handle(e); 294 } finally { 295 graph.checkCancellation(); 296 } 297 } 298 299 @SuppressWarnings("try") 300 public static LIRGenerationResult emitLIR(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites) { 301 String registerPressure = GraalOptions.RegisterPressure.getValue(graph.getOptions()); 302 String[] allocationRestrictedTo = registerPressure == null ? null : registerPressure.split(","); 303 try { 304 return emitLIR0(backend, graph, stub, registerConfig, lirSuites, allocationRestrictedTo); 305 } catch (OutOfRegistersException e) { 306 if (allocationRestrictedTo != null) { 307 allocationRestrictedTo = null; 308 return emitLIR0(backend, graph, stub, registerConfig, lirSuites, allocationRestrictedTo); 309 } 310 /* If the re-execution fails we convert the exception into a "hard" failure */ 311 throw new GraalError(e); 312 } finally { 313 graph.checkCancellation(); 314 } 315 } 316 317 @SuppressWarnings("try") 318 private static LIRGenerationResult emitLIR0(Backend backend, StructuredGraph graph, Object stub, RegisterConfig registerConfig, LIRSuites lirSuites, 319 String[] allocationRestrictedTo) { 320 DebugContext debug = graph.getDebug(); 321 try (DebugContext.Scope ds = debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start(debug)) { 322 assert !graph.hasValueProxies(); 323 ScheduleResult schedule = graph.getLastSchedule(); 324 Block[] blocks = schedule.getCFG().getBlocks(); 325 Block startBlock = schedule.getCFG().getStartBlock(); 326 assert startBlock != null; 327 assert startBlock.getPredecessorCount() == 0; 328 329 AbstractBlockBase<?>[] codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock); 330 AbstractBlockBase<?>[] linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock); 331 LIR lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder, graph.getOptions(), graph.getDebug()); 332 333 FrameMapBuilder frameMapBuilder = backend.newFrameMapBuilder(registerConfig); 334 LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(graph.compilationId(), lir, frameMapBuilder, graph, stub); 335 LIRGeneratorTool lirGen = backend.newLIRGenerator(lirGenRes); 336 NodeLIRBuilderTool nodeLirGen = backend.newNodeLIRBuilder(graph, lirGen); 337 338 // LIR generation 339 LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule); 340 new LIRGenerationPhase().apply(backend.getTarget(), lirGenRes, context); 341 342 try (DebugContext.Scope s = debug.scope("LIRStages", nodeLirGen, lirGenRes, lir)) { 343 // Dump LIR along with HIR (the LIR is looked up from context) 344 debug.dump(DebugContext.BASIC_LEVEL, graph.getLastSchedule(), "After LIR generation"); 345 LIRGenerationResult result = emitLowLevel(backend.getTarget(), lirGenRes, lirGen, lirSuites, backend.newRegisterAllocationConfig(registerConfig, allocationRestrictedTo)); 346 return result; 347 } catch (Throwable e) { 348 throw debug.handle(e); 349 } 350 } catch (Throwable e) { 351 throw debug.handle(e); 352 } finally { 353 graph.checkCancellation(); 354 } 355 } 356 357 protected static <T extends CompilationResult> String getCompilationUnitName(StructuredGraph graph, T compilationResult) { 358 if (compilationResult != null && compilationResult.getName() != null) { 359 return compilationResult.getName(); 360 } 361 ResolvedJavaMethod method = graph.method(); 362 if (method == null) { 363 return "<unknown>"; 364 } 365 return method.format("%H.%n(%p)"); 366 } 367 368 public static LIRGenerationResult emitLowLevel(TargetDescription target, LIRGenerationResult lirGenRes, LIRGeneratorTool lirGen, LIRSuites lirSuites, 369 RegisterAllocationConfig registerAllocationConfig) { 370 DebugContext debug = lirGenRes.getLIR().getDebug(); 371 PreAllocationOptimizationContext preAllocOptContext = new PreAllocationOptimizationContext(lirGen); 372 lirSuites.getPreAllocationOptimizationStage().apply(target, lirGenRes, preAllocOptContext); 373 debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After PreAllocationOptimizationStage"); 374 375 AllocationContext allocContext = new AllocationContext(lirGen.getSpillMoveFactory(), registerAllocationConfig); 376 lirSuites.getAllocationStage().apply(target, lirGenRes, allocContext); 377 debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After AllocationStage"); 378 379 PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext(lirGen); 380 lirSuites.getPostAllocationOptimizationStage().apply(target, lirGenRes, postAllocOptContext); 381 debug.dump(DebugContext.BASIC_LEVEL, lirGenRes.getLIR(), "After PostAllocationOptimizationStage"); 382 383 return lirGenRes; 384 } 385 386 @SuppressWarnings("try") 387 public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods, EconomicSet<ResolvedJavaField> accessedFields, 388 int bytecodeSize, LIRGenerationResult lirGenRes, 389 CompilationResult compilationResult, ResolvedJavaMethod installedCodeOwner, CompilationResultBuilderFactory factory) { 390 DebugContext debug = lirGenRes.getLIR().getDebug(); 391 try (DebugCloseable a = EmitCode.start(debug)) { 392 FrameMap frameMap = lirGenRes.getFrameMap(); 393 CompilationResultBuilder crb = backend.newCompilationResultBuilder(lirGenRes, frameMap, compilationResult, factory); 394 backend.emitCode(crb, lirGenRes.getLIR(), installedCodeOwner); 395 if (assumptions != null && !assumptions.isEmpty()) { 396 compilationResult.setAssumptions(assumptions.toArray()); 397 } 398 if (rootMethod != null) { 399 compilationResult.setMethods(rootMethod, inlinedMethods); 400 compilationResult.setFields(accessedFields); 401 compilationResult.setBytecodeSize(bytecodeSize); 402 } 403 crb.finish(); 404 if (debug.isCountEnabled()) { 405 List<DataPatch> ldp = compilationResult.getDataPatches(); 406 JavaKind[] kindValues = JavaKind.values(); 407 CounterKey[] dms = new CounterKey[kindValues.length]; 408 for (int i = 0; i < dms.length; i++) { 409 dms[i] = DebugContext.counter("DataPatches-%s", kindValues[i]); 410 } 411 412 for (DataPatch dp : ldp) { 413 JavaKind kind = JavaKind.Illegal; 414 if (dp.reference instanceof ConstantReference) { 415 VMConstant constant = ((ConstantReference) dp.reference).getConstant(); 416 if (constant instanceof JavaConstant) { 417 kind = ((JavaConstant) constant).getJavaKind(); 418 } 419 } 420 dms[kind.ordinal()].add(debug, 1); 421 } 422 423 DebugContext.counter("CompilationResults").increment(debug); 424 DebugContext.counter("CodeBytesEmitted").add(debug, compilationResult.getTargetCodeSize()); 425 DebugContext.counter("InfopointsEmitted").add(debug, compilationResult.getInfopoints().size()); 426 DebugContext.counter("DataPatches").add(debug, ldp.size()); 427 DebugContext.counter("ExceptionHandlersEmitted").add(debug, compilationResult.getExceptionHandlers().size()); 428 } 429 430 debug.dump(DebugContext.BASIC_LEVEL, compilationResult, "After code generation"); 431 } 432 } 433 }