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