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.Debug; 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 EconomicMap<Constant, Data> dataCache; 149 150 private Consumer<LIRInstruction> beforeOp; 151 private Consumer<LIRInstruction> afterOp; 152 153 public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext, 154 OptionValues options, CompilationResult compilationResult) { 155 this(codeCache, foreignCalls, frameMap, asm, dataBuilder, frameContext, options, compilationResult, EconomicMap.create(Equivalence.DEFAULT)); 156 } 157 158 public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, DataBuilder dataBuilder, FrameContext frameContext, 159 OptionValues options, CompilationResult compilationResult, EconomicMap<Constant, Data> dataCache) { 160 this.target = codeCache.getTarget(); 161 this.codeCache = codeCache; 162 this.foreignCalls = foreignCalls; 163 this.frameMap = frameMap; 164 this.asm = asm; 165 this.dataBuilder = dataBuilder; 166 this.compilationResult = compilationResult; 167 this.frameContext = frameContext; 168 this.options = options; 169 assert frameContext != null; 170 this.dataCache = dataCache; 171 172 if (dataBuilder.needDetailedPatchingInformation() || Assertions.ENABLED) { 173 /* 174 * Always enabled in debug mode, even when the VM does not request detailed information, 175 * to increase test coverage. 176 */ 177 asm.setCodePatchingAnnotationConsumer(assemblerCodeAnnotation -> compilationResult.addAnnotation(new AssemblerAnnotation(assemblerCodeAnnotation))); 178 } 179 } 180 181 public void setTotalFrameSize(int frameSize) { 182 compilationResult.setTotalFrameSize(frameSize); 183 } 184 185 public void setMaxInterpreterFrameSize(int maxInterpreterFrameSize) { 186 compilationResult.setMaxInterpreterFrameSize(maxInterpreterFrameSize); 187 } 188 189 public Mark recordMark(Object id) { 190 return compilationResult.recordMark(asm.position(), id); 191 } 192 193 public void blockComment(String s) { 194 compilationResult.addAnnotation(new CompilationResult.CodeComment(asm.position(), s)); 195 } 196 197 /** 198 * Sets the {@linkplain CompilationResult#setTargetCode(byte[], int) code} and 199 * {@linkplain CompilationResult#recordExceptionHandler(int, int) exception handler} fields of 200 * the compilation result and then {@linkplain #closeCompilationResult() closes} it. 201 */ 202 public void finish() { 203 int position = asm.position(); 204 compilationResult.setTargetCode(asm.close(false), position); 205 206 // Record exception handlers if they exist 207 if (exceptionInfoList != null) { 208 for (ExceptionInfo ei : exceptionInfoList) { 209 int codeOffset = ei.codeOffset; 210 compilationResult.recordExceptionHandler(codeOffset, ei.exceptionEdge.label().position()); 211 } 212 } 213 closeCompilationResult(); 214 } 215 216 /** 217 * Calls {@link CompilationResult#close()} on {@link #compilationResult}. 218 */ 219 protected void closeCompilationResult() { 220 compilationResult.close(); 221 } 222 223 public void recordExceptionHandlers(int pcOffset, LIRFrameState info) { 224 if (info != null) { 225 if (info.exceptionEdge != null) { 226 if (exceptionInfoList == null) { 227 exceptionInfoList = new ArrayList<>(4); 228 } 229 exceptionInfoList.add(new ExceptionInfo(pcOffset, info.exceptionEdge)); 230 } 231 } 232 } 233 234 public void recordImplicitException(int pcOffset, LIRFrameState info) { 235 compilationResult.recordInfopoint(pcOffset, info.debugInfo(), InfopointReason.IMPLICIT_EXCEPTION); 236 assert info.exceptionEdge == null; 237 } 238 239 public void recordDirectCall(int posBefore, int posAfter, InvokeTarget callTarget, LIRFrameState info) { 240 DebugInfo debugInfo = info != null ? info.debugInfo() : null; 241 compilationResult.recordCall(posBefore, posAfter - posBefore, callTarget, debugInfo, true); 242 } 243 244 public void recordIndirectCall(int posBefore, int posAfter, InvokeTarget callTarget, LIRFrameState info) { 245 DebugInfo debugInfo = info != null ? info.debugInfo() : null; 246 compilationResult.recordCall(posBefore, posAfter - posBefore, callTarget, debugInfo, false); 247 } 248 249 public void recordInfopoint(int pos, LIRFrameState info, InfopointReason reason) { 250 // infopoints always need debug info 251 DebugInfo debugInfo = info.debugInfo(); 252 recordInfopoint(pos, debugInfo, reason); 253 } 254 255 public void recordInfopoint(int pos, DebugInfo debugInfo, InfopointReason reason) { 256 compilationResult.recordInfopoint(pos, debugInfo, reason); 257 } 258 259 public void recordSourceMapping(int pcOffset, int endPcOffset, NodeSourcePosition sourcePosition) { 260 compilationResult.recordSourceMapping(pcOffset, endPcOffset, sourcePosition); 261 } 262 263 public void recordInlineDataInCode(Constant data) { 264 assert data != null; 265 int pos = asm.position(); 266 Debug.log("Inline data in code: pos = %d, data = %s", pos, data); 267 if (data instanceof VMConstant) { 268 compilationResult.recordDataPatch(pos, new ConstantReference((VMConstant) data)); 269 } 270 } 271 272 public void recordInlineDataInCodeWithNote(Constant data, Object note) { 273 assert data != null; 274 int pos = asm.position(); 275 Debug.log("Inline data in code: pos = %d, data = %s, note = %s", pos, data, note); 276 if (data instanceof VMConstant) { 277 compilationResult.recordDataPatchWithNote(pos, new ConstantReference((VMConstant) data), note); 278 } 279 } 280 281 public AbstractAddress recordDataSectionReference(Data data) { 282 assert data != null; 283 DataSectionReference reference = compilationResult.getDataSection().insertData(data); 284 int instructionStart = asm.position(); 285 compilationResult.recordDataPatch(instructionStart, reference); 286 return asm.getPlaceholder(instructionStart); 287 } 288 289 public AbstractAddress recordDataReferenceInCode(DataPointerConstant constant) { 290 return recordDataReferenceInCode(constant, constant.getAlignment()); 291 } 292 293 public AbstractAddress recordDataReferenceInCode(Constant constant, int alignment) { 294 assert constant != null; 295 Debug.log("Constant reference in code: pos = %d, data = %s", asm.position(), constant); 296 Data data = dataCache.get(constant); 297 if (data == null) { 298 data = dataBuilder.createDataItem(constant); 299 dataCache.put(constant, data); 300 } 301 data.updateAlignment(alignment); 302 return recordDataSectionReference(data); 303 } 304 305 public AbstractAddress recordDataReferenceInCode(byte[] data, int alignment) { 306 assert data != null; 307 if (Debug.isLogEnabled()) { 308 Debug.log("Data reference in code: pos = %d, data = %s", asm.position(), Arrays.toString(data)); 309 } 310 return recordDataSectionReference(new RawData(data, alignment)); 311 } 312 313 /** 314 * Returns the integer value of any constant that can be represented by a 32-bit integer value, 315 * including long constants that fit into the 32-bit range. 316 */ 317 public int asIntConst(Value value) { 318 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind().isNumericInteger(); 319 JavaConstant constant = asJavaConstant(value); 320 long c = constant.asLong(); 321 if (!NumUtil.isInt(c)) { 322 throw GraalError.shouldNotReachHere(); 323 } 324 return (int) c; 325 } 326 327 /** 328 * Returns the float value of any constant that can be represented by a 32-bit float value. 329 */ 330 public float asFloatConst(Value value) { 331 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Float; 332 JavaConstant constant = asJavaConstant(value); 333 return constant.asFloat(); 334 } 335 336 /** 337 * Returns the long value of any constant that can be represented by a 64-bit long value. 338 */ 339 public long asLongConst(Value value) { 340 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Long; 341 JavaConstant constant = asJavaConstant(value); 342 return constant.asLong(); 343 } 344 345 /** 346 * Returns the double value of any constant that can be represented by a 64-bit float value. 347 */ 348 public double asDoubleConst(Value value) { 349 assert isJavaConstant(value) && asJavaConstant(value).getJavaKind() == JavaKind.Double; 350 JavaConstant constant = asJavaConstant(value); 351 return constant.asDouble(); 352 } 353 354 /** 355 * Returns the address of a float constant that is embedded as a data reference into the code. 356 */ 357 public AbstractAddress asFloatConstRef(JavaConstant value) { 358 return asFloatConstRef(value, 4); 359 } 360 361 public AbstractAddress asFloatConstRef(JavaConstant value, int alignment) { 362 assert value.getJavaKind() == JavaKind.Float; 363 return recordDataReferenceInCode(value, alignment); 364 } 365 366 /** 367 * Returns the address of a double constant that is embedded as a data reference into the code. 368 */ 369 public AbstractAddress asDoubleConstRef(JavaConstant value) { 370 return asDoubleConstRef(value, 8); 371 } 372 373 public AbstractAddress asDoubleConstRef(JavaConstant value, int alignment) { 374 assert value.getJavaKind() == JavaKind.Double; 375 return recordDataReferenceInCode(value, alignment); 376 } 377 378 /** 379 * Returns the address of a long constant that is embedded as a data reference into the code. 380 */ 381 public AbstractAddress asLongConstRef(JavaConstant value) { 382 assert value.getJavaKind() == JavaKind.Long; 383 return recordDataReferenceInCode(value, 8); 384 } 385 386 /** 387 * Returns the address of an object constant that is embedded as a data reference into the code. 388 */ 389 public AbstractAddress asObjectConstRef(JavaConstant value) { 390 assert value.getJavaKind() == JavaKind.Object; 391 return recordDataReferenceInCode(value, 8); 392 } 393 394 public AbstractAddress asByteAddr(Value value) { 395 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Byte.getByteCount(); 396 return asAddress(value); 397 } 398 399 public AbstractAddress asShortAddr(Value value) { 400 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Short.getByteCount(); 401 return asAddress(value); 402 } 403 404 public AbstractAddress asIntAddr(Value value) { 405 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Int.getByteCount(); 406 return asAddress(value); 407 } 408 409 public AbstractAddress asLongAddr(Value value) { 410 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Long.getByteCount(); 411 return asAddress(value); 412 } 413 414 public AbstractAddress asFloatAddr(Value value) { 415 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Float.getByteCount(); 416 return asAddress(value); 417 } 418 419 public AbstractAddress asDoubleAddr(Value value) { 420 assert value.getPlatformKind().getSizeInBytes() >= JavaKind.Double.getByteCount(); 421 return asAddress(value); 422 } 423 424 public AbstractAddress asAddress(Value value) { 425 assert isStackSlot(value); 426 StackSlot slot = asStackSlot(value); 427 return asm.makeAddress(frameMap.getRegisterConfig().getFrameRegister(), frameMap.offsetForStackSlot(slot)); 428 } 429 430 /** 431 * Determines if a given edge from the block currently being emitted goes to its lexical 432 * successor. 433 */ 434 public boolean isSuccessorEdge(LabelRef edge) { 435 assert lir != null; 436 AbstractBlockBase<?>[] order = lir.codeEmittingOrder(); 437 assert order[currentBlockIndex] == edge.getSourceBlock(); 438 AbstractBlockBase<?> nextBlock = LIR.getNextBlock(order, currentBlockIndex); 439 return nextBlock == edge.getTargetBlock(); 440 } 441 442 /** 443 * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}. 444 */ 445 public void emit(@SuppressWarnings("hiding") LIR lir) { 446 assert this.lir == null; 447 assert currentBlockIndex == 0; 448 this.lir = lir; 449 this.currentBlockIndex = 0; 450 frameContext.enter(this); 451 for (AbstractBlockBase<?> b : lir.codeEmittingOrder()) { 452 assert (b == null && lir.codeEmittingOrder()[currentBlockIndex] == null) || lir.codeEmittingOrder()[currentBlockIndex].equals(b); 453 emitBlock(b); 454 currentBlockIndex++; 455 } 456 this.lir = null; 457 this.currentBlockIndex = 0; 458 } 459 460 private void emitBlock(AbstractBlockBase<?> block) { 461 if (block == null) { 462 return; 463 } 464 boolean emitComment = Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL) || PrintLIRWithAssembly.getValue(getOptions()); 465 if (emitComment) { 466 blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); 467 } 468 469 for (LIRInstruction op : lir.getLIRforBlock(block)) { 470 if (emitComment) { 471 blockComment(String.format("%d %s", op.id(), op)); 472 } 473 474 try { 475 if (beforeOp != null) { 476 beforeOp.accept(op); 477 } 478 emitOp(this, op); 479 if (afterOp != null) { 480 afterOp.accept(op); 481 } 482 } catch (GraalError e) { 483 throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + Arrays.toString(lir.codeEmittingOrder())); 484 } 485 } 486 } 487 488 private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) { 489 try { 490 int start = crb.asm.position(); 491 op.emitCode(crb); 492 if (op.getPosition() != null) { 493 crb.recordSourceMapping(start, crb.asm.position(), op.getPosition()); 494 } 495 } catch (AssertionError t) { 496 throw new GraalError(t); 497 } catch (RuntimeException t) { 498 throw new GraalError(t); 499 } 500 } 501 502 public void resetForEmittingCode() { 503 asm.reset(); 504 compilationResult.resetForEmittingCode(); 505 if (exceptionInfoList != null) { 506 exceptionInfoList.clear(); 507 } 508 if (dataCache != null) { 509 dataCache.clear(); 510 } 511 } 512 513 public void setOpCallback(Consumer<LIRInstruction> beforeOp, Consumer<LIRInstruction> afterOp) { 514 this.beforeOp = beforeOp; 515 this.afterOp = afterOp; 516 } 517 518 public OptionValues getOptions() { 519 return options; 520 } 521 }