Form35c.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
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.form;
18
19import com.android.dx.dex.code.CstInsn;
20import com.android.dx.dex.code.DalvInsn;
21import com.android.dx.dex.code.InsnFormat;
22import com.android.dx.rop.code.RegisterSpec;
23import com.android.dx.rop.code.RegisterSpecList;
24import com.android.dx.rop.cst.Constant;
25import com.android.dx.rop.cst.CstMethodRef;
26import com.android.dx.rop.cst.CstType;
27import com.android.dx.rop.type.Type;
28import com.android.dx.util.AnnotatedOutput;
29
30import java.util.BitSet;
31
32/**
33 * Instruction format {@code 35c}. See the instruction format spec
34 * for details.
35 */
36public final class Form35c extends InsnFormat {
37    /** {@code non-null;} unique instance of this class */
38    public static final InsnFormat THE_ONE = new Form35c();
39
40    /** Maximal number of operands */
41    private static final int MAX_NUM_OPS = 5;
42
43    /**
44     * Constructs an instance. This class is not publicly
45     * instantiable. Use {@link #THE_ONE}.
46     */
47    private Form35c() {
48        // This space intentionally left blank.
49    }
50
51    /** {@inheritDoc} */
52    @Override
53    public String insnArgString(DalvInsn insn) {
54        RegisterSpecList regs = explicitize(insn.getRegisters());
55        return regListString(regs) + ", " + cstString(insn);
56    }
57
58    /** {@inheritDoc} */
59    @Override
60    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
61        if (noteIndices) {
62            return cstComment(insn);
63        } else {
64            return "";
65        }
66    }
67
68    /** {@inheritDoc} */
69    @Override
70    public int codeSize() {
71        return 3;
72    }
73
74    /** {@inheritDoc} */
75    @Override
76    public boolean isCompatible(DalvInsn insn) {
77        if (!(insn instanceof CstInsn)) {
78            return false;
79        }
80
81        CstInsn ci = (CstInsn) insn;
82        int cpi = ci.getIndex();
83
84        if (! unsignedFitsInShort(cpi)) {
85            return false;
86        }
87
88        Constant cst = ci.getConstant();
89        if (!((cst instanceof CstMethodRef) ||
90              (cst instanceof CstType))) {
91            return false;
92        }
93
94        RegisterSpecList regs = ci.getRegisters();
95        return (wordCount(regs) >= 0);
96    }
97
98    /** {@inheritDoc} */
99    @Override
100    public BitSet compatibleRegs(DalvInsn insn) {
101        RegisterSpecList regs = insn.getRegisters();
102        int sz = regs.size();
103        BitSet bits = new BitSet(sz);
104
105        for (int i = 0; i < sz; i++) {
106            RegisterSpec reg = regs.get(i);
107            /*
108             * The check below adds (category - 1) to the register, to
109             * account for the fact that the second half of a
110             * category-2 register has to be represented explicitly in
111             * the result.
112             */
113            bits.set(i, unsignedFitsInNibble(reg.getReg() +
114                                             reg.getCategory() - 1));
115        }
116
117        return bits;
118    }
119
120    /** {@inheritDoc} */
121    @Override
122    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
123        int cpi = ((CstInsn) insn).getIndex();
124        RegisterSpecList regs = explicitize(insn.getRegisters());
125        int sz = regs.size();
126        int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
127        int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
128        int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
129        int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
130        int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
131
132        write(out,
133              opcodeUnit(insn,
134                         makeByte(r4, sz)), // encode the fifth operand here
135              (short) cpi,
136              codeUnit(r0, r1, r2, r3));
137    }
138
139    /**
140     * Gets the number of words required for the given register list, where
141     * category-2 values count as two words. Return {@code -1} if the
142     * list requires more than five words or contains registers that need
143     * more than a nibble to identify them.
144     *
145     * @param regs {@code non-null;} the register list in question
146     * @return {@code >= -1;} the number of words required, or {@code -1}
147     * if the list couldn't possibly fit in this format
148     */
149    private static int wordCount(RegisterSpecList regs) {
150        int sz = regs.size();
151
152        if (sz > MAX_NUM_OPS) {
153            // It can't possibly fit.
154            return -1;
155        }
156
157        int result = 0;
158
159        for (int i = 0; i < sz; i++) {
160            RegisterSpec one = regs.get(i);
161            result += one.getCategory();
162            /*
163             * The check below adds (category - 1) to the register, to
164             * account for the fact that the second half of a
165             * category-2 register has to be represented explicitly in
166             * the result.
167             */
168            if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
169                return -1;
170            }
171        }
172
173        return (result <= MAX_NUM_OPS) ? result : -1;
174    }
175
176    /**
177     * Returns a register list which is equivalent to the given one,
178     * except that it splits category-2 registers into two explicit
179     * entries. This returns the original list if no modification is
180     * required
181     *
182     * @param orig {@code non-null;} the original list
183     * @return {@code non-null;} the list with the described transformation
184     */
185    private static RegisterSpecList explicitize(RegisterSpecList orig) {
186        int wordCount = wordCount(orig);
187        int sz = orig.size();
188
189        if (wordCount == sz) {
190            return orig;
191        }
192
193        RegisterSpecList result = new RegisterSpecList(wordCount);
194        int wordAt = 0;
195
196        for (int i = 0; i < sz; i++) {
197            RegisterSpec one = orig.get(i);
198            result.set(wordAt, one);
199            if (one.getCategory() == 2) {
200                result.set(wordAt + 1,
201                           RegisterSpec.make(one.getReg() + 1, Type.VOID));
202                wordAt += 2;
203            } else {
204                wordAt++;
205            }
206        }
207
208        result.setImmutable();
209        return result;
210    }
211}
212