FirstFitLocalCombiningAllocator.java revision 38b61748553bd387bc36b6bd82a8667b6c5f5934
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.ssa.back;
18
19import com.android.dx.rop.code.*;
20import com.android.dx.rop.cst.CstInteger;
21import com.android.dx.ssa.InterferenceRegisterMapper;
22import com.android.dx.ssa.RegisterMapper;
23import com.android.dx.ssa.SsaInsn;
24import com.android.dx.ssa.SsaMethod;
25import com.android.dx.ssa.NormalSsaInsn;
26import com.android.dx.ssa.PhiInsn;
27import com.android.dx.ssa.Optimizer;
28import com.android.dx.ssa.SsaBasicBlock;
29import com.android.dx.util.IntSet;
30import com.android.dx.util.IntIterator;
31
32import java.util.ArrayList;
33import java.util.BitSet;
34import java.util.Map;
35import java.util.TreeMap;
36
37/**
38 * Allocates registers in a first-fit fashion, with the bottom reserved for
39 * method parameters and all SSAregisters representing the same local variable
40 * kept together if possible.
41 */
42public class FirstFitLocalCombiningAllocator extends RegisterAllocator {
43    /** local debug flag */
44    private static final boolean DEBUG = false;
45
46    /** maps local variable to a list of associated SSA registers */
47    private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables;
48
49    /** list of move-result-pesudo instructions seen in this method */
50    private final ArrayList<NormalSsaInsn> moveResultPseudoInsns;
51
52    /** list of invoke-range instructions seen in this method */
53    private final ArrayList<NormalSsaInsn> invokeRangeInsns;
54
55    /** list of phi instructions seen in this method */
56    private final ArrayList<PhiInsn> phiInsns;
57
58    /** indexed by SSA reg; the set of SSA regs we've mapped */
59    private final BitSet ssaRegsMapped;
60
61    /** Register mapper which will be our result */
62    private final InterferenceRegisterMapper mapper;
63
64    /** end of rop registers range (starting at 0) reserved for parameters */
65    private final int paramRangeEnd;
66
67    /** set of rop registers reserved for parameters or local variables */
68    private final BitSet reservedRopRegs;
69
70    /** set of rop registers that have been used by anything */
71    private final BitSet usedRopRegs;
72
73    /** true if converter should take steps to minimize rop-form registers */
74    private final boolean minimizeRegisters;
75
76    /**
77     * Constructs instance.
78     *
79     * @param ssaMeth {@code non-null;} method to process
80     * @param interference non-null interference graph for SSA registers
81     * @param minimizeRegisters true if converter should take steps to
82     * minimize rop-form registers
83     */
84    public FirstFitLocalCombiningAllocator(
85            SsaMethod ssaMeth, InterferenceGraph interference,
86            boolean minimizeRegisters) {
87        super(ssaMeth, interference);
88
89        ssaRegsMapped = new BitSet(ssaMeth.getRegCount());
90
91        mapper = new InterferenceRegisterMapper(
92                interference, ssaMeth.getRegCount());
93
94        this.minimizeRegisters = minimizeRegisters;
95
96        /*
97         * Reserve space for the params at the bottom of the register
98         * space. Later, we'll flip the params to the end of the register
99         * space.
100         */
101
102        paramRangeEnd = ssaMeth.getParamWidth();
103
104        reservedRopRegs = new BitSet(paramRangeEnd * 2);
105        reservedRopRegs.set(0, paramRangeEnd);
106        usedRopRegs = new BitSet(paramRangeEnd * 2);
107        localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>();
108        moveResultPseudoInsns = new ArrayList<NormalSsaInsn>();
109        invokeRangeInsns = new ArrayList<NormalSsaInsn>();
110        phiInsns = new ArrayList<PhiInsn>();
111    }
112
113    /** {@inheritDoc} */
114    @Override
115    public boolean wantsParamsMovedHigh() {
116        return true;
117    }
118
119    /** {@inheritDoc} */
120    @Override
121    public RegisterMapper allocateRegisters() {
122
123        analyzeInstructions();
124
125        if (DEBUG) {
126            printLocalVars();
127        }
128
129        if (DEBUG) System.out.println("--->Mapping local-associated params");
130        handleLocalAssociatedParams();
131
132        if (DEBUG) System.out.println("--->Mapping other params");
133        handleUnassociatedParameters();
134
135        if (DEBUG) System.out.println("--->Mapping invoke-range");
136        handleInvokeRangeInsns();
137
138        if (DEBUG) {
139            System.out.println("--->Mapping local-associated non-params");
140        }
141        handleLocalAssociatedOther();
142
143        if (DEBUG) System.out.println("--->Mapping check-cast results");
144        handleCheckCastResults();
145
146        if (DEBUG) System.out.println("--->Mapping phis");
147        handlePhiInsns();
148
149        if (DEBUG) System.out.println("--->Mapping others");
150        handleNormalUnassociated();
151
152        return mapper;
153    }
154
155    /**
156     * Dumps local variable table to stdout for debugging.
157     */
158    private void printLocalVars() {
159        System.out.println("Printing local vars");
160        for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e :
161                localVariables.entrySet()) {
162            StringBuilder regs = new StringBuilder();
163
164            regs.append('{');
165            regs.append(' ');
166            for (RegisterSpec reg : e.getValue()) {
167                regs.append('v');
168                regs.append(reg.getReg());
169                regs.append(' ');
170            }
171            regs.append('}');
172            System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs);
173        }
174    }
175
176    /**
177     * Maps all local-associated parameters to rop registers.
178     */
179    private void handleLocalAssociatedParams() {
180        for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) {
181            int sz = ssaRegs.size();
182            int paramIndex = -1;
183            int paramCategory = 0;
184
185            // First, find out if this local variable is a parameter.
186            for (int i = 0; i < sz; i++) {
187                RegisterSpec ssaSpec = ssaRegs.get(i);
188                int ssaReg = ssaSpec.getReg();
189
190                paramIndex = getParameterIndexForReg(ssaReg);
191
192                if (paramIndex >= 0) {
193                    paramCategory = ssaSpec.getCategory();
194                    addMapping(ssaSpec, paramIndex);
195                    break;
196                }
197            }
198
199            if (paramIndex < 0) {
200                // This local wasn't a parameter.
201                continue;
202            }
203
204            // Any remaining local-associated registers will be mapped later.
205            tryMapRegs(ssaRegs, paramIndex, paramCategory, true);
206        }
207    }
208
209    /**
210     * Gets the parameter index for SSA registers that are method parameters.
211     * {@code -1} is returned for non-parameter registers.
212     *
213     * @param ssaReg {@code >=0;} SSA register to look up
214     * @return parameter index or {@code -1} if not a parameter
215     */
216    private int getParameterIndexForReg(int ssaReg) {
217        SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg);
218        if (defInsn == null) {
219            return -1;
220        }
221
222        Rop opcode = defInsn.getOpcode();
223
224        // opcode == null for phi insns.
225        if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) {
226            CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn();
227            return  ((CstInteger) origInsn.getConstant()).getValue();
228        }
229
230        return -1;
231    }
232
233    /**
234     * Maps all local-associated registers that are not parameters.
235     * Tries to find an unreserved range that's wide enough for all of
236     * the SSA registers, and then tries to map them all to that
237     * range. If not all fit, a new range is tried until all registers
238     * have been fit.
239     */
240    private void handleLocalAssociatedOther() {
241        for (ArrayList<RegisterSpec> specs : localVariables.values()) {
242            int ropReg = paramRangeEnd;
243
244            boolean done = false;
245            do {
246                int maxCategory = 1;
247
248                // Compute max category for remaining unmapped registers.
249                int sz = specs.size();
250                for (int i = 0; i < sz; i++) {
251                    RegisterSpec ssaSpec = specs.get(i);
252                    int category = ssaSpec.getCategory();
253                    if (!ssaRegsMapped.get(ssaSpec.getReg())
254                            && category > maxCategory) {
255                        maxCategory = category;
256                    }
257                }
258
259                ropReg = findRopRegForLocal(ropReg, maxCategory);
260                if (canMapRegs(specs, ropReg)) {
261                    done = tryMapRegs(specs, ropReg, maxCategory, true);
262                }
263
264                // Increment for next call to findRopRegForLocal.
265                ropReg++;
266            } while (!done);
267        }
268    }
269
270    /**
271     * Tries to map a list of SSA registers into the a rop reg, marking
272     * used rop space as reserved. SSA registers that don't fit are left
273     * unmapped.
274     *
275     * @param specs {@code non-null;} SSA registers to attempt to map
276     * @param ropReg {@code >=0;} rop register to map to
277     * @param maxAllowedCategory {@code 1..2;} maximum category
278     * allowed in mapping.
279     * @param markReserved do so if {@code true}
280     * @return {@code true} if all registers were mapped, {@code false}
281     * if some remain unmapped
282     */
283    private boolean tryMapRegs(
284            ArrayList<RegisterSpec> specs, int ropReg,
285            int maxAllowedCategory, boolean markReserved) {
286        boolean remaining = false;
287        for (RegisterSpec spec : specs) {
288            if (ssaRegsMapped.get(spec.getReg())) {
289                continue;
290            }
291
292            boolean succeeded;
293            succeeded = tryMapReg(spec, ropReg, maxAllowedCategory);
294            remaining = !succeeded || remaining;
295            if (succeeded && markReserved) {
296                // This only needs to be called once really with
297                // the widest category used, but <shrug>
298                markReserved(ropReg, spec.getCategory());
299            }
300        }
301        return !remaining;
302    }
303
304    /**
305     * Tries to map an SSA register to a rop register.
306     *
307     * @param ssaSpec {@code non-null;} SSA register
308     * @param ropReg {@code >=0;} rop register
309     * @param maxAllowedCategory {@code 1..2;} the maximum category
310     * that the SSA register is allowed to be
311     * @return {@code true} if map succeeded, {@code false} if not
312     */
313    private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg,
314            int maxAllowedCategory) {
315        if (ssaSpec.getCategory() <= maxAllowedCategory
316                && !ssaRegsMapped.get(ssaSpec.getReg())
317                && canMapReg(ssaSpec, ropReg)) {
318            addMapping(ssaSpec, ropReg);
319            return true;
320        }
321
322        return false;
323    }
324
325    /**
326     * Marks a range of rop registers as "reserved for a local variable."
327     *
328     * @param ropReg {@code >= 0;} rop register to reserve
329     * @param category {@code > 0;} width to reserve
330     */
331    private void markReserved(int ropReg, int category) {
332        reservedRopRegs.set(ropReg, ropReg + category, true);
333    }
334
335    /**
336     * Checks to see if any rop registers in the specified range are reserved
337     * for local variables or parameters.
338     *
339     * @param ropRangeStart {@code >= 0;} lowest rop register
340     * @param width {@code > 0;} number of rop registers in range.
341     * @return {@code true} if any register in range is marked reserved
342     */
343    private boolean rangeContainsReserved(int ropRangeStart, int width) {
344        for (int i = ropRangeStart; i < (ropRangeStart + width); i++) {
345            if (reservedRopRegs.get(i)) {
346                return true;
347            }
348        }
349        return false;
350    }
351
352    /**
353     * Returns true if given rop register represents the {@code this} pointer
354     * for a non-static method.
355     *
356     * @param startReg rop register
357     * @return true if the "this" pointer is located here.
358     */
359    private boolean isThisPointerReg(int startReg) {
360        // "this" is always the first parameter.
361        return startReg == 0 && !ssaMeth.isStatic();
362    }
363
364    /**
365     * Finds a range of unreserved rop registers.
366     *
367     * @param startReg {@code >= 0;} a rop register to start the search at
368     * @param width {@code > 0;} the width, in registers, required.
369     * @return {@code >= 0;} start of available register range.
370     */
371    private int findNextUnreservedRopReg(int startReg, int width) {
372        int reg;
373
374        reg = reservedRopRegs.nextClearBit(startReg);
375
376        while (true) {
377            int i = 1;
378
379            while (i < width && !reservedRopRegs.get(reg + i)) {
380                i++;
381            }
382
383            if (i == width) {
384                return reg;
385            }
386
387            reg = reservedRopRegs.nextClearBit(reg + i);
388        }
389    }
390
391    /**
392     * Finds a range of rop regs that can be used for local variables.
393     * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any
394     * rop register that has not yet been used.
395     *
396     * @param startReg {@code >= 0;} a rop register to start the search at
397     * @param width {@code > 0;} the width, in registers, required.
398     * @return {@code >= 0;} start of available register range.
399     */
400    private int findRopRegForLocal(int startReg, int width) {
401        int reg;
402
403        reg = usedRopRegs.nextClearBit(startReg);
404
405        while (true) {
406            int i = 1;
407
408            while (i < width && !usedRopRegs.get(reg + i)) {
409                i++;
410            }
411
412            if (i == width) {
413                return reg;
414            }
415
416            reg = usedRopRegs.nextClearBit(reg + i);
417        }
418    }
419
420    /**
421     * Maps any parameter that isn't local-associated, which can happen
422     * in the case where there is no java debug info.
423     */
424    private void handleUnassociatedParameters() {
425        int szSsaRegs = ssaMeth.getRegCount();
426
427        for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
428            if (ssaRegsMapped.get(ssaReg)) {
429                // We already did this one above
430                continue;
431            }
432
433            int paramIndex = getParameterIndexForReg(ssaReg);
434
435            RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
436            if (paramIndex >= 0) {
437                addMapping(ssaSpec, paramIndex);
438            }
439        }
440    }
441
442    /**
443     * Handles all insns that want a register range for their sources.
444     */
445    private void handleInvokeRangeInsns() {
446        for (NormalSsaInsn insn : invokeRangeInsns) {
447            adjustAndMapSourceRangeRange(insn);
448        }
449    }
450
451    /**
452     * Handles check cast results to reuse the same source register.
453     * Inserts a move if it can't map the same register to both and the
454     * check cast is not caught.
455     */
456    private void handleCheckCastResults() {
457        for (NormalSsaInsn insn : moveResultPseudoInsns) {
458            RegisterSpec moveRegSpec = insn.getResult();
459            int moveReg = moveRegSpec.getReg();
460            BitSet predBlocks = insn.getBlock().getPredecessors();
461
462            // Expect one predecessor block only
463            if (predBlocks.cardinality() != 1) {
464                continue;
465            }
466
467            SsaBasicBlock predBlock =
468                    ssaMeth.getBlocks().get(predBlocks.nextSetBit(0));
469            ArrayList<SsaInsn> insnList = predBlock.getInsns();
470
471            /**
472             * If the predecessor block has a check-cast, it will be the last
473             * instruction
474             */
475            SsaInsn checkCastInsn = insnList.get(insnList.size() - 1);
476            if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) {
477                continue;
478            }
479
480            RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0);
481            int checkReg = checkRegSpec.getReg();
482
483            /**
484             * See if either register is already mapped. Most likely the move
485             * result will be mapped already since the cast result is stored
486             * in a local variable.
487             */
488            int category = checkRegSpec.getCategory();
489            boolean moveMapped = ssaRegsMapped.get(moveReg);
490            boolean checkMapped = ssaRegsMapped.get(checkReg);
491            if (moveMapped & !checkMapped) {
492                int moveRopReg = mapper.oldToNew(moveReg);
493                checkMapped = tryMapReg(checkRegSpec, moveRopReg, category);
494            }
495            if (checkMapped & !moveMapped) {
496                int checkRopReg = mapper.oldToNew(checkReg);
497                moveMapped = tryMapReg(moveRegSpec, checkRopReg, category);
498            }
499
500            // Map any unmapped registers to anything available
501            if (!moveMapped || !checkMapped) {
502                int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
503                ArrayList<RegisterSpec> ssaRegs =
504                    new ArrayList<RegisterSpec>(2);
505                ssaRegs.add(moveRegSpec);
506                ssaRegs.add(checkRegSpec);
507
508                while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
509                    ropReg = findNextUnreservedRopReg(ropReg + 1, category);
510                }
511            }
512
513            /*
514             * If source and result have a different mapping, insert a move so
515             * they can have the same mapping. Don't do this if the check cast
516             * is caught, since it will overwrite a potentially live value.
517             */
518            boolean hasExceptionHandlers =
519                checkCastInsn.getOriginalRopInsn().getCatches().size() != 0;
520            int moveRopReg = mapper.oldToNew(moveReg);
521            int checkRopReg = mapper.oldToNew(checkReg);
522            if (moveRopReg != checkRopReg && !hasExceptionHandlers) {
523                ((NormalSsaInsn) checkCastInsn).changeOneSource(0,
524                        insertMoveBefore(checkCastInsn, checkRegSpec));
525                addMapping(checkCastInsn.getSources().get(0), moveRopReg);
526            }
527        }
528    }
529
530    /**
531    * Handles all phi instructions, trying to map them to a common register.
532    */
533    private void handlePhiInsns() {
534        for (PhiInsn insn : phiInsns) {
535            processPhiInsn(insn);
536        }
537    }
538
539    /**
540     * Maps all non-parameter, non-local variable registers.
541     */
542    private void handleNormalUnassociated() {
543        int szSsaRegs = ssaMeth.getRegCount();
544
545        for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
546            if (ssaRegsMapped.get(ssaReg)) {
547                // We already did this one
548                continue;
549            }
550
551            RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
552
553            if (ssaSpec == null) continue;
554
555            int category = ssaSpec.getCategory();
556            // Find a rop reg that does not interfere
557            int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
558            while (!canMapReg(ssaSpec, ropReg)) {
559                ropReg = findNextUnreservedRopReg(ropReg + 1, category);
560            }
561
562            addMapping(ssaSpec, ropReg);
563        }
564    }
565
566    /**
567     * Checks to see if a list of SSA registers can all be mapped into
568     * the same rop reg. Ignores registers that have already been mapped,
569     * and checks the interference graph and ensures the range does not
570     * cross the parameter range.
571     *
572     * @param specs {@code non-null;} SSA registers to check
573     * @param ropReg {@code >=0;} rop register to check mapping to
574     * @return {@code true} if all unmapped registers can be mapped
575     */
576    private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) {
577        for (RegisterSpec spec : specs) {
578            if (ssaRegsMapped.get(spec.getReg())) continue;
579            if (!canMapReg(spec, ropReg)) return false;
580        }
581        return true;
582    }
583
584    /**
585     * Checks to see if {@code ssaSpec} can be mapped to
586     * {@code ropReg}. Checks interference graph and ensures
587     * the range does not cross the parameter range.
588     *
589     * @param ssaSpec {@code non-null;} SSA spec
590     * @param ropReg prosepctive new-namespace reg
591     * @return {@code true} if mapping is possible
592     */
593    private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) {
594        int category = ssaSpec.getCategory();
595        return !(spansParamRange(ropReg, category)
596                || mapper.interferes(ssaSpec, ropReg));
597    }
598
599    /**
600     * Returns true if the specified rop register + category
601     * will cross the boundry between the lower {@code paramWidth}
602     * registers reserved for method params and the upper registers. We cannot
603     * allocate a register that spans the param block and the normal block,
604     * because we will be moving the param block to high registers later.
605     *
606     * @param ssaReg register in new namespace
607     * @param category width that the register will have
608     * @return {@code true} in the case noted above
609     */
610    private boolean spansParamRange(int ssaReg, int category) {
611        return ((ssaReg < paramRangeEnd)
612                && ((ssaReg + category) > paramRangeEnd));
613    }
614
615    /**
616     * Analyze each instruction and find out all the local variable assignments
617     * and move-result-pseudo/invoke-range instrucitons.
618     */
619    private void analyzeInstructions() {
620        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
621            /** {@inheritDoc} */
622            public void visitMoveInsn(NormalSsaInsn insn) {
623                processInsn(insn);
624            }
625
626            /** {@inheritDoc} */
627            public void visitPhiInsn(PhiInsn insn) {
628                processInsn(insn);
629            }
630
631            /** {@inheritDoc} */
632            public void visitNonMoveInsn(NormalSsaInsn insn) {
633                processInsn(insn);
634            }
635
636            /**
637             * This method collects three types of instructions:
638             *
639             * 1) Adds a local variable assignment to the
640             *    {@code localVariables} map.
641             * 2) Add move-result-pseudo to the
642             *    {@code moveResultPseudoInsns} list.
643             * 3) Add invoke-range to the
644             *    {@code invokeRangeInsns} list.
645             *
646             * @param insn {@code non-null;} insn that may represent a
647             * local variable assignment
648             */
649            private void processInsn(SsaInsn insn) {
650                RegisterSpec assignment;
651                assignment = insn.getLocalAssignment();
652
653                if (assignment != null) {
654                    LocalItem local = assignment.getLocalItem();
655
656                    ArrayList<RegisterSpec> regList
657                        = localVariables.get(local);
658
659                    if (regList == null) {
660                        regList = new ArrayList<RegisterSpec>();
661                        localVariables.put(local, regList);
662                    }
663
664                    regList.add(assignment);
665                }
666
667                if (insn instanceof NormalSsaInsn) {
668                    if (insn.getOpcode().getOpcode() ==
669                            RegOps.MOVE_RESULT_PSEUDO) {
670                        moveResultPseudoInsns.add((NormalSsaInsn) insn);
671                    } else if (Optimizer.getAdvice().requiresSourcesInOrder(
672                            insn.getOriginalRopInsn().getOpcode(),
673                            insn.getSources())) {
674                        invokeRangeInsns.add((NormalSsaInsn) insn);
675                    }
676                } else if (insn instanceof PhiInsn) {
677                    phiInsns.add((PhiInsn) insn);
678                }
679
680            }
681        });
682    }
683
684    /**
685     * Adds a mapping from an SSA register to a rop register.
686     * {@link #canMapReg} should have already been called.
687     *
688     * @param ssaSpec {@code non-null;} SSA register to map from
689     * @param ropReg {@code >=0;} rop register to map to
690     */
691    private void addMapping(RegisterSpec ssaSpec, int ropReg) {
692        int ssaReg = ssaSpec.getReg();
693
694        // An assertion.
695        if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) {
696            throw new RuntimeException(
697                    "attempt to add invalid register mapping");
698        }
699
700        if (DEBUG) {
701            System.out.printf("Add mapping s%d -> v%d c:%d\n",
702                    ssaSpec.getReg(), ropReg, ssaSpec.getCategory());
703        }
704
705        int category = ssaSpec.getCategory();
706        mapper.addMapping(ssaSpec.getReg(), ropReg, category);
707        ssaRegsMapped.set(ssaReg);
708        usedRopRegs.set(ropReg, ropReg + category);
709    }
710
711
712    /**
713     * Maps the source registers of the specified instruction such that they
714     * will fall in a contiguous range in rop form. Moves are inserted as
715     * necessary to allow the range to be allocated.
716     *
717     * @param insn {@code non-null;} insn whos sources to process
718     */
719    private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) {
720        int newRegStart = findRangeAndAdjust(insn);
721
722        RegisterSpecList sources = insn.getSources();
723        int szSources = sources.size();
724        int nextRopReg = newRegStart;
725
726        for (int i = 0; i < szSources; i++) {
727            RegisterSpec source = sources.get(i);
728            int sourceReg = source.getReg();
729            int category = source.getCategory();
730            int curRopReg = nextRopReg;
731            nextRopReg += category;
732
733            if (ssaRegsMapped.get(sourceReg)) {
734                continue;
735            }
736
737            LocalItem localItem = getLocalItemForReg(sourceReg);
738            addMapping(source, curRopReg);
739
740            if (localItem != null) {
741                markReserved(curRopReg, category);
742                ArrayList<RegisterSpec> similarRegisters
743                        = localVariables.get(localItem);
744
745                int szSimilar = similarRegisters.size();
746
747                /*
748                 * Try to map all SSA registers also associated with
749                 * this local.
750                 */
751                for (int j = 0; j < szSimilar; j++) {
752                    RegisterSpec similarSpec = similarRegisters.get(j);
753                    int similarReg = similarSpec.getReg();
754
755                    // Don't map anything that's also a source.
756                    if (-1 != sources.indexOfRegister(similarReg)) {
757                        continue;
758                    }
759
760                    // Registers left unmapped will get handled later.
761                    tryMapReg(similarSpec, curRopReg, category);
762                }
763            }
764        }
765    }
766
767    /**
768     * Find a contiguous rop register range that fits the specified
769     * instruction's sources. First, try to center the range around
770     * sources that have already been mapped to rop registers. If that fails,
771     * just find a new contiguous range that doesn't interfere.
772     *
773     * @param insn {@code non-null;} the insn whose sources need to
774     * fit. Must be last insn in basic block.
775     * @return {@code >= 0;} rop register of start of range
776     */
777    private int findRangeAndAdjust(NormalSsaInsn insn) {
778        RegisterSpecList sources = insn.getSources();
779        int szSources = sources.size();
780        // the category for each source index
781        int categoriesForIndex[] = new int[szSources];
782        int rangeLength = 0;
783
784        // Compute rangeLength and categoriesForIndex
785        for (int i = 0; i < szSources; i++) {
786            int category = sources.get(i).getCategory();
787            categoriesForIndex[i] = category;
788            rangeLength += categoriesForIndex[i];
789        }
790
791        // the highest score of fits tried so far
792        int maxScore = Integer.MIN_VALUE;
793        // the high scoring range's start
794        int resultRangeStart = -1;
795        // by source index: set of sources needing moves in high scoring plan
796        BitSet resultMovesRequired = null;
797
798        /*
799         * First, go through each source that's already been mapped. Try
800         * to center the range around the rop register this source is mapped
801         * to.
802         */
803        int rangeStartOffset = 0;
804        for (int i = 0; i < szSources; i++) {
805            int ssaCenterReg = sources.get(i).getReg();
806
807            if (i != 0) {
808                rangeStartOffset -= categoriesForIndex[i - 1];
809            }
810            if (!ssaRegsMapped.get(ssaCenterReg)) {
811                continue;
812            }
813
814            int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset;
815
816            if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) {
817                continue;
818            }
819
820            BitSet curMovesRequired = new BitSet(szSources);
821
822            int fitWidth
823                    = fitPlanForRange(rangeStart, insn, categoriesForIndex,
824                    curMovesRequired);
825
826            if (fitWidth < 0) {
827                continue;
828            }
829
830            int score = fitWidth - curMovesRequired.cardinality();
831
832            if (score > maxScore) {
833                maxScore = score;
834                resultRangeStart = rangeStart;
835                resultMovesRequired = curMovesRequired;
836            }
837
838            if (fitWidth == rangeLength) {
839                // We can't do any better than this, so stop here
840                break;
841            }
842        }
843
844        /*
845         * If we were unable to find a plan for a fit centered around
846         * an already-mapped source, just try to find a range of
847         * registers we can move the range into.
848         */
849
850        if (resultRangeStart == -1) {
851            resultMovesRequired = new BitSet(szSources);
852
853            resultRangeStart = findAnyFittingRange(insn, rangeLength,
854                    categoriesForIndex, resultMovesRequired);
855        }
856
857        /*
858         * Now, insert any moves required.
859         */
860
861        for (int i = resultMovesRequired.nextSetBit(0); i >= 0;
862             i = resultMovesRequired.nextSetBit(i+1)) {
863            insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i)));
864        }
865
866        return resultRangeStart;
867    }
868
869    /**
870     * Finds an unreserved range that will fit the sources of the
871     * specified instruction. Does not bother trying to center the range
872     * around an already-mapped source register;
873     *
874     * @param insn {@code non-null;} insn to build range for
875     * @param rangeLength {@code >=0;} length required in register units
876     * @param categoriesForIndex {@code non-null;} indexed by source index;
877     * the category for each source
878     * @param outMovesRequired {@code non-null;} an output parameter indexed by
879     * source index that will contain the set of sources which need
880     * moves inserted
881     * @return the rop register that starts the fitting range
882     */
883    private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength,
884            int[] categoriesForIndex, BitSet outMovesRequired) {
885        int rangeStart = paramRangeEnd;
886        while (true) {
887            rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength);
888            int fitWidth
889                    = fitPlanForRange(rangeStart, insn,
890                    categoriesForIndex, outMovesRequired);
891
892            if (fitWidth >= 0) {
893                break;
894            }
895            rangeStart++;
896            outMovesRequired.clear();
897        }
898        return rangeStart;
899    }
900
901    /**
902     * Attempts to build a plan for fitting a range of sources into rop
903     * registers.
904     *
905     * @param ropReg {@code >= 0;} rop reg that begins range
906     * @param insn {@code non-null;} insn to plan range for
907     * @param categoriesForIndex {@code non-null;} indexed by source index;
908     * the category for each source
909     * @param outMovesRequired {@code non-null;} an output parameter indexed by
910     * source index that will contain the set of sources which need
911     * moves inserted
912     * @return the width of the fit that that does not involve added moves or
913     * {@code -1} if "no fit possible"
914     */
915    private int fitPlanForRange(int ropReg, NormalSsaInsn insn,
916            int[] categoriesForIndex, BitSet outMovesRequired) {
917        RegisterSpecList sources = insn.getSources();
918        int szSources = sources.size();
919        int fitWidth = 0;
920        IntSet liveOut = insn.getBlock().getLiveOutRegs();
921        RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut);
922
923        // An SSA reg may only be mapped into a range once.
924        BitSet seen = new BitSet(ssaMeth.getRegCount());
925
926        for (int i = 0; i < szSources ; i++) {
927            RegisterSpec ssaSpec = sources.get(i);
928            int ssaReg = ssaSpec.getReg();
929            int category = categoriesForIndex[i];
930
931            if (i != 0) {
932                ropReg += categoriesForIndex[i-1];
933            }
934
935            if (ssaRegsMapped.get(ssaReg)
936                    && mapper.oldToNew(ssaReg) == ropReg) {
937                // This is a register that is already mapped appropriately.
938                fitWidth += category;
939            } else if (rangeContainsReserved(ropReg, category)) {
940                fitWidth = -1;
941                break;
942            } else if (!ssaRegsMapped.get(ssaReg)
943                    && canMapReg(ssaSpec, ropReg)
944                    && !seen.get(ssaReg)) {
945                // This is a register that can be mapped appropriately.
946                fitWidth += category;
947            } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category)
948                    && !mapper.areAnyPinned(sources, ropReg, category)) {
949                /*
950                 * This is a source that can be moved. We can insert a
951                 * move as long as:
952                 *
953                 *   * no SSA register pinned to the desired rop reg
954                 *     is live out on the block
955                 *
956                 *   * no SSA register pinned to desired rop reg is
957                 *     a source of this insn (since this may require
958                 *     overlapping moves, which we can't presently handle)
959                 */
960
961                outMovesRequired.set(i);
962            } else {
963                fitWidth = -1;
964                break;
965            }
966
967            seen.set(ssaReg);
968        }
969        return fitWidth;
970    }
971
972    /**
973     * Converts a bit set of SSA registers into a RegisterSpecList containing
974     * the definition specs of all the registers.
975     *
976     * @param ssaSet {@code non-null;} set of SSA registers
977     * @return list of RegisterSpecs as noted above
978     */
979    RegisterSpecList ssaSetToSpecs(IntSet ssaSet) {
980        RegisterSpecList result = new RegisterSpecList(ssaSet.elements());
981
982        IntIterator iter = ssaSet.iterator();
983
984        int i = 0;
985        while (iter.hasNext()) {
986            result.set(i++, getDefinitionSpecForSsaReg(iter.next()));
987        }
988
989        return result;
990    }
991
992    /**
993     * Gets a local item associated with an ssa register, if one exists.
994     *
995     * @param ssaReg {@code >= 0;} SSA register
996     * @return {@code null-ok;} associated local item or null
997     */
998    private LocalItem getLocalItemForReg(int ssaReg) {
999        for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry :
1000                 localVariables.entrySet()) {
1001            for (RegisterSpec spec : entry.getValue()) {
1002                if (spec.getReg() == ssaReg) {
1003                    return entry.getKey();
1004                }
1005            }
1006        }
1007
1008        return null;
1009    }
1010
1011    /**
1012     * Attempts to map the sources and result of a phi to a common register.
1013     * Will try existing mappings first, from most to least common. If none
1014     * of the registers have mappings yet, a new mapping is created.
1015     */
1016    private void processPhiInsn(PhiInsn insn) {
1017        RegisterSpec result = insn.getResult();
1018        int resultReg = result.getReg();
1019        int category = result.getCategory();
1020
1021        RegisterSpecList sources = insn.getSources();
1022        int sourcesSize = sources.size();
1023
1024        // List of phi sources / result that need mapping
1025        ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>();
1026
1027        // Track how many times a particular mapping is found
1028        Multiset mapSet = new Multiset(sourcesSize + 1);
1029
1030        /*
1031         * If the result of the phi has an existing mapping, get it.
1032         * Otherwise, add it to the list of regs that need mapping.
1033         */
1034        if (ssaRegsMapped.get(resultReg)) {
1035            mapSet.add(mapper.oldToNew(resultReg));
1036        } else {
1037            ssaRegs.add(result);
1038        }
1039
1040        for (int i = 0; i < sourcesSize; i++) {
1041            RegisterSpec source = sources.get(i);
1042            int sourceReg = source.getReg();
1043
1044            /*
1045             * If a source of the phi has an existing mapping, get it.
1046             * Otherwise, add it to the list of regs that need mapping.
1047             */
1048            if (ssaRegsMapped.get(sourceReg)) {
1049                mapSet.add(mapper.oldToNew(sourceReg));
1050            } else {
1051                ssaRegs.add(source);
1052            }
1053        }
1054
1055        // Try all existing mappings, with the most common ones first
1056        for (int i = 0; i < mapSet.getSize(); i++) {
1057            int maxReg = mapSet.getAndRemoveHighestCount();
1058            tryMapRegs(ssaRegs, maxReg, category, false);
1059        }
1060
1061        // Map any remaining unmapped regs with whatever fits
1062        int mapReg = findNextUnreservedRopReg(paramRangeEnd, category);
1063        while (!tryMapRegs(ssaRegs, mapReg, category, false)) {
1064            mapReg = findNextUnreservedRopReg(mapReg + 1, category);
1065        }
1066    }
1067
1068    // A set that tracks how often elements are added to it.
1069    private static class Multiset {
1070        private final int[] reg;
1071        private final int[] count;
1072        private int size;
1073
1074        /**
1075         * Constructs an instance.
1076         *
1077         * @param maxSize the maximum distinct elements the set may have
1078         */
1079        public Multiset(int maxSize) {
1080            reg = new int[maxSize];
1081            count = new int[maxSize];
1082            size = 0;
1083        }
1084
1085        /**
1086         * Adds an element to the set.
1087         *
1088         * @param element element to add
1089         */
1090        public void add(int element) {
1091            for (int i = 0; i < size; i++) {
1092                if (reg[i] == element) {
1093                    count[i]++;
1094                    return;
1095                }
1096            }
1097
1098            reg[size] = element;
1099            count[size] = 1;
1100            size++;
1101        }
1102
1103        /**
1104         * Searches the set for the element that has been added the most.
1105         * In the case of a tie, the element that was added first is returned.
1106         * Then, it clears the count on that element. The size of the set
1107         * remains unchanged.
1108         *
1109         * @return element with the highest count
1110         */
1111        public int getAndRemoveHighestCount() {
1112            int maxIndex = -1;
1113            int maxReg = -1;
1114            int maxCount = 0;
1115
1116            for (int i = 0; i < size; i++) {
1117                if (maxCount < count[i]) {
1118                    maxIndex = i;
1119                    maxReg = reg[i];
1120                    maxCount = count[i];
1121                }
1122            }
1123
1124            count[maxIndex] = 0;
1125            return maxReg;
1126        }
1127
1128        /**
1129         * Gets the number of distinct elements in the set.
1130         *
1131         * @return size of the set
1132         */
1133        public int getSize() {
1134            return size;
1135        }
1136    }
1137}
1138