/*
* 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
};
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 *);
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++) {
union {
uint64_t gpr;
__m128 sse;
long double x87;
} value;
enum {
VALUE_GPR,
VALUE_SSE,
VALUE_X87
} valueType;
typeEncoding = [methodSignature argumentTypeAtIndex: i];
if (*typeEncoding == 'r')
typeEncoding++;
switch (*typeEncoding) {
#define CASE_GPR(encoding, type) \
case encoding: \
{ \
type tmp; \
[invocation getArgument: &tmp \
atIndex: i]; \
value.gpr = tmp; \
valueType = VALUE_GPR; \
} \
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];
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)
/* 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
}
switch (valueType) {
case VALUE_GPR:
if (currentGPR < NUM_GPR_IN)
context->gpr[currentGPR++] = value.gpr;
else {
struct call_context *newContext;
context->stack_size++;
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;
context->stack[context->stack_size - 1] =
value.gpr;
}
break;
case VALUE_SSE:
if (currentSSE < NUM_SSE_IN) {
context->sse[currentSSE++] = value.sse;
context->num_sse_used++;
} else {
struct call_context *newContext;
double tmp;
context->stack_size++;
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;
_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;
}
}
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);
}