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