[ First ]  [ Previous ]  [ Next ]  [ Last ]  [ Manuals ]

 

Chapter 13.

 

Targeting AMD Processors



This chapter discusses topics that are useful to consider when targeting the AMD-K6® and AMD-Athlon® family of processors.


Targeting AMD Processors Overview

The CodeWarrior IDE is capable of generating code that takes full advantage of speed optimizations available with AMD® processors. The AMD-K6, AMD-K6-2®, AMD-K6-III®, and AMD-Athlon processors are fully supported, as is 3DNow! technology.

You can find information about developing for these processors on the World Wide Web at:


http://www.amd.com/swdev/swdev.html

For data sheets and other programming notes for AMD-K6 processors (for example), go to:


http://www.amd.com/K6/k6docs/index.html

Topics in this chapter are:


Optimizations

This section discusses optimizations that are incorporated in the Metrowerks compiler. The information in this section is useful for writing code optimized for speed. It gives guidelines and information on some of the optimizations that are available.

The topics in this section are:


Calling Conventions

When writing code for AMD processors, you should be aware that the Win32 __stdcall calling conventions exact a performance penalty on the execution of AMD or MMX code.

If you enable the use of special calling conventions, the compiler will generate code that preserves fewer registers in the AMD processor, enabling faster execution. To enable these calling conventions, enable the Use MMX/3DNow! Calling Convention option in the x86 Processor panel. See "x86 Processor" to learn how to do this.

When using 3DNow! calling conventions, the entire application program (all source files and library source files) should be compiled using 3DNow! calling conventions. Any inconsistency caused by intermixing calling conventions in source files, library code, and assembler code can lead to runtime errors.


WARNING!

The Use MMX/3DNow! Calling Convention option is inappropriate for functions that are exported as external entry points, and, thus, can be called from other binaries that are not aware of 3DNow! calls. Returning from MMX or 3DNow! code to x86 code in another module without having done an FEMMS is very bad news.



Minimizing Mode Switching

The key to achieving faster runtime performance with AMD processors is minimizing mode switching. For this reason, 3DNow! mode performs better with code that contains int and float types. If your code contains math that uses float and double types, you will incur more mode switching, which will be expensive at runtime because of the AMD mode switch operation.

Since the C and C++ languages assume that floating-point constants are doubles unless explicitly labeled otherwise, you need to be careful how you write your code. If you intend to write your code to keep only int and float types, but you want to avoid double variable types, you need to do take some special steps. For example, if you need to define a floating-point constant called MyFudgeFactor having a value of 2.0 as a float type, you would write:


#define MyFudgeFactor 2.0f

instead of:


#define MyFudgeFactor 2.0

The compiler assumes that you want a type double for the constant if you do not specify 2.0f. This is because the ANSI C/C++ standard specifies that all un-suffixed floating-point constants need to be treated as double by default.


Vectorization

The CodeWarrior compilers perform a limited amount of vectorization of the optimized code.

In order to get this performance benefit, the code has to be written in a certain way. See "Loop Vectorization" for more information on writing code that takes advantage of vectorization.


NOTE

Some of the restrictions on vectorization may be relaxed in a future release of the tools. See "Loop Restrictions" for a list of these restrictions.


The restricted keyword is a C++ language feature that is supported in the IDE. Refer to "Code Generation Pragmas" for more information.


Loop Vectorization

Loop vectorization is a transformation that enables the compiler to generate vector instructions for a processor that supports these instructions, such as a Pentium with MMX and AMD 3DNow!. The optimizer looks at operations and operands in loops to decide whether vector instructions can be used to combine operations and operands. This results in a larger number of operations per cycle and less loop branching per iteration. which means faster code.

Loop vectorization will be utilized if the code is a counted loop that goes up, only contains assignment statements, and has no control code or pointer references. This is useful for doing operations on static or local arrays (arrays with a known base value). In other words, vector analysis generally succeeds if the code contains static objects, meaning that the bases are known at compile time.

The example in Listing 13.1 shows the loop form that the optimizer can improve for AMD targets.

Proper loop vectorization format:


for( i = initial; i op N; i = i + 1 ) {
	... = a[ i + k1 ];
a[ i + k ] = ...; ... = a[ i + k2 ];
a[i+k3] = ... }

In this example, "i" is the loop index and "a" is a static array. The references on the right side of the assignment represent loads of "a" and the references on the left represent stores into "a". The expression on the right side of the assignment can, in general, have one or more vector operands, constants, loop invariant scalars or expressions and vector operations.


Loop Restrictions

The following restrictions should be observed when writing loop structures:


Dataflow Restrictions

The following dataflow restrictions must also be observed.

¯ k > k1

¯ k2 > k

¯ k3 > k

For example, the loops in Listing 13.2 cannot be vectorized because doing so would change their semantics.

Semantics changed by loop vectorization:


//this loop violates the k > k1 condition
for( i = initial; i op N; i = i + 1 ) {
	b[ i ]= a[ i - 1 ];
	a[ i ] =b[ i ] ;
}
//this loop violates the k2 > k condition
for( i = initial; i op N; i = i + 1 ) {
	a[ i ] =b[i];
	a[i+1]= b[i];
} 
//this loop violates the k3 > k condition
for( i = initial; i op N; i = i + 1 ) {
	a[ i ] = b[ i ];
	b[ i ]= a[ i+1 ];
}

Vectorization requires that all loads and stores be disambiguated at compile time. If there is a load or store through a pointer in the loop, vectorization will not be possible.


Loop Distribution

Loop distribution refers to an optimization carried out on loops that can be only partially vectorized. For example, a loop that contains two statements, only one of which is vectorizable, is a candidate for loop distribution:

Loop Distribution:


for ( i = 0; i < 10; i++ ) {
	c[i] = b[i]*c[i]; // vectorizable
	a[i]=b[i]*a[i-1]; // not vectorizable
}

In this case the optimizer will split the original loop into two loops. One loop will be vectorized and one loop will not.


Alias Analysis

Alias Analysis is a facility that allows the compiler to track pointer values during compilation. By gathering extra information about pointer use, the compiler can apply more intelligent optimizations. Optimizations that were not possible before this improvement are now enabled, with the effect that pointer-based code runs faster.

To illustrate how this can improve your code, we examine some code containing a common sub-expression that can be eliminated. Common sub-expression elimination (CSE) is just one of the optimizations that the CodeWarrior IDE can perform. If your code contains the expression a*b*c multiple times, CSE allows the compiler to generate a temporary value and then replace each occurrence of the expression with this temporary value.

The example in Listing 13.4 demonstrates how CSE works across an assignment through a pointer.

CSE for Pointers:


void TestFunction2( int *int1, int *int2, int *int3, int *int4 )
{
	*int1 = *int3/*int4; // the RHS is the CSE here
	int1 = GetGobalIntPtr(NULL);
	*int1 = 0;
	*int2 = *int3/*int4; // the CSE again
}

Note that *int3/*int4 is a CSE, but involves pointers. Through the use of alias analysis this CSE is optimizable even though it involves pointers.

For more information on improving your code through the use of the restrict keyword, see "Code Generation Pragmas."


Handling Reduction Functions

A reduction function is a loop that contains an equation involving an assignment to a scalar value, as in Listing 13.5.

Example reduction function:


scalar = scalar OP vector

Note that an array is a vector.

When the optimizer is trying to employ vectorization to optimize loop code, it usually only makes sense to vectorize when the LHS (left-hand side) of an assignment is a vector. If the LHS is a scalar, then this is not a likely candidate for vectorization. However, the Metrowerks compiler can vectorize loops that have a scalar for the LHS.

For example, the code in Listing 13.6 can be fully optimized, even though there is a scalar for the LHS.

Reduction Functions on Scalars:


float a[100], b[100];
float scalar:
fred()
{
	scalar = 0.0f;
	for ( i = 0; i < 100; i++ ) {
		scalar = scalar + a[i] + b[i];
	}
}

For more information on vectorization, see "Vectorization."


Pragma Support

This section discusses the pragma statements that you can use when developing code for AMD processors. To learn more about #pragma directives, refer to the C Compiler Reference.

The topics in this section include:


Code Generation Pragmas

There are three pragmas that you can use to enable the various MMX and AMD code generation options. The #pragma statements are:

See "x86 Processor" for a description of each of these settings.


NOTE

The k63d #pragma is not Pentium-compatible.



NOTE

The __stdcall keyword overrides the #pragma.


Another useful pragma is c9x.This pragma has syntax of the form in Listing 13.7.

c9x pragma syntax:


#pragma c9x on|off|reset

When used with the restrict keyword, c9x is useful as a hint to the optimizer that the pointer being declared has no aliases later in the code. You can use the restrict keyword in a variable declaration whenever you do not assign one pointer to another, so that a given address in memory does not have two pointers to it. For more information on pointer alias reduction, see "Alias Analysis."


Constant Definition Pragmas

You can alter the compiler's handling of floating-point constants using the pragma in Listing 13.8.The parameter on|off|reset indicates that you must choose to turn the pragma on, turn it off, or reset it.

Example pragma format:


#pragma float_constants on|off|reset

The pragma allows you to override the compiler's treatment of floating-point constants. Normally, the compiler will default all un-suffixed constants of the form in Listing 13.9 to be of type double by default. If you want the compiler to interpret all un-suffixed constants as float constants instead of double constants, this pragma allows you to do this.

Default format:


const float 2.0;

Note that this pragma allows you to reverse the behavior of the compiler specified by ANSI C/C++ standards. The ANSI standard specifies that all un-suffixed floating-point constants need to be treated as double by default. This pragma allows you to alter this.

This is a useful feature because the use of double types can result in excessive mode-switching which adversely impacts performance. See "Library Considerations" and "Minimizing Mode Switching" for more information about this.


Library Considerations

A customized single-precision math library is provided as part of the Metrowerks Standard Library when targeting any AMD 3DNow! enabled processor. This single-precision library provides superior performance to other higher-precision math libraries, including the Metrowerks Standard Library for the X87 family of processors.

You do not need to make any modifications to your single-based code in order to use the new single-precision library. The single-precision library seamlessly replaces the extended-precision library whenever the application is targeting the AMD family of processors and is linking with any 3DNow! version of MSL C.


Name Mangling

The compiler will now mangle C++ names for 3DNow! code differently than code that does not use 3DNow! calling conventions. This causes linker errors when you try to mix code between 3DNow! and non-3DNow! paradigms as a safety feature. This eliminates difficult bugs that occur at runtime.

Only when the 3DNow! calling convention is enabled will the alternate name mangling scheme be used. To learn how to enable 3DNow! calling conventions, see "Use MMX/3DNow! Calling Conventions."


Required IDE Settings

In order to compile your code for AMD processors, you need to modify the settings in the IDE to generate the proper code. This section contains an example of how to do this. Other related items to this topic are discussed in "x86 Processor."

Choose Edit > Target Settings. The Target Settings window appears. Select x86 Processor in the Target Settings Panels area. Choose the processor for which you are developing from the Target Processor list-box. If you wish to generate MMX and 3DNow! instructions, enable the options for these in the Extended Instructions area of the panel. Review the information in "Calling Conventions" for more detailed information about this choice.

x86 Processor panel:

Using Assembly Code with AMD Processors

There are several new features now available to support assembly code:

For more information about writing assembly code, see "Assembly Code Support."


Athlon 3DNow! Instructions

Table 13.1 lists 3DNow! inline assembler instructions specific to the Athlon processor.

 

Instruction  
PF2IW  
PFNACC  
PFPNACC  
PI2FW  
PSWAPD  

Athlon 3DNow! Inline Instructions:

Athlon MMX Inline Instructions

Table 13.2 lists the MMX inline assembler instructions specific to the Athlon processor.

 

Instruction  
MASKMOVQ  
MOVNTQ  
PAVGB  
PAVGW  
PEXTRW  
PINSRW  
PMAXSW  
PMAXUB  
PMINSW  
PMINUB  
PMOVMSKB  
PMULHUW  
PSADBW  
PSHUFW  
PREFETCHNTA  
PREFETCH0  
PREFETCH1  
PREFETCH2  
SFENCE  

Athlon MMX Inline Instructions:

 

 

 


[ First ]  [ Previous ]  [ Next ]  [ Last ]  [ Manuals ]

Visit the Metrowerks website at: http://www.metrowerks.com
For assistance contact Metrowerks Technical Support at: cw_support@metrowerks.com
Copyright © 2000, Metrowerks Corp. All rights reserved.

Last updated: July 21, 2000