# HG changeset patch # User Josef Eisl # Date 1395761832 -3600 # Node ID 3552926a218f17d32df163bf26d093ee21e8910f # Parent 945e622f5e2fc3367a660b56437ad1adc429b5d5 Added NodeLIRGenerator. diff -r 945e622f5e2f -r 3552926a218f graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRGenerator.java Tue Mar 25 16:37:12 2014 +0100 @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.gen; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.api.meta.Value.*; +import static com.oracle.graal.lir.LIR.*; +import static com.oracle.graal.nodes.ConstantNode.*; +import static com.oracle.graal.phases.GraalOptions.*; + +import java.lang.reflect.*; +import java.util.*; +import java.util.Map.Entry; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.asm.*; +import com.oracle.graal.compiler.target.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.BlockEndOp; +import com.oracle.graal.lir.StandardOp.JumpOp; +import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.NoOp; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.PhiNode.PhiType; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.cfg.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.virtual.*; + +/** + * This class traverses the HIR instructions and generates LIR instructions from them. + */ +public abstract class NodeLIRGenerator implements NodeMappableLIRGenerator, NodeLIRGeneratorTool, LIRTypeTool, BaselineLIRGenerator { + + public static class Options { + // @formatter:off +// @Option(help = "Print HIR along side LIR as the latter is generated") +// public static final OptionValue PrintIRWithLIR = new OptionValue<>(false); +// @Option(help = "The trace level for the LIR generator") +// public static final OptionValue TraceLIRGeneratorLevel = new OptionValue<>(0); + // @formatter:on + } + + private final CallingConvention cc; + + private final NodeMap nodeOperands; + private final DebugInfoBuilder debugInfoBuilder; + + protected AbstractBlock currentBlock; + private final int traceLevel; + private final boolean printIRWithLIR; + + private final LIRGenerator gen; + + /** + * Handle for an operation that loads a constant into a variable. The operation starts in the + * first block where the constant is used but will eventually be + * {@linkplain NodeLIRGenerator#insertConstantLoads() moved} to a block dominating all usages of + * the constant. + */ + public static class LoadConstant implements Comparable { + /** + * The index of {@link #op} within {@link #block}'s instruction list or -1 if {@code op} is + * to be moved to a dominator block. + */ + int index; + + /** + * The operation that loads the constant. + */ + private final LIRInstruction op; + + /** + * The block that does or will contain {@link #op}. This is initially the block where the + * first usage of the constant is seen during LIR generation. + */ + private Block block; + + /** + * The variable into which the constant is loaded. + */ + private final Variable variable; + + public LoadConstant(Variable variable, Block block, int index, LIRInstruction op) { + this.variable = variable; + this.block = block; + this.index = index; + this.op = op; + } + + /** + * Sorts {@link LoadConstant} objects according to their enclosing blocks. This is used to + * group loads per block in {@link NodeLIRGenerator#insertConstantLoads()}. + */ + public int compareTo(LoadConstant o) { + if (block.getId() < o.block.getId()) { + return -1; + } + if (block.getId() > o.block.getId()) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return block + "#" + op; + } + + /** + * Removes the {@link #op} from its original location if it is still at that location. + */ + public void unpin(LIR lir) { + if (index >= 0) { + // Replace the move with a filler op so that the operation + // list does not need to be adjusted. + List instructions = lir.getLIRforBlock(block); + instructions.set(index, new NoOp(null, -1)); + index = -1; + } + } + } + + private Map constantLoads; + + private ValueNode currentInstruction; + private ValueNode lastInstructionPrinted; // Debugging only + + protected LIRGenerationResult res; + + /** + * Checks whether the supplied constant can be used without loading it into a register for store + * operations, i.e., on the right hand side of a memory access. + * + * @param c The constant to check. + * @return True if the constant can be used directly, false if the constant needs to be in a + * register. + */ + public abstract boolean canStoreConstant(Constant c, boolean isCompressed); + + public NodeLIRGenerator(CallingConvention cc, LIRGenerationResult res, LIRGenerator gen) { + this(null, cc, res, gen); + } + + public NodeLIRGenerator(StructuredGraph graph, CallingConvention cc, LIRGenerationResult res, LIRGenerator gen) { + this.res = res; + this.cc = cc; + if (graph != null) { + this.nodeOperands = graph.createNodeMap(); + this.debugInfoBuilder = createDebugInfoBuilder(nodeOperands); + } else { + this.nodeOperands = null; + this.debugInfoBuilder = null; + } + this.gen = gen; + this.traceLevel = LIRGenerator.Options.TraceLIRGeneratorLevel.getValue(); + this.printIRWithLIR = LIRGenerator.Options.PrintIRWithLIR.getValue(); + } + + @SuppressWarnings("hiding") + protected DebugInfoBuilder createDebugInfoBuilder(NodeMap nodeOperands) { + return new DebugInfoBuilder(nodeOperands); + } + + /** + * Returns the operand that has been previously initialized by + * {@link #setResult(ValueNode, Value)} with the result of an instruction. + * + * @param node A node that produces a result value. + */ + @Override + public Value operand(ValueNode node) { + if (nodeOperands == null) { + return null; + } + Value operand = nodeOperands.get(node); + if (operand == null) { + return getConstantOperand(node); + } + return operand; + } + + private Value getConstantOperand(ValueNode node) { + if (!ConstantNodeRecordsUsages) { + Constant value = node.asConstant(); + if (value != null) { + if (gen.canInlineConstant(value)) { + return setResult(node, value); + } else { + Variable loadedValue; + if (constantLoads == null) { + constantLoads = new HashMap<>(); + } + LoadConstant load = constantLoads.get(value); + assert currentBlock instanceof Block; + if (load == null) { + int index = res.getLIR().getLIRforBlock(currentBlock).size(); + loadedValue = gen.emitMove(value); + LIRInstruction op = res.getLIR().getLIRforBlock(currentBlock).get(index); + constantLoads.put(value, new LoadConstant(loadedValue, (Block) currentBlock, index, op)); + } else { + Block dominator = ControlFlowGraph.commonDominator(load.block, (Block) currentBlock); + loadedValue = load.variable; + if (dominator != load.block) { + load.unpin(res.getLIR()); + } else { + assert load.block != currentBlock || load.index < res.getLIR().getLIRforBlock(currentBlock).size(); + } + load.block = dominator; + } + return loadedValue; + } + } + } else { + // Constant is loaded by ConstantNode.generate() + } + return null; + } + + public ValueNode valueForOperand(Value value) { + for (Entry entry : getNodeOperands().entries()) { + if (entry.getValue().equals(value)) { + return (ValueNode) entry.getKey(); + } + } + return null; + } + + @Override + public Value setResult(ValueNode x, Value operand) { + assert (!isRegister(operand) || !gen.attributes(asRegister(operand)).isAllocatable()); + assert nodeOperands == null || nodeOperands.get(x) == null : "operand cannot be set twice"; + assert operand != null && isLegal(operand) : "operand must be legal"; + assert operand.getKind().getStackKind() == x.getKind() || x.getKind() == Kind.Illegal : operand.getKind().getStackKind() + " must match " + x.getKind(); + assert !(x instanceof VirtualObjectNode); + nodeOperands.set(x, operand); + return operand; + } + + public LabelRef getLIRBlock(FixedNode b) { + assert res.getLIR().getControlFlowGraph() instanceof ControlFlowGraph; + Block result = ((ControlFlowGraph) res.getLIR().getControlFlowGraph()).blockFor(b); + int suxIndex = currentBlock.getSuccessors().indexOf(result); + assert suxIndex != -1 : "Block not in successor list of current block"; + + assert currentBlock instanceof Block; + return LabelRef.forSuccessor(res.getLIR(), (Block) currentBlock, suxIndex); + } + + /** + * Determines if only oop maps are required for the code generated from the LIR. + */ + protected boolean needOnlyOopMaps() { + return false; + } + + public LIRFrameState state(DeoptimizingNode deopt) { + if (!deopt.canDeoptimize()) { + return null; + } + return stateFor(deopt.getDeoptimizationState()); + } + + public LIRFrameState stateWithExceptionEdge(DeoptimizingNode deopt, LabelRef exceptionEdge) { + if (!deopt.canDeoptimize()) { + return null; + } + return stateForWithExceptionEdge(deopt.getDeoptimizationState(), exceptionEdge); + } + + public LIRFrameState stateFor(FrameState state) { + return stateForWithExceptionEdge(state, null); + } + + public LIRFrameState stateForWithExceptionEdge(FrameState state, LabelRef exceptionEdge) { + if (needOnlyOopMaps()) { + return new LIRFrameState(null, null, null); + } + assert state != null; + return getDebugInfoBuilder().build(state, exceptionEdge); + } + + /** + * Gets the ABI specific operand used to return a value of a given kind from a method. + * + * @param kind the kind of value being returned + * @return the operand representing the ABI defined location used return a value of kind + * {@code kind} + */ + public AllocatableValue resultOperandFor(Kind kind) { + if (kind == Kind.Void) { + return ILLEGAL; + } + return res.getFrameMap().registerConfig.getReturnRegister(kind).asValue(kind); + } + + public void append(LIRInstruction op) { + if (printIRWithLIR && !TTY.isSuppressed()) { + if (currentInstruction != null && lastInstructionPrinted != currentInstruction) { + lastInstructionPrinted = currentInstruction; + InstructionPrinter ip = new InstructionPrinter(TTY.out()); + ip.printInstructionListing(currentInstruction); + } + TTY.println(op.toStringWithIdPrefix()); + TTY.println(); + } + assert LIRVerifier.verify(op); + res.getLIR().getLIRforBlock(currentBlock).add(op); + } + + public final void doBlockStart(AbstractBlock block) { + if (printIRWithLIR) { + TTY.print(block.toString()); + } + + currentBlock = block; + + // set up the list of LIR instructions + assert res.getLIR().getLIRforBlock(block) == null : "LIR list already computed for this block"; + res.getLIR().setLIRforBlock(block, new ArrayList()); + + append(new LabelOp(new Label(block.getId()), block.isAligned())); + + if (traceLevel >= 1) { + TTY.println("BEGIN Generating LIR for block B" + block.getId()); + } + } + + public final void doBlockEnd(AbstractBlock block) { + + if (traceLevel >= 1) { + TTY.println("END Generating LIR for block B" + block.getId()); + } + + currentBlock = null; + + if (printIRWithLIR) { + TTY.println(); + } + } + + /** + * For Baseline compilation + */ + + public > void doBlock(T block, ResolvedJavaMethod method, BytecodeParser parser) { + doBlockStart(block); + + if (block == res.getLIR().getControlFlowGraph().getStartBlock()) { + assert block.getPredecessorCount() == 0; + emitPrologue(method, parser); + } else { + assert block.getPredecessorCount() > 0; + } + parser.processBlock(block); + + doBlockEnd(block); + } + + public void doBlock(Block block, StructuredGraph graph, BlockMap> blockMap) { + doBlockStart(block); + + if (block == res.getLIR().getControlFlowGraph().getStartBlock()) { + assert block.getPredecessorCount() == 0; + emitPrologue(graph); + } else { + assert block.getPredecessorCount() > 0; + } + + List nodes = blockMap.get(block); + for (int i = 0; i < nodes.size(); i++) { + Node instr = nodes.get(i); + if (traceLevel >= 3) { + TTY.println("LIRGen for " + instr); + } + if (!ConstantNodeRecordsUsages && instr instanceof ConstantNode) { + // Loading of constants is done lazily by operand() + } else if (instr instanceof ValueNode) { + ValueNode valueNode = (ValueNode) instr; + if (operand(valueNode) == null) { + if (!peephole(valueNode)) { + try { + doRoot((ValueNode) instr); + } catch (GraalInternalError e) { + throw e.addContext(instr); + } catch (Throwable e) { + throw new GraalInternalError(e).addContext(instr); + } + } + } else { + // There can be cases in which the result of an instruction is already set + // before by other instructions. + } + } + } + + if (!hasBlockEnd(block)) { + NodeClassIterable successors = block.getEndNode().successors(); + assert successors.count() == block.getSuccessorCount(); + if (block.getSuccessorCount() != 1) { + /* + * If we have more than one successor, we cannot just use the first one. Since + * successors are unordered, this would be a random choice. + */ + throw new GraalInternalError("Block without BlockEndOp: " + block.getEndNode()); + } + emitJump(getLIRBlock((FixedNode) successors.first())); + } + + assert verifyBlock(res.getLIR(), block); + doBlockEnd(block); + } + + protected abstract boolean peephole(ValueNode valueNode); + + private boolean hasBlockEnd(Block block) { + List ops = res.getLIR().getLIRforBlock(block); + if (ops.size() == 0) { + return false; + } + return ops.get(ops.size() - 1) instanceof BlockEndOp; + } + + private void doRoot(ValueNode instr) { + if (traceLevel >= 2) { + TTY.println("Emitting LIR for instruction " + instr); + } + currentInstruction = instr; + + Debug.log("Visiting %s", instr); + emitNode(instr); + Debug.log("Operand for %s = %s", instr, operand(instr)); + } + + protected void emitNode(ValueNode node) { + if (Debug.isLogEnabled() && node.stamp() instanceof IllegalStamp) { + Debug.log("This node has invalid type, we are emitting dead code(?): %s", node); + } + if (node instanceof LIRGenLowerable) { + ((LIRGenLowerable) node).generate(gen); + } else if (node instanceof LIRGenResLowerable) { + ((LIRGenResLowerable) node).generate(gen, res); + } else if (node instanceof LIRLowerable) { + ((LIRLowerable) node).generate(gen); + } else if (node instanceof ArithmeticLIRLowerable) { + ((ArithmeticLIRLowerable) node).generate(gen); + } else { + throw GraalInternalError.shouldNotReachHere("node is not LIRLowerable: " + node); + } + } + + protected void emitPrologue(StructuredGraph graph) { + CallingConvention incomingArguments = getCallingConvention(); + + Value[] params = new Value[incomingArguments.getArgumentCount()]; + for (int i = 0; i < params.length; i++) { + params[i] = toStackKind(incomingArguments.getArgument(i)); + if (ValueUtil.isStackSlot(params[i])) { + StackSlot slot = ValueUtil.asStackSlot(params[i]); + if (slot.isInCallerFrame() && !res.getLIR().hasArgInCallerFrame()) { + res.getLIR().setHasArgInCallerFrame(); + } + } + } + + emitIncomingValues(params); + + for (ParameterNode param : graph.getNodes(ParameterNode.class)) { + Value paramValue = params[param.index()]; + assert paramValue.getKind() == param.getKind().getStackKind(); + setResult(param, gen.emitMove(paramValue)); + } + } + + protected > void emitPrologue(ResolvedJavaMethod method, BytecodeParser parser) { + CallingConvention incomingArguments = getCallingConvention(); + + Value[] params = new Value[incomingArguments.getArgumentCount()]; + for (int i = 0; i < params.length; i++) { + params[i] = toStackKind(incomingArguments.getArgument(i)); + if (ValueUtil.isStackSlot(params[i])) { + StackSlot slot = ValueUtil.asStackSlot(params[i]); + if (slot.isInCallerFrame() && !res.getLIR().hasArgInCallerFrame()) { + res.getLIR().setHasArgInCallerFrame(); + } + } + } + + emitIncomingValues(params); + + Signature sig = method.getSignature(); + boolean isStatic = Modifier.isStatic(method.getModifiers()); + for (int i = 0; i < sig.getParameterCount(!isStatic); i++) { + Value paramValue = params[i]; + assert paramValue.getKind() == sig.getParameterKind(i).getStackKind(); + // TODO setResult(param, emitMove(paramValue)); + parser.setParameter(i, gen.emitMove(paramValue)); + } + + // return arguments; + } + + public void emitIncomingValues(Value[] params) { + ((LabelOp) res.getLIR().getLIRforBlock(currentBlock).get(0)).setIncomingValues(params); + } + + @Override + public void visitReturn(ReturnNode x) { + AllocatableValue operand = ILLEGAL; + if (x.result() != null) { + operand = resultOperandFor(x.result().getKind()); + gen.emitMove(operand, operand(x.result())); + } + emitReturn(operand); + } + + @Override + public void visitReturn(Value x) { + AllocatableValue operand = ILLEGAL; + if (x != null) { + operand = resultOperandFor(x.getKind()); + gen.emitMove(operand, x); + } + emitReturn(operand); + } + + protected abstract void emitReturn(Value input); + + @Override + public void visitMerge(MergeNode x) { + } + + @Override + public void visitEndNode(AbstractEndNode end) { + moveToPhi(end.merge(), end); + } + + /** + * Runtime specific classes can override this to insert a safepoint at the end of a loop. + */ + @Override + public void visitLoopEnd(LoopEndNode x) { + } + + private void moveToPhi(MergeNode merge, AbstractEndNode pred) { + if (traceLevel >= 1) { + TTY.println("MOVE TO PHI from " + pred + " to " + merge); + } + PhiResolver resolver = new PhiResolver(gen); + for (PhiNode phi : merge.phis()) { + if (phi.type() == PhiType.Value) { + ValueNode curVal = phi.valueAt(pred); + resolver.move(operandForPhi(phi), operand(curVal)); + } + } + resolver.dispose(); + + append(new JumpOp(getLIRBlock(merge))); + } + + protected PlatformKind getPhiKind(PhiNode phi) { + return phi.getKind(); + } + + private Value operandForPhi(PhiNode phi) { + assert phi.type() == PhiType.Value : "wrong phi type: " + phi; + Value result = operand(phi); + if (result == null) { + // allocate a variable for this phi + Variable newOperand = gen.newVariable(getPhiKind(phi)); + setResult(phi, newOperand); + return newOperand; + } else { + return result; + } + } + + @Override + public void emitIf(IfNode x) { + emitBranch(x.condition(), getLIRBlock(x.trueSuccessor()), getLIRBlock(x.falseSuccessor()), x.probability(x.trueSuccessor())); + } + + public void emitBranch(LogicNode node, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + if (node instanceof IsNullNode) { + emitNullCheckBranch((IsNullNode) node, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } else if (node instanceof CompareNode) { + emitCompareBranch((CompareNode) node, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } else if (node instanceof LogicConstantNode) { + emitConstantBranch(((LogicConstantNode) node).getValue(), trueSuccessor, falseSuccessor); + } else if (node instanceof IntegerTestNode) { + emitIntegerTestBranch((IntegerTestNode) node, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } else { + throw GraalInternalError.unimplemented(node.toString()); + } + } + + private void emitNullCheckBranch(IsNullNode node, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + emitCompareBranch(operand(node.object()), Constant.NULL_OBJECT, Condition.EQ, false, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } + + public void emitCompareBranch(CompareNode compare, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + emitCompareBranch(operand(compare.x()), operand(compare.y()), compare.condition(), compare.unorderedIsTrue(), trueSuccessor, falseSuccessor, trueSuccessorProbability); + } + + public void emitIntegerTestBranch(IntegerTestNode test, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + emitIntegerTestBranch(operand(test.x()), operand(test.y()), trueSuccessor, falseSuccessor, trueSuccessorProbability); + } + + public void emitConstantBranch(boolean value, LabelRef trueSuccessorBlock, LabelRef falseSuccessorBlock) { + LabelRef block = value ? trueSuccessorBlock : falseSuccessorBlock; + emitJump(block); + } + + @Override + public void emitConditional(ConditionalNode conditional) { + Value tVal = operand(conditional.trueValue()); + Value fVal = operand(conditional.falseValue()); + setResult(conditional, emitConditional(conditional.condition(), tVal, fVal)); + } + + public Variable emitConditional(LogicNode node, Value trueValue, Value falseValue) { + if (node instanceof IsNullNode) { + IsNullNode isNullNode = (IsNullNode) node; + return emitConditionalMove(operand(isNullNode.object()), Constant.NULL_OBJECT, Condition.EQ, false, trueValue, falseValue); + } else if (node instanceof CompareNode) { + CompareNode compare = (CompareNode) node; + return emitConditionalMove(operand(compare.x()), operand(compare.y()), compare.condition(), compare.unorderedIsTrue(), trueValue, falseValue); + } else if (node instanceof LogicConstantNode) { + return gen.emitMove(((LogicConstantNode) node).getValue() ? trueValue : falseValue); + } else if (node instanceof IntegerTestNode) { + IntegerTestNode test = (IntegerTestNode) node; + return emitIntegerTestMove(operand(test.x()), operand(test.y()), trueValue, falseValue); + } else { + throw GraalInternalError.unimplemented(node.toString()); + } + } + + public abstract void emitJump(LabelRef label); + + public abstract void emitCompareBranch(Value left, Value right, Condition cond, boolean unorderedIsTrue, LabelRef trueDestination, LabelRef falseDestination, double trueDestinationProbability); + + public abstract void emitOverflowCheckBranch(LabelRef overflow, LabelRef noOverflow, double overflowProbability); + + public abstract void emitIntegerTestBranch(Value left, Value right, LabelRef trueDestination, LabelRef falseDestination, double trueSuccessorProbability); + + public abstract Variable emitConditionalMove(Value leftVal, Value right, Condition cond, boolean unorderedIsTrue, Value trueValue, Value falseValue); + + public abstract Variable emitIntegerTestMove(Value leftVal, Value right, Value trueValue, Value falseValue); + + @Override + public void emitInvoke(Invoke x) { + LoweredCallTargetNode callTarget = (LoweredCallTargetNode) x.callTarget(); + CallingConvention invokeCc = res.getFrameMap().registerConfig.getCallingConvention(callTarget.callType(), x.asNode().stamp().javaType(gen.getMetaAccess()), callTarget.signature(), + gen.target(), false); + res.getFrameMap().callsMethod(invokeCc); + + Value[] parameters = visitInvokeArguments(invokeCc, callTarget.arguments()); + + LabelRef exceptionEdge = null; + if (x instanceof InvokeWithExceptionNode) { + exceptionEdge = getLIRBlock(((InvokeWithExceptionNode) x).exceptionEdge()); + } + LIRFrameState callState = stateWithExceptionEdge(x, exceptionEdge); + + Value result = invokeCc.getReturn(); + if (callTarget instanceof DirectCallTargetNode) { + emitDirectCall((DirectCallTargetNode) callTarget, result, parameters, AllocatableValue.NONE, callState); + } else if (callTarget instanceof IndirectCallTargetNode) { + emitIndirectCall((IndirectCallTargetNode) callTarget, result, parameters, AllocatableValue.NONE, callState); + } else { + throw GraalInternalError.shouldNotReachHere(); + } + + if (isLegal(result)) { + setResult(x.asNode(), gen.emitMove(result)); + } + + if (x instanceof InvokeWithExceptionNode) { + emitJump(getLIRBlock(((InvokeWithExceptionNode) x).next())); + } + } + + protected abstract void emitDirectCall(DirectCallTargetNode callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState callState); + + protected abstract void emitIndirectCall(IndirectCallTargetNode callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState callState); + + protected abstract void emitForeignCall(ForeignCallLinkage linkage, Value result, Value[] arguments, Value[] temps, LIRFrameState info); + + protected static AllocatableValue toStackKind(AllocatableValue value) { + if (value.getKind().getStackKind() != value.getKind()) { + // We only have stack-kinds in the LIR, so convert the operand kind for values from the + // calling convention. + if (isRegister(value)) { + return asRegister(value).asValue(value.getKind().getStackKind()); + } else if (isStackSlot(value)) { + return StackSlot.get(value.getKind().getStackKind(), asStackSlot(value).getRawOffset(), asStackSlot(value).getRawAddFrameSize()); + } else { + throw GraalInternalError.shouldNotReachHere(); + } + } + return value; + } + + public Value[] visitInvokeArguments(CallingConvention invokeCc, Collection arguments) { + // for each argument, load it into the correct location + Value[] result = new Value[arguments.size()]; + int j = 0; + for (ValueNode arg : arguments) { + if (arg != null) { + AllocatableValue operand = toStackKind(invokeCc.getArgument(j)); + gen.emitMove(operand, operand(arg)); + result[j] = operand; + j++; + } else { + throw GraalInternalError.shouldNotReachHere("I thought we no longer have null entries for two-slot types..."); + } + } + return result; + } + + @Override + public Variable emitForeignCall(ForeignCallLinkage linkage, DeoptimizingNode info, Value... args) { + LIRFrameState state = null; + if (linkage.canDeoptimize()) { + if (info != null) { + state = stateFor(info.getDeoptimizationState()); + } else { + assert needOnlyOopMaps(); + state = new LIRFrameState(null, null, null); + } + } + + // move the arguments into the correct location + CallingConvention linkageCc = linkage.getOutgoingCallingConvention(); + res.getFrameMap().callsMethod(linkageCc); + assert linkageCc.getArgumentCount() == args.length : "argument count mismatch"; + Value[] argLocations = new Value[args.length]; + for (int i = 0; i < args.length; i++) { + Value arg = args[i]; + AllocatableValue loc = linkageCc.getArgument(i); + gen.emitMove(loc, arg); + argLocations[i] = loc; + } + res.setForeignCall(true); + emitForeignCall(linkage, linkageCc.getReturn(), argLocations, linkage.getTemporaries(), state); + + if (isLegal(linkageCc.getReturn())) { + return gen.emitMove(linkageCc.getReturn()); + } else { + return null; + } + } + + /** + * This method tries to create a switch implementation that is optimal for the given switch. It + * will either generate a sequential if/then/else cascade, a set of range tests or a table + * switch. + * + * If the given switch does not contain int keys, it will always create a sequential + * implementation. + */ + @Override + public void emitSwitch(SwitchNode x) { + assert x.defaultSuccessor() != null; + LabelRef defaultTarget = getLIRBlock(x.defaultSuccessor()); + int keyCount = x.keyCount(); + if (keyCount == 0) { + emitJump(defaultTarget); + } else { + Variable value = gen.load(operand(x.value())); + if (keyCount == 1) { + assert defaultTarget != null; + double probability = x.probability(x.keySuccessor(0)); + emitCompareBranch(gen.load(operand(x.value())), x.keyAt(0), Condition.EQ, false, getLIRBlock(x.keySuccessor(0)), defaultTarget, probability); + } else { + LabelRef[] keyTargets = new LabelRef[keyCount]; + Constant[] keyConstants = new Constant[keyCount]; + double[] keyProbabilities = new double[keyCount]; + for (int i = 0; i < keyCount; i++) { + keyTargets[i] = getLIRBlock(x.keySuccessor(i)); + keyConstants[i] = x.keyAt(i); + keyProbabilities[i] = x.keyProbability(i); + } + if (value.getKind() != Kind.Int || !x.isSorted()) { + // hopefully only a few entries + emitStrategySwitch(new SwitchStrategy.SequentialStrategy(keyProbabilities, keyConstants), value, keyTargets, defaultTarget); + } else { + emitStrategySwitch(keyConstants, keyProbabilities, keyTargets, defaultTarget, value); + } + } + } + } + + protected void emitStrategySwitch(Constant[] keyConstants, double[] keyProbabilities, LabelRef[] keyTargets, LabelRef defaultTarget, Variable value) { + int keyCount = keyConstants.length; + SwitchStrategy strategy = SwitchStrategy.getBestStrategy(keyProbabilities, keyConstants, keyTargets); + long valueRange = keyConstants[keyCount - 1].asLong() - keyConstants[0].asLong() + 1; + double tableSwitchDensity = keyCount / (double) valueRange; + /* + * This heuristic tries to find a compromise between the effort for the best switch strategy + * and the density of a tableswitch. If the effort for the strategy is at least 4, then a + * tableswitch is preferred if better than a certain value that starts at 0.5 and lowers + * gradually with additional effort. + */ + if (strategy.getAverageEffort() < 4 || tableSwitchDensity < (1 / Math.sqrt(strategy.getAverageEffort()))) { + emitStrategySwitch(strategy, value, keyTargets, defaultTarget); + } else { + int minValue = keyConstants[0].asInt(); + assert valueRange < Integer.MAX_VALUE; + LabelRef[] targets = new LabelRef[(int) valueRange]; + for (int i = 0; i < valueRange; i++) { + targets[i] = defaultTarget; + } + for (int i = 0; i < keyCount; i++) { + targets[keyConstants[i].asInt() - minValue] = keyTargets[i]; + } + emitTableSwitch(minValue, defaultTarget, targets, value); + } + } + + protected abstract void emitStrategySwitch(SwitchStrategy strategy, Variable key, LabelRef[] keyTargets, LabelRef defaultTarget); + + protected abstract void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value key); + + public final NodeMap getNodeOperands() { + assert nodeOperands != null; + return nodeOperands; + } + + public CallingConvention getCallingConvention() { + return cc; + } + + public DebugInfoBuilder getDebugInfoBuilder() { + assert debugInfoBuilder != null; + return debugInfoBuilder; + } + + /** + * Moves deferred {@linkplain LoadConstant loads} of constants into blocks dominating all usages + * of the constant. Any operations inserted into a block are guaranteed to be immediately prior + * to the first control flow instruction near the end of the block. + */ + private void insertConstantLoads() { + if (constantLoads != null) { + // Remove loads where all usages are in the same block. + for (Iterator> iter = constantLoads.entrySet().iterator(); iter.hasNext();) { + LoadConstant lc = iter.next().getValue(); + + // Move loads of constant outside of loops + if (OptScheduleOutOfLoops.getValue()) { + Block outOfLoopDominator = lc.block; + while (outOfLoopDominator.getLoop() != null) { + outOfLoopDominator = outOfLoopDominator.getDominator(); + } + if (outOfLoopDominator != lc.block) { + lc.unpin(res.getLIR()); + lc.block = outOfLoopDominator; + } + } + + if (lc.index != -1) { + assert res.getLIR().getLIRforBlock(lc.block).get(lc.index) == lc.op; + iter.remove(); + } + } + if (constantLoads.isEmpty()) { + return; + } + + // Sorting groups the loads per block. + LoadConstant[] groupedByBlock = constantLoads.values().toArray(new LoadConstant[constantLoads.size()]); + Arrays.sort(groupedByBlock); + + int groupBegin = 0; + while (true) { + int groupEnd = groupBegin + 1; + Block block = groupedByBlock[groupBegin].block; + while (groupEnd < groupedByBlock.length && groupedByBlock[groupEnd].block == block) { + groupEnd++; + } + int groupSize = groupEnd - groupBegin; + + List ops = res.getLIR().getLIRforBlock(block); + int lastIndex = ops.size() - 1; + assert ops.get(lastIndex) instanceof BlockEndOp; + int insertionIndex = lastIndex; + for (int i = Math.max(0, lastIndex - MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END); i < lastIndex; i++) { + if (getExceptionEdge(ops.get(i)) != null) { + insertionIndex = i; + break; + } + } + + if (groupSize == 1) { + ops.add(insertionIndex, groupedByBlock[groupBegin].op); + } else { + assert groupSize > 1; + List moves = new ArrayList<>(groupSize); + for (int i = groupBegin; i < groupEnd; i++) { + moves.add(groupedByBlock[i].op); + } + ops.addAll(insertionIndex, moves); + } + + if (groupEnd == groupedByBlock.length) { + break; + } + groupBegin = groupEnd; + } + constantLoads = null; + } + } + + /** + * Gets a garbage value for a given kind. + */ + protected Constant zapValueForKind(PlatformKind kind) { + long dead = 0xDEADDEADDEADDEADL; + switch ((Kind) kind) { + case Boolean: + return Constant.FALSE; + case Byte: + return Constant.forByte((byte) dead); + case Char: + return Constant.forChar((char) dead); + case Short: + return Constant.forShort((short) dead); + case Int: + return Constant.forInt((int) dead); + case Double: + return Constant.forDouble(Double.longBitsToDouble(dead)); + case Float: + return Constant.forFloat(Float.intBitsToFloat((int) dead)); + case Long: + return Constant.forLong(dead); + case Object: + return Constant.NULL_OBJECT; + default: + throw new IllegalArgumentException(kind.toString()); + } + } + + /** + * Default implementation: Return the Java stack kind for each stamp. + */ + public PlatformKind getPlatformKind(Stamp stamp) { + return stamp.getPlatformKind(this); + } + + public PlatformKind getIntegerKind(int bits, boolean unsigned) { + if (bits > 32) { + return Kind.Long; + } else { + return Kind.Int; + } + } + + public PlatformKind getFloatingKind(int bits) { + switch (bits) { + case 32: + return Kind.Float; + case 64: + return Kind.Double; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + + public PlatformKind getObjectKind() { + return Kind.Object; + } + + public abstract void emitBitCount(Variable result, Value operand); + + public abstract void emitBitScanForward(Variable result, Value operand); + + public abstract void emitBitScanReverse(Variable result, Value operand); + + public abstract void emitByteSwap(Variable result, Value operand); + + public abstract void emitArrayEquals(Kind kind, Variable result, Value array1, Value array2, Value length); +}