INF: Behavior of /Oa and /Ow Safer in C 6.0 and Later

ID Number: Q61610

6.00 6.00a 6.00ax | 6.00 6.00a

MS-DOS | OS/2

Summary:

The detection of common aliasing practices has been improved in the

Microsoft C version 6.0 code generator. The primary difference is

that the compiler now tries to detect when the address of a variable

has been taken and then to protect against aliasing corruption of that

variable even when the /Oa option is selected. If no options are

selected that invoke the global optimizer (/Oe, /Og, /Ol), the

optimizer depends on the preprocessor marking all variables that have

had their address taken (&).

With global optimization, the optimizer itself does the necessary data

flow analysis to determine if an address has been taken. This makes

/Oa safer when used in conjunction with global optimization because it

can detect addresses being taken even in the absence of the address-of

operator (&).

More Information:

Aliasing is when more than one expression refers to a single memory

location, as in the following example:

int x,*p,a[3];

void main(void)

{

p=&x; // Now *p and x are aliases to the same memory location.

x=5; // There are now two ways to assign values to that memory.

*p=4;

a[2]=myfunc(p);

// Passing aliases to functions can cause problems when using /Oa.

p=a;

// Arrays also can be aliased; now *p and a[0] are common aliases.

}

Common aliasing problems can occur when the compiler causes one of

these aliases to be stored in a register to speed up code execution.

If both aliases subsequently are used to change the same memory

location, one may change the register value and the other the actual

in-memory value. Furthermore, when the register variable is finally

unloaded, its value will overwrite whatever changes may have been made

to memory.

There are two places where variables stored in registers can become

corrupted through aliasing problems. One place is across function

calls where functions are assumed to not corrupt the value. The other

place is by assignments through pointer variables. The following

paragraphs and table summarize the susceptibility of different types

of data to the above mentioned corruption:

Local Variables

---------------

Because of their restricted scope, local variables are assumed by the

compiler to be safe from aliasing problems. Therefore, locals are

prime candidates for register storage, and if for some reason aliasing

occurs, they are prone to common aliasing problems both over function

calls and through pointer assignment.

Global Variables

----------------

Because of their extensive scope, global variables can often

accumulate multiple aliases. Therefore, the code generator causes them

to be unloaded from registers before function calls to preserve their

value. However, globals are still prone to aliasing problems through

pointer assignment with /Oa and/or /Ow.

*p

--

*p represents a pointer variable accessed primarily to change its

associated memory location. *p as a variable can be stored in a

register, and therefore, can be affected by common aliasing problems.

It is protected under default optimizations by the code generator.

Under /Ow, it is protected across function calls but not from

assignment through another pointer (for example, a *x). Under /Oa, *p

is not protected as a separate symbol at all.

Address Taken

-------------

If a variable's address has been taken, the C code generator will

protect it from common aliasing pitfalls in all but one case involving

the /Oa switch. If a local variable's address is taken and passed to a

function, the local variable's value can then be changed within the

function through an alias without protection from common aliasing

problems.

The following two further limitations apply to the above rule:

1. If global optimization is not invoked, the code generator does not

always recognize if an address is taken without the use of the

address-of operator (&). This is only an issue for arrays because

they are the only object whose address can be taken without the

operator. What this means is that given - int *p,a[4]; p=&a[0] is

compatible with /Oa, but p=a is not protected.

2. The checking is done only on a function-by-function basis, so if

the address of a global is taken outside of the current function,

it won't be detected.

The following interference table summarizes the effects of the code

generator in the various circumstances described above:

Var Type Effect of Function Call Effect of Assign Thru Pointer

-------- ----------------------- -----------------------------

/O /Ow /Oa /O /Ow /Oa

local N N N N N N

global Y Y Y Y N N

*p Y Y N Y N N

addr-taken Y Y N Y Y Y

N - Variable in register not protected from common aliasing problems.

Y - Variable unloaded from registers to memory (protected).

Reference(s):

"Microsoft C Advanced Programming Techniques," versions 6.0, 6.0a, and

6.0ax, pages 13-15

Additional reference words: 6.00 6.00a 6.00ax