Index: src/invocation/apple-call-x86_64.S ================================================================== --- src/invocation/apple-call-x86_64.S +++ src/invocation/apple-call-x86_64.S @@ -25,28 +25,28 @@ subq $16, %rsp andq $-16, %rsp movq %rdi, -8(%rbp) - leaq 208(%rdi), %rdx - movq 200(%rdi), %rcx + leaq 240(%rdi), %rdx + movq 232(%rdi), %rcx testq $1, %rcx - jnz .fix_align + jnz Lfix_align -.fill_stack: +Lfill_stack: testq %rcx, %rcx - jz .stack_filled + jz Lstack_filled decq %rcx movq (%rdx,%rcx,8), %r11 pushq %r11 - jmp .fill_stack + jmp Lfill_stack -.stack_filled: - movb 192(%rdi), %al +Lstack_filled: + movb 224(%rdi), %al movdqa 176(%rdi), %xmm7 movdqa 160(%rdi), %xmm6 movdqa 144(%rdi), %xmm5 movdqa 128(%rdi), %xmm4 @@ -68,14 +68,22 @@ movq %rax, 48(%rdi) movq %rdx, 56(%rdi) movdqa %xmm0, 64(%rdi) movdqa %xmm1, 80(%rdi) + cmpb $2, 225(%rdi) + je Lpop_long_double + +Lreturn: movq %rbp, %rsp popq %rbp ret -.fix_align: +Lfix_align: xorq %r11, %r11 pushq %r11 - jmp .fill_stack + jmp Lfill_stack + +Lpop_long_double: + fstpt 192(%rdi) + jmp Lreturn Index: src/invocation/invoke-x86_64.m ================================================================== --- src/invocation/invoke-x86_64.m +++ src/invocation/invoke-x86_64.m @@ -27,16 +27,24 @@ #import "OFOutOfMemoryException.h" #define NUM_GPR_IN 6 #define NUM_GPR_OUT 2 #define NUM_SSE_IN 8 -#define NUM_SSE_OUT 2 +#define NUM_X87_OUT 2 + +enum { + RETURN_TYPE_NORMAL, + RETURN_TYPE_STRUCT, + RETURN_TYPE_X87 +}; struct call_context { uint64_t gpr[NUM_GPR_IN + NUM_GPR_OUT]; __m128 sse[NUM_SSE_IN]; + long double x87[NUM_X87_OUT]; uint8_t num_sse_used; + uint8_t return_type; uint64_t stack_size; uint64_t stack[]; }; extern void of_invocation_call(struct call_context *); @@ -55,14 +63,16 @@ for (size_t i = 0; i < numberOfArguments; i++) { union { uint64_t gpr; __m128 sse; + long double x87; } value; enum { VALUE_GPR, - VALUE_SSE + VALUE_SSE, + VALUE_X87 } valueType; typeEncoding = [methodSignature argumentTypeAtIndex: i]; if (*typeEncoding == 'r') @@ -87,33 +97,29 @@ 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) -#ifdef __SIZEOF_INT128__ - /* TODO: 't' */ - /* TODO: 'T' */ -#endif - case 'f': - { - float tmp; - [invocation getArgument: &tmp - atIndex: i]; - value.sse = _mm_set_ss(tmp); - valueType = VALUE_SSE; - } - break; - case 'd': - { - double tmp; - [invocation getArgument: &tmp - atIndex: i]; - value.sse = _mm_set_sd(tmp); - valueType = VALUE_SSE; - } - break; - /* TODO: 'D' */ + case 'f':; + float floatTmp; + [invocation getArgument: &floatTmp + atIndex: i]; + value.sse = _mm_set_ss(floatTmp); + valueType = VALUE_SSE; + break; + case 'd':; + double doubleTmp; + [invocation getArgument: &doubleTmp + atIndex: i]; + value.sse = _mm_set_sd(doubleTmp); + valueType = VALUE_SSE; + break; + case 'D': + [invocation getArgument: &value.x87 + atIndex: i]; + valueType = VALUE_X87; + break; CASE_GPR('B', _Bool) CASE_GPR('*', uintptr_t) CASE_GPR('@', uintptr_t) CASE_GPR('#', uintptr_t) CASE_GPR(':', uintptr_t) @@ -122,17 +128,22 @@ /* TODO: '(' */ CASE_GPR('^', uintptr_t) #ifndef __STDC_NO_COMPLEX__ /* TODO: 'j' */ #endif +#ifdef __SIZEOF_INT128__ + /* TODO: 't' */ + /* TODO: 'T' */ +#endif default: free(context); @throw [OFInvalidFormatException exception]; #undef CASE_GPR } - if (valueType == VALUE_GPR) { + switch (valueType) { + case VALUE_GPR: if (currentGPR < NUM_GPR_IN) context->gpr[currentGPR++] = value.gpr; else { struct call_context *newContext; @@ -150,11 +161,12 @@ context = newContext; context->stack[context->stack_size - 1] = value.gpr; } - } else if (valueType == VALUE_SSE) { + break; + case VALUE_SSE: if (currentSSE < NUM_SSE_IN) { context->sse[currentSSE++] = value.sse; context->num_sse_used++; } else { struct call_context *newContext; @@ -175,20 +187,81 @@ context = newContext; _mm_store_sd(&tmp, value.sse); memcpy(&context->stack[context->stack_size - 1], &tmp, 8); } + break; + case VALUE_X87: + { + struct call_context *newContext; + + context->stack_size += 2; + + newContext = realloc(context, + sizeof(*context) + context->stack_size * 8); + if (newContext == NULL) { + free(context); + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: + sizeof(*context) + + context->stack_size * 8]; + } + + context = newContext; + memcpy(&context->stack[context->stack_size - 2], + &value.x87, 16); + } + break; } } - of_invocation_call(context); - typeEncoding = [methodSignature methodReturnType]; if (*typeEncoding == 'r') typeEncoding++; + switch (*typeEncoding) { + case 'c': + case 'C': + case 'i': + case 'I': + case 's': + case 'S': + case 'l': + case 'L': + case 'q': + case 'Q': + case 'f': + case 'd': + case 'B': + case '*': + case '@': + case '#': + case ':': + case '^': + context->return_type = RETURN_TYPE_NORMAL; + break; + case 'D': + context->return_type = RETURN_TYPE_X87; + break; + /* TODO: '[' */ + /* TODO: '{' */ + /* TODO: '(' */ +#ifndef __STDC_NO_COMPLEX__ + /* TODO: 'j' */ +#endif +#ifdef __SIZEOF_INT128__ + /* TODO: 't' */ + /* TODO: 'T' */ +#endif + default: + free(context); + @throw [OFInvalidFormatException exception]; + } + + of_invocation_call(context); + switch (*typeEncoding) { #define CASE_GPR(encoding, type) \ case encoding: \ { \ type tmp = (type)context->gpr[NUM_GPR_IN]; \ @@ -203,29 +276,23 @@ 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) -#ifdef __SIZEOF_INT128__ - /* TODO: 't' */ - /* TODO: 'T' */ -#endif - case 'f': - { - float tmp; - _mm_store_ss(&tmp, context->sse[0]); - [invocation setReturnValue: &tmp]; - } - break; - case 'd': - { - double tmp; - _mm_store_sd(&tmp, context->sse[0]); - [invocation setReturnValue: &tmp]; - } - break; - /* TODO: 'D' */ + case 'f':; + float floatTmp; + _mm_store_ss(&floatTmp, context->sse[0]); + [invocation setReturnValue: &floatTmp]; + break; + case 'd':; + double doubleTmp; + _mm_store_sd(&doubleTmp, context->sse[0]); + [invocation setReturnValue: &doubleTmp]; + break; + case 'D': + [invocation setReturnValue: &context->x87[0]]; + break; CASE_GPR('B', _Bool) CASE_GPR('*', uintptr_t) CASE_GPR('@', uintptr_t) CASE_GPR('#', uintptr_t) CASE_GPR(':', uintptr_t) @@ -233,14 +300,18 @@ /* TODO: '{' */ /* TODO: '(' */ CASE_GPR('^', uintptr_t) #ifndef __STDC_NO_COMPLEX__ /* TODO: 'j' */ +#endif +#ifdef __SIZEOF_INT128__ + /* TODO: 't' */ + /* TODO: 'T' */ #endif default: free(context); @throw [OFInvalidFormatException exception]; #undef CASE_GPR } free(context); } Index: tests/OFInvocationTests.m ================================================================== --- tests/OFInvocationTests.m +++ tests/OFInvocationTests.m @@ -79,10 +79,31 @@ : (double)d16 { return (d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + d14 + d15 + d16) / 16; } + +- (long double)invocationTestMethod4: (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; +} - (void)invocationTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; SEL selector = @selector(invocationTestMethod1::::); @@ -173,10 +194,34 @@ double doubleResult; TEST(@"-[invoke] #2", R([invocation invoke]) && R([invocation getReturnValue: &doubleResult]) && doubleResult == 8.5) + + /* Only when encoding long doubles is supported */ + if (strcmp(@encode(double), @encode(long double)) != 0) { + /* -[invoke] #3 */ + selector = @selector(invocationTestMethod4::::::::::::::::); + invocation = [OFInvocation invocationWithMethodSignature: + [self methodSignatureForSelector: selector]]; + + [invocation setArgument: &self + atIndex: 0]; + [invocation setArgument: &selector + atIndex: 1]; + + for (int i = 1; i <= 16; i++) { + long double d = i; + [invocation setArgument: &d + atIndex: i + 1]; + } + + long double longDoubleResult; + TEST(@"-[invoke] #3", R([invocation invoke]) && + R([invocation getReturnValue: &longDoubleResult]) && + longDoubleResult == 8.5) + } #endif [pool drain]; } @end