cast question

D

David Mathog

I am trying to force the compiler to be sloppy with types to allow 16
byte chunks of
memory to be accessed in some places as in int, and in others as a
char, and so forth.
Mixed in with this is the gcc vector extension, but I don't think that
is the problem.
Here is a bit of sample code

8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* assumes int/uint are 4 bytes, ll/ull are 8*/
typedef union {
int v __attribute__ ((__vector_size__ (16)));
int a[4];
unsigned int u[4];
long long l[2];
unsigned long long U[2];
}__av4si;

typedef long long __m128i __attribute__ ((__vector_size__ (16),
__may_alias__));

int main(void){
int i,j;
int array[4]={1,2,4,8};
#ifdef BAD
__m128i result;
#else
__av4si result;
#endif
memcpy((void*)&result,&array[0],16);
for(j=0;j<4;j++){
i=((__av4si)result).a[j];
(void) fprintf(stdout, "A[%d] is %X\n",j,i);
}
}
8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<

# gcc -o short short.c
# ./short
A[0] is 1
A[1] is 2
A[2] is 4
A[3] is 8
# gcc -DBAD -o short short.c
short.c: In function 'main':
short.c:28: error: cast to union type from type not present in union

And there's the problem. The __m128i type is a sort of generic bag
for holding 16 bytes
of data, but I need a way to force it to be interpreted different
ways. Unfortunately in the many
functions which use it, none of which I can change, __m128i variables
are always passed by value. I am looking for a way to tell the
compiler to access it as in the example above, that is, without having
to resort to doing something like using memcpy to pull the data into
an array of the appropriate type. The only other approach that I have
come up with so far that works is:

i=(*((__av4si *)(&result))).a[j];

which is pretty hideous. In fact, if one has to resort to making
pointers and then changing the type that way the typedef union isn't
needed at all:

i=((int *)&result)[j];

Both of the last forms work when compiled with or without -DBAD. Is
there a cast syntax
more like this

i=((__av4si)result).a[j];

that is, no "&" employed, that will compile if "result" is an __m128i
variable?

Thanks,

David Mathog
 
S

Stefan Ram

David Mathog said:
typedef union

»When a value is stored in a member of an object of union type,
the bytes of the object representation that do not
correspond to that member but do correspond to other
members take unspecified values«
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
ISO/IEC 9899:1999 (E), 6.2.6.1#7

But your C implementation might specified the values for this case.

Otherwise, pointer-casting should be fine. - But, I am not sure:
When a pointer to type A is cast to a pointer to type B and then
an attempt to read a B value is made via this pointer, does
ISO/IEC 9899:1999 (E) specify anything more about the result
than in the case of unions?

For an example C program to read single bits of int- and double-
values using casts, search for »bitprint.c« on

http://www.purl.org/stefan_ram/pub/formal_typwandlung_de

. (The two outputs are from two C implementations.)

If the value is (at most) implementation specified anyway, what
would be the better style to convert from one interpretation
of a bit pattern at a certain memory location to another
interpretation: a union or a pointer-type-cast?
 
B

BartC

David Mathog said:
I am trying to force the compiler to be sloppy with types to allow 16
byte chunks of
memory to be accessed in some places as in int, and in others as a
char, and so forth.
i=((__av4si)result).a[j];

Try this instead:

i=(*((__av4si*)&result)).a[j];

For a simpler example, if you have an int you want to interpret as a float,
without any conversions, it would look like:

int a;
float x;

x=*(float*)&a;
 
T

Tim Rentsch

David Mathog said:
I am trying to force the compiler to be sloppy with types to allow 16
byte chunks of
memory to be accessed in some places as in int, and in others as a
char, and so forth.
Mixed in with this is the gcc vector extension, but I don't think that
is the problem.
Here is a bit of sample code

8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* assumes int/uint are 4 bytes, ll/ull are 8*/
typedef union {
int v __attribute__ ((__vector_size__ (16)));
int a[4];
unsigned int u[4];
long long l[2];
unsigned long long U[2];
}__av4si;

typedef long long __m128i __attribute__ ((__vector_size__ (16),
__may_alias__));

int main(void){
int i,j;
int array[4]={1,2,4,8};
#ifdef BAD
__m128i result;
#else
__av4si result;
#endif
memcpy((void*)&result,&array[0],16);
for(j=0;j<4;j++){
i=((__av4si)result).a[j];
(void) fprintf(stdout, "A[%d] is %X\n",j,i);
}
}
8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<

# gcc -o short short.c
# ./short
A[0] is 1
A[1] is 2
A[2] is 4
A[3] is 8
# gcc -DBAD -o short short.c
short.c: In function 'main':
short.c:28: error: cast to union type from type not present in union

And there's the problem. The __m128i type is a sort of generic bag
for holding 16 bytes
of data, but I need a way to force it to be interpreted different
ways. Unfortunately in the many
functions which use it, none of which I can change, __m128i variables
are always passed by value. I am looking for a way to tell the
compiler to access it as in the example above, that is, without having
to resort to doing something like using memcpy to pull the data into
an array of the appropriate type. The only other approach that I have
come up with so far that works is:

i=(*((__av4si *)(&result))).a[j];

which is pretty hideous. In fact, if one has to resort to making
pointers and then changing the type that way the typedef union isn't
needed at all:

i=((int *)&result)[j];

Both of the last forms work when compiled with or without -DBAD. Is
there a cast syntax
more like this

i=((__av4si)result).a[j];

that is, no "&" employed, that will compile if "result" is an __m128i
variable?

First, the casts you're trying to use are not standard C, which
doesn't allow casting to a union type.

Second, if you still want to use this approach, I expect you will
find this definition for the union type

typedef union {
int v __attribute__ ((__vector_size__ (16)));
int a[4];
unsigned int u[4];
long long l[2];
unsigned long long U[2];
__m128i what_ever;
}__av4si;

will allow the casts you want to be used (assuming of course that
using the __m128i type as the type of one of the members of the
union __av4si type is feasible in your program).

Third, did you notice the text of the error message:

error: cast to union type from type not present in union
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Good luck!
 
D

David Mathog

First, the casts you're trying to use are not standard C, which
doesn't allow casting to a union type.

More on that later
Second, if you still want to use this approach, I expect you will
find this definition for the union type

    typedef union {
      int  v __attribute__ ((__vector_size__ (16)));
      int                a[4];
      unsigned int       u[4];
      long long          l[2];
      unsigned long long U[2];
      __m128i            what_ever;
    }__av4si;

Also suggested by Philip Lantz in an email. That actually did work
and here's the sample code
that shows it for the general 16 byte situation:
8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<
/* short2.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* for memcpy */
typedef double __m128d __attribute__ ((__vector_size__ (16),
__may_alias__));
typedef long long __m128i __attribute__ ((__vector_size__ (16),
__may_alias__));

/* assumes an int/float are 4 bytes, double/long are 8, short is 2,
char is 1*/
typedef union {
__m128i vi;
__m128d vd;
char c[16];
unsigned char uc[16];
short s[8];
unsigned short us[8];
int i[4];
unsigned int ui[4];
long long l[2];
unsigned long long ul[2];
float f[4];
double d[2];
}__all16;

int main(void){
__m128i result;
int i;
int iarray[4]={1,2,3,4};
float farray[4]={1.,2.,3.,4.};
double darray[2]={1.,2.};
long long larray[2]={1,2};
short sarray[8]={1,2,3,4,5,6,7,8};
char carray[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
memcpy(&result,&larray[0],16);
for(i=0;i<2;i++){fprintf(stdout, "llong [%d] is %llX\n",i,
((__all16)result).l); }
for(i=0;i<2;i++){fprintf(stdout, "ullong [%d] is %llX\n",i,
((__all16)result).ul); }
memcpy(&result,&darray[0],16);
for(i=0;i<2;i++){fprintf(stdout, "double [%d] is %f\n",i,
((__all16)result).d); }
memcpy(&result,&iarray[0],16);
for(i=0;i<4;i++){fprintf(stdout, "int [%d] is %X\n",i,
((__all16)result).i); }
for(i=0;i<4;i++){fprintf(stdout, "uint [%d] is %X\n",i,
((__all16)result).ui); }
memcpy(&result,&farray[0],16);
for(i=0;i<4;i++){fprintf(stdout, "float [%d] is %f\n",i,
((__all16)result).f); }
memcpy(&result,&sarray[0],16);
for(i=0;i<8;i++){fprintf(stdout, "short [%d] is %X\n",i,
((__all16)result).s); }
for(i=0;i<8;i++){fprintf(stdout, "ushort [%d] is %X\n",i,
((__all16)result).us); }
memcpy(&result,&carray[0],16);
for(i=0;i<16;i++){fprintf(stdout, "char [%d] is %X\n",i,
((__all16)result).c); }
for(i=0;i<16;i++){fprintf(stdout, "uchar [%d] is %X\n",i,
((__all16)result).uc); }
exit(EXIT_SUCCESS);
}

8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<8<

% gcc -std=c99 -Wall -msse2 -o short2 short2.c
(no warning or error messages)
% ./short2
(correct, all fields occupied by the index value + 1).

So this approach does work. Now back to the standards question, you
are right because:

gcc -std=c99 -pedantic -Wall -msse2 -o short2 short2.c
short2.c: In function 'main':
short2.c:36: warning: ISO C forbids casts to union type
short2.c:37: warning: ISO C forbids casts to union type
short2.c:39: warning: ISO C forbids casts to union type
etc.

Using the form below does not run afoul of the standards, at least not
enough to
trigger a compiler warning with -std=c99 -pedantic :

#define AS_INT4(a,b) ((int *)&(a))[(b)]
for(i=0;i<4;i++){fprintf(stdout, "int*& [%d] is %X
\n",i,AS_INT4(result,i)); }

I suppose there would be alignment issues if an arbitrary pointer into
a character array was
accessed in this manner, but for data already forced onto 16 byte
boundaries that should not
come up.

Thanks,

David Mathog
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top