001/* 002 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.lir.asm; 024 025import static jdk.internal.jvmci.code.ValueUtil.*; 026 027import java.util.*; 028import java.util.function.*; 029 030import jdk.internal.jvmci.code.*; 031import jdk.internal.jvmci.code.CompilationResult.*; 032import jdk.internal.jvmci.code.DataSection.*; 033import jdk.internal.jvmci.common.*; 034 035import com.oracle.graal.debug.*; 036 037import jdk.internal.jvmci.meta.*; 038import jdk.internal.jvmci.options.*; 039 040import com.oracle.graal.asm.*; 041import com.oracle.graal.compiler.common.cfg.*; 042import com.oracle.graal.compiler.common.spi.*; 043import com.oracle.graal.lir.*; 044import com.oracle.graal.lir.framemap.*; 045 046/** 047 * Fills in a {@link CompilationResult} as its code is being assembled. 048 * 049 * @see CompilationResultBuilderFactory 050 */ 051public class CompilationResultBuilder { 052 053 // @formatter:off 054 @Option(help = "Include the LIR as comments with the final assembly.", type = OptionType.Debug) 055 public static final OptionValue<Boolean> PrintLIRWithAssembly = new OptionValue<>(false); 056 // @formatter:on 057 058 private static class ExceptionInfo { 059 060 public final int codeOffset; 061 public final LabelRef exceptionEdge; 062 063 public ExceptionInfo(int pcOffset, LabelRef exceptionEdge) { 064 this.codeOffset = pcOffset; 065 this.exceptionEdge = exceptionEdge; 066 } 067 } 068 069 public final Assembler asm; 070 public final CompilationResult compilationResult; 071 public final TargetDescription target; 072 public final CodeCacheProvider codeCache; 073 public final ForeignCallsProvider foreignCalls; 074 public final FrameMap frameMap; 075 076 /** 077 * The LIR for which code is being generated. 078 */ 079 private LIR lir; 080 081 /** 082 * The index of the block currently being emitted. 083 */ 084 private int currentBlockIndex; 085 086 /** 087 * The object that emits code for managing a method's frame. 088 */ 089 public final FrameContext frameContext; 090 091 private List<ExceptionInfo> exceptionInfoList; 092 093 private final IdentityHashMap<Constant, Data> dataCache; 094 095 private Consumer<LIRInstruction> beforeOp; 096 private Consumer<LIRInstruction> afterOp; 097 098 public CompilationResultBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, FrameContext frameContext, CompilationResult compilationResult) { 099 this.target = codeCache.getTarget(); 100 this.codeCache = codeCache; 101 this.foreignCalls = foreignCalls; 102 this.frameMap = frameMap; 103 this.asm = asm; 104 this.compilationResult = compilationResult; 105 this.frameContext = frameContext; 106 assert frameContext != null; 107 108 // constants are already GVNed in the high level graph, so we can use an IdentityHashMap 109 this.dataCache = new IdentityHashMap<>(); 110 } 111 112 public void setTotalFrameSize(int frameSize) { 113 compilationResult.setTotalFrameSize(frameSize); 114 } 115 116 public CompilationResult.Mark recordMark(Object id) { 117 return compilationResult.recordMark(asm.position(), id); 118 } 119 120 public void blockComment(String s) { 121 compilationResult.addAnnotation(new CompilationResult.CodeComment(asm.position(), s)); 122 } 123 124 /** 125 * Sets the {@linkplain CompilationResult#setTargetCode(byte[], int) code} and 126 * {@linkplain CompilationResult#recordExceptionHandler(int, int) exception handler} fields of 127 * the compilation result. 128 */ 129 public void finish() { 130 compilationResult.setTargetCode(asm.close(false), asm.position()); 131 132 // Record exception handlers if they exist 133 if (exceptionInfoList != null) { 134 for (ExceptionInfo ei : exceptionInfoList) { 135 int codeOffset = ei.codeOffset; 136 compilationResult.recordExceptionHandler(codeOffset, ei.exceptionEdge.label().position()); 137 } 138 } 139 } 140 141 public void recordExceptionHandlers(int pcOffset, LIRFrameState info) { 142 if (info != null) { 143 if (info.exceptionEdge != null) { 144 if (exceptionInfoList == null) { 145 exceptionInfoList = new ArrayList<>(4); 146 } 147 exceptionInfoList.add(new ExceptionInfo(pcOffset, info.exceptionEdge)); 148 } 149 } 150 } 151 152 public void recordImplicitException(int pcOffset, LIRFrameState info) { 153 compilationResult.recordInfopoint(pcOffset, info.debugInfo(), InfopointReason.IMPLICIT_EXCEPTION); 154 assert info.exceptionEdge == null; 155 } 156 157 public void recordDirectCall(int posBefore, int posAfter, InvokeTarget callTarget, LIRFrameState info) { 158 DebugInfo debugInfo = info != null ? info.debugInfo() : null; 159 compilationResult.recordCall(posBefore, posAfter - posBefore, callTarget, debugInfo, true); 160 } 161 162 public void recordIndirectCall(int posBefore, int posAfter, InvokeTarget callTarget, LIRFrameState info) { 163 DebugInfo debugInfo = info != null ? info.debugInfo() : null; 164 compilationResult.recordCall(posBefore, posAfter - posBefore, callTarget, debugInfo, false); 165 } 166 167 public void recordInfopoint(int pos, LIRFrameState info, InfopointReason reason) { 168 // infopoints always need debug info 169 DebugInfo debugInfo = info.debugInfo(); 170 recordInfopoint(pos, debugInfo, reason); 171 } 172 173 public void recordInfopoint(int pos, DebugInfo debugInfo, InfopointReason reason) { 174 compilationResult.recordInfopoint(pos, debugInfo, reason); 175 } 176 177 public void recordInlineDataInCode(Constant data) { 178 assert data != null; 179 int pos = asm.position(); 180 Debug.log("Inline data in code: pos = %d, data = %s", pos, data); 181 if (data instanceof VMConstant) { 182 compilationResult.recordDataPatch(pos, new ConstantReference((VMConstant) data)); 183 } 184 } 185 186 private AbstractAddress recordDataSectionReference(Data data) { 187 assert data != null; 188 DataSectionReference reference = compilationResult.getDataSection().insertData(data); 189 compilationResult.recordDataPatch(asm.position(), reference); 190 return asm.getPlaceholder(); 191 } 192 193 public AbstractAddress recordDataReferenceInCode(Constant constant, int alignment) { 194 assert constant != null; 195 Debug.log("Constant reference in code: pos = %d, data = %s", asm.position(), constant); 196 Data data = dataCache.get(constant); 197 if (data == null) { 198 data = codeCache.createDataItem(constant); 199 dataCache.put(constant, data); 200 } 201 data.updateAlignment(alignment); 202 return recordDataSectionReference(data); 203 } 204 205 public AbstractAddress recordDataReferenceInCode(byte[] data, int alignment) { 206 assert data != null; 207 if (Debug.isLogEnabled()) { 208 Debug.log("Data reference in code: pos = %d, data = %s", asm.position(), Arrays.toString(data)); 209 } 210 return recordDataSectionReference(new Data(alignment, data.length, DataBuilder.raw(data))); 211 } 212 213 /** 214 * Returns the integer value of any constant that can be represented by a 32-bit integer value, 215 * including long constants that fit into the 32-bit range. 216 */ 217 public int asIntConst(Value value) { 218 assert (value.getKind().isNumericInteger()) && isConstant(value); 219 JavaConstant constant = (JavaConstant) value; 220 assert !codeCache.needsDataPatch(constant) : constant + " should be in a DataPatch"; 221 long c = constant.asLong(); 222 if (!NumUtil.isInt(c)) { 223 throw JVMCIError.shouldNotReachHere(); 224 } 225 return (int) c; 226 } 227 228 /** 229 * Returns the float value of any constant that can be represented by a 32-bit float value. 230 */ 231 public float asFloatConst(Value value) { 232 assert (value.getKind().getStackKind() == Kind.Float && isConstant(value)); 233 JavaConstant constant = (JavaConstant) value; 234 assert !codeCache.needsDataPatch(constant) : constant + " should be in a DataPatch"; 235 return constant.asFloat(); 236 } 237 238 /** 239 * Returns the long value of any constant that can be represented by a 64-bit long value. 240 */ 241 public long asLongConst(Value value) { 242 assert (value.getKind().getStackKind() == Kind.Long && isConstant(value)); 243 JavaConstant constant = (JavaConstant) value; 244 assert !codeCache.needsDataPatch(constant) : constant + " should be in a DataPatch"; 245 return constant.asLong(); 246 } 247 248 /** 249 * Returns the double value of any constant that can be represented by a 64-bit float value. 250 */ 251 public double asDoubleConst(Value value) { 252 assert (value.getKind().getStackKind() == Kind.Double && isConstant(value)); 253 JavaConstant constant = (JavaConstant) value; 254 assert !codeCache.needsDataPatch(constant) : constant + " should be in a DataPatch"; 255 return constant.asDouble(); 256 } 257 258 /** 259 * Returns the address of a float constant that is embedded as a data reference into the code. 260 */ 261 public AbstractAddress asFloatConstRef(Value value) { 262 return asFloatConstRef(value, 4); 263 } 264 265 public AbstractAddress asFloatConstRef(Value value, int alignment) { 266 assert value.getKind() == Kind.Float && isConstant(value); 267 return recordDataReferenceInCode((JavaConstant) value, alignment); 268 } 269 270 /** 271 * Returns the address of a double constant that is embedded as a data reference into the code. 272 */ 273 public AbstractAddress asDoubleConstRef(Value value) { 274 return asDoubleConstRef(value, 8); 275 } 276 277 public AbstractAddress asDoubleConstRef(Value value, int alignment) { 278 assert value.getKind() == Kind.Double && isConstant(value); 279 return recordDataReferenceInCode((JavaConstant) value, alignment); 280 } 281 282 /** 283 * Returns the address of a long constant that is embedded as a data reference into the code. 284 */ 285 public AbstractAddress asLongConstRef(Value value) { 286 assert value.getKind() == Kind.Long && isConstant(value); 287 return recordDataReferenceInCode((JavaConstant) value, 8); 288 } 289 290 /** 291 * Returns the address of an object constant that is embedded as a data reference into the code. 292 */ 293 public AbstractAddress asObjectConstRef(Value value) { 294 assert value.getKind() == Kind.Object && isConstant(value); 295 return recordDataReferenceInCode((JavaConstant) value, 8); 296 } 297 298 public AbstractAddress asByteAddr(Value value) { 299 assert value.getKind().getByteCount() >= Kind.Byte.getByteCount(); 300 return asAddress(value); 301 } 302 303 public AbstractAddress asShortAddr(Value value) { 304 assert value.getKind().getByteCount() >= Kind.Short.getByteCount(); 305 return asAddress(value); 306 } 307 308 public AbstractAddress asIntAddr(Value value) { 309 assert value.getKind().getByteCount() >= Kind.Int.getByteCount(); 310 return asAddress(value); 311 } 312 313 public AbstractAddress asLongAddr(Value value) { 314 assert value.getKind().getByteCount() >= Kind.Long.getByteCount(); 315 return asAddress(value); 316 } 317 318 public AbstractAddress asObjectAddr(Value value) { 319 assert value.getKind() == Kind.Object; 320 return asAddress(value); 321 } 322 323 public AbstractAddress asFloatAddr(Value value) { 324 assert value.getKind().getByteCount() >= Kind.Float.getByteCount(); 325 return asAddress(value); 326 } 327 328 public AbstractAddress asDoubleAddr(Value value) { 329 assert value.getKind().getByteCount() >= Kind.Double.getByteCount(); 330 return asAddress(value); 331 } 332 333 public AbstractAddress asAddress(Value value) { 334 assert isStackSlot(value); 335 StackSlot slot = asStackSlot(value); 336 return asm.makeAddress(frameMap.getRegisterConfig().getFrameRegister(), frameMap.offsetForStackSlot(slot)); 337 } 338 339 /** 340 * Determines if a given edge from the block currently being emitted goes to its lexical 341 * successor. 342 */ 343 public boolean isSuccessorEdge(LabelRef edge) { 344 assert lir != null; 345 List<? extends AbstractBlockBase<?>> order = lir.codeEmittingOrder(); 346 assert order.get(currentBlockIndex) == edge.getSourceBlock(); 347 return currentBlockIndex < order.size() - 1 && order.get(currentBlockIndex + 1) == edge.getTargetBlock(); 348 } 349 350 /** 351 * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}. 352 */ 353 public void emit(@SuppressWarnings("hiding") LIR lir) { 354 assert this.lir == null; 355 assert currentBlockIndex == 0; 356 this.lir = lir; 357 this.currentBlockIndex = 0; 358 frameContext.enter(this); 359 for (AbstractBlockBase<?> b : lir.codeEmittingOrder()) { 360 emitBlock(b); 361 currentBlockIndex++; 362 } 363 this.lir = null; 364 this.currentBlockIndex = 0; 365 } 366 367 private void emitBlock(AbstractBlockBase<?> block) { 368 if (Debug.isDumpEnabled() || PrintLIRWithAssembly.getValue()) { 369 blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); 370 } 371 372 for (LIRInstruction op : lir.getLIRforBlock(block)) { 373 if (Debug.isDumpEnabled() || PrintLIRWithAssembly.getValue()) { 374 blockComment(String.format("%d %s", op.id(), op)); 375 } 376 377 try { 378 if (beforeOp != null) { 379 beforeOp.accept(op); 380 } 381 emitOp(this, op); 382 if (afterOp != null) { 383 afterOp.accept(op); 384 } 385 } catch (JVMCIError e) { 386 throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + lir.codeEmittingOrder()); 387 } 388 } 389 } 390 391 private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) { 392 try { 393 op.emitCode(crb); 394 } catch (AssertionError t) { 395 throw new JVMCIError(t); 396 } catch (RuntimeException t) { 397 throw new JVMCIError(t); 398 } 399 } 400 401 public void reset() { 402 asm.reset(); 403 compilationResult.reset(); 404 if (exceptionInfoList != null) { 405 exceptionInfoList.clear(); 406 } 407 if (dataCache != null) { 408 dataCache.clear(); 409 } 410 } 411 412 public void setOpCallback(Consumer<LIRInstruction> beforeOp, Consumer<LIRInstruction> afterOp) { 413 this.beforeOp = beforeOp; 414 this.afterOp = afterOp; 415 } 416}