Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -413,10 +413,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" @@ -441,10 +442,11 @@ AS_IF([test x"$supports_amiga_lib" != x"yes"], [enable_amiga_lib="no"]) AS_IF([test x"$enable_amiga_lib" != x"no"], [ 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_AMIGALIB_A, "lookup-asm.amigalib.a") ]) AC_ARG_ENABLE(static, AS_HELP_STRING([--enable-static], [build static library])) AS_IF([test x"$enable_shared" = x"no" -a x"$enable_amiga_lib" = x"no"], [ @@ -452,10 +454,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: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -30,10 +30,12 @@ EXCEPTIONS_A = @EXCEPTIONS_A@ EXCEPTIONS_LIB_A = @EXCEPTIONS_LIB_A@ FISH_COMPLETIONS = @FISH_COMPLETIONS@ FORWARDING_A = @FORWARDING_A@ FORWARDING_LIB_A = @FORWARDING_LIB_A@ +INVOCATION_A = @INVOCATION_A@ +INVOCATION_LIB_A = @INVOCATION_LIB_A@ LIBBASES_M = @LIBBASES_M@ LIBOBJFWRT_DEP = @LIBOBJFWRT_DEP@ LIBOBJFWRT_DEP_LVL2 = @LIBOBJFWRT_DEP_LVL2@ LIBOBJFW_DEP = @LIBOBJFW_DEP@ LIBOBJFW_DEP_LVL2 = @LIBOBJFW_DEP_LVL2@ 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} ${TLS} DISTCLEAN = Info.plist objfw-defs.h SHARED_LIB = ${OBJFW_SHARED_LIB} STATIC_LIB = ${OBJFW_STATIC_LIB} @@ -226,14 +226,16 @@ SRCS_WINDOWS += platform/Windows/OFWin32ConsoleStdIOStream.m \ versioninfo.rc 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 OFInvocationInvoke(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 +{ + OFInvocationInvoke(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 _OFInvocationCall + +.section __TEXT, __text, regular, pure_instructions +_OFInvocationCall: + pushq %rbp + movq %rsp, %rbp + + subq $16, %rsp + andq $-16, %rsp + movq %rdi, -8(%rbp) + + leaq offsetStack(%rdi), %rdx + movq offsetStackSize(%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 offsetNumSSEUsed(%rdi), %al + + movaps offsetSSEInOut+112(%rdi), %xmm7 + movaps offsetSSEInOut+96(%rdi), %xmm6 + movaps offsetSSEInOut+80(%rdi), %xmm5 + movaps offsetSSEInOut+64(%rdi), %xmm4 + movaps offsetSSEInOut+48(%rdi), %xmm3 + movaps offsetSSEInOut+32(%rdi), %xmm2 + movaps offsetSSEInOut+16(%rdi), %xmm1 + movaps offsetSSEInOut(%rdi), %xmm0 + + movq offsetGPRIn+40(%rdi), %r9 + movq offsetGPRIn+32(%rdi), %r8 + movq offsetGPRIn+24(%rdi), %rcx + movq offsetGPRIn+16(%rdi), %rdx + movq offsetGPRIn+8(%rdi), %rsi + + movb offsetReturnType(%rdi), %r11b + movq offsetGPRIn(%rdi), %rdi + + cmpb $returnTypeStret, %r11b + je Lcall_send_stret + + cmpb $returnTypeJmp, %r11b + je _objc_msgSend + + cmpb $returnTypeJmpStret, %r11b + je _objc_msgSend_stret + + call _objc_msgSend + +Lafter_send: + movq -8(%rbp), %rdi + movq %rax, offsetGPROut(%rdi) + movq %rdx, offsetGPROut+8(%rdi) + movaps %xmm0, offsetSSEInOut(%rdi) + movaps %xmm1, offsetSSEInOut+16(%rdi) + + movb offsetReturnType(%rdi), %r11b + + cmpb $returnTypeX87, %r11b + je Lpop_long_double + + cmpb $returnTypeComplexX87, %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 offsetX87Out(%rdi) + jmp Lreturn + +Lpop_complex_long_double: + fstpt offsetX87Out(%rdi) + fstpt offsetX87Out+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 OFInvocationCall + +.section .text +OFInvocationCall: + pushq %rbp + movq %rsp, %rbp + + subq $16, %rsp + andq $-16, %rsp + movq %rdi, -8(%rbp) + + movb offsetReturnType(%rdi), %r11b + cmpb $returnTypeStret, %r11b + je .Llookup_stret + cmpb $returnTypeJmpStret, %r11b + je .Llookup_stret + + movq offsetGPRIn+8(%rdi), %rsi + movq offsetGPRIn+0(%rdi), %rdi + call objc_msg_lookup@PLT + +.Lafter_lookup: + movq %rax, -16(%rbp) + movq -8(%rbp), %rdi + + leaq offsetStack(%rdi), %rdx + movq offsetStackSize(%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 offsetNumSSEUsed(%rdi), %al + + movaps offsetSSEInOut+112(%rdi), %xmm7 + movaps offsetSSEInOut+96(%rdi), %xmm6 + movaps offsetSSEInOut+80(%rdi), %xmm5 + movaps offsetSSEInOut+64(%rdi), %xmm4 + movaps offsetSSEInOut+48(%rdi), %xmm3 + movaps offsetSSEInOut+32(%rdi), %xmm2 + movaps offsetSSEInOut+16(%rdi), %xmm1 + movaps offsetSSEInOut(%rdi), %xmm0 + + movq offsetGPRIn+40(%rdi), %r9 + movq offsetGPRIn+32(%rdi), %r8 + movq offsetGPRIn+24(%rdi), %rcx + movq offsetGPRIn+16(%rdi), %rdx + movq offsetGPRIn+8(%rdi), %rsi + + movb offsetReturnType(%rdi), %r11b + movq offsetGPRIn(%rdi), %rdi + + cmpb $returnTypeJmp, %r11b + je .Ljmp_into_method + cmpb $returnTypeJmpStret, %r11b + je .Ljmp_into_method + + movq -16(%rbp), %r11 + call *%r11 + +.Lafter_send: + movq -8(%rbp), %rdi + movq %rax, offsetGPROut(%rdi) + movq %rdx, offsetGPROut+8(%rdi) + movaps %xmm0, offsetSSEInOut(%rdi) + movaps %xmm1, offsetSSEInOut+16(%rdi) + + movb offsetReturnType(%rdi), %r11b + + cmpb $returnTypeX87, %r11b + je .Lpop_long_double + + cmpb $returnTypeComplexX87, %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 offsetGPRIn+16(%rdi), %rsi + movq offsetGPRIn+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 offsetX87Out(%rdi) + jmp .Lreturn + +.Lpop_complex_long_double: + fstpt offsetX87Out(%rdi) + fstpt offsetX87Out+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 returnTypeNormal 0 +#define returnTypeStret 1 +#define returnTypeX87 2 +#define returnTypeComplexX87 3 +#define returnTypeJmp 4 +#define returnTypeJmpStret 5 + +#define numGPRIn 6 +#define numGPROut 2 +#define numSSEInOut 8 +#define numX87Out 2 + +#define offsetGPRIn 0 +#define offsetGPROut (offsetGPRIn + numGPRIn * 8) +#define offsetSSEInOut (offsetGPROut + numGPROut * 8) +#define offsetX87Out (offsetSSEInOut + numSSEInOut * 16) +#define offsetNumSSEUsed (offsetX87Out + numX87Out * 16) +#define offsetReturnType (offsetNumSSEUsed + 1) +#define offsetStackSize (offsetReturnType + 7) +#define offsetStack (offsetStackSize + 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 CallContext { + uint64_t GPR[numGPRIn + numGPROut]; + __m128 SSE[numSSEInOut]; + long double X87[numX87Out]; + uint8_t numSSEUsed; + uint8_t returnType; + uint64_t stackSize; + uint64_t stack[]; +}; + +extern void OFInvocationCall(struct CallContext *); + +static void +pushGPR(struct CallContext **context, uint_fast8_t *currentGPR, uint64_t value) +{ + struct CallContext *newContext; + + if (*currentGPR < numGPRIn) { + (*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 CallContext **context, uint_fast8_t *currentSSE, + double value) +{ + struct CallContext *newContext; + + if (*currentSSE < numSSEInOut) { + (*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 CallContext **context, uint_fast8_t *currentSSE, + double low, double high) +{ + size_t stackSize; + struct CallContext *newContext; + + if (*currentSSE + 1 < numSSEInOut) { + (*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 CallContext **context, long double value) +{ + struct CallContext *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 CallContext **context, long double value[2]) +{ + size_t stackSize; + struct CallContext *newContext; + + stackSize = OFRoundUpToPowerOf2(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 CallContext **context, uint_fast8_t *currentGPR, + uint64_t value[2]) +{ + size_t stackSize; + struct CallContext *newContext; + + if (*currentGPR + 1 < numGPRIn) { + (*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 +OFInvocationInvoke(OFInvocation *invocation) +{ + OFMethodSignature *methodSignature = invocation.methodSignature; + size_t numberOfArguments = methodSignature.numberOfArguments; + struct CallContext *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 = returnTypeNormal; + break; + case 'D': + context->returnType = returnTypeX87; + break; + case 'j': + switch (typeEncoding[1]) { + case 'f': + case 'd': + context->returnType = returnTypeNormal; + break; + case 'D': + context->returnType = returnTypeComplexX87; + break; + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + break; + /* TODO: '[' */ + /* TODO: '{' */ + /* TODO: '(' */ + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + OFInvocationCall(context); + + switch (*typeEncoding) { + case 'v': + break; +#define CASE_GPR(encoding, type) \ + case encoding: \ + { \ + type tmp = (type)context->GPR[numGPRIn]; \ + [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[numGPRIn]]; + 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 OFInvocationCannotInvoke; +#endif Index: tests/OFInvocationTests.m ================================================================== --- tests/OFInvocationTests.m +++ tests/OFInvocationTests.m @@ -37,10 +37,223 @@ : (struct TestStruct *)ptr : (struct TestStruct)st { return st; } + +#ifdef OF_INVOCATION_CAN_INVOKE +- (void)invocationTestMethod2: (id)obj +{ + assert(obj == self); +} + +- (int)invocationTestMethod3: (int)i1 + : (int)i2 + : (int)i3 + : (int)i4 + : (int)i5 + : (int)i6 + : (int)i7 + : (int)i8 + : (int)i9 + : (int)i10 + : (int)i11 + : (int)i12 + : (int)i13 + : (int)i14 + : (int)i15 + : (int)i16 +{ + return (i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + + i12 + i13 + i14 + i15 + i16) / 16; +} + +- (double)invocationTestMethod4: (double)d1 + : (double)d2 + : (double)d3 + : (double)d4 + : (double)d5 + : (double)d6 + : (double)d7 + : (double)d8 + : (double)d9 + : (double)d10 + : (double)d11 + : (double)d12 + : (double)d13 + : (double)d14 + : (double)d15 + : (double)d16 +{ + return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + + d12 + d13 + d14 + d15 + d16) / 16; +} + +- (float)invocationTestMethod5: (double)d1 + : (float)f2 + : (float)f3 + : (float)f4 + : (float)f5 + : (float)f6 + : (float)f7 + : (float)f8 + : (float)f9 + : (double)d10 + : (float)f11 + : (float)f12 + : (float)f13 + : (float)f14 + : (float)f15 + : (float)f16 +{ + return (float)((d1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + d10 + f11 + + f12 + f13 + f14 + f15 + f16) / 16); +} + +- (long double)invocationTestMethod6: (long double)d1 + : (long double)d2 + : (long double)d3 + : (long double)d4 + : (long double)d5 + : (long double)d6 + : (long double)d7 + : (long double)d8 + : (long double)d9 + : (long double)d10 + : (long double)d11 + : (long double)d12 + : (long double)d13 + : (long double)d14 + : (long double)d15 + : (long double)d16 +{ + return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + + d12 + d13 + d14 + d15 + d16) / 16; +} + +# if defined(HAVE_COMPLEX_H) && !defined(__STDC_NO_COMPLEX__) +- (complex double)invocationTestMethod7: (complex float)c1 + : (complex double)c2 + : (complex float)c3 + : (complex double)c4 + : (complex float)c5 + : (complex double)c6 + : (complex float)c7 + : (complex double)c8 + : (complex float)c9 + : (complex double)c10 + : (complex float)c11 + : (complex double)c12 + : (complex float)c13 + : (complex double)c14 + : (complex float)c15 + : (complex double)c16 +{ + OFEnsure(creal(c1) == 1.0 && cimag(c1) == 0.5); + OFEnsure(creal(c2) == 2.0 && cimag(c2) == 1.0); + OFEnsure(creal(c3) == 3.0 && cimag(c3) == 1.5); + OFEnsure(creal(c4) == 4.0 && cimag(c4) == 2.0); + OFEnsure(creal(c5) == 5.0 && cimag(c5) == 2.5); + OFEnsure(creal(c6) == 6.0 && cimag(c6) == 3.0); + OFEnsure(creal(c7) == 7.0 && cimag(c7) == 3.5); + OFEnsure(creal(c8) == 8.0 && cimag(c8) == 4.0); + OFEnsure(creal(c9) == 9.0 && cimag(c9) == 4.5); + OFEnsure(creal(c10) == 10.0 && cimag(c10) == 5.0); + OFEnsure(creal(c11) == 11.0 && cimag(c11) == 5.5); + OFEnsure(creal(c12) == 12.0 && cimag(c12) == 6.0); + OFEnsure(creal(c13) == 13.0 && cimag(c13) == 6.5); + OFEnsure(creal(c14) == 14.0 && cimag(c14) == 7.0); + OFEnsure(creal(c15) == 15.0 && cimag(c15) == 7.5); + OFEnsure(creal(c16) == 16.0 && cimag(c16) == 8.0); + + return (c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + + c12 + c13 + c14 + c15 + c16) / 16; +} + +- (complex long double)invocationTestMethod8: (complex double)c1 + : (complex float)c2 + : (complex long double)c3 + : (complex double)c4 + : (complex float)c5 + : (complex long double)c6 + : (complex double)c7 + : (complex float)c8 + : (complex long double)c9 + : (complex double)c10 + : (complex float)c11 + : (complex long double)c12 + : (complex double)c13 + : (complex float)c14 + : (complex long double)c15 + : (complex double)c16 +{ + OFEnsure(creal(c1) == 1.0 && cimag(c1) == 0.5); + OFEnsure(creal(c2) == 2.0 && cimag(c2) == 1.0); + OFEnsure(creal(c3) == 3.0 && cimag(c3) == 1.5); + OFEnsure(creal(c4) == 4.0 && cimag(c4) == 2.0); + OFEnsure(creal(c5) == 5.0 && cimag(c5) == 2.5); + OFEnsure(creal(c6) == 6.0 && cimag(c6) == 3.0); + OFEnsure(creal(c7) == 7.0 && cimag(c7) == 3.5); + OFEnsure(creal(c8) == 8.0 && cimag(c8) == 4.0); + OFEnsure(creal(c9) == 9.0 && cimag(c9) == 4.5); + OFEnsure(creal(c10) == 10.0 && cimag(c10) == 5.0); + OFEnsure(creal(c11) == 11.0 && cimag(c11) == 5.5); + OFEnsure(creal(c12) == 12.0 && cimag(c12) == 6.0); + OFEnsure(creal(c13) == 13.0 && cimag(c13) == 6.5); + OFEnsure(creal(c14) == 14.0 && cimag(c14) == 7.0); + OFEnsure(creal(c15) == 15.0 && cimag(c15) == 7.5); + OFEnsure(creal(c16) == 16.0 && cimag(c16) == 8.0); + + return (c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + + c12 + c13 + c14 + c15 + c16) / 16; +} +# endif + +# ifdef __SIZEOF_INT128__ +__extension__ +- (__int128)invocationTestMethod9: (int)i1 + : (__int128)i2 + : (__int128)i3 + : (__int128)i4 + : (int)i5 + : (__int128)i6 + : (__int128)i7 + : (__int128)i8 + : (__int128)i9 + : (__int128)i10 + : (__int128)i11 + : (__int128)i12 + : (__int128)i13 + : (__int128)i14 + : (__int128)i15 + : (__int128)i16 +{ + __int128 mask = (__int128)0xFFFFFFFFFFFFFFFF << 64; + + OFEnsure(i1 == 1); + OFEnsure(i2 == mask + 2); + OFEnsure(i3 == mask + 3); + OFEnsure(i4 == mask + 4); + OFEnsure(i5 == 5); + OFEnsure(i6 == mask + 6); + OFEnsure(i7 == mask + 7); + OFEnsure(i8 == mask + 8); + OFEnsure(i9 == mask + 9); + OFEnsure(i10 == mask + 10); + OFEnsure(i11 == mask + 11); + OFEnsure(i12 == mask + 12); + OFEnsure(i13 == mask + 13); + OFEnsure(i14 == mask + 14); + OFEnsure(i15 == mask + 15); + OFEnsure(i16 == mask + 16); + + return ((i1 + (int)i2 + (int)i3 + (int)i4 + i5 + (int)i6 + (int)i7 + + (int)i8 + (int)i9 + (int)i10 + (int)i11 + (int)i12 + (int)i13 + + (int)i14 + (int)i15 + (int)i16) / 16) + mask; +} +# endif +#endif - (void)invocationTests { void *pool = objc_autoreleasePoolPush(); SEL selector = @selector(invocationTestMethod1::::); @@ -92,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