Artifact 8ed55a5c76ee80039b83feb040e04478ceb0eef4e280243c783e424c923951f7:
- File
src/invocation/invoke-x86_64.m
— part of check-in
[d6e5e6beb3]
at
2017-09-14 21:51:51
on branch trunk
— apple-call-x86_64.S: Add support for stret and jmp
Jumping is useful in case of forwarding, so that the return value does
not need to be put back into an OFInvocation and then from an
OFInvocation back into registers. (user: js, size: 6904) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 * Jonathan Schleifer <js@heap.zone> * * 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 <stdint.h> #include <stdlib.h> #include <xmmintrin.h> #import "OFInvocation.h" #import "OFMethodSignature.h" #import "OFInvalidFormatException.h" #import "OFOutOfMemoryException.h" #define NUM_GPR_IN 6 #define NUM_GPR_OUT 2 #define NUM_SSE_IN 8 #define NUM_X87_OUT 2 enum { RETURN_TYPE_NORMAL, RETURN_TYPE_STRUCT, RETURN_TYPE_X87, RETURN_TYPE_JMP, RETURN_TYPE_JMP_STRET }; 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 *); static void pushGPR(struct call_context **context, size_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)->stack_size + 1) * 8)) == NULL) { free(*context); @throw [OFOutOfMemoryException exceptionWithRequestedSize: sizeof(**context) + ((*context)->stack_size + 1) * 8]; } newContext->stack[newContext->stack_size] = value; newContext->stack_size++; *context = newContext; } static void pushDouble(struct call_context **context, size_t *currentSSE, double value) { struct call_context *newContext; if (*currentSSE < NUM_SSE_IN) { (*context)->sse[(*currentSSE)++] = _mm_set_sd(value); (*context)->num_sse_used++; return; } if ((newContext = realloc(*context, sizeof(**context) + ((*context)->stack_size + 1) * 8)) == NULL) { free(*context); @throw [OFOutOfMemoryException exceptionWithRequestedSize: sizeof(**context) + ((*context)->stack_size + 1) * 8]; } memcpy(&newContext->stack[newContext->stack_size], &value, 8); newContext->stack_size++; *context = newContext; } static void pushLongDouble(struct call_context **context, long double value) { struct call_context *newContext; if ((newContext = realloc(*context, sizeof(**context) + ((*context)->stack_size + 2) * 8)) == NULL) { free(*context); @throw [OFOutOfMemoryException exceptionWithRequestedSize: sizeof(**context) + ((*context)->stack_size + 2) * 8]; } memcpy(&newContext->stack[newContext->stack_size], &value, 16); newContext->stack_size += 2; *context = newContext; } void of_invocation_invoke(OFInvocation *invocation) { OFMethodSignature *methodSignature = [invocation methodSignature]; size_t numberOfArguments = [methodSignature numberOfArguments]; struct call_context *context; const char *typeEncoding; size_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, 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 'f':; float floatTmp; [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_GPR('B', _Bool) CASE_GPR('*', uintptr_t) CASE_GPR('@', uintptr_t) CASE_GPR('#', uintptr_t) CASE_GPR(':', uintptr_t) /* TODO: '[' */ /* 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 } } 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]; \ [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 '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) /* TODO: '[' */ /* 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); }