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}