SsaMethod.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
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;
18
19import com.android.dx.rop.code.BasicBlockList;
20import com.android.dx.rop.code.PlainInsn;
21import com.android.dx.rop.code.RegisterSpec;
22import com.android.dx.rop.code.RegisterSpecList;
23import com.android.dx.rop.code.RopMethod;
24import com.android.dx.rop.code.Rops;
25import com.android.dx.rop.code.SourcePosition;
26import com.android.dx.rop.code.Insn;
27import com.android.dx.rop.code.RegOps;
28import com.android.dx.rop.code.Rop;
29import com.android.dx.util.IntList;
30
31import java.util.ArrayList;
32import java.util.BitSet;
33import java.util.Collections;
34import java.util.List;
35import java.util.Stack;
36import java.util.Set;
37
38/**
39 * A method in SSA form
40 */
41public final class SsaMethod {
42
43    /** basic blocks, indexed by block index */
44    private ArrayList<SsaBasicBlock> blocks;
45
46    /** Index of first executed block in method */
47    private int entryBlockIndex;
48
49    /**
50     * Index of exit block, which exists only in SSA form,
51     * or or -1 if there is none
52     */
53    private int exitBlockIndex;
54
55    private int registerCount;
56    private int spareRegisterBase;
57    private int borrowedSpareRegisters;
58
59    /** really one greater than the max label */
60    private int maxLabel;
61
62    /** the total width, in register-units, of the method's parameters */
63    private final int paramWidth;
64
65    /** true if this method has no 'this' pointer argument */
66    private final boolean isStatic;
67
68    /**
69     * indexed by register: the insn where said register is defined or null
70     * if undefined. null until (lazily) created.
71     */
72    private SsaInsn[] definitionList;
73
74    /** indexed by register: the list of all insns that use a register */
75    private ArrayList<SsaInsn>[] useList;
76    /** A version of useList with each List unmodifiable */
77    private List<SsaInsn>[] unmodifiableUseList;
78
79    /**
80     * "back-convert mode". Set during back-conversion when registers
81     * are about to be mapped into a non-SSA namespace. When true,
82     * use and def lists are unavailable.
83     *
84     * TODO remove this mode, plase the functionality elsewhere
85     */
86    private boolean backMode = false;
87
88    /**
89     * @param rmeth RopMethod to convert from
90     * @param paramWidth the total width, in register-units, of the
91     * method's parameters
92     * @param isStatic true if this method has no 'this' pointer argument
93     * @return SsaMethod representation
94     */
95    static SsaMethod newFromRopMethod(RopMethod rmeth, int paramWidth,
96            boolean isStatic) {
97        SsaMethod result;
98
99        result = new SsaMethod(paramWidth, isStatic);
100
101        result.maxLabel = rmeth.getBlocks().getMaxLabel();
102        result.registerCount = rmeth.getBlocks().getRegCount();
103        result.spareRegisterBase = result.registerCount;
104
105        result.convertRopToSsaBlocks(rmeth);
106
107        return result;
108    }
109
110    /**
111     * Builds a BitSet of block indices from a basic block list and a list
112     * of labels taken from Rop form
113     * @param blocks Rop blocks
114     * @param labelList list of rop block labels
115     * @return BitSet of block indices
116     */
117    static BitSet bitSetFromLabelList(BasicBlockList blocks,
118            IntList labelList) {
119
120        BitSet result;
121
122        result = new BitSet(blocks.size());
123
124        for (int i = 0, sz = labelList.size() ; i < sz ; i++) {
125            result.set(blocks.indexOfLabel(labelList.get(i)));
126        }
127
128        return result;
129    }
130
131    /**
132     * Builds an IntList of block indices from a basic block list and a list
133     * of labels taken from Rop form
134     * @param ropBlocks Rop blocks
135     * @param labelList list of rop block labels
136     * @return IntList of block indices
137     */
138    public static IntList indexListFromLabelList(BasicBlockList ropBlocks,
139            IntList labelList) {
140
141        IntList result;
142
143        result = new IntList(labelList.size());
144
145        for (int i = 0, sz = labelList.size() ; i < sz ; i++) {
146            result.add(ropBlocks.indexOfLabel(labelList.get(i)));
147        }
148
149        return result;
150    }
151
152    private void convertRopToSsaBlocks(
153            RopMethod rmeth) {
154
155        BasicBlockList ropBlocks;
156
157        ropBlocks = rmeth.getBlocks();
158
159        blocks = new ArrayList<SsaBasicBlock>(ropBlocks.size() + 2);
160
161        for (int i = 0, sz = ropBlocks.size() ; i < sz ; i++) {
162            SsaBasicBlock sbb;
163
164            sbb = SsaBasicBlock.newFromRop(rmeth, i, this);
165
166            blocks.add(sbb);
167        }
168
169        // Add an no-op entry block
170        int origEntryBlockIndex = rmeth.getBlocks()
171                .indexOfLabel(rmeth.getFirstLabel());
172
173        SsaBasicBlock entryBlock
174                = blocks.get(origEntryBlockIndex).insertNewPredecessor();
175
176        entryBlockIndex = entryBlock.getIndex();
177        exitBlockIndex = -1; // this gets made later
178
179    }
180
181
182    /**
183     * Creates an exit block and attaches it to the CFG if this method
184     * exits. Methods that never exit will not have an exit block. This
185     * is called after edge-splitting and phi insertion, since the edges
186     * going into the exit block should not be considered in those steps.
187     */
188    void makeExitBlock() {
189        if (exitBlockIndex >= 0) {
190            throw new RuntimeException("must be called at most once");
191        }
192
193        exitBlockIndex = blocks.size();
194        SsaBasicBlock exitBlock
195                = new SsaBasicBlock(exitBlockIndex, maxLabel++, this);
196
197        blocks.add(exitBlock);
198
199        for (SsaBasicBlock block: blocks) {
200            block.exitBlockFixup(exitBlock);
201        }
202
203        if (exitBlock.getPredecessors().cardinality() == 0) {
204            // In cases where there is no exit...
205            blocks.remove(exitBlockIndex);
206            exitBlockIndex = -1;
207            maxLabel--;
208        }
209    }
210
211    /**
212     * Constructor
213     *
214     * @param paramWidth the total width, in register-units, of the
215     * method's parameters
216     * @param isStatic true if this method has no 'this' pointer argument
217     */
218    private SsaMethod(int paramWidth, boolean isStatic) {
219        this.paramWidth = paramWidth;
220        this.isStatic = isStatic;
221    }
222
223    /**
224     * Gets a new GOTO insn.
225     *
226     * @param block block to which this GOTO will be added
227     * (not it's destination!)
228     * @return an appropriately-constructed instance.
229     */
230    private static SsaInsn getGoto(SsaBasicBlock block) {
231        return new NormalSsaInsn (
232                new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO,
233                    null, RegisterSpecList.EMPTY), block);
234    }
235
236    /**
237     * Makes a new basic block for this method,
238     * which is empty besides a single <code>GOTO</code>. Successors and
239     * predecessors are not yet set.
240     *
241     * @return new block
242     */
243    public SsaBasicBlock makeNewGotoBlock() {
244        int newIndex = blocks.size();
245        SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this);
246
247        newBlock.getInsns().add(getGoto(newBlock));
248        blocks.add(newBlock);
249
250        return newBlock;
251    }
252
253    /**
254     * @return block index of first execution block
255     */
256    public int getEntryBlockIndex() {
257        return entryBlockIndex;
258    }
259
260    /**
261     * @return first execution block
262     */
263    public SsaBasicBlock getEntryBlock() {
264        return blocks.get(entryBlockIndex);
265    }
266
267    /**
268     * @return block index of exit block or -1 if there is none
269     */
270    public int getExitBlockIndex() {
271        return exitBlockIndex;
272    }
273
274    /**
275     * @return null-ok; block of exit block or null if there is none
276     */
277    public SsaBasicBlock getExitBlock() {
278        return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex);
279    }
280
281    /**
282     * @param bi block index or -1 for none
283     * @return rop label or -1 if bi was -1
284     */
285    public int blockIndexToRopLabel(int bi) {
286        if (bi < 0) {
287            return -1;
288        }
289        return blocks.get(bi).getRopLabel();
290    }
291
292    /**
293     * @return count of registers used in this method
294     */
295    public int getRegCount() {
296        return registerCount;
297    }
298
299    /**
300     * @return the total width, in register units, of the method's
301     * parameters
302     */
303    public int getParamWidth() {
304        return paramWidth;
305    }
306
307    /**
308     * Returns true if this is a static method.
309     *
310     * @return true if this is a static method
311     */
312    public boolean isStatic() {
313        return isStatic;
314    }
315
316    /**
317     * Borrow a register to use as a temp. Used in the phi removal process.
318     * Call returnSpareRegisters() when done.
319     * @param category width (1 or 2) of the register
320     * @return register number to use
321     */
322    public int borrowSpareRegister(int category) {
323        int result;
324
325        result = spareRegisterBase + borrowedSpareRegisters;
326
327        borrowedSpareRegisters += category;
328
329        registerCount = Math.max(registerCount, result + category);
330
331        return result;
332    }
333
334    /**
335     * Returns all borrowed registers.
336     */
337    public void returnSpareRegisters() {
338        borrowedSpareRegisters = 0;
339    }
340
341    /**
342     * @return non-null; basic block list, do not modify.
343     */
344    public ArrayList<SsaBasicBlock> getBlocks() {
345        return blocks;
346    }
347
348    /**
349     * Returns the count of reachable blocks in this method: blocks that have
350     * predecessors (or are the start block)
351     *
352     * @return &gt;= 0; number of reachable basic blocks
353     */
354    public int getCountReachableBlocks() {
355        int ret = 0;
356
357        for (SsaBasicBlock b: blocks) {
358            // Blocks that have been disconnected don't count.
359            if (b.isReachable()) {
360                ret++;
361            }
362        }
363
364        return ret;
365    }
366
367    /**
368     * Remaps unversioned registers.
369     * @param mapper maps old registers to new.
370     */
371    public void mapRegisters(RegisterMapper mapper) {
372
373        for (SsaBasicBlock block: getBlocks()) {
374            for (SsaInsn insn: block.getInsns()) {
375                insn.mapRegisters(mapper);
376            }
377        }
378
379        registerCount = mapper.getNewRegisterCount();
380        spareRegisterBase = registerCount;
381    }
382
383    /**
384     * Returns the insn that defines the given register
385     * @param reg register in question
386     * @return insn (actual instance from code) that defined this reg or null
387     * if reg is not defined.
388     */
389    public SsaInsn getDefinitionForRegister(int reg) {
390        if (backMode) {
391            throw new RuntimeException("No def list in back mode");
392        }
393
394        if (definitionList != null) {
395            return definitionList[reg];
396        }
397
398        definitionList = new SsaInsn[getRegCount()];
399
400        forEachInsn(new SsaInsn.Visitor() {
401            public void visitMoveInsn (NormalSsaInsn insn) {
402                definitionList[insn.getResult().getReg()] = insn;
403            }
404            public void visitPhiInsn (PhiInsn phi) {
405                definitionList[phi.getResult().getReg()] = phi;
406            }
407            public void visitNonMoveInsn (NormalSsaInsn insn) {
408                RegisterSpec result = insn.getResult();
409                if (result != null) {
410                    definitionList[insn.getResult().getReg()] = insn;
411                }
412            }
413        });
414
415        return definitionList[reg];
416    }
417
418    /**
419     * Builds useList and unmodifiableUseList.
420     */
421    private void buildUseList() {
422        if (backMode) {
423            throw new RuntimeException("No use list in back mode");
424        }
425
426        useList = new ArrayList[registerCount];
427
428        for (int i = 0 ; i < registerCount; i++) {
429            useList[i] = new ArrayList();
430        }
431
432        forEachInsn(new SsaInsn.Visitor() {
433            /** {@inheritDoc} */
434            public void visitMoveInsn (NormalSsaInsn insn) {
435                addToUses(insn);
436            }
437            /** {@inheritDoc} */
438            public void visitPhiInsn (PhiInsn phi) {
439                addToUses(phi);
440            }
441            /** {@inheritDoc} */
442            public void visitNonMoveInsn (NormalSsaInsn insn) {
443                addToUses(insn);
444            }
445            /**
446             * Adds specified insn to the uses list for all of its sources.
447             * @param insn non-null; insn to process
448             */
449            private void addToUses(SsaInsn insn) {
450                RegisterSpecList rl = insn.getSources();
451                int sz = rl.size();
452
453                for (int i = 0; i < sz; i++) {
454                    useList[rl.get(i).getReg()].add(insn);
455                }
456            }
457        });
458
459        unmodifiableUseList = new List[registerCount];
460
461        for (int i = 0 ; i < registerCount; i++) {
462            unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]);
463        }
464    }
465
466    /**
467     * Updates the use list for a single change in source register.
468     *
469     * @param insn non-null; insn being changed
470     * @param oldSource null-ok; The source that was used, if applicable
471     * @param newSource non-null; the new source being used
472     */
473    void onSourceChanged(SsaInsn insn,
474            RegisterSpec oldSource, RegisterSpec newSource) {
475
476        if (useList == null) return;
477
478        if (oldSource != null) {
479            int reg = oldSource.getReg();
480            useList[reg].remove(insn);
481        }
482
483        int reg = newSource.getReg();
484        if (useList.length <= reg) {
485            useList = null;
486            return;
487        }
488        useList[reg].add(insn);
489    }
490
491    /**
492     * Updates the use list for a source list change.
493     *
494     * @param insn insn non-null; insn being changed. insn.getSources()
495     * must return the new source list.
496     * @param oldSources null-ok; list of sources that were previously used.
497     */
498    void onSourcesChanged(SsaInsn insn, RegisterSpecList oldSources) {
499        if (useList == null) return;
500
501        if (oldSources != null) {
502            removeFromUseList(insn, oldSources);
503        }
504
505        RegisterSpecList sources = insn.getSources();
506        int szNew = sources.size();
507
508        for(int i = 0; i < szNew; i++) {
509            int reg = sources.get(i).getReg();
510            useList[reg].add(insn);
511        }
512    }
513
514    /**
515     * Removes a given <code>insn</code> from the use lists for the given
516     * <code>oldSources</code> (rather than the sources currently
517     * returned by insn.getSources()).
518     *
519     * @param insn non-null; insn in question
520     * @param oldSources null-ok; registers whose use lists <code>insn</code>
521     * should be removed form.
522     */
523    private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) {
524        if (oldSources == null) {
525            return;
526        }
527        int szNew = oldSources.size();
528        for(int i = 0; i < szNew; i++) {
529            if (!useList[oldSources.get(i).getReg()].remove(insn)) {
530                throw new RuntimeException("use not found");
531            }
532        }
533    }
534
535    /**
536     * Adds an insn to both the use and def lists. For use when adding
537     * a new insn to the method.
538     *
539     * @param insn non-null; insn to add
540     */
541    void onInsnAdded(SsaInsn insn) {
542        onSourcesChanged(insn, null);
543        updateOneDefinition(insn, null);
544    }
545
546    /**
547      * Removes an instruction from use and def lists. For use during
548      * instruction removal.
549      *
550      * @param insn non-null; insn to remove.
551      */
552     void onInsnRemoved(SsaInsn insn) {
553         if (useList != null) {
554             removeFromUseList(insn, insn.getSources());
555         }
556
557         RegisterSpec resultReg = insn.getResult();
558         if (definitionList != null && resultReg != null) {
559             definitionList[resultReg.getReg()] = null;
560         }
561     }
562
563    /**
564     * Indicates that the instruction list has changed or the SSA register
565     * count has increased, so that internal datastructures that rely on
566     * it should be rebuild. In general, the various other on* methods
567     * should be called in preference when changes occur if they are
568     * applicable.
569     */
570    public void onInsnsChanged() {
571        // Definition list will need to be recomputed
572        definitionList = null;
573
574        // Use list will need to be recomputed
575        useList = null;
576        unmodifiableUseList = null;
577    }
578
579    /**
580     * Updates a single definition.
581     *
582     * @param insn non-null; insn who's result should be recorded as
583     * a definition
584     * @param oldResult null-ok; a previous result that should be no longer
585     * considered a definition by this insn
586     */
587    void updateOneDefinition(SsaInsn insn, RegisterSpec oldResult) {
588        if (definitionList == null) return;
589        if (oldResult != null) {
590            int reg = oldResult.getReg();
591            definitionList[reg] = null;
592        }
593
594        RegisterSpec resultReg = insn.getResult();
595        if (resultReg != null) {
596            int reg = resultReg.getReg();
597
598            if (definitionList[reg] != null) {
599                throw new RuntimeException("Duplicate add of insn");
600            } else {
601                definitionList[resultReg.getReg()] = insn;
602            }
603        }
604    }
605
606    /**
607     * Returns the list of all source uses (not results) for a register
608     * @param reg register in question
609     * @return unmodifiable instruction list
610     */
611    public List<SsaInsn> getUseListForRegister(int reg) {
612
613        if (unmodifiableUseList == null) {
614            buildUseList();
615        }
616
617        return unmodifiableUseList[reg];
618    }
619
620    /**
621     * Returns a modifiable copy of the register use list.
622     * @return modifiable copy of the use-list, indexed by register
623     */
624    public ArrayList<SsaInsn>[] getUseListCopy() {
625        if (useList == null) {
626            buildUseList();
627        }
628
629        ArrayList<SsaInsn>[] useListCopy
630                = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]);
631
632        for (int i = 0; i < registerCount; i++) {
633            useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i]));
634        }
635
636        return useListCopy;
637    }
638
639    /**
640     * Checks to see if the given SSA reg is ever associated with a local
641     * local variable. Each SSA reg may be associated with at most one
642     * local var.
643     *
644     * @param spec non-null; ssa reg
645     * @return true if reg is ever associated with a local
646     */
647    public boolean isRegALocal(RegisterSpec spec) {
648        SsaInsn defn = getDefinitionForRegister(spec.getReg());
649
650        if (defn == null) {
651            // version 0 registers are never used as locals
652            return false;
653        }
654
655        // Does the definition have a local associated with it?
656        if (defn.getLocalAssignment() != null) return true;
657
658        // If not, is there a mark-local insn?
659        for (SsaInsn use: getUseListForRegister(spec.getReg())) {
660            Insn insn = use.getOriginalRopInsn();
661
662            if (insn != null
663                    && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
664                return true;
665            }
666        }
667
668        return false;
669    }
670
671    /**
672     * Sets the new register count after renaming.
673     * @param newRegCount new register count
674     */
675    /*package*/ void setNewRegCount(int newRegCount) {
676        registerCount = newRegCount;
677        spareRegisterBase = registerCount;
678        onInsnsChanged();
679    }
680
681    /**
682     * Makes a new SSA register. For use after renaming has completed.
683     *
684     * @return &gt;=0 new SSA register.
685     */
686    public int makeNewSsaReg() {
687        int reg = registerCount++;
688        spareRegisterBase = registerCount;
689        onInsnsChanged();
690        return reg;
691    }
692
693    /**
694     * Visit all insns in this method
695     * @param visitor non-null; callback interface
696     */
697    public void forEachInsn(SsaInsn.Visitor visitor) {
698        for (SsaBasicBlock block: blocks) {
699            block.forEachInsn(visitor);
700        }
701    }
702
703    /**
704     * Visits each phi insn in this method
705     * @param v non-null; callback
706     */
707    public void forEachPhiInsn(PhiInsn.Visitor v) {
708        for (SsaBasicBlock block: blocks) {
709            block.forEachPhiInsn(v);
710        }
711    }
712
713
714    /**
715     * Walk the basic block tree in depth-first order, calling the visitor
716     * method once for every block. This depth-first walk may be run forward
717     * from the method entry point or backwards from the method exit points.
718     * @param reverse true if this should walk backwards from the exit points
719     * @param v non-null; callback interface. <code>parent</code>is set
720     * unless this is the root node
721     */
722    public void forEachBlockDepthFirst(boolean reverse,
723            SsaBasicBlock.Visitor v) {
724        BitSet visited = new BitSet(blocks.size());
725        // We push the parent first, then the child on the stack
726        Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
727
728        SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock();
729
730        if (rootBlock == null) {
731            // in the case there's no exit block
732            return;
733        }
734
735        stack.add(null);    // start with null parent
736        stack.add(rootBlock);
737
738        while (stack.size() > 0) {
739            SsaBasicBlock cur = stack.pop();
740            SsaBasicBlock parent = stack.pop();
741
742            if (!visited.get(cur.getIndex())) {
743                BitSet children
744                        = reverse ? cur.getPredecessors() : cur.getSuccessors();
745                for (int i = children.nextSetBit(0); i >= 0
746                        ; i = children.nextSetBit(i + 1)) {
747                    stack.add(cur);
748                    stack.add(blocks.get(i));
749                }
750                visited.set(cur.getIndex());
751                v.visitBlock(cur, parent);
752            }
753        }
754    }
755
756    /**
757     * Visits blocks in dom-tree order, starting at the current node.
758     * The <code>parent</code> parameter of the Visitor.visitBlock callback
759     * is currently always set to null.
760     *
761     * @param v non-null; callback interface
762     */
763    public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) {
764        BitSet visited = new BitSet(getBlocks().size());
765        Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
766
767        stack.add(getEntryBlock());
768
769        while (stack.size() > 0) {
770            SsaBasicBlock cur = stack.pop();
771            ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren();
772
773            if (!visited.get(cur.getIndex())) {
774                // We walk the tree this way for historical reasons...
775                for (int i = curDomChildren.size() - 1; i >= 0; i--) {
776                    SsaBasicBlock child = curDomChildren.get(i);
777                    stack.add(child);
778                }
779                visited.set(cur.getIndex());
780                v.visitBlock(cur, null);
781            }
782        }
783    }
784
785    /**
786     * Deletes all insns in the set from this method
787     *
788     * @param deletedInsns non-null; insns to delete
789     */
790    public void deleteInsns(Set<SsaInsn> deletedInsns) {
791        for (SsaBasicBlock block: getBlocks()) {
792            ArrayList<SsaInsn> insns = block.getInsns();
793
794            for (int i = insns.size() - 1; i >= 0; i--) {
795                SsaInsn insn = insns.get(i);
796
797                if (deletedInsns.contains(insn)) {
798                    onInsnRemoved(insn);
799                    insns.remove(i);
800                }
801            }
802
803            // Check to see if we need to add a GOTO
804
805            int insnsSz = insns.size();
806            SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1);
807
808            if (block != getExitBlock() && (insnsSz == 0
809                    || lastInsn.getOriginalRopInsn() == null
810                    || lastInsn.getOriginalRopInsn().getOpcode()
811                        .getBranchingness() == Rop.BRANCH_NONE)) {
812                // We managed to eat a throwable insn
813
814                Insn gotoInsn = new PlainInsn(Rops.GOTO,
815                        SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY);
816                insns.add(SsaInsn.makeFromRop(gotoInsn, block));
817            }
818        }
819    }
820
821    /**
822     * Set "back-convert mode". Set during back-conversion when registers
823     * are about to be mapped into a non-SSA namespace. When true,
824     * use and def lists are unavailable.
825     */
826    public void setBackMode() {
827        backMode = true;
828        useList = null;
829        definitionList = null;
830    }
831}
832