| // return-void |
| // Format 10x: 00|0e |
| %def op_return_void(): |
| % op_return(is_void=True) |
| |
| |
| // return vAA |
| // Format 11x: AA|0f |
| // Clobbers: t0 |
| %def op_return(is_object=False, is_void=False, is_wide=False): |
| % if is_void: |
| // Thread fence for constructor |
| fence w, w |
| % else: |
| srliw t0, xINST, 8 // t0 := AA |
| % if is_wide: |
| GET_VREG_WIDE a0, t0 // a0 := fp[AA:AA+1] |
| // The method may return to compiled code, so also place result in fa0. |
| fmv.d.x fa0, a0 |
| % elif is_object: |
| GET_VREG_OBJECT a0, t0 // a0 := refs[AA] |
| % else: |
| % get_vreg("a0", "t0") # a0 := fp[AA] |
| // The method may return to compiled code, so also place result in fa0. |
| fmv.w.x fa0, a0 |
| %#: |
| |
| CFI_REMEMBER_STATE |
| ld sp, -8(xREFS) // caller's interpreted frame pointer |
| .cfi_def_cfa sp, NTERP_SIZE_SAVE_CALLEE_SAVES |
| RESTORE_NTERP_SAVE_CALLEE_SAVES |
| DECREASE_FRAME NTERP_SIZE_SAVE_CALLEE_SAVES |
| ret |
| // Since opcode handlers are merely labeled asm chunks within ExecuteNterpImpl's FDE, we must |
| // restate the correct CFA rule for subsequent handlers. It is initially stated when setting up |
| // the nterp frame (setup_nterp_frame). |
| .cfi_restore_state |
| CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, NTERP_SIZE_SAVE_CALLEE_SAVES |
| |
| // return-wide vAA |
| // Format 11x: AA|10 |
| %def op_return_wide(): |
| % op_return(is_wide=True) |
| |
| // return-object vAA |
| // Format 11x: AA|11 |
| %def op_return_object(): |
| % op_return(is_object=True) |
| |
| // throw vAA |
| // Format 11x: AA|27 |
| // Throw the indicated exception. |
| %def op_throw(): |
| EXPORT_PC |
| srliw t0, xINST, 8 // t0 := AA |
| GET_VREG_OBJECT a0, t0 // a0 := exception object |
| mv a1, xSELF |
| call art_quick_deliver_exception // args a0, a1 |
| unimp |
| |
| // goto +AA |
| // Format 10t: AA|28 |
| // Unconditionally jump to the indicated instruction. |
| // Note: The branch offset must not be 0. |
| %def op_goto(): |
| srliw t0, xINST, 8 // t0 := AA (zext) |
| sext.b t0, t0 // t0 := +AA (sext) |
| BRANCH units=t0 |
| |
| // goto/16 +AAAA |
| // Format 20t: 00|29 AAAA |
| // Unconditionally jump to the indicated instruction. |
| // Note: The branch offset must not be 0. |
| %def op_goto_16(): |
| FETCH t0, 1, signed=1 // t0 := +AAAA (sext) |
| BRANCH units=t0 |
| |
| // goto/32 +AAAAAAAA |
| // Format 30t: 00|2a AAAA(lo) AAAA(hi) |
| // Unconditionally jump to the indicated instruction. |
| %def op_goto_32(): |
| FETCH t0, 1, signed=1, width=32 // t0 := +AAAAAAAA (sext) |
| BRANCH units=t0 |
| |
| // packed-switch vAA, +BBBBBBBB |
| // Format 31t: AA|2b BBBB(lo) BBBB(hi) |
| // Jump to a new instruction based on the value in the given register, using a table of offsets |
| // corresponding to each value in a particular integral range, or fall through to the next |
| // instruction if there is no match. |
| %def op_packed_switch(is_packed=True): |
| srliw t0, xINST, 8 // t0 := AA |
| FETCH t1, count=1, signed=1, width=32 // t1 := +BBBBBBBB (sext) |
| % get_vreg("a1", "t0") # a1 := vAA |
| sh1add a0, t1, xPC // a0 := +BBBBBBBB * 2 + xPC |
| % if is_packed: |
| call NterpDoPackedSwitch // args a0 (switchData), a1 (value) |
| % else: |
| call NterpDoSparseSwitch // args a0 (switchData), a1 (value) |
| %#: |
| BRANCH units=a0 |
| |
| // sparse-switch vAA, +BBBBBBBB |
| // Format 31t: AA|2c BBBB(lo) BBBB(hi) |
| // Jump to a new instruction based on the value in the given register, using an ordered table of |
| // value-offset pairs, or fall through to the next instruction if there is no match. |
| %def op_sparse_switch(): |
| % op_packed_switch(is_packed=False) |
| |
| // cmp-long vAA, vBB, vCC |
| // Format 23x: AA|31 CC|BB |
| %def op_cmp_long(): |
| FETCH t1, count=1 // t1 := CC|BB |
| srliw t0, xINST, 8 // t0 := AA |
| srliw t2, t1, 8 // t2 := CC |
| andi t1, t1, 0xFF // t1 := BB |
| GET_VREG_WIDE t1, t1 // t1 := fp[BB] |
| GET_VREG_WIDE t2, t2 // t2 := fp[CC] |
| // Note: Formula "(SLT r,l) - (SLT l,r)" lifted from compiler. |
| slt t3, t1, t2 |
| slt t4, t2, t1 |
| sub t4, t4, t3 |
| FETCH_ADVANCE_INST 2 |
| % set_vreg("t4", "t0", z0="t1") # fp[AA] := t4 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| // Common helper for if-test. |
| // Format 22t: B|A|op CCCC |
| %def bincmp(op): |
| srliw t0, xINST, 8 // t0 := B|A |
| srliw t1, xINST, 12 // t1 := B |
| andi t0, t0, 0xF // t0 := A |
| % get_vreg("t0", "t0") # t0 := vA |
| % get_vreg("t1", "t1") # t1 := vB |
| b${op} t0, t1, .L${opcode}_branch |
| |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t2 |
| GOTO_OPCODE t2 |
| |
| .L${opcode}_branch: |
| FETCH t2, count=1, signed=1 // t2 := +CCCC (sext) |
| BRANCH units=t2 |
| |
| // if-eq vA, vB, +CCCC |
| // Format 22t: B|A|32 CCCC |
| // Branch to the given destination if the given two registers' values compare as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_eq(): |
| % bincmp(op="eq") |
| |
| // if-ne vA, vB, +CCCC |
| // Format 22t: B|A|33 CCCC |
| // Branch to the given destination if the given two registers' values compare as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_ne(): |
| % bincmp(op="ne") |
| |
| // if-lt vA, vB, +CCCC |
| // Format 22t: B|A|34 CCCC |
| // Branch to the given destination if the given two registers' values compare as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_lt(): |
| % bincmp(op="lt") |
| |
| // if-ge vA, vB, +CCCC |
| // Format 22t: B|A|35 CCCC |
| // Branch to the given destination if the given two registers' values compare as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_ge(): |
| % bincmp(op="ge") |
| |
| // if-gt vA, vB, +CCCC |
| // Format 22t: B|A|36 CCCC |
| // Branch to the given destination if the given two registers' values compare as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_gt(): |
| % bincmp(op="gt") |
| |
| // if-le vA, vB, +CCCC |
| // Format 22t: B|A|37 CCCC |
| // Branch to the given destination if the given two registers' values compare as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_le(): |
| % bincmp(op="le") |
| |
| // Common helper for if-testz. |
| // Format 21t: AA|op BBBB |
| %def zcmp(op): |
| srliw t0, xINST, 8 // t0 := AA |
| % get_vreg("t0", "t0") # t0 := vAA |
| b${op} t0, .L${opcode}_branch |
| |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t1 |
| GOTO_OPCODE t1 |
| |
| .L${opcode}_branch: |
| FETCH t1, count=1, signed=1 // t1 := +BBBB (sext) |
| BRANCH units=t1 |
| |
| // if-eqz vAA, +BBBB |
| // Format 21t: AA|38 BBBB |
| // Branch to the given destination if the given register's value compares with 0 as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_eqz(): |
| % zcmp(op="eqz") |
| |
| // if-nez vAA, +BBBB |
| // Format 21t: AA|39 BBBB |
| // Branch to the given destination if the given register's value compares with 0 as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_nez(): |
| % zcmp(op="nez") |
| |
| // if-ltz vAA, +BBBB |
| // Format 21t: AA|3a BBBB |
| // Branch to the given destination if the given register's value compares with 0 as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_ltz(): |
| % zcmp(op="ltz") |
| |
| // if-gez vAA, +BBBB |
| // Format 21t: AA|3b BBBB |
| // Branch to the given destination if the given register's value compares with 0 as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_gez(): |
| % zcmp(op="gez") |
| |
| // if-gtz vAA, +BBBB |
| // Format 21t: AA|3c BBBB |
| // Branch to the given destination if the given register's value compares with 0 as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_gtz(): |
| % zcmp(op="gtz") |
| |
| // if-lez vAA, +BBBB |
| // Format 21t: AA|3d BBBB |
| // Branch to the given destination if the given register's value compares with 0 as specified. |
| // Note: The branch offset must not be 0. |
| %def op_if_lez(): |
| % zcmp(op="lez") |
| |