Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -357,10 +357,11 @@ AS_IF([test x"$enable_shared" != x"no"], [ BUILDSYS_SHARED_LIB AC_SUBST(OBJFW_SHARED_LIB, "${LIB_PREFIX}objfw${LIB_SUFFIX}") AC_SUBST(EXCEPTIONS_LIB_A, "exceptions.lib.a") AC_SUBST(FORWARDING_LIB_A, "forwarding.lib.a") + AC_SUBST(INVOCATION_LIB_A, "invocation.lib.a") AC_SUBST(LOOKUP_ASM_LIB_A, "lookup-asm.lib.a") BUILDSYS_FRAMEWORK([ AC_SUBST(OBJFW_FRAMEWORK, "ObjFW.framework") build_framework="yes" @@ -397,10 +398,11 @@ ]) AS_IF([test x"$enable_static" = x"yes"], [ AC_SUBST(OBJFW_STATIC_LIB, "libobjfw.a") AC_SUBST(EXCEPTIONS_A, "exceptions.a") AC_SUBST(FORWARDING_A, "forwarding.a") + AC_SUBST(INVOCATION_A, "invocation.a") AC_SUBST(LOOKUP_ASM_A, "lookup-asm.a") ]) AC_DEFINE_UNQUOTED(PLUGIN_SUFFIX, "$PLUGIN_SUFFIX", [Suffix for plugins]) AS_IF([test x"$enable_files" != x"no" -a x"$PLUGIN_SUFFIX" != x""], [ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -1,8 +1,8 @@ include ../extra.mk -SUBDIRS = ${RUNTIME} exceptions encodings forwarding +SUBDIRS = ${RUNTIME} exceptions encodings forwarding invocation SUBDIRS_AFTER = ${BRIDGE} DISTCLEAN = Info.plist objfw-defs.h SHARED_LIB = ${OBJFW_SHARED_LIB} STATIC_LIB = ${OBJFW_STATIC_LIB} @@ -230,14 +230,16 @@ ${OF_SELECT_KERNEL_EVENT_OBSERVER_M} \ OFTCPSocketSOCKS5Connector.m OBJS_EXTRA = exceptions/exceptions.a \ encodings/encodings.a \ - forwarding/forwarding.a + forwarding/forwarding.a \ + invocation/invocation.a LIB_OBJS_EXTRA = exceptions/exceptions.lib.a \ encodings/encodings.lib.a \ - forwarding/forwarding.lib.a + forwarding/forwarding.lib.a \ + invocation/invocation.lib.a include ../buildsys.mk CPPFLAGS += -I. -I.. -Iexceptions -Iruntime LD = ${OBJC} Index: src/OFInvocation.h ================================================================== --- src/OFInvocation.h +++ src/OFInvocation.h @@ -14,10 +14,22 @@ */ #import "OFObject.h" OF_ASSUME_NONNULL_BEGIN + +#ifdef OF_APPLE_RUNTIME +# ifdef OF_X86_64 +# define OF_INVOCATION_CAN_INVOKE +# endif +#else +# ifdef OF_ELF +# ifdef OF_X86_64 +# define OF_INVOCATION_CAN_INVOKE +# endif +# endif +#endif @class OFMethodSignature; @class OFMutableArray OF_GENERIC(ObjectType); @class OFMutableData; @@ -83,8 +95,15 @@ * @brief Gets the return value. * * @param buffer The buffer in which the return value is stored */ - (void)getReturnValue: (void *)buffer; + +#ifdef OF_INVOCATION_CAN_INVOKE +/** + * @brief Invokes the method. + */ +- (void)invoke; +#endif @end OF_ASSUME_NONNULL_END Index: src/OFInvocation.m ================================================================== --- src/OFInvocation.m +++ src/OFInvocation.m @@ -19,10 +19,14 @@ #import "OFInvocation.h" #import "OFArray.h" #import "OFData.h" #import "OFMethodSignature.h" + +#ifdef OF_INVOCATION_CAN_INVOKE +extern void of_invocation_invoke(OFInvocation *); +#endif @implementation OFInvocation @synthesize methodSignature = _methodSignature; + (instancetype)invocationWithMethodSignature: (OFMethodSignature *)signature @@ -103,6 +107,13 @@ - (void)getReturnValue: (void *)buffer { memcpy(buffer, _returnValue.items, _returnValue.itemSize); } + +#ifdef OF_INVOCATION_CAN_INVOKE +- (void)invoke +{ + of_invocation_invoke(self); +} +#endif @end ADDED src/invocation/Makefile Index: src/invocation/Makefile ================================================================== --- src/invocation/Makefile +++ src/invocation/Makefile @@ -0,0 +1,12 @@ +include ../../extra.mk + +STATIC_PIC_LIB_NOINST = ${INVOCATION_LIB_A} +STATIC_LIB_NOINST = ${INVOCATION_A} + +SRCS = call.S \ + invoke.m + +include ../../buildsys.mk + +ASFLAGS += -I../.. -I.. +OBJCFLAGS += -I../.. -I.. -I../exceptions -I../runtime ADDED src/invocation/apple-call-x86_64.S Index: src/invocation/apple-call-x86_64.S ================================================================== --- src/invocation/apple-call-x86_64.S +++ src/invocation/apple-call-x86_64.S @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include "invoke-x86_64.h" + +.globl _of_invocation_call + +.section __TEXT, __text, regular, pure_instructions +_of_invocation_call: + pushq %rbp + movq %rsp, %rbp + + subq $16, %rsp + andq $-16, %rsp + movq %rdi, -8(%rbp) + + leaq OFFSET_STACK(%rdi), %rdx + movq OFFSET_STACK_SIZE(%rdi), %rcx + + testq $1, %rcx + jnz Lfix_align + +Lfill_stack: + testq %rcx, %rcx + jz Lstack_filled + + decq %rcx + movq (%rdx,%rcx,8), %r11 + pushq %r11 + + jmp Lfill_stack + +Lstack_filled: + movb OFFSET_NUM_SSE_USED(%rdi), %al + + movaps OFFSET_SSE_INOUT+112(%rdi), %xmm7 + movaps OFFSET_SSE_INOUT+96(%rdi), %xmm6 + movaps OFFSET_SSE_INOUT+80(%rdi), %xmm5 + movaps OFFSET_SSE_INOUT+64(%rdi), %xmm4 + movaps OFFSET_SSE_INOUT+48(%rdi), %xmm3 + movaps OFFSET_SSE_INOUT+32(%rdi), %xmm2 + movaps OFFSET_SSE_INOUT+16(%rdi), %xmm1 + movaps OFFSET_SSE_INOUT(%rdi), %xmm0 + + movq OFFSET_GPR_IN+40(%rdi), %r9 + movq OFFSET_GPR_IN+32(%rdi), %r8 + movq OFFSET_GPR_IN+24(%rdi), %rcx + movq OFFSET_GPR_IN+16(%rdi), %rdx + movq OFFSET_GPR_IN+8(%rdi), %rsi + + movb OFFSET_RETURN_TYPE(%rdi), %r11b + movq OFFSET_GPR_IN(%rdi), %rdi + + cmpb $RETURN_TYPE_STRET, %r11b + je Lcall_send_stret + + cmpb $RETURN_TYPE_JMP, %r11b + je _objc_msgSend + + cmpb $RETURN_TYPE_JMP_STRET, %r11b + je _objc_msgSend_stret + + call _objc_msgSend + +Lafter_send: + movq -8(%rbp), %rdi + movq %rax, OFFSET_GPR_OUT(%rdi) + movq %rdx, OFFSET_GPR_OUT+8(%rdi) + movaps %xmm0, OFFSET_SSE_INOUT(%rdi) + movaps %xmm1, OFFSET_SSE_INOUT+16(%rdi) + + movb OFFSET_RETURN_TYPE(%rdi), %r11b + + cmpb $RETURN_TYPE_X87, %r11b + je Lpop_long_double + + cmpb $RETURN_TYPE_COMPLEX_X87, %r11b + je Lpop_complex_long_double + +Lreturn: + movq %rbp, %rsp + popq %rbp + + ret + +Lfix_align: + xorq %r11, %r11 + pushq %r11 + jmp Lfill_stack + +Lcall_send_stret: + call _objc_msgSend_stret + jmp Lafter_send + +Lpop_long_double: + fstpt OFFSET_X87_OUT(%rdi) + jmp Lreturn + +Lpop_complex_long_double: + fstpt OFFSET_X87_OUT(%rdi) + fstpt OFFSET_X87_OUT+16(%rdi) + jmp Lreturn ADDED src/invocation/call-x86_64-elf.S Index: src/invocation/call-x86_64-elf.S ================================================================== --- src/invocation/call-x86_64-elf.S +++ src/invocation/call-x86_64-elf.S @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include "invoke-x86_64.h" + +.globl of_invocation_call + +.section .text +of_invocation_call: + pushq %rbp + movq %rsp, %rbp + + subq $16, %rsp + andq $-16, %rsp + movq %rdi, -8(%rbp) + + movb OFFSET_RETURN_TYPE(%rdi), %r11b + cmpb $RETURN_TYPE_STRET, %r11b + je .Llookup_stret + cmpb $RETURN_TYPE_JMP_STRET, %r11b + je .Llookup_stret + + movq OFFSET_GPR_IN+8(%rdi), %rsi + movq OFFSET_GPR_IN+0(%rdi), %rdi + call objc_msg_lookup@PLT + +.Lafter_lookup: + movq %rax, -16(%rbp) + movq -8(%rbp), %rdi + + leaq OFFSET_STACK(%rdi), %rdx + movq OFFSET_STACK_SIZE(%rdi), %rcx + + testq $1, %rcx + jnz .Lfix_align + +.Lfill_stack: + testq %rcx, %rcx + jz .Lstack_filled + + decq %rcx + movq (%rdx,%rcx,8), %r11 + pushq %r11 + + jmp .Lfill_stack + +.Lstack_filled: + movb OFFSET_NUM_SSE_USED(%rdi), %al + + movaps OFFSET_SSE_INOUT+112(%rdi), %xmm7 + movaps OFFSET_SSE_INOUT+96(%rdi), %xmm6 + movaps OFFSET_SSE_INOUT+80(%rdi), %xmm5 + movaps OFFSET_SSE_INOUT+64(%rdi), %xmm4 + movaps OFFSET_SSE_INOUT+48(%rdi), %xmm3 + movaps OFFSET_SSE_INOUT+32(%rdi), %xmm2 + movaps OFFSET_SSE_INOUT+16(%rdi), %xmm1 + movaps OFFSET_SSE_INOUT(%rdi), %xmm0 + + movq OFFSET_GPR_IN+40(%rdi), %r9 + movq OFFSET_GPR_IN+32(%rdi), %r8 + movq OFFSET_GPR_IN+24(%rdi), %rcx + movq OFFSET_GPR_IN+16(%rdi), %rdx + movq OFFSET_GPR_IN+8(%rdi), %rsi + + movb OFFSET_RETURN_TYPE(%rdi), %r11b + movq OFFSET_GPR_IN(%rdi), %rdi + + cmpb $RETURN_TYPE_JMP, %r11b + je .Ljmp_into_method + cmpb $RETURN_TYPE_JMP_STRET, %r11b + je .Ljmp_into_method + + movq -16(%rbp), %r11 + call *%r11 + +.Lafter_send: + movq -8(%rbp), %rdi + movq %rax, OFFSET_GPR_OUT(%rdi) + movq %rdx, OFFSET_GPR_OUT+8(%rdi) + movaps %xmm0, OFFSET_SSE_INOUT(%rdi) + movaps %xmm1, OFFSET_SSE_INOUT+16(%rdi) + + movb OFFSET_RETURN_TYPE(%rdi), %r11b + + cmpb $RETURN_TYPE_X87, %r11b + je .Lpop_long_double + + cmpb $RETURN_TYPE_COMPLEX_X87, %r11b + je .Lpop_complex_long_double + +.Lreturn: + movq %rbp, %rsp + popq %rbp + + ret + +.Lfix_align: + xorq %r11, %r11 + pushq %r11 + jmp .Lfill_stack + +.Llookup_stret: + movq OFFSET_GPR_IN+16(%rdi), %rsi + movq OFFSET_GPR_IN+8(%rdi), %rdi + call objc_msg_lookup_stret@PLT + + jmp .Lafter_lookup + +.Ljmp_into_method: + movq -16(%rbp), %r11 + jmp *%r11 + +.Lpop_long_double: + fstpt OFFSET_X87_OUT(%rdi) + jmp .Lreturn + +.Lpop_complex_long_double: + fstpt OFFSET_X87_OUT(%rdi) + fstpt OFFSET_X87_OUT+16(%rdi) + jmp .Lreturn + +#ifdef OF_LINUX +.section .note.GNU-stack, "", %progbits +#endif ADDED src/invocation/call.S Index: src/invocation/call.S ================================================================== --- src/invocation/call.S +++ src/invocation/call.S @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include "platform.h" + +#ifdef OF_APPLE_RUNTIME +# ifdef OF_X86_64 +# include "apple-call-x86_64.S" +# endif +#else +# ifdef OF_ELF +# ifdef OF_X86_64 +# include "call-x86_64-elf.S" +# endif +# endif +#endif ADDED src/invocation/invoke-x86_64.h Index: src/invocation/invoke-x86_64.h ================================================================== --- src/invocation/invoke-x86_64.h +++ src/invocation/invoke-x86_64.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#define RETURN_TYPE_NORMAL 0 +#define RETURN_TYPE_STRET 1 +#define RETURN_TYPE_X87 2 +#define RETURN_TYPE_COMPLEX_X87 3 +#define RETURN_TYPE_JMP 4 +#define RETURN_TYPE_JMP_STRET 5 + +#define NUM_GPR_IN 6 +#define NUM_GPR_OUT 2 +#define NUM_SSE_INOUT 8 +#define NUM_X87_OUT 2 + +#define OFFSET_GPR_IN 0 +#define OFFSET_GPR_OUT (OFFSET_GPR_IN + NUM_GPR_IN * 8) +#define OFFSET_SSE_INOUT (OFFSET_GPR_OUT + NUM_GPR_OUT * 8) +#define OFFSET_X87_OUT (OFFSET_SSE_INOUT + NUM_SSE_INOUT * 16) +#define OFFSET_NUM_SSE_USED (OFFSET_X87_OUT + NUM_X87_OUT * 16) +#define OFFSET_RETURN_TYPE (OFFSET_NUM_SSE_USED + 1) +#define OFFSET_STACK_SIZE (OFFSET_RETURN_TYPE + 7) +#define OFFSET_STACK (OFFSET_STACK_SIZE + 8) ADDED src/invocation/invoke-x86_64.m Index: src/invocation/invoke-x86_64.m ================================================================== --- src/invocation/invoke-x86_64.m +++ src/invocation/invoke-x86_64.m @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#import "OFInvocation.h" +#import "OFMethodSignature.h" + +#import "OFInvalidFormatException.h" +#import "OFOutOfMemoryException.h" + +#import "invoke-x86_64.h" + +#import "macros.h" + +struct call_context { + uint64_t GPR[NUM_GPR_IN + NUM_GPR_OUT]; + __m128 SSE[NUM_SSE_INOUT]; + long double X87[NUM_X87_OUT]; + uint8_t numSSEUsed; + uint8_t returnType; + uint64_t stackSize; + uint64_t stack[]; +}; + +extern void of_invocation_call(struct call_context *); + +static void +pushGPR(struct call_context **context, uint_fast8_t *currentGPR, uint64_t value) +{ + struct call_context *newContext; + + if (*currentGPR < NUM_GPR_IN) { + (*context)->GPR[(*currentGPR)++] = value; + return; + } + + if ((newContext = realloc(*context, + sizeof(**context) + ((*context)->stackSize + 1) * 8)) == NULL) { + free(*context); + @throw [OFOutOfMemoryException exceptionWithRequestedSize: + sizeof(**context) + ((*context)->stackSize + 1) * 8]; + } + + newContext->stack[newContext->stackSize] = value; + newContext->stackSize++; + *context = newContext; +} + +static void +pushDouble(struct call_context **context, uint_fast8_t *currentSSE, + double value) +{ + struct call_context *newContext; + + if (*currentSSE < NUM_SSE_INOUT) { + (*context)->SSE[(*currentSSE)++] = (__m128)_mm_set_sd(value); + (*context)->numSSEUsed++; + return; + } + + if ((newContext = realloc(*context, + sizeof(**context) + ((*context)->stackSize + 1) * 8)) == NULL) { + free(*context); + @throw [OFOutOfMemoryException exceptionWithRequestedSize: + sizeof(**context) + ((*context)->stackSize + 1) * 8]; + } + + memcpy(&newContext->stack[newContext->stackSize], &value, 8); + newContext->stackSize++; + *context = newContext; +} + +static void +pushQuad(struct call_context **context, uint_fast8_t *currentSSE, + double low, double high) +{ + size_t stackSize; + struct call_context *newContext; + + if (*currentSSE + 1 < NUM_SSE_INOUT) { + (*context)->SSE[(*currentSSE)++] = (__m128)_mm_set_sd(low); + (*context)->SSE[(*currentSSE)++] = (__m128)_mm_set_sd(high); + (*context)->numSSEUsed += 2; + return; + } + + stackSize = (*context)->stackSize + 2; + + if ((newContext = realloc(*context, + sizeof(**context) + stackSize * 8)) == NULL) { + free(*context); + @throw [OFOutOfMemoryException exceptionWithRequestedSize: + sizeof(**context) + stackSize * 8]; + } + + memset(&newContext->stack[newContext->stackSize], '\0', + (stackSize - newContext->stackSize) * 8); + memcpy(&newContext->stack[stackSize - 2], &low, 8); + memcpy(&newContext->stack[stackSize - 1], &high, 8); + newContext->stackSize = stackSize; + *context = newContext; +} + +static void +pushLongDouble(struct call_context **context, long double value) +{ + struct call_context *newContext; + + if ((newContext = realloc(*context, + sizeof(**context) + ((*context)->stackSize + 2) * 8)) == NULL) { + free(*context); + @throw [OFOutOfMemoryException exceptionWithRequestedSize: + sizeof(**context) + ((*context)->stackSize + 2) * 8]; + } + + memcpy(&newContext->stack[newContext->stackSize], &value, 16); + newContext->stackSize += 2; + *context = newContext; +} + +static void +pushLongDoublePair(struct call_context **context, long double value[2]) +{ + size_t stackSize; + struct call_context *newContext; + + stackSize = OF_ROUND_UP_POW2(2UL, (*context)->stackSize) + 4; + + if ((newContext = realloc(*context, + sizeof(**context) + stackSize * 8)) == NULL) { + free(*context); + @throw [OFOutOfMemoryException exceptionWithRequestedSize: + sizeof(**context) + stackSize * 8]; + } + + memset(&newContext->stack[newContext->stackSize], '\0', + (stackSize - newContext->stackSize) * 8); + memcpy(&newContext->stack[stackSize - 4], value, 32); + newContext->stackSize = stackSize; + *context = newContext; +} + +#if defined(__SIZEOF_INT128__) && !defined(__clang__) +static void +pushInt128(struct call_context **context, uint_fast8_t *currentGPR, + uint64_t value[2]) +{ + size_t stackSize; + struct call_context *newContext; + + if (*currentGPR + 1 < NUM_GPR_IN) { + (*context)->GPR[(*currentGPR)++] = value[0]; + (*context)->GPR[(*currentGPR)++] = value[1]; + return; + } + + stackSize = OF_ROUND_UP_POW2(2, (*context)->stackSize) + 2; + + if ((newContext = realloc(*context, + sizeof(**context) + stackSize * 8)) == NULL) { + free(*context); + @throw [OFOutOfMemoryException exceptionWithRequestedSize: + sizeof(**context) + stackSize * 8]; + } + + memset(&newContext->stack[newContext->stackSize], '\0', + (stackSize - newContext->stackSize) * 8); + memcpy(&newContext->stack[stackSize - 2], value, 16); + newContext->stackSize = stackSize; + *context = newContext; +} +#endif + +void +of_invocation_invoke(OFInvocation *invocation) +{ + OFMethodSignature *methodSignature = invocation.methodSignature; + size_t numberOfArguments = methodSignature.numberOfArguments; + struct call_context *context; + const char *typeEncoding; + uint_fast8_t currentGPR = 0, currentSSE = 0; + + if ((context = calloc(sizeof(*context), 1)) == NULL) + @throw [OFOutOfMemoryException exception]; + + for (size_t i = 0; i < numberOfArguments; i++) { + typeEncoding = [methodSignature argumentTypeAtIndex: i]; + + if (*typeEncoding == 'r') + typeEncoding++; + + switch (*typeEncoding) { +#define CASE_GPR(encoding, type) \ + case encoding: \ + { \ + type tmp; \ + [invocation getArgument: &tmp \ + atIndex: i]; \ + pushGPR(&context, ¤tGPR, (uint64_t)tmp); \ + } \ + break; + CASE_GPR('c', char) + CASE_GPR('C', unsigned char) + CASE_GPR('i', int) + CASE_GPR('I', unsigned int) + CASE_GPR('s', short) + CASE_GPR('S', unsigned short) + CASE_GPR('l', long) + CASE_GPR('L', unsigned long) + CASE_GPR('q', long long) + CASE_GPR('Q', unsigned long long) + CASE_GPR('B', _Bool) + CASE_GPR('*', char *) + CASE_GPR('@', id) + CASE_GPR('#', Class) + /* + * Using SEL triggers a warning that casting a SEL to an + * integer is deprecated. + */ + CASE_GPR(':', void *) + CASE_GPR('^', void *) +#undef CASE_GPR +#ifdef __SIZEOF_INT128__ + case 't': + case 'T':; + uint64_t int128Tmp[2]; + [invocation getArgument: &int128Tmp + atIndex: i]; +# ifndef __clang__ + pushInt128(&context, ¤tGPR, int128Tmp); +# else + /* See https://bugs.llvm.org/show_bug.cgi?id=34646 */ + pushGPR(&context, ¤tGPR, int128Tmp[0]); + pushGPR(&context, ¤tGPR, int128Tmp[1]); +# endif + break; +#endif + case 'f':; + double floatTmp = 0; + [invocation getArgument: &floatTmp + atIndex: i]; + pushDouble(&context, ¤tSSE, floatTmp); + break; + case 'd':; + double doubleTmp; + [invocation getArgument: &doubleTmp + atIndex: i]; + pushDouble(&context, ¤tSSE, doubleTmp); + break; + case 'D':; + long double longDoubleTmp; + [invocation getArgument: &longDoubleTmp + atIndex: i]; + pushLongDouble(&context, longDoubleTmp); + break; + case 'j': + switch (typeEncoding[1]) { + case 'f':; + double complexFloatTmp; + [invocation getArgument: &complexFloatTmp + atIndex: i]; + pushDouble(&context, ¤tSSE, + complexFloatTmp); + break; + case 'd':; + double complexDoubleTmp[2]; + [invocation getArgument: &complexDoubleTmp + atIndex: i]; + pushQuad(&context, ¤tSSE, + complexDoubleTmp[0], complexDoubleTmp[1]); + break; + case 'D':; + long double complexLongDoubleTmp[2]; + [invocation getArgument: &complexLongDoubleTmp + atIndex: i]; + pushLongDoublePair(&context, + complexLongDoubleTmp); + break; + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + break; + /* TODO: '[' */ + /* TODO: '{' */ + /* TODO: '(' */ + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + } + + typeEncoding = methodSignature.methodReturnType; + + if (*typeEncoding == 'r') + typeEncoding++; + + switch (*typeEncoding) { + case 'v': + case 'c': + case 'C': + case 'i': + case 'I': + case 's': + case 'S': + case 'l': + case 'L': + case 'q': + case 'Q': + case 'B': + case '*': + case '@': + case '#': + case ':': + case '^': +#ifdef __SIZEOF_INT128__ + case 't': + case 'T': +#endif + case 'f': + case 'd': + context->returnType = RETURN_TYPE_NORMAL; + break; + case 'D': + context->returnType = RETURN_TYPE_X87; + break; + case 'j': + switch (typeEncoding[1]) { + case 'f': + case 'd': + context->returnType = RETURN_TYPE_NORMAL; + break; + case 'D': + context->returnType = RETURN_TYPE_COMPLEX_X87; + break; + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + break; + /* TODO: '[' */ + /* TODO: '{' */ + /* TODO: '(' */ + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + of_invocation_call(context); + + switch (*typeEncoding) { + case 'v': + break; +#define CASE_GPR(encoding, type) \ + case encoding: \ + { \ + type tmp = (type)context->GPR[NUM_GPR_IN]; \ + [invocation setReturnValue: &tmp]; \ + } \ + break; + CASE_GPR('c', char) + CASE_GPR('C', unsigned char) + CASE_GPR('i', int) + CASE_GPR('I', unsigned int) + CASE_GPR('s', short) + CASE_GPR('S', unsigned short) + CASE_GPR('l', long) + CASE_GPR('L', unsigned long) + CASE_GPR('q', long long) + CASE_GPR('Q', unsigned long long) + CASE_GPR('B', _Bool) + CASE_GPR('*', char *) + CASE_GPR('@', id) + CASE_GPR('#', Class) + CASE_GPR(':', SEL) + CASE_GPR('^', void *) +#undef CASE_GPR +#ifdef __SIZEOF_INT128__ + case 't': + case 'T':; + [invocation setReturnValue: &context->GPR[NUM_GPR_IN]]; + break; +#endif + case 'f':; + float floatTmp; + _mm_store_ss(&floatTmp, context->SSE[0]); + [invocation setReturnValue: &floatTmp]; + break; + case 'd':; + double doubleTmp; + _mm_store_sd(&doubleTmp, (__m128d)context->SSE[0]); + [invocation setReturnValue: &doubleTmp]; + break; + case 'D': + [invocation setReturnValue: &context->X87[0]]; + break; + case 'j': + switch (typeEncoding[1]) { + case 'f':; + double complexFloatTmp; + _mm_store_sd(&complexFloatTmp, + (__m128d)context->SSE[0]); + [invocation setReturnValue: &complexFloatTmp]; + break; + case 'd':; + double complexDoubleTmp[2]; + _mm_store_sd(&complexDoubleTmp[0], + (__m128d)context->SSE[0]); + _mm_store_sd(&complexDoubleTmp[1], + (__m128d)context->SSE[1]); + [invocation setReturnValue: &complexDoubleTmp]; + break; + case 'D': + [invocation setReturnValue: context->X87]; + break; + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + break; + /* TODO: '[' */ + /* TODO: '{' */ + /* TODO: '(' */ + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + free(context); +} ADDED src/invocation/invoke.m Index: src/invocation/invoke.m ================================================================== --- src/invocation/invoke.m +++ src/invocation/invoke.m @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include "platform.h" + +#if defined(OF_X86_64) && (defined(OF_APPLE_RUNTIME) || defined(OF_ELF)) +# include "invoke-x86_64.m" +#else +/* To not have an empty translation unit otherwise */ +int of_invocation_cannot_invoke; +#endif Index: tests/OFInvocationTests.m ================================================================== --- tests/OFInvocationTests.m +++ tests/OFInvocationTests.m @@ -305,9 +305,185 @@ R([invocation getArgument: &stp2 atIndex: 4]) && stp == stp2) TEST(@"-[getArgument:atIndex:] #4", R([invocation getArgument: &st2 atIndex: 5]) && memcmp(&st, &st2, sizeof(st)) == 0) + +#ifdef OF_INVOCATION_CAN_INVOKE + /* -[invoke] #1 */ + selector = @selector(invocationTestMethod2:); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + [invocation setArgument: &self atIndex: 2]; + + TEST(@"-[invoke] #1", R([invocation invoke])) + + /* -[invoke] #2 */ + selector = @selector(invocationTestMethod3::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) + [invocation setArgument: &j atIndex: j + 1]; + + int intResult; + TEST(@"-[invoke] #2", R([invocation invoke]) && + R([invocation getReturnValue: &intResult]) && intResult == 8) + + /* -[invoke] #3 */ + selector = @selector(invocationTestMethod4::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) { + double d = j; + [invocation setArgument: &d atIndex: j + 1]; + } + + double doubleResult; + TEST(@"-[invoke] #3", R([invocation invoke]) && + R([invocation getReturnValue: &doubleResult]) && + doubleResult == 8.5) + + /* -[invoke] #4 */ + selector = @selector(invocationTestMethod5::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) { + float f = j; + double d = j; + + if (j == 1 || j == 10) + [invocation setArgument: &d atIndex: j + 1]; + else + [invocation setArgument: &f atIndex: j + 1]; + } + + float floatResult; + TEST(@"-[invoke] #4", R([invocation invoke]) && + R([invocation getReturnValue: &floatResult]) && floatResult == 8.5) + + /* Only when encoding long doubles is supported */ + if (strcmp(@encode(double), @encode(long double)) != 0) { + /* -[invoke] #5 */ + selector = @selector(invocationTestMethod6::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) { + long double d = j; + [invocation setArgument: &d atIndex: j + 1]; + } + + long double longDoubleResult; + TEST(@"-[invoke] #5", R([invocation invoke]) && + R([invocation getReturnValue: &longDoubleResult]) && + longDoubleResult == 8.5) + } + +# if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__) + /* -[invoke] #6 */ + selector = @selector(invocationTestMethod7::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) { + complex float cf = j + 0.5 * j * I; + complex double cd = j + 0.5 * j * I; + + if (j & 1) + [invocation setArgument: &cf atIndex: j + 1]; + else + [invocation setArgument: &cd atIndex: j + 1]; + } + + complex double complexDoubleResult; + TEST(@"-[invoke] #6", R([invocation invoke]) && + R([invocation getReturnValue: &complexDoubleResult]) && + complexDoubleResult == 8.5 + 4.25 * I) + + /* Only when encoding complex long doubles is supported */ + if (strcmp(@encode(complex double), + @encode(complex long double)) != 0) { + /* -[invoke] #7 */ + selector = @selector(invocationTestMethod8::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) { + complex double cd = j + 0.5 * j * I; + complex float cf = j + 0.5 * j * I; + complex long double cld = j + 0.5 * j * I; + + switch (j % 3) { + case 0: + [invocation setArgument: &cld atIndex: j + 1]; + break; + case 1: + [invocation setArgument: &cd atIndex: j + 1]; + break; + case 2: + [invocation setArgument: &cf atIndex: j + 1]; + break; + } + } + + complex long double complexLongDoubleResult; + TEST(@"-[invoke] #7", R([invocation invoke]) && + R([invocation getReturnValue: &complexLongDoubleResult]) && + complexLongDoubleResult == 8.5 + 4.25 * I) + } +# endif + +# ifdef __SIZEOF_INT128__ + /* -[invoke] #8 */ + selector = @selector(invocationTestMethod9::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self atIndex: 0]; + [invocation setArgument: &selector atIndex: 1]; + + for (int j = 1; j <= 16; j++) { + __extension__ __int128 i128 = 0xFFFFFFFFFFFFFFFF; + i128 <<= 64; + i128 |= j; + + if (j == 1 || j == 5) + [invocation setArgument: &j atIndex: j + 1]; + else + [invocation setArgument: &i128 atIndex: j + 1]; + } + + __extension__ __int128 int128Result; + TEST(@"-[invoke] #8", R([invocation invoke]) && + R([invocation getReturnValue: &int128Result]) && + int128Result == __extension__ ((__int128)0xFFFFFFFFFFFFFFFF << 64) + + 8) +# endif +#endif objc_autoreleasePoolPop(pool); } @end