github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/gofrontend/libffi/src/microblaze/ffi.c (about)

     1  /* -----------------------------------------------------------------------
     2     ffi.c - Copyright (c) 2012, 2013 Xilinx, Inc
     3  
     4     MicroBlaze Foreign Function Interface
     5  
     6     Permission is hereby granted, free of charge, to any person obtaining
     7     a copy of this software and associated documentation files (the
     8     ``Software''), to deal in the Software without restriction, including
     9     without limitation the rights to use, copy, modify, merge, publish,
    10     distribute, sublicense, and/or sell copies of the Software, and to
    11     permit persons to whom the Software is furnished to do so, subject to
    12     the following conditions:
    13  
    14     The above copyright notice and this permission notice shall be included
    15     in all copies or substantial portions of the Software.
    16  
    17     THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
    18     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    19     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    20     NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    21     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    22     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    23     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    24     DEALINGS IN THE SOFTWARE.
    25     ----------------------------------------------------------------------- */
    26  
    27  #include <ffi.h>
    28  #include <ffi_common.h>
    29  
    30  extern void ffi_call_SYSV(void (*)(void*, extended_cif*), extended_cif*,
    31  		unsigned int, unsigned int, unsigned int*, void (*fn)(void),
    32  		unsigned int, unsigned int);
    33  
    34  extern void ffi_closure_SYSV(void);
    35  
    36  #define WORD_SIZE			sizeof(unsigned int)
    37  #define ARGS_REGISTER_SIZE	(WORD_SIZE * 6)
    38  #define WORD_ALIGN(x)		ALIGN(x, WORD_SIZE)
    39  
    40  /* ffi_prep_args is called by the assembly routine once stack space
    41     has been allocated for the function's arguments */
    42  void ffi_prep_args(void* stack, extended_cif* ecif)
    43  {
    44  	unsigned int i;
    45  	ffi_type** p_arg;
    46  	void** p_argv;
    47  	void* stack_args_p = stack;
    48  
    49  	p_argv = ecif->avalue;
    50  
    51  	if (ecif == NULL || ecif->cif == NULL) {
    52  		return; /* no description to prepare */
    53  	}
    54  
    55  	if ((ecif->cif->rtype != NULL) &&
    56  			(ecif->cif->rtype->type == FFI_TYPE_STRUCT))
    57  	{
    58  		/* if return type is a struct which is referenced on the stack/reg5,
    59  		 * by a pointer. Stored the return value pointer in r5.
    60  		 */
    61  		char* addr = stack_args_p;
    62  		memcpy(addr, &(ecif->rvalue), WORD_SIZE);
    63  		stack_args_p += WORD_SIZE;
    64  	}
    65  
    66  	if (ecif->avalue == NULL) {
    67  		return; /* no arguments to prepare */
    68  	}
    69  
    70  	for (i = 0, p_arg = ecif->cif->arg_types; i < ecif->cif->nargs;
    71  			i++, p_arg++)
    72  	{
    73  		size_t size = (*p_arg)->size;
    74  		int type = (*p_arg)->type;
    75  		void* value = p_argv[i];
    76  		char* addr = stack_args_p;
    77  		int aligned_size = WORD_ALIGN(size);
    78  
    79  		/* force word alignment on the stack */
    80  		stack_args_p += aligned_size;
    81  		
    82  		switch (type)
    83  		{
    84  			case FFI_TYPE_UINT8:
    85  				*(unsigned int *)addr = (unsigned int)*(UINT8*)(value);
    86  				break;
    87  			case FFI_TYPE_SINT8:
    88  				*(signed int *)addr = (signed int)*(SINT8*)(value);
    89  				break;
    90  			case FFI_TYPE_UINT16:
    91  				*(unsigned int *)addr = (unsigned int)*(UINT16*)(value);
    92  				break;
    93  			case FFI_TYPE_SINT16:
    94  				*(signed int *)addr = (signed int)*(SINT16*)(value);
    95  				break;
    96  			case FFI_TYPE_STRUCT:
    97  #if __BIG_ENDIAN__
    98  				/*
    99  				 * MicroBlaze toolchain appears to emit:
   100  				 * bsrli r5, r5, 8 (caller)
   101  				 * ...
   102  				 * <branch to callee>
   103  				 * ...
   104  				 * bslli r5, r5, 8 (callee)
   105  				 * 
   106  				 * For structs like "struct a { uint8_t a[3]; };", when passed
   107  				 * by value.
   108  				 *
   109  				 * Structs like "struct b { uint16_t a; };" are also expected
   110  				 * to be packed strangely in registers.
   111  				 *
   112  				 * This appears to be because the microblaze toolchain expects
   113  				 * "struct b == uint16_t", which is only any issue for big
   114  				 * endian.
   115  				 *
   116  				 * The following is a work around for big-endian only, for the
   117  				 * above mentioned case, it will re-align the contents of a
   118  				 * <= 3-byte struct value.
   119  				 */
   120  				if (size < WORD_SIZE)
   121  				{
   122  				  memcpy (addr + (WORD_SIZE - size), value, size);
   123  				  break;
   124  				}
   125  #endif
   126  			case FFI_TYPE_SINT32:
   127  			case FFI_TYPE_UINT32:
   128  			case FFI_TYPE_FLOAT:
   129  			case FFI_TYPE_SINT64:
   130  			case FFI_TYPE_UINT64:
   131  			case FFI_TYPE_DOUBLE:
   132  			default:
   133  				memcpy(addr, value, aligned_size);
   134  		}
   135  	}
   136  }
   137  
   138  ffi_status ffi_prep_cif_machdep(ffi_cif* cif)
   139  {
   140  	/* check ABI */
   141  	switch (cif->abi)
   142  	{
   143  		case FFI_SYSV:
   144  			break;
   145  		default:
   146  			return FFI_BAD_ABI;
   147  	}
   148  	return FFI_OK;
   149  }
   150  
   151  void ffi_call(ffi_cif* cif, void (*fn)(void), void* rvalue, void** avalue)
   152  {
   153  	extended_cif ecif;
   154  	ecif.cif = cif;
   155  	ecif.avalue = avalue;
   156  
   157  	/* If the return value is a struct and we don't have a return */
   158  	/* value address then we need to make one */
   159  	if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) {
   160  		ecif.rvalue = alloca(cif->rtype->size);
   161  	} else {
   162  		ecif.rvalue = rvalue;
   163  	}
   164  
   165  	switch (cif->abi)
   166  	{
   167  	case FFI_SYSV:
   168  		ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags,
   169  				ecif.rvalue, fn, cif->rtype->type, cif->rtype->size);
   170  		break;
   171  	default:
   172  		FFI_ASSERT(0);
   173  		break;
   174  	}
   175  }
   176  
   177  void ffi_closure_call_SYSV(void* register_args, void* stack_args,
   178  			ffi_closure* closure, void* rvalue,
   179  			unsigned int* rtype, unsigned int* rsize)
   180  {
   181  	/* prepare arguments for closure call */
   182  	ffi_cif* cif = closure->cif;
   183  	ffi_type** arg_types = cif->arg_types;
   184  
   185  	/* re-allocate data for the args. This needs to be done in order to keep
   186  	 * multi-word objects (e.g. structs) in contiguous memory. Callers are not
   187  	 * required to store the value of args in the lower 6 words in the stack
   188  	 * (although they are allocated in the stack).
   189  	 */
   190  	char* stackclone = alloca(cif->bytes);
   191  	void** avalue = alloca(cif->nargs * sizeof(void*));
   192  	void* struct_rvalue = NULL;
   193  	char* ptr = stackclone;
   194  	int i;
   195  
   196  	/* copy registers into stack clone */
   197  	int registers_used = cif->bytes;
   198  	if (registers_used > ARGS_REGISTER_SIZE) {
   199  		registers_used = ARGS_REGISTER_SIZE;
   200  	}
   201  	memcpy(stackclone, register_args, registers_used);
   202  
   203  	/* copy stack allocated args into stack clone */
   204  	if (cif->bytes > ARGS_REGISTER_SIZE) {
   205  		int stack_used = cif->bytes - ARGS_REGISTER_SIZE;
   206  		memcpy(stackclone + ARGS_REGISTER_SIZE, stack_args, stack_used);
   207  	}
   208  
   209  	/* preserve struct type return pointer passing */
   210  	if ((cif->rtype != NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) {
   211  		struct_rvalue = *((void**)ptr);
   212  		ptr += WORD_SIZE;
   213  	}
   214  
   215  	/* populate arg pointer list */
   216  	for (i = 0; i < cif->nargs; i++)
   217  	{
   218  		switch (arg_types[i]->type)
   219  		{
   220  			case FFI_TYPE_SINT8:
   221  			case FFI_TYPE_UINT8:
   222  #ifdef __BIG_ENDIAN__
   223  				avalue[i] = ptr + 3;
   224  #else
   225  				avalue[i] = ptr;
   226  #endif
   227  				break;
   228  			case FFI_TYPE_SINT16:
   229  			case FFI_TYPE_UINT16:
   230  #ifdef __BIG_ENDIAN__
   231  				avalue[i] = ptr + 2;
   232  #else
   233  				avalue[i] = ptr;
   234  #endif
   235  				break;
   236  			case FFI_TYPE_STRUCT:
   237  #if __BIG_ENDIAN__
   238  				/*
   239  				 * Work around strange ABI behaviour.
   240  				 * (see info in ffi_prep_args)
   241  				 */
   242  				if (arg_types[i]->size < WORD_SIZE)
   243  				{
   244  				  memcpy (ptr, ptr + (WORD_SIZE - arg_types[i]->size), arg_types[i]->size);
   245  				}
   246  #endif
   247  				avalue[i] = (void*)ptr;
   248  				break;
   249  			case FFI_TYPE_UINT64:
   250  			case FFI_TYPE_SINT64:
   251  			case FFI_TYPE_DOUBLE:
   252  				avalue[i] = ptr;
   253  				break;
   254  			case FFI_TYPE_SINT32:
   255  			case FFI_TYPE_UINT32:
   256  			case FFI_TYPE_FLOAT:
   257  			default:
   258  				/* default 4-byte argument */
   259  				avalue[i] = ptr;
   260  				break;
   261  		}
   262  		ptr += WORD_ALIGN(arg_types[i]->size);
   263  	}
   264  
   265  	/* set the return type info passed back to the wrapper */
   266  	*rsize = cif->rtype->size;
   267  	*rtype = cif->rtype->type;
   268  	if (struct_rvalue != NULL) {
   269  		closure->fun(cif, struct_rvalue, avalue, closure->user_data);
   270  		/* copy struct return pointer value into function return value */
   271  		*((void**)rvalue) = struct_rvalue;
   272  	} else {
   273  		closure->fun(cif, rvalue, avalue, closure->user_data);
   274  	}
   275  }
   276  
   277  ffi_status ffi_prep_closure_loc(
   278  		ffi_closure* closure, ffi_cif* cif,
   279  		void (*fun)(ffi_cif*, void*, void**, void*),
   280  		void* user_data, void* codeloc)
   281  {
   282  	unsigned long* tramp = (unsigned long*)&(closure->tramp[0]);
   283  	unsigned long cls = (unsigned long)codeloc;
   284  	unsigned long fn = 0;
   285  	unsigned long fn_closure_call_sysv = (unsigned long)ffi_closure_call_SYSV;
   286  
   287  	closure->cif = cif;
   288  	closure->fun = fun;
   289  	closure->user_data = user_data;
   290  
   291  	switch (cif->abi)
   292  	{
   293  	case FFI_SYSV:
   294  		fn = (unsigned long)ffi_closure_SYSV;
   295  
   296  		/* load r11 (temp) with fn */
   297  		/* imm fn(upper) */
   298  		tramp[0] = 0xb0000000 | ((fn >> 16) & 0xffff);
   299  		/* addik r11, r0, fn(lower) */
   300  		tramp[1] = 0x31600000 | (fn & 0xffff);
   301  
   302  		/* load r12 (temp) with cls */
   303  		/* imm cls(upper) */
   304  		tramp[2] = 0xb0000000 | ((cls >> 16) & 0xffff);
   305  		/* addik r12, r0, cls(lower) */
   306  		tramp[3] = 0x31800000 | (cls & 0xffff);
   307  
   308  		/* load r3 (temp) with ffi_closure_call_SYSV */
   309  		/* imm fn_closure_call_sysv(upper) */
   310  		tramp[4] = 0xb0000000 | ((fn_closure_call_sysv >> 16) & 0xffff);
   311  		/* addik r3, r0, fn_closure_call_sysv(lower) */
   312  		tramp[5] = 0x30600000 | (fn_closure_call_sysv & 0xffff);
   313  		/* branch/jump to address stored in r11 (fn) */
   314  		tramp[6] = 0x98085800; /* bra r11 */
   315  
   316  		break;
   317  	default:
   318  		return FFI_BAD_ABI;
   319  	}
   320  	return FFI_OK;
   321  }