%def invoke(helper="NterpUnimplemented"): call SYMBOL($helper) %def op_invoke_custom(): EXPORT_PC movzwl 2(rPC), %eax // call_site index, first argument of runtime call. jmp NterpCommonInvokeCustom %def op_invoke_custom_range(): EXPORT_PC movzwl 2(rPC), %eax // call_site index, first argument of runtime call. jmp NterpCommonInvokeCustomRange %def invoke_direct_or_super(helper="", range="", is_super=""): EXPORT_PC // Fast-path which gets the method from thread-local cache. % fetch_from_thread_cache("%eax", miss_label="2f") 1: // Load the first argument (the 'this' pointer). movzwl 4(rPC), %ecx // arguments .if !$range andl $$0xf, %ecx .endif movl (rFP, %ecx, 4), %ecx // NullPointerException check. testl %ecx, %ecx je common_errNullObject jmp $helper 2: movl rSELF:THREAD_SELF_OFFSET, ARG0 movl 0(%esp), ARG1 movl rPC, ARG2 call nterp_get_method .if $is_super jmp 1b .else testl MACRO_LITERAL(1), %eax je 1b andl $$-2, %eax // Remove the extra bit that marks it's a String. method. .if $range jmp NterpHandleStringInitRange .else jmp NterpHandleStringInit .endif .endif %def op_invoke_direct(): % invoke_direct_or_super(helper="NterpCommonInvokeInstance", range="0", is_super="0") %def op_invoke_direct_range(): % invoke_direct_or_super(helper="NterpCommonInvokeInstanceRange", range="1", is_super="0") %def op_invoke_polymorphic(): EXPORT_PC // No need to fetch the target method. // Load the first argument (the 'this' pointer). movzwl 4(rPC), %ecx // arguments andl $$0xf, %ecx movl (rFP, %ecx, 4), %ecx // NullPointerException check. testl %ecx, %ecx je common_errNullObject jmp NterpCommonInvokePolymorphic %def op_invoke_polymorphic_range(): EXPORT_PC // No need to fetch the target method. // Load the first argument (the 'this' pointer). movzwl 4(rPC), %ecx // arguments movl (rFP, %ecx, 4), %ecx // NullPointerException check. testl %ecx, %ecx je common_errNullObject jmp NterpCommonInvokePolymorphicRange %def invoke_interface(helper="", range=""): % slow_path = add_slow_path(op_invoke_interface_slow_path) EXPORT_PC // Fast-path which gets the interface method from thread-local cache. % fetch_from_thread_cache("%eax", miss_label=slow_path) .L${opcode}_resume: // First argument is the 'this' pointer. movzwl 4(rPC), %ecx // arguments .if !$range andl $$0xf, %ecx .endif movl (rFP, %ecx, 4), %ecx movl MIRROR_OBJECT_CLASS_OFFSET(%ecx), %edx UNPOISON_HEAP_REF edx // Test the first two bits of the fetched ArtMethod: // - If the first bit is set, this is a method on j.l.Object // - If the second bit is set, this is a default method. testl $$3, %eax jne 2f // Save interface method as hidden argument. movd %eax, %xmm7 movzw ART_METHOD_IMT_INDEX_OFFSET(%eax), %eax 1: movl MIRROR_CLASS_IMT_PTR_OFFSET_32(%edx), %edx movl (%edx, %eax, 4), %eax jmp $helper 2: testl $$1, %eax .if $range jne NterpHandleInvokeInterfaceOnObjectMethodRange .else jne NterpHandleInvokeInterfaceOnObjectMethod .endif // Default method andl $$-4, %eax // Save interface method as hidden argument. movd %eax, %xmm7 movzw ART_METHOD_METHOD_INDEX_OFFSET(%eax), %eax andl $$ART_METHOD_IMT_MASK, %eax jmp 1b %def op_invoke_interface_slow_path(): movl rSELF:THREAD_SELF_OFFSET, ARG0 movl 0(%esp), ARG1 movl rPC, ARG2 call nterp_get_method jmp .L${opcode}_resume %def op_invoke_interface(): % invoke_interface(helper="NterpCommonInvokeInterface", range="0") %def op_invoke_interface_range(): % invoke_interface(helper="NterpCommonInvokeInterfaceRange", range="1") %def invoke_static(helper=""): EXPORT_PC // Fast-path which gets the method from thread-local cache. % fetch_from_thread_cache("%eax", miss_label="1f") jmp $helper 1: movl rSELF:THREAD_SELF_OFFSET, ARG0 movl 0(%esp), ARG1 movl rPC, ARG2 call nterp_get_method jmp $helper %def op_invoke_static(): % invoke_static(helper="NterpCommonInvokeStatic") %def op_invoke_static_range(): % invoke_static(helper="NterpCommonInvokeStaticRange") %def op_invoke_super(): % invoke_direct_or_super(helper="NterpCommonInvokeInstance", range="0", is_super="1") %def op_invoke_super_range(): % invoke_direct_or_super(helper="NterpCommonInvokeInstanceRange", range="1", is_super="1") %def invoke_virtual(helper="", range=""): EXPORT_PC // Fast-path which gets the method from thread-local cache. % fetch_from_thread_cache("%eax", miss_label="2f") 1: // First argument is the 'this' pointer. movzwl 4(rPC), %ecx // arguments .if !$range andl $$0xf, %ecx .endif movl (rFP, %ecx, 4), %ecx // Note: if ecx is null, this will be handled by our SIGSEGV handler. movl MIRROR_OBJECT_CLASS_OFFSET(%ecx), %edx UNPOISON_HEAP_REF edx movl MIRROR_CLASS_VTABLE_OFFSET_32(%edx, %eax, 4), %eax jmp $helper 2: movl rSELF:THREAD_SELF_OFFSET, ARG0 movl 0(%esp), ARG1 movl rPC, ARG2 call nterp_get_method jmp 1b %def op_invoke_virtual(): % invoke_virtual(helper="NterpCommonInvokeInstance", range="0") %def op_invoke_virtual_range(): % invoke_virtual(helper="NterpCommonInvokeInstanceRange", range="1")