ObjFW  Artifact [4f0bfb5114]

Artifact 4f0bfb5114baaa86ce2b96bab475e6fe60e1d3153defa34a1170ea1cb14d48e1:


/*
 * 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);
}