1/*
2 * Copyright (C) 2012 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
17
18/*! \file LowerJump.cpp
19    \brief This file lowers the following bytecodes: IF_XXX, GOTO
20*/
21#include <math.h>
22#include "libdex/DexOpcodes.h"
23#include "libdex/DexFile.h"
24#include "Lower.h"
25#include "NcgAot.h"
26#include "enc_wrapper.h"
27#include "interp/InterpDefs.h"
28#include "NcgHelper.h"
29
30LabelMap* globalMap;
31LabelMap* globalShortMap;//make sure for each bytecode, there is no duplicated label
32LabelMap* globalWorklist = NULL;
33LabelMap* globalShortWorklist;
34
35int globalMapNum;
36int globalWorklistNum;
37int globalDataWorklistNum;
38int VMAPIWorklistNum;
39int globalPCWorklistNum;
40int chainingWorklistNum;
41
42LabelMap* globalDataWorklist = NULL;
43LabelMap* globalPCWorklist = NULL;
44LabelMap* chainingWorklist = NULL;
45LabelMap* VMAPIWorklist = NULL;
46
47char* ncgClassData;
48char* ncgClassDataPtr;
49char* ncgMethodData;
50char* ncgMethodDataPtr;
51int   ncgClassNum;
52int   ncgMethodNum;
53
54NCGWorklist* globalNCGWorklist;
55DataWorklist* methodDataWorklist;
56#ifdef ENABLE_TRACING
57MapWorklist* methodMapWorklist;
58#endif
59/*!
60\brief search globalShortMap to find the entry for the given label
61
62*/
63LabelMap* findItemForShortLabel(const char* label) {
64    LabelMap* ptr = globalShortMap;
65    while(ptr != NULL) {
66        if(!strcmp(label, ptr->label)) {
67            return ptr;
68        }
69        ptr = ptr->nextItem;
70    }
71    return NULL;
72}
73//assume size of "jump reg" is 2
74#define JUMP_REG_SIZE 2
75#define ADD_REG_REG_SIZE 3
76/*!
77\brief update value of the immediate in the given jump instruction
78
79check whether the immediate is out of range for the pre-set size
80*/
81int updateJumpInst(char* jumpInst, OpndSize immSize, int relativeNCG) {
82#ifdef DEBUG_NCG_JUMP
83    ALOGI("update jump inst @ %p with %d", jumpInst, relativeNCG);
84#endif
85    if(immSize == OpndSize_8) { //-128 to 127
86        if(relativeNCG >= 128 || relativeNCG < -128) {
87            ALOGE("pre-allocated space for a forward jump is not big enough");
88            dvmAbort();
89        }
90    }
91    if(immSize == OpndSize_16) { //-2^16 to 2^16-1
92        if(relativeNCG >= 32768 || relativeNCG < -32768) {
93            ALOGE("pre-allocated space for a forward jump is not big enough");
94            dvmAbort();
95        }
96    }
97    encoder_update_imm(relativeNCG, jumpInst);
98    return 0;
99}
100
101/*!
102\brief insert a label
103
104It takes argument checkDup, if checkDup is true, an entry is created in globalShortMap, entries in globalShortWorklist are checked, if there exists a match, the immediate in the jump instruction is updated and the entry is removed from globalShortWorklist;
105otherwise, an entry is created in globalMap.
106*/
107int insertLabel(const char* label, bool checkDup) {
108    LabelMap* item = NULL;
109    if(!checkDup) {
110        item = (LabelMap*)malloc(sizeof(LabelMap));
111        if(item == NULL) {
112            ALOGE("Memory allocation failed");
113            return -1;
114        }
115        snprintf(item->label, LABEL_SIZE, "%s", label);
116        item->codePtr = stream;
117        item->nextItem = globalMap;
118        globalMap = item;
119#ifdef DEBUG_NCG_CODE_SIZE
120        ALOGI("insert global label %s %p", label, stream);
121#endif
122        globalMapNum++;
123        return 0;
124    }
125
126    item = (LabelMap*)malloc(sizeof(LabelMap));
127    if(item == NULL) {
128        ALOGE("Memory allocation failed");
129        return -1;
130    }
131    snprintf(item->label, LABEL_SIZE, "%s", label);
132    item->codePtr = stream;
133    item->nextItem = globalShortMap;
134    globalShortMap = item;
135#ifdef DEBUG_NCG
136    ALOGI("insert short-term label %s %p", label, stream);
137#endif
138    LabelMap* ptr = globalShortWorklist;
139    LabelMap* ptr_prevItem = NULL;
140    while(ptr != NULL) {
141        if(!strcmp(ptr->label, label)) {
142            //perform work
143            int relativeNCG = stream - ptr->codePtr;
144            unsigned instSize = encoder_get_inst_size(ptr->codePtr);
145            relativeNCG -= instSize; //size of the instruction
146#ifdef DEBUG_NCG
147            ALOGI("perform work short-term %p for label %s relative %d", ptr->codePtr, label, relativeNCG);
148#endif
149            updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
150            //remove work
151            if(ptr_prevItem == NULL) {
152                globalShortWorklist = ptr->nextItem;
153                free(ptr);
154                ptr = globalShortWorklist; //ptr_prevItem is still NULL
155            }
156            else {
157                ptr_prevItem->nextItem = ptr->nextItem;
158                free(ptr);
159                ptr = ptr_prevItem->nextItem;
160            }
161        }
162        else {
163            ptr_prevItem = ptr;
164            ptr = ptr->nextItem;
165        }
166    } //while
167    return 0;
168}
169/*!
170\brief search globalMap to find the entry for the given label
171
172*/
173char* findCodeForLabel(const char* label) {
174    LabelMap* ptr = globalMap;
175    while(ptr != NULL) {
176        if(!strcmp(label, ptr->label)) {
177            return ptr->codePtr;
178        }
179        ptr = ptr->nextItem;
180    }
181    return NULL;
182}
183/*!
184\brief search globalShortMap to find the entry for the given label
185
186*/
187char* findCodeForShortLabel(const char* label) {
188    LabelMap* ptr = globalShortMap;
189    while(ptr != NULL) {
190        if(!strcmp(label, ptr->label)) {
191            return ptr->codePtr;
192        }
193        ptr = ptr->nextItem;
194    }
195    return NULL;
196}
197int insertLabelWorklist(const char* label, OpndSize immSize) {
198    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
199    if(item == NULL) {
200        ALOGE("Memory allocation failed");
201        return -1;
202    }
203    snprintf(item->label, LABEL_SIZE, "%s", label);
204    item->codePtr = stream;
205    item->size = immSize;
206    item->nextItem = globalWorklist;
207    globalWorklist = item;
208#ifdef DEBUG_NCG
209    ALOGI("insert globalWorklist: %s %p", label, stream);
210#endif
211    return 0;
212}
213
214int insertShortWorklist(const char* label, OpndSize immSize) {
215    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
216    if(item == NULL) {
217        ALOGE("Memory allocation failed");
218        return -1;
219    }
220    snprintf(item->label, LABEL_SIZE, "%s", label);
221    item->codePtr = stream;
222    item->size = immSize;
223    item->nextItem = globalShortWorklist;
224    globalShortWorklist = item;
225#ifdef DEBUG_NCG
226    ALOGI("insert globalShortWorklist: %s %p", label, stream);
227#endif
228    return 0;
229}
230/*!
231\brief free memory allocated for globalMap
232
233*/
234void freeLabelMap() {
235    LabelMap* ptr = globalMap;
236    while(ptr != NULL) {
237        globalMap = ptr->nextItem;
238        free(ptr);
239        ptr = globalMap;
240    }
241}
242/*!
243\brief free memory allocated for globalShortMap
244
245*/
246void freeShortMap() {
247    LabelMap* ptr = globalShortMap;
248    while(ptr != NULL) {
249        globalShortMap = ptr->nextItem;
250        free(ptr);
251        ptr = globalShortMap;
252    }
253    globalShortMap = NULL;
254}
255
256int insertGlobalPCWorklist(char * offset, char * codeStart)
257{
258    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
259    if(item == NULL) {
260        ALOGE("Memory allocation failed");
261        return -1;
262    }
263    snprintf(item->label, LABEL_SIZE, "%s", "export_pc");
264    item->size = OpndSize_32;
265    item->codePtr = offset; //points to the immediate operand
266    item->addend = codeStart - streamMethodStart; //relative code pointer
267    item->nextItem = globalPCWorklist;
268    globalPCWorklist = item;
269    globalPCWorklistNum ++;
270
271#ifdef DEBUG_NCG
272    ALOGI("insert globalPCWorklist: %p %p %p %x %p", globalDvmNcg->streamCode,  codeStart, streamCode, item->addend, item->codePtr);
273#endif
274    return 0;
275}
276
277int insertChainingWorklist(int bbId, char * codeStart)
278{
279    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
280    if(item == NULL) {
281        ALOGE("Memory allocation failed");
282        return -1;
283    }
284    item->size = OpndSize_32;
285    item->codePtr = codeStart; //points to the move instruction
286    item->addend = bbId; //relative code pointer
287    item->nextItem = chainingWorklist;
288    chainingWorklist = item;
289
290#ifdef DEBUG_NCG
291    ALOGI("insertChainingWorklist: %p basic block %d", codeStart, bbId);
292#endif
293    return 0;
294}
295
296int insertGlobalDataWorklist(char * offset, const char* label)
297{
298    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
299    if(item == NULL) {
300        ALOGE("Memory allocation failed");
301        return -1;
302    }
303    snprintf(item->label, LABEL_SIZE, "%s", label);
304    item->codePtr = offset;
305    item->size = OpndSize_32;
306    item->nextItem = globalDataWorklist;
307    globalDataWorklist = item;
308    globalDataWorklistNum ++;
309
310#ifdef DEBUG_NCG
311    ALOGI("insert globalDataWorklist: %s %p", label, offset);
312#endif
313
314    return 0;
315}
316
317int insertVMAPIWorklist(char * offset, const char* label)
318{
319    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
320    if(item == NULL) {
321        ALOGE("Memory allocation failed");
322        return -1;
323    }
324    snprintf(item->label, LABEL_SIZE, "%s", label);
325    item->codePtr = offset;
326    item->size = OpndSize_32;
327
328    item->nextItem = VMAPIWorklist;
329    VMAPIWorklist = item;
330
331    VMAPIWorklistNum ++;
332
333#ifdef DEBUG_NCG
334    ALOGI("insert VMAPIWorklist: %s %p", label, offset);
335#endif
336    return 0;
337}
338////////////////////////////////////////////////
339
340
341int updateImmRMInst(char* moveInst, const char* label, int relativeNCG); //forward declaration
342//////////////////// performLabelWorklist is defined differently for code cache
343void performChainingWorklist() {
344    LabelMap* ptr = chainingWorklist;
345    while(ptr != NULL) {
346        int tmpNCG = traceLabelList[ptr->addend].lop.generic.offset;
347        char* NCGaddr = streamMethodStart + tmpNCG;
348        updateImmRMInst(ptr->codePtr, "", (int)NCGaddr);
349        chainingWorklist = ptr->nextItem;
350        free(ptr);
351        ptr = chainingWorklist;
352    }
353}
354void freeChainingWorklist() {
355    LabelMap* ptr = chainingWorklist;
356    while(ptr != NULL) {
357        chainingWorklist = ptr->nextItem;
358        free(ptr);
359        ptr = chainingWorklist;
360    }
361}
362
363//Work only for initNCG
364void performLabelWorklist() {
365    LabelMap* ptr = globalWorklist;
366    while(ptr != NULL) {
367#ifdef DEBUG_NCG
368        ALOGI("perform work global %p for label %s", ptr->codePtr, ptr->label);
369#endif
370        char* targetCode = findCodeForLabel(ptr->label);
371        assert(targetCode != NULL);
372        int relativeNCG = targetCode - ptr->codePtr;
373        unsigned instSize = encoder_get_inst_size(ptr->codePtr);
374        relativeNCG -= instSize; //size of the instruction
375        updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
376        globalWorklist = ptr->nextItem;
377        free(ptr);
378        ptr = globalWorklist;
379    }
380}
381void freeLabelWorklist() {
382    LabelMap* ptr = globalWorklist;
383    while(ptr != NULL) {
384        globalWorklist = ptr->nextItem;
385        free(ptr);
386        ptr = globalWorklist;
387    }
388}
389
390///////////////////////////////////////////////////
391/*!
392\brief update value of the immediate in the given move instruction
393
394*/
395int updateImmRMInst(char* moveInst, const char* label, int relativeNCG) {
396#ifdef DEBUG_NCG
397    ALOGI("perform work ImmRM inst @ %p for label %s with %d", moveInst, label, relativeNCG);
398#endif
399    encoder_update_imm_rm(relativeNCG, moveInst);
400    return 0;
401}
402//! maximum instruction size for jump,jcc,call: 6 for jcc rel32
403#define MAX_JCC_SIZE 6
404//! minimum instruction size for jump,jcc,call: 2
405#define MIN_JCC_SIZE 2
406/*!
407\brief estimate size of the immediate
408
409Somehow, 16 bit jump does not work. This function will return either 8 bit or 32 bit
410EXAMPLE:
411  native code at A: ...
412  native code at B: jump relOffset (target is A)
413  native code at B':
414  --> relOffset = A - B' = A - B - size of the jump instruction
415  Argument "target" is equal to A - B. To determine size of the immediate, we check tha value of "target - size of the jump instructoin"
416*/
417OpndSize estOpndSizeFromImm(int target) {
418    if(target-MIN_JCC_SIZE < 128 && target-MAX_JCC_SIZE >= -128) return OpndSize_8;
419#ifdef SUPPORT_IMM_16
420    if(target-MIN_JCC_SIZE < 32768 && target-MAX_JCC_SIZE >= -32768) return OpndSize_16;
421#endif
422    return OpndSize_32;
423}
424/*!
425\brief return size of a jump or call instruction
426
427*/
428unsigned getJmpCallInstSize(OpndSize size, JmpCall_type type) {
429    if(type == JmpCall_uncond) {
430        if(size == OpndSize_8) return 2;
431        if(size == OpndSize_16) return 4;
432        return 5;
433    }
434    if(type == JmpCall_cond) {
435        if(size == OpndSize_8) return 2;
436        if(size == OpndSize_16) return 5;
437        return 6;
438    }
439    if(type == JmpCall_reg) {
440        assert(size == OpndSize_32);
441        return JUMP_REG_SIZE;
442    }
443    if(type == JmpCall_call) {
444        assert(size != OpndSize_8);
445        if(size == OpndSize_16) return 4;
446        return 5;
447    }
448    return 0;
449}
450/*!
451\brief check whether a branch target is already handled, if yes, return the size of the immediate; otherwise, call insertShortWorklist or insertLabelWorklist.
452
453If the branch target is not handled, call insertShortWorklist or insertLabelWorklist depending on isShortTerm, unknown is set to true, immSize is set to 32 if isShortTerm is false, set to 32 if isShortTerm is true and target is check_cast_null, set to 8 otherwise.
454
455If the branch target is handled, call estOpndSizeFromImm to set immSize for jump instruction, returns the value of the immediate
456*/
457int getRelativeOffset(const char* target, bool isShortTerm, JmpCall_type type, bool* unknown, OpndSize* immSize) {
458    char* targetPtrInStream = NULL;
459    if(isShortTerm) targetPtrInStream = findCodeForShortLabel(target);
460    else targetPtrInStream = findCodeForLabel(target);
461
462    int relOffset;
463    *unknown = false;
464    if(targetPtrInStream == NULL) {
465        //branch target is not handled yet
466        relOffset = 0;
467        *unknown = true;
468        if(isShortTerm) {
469            /* for backward jump, at this point, we don't know how far the target is from this jump
470               since the lable is only used within a single bytecode, we assume OpndSize_8 is big enough
471               but there are special cases where we should use 32 bit offset
472            */
473            if(!strcmp(target, ".check_cast_null") || !strcmp(target, ".stackOverflow") ||
474               !strcmp(target, ".invokeChain") ||
475               !strcmp(target, ".new_instance_done") ||
476               !strcmp(target, ".new_array_done") ||
477               !strcmp(target, ".fill_array_data_done") ||
478               !strcmp(target, ".inlined_string_compare_done") ||
479               !strncmp(target, "after_exception", 15)) {
480#ifdef SUPPORT_IMM_16
481                *immSize = OpndSize_16;
482#else
483                *immSize = OpndSize_32;
484#endif
485            } else {
486                *immSize = OpndSize_8;
487            }
488#ifdef DEBUG_NCG_JUMP
489            ALOGI("insert to short worklist %s %d", target, *immSize);
490#endif
491            insertShortWorklist(target, *immSize);
492        }
493        else {
494#ifdef SUPPORT_IMM_16
495            *immSize = OpndSize_16;
496#else
497            *immSize = OpndSize_32;
498#endif
499            insertLabelWorklist(target, *immSize);
500        }
501        if(type == JmpCall_call) { //call sz16 does not work in gdb
502            *immSize = OpndSize_32;
503        }
504        return 0;
505    }
506    else if (!isShortTerm) {
507#ifdef SUPPORT_IMM_16
508        *immSize = OpndSize_16;
509#else
510        *immSize = OpndSize_32;
511#endif
512        insertLabelWorklist(target, *immSize);
513    }
514
515#ifdef DEBUG_NCG
516    ALOGI("backward branch @ %p for label %s", stream, target);
517#endif
518    relOffset = targetPtrInStream - stream;
519    if(type == JmpCall_call) *immSize = OpndSize_32;
520    else
521        *immSize = estOpndSizeFromImm(relOffset);
522
523    relOffset -= getJmpCallInstSize(*immSize, type);
524    return relOffset;
525}
526
527/*!
528\brief generate a single native instruction "jcc imm" to jump to a label
529
530*/
531void conditional_jump(ConditionCode cc, const char* target, bool isShortTerm) {
532    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
533        condJumpToBasicBlock(stream, cc, currentExceptionBlockIdx);
534        return;
535    }
536    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cc);
537    bool unknown;
538    OpndSize size;
539    int imm = 0;
540    imm = getRelativeOffset(target, isShortTerm, JmpCall_cond, &unknown, &size);
541    dump_label(m, size, imm, target, isShortTerm);
542}
543/*!
544\brief generate a single native instruction "jmp imm" to jump to ".invokeArgsDone"
545
546*/
547void goto_invokeArgsDone() {
548    unconditional_jump_global_API(".invokeArgsDone", false);
549}
550/*!
551\brief generate a single native instruction "jmp imm" to jump to a label
552
553If the target is ".invokeArgsDone" and mode is NCG O1, extra work is performed to dump content of virtual registers to memory.
554*/
555void unconditional_jump(const char* target, bool isShortTerm) {
556    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
557        jumpToBasicBlock(stream, currentExceptionBlockIdx);
558        return;
559    }
560    Mnemonic m = Mnemonic_JMP;
561    bool unknown;
562    OpndSize size;
563    if(gDvm.executionMode == kExecutionModeNcgO1) {
564        //for other three labels used by JIT: invokeArgsDone_formal, _native, _jit
565        if(!strncmp(target, ".invokeArgsDone", 15)) {
566            touchEcx(); //keep ecx live, if ecx was spilled, it is loaded here
567            beforeCall(target); //
568        }
569        if(!strcmp(target, ".invokeArgsDone")) {
570            nextVersionOfHardReg(PhysicalReg_EDX, 1); //edx will be used in a function
571            call("ncgGetEIP"); //must be immediately before JMP
572        }
573    }
574    int imm = 0;
575    imm = getRelativeOffset(target, isShortTerm, JmpCall_uncond, &unknown, &size);
576    dump_label(m, size, imm, target, isShortTerm);
577    if(gDvm.executionMode == kExecutionModeNcgO1) {
578        if(!strncmp(target, ".invokeArgsDone", 15)) {
579            afterCall(target); //un-spill before executing the next bytecode
580        }
581    }
582}
583/*!
584\brief generate a single native instruction "jcc imm"
585
586*/
587void conditional_jump_int(ConditionCode cc, int target, OpndSize size) {
588    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cc);
589    dump_ncg(m, size, target);
590}
591/*!
592\brief generate a single native instruction "jmp imm"
593
594*/
595void unconditional_jump_int(int target, OpndSize size) {
596    Mnemonic m = Mnemonic_JMP;
597    dump_ncg(m, size, target);
598}
599/*!
600\brief generate a single native instruction "jmp reg"
601
602*/
603void unconditional_jump_reg(int reg, bool isPhysical) {
604    dump_reg(Mnemonic_JMP, ATOM_NORMAL, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
605}
606
607/*!
608\brief generate a single native instruction to call a function
609
610If mode is NCG O1, extra work is performed to dump content of virtual registers to memory.
611*/
612void call(const char* target) {
613    if(gDvm.executionMode == kExecutionModeNcgO1) {
614        beforeCall(target);
615    }
616    Mnemonic m = Mnemonic_CALL;
617    bool dummy;
618    OpndSize size;
619    int relOffset = 0;
620    relOffset = getRelativeOffset(target, false, JmpCall_call, &dummy, &size);
621    dump_label(m, size, relOffset, target, false);
622    if(gDvm.executionMode == kExecutionModeNcgO1) {
623        afterCall(target);
624    }
625}
626/*!
627\brief generate a single native instruction to call a function
628
629*/
630void call_reg(int reg, bool isPhysical) {
631    Mnemonic m = Mnemonic_CALL;
632    dump_reg(m, ATOM_NORMAL, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
633}
634void call_reg_noalloc(int reg, bool isPhysical) {
635    Mnemonic m = Mnemonic_CALL;
636    dump_reg_noalloc(m, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
637}
638
639/*!
640\brief generate a single native instruction to call a function
641
642*/
643void call_mem(int disp, int reg, bool isPhysical) {
644    Mnemonic m = Mnemonic_CALL;
645    dump_mem(m, ATOM_NORMAL, OpndSize_32, disp, reg, isPhysical);
646}
647
648/*!
649\brief insert an entry to globalNCGWorklist
650
651*/
652int insertNCGWorklist(s4 relativePC, OpndSize immSize) {
653    int offsetNCG2 = stream - streamMethodStart;
654#ifdef DEBUG_NCG
655    ALOGI("insert NCGWorklist (goto forward) @ %p offsetPC %x relativePC %x offsetNCG %x", stream, offsetPC, relativePC, offsetNCG2);
656#endif
657    NCGWorklist* item = (NCGWorklist*)malloc(sizeof(NCGWorklist));
658    if(item == NULL) {
659        ALOGE("Memory allocation failed");
660        return -1;
661    }
662    item->relativePC = relativePC;
663    item->offsetPC = offsetPC;
664    item->offsetNCG = offsetNCG2;
665    item->codePtr = stream;
666    item->size = immSize;
667    item->nextItem = globalNCGWorklist;
668    globalNCGWorklist = item;
669    return 0;
670}
671#ifdef ENABLE_TRACING
672int insertMapWorklist(s4 BCOffset, s4 NCGOffset, int isStartOfPC) {
673    return 0;
674}
675#endif
676/*!
677\brief insert an entry to methodDataWorklist
678
679This function is used by bytecode FILL_ARRAY_DATA, PACKED_SWITCH, SPARSE_SWITCH
680*/
681int insertDataWorklist(s4 relativePC, char* codePtr1) {
682    //insert according to offsetPC+relativePC, smallest at the head
683    DataWorklist* item = (DataWorklist*)malloc(sizeof(DataWorklist));
684    if(item == NULL) {
685        ALOGE("Memory allocation failed");
686        return -1;
687    }
688    item->relativePC = relativePC;
689    item->offsetPC = offsetPC;
690    item->codePtr = codePtr1;
691    item->codePtr2 = stream; //jump_reg for switch
692    DataWorklist* ptr = methodDataWorklist;
693    DataWorklist* prev_ptr = NULL;
694    while(ptr != NULL) {
695        int tmpPC = ptr->offsetPC + ptr->relativePC;
696        int tmpPC2 = relativePC + offsetPC;
697        if(tmpPC2 < tmpPC) {
698            break;
699        }
700        prev_ptr = ptr;
701        ptr = ptr->nextItem;
702    }
703    //insert item before ptr
704    if(prev_ptr != NULL) {
705        prev_ptr->nextItem = item;
706    }
707    else methodDataWorklist = item;
708    item->nextItem = ptr;
709    return 0;
710}
711
712/*!
713\brief work on globalNCGWorklist
714
715*/
716int performNCGWorklist() {
717    NCGWorklist* ptr = globalNCGWorklist;
718    while(ptr != NULL) {
719        ALOGV("perform NCG worklist: @ %p target block %d target NCG %x",
720             ptr->codePtr, ptr->relativePC, traceLabelList[ptr->relativePC].lop.generic.offset);
721        int tmpNCG = traceLabelList[ptr->relativePC].lop.generic.offset;
722        assert(tmpNCG >= 0);
723        int relativeNCG = tmpNCG - ptr->offsetNCG;
724        unsigned instSize = encoder_get_inst_size(ptr->codePtr);
725        relativeNCG -= instSize;
726        updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
727        globalNCGWorklist = ptr->nextItem;
728        free(ptr);
729        ptr = globalNCGWorklist;
730    }
731    return 0;
732}
733void freeNCGWorklist() {
734    NCGWorklist* ptr = globalNCGWorklist;
735    while(ptr != NULL) {
736        globalNCGWorklist = ptr->nextItem;
737        free(ptr);
738        ptr = globalNCGWorklist;
739    }
740}
741
742/*!
743\brief used by bytecode SWITCH
744
745targetPC points to start of the data section
746Code sequence for SWITCH
747  call ncgGetEIP
748  @codeInst: add_reg_reg %eax, %edx
749  jump_reg %edx
750This function returns the offset in native code between add_reg_reg and the data section
751*/
752int getRelativeNCGForSwitch(int targetPC, char* codeInst) {
753    int tmpNCG = mapFromBCtoNCG[targetPC];
754    int offsetNCG2 = codeInst - streamMethodStart;
755    int relativeOff = tmpNCG - offsetNCG2;
756    return relativeOff;
757}
758/*!
759\brief work on methodDataWorklist
760
761*/
762int performDataWorklist() {
763    DataWorklist* ptr = methodDataWorklist;
764    if(ptr == NULL) return 0;
765
766    char* codeCacheEnd = ((char *) gDvmJit.codeCache) + gDvmJit.codeCacheSize - CODE_CACHE_PADDING;
767    u2 insnsSize = dvmGetMethodInsnsSize(currentMethod); //bytecode
768    //align stream to multiple of 4
769    int alignBytes = (int)stream & 3;
770    if(alignBytes != 0) alignBytes = 4-alignBytes;
771    stream += alignBytes;
772
773    while(ptr != NULL) {
774        int tmpPC = ptr->offsetPC + ptr->relativePC;
775        int endPC = insnsSize;
776        if(ptr->nextItem != NULL) endPC = ptr->nextItem->offsetPC + ptr->nextItem->relativePC;
777        mapFromBCtoNCG[tmpPC] = stream - streamMethodStart; //offsetNCG in byte
778
779        //handle fill_array_data, packed switch & sparse switch
780        u2 tmpInst = *(currentMethod->insns + ptr->offsetPC);
781        u2* sizePtr;
782        s4* entryPtr_bytecode;
783        u2 tSize, iVer;
784        u4 sz;
785
786        if (gDvmJit.codeCacheFull == true) {
787            // We are out of code cache space. Skip writing data/code to
788            //   code cache. Simply free the item.
789            methodDataWorklist = ptr->nextItem;
790            free(ptr);
791            ptr = methodDataWorklist;
792        }
793
794        switch (INST_INST(tmpInst)) {
795        case OP_FILL_ARRAY_DATA:
796            sz = (endPC-tmpPC)*sizeof(u2);
797            if ((stream + sz) < codeCacheEnd) {
798                memcpy(stream, (u2*)currentMethod->insns+tmpPC, sz);
799#ifdef DEBUG_NCG_CODE_SIZE
800                ALOGI("copy data section to stream %p: start at %d, %d bytes", stream, tmpPC, sz);
801#endif
802#ifdef DEBUG_NCG
803                ALOGI("update data section at %p with %d", ptr->codePtr, stream-ptr->codePtr);
804#endif
805                updateImmRMInst(ptr->codePtr, "", stream - ptr->codePtr);
806                stream += sz;
807            } else {
808                gDvmJit.codeCacheFull = true;
809            }
810            break;
811        case OP_PACKED_SWITCH:
812            updateImmRMInst(ptr->codePtr, "", stream-ptr->codePtr);
813            sizePtr = (u2*)currentMethod->insns+tmpPC + 1 /*signature*/;
814            entryPtr_bytecode = (s4*)(sizePtr + 1 /*size*/ + 2 /*firstKey*/);
815            tSize = *(sizePtr);
816            sz = tSize * 4;     /* expected size needed in stream */
817            if ((stream + sz) < codeCacheEnd) {
818                for(iVer = 0; iVer < tSize; iVer++) {
819                    //update entries
820                    s4 relativePC = *entryPtr_bytecode; //relative to ptr->offsetPC
821                    //need stream, offsetPC,
822                    int relativeNCG = getRelativeNCGForSwitch(relativePC+ptr->offsetPC, ptr->codePtr2);
823#ifdef DEBUG_NCG_CODE_SIZE
824                    ALOGI("convert target from %d to %d", relativePC+ptr->offsetPC, relativeNCG);
825#endif
826                    *((s4*)stream) = relativeNCG;
827                    stream += 4;
828                    entryPtr_bytecode++;
829                }
830            } else {
831                gDvmJit.codeCacheFull = true;
832            }
833            break;
834        case OP_SPARSE_SWITCH:
835            updateImmRMInst(ptr->codePtr, "", stream-ptr->codePtr);
836            sizePtr = (u2*)currentMethod->insns+tmpPC + 1 /*signature*/;
837            s4* keyPtr_bytecode = (s4*)(sizePtr + 1 /*size*/);
838            tSize = *(sizePtr);
839            entryPtr_bytecode = (s4*)(keyPtr_bytecode + tSize);
840            sz = tSize * (sizeof(s4) + 4); /* expected size needed in stream */
841            if ((stream + sz) < codeCacheEnd) {
842                memcpy(stream, keyPtr_bytecode, tSize*sizeof(s4));
843                stream += tSize*sizeof(s4);
844                for(iVer = 0; iVer < tSize; iVer++) {
845                    //update entries
846                    s4 relativePC = *entryPtr_bytecode; //relative to ptr->offsetPC
847                    //need stream, offsetPC,
848                    int relativeNCG = getRelativeNCGForSwitch(relativePC+ptr->offsetPC, ptr->codePtr2);
849                    *((s4*)stream) = relativeNCG;
850                    stream += 4;
851                    entryPtr_bytecode++;
852                }
853            } else {
854                gDvmJit.codeCacheFull = true;
855            }
856            break;
857        }
858
859        //remove the item
860        methodDataWorklist = ptr->nextItem;
861        free(ptr);
862        ptr = methodDataWorklist;
863    }
864    return 0;
865}
866void freeDataWorklist() {
867    DataWorklist* ptr = methodDataWorklist;
868    while(ptr != NULL) {
869        methodDataWorklist = ptr->nextItem;
870        free(ptr);
871        ptr = methodDataWorklist;
872    }
873}
874
875//////////////////////////
876/*!
877\brief check whether a branch target (specified by relative offset in bytecode) is already handled, if yes, return the size of the immediate; otherwise, call insertNCGWorklist.
878
879If the branch target is not handled, call insertNCGWorklist, unknown is set to true, immSize is set to 32.
880
881If the branch target is handled, call estOpndSizeFromImm to set immSize for jump instruction, returns the value of the immediate
882*/
883int getRelativeNCG(s4 tmp, JmpCall_type type, bool* unknown, OpndSize* size) {//tmp: relativePC
884    int tmpNCG = traceLabelList[tmp].lop.generic.offset;
885
886    *unknown = false;
887    if(tmpNCG <0) {
888        *unknown = true;
889#ifdef SUPPORT_IMM_16
890        *size = OpndSize_16;
891#else
892        *size = OpndSize_32;
893#endif
894        insertNCGWorklist(tmp, *size);
895        return 0;
896    }
897    int offsetNCG2 = stream - streamMethodStart;
898#ifdef DEBUG_NCG
899    ALOGI("goto backward @ %p offsetPC %d relativePC %d offsetNCG %d relativeNCG %d", stream, offsetPC, tmp, offsetNCG2, tmpNCG-offsetNCG2);
900#endif
901    int relativeOff = tmpNCG - offsetNCG2;
902    *size = estOpndSizeFromImm(relativeOff);
903    return relativeOff - getJmpCallInstSize(*size, type);
904}
905/*!
906\brief a helper function to handle backward branch
907
908input: jump target in %eax; at end of the function, jump to %eax
909*/
910int common_backwardBranch() {
911    insertLabel("common_backwardBranch", false);
912    spill_reg(PhysicalReg_EAX, true);
913    call("common_periodicChecks_entry");
914    unspill_reg(PhysicalReg_EAX, true);
915    unconditional_jump_reg(PhysicalReg_EAX, true);
916    return 0;
917}
918//when this is called from JIT, there is no need to check GC
919int common_goto(s4 tmp) { //tmp: target basic block id
920    bool unknown;
921    OpndSize size;
922    constVREndOfBB();
923    globalVREndOfBB(currentMethod);
924
925    int relativeNCG = tmp;
926    relativeNCG = getRelativeNCG(tmp, JmpCall_uncond, &unknown, &size);
927    unconditional_jump_int(relativeNCG, size);
928    return 1;
929}
930int common_if(s4 tmp, ConditionCode cc_next, ConditionCode cc) {
931    bool unknown;
932    OpndSize size;
933    int relativeNCG = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
934
935    if(traceCurrentBB->taken)
936        relativeNCG = getRelativeNCG(traceCurrentBB->taken->id, JmpCall_cond, &unknown, &size);
937    conditional_jump_int(cc, relativeNCG, size);
938    relativeNCG = traceCurrentBB->fallThrough ? traceCurrentBB->fallThrough->id : 0;
939    if(traceCurrentBB->fallThrough)
940        relativeNCG = getRelativeNCG(traceCurrentBB->fallThrough->id, JmpCall_uncond, &unknown, &size);
941    unconditional_jump_int(relativeNCG, size);
942    return 2;
943}
944
945/*!
946\brief helper function to handle null object error
947
948*/
949int common_errNullObject() {
950    insertLabel("common_errNullObject", false);
951    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
952    move_imm_to_reg(OpndSize_32, LstrNullPointerException, PhysicalReg_ECX, true);
953    unconditional_jump("common_throw", false);
954    return 0;
955}
956/*!
957\brief helper function to handle string index error
958
959*/
960int common_StringIndexOutOfBounds() {
961    insertLabel("common_StringIndexOutOfBounds", false);
962    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
963    move_imm_to_reg(OpndSize_32, LstrStringIndexOutOfBoundsException, PhysicalReg_ECX, true);
964    unconditional_jump("common_throw", false);
965    return 0;
966}
967
968/*!
969\brief helper function to handle array index error
970
971*/
972int common_errArrayIndex() {
973    insertLabel("common_errArrayIndex", false);
974    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
975    move_imm_to_reg(OpndSize_32, LstrArrayIndexException, PhysicalReg_ECX, true);
976    unconditional_jump("common_throw", false);
977    return 0;
978}
979/*!
980\brief helper function to handle array store error
981
982*/
983int common_errArrayStore() {
984    insertLabel("common_errArrayStore", false);
985    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
986    move_imm_to_reg(OpndSize_32, LstrArrayStoreException, PhysicalReg_ECX, true);
987    unconditional_jump("common_throw", false);
988    return 0;
989}
990/*!
991\brief helper function to handle negative array size error
992
993*/
994int common_errNegArraySize() {
995    insertLabel("common_errNegArraySize", false);
996    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
997    move_imm_to_reg(OpndSize_32, LstrNegativeArraySizeException, PhysicalReg_ECX, true);
998    unconditional_jump("common_throw", false);
999    return 0;
1000}
1001/*!
1002\brief helper function to handle divide-by-zero error
1003
1004*/
1005int common_errDivideByZero() {
1006    insertLabel("common_errDivideByZero", false);
1007    move_imm_to_reg(OpndSize_32, LstrDivideByZero, PhysicalReg_EAX, true);
1008    move_imm_to_reg(OpndSize_32, LstrArithmeticException, PhysicalReg_ECX, true);
1009    unconditional_jump("common_throw", false);
1010    return 0;
1011}
1012/*!
1013\brief helper function to handle no such method error
1014
1015*/
1016int common_errNoSuchMethod() {
1017    insertLabel("common_errNoSuchMethod", false);
1018    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1019    move_imm_to_reg(OpndSize_32, LstrNoSuchMethodError, PhysicalReg_ECX, true);
1020    unconditional_jump("common_throw", false);
1021    return 0;
1022}
1023int call_dvmFindCatchBlock();
1024
1025#define P_GPR_1 PhysicalReg_ESI //self callee-saved
1026#define P_GPR_2 PhysicalReg_EBX //exception callee-saved
1027#define P_GPR_3 PhysicalReg_EAX //method that caused exception
1028/*!
1029\brief helper function common_exceptionThrown
1030
1031*/
1032int common_exceptionThrown() {
1033    insertLabel("common_exceptionThrown", false);
1034    typedef void (*vmHelper)(int);
1035    vmHelper funcPtr = dvmJitToExceptionThrown;
1036    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
1037    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
1038    return 0;
1039}
1040#undef P_GPR_1
1041#undef P_GPR_2
1042#undef P_GPR_3
1043
1044/*!
1045\brief helper function to throw an exception with message
1046
1047INPUT: obj_reg(%eax), exceptionPtrReg(%ecx)
1048SCRATCH: C_SCRATCH_1(%esi) & C_SCRATCH_2(%edx)
1049OUTPUT: no
1050*/
1051int throw_exception_message(int exceptionPtrReg, int obj_reg, bool isPhysical,
1052                            int startLR/*logical register index*/, bool startPhysical) {
1053    insertLabel("common_throw_message", false);
1054    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
1055    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
1056
1057    move_mem_to_reg(OpndSize_32, offObject_clazz, obj_reg, isPhysical, C_SCRATCH_1, isScratchPhysical);
1058    move_mem_to_reg(OpndSize_32, offClassObject_descriptor, C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
1059    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1060    move_reg_to_mem(OpndSize_32, C_SCRATCH_2, isScratchPhysical, 4, PhysicalReg_ESP, true);
1061    move_reg_to_mem(OpndSize_32, exceptionPtrReg, true, 0, PhysicalReg_ESP, true);
1062    call_dvmThrowWithMessage();
1063    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1064    unconditional_jump("common_exceptionThrown", false);
1065    return 0;
1066}
1067/*!
1068\brief helper function to throw an exception
1069
1070scratch: C_SCRATCH_1(%edx)
1071*/
1072int throw_exception(int exceptionPtrReg, int immReg,
1073                    int startLR/*logical register index*/, bool startPhysical) {
1074    insertLabel("common_throw", false);
1075    scratchRegs[0] = PhysicalReg_EDX; scratchRegs[1] = PhysicalReg_Null;
1076    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
1077
1078    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1079    move_reg_to_mem(OpndSize_32, immReg, true, 4, PhysicalReg_ESP, true);
1080    move_reg_to_mem(OpndSize_32, exceptionPtrReg, true, 0, PhysicalReg_ESP, true);
1081    call_dvmThrow();
1082    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1083    unconditional_jump("common_exceptionThrown", false);
1084    return 0;
1085}
1086
1087//! lower bytecode GOTO
1088
1089//!
1090int op_goto() {
1091    s2 tmp = traceCurrentBB->taken->id;
1092    int retval = common_goto(tmp);
1093    rPC += 1;
1094    return retval;
1095}
1096//! lower bytecode GOTO_16
1097
1098//!
1099int op_goto_16() {
1100    s2 tmp = traceCurrentBB->taken->id;
1101    int retval = common_goto(tmp);
1102    rPC += 2;
1103    return retval;
1104}
1105//! lower bytecode GOTO_32
1106
1107//!
1108int op_goto_32() {
1109    s2 tmp = traceCurrentBB->taken->id;
1110    int retval = common_goto((s4)tmp);
1111    rPC += 3;
1112    return retval;
1113}
1114#define P_GPR_1 PhysicalReg_EBX
1115//! lower bytecode PACKED_SWITCH
1116
1117//!
1118int op_packed_switch() {
1119    u4 tmp = (u4)FETCH(1);
1120    tmp |= (u4)FETCH(2) << 16;
1121    u2 vA = INST_AA(inst);
1122
1123#ifdef DEBUG_EACH_BYTECODE
1124    u2 tSize = 0;
1125    s4 firstKey = 0;
1126    s4* entries = NULL;
1127#else
1128    u2* switchData = rPC + (s4)tmp;
1129    if (*switchData++ != kPackedSwitchSignature) {
1130        /* should have been caught by verifier */
1131        dvmThrowInternalError(
1132                          "bad packed switch magic");
1133        return 0; //no_op
1134    }
1135    u2 tSize = *switchData++;
1136    assert(tSize > 0);
1137    s4 firstKey = *switchData++;
1138    firstKey |= (*switchData++) << 16;
1139    s4* entries = (s4*) switchData;
1140    assert(((u4)entries & 0x3) == 0);
1141#endif
1142
1143    get_virtual_reg(vA, OpndSize_32, 1, false);
1144    //dvmNcgHandlePackedSwitch: testVal, size, first_key, targets
1145    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1146    move_imm_to_mem(OpndSize_32, tSize, 8, PhysicalReg_ESP, true);
1147    move_imm_to_mem(OpndSize_32, firstKey, 4, PhysicalReg_ESP, true);
1148
1149    /* "entries" is constant for JIT
1150       it is the 1st argument to dvmJitHandlePackedSwitch */
1151    move_imm_to_mem(OpndSize_32, (int)entries, 0, PhysicalReg_ESP, true);
1152    move_reg_to_mem(OpndSize_32, 1, false, 12, PhysicalReg_ESP, true);
1153
1154    //if value out of range, fall through (no_op)
1155    //return targets[testVal - first_key]
1156    scratchRegs[0] = PhysicalReg_SCRATCH_1;
1157    call_dvmJitHandlePackedSwitch();
1158    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1159    //TODO: eax should be absolute address, call globalVREndOfBB, constVREndOfBB
1160    //conditional_jump_global_API(Condition_LE, "common_backwardBranch", false);
1161    constVREndOfBB();
1162    globalVREndOfBB(currentMethod); //update GG VRs
1163    //get rPC, %eax has the relative PC offset
1164    alu_binary_imm_reg(OpndSize_32, add_opc, (int)rPC, PhysicalReg_EAX, true);
1165    scratchRegs[0] = PhysicalReg_SCRATCH_2;
1166#if defined(WITH_JIT_TUNING)
1167    /* Fall back to interpreter after resolving address of switch target.
1168     * Indicate a kSwitchOverflow. Note: This is not an "overflow". But it helps
1169     * count the times we return from a Switch
1170     */
1171    move_imm_to_mem(OpndSize_32, kSwitchOverflow, 0, PhysicalReg_ESP, true);
1172#endif
1173    jumpToInterpNoChain();
1174    rPC += 3;
1175    return 0;
1176}
1177#undef P_GPR_1
1178
1179#define P_GPR_1 PhysicalReg_EBX
1180//! lower bytecode SPARSE_SWITCH
1181
1182//!
1183int op_sparse_switch() {
1184    u4 tmp = (u4)FETCH(1);
1185    tmp |= (u4)FETCH(2) << 16;
1186    u2 vA = INST_AA(inst);
1187#ifdef DEBUG_EACH_BYTECODE
1188    u2 tSize = 0;
1189    const s4* keys = NULL;
1190    s4* entries = NULL;
1191#else
1192    u2* switchData = rPC + (s4)tmp;
1193
1194    if (*switchData++ != kSparseSwitchSignature) {
1195        /* should have been caught by verifier */
1196        dvmThrowInternalError(
1197                          "bad sparse switch magic");
1198        return 0; //no_op
1199    }
1200    u2 tSize = *switchData++;
1201    assert(tSize > 0);
1202    const s4* keys = (const s4*) switchData;
1203    assert(((u4)keys & 0x3) == 0);
1204    s4* entries = (s4*)switchData + tSize;
1205    assert(((u4)entries & 0x3) == 0);
1206#endif
1207
1208    get_virtual_reg(vA, OpndSize_32, 1, false);
1209    //dvmNcgHandleSparseSwitch: keys, size, testVal
1210    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1211    move_imm_to_mem(OpndSize_32, tSize, 4, PhysicalReg_ESP, true);
1212
1213    /* "keys" is constant for JIT
1214       it is the 1st argument to dvmJitHandleSparseSwitch */
1215    move_imm_to_mem(OpndSize_32, (int)keys, 0, PhysicalReg_ESP, true);
1216    move_reg_to_mem(OpndSize_32, 1, false, 8, PhysicalReg_ESP, true);
1217
1218    scratchRegs[0] = PhysicalReg_SCRATCH_1;
1219    //if testVal is in keys, return the corresponding target
1220    //otherwise, fall through (no_op)
1221    call_dvmJitHandleSparseSwitch();
1222    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1223    //TODO: eax should be absolute address, call globalVREndOfBB constVREndOfBB
1224    //conditional_jump_global_API(Condition_LE, "common_backwardBranch", false);
1225    constVREndOfBB();
1226    globalVREndOfBB(currentMethod);
1227    //get rPC, %eax has the relative PC offset
1228    alu_binary_imm_reg(OpndSize_32, add_opc, (int)rPC, PhysicalReg_EAX, true);
1229    scratchRegs[0] = PhysicalReg_SCRATCH_2;
1230#if defined(WITH_JIT_TUNING)
1231    /* Fall back to interpreter after resolving address of switch target.
1232     * Indicate a kSwitchOverflow. Note: This is not an "overflow". But it helps
1233     * count the times we return from a Switch
1234     */
1235    move_imm_to_mem(OpndSize_32, kSwitchOverflow, 0, PhysicalReg_ESP, true);
1236#endif
1237    jumpToInterpNoChain();
1238    rPC += 3;
1239    return 0;
1240}
1241
1242#undef P_GPR_1
1243
1244#define P_GPR_1 PhysicalReg_EBX
1245//! lower bytecode IF_EQ
1246
1247//!
1248int op_if_eq() {
1249    u2 vA = INST_A(inst);
1250    u2 vB = INST_B(inst);
1251    s2 tmp = (s2)FETCH(1);
1252    get_virtual_reg(vA, OpndSize_32, 1, false);
1253    compare_VR_reg(OpndSize_32, vB, 1, false);
1254    constVREndOfBB();
1255    globalVREndOfBB(currentMethod);
1256    common_if(tmp, Condition_NE, Condition_E);
1257    rPC += 2;
1258    return 0;
1259}
1260//! lower bytecode IF_NE
1261
1262//!
1263int op_if_ne() {
1264    u2 vA = INST_A(inst);
1265    u2 vB = INST_B(inst);
1266    s2 tmp = (s2)FETCH(1);
1267    get_virtual_reg(vA, OpndSize_32, 1, false);
1268    compare_VR_reg(OpndSize_32, vB, 1, false);
1269    constVREndOfBB();
1270    globalVREndOfBB(currentMethod);
1271    common_if(tmp, Condition_E, Condition_NE);
1272    rPC += 2;
1273    return 0;
1274}
1275//! lower bytecode IF_LT
1276
1277//!
1278int op_if_lt() {
1279    u2 vA = INST_A(inst);
1280    u2 vB = INST_B(inst);
1281    s2 tmp = (s2)FETCH(1);
1282    get_virtual_reg(vA, OpndSize_32, 1, false);
1283    compare_VR_reg(OpndSize_32, vB, 1, false);
1284    constVREndOfBB();
1285    globalVREndOfBB(currentMethod);
1286    common_if(tmp, Condition_GE, Condition_L);
1287    rPC += 2;
1288    return 0;
1289}
1290//! lower bytecode IF_GE
1291
1292//!
1293int op_if_ge() {
1294    u2 vA = INST_A(inst);
1295    u2 vB = INST_B(inst);
1296    s2 tmp = (s2)FETCH(1);
1297    get_virtual_reg(vA, OpndSize_32, 1, false);
1298    compare_VR_reg(OpndSize_32, vB, 1, false);
1299    constVREndOfBB();
1300    globalVREndOfBB(currentMethod);
1301    common_if(tmp, Condition_L, Condition_GE);
1302    rPC += 2;
1303    return 0;
1304}
1305//! lower bytecode IF_GT
1306
1307//!
1308int op_if_gt() {
1309    u2 vA = INST_A(inst);
1310    u2 vB = INST_B(inst);
1311    s2 tmp = (s2)FETCH(1);
1312    get_virtual_reg(vA, OpndSize_32, 1, false);
1313    compare_VR_reg(OpndSize_32, vB, 1, false);
1314    constVREndOfBB();
1315    globalVREndOfBB(currentMethod);
1316    common_if(tmp, Condition_LE, Condition_G);
1317    rPC += 2;
1318    return 0;
1319}
1320//! lower bytecode IF_LE
1321
1322//!
1323int op_if_le() {
1324    u2 vA = INST_A(inst);
1325    u2 vB = INST_B(inst);
1326    s2 tmp = (s2)FETCH(1);
1327    get_virtual_reg(vA, OpndSize_32, 1, false);
1328    compare_VR_reg(OpndSize_32, vB, 1, false);
1329    constVREndOfBB();
1330    globalVREndOfBB(currentMethod);
1331    common_if(tmp, Condition_G, Condition_LE);
1332    rPC += 2;
1333    return 0;
1334}
1335#undef P_GPR_1
1336//! lower bytecode IF_EQZ
1337
1338//!
1339int op_if_eqz() {
1340    u2 vA = INST_AA(inst);
1341    s2 tmp = (s2)FETCH(1);
1342    compare_imm_VR(OpndSize_32,
1343                                  0, vA);
1344    constVREndOfBB();
1345    globalVREndOfBB(currentMethod);
1346    common_if(tmp, Condition_NE, Condition_E);
1347    rPC += 2;
1348    return 0;
1349}
1350//! lower bytecode IF_NEZ
1351
1352//!
1353int op_if_nez() {
1354    u2 vA = INST_AA(inst);
1355    s2 tmp = (s2)FETCH(1);
1356    compare_imm_VR(OpndSize_32,
1357                                  0, vA);
1358    constVREndOfBB();
1359    globalVREndOfBB(currentMethod);
1360    common_if(tmp, Condition_E, Condition_NE);
1361    rPC += 2;
1362    return 0;
1363}
1364//! lower bytecode IF_LTZ
1365
1366//!
1367int op_if_ltz() {
1368    u2 vA = INST_AA(inst);
1369    s2 tmp = (s2)FETCH(1);
1370    compare_imm_VR(OpndSize_32,
1371                                  0, vA);
1372    constVREndOfBB();
1373    globalVREndOfBB(currentMethod);
1374    common_if(tmp, Condition_GE, Condition_L);
1375    rPC += 2;
1376    return 0;
1377}
1378//! lower bytecode IF_GEZ
1379
1380//!
1381int op_if_gez() {
1382    u2 vA = INST_AA(inst);
1383    s2 tmp = (s2)FETCH(1);
1384    compare_imm_VR(OpndSize_32,
1385                                  0, vA);
1386    constVREndOfBB();
1387    globalVREndOfBB(currentMethod);
1388    common_if(tmp, Condition_L, Condition_GE);
1389    rPC += 2;
1390    return 0;
1391}
1392//! lower bytecode IF_GTZ
1393
1394//!
1395int op_if_gtz() {
1396    u2 vA = INST_AA(inst);
1397    s2 tmp = (s2)FETCH(1);
1398    compare_imm_VR(OpndSize_32,
1399                                  0, vA);
1400    constVREndOfBB();
1401    globalVREndOfBB(currentMethod);
1402    common_if(tmp, Condition_LE, Condition_G);
1403    rPC += 2;
1404    return 0;
1405}
1406//! lower bytecode IF_LEZ
1407
1408//!
1409int op_if_lez() {
1410    u2 vA = INST_AA(inst);
1411    s2 tmp = (s2)FETCH(1);
1412    compare_imm_VR(OpndSize_32,
1413                                  0, vA);
1414    constVREndOfBB();
1415    globalVREndOfBB(currentMethod);
1416    common_if(tmp, Condition_G, Condition_LE);
1417    rPC += 2;
1418    return 0;
1419}
1420
1421#define P_GPR_1 PhysicalReg_ECX
1422#define P_GPR_2 PhysicalReg_EBX
1423/*!
1424\brief helper function common_periodicChecks4 to check GC request
1425BCOffset in %edx
1426*/
1427int common_periodicChecks4() {
1428    insertLabel("common_periodicChecks4", false);
1429#if (!defined(ENABLE_TRACING))
1430    get_self_pointer(PhysicalReg_ECX, true);
1431    move_mem_to_reg(OpndSize_32, offsetof(Thread, suspendCount), PhysicalReg_ECX, true, PhysicalReg_EAX, true);
1432    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //suspendCount
1433    conditional_jump(Condition_NE, "common_handleSuspend4", true); //called once
1434    x86_return();
1435
1436    insertLabel("common_handleSuspend4", true);
1437    push_reg_to_stack(OpndSize_32, PhysicalReg_ECX, true);
1438    call_dvmCheckSuspendPending();
1439    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1440    x86_return();
1441
1442#else
1443    ///////////////////
1444    //get debuggerActive: 3 memory accesses, and $7
1445    move_mem_to_reg(OpndSize_32, offGlue_pSelfSuspendCount, PhysicalReg_Glue, true, P_GPR_1, true);
1446    move_mem_to_reg(OpndSize_32, offGlue_pIntoDebugger, PhysicalReg_Glue, true, P_GPR_2, true);
1447
1448    compare_imm_mem(OpndSize_32, 0, 0, P_GPR_1, true); //suspendCount
1449    conditional_jump(Condition_NE, "common_handleSuspend4_1", true); //called once
1450
1451    compare_imm_mem(OpndSize_32, 0, 0, P_GPR_2, true); //debugger active
1452
1453    conditional_jump(Condition_NE, "common_debuggerActive4", true);
1454
1455    //recover registers and return
1456    x86_return();
1457
1458    insertLabel("common_handleSuspend4_1", true);
1459    push_mem_to_stack(OpndSize_32, offGlue_self, PhysicalReg_Glue, true);
1460    call_dvmCheckSuspendPending();
1461    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1462    x86_return();
1463
1464    insertLabel("common_debuggerActive4", true);
1465    //%edx: offsetBC (at run time, get method->insns_bytecode, then calculate BCPointer)
1466    move_mem_to_reg(OpndSize_32, offGlue_method, PhysicalReg_Glue, true, P_GPR_1, true);
1467    move_mem_to_reg(OpndSize_32, offMethod_insns_bytecode, P_GPR_1, true, P_GPR_2, true);
1468    alu_binary_reg_reg(OpndSize_32, add_opc, P_GPR_2, true, PhysicalReg_EDX, true);
1469    move_imm_to_mem(OpndSize_32, 0, offGlue_entryPoint, PhysicalReg_Glue, true);
1470    unconditional_jump("common_gotoBail", false); //update glue->rPC with edx
1471#endif
1472    return 0;
1473}
1474//input: %edx PC adjustment
1475//CHECK: should %edx be saved before calling dvmCheckSuspendPending?
1476/*!
1477\brief helper function common_periodicChecks_entry to check GC request
1478
1479*/
1480int common_periodicChecks_entry() {
1481    insertLabel("common_periodicChecks_entry", false);
1482    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EAX;
1483    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
1484    get_suspendCount(P_GPR_1, true);
1485
1486    //get debuggerActive: 3 memory accesses, and $7
1487#if 0 //defined(WITH_DEBUGGER)
1488    get_debuggerActive(P_GPR_2, true);
1489#endif
1490
1491    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true); //suspendCount
1492    conditional_jump(Condition_NE, "common_handleSuspend", true); //called once
1493
1494#if 0 //defined(WITH_DEBUGGER)
1495#ifdef NCG_DEBUG
1496    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true); //debugger active
1497    conditional_jump(Condition_NE, "common_debuggerActive", true);
1498#endif
1499#endif
1500
1501    //recover registers and return
1502    x86_return();
1503    insertLabel("common_handleSuspend", true);
1504    get_self_pointer(P_GPR_1, true);
1505    load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1506    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
1507    call_dvmCheckSuspendPending();
1508    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1509    x86_return();
1510#ifdef NCG_DEBUG
1511    insertLabel("common_debuggerActive", true);
1512    //adjust PC!!! use 0(%esp) TODO
1513    set_glue_entryPoint_imm(0); //kInterpEntryInstr);
1514    unconditional_jump("common_gotoBail", false);
1515#endif
1516    return 0;
1517}
1518#undef P_GPR_1
1519#undef P_GPR_2
1520/*!
1521\brief helper function common_gotoBail
1522  input: %edx: BCPointer %esi: Glue
1523  set %eax to 1 (switch interpreter = true), recover the callee-saved registers and return
1524*/
1525int common_gotoBail() {
1526    insertLabel("common_gotoBail", false);
1527    //scratchRegs[0] = PhysicalReg_EDX; scratchRegs[1] = PhysicalReg_ESI;
1528    //scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
1529    //save_pc_fp_to_glue();
1530    get_self_pointer(PhysicalReg_EAX, true);
1531    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), PhysicalReg_EAX, true);
1532    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offsetof(Thread, interpSave.pc), PhysicalReg_EAX, true);
1533
1534    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.bailPtr), PhysicalReg_EAX, true, PhysicalReg_ESP, true);
1535    move_reg_to_reg(OpndSize_32, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
1536    load_effective_addr(FRAME_SIZE-4, PhysicalReg_EBP, true, PhysicalReg_EBP, true);
1537    move_imm_to_reg(OpndSize_32, 1, PhysicalReg_EAX, true); //return value
1538    move_mem_to_reg(OpndSize_32, -4, PhysicalReg_EBP, true, PhysicalReg_EDI, true);
1539    move_mem_to_reg(OpndSize_32, -8, PhysicalReg_EBP, true, PhysicalReg_ESI, true);
1540    move_mem_to_reg(OpndSize_32, -12, PhysicalReg_EBP, true, PhysicalReg_EBX, true);
1541    move_reg_to_reg(OpndSize_32, PhysicalReg_EBP, true, PhysicalReg_ESP, true);
1542    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
1543    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1544    x86_return();
1545    return 0;
1546}
1547/*!
1548\brief helper function common_gotoBail_0
1549
1550  set %eax to 0, recover the callee-saved registers and return
1551*/
1552int common_gotoBail_0() {
1553    insertLabel("common_gotoBail_0", false);
1554
1555    get_self_pointer(PhysicalReg_EAX, true);
1556    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), PhysicalReg_EAX, true);
1557    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offsetof(Thread, interpSave.pc), PhysicalReg_EAX, true);
1558
1559    /*
1560    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
1561    movl    %esp,%ebp
1562    addl    $(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
1563    movl    EDI_SPILL(%ebp),%edi
1564    movl    ESI_SPILL(%ebp),%esi
1565    movl    EBX_SPILL(%ebp),%ebx
1566    movl    %ebp, %esp                   # strip frame
1567    pop     %ebp                         # restore caller's ebp
1568    ret                                  # return to dvmMterpStdRun's caller
1569    */
1570    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.bailPtr), PhysicalReg_EAX, true, PhysicalReg_ESP, true);
1571    move_reg_to_reg(OpndSize_32, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
1572    load_effective_addr(FRAME_SIZE-4, PhysicalReg_EBP, true, PhysicalReg_EBP, true);
1573    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //return value
1574    move_mem_to_reg(OpndSize_32, -4, PhysicalReg_EBP, true, PhysicalReg_EDI, true);
1575    move_mem_to_reg(OpndSize_32, -8, PhysicalReg_EBP, true, PhysicalReg_ESI, true);
1576    move_mem_to_reg(OpndSize_32, -12, PhysicalReg_EBP, true, PhysicalReg_EBX, true);
1577    move_reg_to_reg(OpndSize_32, PhysicalReg_EBP, true, PhysicalReg_ESP, true);
1578    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
1579    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1580    x86_return();
1581    return 0;
1582}
1583