%def op_check_cast(): % slow_path = add_slow_path(op_check_cast_slow_path) // Fast-path which gets the class from thread-local cache. % fetch_from_thread_cache("x1", miss_label="2f") 1: lsr w2, wINST, #8 // w2<- A GET_VREG w0, w2 // w0<- vA (object) cbz w0, .L${opcode}_resume ldr w2, [x0, #MIRROR_OBJECT_CLASS_OFFSET] UNPOISON_HEAP_REF w2 // Fast path: do a comparison without read barrier. cmp w1, w2 bne ${slow_path} .L${opcode}_resume: FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip 2: EXPORT_PC mov x0, xSELF ldr x1, [sp] mov x2, xPC bl nterp_get_class mov x1, x0 b 1b %def op_check_cast_slow_path(): // We don't do read barriers for simplicity. However, this means that x1 // (and all other fetched objects) may be a from-space reference. Tthat's OK as // we only fetch constant information from the references. // This also means that some of the comparisons below may lead to false negative, // but it will eventually be handled in the runtime. ldr w3, [x1, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET] tbnz w3, #MIRROR_CLASS_IS_INTERFACE_FLAG_BIT, 2f ldr w3, [x1, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] UNPOISON_HEAP_REF w3 cbnz w3, 5f 1: ldr w2, [x2, #MIRROR_CLASS_SUPER_CLASS_OFFSET] UNPOISON_HEAP_REF w2 cmp w1, w2 beq .L${opcode}_resume cbnz w2, 1b 2: TEST_IF_MARKING 4f 3: EXPORT_PC bl art_quick_check_instance_of b .L${opcode}_resume 4: bl art_quick_read_barrier_mark_reg01 b 3b 5: // Class in w1 is an array, w3 is the component type. ldr w2, [x2, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] UNPOISON_HEAP_REF w2 // Check if object is an array. cbz w2, 2b ldr w4, [x3, #MIRROR_CLASS_SUPER_CLASS_OFFSET] UNPOISON_HEAP_REF w4 // If the super class of the component type is not null, go slow path. cbnz w4, 2b ldrh w3, [x3, #MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET] // If the component type is primitive, go slow path. cbnz w3, 2b // Check if the object is a primitive array. ldrh w2, [x2, #MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET] cbz w2, .L${opcode}_resume // Go slow path for throwing the exception. b 2b %def op_instance_of(): % slow_path = add_slow_path(op_instance_of_slow_path) /* instance-of vA, vB, class@CCCC */ // Fast-path which gets the class from thread-local cache. % fetch_from_thread_cache("x1", miss_label="2f") 1: lsr w2, wINST, #12 // w2<- B GET_VREG w0, w2 // w0<- vB (object) cbz w0, .L${opcode}_resume ldr w2, [x0, #MIRROR_OBJECT_CLASS_OFFSET] UNPOISON_HEAP_REF w2 // Fast path: do a comparison without read barrier. cmp w1, w2 bne ${slow_path} .L${opcode}_set_one: mov w0, #1 .L${opcode}_resume: ubfx w1, wINST, #8, #4 // w1<- A SET_VREG w0, w1 FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip 2: EXPORT_PC mov x0, xSELF ldr x1, [sp] mov x2, xPC bl nterp_get_class mov x1, x0 b 1b %def op_instance_of_slow_path(): // Go slow path if we are marking. Checking now allows // not going to slow path if the super class hierarchy check fails. TEST_IF_MARKING 4f ldr w3, [x1, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET] tbnz w3, #MIRROR_CLASS_IS_INTERFACE_FLAG_BIT, 5f ldr w3, [x1, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] UNPOISON_HEAP_REF w3 cbnz w3, 3f 1: ldr w2, [x2, #MIRROR_CLASS_SUPER_CLASS_OFFSET] UNPOISON_HEAP_REF w2 cmp w1, w2 beq .L${opcode}_set_one cbnz w2, 1b 2: mov w0, #0 b .L${opcode}_resume 3: // Class in x1 is an array, x3 is the component type of x1, and x2 is the class of the object. ldr w2, [x2, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] UNPOISON_HEAP_REF w2 // Check if object is an array. cbz w2, 2b // Check of x1 is Object[] ldr w4, [x3, #MIRROR_CLASS_SUPER_CLASS_OFFSET] UNPOISON_HEAP_REF w4 // If the super class is not Object, go to slow path. cbnz w4, 5f // Super class is null, this could either be a primitive array or Object[]. ldrh w3, [x3, #MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET] // If x1 is a primitive array class, we know the check is false. cbnz w3, 2b // Check if x2 is a primitive array class. ldrh w2, [x2, #MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET] cmp w2, #0 cset w0, eq b .L${opcode}_resume 4: bl art_quick_read_barrier_mark_reg01 5: EXPORT_PC bl artInstanceOfFromCode b .L${opcode}_resume %def op_iget_boolean(): % op_iget(load="ldrb", volatile_load="ldarb", maybe_extend="", wide="0", is_object="0") %def op_iget_byte(): % op_iget(load="ldrsb", volatile_load="ldarb", maybe_extend="sxtb w0, w0", wide="0", is_object="0") %def op_iget_char(): % op_iget(load="ldrh", volatile_load="ldarh", maybe_extend="", wide="0", is_object="0") %def op_iget_short(): % op_iget(load="ldrsh", volatile_load="ldarh", maybe_extend="sxth w0, w0", wide="0", is_object="0") %def op_iget(load="ldr", volatile_load="ldar", maybe_extend="", wide="0", is_object="0"): % slow_path = add_slow_path(op_iget_slow_path, volatile_load, maybe_extend, wide, is_object) // Fast-path which gets the field from thread-local cache. % fetch_from_thread_cache("x0", miss_label=slow_path) .L${opcode}_resume: lsr w2, wINST, #12 // w2<- B GET_VREG w3, w2 // w3<- object we're operating on ubfx w2, wINST, #8, #4 // w2<- A cbz w3, common_errNullObject // object was null .if $wide $load x0, [x3, x0] SET_VREG_WIDE x0, w2 // fp[A] <- value .elseif $is_object $load w0, [x3, x0] UNPOISON_HEAP_REF w0 TEST_IF_MARKING .L${opcode}_read_barrier .L${opcode}_resume_after_read_barrier: SET_VREG_OBJECT w0, w2 // fp[A] <- value .else $load w0, [x3, x0] SET_VREG w0, w2 // fp[A] <- value .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip .if $is_object .L${opcode}_read_barrier: bl art_quick_read_barrier_mark_reg00 b .L${opcode}_resume_after_read_barrier .endif %def op_iget_slow_path(volatile_load, maybe_extend, wide, is_object): mov x0, xSELF ldr x1, [sp] mov x2, xPC mov x3, #0 EXPORT_PC bl nterp_get_instance_field_offset // Zero extension (nterp_get_instance_field_offset returns uint32_t) of the return value is // needed as the value is used below via wider X0 register - AARCH64 AAPCS specifies that // "... any unused bits in the register have unspecified value" (see 6.8.2, 6.9). mov w0, w0 tbz w0, #31, .L${opcode}_resume CLEAR_INSTANCE_VOLATILE_MARKER w0 lsr w2, wINST, #12 // w2<- B GET_VREG w3, w2 // w3<- object we're operating on ubfx w2, wINST, #8, #4 // w2<- A cbz w3, common_errNullObject // object was null add x3, x3, x0 .if $wide $volatile_load x0, [x3] SET_VREG_WIDE x0, w2 // fp[A] <- value .elseif $is_object $volatile_load w0, [x3] UNPOISON_HEAP_REF w0 TEST_IF_MARKING .L${opcode}_read_barrier SET_VREG_OBJECT w0, w2 // fp[A] <- value .else $volatile_load w0, [x3] $maybe_extend SET_VREG w0, w2 // fp[A] <- value .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip %def op_iget_wide(): % op_iget(load="ldr", volatile_load="ldar", maybe_extend="", wide="1", is_object="0") %def op_iget_object(): % op_iget(load="ldr", volatile_load="ldar", maybe_extend="", wide="0", is_object="1") %def op_iput_boolean(): % op_iput(store="strb", volatile_store="stlrb", wide="0", is_object="0") %def op_iput_byte(): % op_iput(store="strb", volatile_store="stlrb", wide="0", is_object="0") %def op_iput_char(): % op_iput(store="strh", volatile_store="stlrh", wide="0", is_object="0") %def op_iput_short(): % op_iput(store="strh", volatile_store="stlrh", wide="0", is_object="0") %def op_iput(store="str", volatile_store="stlr", wide="0", is_object="0"): % slow_path = add_slow_path(op_iput_slow_path, volatile_store, wide, is_object) ubfx w1, wINST, #8, #4 // w1<- A .if $wide GET_VREG_WIDE x26, w1 // x26<- fp[A]/fp[A+1] .else GET_VREG w26, w1 // w26 <- v[A] .endif // Fast-path which gets the field from thread-local cache. % fetch_from_thread_cache("x0", miss_label=slow_path) .L${opcode}_resume: lsr w2, wINST, #12 // w2<- B GET_VREG w2, w2 // vB (object we're operating on) cbz w2, common_errNullObject .if $wide $store x26, [x2, x0] .else POISON_HEAP_REF_IF_OBJECT $is_object, w26 $store w26, [x2, x0] WRITE_BARRIER_IF_OBJECT $is_object, w26, w2, .L${opcode}_skip_write_barrier .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip %def op_iput_slow_path(volatile_store, wide, is_object): mov x0, xSELF ldr x1, [sp] mov x2, xPC .if $is_object mov x3, x26 .else mov x3, #0 .endif EXPORT_PC bl nterp_get_instance_field_offset // Zero extension (nterp_get_instance_field_offset returns uint32_t) of the return value is // needed as the value is used below via wider X0 register - AARCH64 AAPCS specifies that // "... any unused bits in the register have unspecified value" (see 6.8.2, 6.9). mov w0, w0 .if $is_object // Reload the value as it may have moved. ubfx w1, wINST, #8, #4 // w1<- A GET_VREG w26, w1 // w26 <- v[A] .endif tbz w0, #31, .L${opcode}_resume CLEAR_INSTANCE_VOLATILE_MARKER w0 lsr w2, wINST, #12 // w2<- B GET_VREG w2, w2 // vB (object we're operating on) cbz w2, common_errNullObject add x3, x2, x0 .if $wide $volatile_store x26, [x3] .else POISON_HEAP_REF_IF_OBJECT $is_object, w26 $volatile_store w26, [x3] WRITE_BARRIER_IF_OBJECT $is_object, w26, w2, .L${opcode}_slow_path_skip_write_barrier .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip %def op_iput_wide(): % op_iput(store="str", volatile_store="stlr", wide="1", is_object="0") %def op_iput_object(): % op_iput(store="str", volatile_store="stlr", wide="0", is_object="1") %def op_sget_boolean(): % op_sget(load="ldrb", volatile_load="ldarb", maybe_extend="", wide="0", is_object="0") %def op_sget_byte(): % op_sget(load="ldrsb", volatile_load="ldarb", maybe_extend="sxtb w0, w0", wide="0", is_object="0") %def op_sget_char(): % op_sget(load="ldrh", volatile_load="ldarh", maybe_extend="", wide="0", is_object="0") %def op_sget_short(): % op_sget(load="ldrsh", volatile_load="ldarh", maybe_extend="sxth w0, w0", wide="0", is_object="0") %def op_sget(load="ldr", volatile_load="ldar", maybe_extend="", wide="0", is_object="0"): % slow_path = add_slow_path(op_sget_slow_path, volatile_load, maybe_extend, wide, is_object) // Fast-path which gets the field from thread-local cache. % fetch_from_thread_cache("x0", miss_label=slow_path) .L${opcode}_resume: ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET] lsr w2, wINST, #8 // w2 <- A ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET] TEST_IF_MARKING .L${opcode}_read_barrier .L${opcode}_resume_after_read_barrier: .if $wide ldr x0, [x0, x1] SET_VREG_WIDE x0, w2 // fp[A] <- value .elseif $is_object $load w0, [x0, x1] UNPOISON_HEAP_REF w0 // No need to check the marking register, we know it's not set here. .L${opcode}_after_reference_load: SET_VREG_OBJECT w0, w2 // fp[A] <- value .else $load w0, [x0, x1] SET_VREG w0, w2 // fp[A] <- value .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip .L${opcode}_read_barrier: bl art_quick_read_barrier_mark_reg00 .if $is_object $load w0, [x0, x1] UNPOISON_HEAP_REF w0 .L${opcode}_mark_after_load: // Here, we know the marking register is set. bl art_quick_read_barrier_mark_reg00 b .L${opcode}_after_reference_load .else b .L${opcode}_resume_after_read_barrier .endif %def op_sget_slow_path(volatile_load, maybe_extend, wide, is_object): mov x0, xSELF ldr x1, [sp] mov x2, xPC mov x3, #0 EXPORT_PC bl nterp_get_static_field tbz x0, #0, .L${opcode}_resume CLEAR_STATIC_VOLATILE_MARKER x0 ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET] lsr w2, wINST, #8 // w2 <- A ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET] TEST_IF_MARKING .L${opcode}_slow_path_read_barrier .L${opcode}_slow_path_resume_after_read_barrier: add x0, x0, x1 .if $wide ldar x0, [x0] SET_VREG_WIDE x0, w2 // fp[A] <- value .elseif $is_object $volatile_load w0, [x0] UNPOISON_HEAP_REF w0 TEST_IF_MARKING .L${opcode}_mark_after_load SET_VREG_OBJECT w0, w2 // fp[A] <- value .else $volatile_load w0, [x0] $maybe_extend SET_VREG w0, w2 // fp[A] <- value .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip .L${opcode}_slow_path_read_barrier: bl art_quick_read_barrier_mark_reg00 b .L${opcode}_slow_path_resume_after_read_barrier %def op_sget_wide(): % op_sget(load="ldr", volatile_load="ldar", maybe_extend="", wide="1", is_object="0") %def op_sget_object(): % op_sget(load="ldr", volatile_load="ldar", maybe_extend="", wide="0", is_object="1") %def op_sput_boolean(): % op_sput(store="strb", volatile_store="stlrb", wide="0", is_object="0") %def op_sput_byte(): % op_sput(store="strb", volatile_store="stlrb", wide="0", is_object="0") %def op_sput_char(): % op_sput(store="strh", volatile_store="stlrh", wide="0", is_object="0") %def op_sput_short(): % op_sput(store="strh", volatile_store="stlrh", wide="0", is_object="0") %def op_sput(store="str", volatile_store="stlr", wide="0", is_object="0"): % slow_path = add_slow_path(op_sput_slow_path, volatile_store, wide, is_object) lsr w2, wINST, #8 // w2 <- A .if $wide GET_VREG_WIDE x26, w2 // x26 <- v[A] .else GET_VREG w26, w2 // w26 <- v[A] .endif // Fast-path which gets the field from thread-local cache. % fetch_from_thread_cache("x0", miss_label=slow_path) .L${opcode}_resume: ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET] ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET] TEST_IF_MARKING .L${opcode}_read_barrier .L${opcode}_resume_after_read_barrier: .if $wide $store x26, [x0, x1] .else POISON_HEAP_REF_IF_OBJECT $is_object, w26 $store w26, [x0, x1] WRITE_BARRIER_IF_OBJECT $is_object, w26, w0, .L${opcode}_skip_write_barrier .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip .L${opcode}_read_barrier: bl art_quick_read_barrier_mark_reg00 b .L${opcode}_resume_after_read_barrier %def op_sput_slow_path(volatile_store, wide, is_object): mov x0, xSELF ldr x1, [sp] mov x2, xPC .if $is_object mov x3, x26 .else mov x3, #0 .endif EXPORT_PC bl nterp_get_static_field .if $is_object // Reload the value as it may have moved. lsr w2, wINST, #8 // w2 <- A GET_VREG w26, w2 // w26 <- v[A] .endif tbz x0, #0, .L${opcode}_resume CLEAR_STATIC_VOLATILE_MARKER x0 ldr w1, [x0, #ART_FIELD_OFFSET_OFFSET] ldr w0, [x0, #ART_FIELD_DECLARING_CLASS_OFFSET] TEST_IF_MARKING .L${opcode}_slow_path_read_barrier .L${opcode}_slow_path_resume_after_read_barrier: add x1, x0, x1 .if $wide $volatile_store x26, [x1] .else POISON_HEAP_REF_IF_OBJECT $is_object, w26 $volatile_store w26, [x1] WRITE_BARRIER_IF_OBJECT $is_object, w26, w0, .L${opcode}_slow_path_skip_write_barrier .endif FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip .L${opcode}_slow_path_read_barrier: bl art_quick_read_barrier_mark_reg00 b .L${opcode}_slow_path_resume_after_read_barrier %def op_sput_wide(): % op_sput(store="str", volatile_store="stlr", wide="1", is_object="0") %def op_sput_object(): % op_sput(store="str", volatile_store="stlr", wide="0", is_object="1") %def op_new_instance(): EXPORT_PC // Fast-path which gets the class from thread-local cache. % fetch_from_thread_cache("x0", miss_label="2f") TEST_IF_MARKING 3f 4: ldr lr, [xSELF, #THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET] blr lr dmb ishst // need fence for making object's class visible 1: lsr w1, wINST, #8 // w1 <- A SET_VREG_OBJECT w0, w1 // fp[A] <- value FETCH_ADVANCE_INST 2 GET_INST_OPCODE ip GOTO_OPCODE ip 2: mov x0, xSELF ldr x1, [sp] mov x2, xPC bl nterp_allocate_object b 1b 3: bl art_quick_read_barrier_mark_reg00 b 4b