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.gen;
024
025import static com.oracle.graal.lir.LIRValueUtil.*;
026import static jdk.internal.jvmci.code.ValueUtil.*;
027
028import java.util.*;
029
030import jdk.internal.jvmci.meta.*;
031
032import com.oracle.graal.lir.*;
033import com.oracle.graal.lir.LIRInstruction.OperandFlag;
034import com.oracle.graal.lir.LIRInstruction.OperandMode;
035import com.oracle.graal.lir.StandardOp.StackMove;
036import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
037
038/**
039 * Base class for {@link SpillMoveFactory} that checks that the instructions created adhere to the
040 * contract of {@link SpillMoveFactory}.
041 */
042public abstract class SpillMoveFactoryBase implements SpillMoveFactory {
043
044    public final LIRInstruction createMove(AllocatableValue result, Value input) {
045        LIRInstruction inst = createMoveIntern(result, input);
046        assert checkResult(inst, result, input);
047        return inst;
048    }
049
050    public final LIRInstruction createStackMove(AllocatableValue result, Value input) {
051        LIRInstruction inst = createStackMoveIntern(result, input);
052        assert checkResult(inst, result, input);
053        return inst;
054    }
055
056    protected abstract LIRInstruction createMoveIntern(AllocatableValue result, Value input);
057
058    protected LIRInstruction createStackMoveIntern(AllocatableValue result, Value input) {
059        return new StackMove(result, input);
060    }
061
062    /** Closure for {@link SpillMoveFactoryBase#checkResult}. */
063    @SuppressWarnings("unused")
064    private static class CheckClosure {
065
066        private final AllocatableValue result;
067        private final Value input;
068
069        private int tempCount = 0;
070        private int aliveCount = 0;
071        private int stateCount = 0;
072        private int inputCount = 0;
073        private int outputCount = 0;
074
075        CheckClosure(AllocatableValue result, Value input) {
076            this.result = result;
077            this.input = input;
078        }
079
080        void tempProc(LIRInstruction op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
081            assert false : String.format("SpillMoveFactory: Instruction %s is not allowed to contain operand %s of mode %s", op, value, mode);
082            tempCount++;
083        }
084
085        void stateProc(LIRInstruction op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
086            assert false : String.format("SpillMoveFactory: Instruction %s is not allowed to contain operand %s of mode %s", op, value, mode);
087            stateCount++;
088        }
089
090        void aliveProc(LIRInstruction op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
091            assert !isVariable(value) && flags.contains(OperandFlag.UNINITIALIZED) : String.format("SpillMoveFactory: Instruction %s is not allowed to contain operand %s of mode %s", op, value, mode);
092            aliveCount++;
093        }
094
095        void inputProc(LIRInstruction op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
096            assert value.equals(input) || isConstant(value) : String.format("SpillMoveFactory: Instruction %s can only have %s as input, got %s", op, input, value);
097            inputCount++;
098        }
099
100        void outputProc(LIRInstruction op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
101            assert value.equals(result) : String.format("SpillMoveFactory: Instruction %s can only have %s as input, got %s", op, input, value);
102            outputCount++;
103        }
104    }
105
106    /** Checks that the instructions adheres to the contract of {@link SpillMoveFactory}. */
107    private static boolean checkResult(LIRInstruction inst, AllocatableValue result, Value input) {
108
109        SpillMoveFactoryBase.CheckClosure c = new CheckClosure(result, input);
110        inst.visitEachInput(c::inputProc);
111        inst.visitEachOutput(c::outputProc);
112        inst.visitEachAlive(c::aliveProc);
113        inst.visitEachTemp(c::tempProc);
114        inst.visitEachState(c::stateProc);
115
116        assert c.outputCount >= 1 : "no output produced" + inst;
117        assert c.stateCount == 0 : "SpillMoveFactory: instruction must not have a state: " + inst;
118        return true;
119    }
120}