1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.dex.code;
18
19import com.android.dx.rop.code.RegisterSpec;
20import com.android.dx.rop.code.RegisterSpecList;
21import com.android.dx.rop.code.SourcePosition;
22import com.android.dx.util.AnnotatedOutput;
23
24/**
25 * Combination instruction which turns into a variable number of
26 * {@code move*} instructions to move a set of registers into
27 * registers starting at {@code 0} sequentially. This is used
28 * in translating an instruction whose register requirements cannot
29 * be met using a straightforward choice of a single opcode.
30 */
31public final class HighRegisterPrefix extends VariableSizeInsn {
32    /** {@code null-ok;} cached instructions, if constructed */
33    private SimpleInsn[] insns;
34
35    /**
36     * Constructs an instance. The output address of this instance is initially
37     * unknown ({@code -1}).
38     *
39     * @param position {@code non-null;} source position
40     * @param registers {@code non-null;} source registers
41     */
42    public HighRegisterPrefix(SourcePosition position,
43                              RegisterSpecList registers) {
44        super(position, registers);
45
46        if (registers.size() == 0) {
47            throw new IllegalArgumentException("registers.size() == 0");
48        }
49
50        insns = null;
51    }
52
53    /** {@inheritDoc} */
54    @Override
55    public int codeSize() {
56        int result = 0;
57
58        calculateInsnsIfNecessary();
59
60        for (SimpleInsn insn : insns) {
61            result += insn.codeSize();
62        }
63
64        return result;
65    }
66
67    /** {@inheritDoc} */
68    @Override
69    public void writeTo(AnnotatedOutput out) {
70        calculateInsnsIfNecessary();
71
72        for (SimpleInsn insn : insns) {
73            insn.writeTo(out);
74        }
75    }
76
77    /**
78     * Helper for {@link #codeSize} and {@link #writeTo} which sets up
79     * {@link #insns} if not already done.
80     */
81    private void calculateInsnsIfNecessary() {
82        if (insns != null) {
83            return;
84        }
85
86        RegisterSpecList registers = getRegisters();
87        int sz = registers.size();
88
89        insns = new SimpleInsn[sz];
90
91        for (int i = 0, outAt = 0; i < sz; i++) {
92            RegisterSpec src = registers.get(i);
93            insns[i] = moveInsnFor(src, outAt);
94            outAt += src.getCategory();
95        }
96    }
97
98    /** {@inheritDoc} */
99    @Override
100    public DalvInsn withRegisters(RegisterSpecList registers) {
101        return new HighRegisterPrefix(getPosition(), registers);
102    }
103
104    /** {@inheritDoc} */
105    @Override
106    protected String argString() {
107        return null;
108    }
109
110    /** {@inheritDoc} */
111    @Override
112    protected String listingString0(boolean noteIndices) {
113        RegisterSpecList registers = getRegisters();
114        int sz = registers.size();
115        StringBuffer sb = new StringBuffer(100);
116
117        for (int i = 0, outAt = 0; i < sz; i++) {
118            RegisterSpec src = registers.get(i);
119            SimpleInsn insn = moveInsnFor(src, outAt);
120
121            if (i != 0) {
122                sb.append('\n');
123            }
124
125            sb.append(insn.listingString0(noteIndices));
126
127            outAt += src.getCategory();
128        }
129
130        return sb.toString();
131    }
132
133    /**
134     * Returns the proper move instruction for the given source spec
135     * and destination index.
136     *
137     * @param src {@code non-null;} the source register spec
138     * @param destIndex {@code >= 0;} the destination register index
139     * @return {@code non-null;} the appropriate move instruction
140     */
141    private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) {
142        return DalvInsn.makeMove(SourcePosition.NO_INFO,
143                RegisterSpec.make(destIndex, src.getType()),
144                src);
145    }
146}
147