001/*
002 * Copyright (c) 2015, 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.amd64.phases;
024
025import static com.oracle.graal.lir.phases.LIRPhase.Options.*;
026
027import java.util.*;
028
029import jdk.internal.jvmci.code.*;
030import com.oracle.graal.debug.*;
031import jdk.internal.jvmci.meta.*;
032import jdk.internal.jvmci.options.*;
033
034import com.oracle.graal.compiler.common.cfg.*;
035import com.oracle.graal.lir.*;
036import com.oracle.graal.lir.amd64.AMD64Move.AMD64MultiStackMove;
037import com.oracle.graal.lir.amd64.AMD64Move.AMD64StackMove;
038import com.oracle.graal.lir.gen.*;
039import com.oracle.graal.lir.phases.*;
040
041/**
042 * Replaces sequential {@link AMD64StackMove}s of the same type with a single
043 * {@link AMD64MultiStackMove} to avoid storing/restoring the scratch register multiple times.
044 *
045 * Note: this phase must be inserted <b>after</b> {@link RedundantMoveElimination} phase because
046 * {@link AMD64MultiStackMove} are not probably detected.
047 */
048public class StackMoveOptimizationPhase extends PostAllocationOptimizationPhase {
049    public static class Options {
050        // @formatter:off
051        @Option(help = "", type = OptionType.Debug)
052        public static final NestedBooleanOptionValue LIROptStackMoveOptimizer = new NestedBooleanOptionValue(LIROptimization, true);
053        // @formatter:on
054    }
055
056    private static final DebugMetric eliminatedBackup = Debug.metric("StackMoveOptimizer[EliminatedScratchBackupRestore]");
057
058    @Override
059    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
060                    BenchmarkCounterFactory counterFactory) {
061        LIR lir = lirGenRes.getLIR();
062        for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
063            List<LIRInstruction> instructions = lir.getLIRforBlock(block);
064            new Closure().process(instructions);
065        }
066    }
067
068    private static class Closure {
069        private static final int NONE = -1;
070
071        private int begin = NONE;
072        private Register reg = null;
073        private List<AllocatableValue> dst;
074        private List<Value> src;
075        private StackSlotValue slot;
076        private boolean removed = false;
077
078        public void process(List<LIRInstruction> instructions) {
079            for (int i = 0; i < instructions.size(); i++) {
080                LIRInstruction inst = instructions.get(i);
081
082                if (isStackMove(inst)) {
083                    AMD64StackMove move = asStackMove(inst);
084
085                    if (reg != null && !reg.equals(move.getScratchRegister())) {
086                        // end of trace & start of new
087                        replaceStackMoves(instructions);
088                    }
089
090                    // lazy initialize
091                    if (dst == null) {
092                        assert src == null;
093                        dst = new ArrayList<>();
094                        src = new ArrayList<>();
095                    }
096
097                    dst.add(move.getResult());
098                    src.add(move.getInput());
099
100                    if (begin == NONE) {
101                        // trace begin
102                        begin = i;
103                        reg = move.getScratchRegister();
104                        slot = move.getBackupSlot();
105                    }
106
107                } else if (begin != NONE) {
108                    // end of trace
109                    replaceStackMoves(instructions);
110                }
111            }
112            // remove instructions
113            if (removed) {
114                instructions.removeAll(Collections.singleton(null));
115            }
116
117        }
118
119        private void replaceStackMoves(List<LIRInstruction> instructions) {
120            int size = dst.size();
121            if (size > 1) {
122                AMD64MultiStackMove multiMove = new AMD64MultiStackMove(dst.toArray(new AllocatableValue[size]), src.toArray(new AllocatableValue[size]), reg, slot);
123                // replace first instruction
124                instructions.set(begin, multiMove);
125                // and null out others
126                Collections.fill(instructions.subList(begin + 1, begin + size), null);
127                // removed
128                removed = true;
129                eliminatedBackup.add(size - 1);
130            }
131            // reset
132            dst.clear();
133            src.clear();
134            begin = NONE;
135            reg = null;
136            slot = null;
137        }
138    }
139
140    private static AMD64StackMove asStackMove(LIRInstruction inst) {
141        assert isStackMove(inst);
142        return (AMD64StackMove) inst;
143    }
144
145    private static boolean isStackMove(LIRInstruction inst) {
146        return inst instanceof AMD64StackMove;
147    }
148
149}