Exemplo n.º 1
0
/*	Examine the argument and return set number of register required in each
	class.  Return zero if parameter should be passed in memory, otherwise
	the number of registers.  */
static int
examine_argument(
	ffi_type*				type,
	enum x86_64_reg_class	classes[MAX_CLASSES],
	_Bool					in_return,
	int*					pngpr,
	int*					pnsse)
{
	int	n = classify_argument(type, classes, 0);
	int ngpr = 0;
	int	nsse = 0;
	int	i;

	if (n == 0)
		return 0;

	for (i = 0; i < n; ++i)
	{
		switch (classes[i])
		{
			case X86_64_INTEGER_CLASS:
			case X86_64_INTEGERSI_CLASS:
				ngpr++;
				break;

			case X86_64_SSE_CLASS:
			case X86_64_SSESF_CLASS:
			case X86_64_SSEDF_CLASS:
				nsse++;
				break;

			case X86_64_NO_CLASS:
			case X86_64_SSEUP_CLASS:
				break;

			case X86_64_X87_CLASS:
			case X86_64_X87UP_CLASS:
			case X86_64_COMPLEX_X87_CLASS:
				return in_return != 0;

			default:
				abort();
		}
	}

	*pngpr = ngpr;
	*pnsse = nsse;

	return n;
}
Exemplo n.º 2
0
/* Classify the argument of type TYPE and mode MODE.
   CLASSES will be filled by the register class used to pass each word
   of the operand.  The number of words is returned.  In case the parameter
   should be passed in memory, 0 is returned. As a special case for zero
   sized containers, classes[0] will be NO_CLASS and 1 is returned.

   See the x86-64 PS ABI for details.
*/
static int
classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
		   size_t byte_offset)
{
  switch (type->type)
    {
    case FFI_TYPE_UINT8:
    case FFI_TYPE_SINT8:
    case FFI_TYPE_UINT16:
    case FFI_TYPE_SINT16:
    case FFI_TYPE_UINT32:
    case FFI_TYPE_SINT32:
    case FFI_TYPE_UINT64:
    case FFI_TYPE_SINT64:
    case FFI_TYPE_POINTER:
      {
	int size = byte_offset + type->size;

	if (size <= 4)
	  {
	    classes[0] = X86_64_INTEGERSI_CLASS;
	    return 1;
	  }
	else if (size <= 8)
	  {
	    classes[0] = X86_64_INTEGER_CLASS;
	    return 1;
	  }
	else if (size <= 12)
	  {
	    classes[0] = X86_64_INTEGER_CLASS;
	    classes[1] = X86_64_INTEGERSI_CLASS;
	    return 2;
	  }
	else if (size <= 16)
	  {
	    classes[0] = classes[1] = X86_64_INTEGERSI_CLASS;
	    return 2;
	  }
	else
	  FFI_ASSERT (0);
      }
    case FFI_TYPE_FLOAT:
      if (!(byte_offset % 8))
	classes[0] = X86_64_SSESF_CLASS;
      else
	classes[0] = X86_64_SSE_CLASS;
      return 1;
    case FFI_TYPE_DOUBLE:
      classes[0] = X86_64_SSEDF_CLASS;
      return 1;
    case FFI_TYPE_LONGDOUBLE:
      classes[0] = X86_64_X87_CLASS;
      classes[1] = X86_64_X87UP_CLASS;
      return 2;
    case FFI_TYPE_STRUCT:
      {
	const int UNITS_PER_WORD = 8;
	int words = (type->size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
	ffi_type **ptr; 
	int i;
	enum x86_64_reg_class subclasses[MAX_CLASSES];

	/* If the struct is larger than 32 bytes, pass it on the stack.  */
	if (type->size > 32)
	  return 0;

	for (i = 0; i < words; i++)
	  classes[i] = X86_64_NO_CLASS;

	/* Zero sized arrays or structures are NO_CLASS.  We return 0 to
	   signalize memory class, so handle it as special case.  */
	if (!words)
	  {
	    classes[0] = X86_64_NO_CLASS;
	    return 1;
	  }

	/* Merge the fields of structure.  */
	for (ptr = type->elements; *ptr != NULL; ptr++)
	  {
	    int num;

	    byte_offset = ALIGN (byte_offset, (*ptr)->alignment);

	    num = classify_argument (*ptr, subclasses, byte_offset % 8);
	    if (num == 0)
	      return 0;
	    for (i = 0; i < num; i++)
	      {
		int pos = byte_offset / 8;
		classes[i + pos] =
		  merge_classes (subclasses[i], classes[i + pos]);
	      }

	    byte_offset += (*ptr)->size;
	  }

	if (words > 2)
	  {
	    /* When size > 16 bytes, if the first one isn't
	       X86_64_SSE_CLASS or any other ones aren't
	       X86_64_SSEUP_CLASS, everything should be passed in
	       memory.  */
	    if (classes[0] != X86_64_SSE_CLASS)
	      return 0;

	    for (i = 1; i < words; i++)
	      if (classes[i] != X86_64_SSEUP_CLASS)
		return 0;
	  }

	/* Final merger cleanup.  */
	for (i = 0; i < words; i++)
	  {
	    /* If one class is MEMORY, everything should be passed in
	       memory.  */
	    if (classes[i] == X86_64_MEMORY_CLASS)
	      return 0;

	    /* The X86_64_SSEUP_CLASS should be always preceded by
	       X86_64_SSE_CLASS or X86_64_SSEUP_CLASS.  */
	    if (classes[i] == X86_64_SSEUP_CLASS
		&& classes[i - 1] != X86_64_SSE_CLASS
		&& classes[i - 1] != X86_64_SSEUP_CLASS)
	      {
		/* The first one should never be X86_64_SSEUP_CLASS.  */
		FFI_ASSERT (i != 0);
		classes[i] = X86_64_SSE_CLASS;
	      }

	    /*  If X86_64_X87UP_CLASS isn't preceded by X86_64_X87_CLASS,
		everything should be passed in memory.  */
	    if (classes[i] == X86_64_X87UP_CLASS
		&& (classes[i - 1] != X86_64_X87_CLASS))
	      {
		/* The first one should never be X86_64_X87UP_CLASS.  */
		FFI_ASSERT (i != 0);
		return 0;
	      }
	  }
	return words;
      }

    default:
      FFI_ASSERT(0);
    }
  return 0; /* Never reached.  */
}
Exemplo n.º 3
0
void
ffi_fill_return_value (return_value *rv, extended_cif *ecif)
{
  enum x86_64_reg_class classes[MAX_CLASSES];
  int i = 0, num;
  long *gpr = rv->gpr;
  __int128_t *sse = rv->sse;
  signed char sc;
  signed short ss;

  /* This is needed because of the way x86-64 handles signed short
     integers.  */
  switch (ecif->cif->rtype->type)
    {
    case FFI_TYPE_SINT8:
      sc = *(signed char *)gpr;
      *(long long *)ecif->rvalue = (long long)sc;
      return;
    case FFI_TYPE_SINT16:
      ss = *(signed short *)gpr;
      *(long long *)ecif->rvalue = (long long)ss;
      return;
    default:
      /* Just continue.  */
      ;
    }

  num = classify_argument (ecif->cif->rtype, classes, &i);

  if (num == 0)
    /* Return in memory.  */
    ecif->rvalue = (void *) rv->gpr[0];
  else if (num == 2 && classes[0] == X86_64_X87_CLASS &&
	classes[1] == X86_64_X87UP_CLASS)
    /* This is a long double (this is easiest to handle this way instead
       of an eightbyte at a time as in the loop below.  */
    *((long double *)ecif->rvalue) = rv->st0;
  else
    {
      void *a;

      for (i=0, a=ecif->rvalue; i<num; i++, a+=8)
	{
	  switch (classes[i])
	    {
	    case X86_64_INTEGER_CLASS:
	    case X86_64_INTEGERSI_CLASS:
	      *(long long *)a = *gpr;
	      gpr++;
	      break;
	    case X86_64_SSE_CLASS:
	      sse2floatfloat (sse++, a);
	      break;
	    case X86_64_SSESF_CLASS:
	      *(float *)a = sse2float (sse++);
	      break;
	    case X86_64_SSEDF_CLASS:
	      *(double *)a = sse2double (sse++);
	      break;
	    default:
	      abort();
	    }
	}
    }
}
Exemplo n.º 4
0
/*@-exportheader@*/
void
ffi_prep_args (stackLayout *stack, extended_cif *ecif)
/*@=exportheader@*/
{
  int gprcount, ssecount, i, g, s;
  void **p_argv;
  void *argp = &stack->argspace;
  ffi_type **p_arg;

  /* First check if the return value should be passed in memory. If so,
     pass the pointer as the first argument.  */
  gprcount = ssecount = 0;
  if (ecif->cif->rtype->type != FFI_TYPE_VOID 
      && examine_argument (ecif->cif->rtype, 1, &g, &s) == 0)
    stack->gpr[gprcount++] = (long) ecif->rvalue;

  for (i=ecif->cif->nargs, p_arg=ecif->cif->arg_types, p_argv = ecif->avalue;
       i!=0; i--, p_arg++, p_argv++)
    {
      int in_register = 0;

      switch ((*p_arg)->type)
	{
	case FFI_TYPE_SINT8:
	case FFI_TYPE_SINT16:
	case FFI_TYPE_SINT32:
	case FFI_TYPE_SINT64:
	case FFI_TYPE_UINT8:
	case FFI_TYPE_UINT16:
	case FFI_TYPE_UINT32:
	case FFI_TYPE_UINT64:
	case FFI_TYPE_POINTER:
	  if (gprcount < MAX_GPR_REGS)
	    {
	      stack->gpr[gprcount] = 0;
	      stack->gpr[gprcount++] = *(long long *)(*p_argv);
	      in_register = 1;
	    }
	  break;

	case FFI_TYPE_FLOAT:
	  if (ssecount < MAX_SSE_REGS)
	    {
	      float2sse (*(float *)(*p_argv), &stack->sse[ssecount++]);
	      in_register = 1;
	    }
	  break;

	case FFI_TYPE_DOUBLE:
	  if (ssecount < MAX_SSE_REGS)
	    {
	      double2sse (*(double *)(*p_argv), &stack->sse[ssecount++]);
	      in_register = 1;
	    }
	  break;
	}

      if (in_register)
	continue;

      /* Either all places in registers where filled, or this is a
	 type that potentially goes into a memory slot.  */
      if (examine_argument (*p_arg, 0, &g, &s) == 0
	  || gprcount + g > MAX_GPR_REGS || ssecount + s > MAX_SSE_REGS)
	{
	  /* Pass this argument in memory.  */
	  argp = (void *)ALIGN(argp, (*p_arg)->alignment);
	  memcpy (argp, *p_argv, (*p_arg)->size);
	  argp += (*p_arg)->size;
	}
      else
	{
	  /* All easy cases are eliminated. Now fire the big guns.  */

	  enum x86_64_reg_class classes[MAX_CLASSES];
	  int offset = 0, j, num;
	  void *a;

	  num = classify_argument (*p_arg, classes, &offset);
	  for (j=0, a=*p_argv; j<num; j++, a+=8)
	    {
	      switch (classes[j])
		{
		case X86_64_INTEGER_CLASS:
		case X86_64_INTEGERSI_CLASS:
		  stack->gpr[gprcount++] = *(long long *)a;
		  break;
		case X86_64_SSE_CLASS:
		  floatfloat2sse (a, &stack->sse[ssecount++]);
		  break;
		case X86_64_SSESF_CLASS:
		  float2sse (*(float *)a, &stack->sse[ssecount++]);
		  break;
		case X86_64_SSEDF_CLASS:
		  double2sse (*(double *)a, &stack->sse[ssecount++]);
		  break;
		default:
		  abort();
		}
	    }
	}
    }
}
Exemplo n.º 5
0
/* Classify the argument of type TYPE and mode MODE.
   CLASSES will be filled by the register class used to pass each word
   of the operand.  The number of words is returned.  In case the parameter
   should be passed in memory, 0 is returned. As a special case for zero
   sized containers, classes[0] will be NO_CLASS and 1 is returned.

   See the x86-64 PS ABI for details.
*/
static int
classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
		   int *byte_offset)
{
  /* First, align to the right place.  */
  *byte_offset = ALIGN(*byte_offset, type->alignment);

  switch (type->type)
    {
    case FFI_TYPE_UINT8:
    case FFI_TYPE_SINT8:
    case FFI_TYPE_UINT16:
    case FFI_TYPE_SINT16:
    case FFI_TYPE_UINT32:
    case FFI_TYPE_SINT32:
    case FFI_TYPE_UINT64:
    case FFI_TYPE_SINT64:
    case FFI_TYPE_POINTER:
      if (((*byte_offset) % 8 + type->size) <= 4)
	classes[0] = X86_64_INTEGERSI_CLASS;
      else
	classes[0] = X86_64_INTEGER_CLASS;
      return 1;
    case FFI_TYPE_FLOAT:
      if (((*byte_offset) % 8) == 0)
	classes[0] = X86_64_SSESF_CLASS;
      else
	classes[0] = X86_64_SSE_CLASS;
      return 1;
    case FFI_TYPE_DOUBLE:
      classes[0] = X86_64_SSEDF_CLASS;
      return 1;
    case FFI_TYPE_LONGDOUBLE:
      classes[0] = X86_64_X87_CLASS;
      classes[1] = X86_64_X87UP_CLASS;
      return 2;
    case FFI_TYPE_STRUCT:
      {
	const int UNITS_PER_WORD = 8;
	int words = (type->size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
	ffi_type **ptr; 
	int i;
	enum x86_64_reg_class subclasses[MAX_CLASSES];

	/* If the struct is larger than 16 bytes, pass it on the stack.  */
	if (type->size > 16)
	  return 0;

	for (i = 0; i < words; i++)
	  classes[i] = X86_64_NO_CLASS;

	/* Merge the fields of structure.  */
	for (ptr=type->elements; (*ptr)!=NULL; ptr++)
	  {
	    int num;

	    num = classify_argument (*ptr, subclasses, byte_offset);
	    if (num == 0)
	      return 0;
	    for (i = 0; i < num; i++)
	      {
		int pos = *byte_offset / 8;
		classes[i + pos] =
		  merge_classes (subclasses[i], classes[i + pos]);
	      }

	    if ((*ptr)->type != FFI_TYPE_STRUCT)
	      *byte_offset += (*ptr)->size;
	  }

	/* Final merger cleanup.  */
	for (i = 0; i < words; i++)
	  {
	    /* If one class is MEMORY, everything should be passed in
	       memory.  */
	    if (classes[i] == X86_64_MEMORY_CLASS)
	      return 0;

	    /* The X86_64_SSEUP_CLASS should be always preceded by
	       X86_64_SSE_CLASS.  */
	    if (classes[i] == X86_64_SSEUP_CLASS
		&& (i == 0 || classes[i - 1] != X86_64_SSE_CLASS))
	      classes[i] = X86_64_SSE_CLASS;

	    /*  X86_64_X87UP_CLASS should be preceded by X86_64_X87_CLASS.  */
	    if (classes[i] == X86_64_X87UP_CLASS
		&& (i == 0 || classes[i - 1] != X86_64_X87_CLASS))
	      classes[i] = X86_64_SSE_CLASS;
	  }
	return words;
      }

    default:
      FFI_ASSERT(0);
    }
  return 0; /* Never reached.  */
}