// check-cast vAA, type@BBBB
// Format 21c: AA|1f BBBB
// Throw a ClassCastException if the reference in the given register cannot be cast to the indicated
// type.
%def op_check_cast():
% fetch_from_thread_cache(/*expected klass*/"a1", f".L{opcode}_miss", "t0", "t1")
.L${opcode}_miss_resume:
srliw t0, xINST, 8 // t0 := AA
% get_vreg("a0", "t0", is_unsigned=True) # a0 := fp[AA], zext
beqz a0, .L${opcode}_next // null
lwu a2, MIRROR_OBJECT_CLASS_OFFSET(a0) // a2 := actual klass
UNPOISON_HEAP_REF a2
// Fast path: compare without read barrier.
bne a1, a2, .L${opcode}_slow
.L${opcode}_slow:
// A0 and A1 in position for quick call.
% slow_path = add_slow_path(op_check_cast_slow_path, "t0", "t1", "t2")
tail $slow_path // slow offset exceeds branch imm
// args a0, a1, a2
// Checks cases for (1) interface, (2) array, and (3) super classes.
// Hardcoded: a0 (obj), a1 (expected klass), a2 (actual klass)
//
// Note. We don't do read barriers for simplicity. However, this means that fetched objects may be a
// from-space reference. That'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 due to stale data, so
// all negative cases must pass through the runtime, via potential read barrier.
%def op_check_cast_slow_path(z0, z1, z2):
// Interface check: cut to runtime.
lwu $z0, MIRROR_CLASS_ACCESS_FLAGS_OFFSET(a1)
BRANCH_IF_BIT_SET $z0, $z0, MIRROR_CLASS_IS_INTERFACE_FLAG_BIT, .L${opcode}_runtime
// Advancement logic replicated here for branch distance.
.L${opcode}_slow_next:
FETCH_ADVANCE_INST 2
GET_INST_OPCODE $z0
GOTO_OPCODE $z0
.L${opcode}_mark:
call art_quick_read_barrier_mark_reg11 // a1, expected klass
j .L${opcode}_mark_resume
.L${opcode}_array:
UNPOISON_HEAP_REF $z0 // z0 = expected.component
lwu $z1, MIRROR_CLASS_COMPONENT_TYPE_OFFSET(a2) // z1 := actual.component
beqz $z1, .L${opcode}_runtime // null: actual not an array
UNPOISON_HEAP_REF $z1
lwu $z2, MIRROR_CLASS_SUPER_CLASS_OFFSET($z0) // z2 := expected.component.super
// z2 can skip unpoison for null check
bnez $z2, .L${opcode}_runtime // super type exists
// expected.component.super is null: expected is either Object[] or primitive array.
lhu $z2, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($z0) // z2 := expected.component.primitive
bnez $z2, .L${opcode}_runtime // expected's component is primitive
lwu $z2, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($z1) // z2 := actual.component.primitive
bnez $z2, .L${opcode}_runtime // actual's component is primitive
// Here, z0 is Object, and z1 is a subclass of Object.
j .L${opcode}_slow_next
// instance-of vA, vB, type@CCCC
// Format 22c: B|A|20 CCCC
// vA := 1 if vB instance-of CCCC, else 0
// Store in the given destination register 1 if the indicated reference is an instance of the given
// type, or 0 if not.
%def op_instance_of():
srliw s7, xINST, 8 // s7 := B|A
srliw s8, xINST, 12 // s8 := B
andi s7, s7, 0xF // s7 := A, used in slow path
% fetch_from_thread_cache(/*expected klass*/ "a1", f".L{opcode}_miss", "t0", "t1")
.L${opcode}_miss_resume:
% get_vreg("a0", "s8", is_unsigned=True) # a0 := fp[B], zext
beqz a0, .L${opcode}_next // a0 = null = dst value "false"
lwu a2, MIRROR_OBJECT_CLASS_OFFSET(a0) // a2 := actual klass
UNPOISON_HEAP_REF a2
// Fast path: compare without read barrier.
bne a1, a2, .L${opcode}_slow
.L${opcode}_slow:
// A0 and A1 in position for quick call.
% slow_path = add_slow_path(op_instance_of_slow_path, "s7", "t0", "t1", "t2")
tail $slow_path // slow offset exceeds branch imm
// args a0, a1, a2
// Checks cases for (1) interface, (2) array, and (3) super classes.
// Hardcoded: a0 (obj), a1 (expected klass), a2 (actual klass)
//
// Npte. If marking, don't bother with read barrier calls - cut to runtime. This arrangement allows
// the (non marking) super class fast path's negative case to skip the read barrier and runtime
// call, and correctly diagnose the situation with fp[A] := 0.
%def op_instance_of_slow_path(vA, z0, z1, z2):
TEST_IF_MARKING $z0, .L${opcode}_runtime_with_read_barrier
.L${opcode}_array:
UNPOISON_HEAP_REF $z0 // z0 = expected.component
lwu $z1, MIRROR_CLASS_SUPER_CLASS_OFFSET($z0) // z1 := expected.component.super
// z1 can skip unpoison for null check
bnez $z1, .L${opcode}_runtime // super type exists
// Here, expected.component.super is null: expected is either Object[] or primitive array.
lwu a0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET(a2) // a0 := actual.component
beqz a0, .L${opcode}_slow_next // actual not an array, a0 = null = dst value "false"
UNPOISON_HEAP_REF a0
lhu $z1, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET($z0) // z1 := expected.component.primitive
lhu $z2, MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(a0) // z2 := actual.component.primitive
or a0, $z1, $z2 // a0 := 0 if z1 = z2 = 0, else non-zero (Primitive::Type enum)
seqz a0, a0 // a0 := 1 if both are class types, else 0
// Here, when a0 = 1, expected.component is Object, and actual.component is a subclass of Object.
j .L${opcode}_slow_next
// new-instance vAA, type@BBBB
// Format 21c: AA|22 BBBB
// Construct a new instance of the indicated type, storing a reference to it in the destination. The
// type must refer to a non-array class.
%def op_new_instance():
EXPORT_PC
srliw s7, xINST, 8 // s7 := AA
% fetch_from_thread_cache(/*resolved klass*/"a0", f".L{opcode}_miss", "t0", "t1")
TEST_IF_MARKING t0, .L${opcode}_mark
.L${opcode}_mark_resume:
// sget-object vAA, field@BBBB
// Format 21c: AA|62 BBBB
// Variant for object load contains extra logic for GC mark.
%def op_sget_object():
srliw s7, xINST, 8 // s7 := AA (live through volatile path)
// Fast path: NterpGetStaticField's resolved_field from thread-local cache.
// Stores cache value in a0 to match slow path's return from NterpGetStaticField.
% fetch_from_thread_cache(/*resolved_field*/"a0", f".L{opcode}_slow", "t0", "t1")
.L${opcode}_slow_resume:
sext.w t0, a0 // t0 := field offset
srli a0, a0, 32 // a0 := class
TEST_IF_MARKING t1, .L${opcode}_mark
add t0, t0, a0 // t0 := field addr
lwu a0, (t0) // a0 := value (ref)
UNPOISON_HEAP_REF a0
// sput vAA, field@BBBB
// Format 21c: AA|67 BBBB
// klass.field := vAA
%def op_sput(width=32):
srliw t2, xINST, 8 // t2 := AA
% get_vreg("s7", "t2", width=width)
// s7 := value, held across slow path call
// Fast path: NterpGetStaticField's resolved_field from thread-local cache.
// Stores cache value in a0 to match slow path's return from NterpGetStaticField.
% fetch_from_thread_cache(/*resolved_field*/"a0", f".L{opcode}_slow", "t0", "t1")
.L${opcode}_slow_resume:
sext.w t0, a0 // t0 := field offset
srli a0, a0, 32 // a0 := class
TEST_IF_MARKING t1, .L${opcode}_mark
.L${opcode}_mark_resume:
add t0, t0, a0 // t0 := field addr, after possible a0 update
FETCH_ADVANCE_INST 2
% store(src="s7", dst="t0", width=width)
GET_INST_OPCODE t0
GOTO_OPCODE t0
// sput-object vAA, field@BBBB
// Format 21c: AA|69 BBBB
%def op_sput_object():
srliw s7, xINST, 8 // s7 := AA (live through slow path)
% get_vreg("s8", "s7", is_unsigned=True) # s8 := reference, replaced in slow path
// Fast path: NterpGetStaticField's resolved_field from thread-local cache.
// Stores cache value in a0 to match slow path's return from NterpGetStaticField.
% fetch_from_thread_cache(/*resolved_field*/"a0", f".L{opcode}_slow", "t0", "t1")
.L${opcode}_slow_resume: // s8 := reference (slow path only)
sext.w t0, a0 // t0 := field offset
srli a0, a0, 32 // a0 := class
TEST_IF_MARKING t1, .L${opcode}_mark
.L${opcode}_mark_resume:
add t0, t0, a0 // t0 := field addr, after possible a0 update
POISON_HEAP_REF s8 // Poisoning maps null to null for the null check in write barrier.
sw s8, (t0) // store reference
% object_write_barrier(value="s8", holder="a0", z0="t0", z1="t1", uniq=f"{opcode}")
FETCH_ADVANCE_INST 2
GET_INST_OPCODE t0
GOTO_OPCODE t0
add $z0, $z0, a0 // z0 := field addr, after possible a0 update
// Ensure the volatile store is released.
// \value must NOT be the destination register, the destination gets clobbered!
// \value's original value is used in the write barrier below.
POISON_HEAP_REF $value // Poisoning maps null to null for the null check in write barrier.
amoswap.w.rl zero, $value, ($z0)
% object_write_barrier(value=value, holder="a0", z0=z0, z1=z1, uniq=f"slow_{opcode}")
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.