1 /* 2 * Copyright (c) 2013, 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.lir.asm; 24 25 import static jdk.vm.ci.code.ValueUtil.asStackSlot; 26 import static jdk.vm.ci.code.ValueUtil.isStackSlot; 27 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant; 28 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant; 29 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import java.util.function.Consumer; 34 35 import org.graalvm.compiler.asm.AbstractAddress; 36 import org.graalvm.compiler.asm.Assembler; 37 import org.graalvm.compiler.code.CompilationResult; 38 import org.graalvm.compiler.code.CompilationResult.CodeAnnotation; 39 import org.graalvm.compiler.code.DataSection.Data; 40 import org.graalvm.compiler.code.DataSection.RawData; 41 import org.graalvm.compiler.core.common.NumUtil; 42 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 43 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; 44 import org.graalvm.compiler.core.common.type.DataPointerConstant; 45 import org.graalvm.compiler.debug.Assertions; 46 import org.graalvm.compiler.debug.DebugContext; 47 import org.graalvm.compiler.debug.GraalError; 48 import org.graalvm.compiler.graph.NodeSourcePosition; 49 import org.graalvm.compiler.lir.LIR; 50 import org.graalvm.compiler.lir.LIRFrameState; 51 import org.graalvm.compiler.lir.LIRInstruction; 52 import org.graalvm.compiler.lir.LabelRef; 53 import org.graalvm.compiler.lir.framemap.FrameMap; 54 import org.graalvm.compiler.options.Option; 55 import org.graalvm.compiler.options.OptionKey; 56 import org.graalvm.compiler.options.OptionType; 57 import org.graalvm.compiler.options.OptionValues; 58 import org.graalvm.util.EconomicMap; 59 import org.graalvm.util.Equivalence; 60 61 import jdk.vm.ci.code.CodeCacheProvider; 62 import jdk.vm.ci.code.DebugInfo; 63 import jdk.vm.ci.code.StackSlot; 64 import jdk.vm.ci.code.TargetDescription; 65 import jdk.vm.ci.code.site.ConstantReference; 66 import jdk.vm.ci.code.site.DataSectionReference; 67 import jdk.vm.ci.code.site.InfopointReason; 68 import jdk.vm.ci.code.site.Mark; 69 import jdk.vm.ci.meta.Constant; 70 import jdk.vm.ci.meta.InvokeTarget; 71 import jdk.vm.ci.meta.JavaConstant; 72 import jdk.vm.ci.meta.JavaKind; 73 import jdk.vm.ci.meta.VMConstant; 74 import jdk.vm.ci.meta.Value; 75 76 /** 77 * Fills in a {@link CompilationResult} as its code is being assembled. 78 * 79 * @see CompilationResultBuilderFactory 80 */ 81 public class CompilationResultBuilder { 82 83 // @formatter:off 84 @Option(help = "Include the LIR as comments with the final assembly.", type = OptionType.Debug) 85 public static final OptionKey<Boolean> PrintLIRWithAssembly = new OptionKey<>(false); 86 // @formatter:on 87 88 private static class ExceptionInfo { 89 90 public final int codeOffset; 91 public final LabelRef exceptionEdge; 92 93 ExceptionInfo(int pcOffset, LabelRef exceptionEdge) { 94 this.codeOffset = pcOffset; 95 this.exceptionEdge = exceptionEdge; 96 } 97 } 98 99 /** 100 * Wrapper for a code annotation that was produced by the {@link Assembler}. 101 */ 102 public static final class AssemblerAnnotation extends CodeAnnotation { 103 104 public final Assembler.CodeAnnotation assemblerCodeAnnotation; 105 106 public AssemblerAnnotation(Assembler.CodeAnnotation assemblerCodeAnnotation) { 107 super(assemblerCodeAnnotation.instructionPosition); 108 this.assemblerCodeAnnotation = assemblerCodeAnnotation; 109 } 110 111 @Override 112 public boolean equals(Object obj) { 113 return this == obj; 114 } 115 116 @Override 117 public String toString() { 118 return assemblerCodeAnnotation.toString(); 119 } 120 } 121 122 public final Assembler asm; 123 public final DataBuilder dataBuilder; 124 public final CompilationResult compilationResult; 125 public final TargetDescription target; 126 public final CodeCacheProvider codeCache; 127 public final ForeignCallsProvider foreignCalls; 128 public final FrameMap frameMap; 129 130 /** 131 * The LIR for which code is being generated. 132 */ 133 private LIR lir; 134 135 /** 136 * The index of the block currently being emitted. 137 */ 138 private int currentBlockIndex; 139 140 /** 141 * The object that emits code for managing a method's frame. 142 */ 143 public final FrameContext frameContext; 144 145 private List<ExceptionInfo> exceptionInfoList; 146 147 private final OptionValues options; 148 private final DebugContext debug; 149 private final EconomicMap<Constant, Data> dataCache; 150 151 private Consumer<LIRInstruction> beforeOp; 152 private Consumer<LIRInstruction> afterOp; 153 154 public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext, 155 OptionValues options, DebugContext debug, CompilationResult compilationResult) { 156 this(codeCache, foreignCalls, frameMap, asm, dataBuilder, frameContext, options, debug, compilationResult, EconomicMap.create(Equivalence.DEFAULT)); 157 } 158 159 public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext, 160 OptionValues options, DebugContext debug, CompilationResult compilationResult, EconomicMap<Constant, Data> dataCache) { 161 this.target = codeCache.getTarget(); 162 this.codeCache = codeCache; 163 this.foreignCalls = foreignCalls; 164 this.frameMap = frameMap; 165 this.asm = asm; 166 this.dataBuilder = dataBuilder; 167 this.compilationResult = compilationResult; 168 this.frameContext = frameContext; 169 this.options = options; 170 this.debug = debug; 171 assert frameContext != null; 172 this.dataCache = dataCache; 173 174 if (dataBuilder.needDetailedPatchingInformation() || Assertions.assertionsEnabled()) { 175 /* 176 * Always enabled in debug mode, even when the VM does not request detailed information, 177 * to increase test coverage. 178 */ 179 asm.setCodePatchingAnnotationConsumer(assemblerCodeAnnotation -> compilationResult.addAnnotation(new AssemblerAnnotation(assemblerCodeAnnotation))); 180 } 181 } 182 183 public void setTotalFrameSize(int frameSize) { 184 compilationResult.setTotalFrameSize(frameSize); 185 } 186 187 public void setMaxInterpreterFrameSize(int maxInterpreterFrameSize) { 188 compilationResult.setMaxInterpreterFrameSize(maxInterpreterFrameSize); 189 } 190 191 public Mark recordMark(Object id) { 192 return compilationResult.recordMark(asm.position(), id); 193 } 194 195 public void blockComment(String s) { 196 compilationResult.addAnnotation(new CompilationResult.CodeComment(asm.position(), s)); 197 } 198 199 /** 200 * Sets the {@linkplain CompilationResult#setTargetCode(byte[], int) code} and 201 * {@linkplain CompilationResult#recordExceptionHandler(int, int) exception handler} fields of 202 * the compilation result and then {@linkplain #closeCompilationResult() closes} it. 203 */ 204 public void finish() { 205 int position = asm.position(); 206 compilationResult.setTargetCode(asm.close(false), position); 207 208 // Record exception handlers if they exist 209 if (exceptionInfoList != null) { 210 for (ExceptionInfo ei : exceptionInfoList) { 211 int codeOffset = ei.codeOffset; 212 compilationResult.recordExceptionHandler(codeOffset, ei.exceptionEdge.label().position()); 213 } 214 } 215 closeCompilationResult(); 216 } 217 218 /** 219 * Calls {@link CompilationResult#close()} on {@link #compilationResult}. 220 */ 221 protected void closeCompilationResult() { 222 compilationResult.close(); 223 } 224 225 public void recordExceptionHandlers(int pcOffset, LIRFrameState info) { 226 if (info != null) { 227 if (info.exceptionEdge != null) { 228 if (exceptionInfoList == null) { 229 exceptionInfoList = new ArrayList<>(4); 230 } 231 exceptionInfoList.add(new ExceptionInfo(pcOffset, info.exceptionEdge)); 232 } 233 } 234 } 235 236 public void recordImplicitException(int pcOffset, LIRFrameState info) { 237 compilationResult.recordInfopoint(pcOffset, info.debugInfo(), InfopointReason.IMPLICIT_EXCEPTION); 238 assert info.exceptionEdge == null; 239 } 240 241 public void recordDirectCall(int posBefore, int posAfter, InvokeTarget callTarget, LIRFrameState info) { 242 DebugInfo debugInfo = info != null ? info.debugInfo() : null; 243 compilationResult.recordCall(posBefore, posAfter - posBefore, callTarget, debugInfo, true); 244 } 245 246 public void recordIndirectCall(int posBefore, int posAfter, InvokeTarget callTarget, LIRFrameState info) { 247 DebugInfo debugInfo = info != null ? info.debugInfo() : null; 248 compilationResult.recordCall(posBefore, posAfter - posBefore, callTarget, debugInfo, false); 249 } 250 251 public void recordInfopoint(int pos, LIRFrameState info, InfopointReason reason) { 252 // infopoints always need debug info 253 DebugInfo debugInfo = info.debugInfo(); 254 recordInfopoint(pos, debugInfo, reason); 255 } 256 257 public void recordInfopoint(int pos, DebugInfo debugInfo, InfopointReason reason) { 258 compilationResult.recordInfopoint(pos, debugInfo, reason); 259 } 260 261 public void recordSourceMapping(int pcOffset, int endPcOffset, NodeSourcePosition sourcePosition) { 262 compilationResult.recordSourceMapping(pcOffset, endPcOffset, sourcePosition); 263 } 264 265 public void recordInlineDataInCode(Constant data) { 266 assert data != null; 267 int pos = asm.position(); 268 debug.log("Inline data in code: pos = %d, data = %s", pos, data); 269 if (data instanceof VMConstant) { 270 compilationResult.recordDataPatch(pos, new ConstantReference((VMConstant) data)); 271 } 272 } 273 274 public void recordInlineDataInCodeWithNote(Constant data, Object note) { 275 assert data != null; 276 int pos = asm.position(); 277 debug.log("Inline data in code: pos = %d, data = %s, note = %s", pos, data, note); 278 if (data instanceof VMConstant) { 279 compilationResult.recordDataPatchWithNote(pos, new ConstantReference((VMConstant) data), note); 280 } 281 } 282 283 public AbstractAddress recordDataSectionReference(Data data) { 284 assert data != null; 285 DataSectionReference reference = compilationResult.getDataSection().insertData(data); 286 int instructionStart = asm.position(); 287 compilationResult.recordDataPatch(instructionStart, reference); 288 return asm.getPlaceholder(instructionStart); 289 } 290 291 public AbstractAddress recordDataReferenceInCode(DataPointerConstant constant) { 292 return recordDataReferenceInCode(constant, constant.getAlignment()); 293 } 294 295 public AbstractAddress recordDataReferenceInCode(Constant constant, int alignment) { 296 assert constant != null; 297 debug.log("Constant reference in code: pos = %d, data = %s", asm.position(), constant); 298 Data data = dataCache.get(constant); 299 if (data == null) { 300 data = dataBuilder.createDataItem(constant); 301 dataCache.put(constant, data); 302 } 303 data.updateAlignment(alignment); 304 return recordDataSectionReference(data); 305 } 306 307 public AbstractAddress recordDataReferenceInCode(byte[] data, int alignment) { 308 assert data != null; 309 if (debug.isLogEnabled()) { 310 debug.log("Data reference in code: pos = %d, data = %s", asm.position(), Arrays.toString(data)); 311 } 312 return recordDataSectionReference(new RawData(data, alignment)); 313 } 314 315 /** 316 * Returns the integer value of any constant that can be represented by a 32-bit integer value, 317 * including long constants that fit into the 32-bit range. 318 */ 319 public int asIntConst(Value value) { 320 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind().isNumericInteger(); 321 JavaConstant constant = asJavaConstant(value); 322 long c = constant.asLong(); 323 if (!NumUtil.isInt(c)) { 324 throw GraalError.shouldNotReachHere(); 325 } 326 return (int) c; 327 } 328 329 /** 330 * Returns the float value of any constant that can be represented by a 32-bit float value. 331 */ 332 public float asFloatConst(Value value) { 333 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Float; 334 JavaConstant constant = asJavaConstant(value); 335 return constant.asFloat(); 336 } 337 338 /** 339 * Returns the long value of any constant that can be represented by a 64-bit long value. 340 */ 341 public long asLongConst(Value value) { 342 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Long; 343 JavaConstant constant = asJavaConstant(value); 344 return constant.asLong(); 345 } 346 347 /** 348 * Returns the double value of any constant that can be represented by a 64-bit float value. 349 */ 350 public double asDoubleConst(Value value) { 351 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Double; 352 JavaConstant constant = asJavaConstant(value); 353 return constant.asDouble(); 354 } 355 356 /** 357 * Returns the address of a float constant that is embedded as a data reference into the code. 358 */ 359 public AbstractAddress asFloatConstRef(JavaConstant value) { 360 return asFloatConstRef(value, 4); 361 } 362 363 public AbstractAddress asFloatConstRef(JavaConstant value, int alignment) { 364 assert value.getJavaKind() == JavaKind.Float; 365 return recordDataReferenceInCode(value, alignment); 366 } 367 368 /** 369 * Returns the address of a double constant that is embedded as a data reference into the code. 370 */ 371 public AbstractAddress asDoubleConstRef(JavaConstant value) { 372 return asDoubleConstRef(value, 8); 373 } 374 375 public AbstractAddress asDoubleConstRef(JavaConstant value, int alignment) { 376 assert value.getJavaKind() == JavaKind.Double; 377 return recordDataReferenceInCode(value, alignment); 378 } 379 380 /** 381 * Returns the address of a long constant that is embedded as a data reference into the code. 382 */ 383 public AbstractAddress asLongConstRef(JavaConstant value) { 384 assert value.getJavaKind() == JavaKind.Long; 385 return recordDataReferenceInCode(value, 8); 386 } 387 388 /** 389 * Returns the address of an object constant that is embedded as a data reference into the code. 390 */ 391 public AbstractAddress asObjectConstRef(JavaConstant value) { 392 assert value.getJavaKind() == JavaKind.Object; 393 return recordDataReferenceInCode(value, 8); 394 } 395 396 public AbstractAddress asByteAddr(Value value) { 397 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Byte.getByteCount(); 398 return asAddress(value); 399 } 400 401 public AbstractAddress asShortAddr(Value value) { 402 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Short.getByteCount(); 403 return asAddress(value); 404 } 405 406 public AbstractAddress asIntAddr(Value value) { 407 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Int.getByteCount(); 408 return asAddress(value); 409 } 410 411 public AbstractAddress asLongAddr(Value value) { 412 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Long.getByteCount(); 413 return asAddress(value); 414 } 415 416 public AbstractAddress asFloatAddr(Value value) { 417 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Float.getByteCount(); 418 return asAddress(value); 419 } 420 421 public AbstractAddress asDoubleAddr(Value value) { 422 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Double.getByteCount(); 423 return asAddress(value); 424 } 425 426 public AbstractAddress asAddress(Value value) { 427 assert isStackSlot(value); 428 StackSlot slot = asStackSlot(value); 429 return asm.makeAddress(frameMap.getRegisterConfig().getFrameRegister(), frameMap.offsetForStackSlot(slot)); 430 } 431 432 /** 433 * Determines if a given edge from the block currently being emitted goes to its lexical 434 * successor. 435 */ 436 public boolean isSuccessorEdge(LabelRef edge) { 437 assert lir != null; 438 AbstractBlockBase<?>[] order = lir.codeEmittingOrder(); 439 assert order[currentBlockIndex] == edge.getSourceBlock(); 440 AbstractBlockBase<?> nextBlock = LIR.getNextBlock(order, currentBlockIndex); 441 return nextBlock == edge.getTargetBlock(); 442 } 443 444 /** 445 * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}. 446 */ 447 public void emit(@SuppressWarnings("hiding") LIR lir) { 448 assert this.lir == null; 449 assert currentBlockIndex == 0; 450 this.lir = lir; 451 this.currentBlockIndex = 0; 452 frameContext.enter(this); 453 for (AbstractBlockBase<?> b : lir.codeEmittingOrder()) { 454 assert (b == null && lir.codeEmittingOrder()[currentBlockIndex] == null) || lir.codeEmittingOrder()[currentBlockIndex].equals(b); 455 emitBlock(b); 456 currentBlockIndex++; 457 } 458 this.lir = null; 459 this.currentBlockIndex = 0; 460 } 461 462 private void emitBlock(AbstractBlockBase<?> block) { 463 if (block == null) { 464 return; 465 } 466 boolean emitComment = debug.isDumpEnabled(DebugContext.BASIC_LEVEL) || PrintLIRWithAssembly.getValue(getOptions()); 467 if (emitComment) { 468 blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); 469 } 470 471 for (LIRInstruction op : lir.getLIRforBlock(block)) { 472 if (emitComment) { 473 blockComment(String.format("%d %s", op.id(), op)); 474 } 475 476 try { 477 if (beforeOp != null) { 478 beforeOp.accept(op); 479 } 480 emitOp(this, op); 481 if (afterOp != null) { 482 afterOp.accept(op); 483 } 484 } catch (GraalError e) { 485 throw e.addContext("lir instruction", block + "@" + op.id() + " " + op.getClass().getName() + " " + op + "\n" + Arrays.toString(lir.codeEmittingOrder())); 486 } 487 } 488 } 489 490 private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) { 491 try { 492 int start = crb.asm.position(); 493 op.emitCode(crb); 494 if (op.getPosition() != null) { 495 crb.recordSourceMapping(start, crb.asm.position(), op.getPosition()); 496 } 497 } catch (AssertionError t) { 498 throw new GraalError(t); 499 } catch (RuntimeException t) { 500 throw new GraalError(t); 501 } 502 } 503 504 public void resetForEmittingCode() { 505 asm.reset(); 506 compilationResult.resetForEmittingCode(); 507 if (exceptionInfoList != null) { 508 exceptionInfoList.clear(); 509 } 510 if (dataCache != null) { 511 dataCache.clear(); 512 } 513 } 514 515 public void setOpCallback(Consumer<LIRInstruction> beforeOp, Consumer<LIRInstruction> afterOp) { 516 this.beforeOp = beforeOp; 517 this.afterOp = afterOp; 518 } 519 520 public OptionValues getOptions() { 521 return options; 522 } 523 }