%def bincmp(condition=""): /* * Generic two-operand compare-and-branch operation. Provide a "condition" * fragment that specifies the comparison to perform. * * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) b.${condition} MterpCommonTakenBranchNoFlags cmp wPROFILE, #JIT_CHECK_OSR // possible OSR re-entry? b.eq .L_check_not_taken_osr FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction %def zcmp(compare="1", branch=""): /* * Generic one-operand compare-and-branch operation. Provide a "condition" * fragment that specifies the comparison to perform. * * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S wINST, 1 // w1<- branch offset, in code units .if ${compare} cmp w2, #0 // compare (vA, 0) .endif ${branch} MterpCommonTakenBranchNoFlags cmp wPROFILE, #JIT_CHECK_OSR // possible OSR re-entry? b.eq .L_check_not_taken_osr FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction %def op_goto(): /* * Unconditional branch, 8-bit offset. * * The branch distance is a signed code-unit offset, which we need to * double to get a byte offset. */ /* goto +AA */ sbfx wINST, wINST, #8, #8 // wINST<- ssssssAA (sign-extended) b MterpCommonTakenBranchNoFlags %def op_goto_16(): /* * Unconditional branch, 16-bit offset. * * The branch distance is a signed code-unit offset, which we need to * double to get a byte offset. */ /* goto/16 +AAAA */ FETCH_S wINST, 1 // wINST<- ssssAAAA (sign-extended) b MterpCommonTakenBranchNoFlags %def op_goto_32(): /* * Unconditional branch, 32-bit offset. * * The branch distance is a signed code-unit offset, which we need to * double to get a byte offset. * * Unlike most opcodes, this one is allowed to branch to itself, so * our "backward branch" test must be "<=0" instead of "<0". Because * we need the V bit set, we'll use an adds to convert from Dalvik * offset to byte offset. */ /* goto/32 +AAAAAAAA */ FETCH w0, 1 // w0<- aaaa (lo) FETCH w1, 2 // w1<- AAAA (hi) orr wINST, w0, w1, lsl #16 // wINST<- AAAAaaaa b MterpCommonTakenBranchNoFlags %def op_if_eq(): % bincmp(condition="eq") %def op_if_eqz(): % zcmp(compare="0", branch="cbz w2,") %def op_if_ge(): % bincmp(condition="ge") %def op_if_gez(): % zcmp(compare="0", branch="tbz w2, #31,") %def op_if_gt(): % bincmp(condition="gt") %def op_if_gtz(): % zcmp(branch="b.gt") %def op_if_le(): % bincmp(condition="le") %def op_if_lez(): % zcmp(branch="b.le") %def op_if_lt(): % bincmp(condition="lt") %def op_if_ltz(): % zcmp(compare="0", branch="tbnz w2, #31,") %def op_if_ne(): % bincmp(condition="ne") %def op_if_nez(): % zcmp(compare="0", branch="cbnz w2,") %def op_packed_switch(func="MterpDoPackedSwitch"): /* * Handle a packed-switch or sparse-switch instruction. In both cases * we decode it and hand it off to a helper function. * * We don't really expect backward branches in a switch statement, but * they're perfectly legal, so we check for them here. * * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ FETCH w0, 1 // x0<- 000000000000bbbb (lo) FETCH_S x1, 2 // x1<- ssssssssssssBBBB (hi) lsr w3, wINST, #8 // w3<- AA orr x0, x0, x1, lsl #16 // x0<- ssssssssBBBBbbbb GET_VREG w1, w3 // w1<- vAA add x0, xPC, x0, lsl #1 // x0<- PC + ssssssssBBBBbbbb*2 bl $func // w0<- code-unit branch offset sxtw xINST, w0 b MterpCommonTakenBranchNoFlags %def op_return(): /* * Return a 32-bit value. * * for: return, return-object */ /* op vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: lsr w2, wINST, #8 // r2<- AA GET_VREG w0, w2 // r0<- vAA b MterpReturn .L${opcode}_check: bl MterpSuspendCheck // (self) b .L${opcode}_return %def op_return_object(): % op_return() %def op_return_void(): .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: mov x0, #0 b MterpReturn .L${opcode}_check: bl MterpSuspendCheck // (self) b .L${opcode}_return %def op_return_void_no_barrier(): ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: mov x0, #0 b MterpReturn .L${opcode}_check: bl MterpSuspendCheck // (self) b .L${opcode}_return %def op_return_wide(): /* * Return a 64-bit value. */ /* return-wide vAA */ /* op vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: lsr w2, wINST, #8 // w2<- AA GET_VREG_WIDE x0, w2 // x0<- vAA b MterpReturn .L${opcode}_check: bl MterpSuspendCheck // (self) b .L${opcode}_return %def op_sparse_switch(): % op_packed_switch(func="MterpDoSparseSwitch") %def op_throw(): /* * Throw an exception object in the current thread. */ /* throw vAA */ EXPORT_PC lsr w2, wINST, #8 // r2<- AA GET_VREG w1, w2 // r1<- vAA (exception object) cbz w1, common_errNullObject str x1, [xSELF, #THREAD_EXCEPTION_OFFSET] // thread->exception<- obj b MterpException