This chapter describes both the CodeWarrior back-end compiler and linker for PowerPC/AltiVec.
The topics in this chapter are:
This chapter also discusses the numerous extensions to the compiler for supporting AltiVec features, like preprocessor macros, vector data types, and alignment considerations.
However, this chapter does not discuss several important topics with respect to code generation, including front-end compiler issues, compiler and linker errors, controlling the size of C++ code, and so forth. These topics are covered in other CodeWarrior documentation as outlined in Table 9.1.
The "back-end" of the compiler refers to the module that actually generates code for the target processor. The "front-end" refers to the module that parses and interprets your source code. See the C Compiler Reference for additional compiler and linker information.
Other compiler/linker documentation:
| For this topic... |
See... |
|---|---|
You may find the following website to be worth a careful look:
http://motorola.com/SPS/PowerPC/
When compiling your code, you can take advantage of some predefined preprocessor symbols for conditionally compiling code. You can find a list of these in the C Compilers Reference. For more information on pragmas, refer to "Pragmas."
In particular, the following symbols are defined:
Defined as 1 if data is A5-relative, 0 if data is A4 relative. This symbol is defined for 68K compilers. It is undefined for other target platforms.
This symbol is set to a value indicating the feature level of
the AltiVec compiler. It currently returns 100000000. This means that the AltiVec-specific instructions are supported
in the compiler and are available for use. You may find pragmas
to be useful in working with AltiVec.
Defined as 1, if you turn on the 4-byte Ints option in the 68K Processor settings panel. 0, if you turn off that option. This symbol is defined for 68K compilers. It is undefined for other platforms.
Defined as 1 if you turn on the 8-Byte Doubles option in the 68K Processor settings panel. 0, if you turn off that option. This symbol is defined for 68K compilers. It is undefined for all other target platforms.
The Metrowerks compilers for MacOS define the __MACINTOSH__ symbol. You can conditionalize platform-dependent code using
this symbol in a statement such as this:
#ifdef __MACINTOSH__ ... your code here #endif
Defined as 1 if you turn on the 68020 Codegen option in the Processor settings panel, 0 if you turn that option off. This symbol is defined for 68K compilers. It is undefined for all other target platforms.
Defined as 1 if you turn on the 68881 Codegen option in the 68K Processor settings panel, 0 if you turn that option off. This symbol is defined for 68K compilers and undefined for all other target platforms.
Defined as 1 if you're compiling this code with the 68K compiler and undefined for all other target platforms.
Another trick you might find useful for conditionalizing 68K and PowerPC code works as illustrated here.
#if defined(powerc) /* do something for PowerPC */ #else SetA5(oldA5); /* set A5 world for 68K */ #endif
If this is defined, then the generation of AltiVec instructions is currently enabled. The value returned by this symbol indicates the version of the AltiVec Pramming Interface Manual which the compiler conforms to. This is currently 10205, or when converted to decimal reads 1.2.5. This is typically used in this form:
#if __VEC__ // do stuff here... #endif
This is enabled via the PPC Processor panel or with the pragma:
#pragma altivec_model [on | off]
Table 9.2 shows all the currently used proprocessor symbols and their values.
| Preprocessor |
Value |
|---|---|
This section describes how the CodeWarrior C/C++ compilers implement
integer and floating-point types for PowerPC processors. You can
also read about limits.h for more information on integer types, and float.h for more information on floating-point types in the MSL C Reference. You may also find the altivec.h file interesting.
The topics in this section are:
There are 11 new vector data types for use in writing AltiVec-specific code, shown in Table 9.3. Notice that all the types are a constant size, either 128 bits or 16 bytes. This is due to the AltiVec programming model which is optimized for quantities of this size.
| Vector Data Type |
Size (bytes) |
Contents |
Possible Values |
|---|---|---|---|
The types shown in Table 9.4 still work, but are deprecated and are not recommended for use.
The preferred types to use instead are vector signed int, vector unsigned int, and vector bool int. In the table, the [int] portion of the Vector Data Type is optional except in the case
of 32-bit integer elements.
NOTE Always use int instead of long to designate 32-bit elements. long is now deprecated and should only appear in legacy code.
| Vector Data Type |
Size (bytes) |
Contents |
Possible Values |
|---|---|---|---|
There are two additional keywords besides pixel and vector, __pixel and __vector. These keywords can be used in C or C++ code.
bool is not a reserved word in C unless it is used as an AltiVec vector
data type.
Table 9.5 shows the size and range of the integer types for the PowerPC compiler.
| For this type |
Option setting |
Size is |
and its range is |
|---|---|---|---|
Table 9.6 shows the sizes and ranges of the floating point types for the PowerPC Mac OS compiler.
(K&R, §A4.1, §A8.1) The PowerPC back-end compiler automatically allocates local variables and parameters to registers according to how frequently they're used and how many registers are available. If you're optimizing for speed, the compiler give preference to variables used in loops.
The PowerPC back-end compiler gives preference to variables declared
to be register, but does not automatically assign them to registers. For example,
the compiler is more likely to place a variable from an inner
loop in a register than a variable declared register.
This section discusses optimizations that are specific to PowerPC development with CodeWarrior.
The PowerPC back-end compiler can perform a register optimization
called register coloring. In this optimization, the compiler lets two or more variables
share a register. It assigns different variables or parameters
to the same register if you do not use the variables at the same
time. In this example, the compiler could place i and j in the same register:
short i;
int j;
for (i=0; i<100; i++) { MyFunc(i); }
for (j=0; j<1000; j++) { OurFunc(j); }
However, if a line like the one below appears anywhere in the
function, the compiler would realize that you're using i and j at the same time and place them in different registers:
int k = i + j;
You control whether the PPC back-end compiler performs the register coloring optimization from the Global Optimizations settings panel. Refer to the IDE User Guide for information on this panel.
If Register Coloring is on while you debug your project, it may appear as though there's
something wrong with the variables sharing a register. In the
example above, i and j would always have the same value. When i changes, j changes in the same way. When j changes, i changes in the same way. To avoid this confusion while debugging,
turn off Register Coloring or declare the variables you want to watch as volatile.
This section discusses pragmas you can use in your development. The two types of pragmas are:
These pragmas, pragma syntax, and how to determine and modify the state of the compiler using pragmas are all detailed in the C Compilers Reference.
Table Table 9.7 list the pragmas supported for PowerPC development.
Pragmas for PowerPC development:
There are a couple of pragmas you might find useful in working with AltiVec code.
#pragma altivec_model [on | off | reset]
This pragma turns on AltiVec code generation and vectorization. Use it to make a portion of your code generate AltiVec instructions.
If this is placed outside a function, then the effect continues
until another one of these pragmas is encountered outside a function.
If reset is specified, the setting is changed to off.
If the pragma is used inside a function, the effect of the pragma
is for that function only. Even if the pragma does not occur at
the beginning of the function, the entire function will be treated.
One, and only one, instance of this pragma can occur inside a
function. This implies that inside a function the reset form of this pragma is illegal.
#pragma scheduling altivec
This pragma is needed to turn on AltiVec instruction scheduling.
Instead of specifying altivec there are also other values that are legal. Using 7400 is the same as altivec.
In addition, you might find the following pragmas helpful.
#pragma schedule once #pragma schedule twice
On highly-optimized C code where loops have been unrolled by hand, running the scheduler once is better than running it twice. This especially applies in routines where the register usage is relatively high. Scheduling introduces register pressure before register colorizing causing excessive spilling of registers to memory. When the scheduler is run twice it is run before and after register coloring. If it is only run once after register coloring.
#pragma altivec_vrsave [on | off | allon]
This pragma is used to turn VRSAVE updating on or off. If you turn it off, you will have to set
save the state yourself. Using allon sets VRSAVE assuming that all AltiVec registers are in use. You can use this
in conjunction with the off version of this pragma so that only the parent routine updates
the VRSAVE register.
Other pragmas that may be useful for optimizations are:
#pragma ppc_unroll_instructions_limit #pragma_unroll_factor_limit #pragma ppc_unroll_speculative
You might want to make use of this pragma in order to obtain more favorable execution times due to proper memory alignment:
#pragma function_align [4|8|16|32|64|128|reset]
There are a couple of considerations to note when working with constants in your code.
Variables that are marked const should be placed in a read-only section whenever possible. However,
in some cases C++ const variables are initialized at runtime so they can't be placed
in a read-only section.
For AltiVec, constants are pooled. Saving vector constants in the TOC will not be successful, and this option is ignored.
For AltiVec processors, alignment is critical to proper performance. A data location for a vector quantity must be aligned on a 16-byte boundary. The compiler forces this alignment when generating code, however, you should be aware of it if you are writing assembly code.
If you try to access a quantity on a non-aligned boundary, such as during a pointer dereferencing operation, the low-order bits of the address are ignored.
The topics discussed in this section are:
When stuffing vector registers with data, the data does not have
to be aligned. When accessing it you need to use the operators
provided for this purpose, like vec_perm(), vec_lvsl(), and vec_lvsr().
If your data structures or classes contain data of type vector then the class or structure will be aligned on 16-byte boundaries.
Any padding or packing required to bring about this alignment
internally to the structure or class is done by the compiler.
Any alignment pragmas are ignored for the purposes of arranging
this embedded vector data.
All dynamic allocations for containing vector data must be 16-byte aligned. This is required whether the space
is created on the stack at runtime by the compiler, or by the
user. Also, heap allocations and C++ class objects instantiations
are also aligned.
The compiler will generate any needed function entry "prolog"
code to ensure 16-byte alignment. This code makes sure that the
stack variables in the function are 16-byte aligned. The code
is not always necessary, but if it is, the compiler will put it
there. This is typically only required when parameters or local
variables for the function are vector type.
The code generated by the compiler for stack variables will be
16-byte aligned when vector data is present.
Using the vec_malloc() operator is required in certain cases to obtain 16-byte alignment.
This is similar to, and replaces, the ubiquitous malloc() call for vector data. In addition, vec_realloc() replaces realloc() and vec_calloc() replaces calloc(). These are defined in stdlib.h.
In order to preserve 16-byte alignment you should always use the
vec_ equivalents of these popular functions when the space allocated
will contain vector data.
You will need to use vec_free() to unallocate any space that you requested with these vector
allocation operators.
When default operator new is invoked for a class that has vector data someplace in the class definition or inheritance tree, vec_new() is invoked instead of the normal runtime new operator. The analogous situation is true for delete, in which case vec_delete() is called. If you define your own new or delete operator, you are responsible for properly-allocating and freeing
any resources by calling the proper operators.
There are many caveats to be aware of when using AltiVec and writing your code. This section details information you need to effectively work with many popular expressions.
Most common C/C++ operators do not interoperate with the vector data type. The following sections describe extensions that have
been made to accomodate AltiVec code.
The topics in this section are:
The sizeof() operator will return the value of 16 for the following usages:
main( void ) {
vector unsigned int a;
vector unsigned int *p;
int i = (int) sizeof(a); // returns 16 for i
int j = (int) sizeof(*p); // returns 16 for j
}
If either the left-hand side or the right-hand side of an expression
has a vector type, both sides of the expression must be the same type. So
this example is correct:
vector unsigned int a = (vector unsigned int) 1; vector unsigned int b = (vector unsigned int) 2; a = b;
However, this would be an error:
vector unsigned int a = (vector unsigned int) 1; vector float b = 2.09; a = b; // error
Using the address operator & works fine as long as it is applied to values of the correct (same) type. This is correct:
vector unsigned int a = (vector unsigned int) 1; vector unsigned int *p; p = &a; // correct usage
This is incorrect usage because the address of the variable a does not contain the same type as p:
vector unsigned int a = (vector unsigned int) 1; vector float *p; p = &a; // incorrect usage
Most pointer arithmetic techniques work with AltiVec. To access a vector element in an array of vector data, this works fine:
vector unsigned int foo[4]; vector unsigned int *p = &foo; vector unsigned int *q; q = ++p;
If p is a pointer to a vector type, then the value returned by *p is a 128-bit vector load operation from the address obtained
by clearing the low-order bits of p. This is equivalent to vec_ld(0,p). A vector store would be accomplished by vec_st(0,p). If you want to mark the data as Least-Recently Used (LRU), the
explicit instruction vec_ldl(0,p) or vec_st(int,type*) must be used.
Dereferencing a pointer to a non-vector type results in the standard behavior of either a load or copy of the corresponding type.
Accessing of non-aligned memory must be carried out explicitly
by one of the following vec_ld( int,type*) operation, a vec_ldl(int,type*) operation, a vec_st(int,type*) operation or a vec_stl( int,type*) operations.
Pointers to both vector and non-vector quantities can be assigned to each other. If you
apply a cast to a non-vector variable in an assignment to a vector quantity, you must ensure that you are assigning a 16-byte aligned
value to the vector data type. By using a cast, you are implying that you know this
to be true.
Casts from one vector type to another are provided using the usual C syntax:
long vector b = 0; int unsigned vector a = (vector unsigned int) b;
The data in b is then converted to the vector a type without changing the bit pattern.
You can use a vector constant wherever a vector data value is permitted. In other words, you can use a vector constant in a static or dynamic initializer, as a parameter to
a function call, or in an assignment. The compiler generates code
which either computes or loads the values into an AltiVec register.
The following forms are represented:
vector unsigned char Represents a vector unsigned char constant consisting of a set of 16 unsigned 8-bit quantities,
which all have the value specified by a single unsigned integer,
or as individually specified by 16 unsigned integers.
(vector unsigned char)(unsigned int)(vector unsigned char)(unsigned int1...unsigned int16)
vector signed char Represents a vector signed char constant consisting of a set of 16 signed 8-bit quantities, which
all have the value specified by a single integer, or as individually
specified by 16 integers.
(vector signed char)(signed int)(vector signed char)(signed int1...signed int16)
vector unsigned short Represents a vector unsigned short constant consisting of a set of 8 unsigned 16-bit quantities,
which all have the value specified by a single unsigned integer,
or as individually specified by 8 unsigned integers.
(vector unsigned short)(unsigned int)(vector unsigned short)(unsigned int1...unsigned int8)
vector unsigned int Represents a vector unsigned int constant consisting of a set of 4 unsigned 32-bit quantities,
which all have the value specified by a single unsigned integer,
or as individually specified by 4 unsigned integers.
(vector unsigned int)(unsigned int)(vector unsigned int)(unsigned int1...unsigned int4)
vector signed int Represents a vector signed int constant consisting of a set of 4 signed 32-bit quantities, which
all have the value specified by a single integer, or as individually
specified by 4 integers.
(vector signed int)(int)(vector signed int)(int1...unsigned int4)
vector float Represents a vector float constant consisting of a set of 4 32-bit floating-point quantities,
which all have the value specified by a single float value, or
as individually specified by 4 float values.
(vector float)(float)(vector float)(float1...unsigned float4)
The table shown in Table 9.8 shows what happens when you apply the vec_step() operator to a type to determine the size of a given vector type.
This is illustrated by the following example:
vector float c; int a = vec_step(c); int b = vec_step(vector float);
A vector unsigned short type is considered to contain 8 unsigned 2-byte values. A pointer
to unsigned 2-byte values used to index through an array of unsigned
2-byte values one full vector at a time should be incremented
by vec_step(vector unsigned short), which gives a value of 8.
For a complete list of AltiVec vector types, refer to "AltiVec Vector Data Formats."
Values for Adjusting Pointers:
The vector operators allow you to fully exploit the power of the AltiVec processors. A partial list of these operators is found in "AltiVec Intrinsics Support."
There are five different ways that these operators function:
This generates a vector instruction depending on the argument
types. An example of this is vec_add(). The operand types are used to determine whether the operation
is acceptable, to select a particular operation according to the
types of the arguments, and to determine the type of the result.
vec_add(vector signed char, vector signed char);
maps to vec_addubm() and returns a result of type vector signed char.
The following case maps onto vec_addubm() and returns a result of type vector unsigned short:
vec_add(vector unsigned short, vector unsigned short);
Each operator for an AltiVec operation requires a list of arguments representing the input operands, and returns a result. The permissible operand types for an AltiVec operation are restricted to those documented for the given operator. You can override these by explicitly casting the arguments to the correct type.
This is an operator that maps directly to an AltiVec instruction.
An example of this is vec_addubm().
The operand types are used to determine whether an operation is valid and what the result type is. For example, this is valid:
vec_addubm(vector signed char, vector signed char); // valid
because it represents a reasonable way to do modular addition with signed bytes. This would produce a result in which saturation treats the operands as unsigned.
vec_addubs(vector signed char, vector signed char); // not valid
This would produce a result in which adjacent pairs of signed bytes are treated as signed half words.
This is an operator that results in a 0 or 1, computed from an
AltiVec operation. An example of this is vec_all_eq().
Casting operations are discussed in "Type Casting."
Loading a vector of constant components is discussed in "Vector Constants."
The AltiVec operations that set condition register CR6 are treated somewhat differently. The programmer does not have access to specific register names. Instead of directly specifying a compare dot instruction, you reference a predicate that returns an integer value derived from the result of a compare dot instruction. This value can be used directly as a condition for branching.
The predicates all begin with vec_all_ or vec_any_. There are predicates to test the true and false state of any
bit that can be set by a compare dot instruction. For example,
vec_all_gt(p, q) tests the true value of bit 24 of the CR after executing some
vcmpgt. instruction.
To complete the coverage by predicates, additional predicates
exercise compare dot instructions with reversed or duplicated
arguments. As examples, vec_all_lt(p,q) performs a vcmpgtx(q,p) and vec_all_nan(s) is mapped onto vcmpeqfp(p,p). If you want to have both the result of the compare dot instruction
as returned in the vector register, and the value of CR6, you
must specify two instructions.
This section discusses the background information on the Mac OS linker and how it works. The topics in this section are:
The PowerPC linker deadstrips unused code and data only from files compiled by the CodeWarrior C/C++ compiler. Assembler relocatable files and C/C++ object files built by other compilers are never deadstripped. Deadstripping is particularly useful for C++ programs. Libraries (archives) built with the CodeWarrior C/C++ compiler only contribute the used objects to the linked program. If a library has assembly or other C/C++ compiler built files, only those files that have at least one referenced object contribute to the linked program. Completely unreferenced object files are always ignored.
It is useful to know that 'ckid' resources are stripped, in addition to stripping 'mcvs' resources with ID=128. These are the version control resources
used by Metrowerks Visual SourceSafe and MacCVSPro.
Link order is generally specified in the Link Order view of the Project window. For general information on setting link order, see the IDE User Guide.