| // array-length vA, vB |
| // Format 12x: B|A|21 |
| // Store in the given destination register the length of the indicated array, in entries |
| %def op_array_length(): |
| srliw t0, xINST, 12 // t0 := B |
| GET_VREG_OBJECT t0, t0 // t0 := refs[B] |
| beqz t0, 1f |
| srliw t1, xINST, 8 // t1 := B|A |
| FETCH_ADVANCE_INST 1 |
| andi t1, t1, 0xF // t1 := A |
| GET_INST_OPCODE t3 |
| lw t2, MIRROR_ARRAY_LENGTH_OFFSET(t0) |
| % set_vreg("t2", "t1", z0="t0") |
| GOTO_OPCODE t3 |
| 1: |
| tail common_errNullObject |
| |
| |
| // new-array vA, vB, type@CCCC |
| // Format 22c: B|A|23 CCCC |
| // Construct a new array of the indicated type and size. The type must be an array type. |
| %def op_new_array(): |
| EXPORT_PC |
| srliw s8, xINST, 8 // s8 := B|A |
| srliw s7, xINST, 12 // s7 := B |
| andi s8, s8, 0xF // s8 := A |
| FETCH_FROM_THREAD_CACHE /*resolved klass*/a0, .L${opcode}_miss, t0, t1 |
| TEST_IF_MARKING t0, .L${opcode}_mark |
| .L${opcode}_resume: |
| |
| % get_vreg("a1", "s7") # a1 := fp[B] length |
| ld t0, THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET(xSELF) |
| jalr t0 // args a0 (klass), a1 (length) |
| // return a0 := new-array |
| fence w, w // constructor fence; main.S has details |
| |
| SET_VREG_OBJECT a0, s8, z0=t0 // refs[A] := new-array |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| .L${opcode}_mark: |
| call art_quick_read_barrier_mark_reg10 // a0, klass |
| j .L${opcode}_resume |
| |
| .L${opcode}_miss: |
| EXPORT_PC |
| mv a0, xSELF |
| ld a1, (sp) // caller ArtMethod* |
| mv a2, xPC |
| call nterp_get_class |
| j .L${opcode}_resume |
| |
| |
| |
| |
| // filled-new-array {vC, vD, vE, vF, vG}, type@BBBB |
| // Format 35c: A|G|24 BBBB F|E|D|C |
| // Construct an array of the given type and size, filling it with the supplied contents. The type |
| // must be an array type. The array's contents must be single-word (that is, no arrays of long or |
| // double, but reference types are acceptable). The constructed instance is stored as a "result" in |
| // the same way that the method invocation instructions store their results, so the constructed |
| // instance must be moved to a register with an immediately subsequent move-result-object |
| // instruction (if it is to be used). |
| %def op_filled_new_array(is_range=False): |
| EXPORT_PC |
| mv a0, xSELF |
| ld a1, (sp) // a1 := caller ArtMethod* |
| mv a2, xFP // a2 := vreg array |
| mv a3, xPC |
| % if is_range: |
| call nterp_filled_new_array_range // args a0, a1, a2, a3 |
| % else: |
| call nterp_filled_new_array // args a0, a1, a2, a3 |
| %#: |
| FETCH_ADVANCE_INST 3 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| |
| // filled-new-array/range {vCCCC .. vNNNN}, type@BBBB |
| // where NNNN = CCCC + AA - 1 |
| // Format 3rc: AA|25 BBBB CCCC |
| // Construct an array of the given type and size, filling it with the supplied contents. |
| // Clarifications and restrictions are the same as filled-new-array, described above. |
| %def op_filled_new_array_range(): |
| % op_filled_new_array(is_range=True) |
| |
| |
| // fill-array-data vAA, +BBBBBBBB |
| // Format 31t: AA|26 BBBB(lo) BBBB(hi) |
| // Fill the given array with the indicated data. The reference must be to an array of primitives, |
| // and the data table must match it in type and must contain no more elements than will fit in the |
| // array. That is, the array may be larger than the table, and if so, only the initial elements of |
| // the array are set, leaving the remainder alone. |
| %def op_fill_array_data(): |
| EXPORT_PC |
| srliw t0, xINST, 8 // t0 := AA |
| FETCH t1, count=1, signed=1, width=32 |
| // t1 := ssssssssBBBBBBBB |
| GET_VREG_OBJECT a1, t0 // a1 := refs[AA] (array ref) |
| // +BBBBBBBB offset is in code units. Multiply by 2 for byte offset against dex PC. |
| sh1add a0, t1, xPC // a0 := payload address |
| call art_quick_handle_fill_data // args a0, a1 |
| FETCH_ADVANCE_INST 3 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| |
| // Common setup across APUT and AGET variants. |
| // Sets \array, \index, and \length registers. |
| // Branches to null handler and out-of-bounds handler. |
| %def array_prelude(array, index, length, null_label, oob_label): |
| FETCH $index, count=1 // index := CC|BB |
| andi $array, $index, 0xFF // array := BB |
| GET_VREG_OBJECT $array, $array // array := refs[BB], array obj |
| beqz $array, $null_label |
| |
| srliw $index, $index, 8 // index := CC |
| % get_vreg(index, index) # index := fp[CC] |
| lw $length, MIRROR_ARRAY_LENGTH_OFFSET($array) // length (signed 32b) |
| bgeu $index, $length, $oob_label // Unsigned comparison also catches negative index. |
| |
| |
| // aget vAA, vBB, vCC |
| // Format 23x: AA|44 CC|BB |
| // vAA := vBB[vCC] |
| %def op_aget(width=32, zext=False): |
| % array_prelude(array="t0", index="a0", length="a1", null_label=f".L{opcode}_null", oob_label=f".L{opcode}_oob") |
| // t0 := vBB array object, a0 := vCC index, a1 := array length |
| |
| % if width == 8 and zext: |
| add t0, a0, t0 |
| lbu t0, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(t0) |
| % elif width == 8: |
| add t0, a0, t0 |
| lb t0, MIRROR_BYTE_ARRAY_DATA_OFFSET(t0) |
| % elif width == 16 and zext: |
| sh1add t0, a0, t0 |
| lhu t0, MIRROR_CHAR_ARRAY_DATA_OFFSET(t0) |
| % elif width == 16: |
| sh1add t0, a0, t0 |
| lh t0, MIRROR_SHORT_ARRAY_DATA_OFFSET(t0) |
| % elif width == 32: |
| sh2add t0, a0, t0 |
| lw t0, MIRROR_INT_ARRAY_DATA_OFFSET(t0) |
| % elif width == 64: |
| sh3add t0, a0, t0 |
| ld t0, MIRROR_WIDE_ARRAY_DATA_OFFSET(t0) |
| % else: |
| % assert False, width |
| %#: |
| // t0 := *(array obj + data offset + idx * elem_size) |
| srliw t1, xINST, 8 // t1 := AA |
| % set_vreg("t0", "t1", z0="t2", width=width) |
| |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| .L${opcode}_null: |
| tail common_errNullObject |
| .L${opcode}_oob: |
| tail common_errArrayIndex // args a0 (index), a1 (length) |
| |
| |
| // aget-wide vAA, vBB, vCC |
| // Format 23x: AA|45 CC|BB |
| %def op_aget_wide(): |
| % op_aget(width=64) |
| |
| |
| // aget-object vAA, vBB, vCC |
| // Format 23x: AA|46 CC|BB |
| %def op_aget_object(): |
| % array_prelude(array="t0", index="a0", length="a1", null_label=f".L{opcode}_null", oob_label=f".L{opcode}_oob") |
| // t0 := vBB array object, a0 := vCC index, a1 := array length |
| |
| sh2add t0, a0, t0 |
| lwu a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET(t0) |
| // a0 := *(array obj + data offset + idx * elem_size) |
| UNPOISON_HEAP_REF a0 |
| TEST_IF_MARKING t1, .L${opcode}_mark |
| .L${opcode}_mark_resume: |
| srliw t1, xINST, 8 // t1 := AA |
| SET_VREG_OBJECT a0, t1, z0=t2 |
| |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| .L${opcode}_mark: |
| call art_quick_read_barrier_mark_reg10 // a0, object elem |
| j .L${opcode}_mark_resume |
| .L${opcode}_null: |
| tail common_errNullObject |
| .L${opcode}_oob: |
| tail common_errArrayIndex // args a0 (index), a1 (length) |
| |
| |
| // aget-boolean vAA, vBB, vCC |
| // Format 23x: AA|47 CC|BB |
| %def op_aget_boolean(): |
| % op_aget(width=8, zext=True) |
| |
| |
| // aget-byte vAA, vBB, vCC |
| // Format 23x: AA|48 CC|BB |
| %def op_aget_byte(): |
| % op_aget(width=8) |
| |
| |
| // aget_char vAA, vBB, vCC |
| // Format 23x: AA|49 CC|BB |
| %def op_aget_char(): |
| % op_aget(width=16, zext=True) |
| |
| |
| // aget-short vAA, vBB, vCC |
| // Format 23x: AA|4a CC|BB |
| %def op_aget_short(): |
| % op_aget(width=16) |
| |
| |
| // aput vAA, vBB, vCC |
| // Format 23x: AA|4b CC|BB |
| // vBB[vCC] := vAA |
| %def op_aput(width=32, zext=False): |
| % array_prelude(array="t0", index="t1", length="t2", null_label=f".L{opcode}_null", oob_label=f".L{opcode}_oob") |
| // t0 := vBB array object, t1 := vCC zext index, t2 := array length |
| |
| srliw t2, xINST, 8 // t2 := AA |
| % get_vreg("t2", "t2", width=width) # t2 := fp[AA] |
| % if width == 8 and zext: |
| add t0, t1, t0 |
| sb t2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(t0) |
| % elif width == 8: |
| add t0, t1, t0 |
| sb t2, MIRROR_BYTE_ARRAY_DATA_OFFSET(t0) |
| % elif width == 16 and zext: |
| sh1add t0, t1, t0 |
| sh t2, MIRROR_CHAR_ARRAY_DATA_OFFSET(t0) |
| % elif width == 16: |
| sh1add t0, t1, t0 |
| sh t2, MIRROR_SHORT_ARRAY_DATA_OFFSET(t0) |
| % elif width == 32: |
| sh2add t0, t1, t0 |
| sw t2, MIRROR_INT_ARRAY_DATA_OFFSET(t0) |
| % elif width == 64: |
| sh3add t0, t1, t0 |
| sd t2, MIRROR_WIDE_ARRAY_DATA_OFFSET(t0) |
| % else: |
| % assert False, width |
| %#: |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| .L${opcode}_null: |
| tail common_errNullObject |
| .L${opcode}_oob: |
| sext.w a0, t1 |
| mv a1, t2 |
| tail common_errArrayIndex // args a0 (index), a1 (length) |
| |
| |
| // aput-wide vAA, vBB, vCC |
| // Format 23x: AA|4c CC|BB |
| %def op_aput_wide(): |
| % op_aput(width=64) |
| |
| |
| // aput-object vAA, vBB, vCC |
| // Format 23x: AA|4d CC|BB |
| %def op_aput_object(): |
| % array_prelude(array="a0", index="a1", length="a2", null_label=f".L{opcode}_null", oob_label=f".L{opcode}_oob") |
| // a0 := vBB array object, a1 := vCC zext index, a2 := array length |
| |
| EXPORT_PC |
| srliw a2, xINST, 8 // a2 := AA |
| GET_VREG_OBJECT a2, a2 // a2 := fp[AA] |
| sext.w a1, a1 // a1 := sext index |
| call art_quick_aput_obj // args a0 (array obj), a1 (index), a2 (obj) |
| |
| FETCH_ADVANCE_INST 2 |
| GET_INST_OPCODE t0 |
| GOTO_OPCODE t0 |
| |
| .L${opcode}_null: |
| tail common_errNullObject |
| .L${opcode}_oob: |
| sext.w a0, a1 |
| mv a1, a2 |
| tail common_errArrayIndex // args a0 (index), a1 (length) |
| |
| // aput-boolean vAA, vBB, vCC |
| // Format 23x: AA|4e CC|BB |
| %def op_aput_boolean(): |
| % op_aput(width=8, zext=True) |
| |
| |
| // aput-byte vAA, vBB, vCC |
| // Format 23x: AA|4f CC|BB |
| %def op_aput_byte(): |
| % op_aput(width=8) |
| |
| |
| // aput-char vAA, vBB, vCC |
| // Format 23x: AA|50 CC|BB |
| %def op_aput_char(): |
| % op_aput(width=16, zext=True) |
| |
| |
| // aput-short vAA, vBB, vCC |
| // Format 23x: AA|51 CC|BB |
| %def op_aput_short(): |
| % op_aput(width=16) |