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.core.common.NumUtil; 38 import org.graalvm.compiler.code.CompilationResult; 39 import org.graalvm.compiler.code.CompilationResult.CodeAnnotation; 40 import org.graalvm.compiler.code.DataSection.Data; 41 import org.graalvm.compiler.code.DataSection.RawData; 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 protected LIR lir; 134 135 /** 136 * The index of the block currently being emitted. 137 */ 138 protected 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 * Notifies this object of a branch instruction at offset {@code pos} in the code. 317 * 318 * @param isNegated negation status of the branch's condition. 319 */ 320 @SuppressWarnings("unused") 321 public void recordBranch(int pos, boolean isNegated) { 322 } 323 324 /** 325 * Returns the integer value of any constant that can be represented by a 32-bit integer value, 326 * including long constants that fit into the 32-bit range. 327 */ 328 public int asIntConst(Value value) { 329 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind().isNumericInteger(); 330 JavaConstant constant = asJavaConstant(value); 331 long c = constant.asLong(); 332 if (!NumUtil.isInt(c)) { 333 throw GraalError.shouldNotReachHere(); 334 } 335 return (int) c; 336 } 337 338 /** 339 * Returns the float value of any constant that can be represented by a 32-bit float value. 340 */ 341 public float asFloatConst(Value value) { 342 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Float; 343 JavaConstant constant = asJavaConstant(value); 344 return constant.asFloat(); 345 } 346 347 /** 348 * Returns the long value of any constant that can be represented by a 64-bit long value. 349 */ 350 public long asLongConst(Value value) { 351 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Long; 352 JavaConstant constant = asJavaConstant(value); 353 return constant.asLong(); 354 } 355 356 /** 357 * Returns the double value of any constant that can be represented by a 64-bit float value. 358 */ 359 public double asDoubleConst(Value value) { 360 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Double; 361 JavaConstant constant = asJavaConstant(value); 362 return constant.asDouble(); 363 } 364 365 /** 366 * Returns the address of a float constant that is embedded as a data reference into the code. 367 */ 368 public AbstractAddress asFloatConstRef(JavaConstant value) { 369 return asFloatConstRef(value, 4); 370 } 371 372 public AbstractAddress asFloatConstRef(JavaConstant value, int alignment) { 373 assert value.getJavaKind() == JavaKind.Float; 374 return recordDataReferenceInCode(value, alignment); 375 } 376 377 /** 378 * Returns the address of a double constant that is embedded as a data reference into the code. 379 */ 380 public AbstractAddress asDoubleConstRef(JavaConstant value) { 381 return asDoubleConstRef(value, 8); 382 } 383 384 public AbstractAddress asDoubleConstRef(JavaConstant value, int alignment) { 385 assert value.getJavaKind() == JavaKind.Double; 386 return recordDataReferenceInCode(value, alignment); 387 } 388 389 /** 390 * Returns the address of a long constant that is embedded as a data reference into the code. 391 */ 392 public AbstractAddress asLongConstRef(JavaConstant value) { 393 assert value.getJavaKind() == JavaKind.Long; 394 return recordDataReferenceInCode(value, 8); 395 } 396 397 /** 398 * Returns the address of an object constant that is embedded as a data reference into the code. 399 */ 400 public AbstractAddress asObjectConstRef(JavaConstant value) { 401 assert value.getJavaKind() == JavaKind.Object; 402 return recordDataReferenceInCode(value, 8); 403 } 404 405 public AbstractAddress asByteAddr(Value value) { 406 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Byte.getByteCount(); 407 return asAddress(value); 408 } 409 410 public AbstractAddress asShortAddr(Value value) { 411 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Short.getByteCount(); 412 return asAddress(value); 413 } 414 415 public AbstractAddress asIntAddr(Value value) { 416 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Int.getByteCount(); 417 return asAddress(value); 418 } 419 420 public AbstractAddress asLongAddr(Value value) { 421 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Long.getByteCount(); 422 return asAddress(value); 423 } 424 425 public AbstractAddress asFloatAddr(Value value) { 426 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Float.getByteCount(); 427 return asAddress(value); 428 } 429 430 public AbstractAddress asDoubleAddr(Value value) { 431 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Double.getByteCount(); 432 return asAddress(value); 433 } 434 435 public AbstractAddress asAddress(Value value) { 436 assert isStackSlot(value); 437 StackSlot slot = asStackSlot(value); 438 return asm.makeAddress(frameMap.getRegisterConfig().getFrameRegister(), frameMap.offsetForStackSlot(slot)); 439 } 440 441 /** 442 * Determines if a given edge from the block currently being emitted goes to its lexical 443 * successor. 444 */ 445 public boolean isSuccessorEdge(LabelRef edge) { 446 assert lir != null; 447 AbstractBlockBase<?>[] order = lir.codeEmittingOrder(); 448 assert order[currentBlockIndex] == edge.getSourceBlock(); 449 AbstractBlockBase<?> nextBlock = LIR.getNextBlock(order, currentBlockIndex); 450 return nextBlock == edge.getTargetBlock(); 451 } 452 453 /** 454 * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}. 455 */ 456 public void emit(@SuppressWarnings("hiding") LIR lir) { 457 assert this.lir == null; 458 assert currentBlockIndex == 0; 459 this.lir = lir; 460 this.currentBlockIndex = 0; 461 frameContext.enter(this); 462 for (AbstractBlockBase<?> b : lir.codeEmittingOrder()) { 463 assert (b == null && lir.codeEmittingOrder()[currentBlockIndex] == null) || lir.codeEmittingOrder()[currentBlockIndex].equals(b); 464 emitBlock(b); 465 currentBlockIndex++; 466 } 467 this.lir = null; 468 this.currentBlockIndex = 0; 469 } 470 471 private void emitBlock(AbstractBlockBase<?> block) { 472 if (block == null) { 473 return; 474 } 475 boolean emitComment = debug.isDumpEnabled(DebugContext.BASIC_LEVEL) || PrintLIRWithAssembly.getValue(getOptions()); 476 if (emitComment) { 477 blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); 478 } 479 480 for (LIRInstruction op : lir.getLIRforBlock(block)) { 481 if (emitComment) { 482 blockComment(String.format("%d %s", op.id(), op)); 483 } 484 485 try { 486 if (beforeOp != null) { 487 beforeOp.accept(op); 488 } 489 emitOp(this, op); 490 if (afterOp != null) { 491 afterOp.accept(op); 492 } 493 } catch (GraalError e) { 494 throw e.addContext("lir instruction", block + "@" + op.id() + " " + op.getClass().getName() + " " + op + "\n" + Arrays.toString(lir.codeEmittingOrder())); 495 } 496 } 497 } 498 499 private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) { 500 try { 501 int start = crb.asm.position(); 502 op.emitCode(crb); 503 if (op.getPosition() != null) { 504 crb.recordSourceMapping(start, crb.asm.position(), op.getPosition()); 505 } 506 } catch (AssertionError t) { 507 throw new GraalError(t); 508 } catch (RuntimeException t) { 509 throw new GraalError(t); 510 } 511 } 512 513 public void resetForEmittingCode() { 514 asm.reset(); 515 compilationResult.resetForEmittingCode(); 516 if (exceptionInfoList != null) { 517 exceptionInfoList.clear(); 518 } 519 if (dataCache != null) { 520 dataCache.clear(); 521 } 522 } 523 524 public void setOpCallback(Consumer<LIRInstruction> beforeOp, Consumer<LIRInstruction> afterOp) { 525 this.beforeOp = beforeOp; 526 this.afterOp = afterOp; 527 } 528 529 public OptionValues getOptions() { 530 return options; 531 } 532 533 }