package org.graalvm.compiler.lir.amd64.phases;
import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.CounterKey;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.lir.LIR;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.RedundantMoveElimination;
import org.graalvm.compiler.lir.amd64.AMD64Move.AMD64MultiStackMove;
import org.graalvm.compiler.lir.amd64.AMD64Move.AMD64StackMove;
import org.graalvm.compiler.lir.gen.LIRGenerationResult;
import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase;
import org.graalvm.compiler.options.NestedBooleanOptionKey;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionType;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.Value;
public class StackMoveOptimizationPhase extends PostAllocationOptimizationPhase {
public static class Options {
@Option(help = "", type = OptionType.Debug)
public static final NestedBooleanOptionKey LIROptStackMoveOptimizer = new NestedBooleanOptionKey(LIROptimization, true);
}
private static final CounterKey eliminatedBackup = DebugContext.counter("StackMoveOptimizer[EliminatedScratchBackupRestore]");
@Override
protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PostAllocationOptimizationContext context) {
LIR lir = lirGenRes.getLIR();
DebugContext debug = lir.getDebug();
for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
new Closure().process(debug, instructions);
}
}
private static class Closure {
private static final int NONE = -1;
private int begin = NONE;
private Register reg = null;
private List<AllocatableValue> dst;
private List<Value> src;
private AllocatableValue slot;
private boolean removed = false;
public void process(DebugContext debug, List<LIRInstruction> instructions) {
for (int i = 0; i < instructions.size(); i++) {
LIRInstruction inst = instructions.get(i);
if (isStackMove(inst)) {
AMD64StackMove move = asStackMove(inst);
if (reg != null && !reg.equals(move.getScratchRegister())) {
replaceStackMoves(debug, instructions);
}
if (dst == null) {
assert src == null;
dst = new ArrayList<>();
src = new ArrayList<>();
}
dst.add(move.getResult());
src.add(move.getInput());
if (begin == NONE) {
begin = i;
reg = move.getScratchRegister();
slot = move.getBackupSlot();
}
} else if (begin != NONE) {
replaceStackMoves(debug, instructions);
}
}
if (removed) {
instructions.removeAll(Collections.singleton(null));
}
}
private void replaceStackMoves(DebugContext debug, List<LIRInstruction> instructions) {
int size = dst.size();
if (size > 1) {
AMD64MultiStackMove multiMove = new AMD64MultiStackMove(dst.toArray(new AllocatableValue[size]), src.toArray(new AllocatableValue[size]), reg, slot);
instructions.set(begin, multiMove);
Collections.fill(instructions.subList(begin + 1, begin + size), null);
removed = true;
eliminatedBackup.add(debug, size - 1);
}
dst.clear();
src.clear();
begin = NONE;
reg = null;
slot = null;
}
}
private static AMD64StackMove asStackMove(LIRInstruction inst) {
assert isStackMove(inst);
return (AMD64StackMove) inst;
}
private static boolean isStackMove(LIRInstruction inst) {
return inst instanceof AMD64StackMove;
}
}