[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Circumventing the -fno-strict-aliasing switch

Marco Devillers

5/25/2011 12:53:00 PM

Hi all, a short question.

I wrote a compiler which compiles to C and has a garbage collector
written in C. The garbage collector assumes that nodes in memory are
arrays of integers or pointers (I use the default integer of pointer
size type for that).

However, sometimes, terminal nodes may hold other values, such as
floats, doubles, etc. Naturally, I cast these then to the correct
type, so a intptr_t* becomes a float*. Of course, this is unspecified
behavior according to the C standard, so I need to compile with the -
fno-strict-aliasing switch in gcc.

I want to rewrite my code such that I don't need that switch anymore,
i.e., I want to end up with portable code.

My question: What is the correct manner to rewrite this code?

I know there's an exception to strict aliasing, i.e., the char
pointer. Should I redefine the node pointer type as char*, or is it
sufficient to cast intptr_t* to char* and then to float* (can I use a
char* as an intermediate to trick the C type system)?

Thanks all,
Marco
26 Answers

jacob navia

5/25/2011 2:07:00 PM

0

Le 25/05/11 14:53, Marco Devillers a écrit :
> Hi all, a short question.
>
> I wrote a compiler which compiles to C and has a garbage collector
> written in C. The garbage collector assumes that nodes in memory are
> arrays of integers or pointers (I use the default integer of pointer
> size type for that).
>
> However, sometimes, terminal nodes may hold other values, such as
> floats, doubles, etc. Naturally, I cast these then to the correct
> type, so a intptr_t* becomes a float*. Of course, this is unspecified
> behavior according to the C standard, so I need to compile with the -
> fno-strict-aliasing switch in gcc.
>
> I want to rewrite my code such that I don't need that switch anymore,
> i.e., I want to end up with portable code.
>
> My question: What is the correct manner to rewrite this code?
>
> I know there's an exception to strict aliasing, i.e., the char
> pointer. Should I redefine the node pointer type as char*, or is it
> sufficient to cast intptr_t* to char* and then to float* (can I use a
> char* as an intermediate to trick the C type system)?
>
> Thanks all,
> Marco

This will never work correctly. As you say "this is unspecified
behavior according to the C standard". Do not do this

Make a union of the different pointer types (int * float * whatever)
and use the language instead of fighting against it.

typedef union ptrs {
int *pInt;
float *pFloat;
double *pDouble;
// etc
} Ptrs;


Marco Devillers

5/25/2011 2:16:00 PM

0

On May 25, 4:07 pm, jacob navia <ja...@spamsink.net> wrote:
> Le 25/05/11 14:53, Marco Devillers a écrit :
>
>
>
>
>
>
>
>
>
> > Hi all, a short question.
>
> > I wrote a compiler which compiles to C and has a garbage collector
> > written in C. The garbage collector assumes that nodes in memory are
> > arrays of integers or pointers (I use the default integer of pointer
> > size type for that).
>
> > However, sometimes, terminal nodes may hold other values, such as
> > floats, doubles, etc. Naturally, I cast these then to the correct
> > type, so a intptr_t* becomes a float*. Of course, this is unspecified
> > behavior according to the C standard, so I need to compile with the -
> > fno-strict-aliasing switch in gcc.
>
> > I want to rewrite my code such that I don't need that switch anymore,
> > i.e., I want to end up with portable code.
>
> > My question: What is the correct manner to rewrite this code?
>
> > I know there's an exception to strict aliasing, i.e., the char
> > pointer. Should I redefine the node pointer type as char*, or is it
> > sufficient to cast intptr_t* to char* and then to float* (can I use a
> > char* as an intermediate to trick the C type system)?
>
> > Thanks all,
> >       Marco
>
> This will never work correctly. As you say "this is unspecified
> behavior according to the C standard". Do not do this
>
> Make a union of the different pointer types (int * float * whatever)
> and use the language instead of fighting against it.
>
> typedef union ptrs {
>         int *pInt;
>         float *pFloat;
>         double *pDouble;
>         // etc
> } Ptrs;

Can't. I don't know, a priory, what will be stored in a terminal node.

jacob navia

5/25/2011 2:23:00 PM

0

Le 25/05/11 16:15, Marco Devillers a écrit :
>> typedef union ptrs {
>> int *pInt;
>> float *pFloat;
>> double *pDouble;
>> // etc
>> } Ptrs;
>
> Can't. I don't know, a priory, what will be stored in a terminal node.

Then use a void pointer at the end. That is the official "wildcard"

Marco Devillers

5/25/2011 2:45:00 PM

0

On May 25, 4:22 pm, jacob navia <ja...@spamsink.net> wrote:
> Le 25/05/11 16:15, Marco Devillers a écrit :
>
> >> typedef union ptrs {
> >>          int *pInt;
> >>          float *pFloat;
> >>          double *pDouble;
> >>          // etc
> >> } Ptrs;
>
> > Can't. I don't know, a priory, what will be stored in a terminal node.
>
> Then use a void pointer at the end. That is the official "wildcard"

Both remarks are nonsense. The union will not work since I am storing
arbitrary data; if anything, I need a union of primitive types.
Moreover, even in a union, there are no guarantees according to the
standard that your solution will work since reading a value from a
union with a different type than written to is unspecified also.
Moreover, the void* isn't the official wildcard when dealing with
aliasing rules: the char pointer is.

Tom St Denis

5/25/2011 2:53:00 PM

0

On May 25, 8:53 am, Marco Devillers <marco.devill...@gmail.com> wrote:
> Hi all, a short question.
>
> I wrote a compiler which compiles to C and has a garbage collector
> written in C. The garbage collector assumes that nodes in memory are
> arrays of integers or pointers (I use the default integer of pointer
> size type for that).
>
> However, sometimes, terminal nodes may hold other values, such as
> floats, doubles, etc. Naturally, I cast these then to the correct
> type, so a intptr_t* becomes a float*. Of course, this is unspecified
> behavior according to the C standard, so I need to compile with the -
> fno-strict-aliasing switch in gcc.
>
> I want to rewrite my code such that I don't need that switch anymore,
> i.e., I want to end up with portable code.
>
> My question: What is the correct manner to rewrite this code?
>
> I know there's an exception to strict aliasing, i.e., the char
> pointer. Should I redefine the node pointer type as char*, or is it
> sufficient to cast intptr_t* to char* and then to float* (can I use a
> char* as an intermediate to trick the C type system)?

As Jacob pointed out use a "void *" pointer in your terminal node so
you can assign any pointer type to it. You might want to have some
form of metadata along for the ride so you know what type the node is
when using it...

Tom

Marco Devillers

5/25/2011 3:21:00 PM

0

On May 25, 4:53 pm, Tom St Denis <t...@iahu.ca> wrote:
> On May 25, 8:53 am, Marco Devillers <marco.devill...@gmail.com> wrote:
>
>
>
>
>
>
>
>
>
> > Hi all, a short question.
>
> > I wrote a compiler which compiles to C and has a garbage collector
> > written in C. The garbage collector assumes that nodes in memory are
> > arrays of integers or pointers (I use the default integer of pointer
> > size type for that).
>
> > However, sometimes, terminal nodes may hold other values, such as
> > floats, doubles, etc. Naturally, I cast these then to the correct
> > type, so a intptr_t* becomes a float*. Of course, this is unspecified
> > behavior according to the C standard, so I need to compile with the -
> > fno-strict-aliasing switch in gcc.
>
> > I want to rewrite my code such that I don't need that switch anymore,
> > i.e., I want to end up with portable code.
>
> > My question: What is the correct manner to rewrite this code?
>
> > I know there's an exception to strict aliasing, i.e., the char
> > pointer. Should I redefine the node pointer type as char*, or is it
> > sufficient to cast intptr_t* to char* and then to float* (can I use a
> > char* as an intermediate to trick the C type system)?
>
> As Jacob pointed out use a "void *" pointer in your terminal node so
> you can assign any pointer type to it.  You might want to have some
> form of metadata along for the ride so you know what type the node is
> when using it...
>
> Tom

The metadata is -of course- already present; size and type tags
discriminate all terminal nodes.

Terminal nodes hold series of bits, integers or pointers, but also may
hold data, not pointers to data. I don't see how your solution would
work.

Marco Devillers

5/25/2011 3:26:00 PM

0


Forget those remarks. The only way out, I think, is casting stuff to
char* (which is allowed) and copying that with memcopy/sizeof to
pointers to memory of a given type.

Is that portable?

Tim Rentsch

5/25/2011 3:28:00 PM

0

Marco Devillers <marco.devillers@gmail.com> writes:

> Hi all, a short question.
>
> I wrote a compiler which compiles to C and has a garbage collector
> written in C. The garbage collector assumes that nodes in memory are
> arrays of integers or pointers (I use the default integer of pointer
> size type for that).

It would help a lot if you showed some code to make the question
more specific. Obvious first question: is the format of a Node
(ignoring the cases with floats, etc) like this

struct Node_s {
union {
some_integer_type integers[ ELEMENTS_PER_NODE ];
struct Node_s *pointers[ ELEMENTS_PER_NODE ];
} element_arrays;
};

or like this

struct Node_s {
union {
some_integer_type integer;
struct Node_s *pointer;
} elements[ ELEMENTS_PER_NODE ];
};

or something else? The description you give isn't
specific enough so people can tell.


> However, sometimes, terminal nodes may hold other values, such as
> floats, doubles, etc. Naturally, I cast these then to the correct
> type, so a intptr_t* becomes a float*. Of course, this is unspecified
> behavior according to the C standard, so I need to compile with the -
> fno-strict-aliasing switch in gcc.

Most likely the behavior being evoked is undefined, not
unspecified.


> I want to rewrite my code such that I don't need that switch anymore,
> i.e., I want to end up with portable code.

Excellent.


> My question: What is the correct manner to rewrite this code?

Please show some example code fragments (and including the
relevant data type definitions) of the code you're asking
about.


> I know there's an exception to strict aliasing, i.e., the char
> pointer. Should I redefine the node pointer type as char*, or is it
> sufficient to cast intptr_t* to char* and then to float* (can I use a
> char* as an intermediate to trick the C type system)?

Beware! The term 'strict aliasing' is a gcc-ism, and doesn't
necessarily map onto what the Standard defines as conforming.
The terminology used in the Standard is 'effective type rules'.
I suspect you intend 'strict aliasing' to be synonymous with
obeying the Standard's effective type rules, but it isn't,
and so it's important to distinguish between them.

jacob navia

5/25/2011 3:36:00 PM

0

Le 25/05/11 16:44, Marco Devillers a écrit :
> On May 25, 4:22 pm, jacob navia<ja...@spamsink.net> wrote:
>> Le 25/05/11 16:15, Marco Devillers a écrit :
>>
>>>> typedef union ptrs {
>>>> int *pInt;
>>>> float *pFloat;
>>>> double *pDouble;
>>>> // etc
>>>> } Ptrs;
>>
>>> Can't. I don't know, a priory, what will be stored in a terminal node.
>>
>> Then use a void pointer at the end. That is the official "wildcard"
>
> Both remarks are nonsense. The union will not work since I am storing
> arbitrary data; if anything, I need a union of primitive types.

excuse me but it was YOU that wrote:
However, sometimes, terminal nodes may hold other values, such as
floats, doubles, etc. Naturally, I cast these then to the correct
type, so a intptr_t* becomes a float*

It is you that spoke about pointers in the first place.

Now, post the definition of the structure.

I wrote several years ago a lisp interpreter. I had the following
structure;

typedef struct cons {
unsigned char n_type;
unsigned char n_flags;
union { /* valeur */
struct lsym { /* ------------------symbole */
SYMBOL_POINTER plist; /* symbol plist */
struct cons *value; /* sa valeur */
} n_lsym;
struct csym { /* compiled-symbol */
unsigned short offset; /* deplacement */
struct cons *psym; /* pointeur vers le symbole */
} n_csym;
struct lcode {
unsigned char *code;
struct cons *data;
} n_lcode;
struct clabel { /* -----Etiquette pour gotos */
struct cons *psym; /* pointeur vers le symbole */
struct context *pcontext; /* pointeur vers le contexte */
} n_clabel;
struct lsubr { /* ----------subr/fsubr node */
short xorder;
struct cons *(*MachineCode)();/* pointeur vers le code */
} n_lsubr;
struct llist { /* ---------list node (cons) */
struct cons *list_car; /* the car pointer */
struct cons *list_cdr; /* the cdr pointer */
} n_llist;
struct lint { /* -------------integer node */
ENTIER xi_int; /* integer value */
} n_lint;
struct lchar { /* ------------caracter node */
ENTIER caractere; /* caracter value */
} n_lettre;
struct lratio { /* ---------------ratio node */
ENTIER numerator;
ENTIER denominator;
} n_ratio;
struct lfloat { /* --------------float node */
FLOTTANT lf_float; /* float value */
} n_lfloat;
struct xcmplx { /* ------------complex node */
struct cons *real_part; /* real value */
struct cons *imag_part; /* imag value */
} n_xcmplx;
struct lstr { /* -------------string node */
unsigned char *xst_str; /* Pointeur */
unsigned long xstrlen; /* Longueur */
} n_lstr;
struct lfixstr { /* ------------fixed string */
unsigned char fixstr[MAX_FIX_STR];
} n_lfixstr;
struct lfptr { /* -----------------fichier */
STREAM *lf_sp; /* Pointeur vers info */
short lf_savech; /* lookahead */
} n_lfptr;
struct intvect { /* simple vector (integers or
floats) */
Vector *ivdescriptor;
ENTIER *int_data;
} n_intvect;
struct vecteur { /* -------------vector node */
//
// BIG SNIP
//

}
inform;
}LISP_VALUE;

If you are writing a compiler you KNOW what types you have.

And, as far as I know, void * is the wildcard and NOT char pointer.

Tim Rentsch

5/25/2011 3:41:00 PM

0

Marco Devillers <marco.devillers@gmail.com> writes:

> On May 25, 4:22 pm, jacob navia <ja...@spamsink.net> wrote:
>> [suggesting an approach using a union]
>
> The union will not work

Using a union (not necessarily the suggested one) is the
approach that is most likely _to_ work.

> since I am storing
> arbitrary data; if anything, I need a union of primitive types.

Obviously you know all the different types that can be
held, since you are casting the pointer types to (float *),
(double *), etc. Make a union that has all the different
types that a node can hold.


> Moreover, even in a union, there are no guarantees according to the
> standard that your solution will work since reading a value from a
> union with a different type than written to is unspecified also.

Irrelevant, since presumably you will be using the same
type as what the node actually holds, so the same type
will be used for both reading and writing.

(Technical point: the rules for reading a union member
other than the last one stored are not unspecified (or
undefined), and the semantics is more well-defined than
most people think. This probably isn't relevant for
what you're doing, but it's a common misconception and
one worth clarifying.)